summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbellard <6490144+bellard@users.noreply.github.com>2020-09-06 19:07:30 +0200
committerbellard <6490144+bellard@users.noreply.github.com>2020-09-06 19:07:30 +0200
commit89007660998db0ee55f0ab3b34bb1a84f86fd3c4 (patch)
tree84311e5628175a91035ac2a62b16726650f2b62e
parent1722758717730ac0838418f142d82ca3cff4ad4b (diff)
downloadquickjs-89007660998db0ee55f0ab3b34bb1a84f86fd3c4.tar.gz
quickjs-89007660998db0ee55f0ab3b34bb1a84f86fd3c4.zip
2020-07-05 release
-rw-r--r--Changelog9
-rw-r--r--Makefile27
-rw-r--r--TODO8
-rw-r--r--VERSION2
-rw-r--r--cutils.h4
-rw-r--r--doc/jsbignum.html4
-rw-r--r--doc/jsbignum.pdfbin153057 -> 153049 bytes
-rw-r--r--doc/quickjs.html95
-rw-r--r--doc/quickjs.pdfbin163753 -> 165612 bytes
-rw-r--r--doc/quickjs.texi70
-rw-r--r--examples/point.c2
-rw-r--r--libbf.c75
-rw-r--r--libregexp.c49
-rw-r--r--qjs.c15
-rw-r--r--qjsc.c17
-rw-r--r--quickjs-atom.h1
-rw-r--r--quickjs-libc.c1322
-rw-r--r--quickjs-libc.h9
-rw-r--r--quickjs.c3311
-rw-r--r--quickjs.h29
-rw-r--r--repl.js72
-rw-r--r--test262.conf1
-rw-r--r--test262_errors.txt6
-rw-r--r--tests/bjson.c16
-rw-r--r--tests/microbench.js14
-rw-r--r--tests/test_bjson.js80
-rw-r--r--tests/test_builtin.js4
-rw-r--r--tests/test_language.js (renamed from tests/test_op.js)16
-rw-r--r--tests/test_std.js21
-rw-r--r--tests/test_worker.js93
30 files changed, 3709 insertions, 1663 deletions
diff --git a/Changelog b/Changelog
index 0f447db..86d965c 100644
--- a/Changelog
+++ b/Changelog
@@ -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
diff --git a/Makefile b/Makefile
index 88e605b..94c8e31 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/TODO b/TODO
index 8cfe0be..e5583c1 100644
--- a/TODO
+++ b/TODO
@@ -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
diff --git a/VERSION b/VERSION
index 69f162a..e970cf6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2020-04-12
+2020-07-05
diff --git a/cutils.h b/cutils.h
index 26c68ee..31f7cd8 100644
--- a/cutils.h
+++ b/cutils.h
@@ -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
diff --git a/doc/jsbignum.html b/doc/jsbignum.html
index ab31612..62c0287 100644
--- a/doc/jsbignum.html
+++ b/doc/jsbignum.html
@@ -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">
<!--
diff --git a/doc/jsbignum.pdf b/doc/jsbignum.pdf
index 442a8c0..7ca15ea 100644
--- a/doc/jsbignum.pdf
+++ b/doc/jsbignum.pdf
Binary files differ
diff --git a/doc/quickjs.html b/doc/quickjs.html
index 550d2a6..554f391 100644
--- a/doc/quickjs.html
+++ b/doc/quickjs.html
@@ -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>
@@ -637,6 +631,21 @@ optional properties:
</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>
<p>FILE prototype:
@@ -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>
@@ -973,6 +989,49 @@ to the timer.
<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>
<a name="QuickJS-C-API"></a>
diff --git a/doc/quickjs.pdf b/doc/quickjs.pdf
index 1cba474..04db9d2 100644
--- a/doc/quickjs.pdf
+++ b/doc/quickjs.pdf
Binary files differ
diff --git a/doc/quickjs.texi b/doc/quickjs.texi
index 04156af..17c1b9c 100644
--- a/doc/quickjs.texi
+++ b/doc/quickjs.texi
@@ -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
diff --git a/examples/point.c b/examples/point.c
index 049d135..fbe2ce1 100644
--- a/examples/point.c
+++ b/examples/point.c
@@ -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;
diff --git a/libbf.c b/libbf.c
index 2ef5104..3bf257a 100644
--- a/libbf.c
+++ b/libbf.c
@@ -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);
}
diff --git a/libregexp.c b/libregexp.c
index 7f6eef4..b455bf2 100644
--- a/libregexp.c
+++ b/libregexp.c
@@ -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;
}
diff --git a/qjs.c b/qjs.c
index 1c5974f..7e4839f 100644
--- a/qjs.c
+++ b/qjs.c
@@ -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");
diff --git a/qjsc.c b/qjsc.c
index e2aa6ad..de5a947 100644
--- a/qjsc.c
+++ b/qjsc.c
@@ -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");
diff --git a/quickjs-atom.h b/quickjs-atom.h
index 501aa1f..a353ad4 100644
--- a/quickjs-atom.h
+++ b/quickjs-atom.h
@@ -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")
diff --git a/quickjs-libc.c b/quickjs-libc.c
index 4e6d221..eda23c7 100644
--- a/quickjs-libc.c
+++ b/quickjs-libc.c
@@ -40,28 +40,38 @@
#if defined(_WIN32)
#include <windows.h>
#include <conio.h>
+#include <utime.h>
#else
#include <dlfcn.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
+
#if defined(__APPLE__)
typedef sig_t sighandler_t;
+#if !defined(environ)
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
+#endif
+#endif /* __APPLE__ */
+
#endif
+
+#if !defined(_WIN32)
+/* enable the os.Worker API. IT relies on POSIX threads */
+#define USE_WORKER
+#endif
+
+#ifdef USE_WORKER
+#include <pthread.h>
+#include <stdatomic.h>
#endif
#include "cutils.h"
#include "list.h"
#include "quickjs-libc.h"
-static void js_std_dbuf_init(JSContext *ctx, DynBuf *s)
-{
- dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt);
-}
-
/* TODO:
- - add worker
- - add minimal VT100 emulation for win32
- add socket calls
*/
@@ -84,14 +94,54 @@ typedef struct {
JSValue func;
} JSOSTimer;
-/* initialize the lists so js_std_free_handlers() can always be called */
-static struct list_head os_rw_handlers = LIST_HEAD_INIT(os_rw_handlers);
-static struct list_head os_signal_handlers = LIST_HEAD_INIT(os_signal_handlers);
-static struct list_head os_timers = LIST_HEAD_INIT(os_timers);
+typedef struct {
+ struct list_head link;
+ uint8_t *data;
+ size_t data_len;
+ /* list of SharedArrayBuffers, necessary to free the message */
+ uint8_t **sab_tab;
+ size_t sab_tab_len;
+} JSWorkerMessage;
+
+typedef struct {
+ int ref_count;
+#ifdef USE_WORKER
+ pthread_mutex_t mutex;
+#endif
+ struct list_head msg_queue; /* list of JSWorkerMessage.link */
+ int read_fd;
+ int write_fd;
+} JSWorkerMessagePipe;
+
+typedef struct {
+ struct list_head link;
+ JSWorkerMessagePipe *recv_pipe;
+ JSValue on_message_func;
+} JSWorkerMessageHandler;
+
+typedef struct JSThreadState {
+ struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */
+ struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */
+ struct list_head os_timers; /* list of JSOSTimer.link */
+ struct list_head port_list; /* list of JSWorkerMessageHandler.link */
+ int eval_script_recurse; /* only used in the main thread */
+ /* not used in the main thread */
+ JSWorkerMessagePipe *recv_pipe, *send_pipe;
+} JSThreadState;
+
static uint64_t os_pending_signals;
-static int eval_script_recurse;
static int (*os_poll_func)(JSContext *ctx);
+static void js_std_dbuf_init(JSContext *ctx, DynBuf *s)
+{
+ dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt);
+}
+
+static BOOL my_isdigit(int c)
+{
+ return (c >= '0' && c <= '9');
+}
+
static JSValue js_printf_internal(JSContext *ctx,
int argc, JSValueConst *argv, FILE *fp)
{
@@ -103,14 +153,12 @@ static JSValue js_printf_internal(JSContext *ctx,
const uint8_t *fmt, *fmt_end;
const uint8_t *p;
char *q;
- int i, c, len;
+ int i, c, len, mod;
size_t fmt_len;
int32_t int32_arg;
int64_t int64_arg;
double double_arg;
const char *string_arg;
- enum { PART_FLAGS, PART_WIDTH, PART_DOT, PART_PREC, PART_MODIFIER } part;
- int modsize;
/* Use indirect call to dbuf_printf to prevent gcc warning */
int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf;
@@ -132,150 +180,162 @@ static JSValue js_printf_internal(JSContext *ctx,
break;
q = fmtbuf;
*q++ = *fmt++; /* copy '%' */
- part = PART_FLAGS;
- modsize = 0;
- for (;;) {
- if (q >= fmtbuf + sizeof(fmtbuf) - 1)
- goto invalid;
-
- c = *fmt++;
- *q++ = c;
- *q = '\0';
-
- switch (c) {
- case '1': case '2': case '3':
- case '4': case '5': case '6':
- case '7': case '8': case '9':
- if (part != PART_PREC) {
- if (part <= PART_WIDTH)
- part = PART_WIDTH;
- else
- goto invalid;
- }
- continue;
-
- case '0': case '#': case '+': case '-': case ' ': case '\'':
- if (part > PART_FLAGS)
- goto invalid;
- continue;
-
- case '.':
- if (part > PART_DOT)
+
+ /* flags */
+ for(;;) {
+ c = *fmt;
+ if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' ||
+ c == '\'') {
+ if (q >= fmtbuf + sizeof(fmtbuf) - 1)
goto invalid;
- part = PART_DOT;
- continue;
-
- case '*':
- if (part < PART_WIDTH)
- part = PART_DOT;
- else if (part == PART_DOT)
- part = PART_MODIFIER;
- else
+ *q++ = c;
+ fmt++;
+ } else {
+ break;
+ }
+ }
+ /* width */
+ if (*fmt == '*') {
+ if (i >= argc)
+ goto missing;
+ if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
+ goto fail;
+ q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);
+ fmt++;
+ } else {
+ while (my_isdigit(*fmt)) {
+ if (q >= fmtbuf + sizeof(fmtbuf) - 1)
goto invalid;
-
+ *q++ = *fmt++;
+ }
+ }
+ if (*fmt == '.') {
+ if (q >= fmtbuf + sizeof(fmtbuf) - 1)
+ goto invalid;
+ *q++ = *fmt++;
+ if (*fmt == '*') {
if (i >= argc)
goto missing;
-
if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
goto fail;
q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);
- continue;
-
- case 'h':
- if (modsize != 0 && modsize != -1)
- goto invalid;
- modsize--;
- part = PART_MODIFIER;
- continue;
- case 'l':
- q--;
- if (modsize != 0 && modsize != 1)
- goto invalid;
- modsize++;
- part = PART_MODIFIER;
- continue;
-
- case 'c':
- if (i >= argc)
- goto missing;
- if (JS_IsString(argv[i])) {
- string_arg = JS_ToCString(ctx, argv[i++]);
- if (!string_arg)
- goto fail;
- int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);
- JS_FreeCString(ctx, string_arg);
- } else {
- if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
- goto fail;
- }
- /* handle utf-8 encoding explicitly */
- if ((unsigned)int32_arg > 0x10FFFF)
- int32_arg = 0xFFFD;
- /* ignore conversion flags, width and precision */
- len = unicode_to_utf8(cbuf, int32_arg);
- dbuf_put(&dbuf, cbuf, len);
- break;
-
- case 'd':
- case 'i':
- case 'o':
- case 'u':
- case 'x':
- case 'X':
- if (i >= argc)
- goto missing;
- if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++]))
- goto fail;
- if (modsize > 0) {
- q[1] = q[-1];
- q[-1] = q[0] = 'l';
- q[2] = '\0';
- dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg);
- } else {
- dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg);
+ fmt++;
+ } else {
+ while (my_isdigit(*fmt)) {
+ if (q >= fmtbuf + sizeof(fmtbuf) - 1)
+ goto invalid;
+ *q++ = *fmt++;
}
- break;
+ }
+ }
- case 's':
- if (i >= argc)
- goto missing;
- /* XXX: handle strings containing null characters */
+ /* we only support the "l" modifier for 64 bit numbers */
+ mod = ' ';
+ if (*fmt == 'l') {
+ mod = *fmt++;
+ }
+
+ /* type */
+ c = *fmt++;
+ if (q >= fmtbuf + sizeof(fmtbuf) - 1)
+ goto invalid;
+ *q++ = c;
+ *q = '\0';
+
+ switch (c) {
+ case 'c':
+ if (i >= argc)
+ goto missing;
+ if (JS_IsString(argv[i])) {
string_arg = JS_ToCString(ctx, argv[i++]);
if (!string_arg)
goto fail;
- dbuf_printf_fun(&dbuf, fmtbuf, string_arg);
+ int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);
JS_FreeCString(ctx, string_arg);
- break;
-
- case 'e':
- case 'f':
- case 'g':
- case 'a':
- case 'E':
- case 'F':
- case 'G':
- case 'A':
- if (i >= argc)
- goto missing;
- if (JS_ToFloat64(ctx, &double_arg, argv[i++]))
+ } else {
+ if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
goto fail;
- dbuf_printf_fun(&dbuf, fmtbuf, double_arg);
- break;
-
- case '%':
- dbuf_putc(&dbuf, '%');
- break;
+ }
+ /* handle utf-8 encoding explicitly */
+ if ((unsigned)int32_arg > 0x10FFFF)
+ int32_arg = 0xFFFD;
+ /* ignore conversion flags, width and precision */
+ len = unicode_to_utf8(cbuf, int32_arg);
+ dbuf_put(&dbuf, cbuf, len);
+ break;
+
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ if (i >= argc)
+ goto missing;
+ if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++]))
+ goto fail;
+ if (mod == 'l') {
+ /* 64 bit number */
+#if defined(_WIN32)
+ if (q >= fmtbuf + sizeof(fmtbuf) - 3)
+ goto invalid;
+ q[2] = q[-1];
+ q[-1] = 'I';
+ q[0] = '6';
+ q[1] = '4';
+ q[3] = '\0';
+ dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg);
+#else
+ if (q >= fmtbuf + sizeof(fmtbuf) - 2)
+ goto invalid;
+ q[1] = q[-1];
+ q[-1] = q[0] = 'l';
+ q[2] = '\0';
+ dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg);
+#endif
+ } else {
+ dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg);
+ }
+ break;
- default:
- /* XXX: should support an extension mechanism */
- invalid:
- JS_ThrowTypeError(ctx, "invalid conversion specifier in format string");
+ case 's':
+ if (i >= argc)
+ goto missing;
+ /* XXX: handle strings containing null characters */
+ string_arg = JS_ToCString(ctx, argv[i++]);
+ if (!string_arg)
goto fail;
- missing:
- JS_ThrowReferenceError(ctx, "missing argument for conversion specifier");
+ dbuf_printf_fun(&dbuf, fmtbuf, string_arg);
+ JS_FreeCString(ctx, string_arg);
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'a':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'A':
+ if (i >= argc)
+ goto missing;
+ if (JS_ToFloat64(ctx, &double_arg, argv[i++]))
goto fail;
- }
+ dbuf_printf_fun(&dbuf, fmtbuf, double_arg);
+ break;
+
+ case '%':
+ dbuf_putc(&dbuf, '%');
break;
+
+ default:
+ /* XXX: should support an extension mechanism */
+ invalid:
+ JS_ThrowTypeError(ctx, "invalid conversion specifier in format string");
+ goto fail;
+ missing:
+ JS_ThrowReferenceError(ctx, "missing argument for conversion specifier");
+ goto fail;
}
}
JS_FreeCString(ctx, fmt_str);
@@ -593,6 +653,8 @@ static int get_bool_option(JSContext *ctx, BOOL *pbool,
static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
const char *str;
size_t len;
JSValue ret;
@@ -610,7 +672,7 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
str = JS_ToCStringLen(ctx, &len, argv[0]);
if (!str)
return JS_EXCEPTION;
- if (++eval_script_recurse == 1) {
+ if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) {
/* install the interrupt handler */
JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL);
}
@@ -619,7 +681,7 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
JS_FreeCString(ctx, str);
- if (--eval_script_recurse == 0) {
+ if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
/* remove the interrupt handler */
JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL);
os_pending_signals &= ~((uint64_t)1 << SIGINT);
@@ -669,6 +731,21 @@ static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val,
return JS_NewString(ctx, strerror(err));
}
+static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ const char *str;
+ size_t len;
+
+ str = JS_ToCStringLen(ctx, &len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ obj = JS_ParseJSON2(ctx, str, len, "<input>", JS_PARSE_JSON_EXT);
+ JS_FreeCString(ctx, str);
+ return obj;
+}
+
static JSValue js_new_std_file(JSContext *ctx, FILE *f,
BOOL close_in_finalizer,
BOOL is_popen)
@@ -1321,6 +1398,7 @@ static const JSCFunctionListEntry js_std_funcs[] = {
JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ),
JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ),
JS_CFUNC_DEF("strerror", 1, js_std_strerror ),
+ JS_CFUNC_DEF("parseExtJSON", 1, js_std_parseExtJSON ),
/* FILE I/O */
JS_CFUNC_DEF("open", 2, js_std_open ),
@@ -1626,11 +1704,18 @@ static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val,
return JS_NewInt32(ctx, ret);
}
-static JSOSRWHandler *find_rh(int fd)
+static BOOL is_main_thread(JSRuntime *rt)
+{
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
+ return !ts->recv_pipe;
+}
+
+static JSOSRWHandler *find_rh(JSThreadState *ts, int fd)
{
JSOSRWHandler *rh;
struct list_head *el;
- list_for_each(el, &os_rw_handlers) {
+
+ list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == fd)
return rh;
@@ -1651,6 +1736,8 @@ static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh)
static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
JSOSRWHandler *rh;
int fd;
JSValueConst func;
@@ -1659,7 +1746,7 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
return JS_EXCEPTION;
func = argv[1];
if (JS_IsNull(func)) {
- rh = find_rh(fd);
+ rh = find_rh(ts, fd);
if (rh) {
JS_FreeValue(ctx, rh->rw_func[magic]);
rh->rw_func[magic] = JS_NULL;
@@ -1672,7 +1759,7 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
} else {
if (!JS_IsFunction(ctx, func))
return JS_ThrowTypeError(ctx, "not a function");
- rh = find_rh(fd);
+ rh = find_rh(ts, fd);
if (!rh) {
rh = js_mallocz(ctx, sizeof(*rh));
if (!rh)
@@ -1680,7 +1767,7 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
rh->fd = fd;
rh->rw_func[0] = JS_NULL;
rh->rw_func[1] = JS_NULL;
- list_add_tail(&rh->link, &os_rw_handlers);
+ list_add_tail(&rh->link, &ts->os_rw_handlers);
}
JS_FreeValue(ctx, rh->rw_func[magic]);
rh->rw_func[magic] = JS_DupValue(ctx, func);
@@ -1688,11 +1775,11 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
return JS_UNDEFINED;
}
-static JSOSSignalHandler *find_sh(int sig_num)
+static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num)
{
JSOSSignalHandler *sh;
struct list_head *el;
- list_for_each(el, &os_signal_handlers) {
+ list_for_each(el, &ts->os_signal_handlers) {
sh = list_entry(el, JSOSSignalHandler, link);
if (sh->sig_num == sig_num)
return sh;
@@ -1719,10 +1806,15 @@ typedef void (*sighandler_t)(int sig_num);
static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
JSOSSignalHandler *sh;
uint32_t sig_num;
JSValueConst func;
sighandler_t handler;
+
+ if (!is_main_thread(rt))
+ return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread");
if (JS_ToUint32(ctx, &sig_num, argv[0]))
return JS_EXCEPTION;
@@ -1731,7 +1823,7 @@ static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val,
func = argv[1];
/* func = null: SIG_DFL, func = undefined, SIG_IGN */
if (JS_IsNull(func) || JS_IsUndefined(func)) {
- sh = find_sh(sig_num);
+ sh = find_sh(ts, sig_num);
if (sh) {
free_sh(JS_GetRuntime(ctx), sh);
}
@@ -1743,13 +1835,13 @@ static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val,
} else {
if (!JS_IsFunction(ctx, func))
return JS_ThrowTypeError(ctx, "not a function");
- sh = find_sh(sig_num);
+ sh = find_sh(ts, sig_num);
if (!sh) {
sh = js_mallocz(ctx, sizeof(*sh));
if (!sh)
return JS_EXCEPTION;
sh->sig_num = sig_num;
- list_add_tail(&sh->link, &os_signal_handlers);
+ list_add_tail(&sh->link, &ts->os_signal_handlers);
}
JS_FreeValue(ctx, sh->func);
sh->func = JS_DupValue(ctx, func);
@@ -1813,6 +1905,8 @@ static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,
static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
int64_t delay;
JSValueConst func;
JSOSTimer *th;
@@ -1834,7 +1928,7 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
th->has_object = TRUE;
th->timeout = get_time_ms() + delay;
th->func = JS_DupValue(ctx, func);
- list_add_tail(&th->link, &os_timers);
+ list_add_tail(&th->link, &ts->os_timers);
JS_SetOpaque(obj, th);
return obj;
}
@@ -1872,6 +1966,8 @@ static void call_handler(JSContext *ctx, JSValueConst func)
static int js_os_poll(JSContext *ctx)
{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
int min_delay, console_fd;
int64_t cur_time, delay;
JSOSRWHandler *rh;
@@ -1879,14 +1975,14 @@ static int js_os_poll(JSContext *ctx)
/* XXX: handle signals if useful */
- if (list_empty(&os_rw_handlers) && list_empty(&os_timers))
+ if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers))
return -1; /* no more events */
/* XXX: only timers and basic console input are supported */
- if (!list_empty(&os_timers)) {
+ if (!list_empty(&ts->os_timers)) {
cur_time = get_time_ms();
min_delay = 10000;
- list_for_each(el, &os_timers) {
+ list_for_each(el, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link);
delay = th->timeout - cur_time;
if (delay <= 0) {
@@ -1894,9 +1990,9 @@ static int js_os_poll(JSContext *ctx)
/* the timer expired */
func = th->func;
th->func = JS_UNDEFINED;
- unlink_timer(JS_GetRuntime(ctx), th);
+ unlink_timer(rt, th);
if (!th->has_object)
- free_timer(JS_GetRuntime(ctx), th);
+ free_timer(rt, th);
call_handler(ctx, func);
JS_FreeValue(ctx, func);
return 0;
@@ -1909,7 +2005,7 @@ static int js_os_poll(JSContext *ctx)
}
console_fd = -1;
- list_for_each(el, &os_rw_handlers) {
+ list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {
console_fd = rh->fd;
@@ -1927,7 +2023,7 @@ static int js_os_poll(JSContext *ctx)
handle = (HANDLE)_get_osfhandle(console_fd);
ret = WaitForSingleObject(handle, ti);
if (ret == WAIT_OBJECT_0) {
- list_for_each(el, &os_rw_handlers) {
+ list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) {
call_handler(ctx, rh->rw_func[0]);
@@ -1942,8 +2038,88 @@ static int js_os_poll(JSContext *ctx)
return 0;
}
#else
+
+#ifdef USE_WORKER
+
+static void js_free_message(JSWorkerMessage *msg);
+
+/* return 1 if a message was handled, 0 if no message */
+static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
+ JSWorkerMessageHandler *port)
+{
+ JSWorkerMessagePipe *ps = port->recv_pipe;
+ int ret;
+ struct list_head *el;
+ JSWorkerMessage *msg;
+ JSValue obj, data_obj, func, retval;
+
+ pthread_mutex_lock(&ps->mutex);
+ if (!list_empty(&ps->msg_queue)) {
+ el = ps->msg_queue.next;
+ msg = list_entry(el, JSWorkerMessage, link);
+
+ /* remove the message from the queue */
+ list_del(&msg->link);
+
+ if (list_empty(&ps->msg_queue)) {
+ uint8_t buf[16];
+ int ret;
+ for(;;) {
+ ret = read(ps->read_fd, buf, sizeof(buf));
+ if (ret >= 0)
+ break;
+ if (errno != EAGAIN && errno != EINTR)
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&ps->mutex);
+
+ data_obj = JS_ReadObject(ctx, msg->data, msg->data_len,
+ JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE);
+
+ js_free_message(msg);
+
+ if (JS_IsException(data_obj))
+ goto fail;
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj)) {
+ JS_FreeValue(ctx, data_obj);
+ goto fail;
+ }
+ JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E);
+
+ /* 'func' might be destroyed when calling itself (if it frees the
+ handler), so must take extra care */
+ func = JS_DupValue(ctx, port->on_message_func);
+ retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj);
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, func);
+ if (JS_IsException(retval)) {
+ fail:
+ js_std_dump_error(ctx);
+ } else {
+ JS_FreeValue(ctx, retval);
+ }
+ ret = 1;
+ } else {
+ pthread_mutex_unlock(&ps->mutex);
+ ret = 0;
+ }
+ return ret;
+}
+#else
+static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
+ JSWorkerMessageHandler *port)
+{
+ return 0;
+}
+#endif
+
static int js_os_poll(JSContext *ctx)
{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
int ret, fd_max, min_delay;
int64_t cur_time, delay;
fd_set rfds, wfds;
@@ -1951,11 +2127,13 @@ static int js_os_poll(JSContext *ctx)
struct list_head *el;
struct timeval tv, *tvp;
- if (unlikely(os_pending_signals != 0)) {
+ /* only check signals in the main thread */
+ if (!ts->recv_pipe &&
+ unlikely(os_pending_signals != 0)) {
JSOSSignalHandler *sh;
uint64_t mask;
- list_for_each(el, &os_signal_handlers) {
+ list_for_each(el, &ts->os_signal_handlers) {
sh = list_entry(el, JSOSSignalHandler, link);
mask = (uint64_t)1 << sh->sig_num;
if (os_pending_signals & mask) {
@@ -1965,14 +2143,15 @@ static int js_os_poll(JSContext *ctx)
}
}
}
-
- if (list_empty(&os_rw_handlers) && list_empty(&os_timers))
+
+ if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) &&
+ list_empty(&ts->port_list))
return -1; /* no more events */
- if (!list_empty(&os_timers)) {
+ if (!list_empty(&ts->os_timers)) {
cur_time = get_time_ms();
min_delay = 10000;
- list_for_each(el, &os_timers) {
+ list_for_each(el, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link);
delay = th->timeout - cur_time;
if (delay <= 0) {
@@ -1980,9 +2159,9 @@ static int js_os_poll(JSContext *ctx)
/* the timer expired */
func = th->func;
th->func = JS_UNDEFINED;
- unlink_timer(JS_GetRuntime(ctx), th);
+ unlink_timer(rt, th);
if (!th->has_object)
- free_timer(JS_GetRuntime(ctx), th);
+ free_timer(rt, th);
call_handler(ctx, func);
JS_FreeValue(ctx, func);
return 0;
@@ -2000,7 +2179,7 @@ static int js_os_poll(JSContext *ctx)
FD_ZERO(&rfds);
FD_ZERO(&wfds);
fd_max = -1;
- list_for_each(el, &os_rw_handlers) {
+ list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
fd_max = max_int(fd_max, rh->fd);
if (!JS_IsNull(rh->rw_func[0]))
@@ -2008,25 +2187,46 @@ static int js_os_poll(JSContext *ctx)
if (!JS_IsNull(rh->rw_func[1]))
FD_SET(rh->fd, &wfds);
}
-
+
+ list_for_each(el, &ts->port_list) {
+ JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
+ if (!JS_IsNull(port->on_message_func)) {
+ JSWorkerMessagePipe *ps = port->recv_pipe;
+ fd_max = max_int(fd_max, ps->read_fd);
+ FD_SET(ps->read_fd, &rfds);
+ }
+ }
+
ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp);
if (ret > 0) {
- list_for_each(el, &os_rw_handlers) {
+ list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (!JS_IsNull(rh->rw_func[0]) &&
FD_ISSET(rh->fd, &rfds)) {
call_handler(ctx, rh->rw_func[0]);
/* must stop because the list may have been modified */
- break;
+ goto done;
}
- if (!JS_IsNull(rh->rw_func[1])) {
- FD_SET(rh->fd, &wfds);
+ if (!JS_IsNull(rh->rw_func[1]) &&
+ FD_ISSET(rh->fd, &wfds)) {
call_handler(ctx, rh->rw_func[1]);
/* must stop because the list may have been modified */
- break;
+ goto done;
+ }
+ }
+
+ list_for_each(el, &ts->port_list) {
+ JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
+ if (!JS_IsNull(port->on_message_func)) {
+ JSWorkerMessagePipe *ps = port->recv_pipe;
+ if (FD_ISSET(ps->read_fd, &rfds)) {
+ if (handle_posted_message(rt, ctx, port))
+ goto done;
+ }
}
}
}
+ done:
return 0;
}
#endif /* !_WIN32 */
@@ -2071,8 +2271,6 @@ static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val,
return make_string_error(ctx, buf, err);
}
-#if !defined(_WIN32)
-
static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@@ -2087,28 +2285,6 @@ static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val,
return JS_NewInt32(ctx, err);
}
-/* return [path, errorcode] */
-static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- const char *path;
- char buf[PATH_MAX], *res;
- int err;
-
- path = JS_ToCString(ctx, argv[0]);
- if (!path)
- return JS_EXCEPTION;
- res = realpath(path, buf);
- JS_FreeCString(ctx, path);
- if (!res) {
- buf[0] = '\0';
- err = errno;
- } else {
- err = 0;
- }
- return make_string_error(ctx, buf, err);
-}
-
static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@@ -2124,15 +2300,66 @@ static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val,
path = JS_ToCString(ctx, argv[0]);
if (!path)
return JS_EXCEPTION;
+#if defined(_WIN32)
+ (void)mode;
+ ret = js_get_errno(mkdir(path));
+#else
ret = js_get_errno(mkdir(path, mode));
+#endif
JS_FreeCString(ctx, path);
return JS_NewInt32(ctx, ret);
}
+/* return [array, errorcode] */
+static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *path;
+ DIR *f;
+ struct dirent *d;
+ JSValue obj;
+ int err;
+ uint32_t len;
+
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj)) {
+ JS_FreeCString(ctx, path);
+ return JS_EXCEPTION;
+ }
+ f = opendir(path);
+ if (!f)
+ err = errno;
+ else
+ err = 0;
+ JS_FreeCString(ctx, path);
+ if (!f)
+ goto done;
+ len = 0;
+ for(;;) {
+ errno = 0;
+ d = readdir(f);
+ if (!d) {
+ err = errno;
+ break;
+ }
+ JS_DefinePropertyValueUint32(ctx, obj, len++,
+ JS_NewString(ctx, d->d_name),
+ JS_PROP_C_W_E);
+ }
+ closedir(f);
+ done:
+ return make_obj_error(ctx, obj, err);
+}
+
+#if !defined(_WIN32)
static int64_t timespec_to_ms(const struct timespec *tv)
{
return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000);
}
+#endif
/* return [obj, errcode] */
static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
@@ -2146,10 +2373,14 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
path = JS_ToCString(ctx, argv[0]);
if (!path)
return JS_EXCEPTION;
+#if defined(_WIN32)
+ res = stat(path, &st);
+#else
if (is_lstat)
res = lstat(path, &st);
else
res = stat(path, &st);
+#endif
JS_FreeCString(ctx, path);
if (res < 0) {
err = errno;
@@ -2183,10 +2414,22 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
JS_DefinePropertyValueStr(ctx, obj, "size",
JS_NewInt64(ctx, st.st_size),
JS_PROP_C_W_E);
+#if !defined(_WIN32)
JS_DefinePropertyValueStr(ctx, obj, "blocks",
JS_NewInt64(ctx, st.st_blocks),
JS_PROP_C_W_E);
-#if defined(__APPLE__)
+#endif
+#if defined(_WIN32)
+ JS_DefinePropertyValueStr(ctx, obj, "atime",
+ JS_NewInt64(ctx, (int64_t)st.st_atime * 1000),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "mtime",
+ JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000),
+ JS_PROP_C_W_E);
+ JS_DefinePropertyValueStr(ctx, obj, "ctime",
+ JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000),
+ JS_PROP_C_W_E);
+#elif defined(__APPLE__)
JS_DefinePropertyValueStr(ctx, obj, "atime",
JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)),
JS_PROP_C_W_E);
@@ -2211,6 +2454,71 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
return make_obj_error(ctx, obj, err);
}
+#if !defined(_WIN32)
+static void ms_to_timeval(struct timeval *tv, uint64_t v)
+{
+ tv->tv_sec = v / 1000;
+ tv->tv_usec = (v % 1000) * 1000;
+}
+#endif
+
+static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *path;
+ int64_t atime, mtime;
+ int ret;
+
+ if (JS_ToInt64(ctx, &atime, argv[1]))
+ return JS_EXCEPTION;
+ if (JS_ToInt64(ctx, &mtime, argv[2]))
+ return JS_EXCEPTION;
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+#if defined(_WIN32)
+ {
+ struct _utimbuf times;
+ times.actime = atime / 1000;
+ times.modtime = mtime / 1000;
+ ret = js_get_errno(_utime(path, &times));
+ }
+#else
+ {
+ struct timeval times[2];
+ ms_to_timeval(&times[0], atime);
+ ms_to_timeval(&times[1], mtime);
+ ret = js_get_errno(utimes(path, times));
+ }
+#endif
+ JS_FreeCString(ctx, path);
+ return JS_NewInt32(ctx, ret);
+}
+
+#if !defined(_WIN32)
+
+/* return [path, errorcode] */
+static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *path;
+ char buf[PATH_MAX], *res;
+ int err;
+
+ path = JS_ToCString(ctx, argv[0]);
+ if (!path)
+ return JS_EXCEPTION;
+ res = realpath(path, buf);
+ JS_FreeCString(ctx, path);
+ if (!res) {
+ buf[0] = '\0';
+ err = errno;
+ } else {
+ err = 0;
+ }
+ return make_string_error(ctx, buf, err);
+}
+
static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@@ -2255,78 +2563,6 @@ static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val,
return make_string_error(ctx, buf, err);
}
-/* return [array, errorcode] */
-static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- const char *path;
- DIR *f;
- struct dirent *d;
- JSValue obj;
- int err;
- uint32_t len;
-
- path = JS_ToCString(ctx, argv[0]);
- if (!path)
- return JS_EXCEPTION;
- obj = JS_NewArray(ctx);
- if (JS_IsException(obj)) {
- JS_FreeCString(ctx, path);
- return JS_EXCEPTION;
- }
- f = opendir(path);
- if (!f)
- err = errno;
- else
- err = 0;
- JS_FreeCString(ctx, path);
- if (!f)
- goto done;
- len = 0;
- for(;;) {
- errno = 0;
- d = readdir(f);
- if (!d) {
- err = errno;
- break;
- }
- JS_DefinePropertyValueUint32(ctx, obj, len++,
- JS_NewString(ctx, d->d_name),
- JS_PROP_C_W_E);
- }
- closedir(f);
- done:
- return make_obj_error(ctx, obj, err);
-}
-
-static void ms_to_timeval(struct timeval *tv, uint64_t v)
-{
- tv->tv_sec = v / 1000;
- tv->tv_usec = (v % 1000) * 1000;
-}
-
-static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- const char *path;
- int64_t atime, mtime;
- int ret;
- struct timeval times[2];
-
- if (JS_ToInt64(ctx, &atime, argv[1]))
- return JS_EXCEPTION;
- if (JS_ToInt64(ctx, &mtime, argv[2]))
- return JS_EXCEPTION;
- path = JS_ToCString(ctx, argv[0]);
- if (!path)
- return JS_EXCEPTION;
- ms_to_timeval(&times[0], atime);
- ms_to_timeval(&times[1], mtime);
- ret = js_get_errno(utimes(path, times));
- JS_FreeCString(ctx, path);
- return JS_NewInt32(ctx, ret);
-}
-
static char **build_envp(JSContext *ctx, JSValueConst obj)
{
uint32_t len, i;
@@ -2404,7 +2640,7 @@ static int my_execvpe(const char *filename, char **argv, char **envp)
path = getenv("PATH");
if (!path)
- path = "/bin:/usr/bin";
+ path = (char *)"/bin:/usr/bin";
eacces_error = FALSE;
p = path;
for(p = path; p != NULL; p = p_next) {
@@ -2746,6 +2982,441 @@ static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val,
#endif /* !_WIN32 */
+#ifdef USE_WORKER
+
+/* Worker */
+
+typedef struct {
+ JSWorkerMessagePipe *recv_pipe;
+ JSWorkerMessagePipe *send_pipe;
+ JSWorkerMessageHandler *msg_handler;
+} JSWorkerData;
+
+typedef struct {
+ /* source code of the worker */
+ char *eval_buf;
+ size_t eval_buf_len;
+ JSWorkerMessagePipe *recv_pipe, *send_pipe;
+} WorkerFuncArgs;
+
+typedef struct {
+ int ref_count;
+ uint64_t buf[0];
+} JSSABHeader;
+
+static JSClassID js_worker_class_id;
+
+static int atomic_add_int(int *ptr, int v)
+{
+ return atomic_fetch_add((_Atomic(uint32_t) *)ptr, v) + v;
+}
+
+/* shared array buffer allocator */
+static void *js_sab_alloc(void *opaque, size_t size)
+{
+ JSSABHeader *sab;
+ sab = malloc(sizeof(JSSABHeader) + size);
+ if (!sab)
+ return NULL;
+ sab->ref_count = 1;
+ return sab->buf;
+}
+
+static void js_sab_free(void *opaque, void *ptr)
+{
+ JSSABHeader *sab;
+ int ref_count;
+ sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader));
+ ref_count = atomic_add_int(&sab->ref_count, -1);
+ assert(ref_count >= 0);
+ if (ref_count == 0) {
+ free(sab);
+ }
+}
+
+static void js_sab_dup(void *opaque, void *ptr)
+{
+ JSSABHeader *sab;
+ sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader));
+ atomic_add_int(&sab->ref_count, 1);
+}
+
+static JSWorkerMessagePipe *js_new_message_pipe(void)
+{
+ JSWorkerMessagePipe *ps;
+ int pipe_fds[2];
+
+ if (pipe(pipe_fds) < 0)
+ return NULL;
+
+ ps = malloc(sizeof(*ps));
+ if (!ps) {
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return NULL;
+ }
+ ps->ref_count = 1;
+ init_list_head(&ps->msg_queue);
+ pthread_mutex_init(&ps->mutex, NULL);
+ ps->read_fd = pipe_fds[0];
+ ps->write_fd = pipe_fds[1];
+ return ps;
+}
+
+static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps)
+{
+ atomic_add_int(&ps->ref_count, 1);
+ return ps;
+}
+
+static void js_free_message(JSWorkerMessage *msg)
+{
+ size_t i;
+ /* free the SAB */
+ for(i = 0; i < msg->sab_tab_len; i++) {
+ js_sab_free(NULL, msg->sab_tab[i]);
+ }
+ free(msg->sab_tab);
+ free(msg->data);
+ free(msg);
+}
+
+static void js_free_message_pipe(JSWorkerMessagePipe *ps)
+{
+ struct list_head *el, *el1;
+ JSWorkerMessage *msg;
+ int ref_count;
+
+ if (!ps)
+ return;
+
+ ref_count = atomic_add_int(&ps->ref_count, -1);
+ assert(ref_count >= 0);
+ if (ref_count == 0) {
+ list_for_each_safe(el, el1, &ps->msg_queue) {
+ msg = list_entry(el, JSWorkerMessage, link);
+ js_free_message(msg);
+ }
+ pthread_mutex_destroy(&ps->mutex);
+ close(ps->read_fd);
+ close(ps->write_fd);
+ free(ps);
+ }
+}
+
+static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port)
+{
+ if (port) {
+ js_free_message_pipe(port->recv_pipe);
+ JS_FreeValueRT(rt, port->on_message_func);
+ list_del(&port->link);
+ js_free_rt(rt, port);
+ }
+}
+
+static void js_worker_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSWorkerData *worker = JS_GetOpaque(val, js_worker_class_id);
+ if (worker) {
+ js_free_message_pipe(worker->recv_pipe);
+ js_free_message_pipe(worker->send_pipe);
+ js_free_port(rt, worker->msg_handler);
+ js_free_rt(rt, worker);
+ }
+}
+
+static JSClassDef js_worker_class = {
+ "Worker",
+ .finalizer = js_worker_finalizer,
+};
+
+static void *worker_func(void *opaque)
+{
+ WorkerFuncArgs *args = opaque;
+ JSRuntime *rt;
+ JSThreadState *ts;
+ JSContext *ctx;
+ JSValue retval;
+
+ rt = JS_NewRuntime();
+ if (rt == NULL) {
+ fprintf(stderr, "JS_NewRuntime failure");
+ exit(1);
+ }
+ js_std_init_handlers(rt);
+
+ /* set the pipe to communicate with the parent */
+ ts = JS_GetRuntimeOpaque(rt);
+ ts->recv_pipe = args->recv_pipe;
+ ts->send_pipe = args->send_pipe;
+
+ ctx = JS_NewContext(rt);
+ if (ctx == NULL) {
+ fprintf(stderr, "JS_NewContext failure");
+ }
+
+ JS_SetCanBlock(rt, TRUE);
+
+ js_std_add_helpers(ctx, -1, NULL);
+
+ /* system modules */
+ js_init_module_std(ctx, "std");
+ js_init_module_os(ctx, "os");
+
+ retval = JS_Eval(ctx, args->eval_buf, args->eval_buf_len,
+ "<worker>", JS_EVAL_TYPE_MODULE);
+ free(args->eval_buf);
+ free(args);
+
+ if (JS_IsException(retval))
+ js_std_dump_error(ctx);
+ JS_FreeValue(ctx, retval);
+
+ js_std_loop(ctx);
+
+ JS_FreeContext(ctx);
+ js_std_free_handlers(rt);
+ JS_FreeRuntime(rt);
+ return NULL;
+}
+
+static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target,
+ JSWorkerMessagePipe *recv_pipe,
+ JSWorkerMessagePipe *send_pipe)
+{
+ JSValue obj = JS_UNDEFINED, proto;
+ JSWorkerData *s;
+
+ /* create the object */
+ if (JS_IsUndefined(new_target)) {
+ proto = JS_GetClassProto(ctx, js_worker_class_id);
+ } else {
+ proto = JS_GetPropertyStr(ctx, new_target, "prototype");
+ if (JS_IsException(proto))
+ goto fail;
+ }
+ obj = JS_NewObjectProtoClass(ctx, proto, js_worker_class_id);
+ JS_FreeValue(ctx, proto);
+ if (JS_IsException(obj))
+ goto fail;
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ goto fail;
+ s->recv_pipe = js_dup_message_pipe(recv_pipe);
+ s->send_pipe = js_dup_message_pipe(send_pipe);
+
+ JS_SetOpaque(obj, s);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ WorkerFuncArgs *args;
+ const char *str;
+ size_t str_len;
+ pthread_t tid;
+ pthread_attr_t attr;
+ JSValue obj = JS_UNDEFINED;
+ int ret;
+
+ /* XXX: in order to avoid problems with resource liberation, we
+ don't support creating workers inside workers */
+ if (!is_main_thread(rt))
+ return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker");
+
+ /* script source */
+
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+
+ args = malloc(sizeof(*args));
+ if (!args) {
+ JS_ThrowOutOfMemory(ctx);
+ goto fail;
+ }
+ memset(args, 0, sizeof(*args));
+ args->eval_buf = malloc(str_len + 1);
+ if (!args->eval_buf) {
+ JS_ThrowOutOfMemory(ctx);
+ goto fail;
+ }
+ memcpy(args->eval_buf, str, str_len + 1);
+ args->eval_buf_len = str_len;
+ JS_FreeCString(ctx, str);
+ str = NULL;
+
+ /* ports */
+ args->recv_pipe = js_new_message_pipe();
+ if (!args->recv_pipe)
+ goto fail;
+ args->send_pipe = js_new_message_pipe();
+ if (!args->send_pipe)
+ goto fail;
+
+ obj = js_worker_ctor_internal(ctx, new_target,
+ args->send_pipe, args->recv_pipe);
+ if (JS_IsUndefined(obj))
+ goto fail;
+
+ pthread_attr_init(&attr);
+ /* no join at the end */
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ret = pthread_create(&tid, &attr, worker_func, args);
+ pthread_attr_destroy(&attr);
+ if (ret != 0) {
+ JS_ThrowTypeError(ctx, "could not create worker");
+ goto fail;
+ }
+ return obj;
+ fail:
+ JS_FreeCString(ctx, str);
+ if (args) {
+ free(args->eval_buf);
+ js_free_message_pipe(args->recv_pipe);
+ js_free_message_pipe(args->send_pipe);
+ free(args);
+ }
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);
+ JSWorkerMessagePipe *ps;
+ size_t data_len, sab_tab_len, i;
+ uint8_t *data;
+ JSWorkerMessage *msg;
+ uint8_t **sab_tab;
+
+ if (!worker)
+ return JS_EXCEPTION;
+
+ data = JS_WriteObject2(ctx, &data_len, argv[0],
+ JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE,
+ &sab_tab, &sab_tab_len);
+ if (!data)
+ return JS_EXCEPTION;
+
+ msg = malloc(sizeof(*msg));
+ if (!msg)
+ goto fail;
+ msg->data = NULL;
+ msg->sab_tab = NULL;
+
+ /* must reallocate because the allocator may be different */
+ msg->data = malloc(data_len);
+ if (!msg->data)
+ goto fail;
+ memcpy(msg->data, data, data_len);
+ msg->data_len = data_len;
+
+ msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len);
+ if (!msg->sab_tab)
+ goto fail;
+ memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len);
+ msg->sab_tab_len = sab_tab_len;
+
+ js_free(ctx, data);
+ js_free(ctx, sab_tab);
+
+ /* increment the SAB reference counts */
+ for(i = 0; i < msg->sab_tab_len; i++) {
+ js_sab_dup(NULL, msg->sab_tab[i]);
+ }
+
+ ps = worker->send_pipe;
+ pthread_mutex_lock(&ps->mutex);
+ /* indicate that data is present */
+ if (list_empty(&ps->msg_queue)) {
+ uint8_t ch = '\0';
+ int ret;
+ for(;;) {
+ ret = write(ps->write_fd, &ch, 1);
+ if (ret == 1)
+ break;
+ if (ret < 0 && (errno != EAGAIN || errno != EINTR))
+ break;
+ }
+ }
+ list_add_tail(&msg->link, &ps->msg_queue);
+ pthread_mutex_unlock(&ps->mutex);
+ return JS_UNDEFINED;
+ fail:
+ if (msg) {
+ free(msg->data);
+ free(msg->sab_tab);
+ free(msg);
+ }
+ js_free(ctx, data);
+ js_free(ctx, sab_tab);
+ return JS_EXCEPTION;
+
+}
+
+static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val,
+ JSValueConst func)
+{
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
+ JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);
+ JSWorkerMessageHandler *port;
+
+ if (!worker)
+ return JS_EXCEPTION;
+
+ port = worker->msg_handler;
+ if (JS_IsNull(func)) {
+ if (port) {
+ js_free_port(rt, port);
+ worker->msg_handler = NULL;
+ }
+ } else {
+ if (!JS_IsFunction(ctx, func))
+ return JS_ThrowTypeError(ctx, "not a function");
+ if (!port) {
+ port = js_mallocz(ctx, sizeof(*port));
+ if (!port)
+ return JS_EXCEPTION;
+ port->recv_pipe = js_dup_message_pipe(worker->recv_pipe);
+ port->on_message_func = JS_NULL;
+ list_add_tail(&port->link, &ts->port_list);
+ worker->msg_handler = port;
+ }
+ JS_FreeValue(ctx, port->on_message_func);
+ port->on_message_func = JS_DupValue(ctx, func);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_worker_get_onmessage(JSContext *ctx, JSValueConst this_val)
+{
+ JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);
+ JSWorkerMessageHandler *port;
+ if (!worker)
+ return JS_EXCEPTION;
+ port = worker->msg_handler;
+ if (port) {
+ return JS_DupValue(ctx, port->on_message_func);
+ } else {
+ return JS_NULL;
+ }
+}
+
+static const JSCFunctionListEntry js_worker_proto_funcs[] = {
+ JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ),
+ JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ),
+};
+
+#endif /* USE_WORKER */
+
#if defined(_WIN32)
#define OS_PLATFORM "win32"
#elif defined(__APPLE__)
@@ -2806,12 +3477,9 @@ static const JSCFunctionListEntry js_os_funcs[] = {
JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ),
JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),
JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
-#if !defined(_WIN32)
JS_CFUNC_DEF("chdir", 0, js_os_chdir ),
- JS_CFUNC_DEF("realpath", 1, js_os_realpath ),
JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ),
- JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ),
- JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ),
+ JS_CFUNC_DEF("readdir", 1, js_os_readdir ),
/* st_mode constants */
OS_FLAG(S_IFMT),
OS_FLAG(S_IFIFO),
@@ -2819,14 +3487,19 @@ static const JSCFunctionListEntry js_os_funcs[] = {
OS_FLAG(S_IFDIR),
OS_FLAG(S_IFBLK),
OS_FLAG(S_IFREG),
+#if !defined(_WIN32)
OS_FLAG(S_IFSOCK),
OS_FLAG(S_IFLNK),
OS_FLAG(S_ISGID),
OS_FLAG(S_ISUID),
+#endif
+ JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ),
+ JS_CFUNC_DEF("utimes", 3, js_os_utimes ),
+#if !defined(_WIN32)
+ JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ),
+ JS_CFUNC_DEF("realpath", 1, js_os_realpath ),
JS_CFUNC_DEF("symlink", 2, js_os_symlink ),
JS_CFUNC_DEF("readlink", 1, js_os_readlink ),
- JS_CFUNC_DEF("readdir", 1, js_os_readdir ),
- JS_CFUNC_DEF("utimes", 3, js_os_utimes ),
JS_CFUNC_DEF("exec", 1, js_os_exec ),
JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ),
OS_FLAG(WNOHANG),
@@ -2845,7 +3518,35 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
/* OSTimer class */
JS_NewClassID(&js_os_timer_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);
-
+
+#ifdef USE_WORKER
+ {
+ JSRuntime *rt = JS_GetRuntime(ctx);
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
+ JSValue proto, obj;
+ /* Worker class */
+ JS_NewClassID(&js_worker_class_id);
+ JS_NewClass(JS_GetRuntime(ctx), js_worker_class_id, &js_worker_class);
+ proto = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs));
+
+ obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1,
+ JS_CFUNC_constructor, 0);
+ JS_SetConstructor(ctx, obj, proto);
+
+ JS_SetClassProto(ctx, js_worker_class_id, proto);
+
+ /* set 'Worker.parent' if necessary */
+ if (ts->recv_pipe && ts->send_pipe) {
+ JS_DefinePropertyValueStr(ctx, obj, "parent",
+ js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe),
+ JS_PROP_C_W_E);
+ }
+
+ JS_SetModuleExport(ctx, m, "Worker", obj);
+ }
+#endif /* USE_WORKER */
+
return JS_SetModuleExportList(ctx, m, js_os_funcs,
countof(js_os_funcs));
}
@@ -2857,6 +3558,9 @@ JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name)
if (!m)
return NULL;
JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs));
+#ifdef USE_WORKER
+ JS_AddModuleExport(ctx, m, "Worker");
+#endif
return m;
}
@@ -2896,46 +3600,76 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv)
JS_SetPropertyStr(ctx, global_obj, "console", console);
/* same methods as the mozilla JS shell */
- args = JS_NewArray(ctx);
- for(i = 0; i < argc; i++) {
- JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i]));
+ if (argc >= 0) {
+ args = JS_NewArray(ctx);
+ for(i = 0; i < argc; i++) {
+ JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i]));
+ }
+ JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args);
}
- JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args);
-
+
JS_SetPropertyStr(ctx, global_obj, "print",
JS_NewCFunction(ctx, js_print, "print", 1));
JS_SetPropertyStr(ctx, global_obj, "__loadScript",
JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1));
JS_FreeValue(ctx, global_obj);
+}
- /* XXX: not multi-context */
- init_list_head(&os_rw_handlers);
- init_list_head(&os_signal_handlers);
- init_list_head(&os_timers);
- os_pending_signals = 0;
+void js_std_init_handlers(JSRuntime *rt)
+{
+ JSThreadState *ts;
+
+ ts = malloc(sizeof(*ts));
+ if (!ts) {
+ fprintf(stderr, "Could not allocate memory for the worker");
+ exit(1);
+ }
+ memset(ts, 0, sizeof(*ts));
+ init_list_head(&ts->os_rw_handlers);
+ init_list_head(&ts->os_signal_handlers);
+ init_list_head(&ts->os_timers);
+ init_list_head(&ts->port_list);
+
+ JS_SetRuntimeOpaque(rt, ts);
+
+#ifdef USE_WORKER
+ /* set the SharedArrayBuffer memory handlers */
+ {
+ JSSharedArrayBufferFunctions sf;
+ memset(&sf, 0, sizeof(sf));
+ sf.sab_alloc = js_sab_alloc;
+ sf.sab_free = js_sab_free;
+ sf.sab_dup = js_sab_dup;
+ JS_SetSharedArrayBufferFunctions(rt, &sf);
+ }
+#endif
}
void js_std_free_handlers(JSRuntime *rt)
{
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
struct list_head *el, *el1;
- list_for_each_safe(el, el1, &os_rw_handlers) {
+ list_for_each_safe(el, el1, &ts->os_rw_handlers) {
JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link);
free_rw_handler(rt, rh);
}
- list_for_each_safe(el, el1, &os_signal_handlers) {
+ list_for_each_safe(el, el1, &ts->os_signal_handlers) {
JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link);
free_sh(rt, sh);
}
- list_for_each_safe(el, el1, &os_timers) {
+ list_for_each_safe(el, el1, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link);
unlink_timer(rt, th);
if (!th->has_object)
free_timer(rt, th);
}
+
+ free(ts);
+ JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
}
static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val)
diff --git a/quickjs-libc.h b/quickjs-libc.h
index 93a53da..b105028 100644
--- a/quickjs-libc.h
+++ b/quickjs-libc.h
@@ -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 */
diff --git a/quickjs.c b/quickjs.c
index 7da7253..8fbb7a3 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -71,6 +71,12 @@
#define CONFIG_ATOMICS
#endif
+#if !defined(EMSCRIPTEN)
+/* enable stack limitation */
+#define CONFIG_STACK_CHECK
+#endif
+
+
/* dump object free */
//#define DUMP_FREE
//#define DUMP_CLOSURE
@@ -281,7 +287,9 @@ struct JSRuntime {
void *module_loader_opaque;
BOOL can_block : 8; /* TRUE if Atomics.wait can block */
-
+ /* used to allocate, free and clone SharedArrayBuffers */
+ JSSharedArrayBufferFunctions sab_funcs;
+
/* Shape hash table */
int shape_hash_bits;
int shape_hash_size;
@@ -392,7 +400,7 @@ typedef struct JSBigDecimal {
typedef enum {
JS_AUTOINIT_ID_PROTOTYPE,
JS_AUTOINIT_ID_MODULE_NS,
- JS_AUTOINIT_ID_PROP, /* must be last */
+ JS_AUTOINIT_ID_PROP,
} JSAutoInitIDEnum;
/* must be large enough to have a negligible runtime cost and small
@@ -625,7 +633,6 @@ typedef struct JSRegExp {
typedef struct JSProxyData {
JSValue target;
JSValue handler;
- JSValue proto;
uint8_t is_func;
uint8_t is_revoked;
} JSProxyData;
@@ -770,6 +777,7 @@ struct JSModuleDef {
JSValue func_obj; /* only used for JS modules */
JSModuleInitFunc *init_func; /* only used for C modules */
BOOL resolved : 8;
+ BOOL func_created : 8;
BOOL instantiated : 8;
BOOL evaluated : 8;
BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */
@@ -799,10 +807,8 @@ typedef struct JSProperty {
struct { /* JS_PROP_AUTOINIT */
/* in order to use only 2 pointers, we compress the realm
and the init function pointer */
- union {
- JSContext *realm; /* for JS_AUTOINIT_ID_PROP */
- uintptr_t init_id; /* JS_AUTOINIT_ID_x */
- } u;
+ uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
+ in the 2 low bits */
void *opaque;
} init;
} u;
@@ -832,11 +838,13 @@ struct JSShape {
uint32_t hash; /* current hash value */
uint32_t prop_hash_mask;
int prop_size; /* allocated properties */
- int prop_count;
+ int prop_count; /* include deleted properties */
+ int deleted_prop_count;
JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
JSObject *proto;
JSShapeProperty prop[0]; /* prop_size elements */
};
+
struct JSObject {
union {
JSGCObjectHeader header;
@@ -850,7 +858,6 @@ struct JSObject {
uint8_t fast_array : 1; /* TRUE if u.array is used for get/put */
uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */
- uint8_t is_class : 1; /* TRUE if object is a class constructor */
uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
uint16_t class_id; /* see JS_CLASS_x */
};
@@ -1124,7 +1131,7 @@ static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
#endif
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
-static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
+static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
JSValueConst proto_val, BOOL throw_flag);
static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);
@@ -1136,6 +1143,17 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
int flags);
static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);
static void reset_weak_ref(JSRuntime *rt, JSObject *p);
+static JSValue js_array_buffer_constructor3(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len, JSClassID class_id,
+ uint8_t *buf,
+ JSFreeArrayBufferDataFunc *free_func,
+ void *opaque, BOOL alloc_flag);
+static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
+static JSValue js_typed_array_constructor(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int classid);
static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
@@ -1212,6 +1230,7 @@ static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
void *opaque);
static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p,
JSAtom atom, void *opaque);
+void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);
static const JSClassExoticMethods js_arguments_exotic_methods;
static const JSClassExoticMethods js_string_exotic_methods;
@@ -1361,6 +1380,33 @@ char *js_strdup(JSContext *ctx, const char *str)
return js_strndup(ctx, str, strlen(str));
}
+static no_inline int js_realloc_array(JSContext *ctx, void **parray,
+ int elem_size, int *psize, int req_size)
+{
+ int new_size;
+ size_t slack;
+ void *new_array;
+ /* XXX: potential arithmetic overflow */
+ new_size = max_int(req_size, *psize * 3 / 2);
+ new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
+ if (!new_array)
+ return -1;
+ new_size += slack / elem_size;
+ *psize = new_size;
+ *parray = new_array;
+ return 0;
+}
+
+/* resize the array and update its size if req_size > *psize */
+static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
+ int *psize, int req_size)
+{
+ if (unlikely(req_size > *psize))
+ return js_realloc_array(ctx, parray, elem_size, psize, req_size);
+ else
+ return 0;
+}
+
static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
{
dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
@@ -1379,7 +1425,7 @@ typedef struct JSClassShortDef {
static JSClassShortDef const js_std_class_def[] = {
{ JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */
{ JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */
- { JS_ATOM_Error, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_ERROR */
+ { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
{ JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
{ JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
{ JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
@@ -1426,7 +1472,7 @@ static JSClassShortDef const js_std_class_def[] = {
{ JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
{ JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
{ JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
- { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
+ { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
{ JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
};
@@ -1505,8 +1551,8 @@ static void set_dummy_numeric_ops(JSNumericOperations *ops)
#endif /* CONFIG_BIGNUM */
-#if defined(EMSCRIPTEN)
-/* currently no stack limitation */
+#if !defined(CONFIG_STACK_CHECK)
+/* no stack limitation */
static inline uint8_t *js_get_stack_pointer(void)
{
return NULL;
@@ -1729,6 +1775,12 @@ void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
rt->can_block = can_block;
}
+void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
+ const JSSharedArrayBufferFunctions *sf)
+{
+ rt->sab_funcs = *sf;
+}
+
/* return 0 if OK, < 0 if exception */
int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
int argc, JSValueConst *argv)
@@ -4262,9 +4314,10 @@ static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
memset(sh->prop_hash_end - hash_size, 0, sizeof(sh->prop_hash_end[0]) *
hash_size);
sh->prop_hash_mask = hash_size - 1;
- sh->prop_count = 0;
sh->prop_size = prop_size;
-
+ sh->prop_count = 0;
+ sh->deleted_prop_count = 0;
+
/* insert in the hash table */
sh->hash = shape_initial_hash(proto);
sh->is_hashed = TRUE;
@@ -4415,6 +4468,74 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
return 0;
}
+/* remove the deleted properties. */
+static int compact_properties(JSContext *ctx, JSObject *p)
+{
+ JSShape *sh, *old_sh;
+ void *sh_alloc;
+ intptr_t h;
+ uint32_t new_hash_size, i, j, new_hash_mask, new_size;
+ JSShapeProperty *old_pr, *pr;
+ JSProperty *prop, *new_prop;
+
+ sh = p->shape;
+ assert(!sh->is_hashed);
+
+ new_size = max_int(JS_PROP_INITIAL_SIZE,
+ sh->prop_count - sh->deleted_prop_count);
+ assert(new_size <= sh->prop_size);
+
+ new_hash_size = sh->prop_hash_mask + 1;
+ while ((new_hash_size / 2) >= new_size)
+ new_hash_size = new_hash_size / 2;
+ new_hash_mask = new_hash_size - 1;
+
+ /* resize the hash table and the properties */
+ old_sh = sh;
+ sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
+ if (!sh_alloc)
+ return -1;
+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);
+ list_del(&old_sh->header.link);
+ memcpy(sh, old_sh, sizeof(JSShape));
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+
+ memset(sh->prop_hash_end - new_hash_size, 0,
+ sizeof(sh->prop_hash_end[0]) * new_hash_size);
+
+ j = 0;
+ old_pr = old_sh->prop;
+ pr = sh->prop;
+ prop = p->prop;
+ for(i = 0; i < sh->prop_count; i++) {
+ if (old_pr->atom != JS_ATOM_NULL) {
+ pr->atom = old_pr->atom;
+ pr->flags = old_pr->flags;
+ h = ((uintptr_t)old_pr->atom & new_hash_mask);
+ pr->hash_next = sh->prop_hash_end[-h - 1];
+ sh->prop_hash_end[-h - 1] = j + 1;
+ prop[j] = prop[i];
+ j++;
+ pr++;
+ }
+ old_pr++;
+ }
+ assert(j == (sh->prop_count - sh->deleted_prop_count));
+ sh->prop_hash_mask = new_hash_mask;
+ sh->prop_size = new_size;
+ sh->deleted_prop_count = 0;
+ sh->prop_count = j;
+
+ p->shape = sh;
+ js_free(ctx, get_alloc_from_shape(old_sh));
+
+ /* reduce the size of the object properties */
+ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
+ if (new_prop)
+ p->prop = new_prop;
+ return 0;
+}
+
static int add_shape_property(JSContext *ctx, JSShape **psh,
JSObject *p, JSAtom atom, int prop_flags)
{
@@ -4571,7 +4692,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
p->fast_array = 0;
p->is_constructor = 0;
p->is_uncatchable_error = 0;
- p->is_class = 0;
p->tmp_mark = 0;
p->first_weak_ref = NULL;
p->u.opaque = NULL;
@@ -4621,7 +4741,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
p->u.array.u.ptr = NULL;
p->u.array.count = 0;
break;
- case JS_CLASS_ERROR:
case JS_CLASS_NUMBER:
case JS_CLASS_STRING:
case JS_CLASS_BOOLEAN:
@@ -4967,19 +5086,25 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
return func_obj;
}
+static JSContext *js_autoinit_get_realm(JSProperty *pr)
+{
+ return (JSContext *)(pr->u.init.realm_and_id & ~3);
+}
+
+static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
+{
+ return pr->u.init.realm_and_id & 3;
+}
+
static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
{
- if (pr->u.init.u.init_id >= JS_AUTOINIT_ID_PROP) {
- JS_FreeContext(pr->u.init.u.realm);
- }
+ JS_FreeContext(js_autoinit_get_realm(pr));
}
static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
JS_MarkFunc *mark_func)
{
- if (pr->u.init.u.init_id >= JS_AUTOINIT_ID_PROP) {
- mark_func(rt, &pr->u.init.u.realm->header);
- }
+ mark_func(rt, &js_autoinit_get_realm(pr)->header);
}
static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
@@ -6331,7 +6456,7 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
backtrace_barrier = FALSE;
if (js_class_has_bytecode(p->class_id)) {
JSFunctionBytecode *b;
- char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ const char *atom_str;
int line_num1;
b = p->u.func.function_bytecode;
@@ -6339,9 +6464,10 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
if (b->has_debug) {
line_num1 = find_line_num(ctx, b,
sf->cur_pc - b->byte_code_buf - 1);
+ atom_str = JS_AtomToCString(ctx, b->debug.filename);
dbuf_printf(&dbuf, " (%s",
- JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
- b->debug.filename));
+ atom_str ? atom_str : "<null>");
+ JS_FreeCString(ctx, atom_str);
if (line_num1 != -1)
dbuf_printf(&dbuf, ":%d", line_num1);
dbuf_putc(&dbuf, ')');
@@ -6459,13 +6585,32 @@ static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSCont
}
}
+/* never use it directly */
+static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowTypeError(ctx, fmt,
+ JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
+}
+
+/* never use it directly */
+static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowSyntaxError(ctx, fmt,
+ JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
+}
+
+/* %s is replaced by 'atom'. The macro is used so that gcc can check
+ the format string. */
+#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")
+#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")
+
static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
{
if ((flags & JS_PROP_THROW) ||
((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
- char buf[ATOM_GET_STR_BUF_SIZE];
- JS_ThrowTypeError(ctx, "%s is read-only",
- JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
+ JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
return -1;
} else {
return FALSE;
@@ -6534,7 +6679,7 @@ static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
{
char buf[ATOM_GET_STR_BUF_SIZE];
- return JS_ThrowReferenceError(ctx, "%s is not defined",
+ return JS_ThrowReferenceError(ctx, "'%s' is not defined",
JS_AtomGetStr(ctx, buf, sizeof(buf), name));
}
@@ -6549,11 +6694,33 @@ static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
{
JSRuntime *rt = ctx->rt;
- char buf[ATOM_GET_STR_BUF_SIZE];
JSAtom name;
name = rt->class_array[class_id].class_name;
- return JS_ThrowTypeError(ctx, "%s object expected",
- JS_AtomGetStr(ctx, buf, sizeof(buf), name));
+ return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
+}
+
+static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
+ if (rt->interrupt_handler) {
+ if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
+ /* XXX: should set a specific flag to avoid catching */
+ JS_ThrowInternalError(ctx, "interrupted");
+ JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static inline __exception int js_poll_interrupts(JSContext *ctx)
+{
+ if (unlikely(--ctx->interrupt_counter <= 0)) {
+ return __js_poll_interrupts(ctx);
+ } else {
+ return 0;
+ }
}
/* return -1 (exception) or TRUE/FALSE */
@@ -6633,11 +6800,9 @@ int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);
}
-/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */
-JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val)
+/* Only works for primitive types, otherwise return JS_NULL. */
+static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
{
- JSObject *p;
-
switch(JS_VALUE_GET_NORM_TAG(val)) {
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_INT:
@@ -6664,26 +6829,45 @@ JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val)
val = ctx->class_proto[JS_CLASS_SYMBOL];
break;
case JS_TAG_OBJECT:
- p = JS_VALUE_GET_OBJ(val);
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ default:
+ val = JS_NULL;
+ break;
+ }
+ return val;
+}
+
+/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */
+JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
+{
+ JSValue val;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p;
+ p = JS_VALUE_GET_OBJ(obj);
if (unlikely(p->class_id == JS_CLASS_PROXY)) {
- val = js_proxy_getPrototypeOf(ctx, val);
+ val = js_proxy_getPrototypeOf(ctx, obj);
} else {
p = p->shape->proto;
if (!p)
val = JS_NULL;
else
- val = JS_MKPTR(JS_TAG_OBJECT, p);
+ val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
}
- break;
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- default:
- val = JS_NULL;
- break;
+ } else {
+ val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
}
return val;
}
+static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)
+{
+ JSValue obj1;
+ obj1 = JS_GetPrototype(ctx, obj);
+ JS_FreeValue(ctx, obj);
+ return obj1;
+}
+
/* return TRUE, FALSE or (-1) in case of exception */
static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
JSValueConst obj)
@@ -6704,7 +6888,6 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
/* Only explicitly boxed values are instances of constructors */
if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
return FALSE;
- ret = FALSE;
obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);
if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {
if (!JS_IsException(obj_proto))
@@ -6717,19 +6900,36 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
for(;;) {
proto1 = p->shape->proto;
if (!proto1) {
- if (p->class_id == JS_CLASS_PROXY) {
- JSValueConst proto_val;
- proto_val = JS_GetPrototype(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
- if (JS_IsException(proto_val)) {
- ret = -1;
- goto done;
+ /* slow case if proxy in the prototype chain */
+ if (unlikely(p->class_id == JS_CLASS_PROXY)) {
+ JSValue obj1;
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
+ for(;;) {
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsException(obj1)) {
+ ret = -1;
+ break;
+ }
+ if (JS_IsNull(obj1)) {
+ ret = FALSE;
+ break;
+ }
+ if (proto == JS_VALUE_GET_OBJ(obj1)) {
+ JS_FreeValue(ctx, obj1);
+ ret = TRUE;
+ break;
+ }
+ /* must check for timeout to avoid infinite loop */
+ if (js_poll_interrupts(ctx)) {
+ JS_FreeValue(ctx, obj1);
+ ret = -1;
+ break;
+ }
}
- proto1 = JS_VALUE_GET_OBJ(proto_val);
- if (!proto1)
- break;
} else {
- break;
+ ret = FALSE;
}
+ break;
}
p = proto1;
if (proto == p) {
@@ -6767,28 +6967,24 @@ int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)
return JS_OrdinaryIsInstanceOf(ctx, val, obj);
}
-static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSProperty *pr)
+typedef int JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
+
+static JSAutoInitFunc *js_autoinit_func_table[] = {
+ js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */
+ js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */
+ JS_InstantiateFunctionListItem, /* JS_AUTOINIT_ID_PROP */
+};
+
+static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
+ JSProperty *pr)
{
int ret;
JSContext *realm;
-
- if (pr->u.init.u.init_id >= JS_AUTOINIT_ID_PROP) {
- realm = pr->u.init.u.realm;
- ret = JS_InstantiateFunctionListItem(realm, p, prop, pr->u.init.opaque);
- if (ret)
- return ret;
- } else {
- switch(pr->u.init.u.init_id) {
- case JS_AUTOINIT_ID_PROTOTYPE:
- ret = js_instantiate_prototype(ctx, p, prop, pr->u.init.opaque);
- break;
- case JS_AUTOINIT_ID_MODULE_NS:
- ret = js_module_ns_autoinit(ctx, p, prop, pr->u.init.opaque);
- break;
- default:
- abort();
- }
- }
+ JSAutoInitFunc *func;
+
+ realm = js_autoinit_get_realm(pr);
+ func = js_autoinit_func_table[js_autoinit_get_id(pr)];
+ ret = func(realm, p, prop, pr->u.init.opaque);
return ret;
}
@@ -6805,8 +7001,9 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
if (unlikely(tag != JS_TAG_OBJECT)) {
switch(tag) {
case JS_TAG_NULL:
+ return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);
case JS_TAG_UNDEFINED:
- return JS_ThrowTypeError(ctx, "value has no property");
+ return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);
case JS_TAG_EXCEPTION:
return JS_EXCEPTION;
case JS_TAG_STRING:
@@ -6831,7 +7028,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
break;
}
/* cannot raise an exception */
- p = JS_VALUE_GET_OBJ(JS_GetPrototype(ctx, obj));
+ p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
if (!p)
return JS_UNDEFINED;
} else {
@@ -6896,15 +7093,25 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
if (em) {
if (em->get_property) {
+ JSValue obj1, retval;
/* XXX: should pass throw_ref_error */
- return em->get_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),
- prop, this_obj);
+ /* Note: if 'p' is a prototype, it can be
+ freed in the called function */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ retval = em->get_property(ctx, obj1, prop, this_obj);
+ JS_FreeValue(ctx, obj1);
+ return retval;
}
if (em->get_own_property) {
JSPropertyDescriptor desc;
int ret;
+ JSValue obj1;
- ret = em->get_own_property(ctx, &desc, JS_MKPTR(JS_TAG_OBJECT, p), prop);
+ /* Note: if 'p' is a prototype, it can be
+ freed in the called function */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = em->get_own_property(ctx, &desc, obj1, prop);
+ JS_FreeValue(ctx, obj1);
if (ret < 0)
return JS_EXCEPTION;
if (ret) {
@@ -6932,9 +7139,8 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
{
- char buf[ATOM_GET_STR_BUF_SIZE];
- return JS_ThrowTypeError(ctx, "private class field %s does not exist",
- JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
+ return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
+ atom);
}
/* Private fields can be added even on non extensible objects or
@@ -6960,9 +7166,8 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
p = JS_VALUE_GET_OBJ(obj);
prs = find_own_property(&pr, p, prop);
if (prs) {
- char buf[ATOM_GET_STR_BUF_SIZE];
- JS_ThrowTypeError(ctx, "private class field %s already exists",
- JS_AtomGetStr(ctx, buf, sizeof(buf), prop));
+ JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
+ prop);
goto fail;
}
pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
@@ -7211,7 +7416,9 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
return -1;
}
- num_keys_count += p->u.array.count;
+ if (flags & JS_GPN_STRING_MASK) {
+ num_keys_count += p->u.array.count;
+ }
} else {
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
if (em && em->get_own_property_names) {
@@ -7293,15 +7500,17 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
if (p->is_exotic) {
if (p->fast_array) {
- for(i = 0; i < p->u.array.count; i++) {
- tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
- if (tab_atom[num_index].atom == JS_ATOM_NULL) {
- js_free_prop_enum(ctx, tab_exotic, exotic_count);
- js_free_prop_enum(ctx, tab_atom, num_index);
- return -1;
+ if (flags & JS_GPN_STRING_MASK) {
+ for(i = 0; i < p->u.array.count; i++) {
+ tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
+ if (tab_atom[num_index].atom == JS_ATOM_NULL) {
+ js_free_prop_enum(ctx, tab_exotic, exotic_count);
+ js_free_prop_enum(ctx, tab_atom, num_index);
+ return -1;
+ }
+ tab_atom[num_index].is_enumerable = TRUE;
+ num_index++;
}
- tab_atom[num_index].is_enumerable = TRUE;
- num_index++;
}
}
if (exotic_count > 0) {
@@ -7492,6 +7701,7 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
{
JSObject *p;
int ret;
+ JSValue obj1;
if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
return FALSE;
@@ -7499,10 +7709,18 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
for(;;) {
if (p->is_exotic) {
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->has_property)
- return em->has_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), prop);
+ if (em && em->has_property) {
+ /* has_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = em->has_property(ctx, obj1, prop);
+ JS_FreeValue(ctx, obj1);
+ return ret;
+ }
}
+ /* JS_GetOwnPropertyInternal can free the prototype */
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
if (ret != 0)
return ret;
if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
@@ -7799,6 +8017,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
} else {
sh->prop_hash_end[-h1 - 1] = pr->hash_next;
}
+ sh->deleted_prop_count++;
/* free the entry */
pr1 = &p->prop[h - 1];
free_property(ctx->rt, pr1, pr->flags);
@@ -7807,6 +8026,12 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
pr->flags = 0;
pr->atom = JS_ATOM_NULL;
pr1->u.value = JS_UNDEFINED;
+
+ /* compact the properties if too many deleted properties */
+ if (sh->deleted_prop_count >= 8 &&
+ sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
+ compact_properties(ctx, p);
+ }
return TRUE;
}
lpr = pr;
@@ -8016,8 +8241,12 @@ static int JS_SetPropertyGeneric(JSContext *ctx,
if (p->is_exotic) {
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
if (em && em->set_property) {
- ret = em->set_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), prop,
+ JSValue obj1;
+ /* set_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = em->set_property(ctx, obj1, prop,
val, this_obj, flags);
+ JS_FreeValue(ctx, obj1);
JS_FreeValue(ctx, val);
return ret;
}
@@ -8110,14 +8339,17 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
if (unlikely(tag != JS_TAG_OBJECT)) {
switch(tag) {
case JS_TAG_NULL:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
+ return -1;
case JS_TAG_UNDEFINED:
JS_FreeValue(ctx, val);
- JS_ThrowTypeError(ctx, "value has no property");
+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
return -1;
default:
/* even on a primitive type we can have setters on the prototype */
p = NULL;
- p1 = JS_VALUE_GET_OBJ(JS_GetPrototype(ctx, this_obj));
+ p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));
goto prototype_lookup;
}
}
@@ -8195,15 +8427,22 @@ retry:
} else {
const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;
if (em) {
+ JSValue obj1;
if (em->set_property) {
- ret = em->set_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p1), prop,
+ /* set_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ ret = em->set_property(ctx, obj1, prop,
val, this_obj, flags);
+ JS_FreeValue(ctx, obj1);
JS_FreeValue(ctx, val);
return ret;
}
if (em->get_own_property) {
+ /* get_own_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
ret = em->get_own_property(ctx, &desc,
- JS_MKPTR(JS_TAG_OBJECT, p1), prop);
+ obj1, prop);
+ JS_FreeValue(ctx, obj1);
if (ret < 0) {
JS_FreeValue(ctx, val);
return ret;
@@ -8973,11 +9212,10 @@ static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
if (unlikely(!pr))
return -1;
- if (id == JS_AUTOINIT_ID_PROP) {
- pr->u.init.u.realm = JS_DupContext(ctx);
- } else {
- pr->u.init.u.init_id = id;
- }
+ pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
+ assert((pr->u.init.realm_and_id & 3) == 0);
+ assert(id <= 3);
+ pr->u.init.realm_and_id |= id;
pr->u.init.opaque = opaque;
return TRUE;
}
@@ -9114,9 +9352,7 @@ static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,
static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
{
- char buf[ATOM_GET_STR_BUF_SIZE];
- return JS_ThrowSyntaxError(ctx, "redeclaration of %s",
- JS_AtomGetStr(ctx, buf, sizeof(buf), prop));
+ return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
}
/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
@@ -9125,7 +9361,6 @@ static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
{
JSObject *p;
JSShapeProperty *prs;
- char buf[ATOM_GET_STR_BUF_SIZE];
p = JS_VALUE_GET_OBJ(ctx->global_obj);
prs = find_own_property1(p, prop);
@@ -9143,8 +9378,8 @@ static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
(JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
define_error:
- JS_ThrowTypeError(ctx, "cannot define variable %s",
- JS_AtomGetStr(ctx, buf, sizeof(buf), prop));
+ JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
+ prop);
return -1;
}
}
@@ -11395,7 +11630,9 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
printf("[varref %p]", (void *)pr->u.var_ref);
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- printf("[autoinit %p %p]", (void *)pr->u.init.u.realm,
+ printf("[autoinit %p %d %p]",
+ (void *)js_autoinit_get_realm(pr),
+ js_autoinit_get_id(pr),
(void *)pr->u.init.opaque);
} else {
JS_DumpValueShort(rt, pr->u.value);
@@ -15726,30 +15963,6 @@ static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
}
}
-static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
-{
- JSRuntime *rt = ctx->rt;
- ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
- if (rt->interrupt_handler) {
- if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
- /* XXX: should set a specific flag to avoid catching */
- JS_ThrowInternalError(ctx, "interrupted");
- JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
- return -1;
- }
- }
- return 0;
-}
-
-static inline __exception int js_poll_interrupts(JSContext *ctx)
-{
- if (unlikely(--ctx->interrupt_counter <= 0)) {
- return __js_poll_interrupts(ctx);
- } else {
- return 0;
- }
-}
-
/* argument of OP_special_object */
typedef enum {
OP_SPECIAL_OBJECT_ARGUMENTS,
@@ -16440,7 +16653,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
CASE(OP_get_super):
{
JSValue proto;
- proto = JS_DupValue(ctx, JS_GetPrototype(ctx, sp[-1]));
+ proto = JS_GetPrototype(ctx, sp[-1]);
if (JS_IsException(proto))
goto exception;
JS_FreeValue(ctx, sp[-1]);
@@ -18543,6 +18756,9 @@ static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
{
JSValue func_obj;
+ if (js_check_stack_overflow(ctx->rt, 0))
+ return JS_ThrowStackOverflow(ctx);
+
/* the tag does not matter provided it is not an object */
func_obj = JS_MKPTR(JS_TAG_INT, s);
return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
@@ -19670,8 +19886,8 @@ typedef struct JSFunctionDef {
/* constant pool (strings, functions, numbers) */
JSValue *cpool;
- uint32_t cpool_count;
- uint32_t cpool_size;
+ int cpool_count;
+ int cpool_size;
/* list of variables in the closure */
int closure_var_count;
@@ -19741,6 +19957,7 @@ typedef struct JSParseState {
JSFunctionDef *cur_func;
BOOL is_module; /* parsing a module */
BOOL allow_html_comments;
+ BOOL ext_json; /* true if accepting JSON superset */
} JSParseState;
typedef struct JSOpCode {
@@ -20308,23 +20525,18 @@ static __exception int next_token(JSParseState *s)
c = *p;
switch(c) {
case 0:
- s->token.val = TOK_EOF;
- break;
- case '`':
- if (!s->cur_func) {
- /* JSON does not accept templates */
+ if (p >= s->buf_end) {
+ s->token.val = TOK_EOF;
+ } else {
goto def_token;
}
+ break;
+ case '`':
if (js_parse_template_part(s, p + 1))
goto fail;
p = s->buf_ptr;
break;
case '\'':
- if (!s->cur_func) {
- /* JSON does not accept single quoted strings */
- goto def_token;
- }
- /* fall through */
case '\"':
if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
goto fail;
@@ -20342,11 +20554,6 @@ static __exception int next_token(JSParseState *s)
goto redo;
case '\f':
case '\v':
- if (!s->cur_func) {
- /* JSONWhitespace does not match <VT>, nor <FF> */
- goto def_token;
- }
- /* fall through */
case ' ':
case '\t':
p++;
@@ -20443,16 +20650,15 @@ static __exception int next_token(JSParseState *s)
s->token.u.ident.is_reserved = FALSE;
if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
(s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
- s->cur_func && (s->cur_func->js_mode & JS_MODE_STRICT)) ||
- (s->token.u.ident.atom == JS_ATOM_yield && s->cur_func &&
+ (s->cur_func->js_mode & JS_MODE_STRICT)) ||
+ (s->token.u.ident.atom == JS_ATOM_yield &&
((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
(s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
!s->cur_func->in_function_body && s->cur_func->parent &&
(s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
(s->token.u.ident.atom == JS_ATOM_await &&
(s->is_module ||
- (s->cur_func &&
- ((s->cur_func->func_kind & JS_FUNC_ASYNC) ||
+ (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||
(s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
!s->cur_func->in_function_body && s->cur_func->parent &&
(s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {
@@ -20505,9 +20711,8 @@ static __exception int next_token(JSParseState *s)
}
break;
case '0':
- /* in strict or JSON parsing mode, octal literals are not accepted */
- if (is_digit(p[1]) && (!s->cur_func ||
- (s->cur_func->js_mode & JS_MODE_STRICT))) {
+ /* in strict mode, octal literals are not accepted */
+ if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
js_parse_error(s, "octal literals are deprecated in strict mode");
goto fail;
}
@@ -20519,22 +20724,17 @@ static __exception int next_token(JSParseState *s)
JSValue ret;
const uint8_t *p1;
int flags, radix;
- if (!s->cur_func) {
- flags = 0;
- radix = 10;
- } else {
- flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
- ATOD_ACCEPT_UNDERSCORES;
+ flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
+ ATOD_ACCEPT_UNDERSCORES;
#ifdef CONFIG_BIGNUM
- flags |= ATOD_ACCEPT_SUFFIX;
- if (s->cur_func->js_mode & JS_MODE_MATH) {
- flags |= ATOD_MODE_BIGINT;
- if (s->cur_func->js_mode & JS_MODE_MATH)
- flags |= ATOD_TYPE_BIG_FLOAT;
- }
-#endif
- radix = 0;
+ flags |= ATOD_ACCEPT_SUFFIX;
+ if (s->cur_func->js_mode & JS_MODE_MATH) {
+ flags |= ATOD_MODE_BIGINT;
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ flags |= ATOD_TYPE_BIG_FLOAT;
}
+#endif
+ radix = 0;
#ifdef CONFIG_BIGNUM
s->token.u.num.exponent = 0;
ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
@@ -20699,7 +20899,7 @@ static __exception int next_token(JSParseState *s)
case '^':
if (p[1] == '=') {
p += 2;
- if (s->cur_func && (s->cur_func->js_mode & JS_MODE_MATH))
+ if (s->cur_func->js_mode & JS_MODE_MATH)
s->token.val = TOK_MATH_POW_ASSIGN;
else
s->token.val = TOK_XOR_ASSIGN;
@@ -20713,7 +20913,7 @@ static __exception int next_token(JSParseState *s)
}
} else {
p++;
- if (s->cur_func && (s->cur_func->js_mode & JS_MODE_MATH))
+ if (s->cur_func->js_mode & JS_MODE_MATH)
s->token.val = TOK_MATH_POW;
else
s->token.val = '^';
@@ -20758,22 +20958,12 @@ static __exception int next_token(JSParseState *s)
switch(c) {
case CP_PS:
case CP_LS:
- if (!s->cur_func) {
- /* <PS> and <LS> are not JSONWhitespace */
- goto def_token;
- } else {
- /* XXX: should avoid incrementing line_number, but
- needed to handle HTML comments */
- goto line_terminator;
- }
+ /* XXX: should avoid incrementing line_number, but
+ needed to handle HTML comments */
+ goto line_terminator;
default:
if (lre_is_space(c)) {
- if (!s->cur_func) {
- /* category z spaces are not JSONWhitespace */
- goto def_token;
- } else {
- goto redo;
- }
+ goto redo;
} else if (lre_js_is_ident_first(c)) {
ident_has_escape = FALSE;
goto has_ident;
@@ -20798,6 +20988,220 @@ static __exception int next_token(JSParseState *s)
return -1;
}
+/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
+static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
+{
+ const uint8_t *p;
+ char ident_buf[128], *buf;
+ size_t ident_size, ident_pos;
+ JSAtom atom;
+
+ p = *pp;
+ buf = ident_buf;
+ ident_size = sizeof(ident_buf);
+ ident_pos = 0;
+ for(;;) {
+ buf[ident_pos++] = c;
+ c = *p;
+ if (c >= 128 ||
+ !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))
+ break;
+ p++;
+ if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
+ if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
+ atom = JS_ATOM_NULL;
+ goto done;
+ }
+ }
+ }
+ atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
+ done:
+ if (unlikely(buf != ident_buf))
+ js_free(s->ctx, buf);
+ *pp = p;
+ return atom;
+}
+
+static __exception int json_next_token(JSParseState *s)
+{
+ const uint8_t *p;
+ int c;
+ JSAtom atom;
+
+ if (js_check_stack_overflow(s->ctx->rt, 0)) {
+ return js_parse_error(s, "stack overflow");
+ }
+
+ free_token(s, &s->token);
+
+ p = s->last_ptr = s->buf_ptr;
+ s->last_line_num = s->token.line_num;
+ redo:
+ s->token.line_num = s->line_num;
+ s->token.ptr = p;
+ c = *p;
+ switch(c) {
+ case 0:
+ if (p >= s->buf_end) {
+ s->token.val = TOK_EOF;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '\'':
+ if (!s->ext_json) {
+ /* JSON does not accept single quoted strings */
+ goto def_token;
+ }
+ /* fall through */
+ case '\"':
+ if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
+ goto fail;
+ break;
+ case '\r': /* accept DOS and MAC newline sequences */
+ if (p[1] == '\n') {
+ p++;
+ }
+ /* fall thru */
+ case '\n':
+ p++;
+ s->line_num++;
+ goto redo;
+ case '\f':
+ case '\v':
+ if (!s->ext_json) {
+ /* JSONWhitespace does not match <VT>, nor <FF> */
+ goto def_token;
+ }
+ /* fall through */
+ case ' ':
+ case '\t':
+ p++;
+ goto redo;
+ case '/':
+ if (!s->ext_json) {
+ /* JSON does not accept comments */
+ goto def_token;
+ }
+ if (p[1] == '*') {
+ /* comment */
+ p += 2;
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end) {
+ js_parse_error(s, "unexpected end of comment");
+ goto fail;
+ }
+ if (p[0] == '*' && p[1] == '/') {
+ p += 2;
+ break;
+ }
+ if (*p == '\n') {
+ s->line_num++;
+ p++;
+ } else if (*p == '\r') {
+ p++;
+ } else if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else if (p[1] == '/') {
+ /* line comment */
+ p += 2;
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end)
+ break;
+ if (*p == '\r' || *p == '\n')
+ break;
+ if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ /* LS or PS are considered as line terminator */
+ if (c == CP_LS || c == CP_PS) {
+ break;
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else {
+ goto def_token;
+ }
+ break;
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '_':
+ case '$':
+ /* identifier : only pure ascii characters are accepted */
+ p++;
+ atom = json_parse_ident(s, &p, c);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ s->token.u.ident.atom = atom;
+ s->token.u.ident.has_escape = FALSE;
+ s->token.u.ident.is_reserved = FALSE;
+ s->token.val = TOK_IDENT;
+ break;
+ case '+':
+ if (!s->ext_json || !is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '0':
+ if (is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '-':
+ if (!is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '1' ... '9':
+ /* number */
+ parse_number:
+ {
+ JSValue ret;
+ int flags, radix;
+ if (!s->ext_json) {
+ flags = 0;
+ radix = 10;
+ } else {
+ flags = ATOD_ACCEPT_BIN_OCT;
+ radix = 0;
+ }
+ ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
+ flags);
+ if (JS_IsException(ret))
+ goto fail;
+ s->token.val = TOK_NUMBER;
+ s->token.u.num.val = ret;
+ }
+ break;
+ default:
+ if (c >= 128) {
+ js_parse_error(s, "unexpected character");
+ goto fail;
+ }
+ def_token:
+ s->token.val = c;
+ p++;
+ break;
+ }
+ s->buf_ptr = p;
+
+ // dump_token(s, &s->token);
+ return 0;
+
+ fail:
+ s->token.val = TOK_ERROR;
+ return -1;
+}
+
/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is
only set if TOK_IMPORT is returned */
/* XXX: handle all unicode cases */
@@ -20858,6 +21262,12 @@ static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
}
} else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {
return TOK_OF;
+ } else if (c == 'e' &&
+ p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&
+ p[3] == 'r' && p[4] == 't' &&
+ !lre_js_is_ident_next(p[5])) {
+ *pp = p + 5;
+ return TOK_EXPORT;
} else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&
p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&
p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {
@@ -20881,16 +21291,21 @@ static int peek_token(JSParseState *s, BOOL no_line_terminator)
(heuristic). 'input' must be a zero terminated.
Heuristic: skip comments and expect 'import' keyword not followed
- by '(' or '.'
+ by '(' or '.' or export keyword.
*/
BOOL JS_DetectModule(const char *input, size_t input_len)
{
const uint8_t *p = (const uint8_t *)input;
int tok;
- if (simple_next_token(&p, FALSE) != TOK_IMPORT)
+ switch(simple_next_token(&p, FALSE)) {
+ case TOK_IMPORT:
+ tok = simple_next_token(&p, FALSE);
+ return (tok != '.' && tok != '(');
+ case TOK_EXPORT:
+ return TRUE;
+ default:
return FALSE;
- tok = simple_next_token(&p, FALSE);
- return (tok != '.' && tok != '(');
+ }
}
static inline int get_prev_opcode(JSFunctionDef *fd) {
@@ -20974,20 +21389,10 @@ static int new_label_fd(JSFunctionDef *fd, int label)
LabelSlot *ls;
if (label < 0) {
- if (fd->label_count >= fd->label_size) {
- int new_size;
- size_t slack;
- LabelSlot *new_tab;
-
- /* XXX: potential arithmetic overflow */
- new_size = fd->label_size * 3 / 2 + 4;
- new_tab = js_realloc2(fd->ctx, fd->label_slots, new_size * sizeof(*new_tab), &slack);
- if (!new_tab)
- return -1;
- new_size += slack / sizeof(*new_tab);
- fd->label_slots = new_tab;
- fd->label_size = new_size;
- }
+ if (js_resize_array(fd->ctx, (void *)&fd->label_slots,
+ sizeof(fd->label_slots[0]),
+ &fd->label_size, fd->label_count + 1))
+ return -1;
label = fd->label_count++;
ls = &fd->label_slots[label];
ls->ref_count = 0;
@@ -21035,19 +21440,10 @@ static int emit_goto(JSParseState *s, int opcode, int label)
static int cpool_add(JSParseState *s, JSValue val)
{
JSFunctionDef *fd = s->cur_func;
- if (fd->cpool_count >= fd->cpool_size) {
- int new_size;
- size_t slack;
- JSValue *new_tab;
- /* XXX: potential arithmetic overflow */
- new_size = max_int(fd->cpool_count + 1, fd->cpool_size * 3 / 2);
- new_tab = js_realloc2(s->ctx, fd->cpool, new_size * sizeof(JSValue), &slack);
- if (!new_tab)
- return -1;
- new_size += slack / sizeof(*new_tab);
- fd->cpool = new_tab;
- fd->cpool_size = new_size;
- }
+
+ if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
+ &fd->cpool_size, fd->cpool_count + 1))
+ return -1;
fd->cpool[fd->cpool_count++] = val;
return fd->cpool_count - 1;
}
@@ -21246,18 +21642,9 @@ static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
JS_ThrowInternalError(ctx, "too many local variables");
return -1;
}
- if ((fd->var_count + 1) > fd->var_size) {
- int new_size;
- size_t slack;
- JSVarDef *new_buf;
- new_size = max_int(fd->var_count + 1, fd->var_size * 3 / 2);
- new_buf = js_realloc2(ctx, fd->vars, new_size * sizeof(*fd->vars), &slack);
- if (!new_buf)
- return -1;
- new_size += slack / sizeof(*new_buf);
- fd->vars = new_buf;
- fd->var_size = new_size;
- }
+ if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),
+ &fd->var_size, fd->var_count + 1))
+ return -1;
vd = &fd->vars[fd->var_count++];
memset(vd, 0, sizeof(*vd));
vd->var_name = JS_DupAtom(ctx, name);
@@ -21309,18 +21696,9 @@ static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
JS_ThrowInternalError(ctx, "too many arguments");
return -1;
}
- if ((fd->arg_count + 1) > fd->arg_size) {
- int new_size;
- size_t slack;
- JSVarDef *new_buf;
- new_size = max_int(fd->arg_count + 1, fd->arg_size * 3 / 2);
- new_buf = js_realloc2(ctx, fd->args, new_size * sizeof(*fd->args), &slack);
- if (!new_buf)
- return -1;
- new_size += slack / sizeof(*new_buf);
- fd->args = new_buf;
- fd->arg_size = new_size;
- }
+ if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),
+ &fd->arg_size, fd->arg_count + 1))
+ return -1;
vd = &fd->args[fd->arg_count++];
memset(vd, 0, sizeof(*vd));
vd->var_name = JS_DupAtom(ctx, name);
@@ -21335,19 +21713,10 @@ static JSHoistedDef *add_hoisted_def(JSContext *ctx,
{
JSHoistedDef *hf;
- if (s->hoisted_def_count >= s->hoisted_def_size) {
- int new_size;
- size_t slack;
- JSHoistedDef *new_tab;
- new_size = max_int(s->hoisted_def_count + 1,
- s->hoisted_def_size * 3 / 2);
- new_tab = js_realloc2(ctx, s->hoisted_def, new_size * sizeof(s->hoisted_def[0]), &slack);
- if (!new_tab)
- return NULL;
- new_size += slack / sizeof(*new_tab);
- s->hoisted_def = new_tab;
- s->hoisted_def_size = new_size;
- }
+ if (js_resize_array(ctx, (void **)&s->hoisted_def,
+ sizeof(s->hoisted_def[0]),
+ &s->hoisted_def_size, s->hoisted_def_count + 1))
+ return NULL;
hf = &s->hoisted_def[s->hoisted_def_count++];
hf->cpool_idx = cpool_idx;
hf->force_init = 0;
@@ -21639,7 +22008,7 @@ static __exception int js_parse_template(JSParseState *s, int call, int *argc)
goto done;
if (next_token(s))
return -1;
- if (js_parse_assign_expr(s, TRUE))
+ if (js_parse_expr(s))
return -1;
depth++;
if (s->token.val != '}') {
@@ -21647,7 +22016,8 @@ static __exception int js_parse_template(JSParseState *s, int call, int *argc)
}
/* XXX: should convert to string at this stage? */
free_token(s, &s->token);
- /* Resume TOK_TEMPLATE parsing (s->token.line_num and s->token.ptr are OK) */
+ /* Resume TOK_TEMPLATE parsing (s->token.line_num and
+ * s->token.ptr are OK) */
s->got_lf = FALSE;
s->last_line_num = s->token.line_num;
if (js_parse_template_part(s, s->buf_ptr))
@@ -21875,7 +22245,7 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_
size_t level = 0;
JSParsePos pos;
int last_tok, tok = TOK_EOF;
- int tok_len, bits = 0;
+ int c, tok_len, bits = 0;
/* protect from underflow */
state[level++] = 0;
@@ -21900,8 +22270,30 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_
goto done;
break;
case '}':
- if (state[--level] != '{')
+ c = state[--level];
+ if (c == '`') {
+ /* continue the parsing of the template */
+ free_token(s, &s->token);
+ /* Resume TOK_TEMPLATE parsing (s->token.line_num and
+ * s->token.ptr are OK) */
+ s->got_lf = FALSE;
+ s->last_line_num = s->token.line_num;
+ if (js_parse_template_part(s, s->buf_ptr))
+ goto done;
+ goto handle_template;
+ } else if (c != '{') {
goto done;
+ }
+ break;
+ case TOK_TEMPLATE:
+ handle_template:
+ if (s->token.u.str.sep != '`') {
+ /* '${' inside the template : closing '}' and continue
+ parsing the template */
+ if (level >= sizeof(state))
+ goto done;
+ state[level++] = '`';
+ }
break;
case TOK_EOF:
goto done;
@@ -24005,7 +24397,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen
int scope;
name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
- if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL) {
+ if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {
/* direct 'eval' */
opcode = OP_eval;
} else {
@@ -26318,26 +26710,6 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
js_free(ctx, m);
}
-static int js_resize_array(JSContext *ctx, void **parray, int elem_size,
- int *psize, int *pcount, int new_count)
-{
- if (unlikely(new_count > *psize)) {
- int new_size;
- size_t slack;
- void *new_array;
- /* XXX: potential arithmetic overflow */
- new_size = max_int(new_count, *psize * 3 / 2);
- new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
- if (!new_array)
- return -1;
- new_size += slack / elem_size;
- *psize = new_size;
- *parray = new_array;
- }
- *pcount = new_count;
- return 0;
-}
-
static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
JSAtom module_name)
{
@@ -26353,11 +26725,10 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
if (js_resize_array(ctx, (void **)&m->req_module_entries,
sizeof(JSReqModuleEntry),
- &m->req_module_entries_size, &m->req_module_entries_count,
+ &m->req_module_entries_size,
m->req_module_entries_count + 1))
return -1;
- i = m->req_module_entries_count - 1;
- rme = &m->req_module_entries[i];
+ rme = &m->req_module_entries[m->req_module_entries_count++];
rme->module_name = JS_DupAtom(ctx, module_name);
rme->module = NULL;
return i;
@@ -26389,18 +26760,17 @@ static JSExportEntry *add_export_entry2(JSContext *ctx,
js_parse_error(s, "duplicate exported name '%s'",
JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
} else {
- JS_ThrowSyntaxError(ctx, "duplicate exported name '%s'",
- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
+ JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);
}
return NULL;
}
if (js_resize_array(ctx, (void **)&m->export_entries,
sizeof(JSExportEntry),
- &m->export_entries_size, &m->export_entries_count,
+ &m->export_entries_size,
m->export_entries_count + 1))
return NULL;
- me = &m->export_entries[m->export_entries_count - 1];
+ me = &m->export_entries[m->export_entries_count++];
memset(me, 0, sizeof(*me));
me->local_name = JS_DupAtom(ctx, local_name);
me->export_name = JS_DupAtom(ctx, export_name);
@@ -26423,10 +26793,10 @@ static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
if (js_resize_array(ctx, (void **)&m->star_export_entries,
sizeof(JSStarExportEntry),
- &m->star_export_entries_size, &m->star_export_entries_count,
+ &m->star_export_entries_size,
m->star_export_entries_count + 1))
return -1;
- se = &m->star_export_entries[m->star_export_entries_count - 1];
+ se = &m->star_export_entries[m->star_export_entries_count++];
se->req_module_idx = req_module_idx;
return 0;
}
@@ -26652,10 +27022,9 @@ static int add_resolve_entry(JSContext *ctx, JSResolveState *s,
if (js_resize_array(ctx, (void **)&s->array,
sizeof(JSResolveEntry),
- &s->size, &s->count,
- s->count + 1))
+ &s->size, s->count + 1))
return -1;
- re = &s->array[s->count - 1];
+ re = &s->array[s->count++];
re->module = m;
re->name = JS_DupAtom(ctx, name);
return 0;
@@ -26780,17 +27149,17 @@ static void js_resolve_export_throw_error(JSContext *ctx,
break;
default:
case JS_RESOLVE_RES_NOT_FOUND:
- JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
+ JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
break;
case JS_RESOLVE_RES_CIRCULAR:
- JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
+ JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
break;
case JS_RESOLVE_RES_AMBIGUOUS:
- JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
+ JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
break;
@@ -26847,9 +27216,9 @@ static __exception int get_exported_names(JSContext *ctx,
return 0;
}
if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),
- &s->modules_size, &s->modules_count, s->modules_count + 1))
+ &s->modules_size, s->modules_count + 1))
return -1;
- s->modules[s->modules_count - 1] = m;
+ s->modules[s->modules_count++] = m;
for(i = 0; i < m->export_entries_count; i++) {
JSExportEntry *me = &m->export_entries[i];
@@ -26858,9 +27227,10 @@ static __exception int get_exported_names(JSContext *ctx,
j = find_exported_name(s, me->export_name);
if (j < 0) {
if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),
- &s->exported_names_size, &s->exported_names_count, s->exported_names_count + 1))
+ &s->exported_names_size,
+ s->exported_names_count + 1))
return -1;
- en = &s->exported_names[s->exported_names_count - 1];
+ en = &s->exported_names[s->exported_names_count++];
en->export_name = me->export_name;
/* avoid a second lookup for simple module exports */
if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)
@@ -27096,7 +27466,7 @@ static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)
}
/* Create the <eval> function associated with the module */
-static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
+static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
{
JSFunctionBytecode *b;
int i;
@@ -27147,20 +27517,15 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
return -1;
}
-/* Prepare a module to be executed by resolving all the imported
- variables. */
-static int js_instantiate_module(JSContext *ctx, JSModuleDef *m)
+/* must be done before js_instantiate_module() because of cyclic references */
+static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
{
- int i;
- JSImportEntry *mi;
- JSModuleDef *m1;
- JSVarRef **var_refs, *var_ref;
- JSObject *p;
BOOL is_c_module;
-
- if (m->instantiated)
+ int i;
+ JSVarRef *var_ref;
+
+ if (m->func_created)
return 0;
- m->instantiated = TRUE;
is_c_module = (m->init_func != NULL);
@@ -27171,14 +27536,49 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m)
if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
var_ref = js_create_module_var(ctx, FALSE);
if (!var_ref)
- goto fail;
+ return -1;
me->u.local.var_ref = var_ref;
}
}
} else {
- if (js_create_module_function(ctx, m) < 0)
- goto fail;
+ if (js_create_module_bytecode_function(ctx, m))
+ return -1;
+ }
+ m->func_created = TRUE;
+
+ /* do it on the dependencies */
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ if (js_create_module_function(ctx, rme->module) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Prepare a module to be executed by resolving all the imported
+ variables. */
+static int js_instantiate_module(JSContext *ctx, JSModuleDef *m)
+{
+ int i;
+ JSImportEntry *mi;
+ JSModuleDef *m1;
+ JSVarRef **var_refs, *var_ref;
+ JSObject *p;
+ BOOL is_c_module;
+
+ if (m->instantiated)
+ return 0;
+ m->instantiated = TRUE;
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
}
+#endif
for(i = 0; i < m->req_module_entries_count; i++) {
JSReqModuleEntry *rme = &m->req_module_entries[i];
@@ -27221,6 +27621,8 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m)
}
#endif
+ is_c_module = (m->init_func != NULL);
+
if (!is_c_module) {
p = JS_VALUE_GET_OBJ(m->func_obj);
var_refs = p->u.func.var_refs;
@@ -27740,10 +28142,10 @@ static int add_import(JSParseState *s, JSModuleDef *m,
return -1;
if (js_resize_array(ctx, (void **)&m->import_entries,
sizeof(JSImportEntry),
- &m->import_entries_size, &m->import_entries_count,
+ &m->import_entries_size,
m->import_entries_count + 1))
return -1;
- mi = &m->import_entries[m->import_entries_count - 1];
+ mi = &m->import_entries[m->import_entries_count++];
mi->import_name = JS_DupAtom(ctx, import_name);
mi->var_idx = var_idx;
return 0;
@@ -28474,20 +28876,10 @@ static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
return -1;
}
- if (s->closure_var_count >= s->closure_var_size) {
- JSClosureVar *new_tab;
- int new_size;
- size_t slack;
- new_size = max_int(s->closure_var_count + 1,
- s->closure_var_size * 3 / 2);
- new_tab = js_realloc2(ctx, s->closure_var,
- new_size * sizeof(JSClosureVar), &slack);
- if (!new_tab)
- return -1;
- new_size += slack / sizeof(*new_tab);
- s->closure_var = new_tab;
- s->closure_var_size = new_size;
- }
+ if (js_resize_array(ctx, (void **)&s->closure_var,
+ sizeof(s->closure_var[0]),
+ &s->closure_var_size, s->closure_var_count + 1))
+ return -1;
cv = &s->closure_var[s->closure_var_count++];
cv->is_local = is_local;
cv->is_arg = is_arg;
@@ -29243,8 +29635,6 @@ static int resolve_scope_private_field1(JSContext *ctx,
}
scope_level = fd->parent_scope_level;
if (!fd->parent) {
- char buf[ATOM_GET_STR_BUF_SIZE];
-
if (fd->is_eval) {
/* closure of the eval function (top level) */
for (idx = 0; idx < fd->closure_var_count; idx++) {
@@ -29267,8 +29657,8 @@ static int resolve_scope_private_field1(JSContext *ctx,
}
}
/* XXX: no line number info */
- JS_ThrowSyntaxError(ctx, "undefined private field %s",
- JS_AtomGetStr(ctx, buf, sizeof(buf), var_name));
+ JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",
+ var_name);
return -1;
} else {
fd = fd->parent;
@@ -31535,9 +31925,8 @@ static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)
if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
idx = find_closure_var(ctx, fd, me->local_name);
if (idx < 0) {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- JS_ThrowSyntaxError(ctx, "exported variable '%s' does not exist",
- JS_AtomGetStr(ctx, buf1, sizeof(buf1), me->local_name));
+ JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",
+ me->local_name);
return -1;
}
me->u.local.var_idx = idx;
@@ -32559,6 +32948,8 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
m = JS_VALUE_GET_PTR(fun_obj);
/* the module refcount should be >= 2 */
JS_FreeValue(ctx, fun_obj);
+ if (js_create_module_function(ctx, m) < 0)
+ goto fail;
if (js_instantiate_module(ctx, m) < 0)
goto fail;
ret_val = js_evaluate_module(ctx, m);
@@ -32770,6 +33161,110 @@ int JS_ResolveModule(JSContext *ctx, JSValueConst obj)
}
/*******************************************************************/
+/* object list */
+
+typedef struct {
+ JSObject *obj;
+ uint32_t hash_next; /* -1 if no next entry */
+} JSObjectListEntry;
+
+/* XXX: reuse it to optimize weak references */
+typedef struct {
+ JSObjectListEntry *object_tab;
+ int object_count;
+ int object_size;
+ uint32_t *hash_table;
+ uint32_t hash_size;
+} JSObjectList;
+
+static void js_object_list_init(JSObjectList *s)
+{
+ memset(s, 0, sizeof(*s));
+}
+
+static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
+{
+ return ((uintptr_t)p * 3163) & (hash_size - 1);
+}
+
+static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
+ uint32_t new_hash_size)
+{
+ JSObjectListEntry *e;
+ uint32_t i, h, *new_hash_table;
+
+ new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
+ if (!new_hash_table)
+ return -1;
+ js_free(ctx, s->hash_table);
+ s->hash_table = new_hash_table;
+ s->hash_size = new_hash_size;
+
+ for(i = 0; i < s->hash_size; i++) {
+ s->hash_table[i] = -1;
+ }
+ for(i = 0; i < s->object_count; i++) {
+ e = &s->object_tab[i];
+ h = js_object_list_get_hash(e->obj, s->hash_size);
+ e->hash_next = s->hash_table[h];
+ s->hash_table[h] = i;
+ }
+ return 0;
+}
+
+/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
+ memory error */
+static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
+{
+ JSObjectListEntry *e;
+ uint32_t h, new_hash_size;
+
+ if (js_resize_array(ctx, (void *)&s->object_tab,
+ sizeof(s->object_tab[0]),
+ &s->object_size, s->object_count + 1))
+ return -1;
+ if (unlikely((s->object_count + 1) >= s->hash_size)) {
+ new_hash_size = max_uint32(s->hash_size, 4);
+ while (new_hash_size <= s->object_count)
+ new_hash_size *= 2;
+ if (js_object_list_resize_hash(ctx, s, new_hash_size))
+ return -1;
+ }
+ e = &s->object_tab[s->object_count++];
+ h = js_object_list_get_hash(obj, s->hash_size);
+ e->obj = obj;
+ e->hash_next = s->hash_table[h];
+ s->hash_table[h] = s->object_count - 1;
+ return 0;
+}
+
+/* return -1 if not present or the object index */
+static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
+{
+ JSObjectListEntry *e;
+ uint32_t h, p;
+
+ /* must test empty size because there is no hash table */
+ if (s->object_count == 0)
+ return -1;
+ h = js_object_list_get_hash(obj, s->hash_size);
+ p = s->hash_table[h];
+ while (p != -1) {
+ e = &s->object_tab[p];
+ if (e->obj == obj)
+ return p;
+ p = e->hash_next;
+ }
+ return -1;
+}
+
+static void js_object_list_end(JSContext *ctx, JSObjectList *s)
+{
+ js_free(ctx, s->object_tab);
+ js_free(ctx, s->hash_table);
+}
+
+/*******************************************************************/
/* binary object writer & reader */
typedef enum BCTagEnum {
@@ -32788,6 +33283,12 @@ typedef enum BCTagEnum {
BC_TAG_TEMPLATE_OBJECT,
BC_TAG_FUNCTION_BYTECODE,
BC_TAG_MODULE,
+ BC_TAG_TYPED_ARRAY,
+ BC_TAG_ARRAY_BUFFER,
+ BC_TAG_SHARED_ARRAY_BUFFER,
+ BC_TAG_DATE,
+ BC_TAG_OBJECT_VALUE,
+ BC_TAG_OBJECT_REFERENCE,
} BCTagEnum;
#ifdef CONFIG_BIGNUM
@@ -32805,14 +33306,21 @@ typedef enum BCTagEnum {
typedef struct BCWriterState {
JSContext *ctx;
DynBuf dbuf;
- BOOL byte_swap;
- BOOL allow_bytecode;
+ BOOL byte_swap : 8;
+ BOOL allow_bytecode : 8;
+ BOOL allow_sab : 8;
+ BOOL allow_reference : 8;
uint32_t first_atom;
uint32_t *atom_to_idx;
int atom_to_idx_size;
JSAtom *idx_to_atom;
int idx_to_atom_count;
int idx_to_atom_size;
+ uint8_t **sab_tab;
+ int sab_tab_len;
+ int sab_tab_size;
+ /* list of referenced objects (used if allow_reference = TRUE) */
+ JSObjectList object_list;
} BCWriterState;
#ifdef DUMP_READ_OBJECT
@@ -32833,6 +33341,12 @@ static const char * const bc_tag_str[] = {
"template",
"function",
"module",
+ "TypedArray",
+ "ArrayBuffer",
+ "SharedArrayBuffer",
+ "Date",
+ "ObjectValue",
+ "ObjectReference",
};
#endif
@@ -32892,36 +33406,20 @@ static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
return 0;
}
if (atom >= s->atom_to_idx_size) {
- size_t new_size, i, slack;
- uint32_t *new_tab;
- /* XXX: potential arithmetic overflow */
- new_size = s->atom_to_idx_size * 3 / 2;
- if ((atom + 1) > new_size)
- new_size = atom + 1;
- new_tab = js_realloc2(s->ctx, s->atom_to_idx,
- new_size * sizeof(s->atom_to_idx[0]), &slack);
- if (!new_tab)
- goto fail;
- new_size += slack / sizeof(*new_tab);
- for(i = s->atom_to_idx_size; i < new_size; i++)
- new_tab[i] = 0;
- s->atom_to_idx = new_tab;
- s->atom_to_idx_size = new_size;
- }
- if ((s->idx_to_atom_count + 1) > s->idx_to_atom_size) {
- size_t new_size, slack;
- JSAtom *new_tab;
- new_size = s->idx_to_atom_size * 3 / 2;
- if ((s->idx_to_atom_count + 1) > new_size)
- new_size = s->idx_to_atom_count + 1;
- new_tab = js_realloc2(s->ctx, s->idx_to_atom,
- new_size * sizeof(s->idx_to_atom[0]), &slack);
- if (!new_tab)
- goto fail;
- new_size += slack / sizeof(*new_tab);
- s->idx_to_atom = new_tab;
- s->idx_to_atom_size = new_size;
+ int old_size, i;
+ old_size = s->atom_to_idx_size;
+ if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
+ sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
+ atom + 1))
+ return -1;
+ /* XXX: could add a specific js_resize_array() function to do it */
+ for(i = old_size; i < s->atom_to_idx_size; i++)
+ s->atom_to_idx[i] = 0;
}
+ if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
+ sizeof(s->idx_to_atom[0]),
+ &s->idx_to_atom_size, s->idx_to_atom_count + 1))
+ goto fail;
v = s->idx_to_atom_count++;
s->idx_to_atom[v] = atom + s->first_atom;
@@ -33204,10 +33702,276 @@ static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
}
#endif /* CONFIG_BIGNUM */
+static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
+
+static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
+{
+ JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
+ uint32_t flags;
+ int idx, i;
+
+ bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, b->has_prototype, 1);
+ bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
+ bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
+ bc_set_flags(&flags, &idx, b->need_home_object, 1);
+ bc_set_flags(&flags, &idx, b->func_kind, 2);
+ bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
+ bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
+ bc_set_flags(&flags, &idx, b->super_allowed, 1);
+ bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
+ bc_set_flags(&flags, &idx, b->has_debug, 1);
+ bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
+ assert(idx <= 16);
+ bc_put_u16(s, flags);
+ bc_put_u8(s, b->js_mode);
+ bc_put_atom(s, b->func_name);
+
+ bc_put_leb128(s, b->arg_count);
+ bc_put_leb128(s, b->var_count);
+ bc_put_leb128(s, b->defined_arg_count);
+ bc_put_leb128(s, b->stack_size);
+ bc_put_leb128(s, b->closure_var_count);
+ bc_put_leb128(s, b->cpool_count);
+ bc_put_leb128(s, b->byte_code_len);
+ if (b->vardefs) {
+ bc_put_leb128(s, b->arg_count + b->var_count);
+ for(i = 0; i < b->arg_count + b->var_count; i++) {
+ JSVarDef *vd = &b->vardefs[i];
+ bc_put_atom(s, vd->var_name);
+ bc_put_leb128(s, vd->scope_level);
+ bc_put_leb128(s, vd->scope_next + 1);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, vd->var_kind, 4);
+ bc_set_flags(&flags, &idx, vd->is_func_var, 1);
+ bc_set_flags(&flags, &idx, vd->is_const, 1);
+ bc_set_flags(&flags, &idx, vd->is_lexical, 1);
+ bc_set_flags(&flags, &idx, vd->is_captured, 1);
+ assert(idx <= 8);
+ bc_put_u8(s, flags);
+ }
+ } else {
+ bc_put_leb128(s, 0);
+ }
+
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ bc_put_atom(s, cv->var_name);
+ bc_put_leb128(s, cv->var_idx);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, cv->is_local, 1);
+ bc_set_flags(&flags, &idx, cv->is_arg, 1);
+ bc_set_flags(&flags, &idx, cv->is_const, 1);
+ bc_set_flags(&flags, &idx, cv->is_lexical, 1);
+ bc_set_flags(&flags, &idx, cv->var_kind, 4);
+ assert(idx <= 8);
+ bc_put_u8(s, flags);
+ }
+
+ if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
+ goto fail;
+
+ if (b->has_debug) {
+ bc_put_atom(s, b->debug.filename);
+ bc_put_leb128(s, b->debug.line_num);
+ bc_put_leb128(s, b->debug.pc2line_len);
+ dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
+ }
+
+ for(i = 0; i < b->cpool_count; i++) {
+ if (JS_WriteObjectRec(s, b->cpool[i]))
+ goto fail;
+ }
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
+{
+ JSModuleDef *m = JS_VALUE_GET_PTR(obj);
+ int i;
+
+ bc_put_u8(s, BC_TAG_MODULE);
+ bc_put_atom(s, m->module_name);
+
+ bc_put_leb128(s, m->req_module_entries_count);
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ bc_put_atom(s, rme->module_name);
+ }
+
+ bc_put_leb128(s, m->export_entries_count);
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ bc_put_u8(s, me->export_type);
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ bc_put_leb128(s, me->u.local.var_idx);
+ } else {
+ bc_put_leb128(s, me->u.req_module_idx);
+ bc_put_atom(s, me->local_name);
+ }
+ bc_put_atom(s, me->export_name);
+ }
+
+ bc_put_leb128(s, m->star_export_entries_count);
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ bc_put_leb128(s, se->req_module_idx);
+ }
+
+ bc_put_leb128(s, m->import_entries_count);
+ for(i = 0; i < m->import_entries_count; i++) {
+ JSImportEntry *mi = &m->import_entries[i];
+ bc_put_leb128(s, mi->var_idx);
+ bc_put_atom(s, mi->import_name);
+ bc_put_leb128(s, mi->req_module_idx);
+ }
+
+ if (JS_WriteObjectRec(s, m->func_obj))
+ goto fail;
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ uint32_t i, len;
+ JSValue val;
+ int ret;
+ BOOL is_template;
+
+ if (s->allow_bytecode && !p->extensible) {
+ /* not extensible array: we consider it is a
+ template when we are saving bytecode */
+ bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
+ is_template = TRUE;
+ } else {
+ bc_put_u8(s, BC_TAG_ARRAY);
+ is_template = FALSE;
+ }
+ if (js_get_length32(s->ctx, &len, obj))
+ goto fail1;
+ bc_put_leb128(s, len);
+ for(i = 0; i < len; i++) {
+ val = JS_GetPropertyUint32(s->ctx, obj, i);
+ if (JS_IsException(val))
+ goto fail1;
+ ret = JS_WriteObjectRec(s, val);
+ JS_FreeValue(s->ctx, val);
+ if (ret)
+ goto fail1;
+ }
+ if (is_template) {
+ val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
+ if (JS_IsException(val))
+ goto fail1;
+ ret = JS_WriteObjectRec(s, val);
+ JS_FreeValue(s->ctx, val);
+ if (ret)
+ goto fail1;
+ }
+ return 0;
+ fail1:
+ return -1;
+}
+
+static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ uint32_t i, prop_count;
+ JSShape *sh;
+ JSShapeProperty *pr;
+ int pass;
+ JSAtom atom;
+
+ bc_put_u8(s, BC_TAG_OBJECT);
+ prop_count = 0;
+ sh = p->shape;
+ for(pass = 0; pass < 2; pass++) {
+ if (pass == 1)
+ bc_put_leb128(s, prop_count);
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
+ atom = pr->atom;
+ if (atom != JS_ATOM_NULL &&
+ JS_AtomIsString(s->ctx, atom) &&
+ (pr->flags & JS_PROP_ENUMERABLE)) {
+ if (pr->flags & JS_PROP_TMASK) {
+ JS_ThrowTypeError(s->ctx, "only value properties are supported");
+ goto fail;
+ }
+ if (pass == 0) {
+ prop_count++;
+ } else {
+ bc_put_atom(s, atom);
+ if (JS_WriteObjectRec(s, p->prop[i].u.value))
+ goto fail;
+ }
+ }
+ }
+ }
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSTypedArray *ta = p->u.typed_array;
+
+ bc_put_u8(s, BC_TAG_TYPED_ARRAY);
+ bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
+ bc_put_leb128(s, p->u.array.count);
+ bc_put_leb128(s, ta->offset);
+ if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
+ return -1;
+ return 0;
+}
+
+static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
+ return -1;
+ }
+ bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
+ bc_put_leb128(s, abuf->byte_length);
+ dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
+ return 0;
+}
+
+static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ assert(!abuf->detached); /* SharedArrayBuffer are never detached */
+ bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
+ bc_put_leb128(s, abuf->byte_length);
+ bc_put_u64(s, (uintptr_t)abuf->data);
+ if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
+ &s->sab_tab_size, s->sab_tab_len + 1))
+ return -1;
+ /* keep the SAB pointer so that the user can clone it or free it */
+ s->sab_tab[s->sab_tab_len++] = abuf->data;
+ return 0;
+}
+
static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
{
- uint32_t tag = JS_VALUE_GET_NORM_TAG(obj);
+ uint32_t tag;
+
+ if (js_check_stack_overflow(s->ctx->rt, 0)) {
+ JS_ThrowStackOverflow(s->ctx);
+ return -1;
+ }
+ tag = JS_VALUE_GET_NORM_TAG(obj);
switch(tag) {
case JS_TAG_NULL:
bc_put_u8(s, BC_TAG_NULL);
@@ -33238,218 +34002,82 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
}
break;
case JS_TAG_FUNCTION_BYTECODE:
- {
- JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
- uint32_t flags;
- int idx, i;
-
- if (!s->allow_bytecode)
- goto invalid_tag;
- bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
- flags = idx = 0;
- bc_set_flags(&flags, &idx, b->has_prototype, 1);
- bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
- bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
- bc_set_flags(&flags, &idx, b->need_home_object, 1);
- bc_set_flags(&flags, &idx, b->func_kind, 2);
- bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
- bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
- bc_set_flags(&flags, &idx, b->super_allowed, 1);
- bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
- bc_set_flags(&flags, &idx, b->has_debug, 1);
- bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
- assert(idx <= 16);
- bc_put_u16(s, flags);
- bc_put_u8(s, b->js_mode);
- bc_put_atom(s, b->func_name);
-
- bc_put_leb128(s, b->arg_count);
- bc_put_leb128(s, b->var_count);
- bc_put_leb128(s, b->defined_arg_count);
- bc_put_leb128(s, b->stack_size);
- bc_put_leb128(s, b->closure_var_count);
- bc_put_leb128(s, b->cpool_count);
- bc_put_leb128(s, b->byte_code_len);
- if (b->vardefs) {
- bc_put_leb128(s, b->arg_count + b->var_count);
- for(i = 0; i < b->arg_count + b->var_count; i++) {
- JSVarDef *vd = &b->vardefs[i];
- bc_put_atom(s, vd->var_name);
- bc_put_leb128(s, vd->scope_level);
- bc_put_leb128(s, vd->scope_next + 1);
- flags = idx = 0;
- bc_set_flags(&flags, &idx, vd->var_kind, 4);
- bc_set_flags(&flags, &idx, vd->is_func_var, 1);
- bc_set_flags(&flags, &idx, vd->is_const, 1);
- bc_set_flags(&flags, &idx, vd->is_lexical, 1);
- bc_set_flags(&flags, &idx, vd->is_captured, 1);
- assert(idx <= 8);
- bc_put_u8(s, flags);
- }
- } else {
- bc_put_leb128(s, 0);
- }
-
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- bc_put_atom(s, cv->var_name);
- bc_put_leb128(s, cv->var_idx);
- flags = idx = 0;
- bc_set_flags(&flags, &idx, cv->is_local, 1);
- bc_set_flags(&flags, &idx, cv->is_arg, 1);
- bc_set_flags(&flags, &idx, cv->is_const, 1);
- bc_set_flags(&flags, &idx, cv->is_lexical, 1);
- bc_set_flags(&flags, &idx, cv->var_kind, 4);
- assert(idx <= 8);
- bc_put_u8(s, flags);
- }
-
- if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
- goto fail;
-
- if (b->has_debug) {
- bc_put_atom(s, b->debug.filename);
- bc_put_leb128(s, b->debug.line_num);
- bc_put_leb128(s, b->debug.pc2line_len);
- dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
- }
-
- for(i = 0; i < b->cpool_count; i++) {
- if (JS_WriteObjectRec(s, b->cpool[i]))
- goto fail;
- }
- }
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ if (JS_WriteFunctionTag(s, obj))
+ goto fail;
break;
case JS_TAG_MODULE:
- {
- JSModuleDef *m = JS_VALUE_GET_PTR(obj);
- int i;
-
- if (!s->allow_bytecode)
- goto invalid_tag;
- bc_put_u8(s, BC_TAG_MODULE);
- bc_put_atom(s, m->module_name);
-
- bc_put_leb128(s, m->req_module_entries_count);
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- bc_put_atom(s, rme->module_name);
- }
-
- bc_put_leb128(s, m->export_entries_count);
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- bc_put_u8(s, me->export_type);
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- bc_put_leb128(s, me->u.local.var_idx);
- } else {
- bc_put_leb128(s, me->u.req_module_idx);
- bc_put_atom(s, me->local_name);
- }
- bc_put_atom(s, me->export_name);
- }
-
- bc_put_leb128(s, m->star_export_entries_count);
- for(i = 0; i < m->star_export_entries_count; i++) {
- JSStarExportEntry *se = &m->star_export_entries[i];
- bc_put_leb128(s, se->req_module_idx);
- }
-
- bc_put_leb128(s, m->import_entries_count);
- for(i = 0; i < m->import_entries_count; i++) {
- JSImportEntry *mi = &m->import_entries[i];
- bc_put_leb128(s, mi->var_idx);
- bc_put_atom(s, mi->import_name);
- bc_put_leb128(s, mi->req_module_idx);
- }
-
- if (JS_WriteObjectRec(s, m->func_obj))
- goto fail;
- }
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ if (JS_WriteModule(s, obj))
+ goto fail;
break;
case JS_TAG_OBJECT:
{
JSObject *p = JS_VALUE_GET_OBJ(obj);
- uint32_t i, prop_count, len;
- JSShape *sh;
- JSShapeProperty *pr;
- JSValue val;
- int ret, pass;
- BOOL is_template;
- JSAtom atom;
-
- if (p->class_id != JS_CLASS_ARRAY &&
- p->class_id != JS_CLASS_OBJECT) {
- JS_ThrowTypeError(s->ctx, "unsupported object class");
- goto fail;
- }
- if (p->tmp_mark) {
- JS_ThrowTypeError(s->ctx, "circular reference");
- goto fail;
- }
- p->tmp_mark = 1;
- if (p->class_id == JS_CLASS_ARRAY) {
- if (s->allow_bytecode && !p->extensible) {
- /* not extensible array: we consider it is a
- template when we are saving bytecode */
- bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
- is_template = TRUE;
+ int ret, idx;
+
+ if (s->allow_reference) {
+ idx = js_object_list_find(s->ctx, &s->object_list, p);
+ if (idx >= 0) {
+ bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
+ bc_put_leb128(s, idx);
+ break;
} else {
- bc_put_u8(s, BC_TAG_ARRAY);
- is_template = FALSE;
- }
- if (js_get_length32(s->ctx, &len, obj))
- goto fail1;
- bc_put_leb128(s, len);
- for(i = 0; i < len; i++) {
- val = JS_GetPropertyUint32(s->ctx, obj, i);
- if (JS_IsException(val))
- goto fail1;
- ret = JS_WriteObjectRec(s, val);
- JS_FreeValue(s->ctx, val);
- if (ret)
- goto fail1;
- }
- if (is_template) {
- val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
- if (JS_IsException(val))
- goto fail1;
- ret = JS_WriteObjectRec(s, val);
- JS_FreeValue(s->ctx, val);
- if (ret)
- goto fail1;
+ if (js_object_list_add(s->ctx, &s->object_list, p))
+ goto fail;
}
} else {
- bc_put_u8(s, BC_TAG_OBJECT);
- prop_count = 0;
- sh = p->shape;
- for(pass = 0; pass < 2; pass++) {
- if (pass == 1)
- bc_put_leb128(s, prop_count);
- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
- atom = pr->atom;
- if (atom != JS_ATOM_NULL &&
- JS_AtomIsString(s->ctx, atom) &&
- (pr->flags & JS_PROP_ENUMERABLE)) {
- if (pr->flags & JS_PROP_TMASK) {
- JS_ThrowTypeError(s->ctx, "only value properties are supported");
- goto fail;
- }
- if (pass == 0) {
- prop_count++;
- } else {
- bc_put_atom(s, atom);
- if (JS_WriteObjectRec(s, p->prop[i].u.value)) {
- fail1:
- p->tmp_mark = 0;
- goto fail;
- }
- }
- }
- }
+ if (p->tmp_mark) {
+ JS_ThrowTypeError(s->ctx, "circular reference");
+ goto fail;
+ }
+ p->tmp_mark = 1;
+ }
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY:
+ ret = JS_WriteArray(s, obj);
+ break;
+ case JS_CLASS_OBJECT:
+ ret = JS_WriteObjectTag(s, obj);
+ break;
+ case JS_CLASS_ARRAY_BUFFER:
+ ret = JS_WriteArrayBuffer(s, obj);
+ break;
+ case JS_CLASS_SHARED_ARRAY_BUFFER:
+ if (!s->allow_sab)
+ goto invalid_tag;
+ ret = JS_WriteSharedArrayBuffer(s, obj);
+ break;
+ case JS_CLASS_DATE:
+ bc_put_u8(s, BC_TAG_DATE);
+ ret = JS_WriteObjectRec(s, p->u.object_data);
+ break;
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ bc_put_u8(s, BC_TAG_OBJECT_VALUE);
+ ret = JS_WriteObjectRec(s, p->u.object_data);
+ break;
+ default:
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_WriteTypedArray(s, obj);
+ } else {
+ JS_ThrowTypeError(s->ctx, "unsupported object class");
+ ret = -1;
}
+ break;
}
p->tmp_mark = 0;
+ if (ret)
+ goto fail;
}
break;
#ifdef CONFIG_BIGNUM
@@ -33511,8 +34139,8 @@ static int JS_WriteObjectAtoms(BCWriterState *s)
return -1;
}
-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)
{
BCWriterState ss, *s = &ss;
@@ -33521,29 +34149,48 @@ uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
/* XXX: byte swapped output is untested */
s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);
s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
+ s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
+ s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
/* XXX: could use a different version when bytecode is included */
if (s->allow_bytecode)
s->first_atom = JS_ATOM_END;
else
s->first_atom = 1;
js_dbuf_init(ctx, &s->dbuf);
-
+ js_object_list_init(&s->object_list);
+
if (JS_WriteObjectRec(s, obj))
goto fail;
if (JS_WriteObjectAtoms(s))
goto fail;
+ js_object_list_end(ctx, &s->object_list);
js_free(ctx, s->atom_to_idx);
js_free(ctx, s->idx_to_atom);
*psize = s->dbuf.size;
+ if (psab_tab)
+ *psab_tab = s->sab_tab;
+ if (psab_tab_len)
+ *psab_tab_len = s->sab_tab_len;
return s->dbuf.buf;
fail:
+ js_object_list_end(ctx, &s->object_list);
js_free(ctx, s->atom_to_idx);
js_free(ctx, s->idx_to_atom);
dbuf_free(&s->dbuf);
*psize = 0;
+ if (psab_tab)
+ *psab_tab = NULL;
+ if (psab_tab_len)
+ *psab_tab_len = 0;
return NULL;
}
+uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags)
+{
+ return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
+}
+
typedef struct BCReaderState {
JSContext *ctx;
const uint8_t *buf_start, *ptr, *buf_end;
@@ -33551,8 +34198,15 @@ typedef struct BCReaderState {
uint32_t idx_to_atom_count;
JSAtom *idx_to_atom;
int error_state;
- BOOL allow_bytecode;
- BOOL is_rom_data;
+ BOOL allow_sab : 8;
+ BOOL allow_bytecode : 8;
+ BOOL is_rom_data : 8;
+ BOOL allow_reference : 8;
+ /* object references */
+ JSObject **objects;
+ int objects_count;
+ int objects_size;
+
#ifdef DUMP_READ_OBJECT
const uint8_t *ptr_last;
int level;
@@ -33756,7 +34410,7 @@ static JSString *JS_ReadString(BCReaderState *s)
p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
}
#ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "string: "); JS_DumpString(s->ctx->rt, p); printf("\n");
+ JS_DumpString(s->ctx->rt, p); printf("\n");
#endif
return p;
}
@@ -33838,8 +34492,6 @@ static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
bf_t *a;
int bpos, d;
- bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
-
p = js_new_bf(s->ctx);
if (!p)
goto fail;
@@ -33961,28 +34613,560 @@ static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
}
#endif /* CONFIG_BIGNUM */
+static JSValue JS_ReadObjectRec(BCReaderState *s);
+
+static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
+{
+ if (s->allow_reference) {
+ if (js_resize_array(s->ctx, (void *)&s->objects,
+ sizeof(s->objects[0]),
+ &s->objects_size, s->objects_count + 1))
+ return -1;
+ s->objects[s->objects_count++] = p;
+ }
+ return 0;
+}
+
+static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
+{
+ return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
+}
+
+static JSValue JS_ReadFunctionTag(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionBytecode bc, *b;
+ JSValue obj = JS_UNDEFINED;
+ uint16_t v16;
+ uint8_t v8;
+ int idx, i, local_count;
+ int function_size, cpool_offset, byte_code_offset;
+ int closure_var_offset, vardefs_offset;
+
+ memset(&bc, 0, sizeof(bc));
+ bc.header.ref_count = 1;
+ //bc.gc_header.mark = 0;
+
+ if (bc_get_u16(s, &v16))
+ goto fail;
+ idx = 0;
+ bc.has_prototype = bc_get_flags(v16, &idx, 1);
+ bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
+ bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
+ bc.need_home_object = bc_get_flags(v16, &idx, 1);
+ bc.func_kind = bc_get_flags(v16, &idx, 2);
+ bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
+ bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
+ bc.super_allowed = bc_get_flags(v16, &idx, 1);
+ bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
+ bc.has_debug = bc_get_flags(v16, &idx, 1);
+ bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
+ bc.read_only_bytecode = s->is_rom_data;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ bc.js_mode = v8;
+ if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.arg_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.var_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.defined_arg_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.stack_size))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.closure_var_count))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.cpool_count))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.byte_code_len))
+ goto fail;
+ if (bc_get_leb128_int(s, &local_count))
+ goto fail;
+
+ if (bc.has_debug) {
+ function_size = sizeof(*b);
+ } else {
+ function_size = offsetof(JSFunctionBytecode, debug);
+ }
+ cpool_offset = function_size;
+ function_size += bc.cpool_count * sizeof(*bc.cpool);
+ vardefs_offset = function_size;
+ function_size += local_count * sizeof(*bc.vardefs);
+ closure_var_offset = function_size;
+ function_size += bc.closure_var_count * sizeof(*bc.closure_var);
+ byte_code_offset = function_size;
+ if (!bc.read_only_bytecode) {
+ function_size += bc.byte_code_len;
+ }
+
+ b = js_mallocz(ctx, function_size);
+ if (!b)
+ return JS_EXCEPTION;
+
+ memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
+ b->header.ref_count = 1;
+ add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
+
+ obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
+
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
+#endif
+ bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
+ b->arg_count, b->var_count, b->defined_arg_count,
+ b->closure_var_count, b->cpool_count);
+ bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
+ b->stack_size, b->byte_code_len, local_count);
+
+ if (local_count != 0) {
+ bc_read_trace(s, "vars {\n");
+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
+ for(i = 0; i < local_count; i++) {
+ JSVarDef *vd = &b->vardefs[i];
+ if (bc_get_atom(s, &vd->var_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &vd->scope_level))
+ goto fail;
+ if (bc_get_leb128_int(s, &vd->scope_next))
+ goto fail;
+ vd->scope_next--;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ idx = 0;
+ vd->var_kind = bc_get_flags(v8, &idx, 4);
+ vd->is_func_var = bc_get_flags(v8, &idx, 1);
+ vd->is_const = bc_get_flags(v8, &idx, 1);
+ vd->is_lexical = bc_get_flags(v8, &idx, 1);
+ vd->is_captured = bc_get_flags(v8, &idx, 1);
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
+#endif
+ }
+ bc_read_trace(s, "}\n");
+ }
+ if (b->closure_var_count != 0) {
+ bc_read_trace(s, "closure vars {\n");
+ b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ int var_idx;
+ if (bc_get_atom(s, &cv->var_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &var_idx))
+ goto fail;
+ cv->var_idx = var_idx;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ idx = 0;
+ cv->is_local = bc_get_flags(v8, &idx, 1);
+ cv->is_arg = bc_get_flags(v8, &idx, 1);
+ cv->is_const = bc_get_flags(v8, &idx, 1);
+ cv->is_lexical = bc_get_flags(v8, &idx, 1);
+ cv->var_kind = bc_get_flags(v8, &idx, 4);
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
+#endif
+ }
+ bc_read_trace(s, "}\n");
+ }
+ {
+ bc_read_trace(s, "bytecode {\n");
+ if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
+ goto fail;
+ bc_read_trace(s, "}\n");
+ }
+ if (b->has_debug) {
+ /* read optional debug information */
+ bc_read_trace(s, "debug {\n");
+ if (bc_get_atom(s, &b->debug.filename))
+ goto fail;
+ if (bc_get_leb128_int(s, &b->debug.line_num))
+ goto fail;
+ if (bc_get_leb128_int(s, &b->debug.pc2line_len))
+ goto fail;
+ if (b->debug.pc2line_len) {
+ b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
+ if (!b->debug.pc2line_buf)
+ goto fail;
+ if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
+ goto fail;
+ }
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
+#endif
+ bc_read_trace(s, "}\n");
+ }
+ if (b->cpool_count != 0) {
+ bc_read_trace(s, "cpool {\n");
+ b->cpool = (void *)((uint8_t*)b + cpool_offset);
+ for(i = 0; i < b->cpool_count; i++) {
+ JSValue val;
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ b->cpool[i] = val;
+ }
+ bc_read_trace(s, "}\n");
+ }
+ b->realm = JS_DupContext(ctx);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadModule(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ JSModuleDef *m = NULL;
+ JSAtom module_name;
+ int i;
+ uint8_t v8;
+
+ if (bc_get_atom(s, &module_name))
+ goto fail;
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
+#endif
+ m = js_new_module_def(ctx, module_name);
+ if (!m)
+ goto fail;
+ obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ if (bc_get_leb128_int(s, &m->req_module_entries_count))
+ goto fail;
+ if (m->req_module_entries_count != 0) {
+ m->req_module_entries_size = m->req_module_entries_count;
+ m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
+ if (!m->req_module_entries)
+ goto fail;
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ if (bc_get_atom(s, &rme->module_name))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->export_entries_count))
+ goto fail;
+ if (m->export_entries_count != 0) {
+ m->export_entries_size = m->export_entries_count;
+ m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
+ if (!m->export_entries)
+ goto fail;
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ me->export_type = v8;
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ if (bc_get_leb128_int(s, &me->u.local.var_idx))
+ goto fail;
+ } else {
+ if (bc_get_leb128_int(s, &me->u.req_module_idx))
+ goto fail;
+ if (bc_get_atom(s, &me->local_name))
+ goto fail;
+ }
+ if (bc_get_atom(s, &me->export_name))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->star_export_entries_count))
+ goto fail;
+ if (m->star_export_entries_count != 0) {
+ m->star_export_entries_size = m->star_export_entries_count;
+ m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
+ if (!m->star_export_entries)
+ goto fail;
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ if (bc_get_leb128_int(s, &se->req_module_idx))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->import_entries_count))
+ goto fail;
+ if (m->import_entries_count != 0) {
+ m->import_entries_size = m->import_entries_count;
+ m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
+ if (!m->import_entries)
+ goto fail;
+ for(i = 0; i < m->import_entries_count; i++) {
+ JSImportEntry *mi = &m->import_entries[i];
+ if (bc_get_leb128_int(s, &mi->var_idx))
+ goto fail;
+ if (bc_get_atom(s, &mi->import_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &mi->req_module_idx))
+ goto fail;
+ }
+ }
+
+ m->func_obj = JS_ReadObjectRec(s);
+ if (JS_IsException(m->func_obj))
+ goto fail;
+ return obj;
+ fail:
+ if (m) {
+ js_free_module_def(ctx, m);
+ }
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadObjectTag(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ uint32_t prop_count, i;
+ JSAtom atom;
+ JSValue val;
+ int ret;
+
+ obj = JS_NewObject(ctx);
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ if (bc_get_leb128(s, &prop_count))
+ goto fail;
+ for(i = 0; i < prop_count; i++) {
+ if (bc_get_atom(s, &atom))
+ goto fail;
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
+#endif
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val)) {
+ JS_FreeAtom(ctx, atom);
+ goto fail;
+ }
+ ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ goto fail;
+ }
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadArray(BCReaderState *s, int tag)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ uint32_t len, i;
+ JSValue val;
+ int ret, prop_flags;
+ BOOL is_template;
+
+ obj = JS_NewArray(ctx);
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
+ if (bc_get_leb128(s, &len))
+ goto fail;
+ for(i = 0; i < len; i++) {
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (is_template)
+ prop_flags = JS_PROP_ENUMERABLE;
+ else
+ prop_flags = JS_PROP_C_W_E;
+ ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
+ prop_flags);
+ if (ret < 0)
+ goto fail;
+ }
+ if (is_template) {
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (!JS_IsUndefined(val)) {
+ ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
+ if (ret < 0)
+ goto fail;
+ }
+ JS_PreventExtensions(ctx, obj);
+ }
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadTypedArray(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
+ uint8_t array_tag;
+ JSValueConst args[3];
+ uint32_t offset, len, idx;
+
+ if (bc_get_u8(s, &array_tag))
+ return JS_EXCEPTION;
+ if (array_tag >= JS_TYPED_ARRAY_COUNT)
+ return JS_ThrowTypeError(ctx, "invalid typed array");
+ if (bc_get_leb128(s, &len))
+ return JS_EXCEPTION;
+ if (bc_get_leb128(s, &offset))
+ return JS_EXCEPTION;
+ /* XXX: this hack could be avoided if the typed array could be
+ created before the array buffer */
+ idx = s->objects_count;
+ if (BC_add_object_ref1(s, NULL))
+ goto fail;
+ array_buffer = JS_ReadObjectRec(s);
+ if (JS_IsException(array_buffer))
+ return JS_EXCEPTION;
+ if (!js_get_array_buffer(ctx, array_buffer)) {
+ JS_FreeValue(ctx, array_buffer);
+ return JS_EXCEPTION;
+ }
+ args[0] = array_buffer;
+ args[1] = JS_NewInt64(ctx, offset);
+ args[2] = JS_NewInt64(ctx, len);
+ obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
+ 3, args,
+ JS_CLASS_UINT8C_ARRAY + array_tag);
+ if (JS_IsException(obj))
+ goto fail;
+ if (s->allow_reference) {
+ s->objects[idx] = JS_VALUE_GET_OBJ(obj);
+ }
+ JS_FreeValue(ctx, array_buffer);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, array_buffer);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadArrayBuffer(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ uint32_t byte_length;
+ JSValue obj;
+
+ if (bc_get_leb128(s, &byte_length))
+ return JS_EXCEPTION;
+ if (unlikely(s->buf_end - s->ptr < byte_length)) {
+ bc_read_error_end(s);
+ return JS_EXCEPTION;
+ }
+ obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ s->ptr += byte_length;
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ uint32_t byte_length;
+ uint8_t *data_ptr;
+ JSValue obj;
+ uint64_t u64;
+
+ if (bc_get_leb128(s, &byte_length))
+ return JS_EXCEPTION;
+ if (bc_get_u64(s, &u64))
+ return JS_EXCEPTION;
+ data_ptr = (uint8_t *)(uintptr_t)u64;
+ /* the SharedArrayBuffer is cloned */
+ obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,
+ JS_CLASS_SHARED_ARRAY_BUFFER,
+ data_ptr,
+ NULL, NULL, FALSE);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadDate(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue val, obj = JS_UNDEFINED;
+
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (!JS_IsNumber(val)) {
+ JS_ThrowTypeError(ctx, "Number tag expected for date");
+ goto fail;
+ }
+ obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
+ JS_CLASS_DATE);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ JS_SetObjectData(ctx, obj, val);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadObjectValue(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue val, obj = JS_UNDEFINED;
+
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ obj = JS_ToObject(ctx, val);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ JS_FreeValue(ctx, val);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
static JSValue JS_ReadObjectRec(BCReaderState *s)
{
JSContext *ctx = s->ctx;
uint8_t tag;
JSValue obj = JS_UNDEFINED;
- JSModuleDef *m = NULL;
+
+ if (js_check_stack_overflow(ctx->rt, 0))
+ return JS_ThrowStackOverflow(ctx);
if (bc_get_u8(s, &tag))
return JS_EXCEPTION;
+ bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
+
switch(tag) {
case BC_TAG_NULL:
- bc_read_trace(s, "null\n");
obj = JS_NULL;
break;
case BC_TAG_UNDEFINED:
- bc_read_trace(s, "undefined\n");
obj = JS_UNDEFINED;
break;
case BC_TAG_BOOL_FALSE:
case BC_TAG_BOOL_TRUE:
- bc_read_trace(s, "%s\n", bc_tag_str[tag]);
obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
break;
case BC_TAG_INT32:
@@ -33990,7 +35174,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s)
int32_t val;
if (bc_get_sleb128(s, &val))
return JS_EXCEPTION;
- bc_read_trace(s, "int32 %d\n", val);
+ bc_read_trace(s, "%d\n", val);
obj = JS_NewInt32(ctx, val);
}
break;
@@ -33999,7 +35183,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s)
JSFloat64Union u;
if (bc_get_u64(s, &u.u64))
return JS_EXCEPTION;
- bc_read_trace(s, "float64 %g\n", u.d);
+ bc_read_trace(s, "%g\n", u.d);
obj = __JS_NewFloat64(ctx, u.d);
}
break;
@@ -34013,378 +35197,68 @@ static JSValue JS_ReadObjectRec(BCReaderState *s)
}
break;
case BC_TAG_FUNCTION_BYTECODE:
- {
- JSFunctionBytecode bc, *b;
- uint16_t v16;
- uint8_t v8;
- int idx, i, local_count;
- int function_size, cpool_offset, byte_code_offset;
- int closure_var_offset, vardefs_offset;
-
- if (!s->allow_bytecode)
- goto invalid_tag;
- bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
-
- memset(&bc, 0, sizeof(bc));
- bc.header.ref_count = 1;
- //bc.gc_header.mark = 0;
-
- if (bc_get_u16(s, &v16))
- goto fail;
- idx = 0;
- bc.has_prototype = bc_get_flags(v16, &idx, 1);
- bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
- bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
- bc.need_home_object = bc_get_flags(v16, &idx, 1);
- bc.func_kind = bc_get_flags(v16, &idx, 2);
- bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
- bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
- bc.super_allowed = bc_get_flags(v16, &idx, 1);
- bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
- bc.has_debug = bc_get_flags(v16, &idx, 1);
- bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
- bc.read_only_bytecode = s->is_rom_data;
- if (bc_get_u8(s, &v8))
- goto fail;
- bc.js_mode = v8;
- if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure
- goto fail;
- if (bc_get_leb128_u16(s, &bc.arg_count))
- goto fail;
- if (bc_get_leb128_u16(s, &bc.var_count))
- goto fail;
- if (bc_get_leb128_u16(s, &bc.defined_arg_count))
- goto fail;
- if (bc_get_leb128_u16(s, &bc.stack_size))
- goto fail;
- if (bc_get_leb128_int(s, &bc.closure_var_count))
- goto fail;
- if (bc_get_leb128_int(s, &bc.cpool_count))
- goto fail;
- if (bc_get_leb128_int(s, &bc.byte_code_len))
- goto fail;
- if (bc_get_leb128_int(s, &local_count))
- goto fail;
-
- if (bc.has_debug) {
- function_size = sizeof(*b);
- } else {
- function_size = offsetof(JSFunctionBytecode, debug);
- }
- cpool_offset = function_size;
- function_size += bc.cpool_count * sizeof(*bc.cpool);
- vardefs_offset = function_size;
- function_size += local_count * sizeof(*bc.vardefs);
- closure_var_offset = function_size;
- function_size += bc.closure_var_count * sizeof(*bc.closure_var);
- byte_code_offset = function_size;
- if (!bc.read_only_bytecode) {
- function_size += bc.byte_code_len;
- }
-
- b = js_mallocz(ctx, function_size);
- if (!b)
- return JS_EXCEPTION;
-
- memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
- b->header.ref_count = 1;
- add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
-
- obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
-
-#ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
-#endif
- bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
- b->arg_count, b->var_count, b->defined_arg_count,
- b->closure_var_count, b->cpool_count);
- bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
- b->stack_size, b->byte_code_len, local_count);
-
- if (local_count != 0) {
- bc_read_trace(s, "vars {\n");
- b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
- for(i = 0; i < local_count; i++) {
- JSVarDef *vd = &b->vardefs[i];
- if (bc_get_atom(s, &vd->var_name))
- goto fail;
- if (bc_get_leb128_int(s, &vd->scope_level))
- goto fail;
- if (bc_get_leb128_int(s, &vd->scope_next))
- goto fail;
- vd->scope_next--;
- if (bc_get_u8(s, &v8))
- goto fail;
- idx = 0;
- vd->var_kind = bc_get_flags(v8, &idx, 4);
- vd->is_func_var = bc_get_flags(v8, &idx, 1);
- vd->is_const = bc_get_flags(v8, &idx, 1);
- vd->is_lexical = bc_get_flags(v8, &idx, 1);
- vd->is_captured = bc_get_flags(v8, &idx, 1);
-#ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
-#endif
- }
- bc_read_trace(s, "}\n");
- }
- if (b->closure_var_count != 0) {
- bc_read_trace(s, "closure vars {\n");
- b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- int var_idx;
- if (bc_get_atom(s, &cv->var_name))
- goto fail;
- if (bc_get_leb128_int(s, &var_idx))
- goto fail;
- cv->var_idx = var_idx;
- if (bc_get_u8(s, &v8))
- goto fail;
- idx = 0;
- cv->is_local = bc_get_flags(v8, &idx, 1);
- cv->is_arg = bc_get_flags(v8, &idx, 1);
- cv->is_const = bc_get_flags(v8, &idx, 1);
- cv->is_lexical = bc_get_flags(v8, &idx, 1);
- cv->var_kind = bc_get_flags(v8, &idx, 4);
-#ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
-#endif
- }
- bc_read_trace(s, "}\n");
- }
- {
- bc_read_trace(s, "bytecode {\n");
- if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
- goto fail;
- bc_read_trace(s, "}\n");
- }
- if (b->has_debug) {
- /* read optional debug information */
- bc_read_trace(s, "debug {\n");
- if (bc_get_atom(s, &b->debug.filename))
- goto fail;
- if (bc_get_leb128_int(s, &b->debug.line_num))
- goto fail;
- if (bc_get_leb128_int(s, &b->debug.pc2line_len))
- goto fail;
- if (b->debug.pc2line_len) {
- b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
- if (!b->debug.pc2line_buf)
- goto fail;
- if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
- goto fail;
- }
-#ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
-#endif
- bc_read_trace(s, "}\n");
- }
- if (b->cpool_count != 0) {
- bc_read_trace(s, "cpool {\n");
- b->cpool = (void *)((uint8_t*)b + cpool_offset);
- for(i = 0; i < b->cpool_count; i++) {
- JSValue val;
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- b->cpool[i] = val;
- }
- bc_read_trace(s, "}\n");
- }
- bc_read_trace(s, "}\n");
- b->realm = JS_DupContext(ctx);
- }
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ obj = JS_ReadFunctionTag(s);
break;
case BC_TAG_MODULE:
- {
- JSAtom module_name;
- int i;
- uint8_t v8;
-
- if (!s->allow_bytecode)
- goto invalid_tag;
- bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
- if (bc_get_atom(s, &module_name))
- goto fail;
-#ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
-#endif
- m = js_new_module_def(ctx, module_name);
- if (!m)
- goto fail;
- obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
- if (bc_get_leb128_int(s, &m->req_module_entries_count))
- goto fail;
- if (m->req_module_entries_count != 0) {
- m->req_module_entries_size = m->req_module_entries_count;
- m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
- if (!m->req_module_entries)
- goto fail;
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- if (bc_get_atom(s, &rme->module_name))
- goto fail;
- }
- }
-
- if (bc_get_leb128_int(s, &m->export_entries_count))
- goto fail;
- if (m->export_entries_count != 0) {
- m->export_entries_size = m->export_entries_count;
- m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
- if (!m->export_entries)
- goto fail;
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (bc_get_u8(s, &v8))
- goto fail;
- me->export_type = v8;
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- if (bc_get_leb128_int(s, &me->u.local.var_idx))
- goto fail;
- } else {
- if (bc_get_leb128_int(s, &me->u.req_module_idx))
- goto fail;
- if (bc_get_atom(s, &me->local_name))
- goto fail;
- }
- if (bc_get_atom(s, &me->export_name))
- goto fail;
- }
- }
-
- if (bc_get_leb128_int(s, &m->star_export_entries_count))
- goto fail;
- if (m->star_export_entries_count != 0) {
- m->star_export_entries_size = m->star_export_entries_count;
- m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
- if (!m->star_export_entries)
- goto fail;
- for(i = 0; i < m->star_export_entries_count; i++) {
- JSStarExportEntry *se = &m->star_export_entries[i];
- if (bc_get_leb128_int(s, &se->req_module_idx))
- goto fail;
- }
- }
-
- if (bc_get_leb128_int(s, &m->import_entries_count))
- goto fail;
- if (m->import_entries_count != 0) {
- m->import_entries_size = m->import_entries_count;
- m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
- if (!m->import_entries)
- goto fail;
- for(i = 0; i < m->import_entries_count; i++) {
- JSImportEntry *mi = &m->import_entries[i];
- if (bc_get_leb128_int(s, &mi->var_idx))
- goto fail;
- if (bc_get_atom(s, &mi->import_name))
- goto fail;
- if (bc_get_leb128_int(s, &mi->req_module_idx))
- goto fail;
- }
- }
-
- m->func_obj = JS_ReadObjectRec(s);
- if (JS_IsException(m->func_obj))
- goto fail;
- bc_read_trace(s, "}\n");
- }
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ obj = JS_ReadModule(s);
break;
case BC_TAG_OBJECT:
- {
- uint32_t prop_count, i;
- JSAtom atom;
- JSValue val;
- int ret;
-
- bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
-
- obj = JS_NewObject(ctx);
- if (bc_get_leb128(s, &prop_count))
- goto fail;
- for(i = 0; i < prop_count; i++) {
- if (bc_get_atom(s, &atom))
- goto fail;
-#ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
-#endif
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val)) {
- JS_FreeAtom(ctx, atom);
- goto fail;
- }
- ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
- JS_FreeAtom(ctx, atom);
- if (ret < 0)
- goto fail;
- }
- bc_read_trace(s, "}\n");
- }
+ obj = JS_ReadObjectTag(s);
break;
case BC_TAG_ARRAY:
case BC_TAG_TEMPLATE_OBJECT:
- {
- uint32_t len, i;
- JSValue val;
- int ret, prop_flags;
- BOOL is_template;
-
- bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
-
- obj = JS_NewArray(ctx);
- is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
- if (bc_get_leb128(s, &len))
- goto fail;
- for(i = 0; i < len; i++) {
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- if (is_template)
- prop_flags = JS_PROP_ENUMERABLE;
- else
- prop_flags = JS_PROP_C_W_E;
- ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
- prop_flags);
- if (ret < 0)
- goto fail;
- }
- if (is_template) {
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- if (!JS_IsUndefined(val)) {
- ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
- if (ret < 0)
- goto fail;
- }
- JS_PreventExtensions(ctx, obj);
- }
- bc_read_trace(s, "}\n");
- }
+ obj = JS_ReadArray(s, tag);
+ break;
+ case BC_TAG_TYPED_ARRAY:
+ obj = JS_ReadTypedArray(s);
+ break;
+ case BC_TAG_ARRAY_BUFFER:
+ obj = JS_ReadArrayBuffer(s);
+ break;
+ case BC_TAG_SHARED_ARRAY_BUFFER:
+ if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
+ goto invalid_tag;
+ obj = JS_ReadSharedArrayBuffer(s);
+ break;
+ case BC_TAG_DATE:
+ obj = JS_ReadDate(s);
+ break;
+ case BC_TAG_OBJECT_VALUE:
+ obj = JS_ReadObjectValue(s);
break;
#ifdef CONFIG_BIGNUM
case BC_TAG_BIG_INT:
case BC_TAG_BIG_FLOAT:
case BC_TAG_BIG_DECIMAL:
obj = JS_ReadBigNum(s, tag);
- if (JS_IsException(obj))
- goto fail;
break;
#endif
+ case BC_TAG_OBJECT_REFERENCE:
+ {
+ uint32_t val;
+ if (!s->allow_reference)
+ return JS_ThrowSyntaxError(ctx, "object references are not allowed");
+ if (bc_get_leb128(s, &val))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%u\n", val);
+ if (val >= s->objects_count) {
+ return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
+ val, s->objects_count);
+ }
+ obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
+ }
+ break;
default:
invalid_tag:
return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
tag, (unsigned int)(s->ptr - s->buf_start));
}
+ bc_read_trace(s, "}\n");
return obj;
- fail:
- if (m) {
- js_free_module_def(ctx, m);
- } else {
- JS_FreeValue(ctx, obj);
- }
- return JS_EXCEPTION;
}
static int JS_ReadObjectAtoms(BCReaderState *s)
@@ -34437,10 +35311,11 @@ static void bc_reader_free(BCReaderState *s)
}
js_free(s->ctx, s->idx_to_atom);
}
+ js_free(s->ctx, s->objects);
}
JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
- int flags)
+ int flags)
{
BCReaderState ss, *s = &ss;
JSValue obj;
@@ -34455,6 +35330,8 @@ JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
s->ptr = buf;
s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
+ s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
+ s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
if (s->allow_bytecode)
s->first_atom = JS_ATOM_END;
else
@@ -34944,7 +35821,7 @@ static __exception int JS_ObjectDefineProperties(JSContext *ctx,
if (JS_IsException(props))
return -1;
p = JS_VALUE_GET_OBJ(props);
- if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK) < 0)
+ if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
goto exception;
for(i = 0; i < len; i++) {
JS_FreeValue(ctx, desc);
@@ -35021,7 +35898,7 @@ static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
return JS_ThrowTypeErrorNotAnObject(ctx);
}
- return JS_DupValue(ctx, JS_GetPrototype(ctx, val));
+ return JS_GetPrototype(ctx, val);
}
static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
@@ -35815,7 +36692,7 @@ static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
val = JS_ToObject(ctx, this_val);
if (JS_IsException(val))
return val;
- ret = JS_DupValue(ctx, JS_GetPrototype(ctx, val));
+ ret = JS_GetPrototype(ctx, val);
JS_FreeValue(ctx, val);
return ret;
}
@@ -35836,9 +36713,9 @@ static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
- JSValue obj;
+ JSValue obj, v1;
JSValueConst v;
- int max_depth = 1000, res = -1;
+ int res;
v = argv[0];
if (!JS_IsObject(v))
@@ -35846,27 +36723,29 @@ static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
obj = JS_ToObject(ctx, this_val);
if (JS_IsException(obj))
return JS_EXCEPTION;
- while (--max_depth > 0) {
- v = JS_GetPrototype(ctx, v);
- if (JS_IsException(v))
+ v1 = JS_DupValue(ctx, v);
+ for(;;) {
+ v1 = JS_GetPrototypeFree(ctx, v1);
+ if (JS_IsException(v1))
goto exception;
- if (JS_IsNull(v)) {
+ if (JS_IsNull(v1)) {
res = FALSE;
break;
}
- if (js_strict_eq2(ctx, JS_DupValue(ctx, obj), JS_DupValue(ctx, v),
- JS_EQ_STRICT)) {
+ if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
res = TRUE;
break;
}
+ /* avoid infinite loop (possible with proxies) */
+ if (js_poll_interrupts(ctx))
+ goto exception;
}
+ JS_FreeValue(ctx, v1);
JS_FreeValue(ctx, obj);
- if (res < 0)
- return JS_ThrowInternalError(ctx, "prototype chain cycle");
- else
- return JS_NewBool(ctx, res);
+ return JS_NewBool(ctx, res);
exception:
+ JS_FreeValue(ctx, v1);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
@@ -35906,7 +36785,6 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int setter)
{
JSValue obj, res = JS_EXCEPTION;
- JSValueConst v;
JSAtom prop = JS_ATOM_NULL;
JSPropertyDescriptor desc;
int has_prop;
@@ -35918,8 +36796,8 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
if (unlikely(prop == JS_ATOM_NULL))
goto exception;
- for (v = obj;;) {
- has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(v), prop);
+ for (;;) {
+ has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
if (has_prop < 0)
goto exception;
if (has_prop) {
@@ -35930,13 +36808,16 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
js_free_desc(ctx, &desc);
break;
}
- v = JS_GetPrototype(ctx, v);
- if (JS_IsException(v))
+ obj = JS_GetPrototypeFree(ctx, obj);
+ if (JS_IsException(obj))
goto exception;
- if (JS_IsNull(v)) {
+ if (JS_IsNull(obj)) {
res = JS_UNDEFINED;
break;
}
+ /* avoid infinite loop (possible with proxies) */
+ if (js_poll_interrupts(ctx))
+ goto exception;
}
exception:
@@ -36002,59 +36883,12 @@ static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,
return JS_UNDEFINED;
}
-/* insert a '\n' at unicode character position 'pos' in the source of
- 'func_obj' */
-static int patch_function_constructor_source(JSContext *ctx,
- JSValueConst func_obj,
- size_t pos)
-{
- JSObject *p;
- JSFunctionBytecode *b;
- char *r, *r_end, *new_source;
- int c;
- size_t idx, len;
-
- if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
- return 0;
- p = JS_VALUE_GET_OBJ(func_obj);
- if (!js_class_has_bytecode(p->class_id))
- return 0;
- b = p->u.func.function_bytecode;
- if (!b->has_debug)
- return 0;
- r = b->debug.source;
- r_end = b->debug.source + b->debug.source_len;
- idx = 0;
- while (r < r_end) {
- if (idx == pos) {
- /* add the '\n' */
- new_source = js_realloc(ctx, b->debug.source,
- b->debug.source_len + 2);
- if (!new_source)
- return -1;
- len = r - b->debug.source;
- memmove(new_source + len + 1, new_source + len,
- b->debug.source_len + 1 - len);
- new_source[len] = '\n';
- b->debug.source = new_source;
- b->debug.source_len++;
- break;
- }
- c = unicode_from_utf8((const uint8_t *)r, UTF8_CHAR_LEN_MAX,
- (const uint8_t **)&r);
- if (c < 0)
- break;
- idx++;
- }
- return 0;
-}
-
/* XXX: add a specific eval mode so that Function("}), ({") is rejected */
static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv, int magic)
{
JSFunctionKindEnum func_kind = magic;
- int i, n, ret, func_start_pos;
+ int i, n, ret;
JSValue s, proto, obj = JS_UNDEFINED;
StringBuffer b_s, *b = &b_s;
@@ -36079,13 +36913,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
if (string_buffer_concat_value(b, argv[i]))
goto fail;
}
- string_buffer_puts8(b, "\n) {");
- /* Annex B HTML comments: We don't add a '\n' after "{" so that
- "-->" is not considered as an HTML comment. It is necessary
- because in the spec the function body is parsed separately. */
- /* XXX: find a simpler way or be deliberately incompatible to
- simplify the code ? */
- func_start_pos = b->len - 1; /* the leading '(' is not in the source */
+ string_buffer_puts8(b, "\n) {\n");
if (n >= 0) {
if (string_buffer_concat_value(b, argv[n]))
goto fail;
@@ -36099,8 +36927,6 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
JS_FreeValue(ctx, s);
if (JS_IsException(obj))
goto fail1;
- if (patch_function_constructor_source(ctx, obj, func_start_pos) < 0)
- goto fail1;
if (!JS_IsUndefined(new_target)) {
/* set the prototype */
proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
@@ -36315,27 +37141,22 @@ static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
JSValue name;
const char *pref, *suff;
- if (p->is_class) {
- pref = "class ";
- suff = " {\n [native code]\n}";
- } else {
- switch(func_kind) {
- default:
- case JS_FUNC_NORMAL:
- pref = "function ";
- break;
- case JS_FUNC_GENERATOR:
- pref = "function *";
- break;
- case JS_FUNC_ASYNC:
- pref = "async function ";
- break;
- case JS_FUNC_ASYNC_GENERATOR:
- pref = "async function *";
- break;
- }
- suff = "() {\n [native code]\n}";
+ switch(func_kind) {
+ default:
+ case JS_FUNC_NORMAL:
+ pref = "function ";
+ break;
+ case JS_FUNC_GENERATOR:
+ pref = "function *";
+ break;
+ case JS_FUNC_ASYNC:
+ pref = "async function ";
+ break;
+ case JS_FUNC_ASYNC_GENERATOR:
+ pref = "async function *";
+ break;
}
+ suff = "() {\n [native code]\n}";
name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
if (JS_IsUndefined(name))
name = JS_AtomToString(ctx, JS_ATOM_empty_string);
@@ -36435,12 +37256,6 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
if (JS_IsException(obj))
return obj;
if (magic == JS_AGGREGATE_ERROR) {
- JSObject *p;
- JSValue error_list = iterator_to_array(ctx, argv[0]);
- if (JS_IsException(error_list))
- goto exception;
- p = JS_VALUE_GET_OBJ(obj);
- p->u.object_data = error_list;
message = argv[1];
} else {
message = argv[0];
@@ -36448,17 +37263,26 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
if (!JS_IsUndefined(message)) {
msg = JS_ToString(ctx, message);
- if (unlikely(JS_IsException(msg))) {
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
+ if (unlikely(JS_IsException(msg)))
+ goto exception;
JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
}
+
+ if (magic == JS_AGGREGATE_ERROR) {
+ JSValue error_list = iterator_to_array(ctx, argv[0]);
+ if (JS_IsException(error_list))
+ goto exception;
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+
/* skip the Error() function in the backtrace */
build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
return obj;
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
}
static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
@@ -36498,56 +37322,22 @@ static const JSCFunctionListEntry js_error_proto_funcs[] = {
/* AggregateError */
-/* used by C code. 'errors' must be a fast array. */
+/* used by C code. */
static JSValue js_aggregate_error_constructor(JSContext *ctx,
JSValueConst errors)
{
JSValue obj;
- JSObject *p;
obj = JS_NewObjectProtoClass(ctx,
ctx->native_error_proto[JS_AGGREGATE_ERROR],
JS_CLASS_ERROR);
if (JS_IsException(obj))
return obj;
- p = JS_VALUE_GET_OBJ(obj);
- p->u.object_data = JS_DupValue(ctx, errors);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
return obj;
}
-static JSValue js_aggregate_error_errors(JSContext *ctx, JSValueConst this_val)
-{
- JSObject *p;
- JSValue r, *arr;
- uint32_t len, i;
-
- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
- goto invalid_type;
- p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id != JS_CLASS_ERROR)
- goto invalid_type;
- if (!js_get_fast_array(ctx, p->u.object_data, &arr, &len)) {
- invalid_type:
- return JS_ThrowTypeError(ctx, "not an AggregateError");
- }
- r = JS_NewArray(ctx);
- if (JS_IsException(r))
- goto exception;
- for(i = 0; i < len; i++) {
- if (JS_DefinePropertyValueInt64(ctx, r, i, JS_DupValue(ctx, arr[i]),
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- }
- return r;
- exception:
- JS_FreeValue(ctx, r);
- return JS_EXCEPTION;
-}
-
-static const JSCFunctionListEntry js_aggregate_error_proto_funcs[] = {
- JS_CGETSET_DEF("errors", js_aggregate_error_errors, NULL),
-};
-
/* Array */
static int JS_CopySubArray(JSContext *ctx,
@@ -39130,22 +39920,25 @@ static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
p1 = JS_VALUE_GET_STRING(v);
len = p->len;
v_len = p1->len;
- pos = (magic & 2) ? len : 0;
+ pos = (magic == 2) ? len : 0;
if (argc > 1 && !JS_IsUndefined(argv[1])) {
if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
goto fail;
}
len -= v_len;
- start = pos;
- stop = len;
- if (magic & 1) {
- stop = pos;
- }
- if (magic & 2) {
- pos -= v_len;
+ ret = 0;
+ if (magic == 0) {
+ start = pos;
+ stop = len;
+ } else {
+ if (magic == 1) {
+ if (pos > len)
+ goto done;
+ } else {
+ pos -= v_len;
+ }
start = stop = pos;
}
- ret = 0;
if (start >= 0 && start <= stop) {
for (i = start;; i++) {
if (!string_cmp(p, p1, i, 0, v_len)) {
@@ -39156,6 +39949,7 @@ static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
break;
}
}
+ done:
JS_FreeValue(ctx, str);
JS_FreeValue(ctx, v);
return JS_NewBool(ctx, ret);
@@ -40372,46 +41166,23 @@ static double js_math_round(double a)
static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
- double r, a, b;
+ double r, a;
int i;
- if (argc == 2) {
- /* use the more precise built-in function when possible */
- if (JS_ToFloat64(ctx, &a, argv[0]))
- return JS_EXCEPTION;
- if (JS_ToFloat64(ctx, &b, argv[1]))
- return JS_EXCEPTION;
- r = hypot(a, b);
- } else if (argc == 0) {
- r = 0;
- } else {
- double *tab, a_max;
- tab = js_malloc(ctx, sizeof(tab[0]) * argc);
- if (!tab)
+ r = 0;
+ if (argc > 0) {
+ if (JS_ToFloat64(ctx, &r, argv[0]))
return JS_EXCEPTION;
- /* avoid overflow by taking the maximum */
- a_max = 0;
- for(i = 0; i < argc; i++) {
- if (JS_ToFloat64(ctx, &a, argv[i])) {
- js_free(ctx, tab);
- return JS_EXCEPTION;
- }
- a = fabs(a);
- tab[i] = a;
- if (a > a_max)
- a_max = a;
- }
- if (a_max == 0 || !isfinite(a_max)) {
- r = a_max;
+ if (argc == 1) {
+ r = fabs(r);
} else {
- r = 0;
- for(i = 0; i < argc; i++) {
- a = tab[i] / a_max;
- r += a * a;
+ /* use the built-in function to minimize precision loss */
+ for (i = 1; i < argc; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ r = hypot(r, a);
}
- r = a_max * sqrt(r);
}
- js_free(ctx, tab);
}
return JS_NewFloat64(ctx, r);
}
@@ -42093,58 +42864,69 @@ void JS_AddIntrinsicRegExp(JSContext *ctx)
/* JSON */
-/* XXX: this parser is less strict than the JSON standard because we
- reuse the Javascript tokenizer. It could be improved by adding a
- specific JSON parse flag. */
+static int json_parse_expect(JSParseState *s, int tok)
+{
+ if (s->token.val != tok) {
+ /* XXX: dump token correctly in all cases */
+ return js_parse_error(s, "expecting '%c'", tok);
+ }
+ return json_next_token(s);
+}
+
static JSValue json_parse_value(JSParseState *s)
{
JSContext *ctx = s->ctx;
JSValue val = JS_NULL;
- BOOL is_neg;
int ret;
switch(s->token.val) {
case '{':
{
- JSValue prop_val, prop_str;
-
- if (next_token(s))
+ JSValue prop_val;
+ JSAtom prop_name;
+
+ if (json_next_token(s))
goto fail;
val = JS_NewObject(ctx);
if (JS_IsException(val))
goto fail;
if (s->token.val != '}') {
for(;;) {
- if (s->token.val != TOK_STRING) {
+ if (s->token.val == TOK_STRING) {
+ prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
+ if (prop_name == JS_ATOM_NULL)
+ goto fail;
+ } else if (s->ext_json && s->token.val == TOK_IDENT) {
+ prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ } else {
js_parse_error(s, "expecting property name");
goto fail;
}
- prop_str = JS_DupValue(ctx, s->token.u.str.str);
- if (next_token(s)) {
- JS_FreeValue(ctx, prop_str);
- goto fail;
- }
- if (js_parse_expect(s, ':')) {
- JS_FreeValue(ctx, prop_str);
- goto fail;
- }
+ if (json_next_token(s))
+ goto fail1;
+ if (json_parse_expect(s, ':'))
+ goto fail1;
prop_val = json_parse_value(s);
if (JS_IsException(prop_val)) {
- JS_FreeValue(ctx, prop_str);
+ fail1:
+ JS_FreeAtom(ctx, prop_name);
goto fail;
}
- ret = JS_DefinePropertyValueValue(ctx, val, prop_str,
- prop_val, JS_PROP_C_W_E);
+ ret = JS_DefinePropertyValue(ctx, val, prop_name,
+ prop_val, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, prop_name);
if (ret < 0)
goto fail;
if (s->token.val != ',')
break;
- if (next_token(s))
+ if (json_next_token(s))
goto fail;
+ if (s->ext_json && s->token.val == '}')
+ break;
}
}
- if (js_parse_expect(s, '}'))
+ if (json_parse_expect(s, '}'))
goto fail;
}
break;
@@ -42153,7 +42935,7 @@ static JSValue json_parse_value(JSParseState *s)
JSValue el;
uint32_t idx;
- if (next_token(s))
+ if (json_next_token(s))
goto fail;
val = JS_NewArray(ctx);
if (JS_IsException(val))
@@ -42169,52 +42951,41 @@ static JSValue json_parse_value(JSParseState *s)
goto fail;
if (s->token.val != ',')
break;
- if (next_token(s))
+ if (json_next_token(s))
goto fail;
idx++;
+ if (s->ext_json && s->token.val == ']')
+ break;
}
}
- if (js_parse_expect(s, ']'))
+ if (json_parse_expect(s, ']'))
goto fail;
}
break;
case TOK_STRING:
val = JS_DupValue(ctx, s->token.u.str.str);
- if (next_token(s))
+ if (json_next_token(s))
goto fail;
break;
case TOK_NUMBER:
- is_neg = 0;
- goto number;
- case '-':
- if (next_token(s))
- goto fail;
- if (s->token.val != TOK_NUMBER) {
- js_parse_error(s, "number expected");
- goto fail;
- }
- is_neg = 1;
- number:
val = s->token.u.num.val;
- if (is_neg) {
- double d;
- JS_ToFloat64(ctx, &d, val); /* no exception possible */
- val = JS_NewFloat64(ctx, -d);
- }
- if (next_token(s))
- goto fail;
- break;
- case TOK_FALSE:
- case TOK_TRUE:
- val = JS_NewBool(ctx, s->token.val - TOK_FALSE);
- if (next_token(s))
+ if (json_next_token(s))
goto fail;
break;
- case TOK_NULL:
- if (next_token(s))
+ case TOK_IDENT:
+ if (s->token.u.ident.atom == JS_ATOM_false ||
+ s->token.u.ident.atom == JS_ATOM_true) {
+ val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));
+ } else if (s->token.u.ident.atom == JS_ATOM_null) {
+ val = JS_NULL;
+ } else {
+ goto def_token;
+ }
+ if (json_next_token(s))
goto fail;
break;
default:
+ def_token:
if (s->token.val == TOK_EOF) {
js_parse_error(s, "unexpected end of input");
} else {
@@ -42229,15 +43000,15 @@ static JSValue json_parse_value(JSParseState *s)
return JS_EXCEPTION;
}
-JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
- const char *filename)
+JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename, int flags)
{
JSParseState s1, *s = &s1;
- JSValue val;
+ JSValue val = JS_UNDEFINED;
js_parse_init(ctx, s, buf, buf_len, filename);
-
- if (next_token(s))
+ s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
+ if (json_next_token(s))
goto fail;
val = json_parse_value(s);
if (JS_IsException(val))
@@ -42248,10 +43019,17 @@ JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
}
return val;
fail:
+ JS_FreeValue(ctx, val);
free_token(s, &s->token);
return JS_EXCEPTION;
}
+JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename)
+{
+ return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
+}
+
static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
JSAtom name, JSValueConst reviver)
{
@@ -42960,7 +43738,6 @@ static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
if (s) {
JS_FreeValueRT(rt, s->target);
JS_FreeValueRT(rt, s->handler);
- JS_FreeValueRT(rt, s->proto);
js_free_rt(rt, s);
}
}
@@ -42972,7 +43749,6 @@ static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
if (s) {
JS_MarkValue(rt, s->target, mark_func);
JS_MarkValue(rt, s->handler, mark_func);
- JS_MarkValue(rt, s->proto, mark_func);
}
}
@@ -43007,17 +43783,12 @@ static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
return s;
}
-static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
+static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
{
JSProxyData *s;
- JSValue method, ret;
- JSValueConst proto1;
+ JSValue method, ret, proto1;
int res;
- /* must check for timeout to avoid infinite loop in instanceof */
- if (js_poll_interrupts(ctx))
- return JS_EXCEPTION;
-
s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
if (!s)
return JS_EXCEPTION;
@@ -43043,22 +43814,22 @@ static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
return JS_EXCEPTION;
}
if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {
+ JS_FreeValue(ctx, proto1);
fail:
JS_FreeValue(ctx, ret);
return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
}
+ JS_FreeValue(ctx, proto1);
}
- /* store the prototype in the proxy so that its refcount is at least 1 */
- set_value(ctx, &s->proto, ret);
- return (JSValueConst)ret;
+ return ret;
}
static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
JSValueConst proto_val, BOOL throw_flag)
{
JSProxyData *s;
- JSValue method, ret;
- JSValueConst args[2], proto1;
+ JSValue method, ret, proto1;
+ JSValueConst args[2];
BOOL res;
int res2;
@@ -43089,9 +43860,11 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
if (JS_IsException(proto1))
return -1;
if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {
+ JS_FreeValue(ctx, proto1);
JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
return -1;
}
+ JS_FreeValue(ctx, proto1);
}
return TRUE;
}
@@ -43829,7 +44602,6 @@ static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
}
s->target = JS_DupValue(ctx, target);
s->handler = JS_DupValue(ctx, handler);
- s->proto = JS_NULL;
s->is_func = JS_IsFunction(ctx, target);
s->is_revoked = FALSE;
JS_SetOpaque(obj, s);
@@ -45414,10 +46186,10 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx,
static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
- JSValue result_promise, resolving_funcs[2], iter, item, next_promise, ret;
+ JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
- JSValue promise_resolve = JS_UNDEFINED;
+ JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
JSValueConst then_args[2], resolve_element_data[5];
BOOL done;
int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
@@ -45427,6 +46199,10 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
if (JS_IsException(result_promise))
return result_promise;
+ promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
+ if (JS_IsException(promise_resolve) ||
+ check_function(ctx, promise_resolve))
+ goto fail_reject;
iter = JS_GetIterator(ctx, argv[0], FALSE);
if (JS_IsException(iter)) {
JSValue error;
@@ -45454,11 +46230,6 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
goto fail_reject;
- promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
- if (JS_IsException(promise_resolve) ||
- check_function(ctx, promise_resolve))
- goto fail_reject1;
-
index = 0;
for(;;) {
/* XXX: conformance: should close the iterator if error on 'done'
@@ -45560,8 +46331,8 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
- JSValue result_promise, resolving_funcs[2], iter, item, next_promise, ret;
- JSValue next_method = JS_UNDEFINED;
+ JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
+ JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
JSValue promise_resolve = JS_UNDEFINED;
BOOL done;
@@ -45570,6 +46341,10 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
if (JS_IsException(result_promise))
return result_promise;
+ promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
+ if (JS_IsException(promise_resolve) ||
+ check_function(ctx, promise_resolve))
+ goto fail_reject;
iter = JS_GetIterator(ctx, argv[0], FALSE);
if (JS_IsException(iter)) {
JSValue error;
@@ -45586,11 +46361,6 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
if (JS_IsException(next_method))
goto fail_reject;
- promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
- if (JS_IsException(promise_resolve) ||
- check_function(ctx, promise_resolve))
- goto fail_reject1;
-
for(;;) {
/* XXX: conformance: should close the iterator if error on 'done'
access, but not on 'value' access */
@@ -49385,9 +50155,6 @@ static void JS_AddIntrinsicBasicObjects(JSContext *ctx)
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
ctx->native_error_proto[i] = proto;
}
- JS_SetPropertyFunctionList(ctx, ctx->native_error_proto[JS_AGGREGATE_ERROR],
- js_aggregate_error_proto_funcs,
- countof(js_aggregate_error_proto_funcs));
/* the array prototype is an array */
ctx->class_proto[JS_CLASS_ARRAY] =
@@ -49621,6 +50388,7 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx,
JSFreeArrayBufferDataFunc *free_func,
void *opaque, BOOL alloc_flag)
{
+ JSRuntime *rt = ctx->rt;
JSValue obj;
JSArrayBuffer *abuf = NULL;
@@ -49637,11 +50405,24 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx,
goto fail;
abuf->byte_length = len;
if (alloc_flag) {
- /* the allocation must be done after the object creation */
- abuf->data = js_mallocz(ctx, max_int(len, 1));
- if (!abuf->data)
- goto fail;
+ if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
+ rt->sab_funcs.sab_alloc) {
+ abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,
+ max_int(len, 1));
+ if (!abuf->data)
+ goto fail;
+ memset(abuf->data, 0, len);
+ } else {
+ /* the allocation must be done after the object creation */
+ abuf->data = js_mallocz(ctx, max_int(len, 1));
+ if (!abuf->data)
+ goto fail;
+ }
} else {
+ if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
+ rt->sab_funcs.sab_dup) {
+ rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);
+ }
abuf->data = buf;
}
init_list_head(&abuf->array_list);
@@ -49731,8 +50512,12 @@ static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
array finalizers using it, so abuf->array_list is not
necessarily empty. */
// assert(list_empty(&abuf->array_list));
- if (abuf->free_func)
- abuf->free_func(rt, abuf->opaque, abuf->data);
+ if (abuf->shared && rt->sab_funcs.sab_free) {
+ rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
+ } else {
+ if (abuf->free_func)
+ abuf->free_func(rt, abuf->opaque, abuf->data);
+ }
js_free_rt(rt, abuf);
}
}
@@ -50208,11 +50993,6 @@ static JSValue js_typed_array___getLength(JSContext *ctx,
}
#endif
-static JSValue js_typed_array_constructor(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int classid);
-
static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,
int argc, JSValueConst *argv)
{
@@ -51967,9 +52747,10 @@ typedef enum AtomicsOpEnum {
} AtomicsOpEnum;
static void *js_atomics_get_ptr(JSContext *ctx,
+ JSArrayBuffer **pabuf,
int *psize_log2, JSClassID *pclass_id,
JSValueConst obj, JSValueConst idx_val,
- BOOL is_waitable)
+ int is_waitable)
{
JSObject *p;
JSTypedArray *ta;
@@ -52004,18 +52785,27 @@ static void *js_atomics_get_ptr(JSContext *ctx,
ta = p->u.typed_array;
abuf = ta->buffer->u.array_buffer;
if (!abuf->shared) {
- JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
- return NULL;
+ if (is_waitable == 2) {
+ JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
+ return NULL;
+ }
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ return NULL;
+ }
}
if (JS_ToIndex(ctx, &idx, idx_val)) {
return NULL;
}
+ /* if the array buffer is detached, p->u.array.count = 0 */
if (idx >= p->u.array.count) {
JS_ThrowRangeError(ctx, "out-of-bound access");
return NULL;
}
size_log2 = typed_array_size_log2(p->class_id);
ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+ if (pabuf)
+ *pabuf = abuf;
if (psize_log2)
*psize_log2 = size_log2;
if (pclass_id)
@@ -52036,17 +52826,18 @@ static JSValue js_atomics_op(JSContext *ctx,
void *ptr;
JSValue ret;
JSClassID class_id;
+ JSArrayBuffer *abuf;
- ptr = js_atomics_get_ptr(ctx, &size_log2, &class_id,
- argv[0], argv[1], FALSE);
+ ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
+ argv[0], argv[1], 0);
if (!ptr)
return JS_EXCEPTION;
rep_val = 0;
if (op == ATOMICS_OP_LOAD) {
v = 0;
- } else
+ } else {
#ifdef CONFIG_BIGNUM
- if (size_log2 == 3) {
+ if (size_log2 == 3) {
int64_t v64;
if (JS_ToBigInt64(ctx, &v64, argv[2]))
return JS_EXCEPTION;
@@ -52056,19 +52847,23 @@ static JSValue js_atomics_op(JSContext *ctx,
return JS_EXCEPTION;
rep_val = v64;
}
- } else
+ } else
#endif
- {
- uint32_t v32;
- if (JS_ToUint32(ctx, &v32, argv[2]))
- return JS_EXCEPTION;
- v = v32;
- if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
- if (JS_ToUint32(ctx, &v32, argv[3]))
- return JS_EXCEPTION;
- rep_val = v32;
- }
+ {
+ uint32_t v32;
+ if (JS_ToUint32(ctx, &v32, argv[2]))
+ return JS_EXCEPTION;
+ v = v32;
+ if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
+ if (JS_ToUint32(ctx, &v32, argv[3]))
+ return JS_EXCEPTION;
+ rep_val = v32;
+ }
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
}
+
switch(op | (size_log2 << 3)) {
#ifdef CONFIG_BIGNUM
@@ -52195,8 +52990,10 @@ static JSValue js_atomics_store(JSContext *ctx,
int size_log2;
void *ptr;
JSValue ret;
+ JSArrayBuffer *abuf;
- ptr = js_atomics_get_ptr(ctx, &size_log2, NULL, argv[0], argv[1], FALSE);
+ ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
+ argv[0], argv[1], 0);
if (!ptr)
return JS_EXCEPTION;
#ifdef CONFIG_BIGNUM
@@ -52209,6 +53006,8 @@ static JSValue js_atomics_store(JSContext *ctx,
JS_FreeValue(ctx, ret);
return JS_EXCEPTION;
}
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
atomic_store((_Atomic(uint64_t) *)ptr, v64);
} else
#endif
@@ -52222,6 +53021,8 @@ static JSValue js_atomics_store(JSContext *ctx,
JS_FreeValue(ctx, ret);
return JS_EXCEPTION;
}
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
switch(size_log2) {
case 0:
atomic_store((_Atomic(uint8_t) *)ptr, v);
@@ -52278,7 +53079,8 @@ static JSValue js_atomics_wait(JSContext *ctx,
int ret, size_log2, res;
double d;
- ptr = js_atomics_get_ptr(ctx, &size_log2, NULL, argv[0], argv[1], TRUE);
+ ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
+ argv[0], argv[1], 2);
if (!ptr)
return JS_EXCEPTION;
#ifdef CONFIG_BIGNUM
@@ -52357,8 +53159,9 @@ static JSValue js_atomics_notify(JSContext *ctx,
int32_t count, n;
void *ptr;
JSAtomicsWaiter *waiter;
+ JSArrayBuffer *abuf;
- ptr = js_atomics_get_ptr(ctx, NULL, NULL, argv[0], argv[1], TRUE);
+ ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
if (!ptr)
return JS_EXCEPTION;
@@ -52368,9 +53171,11 @@ static JSValue js_atomics_notify(JSContext *ctx,
if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
return JS_EXCEPTION;
}
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
n = 0;
- if (count > 0) {
+ if (abuf->shared && count > 0) {
pthread_mutex_lock(&js_atomics_mutex);
init_list_head(&waiter_list);
list_for_each_safe(el, el1, &js_atomics_waiter_list) {
diff --git a/quickjs.h b/quickjs.h
index f6bfdb0..db29372 100644
--- a/quickjs.h
+++ b/quickjs.h
@@ -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);
diff --git a/repl.js b/repl.js
index a5f95f5..484269e 100644
--- a/repl.js
+++ b/repl.js
@@ -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 {
diff --git a/test262.conf b/test262.conf
index 4971dd8..17abf1e 100644
--- a/test262.conf
+++ b/test262.conf
@@ -58,6 +58,7 @@ arrow-function
async-functions
async-iteration
Atomics
+Atomics.waitAsync=skip
BigInt
caller
class
diff --git a/test262_errors.txt b/test262_errors.txt
index 024f357..d00d4f1 100644
--- a/test262_errors.txt
+++ b/test262_errors.txt
@@ -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
diff --git a/tests/bjson.c b/tests/bjson.c
index 9631300..8e52741 100644
--- a/tests/bjson.c
+++ b/tests/bjson.c
@@ -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)
diff --git a/tests/microbench.js b/tests/microbench.js
index 0f05584..1c5f52d 100644
--- a/tests/microbench.js
+++ b/tests/microbench.js
@@ -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,
diff --git a/tests/test_bjson.js b/tests/test_bjson.js
index a6196df..fcbb8e6 100644
--- a/tests/test_bjson.js
+++ b/tests/test_bjson.js
@@ -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();
diff --git a/tests/test_builtin.js b/tests/test_builtin.js
index bbb6629..0ce7b8b 100644
--- a/tests/test_builtin.js
+++ b/tests/test_builtin.js
@@ -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()
diff --git a/tests/test_op.js b/tests/test_language.js
index 08656f2..24ddb83 100644
--- a/tests/test_op.js
+++ b/tests/test_language.js
@@ -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();
diff --git a/tests/test_std.js b/tests/test_std.js
index 54917c9..3ea6e34 100644
--- a/tests/test_std.js
+++ b/tests/test_std.js
@@ -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();
diff --git a/tests/test_worker.js b/tests/test_worker.js
new file mode 100644
index 0000000..0a4b3dc
--- /dev/null
+++ b/tests/test_worker.js
@@ -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();