diff options
author | bellard <6490144+bellard@users.noreply.github.com> | 2020-11-08 14:30:56 +0100 |
---|---|---|
committer | bellard <6490144+bellard@users.noreply.github.com> | 2020-11-08 14:30:56 +0100 |
commit | b1f67dfc1a7372dd665246cf1c203528e5057e42 (patch) | |
tree | b68346b1d20aa2e709c44a4f7ea902dd3ba6336f | |
parent | 7c312df422572cf867f29a1d80693e8a77f7fb2a (diff) | |
download | quickjs-b1f67dfc1a7372dd665246cf1c203528e5057e42.tar.gz quickjs-b1f67dfc1a7372dd665246cf1c203528e5057e42.zip |
2020-11-08 release
-rw-r--r-- | Changelog | 7 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | TODO | 68 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | doc/jsbignum.html | 734 | ||||
-rw-r--r-- | doc/jsbignum.pdf | bin | 153057 -> 0 bytes | |||
-rw-r--r-- | doc/quickjs.html | 1369 | ||||
-rw-r--r-- | doc/quickjs.pdf | bin | 165855 -> 0 bytes | |||
-rw-r--r-- | doc/quickjs.texi | 12 | ||||
-rw-r--r-- | jscompress.c | 918 | ||||
-rw-r--r-- | libbf.h | 2 | ||||
-rw-r--r-- | libregexp.c | 26 | ||||
-rw-r--r-- | qjscalc.js | 11 | ||||
-rw-r--r-- | quickjs-atom.h | 1 | ||||
-rw-r--r-- | quickjs-libc.c | 95 | ||||
-rw-r--r-- | quickjs-opcode.h | 13 | ||||
-rw-r--r-- | quickjs.c | 2541 | ||||
-rw-r--r-- | quickjs.h | 9 | ||||
-rwxr-xr-x | release.sh | 79 | ||||
-rw-r--r-- | test262.conf | 17 | ||||
-rw-r--r-- | test262_errors.txt | 65 | ||||
-rw-r--r-- | tests/test_builtin.js | 25 | ||||
-rw-r--r-- | tests/test_language.js | 155 |
23 files changed, 1870 insertions, 4294 deletions
@@ -1,3 +1,10 @@ +2020-11-08: + +- improved function parameter initializers +- added std.setenv(), std.unsetenv() and std.getenviron() +- added JS_EvalThis() +- misc bug fixes + 2020-09-06: - added logical assignment operators @@ -28,9 +28,9 @@ endif # Windows cross compilation from Linux #CONFIG_WIN32=y # use link time optimization (smaller and faster executables but slower build) -CONFIG_LTO=y +#CONFIG_LTO=y # consider warnings as errors (for development) -#CONFIG_WERROR=y +CONFIG_WERROR=y # force 32 bit build for some utilities #CONFIG_M32=y @@ -53,7 +53,11 @@ CONFIG_BIGNUM=y OBJDIR=.obj ifdef CONFIG_WIN32 - CROSS_PREFIX=i686-w64-mingw32- + ifdef CONFIG_M32 + CROSS_PREFIX=i686-w64-mingw32- + else + CROSS_PREFIX=x86_64-w64-mingw32- + endif EXE=.exe else CROSS_PREFIX= @@ -281,15 +285,12 @@ $(OBJDIR)/%.check.o: %.c | $(OBJDIR) regexp_test: libregexp.c libunicode.c cutils.c $(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS) -jscompress: jscompress.c - $(CC) $(LDFLAGS) $(CFLAGS) -o $@ jscompress.c - unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c unicode_gen_def.h $(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o clean: rm -f repl.c qjscalc.c out.c - rm -f *.a *.o *.d *~ jscompress unicode_gen regexp_test $(PROGS) + rm -f *.a *.o *.d *~ unicode_gen regexp_test $(PROGS) rm -f hello.c test_fib.c rm -f examples/*.so tests/*.so rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug @@ -1,13 +1,11 @@ -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 ? +Bugs: +- modules: better error handling with cyclic module references + +Misc ideas: +- use custom printf to avoid compatibility issues with floating point numbers +- consistent naming for preprocessor defines - unify coding style and naming conventions - use names from the ECMA spec in library implementation -- modules: if no ".", use a well known module loading path ? -- use JSHoistedDef only for global variables (JSHoistedDef.var_name != JS_ATOM_NULL) -- add index in JSVarDef and is_arg flag to merge args and vars in JSFunctionDef -- replace most JSVarDef flags with var_type enumeration - use byte code emitters with typed arguments (for clarity) - use 2 bytecode DynBufs in JSFunctionDef, one for reading, one for writing and use the same wrappers in all phases @@ -15,19 +13,36 @@ Misc: - use custom timezone support to avoid C library compatibility issues Memory: +- use memory pools for objects, etc? - test border cases for max number of atoms, object properties, string length - add emergency malloc mode for out of memory exceptions. - test all DynBuf memory errors - test all js_realloc memory errors -- bignum: handle memory errors -- use memory pools for objects, etc? - improve JS_ComputeMemoryUsage() with more info -Optimizations: +Built-in standard library: +- BSD sockets +- modules: use realpath in module name normalizer and put it in quickjs-libc +- modules: if no ".", use a well known module loading path ? +- get rid of __loadScript, use more common name + +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 +- use more generic approach to display evaluation results +- improve directive handling: dispatch, colorize, completion... +- save history +- close all predefined methods in repl.js and jscalc.js + +Optimization ideas: - 64-bit atoms in 64-bit mode ? -- use auto-init properties for more global objects +- 64-bit small bigint in 64-bit mode ? - reuse stack slots for disjoint scopes, if strip -- optimize `for of` iterator for built-in array objects - add heuristic to avoid some cycles in closures - small String (0-2 charcodes) with immediate storage - perform static string concatenation at compile time @@ -36,43 +51,20 @@ Optimizations: - optimize `s += a + b`, `s += a.b` and similar simple expressions - ensure string canonical representation and optimise comparisons and hashes? - remove JSObject.first_weak_ref, use bit+context based hashed array for weak references -- optimize function storage with length and name accessors? - property access optimization on the global object, functions, prototypes and special non extensible objects. - create object literals with the correct length by backpatching length argument - remove redundant set_loc_uninitialized/check_uninitialized opcodes - peephole optim: push_atom_value, to_propkey -> push_atom_value - peephole optim: put_loc x, get_loc_check x -> set_loc x -- comparative performance benchmark -- use variable name when throwing uninitialized exception if available - convert slow array to fast array when all properties != length are numeric - optimize destructuring assignments for global and local variables - implement some form of tail-call-optimization - optimize OP_apply - optimize f(...b) -Extensions: -- support more features in [features] section -- add built-in preprocessor in compiler, get rid of jscompress - handle #if, #ifdef, #line, limited support for #define -- get rid of __loadScript, use more common name -- BSD sockets - -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 -- use more generic approach to display evaluation results -- improve directive handling: dispatch, colorize, completion... -- save history -- close all predefined methods in repl.js and jscalc.js - Test262o: 0/11262 errors, 463 excluded Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) -Test262: 30/71748 errors, 868 excluded, 474 skipped -Test262 commit: 24c67328062383079ada85f4d253eb0526fd209b +Result: 51/75119 errors, 899 excluded, 570 skipped +Test262 commit: 1c33fdb0ca60fb9d7392403be769ed0d26209132 @@ -1 +1 @@ -2020-09-06 +2020-11-08 diff --git a/doc/jsbignum.html b/doc/jsbignum.html deleted file mode 100644 index ab31612..0000000 --- a/doc/jsbignum.html +++ /dev/null @@ -1,734 +0,0 @@ -<!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/ --> -<head> -<title>Javascript Bignum Extensions</title> - -<meta name="description" content="Javascript Bignum Extensions"> -<meta name="keywords" content="Javascript Bignum Extensions"> -<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"> -<!-- -a.summary-letter {text-decoration: none} -blockquote.indentedblock {margin-right: 0em} -blockquote.smallindentedblock {margin-right: 0em; font-size: smaller} -blockquote.smallquotation {font-size: smaller} -div.display {margin-left: 3.2em} -div.example {margin-left: 3.2em} -div.lisp {margin-left: 3.2em} -div.smalldisplay {margin-left: 3.2em} -div.smallexample {margin-left: 3.2em} -div.smalllisp {margin-left: 3.2em} -kbd {font-style: oblique} -pre.display {font-family: inherit} -pre.format {font-family: inherit} -pre.menu-comment {font-family: serif} -pre.menu-preformatted {font-family: serif} -pre.smalldisplay {font-family: inherit; font-size: smaller} -pre.smallexample {font-size: smaller} -pre.smallformat {font-family: inherit; font-size: smaller} -pre.smalllisp {font-size: smaller} -span.nolinebreak {white-space: nowrap} -span.roman {font-family: initial; font-weight: normal} -span.sansserif {font-family: sans-serif; font-weight: normal} -ul.no-bullet {list-style: none} ---> -</style> -<meta name="viewport" content="width=device-width, initial-scale=1.0"> - - -</head> - -<body lang="en"> -<h1 class="settitle" align="center">Javascript Bignum Extensions</h1> - -<a name="SEC_Contents"></a> -<h2 class="contents-heading">Table of Contents</h2> - -<div class="contents"> -<ul class="no-bullet"> -<li><a name="toc-Introduction" href="#Introduction">1 Introduction</a></li> -<li><a name="toc-Operator-overloading" href="#Operator-overloading">2 Operator overloading</a></li> -<li><a name="toc-BigInt-extensions" href="#BigInt-extensions">3 BigInt extensions</a></li> -<li><a name="toc-BigFloat" href="#BigFloat">4 BigFloat</a> -<ul class="no-bullet"> - <li><a name="toc-Introduction-1" href="#Introduction-1">4.1 Introduction</a></li> - <li><a name="toc-Floating-point-rounding" href="#Floating-point-rounding">4.2 Floating point rounding</a></li> - <li><a name="toc-Operators" href="#Operators">4.3 Operators</a></li> - <li><a name="toc-BigFloat-literals" href="#BigFloat-literals">4.4 BigFloat literals</a></li> - <li><a name="toc-Builtin-Object-changes" href="#Builtin-Object-changes">4.5 Builtin Object changes</a> - <ul class="no-bullet"> - <li><a name="toc-BigFloat-function" href="#BigFloat-function">4.5.1 <code>BigFloat</code> function</a></li> - <li><a name="toc-BigFloat_002eprototype" href="#BigFloat_002eprototype">4.5.2 <code>BigFloat.prototype</code></a></li> - <li><a name="toc-BigFloatEnv-constructor" href="#BigFloatEnv-constructor">4.5.3 <code>BigFloatEnv</code> constructor</a></li> - </ul></li> -</ul></li> -<li><a name="toc-BigDecimal" href="#BigDecimal">5 BigDecimal</a> -<ul class="no-bullet"> - <li><a name="toc-Operators-1" href="#Operators-1">5.1 Operators</a></li> - <li><a name="toc-BigDecimal-literals" href="#BigDecimal-literals">5.2 BigDecimal literals</a></li> - <li><a name="toc-Builtin-Object-changes-1" href="#Builtin-Object-changes-1">5.3 Builtin Object changes</a> - <ul class="no-bullet"> - <li><a name="toc-The-BigDecimal-function_002e" href="#The-BigDecimal-function_002e">5.3.1 The <code>BigDecimal</code> function.</a></li> - <li><a name="toc-Properties-of-the-BigDecimal-object" href="#Properties-of-the-BigDecimal-object">5.3.2 Properties of the <code>BigDecimal</code> object</a></li> - <li><a name="toc-Properties-of-the-BigDecimal_002eprototype-object" href="#Properties-of-the-BigDecimal_002eprototype-object">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</a></li> - </ul></li> -</ul></li> -<li><a name="toc-Math-mode" href="#Math-mode">6 Math mode</a></li> - -</ul> -</div> - - -<a name="Introduction"></a> -<h2 class="chapter">1 Introduction</h2> - -<p>The Bignum extensions add the following features to the Javascript -language while being 100% backward compatible: -</p> -<ul> -<li> Operator overloading with a dispatch logic inspired from the proposal available at <a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>. - -</li><li> Arbitrarily large floating point numbers (<code>BigFloat</code>) in base 2 using the IEEE 754 semantics. - -</li><li> Arbitrarily large floating point numbers (<code>BigDecimal</code>) in base 10 based on the proposal available at -<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>. - -</li><li> <code>math</code> mode: arbitrarily large integers and floating point numbers are available by default. The integer division and power can be overloaded for example to return a fraction. The modulo operator (<code>%</code>) is defined as the Euclidian -remainder. <code>^</code> is an alias to the power operator -(<code>**</code>). <code>^^</code> is used as the exclusive or operator. - -</li></ul> - -<p>The extensions are independent from each other except the <code>math</code> -mode which relies on BigFloat and operator overloading. -</p> -<a name="Operator-overloading"></a> -<h2 class="chapter">2 Operator overloading</h2> - -<p>Operator overloading is inspired from the proposal available at -<a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>. It -implements the same dispatch logic but finds the operator sets by -looking at the <code>Symbol.operatorSet</code> property in the objects. The -changes were done in order to simplify the implementation. -</p> -<p>More precisely, the following modifications were made: -</p> -<ul> -<li> <code>with operators from</code> is not supported. Operator overloading is always enabled. - -</li><li> The dispatch is not based on a static <code>[[OperatorSet]]</code> field in all instances. Instead, a dynamic lookup of the <code>Symbol.operatorSet</code> property is done. This property is typically added in the prototype of each object. - -</li><li> <code>Operators.create(...dictionaries)</code> is used to create a new OperatorSet object. The <code>Operators</code> function is supported as an helper to be closer to the TC39 proposal. - -</li><li> <code>[]</code> cannot be overloaded. - -</li><li> In math mode, the BigInt division and power operators can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>. - -</li></ul> - -<a name="BigInt-extensions"></a> -<h2 class="chapter">3 BigInt extensions</h2> - -<p>A few properties are added to the BigInt object: -</p> -<dl compact="compact"> -<dt><code>tdiv(a, b)</code></dt> -<dd><p>Return <em>trunc(a/b)</em>. <code>b = 0</code> raises a RangeError -exception. -</p> -</dd> -<dt><code>fdiv(a, b)</code></dt> -<dd><p>Return <em>\lfloor a/b \rfloor</em>. <code>b = 0</code> raises a RangeError -exception. -</p> -</dd> -<dt><code>cdiv(a, b)</code></dt> -<dd><p>Return <em>\lceil a/b \rceil</em>. <code>b = 0</code> raises a RangeError -exception. -</p> -</dd> -<dt><code>ediv(a, b)</code></dt> -<dd><p>Return <em>sgn(b) \lfloor a/{|b|} \rfloor</em> (Euclidian -division). <code>b = 0</code> raises a RangeError exception. -</p> -</dd> -<dt><code>tdivrem(a, b)</code></dt> -<dt><code>fdivrem(a, b)</code></dt> -<dt><code>cdivrem(a, b)</code></dt> -<dt><code>edivrem(a, b)</code></dt> -<dd><p>Return an array of two elements. The first element is the quotient, -the second is the remainder. The same rounding is done as the -corresponding division operation. -</p> -</dd> -<dt><code>sqrt(a)</code></dt> -<dd><p>Return <em>\lfloor \sqrt(a) \rfloor</em>. A RangeError exception is -raised if <em>a < 0</em>. -</p> -</dd> -<dt><code>sqrtrem(a)</code></dt> -<dd><p>Return an array of two elements. The first element is <em>\lfloor -\sqrt{a} \rfloor</em>. The second element is <em>a-\lfloor \sqrt{a} -\rfloor^2</em>. A RangeError exception is raised if <em>a < 0</em>. -</p> -</dd> -<dt><code>floorLog2(a)</code></dt> -<dd><p>Return -1 if <em>a \leq 0</em> otherwise return <em>\lfloor \log2(a) \rfloor</em>. -</p> -</dd> -<dt><code>ctz(a)</code></dt> -<dd><p>Return the number of trailing zeros in the two’s complement binary representation of a. Return -1 if <em>a=0</em>. -</p> -</dd> -</dl> - -<a name="BigFloat"></a> -<h2 class="chapter">4 BigFloat</h2> - -<a name="Introduction-1"></a> -<h3 class="section">4.1 Introduction</h3> - -<p>This extension adds the <code>BigFloat</code> primitive type. The -<code>BigFloat</code> type represents floating point numbers in base 2 -with the IEEE 754 semantics. A floating -point number is represented as a sign, mantissa and exponent. The -special values <code>NaN</code>, <code>+/-Infinity</code>, <code>+0</code> and <code>-0</code> -are supported. The mantissa and exponent can have any bit length with -an implementation specific minimum and maximum. -</p> -<a name="Floating-point-rounding"></a> -<h3 class="section">4.2 Floating point rounding</h3> - -<p>Each floating point operation operates with infinite precision and -then rounds the result according to the specified floating point -environment (<code>BigFloatEnv</code> object). The status flags of the -environment are also set according to the result of the operation. -</p> -<p>If no floating point environment is provided, the global floating -point environment is used. -</p> -<p>The rounding mode of the global floating point environment is always -<code>RNDN</code> (“round to nearest with ties to even”)<a name="DOCF1" href="#FOOT1"><sup>1</sup></a>. The status flags of the global environment cannot be -read<a name="DOCF2" href="#FOOT2"><sup>2</sup></a>. The precision of the global environment is -<code>BigFloatEnv.prec</code>. The number of exponent bits of the global -environment is <code>BigFloatEnv.expBits</code>. The global environment -subnormal flag is set to <code>true</code>. -</p> -<p>For example, <code>prec = 53</code> and <code> expBits = 11</code> exactly give -the same precision as the IEEE 754 64 bit floating point format. The -default precision is <code>prec = 113</code> and <code> expBits = 15</code> (IEEE -754 128 bit floating point format). -</p> -<p>The global floating point environment can only be modified temporarily -when calling a function (see <code>BigFloatEnv.setPrec</code>). Hence a -function can change the global floating point environment for its -callees but not for its caller. -</p> -<a name="Operators"></a> -<h3 class="section">4.3 Operators</h3> - -<p>The builtin operators are extended so that a BigFloat is returned if -at least one operand is a BigFloat. The computations are always done -with infinite precision and rounded according to the global floating -point environment. -</p> -<p><code>typeof</code> applied on a <code>BigFloat</code> returns <code>bigfloat</code>. -</p> -<p>BigFloat can be compared with all the other numeric types and the -result follows the expected mathematical relations. -</p> -<p>However, since BigFloat and Number are different types they are never -equal when using the strict comparison operators (e.g. <code>0.0 === -0.0l</code> is false). -</p> -<a name="BigFloat-literals"></a> -<h3 class="section">4.4 BigFloat literals</h3> - -<p>BigFloat literals are floating point numbers with a trailing <code>l</code> -suffix. BigFloat literals have an infinite precision. They are rounded -according to the global floating point environment when they are -evaluated.<a name="DOCF3" href="#FOOT3"><sup>3</sup></a> -</p> -<a name="Builtin-Object-changes"></a> -<h3 class="section">4.5 Builtin Object changes</h3> - -<a name="BigFloat-function"></a> -<h4 class="subsection">4.5.1 <code>BigFloat</code> function</h4> - -<p>The <code>BigFloat</code> function cannot be invoked as a constructor. When -invoked as a function: the parameter is converted to a primitive -type. If the result is a numeric type, it is converted to BigFloat -without rounding. If the result is a string, it is converted to -BigFloat using the precision of the global floating point environment. -</p> -<p><code>BigFloat</code> properties: -</p> -<dl compact="compact"> -<dt><code>LN2</code></dt> -<dt><code>PI</code></dt> -<dd><p>Getter. Return the value of the corresponding mathematical constant -rounded to nearest, ties to even with the current global -precision. The constant values are cached for small precisions. -</p> -</dd> -<dt><code>MIN_VALUE</code></dt> -<dt><code>MAX_VALUE</code></dt> -<dt><code>EPSILON</code></dt> -<dd><p>Getter. Return the minimum, maximum and epsilon <code>BigFloat</code> values -(same definition as the corresponding <code>Number</code> constants). -</p> -</dd> -<dt><code>fpRound(a[, e])</code></dt> -<dd><p>Round the floating point number <code>a</code> according to the floating -point environment <code>e</code> or the global environment if <code>e</code> is -undefined. -</p> -</dd> -<dt><code>parseFloat(a[, radix[, e]])</code></dt> -<dd><p>Parse the string <code>a</code> as a floating point number in radix -<code>radix</code>. The radix is 0 (default) or from 2 to 36. The radix 0 -means radix 10 unless there is a hexadecimal or binary prefix. The -result is rounded according to the floating point environment <code>e</code> -or the global environment if <code>e</code> is undefined. -</p> -</dd> -<dt><code>isFinite(a)</code></dt> -<dd><p>Return true if <code>a</code> is a finite bigfloat. -</p> -</dd> -<dt><code>isNaN(a)</code></dt> -<dd><p>Return true if <code>a</code> is a NaN bigfloat. -</p> -</dd> -<dt><code>add(a, b[, e])</code></dt> -<dt><code>sub(a, b[, e])</code></dt> -<dt><code>mul(a, b[, e])</code></dt> -<dt><code>div(a, b[, e])</code></dt> -<dd><p>Perform the specified floating point operation and round the floating -point number <code>a</code> according to the floating point environment -<code>e</code> or the global environment if <code>e</code> is undefined. If -<code>e</code> is specified, the floating point status flags are updated. -</p> -</dd> -<dt><code>floor(x)</code></dt> -<dt><code>ceil(x)</code></dt> -<dt><code>round(x)</code></dt> -<dt><code>trunc(x)</code></dt> -<dd><p>Round to an integer. No additional rounding is performed. -</p> -</dd> -<dt><code>abs(x)</code></dt> -<dd><p>Return the absolute value of x. No additional rounding is performed. -</p> -</dd> -<dt><code>fmod(x, y[, e])</code></dt> -<dt><code>remainder(x, y[, e])</code></dt> -<dd><p>Floating point remainder. The quotient is truncated to zero (fmod) or -to the nearest integer with ties to even (remainder). <code>e</code> is an -optional floating point environment. -</p> -</dd> -<dt><code>sqrt(x[, e])</code></dt> -<dd><p>Square root. Return a rounded floating point number. <code>e</code> is an -optional floating point environment. -</p> -</dd> -<dt><code>sin(x[, e])</code></dt> -<dt><code>cos(x[, e])</code></dt> -<dt><code>tan(x[, e])</code></dt> -<dt><code>asin(x[, e])</code></dt> -<dt><code>acos(x[, e])</code></dt> -<dt><code>atan(x[, e])</code></dt> -<dt><code>atan2(x, y[, e])</code></dt> -<dt><code>exp(x[, e])</code></dt> -<dt><code>log(x[, e])</code></dt> -<dt><code>pow(x, y[, e])</code></dt> -<dd><p>Transcendental operations. Return a rounded floating point -number. <code>e</code> is an optional floating point environment. -</p> -</dd> -</dl> - -<a name="BigFloat_002eprototype"></a> -<h4 class="subsection">4.5.2 <code>BigFloat.prototype</code></h4> - -<p>The following properties are modified: -</p> -<dl compact="compact"> -<dt><code>valueOf()</code></dt> -<dd><p>Return the bigfloat primitive value corresponding to <code>this</code>. -</p> -</dd> -<dt><code>toString(radix)</code></dt> -<dd> -<p>For floating point numbers: -</p> -<ul> -<li> If the radix is a power of two, the conversion is done with infinite -precision. -</li><li> Otherwise, the number is rounded to nearest with ties to even using -the global precision. It is then converted to string using the minimum -number of digits so that its conversion back to a floating point using -the global precision and round to nearest gives the same number. - -</li></ul> - -<p>The exponent letter is <code>e</code> for base 10, <code>p</code> for bases 2, 8, -16 with a binary exponent and <code>@</code> for the other bases. -</p> -</dd> -<dt><code>toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt> -<dt><code>toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt> -<dt><code>toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt> -<dd><p>Same semantics as the corresponding <code>Number</code> functions with -BigFloats. There is no limit on the accepted precision <code>p</code>. The -rounding mode and radix can be optionally specified. The radix must be -between 2 and 36. -</p> -</dd> -</dl> - -<a name="BigFloatEnv-constructor"></a> -<h4 class="subsection">4.5.3 <code>BigFloatEnv</code> constructor</h4> - -<p>The <code>BigFloatEnv([p, [,rndMode]]</code> constructor cannot be invoked as a -function. The floating point environment contains: -</p> -<ul> -<li> the mantissa precision in bits - -</li><li> the exponent size in bits assuming an IEEE 754 representation; - -</li><li> the subnormal flag (if true, subnormal floating point numbers can -be generated by the floating point operations). - -</li><li> the rounding mode - -</li><li> the floating point status. The status flags can only be set by the floating point operations. They can be reset with <code>BigFloatEnv.prototype.clearStatus()</code> or with the various status flag setters. - -</li></ul> - -<p><code>new BigFloatEnv([p, [,rndMode]]</code> creates a new floating point -environment. The status flags are reset. If no parameter is given the -precision, exponent bits and subnormal flags are copied from the -global floating point environment. Otherwise, the precision is set to -<code>p</code>, the number of exponent bits is set to <code>expBitsMax</code> and the -subnormal flags is set to <code>false</code>. If <code>rndMode</code> is -<code>undefined</code>, the rounding mode is set to <code>RNDN</code>. -</p> -<p><code>BigFloatEnv</code> properties: -</p> -<dl compact="compact"> -<dt><code>prec</code></dt> -<dd><p>Getter. Return the precision in bits of the global floating point -environment. The initial value is <code>113</code>. -</p> -</dd> -<dt><code>expBits</code></dt> -<dd><p>Getter. Return the exponent size in bits of the global floating point -environment assuming an IEEE 754 representation. The initial value is -<code>15</code>. -</p> -</dd> -<dt><code>setPrec(f, p[, e])</code></dt> -<dd><p>Set the precision of the global floating point environment to <code>p</code> -and the exponent size to <code>e</code> then call the function -<code>f</code>. Then the Float precision and exponent size are reset to -their precious value and the return value of <code>f</code> is returned (or -an exception is raised if <code>f</code> raised an exception). If <code>e</code> -is <code>undefined</code> it is set to <code>BigFloatEnv.expBitsMax</code>. -</p> -</dd> -<dt><code>precMin</code></dt> -<dd><p>Read-only integer. Return the minimum allowed precision. Must be at least 2. -</p> -</dd> -<dt><code>precMax</code></dt> -<dd><p>Read-only integer. Return the maximum allowed precision. Must be at least 113. -</p> -</dd> -<dt><code>expBitsMin</code></dt> -<dd><p>Read-only integer. Return the minimum allowed exponent size in -bits. Must be at least 3. -</p> -</dd> -<dt><code>expBitsMax</code></dt> -<dd><p>Read-only integer. Return the maximum allowed exponent size in -bits. Must be at least 15. -</p> -</dd> -<dt><code>RNDN</code></dt> -<dd><p>Read-only integer. Round to nearest, with ties to even rounding mode. -</p> -</dd> -<dt><code>RNDZ</code></dt> -<dd><p>Read-only integer. Round to zero rounding mode. -</p> -</dd> -<dt><code>RNDD</code></dt> -<dd><p>Read-only integer. Round to -Infinity rounding mode. -</p> -</dd> -<dt><code>RNDU</code></dt> -<dd><p>Read-only integer. Round to +Infinity rounding mode. -</p> -</dd> -<dt><code>RNDNA</code></dt> -<dd><p>Read-only integer. Round to nearest, with ties away from zero rounding mode. -</p> -</dd> -<dt><code>RNDA</code></dt> -<dd><p>Read-only integer. Round away from zero rounding mode. -</p> -</dd> -<dt><code>RNDF<a name="DOCF4" href="#FOOT4"><sup>4</sup></a></code></dt> -<dd><p>Read-only integer. Faithful rounding mode. The result is -non-deterministically rounded to -Infinity or +Infinity. This rounding -mode usually gives a faster and deterministic running time for the -floating point operations. -</p> -</dd> -</dl> - -<p><code>BigFloatEnv.prototype</code> properties: -</p> -<dl compact="compact"> -<dt><code>prec</code></dt> -<dd><p>Getter and setter (Integer). Return or set the precision in bits. -</p> -</dd> -<dt><code>expBits</code></dt> -<dd><p>Getter and setter (Integer). Return or set the exponent size in bits -assuming an IEEE 754 representation. -</p> -</dd> -<dt><code>rndMode</code></dt> -<dd><p>Getter and setter (Integer). Return or set the rounding mode. -</p> -</dd> -<dt><code>subnormal</code></dt> -<dd><p>Getter and setter (Boolean). subnormal flag. It is false when -<code>expBits = expBitsMax</code>. -</p> -</dd> -<dt><code>clearStatus()</code></dt> -<dd><p>Clear the status flags. -</p> -</dd> -<dt><code>invalidOperation</code></dt> -<dt><code>divideByZero</code></dt> -<dt><code>overflow</code></dt> -<dt><code>underflow</code></dt> -<dt><code>inexact</code></dt> -<dd><p>Getter and setter (Boolean). Status flags. -</p> -</dd> -</dl> - -<a name="BigDecimal"></a> -<h2 class="chapter">5 BigDecimal</h2> - -<p>This extension adds the <code>BigDecimal</code> primitive type. The -<code>BigDecimal</code> type represents floating point numbers in base -10. It is inspired from the proposal available at -<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>. -</p> -<p>The <code>BigDecimal</code> floating point numbers are always normalized and -finite. There is no concept of <code>-0</code>, <code>Infinity</code> or -<code>NaN</code>. By default, all the computations are done with infinite -precision. -</p> -<a name="Operators-1"></a> -<h3 class="section">5.1 Operators</h3> - -<p>The following builtin operators support BigDecimal: -</p> -<dl compact="compact"> -<dt><code>+</code></dt> -<dt><code>-</code></dt> -<dt><code>*</code></dt> -<dd><p>Both operands must be BigDecimal. The result is computed with infinite -precision. -</p></dd> -<dt><code>%</code></dt> -<dd><p>Both operands must be BigDecimal. The result is computed with infinite -precision. A range error is throws in case of division by zero. -</p> -</dd> -<dt><code>/</code></dt> -<dd><p>Both operands must be BigDecimal. A range error is throws in case of -division by zero or if the result cannot be represented with infinite -precision (use <code>BigDecimal.div</code> to specify the rounding). -</p> -</dd> -<dt><code>**</code></dt> -<dd><p>Both operands must be BigDecimal. The exponent must be a positive -integer. The result is computed with infinite precision. -</p> -</dd> -<dt><code>===</code></dt> -<dd><p>When one of the operand is a BigDecimal, return true if both operands -are a BigDecimal and if they are equal. -</p> -</dd> -<dt><code>==</code></dt> -<dt><code>!=</code></dt> -<dt><code><=</code></dt> -<dt><code>>=</code></dt> -<dt><code><</code></dt> -<dt><code>></code></dt> -<dd> -<p>Numerical comparison. When one of the operand is not a BigDecimal, it is -converted to BigDecimal by using ToString(). Hence comparisons between -Number and BigDecimal do not use the exact mathematical value of the -Number value. -</p> -</dd> -</dl> - -<a name="BigDecimal-literals"></a> -<h3 class="section">5.2 BigDecimal literals</h3> - -<p>BigDecimal literals are decimal floating point numbers with a trailing -<code>m</code> suffix. -</p> -<a name="Builtin-Object-changes-1"></a> -<h3 class="section">5.3 Builtin Object changes</h3> - -<a name="The-BigDecimal-function_002e"></a> -<h4 class="subsection">5.3.1 The <code>BigDecimal</code> function.</h4> - -<p>It returns <code>0m</code> if no parameter is provided. Otherwise the first -parameter is converted to a bigdecimal by using ToString(). Hence -Number values are not converted to their exact numerical value as -BigDecimal. -</p> -<a name="Properties-of-the-BigDecimal-object"></a> -<h4 class="subsection">5.3.2 Properties of the <code>BigDecimal</code> object</h4> - -<dl compact="compact"> -<dt><code>add(a, b[, e])</code></dt> -<dt><code>sub(a, b[, e])</code></dt> -<dt><code>mul(a, b[, e])</code></dt> -<dt><code>div(a, b[, e])</code></dt> -<dt><code>mod(a, b[, e])</code></dt> -<dt><code>sqrt(a, e)</code></dt> -<dt><code>round(a, e)</code></dt> -<dd><p>Perform the specified floating point operation and round the floating -point result according to the rounding object <code>e</code>. If the -rounding object is not present, the operation is executed with -infinite precision. -</p> -<p>For <code>div</code>, a <code>RangeError</code> exception is thrown in case of -division by zero or if the result cannot be represented with infinite -precision if no rounding object is present. -</p> -<p>For <code>sqrt</code>, a range error is thrown if <code>a</code> is less than -zero. -</p> -<p>The rounding object must contain the following properties: -<code>roundingMode</code> is a string specifying the rounding mode -(<code>"floor"</code>, <code>"ceiling"</code>, <code>"down"</code>, <code>"up"</code>, -<code>"half-even"</code>, <code>"half-up"</code>). Either -<code>maximumSignificantDigits</code> or <code>maximumFractionDigits</code> must -be present to specify respectively the number of significant digits -(must be >= 1) or the number of digits after the decimal point (must -be >= 0). -</p> -</dd> -</dl> - -<a name="Properties-of-the-BigDecimal_002eprototype-object"></a> -<h4 class="subsection">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</h4> - -<dl compact="compact"> -<dt><code>valueOf()</code></dt> -<dd><p>Return the bigdecimal primitive value corresponding to <code>this</code>. -</p> -</dd> -<dt><code>toString()</code></dt> -<dd><p>Convert <code>this</code> to a string with infinite precision in base 10. -</p> -</dd> -<dt><code>toPrecision(p, rnd_mode = "half-up")</code></dt> -<dt><code>toFixed(p, rnd_mode = "half-up")</code></dt> -<dt><code>toExponential(p, rnd_mode = "half-up")</code></dt> -<dd><p>Convert the BigDecimal <code>this</code> to string with the specified -precision <code>p</code>. There is no limit on the accepted precision -<code>p</code>. The rounding mode can be optionally -specified. <code>toPrecision</code> outputs either in decimal fixed notation -or in decimal exponential notation with a <code>p</code> digits of -precision. <code>toExponential</code> outputs in decimal exponential -notation with <code>p</code> digits after the decimal point. <code>toFixed</code> -outputs in decimal notation with <code>p</code> digits after the decimal -point. -</p> -</dd> -</dl> - -<a name="Math-mode"></a> -<h2 class="chapter">6 Math mode</h2> - -<p>A new <em>math mode</em> is enabled with the <code>"use math"</code> -directive. It propagates the same way as the <em>strict mode</em>. It is -designed so that arbitrarily large integers and floating point numbers -are available by default. In order to minimize the number of changes -in the Javascript semantics, integers are represented either as Number -or BigInt depending on their magnitude. Floating point numbers are -always represented as BigFloat. -</p> -<p>The following changes are made to the Javascript semantics: -</p> -<ul> -<li> Floating point literals (i.e. number with a decimal point or an exponent) are <code>BigFloat</code> by default (i.e. a <code>l</code> suffix is implied). Hence <code>typeof 1.0 === "bigfloat"</code>. - -</li><li> Integer literals (i.e. numbers without a decimal point or an exponent) with or without the <code>n</code> suffix are <code>BigInt</code> if their value cannot be represented as a safe integer. A safe integer is defined as a integer whose absolute value is smaller or equal to <code>2**53-1</code>. Hence <code>typeof 1 === "number "</code>, <code>typeof 1n === "number"</code> but <code>typeof 9007199254740992 === "bigint" </code>. - -</li><li> All the bigint builtin operators and functions are modified so that their result is returned as a Number if it is a safe integer. Otherwise the result stays a BigInt. - -</li><li> The builtin operators are modified so that they return an exact result (which can be a BigInt) if their operands are safe integers. Operands between Number and BigInt are accepted provided the Number operand is a safe integer. The integer power with a negative exponent returns a BigFloat as result. The integer division returns a BigFloat as result. - -</li><li> The <code>^</code> operator is an alias to the power operator (<code>**</code>). - -</li><li> The power operator (both <code>^</code> and <code>**</code>) grammar is modified so that <code>-2^2</code> is allowed and yields <code>-4</code>. - -</li><li> The logical xor operator is still available with the <code>^^</code> operator. - -</li><li> The modulo operator (<code>%</code>) returns the Euclidian remainder (always positive) instead of the truncated remainder. - -</li><li> The integer division operator can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>. - -</li><li> The integer power operator with a non zero negative exponent can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>. - -</li></ul> - -<div class="footnote"> -<hr> -<h4 class="footnotes-heading">Footnotes</h4> - -<h3><a name="FOOT1" href="#DOCF1">(1)</a></h3> -<p>The -rationale is that the rounding mode changes must always be -explicit.</p> -<h3><a name="FOOT2" href="#DOCF2">(2)</a></h3> -<p>The rationale is to avoid side effects for the built-in -operators.</p> -<h3><a name="FOOT3" href="#DOCF3">(3)</a></h3> -<p>Base 10 floating point literals cannot usually be -exactly represented as base 2 floating point number. In order to -ensure that the literal is represented accurately with the current -precision, it must be evaluated at runtime.</p> -<h3><a name="FOOT4" href="#DOCF4">(4)</a></h3> -<p>Could be removed in case a deterministic behavior for floating point operations is required.</p> -</div> -<hr> - - - -</body> -</html> diff --git a/doc/jsbignum.pdf b/doc/jsbignum.pdf Binary files differdeleted file mode 100644 index 442a8c0..0000000 --- a/doc/jsbignum.pdf +++ /dev/null diff --git a/doc/quickjs.html b/doc/quickjs.html deleted file mode 100644 index e8b3369..0000000 --- a/doc/quickjs.html +++ /dev/null @@ -1,1369 +0,0 @@ -<!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/ --> -<head> -<title>QuickJS Javascript Engine</title> - -<meta name="description" content="QuickJS Javascript Engine"> -<meta name="keywords" content="QuickJS Javascript Engine"> -<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"> -<!-- -a.summary-letter {text-decoration: none} -blockquote.indentedblock {margin-right: 0em} -blockquote.smallindentedblock {margin-right: 0em; font-size: smaller} -blockquote.smallquotation {font-size: smaller} -div.display {margin-left: 3.2em} -div.example {margin-left: 3.2em} -div.lisp {margin-left: 3.2em} -div.smalldisplay {margin-left: 3.2em} -div.smallexample {margin-left: 3.2em} -div.smalllisp {margin-left: 3.2em} -kbd {font-style: oblique} -pre.display {font-family: inherit} -pre.format {font-family: inherit} -pre.menu-comment {font-family: serif} -pre.menu-preformatted {font-family: serif} -pre.smalldisplay {font-family: inherit; font-size: smaller} -pre.smallexample {font-size: smaller} -pre.smallformat {font-family: inherit; font-size: smaller} -pre.smalllisp {font-size: smaller} -span.nolinebreak {white-space: nowrap} -span.roman {font-family: initial; font-weight: normal} -span.sansserif {font-family: sans-serif; font-weight: normal} -ul.no-bullet {list-style: none} ---> -</style> -<meta name="viewport" content="width=device-width, initial-scale=1.0"> - - -</head> - -<body lang="en"> -<h1 class="settitle" align="center">QuickJS Javascript Engine</h1> - -<a name="SEC_Contents"></a> -<h2 class="contents-heading">Table of Contents</h2> - -<div class="contents"> -<ul class="no-bullet"> -<li><a name="toc-Introduction" href="#Introduction">1 Introduction</a> -<ul class="no-bullet"> - <li><a name="toc-Main-Features" href="#Main-Features">1.1 Main Features</a></li> -</ul></li> -<li><a name="toc-Usage" href="#Usage">2 Usage</a> -<ul class="no-bullet"> - <li><a name="toc-Installation" href="#Installation">2.1 Installation</a></li> - <li><a name="toc-Quick-start" href="#Quick-start">2.2 Quick start</a></li> - <li><a name="toc-Command-line-options" href="#Command-line-options">2.3 Command line options</a> - <ul class="no-bullet"> - <li><a name="toc-qjs-interpreter" href="#qjs-interpreter">2.3.1 <code>qjs</code> interpreter</a></li> - <li><a name="toc-qjsc-compiler" href="#qjsc-compiler">2.3.2 <code>qjsc</code> compiler</a></li> - </ul></li> - <li><a name="toc-qjscalc-application" href="#qjscalc-application">2.4 <code>qjscalc</code> application</a></li> - <li><a name="toc-Built_002din-tests" href="#Built_002din-tests">2.5 Built-in tests</a></li> - <li><a name="toc-Test262-_0028ECMAScript-Test-Suite_0029" href="#Test262-_0028ECMAScript-Test-Suite_0029">2.6 Test262 (ECMAScript Test Suite)</a></li> -</ul></li> -<li><a name="toc-Specifications" href="#Specifications">3 Specifications</a> -<ul class="no-bullet"> - <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-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> - <ul class="no-bullet"> - <li><a name="toc-Global-objects" href="#Global-objects">3.3.1 Global objects</a></li> - <li><a name="toc-std-module" href="#std-module">3.3.2 <code>std</code> module</a></li> - <li><a name="toc-os-module" href="#os-module">3.3.3 <code>os</code> module</a></li> - </ul></li> - <li><a name="toc-QuickJS-C-API" href="#QuickJS-C-API">3.4 QuickJS C API</a> - <ul class="no-bullet"> - <li><a name="toc-Runtime-and-contexts" href="#Runtime-and-contexts">3.4.1 Runtime and contexts</a></li> - <li><a name="toc-JSValue" href="#JSValue">3.4.2 JSValue</a></li> - <li><a name="toc-C-functions" href="#C-functions">3.4.3 C functions</a></li> - <li><a name="toc-Exceptions" href="#Exceptions">3.4.4 Exceptions</a></li> - <li><a name="toc-Script-evaluation" href="#Script-evaluation">3.4.5 Script evaluation</a></li> - <li><a name="toc-JS-Classes" href="#JS-Classes">3.4.6 JS Classes</a></li> - <li><a name="toc-C-Modules" href="#C-Modules">3.4.7 C Modules</a></li> - <li><a name="toc-Memory-handling" href="#Memory-handling">3.4.8 Memory handling</a></li> - <li><a name="toc-Execution-timeout-and-interrupts" href="#Execution-timeout-and-interrupts">3.4.9 Execution timeout and interrupts</a></li> - </ul></li> -</ul></li> -<li><a name="toc-Internals" href="#Internals">4 Internals</a> -<ul class="no-bullet"> - <li><a name="toc-Bytecode" href="#Bytecode">4.1 Bytecode</a></li> - <li><a name="toc-Executable-generation" href="#Executable-generation">4.2 Executable generation</a> - <ul class="no-bullet"> - <li><a name="toc-qjsc-compiler-1" href="#qjsc-compiler-1">4.2.1 <code>qjsc</code> compiler</a></li> - <li><a name="toc-Binary-JSON" href="#Binary-JSON">4.2.2 Binary JSON</a></li> - </ul></li> - <li><a name="toc-Runtime" href="#Runtime">4.3 Runtime</a> - <ul class="no-bullet"> - <li><a name="toc-Strings" href="#Strings">4.3.1 Strings</a></li> - <li><a name="toc-Objects" href="#Objects">4.3.2 Objects</a></li> - <li><a name="toc-Atoms" href="#Atoms">4.3.3 Atoms</a></li> - <li><a name="toc-Numbers" href="#Numbers">4.3.4 Numbers</a></li> - <li><a name="toc-Garbage-collection" href="#Garbage-collection">4.3.5 Garbage collection</a></li> - <li><a name="toc-JSValue-1" href="#JSValue-1">4.3.6 JSValue</a></li> - <li><a name="toc-Function-call" href="#Function-call">4.3.7 Function call</a></li> - </ul></li> - <li><a name="toc-RegExp" href="#RegExp">4.4 RegExp</a></li> - <li><a name="toc-Unicode" href="#Unicode">4.5 Unicode</a></li> - <li><a name="toc-BigInt_002c-BigFloat_002c-BigDecimal" href="#BigInt_002c-BigFloat_002c-BigDecimal">4.6 BigInt, BigFloat, BigDecimal</a></li> -</ul></li> -<li><a name="toc-License" href="#License">5 License</a></li> - -</ul> -</div> - - -<a name="Introduction"></a> -<h2 class="chapter">1 Introduction</h2> - -<p>QuickJS is a small and embeddable Javascript engine. It supports the -ES2020 specification -<a name="DOCF1" href="#FOOT1"><sup>1</sup></a> -including modules, asynchronous generators, proxies and BigInt. -</p> -<p>It supports mathematical extensions such as big decimal float float -numbers (BigDecimal), big binary floating point numbers (BigFloat), -and operator overloading. -</p> -<a name="Main-Features"></a> -<h3 class="section">1.1 Main Features</h3> - -<ul> -<li> Small and easily embeddable: just a few C files, no external dependency, 210 KiB of x86 code for a simple “hello world” program. - -</li><li> Fast interpreter with very low startup time: runs the 69000 tests of the ECMAScript Test Suite<a name="DOCF2" href="#FOOT2"><sup>2</sup></a> in about 95 seconds on a single core of a desktop PC. The complete life cycle of a runtime instance completes in less than 300 microseconds. - -</li><li> Almost complete ES2020 support including modules, asynchronous -generators and full Annex B support (legacy web compatibility). Many -features from the upcoming ES2021 specification -<a name="DOCF3" href="#FOOT3"><sup>3</sup></a> are also supported. - -</li><li> Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2020 features. - -</li><li> Compile Javascript sources to executables with no external dependency. - -</li><li> Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal. - -</li><li> Mathematical extensions: BigDecimal, BigFloat, operator overloading, bigint mode, math mode. - -</li><li> Command line interpreter with contextual colorization and completion implemented in Javascript. - -</li><li> Small built-in standard library with C library wrappers. - -</li></ul> - -<a name="Usage"></a> -<h2 class="chapter">2 Usage</h2> - -<a name="Installation"></a> -<h3 class="section">2.1 Installation</h3> - -<p>A Makefile is provided to compile the engine on Linux or MacOS/X. A -preliminary Windows support is available thru cross compilation on a -Linux host with the MingGW tools. -</p> -<p>Edit the top of the <code>Makefile</code> if you wish to select specific -options then run <code>make</code>. -</p> -<p>You can type <code>make install</code> as root if you wish to install the binaries and support files to -<code>/usr/local</code> (this is not necessary to use QuickJS). -</p> -<a name="Quick-start"></a> -<h3 class="section">2.2 Quick start</h3> - -<p><code>qjs</code> is the command line interpreter (Read-Eval-Print Loop). You can pass -Javascript files and/or expressions as arguments to execute them: -</p> -<div class="example"> -<pre class="example">./qjs examples/hello.js -</pre></div> - -<p><code>qjsc</code> is the command line compiler: -</p> -<div class="example"> -<pre class="example">./qjsc -o hello examples/hello.js -./hello -</pre></div> - -<p>generates a <code>hello</code> executable with no external dependency. -</p> -<a name="Command-line-options"></a> -<h3 class="section">2.3 Command line options</h3> - -<a name="qjs-interpreter"></a> -<h4 class="subsection">2.3.1 <code>qjs</code> interpreter</h4> - -<pre class="verbatim">usage: qjs [options] [file [args]] -</pre> -<p>Options are: -</p><dl compact="compact"> -<dt><code>-h</code></dt> -<dt><code>--help</code></dt> -<dd><p>List options. -</p> -</dd> -<dt><code>-e <code>EXPR</code></code></dt> -<dt><code>--eval <code>EXPR</code></code></dt> -<dd><p>Evaluate EXPR. -</p> -</dd> -<dt><code>-i</code></dt> -<dt><code>--interactive</code></dt> -<dd><p>Go to interactive mode (it is not the default when files are provided on the command line). -</p> -</dd> -<dt><code>-m</code></dt> -<dt><code>--module</code></dt> -<dd><p>Load as ES6 module (default=autodetect). A module is autodetected if -the filename extension is <code>.mjs</code> or if the first keyword of the -source is <code>import</code>. -</p> -</dd> -<dt><code>--script</code></dt> -<dd><p>Load as ES6 script (default=autodetect). -</p> -</dd> -<dt><code>--bignum</code></dt> -<dd><p>Enable the bignum extensions: BigDecimal object, BigFloat object and -the <code>"use math"</code> directive. -</p> -</dd> -<dt><code>-I file</code></dt> -<dt><code>--include file</code></dt> -<dd><p>Include an additional file. -</p> -</dd> -</dl> - -<p>Advanced options are: -</p> -<dl compact="compact"> -<dt><code>--std</code></dt> -<dd><p>Make the <code>std</code> and <code>os</code> modules available to the loaded -script even if it is not a module. -</p> -</dd> -<dt><code>-d</code></dt> -<dt><code>--dump</code></dt> -<dd><p>Dump the memory usage stats. -</p> -</dd> -<dt><code>-q</code></dt> -<dt><code>--quit</code></dt> -<dd><p>just instantiate the interpreter and quit. -</p> -</dd> -</dl> - -<a name="qjsc-compiler"></a> -<h4 class="subsection">2.3.2 <code>qjsc</code> compiler</h4> - -<pre class="verbatim">usage: qjsc [options] [files] -</pre> -<p>Options are: -</p><dl compact="compact"> -<dt><code>-c</code></dt> -<dd><p>Only output bytecode in a C file. The default is to output an executable file. -</p></dd> -<dt><code>-e</code></dt> -<dd><p>Output <code>main()</code> and bytecode in a C file. The default is to output an -executable file. -</p></dd> -<dt><code>-o output</code></dt> -<dd><p>Set the output filename (default = <samp>out.c</samp> or <samp>a.out</samp>). -</p> -</dd> -<dt><code>-N cname</code></dt> -<dd><p>Set the C name of the generated data. -</p> -</dd> -<dt><code>-m</code></dt> -<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. -</p> -</dd> -<dt><code>-x</code></dt> -<dd><p>Byte swapped output (only used for cross compilation). -</p> -</dd> -<dt><code>-flto</code></dt> -<dd><p>Use link time optimization. The compilation is slower but the -executable is smaller and faster. This option is automatically set -when the <code>-fno-x</code> options are used. -</p> -</dd> -<dt><code>-fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint]</code></dt> -<dd><p>Disable selected language features to produce a smaller executable file. -</p> -</dd> -<dt><code>-fbignum</code></dt> -<dd><p>Enable the bignum extensions: BigDecimal object, BigFloat object and -the <code>"use math"</code> directive. -</p> -</dd> -</dl> - -<a name="qjscalc-application"></a> -<h3 class="section">2.4 <code>qjscalc</code> application</h3> - -<p>The <code>qjscalc</code> application is a superset of the <code>qjs</code> -command line interpreter implementing a Javascript calculator with -arbitrarily large integer and floating point numbers, fractions, -complex numbers, polynomials and matrices. The source code is in -<samp>qjscalc.js</samp>. More documentation and a web version are available at -<a href="http://numcalc.com">http://numcalc.com</a>. -</p> -<a name="Built_002din-tests"></a> -<h3 class="section">2.5 Built-in tests</h3> - -<p>Run <code>make test</code> to run the few built-in tests included in the -QuickJS archive. -</p> -<a name="Test262-_0028ECMAScript-Test-Suite_0029"></a> -<h3 class="section">2.6 Test262 (ECMAScript Test Suite)</h3> - -<p>A test262 runner is included in the QuickJS archive. The test262 tests -can be installed in the QuickJS source directory with: -</p> -<div class="example"> -<pre class="example">git clone https://github.com/tc39/test262.git test262 -cd test262 -patch -p1 < ../tests/test262.patch -cd .. -</pre></div> - -<p>The patch adds the implementation specific <code>harness</code> functions -and optimizes the inefficient RegExp character classes and Unicode -property escapes tests (the tests themselves are not modified, only a -slow string initialization function is optimized). -</p> -<p>The tests can be run with -</p><div class="example"> -<pre class="example">make test2 -</pre></div> - -<p>The configuration files <code>test262.conf</code> -(resp. <code>test262o.conf</code> for the old ES5.1 tests<a name="DOCF4" href="#FOOT4"><sup>4</sup></a>)) -contain the options to run the various tests. Tests can be excluded -based on features or filename. -</p> -<p>The file <code>test262_errors.txt</code> contains the current list of -errors. The runner displays a message when a new error appears or when -an existing error is corrected or modified. Use the <code>-u</code> option -to update the current list of errors (or <code>make test2-update</code>). -</p> -<p>The file <code>test262_report.txt</code> contains the logs of all the -tests. It is useful to have a clearer analysis of a particular -error. In case of crash, the last line corresponds to the failing -test. -</p> -<p>Use the syntax <code>./run-test262 -c test262.conf -f filename.js</code> to -run a single test. Use the syntax <code>./run-test262 -c test262.conf -N</code> to start testing at test number <code>N</code>. -</p> -<p>For more information, run <code>./run-test262</code> to see the command line -options of the test262 runner. -</p> -<p><code>run-test262</code> accepts the <code>-N</code> option to be invoked from -<code>test262-harness</code><a name="DOCF5" href="#FOOT5"><sup>5</sup></a> -thru <code>eshost</code>. Unless you want to compare QuickJS with other -engines under the same conditions, we do not recommend to run the -tests this way as it is much slower (typically half an hour instead of -about 100 seconds). -</p> -<a name="Specifications"></a> -<h2 class="chapter">3 Specifications</h2> - -<a name="Language-support"></a> -<h3 class="section">3.1 Language support</h3> - -<a name="ES2020-support"></a> -<h4 class="subsection">3.1.1 ES2020 support</h4> - -<p>The ES2020 specification is almost fully supported including the Annex -B (legacy web compatibility) and the Unicode related features. -</p> -<p>The following features are not supported yet: -</p> -<ul> -<li> Tail calls<a name="DOCF6" href="#FOOT6"><sup>6</sup></a> - -</li></ul> - -<a name="ECMA402"></a> -<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.3 Extensions</h4> - -<ul> -<li> The directive <code>"use strip"</code> indicates that the debug information (including the source code of the functions) should not be retained to save memory. As <code>"use strict"</code>, the directive can be global to a script or local to a function. - -</li><li> The first line of a script beginning with <code>#!</code> is ignored. - -</li></ul> - -<a name="Mathematical-extensions"></a> -<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. -</p> -<ul> -<li> <code>BigDecimal</code> support: arbitrary large floating point numbers in base 10. - -</li><li> <code>BigFloat</code> support: arbitrary large floating point numbers in base 2. - -</li><li> Operator overloading. - -</li><li> The directive <code>"use bigint"</code> enables the bigint mode where integers are <code>BigInt</code> by default. - -</li><li> The directive <code>"use math"</code> enables the math mode where the division and power operators on integers produce fractions. Floating point literals are <code>BigFloat</code> by default and integers are <code>BigInt</code> by default. - -</li></ul> - -<a name="Modules"></a> -<h3 class="section">3.2 Modules</h3> - -<p>ES6 modules are fully supported. The default name resolution is the -following: -</p> -<ul> -<li> Module names with a leading <code>.</code> or <code>..</code> are relative -to the current module path. - -</li><li> Module names without a leading <code>.</code> or <code>..</code> are system -modules, such as <code>std</code> or <code>os</code>. - -</li><li> Module names ending with <code>.so</code> are native modules using the -QuickJS C API. - -</li></ul> - -<a name="Standard-library"></a> -<h3 class="section">3.3 Standard library</h3> - -<p>The standard library is included by default in the command line -interpreter. It contains the two modules <code>std</code> and <code>os</code> and -a few global objects. -</p> -<a name="Global-objects"></a> -<h4 class="subsection">3.3.1 Global objects</h4> - -<dl compact="compact"> -<dt><code>scriptArgs</code></dt> -<dd><p>Provides the command line arguments. The first argument is the script name. -</p></dd> -<dt><code>print(...args)</code></dt> -<dd><p>Print the arguments separated by spaces and a trailing newline. -</p></dd> -<dt><code>console.log(...args)</code></dt> -<dd><p>Same as print(). -</p> -</dd> -</dl> - -<a name="std-module"></a> -<h4 class="subsection">3.3.2 <code>std</code> module</h4> - -<p>The <code>std</code> module provides wrappers to the libc <samp>stdlib.h</samp> -and <samp>stdio.h</samp> and a few other utilities. -</p> -<p>Available exports: -</p> -<dl compact="compact"> -<dt><code>exit(n)</code></dt> -<dd><p>Exit the process. -</p> -</dd> -<dt><code>evalScript(str, options = undefined)</code></dt> -<dd><p>Evaluate the string <code>str</code> as a script (global -eval). <code>options</code> is an optional object containing the following -optional properties: -</p> -<dl compact="compact"> -<dt><code>backtrace_barrier</code></dt> -<dd><p>Boolean (default = false). If true, error backtraces do not list the - stack frames below the evalScript. - </p></dd> -</dl> - -</dd> -<dt><code>loadScript(filename)</code></dt> -<dd><p>Evaluate the file <code>filename</code> as a script (global eval). -</p> -</dd> -<dt><code>loadFile(filename)</code></dt> -<dd><p>Load the file <code>filename</code> and return it as a string assuming UTF-8 -encoding. Return <code>null</code> in case of I/O error. -</p> -</dd> -<dt><code>open(filename, flags, errorObj = undefined)</code></dt> -<dd><p>Open a file (wrapper to the libc <code>fopen()</code>). Return the FILE -object or <code>null</code> in case of I/O error. If <code>errorObj</code> is not -undefined, set its <code>errno</code> property to the error code or to 0 if -no error occured. -</p> -</dd> -<dt><code>popen(command, flags, errorObj = undefined)</code></dt> -<dd><p>Open a process by creating a pipe (wrapper to the libc -<code>popen()</code>). Return the FILE -object or <code>null</code> in case of I/O error. If <code>errorObj</code> is not -undefined, set its <code>errno</code> property to the error code or to 0 if -no error occured. -</p> -</dd> -<dt><code>fdopen(fd, flags, errorObj = undefined)</code></dt> -<dd><p>Open a file from a file handle (wrapper to the libc -<code>fdopen()</code>). Return the FILE -object or <code>null</code> in case of I/O error. If <code>errorObj</code> is not -undefined, set its <code>errno</code> property to the error code or to 0 if -no error occured. -</p> -</dd> -<dt><code>tmpfile(errorObj = undefined)</code></dt> -<dd><p>Open a temporary file. Return the FILE -object or <code>null</code> in case of I/O error. If <code>errorObj</code> is not -undefined, set its <code>errno</code> property to the error code or to 0 if -no error occured. -</p> -</dd> -<dt><code>puts(str)</code></dt> -<dd><p>Equivalent to <code>std.out.puts(str)</code>. -</p> -</dd> -<dt><code>printf(fmt, ...args)</code></dt> -<dd><p>Equivalent to <code>std.out.printf(fmt, ...args)</code>. -</p> -</dd> -<dt><code>sprintf(fmt, ...args)</code></dt> -<dd><p>Equivalent to the libc sprintf(). -</p> -</dd> -<dt><code>in</code></dt> -<dt><code>out</code></dt> -<dt><code>err</code></dt> -<dd><p>Wrappers to the libc file <code>stdin</code>, <code>stdout</code>, <code>stderr</code>. -</p> -</dd> -<dt><code>SEEK_SET</code></dt> -<dt><code>SEEK_CUR</code></dt> -<dt><code>SEEK_END</code></dt> -<dd><p>Constants for seek(). -</p> -</dd> -<dt><code>Error</code></dt> -<dd> -<p>Enumeration object containing the integer value of common errors -(additional error codes may be defined): -</p> -<dl compact="compact"> -<dt><code>EINVAL</code></dt> -<dt><code>EIO</code></dt> -<dt><code>EACCES</code></dt> -<dt><code>EEXIST</code></dt> -<dt><code>ENOSPC</code></dt> -<dt><code>ENOSYS</code></dt> -<dt><code>EBUSY</code></dt> -<dt><code>ENOENT</code></dt> -<dt><code>EPERM</code></dt> -<dt><code>EPIPE</code></dt> -</dl> - -</dd> -<dt><code>strerror(errno)</code></dt> -<dd><p>Return a string that describes the error <code>errno</code>. -</p> -</dd> -<dt><code>gc()</code></dt> -<dd><p>Manually invoke the cycle removal algorithm. The cycle removal -algorithm is automatically started when needed, so this function is -useful in case of specific memory constraints or for testing. -</p> -</dd> -<dt><code>getenv(name)</code></dt> -<dd><p>Return the value of the environment variable <code>name</code> or -<code>undefined</code> if it is not defined. -</p> -</dd> -<dt><code>urlGet(url, options = undefined)</code></dt> -<dd> -<p>Download <code>url</code> using the <samp>curl</samp> command line -utility. <code>options</code> is an optional object containing the following -optional properties: -</p> -<dl compact="compact"> -<dt><code>binary</code></dt> -<dd><p>Boolean (default = false). If true, the response is an ArrayBuffer - instead of a string. When a string is returned, the data is assumed - to be UTF-8 encoded. -</p> -</dd> -<dt><code>full</code></dt> -<dd> -<p>Boolean (default = false). If true, return the an object contains - the properties <code>response</code> (response content), - <code>responseHeaders</code> (headers separated by CRLF), <code>status</code> - (status code). <code>response</code> is <code>null</code> is case of protocol or - network error. If <code>full</code> is false, only the response is - returned if the status is between 200 and 299. Otherwise <code>null</code> - is returned. -</p> -</dd> -</dl> - -</dd> -<dt><code>parseExtJSON(str)</code></dt> -<dd> -<p>Parse <code>str</code> using a superset of <code>JSON.parse</code>. The - following extensions are accepted: -</p> -<ul> -<li> Single line and multiline comments - </li><li> unquoted properties (ASCII-only Javascript identifiers) - </li><li> trailing comma in array and object definitions - </li><li> single quoted strings - </li><li> <code>\f</code> and <code>\v</code> are accepted as space characters - </li><li> leading plus in numbers - </li><li> octal (<code>0o</code> prefix) and hexadecimal (<code>0x</code> prefix) numbers - </li></ul> -</dd> -</dl> - -<p>FILE prototype: -</p> -<dl compact="compact"> -<dt><code>close()</code></dt> -<dd><p>Close the file. Return 0 if OK or <code>-errno</code> in case of I/O error. -</p></dd> -<dt><code>puts(str)</code></dt> -<dd><p>Outputs the string with the UTF-8 encoding. -</p></dd> -<dt><code>printf(fmt, ...args)</code></dt> -<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> -<dt><code>seek(offset, whence)</code></dt> -<dd><p>Seek to a give file position (whence is -<code>std.SEEK_*</code>). <code>offset</code> can be a number or a bigint. Return -0 if OK or <code>-errno</code> in case of I/O error. -</p></dd> -<dt><code>tell()</code></dt> -<dd><p>Return the current file position. -</p></dd> -<dt><code>tello()</code></dt> -<dd><p>Return the current file position as a bigint. -</p></dd> -<dt><code>eof()</code></dt> -<dd><p>Return true if end of file. -</p></dd> -<dt><code>fileno()</code></dt> -<dd><p>Return the associated OS handle. -</p></dd> -<dt><code>error()</code></dt> -<dd><p>Return true if there was an error. -</p></dd> -<dt><code>clearerr()</code></dt> -<dd><p>Clear the error indication. -</p> -</dd> -<dt><code>read(buffer, position, length)</code></dt> -<dd><p>Read <code>length</code> bytes from the file to the ArrayBuffer <code>buffer</code> at byte -position <code>position</code> (wrapper to the libc <code>fread</code>). -</p> -</dd> -<dt><code>write(buffer, position, length)</code></dt> -<dd><p>Write <code>length</code> bytes to the file from the ArrayBuffer <code>buffer</code> at byte -position <code>position</code> (wrapper to the libc <code>fread</code>). -</p> -</dd> -<dt><code>getline()</code></dt> -<dd><p>Return the next line from the file, assuming UTF-8 encoding, excluding -the trailing line feed. -</p> -</dd> -<dt><code>readAsString(max_size = undefined)</code></dt> -<dd><p>Read <code>max_size</code> bytes from the file and return them as a string -assuming UTF-8 encoding. If <code>max_size</code> is not present, the file -is read up its end. -</p> -</dd> -<dt><code>getByte()</code></dt> -<dd><p>Return the next byte from the file. Return -1 if the end of file is reached. -</p> -</dd> -<dt><code>putByte(c)</code></dt> -<dd><p>Write one byte to the file. -</p></dd> -</dl> - -<a name="os-module"></a> -<h4 class="subsection">3.3.3 <code>os</code> module</h4> - -<p>The <code>os</code> module provides Operating System specific functions: -</p> -<ul> -<li> low level file access -</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 -error code. -</p> -<p>Available exports: -</p> -<dl compact="compact"> -<dt><code>open(filename, flags, mode = 0o666)</code></dt> -<dd><p>Open a file. Return a handle or < 0 if error. -</p> -</dd> -<dt><code>O_RDONLY</code></dt> -<dt><code>O_WRONLY</code></dt> -<dt><code>O_RDWR</code></dt> -<dt><code>O_APPEND</code></dt> -<dt><code>O_CREAT</code></dt> -<dt><code>O_EXCL</code></dt> -<dt><code>O_TRUNC</code></dt> -<dd><p>POSIX open flags. -</p> -</dd> -<dt><code>O_TEXT</code></dt> -<dd><p>(Windows specific). Open the file in text mode. The default is binary mode. -</p> -</dd> -<dt><code>close(fd)</code></dt> -<dd><p>Close the file handle <code>fd</code>. -</p> -</dd> -<dt><code>seek(fd, offset, whence)</code></dt> -<dd><p>Seek in the file. Use <code>std.SEEK_*</code> for -<code>whence</code>. <code>offset</code> is either a number or a bigint. If -<code>offset</code> is a bigint, a bigint is returned too. -</p> -</dd> -<dt><code>read(fd, buffer, offset, length)</code></dt> -<dd><p>Read <code>length</code> bytes from the file handle <code>fd</code> to the -ArrayBuffer <code>buffer</code> at byte position <code>offset</code>. -Return the number of read bytes or < 0 if error. -</p> -</dd> -<dt><code>write(fd, buffer, offset, length)</code></dt> -<dd><p>Write <code>length</code> bytes to the file handle <code>fd</code> from the -ArrayBuffer <code>buffer</code> at byte position <code>offset</code>. -Return the number of written bytes or < 0 if error. -</p> -</dd> -<dt><code>isatty(fd)</code></dt> -<dd><p>Return <code>true</code> is <code>fd</code> is a TTY (terminal) handle. -</p> -</dd> -<dt><code>ttyGetWinSize(fd)</code></dt> -<dd><p>Return the TTY size as <code>[width, height]</code> or <code>null</code> if not available. -</p> -</dd> -<dt><code>ttySetRaw(fd)</code></dt> -<dd><p>Set the TTY in raw mode. -</p> -</dd> -<dt><code>remove(filename)</code></dt> -<dd><p>Remove a file. Return 0 if OK or <code>-errno</code>. -</p> -</dd> -<dt><code>rename(oldname, newname)</code></dt> -<dd><p>Rename a file. Return 0 if OK or <code>-errno</code>. -</p> -</dd> -<dt><code>realpath(path)</code></dt> -<dd><p>Return <code>[str, err]</code> where <code>str</code> is the canonicalized absolute -pathname of <code>path</code> and <code>err</code> the error code. -</p> -</dd> -<dt><code>getcwd()</code></dt> -<dd><p>Return <code>[str, err]</code> where <code>str</code> is the current working directory -and <code>err</code> the error code. -</p> -</dd> -<dt><code>chdir(path)</code></dt> -<dd><p>Change the current directory. Return 0 if OK or <code>-errno</code>. -</p> -</dd> -<dt><code>mkdir(path, mode = 0o777)</code></dt> -<dd><p>Create a directory at <code>path</code>. Return 0 if OK or <code>-errno</code>. -</p> -</dd> -<dt><code>stat(path)</code></dt> -<dt><code>lstat(path)</code></dt> -<dd> -<p>Return <code>[obj, err]</code> where <code>obj</code> is an object containing the -file status of <code>path</code>. <code>err</code> is the error code. The -following fields are defined in <code>obj</code>: dev, ino, mode, nlink, -uid, gid, rdev, size, blocks, atime, mtime, ctime. The times are -specified in milliseconds since 1970. <code>lstat()</code> is the same as -<code>stat()</code> excepts that it returns information about the link -itself. -</p> -</dd> -<dt><code>S_IFMT</code></dt> -<dt><code>S_IFIFO</code></dt> -<dt><code>S_IFCHR</code></dt> -<dt><code>S_IFDIR</code></dt> -<dt><code>S_IFBLK</code></dt> -<dt><code>S_IFREG</code></dt> -<dt><code>S_IFSOCK</code></dt> -<dt><code>S_IFLNK</code></dt> -<dt><code>S_ISGID</code></dt> -<dt><code>S_ISUID</code></dt> -<dd><p>Constants to interpret the <code>mode</code> property returned by -<code>stat()</code>. They have the same value as in the C system header -<samp>sys/stat.h</samp>. -</p> -</dd> -<dt><code>utimes(path, atime, mtime)</code></dt> -<dd><p>Change the access and modification times of the file <code>path</code>. The -times are specified in milliseconds since 1970. Return 0 if OK or <code>-errno</code>. -</p> -</dd> -<dt><code>symlink(target, linkpath)</code></dt> -<dd><p>Create a link at <code>linkpath</code> containing the string <code>target</code>. Return 0 if OK or <code>-errno</code>. -</p> -</dd> -<dt><code>readlink(path)</code></dt> -<dd><p>Return <code>[str, err]</code> where <code>str</code> is the link target and <code>err</code> -the error code. -</p> -</dd> -<dt><code>readdir(path)</code></dt> -<dd><p>Return <code>[array, err]</code> where <code>array</code> is an array of strings -containing the filenames of the directory <code>path</code>. <code>err</code> is -the error code. -</p> -</dd> -<dt><code>setReadHandler(fd, func)</code></dt> -<dd><p>Add a read handler to the file handle <code>fd</code>. <code>func</code> is called -each time there is data pending for <code>fd</code>. A single read handler -per file handle is supported. Use <code>func = null</code> to remove the -handler. -</p> -</dd> -<dt><code>setWriteHandler(fd, func)</code></dt> -<dd><p>Add a write handler to the file handle <code>fd</code>. <code>func</code> is -called each time data can be written to <code>fd</code>. A single write -handler per file handle is supported. Use <code>func = null</code> to remove -the handler. -</p> -</dd> -<dt><code>signal(signal, func)</code></dt> -<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. Signal handlers can only be defined in the main thread. -</p> -</dd> -<dt><code>SIGINT</code></dt> -<dt><code>SIGABRT</code></dt> -<dt><code>SIGFPE</code></dt> -<dt><code>SIGILL</code></dt> -<dt><code>SIGSEGV</code></dt> -<dt><code>SIGTERM</code></dt> -<dd><p>POSIX signal numbers. -</p> -</dd> -<dt><code>kill(pid, sig)</code></dt> -<dd><p>Send the signal <code>sig</code> to the process <code>pid</code>. -</p> -</dd> -<dt><code>exec(args[, options])</code></dt> -<dd><p>Execute a process with the arguments <code>args</code>. <code>options</code> is an -object containing optional parameters: -</p> -<dl compact="compact"> -<dt><code>block</code></dt> -<dd><p>Boolean (default = true). If true, wait until the process is - terminated. In this case, <code>exec</code> return the exit code if positive - or the negated signal number if the process was interrupted by a - signal. If false, do not block and return the process id of the child. -</p> -</dd> -<dt><code>usePath</code></dt> -<dd><p>Boolean (default = true). If true, the file is searched in the - <code>PATH</code> environment variable. -</p> -</dd> -<dt><code>file</code></dt> -<dd><p>String (default = <code>args[0]</code>). Set the file to be executed. -</p> -</dd> -<dt><code>cwd</code></dt> -<dd><p>String. If present, set the working directory of the new process. -</p> -</dd> -<dt><code>stdin</code></dt> -<dt><code>stdout</code></dt> -<dt><code>stderr</code></dt> -<dd><p>If present, set the handle in the child for stdin, stdout or stderr. -</p> -</dd> -<dt><code>env</code></dt> -<dd><p>Object. If present, set the process environment from the object - key-value pairs. Otherwise use the same environment as the current - process. -</p> -</dd> -<dt><code>uid</code></dt> -<dd><p>Integer. If present, the process uid with <code>setuid</code>. -</p> -</dd> -<dt><code>gid</code></dt> -<dd><p>Integer. If present, the process gid with <code>setgid</code>. -</p> -</dd> -</dl> - -</dd> -<dt><code>waitpid(pid, options)</code></dt> -<dd><p><code>waitpid</code> Unix system call. Return the array <code>[ret, -status]</code>. <code>ret</code> contains <code>-errno</code> in case of error. -</p> -</dd> -<dt><code>WNOHANG</code></dt> -<dd><p>Constant for the <code>options</code> argument of <code>waitpid</code>. -</p> -</dd> -<dt><code>dup(fd)</code></dt> -<dd><p><code>dup</code> Unix system call. -</p> -</dd> -<dt><code>dup2(oldfd, newfd)</code></dt> -<dd><p><code>dup2</code> Unix system call. -</p> -</dd> -<dt><code>pipe()</code></dt> -<dd><p><code>pipe</code> Unix system call. Return two handles as <code>[read_fd, -write_fd]</code> or null in case of error. -</p> -</dd> -<dt><code>sleep(delay_ms)</code></dt> -<dd><p>Sleep during <code>delay_ms</code> milliseconds. -</p> -</dd> -<dt><code>setTimeout(func, delay)</code></dt> -<dd><p>Call the function <code>func</code> after <code>delay</code> ms. Return a handle -to the timer. -</p> -</dd> -<dt><code>clearTimeout(handle)</code></dt> -<dd><p>Cancel a timer. -</p> -</dd> -<dt><code>platform</code></dt> -<dd><p>Return a string representing the platform: <code>"linux"</code>, <code>"darwin"</code>, -<code>"win32"</code> or <code>"js"</code>. -</p> -</dd> -<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>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> -<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> -<h3 class="section">3.4 QuickJS C API</h3> - -<p>The C API was designed to be simple and efficient. The C API is -defined in the header <code>quickjs.h</code>. -</p> -<a name="Runtime-and-contexts"></a> -<h4 class="subsection">3.4.1 Runtime and contexts</h4> - -<p><code>JSRuntime</code> represents a Javascript runtime corresponding to an -object heap. Several runtimes can exist at the same time but they -cannot exchange objects. Inside a given runtime, no multi-threading is -supported. -</p> -<p><code>JSContext</code> represents a Javascript context (or Realm). Each -JSContext has its own global objects and system objects. There can be -several JSContexts per JSRuntime and they can share objects, similar -to frames of the same origin sharing Javascript objects in a -web browser. -</p> -<a name="JSValue"></a> -<h4 class="subsection">3.4.2 JSValue</h4> - -<p><code>JSValue</code> represents a Javascript value which can be a primitive -type or an object. Reference counting is used, so it is important to -explicitly duplicate (<code>JS_DupValue()</code>, increment the reference -count) or free (<code>JS_FreeValue()</code>, decrement the reference count) -JSValues. -</p> -<a name="C-functions"></a> -<h4 class="subsection">3.4.3 C functions</h4> - -<p>C functions can be created with -<code>JS_NewCFunction()</code>. <code>JS_SetPropertyFunctionList()</code> is a -shortcut to easily add functions, setters and getters properties to a -given object. -</p> -<p>Unlike other embedded Javascript engines, there is no implicit stack, -so C functions get their parameters as normal C parameters. As a -general rule, C functions take constant <code>JSValue</code>s as parameters -(so they don’t need to free them) and return a newly allocated (=live) -<code>JSValue</code>. -</p> -<a name="Exceptions"></a> -<h4 class="subsection">3.4.4 Exceptions</h4> - -<p>Exceptions: most C functions can return a Javascript exception. It -must be explicitly tested and handled by the C code. The specific -<code>JSValue</code> <code>JS_EXCEPTION</code> indicates that an exception -occurred. The actual exception object is stored in the -<code>JSContext</code> and can be retrieved with <code>JS_GetException()</code>. -</p> -<a name="Script-evaluation"></a> -<h4 class="subsection">3.4.5 Script evaluation</h4> - -<p>Use <code>JS_Eval()</code> to evaluate a script or module source. -</p> -<p>If the script or module was compiled to bytecode with <code>qjsc</code>, it -can be evaluated by calling <code>js_std_eval_binary()</code>. The advantage -is that no compilation is needed so it is faster and smaller because -the compiler can be removed from the executable if no <code>eval</code> is -required. -</p> -<p>Note: the bytecode format is linked to a given QuickJS -version. Moreover, no security check is done before its -execution. Hence the bytecode should not be loaded from untrusted -sources. That’s why there is no option to output the bytecode to a -binary file in <code>qjsc</code>. -</p> -<a name="JS-Classes"></a> -<h4 class="subsection">3.4.6 JS Classes</h4> - -<p>C opaque data can be attached to a Javascript object. The type of the -C opaque data is determined with the class ID (<code>JSClassID</code>) of -the object. Hence the first step is to register a new class ID and JS -class (<code>JS_NewClassID()</code>, <code>JS_NewClass()</code>). Then you can -create objects of this class with <code>JS_NewObjectClass()</code> and get or -set the C opaque point with -<code>JS_GetOpaque()</code>/<code>JS_SetOpaque()</code>. -</p> -<p>When defining a new JS class, it is possible to declare a finalizer -which is called when the object is destroyed. A <code>gc_mark</code> method -can be provided so that the cycle removal algorithm can find the other -objects referenced by this object. Other methods are available to -define exotic object behaviors. -</p> -<p>The Class ID are globally allocated (i.e. for all runtimes). The -JSClass are allocated per <code>JSRuntime</code>. <code>JS_SetClassProto()</code> -is used to define a prototype for a given class in a given -JSContext. <code>JS_NewObjectClass()</code> sets this prototype in the -created object. -</p> -<p>Examples are available in <samp>quickjs-libc.c</samp>. -</p> -<a name="C-Modules"></a> -<h4 class="subsection">3.4.7 C Modules</h4> - -<p>Native ES6 modules are supported and can be dynamically or statically -linked. Look at the <samp>test_bjson</samp> and <samp>bjson.so</samp> -examples. The standard library <samp>quickjs-libc.c</samp> is also a good example -of a native module. -</p> -<a name="Memory-handling"></a> -<h4 class="subsection">3.4.8 Memory handling</h4> - -<p>Use <code>JS_SetMemoryLimit()</code> to set a global memory allocation limit -to a given JSRuntime. -</p> -<p>Custom memory allocation functions can be provided with -<code>JS_NewRuntime2()</code>. -</p> -<p>The maximum system stack size can be set with <code>JS_SetMaxStackSize()</code>. -</p> -<a name="Execution-timeout-and-interrupts"></a> -<h4 class="subsection">3.4.9 Execution timeout and interrupts</h4> - -<p>Use <code>JS_SetInterruptHandler()</code> to set a callback which is -regularly called by the engine when it is executing code. This -callback can be used to implement an execution timeout. -</p> -<p>It is used by the command line interpreter to implement a -<code>Ctrl-C</code> handler. -</p> -<a name="Internals"></a> -<h2 class="chapter">4 Internals</h2> - -<a name="Bytecode"></a> -<h3 class="section">4.1 Bytecode</h3> - -<p>The compiler generates bytecode directly with no intermediate -representation such as a parse tree, hence it is very fast. Several -optimizations passes are done over the generated bytecode. -</p> -<p>A stack-based bytecode was chosen because it is simple and generates -compact code. -</p> -<p>For each function, the maximum stack size is computed at compile time so that -no runtime stack overflow tests are needed. -</p> -<p>A separate compressed line number table is maintained for the debug -information. -</p> -<p>Access to closure variables is optimized and is almost as fast as local -variables. -</p> -<p>Direct <code>eval</code> in strict mode is optimized. -</p> -<a name="Executable-generation"></a> -<h3 class="section">4.2 Executable generation</h3> - -<a name="qjsc-compiler-1"></a> -<h4 class="subsection">4.2.1 <code>qjsc</code> compiler</h4> - -<p>The <code>qjsc</code> compiler generates C sources from Javascript files. By -default the C sources are compiled with the system compiler -(<code>gcc</code> or <code>clang</code>). -</p> -<p>The generated C source contains the bytecode of the compiled functions -or modules. If a full complete executable is needed, it also -contains a <code>main()</code> function with the necessary C code to initialize the -Javascript engine and to load and execute the compiled functions and -modules. -</p> -<p>Javascript code can be mixed with C modules. -</p> -<p>In order to have smaller executables, specific Javascript features can -be disabled, in particular <code>eval</code> or the regular expressions. The -code removal relies on the Link Time Optimization of the system -compiler. -</p> -<a name="Binary-JSON"></a> -<h4 class="subsection">4.2.2 Binary JSON</h4> - -<p><code>qjsc</code> works by compiling scripts or modules and then serializing -them to a binary format. A subset of this format (without functions or -modules) can be used as binary JSON. The example <samp>test_bjson.js</samp> -shows how to use it. -</p> -<p>Warning: the binary JSON format may change without notice, so it -should not be used to store persistent data. The <samp>test_bjson.js</samp> -example is only used to test the binary object format functions. -</p> -<a name="Runtime"></a> -<h3 class="section">4.3 Runtime</h3> - -<a name="Strings"></a> -<h4 class="subsection">4.3.1 Strings</h4> - -<p>Strings are stored either as an 8 bit or a 16 bit array of -characters. Hence random access to characters is always fast. -</p> -<p>The C API provides functions to convert Javascript Strings to C UTF-8 encoded -strings. The most common case where the Javascript string contains -only ASCII characters involves no copying. -</p> -<a name="Objects"></a> -<h4 class="subsection">4.3.2 Objects</h4> - -<p>The object shapes (object prototype, property names and flags) are shared -between objects to save memory. -</p> -<p>Arrays with no holes (except at the end of the array) are optimized. -</p> -<p>TypedArray accesses are optimized. -</p> -<a name="Atoms"></a> -<h4 class="subsection">4.3.3 Atoms</h4> - -<p>Object property names and some strings are stored as Atoms (unique -strings) to save memory and allow fast comparison. Atoms are -represented as a 32 bit integer. Half of the atom range is reserved for -immediate integer literals from <em>0</em> to <em>2^{31}-1</em>. -</p> -<a name="Numbers"></a> -<h4 class="subsection">4.3.4 Numbers</h4> - -<p>Numbers are represented either as 32-bit signed integers or 64-bit IEEE-754 -floating point values. Most operations have fast paths for the 32-bit -integer case. -</p> -<a name="Garbage-collection"></a> -<h4 class="subsection">4.3.5 Garbage collection</h4> - -<p>Reference counting is used to free objects automatically and -deterministically. A separate cycle removal pass is done when the allocated -memory becomes too large. The cycle removal algorithm only uses the -reference counts and the object content, so no explicit garbage -collection roots need to be manipulated in the C code. -</p> -<a name="JSValue-1"></a> -<h4 class="subsection">4.3.6 JSValue</h4> - -<p>It is a Javascript value which can be a primitive type (such as -Number, String, ...) or an Object. NaN boxing is used in the 32-bit version -to store 64-bit floating point numbers. The representation is -optimized so that 32-bit integers and reference counted values can be -efficiently tested. -</p> -<p>In 64-bit code, JSValue are 128-bit large and no NaN boxing is used. The -rationale is that in 64-bit code memory usage is less critical. -</p> -<p>In both cases (32 or 64 bits), JSValue exactly fits two CPU registers, -so it can be efficiently returned by C functions. -</p> -<a name="Function-call"></a> -<h4 class="subsection">4.3.7 Function call</h4> - -<p>The engine is optimized so that function calls are fast. The system -stack holds the Javascript parameters and local variables. -</p> -<a name="RegExp"></a> -<h3 class="section">4.4 RegExp</h3> - -<p>A specific regular expression engine was developed. It is both small -and efficient and supports all the ES2020 features including the -Unicode properties. As the Javascript compiler, it directly generates -bytecode without a parse tree. -</p> -<p>Backtracking with an explicit stack is used so that there is no -recursion on the system stack. Simple quantifiers are specifically -optimized to avoid recursions. -</p> -<p>Infinite recursions coming from quantifiers with empty terms are -avoided. -</p> -<p>The full regexp library weights about 15 KiB (x86 code), excluding the -Unicode library. -</p> -<a name="Unicode"></a> -<h3 class="section">4.5 Unicode</h3> - -<p>A specific Unicode library was developed so that there is no -dependency on an external large Unicode library such as ICU. All the -Unicode tables are compressed while keeping a reasonable access -speed. -</p> -<p>The library supports case conversion, Unicode normalization, Unicode -script queries, Unicode general category queries and all Unicode -binary properties. -</p> -<p>The full Unicode library weights about 45 KiB (x86 code). -</p> -<a name="BigInt_002c-BigFloat_002c-BigDecimal"></a> -<h3 class="section">4.6 BigInt, BigFloat, BigDecimal</h3> - -<p>BigInt, BigFloat and BigDecimal are implemented with the <code>libbf</code> -library<a name="DOCF7" href="#FOOT7"><sup>7</sup></a>. It weights about 90 -KiB (x86 code) and provides arbitrary precision IEEE 754 floating -point operations and transcendental functions with exact rounding. -</p> -<a name="License"></a> -<h2 class="chapter">5 License</h2> - -<p>QuickJS is released under the MIT license. -</p> -<p>Unless otherwise specified, the QuickJS sources are copyright Fabrice -Bellard and Charlie Gordon. -</p> -<div class="footnote"> -<hr> -<h4 class="footnotes-heading">Footnotes</h4> - -<h3><a name="FOOT1" href="#DOCF1">(1)</a></h3> -<p><a href="https://tc39.es/ecma262/">https://tc39.es/ecma262/</a></p> -<h3><a name="FOOT2" href="#DOCF2">(2)</a></h3> -<p><a href="https://github.com/tc39/test262">https://github.com/tc39/test262</a></p> -<h3><a name="FOOT3" href="#DOCF3">(3)</a></h3> -<p><a href="https://tc39.github.io/ecma262/">https://tc39.github.io/ecma262/</a></p> -<h3><a name="FOOT4" href="#DOCF4">(4)</a></h3> -<p>The old -ES5.1 tests can be extracted with <code>git clone --single-branch ---branch es5-tests https://github.com/tc39/test262.git test262o</code></p> -<h3><a name="FOOT5" href="#DOCF5">(5)</a></h3> -<p><a href="https://github.com/bterlson/test262-harness">https://github.com/bterlson/test262-harness</a></p> -<h3><a name="FOOT6" href="#DOCF6">(6)</a></h3> -<p>We believe the current specification of tails calls is too complicated and presents limited practical interests.</p> -<h3><a name="FOOT7" href="#DOCF7">(7)</a></h3> -<p><a href="https://bellard.org/libbf">https://bellard.org/libbf</a></p> -</div> -<hr> - - - -</body> -</html> diff --git a/doc/quickjs.pdf b/doc/quickjs.pdf Binary files differdeleted file mode 100644 index 497964b..0000000 --- a/doc/quickjs.pdf +++ /dev/null diff --git a/doc/quickjs.texi b/doc/quickjs.texi index 57d13e6..40c0bb5 100644 --- a/doc/quickjs.texi +++ b/doc/quickjs.texi @@ -452,6 +452,16 @@ useful in case of specific memory constraints or for testing. Return the value of the environment variable @code{name} or @code{undefined} if it is not defined. +@item setenv(name, value) +Set the value of the environment variable @code{name} to the string +@code{value}. + +@item unsetenv(name) +Delete the environment variable @code{name}. + +@item getenviron() +Return an object containing the environment variables as key-value pairs. + @item urlGet(url, options = undefined) Download @code{url} using the @file{curl} command line @@ -532,7 +542,7 @@ position @code{position} (wrapper to the libc @code{fread}). @item write(buffer, position, length) Write @code{length} bytes to the file from the ArrayBuffer @code{buffer} at byte -position @code{position} (wrapper to the libc @code{fread}). +position @code{position} (wrapper to the libc @code{fwrite}). @item getline() Return the next line from the file, assuming UTF-8 encoding, excluding diff --git a/jscompress.c b/jscompress.c deleted file mode 100644 index a68c0e8..0000000 --- a/jscompress.c +++ /dev/null @@ -1,918 +0,0 @@ -/* - * Javascript Compressor - * - * Copyright (c) 2008-2018 Fabrice Bellard - * Copyright (c) 2017-2018 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include <stdlib.h> -#include <stdio.h> -#include <getopt.h> -#include <stdarg.h> -#include <string.h> -#include <inttypes.h> -#include <unistd.h> - -#include "cutils.h" - -typedef struct JSToken { - int tok; - char buf[20]; - char *str; - int len; - int size; - int line_num; /* line number for start of token */ - int lines; /* number of embedded linefeeds in token */ -} JSToken; - -enum { - TOK_EOF = 256, - TOK_IDENT, - TOK_STR1, - TOK_STR2, - TOK_STR3, - TOK_NUM, - TOK_COM, - TOK_LCOM, -}; - -void tok_reset(JSToken *tt) -{ - if (tt->str != tt->buf) { - free(tt->str); - tt->str = tt->buf; - tt->size = sizeof(tt->buf); - } - tt->len = 0; -} - -void tok_add_ch(JSToken *tt, int c) -{ - if (tt->len + 1 > tt->size) { - tt->size *= 2; - if (tt->str == tt->buf) { - tt->str = malloc(tt->size); - memcpy(tt->str, tt->buf, tt->len); - } else { - tt->str = realloc(tt->str, tt->size); - } - } - tt->str[tt->len++] = c; -} - -FILE *infile; -const char *filename; -int output_line_num; -int line_num; -int ch; -JSToken tokc; - -int skip_mask; -#define DEFINE_MAX 20 -char *define_tab[DEFINE_MAX]; -int define_len; - -void error(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - if (filename) { - fprintf(stderr, "%s:%d: ", filename, line_num); - } else { - fprintf(stderr, "jscompress: "); - } - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - exit(1); -} - -void define_symbol(const char *def) -{ - int i; - for (i = 0; i < define_len; i++) { - if (!strcmp(tokc.str, define_tab[i])) - return; - } - if (define_len >= DEFINE_MAX) - error("too many defines"); - define_tab[define_len++] = strdup(def); -} - -void undefine_symbol(const char *def) -{ - int i, j; - for (i = j = 0; i < define_len; i++) { - if (!strcmp(tokc.str, define_tab[i])) { - free(define_tab[i]); - } else { - define_tab[j++] = define_tab[i]; - } - } - define_len = j; -} - -const char *find_symbol(const char *def) -{ - int i; - for (i = 0; i < define_len; i++) { - if (!strcmp(tokc.str, define_tab[i])) - return "1"; - } - return NULL; -} - -void next(void); - -void nextch(void) -{ - ch = fgetc(infile); - if (ch == '\n') - line_num++; -} - -int skip_blanks(void) -{ - for (;;) { - next(); - if (tokc.tok != ' ' && tokc.tok != '\t' && - tokc.tok != TOK_COM && tokc.tok != TOK_LCOM) - return tokc.tok; - } -} - -void parse_directive(void) -{ - int ifdef, mask = skip_mask; - /* simplistic preprocessor: - #define / #undef / #ifdef / #ifndef / #else / #endif - no symbol substitution. - */ - skip_mask = 0; /* disable skipping to parse preprocessor line */ - nextch(); - if (skip_blanks() != TOK_IDENT) - error("expected preprocessing directive after #"); - - if (!strcmp(tokc.str, "define")) { - if (skip_blanks() != TOK_IDENT) - error("expected identifier after #define"); - define_symbol(tokc.str); - } else if (!strcmp(tokc.str, "undef")) { - if (skip_blanks() != TOK_IDENT) - error("expected identifier after #undef"); - undefine_symbol(tokc.str); - } else if ((ifdef = 1, !strcmp(tokc.str, "ifdef")) || - (ifdef = 0, !strcmp(tokc.str, "ifndef"))) { - if (skip_blanks() != TOK_IDENT) - error("expected identifier after #ifdef/#ifndef"); - mask = (mask << 2) | 2 | ifdef; - if (find_symbol(tokc.str)) - mask ^= 1; - } else if (!strcmp(tokc.str, "else")) { - if (!(mask & 2)) - error("#else without a #if"); - mask ^= 1; - } else if (!strcmp(tokc.str, "endif")) { - if (!(mask & 2)) - error("#endif without a #if"); - mask >>= 2; - } else { - error("unsupported preprocessing directive"); - } - if (skip_blanks() != '\n') - error("extra characters on preprocessing line"); - skip_mask = mask; -} - -/* return -1 if invalid char */ -static int hex_to_num(int ch) -{ - if (ch >= 'a' && ch <= 'f') - return ch - 'a' + 10; - else if (ch >= 'A' && ch <= 'F') - return ch - 'A' + 10; - else if (ch >= '0' && ch <= '9') - return ch - '0'; - else - return -1; -} - -void next(void) -{ -again: - tok_reset(&tokc); - tokc.line_num = line_num; - tokc.lines = 0; - switch(ch) { - case EOF: - tokc.tok = TOK_EOF; - if (skip_mask) - error("missing #endif"); - break; - case 'a' ... 'z': - case 'A' ... 'Z': - case '_': - case '$': - tok_add_ch(&tokc, ch); - nextch(); - while ((ch >= 'a' && ch <= 'z') || - (ch >= 'A' && ch <= 'Z') || - (ch >= '0' && ch <= '9') || - (ch == '_' || ch == '$')) { - tok_add_ch(&tokc, ch); - nextch(); - } - tok_add_ch(&tokc, '\0'); - tokc.tok = TOK_IDENT; - break; - case '.': - nextch(); - if (ch >= '0' && ch <= '9') { - tok_add_ch(&tokc, '.'); - goto has_dot; - } - tokc.tok = '.'; - break; - case '0': - tok_add_ch(&tokc, ch); - nextch(); - if (ch == 'x' || ch == 'X') { - /* hexa */ - tok_add_ch(&tokc, ch); - nextch(); - while ((ch >= 'a' && ch <= 'f') || - (ch >= 'A' && ch <= 'F') || - (ch >= '0' && ch <= '9')) { - tok_add_ch(&tokc, ch); - nextch(); - } - tok_add_ch(&tokc, '\0'); - tokc.tok = TOK_NUM; - break; - } - goto has_digit; - - case '1' ... '9': - tok_add_ch(&tokc, ch); - nextch(); - has_digit: - /* decimal */ - while (ch >= '0' && ch <= '9') { - tok_add_ch(&tokc, ch); - nextch(); - } - if (ch == '.') { - tok_add_ch(&tokc, ch); - nextch(); - has_dot: - while (ch >= '0' && ch <= '9') { - tok_add_ch(&tokc, ch); - nextch(); - } - } - if (ch == 'e' || ch == 'E') { - tok_add_ch(&tokc, ch); - nextch(); - if (ch == '+' || ch == '-') { - tok_add_ch(&tokc, ch); - nextch(); - } - while (ch >= '0' && ch <= '9') { - tok_add_ch(&tokc, ch); - nextch(); - } - } - tok_add_ch(&tokc, '\0'); - tokc.tok = TOK_NUM; - break; - case '`': - { - nextch(); - while (ch != '`' && ch != EOF) { - if (ch == '\\') { - tok_add_ch(&tokc, ch); - nextch(); - if (ch == EOF) { - error("unexpected char after '\\'"); - } - tok_add_ch(&tokc, ch); - } else { - tok_add_ch(&tokc, ch); - nextch(); - } - } - nextch(); - tok_add_ch(&tokc, 0); - tokc.tok = TOK_STR3; - } - break; - case '\"': - case '\'': - { - int n, i, c, hex_digit_count; - int quote_ch; - quote_ch = ch; - nextch(); - while (ch != quote_ch && ch != EOF) { - if (ch == '\\') { - nextch(); - switch(ch) { - case 'n': - tok_add_ch(&tokc, '\n'); - nextch(); - break; - case 'r': - tok_add_ch(&tokc, '\r'); - nextch(); - break; - case 't': - tok_add_ch(&tokc, '\t'); - nextch(); - break; - case 'v': - tok_add_ch(&tokc, '\v'); - nextch(); - break; - case '\"': - case '\'': - case '\\': - tok_add_ch(&tokc, ch); - nextch(); - break; - case '0' ... '7': - n = 0; - while (ch >= '0' && ch <= '7') { - n = n * 8 + (ch - '0'); - nextch(); - } - tok_add_ch(&tokc, n); - break; - case 'x': - case 'u': - if (ch == 'x') - hex_digit_count = 2; - else - hex_digit_count = 4; - nextch(); - n = 0; - for(i = 0; i < hex_digit_count; i++) { - c = hex_to_num(ch); - if (c < 0) - error("unexpected char after '\\x'"); - n = n * 16 + c; - nextch(); - } - if (n >= 256) - error("unicode is currently unsupported"); - tok_add_ch(&tokc, n); - break; - - default: - error("unexpected char after '\\'"); - } - } else { - /* XXX: should refuse embedded newlines */ - tok_add_ch(&tokc, ch); - nextch(); - } - } - nextch(); - tok_add_ch(&tokc, 0); - if (quote_ch == '\'') - tokc.tok = TOK_STR1; - else - tokc.tok = TOK_STR2; - } - break; - case '/': - nextch(); - if (ch == '/') { - tok_add_ch(&tokc, '/'); - tok_add_ch(&tokc, ch); - nextch(); - while (ch != '\n' && ch != EOF) { - tok_add_ch(&tokc, ch); - nextch(); - } - tok_add_ch(&tokc, '\0'); - tokc.tok = TOK_LCOM; - } else if (ch == '*') { - int last; - tok_add_ch(&tokc, '/'); - tok_add_ch(&tokc, ch); - last = 0; - for(;;) { - nextch(); - if (ch == EOF) - error("unterminated comment"); - if (ch == '\n') - tokc.lines++; - tok_add_ch(&tokc, ch); - if (last == '*' && ch == '/') - break; - last = ch; - } - nextch(); - tok_add_ch(&tokc, '\0'); - tokc.tok = TOK_COM; - } else { - tokc.tok = '/'; - } - break; - case '#': - parse_directive(); - goto again; - case '\n': - /* adjust line number */ - tokc.line_num--; - tokc.lines++; - /* fall thru */ - default: - tokc.tok = ch; - nextch(); - break; - } - if (skip_mask & 1) - goto again; -} - -void print_tok(FILE *f, JSToken *tt) -{ - /* keep output lines in sync with input lines */ - while (output_line_num < tt->line_num) { - putc('\n', f); - output_line_num++; - } - - switch(tt->tok) { - case TOK_IDENT: - case TOK_COM: - case TOK_LCOM: - fprintf(f, "%s", tt->str); - break; - case TOK_NUM: - { - unsigned long a; - char *p; - a = strtoul(tt->str, &p, 0); - if (*p == '\0' && a <= 0x7fffffff) { - /* must be an integer */ - fprintf(f, "%d", (int)a); - } else { - fprintf(f, "%s", tt->str); - } - } - break; - case TOK_STR3: - fprintf(f, "`%s`", tt->str); - break; - case TOK_STR1: - case TOK_STR2: - { - int i, c, quote_ch; - if (tt->tok == TOK_STR1) - quote_ch = '\''; - else - quote_ch = '\"'; - fprintf(f, "%c", quote_ch); - for(i = 0; i < tt->len - 1; i++) { - c = (uint8_t)tt->str[i]; - switch(c) { - case '\r': - fprintf(f, "\\r"); - break; - case '\n': - fprintf(f, "\\n"); - break; - case '\t': - fprintf(f, "\\t"); - break; - case '\v': - fprintf(f, "\\v"); - break; - case '\"': - case '\'': - if (c == quote_ch) - fprintf(f, "\\%c", c); - else - fprintf(f, "%c", c); - break; - case '\\': - fprintf(f, "\\\\"); - break; - default: - /* XXX: no utf-8 support! */ - if (c >= 32 && c <= 255) { - fprintf(f, "%c", c); - } else if (c <= 255) - fprintf(f, "\\x%02x", c); - else - fprintf(f, "\\u%04x", c); - break; - } - } - fprintf(f, "%c", quote_ch); - } - break; - default: - if (tokc.tok >= 256) - error("unsupported token in print_tok: %d", tt->tok); - fprintf(f, "%c", tt->tok); - break; - } - output_line_num += tt->lines; -} - -/* check if token pasting could occur */ -static BOOL compat_token(int c1, int c2) -{ - if ((c1 == TOK_IDENT || c1 == TOK_NUM) && - (c2 == TOK_IDENT || c2 == TOK_NUM)) - return FALSE; - - if ((c1 == c2 && strchr("+-<>&|=*/.", c1)) - || (c2 == '=' && strchr("+-<>&|!*/^%", c1)) - || (c1 == '=' && c2 == '>') - || (c1 == '/' && c2 == '*') - || (c1 == '.' && c2 == TOK_NUM) - || (c1 == TOK_NUM && c2 == '.')) - return FALSE; - - return TRUE; -} - -void js_compress(const char *filename, const char *outfilename, - BOOL do_strip, BOOL keep_header) -{ - FILE *outfile; - int ltok, seen_space; - - line_num = 1; - infile = fopen(filename, "rb"); - if (!infile) { - perror(filename); - exit(1); - } - - output_line_num = 1; - outfile = fopen(outfilename, "wb"); - if (!outfile) { - perror(outfilename); - exit(1); - } - - nextch(); - next(); - ltok = 0; - seen_space = 0; - if (do_strip) { - if (keep_header) { - while (tokc.tok == ' ' || - tokc.tok == '\n' || - tokc.tok == '\t' || - tokc.tok == '\v' || - tokc.tok == '\b' || - tokc.tok == '\f') { - seen_space = 1; - next(); - } - if (tokc.tok == TOK_COM) { - print_tok(outfile, &tokc); - //fprintf(outfile, "\n"); - ltok = tokc.tok; - seen_space = 0; - next(); - } - } - - for(;;) { - if (tokc.tok == TOK_EOF) - break; - if (tokc.tok == ' ' || - tokc.tok == '\r' || - tokc.tok == '\t' || - tokc.tok == '\v' || - tokc.tok == '\b' || - tokc.tok == '\f' || - tokc.tok == TOK_LCOM || - tokc.tok == TOK_COM) { - /* don't print spaces or comments */ - seen_space = 1; - } else if (tokc.tok == TOK_STR3) { - print_tok(outfile, &tokc); - ltok = tokc.tok; - seen_space = 0; - } else if (tokc.tok == TOK_STR1 || tokc.tok == TOK_STR2) { - int count, i; - /* find the optimal quote char */ - count = 0; - for(i = 0; i < tokc.len; i++) { - if (tokc.str[i] == '\'') - count++; - else if (tokc.str[i] == '\"') - count--; - } - if (count > 0) - tokc.tok = TOK_STR2; - else if (count < 0) - tokc.tok = TOK_STR1; - print_tok(outfile, &tokc); - ltok = tokc.tok; - seen_space = 0; - } else { - if (seen_space && !compat_token(ltok, tokc.tok)) { - fprintf(outfile, " "); - } - print_tok(outfile, &tokc); - ltok = tokc.tok; - seen_space = 0; - } - next(); - } - } else { - /* just handle preprocessing */ - while (tokc.tok != TOK_EOF) { - print_tok(outfile, &tokc); - next(); - } - } - - fclose(outfile); - fclose(infile); -} - -#define HASH_SIZE 30011 -#define MATCH_LEN_MIN 3 -#define MATCH_LEN_MAX (4 + 63) -#define DIST_MAX 65535 - -static int find_longest_match(int *pdist, const uint8_t *src, int src_len, - const int *hash_next, int cur_pos) -{ - int pos, i, match_len, match_pos, pos_min, len_max; - - len_max = min_int(src_len - cur_pos, MATCH_LEN_MAX); - match_len = 0; - match_pos = 0; - pos_min = max_int(cur_pos - DIST_MAX - 1, 0); - pos = hash_next[cur_pos]; - while (pos >= pos_min) { - for(i = 0; i < len_max; i++) { - if (src[cur_pos + i] != src[pos + i]) - break; - } - if (i > match_len) { - match_len = i; - match_pos = pos; - } - pos = hash_next[pos]; - } - *pdist = cur_pos - match_pos - 1; - return match_len; -} - -int lz_compress(uint8_t **pdst, const uint8_t *src, int src_len) -{ - int *hash_table, *hash_next; - uint32_t h, v; - int i, dist, len, len1, dist1; - uint8_t *dst, *q; - - /* build the hash table */ - - hash_table = malloc(sizeof(hash_table[0]) * HASH_SIZE); - for(i = 0; i < HASH_SIZE; i++) - hash_table[i] = -1; - hash_next = malloc(sizeof(hash_next[0]) * src_len); - for(i = 0; i < src_len; i++) - hash_next[i] = -1; - - for(i = 0; i < src_len - MATCH_LEN_MIN + 1; i++) { - h = ((src[i] << 16) | (src[i + 1] << 8) | src[i + 2]) % HASH_SIZE; - hash_next[i] = hash_table[h]; - hash_table[h] = i; - } - for(;i < src_len; i++) { - hash_next[i] = -1; - } - free(hash_table); - - dst = malloc(src_len + 4); /* never larger than the source */ - q = dst; - *q++ = src_len >> 24; - *q++ = src_len >> 16; - *q++ = src_len >> 8; - *q++ = src_len >> 0; - /* compress */ - i = 0; - while (i < src_len) { - if (src[i] >= 128) - return -1; - len = find_longest_match(&dist, src, src_len, hash_next, i); - if (len >= MATCH_LEN_MIN) { - /* heuristic: see if better length just after */ - len1 = find_longest_match(&dist1, src, src_len, hash_next, i + 1); - if (len1 > len) - goto no_match; - } - if (len < MATCH_LEN_MIN) { - no_match: - *q++ = src[i]; - i++; - } else if (len <= (3 + 15) && dist < (1 << 10)) { - v = 0x8000 | ((len - 3) << 10) | dist; - *q++ = v >> 8; - *q++ = v; - i += len; - } else if (len >= 4 && len <= (4 + 63) && dist < (1 << 16)) { - v = 0xc00000 | ((len - 4) << 16) | dist; - *q++ = v >> 16; - *q++ = v >> 8; - *q++ = v; - i += len; - } else { - goto no_match; - } - } - free(hash_next); - *pdst = dst; - return q - dst; -} - -static int load_file(uint8_t **pbuf, const char *filename) -{ - FILE *f; - uint8_t *buf; - int buf_len; - - f = fopen(filename, "rb"); - if (!f) { - perror(filename); - exit(1); - } - fseek(f, 0, SEEK_END); - buf_len = ftell(f); - fseek(f, 0, SEEK_SET); - buf = malloc(buf_len + 1); - fread(buf, 1, buf_len, f); - buf[buf_len] = '\0'; - fclose(f); - *pbuf = buf; - return buf_len; -} - -static void save_file(const char *filename, const uint8_t *buf, int buf_len) -{ - FILE *f; - - f = fopen(filename, "wb"); - if (!f) { - perror(filename); - exit(1); - } - fwrite(buf, 1, buf_len, f); - fclose(f); -} - -static void save_c_source(const char *filename, const uint8_t *buf, int buf_len, - const char *var_name) -{ - FILE *f; - int i; - - f = fopen(filename, "wb"); - if (!f) { - perror(filename); - exit(1); - } - fprintf(f, "/* This file is automatically generated - do not edit */\n\n"); - fprintf(f, "const uint8_t %s[] = {\n", var_name); - for(i = 0; i < buf_len; i++) { - fprintf(f, " 0x%02x,", buf[i]); - if ((i % 8) == 7 || (i == buf_len - 1)) - fprintf(f, "\n"); - } - fprintf(f, "};\n"); - fclose(f); -} - -#define DEFAULT_OUTPUT_FILENAME "out.js" - -void help(void) -{ - printf("jscompress version 1.0 Copyright (c) 2008-2018 Fabrice Bellard\n" - "usage: jscompress [options] filename\n" - "Javascript compressor\n" - "\n" - "-h print this help\n" - "-n do not compress spaces\n" - "-H keep the first comment\n" - "-c compress to file\n" - "-C name compress to C source ('name' is the variable name)\n" - "-D symbol define preprocessor symbol\n" - "-U symbol undefine preprocessor symbol\n" - "-o outfile set the output filename (default=%s)\n", - DEFAULT_OUTPUT_FILENAME); - exit(1); -} - -int main(int argc, char **argv) -{ - int c, do_strip, keep_header, compress; - const char *out_filename, *c_var, *fname; - char tmpfilename[1024]; - - do_strip = 1; - keep_header = 0; - out_filename = DEFAULT_OUTPUT_FILENAME; - compress = 0; - c_var = NULL; - for(;;) { - c = getopt(argc, argv, "hno:HcC:D:U:"); - if (c == -1) - break; - switch(c) { - case 'h': - help(); - break; - case 'n': - do_strip = 0; - break; - case 'o': - out_filename = optarg; - break; - case 'H': - keep_header = 1; - break; - case 'c': - compress = 1; - break; - case 'C': - c_var = optarg; - compress = 1; - break; - case 'D': - define_symbol(optarg); - break; - case 'U': - undefine_symbol(optarg); - break; - } - } - if (optind >= argc) - help(); - - filename = argv[optind++]; - - if (compress) { -#if defined(__ANDROID__) - /* XXX: use another directory ? */ - snprintf(tmpfilename, sizeof(tmpfilename), "out.%d", getpid()); -#else - snprintf(tmpfilename, sizeof(tmpfilename), "/tmp/out.%d", getpid()); -#endif - fname = tmpfilename; - } else { - fname = out_filename; - } - js_compress(filename, fname, do_strip, keep_header); - - if (compress) { - uint8_t *buf1, *buf2; - int buf1_len, buf2_len; - - buf1_len = load_file(&buf1, fname); - unlink(fname); - buf2_len = lz_compress(&buf2, buf1, buf1_len); - if (buf2_len < 0) { - fprintf(stderr, "Could not compress file (UTF8 chars are forbidden)\n"); - exit(1); - } - - if (c_var) { - save_c_source(out_filename, buf2, buf2_len, c_var); - } else { - save_file(out_filename, buf2, buf2_len); - } - free(buf1); - free(buf2); - } - return 0; -} @@ -27,7 +27,7 @@ #include <stddef.h> #include <stdint.h> -#if defined(__x86_64__) +#if INTPTR_MAX >= INT64_MAX #define LIMB_LOG2_BITS 6 #else #define LIMB_LOG2_BITS 5 diff --git a/libregexp.c b/libregexp.c index bbb5e9d..379bfc7 100644 --- a/libregexp.c +++ b/libregexp.c @@ -75,7 +75,7 @@ typedef struct { int capture_count; int total_capture_count; /* -1 = not computed yet */ int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */ - void *mem_opaque; + void *opaque; DynBuf group_names; union { char error_msg[TMP_BUF_SIZE]; @@ -230,7 +230,7 @@ static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c) invert = c & 1; c_pt = char_range_table[c >> 1]; len = *c_pt++; - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); for(i = 0; i < len * 2; i++) { if (cr_add_point(cr, c_pt[i])) goto fail; @@ -625,7 +625,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, p++; q = name; while (is_unicode_char(*p)) { - if ((q - name) > sizeof(name) - 1) + if ((q - name) >= sizeof(name) - 1) goto unknown_property_name; *q++ = *p++; } @@ -634,7 +634,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, if (*p == '=') { p++; while (is_unicode_char(*p)) { - if ((q - value) > sizeof(value) - 1) + if ((q - value) >= sizeof(value) - 1) return re_parse_error(s, "unknown unicode property value"); *q++ = *p++; } @@ -651,7 +651,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { script_ext = TRUE; do_script: - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); ret = unicode_script(cr, value, script_ext); if (ret) { cr_free(cr); @@ -661,7 +661,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, goto out_of_memory; } } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); ret = unicode_general_category(cr, value); if (ret) { cr_free(cr); @@ -671,7 +671,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, goto out_of_memory; } } else if (value[0] == '\0') { - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); ret = unicode_general_category(cr, name); if (ret == -1) { cr_free(cr); @@ -864,7 +864,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp) CharRange cr1_s, *cr1 = &cr1_s; BOOL invert; - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); p = *pp; p++; /* skip '[' */ invert = FALSE; @@ -1147,9 +1147,13 @@ static int re_parse_captures(REParseState *s, int *phas_named_captures, } } capture_index++; + if (capture_index >= CAPTURE_COUNT_MAX) + goto done; } } else { capture_index++; + if (capture_index >= CAPTURE_COUNT_MAX) + goto done; } break; case '\\': @@ -1163,6 +1167,7 @@ static int re_parse_captures(REParseState *s, int *phas_named_captures, break; } } + done: if (capture_name) return -1; else @@ -1734,6 +1739,9 @@ static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) { int start, len, pos; + if (lre_check_stack_overflow(s->opaque, 0)) + return re_parse_error(s, "stack overflow"); + start = s->byte_code.size; if (re_parse_alternative(s, is_backward_dir)) return -1; @@ -1819,7 +1827,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, BOOL is_sticky; memset(s, 0, sizeof(*s)); - s->mem_opaque = opaque; + s->opaque = opaque; s->buf_ptr = (const uint8_t *)buf; s->buf_end = s->buf_ptr + buf_len; s->buf_start = s->buf_ptr; @@ -2625,6 +2625,17 @@ function atanh(a) return 0.5 * log((1 + x) / (1 - x)); } +function sigmoid(x) +{ + x = Float(x); + return 1 / (1 + exp(-x)); +} + +function lerp(a, b, t) +{ + return a + (b - a) * t; +} + var idn = Matrix.idn; var diag = Matrix.diag; var trans = Matrix.trans; diff --git a/quickjs-atom.h b/quickjs-atom.h index a353ad4..4c22794 100644 --- a/quickjs-atom.h +++ b/quickjs-atom.h @@ -113,6 +113,7 @@ DEF(caller, "caller") DEF(_eval_, "<eval>") DEF(_ret_, "<ret>") DEF(_var_, "<var>") +DEF(_arg_var_, "<arg_var>") DEF(_with_, "<with>") DEF(lastIndex, "lastIndex") DEF(target, "target") diff --git a/quickjs-libc.c b/quickjs-libc.c index 00a7536..e8b81e9 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -623,6 +623,97 @@ static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, return JS_NewString(ctx, str); } +#if defined(_WIN32) +static void setenv(const char *name, const char *value, int overwrite) +{ + char *str; + size_t name_len, value_len; + name_len = strlen(name); + value_len = strlen(value); + str = malloc(name_len + 1 + value_len + 1); + memcpy(str, name, name_len); + str[name_len] = '='; + memcpy(str + name_len + 1, value, value_len); + str[name_len + 1 + value_len] = '\0'; + _putenv(str); + free(str); +} + +static void unsetenv(const char *name) +{ + setenv(name, "", TRUE); +} +#endif /* _WIN32 */ + +static JSValue js_std_setenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name, *value; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + value = JS_ToCString(ctx, argv[1]); + if (!value) { + JS_FreeCString(ctx, name); + return JS_EXCEPTION; + } + setenv(name, value, TRUE); + JS_FreeCString(ctx, name); + JS_FreeCString(ctx, value); + return JS_UNDEFINED; +} + +static JSValue js_std_unsetenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + unsetenv(name); + JS_FreeCString(ctx, name); + return JS_UNDEFINED; +} + +/* return an object containing the list of the available environment + variables. */ +static JSValue js_std_getenviron(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char **envp; + const char *name, *p, *value; + JSValue obj; + uint32_t idx; + size_t name_len; + JSAtom atom; + int ret; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + envp = environ; + for(idx = 0; envp[idx] != NULL; idx++) { + name = envp[idx]; + p = strchr(name, '='); + name_len = p - name; + if (!p) + continue; + value = p + 1; + atom = JS_NewAtomLen(ctx, name, name_len); + if (atom == JS_ATOM_NULL) + goto fail; + ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value), + 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_std_gc(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1395,6 +1486,9 @@ static const JSCFunctionListEntry js_std_funcs[] = { JS_CFUNC_DEF("evalScript", 1, js_evalScript ), JS_CFUNC_DEF("loadScript", 1, js_loadScript ), JS_CFUNC_DEF("getenv", 1, js_std_getenv ), + JS_CFUNC_DEF("setenv", 1, js_std_setenv ), + JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ), + JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), JS_CFUNC_DEF("strerror", 1, js_std_strerror ), @@ -1412,7 +1506,6 @@ static const JSCFunctionListEntry js_std_funcs[] = { JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE), - /* setenv, ... */ }; static const JSCFunctionListEntry js_std_file_proto_funcs[] = { diff --git a/quickjs-opcode.h b/quickjs-opcode.h index 387363c..c731a14 100644 --- a/quickjs-opcode.h +++ b/quickjs-opcode.h @@ -114,7 +114,7 @@ DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ DEF( return_async, 1, 1, 0, none) DEF( throw, 1, 1, 0, none) -DEF( throw_var, 6, 0, 0, atom_u8) +DEF( throw_error, 6, 0, 0, atom_u8) DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a @@ -205,16 +205,15 @@ DEF( for_of_start, 1, 1, 3, none) DEF(for_await_of_start, 1, 1, 3, none) DEF( for_in_next, 1, 1, 3, none) DEF( for_of_next, 2, 3, 5, u8) -DEF(for_await_of_next, 1, 3, 4, none) +DEF(iterator_check_object, 1, 1, 1, none) DEF(iterator_get_value_done, 1, 1, 2, none) DEF( iterator_close, 1, 3, 0, none) DEF(iterator_close_return, 1, 4, 4, none) -DEF(async_iterator_close, 1, 3, 2, none) -DEF(async_iterator_next, 1, 4, 4, none) -DEF(async_iterator_get, 2, 4, 5, u8) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) DEF( initial_yield, 1, 0, 0, none) DEF( yield, 1, 1, 2, none) -DEF( yield_star, 1, 2, 2, none) +DEF( yield_star, 1, 1, 2, none) DEF(async_yield_star, 1, 1, 2, none) DEF( await, 1, 1, 1, none) @@ -266,8 +265,6 @@ DEF( nop, 1, 0, 0, none) /* temporary opcodes: never emitted in the final bytecode */ -def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */ - def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ @@ -199,7 +199,7 @@ typedef enum JSErrorEnum { } JSErrorEnum; #define JS_MAX_LOCAL_VARS 65536 -#define JS_STACK_SIZE_MAX 65536 +#define JS_STACK_SIZE_MAX 65534 #define JS_STRING_LEN_MAX ((1 << 30) - 1) #define __exception __attribute__((warn_unused_result)) @@ -506,14 +506,17 @@ typedef struct JSClosureVar { uint8_t is_arg : 1; uint8_t is_const : 1; uint8_t is_lexical : 1; - uint8_t var_kind : 3; /* see JSVarKindEnum */ - /* 9 bits available */ + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* 8 bits available */ uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the parent function. otherwise: index to a closure variable of the parent function */ JSAtom var_name; } JSClosureVar; +#define ARG_SCOPE_INDEX 1 +#define ARG_SCOPE_END (-2) + typedef struct JSVarScope { int parent; /* index into fd->scopes of the enclosing scope */ int first; /* index into fd->vars of the last variable in this scope */ @@ -526,6 +529,7 @@ typedef enum { JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator function declaration */ JS_VAR_CATCH, + JS_VAR_FUNCTION_NAME, /* function expression name */ JS_VAR_PRIVATE_FIELD, JS_VAR_PRIVATE_METHOD, JS_VAR_PRIVATE_GETTER, @@ -533,12 +537,21 @@ typedef enum { JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ } JSVarKindEnum; +/* XXX: could use a different structure in bytecode functions to save + memory */ typedef struct JSVarDef { JSAtom var_name; - int scope_level; /* index into fd->scopes of this variable lexical scope */ - int scope_next; /* index into fd->vars of the next variable in the - * same or enclosing lexical scope */ - uint8_t is_func_var : 1; /* used for the function self reference */ + /* index into fd->scopes of this variable lexical scope */ + int scope_level; + /* during compilation: + - if scope_level = 0: scope in which the variable is defined + - if scope_level != 0: index into fd->vars of the next + variable in the same or enclosing lexical scope + in a bytecode function: + index into fd->vars of the next + variable in the same or enclosing lexical scope + */ + int scope_next; uint8_t is_const : 1; uint8_t is_lexical : 1; uint8_t is_captured : 1; @@ -548,7 +561,9 @@ typedef struct JSVarDef { JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of the definition of the 'var' variables (they have scope_level = 0) */ - int func_pool_or_scope_idx : 24; /* only used during compilation */ + int func_pool_idx : 24; /* only used during compilation : index in + the constant pool for hoisted function + definition */ } JSVarDef; /* for the encoding of the pc2line table */ @@ -855,7 +870,7 @@ struct JSObject { uint8_t extensible : 1; uint8_t free_mark : 1; /* only used when freeing objects with cycles */ uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ - uint8_t fast_array : 1; /* TRUE if u.array is used for get/put */ + uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ 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() */ @@ -1000,7 +1015,7 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, int argc, JSValueConst *argv); static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val); + JSValue val, BOOL is_array_ctor); static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, JSValueConst val, int flags, int scope_idx); JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); @@ -1226,11 +1241,11 @@ static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, JSGCObjectTypeEnum type); static void remove_gc_object(JSGCObjectHeader *h); static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); -static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); -static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); -static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, - JSAtom atom, void *opaque); +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque); void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag); static const JSClassExoticMethods js_arguments_exotic_methods; @@ -6710,6 +6725,21 @@ static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) JS_AtomGetStr(ctx, buf, sizeof(buf), name)); } +static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, + JSFunctionBytecode *b, + int idx, BOOL is_ref) +{ + JSAtom atom = JS_ATOM_NULL; + if (is_ref) { + atom = b->closure_var[idx].var_name; + } else { + /* not present if the function is stripped and contains no eval() */ + if (b->vardefs) + atom = b->vardefs[b->arg_count + idx].var_name; + } + return JS_ThrowReferenceErrorUninitialized(ctx, atom); +} + static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) { JSRuntime *rt = ctx->rt; @@ -6986,25 +7016,37 @@ int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) return JS_OrdinaryIsInstanceOf(ctx, val, obj); } -typedef int JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +/* return the value associated to the autoinit property or an exception */ +typedef JSValue 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 */ + JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ }; +/* warning: 'prs' is reallocated after it */ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, - JSProperty *pr) + JSProperty *pr, JSShapeProperty *prs) { - int ret; + JSValue val; JSContext *realm; JSAutoInitFunc *func; - + + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + 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; + /* 'func' shall not modify the object properties 'pr' */ + val = func(realm, p, prop, pr->u.init.opaque); + js_autoinit_free(ctx->rt, pr); + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + if (JS_IsException(val)) + return -1; + pr->u.value = val; + return 0; } JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, @@ -7075,7 +7117,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, return JS_DupValue(ctx, val); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, prop, pr)) + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) return JS_EXCEPTION; continue; } @@ -7093,7 +7135,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - goto typed_array_oob; + return JS_UNDEFINED; } } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { @@ -7102,9 +7144,6 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, if (ret != 0) { if (ret < 0) return JS_EXCEPTION; - typed_array_oob: - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); return JS_UNDEFINED; } } @@ -7337,6 +7376,22 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) return 0; } +static uint32_t js_string_obj_get_length(JSContext *ctx, + JSValueConst obj) +{ + JSObject *p; + JSString *p1; + uint32_t len = 0; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + len = p1->len; + } + return len; +} + static int num_keys_cmp(const void *p1, const void *p2, void *opaque) { JSContext *ctx = opaque; @@ -7379,7 +7434,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, JSPropertyEnum *tab_atom, *tab_exotic; JSAtom atom; uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; - uint32_t num_index, str_index, sym_index, exotic_count; + uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; BOOL is_enumerable, num_sorted; uint32_t num_key; JSAtomKindEnum kind; @@ -7392,6 +7447,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, num_keys_count = 0; str_keys_count = 0; sym_keys_count = 0; + exotic_keys_count = 0; exotic_count = 0; tab_exotic = NULL; sh = p->shape; @@ -7425,19 +7481,13 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, if (p->is_exotic) { if (p->fast_array) { - /* the implicit GetOwnProperty raises an exception if the - typed array is detached */ - if ((flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) && - (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) && - typed_array_is_detached(ctx, p) && - typed_array_get_length(ctx, p) != 0) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } if (flags & JS_GPN_STRING_MASK) { num_keys_count += p->u.array.count; } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + } } else { const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; if (em && em->get_own_property_names) { @@ -7466,13 +7516,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, tab_exotic[i].is_enumerable = is_enumerable; } if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { - if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { - num_keys_count++; - } else if (kind == JS_ATOM_KIND_STRING) { - str_keys_count++; - } else { - sym_keys_count++; - } + exotic_keys_count++; } } } @@ -7482,7 +7526,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, /* fill them */ - atom_count = num_keys_count + str_keys_count + sym_keys_count; + atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; /* avoid allocating 0 bytes */ tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); if (!tab_atom) { @@ -7518,12 +7562,19 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } if (p->is_exotic) { + int len; if (p->fast_array) { if (flags & JS_GPN_STRING_MASK) { - for(i = 0; i < p->u.array.count; i++) { + len = p->u.array.count; + goto add_array_keys; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + add_array_keys: + for(i = 0; i < len; 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; } @@ -7531,30 +7582,23 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, num_index++; } } - } - if (exotic_count > 0) { + } else { + /* Note: exotic keys are not reordered and comes after the object own properties. */ for(i = 0; i < exotic_count; i++) { atom = tab_exotic[i].atom; is_enumerable = tab_exotic[i].is_enumerable; kind = JS_AtomGetKind(ctx, atom); if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && ((flags >> kind) & 1) != 0) { - if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { - j = num_index++; - num_sorted = FALSE; - } else if (kind == JS_ATOM_KIND_STRING) { - j = str_index++; - } else { - j = sym_index++; - } - tab_atom[j].atom = atom; - tab_atom[j].is_enumerable = is_enumerable; + tab_atom[sym_index].atom = atom; + tab_atom[sym_index].is_enumerable = is_enumerable; + sym_index++; } else { JS_FreeAtom(ctx, atom); } } + js_free(ctx, tab_exotic); } - js_free(ctx, tab_exotic); } assert(num_index == num_keys_count); @@ -7614,7 +7658,7 @@ retry: desc->value = JS_DupValue(ctx, val); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, prop, pr)) + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) return -1; goto retry; } @@ -7642,10 +7686,8 @@ retry: idx = __JS_AtomToUInt32(prop); if (idx < p->u.array.count) { if (desc) { - desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE; - if (p->class_id == JS_CLASS_ARRAY || - p->class_id == JS_CLASS_ARGUMENTS) - desc->flags |= JS_PROP_CONFIGURABLE; + desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | + JS_PROP_CONFIGURABLE; desc->getter = JS_UNDEFINED; desc->setter = JS_UNDEFINED; desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); @@ -7653,19 +7695,6 @@ retry: return TRUE; } } - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - int ret; - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) - return -1; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } - } - } } else { const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; if (em && em->get_own_property) { @@ -7748,8 +7777,6 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) if (ret != 0) { if (ret < 0) return -1; - /* the detached array test was done in - JS_GetOwnPropertyInternal() */ return FALSE; } } @@ -8074,7 +8101,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) return -1; goto redo; } else { - return FALSE; /* not configurable */ + return FALSE; } } } else { @@ -8114,15 +8141,19 @@ 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, 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); + ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); if (ret) return -1; + /* JS_ToArrayLengthFree() must be done before the read-only test */ + if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); if (likely(p->fast_array)) { uint32_t old_len = p->u.array.count; @@ -8382,8 +8413,7 @@ retry: /* fast case */ set_value(ctx, &pr->u.value, val); return TRUE; - } else if ((prs->flags & (JS_PROP_LENGTH | JS_PROP_WRITABLE)) == - (JS_PROP_LENGTH | JS_PROP_WRITABLE)) { + } else if (prs->flags & JS_PROP_LENGTH) { assert(p->class_id == JS_CLASS_ARRAY); assert(prop == JS_ATOM_length); return set_array_length(ctx, p, val, flags); @@ -8399,7 +8429,7 @@ retry: return TRUE; } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { /* Instantiate property and retry (potentially useless) */ - if (JS_AutoInitProperty(ctx, p, prop, pr)) { + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) { JS_FreeValue(ctx, val); return -1; } @@ -8437,10 +8467,6 @@ retry: JS_FreeValue(ctx, val); if (JS_IsException(val)) return -1; - if (typed_array_is_detached(ctx, p1)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); } } @@ -8509,7 +8535,7 @@ retry: return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { /* Instantiate property and retry (potentially useless) */ - if (JS_AutoInitProperty(ctx, p1, prop, pr)) + if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) return -1; goto retry2; } else if (!(prs->flags & JS_PROP_WRITABLE)) { @@ -8677,12 +8703,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, return -1; if (unlikely(idx >= (uint32_t)p->u.array.count)) { ta_out_of_bound: - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } else { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); - } + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); } p->u.array.u.double_ptr[idx] = d; break; @@ -8967,13 +8988,32 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, redo_prop_update: prs = find_own_property(&pr, p, prop); if (prs) { + /* the range of the Array length property is always tested before */ + if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { + uint32_t array_length; + if (JS_ToArrayLengthFree(ctx, &array_length, + JS_DupValue(ctx, val), FALSE)) { + return -1; + } + /* this code relies on the fact that Uint32 are never allocated */ + val = (JSValueConst)JS_NewUint32(ctx, array_length); + /* prs may have been modified */ + prs = find_own_property(&pr, p, prop); + assert(prs != NULL); + } /* property already exists */ if (!check_define_prop_flags(prs->flags, flags)) { not_configurable: return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); } - retry: + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto redo_prop_update; + } + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { @@ -8996,14 +9036,6 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, /* convert to getset */ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { free_var_ref(ctx->rt, pr->u.var_ref); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* clear property and update */ - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - js_autoinit_free(ctx->rt, pr); - prs->flags &= ~JS_PROP_TMASK; - pr->u.value = JS_UNDEFINED; - goto retry; } else { JS_FreeValue(ctx, pr->u.value); } @@ -9051,39 +9083,17 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, pr->u.value = JS_UNDEFINED; } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { /* Note: JS_PROP_VARREF is always writable */ - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* clear property and update */ - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - js_autoinit_free(ctx->rt, pr); - prs->flags &= ~JS_PROP_TMASK; - pr->u.value = JS_UNDEFINED; } else { if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && - (flags & JS_PROP_HAS_VALUE) && - !js_same_value(ctx, val, pr->u.value)) { - goto not_configurable; + (flags & JS_PROP_HAS_VALUE)) { + if (!js_same_value(ctx, val, pr->u.value)) { + goto not_configurable; + } else { + return TRUE; + } } } - if (prs->flags & JS_PROP_LENGTH) { - if (flags & JS_PROP_HAS_VALUE) { - res = set_array_length(ctx, p, JS_DupValue(ctx, val), - flags); - } else { - res = TRUE; - } - /* still need to reset the writable flag if needed. - The JS_PROP_LENGTH is reset to have the correct - read-only behavior in JS_SetProperty(). */ - if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == - JS_PROP_HAS_WRITABLE) { - prs = get_shape_prop(p->shape); - if (js_update_property_flags(ctx, p, &prs, - prs->flags & ~(JS_PROP_WRITABLE | JS_PROP_LENGTH))) - return -1; - } - return res; - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { if (flags & JS_PROP_HAS_VALUE) { if (p->class_id == JS_CLASS_MODULE_NS) { /* JS_PROP_WRITABLE is always true for variable @@ -9107,9 +9117,27 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, pr->u.value = val1; prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); } - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* XXX: should never happen, type was reset above */ - abort(); + } else if (prs->flags & JS_PROP_LENGTH) { + if (flags & JS_PROP_HAS_VALUE) { + /* Note: no JS code is executable because + 'val' is guaranted to be a Uint32 */ + res = set_array_length(ctx, p, JS_DupValue(ctx, val), + flags); + } else { + res = TRUE; + } + /* still need to reset the writable flag if + needed. The JS_PROP_LENGTH is kept because the + Uint32 test is still done if the length + property is read-only. */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + JS_PROP_HAS_WRITABLE) { + prs = get_shape_prop(p->shape); + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~JS_PROP_WRITABLE)) + return -1; + } + return res; } else { if (flags & JS_PROP_HAS_VALUE) { JS_FreeValue(ctx, pr->u.value); @@ -9194,9 +9222,9 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, typed_array_oob: return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); } - prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); + prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || - prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE)) { + prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); } if (flags & JS_PROP_HAS_VALUE) { @@ -9581,9 +9609,8 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, set_value(ctx, &pr->u.value, val); return 0; } - flags = JS_PROP_THROW_STRICT; - if (flag != 2 && is_strict_mode(ctx)) + if (is_strict_mode(ctx)) flags |= JS_PROP_NO_ADD; return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); } @@ -10918,11 +10945,10 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) } static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val) + JSValue val, BOOL is_array_ctor) { uint32_t tag, len; - redo: tag = JS_VALUE_GET_TAG(val); switch(tag) { case JS_TAG_INT: @@ -10959,16 +10985,36 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, double d; d = JS_VALUE_GET_FLOAT64(val); len = (uint32_t)d; - if (len != d) { - fail: - JS_ThrowRangeError(ctx, "invalid array length"); - return -1; - } + if (len != d) + goto fail; } else { - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - goto redo; + uint32_t len1; + + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } } break; } @@ -15031,15 +15077,6 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) if (prs->flags & JS_PROP_ENUMERABLE) goto normal_case; } - /* the implicit GetOwnProperty raises an exception if the - typed array is detached */ - if ((p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) && - typed_array_is_detached(ctx, p) && - typed_array_get_length(ctx, p) != 0) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } /* for fast arrays, we only store the number of elements */ it->is_array = TRUE; it->array_length = p->u.array.count; @@ -15349,16 +15386,6 @@ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) return 0; } -static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp) -{ - JSValue result; - result = JS_Call(ctx, sp[-2], sp[-3], 0, NULL); - if (JS_IsException(result)) - return -1; - sp[0] = result; - return 0; -} - static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, BOOL *pdone) { @@ -15663,7 +15690,7 @@ static JSValue js_closure2(JSContext *ctx, JSValue func_obj, return JS_EXCEPTION; } -static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) { JSValue obj, this_val; int ret; @@ -15671,15 +15698,17 @@ static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, vo this_val = JS_MKPTR(JS_TAG_OBJECT, p); obj = JS_NewObject(ctx); if (JS_IsException(obj)) - return -1; + return JS_EXCEPTION; set_cycle_flag(ctx, obj); set_cycle_flag(ctx, this_val); ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, JS_DupValue(ctx, this_val), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (JS_DefinePropertyValue(ctx, this_val, atom, obj, JS_PROP_WRITABLE) < 0 || ret < 0) - return -1; - return 0; + if (ret < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; } static const uint16_t func_kind_to_class_id[] = { @@ -16645,11 +16674,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_Throw(ctx, *--sp); goto exception; - CASE(OP_throw_var): + CASE(OP_throw_error): #define JS_THROW_VAR_RO 0 #define JS_THROW_VAR_REDECL 1 #define JS_THROW_VAR_UNINITIALIZED 2 -#define JS_THROW_VAR_DELETE_SUPER 3 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 { JSAtom atom; int type; @@ -16665,9 +16695,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, if (type == JS_THROW_VAR_UNINITIALIZED) JS_ThrowReferenceErrorUninitialized(ctx, atom); else - if (type == JS_THROW_VAR_DELETE_SUPER) + if (type == JS_THROW_ERROR_DELETE_SUPER) JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); else + if (type == JS_THROW_ERROR_ITERATOR_THROW) + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + else JS_ThrowInternalError(ctx, "invalid throw var type %d", type); } goto exception; @@ -16995,7 +17028,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, pc += 2; val = *var_refs[idx]->pvalue; if (unlikely(JS_IsUninitialized(val))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } sp[0] = JS_DupValue(ctx, val); @@ -17008,7 +17041,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } set_value(ctx, var_refs[idx]->pvalue, sp[-1]); @@ -17021,7 +17054,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } set_value(ctx, var_refs[idx]->pvalue, sp[-1]); @@ -17042,7 +17075,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); goto exception; } sp[0] = JS_DupValue(ctx, var_buf[idx]); @@ -17055,7 +17088,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); goto exception; } set_value(ctx, &var_buf[idx], sp[-1]); @@ -17294,16 +17327,17 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp += 1; *sp++ = JS_NewCatchOffset(ctx, 0); BREAK; - CASE(OP_for_await_of_next): - if (js_for_await_of_next(ctx, sp)) - goto exception; - sp += 1; - BREAK; CASE(OP_iterator_get_value_done): if (js_iterator_get_value_done(ctx, sp)) goto exception; sp += 1; BREAK; + CASE(OP_iterator_check_object): + if (unlikely(!JS_IsObject(sp[-1]))) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto exception; + } + BREAK; CASE(OP_iterator_close): /* iter_obj next catch_offset -> */ @@ -17340,37 +17374,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; - CASE(OP_async_iterator_close): - /* iter_obj next catch_offset -> value flag */ - { - JSValue ret, method; - int ret_flag; - sp--; /* remove the catch offset */ - method = JS_GetProperty(ctx, sp[-2], JS_ATOM_return); - if (JS_IsException(method)) - goto exception; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - ret = JS_UNDEFINED; - ret_flag = TRUE; - } else { - ret = JS_CallFree(ctx, method, sp[-2], 0, NULL); - if (JS_IsException(ret)) - goto exception; - if (!JS_IsObject(ret)) { - JS_FreeValue(ctx, ret); - JS_ThrowTypeErrorNotAnObject(ctx); - goto exception; - } - ret_flag = FALSE; - } - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp[-2] = ret; - sp[-1] = JS_NewBool(ctx, ret_flag); - } - BREAK; - - CASE(OP_async_iterator_next): + CASE(OP_iterator_next): /* stack: iter_obj next catch_offset val */ { JSValue ret; @@ -17383,26 +17387,28 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; - CASE(OP_async_iterator_get): + CASE(OP_iterator_call): /* stack: iter_obj next catch_offset val */ { JSValue method, ret; BOOL ret_flag; int flags; flags = *pc++; - /* XXX: use another opcode such as OP_throw_var */ - if (flags == 2) { - JS_ThrowTypeError(ctx, "iterator does not have a throw method"); - goto exception; - } - method = JS_GetProperty(ctx, sp[-4], flags ? JS_ATOM_throw : JS_ATOM_return); + method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? + JS_ATOM_throw : JS_ATOM_return); if (JS_IsException(method)) goto exception; if (JS_IsUndefined(method) || JS_IsNull(method)) { ret_flag = TRUE; } else { - ret = JS_CallFree(ctx, method, sp[-4], - 1, (JSValueConst *)(sp - 1)); + if (flags & 2) { + /* no argument */ + ret = JS_CallFree(ctx, method, sp[-4], + 0, NULL); + } else { + ret = JS_CallFree(ctx, method, sp[-4], + 1, (JSValueConst *)(sp - 1)); + } if (JS_IsException(ret)) goto exception; JS_FreeValue(ctx, sp[-1]); @@ -17728,7 +17734,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_put_ref_value): { - int ret; + int ret, flags; + flags = JS_PROP_THROW_STRICT; if (unlikely(JS_IsUndefined(sp[-3]))) { if (is_strict_mode(ctx)) { JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); @@ -17740,8 +17747,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } else { sp[-3] = JS_DupValue(ctx, ctx->global_obj); } + } else { + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; } - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); JS_FreeValue(ctx, sp[-3]); sp -= 3; if (unlikely(ret < 0)) @@ -18084,8 +18094,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, var_buf[idx] = JS_NewInt32(ctx, val + 1); } else { inc_loc_slow: - if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_inc)) + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = JS_DupValue(ctx, op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) goto exception; + set_value(ctx, &var_buf[idx], op1); } } BREAK; @@ -18105,8 +18119,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, var_buf[idx] = JS_NewInt32(ctx, val - 1); } else { dec_loc_slow: - if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_dec)) + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = JS_DupValue(ctx, op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) goto exception; + set_value(ctx, &var_buf[idx], op1); } } BREAK; @@ -18432,6 +18450,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, set_value(ctx, &sp[-1], val); break; case OP_with_put_var: + /* XXX: check if strict mode */ ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], JS_PROP_THROW_STRICT); JS_FreeValue(ctx, sp[-1]); @@ -18922,13 +18941,11 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); JSStackFrame *sf; JSValue ret, func_ret; - JSValueConst iter_args[1]; *pdone = TRUE; if (!s) return JS_ThrowTypeError(ctx, "not a generator"); sf = &s->func_state.frame; - redo: switch(s->state) { default: case JS_GENERATOR_STATE_SUSPENDED_START: @@ -18940,85 +18957,16 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, } break; case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: - { - int done; - JSValue method, iter_obj; - - iter_obj = sf->cur_sp[-2]; - if (magic == GEN_MAGIC_NEXT) { - method = JS_DupValue(ctx, sf->cur_sp[-1]); - } else { - method = JS_GetProperty(ctx, iter_obj, - magic == GEN_MAGIC_RETURN ? - JS_ATOM_return : JS_ATOM_throw); - if (JS_IsException(method)) - goto iter_exception; - } - if (magic != GEN_MAGIC_NEXT && - (JS_IsUndefined(method) || JS_IsNull(method))) { - /* default action */ - if (magic == GEN_MAGIC_RETURN) { - ret = JS_DupValue(ctx, argv[0]); - goto iter_done; - } else { - if (JS_IteratorClose(ctx, iter_obj, FALSE)) - goto iter_exception; - JS_ThrowTypeError(ctx, "iterator does not have a throw method"); - goto iter_exception; - } - } - ret = JS_IteratorNext2(ctx, iter_obj, method, argc, argv, &done); - JS_FreeValue(ctx, method); - if (JS_IsException(ret)) { - iter_exception: - goto exec_throw; - } - /* if not done, the iterator returns the exact object - returned by 'method' */ - if (done == 2) { - JSValue done_val, value; - done_val = JS_GetProperty(ctx, ret, JS_ATOM_done); - if (JS_IsException(done_val)) { - JS_FreeValue(ctx, ret); - goto iter_exception; - } - done = JS_ToBoolFree(ctx, done_val); - if (done) { - value = JS_GetProperty(ctx, ret, JS_ATOM_value); - JS_FreeValue(ctx, ret); - if (JS_IsException(value)) - goto iter_exception; - ret = value; - goto iter_done; - } else { - *pdone = 2; - } - } else { - if (done) { - /* 'yield *' returns the value associated to done = true */ - iter_done: - JS_FreeValue(ctx, sf->cur_sp[-2]); - JS_FreeValue(ctx, sf->cur_sp[-1]); - sf->cur_sp--; - goto exec_arg; - } else { - *pdone = FALSE; - } - } - break; - } - break; case JS_GENERATOR_STATE_SUSPENDED_YIELD: /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ ret = JS_DupValue(ctx, argv[0]); - if (magic == GEN_MAGIC_THROW) { + if (magic == GEN_MAGIC_THROW && + s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { JS_Throw(ctx, ret); - exec_throw: s->func_state.throw_flag = TRUE; } else { - exec_arg: sf->cur_sp[-1] = ret; - sf->cur_sp[0] = JS_NewBool(ctx, (magic == GEN_MAGIC_RETURN)); + sf->cur_sp[0] = JS_NewInt32(ctx, magic); sf->cur_sp++; exec_no_arg: s->func_state.throw_flag = FALSE; @@ -19032,17 +18980,14 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, return func_ret; } if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + /* get the returned yield value at the top of the stack */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { - /* 'yield *' */ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; - iter_args[0] = JS_UNDEFINED; - argc = 1; - argv = iter_args; - goto redo; + /* return (value, done) object */ + *pdone = 2; } else { - /* get the return the yield value at the top of the stack */ - ret = sf->cur_sp[-1]; - sf->cur_sp[-1] = JS_UNDEFINED; *pdone = FALSE; } } else { @@ -19853,15 +19798,15 @@ typedef struct BlockEnv { int has_iterator; } BlockEnv; -typedef struct JSHoistedDef { - int cpool_idx; /* -1 means variable global definition */ - uint8_t force_init : 1; /* initialize to undefined */ +typedef struct JSGlobalVar { + int cpool_idx; /* if >= 0, index in the constant pool for hoisted + function defintion*/ + uint8_t force_init : 1; /* force initialization to undefined */ uint8_t is_lexical : 1; /* global let/const definition */ uint8_t is_const : 1; /* const definition */ - int var_idx; /* function object index if cpool_idx >= 0 */ int scope_level; /* scope of definition */ - JSAtom var_name; /* variable name if cpool_idx < 0 */ -} JSHoistedDef; + JSAtom var_name; /* variable name */ +} JSGlobalVar; typedef struct RelocEntry { struct RelocEntry *next; @@ -19924,6 +19869,7 @@ typedef struct JSFunctionDef { BOOL has_home_object; /* TRUE if the home object is available */ BOOL has_prototype; /* true if a prototype field is necessary */ BOOL has_simple_parameter_list; + BOOL has_parameter_expressions; /* if true, an argument scope is created */ BOOL has_use_strict; /* to reject directive in special cases */ BOOL has_eval_call; /* true if the function contains a call to eval() */ BOOL has_arguments_binding; /* true if the 'arguments' binding is @@ -19951,7 +19897,10 @@ typedef struct JSFunctionDef { int arg_count; /* number of arguments */ int defined_arg_count; int var_object_idx; /* -1 if none */ + int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ int arguments_var_idx; /* -1 if none */ + int arguments_arg_idx; /* argument variable definition in argument scope, + -1 if none */ int func_var_idx; /* variable containing the current function (-1 if none, only used if is_func_expr is true) */ int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ @@ -19967,10 +19916,11 @@ typedef struct JSFunctionDef { int scope_count; /* number of entries used in the fd->scopes array */ JSVarScope *scopes; JSVarScope def_scope_array[4]; + int body_scope; /* scope of the body of the function or eval */ - int hoisted_def_count; - int hoisted_def_size; - JSHoistedDef *hoisted_def; + int global_var_count; + int global_var_size; + JSGlobalVar *global_vars; DynBuf byte_code; int last_opcode_pos; /* -1 if no last opcode */ @@ -21475,7 +21425,7 @@ static BOOL js_is_live_code(JSParseState *s) { case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_goto: #if SHORT_OPCODES case OP_goto8: @@ -21647,6 +21597,21 @@ static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) return find_arg(ctx, fd, name); } +/* find a variable declaration in a given scope */ +static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_level) +{ + int scope_idx; + for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0; + scope_idx = fd->vars[scope_idx].scope_next) { + if (fd->vars[scope_idx].scope_level != scope_level) + break; + if (fd->vars[scope_idx].var_name == name) + return scope_idx; + } + return -1; +} + /* return true if scope == parent_scope or if scope is a child of parent_scope */ static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd, @@ -21668,7 +21633,7 @@ static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, for(i = 0; i < fd->var_count; i++) { JSVarDef *vd = &fd->vars[i]; if (vd->var_name == name && vd->scope_level == 0) { - if (is_child_scope(ctx, fd, vd->func_pool_or_scope_idx, + if (is_child_scope(ctx, fd, vd->scope_next, scope_level)) return i; } @@ -21677,11 +21642,11 @@ static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, } -static JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name) +static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name) { int i; - for(i = 0; i < fd->hoisted_def_count; i++) { - JSHoistedDef *hf = &fd->hoisted_def[i]; + for(i = 0; i < fd->global_var_count; i++) { + JSGlobalVar *hf = &fd->global_vars[i]; if (hf->var_name == name) return hf; } @@ -21689,9 +21654,9 @@ static JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name) } -static JSHoistedDef *find_lexical_hoisted_def(JSFunctionDef *fd, JSAtom name) +static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name) { - JSHoistedDef *hf = find_hoisted_def(fd, name); + JSGlobalVar *hf = find_global_var(fd, name); if (hf && hf->is_lexical) return hf; else @@ -21711,7 +21676,7 @@ static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name, } if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) { - if (find_lexical_hoisted_def(fd, name)) + if (find_lexical_global_var(fd, name)) return GLOBAL_VAR_OFFSET; } return -1; @@ -21800,6 +21765,7 @@ static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) vd = &fd->vars[fd->var_count++]; memset(vd, 0, sizeof(*vd)); vd->var_name = JS_DupAtom(ctx, name); + vd->func_pool_idx = -1; return fd->var_count - 1; } @@ -21823,22 +21789,47 @@ static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) int idx = fd->func_var_idx; if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { fd->func_var_idx = idx; - fd->vars[idx].is_func_var = TRUE; + fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME; if (fd->js_mode & JS_MODE_STRICT) fd->vars[idx].is_const = TRUE; } return idx; } -static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) +static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd) { int idx = fd->arguments_var_idx; - if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { + if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) { fd->arguments_var_idx = idx; } return idx; } +/* add an argument definition in the argument scope. Only needed when + "eval()" may be called in the argument scope. Return 0 if OK. */ +static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd) +{ + int idx; + if (fd->arguments_arg_idx < 0) { + idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX); + if (idx < 0) { + /* XXX: the scope links are not fully updated. May be an + issue if there are child scopes of the argument + scope */ + idx = add_var(ctx, fd, JS_ATOM_arguments); + if (idx < 0) + return -1; + fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first; + fd->scopes[ARG_SCOPE_INDEX].first = idx; + fd->vars[idx].scope_level = ARG_SCOPE_INDEX; + fd->vars[idx].is_lexical = TRUE; + + fd->arguments_arg_idx = idx; + } + } + return 0; +} + static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) { JSVarDef *vd; @@ -21854,32 +21845,27 @@ static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) vd = &fd->args[fd->arg_count++]; memset(vd, 0, sizeof(*vd)); vd->var_name = JS_DupAtom(ctx, name); + vd->func_pool_idx = -1; return fd->arg_count - 1; } -/* add a Hoisted definition for a function (cpool_idx >= 0) or a - global variable (cpool_idx = -1) */ -static JSHoistedDef *add_hoisted_def(JSContext *ctx, - JSFunctionDef *s, int cpool_idx, - JSAtom name, int var_idx, BOOL is_lexical) +/* add a global variable definition */ +static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s, + JSAtom name) { - JSHoistedDef *hf; + JSGlobalVar *hf; - if (js_resize_array(ctx, (void **)&s->hoisted_def, - sizeof(s->hoisted_def[0]), - &s->hoisted_def_size, s->hoisted_def_count + 1)) + if (js_resize_array(ctx, (void **)&s->global_vars, + sizeof(s->global_vars[0]), + &s->global_var_size, s->global_var_count + 1)) return NULL; - hf = &s->hoisted_def[s->hoisted_def_count++]; - hf->cpool_idx = cpool_idx; - hf->force_init = 0; - hf->is_lexical = is_lexical; + hf = &s->global_vars[s->global_var_count++]; + hf->cpool_idx = -1; + hf->force_init = FALSE; + hf->is_lexical = FALSE; hf->is_const = FALSE; - hf->var_idx = var_idx; hf->scope_level = s->scope_level; - hf->var_name = JS_ATOM_NULL; - if (name != JS_ATOM_NULL) { - hf->var_name = JS_DupAtom(ctx, name); - } + hf->var_name = JS_DupAtom(ctx, name); return hf; } @@ -21924,7 +21910,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, goto redef_lex_error; } } else { - if (fd->scope_level == 1) { + if (fd->scope_level == fd->body_scope) { redef_lex_error: /* redefining a scoped var in the same scope: error */ return js_parse_error(s, "invalid redefinition of lexical identifier"); @@ -21933,7 +21919,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, } if (var_def_type != JS_VAR_DEF_FUNCTION_DECL && var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL && - fd->scope_level == 1 && + fd->scope_level == fd->body_scope && find_arg(ctx, fd, name) >= 0) { /* lexical variable redefines a parameter name */ return js_parse_error(s, "invalid redefinition of parameter name"); @@ -21944,8 +21930,8 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, } if (fd->is_global_var) { - JSHoistedDef *hf; - hf = find_hoisted_def(fd, name); + JSGlobalVar *hf; + hf = find_global_var(fd, name); if (hf && is_child_scope(ctx, fd, hf->scope_level, fd->scope_level)) { return js_parse_error(s, "invalid redefinition of global identifier"); @@ -21955,11 +21941,12 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, if (fd->is_eval && (fd->eval_type == JS_EVAL_TYPE_GLOBAL || fd->eval_type == JS_EVAL_TYPE_MODULE) && - fd->scope_level == 1) { - JSHoistedDef *hf; - hf = add_hoisted_def(s->ctx, fd, -1, name, -1, TRUE); + fd->scope_level == fd->body_scope) { + JSGlobalVar *hf; + hf = add_global_var(s->ctx, fd, name); if (!hf) return -1; + hf->is_lexical = TRUE; hf->is_const = (var_def_type == JS_VAR_DEF_CONST); idx = GLOBAL_VAR_OFFSET; } else { @@ -21991,13 +21978,13 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, return js_parse_error(s, "invalid redefinition of lexical identifier"); } if (fd->is_global_var) { - JSHoistedDef *hf; - hf = find_hoisted_def(fd, name); + JSGlobalVar *hf; + hf = find_global_var(fd, name); if (hf && hf->is_lexical && hf->scope_level == fd->scope_level && fd->eval_type == JS_EVAL_TYPE_MODULE) { goto invalid_lexical_redefinition; } - hf = add_hoisted_def(s->ctx, fd, -1, name, -1, FALSE); + hf = add_global_var(s->ctx, fd, name); if (!hf) return -1; idx = GLOBAL_VAR_OFFSET; @@ -22010,7 +21997,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, if (idx >= 0) { if (name == JS_ATOM_arguments && fd->has_arguments_binding) fd->arguments_var_idx = idx; - fd->vars[idx].func_pool_or_scope_idx = fd->scope_level; + fd->vars[idx].scope_next = fd->scope_level; } } break; @@ -22052,8 +22039,9 @@ static __exception int js_parse_function_decl2(JSParseState *s, int function_line_num, JSParseExportEnum export_flag, JSFunctionDef **pfd); -static __exception int js_parse_assign_expr(JSParseState *s, int in_accepted); -static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag); +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); +static __exception int js_parse_assign_expr(JSParseState *s); +static __exception int js_parse_unary(JSParseState *s, int parse_flags); static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, JSAtom label_name, int label_break, int label_cont, @@ -22385,8 +22373,9 @@ static BOOL is_regexp_allowed(int tok) } } -#define SKIP_HAS_SEMI (1 << 0) -#define SKIP_HAS_ELLIPSIS (1 << 1) +#define SKIP_HAS_SEMI (1 << 0) +#define SKIP_HAS_ELLIPSIS (1 << 1) +#define SKIP_HAS_ASSIGNMENT (1 << 2) /* XXX: improve speed with early bailout */ /* XXX: no longer works if regexps are present. Could use previous @@ -22459,7 +22448,10 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ bits |= SKIP_HAS_ELLIPSIS; } break; - + case '=': + bits |= SKIP_HAS_ASSIGNMENT; + break; + case TOK_DIV_ASSIGN: tok_len = 2; goto parse_regexp; @@ -22574,7 +22566,7 @@ static __exception int js_parse_object_literal(JSParseState *s) if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; emit_op(s, OP_null); /* dummy excludeList */ emit_op(s, OP_copy_data_properties); @@ -22633,7 +22625,7 @@ static __exception int js_parse_object_literal(JSParseState *s) } else { if (js_parse_expect(s, ':')) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; if (name == JS_ATOM_NULL) { set_object_name_computed(s); @@ -22668,8 +22660,23 @@ static __exception int js_parse_object_literal(JSParseState *s) return -1; } -static __exception int js_parse_postfix_expr(JSParseState *s, - BOOL accept_lparen); +/* allow the 'in' binary operator */ +#define PF_IN_ACCEPTED (1 << 0) +/* allow function calls parsing in js_parse_postfix_expr() */ +#define PF_POSTFIX_CALL (1 << 1) +/* allow arrow functions parsing in js_parse_postfix_expr() */ +#define PF_ARROW_FUNC (1 << 2) +/* allow the exponentiation operator in js_parse_unary() */ +#define PF_POW_ALLOWED (1 << 3) +/* forbid the exponentiation operator in js_parse_unary() */ +#define PF_POW_FORBIDDEN (1 << 4) + +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); + +static __exception int js_parse_left_hand_side_expr(JSParseState *s) +{ + return js_parse_postfix_expr(s, PF_POSTFIX_CALL); +} /* XXX: is there is nicer solution ? */ static __exception int js_parse_class_default_ctor(JSParseState *s, @@ -22873,8 +22880,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, class_flags = JS_DEFINE_CLASS_HAS_HERITAGE; if (next_token(s)) goto fail; - /* XXX: the grammar only allows LeftHandSideExpression */ - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) goto fail; } else { emit_op(s, OP_undefined); @@ -23081,7 +23087,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, if (s->token.val == '=') { if (next_token(s)) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; } else { emit_op(s, OP_undefined); @@ -23285,7 +23291,7 @@ static __exception int js_parse_array_literal(JSParseState *s) while (s->token.val != ']' && idx < 32) { if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS) break; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; idx++; /* accept trailing comma */ @@ -23306,7 +23312,7 @@ static __exception int js_parse_array_literal(JSParseState *s) break; need_length = TRUE; if (s->token.val != ',') { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; emit_op(s, OP_define_field); emit_u32(s, __JS_AtomFromUInt32(idx)); @@ -23341,7 +23347,7 @@ static __exception int js_parse_array_literal(JSParseState *s) if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; #if 1 emit_op(s, OP_append); @@ -23373,7 +23379,7 @@ static __exception int js_parse_array_literal(JSParseState *s) } else { need_length = TRUE; if (s->token.val != ',') { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; /* a idx val */ emit_op(s, OP_define_array_el); @@ -23781,6 +23787,8 @@ fail: return JS_ATOM_NULL; } +/* Return -1 if error, 0 if no initializer, 1 if an initializer is + present at the top level. */ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, int hasval, int has_ellipsis, BOOL allow_initializer) @@ -23789,7 +23797,8 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, int start_addr, assign_addr; JSAtom prop_name, var_name; int opcode, scope, tok1, skip_bits; - + BOOL has_initializer; + if (has_ellipsis < 0) { /* pre-parse destructuration target for spread detection */ js_parse_skip_parens_token(s, &skip_bits, FALSE); @@ -23842,7 +23851,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, label_lvalue = -1; depth_lvalue = 0; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, @@ -23898,7 +23907,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_op(s, OP_get_field2); emit_u32(s, prop_name); } - if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE)) + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0) return -1; if (s->token.val == '}') break; @@ -23935,7 +23944,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, if (var_name == JS_ATOM_NULL) goto prop_error; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) goto prop_error; lvalue: if (get_lvalue(s, &opcode, &scope, &var_name, @@ -24033,7 +24042,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, if (next_token(s)) goto var_error; emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto var_error; if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) set_object_name(s, var_name); @@ -24097,7 +24106,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_u8(s, 0); emit_op(s, OP_drop); } - if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { var_name = JS_ATOM_NULL; @@ -24111,7 +24120,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, opcode = OP_scope_get_var; scope = s->cur_func->scope_level; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, &label_lvalue, &enum_depth, FALSE, '[')) { @@ -24135,7 +24144,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, if (next_token(s)) goto var_error; emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto var_error; if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) set_object_name(s, var_name); @@ -24170,10 +24179,11 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_label(s, label_parse); if (hasval) emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; emit_goto(s, OP_goto, label_assign); emit_label(s, label_done); + has_initializer = TRUE; } else { /* normally hasval is true except if js_parse_skip_parens_token() was wrong in the parsing */ @@ -24186,8 +24196,9 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, memset(s->cur_func->byte_code.buf + start_addr, OP_nop, assign_addr - start_addr); s->cur_func->label_slots[label_parse].ref_count--; + has_initializer = FALSE; } - return 0; + return has_initializer; prop_error: JS_FreeAtom(s->ctx, prop_name); @@ -24220,10 +24231,12 @@ static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, emit_label(s, label_next); } -static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen) +/* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */ +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) { FuncCallType call_type; int optional_chaining_label; + BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0; call_type = FUNC_CALL_NORMAL; switch(s->token.val) { @@ -24321,7 +24334,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } break; case '(': - if (js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { + if ((parse_flags & PF_ARROW_FUNC) && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, JS_FUNC_NORMAL, JS_ATOM_NULL, s->token.ptr, s->token.line_num)) @@ -24369,7 +24383,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen if (s->token.u.ident.is_reserved) { return js_parse_error_reserved_identifier(s); } - if (peek_token(s, TRUE) == TOK_ARROW) { + if ((parse_flags & PF_ARROW_FUNC) && + peek_token(s, TRUE) == TOK_ARROW) { if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, JS_FUNC_NORMAL, JS_ATOM_NULL, s->token.ptr, s->token.line_num)) @@ -24388,10 +24403,11 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen JS_FUNC_ASYNC, JS_ATOM_NULL, source_ptr, source_line_num)) return -1; - } else if ((s->token.val == '(' && - js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || - (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && - peek_token(s, TRUE) == TOK_ARROW)) { + } else if ((parse_flags & PF_ARROW_FUNC) && + ((s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && + peek_token(s, TRUE) == TOK_ARROW))) { if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, JS_FUNC_ASYNC, JS_ATOM_NULL, source_ptr, source_line_num)) @@ -24421,7 +24437,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen { int skip_bits; if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { - if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) + if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { if (s->token.val == '{') { @@ -24450,7 +24466,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_atom(s, JS_ATOM_new_target); emit_u16(s, 0); } else { - if (js_parse_postfix_expr(s, FALSE)) + if (js_parse_postfix_expr(s, 0)) return -1; accept_lparen = TRUE; if (s->token.val != '(') { @@ -24503,7 +24519,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen return -1; if (!accept_lparen) return js_parse_error(s, "invalid use of 'import()'"); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; if (js_parse_expect(s, ')')) return -1; @@ -24636,7 +24652,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } if (s->token.val == TOK_ELLIPSIS) break; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; arg_count++; if (s->token.val == ')') @@ -24656,7 +24672,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; #if 1 /* XXX: could pass is_last indicator? */ @@ -24687,7 +24703,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_op(s, OP_nip1); #endif } else { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; /* array idx val */ emit_op(s, OP_define_array_el); @@ -24789,16 +24805,19 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } else if (s->token.val == '.') { if (next_token(s)) return -1; + parse_property: if (s->token.val == TOK_PRIVATE_NAME) { /* private class field */ if (get_prev_opcode(fd) == OP_get_super) { return js_parse_error(s, "private class field forbidden after super"); } + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } emit_op(s, OP_scope_get_private_field); emit_atom(s, s->token.u.ident.atom); emit_u16(s, s->cur_func->scope_level); } else { - parse_property: if (!token_is_ident(s->token.val)) { return js_parse_error(s, "expecting field name"); } @@ -24857,7 +24876,7 @@ static __exception int js_parse_delete(JSParseState *s) if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; switch(opcode = get_prev_opcode(fd)) { case OP_get_field: @@ -24896,9 +24915,9 @@ static __exception int js_parse_delete(JSParseState *s) case OP_scope_get_private_field: return js_parse_error(s, "cannot delete a private class field"); case OP_get_super_value: - emit_op(s, OP_throw_var); + emit_op(s, OP_throw_error); emit_atom(s, JS_ATOM_NULL); - emit_u8(s, JS_THROW_VAR_DELETE_SUPER); + emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); break; default: ret_true: @@ -24909,7 +24928,8 @@ static __exception int js_parse_delete(JSParseState *s) return 0; } -static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) +/* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */ +static __exception int js_parse_unary(JSParseState *s, int parse_flags) { int op; @@ -24922,7 +24942,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) op = s->token.val; if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; switch(op) { case '-': @@ -24944,7 +24964,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) default: abort(); } - exponentiation_flag = 0; + parse_flags = 0; break; case TOK_DEC: case TOK_INC: @@ -24954,7 +24974,6 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) op = s->token.val; if (next_token(s)) return -1; - /* XXX: should parse LeftHandSideExpression */ if (js_parse_unary(s, 0)) return -1; if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) @@ -24969,7 +24988,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) JSFunctionDef *fd; if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; /* reference access should not return an exception, so we patch the get_var */ @@ -24978,13 +24997,13 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef; } emit_op(s, OP_typeof); - exponentiation_flag = 0; + parse_flags = 0; } break; case TOK_DELETE: if (js_parse_delete(s)) return -1; - exponentiation_flag = 0; + parse_flags = 0; break; case TOK_AWAIT: if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) @@ -24993,13 +25012,14 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) return js_parse_error(s, "await in default expression"); if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; emit_op(s, OP_await); - exponentiation_flag = 0; + parse_flags = 0; break; default: - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) | + PF_POSTFIX_CALL)) return -1; if (!s->got_lf && (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { @@ -25016,21 +25036,21 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) } break; } - if (exponentiation_flag) { + if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) { #ifdef CONFIG_BIGNUM if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) { /* Extended exponentiation syntax rules: we extend the ES7 grammar in order to have more intuitive semantics: -2**2 evaluates to -4. */ if (!(s->cur_func->js_mode & JS_MODE_MATH)) { - if (exponentiation_flag < 0) { + if (parse_flags & PF_POW_FORBIDDEN) { JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); return -1; } } if (next_token(s)) return -1; - if (js_parse_unary(s, 1)) + if (js_parse_unary(s, PF_POW_ALLOWED)) return -1; emit_op(s, OP_pow); } @@ -25041,13 +25061,13 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) regarding the precedence of prefix operators and the postifx exponential, ES7 specifies that -2**2 is a syntax error. */ - if (exponentiation_flag < 0) { + if (parse_flags & PF_POW_FORBIDDEN) { JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); return -1; } if (next_token(s)) return -1; - if (js_parse_unary(s, 1)) + if (js_parse_unary(s, PF_POW_ALLOWED)) return -1; emit_op(s, OP_pow); } @@ -25056,15 +25076,17 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) return 0; } +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ static __exception int js_parse_expr_binary(JSParseState *s, int level, - BOOL in_accepted) + int parse_flags) { int op, opcode; if (level == 0) { - return js_parse_unary(s, 1); + return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) | + PF_POW_ALLOWED); } - if (js_parse_expr_binary(s, level - 1, in_accepted)) + if (js_parse_expr_binary(s, level - 1, parse_flags)) return -1; for(;;) { op = s->token.val; @@ -25134,7 +25156,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, opcode = OP_instanceof; break; case TOK_IN: - if (in_accepted) { + if (parse_flags & PF_IN_ACCEPTED) { opcode = OP_in; } else { return 0; @@ -25194,23 +25216,24 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, } if (next_token(s)) return -1; - if (js_parse_expr_binary(s, level - 1, in_accepted)) + if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC)) return -1; emit_op(s, opcode); } return 0; } +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ static __exception int js_parse_logical_and_or(JSParseState *s, int op, - BOOL in_accepted) + int parse_flags) { int label1; if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, in_accepted)) + if (js_parse_expr_binary(s, 8, parse_flags)) return -1; } else { - if (js_parse_logical_and_or(s, TOK_LAND, in_accepted)) + if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) return -1; } if (s->token.val == op) { @@ -25224,10 +25247,11 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, emit_op(s, OP_drop); if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, in_accepted)) + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) return -1; } else { - if (js_parse_logical_and_or(s, TOK_LAND, in_accepted)) + if (js_parse_logical_and_or(s, TOK_LAND, + parse_flags & ~PF_ARROW_FUNC)) return -1; } if (s->token.val != op) { @@ -25242,11 +25266,11 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, return 0; } -static __exception int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted) +static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) { int label1; - if (js_parse_logical_and_or(s, TOK_LOR, in_accepted)) + if (js_parse_logical_and_or(s, TOK_LOR, parse_flags)) return -1; if (s->token.val == TOK_DOUBLE_QUESTION_MARK) { label1 = new_label(s); @@ -25259,7 +25283,7 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted) emit_goto(s, OP_if_false, label1); emit_op(s, OP_drop); - if (js_parse_expr_binary(s, 8, in_accepted)) + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) return -1; if (s->token.val != TOK_DOUBLE_QUESTION_MARK) break; @@ -25269,18 +25293,19 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted) return 0; } -static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ +static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) { int label1, label2; - if (js_parse_coalesce_expr(s, in_accepted)) + if (js_parse_coalesce_expr(s, parse_flags)) return -1; if (s->token.val == '?') { if (next_token(s)) return -1; label1 = emit_goto(s, OP_if_false, -1); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; if (js_parse_expect(s, ':')) return -1; @@ -25289,7 +25314,7 @@ static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) emit_label(s, label1); - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED)) return -1; emit_label(s, label2); @@ -25299,14 +25324,16 @@ static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) static void emit_return(JSParseState *s, BOOL hasval); -static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) { int opcode, op, scope; JSAtom name0 = JS_ATOM_NULL; JSAtom name; if (s->token.val == TOK_YIELD) { - BOOL is_star = FALSE; + BOOL is_star = FALSE, is_async; + if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR)) return js_parse_error(s, "unexpected 'yield' keyword"); if (!s->cur_func->in_function_body) @@ -25323,109 +25350,120 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) if (next_token(s)) return -1; } - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags)) return -1; } else { emit_op(s, OP_undefined); } - if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR); + + if (is_star) { int label_loop, label_return, label_next; int label_return1, label_yield, label_throw, label_throw1; int label_throw2; - if (is_star) { - label_loop = new_label(s); - label_yield = new_label(s); - - emit_op(s, OP_for_await_of_start); + label_loop = new_label(s); + label_yield = new_label(s); - /* remove the catch offset (XXX: could avoid pushing back - undefined) */ - emit_op(s, OP_drop); - emit_op(s, OP_undefined); - - emit_op(s, OP_undefined); /* initial value */ + emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start); - emit_label(s, label_loop); - emit_op(s, OP_async_iterator_next); + /* remove the catch offset (XXX: could avoid pushing back + undefined) */ + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + + emit_op(s, OP_undefined); /* initial value */ + + emit_label(s, label_loop); + emit_op(s, OP_iterator_next); + if (is_async) emit_op(s, OP_await); - emit_op(s, OP_iterator_get_value_done); - label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ + emit_label(s, label_yield); + if (is_async) { + /* OP_async_yield_star takes the value as parameter */ + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); emit_op(s, OP_await); - emit_label(s, label_yield); emit_op(s, OP_async_yield_star); - emit_op(s, OP_dup); - label_return = emit_goto(s, OP_if_true, -1); - emit_op(s, OP_drop); - emit_goto(s, OP_goto, label_loop); - - emit_label(s, label_return); - emit_op(s, OP_push_i32); - emit_u32(s, 2); - emit_op(s, OP_strict_eq); - label_throw = emit_goto(s, OP_if_true, -1); - - /* return handling */ - emit_op(s, OP_await); - emit_op(s, OP_async_iterator_get); - emit_u8(s, 0); - label_return1 = emit_goto(s, OP_if_true, -1); + } else { + /* OP_yield_star takes (value, done) as parameter */ + emit_op(s, OP_yield_star); + } + emit_op(s, OP_dup); + label_return = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_drop); + emit_goto(s, OP_goto, label_loop); + + emit_label(s, label_return); + emit_op(s, OP_push_i32); + emit_u32(s, 2); + emit_op(s, OP_strict_eq); + label_throw = emit_goto(s, OP_if_true, -1); + + /* return handling */ + if (is_async) emit_op(s, OP_await); - emit_op(s, OP_iterator_get_value_done); - /* XXX: the spec does not indicate that an await should be - performed in case done = true, but the tests assume it */ - emit_goto(s, OP_if_false, label_yield); - - emit_label(s, label_return1); - emit_op(s, OP_nip); - emit_op(s, OP_nip); - emit_op(s, OP_nip); - emit_return(s, TRUE); - - /* throw handling */ - emit_label(s, label_throw); - emit_op(s, OP_async_iterator_get); - emit_u8(s, 1); - label_throw1 = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_iterator_call); + emit_u8(s, 0); + label_return1 = emit_goto(s, OP_if_true, -1); + if (is_async) emit_op(s, OP_await); - emit_op(s, OP_iterator_get_value_done); - emit_goto(s, OP_if_false, label_yield); - /* XXX: the spec does not indicate that an await should be - performed in case done = true, but the tests assume it */ + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_goto(s, OP_if_false, label_yield); + + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + + emit_label(s, label_return1); + emit_op(s, OP_nip); + emit_op(s, OP_nip); + emit_op(s, OP_nip); + emit_return(s, TRUE); + + /* throw handling */ + emit_label(s, label_throw); + emit_op(s, OP_iterator_call); + emit_u8(s, 1); + label_throw1 = emit_goto(s, OP_if_true, -1); + if (is_async) emit_op(s, OP_await); - emit_goto(s, OP_goto, label_next); - /* close the iterator and throw a type error exception */ - emit_label(s, label_throw1); - emit_op(s, OP_async_iterator_get); - emit_u8(s, 0); - label_throw2 = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_goto(s, OP_if_false, label_yield); + emit_goto(s, OP_goto, label_next); + /* close the iterator and throw a type error exception */ + emit_label(s, label_throw1); + emit_op(s, OP_iterator_call); + emit_u8(s, 2); + label_throw2 = emit_goto(s, OP_if_true, -1); + if (is_async) emit_op(s, OP_await); - emit_label(s, label_throw2); - emit_op(s, OP_async_iterator_get); - emit_u8(s, 2); /* throw the type error exception */ - emit_op(s, OP_drop); /* never reached */ + emit_label(s, label_throw2); - emit_label(s, label_next); - emit_op(s, OP_nip); /* keep the value associated with - done = true */ - emit_op(s, OP_nip); - emit_op(s, OP_nip); - } else { - emit_op(s, OP_await); - emit_op(s, OP_yield); - label_next = emit_goto(s, OP_if_false, -1); - emit_return(s, TRUE); - emit_label(s, label_next); - } + emit_op(s, OP_throw_error); + emit_atom(s, JS_ATOM_NULL); + emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW); + + emit_label(s, label_next); + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + emit_op(s, OP_nip); /* keep the value associated with + done = true */ + emit_op(s, OP_nip); + emit_op(s, OP_nip); } else { int label_next; - if (is_star) { - emit_op(s, OP_for_of_start); - emit_op(s, OP_drop); /* drop the catch offset */ - emit_op(s, OP_yield_star); - } else { - emit_op(s, OP_yield); - } + + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_yield); label_next = emit_goto(s, OP_if_false, -1); emit_return(s, TRUE); emit_label(s, label_next); @@ -25436,7 +25474,7 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) /* name0 is used to check for OP_set_name pattern, not duplicated */ name0 = s->token.u.ident.atom; } - if (js_parse_cond_expr(s, in_accepted)) + if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC)) return -1; op = s->token.val; @@ -25447,7 +25485,7 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) return -1; - if (js_parse_assign_expr(s, in_accepted)) { + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(s->ctx, name); return -1; } @@ -25491,7 +25529,7 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) -1); emit_op(s, OP_drop); - if (js_parse_assign_expr(s, in_accepted)) { + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(s->ctx, name); return -1; } @@ -25533,11 +25571,17 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) return 0; } -static __exception int js_parse_expr2(JSParseState *s, BOOL in_accepted) +static __exception int js_parse_assign_expr(JSParseState *s) +{ + return js_parse_assign_expr2(s, PF_IN_ACCEPTED); +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_expr2(JSParseState *s, int parse_flags) { BOOL comma = FALSE; for(;;) { - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags)) return -1; if (comma) { /* prevent get_lvalue from using the last expression @@ -25559,7 +25603,7 @@ static __exception int js_parse_expr2(JSParseState *s, BOOL in_accepted) static __exception int js_parse_expr(JSParseState *s) { - return js_parse_expr2(s, TRUE); + return js_parse_expr2(s, PF_IN_ACCEPTED); } static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, @@ -25655,12 +25699,25 @@ static void emit_return(JSParseState *s, BOOL hasval) } emit_op(s, OP_iterator_close_return); if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { - int label_next; - emit_op(s, OP_async_iterator_close); + int label_next, label_next2; + + emit_op(s, OP_drop); /* catch offset */ + emit_op(s, OP_drop); /* next */ + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_return); + /* stack: iter_obj return_func */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); label_next = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_iterator_check_object); emit_op(s, OP_await); + label_next2 = emit_goto(s, OP_goto, -1); emit_label(s, label_next); emit_op(s, OP_drop); + emit_label(s, label_next2); + emit_op(s, OP_drop); } else { emit_op(s, OP_iterator_close); } @@ -25748,7 +25805,8 @@ static __exception int js_parse_block(JSParseState *s) return 0; } -static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, BOOL export_flag) { JSContext *ctx = s->ctx; @@ -25788,7 +25846,7 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, emit_u16(s, fd->scope_level); if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0) goto var_error; - if (js_parse_assign_expr(s, in_accepted)) { + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(ctx, name1); goto var_error; } @@ -25796,7 +25854,7 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, put_lvalue(s, opcode, scope, name1, label, PUT_LVALUE_NOKEEP, FALSE); } else { - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags)) goto var_error; set_object_name(s, name); emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? @@ -25823,7 +25881,7 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, if ((s->token.val == '[' || s->token.val == '{') && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { emit_op(s, OP_undefined); - if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) + if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { return js_parse_error(s, "variable name expected"); @@ -25952,7 +26010,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { - if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE)) + if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0) return -1; has_destructuring = TRUE; } else { @@ -25978,11 +26036,11 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, int skip_bits; if ((s->token.val == '[' || s->token.val == '{') && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) { - if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) + if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { int lvalue_label; - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, NULL, FALSE, TOK_FOR)) @@ -26002,7 +26060,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, /* parse and evaluate initializer prior to evaluating the object (only used with "for in" with a non lexical variable in non strict mode */ - if (next_token(s) || js_parse_assign_expr(s, FALSE)) { + if (next_token(s) || js_parse_assign_expr2(s, 0)) { JS_FreeAtom(ctx, var_name); return -1; } @@ -26035,7 +26093,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (next_token(s)) return -1; if (is_for_of) { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; } else { if (js_parse_expr(s)) @@ -26088,7 +26146,11 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (is_for_of) { if (is_async) { /* call the next method */ - emit_op(s, OP_for_await_of_next); + /* stack: iter_obj next catch_offset */ + emit_op(s, OP_dup3); + emit_op(s, OP_drop); + emit_op(s, OP_call_method); + emit_u16(s, 0); /* get the result of the promise */ emit_op(s, OP_await); /* unwrap the value and done values */ @@ -26646,7 +26708,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { /* XXX: TOK_LET is not completely correct */ - if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE)) + if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0) goto fail; } else { js_parse_error(s, "identifier expected"); @@ -26712,18 +26774,37 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, } emit_label(s, label_finally); if (s->token.val == TOK_FINALLY) { - int saved_eval_ret_idx; + int saved_eval_ret_idx = 0; /* avoid warning */ + if (next_token(s)) goto fail; /* on the stack: ret_value gosub_ret_value */ push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, -1, -1, 2); - saved_eval_ret_idx = s->cur_func->eval_ret_idx; - s->cur_func->eval_ret_idx = -1; - /* 'finally' does not update eval_ret */ + + if (s->cur_func->eval_ret_idx >= 0) { + /* 'finally' updates eval_ret only if not a normal + termination */ + saved_eval_ret_idx = + add_var(s->ctx, s->cur_func, JS_ATOM__ret_); + if (saved_eval_ret_idx < 0) + goto fail; + emit_op(s, OP_get_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + emit_op(s, OP_put_loc); + emit_u16(s, saved_eval_ret_idx); + set_eval_ret_undefined(s); + } + if (js_parse_block(s)) goto fail; - s->cur_func->eval_ret_idx = saved_eval_ret_idx; + + if (s->cur_func->eval_ret_idx >= 0) { + emit_op(s, OP_get_loc); + emit_u16(s, saved_eval_ret_idx); + emit_op(s, OP_put_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + } pop_break_entry(s->cur_func); } emit_op(s, OP_ret); @@ -27511,19 +27592,11 @@ static int exported_names_cmp(const void *p1, const void *p2, void *opaque) static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m); -static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, - void *opaque) +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque) { JSModuleDef *m = opaque; - JSValue val, this_val = JS_MKPTR(JS_TAG_OBJECT, p); - - val = js_get_module_ns(ctx, m); - if (JS_IsException(val)) - return -1; - if (JS_DefinePropertyValue(ctx, this_val, atom, val, - JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) - return -1; - return 0; + return js_get_module_ns(ctx, m); } static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) @@ -27741,7 +27814,7 @@ static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) return -1; } -/* must be done before js_instantiate_module() because of cyclic references */ +/* must be done before js_link_module() because of cyclic references */ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) { BOOL is_c_module; @@ -27784,7 +27857,7 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) /* Prepare a module to be executed by resolving all the imported variables. */ -static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) +static int js_link_module(JSContext *ctx, JSModuleDef *m) { int i; JSImportEntry *mi; @@ -27792,7 +27865,8 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) JSVarRef **var_refs, *var_ref; JSObject *p; BOOL is_c_module; - + JSValue ret_val; + if (m->instantiated) return 0; m->instantiated = TRUE; @@ -27806,7 +27880,7 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (js_instantiate_module(ctx, rme->module) < 0) + if (js_link_module(ctx, rme->module) < 0) goto fail; } @@ -27925,6 +27999,12 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) me->u.local.var_ref = var_ref; } } + + /* initialize the global variables */ + ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL); + if (JS_IsException(ret_val)) + goto fail; + JS_FreeValue(ctx, ret_val); } #ifdef DUMP_MODULE_RESOLVE @@ -28324,7 +28404,7 @@ static __exception int js_parse_export(JSParseState *s) s->token.ptr, s->token.line_num, JS_PARSE_EXPORT_DEFAULT, NULL); } else { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; } /* set the name of anonymous functions */ @@ -28567,7 +28647,9 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, fd->last_opcode_pos = -1; fd->func_name = JS_ATOM_NULL; fd->var_object_idx = -1; + fd->arg_var_object_idx = -1; fd->arguments_var_idx = -1; + fd->arguments_arg_idx = -1; fd->func_var_idx = -1; fd->eval_ret_idx = -1; fd->this_var_idx = -1; @@ -28576,13 +28658,14 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, fd->home_object_var_idx = -1; /* XXX: should distinguish arg, var and var object and body scopes */ - fd->scope_level = 0; /* 0: var/arg scope, 1:body scope */ - fd->scope_first = -1; fd->scopes = fd->def_scope_array; fd->scope_size = countof(fd->def_scope_array); fd->scope_count = 1; fd->scopes[0].first = -1; fd->scopes[0].parent = -1; + fd->scope_level = 0; /* 0: var/arg scope */ + fd->scope_first = -1; + fd->body_scope = -1; fd->filename = JS_NewAtom(ctx, filename); fd->line_num = line_num; @@ -28663,10 +28746,10 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) } js_free(ctx, fd->args); - for(i = 0; i < fd->hoisted_def_count; i++) { - JS_FreeAtom(ctx, fd->hoisted_def[i].var_name); + for(i = 0; i < fd->global_var_count; i++) { + JS_FreeAtom(ctx, fd->global_vars[i].var_name); } - js_free(ctx, fd->hoisted_def); + js_free(ctx, fd->global_vars); for(i = 0; i < fd->closure_var_count; i++) { JSClosureVar *cv = &fd->closure_var[i]; @@ -29350,19 +29433,27 @@ static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, switch(var_name) { case JS_ATOM_home_object: /* 'home_object' pseudo variable */ - var_idx = s->home_object_var_idx = add_var(ctx, s, var_name); + if (s->home_object_var_idx < 0) + s->home_object_var_idx = add_var(ctx, s, var_name); + var_idx = s->home_object_var_idx; break; case JS_ATOM_this_active_func: /* 'this.active_func' pseudo variable */ - var_idx = s->this_active_func_var_idx = add_var(ctx, s, var_name); + if (s->this_active_func_var_idx < 0) + s->this_active_func_var_idx = add_var(ctx, s, var_name); + var_idx = s->this_active_func_var_idx; break; case JS_ATOM_new_target: /* 'new.target' pseudo variable */ - var_idx = s->new_target_var_idx = add_var(ctx, s, var_name); + if (s->new_target_var_idx < 0) + s->new_target_var_idx = add_var(ctx, s, var_name); + var_idx = s->new_target_var_idx; break; case JS_ATOM_this: /* 'this' pseudo variable */ - var_idx = s->this_var_idx = add_var_this(ctx, s); + if (s->this_var_idx < 0) + s->this_var_idx = add_var_this(ctx, s); + var_idx = s->this_var_idx; break; default: var_idx = -1; @@ -29371,18 +29462,32 @@ static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, return var_idx; } +/* test if 'var_name' is in the variable object on the stack. If is it + the case, handle it and jump to 'label_done' */ +static void var_object_test(JSContext *ctx, JSFunctionDef *s, + JSAtom var_name, int op, DynBuf *bc, + int *plabel_done, BOOL is_with) +{ + dbuf_putc(bc, get_with_scope_opcode(op)); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + *plabel_done = new_label_fd(s, *plabel_done); + dbuf_put_u32(bc, *plabel_done); + dbuf_putc(bc, is_with); + update_label(s, *plabel_done, 1); + s->jump_size++; +} + /* return the position of the next opcode */ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, JSAtom var_name, int scope_level, int op, DynBuf *bc, uint8_t *bc_buf, - LabelSlot *ls, int pos_next, int arg_valid) + LabelSlot *ls, int pos_next) { int idx, var_idx, is_put; int label_done; - BOOL is_func_var = FALSE; JSFunctionDef *fd; JSVarDef *vd; - BOOL is_pseudo_var; + BOOL is_pseudo_var, is_arg_scope; label_done = -1; @@ -29400,12 +29505,11 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (vd->var_name == var_name) { if (op == OP_scope_put_var || op == OP_scope_make_ref) { if (vd->is_const) { - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); goto done; } - is_func_var = vd->is_func_var; } var_idx = idx; break; @@ -29413,33 +29517,16 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { dbuf_putc(bc, OP_get_loc); dbuf_put_u16(bc, idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, 1); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, 1); } idx = vd->scope_next; } + is_arg_scope = (idx == ARG_SCOPE_END); if (var_idx < 0) { - /* XXX: scoping issues: - should not resolve vars from the function body during argument parse, - `arguments` and function-name should not be hidden by later vars. - */ - var_idx = find_var(ctx, s, var_name); - if (var_idx >= 0) { - if (scope_level == 0 - && (var_idx & ARGUMENT_VAR_OFFSET) - && (var_idx - ARGUMENT_VAR_OFFSET) >= arg_valid) { - /* referring to an uninitialized argument */ - dbuf_putc(bc, OP_throw_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, JS_THROW_VAR_UNINITIALIZED); - } - if (!(var_idx & ARGUMENT_VAR_OFFSET)) - is_func_var = s->vars[var_idx].is_func_var; + /* argument scope: variables are not visible but pseudo + variables are visible */ + if (!is_arg_scope) { + var_idx = find_var(ctx, s, var_name); } if (var_idx < 0 && is_pseudo_var) @@ -29448,21 +29535,31 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (var_idx < 0 && var_name == JS_ATOM_arguments && s->has_arguments_binding) { /* 'arguments' pseudo variable */ - var_idx = add_arguments_var(ctx, s, var_name); + var_idx = add_arguments_var(ctx, s); } if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) { /* add a new variable with the function name */ var_idx = add_func_var(ctx, s, var_name); - is_func_var = TRUE; } } if (var_idx >= 0) { + if ((op == OP_scope_put_var || op == OP_scope_make_ref) && + !(var_idx & ARGUMENT_VAR_OFFSET) && + s->vars[var_idx].is_const) { + /* only happens when assigning a function expression name + in strict mode */ + dbuf_putc(bc, OP_throw_error); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, JS_THROW_VAR_RO); + goto done; + } /* OP_scope_put_var_init is only used to initialize a lexical variable, so it is never used in a with or var object. It can be used with a closure (module global variable case). */ switch (op) { case OP_scope_make_ref: - if (is_func_var) { + if (!(var_idx & ARGUMENT_VAR_OFFSET) && + s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { /* Create a dummy object reference for the func_var */ dbuf_putc(bc, OP_object); dbuf_putc(bc, OP_get_loc); @@ -29510,7 +29607,6 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (var_idx & ARGUMENT_VAR_OFFSET) { dbuf_putc(bc, OP_get_arg + is_put); dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); - /* XXX: should test if argument reference needs TDZ check */ } else { if (is_put) { if (s->vars[var_idx].is_lexical) { @@ -29543,41 +29639,32 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, goto done; } /* check eval object */ - if (s->var_object_idx >= 0 && !is_pseudo_var) { + if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) { dbuf_putc(bc, OP_get_loc); dbuf_put_u16(bc, s->var_object_idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, 0); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); } + /* check eval object in argument scope */ + if (s->arg_var_object_idx >= 0 && !is_pseudo_var) { + dbuf_putc(bc, OP_get_loc); + dbuf_put_u16(bc, s->arg_var_object_idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); + } + /* check parent scopes */ for (fd = s; fd->parent;) { scope_level = fd->parent_scope_level; fd = fd->parent; - if (scope_level == 0) { - /* function is defined as part of the argument parsing: hide vars - from the function body. - XXX: variables created from argument destructuring might need - to be visible, should refine this method. - */ - var_idx = find_arg(ctx, fd, var_name); - goto check_idx; - } for (idx = fd->scopes[scope_level].first; idx >= 0;) { vd = &fd->vars[idx]; if (vd->var_name == var_name) { if (op == OP_scope_put_var || op == OP_scope_make_ref) { if (vd->is_const) { - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); goto done; } - is_func_var = vd->is_func_var; } var_idx = idx; break; @@ -29587,26 +29674,19 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (idx >= 0) { dbuf_putc(bc, OP_get_var_ref); dbuf_put_u16(bc, idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, 1); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, 1); } } idx = vd->scope_next; } + is_arg_scope = (idx == ARG_SCOPE_END); if (var_idx >= 0) break; - - var_idx = find_var(ctx, fd, var_name); - check_idx: - if (var_idx >= 0) { - if (!(var_idx & ARGUMENT_VAR_OFFSET)) - is_func_var = fd->vars[var_idx].is_func_var; - break; + + if (!is_arg_scope) { + var_idx = find_var(ctx, fd, var_name); + if (var_idx >= 0) + break; } if (is_pseudo_var) { var_idx = resolve_pseudo_var(ctx, fd, var_name); @@ -29614,33 +29694,39 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, break; } if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) { - var_idx = add_arguments_var(ctx, fd, var_name); + var_idx = add_arguments_var(ctx, fd); break; } if (fd->is_func_expr && fd->func_name == var_name) { /* add a new variable with the function name */ var_idx = add_func_var(ctx, fd, var_name); - is_func_var = TRUE; break; } /* check eval object */ - if (fd->var_object_idx >= 0 && !is_pseudo_var) { - fd->vars[fd->var_object_idx].is_captured = 1; + if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { + vd = &fd->vars[fd->var_object_idx]; + vd->is_captured = 1; idx = get_closure_var(ctx, s, fd, FALSE, - fd->var_object_idx, JS_ATOM__var_, + fd->var_object_idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); dbuf_putc(bc, OP_get_var_ref); dbuf_put_u16(bc, idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, 0); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); } + /* check eval object in argument scope */ + if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { + vd = &fd->vars[fd->arg_var_object_idx]; + vd->is_captured = 1; + idx = get_closure_var(ctx, s, fd, FALSE, + fd->arg_var_object_idx, vd->var_name, + FALSE, FALSE, JS_VAR_NORMAL); + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); + } + if (fd->is_eval) break; /* it it necessarily the top level function */ } @@ -29665,6 +29751,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } goto has_idx; } else if ((cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_ || cv->var_name == JS_ATOM__with_) && !is_pseudo_var) { int is_with = (cv->var_name == JS_ATOM__with_); if (fd != s) { @@ -29678,13 +29765,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } dbuf_putc(bc, OP_get_var_ref); dbuf_put_u16(bc, idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, is_with); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, is_with); } } } @@ -29709,14 +29790,14 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, has_idx: if ((op == OP_scope_put_var || op == OP_scope_make_ref) && s->closure_var[idx].is_const) { - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); goto done; } switch (op) { case OP_scope_make_ref: - if (is_func_var) { + if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { /* Create a dummy object reference for the func_var */ dbuf_putc(bc, OP_object); dbuf_putc(bc, OP_get_var_ref); @@ -29956,7 +30037,7 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, break; case JS_VAR_PRIVATE_SETTER: /* XXX: add clearer error message */ - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); break; @@ -29973,7 +30054,7 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, case JS_VAR_PRIVATE_METHOD: case JS_VAR_PRIVATE_GETTER: /* XXX: add clearer error message */ - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); break; @@ -30025,17 +30106,33 @@ static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, } } +/* XXX: should handle the argument scope generically */ +static BOOL is_var_in_arg_scope(const JSVarDef *vd) +{ + return (vd->var_name == JS_ATOM_home_object || + vd->var_name == JS_ATOM_this_active_func || + vd->var_name == JS_ATOM_new_target || + vd->var_name == JS_ATOM_this || + vd->var_name == JS_ATOM__arg_var_ || + vd->var_kind == JS_VAR_FUNCTION_NAME); +} + static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) { JSFunctionDef *fd; JSVarDef *vd; int i, scope_level, scope_idx; - BOOL has_arguments_binding, has_this_binding; + BOOL has_arguments_binding, has_this_binding, is_arg_scope; /* in non strict mode, variables are created in the caller's environment object */ if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) { s->var_object_idx = add_var(ctx, s, JS_ATOM__var_); + if (s->has_parameter_expressions) { + /* an additional variable object is needed for the + argument scope */ + s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_); + } } /* eval can potentially use 'arguments' so we must define it */ @@ -30051,8 +30148,14 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object); } has_arguments_binding = s->has_arguments_binding; - if (has_arguments_binding) - add_arguments_var(ctx, s, JS_ATOM_arguments); + if (has_arguments_binding) { + add_arguments_var(ctx, s); + /* also add an arguments binding in the argument scope to + raise an error if a direct eval in the argument scope tries + to redefine it */ + if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT)) + add_arguments_arg(ctx, s); + } if (s->is_func_expr && s->func_name != JS_ATOM_NULL) add_func_var(ctx, s, s->func_name); @@ -30069,7 +30172,6 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) fd = fd->parent; if (!fd) break; - scope_idx = fd->scopes[scope_level].first; /* add 'this' if it was not previously added */ if (!has_this_binding && fd->has_this_binding) { if (fd->this_var_idx < 0) @@ -30084,7 +30186,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) } /* add 'arguments' if it was not previously added */ if (!has_arguments_binding && fd->has_arguments_binding) { - add_arguments_var(ctx, fd, JS_ATOM_arguments); + add_arguments_var(ctx, fd); has_arguments_binding = TRUE; } /* add function name */ @@ -30092,6 +30194,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) add_func_var(ctx, fd, fd->func_name); /* add lexical variables */ + scope_idx = fd->scopes[scope_level].first; while (scope_idx >= 0) { vd = &fd->vars[scope_idx]; vd->is_captured = 1; @@ -30099,24 +30202,37 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind); scope_idx = vd->scope_next; } - /* add unscoped variables */ - for(i = 0; i < fd->arg_count; i++) { - vd = &fd->args[i]; - if (vd->var_name != JS_ATOM_NULL) { - get_closure_var(ctx, s, fd, - TRUE, i, vd->var_name, FALSE, FALSE, - JS_VAR_NORMAL); + is_arg_scope = (scope_idx == ARG_SCOPE_END); + if (!is_arg_scope) { + /* add unscoped variables */ + for(i = 0; i < fd->arg_count; i++) { + vd = &fd->args[i]; + if (vd->var_name != JS_ATOM_NULL) { + get_closure_var(ctx, s, fd, + TRUE, i, vd->var_name, FALSE, FALSE, + JS_VAR_NORMAL); + } } - } - for(i = 0; i < fd->var_count; i++) { - vd = &fd->vars[i]; - /* do not close top level last result */ - if (vd->scope_level == 0 && - vd->var_name != JS_ATOM__ret_ && - vd->var_name != JS_ATOM_NULL) { - get_closure_var(ctx, s, fd, - FALSE, i, vd->var_name, FALSE, FALSE, - JS_VAR_NORMAL); + for(i = 0; i < fd->var_count; i++) { + vd = &fd->vars[i]; + /* do not close top level last result */ + if (vd->scope_level == 0 && + vd->var_name != JS_ATOM__ret_ && + vd->var_name != JS_ATOM_NULL) { + get_closure_var(ctx, s, fd, + FALSE, i, vd->var_name, FALSE, FALSE, + JS_VAR_NORMAL); + } + } + } else { + for(i = 0; i < fd->var_count; i++) { + vd = &fd->vars[i]; + /* do not close top level last result */ + if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { + get_closure_var(ctx, s, fd, + FALSE, i, vd->var_name, FALSE, FALSE, + JS_VAR_NORMAL); + } } } if (fd->is_eval) { @@ -30134,6 +30250,18 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) } } +static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, + JSVarDef *vd, int var_idx) +{ + cv->is_local = TRUE; + cv->is_arg = FALSE; + cv->is_const = vd->is_const; + cv->is_lexical = vd->is_lexical; + cv->var_kind = vd->var_kind; + cv->var_idx = var_idx; + cv->var_name = JS_DupAtom(ctx, vd->var_name); +} + /* for direct eval compilation: add references to the variables of the calling function */ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, @@ -30141,7 +30269,8 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, { int i, count; JSVarDef *vd; - + BOOL is_arg_scope; + count = b->arg_count + b->var_count + b->closure_var_count; s->closure_var = NULL; s->closure_var_count = 0; @@ -30156,41 +30285,41 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, vd = &b->vardefs[b->arg_count + i]; if (vd->scope_level > 0) { JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - cv->is_local = TRUE; - cv->is_arg = FALSE; - cv->is_const = vd->is_const; - cv->is_lexical = vd->is_lexical; - cv->var_kind = vd->var_kind; - cv->var_idx = i; - cv->var_name = JS_DupAtom(ctx, vd->var_name); + set_closure_from_var(ctx, cv, vd, i); } i = vd->scope_next; } - /* Add argument variables */ - for(i = 0; i < b->arg_count; i++) { - JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - vd = &b->vardefs[i]; - cv->is_local = TRUE; - cv->is_arg = TRUE; - cv->is_const = FALSE; - cv->is_lexical = FALSE; - cv->var_kind = JS_VAR_NORMAL; - cv->var_idx = i; - cv->var_name = JS_DupAtom(ctx, vd->var_name); - } - /* Add local non lexical variables */ - for(i = 0; i < b->var_count; i++) { - vd = &b->vardefs[b->arg_count + i]; - if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) { + is_arg_scope = (i == ARG_SCOPE_END); + if (!is_arg_scope) { + /* Add argument variables */ + for(i = 0; i < b->arg_count; i++) { JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + vd = &b->vardefs[i]; cv->is_local = TRUE; - cv->is_arg = FALSE; + cv->is_arg = TRUE; cv->is_const = FALSE; cv->is_lexical = FALSE; cv->var_kind = JS_VAR_NORMAL; cv->var_idx = i; cv->var_name = JS_DupAtom(ctx, vd->var_name); } + /* Add local non lexical variables */ + for(i = 0; i < b->var_count; i++) { + vd = &b->vardefs[b->arg_count + i]; + if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) { + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + set_closure_from_var(ctx, cv, vd, i); + } + } + } else { + /* only add pseudo variables */ + for(i = 0; i < b->var_count; i++) { + vd = &b->vardefs[b->arg_count + i]; + if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + set_closure_from_var(ctx, cv, vd, i); + } + } } for(i = 0; i < b->closure_var_count; i++) { JSClosureVar *cv0 = &b->closure_var[i]; @@ -30348,59 +30477,93 @@ static BOOL code_match(CodeContext *s, int pos, ...) static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc) { - int i, idx, var_idx; + int i, idx, label_next = -1; + + /* add the hoisted functions in arguments and local variables */ + for(i = 0; i < s->arg_count; i++) { + JSVarDef *vd = &s->args[i]; + if (vd->func_pool_idx >= 0) { + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, vd->func_pool_idx); + dbuf_putc(bc, OP_put_arg); + dbuf_put_u16(bc, i); + } + } + for(i = 0; i < s->var_count; i++) { + JSVarDef *vd = &s->vars[i]; + if (vd->scope_level == 0 && vd->func_pool_idx >= 0) { + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, vd->func_pool_idx); + dbuf_putc(bc, OP_put_loc); + dbuf_put_u16(bc, i); + } + } - /* add the hoisted functions and variables */ - for(i = 0; i < s->hoisted_def_count; i++) { - JSHoistedDef *hf = &s->hoisted_def[i]; + /* the module global variables must be initialized before + evaluating the module so that the exported functions are + visible if there are cyclic module references */ + if (s->module) { + label_next = new_label_fd(s, -1); + + /* if 'this' is true, initialize the global variables and return */ + dbuf_putc(bc, OP_push_this); + dbuf_putc(bc, OP_if_false); + dbuf_put_u32(bc, label_next); + update_label(s, label_next, 1); + s->jump_size++; + } + + /* add the global variables (only happens if s->is_global_var is + true) */ + for(i = 0; i < s->global_var_count; i++) { + JSGlobalVar *hf = &s->global_vars[i]; int has_closure = 0; BOOL force_init = hf->force_init; - if (s->is_global_var && hf->var_name != JS_ATOM_NULL) { - /* we are in an eval, so the closure contains all the - enclosing variables */ - /* If the outer function has a variable environment, - create a property for the variable there */ - for(idx = 0; idx < s->closure_var_count; idx++) { - JSClosureVar *cv = &s->closure_var[idx]; - if (cv->var_name == hf->var_name) { - has_closure = 2; - force_init = FALSE; - break; - } - if (cv->var_name == JS_ATOM__var_) { - dbuf_putc(bc, OP_get_var_ref); - dbuf_put_u16(bc, idx); - has_closure = 1; - force_init = TRUE; - break; - } + /* we are in an eval, so the closure contains all the + enclosing variables */ + /* If the outer function has a variable environment, + create a property for the variable there */ + for(idx = 0; idx < s->closure_var_count; idx++) { + JSClosureVar *cv = &s->closure_var[idx]; + if (cv->var_name == hf->var_name) { + has_closure = 2; + force_init = FALSE; + break; } - if (!has_closure) { - int flags; - - flags = 0; - if (s->eval_type != JS_EVAL_TYPE_GLOBAL) - flags |= JS_PROP_CONFIGURABLE; - if (hf->cpool_idx >= 0 && !hf->is_lexical) { - /* global function definitions need a specific handling */ - dbuf_putc(bc, OP_fclosure); - dbuf_put_u32(bc, hf->cpool_idx); - - dbuf_putc(bc, OP_define_func); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, flags); - - goto done_hoisted_def; - } else { - if (hf->is_lexical) { - flags |= DEFINE_GLOBAL_LEX_VAR; - if (!hf->is_const) - flags |= JS_PROP_WRITABLE; - } - dbuf_putc(bc, OP_define_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, flags); + if (cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_) { + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + has_closure = 1; + force_init = TRUE; + break; + } + } + if (!has_closure) { + int flags; + + flags = 0; + if (s->eval_type != JS_EVAL_TYPE_GLOBAL) + flags |= JS_PROP_CONFIGURABLE; + if (hf->cpool_idx >= 0 && !hf->is_lexical) { + /* global function definitions need a specific handling */ + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, hf->cpool_idx); + + dbuf_putc(bc, OP_define_func); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, flags); + + goto done_global_var; + } else { + if (hf->is_lexical) { + flags |= DEFINE_GLOBAL_LEX_VAR; + if (!hf->is_const) + flags |= JS_PROP_WRITABLE; } + dbuf_putc(bc, OP_define_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, flags); } } if (hf->cpool_idx >= 0 || force_init) { @@ -30415,37 +30578,35 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy } else { dbuf_putc(bc, OP_undefined); } - if (s->is_global_var) { - if (has_closure == 2) { - dbuf_putc(bc, OP_put_var_ref); - dbuf_put_u16(bc, idx); - } else if (has_closure == 1) { - dbuf_putc(bc, OP_define_field); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, OP_drop); - } else { - /* XXX: Check if variable is writable and enumerable */ - dbuf_putc(bc, OP_put_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - } + if (has_closure == 2) { + dbuf_putc(bc, OP_put_var_ref); + dbuf_put_u16(bc, idx); + } else if (has_closure == 1) { + dbuf_putc(bc, OP_define_field); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, OP_drop); } else { - var_idx = hf->var_idx; - if (var_idx & ARGUMENT_VAR_OFFSET) { - dbuf_putc(bc, OP_put_arg); - dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); - } else { - dbuf_putc(bc, OP_put_loc); - dbuf_put_u16(bc, var_idx); - } + /* XXX: Check if variable is writable and enumerable */ + dbuf_putc(bc, OP_put_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); } } - done_hoisted_def: + done_global_var: JS_FreeAtom(ctx, hf->var_name); } - js_free(ctx, s->hoisted_def); - s->hoisted_def = NULL; - s->hoisted_def_count = 0; - s->hoisted_def_size = 0; + + if (s->module) { + dbuf_putc(bc, OP_return_undef); + + dbuf_putc(bc, OP_label); + dbuf_put_u32(bc, label_next); + s->label_slots[label_next].pos2 = bc->size; + } + + js_free(ctx, s->global_vars); + s->global_vars = NULL; + s->global_var_count = 0; + s->global_var_size = 0; } static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len, @@ -30525,7 +30686,7 @@ static int get_label_pos(JSFunctionDef *s, int label) variables when necessary */ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) { - int pos, pos_next, bc_len, op, len, i, idx, arg_valid, line_num; + int pos, pos_next, bc_len, op, len, i, idx, line_num; uint8_t *bc_buf; JSAtom var_name; DynBuf bc_out; @@ -30538,47 +30699,43 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) /* first pass for runtime checks (must be done before the variables are created) */ - if (s->is_global_var) { - for(i = 0; i < s->hoisted_def_count; i++) { - JSHoistedDef *hf = &s->hoisted_def[i]; - int flags; - - if (hf->var_name != JS_ATOM_NULL) { - /* check if global variable (XXX: simplify) */ - for(idx = 0; idx < s->closure_var_count; idx++) { - JSClosureVar *cv = &s->closure_var[idx]; - if (cv->var_name == hf->var_name) { - if (s->eval_type == JS_EVAL_TYPE_DIRECT && - cv->is_lexical) { - /* Check if a lexical variable is - redefined as 'var'. XXX: Could abort - compilation here, but for consistency - with the other checks, we delay the - error generation. */ - dbuf_putc(&bc_out, OP_throw_var); - dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(&bc_out, JS_THROW_VAR_REDECL); - } - goto next; - } - if (cv->var_name == JS_ATOM__var_) - goto next; + for(i = 0; i < s->global_var_count; i++) { + JSGlobalVar *hf = &s->global_vars[i]; + int flags; + + /* check if global variable (XXX: simplify) */ + for(idx = 0; idx < s->closure_var_count; idx++) { + JSClosureVar *cv = &s->closure_var[idx]; + if (cv->var_name == hf->var_name) { + if (s->eval_type == JS_EVAL_TYPE_DIRECT && + cv->is_lexical) { + /* Check if a lexical variable is + redefined as 'var'. XXX: Could abort + compilation here, but for consistency + with the other checks, we delay the + error generation. */ + dbuf_putc(&bc_out, OP_throw_error); + dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(&bc_out, JS_THROW_VAR_REDECL); } - - dbuf_putc(&bc_out, OP_check_define_var); - dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); - flags = 0; - if (hf->is_lexical) - flags |= DEFINE_GLOBAL_LEX_VAR; - if (hf->cpool_idx >= 0) - flags |= DEFINE_GLOBAL_FUNC_VAR; - dbuf_putc(&bc_out, flags); + goto next; } - next: ; + if (cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_) + goto next; } + + dbuf_putc(&bc_out, OP_check_define_var); + dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); + flags = 0; + if (hf->is_lexical) + flags |= DEFINE_GLOBAL_LEX_VAR; + if (hf->cpool_idx >= 0) + flags |= DEFINE_GLOBAL_FUNC_VAR; + dbuf_putc(&bc_out, flags); + next: ; } - arg_valid = 0; line_num = 0; /* avoid warning */ for (pos = 0; pos < bc_len; pos = pos_next) { op = bc_buf[pos]; @@ -30590,9 +30747,6 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) s->line_number_size++; goto no_change; - case OP_set_arg_valid_upto: - arg_valid = get_u16(bc_buf + pos + 1); - break; case OP_eval: /* convert scope index to adjusted variable index */ { int call_argc = get_u16(bc_buf + pos + 1); @@ -30618,7 +30772,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) var_name = get_u32(bc_buf + pos + 1); scope = get_u16(bc_buf + pos + 5); pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, - NULL, NULL, pos_next, arg_valid); + NULL, NULL, pos_next); JS_FreeAtom(ctx, var_name); break; case OP_scope_make_ref: @@ -30631,7 +30785,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) ls = &s->label_slots[label]; ls->ref_count--; /* always remove label reference */ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, - bc_buf, ls, pos_next, arg_valid); + bc_buf, ls, pos_next); JS_FreeAtom(ctx, var_name); } break; @@ -30712,7 +30866,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) case OP_return: case OP_return_undef: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_ret: if (OPTIMIZE) { /* remove dead code */ @@ -30746,25 +30900,27 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) { int scope_idx, scope = get_u16(bc_buf + pos + 1); - if (scope == 1) { + if (scope == s->body_scope) { instantiate_hoisted_definitions(ctx, s, &bc_out); } for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { JSVarDef *vd = &s->vars[scope_idx]; if (vd->scope_level == scope) { - if (vd->var_kind == JS_VAR_FUNCTION_DECL || - vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) { - /* Initialize lexical variable upon entering scope */ - dbuf_putc(&bc_out, OP_fclosure); - dbuf_put_u32(&bc_out, vd->func_pool_or_scope_idx); - dbuf_putc(&bc_out, OP_put_loc); - dbuf_put_u16(&bc_out, scope_idx); - } else { - /* XXX: should check if variable can be used - before initialization */ - dbuf_putc(&bc_out, OP_set_loc_uninitialized); - dbuf_put_u16(&bc_out, scope_idx); + if (scope_idx != s->arguments_arg_idx) { + if (vd->var_kind == JS_VAR_FUNCTION_DECL || + vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) { + /* Initialize lexical variable upon entering scope */ + dbuf_putc(&bc_out, OP_fclosure); + dbuf_put_u32(&bc_out, vd->func_pool_idx); + dbuf_putc(&bc_out, OP_put_loc); + dbuf_put_u16(&bc_out, scope_idx); + } else { + /* XXX: should check if variable can be used + before initialization */ + dbuf_putc(&bc_out, OP_set_loc_uninitialized); + dbuf_put_u16(&bc_out, scope_idx); + } } scope_idx = vd->scope_next; } else { @@ -31167,6 +31323,8 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) dbuf_putc(&bc_out, OP_special_object); dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS); } + if (s->arguments_arg_idx >= 0) + put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx); put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx); } /* initialize a reference to the current function if needed */ @@ -31181,6 +31339,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); put_short_code(&bc_out, OP_put_loc, s->var_object_idx); } + if (s->arg_var_object_idx >= 0) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); + put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx); + } for (pos = 0; pos < bc_len; pos = pos_next) { int val; @@ -31248,7 +31411,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); goto no_change; @@ -31969,56 +32132,91 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) /* compute the maximum stack size needed by the function */ typedef struct StackSizeState { + int bc_len; int stack_len_max; uint16_t *stack_level_tab; + int *pc_stack; + int pc_stack_len; + int pc_stack_size; } StackSizeState; -static __exception int compute_stack_size_rec(JSContext *ctx, - JSFunctionDef *fd, - StackSizeState *s, - int pos, int op, int stack_len) +/* 'op' is only used for error indication */ +static __exception int ss_check(JSContext *ctx, StackSizeState *s, + int pos, int op, int stack_len) { - int bc_len, diff, n_pop, pos_next; - const JSOpCode *oi; - const uint8_t *bc_buf; - + if ((unsigned)pos >= s->bc_len) { + JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); + return -1; + } if (stack_len > s->stack_len_max) { s->stack_len_max = stack_len; - if (s->stack_len_max > JS_STACK_SIZE_MAX) - goto stack_overflow; + if (s->stack_len_max > JS_STACK_SIZE_MAX) { + JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); + return -1; + } } - bc_buf = fd->byte_code.buf; - bc_len = fd->byte_code.size; - for(;;) { - if ((unsigned)pos >= bc_len) - goto buf_overflow; -#if 0 - printf("%5d: %d\n", pos, stack_len); -#endif - if (s->stack_level_tab[pos] != 0xffff) { - /* already explored: check that the stack size is consistent */ - if (s->stack_level_tab[pos] != stack_len) { - JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)", - s->stack_level_tab[pos], stack_len, pos); - return -1; - } else { - return 0; - } + if (s->stack_level_tab[pos] != 0xffff) { + /* already explored: check that the stack size is consistent */ + if (s->stack_level_tab[pos] != stack_len) { + JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)", + s->stack_level_tab[pos], stack_len, pos); + return -1; } else { - s->stack_level_tab[pos] = stack_len; + return 0; } + } + + /* mark as explored and store the stack size */ + s->stack_level_tab[pos] = stack_len; + + /* queue the new PC to explore */ + if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]), + &s->pc_stack_size, s->pc_stack_len + 1)) + return -1; + s->pc_stack[s->pc_stack_len++] = pos; + return 0; +} +static __exception int compute_stack_size(JSContext *ctx, + JSFunctionDef *fd, + int *pstack_size) +{ + StackSizeState s_s, *s = &s_s; + int i, diff, n_pop, pos_next, stack_len, pos, op; + const JSOpCode *oi; + const uint8_t *bc_buf; + + bc_buf = fd->byte_code.buf; + s->bc_len = fd->byte_code.size; + /* bc_len > 0 */ + s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * + s->bc_len); + if (!s->stack_level_tab) + return -1; + for(i = 0; i < s->bc_len; i++) + s->stack_level_tab[i] = 0xffff; + s->stack_len_max = 0; + s->pc_stack = NULL; + s->pc_stack_len = 0; + s->pc_stack_size = 0; + + /* breadth-first graph exploration */ + if (ss_check(ctx, s, 0, OP_invalid, 0)) + goto fail; + + while (s->pc_stack_len > 0) { + pos = s->pc_stack[--s->pc_stack_len]; + stack_len = s->stack_level_tab[pos]; op = bc_buf[pos]; if (op == 0 || op >= OP_COUNT) { JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos); - return -1; + goto fail; } oi = &short_opcode_info(op); pos_next = pos + oi->size; - if (pos_next > bc_len) { - buf_overflow: + if (pos_next > s->bc_len) { JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); - return -1; + goto fail; } n_pop = oi->n_pop; /* call pops a variable number of arguments */ @@ -32034,13 +32232,15 @@ static __exception int compute_stack_size_rec(JSContext *ctx, if (stack_len < n_pop) { JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos); - return -1; + goto fail; } stack_len += oi->n_push - n_pop; if (stack_len > s->stack_len_max) { s->stack_len_max = stack_len; - if (s->stack_len_max > JS_STACK_SIZE_MAX) - goto stack_overflow; + if (s->stack_len_max > JS_STACK_SIZE_MAX) { + JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); + goto fail; + } } switch(op) { case OP_tail_call: @@ -32049,9 +32249,9 @@ static __exception int compute_stack_size_rec(JSContext *ctx, case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_ret: - goto done; + goto done_insn; case OP_goto: diff = get_u32(bc_buf + pos + 1); pos_next = pos + 1 + diff; @@ -32068,73 +32268,57 @@ static __exception int compute_stack_size_rec(JSContext *ctx, case OP_if_true8: case OP_if_false8: diff = (int8_t)bc_buf[pos + 1]; - if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len)) - return -1; + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len)) + goto fail; break; #endif case OP_if_true: case OP_if_false: case OP_catch: diff = get_u32(bc_buf + pos + 1); - if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len)) - return -1; + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len)) + goto fail; break; case OP_gosub: diff = get_u32(bc_buf + pos + 1); - if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len + 1)) - return -1; + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1)) + goto fail; break; case OP_with_get_var: case OP_with_delete_var: diff = get_u32(bc_buf + pos + 5); - if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 1)) - return -1; + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1)) + goto fail; break; case OP_with_make_ref: case OP_with_get_ref: case OP_with_get_ref_undef: diff = get_u32(bc_buf + pos + 5); - if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 2)) - return -1; + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2)) + goto fail; break; case OP_with_put_var: diff = get_u32(bc_buf + pos + 5); - if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len - 1)) - return -1; + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1)) + goto fail; break; default: break; } - pos = pos_next; + if (ss_check(ctx, s, pos_next, op, stack_len)) + goto fail; + done_insn: ; } - done: - return 0; - - stack_overflow: - JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); - return -1; -} - -static __exception int compute_stack_size(JSContext *ctx, - JSFunctionDef *fd, - int *pstack_size) -{ - StackSizeState s_s, *s = &s_s; - int bc_len, i, ret; - - bc_len = fd->byte_code.size; - /* bc_len > 0 */ - s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * bc_len); - if (!s->stack_level_tab) - return -1; - for(i = 0; i < bc_len; i++) - s->stack_level_tab[i] = 0xffff; - s->stack_len_max = 0; - ret = compute_stack_size_rec(ctx, fd, s, 0, OP_invalid, 0); js_free(ctx, s->stack_level_tab); + js_free(ctx, s->pc_stack); *pstack_size = s->stack_len_max; - return ret; + return 0; + fail: + js_free(ctx, s->stack_level_tab); + js_free(ctx, s->pc_stack); + *pstack_size = 0; + return -1; } static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) @@ -32142,14 +32326,14 @@ static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) int i, idx; JSModuleDef *m = fd->module; JSExportEntry *me; - JSHoistedDef *hf; + JSGlobalVar *hf; /* The imported global variables were added as closure variables in js_parse_import(). We add here the module global variables. */ - for(i = 0; i < fd->hoisted_def_count; i++) { - hf = &fd->hoisted_def[i]; + for(i = 0; i < fd->global_var_count; i++) { + hf = &fd->global_vars[i]; if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const, hf->is_lexical, FALSE) < 0) return -1; @@ -32187,6 +32371,10 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) for (scope = 0; scope < fd->scope_count; scope++) { fd->scopes[scope].first = -1; } + if (fd->has_parameter_expressions) { + /* special end of variable list marker for the argument scope */ + fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END; + } for (idx = 0; idx < fd->var_count; idx++) { JSVarDef *vd = &fd->vars[idx]; vd->scope_next = fd->scopes[vd->scope_level].first; @@ -32194,12 +32382,12 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) } for (scope = 2; scope < fd->scope_count; scope++) { JSVarScope *sd = &fd->scopes[scope]; - if (sd->first == -1) + if (sd->first < 0) sd->first = fd->scopes[sd->parent].first; } for (idx = 0; idx < fd->var_count; idx++) { JSVarDef *vd = &fd->vars[idx]; - if (vd->scope_next == -1 && vd->scope_level > 1) { + if (vd->scope_next < 0 && vd->scope_level > 1) { scope = fd->scopes[vd->scope_level].parent; vd->scope_next = fd->scopes[scope].first; } @@ -32571,7 +32759,8 @@ static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, /* Check if argument name duplicates a destructuring parameter */ /* XXX: should have a flag for such variables */ for (i = 0; i < fd->var_count; i++) { - if (fd->vars[i].var_name == name) + if (fd->vars[i].var_name == name && + fd->vars[i].scope_level == 0) goto duplicate; } } @@ -32680,8 +32869,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE && (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) { - JSHoistedDef *hf; - hf = find_hoisted_def(fd, func_name); + JSGlobalVar *hf; + hf = find_global_var(fd, func_name); /* XXX: should check scope chain */ if (hf && hf->scope_level == fd->scope_level) { js_parse_error(s, "invalid redefinition of global identifier in module code"); @@ -32691,21 +32880,23 @@ static __exception int js_parse_function_decl2(JSParseState *s, } if (func_type == JS_PARSE_FUNC_VAR) { - /* Create lexical name here so function closure contains it */ if (!(fd->js_mode & JS_MODE_STRICT) + && func_kind == JS_FUNC_NORMAL && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0 && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET)) && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) { create_func_var = TRUE; } + /* Create the lexical name here so that the function closure + contains it */ if (fd->is_eval && (fd->eval_type == JS_EVAL_TYPE_GLOBAL || fd->eval_type == JS_EVAL_TYPE_MODULE) && - fd->scope_level == 1) { + fd->scope_level == fd->body_scope) { /* avoid creating a lexical variable in the global scope. XXX: check annex B */ - JSHoistedDef *hf; - hf = find_hoisted_def(fd, func_name); + JSGlobalVar *hf; + hf = find_global_var(fd, func_name); /* XXX: should check scope chain */ if (hf && hf->scope_level == fd->scope_level) { js_parse_error(s, "invalid redefinition of global identifier"); @@ -32780,6 +32971,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, /* parse arguments */ fd->has_simple_parameter_list = TRUE; + fd->has_parameter_expressions = FALSE; has_opt_arg = FALSE; if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) { JSAtom name; @@ -32792,13 +32984,30 @@ static __exception int js_parse_function_decl2(JSParseState *s, goto fail; fd->defined_arg_count = 1; } else { - if (js_parse_expect(s, '(')) - goto fail; + if (s->token.val == '(') { + int skip_bits; + /* if there is an '=' inside the parameter list, we + consider there is a parameter expression inside */ + js_parse_skip_parens_token(s, &skip_bits, FALSE); + if (skip_bits & SKIP_HAS_ASSIGNMENT) + fd->has_parameter_expressions = TRUE; + if (next_token(s)) + goto fail; + } else { + if (js_parse_expect(s, '(')) + goto fail; + } + if (fd->has_parameter_expressions) { + fd->scope_level = -1; /* force no parent scope */ + if (push_scope(s) < 0) + return -1; + } + while (s->token.val != ')') { JSAtom name; BOOL rest = FALSE; - int idx; + int idx, has_initializer; if (s->token.val == TOK_ELLIPSIS) { fd->has_simple_parameter_list = FALSE; @@ -32817,8 +33026,13 @@ static __exception int js_parse_function_decl2(JSParseState *s, emit_op(s, OP_get_arg); emit_u16(s, idx); } - if (js_parse_destructuring_element(s, TOK_VAR, 1, TRUE, -1, TRUE)) + has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE); + if (has_initializer < 0) goto fail; + if (has_initializer) + has_opt_arg = TRUE; + if (!has_opt_arg) + fd->defined_arg_count++; } else if (s->token.val == TOK_IDENT) { if (s->token.u.ident.is_reserved) { js_parse_error_reserved_identifier(s); @@ -32829,6 +33043,11 @@ static __exception int js_parse_function_decl2(JSParseState *s, js_parse_error_reserved_identifier(s); goto fail; } + if (fd->has_parameter_expressions) { + if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0) + goto fail; + } + /* XXX: could avoid allocating an argument if rest is true */ idx = add_arg(ctx, fd, name); if (idx < 0) goto fail; @@ -32837,70 +33056,55 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (rest) { emit_op(s, OP_rest); emit_u16(s, idx); + if (fd->has_parameter_expressions) { + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } emit_op(s, OP_put_arg); emit_u16(s, idx); fd->has_simple_parameter_list = FALSE; has_opt_arg = TRUE; } else if (s->token.val == '=') { + int label; + fd->has_simple_parameter_list = FALSE; has_opt_arg = TRUE; if (next_token(s)) goto fail; - /* optimize `x = void 0` default value: no code needed */ - if (s->token.val == TOK_VOID) { - JSParsePos pos; - js_parse_get_pos(s, &pos); - if (next_token(s)) - goto fail; - if (s->token.val == TOK_NUMBER) { - if (next_token(s)) - goto fail; - if (s->token.val == ',') { - if (next_token(s)) - goto fail; - continue; - } - if (s->token.val == ')') { - continue; - } - } - if (js_parse_seek_token(s, &pos)) - goto fail; + label = new_label(s); + emit_op(s, OP_get_arg); + emit_u16(s, idx); + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + emit_goto(s, OP_if_false, label); + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + goto fail; + set_object_name(s, name); + emit_op(s, OP_dup); + emit_op(s, OP_put_arg); + emit_u16(s, idx); + emit_label(s, label); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } else { + if (!has_opt_arg) { + fd->defined_arg_count++; } -#if 0 - /* XXX: not correct for eval code */ - /* Check for a default value of `undefined` - to omit default argument processing */ - if (s->token.val == TOK_IDENT && - s->token.u.ident.atom == JS_ATOM_undefined && - fd->parent == NULL && - ((tok = peek_token(s, FALSE)) == ',' || tok == ')')) { - if (next_token(s)) /* ignore undefined token */ - goto fail; - } else -#endif - { - int label = new_label(s); - if (idx > 0) { - emit_op(s, OP_set_arg_valid_upto); - emit_u16(s, idx); - } + if (fd->has_parameter_expressions) { + /* copy the argument to the argument scope */ emit_op(s, OP_get_arg); emit_u16(s, idx); - emit_op(s, OP_undefined); - emit_op(s, OP_strict_eq); - emit_goto(s, OP_if_false, label); - if (js_parse_assign_expr(s, TRUE)) - goto fail; - set_object_name(s, name); - emit_op(s, OP_put_arg); - emit_u16(s, idx); - emit_label(s, label); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); } - } else if (!has_opt_arg) { - fd->defined_arg_count++; } } else { js_parse_error(s, "missing formal parameter"); @@ -32922,6 +33126,41 @@ static __exception int js_parse_function_decl2(JSParseState *s, } } + if (fd->has_parameter_expressions) { + int idx; + + /* Copy the variables in the argument scope to the variable + scope (see FunctionDeclarationInstantiation() in spec). The + normal arguments are already present, so no need to copy + them. */ + idx = fd->scopes[fd->scope_level].first; + while (idx >= 0) { + JSVarDef *vd = &fd->vars[idx]; + if (vd->scope_level != fd->scope_level) + break; + if (find_var(ctx, fd, vd->var_name) < 0) { + if (add_var(ctx, fd, vd->var_name) < 0) + goto fail; + vd = &fd->vars[idx]; /* fd->vars may have been reallocated */ + emit_op(s, OP_scope_get_var); + emit_atom(s, vd->var_name); + emit_u16(s, fd->scope_level); + emit_op(s, OP_scope_put_var); + emit_atom(s, vd->var_name); + emit_u16(s, 0); + } + idx = vd->scope_next; + } + + /* the argument scope has no parent, hence we don't use pop_scope(s) */ + emit_op(s, OP_leave_scope); + emit_u16(s, fd->scope_level); + + /* set the variable scope as the current scope */ + fd->scope_level = 0; + fd->scope_first = fd->scopes[fd->scope_level].first; + } + if (next_token(s)) goto fail; @@ -32933,7 +33172,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, /* in generators, yield expression is forbidden during the parsing of the arguments */ fd->in_function_body = TRUE; - push_scope(s); /* enter body scope: fd->scope_level = 1 */ + push_scope(s); /* enter body scope */ + fd->body_scope = fd->scope_level; if (s->token.val == TOK_ARROW) { if (next_token(s)) @@ -32943,7 +33183,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (js_parse_function_check_names(s, fd, func_name)) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; if (func_kind != JS_FUNC_NORMAL) @@ -33025,10 +33265,10 @@ done: emit_u32(s, idx); if (create_func_var) { if (s->cur_func->is_global_var) { - JSHoistedDef *hf; + JSGlobalVar *hf; /* the global variable must be defined at the start of the function */ - hf = add_hoisted_def(ctx, s->cur_func, -1, func_name, -1, FALSE); + hf = add_global_var(ctx, s->cur_func, func_name); if (!hf) goto fail; /* it is considered as defined at the top level @@ -33058,7 +33298,7 @@ done: } if (lexical_func_idx >= 0) { /* lexical variable will be initialized upon entering scope */ - s->cur_func->vars[lexical_func_idx].func_pool_or_scope_idx = idx; + s->cur_func->vars[lexical_func_idx].func_pool_idx = idx; emit_op(s, OP_drop); } else { /* store function object into its lexical name */ @@ -33074,17 +33314,23 @@ done: if (var_idx < 0) goto fail; /* the variable will be assigned at the top of the function */ - if (!add_hoisted_def(ctx, s->cur_func, idx, JS_ATOM_NULL, var_idx, FALSE)) - goto fail; + if (var_idx & ARGUMENT_VAR_OFFSET) { + s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx; + } else { + s->cur_func->vars[var_idx].func_pool_idx = idx; + } } else { JSAtom func_var_name; + JSGlobalVar *hf; if (func_name == JS_ATOM_NULL) func_var_name = JS_ATOM__default_; /* export default */ else func_var_name = func_name; /* the variable will be assigned at the top of the function */ - if (!add_hoisted_def(ctx, s->cur_func, idx, func_var_name, -1, FALSE)) + hf = add_global_var(ctx, s->cur_func, func_var_name); + if (!hf) goto fail; + hf->cpool_idx = idx; if (export_flag != JS_PARSE_EXPORT_NONE) { if (!add_export_entry(s, s->cur_func->module, func_var_name, export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL)) @@ -33186,7 +33432,7 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, JS_FreeValue(ctx, fun_obj); if (js_create_module_function(ctx, m) < 0) goto fail; - if (js_instantiate_module(ctx, m) < 0) + if (js_link_module(ctx, m) < 0) goto fail; ret_val = js_evaluate_module(ctx, m); if (JS_IsException(ret_val)) { @@ -33308,7 +33554,8 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, s->allow_html_comments = !s->is_module; push_scope(s); /* body scope */ - + fd->body_scope = fd->scope_level; + err = js_parse_program(s); if (err) { fail: @@ -33371,19 +33618,27 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, } -JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, - const char *filename, int eval_flags) +JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int eval_flags) { int eval_type = eval_flags & JS_EVAL_TYPE_MASK; JSValue ret; assert(eval_type == JS_EVAL_TYPE_GLOBAL || eval_type == JS_EVAL_TYPE_MODULE); - ret = JS_EvalInternal(ctx, ctx->global_obj, input, input_len, filename, + ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, eval_flags, -1); return ret; } +JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags) +{ + return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, + eval_flags); +} + int JS_ResolveModule(JSContext *ctx, JSValueConst obj) { if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { @@ -33980,7 +34235,6 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) 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); @@ -34971,7 +35225,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) 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); @@ -35628,16 +35881,39 @@ static JSAtom find_atom(JSContext *ctx, const char *name) return atom; } -static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, - JSAtom atom, void *opaque) +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque) { const JSCFunctionListEntry *e = opaque; - JSValueConst obj = JS_MKPTR(JS_TAG_OBJECT, p); + JSValue val; + + switch(e->def_type) { + case JS_DEF_CFUNC: + val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, + e->name, e->u.func.length, e->u.func.cproto, e->magic); + break; + case JS_DEF_PROP_STRING: + val = JS_NewAtomString(ctx, e->u.str); + break; + case JS_DEF_OBJECT: + val = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); + break; + default: + abort(); + } + return val; +} + +static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, + JSAtom atom, + const JSCFunctionListEntry *e) +{ JSValue val; int prop_flags = e->prop_flags; switch(e->def_type) { - case JS_DEF_ALIAS: + case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */ { JSAtom atom1 = find_atom(ctx, e->u.alias.name); switch (e->u.alias.base) { @@ -35654,12 +35930,16 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, abort(); } JS_FreeAtom(ctx, atom1); - goto setval; + if (atom == JS_ATOM_Symbol_toPrimitive) { + /* Symbol.toPrimitive functions are not writable */ + prop_flags = JS_PROP_CONFIGURABLE; + } else if (atom == JS_ATOM_Symbol_hasInstance) { + /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ + prop_flags = 0; + } } + break; case JS_DEF_CFUNC: - val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, - e->name, e->u.func.length, e->u.func.cproto, e->magic); - setval: if (atom == JS_ATOM_Symbol_toPrimitive) { /* Symbol.toPrimitive functions are not writable */ prop_flags = JS_PROP_CONFIGURABLE; @@ -35667,8 +35947,10 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ prop_flags = 0; } - break; - case JS_DEF_CGETSET: + JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, + (void *)e, prop_flags); + return 0; + case JS_DEF_CGETSET: /* XXX: use autoinit again ? */ case JS_DEF_CGETSET_MAGIC: { JSValue getter, setter; @@ -35692,9 +35974,6 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, return 0; } break; - case JS_DEF_PROP_STRING: - val = JS_NewAtomString(ctx, e->u.str); - break; case JS_DEF_PROP_INT32: val = JS_NewInt32(ctx, e->u.i32); break; @@ -35707,10 +35986,11 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, case JS_DEF_PROP_UNDEFINED: val = JS_UNDEFINED; break; + case JS_DEF_PROP_STRING: case JS_DEF_OBJECT: - val = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); - break; + JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, + (void *)e, prop_flags); + return 0; default: abort(); } @@ -35721,36 +36001,12 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, const JSCFunctionListEntry *tab, int len) { - int i, prop_flags; + int i; for (i = 0; i < len; i++) { const JSCFunctionListEntry *e = &tab[i]; JSAtom atom = find_atom(ctx, e->name); - - switch (e->def_type) { - case JS_DEF_CFUNC: - case JS_DEF_CGETSET: - case JS_DEF_CGETSET_MAGIC: - case JS_DEF_PROP_STRING: - case JS_DEF_ALIAS: - case JS_DEF_OBJECT: - prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE | (e->prop_flags & JS_PROP_ENUMERABLE); - JS_DefineAutoInitProperty(ctx, obj, atom, - JS_AUTOINIT_ID_PROP, - (void *)e, prop_flags); - break; - case JS_DEF_PROP_INT32: - case JS_DEF_PROP_INT64: - case JS_DEF_PROP_DOUBLE: - case JS_DEF_PROP_UNDEFINED: - { - JSObject *p = JS_VALUE_GET_OBJ(obj); - JS_InstantiateFunctionListItem(ctx, p, atom, (void *)e); - } - break; - default: - abort(); - } + JS_InstantiateFunctionListItem(ctx, obj, atom, e); JS_FreeAtom(ctx, atom); } } @@ -37262,6 +37518,8 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, return tab; } +/* magic value: 0 = normal apply, 1 = apply for constructor, 2 = + Reflect.apply */ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { @@ -37273,14 +37531,14 @@ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; this_arg = argv[0]; array_arg = argv[1]; - if (JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || - JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) { + if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) { return JS_Call(ctx, this_val, this_arg, 0, NULL); } tab = build_arg_list(ctx, &len, array_arg); if (!tab) return JS_EXCEPTION; - if (magic) { + if (magic & 1) { ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab); } else { ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab); @@ -37650,7 +37908,7 @@ static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, return obj; if (argc == 1 && JS_IsNumber(argv[0])) { uint32_t len; - if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) + if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) goto fail; if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) goto fail; @@ -39659,46 +39917,6 @@ static int js_string_get_own_property(JSContext *ctx, return FALSE; } -static uint32_t js_string_obj_get_length(JSContext *ctx, - JSValueConst obj) -{ - JSObject *p; - JSString *p1; - uint32_t len = 0; - - /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ - p = JS_VALUE_GET_OBJ(obj); - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { - p1 = JS_VALUE_GET_STRING(p->u.object_data); - len = p1->len; - } - return len; -} - -static int js_string_get_own_property_names(JSContext *ctx, - JSPropertyEnum **ptab, - uint32_t *plen, - JSValueConst obj) -{ - JSPropertyEnum *tab; - uint32_t len, i; - - len = js_string_obj_get_length(ctx, obj); - tab = NULL; - if (len > 0) { - /* do not allocate 0 bytes */ - tab = js_malloc(ctx, sizeof(JSPropertyEnum) * len); - if (!tab) - return -1; - for(i = 0; i < len; i++) { - tab[i].atom = __JS_AtomFromUInt32(i); - } - } - *ptab = tab; - *plen = len; - return 0; -} - static int js_string_define_own_property(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, @@ -39755,7 +39973,6 @@ static int js_string_delete_property(JSContext *ctx, static const JSClassExoticMethods js_string_exotic_methods = { .get_own_property = js_string_get_own_property, - .get_own_property_names = js_string_get_own_property_names, .define_own_property = js_string_define_own_property, .delete_property = js_string_delete_property, }; @@ -40359,15 +40576,17 @@ static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val string_buffer_concat(b, sp, position + matched_len, sp->len); } else if (c >= '0' && c <= '9') { k = c - '0'; - c1 = string_get(rp, j); - if (c1 >= '0' && c1 <= '9') { - /* This behavior is specified in ES6 and refined in ECMA 2019 */ - /* ECMA 2019 does not have the extra test, but - Test262 S15.5.4.11_A3_T1..3 require this behavior */ - k1 = k * 10 + c1 - '0'; - if (k1 >= 1 && k1 < captures_len) { - k = k1; - j++; + if (j < len) { + c1 = string_get(rp, j); + if (c1 >= '0' && c1 <= '9') { + /* This behavior is specified in ES6 and refined in ECMA 2019 */ + /* ECMA 2019 does not have the extra test, but + Test262 S15.5.4.11_A3_T1..3 require this behavior */ + k1 = k * 10 + c1 - '0'; + if (k1 >= 1 && k1 < captures_len) { + k = k1; + j++; + } } } if (k >= 1 && k < captures_len) { @@ -42805,7 +43024,8 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, tab = JS_NewArray(ctx); if (JS_IsException(tab)) goto exception; - if (JS_SetPropertyInt64(ctx, tab, 0, JS_DupValue(ctx, matched)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; for(n = 1; n < nCaptures; n++) { JSValue capN; @@ -42817,7 +43037,8 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, if (JS_IsException(capN)) goto exception; } - if (JS_SetPropertyInt64(ctx, tab, n, capN) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; } JS_FreeValue(ctx, namedCaptures); @@ -42825,12 +43046,12 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, if (JS_IsException(namedCaptures)) goto exception; if (functionalReplace) { - if (JS_SetPropertyInt64(ctx, tab, n++, JS_NewInt32(ctx, position)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; - if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, str)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (!JS_IsUndefined(namedCaptures)) { - if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; } args[0] = JS_UNDEFINED; @@ -42917,8 +43138,10 @@ static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { JS_FreeValue(ctx, previousLastIndex); } else { - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { + previousLastIndex = JS_UNDEFINED; goto exception; + } } JS_FreeValue(ctx, str); JS_FreeValue(ctx, currentLastIndex); @@ -43023,7 +43246,8 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, sub = js_sub_string(ctx, strp, p, q); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (lengthA == lim) goto done; @@ -43034,7 +43258,7 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (lengthA == lim) goto done; @@ -43049,7 +43273,7 @@ add_tail: sub = js_sub_string(ctx, strp, p, size); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; goto done; exception: @@ -43829,7 +44053,7 @@ void JS_AddIntrinsicJSON(JSContext *ctx) static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 0); + return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); } static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, @@ -46772,26 +46996,15 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va { JSValueConst ctor = func_data[0]; JSValueConst onFinally = func_data[1]; - JSValue res, promise, resolving_funcs[2], ret, then_func; + JSValue res, promise, ret, then_func; res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); if (JS_IsException(res)) return res; - promise = js_new_promise_capability(ctx, resolving_funcs, ctor); - if (JS_IsException(promise)) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst*)&res); + promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); JS_FreeValue(ctx, res); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - if (JS_IsException(ret)) { - JS_FreeValue(ctx, promise); - return ret; - } - JS_FreeValue(ctx, ret); + if (JS_IsException(promise)) + return promise; if (magic == 0) { then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, 0, 1, argv); @@ -47503,7 +47716,7 @@ static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double JSObject *p = JS_VALUE_GET_OBJ(this_val); if (p->class_id == JS_CLASS_DATE) { JS_FreeValue(ctx, p->u.object_data); - p->u.object_data = __JS_NewFloat64(ctx, v); + p->u.object_data = JS_NewFloat64(ctx, v); return JS_DupValue(ctx, p->u.object_data); } } @@ -47547,7 +47760,7 @@ static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; static char const day_names[] = "SunMonTueWedThuFriSat"; static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, - int64_t fields[9], int is_local, int force) + double fields[9], int is_local, int force) { double dval; int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; @@ -47606,12 +47819,18 @@ static double time_clip(double t) { return NAN; } -static double set_date_fields(int64_t fields[], int is_local) { - int64_t days, y, m, md, h, d, i; - - i = fields[1]; - m = math_mod(i, 12); - y = fields[0] + (i - m) / 12; +/* The spec mandates the use of 'double' and it fixes the order + of the operations */ +static double set_date_fields(double fields[], int is_local) { + int64_t y; + double days, d, h, m1; + int i, m, md; + + m1 = fields[1]; + m = fmod(m1, 12); + if (m < 0) + m += 12; + y = (int64_t)(fields[0] + floor(m1 / 12)); days = days_from_year(y); for(i = 0; i < m; i++) { @@ -47621,7 +47840,8 @@ static double set_date_fields(int64_t fields[], int is_local) { days += md; } days += fields[2] - 1; - h = ((fields[3] * 60 + fields[4]) * 60 + fields[5]) * 1000 + fields[6]; + h = fields[3] * 3600000 + fields[4] * 60000 + + fields[5] * 1000 + fields[6]; d = days * 86400000 + h; if (is_local) d += getTimezoneOffset(d) * 60000; @@ -47632,7 +47852,7 @@ static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { // get_date_field(obj, n, is_local) - int64_t fields[9]; + double fields[9]; int res, n, is_local; is_local = magic & 0x0F; @@ -47646,14 +47866,14 @@ static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, if (magic & 0x100) { // getYear fields[0] -= 1900; } - return JS_NewInt64(ctx, fields[n]); + return JS_NewFloat64(ctx, fields[n]); } static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { // _field(obj, first_field, end_field, args, is_local) - int64_t fields[9]; + double fields[9]; int res, first_field, end_field, is_local, i, n; double d, a; @@ -47695,7 +47915,7 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, { // _string(obj, fmt, part) char buf[64]; - int64_t fields[9]; + double fields[9]; int res, fmt, part, pos; int y, mon, d, h, m, s, ms, wd, tz; @@ -47843,7 +48063,7 @@ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, } val = time_clip(val); } else { - int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; if (n > 7) n = 7; for(i = 0; i < n; i++) { @@ -47862,12 +48082,12 @@ has_val: JSValueConst args[3]; args[0] = new_target; args[1] = ctx->class_proto[JS_CLASS_DATE]; - args[2] = __JS_NewFloat64(ctx, val); + args[2] = JS_NewFloat64(ctx, val); rv = js___date_create(ctx, JS_UNDEFINED, 3, args); #else rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); if (!JS_IsException(rv)) - JS_SetObjectData(ctx, rv, __JS_NewFloat64(ctx, val)); + JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); #endif if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { /* invoked as a function, return (new Date()).toString(); */ @@ -47883,7 +48103,7 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { // UTC(y, mon, d, h, m, s, ms) - int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; int i, n; double a; @@ -47901,7 +48121,7 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, if (i == 0 && fields[0] >= 0 && fields[0] < 100) fields[0] += 1900; } - return __JS_NewFloat64(ctx, set_date_fields(fields, 0)); + return JS_NewFloat64(ctx, set_date_fields(fields, 0)); } static void string_skip_spaces(JSString *sp, int *pp) { @@ -48011,6 +48231,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, // parse(s) JSValue s, rv; int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 }; + double fields1[7]; int64_t tz, hh, mm; double d; int p, i, c, sgn; @@ -48123,8 +48344,10 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, } } } - d = set_date_fields(fields, is_local) - tz * 60000; - rv = __JS_NewFloat64(ctx, d); + for(i = 0; i < 7; i++) + fields1[i] = fields[i]; + d = set_date_fields(fields1, is_local) - tz * 60000; + rv = JS_NewFloat64(ctx, d); done: JS_FreeValue(ctx, s); @@ -48194,7 +48417,7 @@ static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, if (JS_ThisTimeValue(ctx, &v, this_val)) return JS_EXCEPTION; - return __JS_NewFloat64(ctx, v); + return JS_NewFloat64(ctx, v); } static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, @@ -48223,7 +48446,7 @@ static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, if (y >= 0 && y < 100) y += 1900; } - args[0] = __JS_NewFloat64(ctx, y); + args[0] = JS_NewFloat64(ctx, y); return set_date_field(ctx, this_val, 1, args, 0x011); } @@ -48714,9 +48937,11 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) } static JSValue js_bigint_constructor(JSContext *ctx, - JSValueConst this_val, + JSValueConst new_target, int argc, JSValueConst *argv) { + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); } @@ -48962,7 +49187,7 @@ static const JSCFunctionListEntry js_bigint_proto_funcs[] = { void JS_AddIntrinsicBigInt(JSContext *ctx) { JSRuntime *rt = ctx->rt; - JSValue obj1; + JSValueConst obj1; rt->bigint_ops.to_string = js_bigint_to_string; rt->bigint_ops.from_string = js_string_to_bigint; @@ -48974,9 +49199,8 @@ void JS_AddIntrinsicBigInt(JSContext *ctx) JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], js_bigint_proto_funcs, countof(js_bigint_proto_funcs)); - obj1 = JS_NewCFunction(ctx, js_bigint_constructor, "BigInt", 1); - JS_NewGlobalCConstructor2(ctx, obj1, "BigInt", - ctx->class_proto[JS_CLASS_BIG_INT]); + obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_INT]); JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, countof(js_bigint_funcs)); } @@ -49194,10 +49418,12 @@ static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { }; static JSValue js_bigfloat_constructor(JSContext *ctx, - JSValueConst this_val, + JSValueConst new_target, int argc, JSValueConst *argv) { JSValue val; + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); if (argc == 0) { bf_t *r; val = JS_NewBigFloat(ctx); @@ -49858,8 +50084,7 @@ static const JSCFunctionListEntry js_float_env_proto_funcs[] = { void JS_AddIntrinsicBigFloat(JSContext *ctx) { JSRuntime *rt = ctx->rt; - JSValue obj1; - JSValueConst obj2; + JSValueConst obj1; rt->bigfloat_ops.to_string = js_bigfloat_to_string; rt->bigfloat_ops.from_string = js_string_to_bigfloat; @@ -49873,9 +50098,8 @@ void JS_AddIntrinsicBigFloat(JSContext *ctx) JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT], js_bigfloat_proto_funcs, countof(js_bigfloat_proto_funcs)); - obj1 = JS_NewCFunction(ctx, js_bigfloat_constructor, "BigFloat", 1); - JS_NewGlobalCConstructor2(ctx, obj1, "BigFloat", - ctx->class_proto[JS_CLASS_BIG_FLOAT]); + obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_FLOAT]); JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs, countof(js_bigfloat_funcs)); @@ -49883,10 +50107,10 @@ void JS_AddIntrinsicBigFloat(JSContext *ctx) JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV], js_float_env_proto_funcs, countof(js_float_env_proto_funcs)); - obj2 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", + obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", js_float_env_constructor, 1, ctx->class_proto[JS_CLASS_FLOAT_ENV]); - JS_SetPropertyFunctionList(ctx, obj2, js_float_env_funcs, + JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, countof(js_float_env_funcs)); } @@ -49989,10 +50213,12 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, } static JSValue js_bigdecimal_constructor(JSContext *ctx, - JSValueConst this_val, + JSValueConst new_target, int argc, JSValueConst *argv) { JSValue val; + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); if (argc == 0) { bfdec_t *r; val = JS_NewBigDecimal(ctx); @@ -50348,7 +50574,7 @@ static const JSCFunctionListEntry js_bigdecimal_funcs[] = { void JS_AddIntrinsicBigDecimal(JSContext *ctx) { JSRuntime *rt = ctx->rt; - JSValue obj1; + JSValueConst obj1; rt->bigdecimal_ops.to_string = js_bigdecimal_to_string; rt->bigdecimal_ops.from_string = js_string_to_bigdecimal; @@ -50360,9 +50586,9 @@ void JS_AddIntrinsicBigDecimal(JSContext *ctx) JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL], js_bigdecimal_proto_funcs, countof(js_bigdecimal_proto_funcs)); - obj1 = JS_NewCFunction(ctx, js_bigdecimal_constructor, "BigDecimal", 1); - JS_NewGlobalCConstructor2(ctx, obj1, "BigDecimal", - ctx->class_proto[JS_CLASS_BIG_DECIMAL]); + obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal", + js_bigdecimal_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_DECIMAL]); JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs, countof(js_bigdecimal_funcs)); } @@ -50818,8 +51044,7 @@ static JSValue js_array_buffer_get_byteLength(JSContext *ctx, JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); if (!abuf) return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + /* return 0 if detached */ return JS_NewUint32(ctx, abuf->byte_length); } @@ -776,7 +776,10 @@ JS_BOOL JS_DetectModule(const char *input, size_t input_len); /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags); -JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); +/* same as JS_Eval() but with an explicit 'this_obj' parameter */ +JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int eval_flags); JSValue JS_GetGlobalObject(JSContext *ctx); int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, @@ -885,7 +888,9 @@ uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, #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); - +/* instantiate and evaluate a bytecode function. Only used when + reading a script or module with JS_ReadObject() */ +JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); /* load the dependencies of the module 'obj'. Useful when JS_ReadObject() returns a module. */ int JS_ResolveModule(JSContext *ctx, JSValueConst obj); @@ -6,30 +6,23 @@ set -e version=`cat VERSION` if [ "$1" = "-h" ] ; then - echo "release.sh [all]" + echo "release.sh [release_list]" echo "" - echo "all: build all the archives. Otherwise only build the quickjs source archive." + echo "release_list: extras binary win_binary quickjs" + exit 1 fi -extras="no" -binary="no" -quickjs="no" - -if [ "$1" = "all" ] ; then - extras="yes" - binary="yes" - quickjs="yes" -elif [ "$1" = "binary" ] ; then - binary="yes" -else - quickjs="yes" +release_list="extras binary win_binary quickjs" + +if [ "$1" != "" ] ; then + release_list="$1" fi #################################################" # extras -if [ "$extras" = "yes" ] ; then +if echo $release_list | grep -w -q extras ; then d="quickjs-${version}" name="quickjs-extras-${version}" @@ -46,10 +39,58 @@ cp -a tests/bench-v8 $outdir/tests fi #################################################" -# binary release +# Windows binary release + +if echo $release_list | grep -w -q win_binary ; then + +# win64 + +dlldir=/usr/x86_64-w64-mingw32/sys-root/mingw/bin +cross_prefix="x86_64-w64-mingw32-" +d="quickjs-win-x86_64-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir + +make CONFIG_WIN32=y qjs.exe +cp qjs.exe $outdir +${cross_prefix}strip $outdir/qjs.exe +cp $dlldir/libwinpthread-1.dll $outdir + +( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) + +make CONFIG_WIN32=y clean + +# win32 + +dlldir=/usr/i686-w64-mingw32/sys-root/mingw/bin +cross_prefix="i686-w64-mingw32-" +d="quickjs-win-i686-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir + +make clean +make CONFIG_WIN32=y clean + +make CONFIG_WIN32=y CONFIG_M32=y qjs.exe +cp qjs.exe $outdir +${cross_prefix}strip $outdir/qjs.exe +cp $dlldir/libwinpthread-1.dll $outdir + +( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) + +fi + +#################################################" +# Linux binary release -if [ "$binary" = "yes" ] ; then +if echo $release_list | grep -w -q binary ; then +make clean +make CONFIG_WIN32=y clean make -j4 qjs run-test262 make -j4 CONFIG_M32=y qjs32 run-test262-32 strip qjs run-test262 qjs32 run-test262-32 @@ -80,7 +121,7 @@ fi #################################################" # quickjs -if [ "$quickjs" = "yes" ] ; then +if echo $release_list | grep -w -q quickjs ; then make build_doc @@ -98,7 +139,7 @@ cp Makefile VERSION TODO Changelog readme.txt release.sh unicode_download.sh \ libregexp.c libregexp.h libregexp-opcode.h \ libunicode.c libunicode.h libunicode-table.h \ libbf.c libbf.h \ - jscompress.c unicode_gen.c unicode_gen_def.h \ + unicode_gen.c unicode_gen_def.h \ run-test262.c test262o.conf test262.conf \ test262o_errors.txt test262_errors.txt \ $outdir diff --git a/test262.conf b/test262.conf index e1eeb03..f30a4ac 100644 --- a/test262.conf +++ b/test262.conf @@ -49,9 +49,12 @@ testdir=test262/test # skipped features are tagged as such to avoid warnings AggregateError +align-detached-buffer-semantics-with-web-reality +arbitrary-module-namespace-names=skip Array.prototype.flat Array.prototype.flatMap Array.prototype.flatten +Array.prototype.item=skip Array.prototype.values ArrayBuffer arrow-function @@ -82,7 +85,6 @@ DataView.prototype.getInt8 DataView.prototype.getUint16 DataView.prototype.getUint32 DataView.prototype.setUint8 -default-arg default-parameters destructuring-assignment destructuring-binding @@ -90,6 +92,7 @@ dynamic-import export-star-as-namespace-from-module FinalizationGroup=skip FinalizationRegistry=skip +FinalizationRegistry.prototype.cleanupSome=skip Float32Array Float64Array for-in-order @@ -99,10 +102,12 @@ globalThis hashbang host-gc-required=skip import.meta +Int16Array Int32Array Int8Array IsHTMLDDA json-superset +legacy-regexp=skip let logical-assignment-operators Map @@ -114,6 +119,7 @@ Object.fromEntries Object.is optional-catch-binding optional-chaining +Promise Promise.allSettled Promise.any Promise.prototype.finally @@ -135,6 +141,7 @@ string-trimming String.fromCodePoint String.prototype.endsWith String.prototype.includes +String.prototype.item=skip String.prototype.matchAll String.prototype.replaceAll String.prototype.trimEnd @@ -159,14 +166,19 @@ tail-call-optimization=skip template top-level-await=skip TypedArray +TypedArray.prototype.item=skip u180e Uint16Array +Uint32Array Uint8Array Uint8ClampedArray WeakMap WeakRef=skip WeakSet well-formed-json-stringify +__getter__ +__proto__ +__setter__ [exclude] # list excluded tests and directories here @@ -183,8 +195,5 @@ test262/test/built-ins/ThrowTypeError/unique-per-realm-function-proto.js #test262/test/built-ins/RegExp/CharacterClassEscapes/ #test262/test/built-ins/RegExp/property-escapes/ -# invalid tests -test262/test/language/module-code/verify-dfs.js - [tests] # list test files or use config.testdir diff --git a/test262_errors.txt b/test262_errors.txt index d00d4f1..502a650 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -1,30 +1,51 @@ test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js:20: Test262Error: Expected a ReferenceError but got a ReferenceError 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 -test262/test/language/expressions/async-function/nameless-eval-var-scope-syntax-err.js:33: TypeError: $DONE() not called -test262/test/language/expressions/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/expressions/async-generator/named-eval-var-scope-syntax-err.js:28: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all -test262/test/language/expressions/class/elements/grammar-private-field-optional-chaining.js:26: SyntaxError: expecting field name -test262/test/language/expressions/class/elements/grammar-private-field-optional-chaining.js:26: strict mode: SyntaxError: expecting field name +test262/test/built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer.js:36: Test262Error: throws a TypeError getting a value from the detached buffer Expected a TypeError to be thrown but no exception was thrown at all (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer.js:36: strict mode: Test262Error: throws a TypeError getting a value from the detached buffer Expected a TypeError to be thrown but no exception was thrown at all (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/map/BigInt/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/map/BigInt/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/reduce/BigInt/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/reduce/BigInt/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/reduceRight/BigInt/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/reduceRight/BigInt/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/some/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/some/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer.js:37: Test262Error: (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer.js:37: strict mode: Test262Error: (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js:38: Test262Error: (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js:38: strict mode: Test262Error: (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/BigInt/index-prop-desc.js:21: Test262Error: Expected SameValue(«43», «42») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/BigInt/index-prop-desc.js:21: strict mode: Test262Error: Expected SameValue(«43», «42») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/index-prop-desc.js:22: Test262Error: Expected SameValue(«43», «42») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/index-prop-desc.js:22: strict mode: Test262Error: Expected SameValue(«43», «42») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer-realm.js:36: strict mode: TypeError: out-of-bound numeric index (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js:34: TypeError: cannot convert bigint to number (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js:32: strict mode: TypeError: out-of-bound numeric index (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer-realm.js:36: strict mode: TypeError: out-of-bound numeric index (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer.js:32: strict mode: TypeError: out-of-bound numeric index (Testing with Float64Array.) test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called -test262/test/language/expressions/function/eval-var-scope-syntax-err.js:48: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all -test262/test/language/expressions/generators/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all -test262/test/language/expressions/object/method-definition/async-gen-meth-eval-var-scope-syntax-err.js:32: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all -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: 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 -test262/test/language/statements/class/elements/grammar-private-field-optional-chaining.js:26: strict mode: SyntaxError: expecting field name -test262/test/language/statements/function/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all -test262/test/language/statements/generators/eval-var-scope-syntax-err.js:49: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all diff --git a/tests/test_builtin.js b/tests/test_builtin.js index 5650c52..c044b2c 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -17,6 +17,22 @@ function assert(actual, expected, message) { (message ? " (" + message + ")" : "")); } +function assert_throws(expected_error, func) +{ + var err = false; + try { + func(); + } catch(e) { + err = true; + if (!(e instanceof expected_error)) { + throw Error("unexpected exception type"); + } + } + if (!err) { + throw Error("expected exception"); + } +} + // load more elaborate version of assert if available try { __loadScript("test_assert.js"); } catch(e) {} @@ -39,7 +55,7 @@ function test_function() function constructor1(a) { this.x = a; } - + var r, g; r = my_func.call(null, 1, 2); @@ -48,6 +64,13 @@ function test_function() r = my_func.apply(null, [1, 2]); assert(r, 3, "apply"); + r = (function () { return 1; }).apply(null, undefined); + assert(r, 1); + + assert_throws(TypeError, (function() { + Reflect.apply((function () { return 1; }), null, undefined); + })); + r = new Function("a", "b", "return a + b;"); assert(r(2,3), 5, "function"); diff --git a/tests/test_language.js b/tests/test_language.js index 6b08467..8d13deb 100644 --- a/tests/test_language.js +++ b/tests/test_language.js @@ -15,6 +15,22 @@ function assert(actual, expected, message) { (message ? " (" + message + ")" : "")); } +function assert_throws(expected_error, func) +{ + var err = false; + try { + func(); + } catch(e) { + err = true; + if (!(e instanceof expected_error)) { + throw Error("unexpected exception type"); + } + } + if (!err) { + throw Error("expected exception"); + } +} + // load more elaborate version of assert if available try { __loadScript("test_assert.js"); } catch(e) {} @@ -233,8 +249,13 @@ function test_delete() function test_prototype() { - function f() { } + var f = function f() { }; assert(f.prototype.constructor, f, "prototype"); + + var g = function g() { }; + /* QuickJS bug */ + Object.defineProperty(g, "prototype", { writable: false }); + assert(g.prototype.constructor, g, "prototype"); } function test_arguments() @@ -376,6 +397,135 @@ function test_spread() assert(Object.getOwnPropertyNames(x).toString(), "0,length"); } +function test_function_length() +{ + assert( ((a, b = 1, c) => {}).length, 1); + assert( (([a,b]) => {}).length, 1); + assert( (({a,b}) => {}).length, 1); + assert( ((c, [a,b] = 1, d) => {}).length, 1); +} + +function test_argument_scope() +{ + var f; + var c = "global"; + + f = function(a = eval("var arguments")) {}; + assert_throws(SyntaxError, f); + + f = function(a = eval("1"), b = arguments[0]) { return b; }; + assert(f(12), 12); + + f = function(a, b = arguments[0]) { return b; }; + assert(f(12), 12); + + f = function(a, b = () => arguments) { return b; }; + assert(f(12)()[0], 12); + + f = function(a = eval("1"), b = () => arguments) { return b; }; + assert(f(12)()[0], 12); + + (function() { + "use strict"; + f = function(a = this) { return a; }; + assert(f.call(123), 123); + + f = function f(a = f) { return a; }; + assert(f(), f); + + f = function f(a = eval("f")) { return a; }; + assert(f(), f); + })(); + + f = (a = eval("var c = 1"), probe = () => c) => { + var c = 2; + assert(c, 2); + assert(probe(), 1); + } + f(); + + f = (a = eval("var arguments = 1"), probe = () => arguments) => { + var arguments = 2; + assert(arguments, 2); + assert(probe(), 1); + } + f(); + + f = function f(a = eval("var c = 1"), b = c, probe = () => c) { + assert(b, 1); + assert(c, 1); + assert(probe(), 1) + } + f(); + + assert(c, "global"); + f = function f(a, b = c, probe = () => c) { + eval("var c = 1"); + assert(c, 1); + assert(b, "global"); + assert(probe(), "global") + } + f(); + assert(c, "global"); + + f = function f(a = eval("var c = 1"), probe = (d = eval("c")) => d) { + assert(probe(), 1) + } + f(); +} + +function test_function_expr_name() +{ + var f; + + /* non strict mode test : assignment to the function name silently + fails */ + + f = function myfunc() { + myfunc = 1; + return myfunc; + }; + assert(f(), f); + + f = function myfunc() { + myfunc = 1; + (() => { + myfunc = 1; + })(); + return myfunc; + }; + assert(f(), f); + + f = function myfunc() { + eval("myfunc = 1"); + return myfunc; + }; + assert(f(), f); + + /* strict mode test : assignment to the function name raises a + TypeError exception */ + + f = function myfunc() { + "use strict"; + myfunc = 1; + }; + assert_throws(TypeError, f); + + f = function myfunc() { + "use strict"; + (() => { + myfunc = 1; + })(); + }; + assert_throws(TypeError, f); + + f = function myfunc() { + "use strict"; + eval("myfunc = 1"); + }; + assert_throws(TypeError, f); +} + test_op1(); test_cvt(); test_eq(); @@ -392,3 +542,6 @@ test_regexp_skip(); test_labels(); test_destructuring(); test_spread(); +test_function_length(); +test_argument_scope(); +test_function_expr_name(); |