commit 7c312df422572cf867f29a1d80693e8a77f7fb2a
parent 89007660998db0ee55f0ab3b34bb1a84f86fd3c4
Author: bellard <6490144+bellard@users.noreply.github.com>
Date: Sun, 6 Sep 2020 19:10:15 +0200
2020-09-06 release
Diffstat:
26 files changed, 898 insertions(+), 462 deletions(-)
diff --git a/Changelog b/Changelog
@@ -1,3 +1,12 @@
+2020-09-06:
+
+- added logical assignment operators
+- added IsHTMLDDA support
+- faster for-of loops
+- os.Worker now takes a module filename as parameter
+- qjsc: added -D option to compile dynamically loaded modules or workers
+- misc bug fixes
+
2020-07-05:
- modified JS_GetPrototype() to return a live value
diff --git a/TODO b/TODO
@@ -74,5 +74,5 @@ REPL:
Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
-Test262: 30/71095 errors, 870 excluded, 549 skipped
-Test262 commit: 281eb10b2844929a7c0ac04527f5b42ce56509fd
+Test262: 30/71748 errors, 868 excluded, 474 skipped
+Test262 commit: 24c67328062383079ada85f4d253eb0526fd209b
diff --git a/VERSION b/VERSION
@@ -1 +1 @@
-2020-07-05
+2020-09-06
diff --git a/cutils.c b/cutils.c
@@ -258,19 +258,30 @@ int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
return c;
}
switch(c) {
- case 0xc0 ... 0xdf:
+ case 0xc0: case 0xc1: case 0xc2: case 0xc3:
+ case 0xc4: case 0xc5: case 0xc6: case 0xc7:
+ case 0xc8: case 0xc9: case 0xca: case 0xcb:
+ case 0xcc: case 0xcd: case 0xce: case 0xcf:
+ case 0xd0: case 0xd1: case 0xd2: case 0xd3:
+ case 0xd4: case 0xd5: case 0xd6: case 0xd7:
+ case 0xd8: case 0xd9: case 0xda: case 0xdb:
+ case 0xdc: case 0xdd: case 0xde: case 0xdf:
l = 1;
break;
- case 0xe0 ... 0xef:
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3:
+ case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb:
+ case 0xec: case 0xed: case 0xee: case 0xef:
l = 2;
break;
- case 0xf0 ... 0xf7:
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3:
+ case 0xf4: case 0xf5: case 0xf6: case 0xf7:
l = 3;
break;
- case 0xf8 ... 0xfb:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb:
l = 4;
break;
- case 0xfc ... 0xfd:
+ case 0xfc: case 0xfd:
l = 5;
break;
default:
diff --git a/doc/jsbignum.html b/doc/jsbignum.html
@@ -1,8 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
-<!-- Created by GNU Texinfo 6.5, http://www.gnu.org/software/texinfo/ -->
+<!-- Created by GNU Texinfo 6.1, 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">
@@ -10,6 +9,7 @@
<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
Binary files differ.
diff --git a/doc/quickjs.html b/doc/quickjs.html
@@ -1,8 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
-<!-- Created by GNU Texinfo 6.5, http://www.gnu.org/software/texinfo/ -->
+<!-- Created by GNU Texinfo 6.1, 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">
@@ -10,6 +9,7 @@
<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">
<!--
@@ -237,7 +237,7 @@ source is <code>import</code>.
</dd>
<dt><code>--bignum</code></dt>
<dd><p>Enable the bignum extensions: BigDecimal object, BigFloat object and
-the <code>"use bigint"</code> and <code>"use math"</code> directives.
+the <code>"use math"</code> directive.
</p>
</dd>
<dt><code>-I file</code></dt>
@@ -293,6 +293,13 @@ executable file.
<dd><p>Compile as Javascript module (default=autodetect).
</p>
</dd>
+<dt><code>-D module_name</code></dt>
+<dd><p>Compile a dynamically loaded module and its dependencies. This option
+is needed when your code uses the <code>import</code> keyword or the
+<code>os.Worker</code> constructor because the compiler cannot statically
+find the name of the dynamically loaded modules.
+</p>
+</dd>
<dt><code>-M module_name[,cname]</code></dt>
<dd><p>Add initialization code for an external C module. See the
<code>c_module</code> example.
@@ -314,7 +321,7 @@ when the <code>-fno-x</code> options are used.
</dd>
<dt><code>-fbignum</code></dt>
<dd><p>Enable the bignum extensions: BigDecimal object, BigFloat object and
-the <code>"use bigint"</code> and <code>"use math"</code> directives.
+the <code>"use math"</code> directive.
</p>
</dd>
</dl>
@@ -989,13 +996,14 @@ to the timer.
<code>"win32"</code> or <code>"js"</code>.
</p>
</dd>
-<dt><code>Worker(source)</code></dt>
+<dt><code>Worker(module_filename)</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’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>.
+<code>WebWorkers</code>. <code>module_filename</code> is a string specifying the
+module filename which is executed in the newly created thread. As for
+dynamically imported module, it is relative to the current script or
+module path. Threads normally don’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>
diff --git a/doc/quickjs.pdf b/doc/quickjs.pdf
Binary files differ.
diff --git a/doc/quickjs.texi b/doc/quickjs.texi
@@ -120,7 +120,7 @@ Load as ES6 script (default=autodetect).
@item --bignum
Enable the bignum extensions: BigDecimal object, BigFloat object and
-the @code{"use bigint"} and @code{"use math"} directives.
+the @code{"use math"} directive.
@item -I file
@item --include file
@@ -167,6 +167,12 @@ Set the C name of the generated data.
@item -m
Compile as Javascript module (default=autodetect).
+@item -D module_name
+Compile a dynamically loaded module and its dependencies. This option
+is needed when your code uses the @code{import} keyword or the
+@code{os.Worker} constructor because the compiler cannot statically
+find the name of the dynamically loaded modules.
+
@item -M module_name[,cname]
Add initialization code for an external C module. See the
@code{c_module} example.
@@ -184,7 +190,7 @@ Disable selected language features to produce a smaller executable file.
@item -fbignum
Enable the bignum extensions: BigDecimal object, BigFloat object and
-the @code{"use bigint"} and @code{"use math"} directives.
+the @code{"use math"} directive.
@end table
@@ -764,13 +770,14 @@ Cancel a timer.
Return a string representing the platform: @code{"linux"}, @code{"darwin"},
@code{"win32"} or @code{"js"}.
-@item Worker(source)
+@item Worker(module_filename)
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}.
+@code{WebWorkers}. @code{module_filename} is a string specifying the
+module filename which is executed in the newly created thread. As for
+dynamically imported module, it is relative to the current script or
+module path. 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:
diff --git a/libregexp.c b/libregexp.c
@@ -569,7 +569,8 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
}
}
break;
- case '0' ... '7':
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
c -= '0';
if (allow_utf16 == 2) {
/* only accept \0 not followed by digit */
@@ -1410,7 +1411,9 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
}
}
goto normal_char;
- case '1' ... '9':
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
{
const uint8_t *q = ++p;
@@ -1434,7 +1437,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
}
goto normal_char;
}
- return re_parse_error(s, "back reference out of range in reguar expression");
+ return re_parse_error(s, "back reference out of range in regular expression");
}
emit_back_reference:
last_atom_start = s->byte_code.size;
@@ -2533,6 +2536,17 @@ int lre_get_flags(const uint8_t *bc_buf)
return bc_buf[RE_HEADER_FLAGS];
}
+/* Return NULL if no group names. Otherwise, return a pointer to
+ 'capture_count - 1' zero terminated UTF-8 strings. */
+const char *lre_get_groupnames(const uint8_t *bc_buf)
+{
+ uint32_t re_bytecode_len;
+ if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0)
+ return NULL;
+ re_bytecode_len = get_u32(bc_buf + 3);
+ return (const char *)(bc_buf + 7 + re_bytecode_len);
+}
+
#ifdef TEST
BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
diff --git a/libregexp.h b/libregexp.h
@@ -44,6 +44,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
void *opaque);
int lre_get_capture_count(const uint8_t *bc_buf);
int lre_get_flags(const uint8_t *bc_buf);
+const char *lre_get_groupnames(const uint8_t *bc_buf);
int lre_exec(uint8_t **capture,
const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
int cbuf_type, void *opaque);
diff --git a/libunicode.c b/libunicode.c
@@ -527,7 +527,13 @@ static int unicode_decomp_entry(uint32_t *res, uint32_t c,
} else {
d = unicode_decomp_data + unicode_decomp_table2[idx];
switch(type) {
- case DECOMP_TYPE_L1 ... DECOMP_TYPE_L7:
+ case DECOMP_TYPE_L1:
+ case DECOMP_TYPE_L2:
+ case DECOMP_TYPE_L3:
+ case DECOMP_TYPE_L4:
+ case DECOMP_TYPE_L5:
+ case DECOMP_TYPE_L6:
+ case DECOMP_TYPE_L7:
l = type - DECOMP_TYPE_L1 + 1;
d += (c - code) * l * 2;
for(i = 0; i < l; i++) {
@@ -535,7 +541,8 @@ static int unicode_decomp_entry(uint32_t *res, uint32_t c,
return 0;
}
return l;
- case DECOMP_TYPE_LL1 ... DECOMP_TYPE_LL2:
+ case DECOMP_TYPE_LL1:
+ case DECOMP_TYPE_LL2:
{
uint32_t k, p;
l = type - DECOMP_TYPE_LL1 + 1;
@@ -551,7 +558,11 @@ static int unicode_decomp_entry(uint32_t *res, uint32_t c,
}
}
return l;
- case DECOMP_TYPE_S1 ... DECOMP_TYPE_S5:
+ case DECOMP_TYPE_S1:
+ case DECOMP_TYPE_S2:
+ case DECOMP_TYPE_S3:
+ case DECOMP_TYPE_S4:
+ case DECOMP_TYPE_S5:
l = type - DECOMP_TYPE_S1 + 1;
d += (c - code) * l;
for(i = 0; i < l; i++) {
@@ -582,7 +593,14 @@ static int unicode_decomp_entry(uint32_t *res, uint32_t c,
case DECOMP_TYPE_B18:
l = 18;
goto decomp_type_b;
- case DECOMP_TYPE_B1 ... DECOMP_TYPE_B8:
+ case DECOMP_TYPE_B1:
+ case DECOMP_TYPE_B2:
+ case DECOMP_TYPE_B3:
+ case DECOMP_TYPE_B4:
+ case DECOMP_TYPE_B5:
+ case DECOMP_TYPE_B6:
+ case DECOMP_TYPE_B7:
+ case DECOMP_TYPE_B8:
l = type - DECOMP_TYPE_B1 + 1;
decomp_type_b:
{
diff --git a/qjs.c b/qjs.c
@@ -46,6 +46,7 @@ extern const uint32_t qjsc_repl_size;
#ifdef CONFIG_BIGNUM
extern const uint8_t qjsc_qjscalc[];
extern const uint32_t qjsc_qjscalc_size;
+static int bignum_ext;
#endif
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
@@ -101,6 +102,27 @@ static int eval_file(JSContext *ctx, const char *filename, int module)
return ret;
}
+/* also used to initialize the worker context */
+static JSContext *JS_NewCustomContext(JSRuntime *rt)
+{
+ JSContext *ctx;
+ ctx = JS_NewContext(rt);
+ if (!ctx)
+ return NULL;
+#ifdef CONFIG_BIGNUM
+ if (bignum_ext) {
+ JS_AddIntrinsicBigFloat(ctx);
+ JS_AddIntrinsicBigDecimal(ctx);
+ JS_AddIntrinsicOperators(ctx);
+ JS_EnableBignumExt(ctx, TRUE);
+ }
+#endif
+ /* system modules */
+ js_init_module_std(ctx, "std");
+ js_init_module_os(ctx, "os");
+ return ctx;
+}
+
#if defined(__APPLE__)
#define MALLOC_OVERHEAD 0
#else
@@ -294,7 +316,7 @@ int main(int argc, char **argv)
char *include_list[32];
int i, include_count = 0;
#ifdef CONFIG_BIGNUM
- int load_jscalc, bignum_ext = 0;
+ int load_jscalc;
#endif
size_t stack_size = 0;
@@ -426,6 +448,9 @@ int main(int argc, char **argv)
}
}
+ if (load_jscalc)
+ bignum_ext = 1;
+
if (trace_memory) {
js_trace_malloc_init(&trace_data);
rt = JS_NewRuntime2(&trace_mf, &trace_data);
@@ -440,22 +465,14 @@ int main(int argc, char **argv)
JS_SetMemoryLimit(rt, memory_limit);
if (stack_size != 0)
JS_SetMaxStackSize(rt, stack_size);
+ js_std_set_worker_new_context_func(JS_NewCustomContext);
js_std_init_handlers(rt);
- ctx = JS_NewContext(rt);
+ ctx = JS_NewCustomContext(rt);
if (!ctx) {
fprintf(stderr, "qjs: cannot allocate JS context\n");
exit(2);
}
-#ifdef CONFIG_BIGNUM
- if (bignum_ext || load_jscalc) {
- JS_AddIntrinsicBigFloat(ctx);
- JS_AddIntrinsicBigDecimal(ctx);
- JS_AddIntrinsicOperators(ctx);
- JS_EnableBignumExt(ctx, TRUE);
- }
-#endif
-
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
@@ -472,10 +489,6 @@ int main(int argc, char **argv)
#endif
js_std_add_helpers(ctx, argc - optind, argv + optind);
- /* system modules */
- js_init_module_std(ctx, "std");
- js_init_module_os(ctx, "os");
-
/* make 'std' and 'os' visible to non module code */
if (load_std) {
const char *str = "import * as std from 'std';\n"
diff --git 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_set_worker_new_context_func(JS_NewCustomContext);\n"
" js_std_init_handlers(rt);\n"
;
@@ -349,6 +350,7 @@ void help(void)
"-o output set the output filename\n"
"-N cname set the C name of the generated data\n"
"-m compile as Javascript module (default=autodetect)\n"
+ "-D module_name compile a dynamically loaded module or worker\n"
"-M module_name[,cname] add initialization code for an external C module\n"
"-x byte swapped output\n"
"-p prefix set the prefix of the generated C names\n"
@@ -494,6 +496,7 @@ int main(int argc, char **argv)
#ifdef CONFIG_BIGNUM
BOOL bignum_ext = FALSE;
#endif
+ namelist_t dynamic_module_list;
out_filename = NULL;
output_type = OUTPUT_EXECUTABLE;
@@ -504,13 +507,14 @@ int main(int argc, char **argv)
verbose = 0;
use_lto = FALSE;
stack_size = 0;
+ memset(&dynamic_module_list, 0, sizeof(dynamic_module_list));
/* 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:S:");
+ c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:D:");
if (c == -1)
break;
switch(c) {
@@ -576,6 +580,9 @@ int main(int argc, char **argv)
namelist_add(&cmodule_list, path, cname, 0);
}
break;
+ case 'D':
+ namelist_add(&dynamic_module_list, optarg, NULL, 0);
+ break;
case 'x':
byte_swap = TRUE;
break;
@@ -656,22 +663,22 @@ int main(int argc, char **argv)
cname = NULL;
}
- if (output_type != OUTPUT_C) {
- 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");
+ for(i = 0; i < dynamic_module_list.count; i++) {
+ if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) {
+ fprintf(stderr, "Could not load dynamic module '%s'\n",
+ dynamic_module_list.array[i].name);
+ exit(1);
}
-
+ }
+
+ if (output_type != OUTPUT_C) {
+ fprintf(fo,
+ "static JSContext *JS_NewCustomContext(JSRuntime *rt)\n"
+ "{\n"
+ " JSContext *ctx = JS_NewContextRaw(rt);\n"
+ " if (!ctx)\n"
+ " return NULL;\n");
/* add the basic objects */
-
fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n");
for(i = 0; i < countof(feature_list); i++) {
if ((feature_bitmap & ((uint64_t)1 << i)) &&
@@ -689,8 +696,8 @@ int main(int argc, char **argv)
" JS_EnableBignumExt(ctx, 1);\n");
}
#endif
- fprintf(fo, " js_std_add_helpers(ctx, argc, argv);\n");
-
+ /* add the precompiled modules (XXX: could modify the module
+ loader instead) */
for(i = 0; i < init_module_list.count; i++) {
namelist_entry_t *e = &init_module_list.array[i];
/* initialize the static C modules */
@@ -702,12 +709,39 @@ int main(int argc, char **argv)
" }\n",
e->short_name, e->short_name, e->name);
}
+ for(i = 0; i < cname_list.count; i++) {
+ namelist_entry_t *e = &cname_list.array[i];
+ if (e->flags) {
+ fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n",
+ e->name, e->name);
+ }
+ }
+ fprintf(fo,
+ " return ctx;\n"
+ "}\n\n");
+
+ fputs(main_c_template1, fo);
+
+ 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");
+ }
+
+ fprintf(fo,
+ " ctx = JS_NewCustomContext(rt);\n"
+ " js_std_add_helpers(ctx, argc, argv);\n");
for(i = 0; i < cname_list.count; i++) {
namelist_entry_t *e = &cname_list.array[i];
- fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, %s);\n",
- e->name, e->name,
- e->flags ? "1" : "0");
+ if (!e->flags) {
+ fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n",
+ e->name, e->name);
+ }
}
fputs(main_c_template2, fo);
}
diff --git a/qjscalc.js b/qjscalc.js
@@ -1826,7 +1826,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
j = emin + i;
if (j == -1) {
if (a[i] != 0)
- throw RangError("cannot represent integ(1/X)");
+ throw RangeError("cannot represent integ(1/X)");
} else {
r[i] = a[i] / (j + 1);
}
@@ -1853,7 +1853,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
log() {
var a = this, r;
if (a.emin != 0)
- throw Range("log argument must have a non zero constant term");
+ throw RangeError("log argument must have a non zero constant term");
r = integ(deriv(a) / a);
/* add the constant term */
r += global.log(a[0]);
diff --git a/quickjs-libc.c b/quickjs-libc.c
@@ -2993,9 +2993,8 @@ typedef struct {
} JSWorkerData;
typedef struct {
- /* source code of the worker */
- char *eval_buf;
- size_t eval_buf_len;
+ char *filename; /* module filename */
+ char *basename; /* module base name */
JSWorkerMessagePipe *recv_pipe, *send_pipe;
} WorkerFuncArgs;
@@ -3005,6 +3004,7 @@ typedef struct {
} JSSABHeader;
static JSClassID js_worker_class_id;
+static JSContext *(*js_worker_new_context_func)(JSRuntime *rt);
static int atomic_add_int(int *ptr, int v)
{
@@ -3136,7 +3136,6 @@ static void *worker_func(void *opaque)
JSRuntime *rt;
JSThreadState *ts;
JSContext *ctx;
- JSValue retval;
rt = JS_NewRuntime();
if (rt == NULL) {
@@ -3145,12 +3144,16 @@ static void *worker_func(void *opaque)
}
js_std_init_handlers(rt);
+ JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
+
/* 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);
+ /* function pointer to avoid linking the whole JS_NewContext() if
+ not needed */
+ ctx = js_worker_new_context_func(rt);
if (ctx == NULL) {
fprintf(stderr, "JS_NewContext failure");
}
@@ -3159,18 +3162,11 @@ static void *worker_func(void *opaque)
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))
+ if (!JS_RunModule(ctx, args->basename, args->filename))
js_std_dump_error(ctx);
- JS_FreeValue(ctx, retval);
+ free(args->filename);
+ free(args->basename);
+ free(args);
js_std_loop(ctx);
@@ -3216,52 +3212,53 @@ 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;
+ WorkerFuncArgs *args = NULL;
pthread_t tid;
pthread_attr_t attr;
JSValue obj = JS_UNDEFINED;
int ret;
+ const char *filename = NULL, *basename;
+ JSAtom basename_atom;
/* 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");
+
+ /* base name, assuming the calling function is a normal JS
+ function */
+ basename_atom = JS_GetScriptOrModuleName(ctx, 1);
+ if (basename_atom == JS_ATOM_NULL) {
+ return JS_ThrowTypeError(ctx, "could not determine calling script or module name");
+ }
+ basename = JS_AtomToCString(ctx, basename_atom);
+ JS_FreeAtom(ctx, basename_atom);
+ if (!basename)
+ goto fail;
- /* script source */
-
- str = JS_ToCStringLen(ctx, &str_len, argv[0]);
- if (!str)
- return JS_EXCEPTION;
+ /* module name */
+ filename = JS_ToCString(ctx, argv[0]);
+ if (!filename)
+ goto fail;
args = malloc(sizeof(*args));
- if (!args) {
- JS_ThrowOutOfMemory(ctx);
- goto fail;
- }
+ if (!args)
+ goto oom_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;
+ args->filename = strdup(filename);
+ args->basename = strdup(basename);
/* ports */
args->recv_pipe = js_new_message_pipe();
if (!args->recv_pipe)
- goto fail;
+ goto oom_fail;
args->send_pipe = js_new_message_pipe();
if (!args->send_pipe)
- goto fail;
+ goto oom_fail;
obj = js_worker_ctor_internal(ctx, new_target,
args->send_pipe, args->recv_pipe);
- if (JS_IsUndefined(obj))
+ if (JS_IsException(obj))
goto fail;
pthread_attr_init(&attr);
@@ -3273,11 +3270,17 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
JS_ThrowTypeError(ctx, "could not create worker");
goto fail;
}
+ JS_FreeCString(ctx, basename);
+ JS_FreeCString(ctx, filename);
return obj;
+ oom_fail:
+ JS_ThrowOutOfMemory(ctx);
fail:
- JS_FreeCString(ctx, str);
+ JS_FreeCString(ctx, basename);
+ JS_FreeCString(ctx, filename);
if (args) {
- free(args->eval_buf);
+ free(args->filename);
+ free(args->basename);
js_free_message_pipe(args->recv_pipe);
js_free_message_pipe(args->send_pipe);
free(args);
@@ -3417,6 +3420,13 @@ static const JSCFunctionListEntry js_worker_proto_funcs[] = {
#endif /* USE_WORKER */
+void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt))
+{
+#ifdef USE_WORKER
+ js_worker_new_context_func = func;
+#endif
+}
+
#if defined(_WIN32)
#define OS_PLATFORM "win32"
#elif defined(__APPLE__)
@@ -3668,6 +3678,12 @@ void js_std_free_handlers(JSRuntime *rt)
free_timer(rt, th);
}
+#ifdef USE_WORKER
+ /* XXX: free port_list ? */
+ js_free_message_pipe(ts->recv_pipe);
+ js_free_message_pipe(ts->send_pipe);
+#endif
+
free(ts);
JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
}
diff --git a/quickjs-libc.h b/quickjs-libc.h
@@ -50,7 +50,8 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason,
JS_BOOL is_handled, void *opaque);
-
+void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));
+
#ifdef __cplusplus
} /* extern "C" { */
#endif
diff --git a/quickjs-opcode.h b/quickjs-opcode.h
@@ -359,7 +359,8 @@ DEF( call3, 1, 1, 1, npopx)
DEF( is_undefined, 1, 1, 1, none)
DEF( is_null, 1, 1, 1, none)
-DEF( is_function, 1, 1, 1, none)
+DEF(typeof_is_undefined, 1, 1, 1, none)
+DEF( typeof_is_function, 1, 1, 1, none)
#endif
#undef DEF
diff --git a/quickjs.c b/quickjs.c
@@ -825,8 +825,8 @@ typedef struct JSShapeProperty {
} JSShapeProperty;
struct JSShape {
- uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1
- before the start of the structure. */
+ /* hash table of size hash_mask + 1 before the start of the
+ structure (see prop_hash_end()). */
JSGCObjectHeader header;
/* true if the shape is inserted in the shape hash table. If not,
JSShape.hash is not valid */
@@ -859,6 +859,7 @@ struct JSObject {
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 tmp_mark : 1; /* used in JS_WriteObjectRec() */
+ uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
uint16_t class_id; /* see JS_CLASS_x */
};
};
@@ -931,7 +932,7 @@ struct JSObject {
/* byte sizes: 40/48/72 */
};
enum {
- JS_ATOM_NULL,
+ __JS_ATOM_NULL = JS_ATOM_NULL,
#define DEF(name, str) JS_ATOM_ ## name,
#include "quickjs-atom.h"
#undef DEF
@@ -4198,9 +4199,14 @@ static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
}
+static inline uint32_t *prop_hash_end(JSShape *sh)
+{
+ return (uint32_t *)sh;
+}
+
static inline void *get_alloc_from_shape(JSShape *sh)
{
- return sh->prop_hash_end - ((intptr_t)sh->prop_hash_mask + 1);
+ return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
}
static inline JSShapeProperty *get_shape_prop(JSShape *sh)
@@ -4311,7 +4317,7 @@ static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
if (proto)
JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
sh->proto = proto;
- memset(sh->prop_hash_end - hash_size, 0, sizeof(sh->prop_hash_end[0]) *
+ memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
hash_size);
sh->prop_hash_mask = hash_size - 1;
sh->prop_size = prop_size;
@@ -4440,13 +4446,13 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
new_hash_mask = new_hash_size - 1;
sh->prop_hash_mask = new_hash_mask;
- memset(sh->prop_hash_end - new_hash_size, 0,
- sizeof(sh->prop_hash_end[0]) * new_hash_size);
+ memset(prop_hash_end(sh) - new_hash_size, 0,
+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);
for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
if (pr->atom != JS_ATOM_NULL) {
h = ((uintptr_t)pr->atom & new_hash_mask);
- pr->hash_next = sh->prop_hash_end[-h - 1];
- sh->prop_hash_end[-h - 1] = i + 1;
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = i + 1;
}
}
js_free(ctx, get_alloc_from_shape(old_sh));
@@ -4500,8 +4506,8 @@ static int compact_properties(JSContext *ctx, JSObject *p)
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);
+ memset(prop_hash_end(sh) - new_hash_size, 0,
+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);
j = 0;
old_pr = old_sh->prop;
@@ -4512,8 +4518,8 @@ static int compact_properties(JSContext *ctx, JSObject *p)
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;
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = j + 1;
prop[j] = prop[i];
j++;
pr++;
@@ -4575,8 +4581,8 @@ static int add_shape_property(JSContext *ctx, JSShape **psh,
/* add in hash table */
hash_mask = sh->prop_hash_mask;
h = atom & hash_mask;
- pr->hash_next = sh->prop_hash_end[-h - 1];
- sh->prop_hash_end[-h - 1] = sh->prop_count;
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = sh->prop_count;
return 0;
}
@@ -4693,6 +4699,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
p->is_constructor = 0;
p->is_uncatchable_error = 0;
p->tmp_mark = 0;
+ p->is_HTMLDDA = 0;
p->first_weak_ref = NULL;
p->u.opaque = NULL;
p->shape = sh;
@@ -4731,7 +4738,19 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
p->prop[0].u.value = JS_UNDEFINED;
break;
case JS_CLASS_ARGUMENTS:
- case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY:
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ case JS_CLASS_FLOAT64_ARRAY:
p->is_exotic = 1;
p->fast_array = 1;
p->u.array.u.ptr = NULL;
@@ -5133,7 +5152,7 @@ static force_inline JSShapeProperty *find_own_property1(JSObject *p,
intptr_t h;
sh = p->shape;
h = (uintptr_t)atom & sh->prop_hash_mask;
- h = sh->prop_hash_end[-h - 1];
+ h = prop_hash_end(sh)[-h - 1];
prop = get_shape_prop(sh);
while (h) {
pr = &prop[h - 1];
@@ -5154,7 +5173,7 @@ static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
intptr_t h;
sh = p->shape;
h = (uintptr_t)atom & sh->prop_hash_mask;
- h = sh->prop_hash_end[-h - 1];
+ h = prop_hash_end(sh)[-h - 1];
prop = get_shape_prop(sh);
while (h) {
pr = &prop[h - 1];
@@ -7994,7 +8013,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
redo:
sh = p->shape;
h1 = atom & sh->prop_hash_mask;
- h = sh->prop_hash_end[-h1 - 1];
+ h = prop_hash_end(sh)[-h1 - 1];
prop = get_shape_prop(sh);
lpr = NULL;
lpr_idx = 0; /* prevent warning */
@@ -8015,7 +8034,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
lpr = get_shape_prop(sh) + lpr_idx;
lpr->hash_next = pr->hash_next;
} else {
- sh->prop_hash_end[-h1 - 1] = pr->hash_next;
+ prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
}
sh->deleted_prop_count++;
/* free the entry */
@@ -8095,15 +8114,16 @@ static int call_setter(JSContext *ctx, JSObject *setter,
}
/* set the array length and remove the array elements if necessary. */
-static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop,
- JSValue val, int flags)
+static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, int flags)
{
uint32_t len, idx, cur_len;
int i, ret;
+ /* Note: this call can reallocate the properties of 'p' */
ret = JS_ToArrayLengthFree(ctx, &len, val);
if (ret)
return -1;
+
if (likely(p->fast_array)) {
uint32_t old_len = p->u.array.count;
if (len < old_len) {
@@ -8112,11 +8132,11 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop,
}
p->u.array.count = len;
}
- prop->u.value = JS_NewUint32(ctx, len);
+ p->prop[0].u.value = JS_NewUint32(ctx, len);
} else {
/* Note: length is always a uint32 because the object is an
array */
- JS_ToUint32(ctx, &cur_len, prop->u.value);
+ JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
if (len < cur_len) {
uint32_t d;
JSShape *sh;
@@ -8366,7 +8386,7 @@ retry:
(JS_PROP_LENGTH | JS_PROP_WRITABLE)) {
assert(p->class_id == JS_CLASS_ARRAY);
assert(prop == JS_ATOM_length);
- return set_array_length(ctx, p, pr, val, flags);
+ return set_array_length(ctx, p, val, flags);
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
@@ -9047,7 +9067,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
}
if (prs->flags & JS_PROP_LENGTH) {
if (flags & JS_PROP_HAS_VALUE) {
- res = set_array_length(ctx, p, pr, JS_DupValue(ctx, val),
+ res = set_array_length(ctx, p, JS_DupValue(ctx, val),
flags);
} else {
res = TRUE;
@@ -9806,6 +9826,24 @@ static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
}
+void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return;
+ p = JS_VALUE_GET_OBJ(obj);
+ p->is_HTMLDDA = TRUE;
+}
+
+static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ return p->is_HTMLDDA;
+}
+
static int JS_ToBoolFree(JSContext *ctx, JSValue val)
{
uint32_t tag = JS_VALUE_GET_TAG(val);
@@ -9843,6 +9881,15 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val)
return ret;
}
#endif
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ BOOL ret;
+ ret = !p->is_HTMLDDA;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ break;
default:
if (JS_TAG_IS_FLOAT64(tag)) {
double d = JS_VALUE_GET_FLOAT64(val);
@@ -11602,7 +11649,19 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
case JS_CLASS_ARGUMENTS:
JS_DumpValueShort(rt, p->u.array.u.values[i]);
break;
- case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY:
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ case JS_CLASS_FLOAT64_ARRAY:
{
int size = 1 << typed_array_size_log2(p->class_id);
const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
@@ -13668,33 +13727,8 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
JS_FreeValue(ctx, op2);
} else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&
(tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {
- /* can use floating point comparison */
- double d1, d2;
- if (tag1 == JS_TAG_FLOAT64) {
- d1 = JS_VALUE_GET_FLOAT64(op1);
- } else {
- d1 = JS_VALUE_GET_INT(op1);
- }
- if (tag2 == JS_TAG_FLOAT64) {
- d2 = JS_VALUE_GET_FLOAT64(op2);
- } else {
- d2 = JS_VALUE_GET_INT(op2);
- }
- switch(op) {
- case OP_lt:
- res = (d1 < d2); /* if NaN return false */
- break;
- case OP_lte:
- res = (d1 <= d2); /* if NaN return false */
- break;
- case OP_gt:
- res = (d1 > d2); /* if NaN return false */
- break;
- default:
- case OP_gte:
- res = (d1 >= d2); /* if NaN return false */
- break;
- }
+ /* fast path for float64/int */
+ goto float64_compare;
} else {
if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
(tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
@@ -13727,20 +13761,51 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
}
}
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_DECIMAL ||
- JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_DECIMAL) {
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
if (res < 0)
goto exception;
- } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_FLOAT ||
- JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_FLOAT) {
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
if (res < 0)
goto exception;
- } else {
+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
if (res < 0)
goto exception;
+ } else {
+ double d1, d2;
+
+ float64_compare:
+ /* can use floating point comparison */
+ if (tag1 == JS_TAG_FLOAT64) {
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ } else {
+ d1 = JS_VALUE_GET_INT(op1);
+ }
+ if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ } else {
+ d2 = JS_VALUE_GET_INT(op2);
+ }
+ switch(op) {
+ case OP_lt:
+ res = (d1 < d2); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = (d1 <= d2); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = (d1 > d2); /* if NaN return false */
+ break;
+ default:
+ case OP_gte:
+ res = (d1 >= d2); /* if NaN return false */
+ break;
+ }
}
}
done:
@@ -13895,9 +13960,17 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
}
goto redo;
} else {
+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
+ if ((JS_IsHTMLDDA(ctx, op1) &&
+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
+ (JS_IsHTMLDDA(ctx, op2) &&
+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
+ res = TRUE;
+ } else {
+ res = FALSE;
+ }
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
- res = FALSE;
}
done:
sp[-2] = JS_NewBool(ctx, res ^ is_neq);
@@ -14348,9 +14421,17 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
}
goto redo;
} else {
+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
+ if ((JS_IsHTMLDDA(ctx, op1) &&
+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
+ (JS_IsHTMLDDA(ctx, op2) &&
+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
+ res = TRUE;
+ } else {
+ res = FALSE;
+ }
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
- res = FALSE;
}
sp[-2] = JS_NewBool(ctx, res ^ is_neq);
return 0;
@@ -14631,7 +14712,7 @@ static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
return 0;
}
-static __exception int js_operator_typeof(JSContext *ctx, JSValue op1)
+static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
{
JSAtom atom;
uint32_t tag;
@@ -14663,10 +14744,16 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1)
atom = JS_ATOM_string;
break;
case JS_TAG_OBJECT:
- if (JS_IsFunction(ctx, op1))
- atom = JS_ATOM_function;
- else
- goto obj_type;
+ {
+ JSObject *p;
+ p = JS_VALUE_GET_OBJ(op1);
+ if (unlikely(p->is_HTMLDDA))
+ atom = JS_ATOM_undefined;
+ else if (JS_IsFunction(ctx, op1))
+ atom = JS_ATOM_function;
+ else
+ goto obj_type;
+ }
break;
case JS_TAG_NULL:
obj_type:
@@ -15231,7 +15318,10 @@ static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
return 0;
}
-/* enum_rec -> enum_rec value done */
+/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
+ objs. If 'done' is true or in case of exception, 'enum_rec' is set
+ to undefined. If 'done' is true, 'value' is always set to
+ undefined. */
static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
{
JSValue value = JS_UNDEFINED;
@@ -15246,8 +15336,12 @@ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
/* replace the iteration object with undefined */
JS_FreeValue(ctx, sp[offset]);
sp[offset] = JS_UNDEFINED;
- if (done < 0)
+ if (done < 0) {
return -1;
+ } else {
+ JS_FreeValue(ctx, value);
+ value = JS_UNDEFINED;
+ }
}
}
sp[0] = value;
@@ -15363,10 +15457,10 @@ static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
{
JSValue iterator, enumobj, method, value;
- int pos, is_array_iterator;
+ int is_array_iterator;
JSValue *arrp;
- uint32_t i, count32;
-
+ uint32_t i, count32, pos;
+
if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
JS_ThrowInternalError(ctx, "invalid index for append");
return -1;
@@ -15399,24 +15493,21 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
if (is_array_iterator
&& JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)
&& js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
- int64_t len;
- /* Handle fast arrays explicitly */
- if (js_get_length64(ctx, &len, sp[-1]))
+ uint32_t len;
+ if (js_get_length32(ctx, &len, sp[-1]))
goto exception;
+ /* if len > count32, the elements >= count32 might be read in
+ the prototypes and might have side effects */
+ if (len != count32)
+ goto general_case;
+ /* Handle fast arrays explicitly */
for (i = 0; i < count32; i++) {
if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
goto exception;
}
- if (len > count32) {
- /* This is not strictly correct because the trailing elements are
- empty instead of undefined. Append undefined entries instead.
- */
- pos += len - count32;
- if (JS_SetProperty(ctx, sp[-3], JS_ATOM_length, JS_NewUint32(ctx, pos)) < 0)
- goto exception;
- }
} else {
+ general_case:
for (;;) {
BOOL done;
value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
@@ -15430,6 +15521,7 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
goto exception;
}
}
+ /* Note: could raise an error if too many elements */
sp[-2] = JS_NewInt32(ctx, pos);
JS_FreeValue(ctx, enumobj);
JS_FreeValue(ctx, method);
@@ -17742,47 +17834,42 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
BREAK;
CASE(OP_add_loc):
{
- JSValue ops[2];
+ JSValue *pv;
int idx;
idx = *pc;
pc += 1;
- ops[0] = var_buf[idx];
- ops[1] = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(ops[0], ops[1]))) {
+ pv = &var_buf[idx];
+ if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) {
int64_t r;
- r = (int64_t)JS_VALUE_GET_INT(ops[0]) + JS_VALUE_GET_INT(ops[1]);
+ r = (int64_t)JS_VALUE_GET_INT(*pv) +
+ JS_VALUE_GET_INT(sp[-1]);
if (unlikely((int)r != r))
goto add_loc_slow;
- var_buf[idx] = JS_NewInt32(ctx, r);
+ *pv = JS_NewInt32(ctx, r);
sp--;
- } else if (JS_VALUE_GET_TAG(ops[0]) == JS_TAG_STRING) {
+ } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {
+ JSValue op1;
+ op1 = sp[-1];
sp--;
- ops[1] = JS_ToPrimitiveFree(ctx, ops[1], HINT_NONE);
- if (JS_IsException(ops[1])) {
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1))
goto exception;
- }
- /* XXX: should not modify the variable in case of
- exception */
- ops[0] = JS_ConcatString(ctx, ops[0], ops[1]);
- if (JS_IsException(ops[0])) {
- var_buf[idx] = JS_UNDEFINED;
+ op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);
+ if (JS_IsException(op1))
goto exception;
- }
- var_buf[idx] = ops[0];
+ set_value(ctx, pv, op1);
} else {
+ JSValue ops[2];
add_loc_slow:
- /* XXX: should not modify the variable in case of
- exception */
- sp--;
/* In case of exception, js_add_slow frees ops[0]
- and ops[1]. */
- /* XXX: change API */
- if (js_add_slow(ctx, ops + 2)) {
- var_buf[idx] = JS_UNDEFINED;
+ and ops[1], so we must duplicate *pv */
+ ops[0] = JS_DupValue(ctx, *pv);
+ ops[1] = sp[-1];
+ sp--;
+ if (js_add_slow(ctx, ops + 2))
goto exception;
- }
- var_buf[idx] = ops[0];
+ set_value(ctx, pv, ops[0]);
}
}
BREAK;
@@ -18427,7 +18514,15 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
} else {
goto free_and_set_false;
}
- CASE(OP_is_function):
+ /* XXX: could merge to a single opcode */
+ CASE(OP_typeof_is_undefined):
+ /* different from OP_is_undefined because of isHTMLDDA */
+ if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
+ goto free_and_set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ CASE(OP_typeof_is_function):
if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
goto free_and_set_true;
} else {
@@ -19656,6 +19751,9 @@ enum {
TOK_MATH_POW_ASSIGN,
#endif
TOK_POW_ASSIGN,
+ TOK_LAND_ASSIGN,
+ TOK_LOR_ASSIGN,
+ TOK_DOUBLE_QUESTION_MARK_ASSIGN,
TOK_DEC,
TOK_INC,
TOK_SHL,
@@ -20015,11 +20113,14 @@ static void free_token(JSParseState *s, JSToken *token)
JS_FreeValue(s->ctx, token->u.regexp.flags);
break;
case TOK_IDENT:
- case TOK_FIRST_KEYWORD ... TOK_LAST_KEYWORD:
case TOK_PRIVATE_NAME:
JS_FreeAtom(s->ctx, token->u.ident.atom);
break;
default:
+ if (token->val >= TOK_FIRST_KEYWORD &&
+ token->val <= TOK_LAST_KEYWORD) {
+ JS_FreeAtom(s->ctx, token->u.ident.atom);
+ }
break;
}
}
@@ -20233,6 +20334,8 @@ static __exception int js_parse_string(JSParseState *s, int sep,
}
if (c == '\\') {
c = *p;
+ /* XXX: need a specific JSON case to avoid
+ accepting invalid escapes */
switch(c) {
case '\0':
if (p >= s->buf_end)
@@ -20256,18 +20359,23 @@ static __exception int js_parse_string(JSParseState *s, int sep,
s->line_num++;
continue;
default:
- if (c >= '0' && c <= '7') {
+ if (c >= '0' && c <= '9') {
if (!s->cur_func)
- goto invalid_octal; /* JSON case */
+ goto invalid_escape; /* JSON case */
if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
goto parse_escape;
if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
p++;
c = '\0';
} else {
- invalid_octal:
- if (do_throw)
- js_parse_error(s, "invalid octal syntax in strict mode");
+ if (c >= '8' || sep == '`') {
+ /* Note: according to ES2021, \8 and \9 are not
+ accepted in strict mode or in templates. */
+ goto invalid_escape;
+ } else {
+ if (do_throw)
+ js_parse_error(s, "octal escape sequences are not allowed in strict mode");
+ }
goto fail;
}
} else if (c >= 0x80) {
@@ -20284,6 +20392,7 @@ static __exception int js_parse_string(JSParseState *s, int sep,
parse_escape:
ret = lre_parse_escape(&p, TRUE);
if (ret == -1) {
+ invalid_escape:
if (do_throw)
js_parse_error(s, "malformed escape sequence in string literal");
goto fail;
@@ -20634,8 +20743,20 @@ static __exception int next_token(JSParseState *s)
}
}
goto def_token;
- case 'a' ... 'z':
- case 'A' ... 'Z':
+ case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f': case 'g': case 'h':
+ case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p':
+ case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D':
+ case 'E': case 'F': case 'G': case 'H':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+ case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
case '_':
case '$':
/* identifier */
@@ -20717,7 +20838,9 @@ static __exception int next_token(JSParseState *s)
goto fail;
}
goto parse_number;
- case '1' ... '9':
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
/* number */
parse_number:
{
@@ -20887,8 +21010,13 @@ static __exception int next_token(JSParseState *s)
p += 2;
s->token.val = TOK_AND_ASSIGN;
} else if (p[1] == '&') {
- p += 2;
- s->token.val = TOK_LAND;
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_LAND_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_LAND;
+ }
} else {
goto def_token;
}
@@ -20934,16 +21062,26 @@ static __exception int next_token(JSParseState *s)
p += 2;
s->token.val = TOK_OR_ASSIGN;
} else if (p[1] == '|') {
- p += 2;
- s->token.val = TOK_LOR;
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_LOR_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_LOR;
+ }
} else {
goto def_token;
}
break;
case '?':
if (p[1] == '?') {
- p += 2;
- s->token.val = TOK_DOUBLE_QUESTION_MARK;
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_DOUBLE_QUESTION_MARK;
+ }
} else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
p += 2;
s->token.val = TOK_QUESTION_MARK_DOT;
@@ -21135,8 +21273,20 @@ static __exception int json_next_token(JSParseState *s)
goto def_token;
}
break;
- case 'a' ... 'z':
- case 'A' ... 'Z':
+ case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f': case 'g': case 'h':
+ case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p':
+ case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D':
+ case 'E': case 'F': case 'G': case 'H':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+ case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
case '_':
case '$':
/* identifier : only pure ascii characters are accepted */
@@ -21161,7 +21311,9 @@ static __exception int json_next_token(JSParseState *s)
if (!is_digit(p[1]))
goto def_token;
goto parse_number;
- case '1' ... '9':
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
/* number */
parse_number:
{
@@ -23267,15 +23419,6 @@ static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
return FALSE;
}
-typedef struct JSLValue {
- int opcode;
- int scope;
- int label;
- int depth;
- int tok;
- JSAtom name;
-} JSLValue;
-
static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
int tok)
@@ -23400,84 +23543,112 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
return 0;
}
-/* if special = TRUE: specific post inc/dec case */
-/* name has a live reference */
+typedef enum {
+ PUT_LVALUE_NOKEEP, /* [depth] v -> */
+ PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently
+ just disable optimizations) */
+ PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */
+ PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */
+ PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */
+} PutLValueEnum;
+
+/* name has a live reference. 'is_let' is only used with opcode =
+ OP_scope_get_var which is never generated by get_lvalue(). */
static void put_lvalue(JSParseState *s, int opcode, int scope,
- JSAtom name, int label, BOOL special)
+ JSAtom name, int label, PutLValueEnum special,
+ BOOL is_let)
{
switch(opcode) {
case OP_get_field:
- if (!special)
- emit_op(s, OP_insert2); /* obj v -> v obj v */
- else
- emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
- emit_op(s, OP_put_field);
- emit_u32(s, name); /* name has refcount */
- break;
case OP_scope_get_private_field:
- if (!special)
+ /* depth = 1 */
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
emit_op(s, OP_insert2); /* obj v -> v obj v */
- else
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
- emit_op(s, OP_scope_put_private_field);
- emit_u32(s, name); /* name has refcount */
- emit_u16(s, scope);
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_swap);
+ break;
+ default:
+ abort();
+ }
break;
case OP_get_array_el:
- if (!special)
- emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
- else
- emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
- emit_op(s, OP_put_array_el);
- break;
case OP_get_ref_value:
- JS_FreeAtom(s->ctx, name);
- emit_label(s, label);
- if (!special)
+ /* depth = 2 */
+ if (opcode == OP_get_ref_value) {
+ JS_FreeAtom(s->ctx, name);
+ emit_label(s, label);
+ }
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ emit_op(s, OP_nop); /* will trigger optimization */
+ break;
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
- else
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
- emit_op(s, OP_put_ref_value);
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_rot3l);
+ break;
+ default:
+ abort();
+ }
break;
case OP_get_super_value:
- if (!special)
+ /* depth = 3 */
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */
- else
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */
- emit_op(s, OP_put_super_value);
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_rot4l);
+ break;
+ default:
+ abort();
+ }
break;
default:
- abort();
+ break;
}
-}
-
-static void put_lvalue_nokeep(JSParseState *s, int opcode, int scope,
- JSAtom name, int label, int var_tok)
-{
+
switch(opcode) {
case OP_scope_get_var: /* val -- */
- emit_op(s, (var_tok == TOK_CONST || var_tok == TOK_LET) ?
- OP_scope_put_var_init : OP_scope_put_var);
+ assert(special == PUT_LVALUE_NOKEEP ||
+ special == PUT_LVALUE_NOKEEP_DEPTH);
+ emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
emit_u32(s, name); /* has refcount */
emit_u16(s, scope);
break;
- case OP_get_field: /* obj val -- */
+ case OP_get_field:
emit_op(s, OP_put_field);
- emit_u32(s, name); /* has refcount */
+ emit_u32(s, name); /* name has refcount */
break;
case OP_scope_get_private_field:
emit_op(s, OP_scope_put_private_field);
- emit_u32(s, name); /* has refcount */
+ emit_u32(s, name); /* name has refcount */
emit_u16(s, scope);
break;
- case OP_get_array_el: /* obj prop val -- */
+ case OP_get_array_el:
emit_op(s, OP_put_array_el);
break;
- case OP_get_ref_value: /* obj prop val -- */
- /* XXX: currently this reference is never optimized */
- JS_FreeAtom(s->ctx, name);
- emit_label(s, label);
- //emit_op(s, OP_nop); /* emit 2 bytes for optimizer */
+ case OP_get_ref_value:
emit_op(s, OP_put_ref_value);
break;
case OP_get_super_value:
@@ -23869,7 +24040,9 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
emit_label(s, label_hasval);
}
/* store value into lvalue object */
- put_lvalue_nokeep(s, opcode, scope, var_name, label_lvalue, tok);
+ put_lvalue(s, opcode, scope, var_name, label_lvalue,
+ PUT_LVALUE_NOKEEP_DEPTH,
+ (tok == TOK_CONST || tok == TOK_LET));
if (s->token.val == '}')
break;
/* accept a trailing comma before the '}' */
@@ -23969,8 +24142,9 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
emit_label(s, label_hasval);
}
/* store value into lvalue object */
- put_lvalue_nokeep(s, opcode, scope, var_name,
- label_lvalue, tok);
+ put_lvalue(s, opcode, scope, var_name,
+ label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,
+ (tok == TOK_CONST || tok == TOK_LET));
}
if (s->token.val == ']')
break;
@@ -24786,7 +24960,8 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag)
if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
return -1;
emit_op(s, OP_dec + op - TOK_DEC);
- put_lvalue(s, opcode, scope, name, label, FALSE);
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,
+ FALSE);
}
break;
case TOK_TYPEOF:
@@ -24834,7 +25009,8 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag)
if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
return -1;
emit_op(s, OP_post_dec + op - TOK_DEC);
- put_lvalue(s, opcode, scope, name, label, TRUE);
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,
+ FALSE);
if (next_token(s))
return -1;
}
@@ -25298,7 +25474,61 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted)
#endif
emit_op(s, op);
}
- put_lvalue(s, opcode, scope, name, label, FALSE);
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
+ } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
+ int label, label1, depth_lvalue, label2;
+
+ if (next_token(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &name, &label,
+ &depth_lvalue, TRUE, op) < 0)
+ return -1;
+
+ emit_op(s, OP_dup);
+ if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)
+ emit_op(s, OP_is_undefined_or_null);
+ label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,
+ -1);
+ emit_op(s, OP_drop);
+
+ if (js_parse_assign_expr(s, in_accepted)) {
+ JS_FreeAtom(s->ctx, name);
+ return -1;
+ }
+
+ if (opcode == OP_get_ref_value && name == name0) {
+ set_object_name(s, name);
+ }
+
+ switch(depth_lvalue) {
+ case 1:
+ emit_op(s, OP_insert2);
+ break;
+ case 2:
+ emit_op(s, OP_insert3);
+ break;
+ case 3:
+ emit_op(s, OP_insert4);
+ break;
+ default:
+ abort();
+ }
+
+ /* XXX: we disable the OP_put_ref_value optimization by not
+ using put_lvalue() otherwise depth_lvalue is not correct */
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,
+ FALSE);
+ label2 = emit_goto(s, OP_goto, -1);
+
+ emit_label(s, label1);
+
+ /* remove the lvalue stack entries */
+ while (depth_lvalue != 0) {
+ emit_op(s, OP_nip);
+ depth_lvalue--;
+ }
+
+ emit_label(s, label2);
}
return 0;
}
@@ -25563,8 +25793,8 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok,
goto var_error;
}
set_object_name(s, name);
- put_lvalue(s, opcode, scope, name1, label, FALSE);
- emit_op(s, OP_drop);
+ put_lvalue(s, opcode, scope, name1, label,
+ PUT_LVALUE_NOKEEP, FALSE);
} else {
if (js_parse_assign_expr(s, in_accepted))
goto var_error;
@@ -25751,29 +25981,14 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))
return -1;
} else {
- int lvalue_label, depth;
+ int lvalue_label;
if (js_parse_postfix_expr(s, TRUE))
return -1;
if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
- &depth, FALSE, TOK_FOR))
+ NULL, FALSE, TOK_FOR))
return -1;
- /* swap value and lvalue object and store it into lvalue object */
- /* enum_rec val [depth] -- enum_rec */
- switch(depth) {
- case 1:
- emit_op(s, OP_swap);
- break;
- case 2:
- emit_op(s, OP_rot3l);
- break;
- case 3:
- emit_op(s, OP_rot4l);
- break;
- default:
- abort();
- }
- put_lvalue_nokeep(s, opcode, scope, var_name, lvalue_label,
- TOK_FOR /* not used */);
+ put_lvalue(s, opcode, scope, var_name, lvalue_label,
+ PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
}
var_name = JS_ATOM_NULL;
}
@@ -26933,32 +27148,20 @@ static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)
/* return NULL in case of exception (e.g. module could not be loaded) */
static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
- JSAtom base_module_name,
- JSAtom module_name1)
+ const char *base_cname,
+ const char *cname1)
{
JSRuntime *rt = ctx->rt;
JSModuleDef *m;
char *cname;
- const char *base_cname, *cname1;
JSAtom module_name;
- base_cname = JS_AtomToCString(ctx, base_module_name);
- if (!base_cname)
- return NULL;
- cname1 = JS_AtomToCString(ctx, module_name1);
- if (!cname1) {
- JS_FreeCString(ctx, base_cname);
- return NULL;
- }
-
if (!rt->module_normalize_func) {
cname = js_default_module_normalize_name(ctx, base_cname, cname1);
} else {
cname = rt->module_normalize_func(ctx, base_cname, cname1,
rt->module_loader_opaque);
}
- JS_FreeCString(ctx, base_cname);
- JS_FreeCString(ctx, cname1);
if (!cname)
return NULL;
@@ -26992,6 +27195,27 @@ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
return m;
}
+static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,
+ JSAtom base_module_name,
+ JSAtom module_name1)
+{
+ const char *base_cname, *cname;
+ JSModuleDef *m;
+
+ base_cname = JS_AtomToCString(ctx, base_module_name);
+ if (!base_cname)
+ return NULL;
+ cname = JS_AtomToCString(ctx, module_name1);
+ if (!cname) {
+ JS_FreeCString(ctx, base_cname);
+ return NULL;
+ }
+ m = js_host_resolve_imported_module(ctx, base_cname, cname);
+ JS_FreeCString(ctx, base_cname);
+ JS_FreeCString(ctx, cname);
+ return m;
+}
+
typedef struct JSResolveEntry {
JSModuleDef *module;
JSAtom name;
@@ -27435,8 +27659,8 @@ static int js_resolve_module(JSContext *ctx, JSModuleDef *m)
/* resolve each requested module */
for(i = 0; i < m->req_module_entries_count; i++) {
JSReqModuleEntry *rme = &m->req_module_entries[i];
- m1 = js_host_resolve_imported_module(ctx, m->module_name,
- rme->module_name);
+ m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,
+ rme->module_name);
if (!m1)
return -1;
rme->module = m1;
@@ -27711,8 +27935,9 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m)
return -1;
}
-/* warning: the returned atom is not allocated */
-static JSAtom js_get_script_or_module_name(JSContext *ctx)
+/* return JS_ATOM_NULL if the name cannot be found. Only works with
+ not striped bytecode functions. */
+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
{
JSStackFrame *sf;
JSFunctionBytecode *b;
@@ -27721,14 +27946,22 @@ static JSAtom js_get_script_or_module_name(JSContext *ctx)
function. It does not work for eval(). Need to add a
ScriptOrModule info in JSFunctionBytecode */
sf = ctx->rt->current_stack_frame;
- assert(sf != NULL);
- assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);
+ if (!sf)
+ return JS_ATOM_NULL;
+ while (n_stack_levels-- > 0) {
+ sf = sf->prev_frame;
+ if (!sf)
+ return JS_ATOM_NULL;
+ }
+ if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
+ return JS_ATOM_NULL;
p = JS_VALUE_GET_OBJ(sf->cur_func);
- assert(js_class_has_bytecode(p->class_id));
+ if (!js_class_has_bytecode(p->class_id))
+ return JS_ATOM_NULL;
b = p->u.func.function_bytecode;
if (!b->has_debug)
return JS_ATOM_NULL;
- return b->debug.filename;
+ return JS_DupAtom(ctx, b->debug.filename);
}
JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
@@ -27755,13 +27988,14 @@ static JSValue js_import_meta(JSContext *ctx)
JSAtom filename;
JSModuleDef *m;
- filename = js_get_script_or_module_name(ctx);
+ filename = JS_GetScriptOrModuleName(ctx, 0);
if (filename == JS_ATOM_NULL)
goto fail;
/* XXX: inefficient, need to add a module or script pointer in
JSFunctionBytecode */
m = js_find_loaded_module(ctx, filename);
+ JS_FreeAtom(ctx, filename);
if (!m) {
fail:
JS_ThrowTypeError(ctx, "import.meta not supported in this context");
@@ -27770,6 +28004,31 @@ static JSValue js_import_meta(JSContext *ctx)
return JS_GetImportMeta(ctx, m);
}
+/* used by os.Worker() and import() */
+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
+ const char *filename)
+{
+ JSModuleDef *m;
+ JSValue ret, func_obj;
+
+ m = js_host_resolve_imported_module(ctx, basename, filename);
+ if (!m)
+ return NULL;
+
+ if (js_resolve_module(ctx, m) < 0) {
+ js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
+ return NULL;
+ }
+
+ /* Evaluate the module code */
+ func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ ret = JS_EvalFunction(ctx, func_obj);
+ if (JS_IsException(ret))
+ return NULL;
+ JS_FreeValue(ctx, ret);
+ return m;
+}
+
static JSValue js_dynamic_import_job(JSContext *ctx,
int argc, JSValueConst *argv)
{
@@ -27777,53 +28036,36 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
JSValueConst basename_val = argv[2];
JSValueConst specifier = argv[3];
JSModuleDef *m;
- JSAtom basename = JS_ATOM_NULL, filename;
- JSValue specifierString, ret, func_obj, err, ns;
+ const char *basename = NULL, *filename;
+ JSValue ret, err, ns;
if (!JS_IsString(basename_val)) {
JS_ThrowTypeError(ctx, "no function filename for import()");
goto exception;
}
- basename = JS_ValueToAtom(ctx, basename_val);
- if (basename == JS_ATOM_NULL)
+ basename = JS_ToCString(ctx, basename_val);
+ if (!basename)
goto exception;
- specifierString = JS_ToString(ctx, specifier);
- if (JS_IsException(specifierString))
- goto exception;
- filename = JS_ValueToAtom(ctx, specifierString);
- JS_FreeValue(ctx, specifierString);
- if (filename == JS_ATOM_NULL)
+ filename = JS_ToCString(ctx, specifier);
+ if (!filename)
goto exception;
- m = js_host_resolve_imported_module(ctx, basename, filename);
- JS_FreeAtom(ctx, filename);
- if (!m) {
- goto exception;
- }
-
- if (js_resolve_module(ctx, m) < 0) {
- js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
- goto exception;
- }
-
- /* Evaluate the module code */
- func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
- ret = JS_EvalFunction(ctx, func_obj);
- if (JS_IsException(ret))
+ m = JS_RunModule(ctx, basename, filename);
+ JS_FreeCString(ctx, filename);
+ if (!m)
goto exception;
- JS_FreeValue(ctx, ret);
/* return the module namespace */
ns = js_get_module_ns(ctx, m);
- if (JS_IsException(ret))
+ if (JS_IsException(ns))
goto exception;
ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
1, (JSValueConst *)&ns);
JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
JS_FreeValue(ctx, ns);
- JS_FreeAtom(ctx, basename);
+ JS_FreeCString(ctx, basename);
return JS_UNDEFINED;
exception:
@@ -27832,7 +28074,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
1, (JSValueConst *)&err);
JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
JS_FreeValue(ctx, err);
- JS_FreeAtom(ctx, basename);
+ JS_FreeCString(ctx, basename);
return JS_UNDEFINED;
}
@@ -27842,11 +28084,12 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
JSValue promise, resolving_funcs[2], basename_val;
JSValueConst args[4];
- basename = js_get_script_or_module_name(ctx);
+ basename = JS_GetScriptOrModuleName(ctx, 0);
if (basename == JS_ATOM_NULL)
basename_val = JS_NULL;
else
basename_val = JS_AtomToValue(ctx, basename);
+ JS_FreeAtom(ctx, basename);
if (JS_IsException(basename_val))
return basename_val;
@@ -28957,7 +29200,7 @@ static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)
return (bc_buf[pos + 1] == OP_put_ref_value &&
(opcode == OP_insert3 ||
opcode == OP_perm4 ||
- //opcode == OP_nop ||
+ opcode == OP_nop ||
opcode == OP_rot3l));
}
@@ -28967,7 +29210,7 @@ static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)
return (bc_buf[pos + 1] == OP_put_ref_value &&
(opcode == OP_insert3 ||
opcode == OP_perm4 ||
- //opcode == OP_nop ||
+ opcode == OP_nop ||
opcode == OP_rot3l));
}
@@ -29049,20 +29292,22 @@ static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
end_pos = label_pos + 2;
op = bc_buf[label_pos];
if (is_strict) {
- switch(op) {
- case OP_insert3:
- op = OP_insert2;
- break;
- case OP_perm4:
- op = OP_perm3;
- break;
- case OP_rot3l:
- op = OP_swap;
- break;
- default:
- abort();
+ if (op != OP_nop) {
+ switch(op) {
+ case OP_insert3:
+ op = OP_insert2;
+ break;
+ case OP_perm4:
+ op = OP_perm3;
+ break;
+ case OP_rot3l:
+ op = OP_swap;
+ break;
+ default:
+ abort();
+ }
+ bc_buf[pos++] = op;
}
- bc_buf[pos++] = op;
} else {
if (op == OP_insert3)
bc_buf[pos++] = OP_dup;
@@ -31567,20 +31812,20 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
}
goto no_change;
+#if SHORT_OPCODES
case OP_typeof:
if (OPTIMIZE) {
/* simplify typeof tests */
if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
if (cc.line_num >= 0) line_num = cc.line_num;
int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
-#if SHORT_OPCODES
int op2 = -1;
switch (cc.atom) {
case JS_ATOM_undefined:
- op2 = OP_is_undefined;
+ op2 = OP_typeof_is_undefined;
break;
case JS_ATOM_function:
- op2 = OP_is_function;
+ op2 = OP_typeof_is_function;
break;
}
if (op2 >= 0) {
@@ -31604,19 +31849,10 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
goto has_label;
}
}
-#endif
- if (cc.atom == JS_ATOM_undefined) {
- /* transform typeof(s) == "undefined" into s === void 0 */
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_undefined);
- dbuf_putc(&bc_out, op1);
- JS_FreeAtom(ctx, cc.atom);
- pos_next = cc.pos;
- break;
- }
}
}
goto no_change;
+#endif
default:
no_change:
@@ -37067,10 +37303,9 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSBoundFunction *bf;
- JSValue func_obj, name1;
+ JSValue func_obj, name1, len_val;
JSObject *p;
- int arg_count, i;
- uint32_t len1;
+ int arg_count, i, ret;
if (check_function(ctx, this_val))
return JS_EXCEPTION;
@@ -37093,6 +37328,44 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
}
p->u.bound_function = bf;
+ /* XXX: the spec could be simpler by only using GetOwnProperty */
+ ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);
+ if (ret < 0)
+ goto exception;
+ if (!ret) {
+ len_val = JS_NewInt32(ctx, 0);
+ } else {
+ len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);
+ if (JS_IsException(len_val))
+ goto exception;
+ if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {
+ /* most common case */
+ int len1 = JS_VALUE_GET_INT(len_val);
+ if (len1 <= arg_count)
+ len1 = 0;
+ else
+ len1 -= arg_count;
+ len_val = JS_NewInt32(ctx, len1);
+ } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {
+ double d = JS_VALUE_GET_FLOAT64(len_val);
+ if (isnan(d)) {
+ d = 0.0;
+ } else {
+ d = trunc(d);
+ if (d <= (double)arg_count)
+ d = 0.0;
+ else
+ d -= (double)arg_count; /* also converts -0 to +0 */
+ }
+ len_val = JS_NewFloat64(ctx, d);
+ } else {
+ JS_FreeValue(ctx, len_val);
+ len_val = JS_NewInt32(ctx, 0);
+ }
+ }
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
+ len_val, JS_PROP_CONFIGURABLE);
+
name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);
if (JS_IsException(name1))
goto exception;
@@ -37105,15 +37378,6 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
goto exception;
JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,
JS_PROP_CONFIGURABLE);
- if (js_get_length32(ctx, &len1, this_val))
- goto exception;
- if (len1 <= (uint32_t)arg_count)
- len1 = 0;
- else
- len1 -= arg_count;
- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
- JS_NewUint32(ctx, len1),
- JS_PROP_CONFIGURABLE);
return func_obj;
exception:
JS_FreeValue(ctx, func_obj);
@@ -40455,10 +40719,6 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
len = p->len;
if (len >= n)
return str;
- if (n > JS_STRING_LEN_MAX) {
- JS_ThrowInternalError(ctx, "string too long");
- goto fail2;
- }
if (argc > 1 && !JS_IsUndefined(argv[1])) {
v = JS_ToString(ctx, argv[1]);
if (JS_IsException(v))
@@ -40473,6 +40733,10 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
p1 = NULL;
}
}
+ if (n > JS_STRING_LEN_MAX) {
+ JS_ThrowInternalError(ctx, "string too long");
+ goto fail2;
+ }
if (string_buffer_init(ctx, b, n))
goto fail3;
n -= len;
@@ -41932,14 +42196,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
if (JS_IsException(obj))
goto fail;
prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
- group_name_ptr = NULL;
- if (re_flags & LRE_FLAG_NAMED_GROUPS) {
- uint32_t re_bytecode_len;
+ group_name_ptr = lre_get_groupnames(re_bytecode);
+ if (group_name_ptr) {
groups = JS_NewObjectProto(ctx, JS_NULL);
if (JS_IsException(groups))
goto fail;
- re_bytecode_len = get_u32(re_bytecode + 3);
- group_name_ptr = (char *)(re_bytecode + 7 + re_bytecode_len);
}
for(i = 0; i < capture_count; i++) {
@@ -43724,6 +43985,7 @@ static const JSCFunctionListEntry js_reflect_funcs[] = {
JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
JS_CFUNC_DEF("set", 3, js_reflect_set ),
JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
};
static const JSCFunctionListEntry js_reflect_obj[] = {
diff --git a/quickjs.h b/quickjs.h
@@ -412,6 +412,8 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s);
void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);
/* atom support */
+#define JS_ATOM_NULL 0
+
JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len);
JSAtom JS_NewAtom(JSContext *ctx, const char *str);
JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n);
@@ -835,6 +837,8 @@ typedef int JSInterruptHandler(JSRuntime *rt, void *opaque);
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
/* if can_block is TRUE, Atomics.wait() can be used */
void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
+/* set the [IsHTMLDDA] internal slot */
+void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj);
typedef struct JSModuleDef JSModuleDef;
@@ -886,6 +890,12 @@ JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
returns a module. */
int JS_ResolveModule(JSContext *ctx, JSValueConst obj);
+/* only exported for os.Worker() */
+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);
+/* only exported for os.Worker() */
+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
+ const char *filename);
+
/* C function definition */
typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
JS_CFUNC_generic,
diff --git a/run-test262.c b/run-test262.c
@@ -743,10 +743,16 @@ static JSValue js_createRealm(JSContext *ctx, JSValue this_val,
return ret;
}
+static JSValue js_IsHTMLDDA(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
+{
+ return JS_NULL;
+}
+
static JSValue add_helpers1(JSContext *ctx)
{
JSValue global_obj;
- JSValue obj262;
+ JSValue obj262, obj;
global_obj = JS_GetGlobalObject(ctx);
@@ -773,6 +779,9 @@ static JSValue add_helpers1(JSContext *ctx)
JS_SetPropertyStr(ctx, obj262, "createRealm",
JS_NewCFunction(ctx, js_createRealm,
"createRealm", 0));
+ obj = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0);
+ JS_SetIsHTMLDDA(ctx, obj);
+ JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj);
JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262));
diff --git a/test262.conf b/test262.conf
@@ -68,6 +68,7 @@ class-methods-private
class-static-fields-public
class-static-fields-private
class-static-methods-private
+cleanupSome=skip
coalesce-expression
computed-property-names
const
@@ -100,10 +101,10 @@ host-gc-required=skip
import.meta
Int32Array
Int8Array
-IsHTMLDDA=skip
+IsHTMLDDA
json-superset
let
-logical-assignment-operators=skip
+logical-assignment-operators
Map
new.target
numeric-separator-literal
diff --git a/tests/test_builtin.js b/tests/test_builtin.js
@@ -277,6 +277,8 @@ function test_string()
assert("aaaa".split("aaaaa", 1), [ "aaaa" ]);
assert(eval('"\0"'), "\0");
+
+ assert("abc".padStart(Infinity, ""), "abc");
}
function test_math()
diff --git a/tests/test_language.js b/tests/test_language.js
@@ -359,6 +359,23 @@ function test_labels()
while (0) x: { break x; };
}
+function test_destructuring()
+{
+ function * g () { return 0; };
+ var [x] = g();
+ assert(x, void 0);
+}
+
+function test_spread()
+{
+ var x;
+ x = [1, 2, ...[3, 4]];
+ assert(x.toString(), "1,2,3,4");
+
+ x = [ ...[ , ] ];
+ assert(Object.getOwnPropertyNames(x).toString(), "0,length");
+}
+
test_op1();
test_cvt();
test_eq();
@@ -373,3 +390,5 @@ test_template_skip();
test_object_literal();
test_regexp_skip();
test_labels();
+test_destructuring();
+test_spread();
diff --git a/tests/test_worker.js b/tests/test_worker.js
@@ -25,38 +25,7 @@ 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();
-`);
+ worker = new os.Worker("./test_worker_module.js");
counter = 0;
worker.onmessage = function (e) {
diff --git a/tests/test_worker_module.js b/tests/test_worker_module.js
@@ -0,0 +1,31 @@
+/* Worker code for test_worker.js */
+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();