diff --git a/Changelog b/Changelog
index 1a7533b..0c25b96 100644
--- a/Changelog
+++ b/Changelog
@@ -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
diff --git a/Makefile b/Makefile
index 94c8e31..766a7e1 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/TODO b/TODO
index 0eec6d3..b5500c9 100644
--- a/TODO
+++ b/TODO
@@ -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,48 +13,18 @@ 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:
-- 64-bit atoms in 64-bit mode ?
-- use auto-init properties for more global objects
-- reuse stack slots for disjoint scopes, if strip
-- optimize `for of` iterator for built-in array objects
-- add heuristic to avoid some cycles in closures
-- small String (0-2 charcodes) with immediate storage
-- perform static string concatenation at compile time
-- optimize string concatenation with ropes or miniropes?
-- add implicit numeric strings for Uint32 numbers?
-- optimize `s += a + b`, `s += a.b` and similar simple expressions
-- ensure string canonical representation and optimise comparisons and hashes?
-- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references
-- optimize function storage with length and name accessors?
-- property access optimization on the global object, functions,
- prototypes and special non extensible objects.
-- create object literals with the correct length by backpatching length argument
-- remove redundant set_loc_uninitialized/check_uninitialized opcodes
-- peephole optim: push_atom_value, to_propkey -> push_atom_value
-- peephole optim: put_loc x, get_loc_check x -> set_loc x
-- comparative performance benchmark
-- use variable name when throwing uninitialized exception if available
-- convert slow array to fast array when all properties != length are numeric
-- optimize destructuring assignments for global and local variables
-- implement some form of tail-call-optimization
-- optimize OP_apply
-- optimize f(...b)
-
-Extensions:
-- support more features in [features] section
-- add built-in preprocessor in compiler, get rid of jscompress
- handle #if, #ifdef, #line, limited support for #define
-- get rid of __loadScript, use more common name
+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
@@ -71,8 +39,32 @@ REPL:
- save history
- close all predefined methods in repl.js and jscalc.js
+Optimization ideas:
+- 64-bit atoms in 64-bit mode ?
+- 64-bit small bigint in 64-bit mode ?
+- reuse stack slots for disjoint scopes, if strip
+- add heuristic to avoid some cycles in closures
+- small String (0-2 charcodes) with immediate storage
+- perform static string concatenation at compile time
+- optimize string concatenation with ropes or miniropes?
+- add implicit numeric strings for Uint32 numbers?
+- optimize `s += a + b`, `s += a.b` and similar simple expressions
+- ensure string canonical representation and optimise comparisons and hashes?
+- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references
+- 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
+- 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)
+
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
diff --git a/VERSION b/VERSION
index c67790a..78749b0 100644
--- a/VERSION
+++ b/VERSION
@@ -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 @@
-
-
-
-
-Javascript Bignum Extensions
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Javascript Bignum Extensions
-
-
-Table of Contents
-
-
-
-
-
-1 Introduction
-
-The Bignum extensions add the following features to the Javascript
-language while being 100% backward compatible:
-
-
- Operator overloading with a dispatch logic inspired from the proposal available at https://github.com/tc39/proposal-operator-overloading/ .
-
- Arbitrarily large floating point numbers (BigFloat
) in base 2 using the IEEE 754 semantics.
-
- Arbitrarily large floating point numbers (BigDecimal
) in base 10 based on the proposal available at
-https://github.com/littledan/proposal-bigdecimal .
-
- math
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 (%
) is defined as the Euclidian
-remainder. ^
is an alias to the power operator
-(**
). ^^
is used as the exclusive or operator.
-
-
-
-The extensions are independent from each other except the math
-mode which relies on BigFloat and operator overloading.
-
-
-2 Operator overloading
-
-Operator overloading is inspired from the proposal available at
-https://github.com/tc39/proposal-operator-overloading/ . It
-implements the same dispatch logic but finds the operator sets by
-looking at the Symbol.operatorSet
property in the objects. The
-changes were done in order to simplify the implementation.
-
-More precisely, the following modifications were made:
-
-
- with operators from
is not supported. Operator overloading is always enabled.
-
- The dispatch is not based on a static [[OperatorSet]]
field in all instances. Instead, a dynamic lookup of the Symbol.operatorSet
property is done. This property is typically added in the prototype of each object.
-
- Operators.create(...dictionaries)
is used to create a new OperatorSet object. The Operators
function is supported as an helper to be closer to the TC39 proposal.
-
- []
cannot be overloaded.
-
- In math mode, the BigInt division and power operators can be overloaded with Operators.updateBigIntOperators(dictionary)
.
-
-
-
-
-3 BigInt extensions
-
-A few properties are added to the BigInt object:
-
-
-tdiv(a, b)
-Return trunc(a/b) . b = 0
raises a RangeError
-exception.
-
-
-fdiv(a, b)
-Return \lfloor a/b \rfloor . b = 0
raises a RangeError
-exception.
-
-
-cdiv(a, b)
-Return \lceil a/b \rceil . b = 0
raises a RangeError
-exception.
-
-
-ediv(a, b)
-Return sgn(b) \lfloor a/{|b|} \rfloor (Euclidian
-division). b = 0
raises a RangeError exception.
-
-
-tdivrem(a, b)
-fdivrem(a, b)
-cdivrem(a, b)
-edivrem(a, b)
-Return an array of two elements. The first element is the quotient,
-the second is the remainder. The same rounding is done as the
-corresponding division operation.
-
-
-sqrt(a)
-Return \lfloor \sqrt(a) \rfloor . A RangeError exception is
-raised if a < 0 .
-
-
-sqrtrem(a)
-Return an array of two elements. The first element is \lfloor
-\sqrt{a} \rfloor . The second element is a-\lfloor \sqrt{a}
-\rfloor^2 . A RangeError exception is raised if a < 0 .
-
-
-floorLog2(a)
-Return -1 if a \leq 0 otherwise return \lfloor \log2(a) \rfloor .
-
-
-ctz(a)
-Return the number of trailing zeros in the two’s complement binary representation of a. Return -1 if a=0 .
-
-
-
-
-
-4 BigFloat
-
-
-4.1 Introduction
-
-This extension adds the BigFloat
primitive type. The
-BigFloat
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 NaN
, +/-Infinity
, +0
and -0
-are supported. The mantissa and exponent can have any bit length with
-an implementation specific minimum and maximum.
-
-
-4.2 Floating point rounding
-
-Each floating point operation operates with infinite precision and
-then rounds the result according to the specified floating point
-environment (BigFloatEnv
object). The status flags of the
-environment are also set according to the result of the operation.
-
-If no floating point environment is provided, the global floating
-point environment is used.
-
-The rounding mode of the global floating point environment is always
-RNDN
(“round to nearest with ties to even”)1 . The status flags of the global environment cannot be
-read2 . The precision of the global environment is
-BigFloatEnv.prec
. The number of exponent bits of the global
-environment is BigFloatEnv.expBits
. The global environment
-subnormal flag is set to true
.
-
-For example, prec = 53
and expBits = 11
exactly give
-the same precision as the IEEE 754 64 bit floating point format. The
-default precision is prec = 113
and expBits = 15
(IEEE
-754 128 bit floating point format).
-
-The global floating point environment can only be modified temporarily
-when calling a function (see BigFloatEnv.setPrec
). Hence a
-function can change the global floating point environment for its
-callees but not for its caller.
-
-
-4.3 Operators
-
-The builtin operators are extended so that a BigFloat is returned if
-at least one operand is a BigFloat. The computations are always done
-with infinite precision and rounded according to the global floating
-point environment.
-
-typeof
applied on a BigFloat
returns bigfloat
.
-
-BigFloat can be compared with all the other numeric types and the
-result follows the expected mathematical relations.
-
-However, since BigFloat and Number are different types they are never
-equal when using the strict comparison operators (e.g. 0.0 ===
-0.0l
is false).
-
-
-4.4 BigFloat literals
-
-BigFloat literals are floating point numbers with a trailing l
-suffix. BigFloat literals have an infinite precision. They are rounded
-according to the global floating point environment when they are
-evaluated.3
-
-
-4.5 Builtin Object changes
-
-
-4.5.1 BigFloat
function
-
-The BigFloat
function cannot be invoked as a constructor. When
-invoked as a function: the parameter is converted to a primitive
-type. If the result is a numeric type, it is converted to BigFloat
-without rounding. If the result is a string, it is converted to
-BigFloat using the precision of the global floating point environment.
-
-BigFloat
properties:
-
-
-LN2
-PI
-Getter. Return the value of the corresponding mathematical constant
-rounded to nearest, ties to even with the current global
-precision. The constant values are cached for small precisions.
-
-
-MIN_VALUE
-MAX_VALUE
-EPSILON
-Getter. Return the minimum, maximum and epsilon BigFloat
values
-(same definition as the corresponding Number
constants).
-
-
-fpRound(a[, e])
-Round the floating point number a
according to the floating
-point environment e
or the global environment if e
is
-undefined.
-
-
-parseFloat(a[, radix[, e]])
-Parse the string a
as a floating point number in radix
-radix
. The radix is 0 (default) or from 2 to 36. The radix 0
-means radix 10 unless there is a hexadecimal or binary prefix. The
-result is rounded according to the floating point environment e
-or the global environment if e
is undefined.
-
-
-isFinite(a)
-Return true if a
is a finite bigfloat.
-
-
-isNaN(a)
-Return true if a
is a NaN bigfloat.
-
-
-add(a, b[, e])
-sub(a, b[, e])
-mul(a, b[, e])
-div(a, b[, e])
-Perform the specified floating point operation and round the floating
-point number a
according to the floating point environment
-e
or the global environment if e
is undefined. If
-e
is specified, the floating point status flags are updated.
-
-
-floor(x)
-ceil(x)
-round(x)
-trunc(x)
-Round to an integer. No additional rounding is performed.
-
-
-abs(x)
-Return the absolute value of x. No additional rounding is performed.
-
-
-fmod(x, y[, e])
-remainder(x, y[, e])
-Floating point remainder. The quotient is truncated to zero (fmod) or
-to the nearest integer with ties to even (remainder). e
is an
-optional floating point environment.
-
-
-sqrt(x[, e])
-Square root. Return a rounded floating point number. e
is an
-optional floating point environment.
-
-
-sin(x[, e])
-cos(x[, e])
-tan(x[, e])
-asin(x[, e])
-acos(x[, e])
-atan(x[, e])
-atan2(x, y[, e])
-exp(x[, e])
-log(x[, e])
-pow(x, y[, e])
-Transcendental operations. Return a rounded floating point
-number. e
is an optional floating point environment.
-
-
-
-
-
-4.5.2 BigFloat.prototype
-
-The following properties are modified:
-
-
-valueOf()
-Return the bigfloat primitive value corresponding to this
.
-
-
-toString(radix)
-
-For floating point numbers:
-
-
- If the radix is a power of two, the conversion is done with infinite
-precision.
- 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.
-
-
-
-The exponent letter is e
for base 10, p
for bases 2, 8,
-16 with a binary exponent and @
for the other bases.
-
-
-toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
-toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
-toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
-Same semantics as the corresponding Number
functions with
-BigFloats. There is no limit on the accepted precision p
. The
-rounding mode and radix can be optionally specified. The radix must be
-between 2 and 36.
-
-
-
-
-
-4.5.3 BigFloatEnv
constructor
-
-The BigFloatEnv([p, [,rndMode]]
constructor cannot be invoked as a
-function. The floating point environment contains:
-
-
- the mantissa precision in bits
-
- the exponent size in bits assuming an IEEE 754 representation;
-
- the subnormal flag (if true, subnormal floating point numbers can
-be generated by the floating point operations).
-
- the rounding mode
-
- the floating point status. The status flags can only be set by the floating point operations. They can be reset with BigFloatEnv.prototype.clearStatus()
or with the various status flag setters.
-
-
-
-new BigFloatEnv([p, [,rndMode]]
creates a new floating point
-environment. The status flags are reset. If no parameter is given the
-precision, exponent bits and subnormal flags are copied from the
-global floating point environment. Otherwise, the precision is set to
-p
, the number of exponent bits is set to expBitsMax
and the
-subnormal flags is set to false
. If rndMode
is
-undefined
, the rounding mode is set to RNDN
.
-
-BigFloatEnv
properties:
-
-
-prec
-Getter. Return the precision in bits of the global floating point
-environment. The initial value is 113
.
-
-
-expBits
-Getter. Return the exponent size in bits of the global floating point
-environment assuming an IEEE 754 representation. The initial value is
-15
.
-
-
-setPrec(f, p[, e])
-Set the precision of the global floating point environment to p
-and the exponent size to e
then call the function
-f
. Then the Float precision and exponent size are reset to
-their precious value and the return value of f
is returned (or
-an exception is raised if f
raised an exception). If e
-is undefined
it is set to BigFloatEnv.expBitsMax
.
-
-
-precMin
-Read-only integer. Return the minimum allowed precision. Must be at least 2.
-
-
-precMax
-Read-only integer. Return the maximum allowed precision. Must be at least 113.
-
-
-expBitsMin
-Read-only integer. Return the minimum allowed exponent size in
-bits. Must be at least 3.
-
-
-expBitsMax
-Read-only integer. Return the maximum allowed exponent size in
-bits. Must be at least 15.
-
-
-RNDN
-Read-only integer. Round to nearest, with ties to even rounding mode.
-
-
-RNDZ
-Read-only integer. Round to zero rounding mode.
-
-
-RNDD
-Read-only integer. Round to -Infinity rounding mode.
-
-
-RNDU
-Read-only integer. Round to +Infinity rounding mode.
-
-
-RNDNA
-Read-only integer. Round to nearest, with ties away from zero rounding mode.
-
-
-RNDA
-Read-only integer. Round away from zero rounding mode.
-
-
-RNDF4
-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.
-
-
-
-
-BigFloatEnv.prototype
properties:
-
-
-prec
-Getter and setter (Integer). Return or set the precision in bits.
-
-
-expBits
-Getter and setter (Integer). Return or set the exponent size in bits
-assuming an IEEE 754 representation.
-
-
-rndMode
-Getter and setter (Integer). Return or set the rounding mode.
-
-
-subnormal
-Getter and setter (Boolean). subnormal flag. It is false when
-expBits = expBitsMax
.
-
-
-clearStatus()
-Clear the status flags.
-
-
-invalidOperation
-divideByZero
-overflow
-underflow
-inexact
-Getter and setter (Boolean). Status flags.
-
-
-
-
-
-5 BigDecimal
-
-This extension adds the BigDecimal
primitive type. The
-BigDecimal
type represents floating point numbers in base
-10. It is inspired from the proposal available at
-https://github.com/littledan/proposal-bigdecimal .
-
-The BigDecimal
floating point numbers are always normalized and
-finite. There is no concept of -0
, Infinity
or
-NaN
. By default, all the computations are done with infinite
-precision.
-
-
-5.1 Operators
-
-The following builtin operators support BigDecimal:
-
-
-+
--
-*
-Both operands must be BigDecimal. The result is computed with infinite
-precision.
-
-%
-Both operands must be BigDecimal. The result is computed with infinite
-precision. A range error is throws in case of division by zero.
-
-
-/
-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 BigDecimal.div
to specify the rounding).
-
-
-**
-Both operands must be BigDecimal. The exponent must be a positive
-integer. The result is computed with infinite precision.
-
-
-===
-When one of the operand is a BigDecimal, return true if both operands
-are a BigDecimal and if they are equal.
-
-
-==
-!=
-<=
->=
-<
->
-
-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.
-
-
-
-
-
-5.2 BigDecimal literals
-
-BigDecimal literals are decimal floating point numbers with a trailing
-m
suffix.
-
-
-5.3 Builtin Object changes
-
-
-5.3.1 The BigDecimal
function.
-
-It returns 0m
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.
-
-
-5.3.2 Properties of the BigDecimal
object
-
-
-add(a, b[, e])
-sub(a, b[, e])
-mul(a, b[, e])
-div(a, b[, e])
-mod(a, b[, e])
-sqrt(a, e)
-round(a, e)
-Perform the specified floating point operation and round the floating
-point result according to the rounding object e
. If the
-rounding object is not present, the operation is executed with
-infinite precision.
-
-For div
, a RangeError
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.
-
-For sqrt
, a range error is thrown if a
is less than
-zero.
-
-The rounding object must contain the following properties:
-roundingMode
is a string specifying the rounding mode
-("floor"
, "ceiling"
, "down"
, "up"
,
-"half-even"
, "half-up"
). Either
-maximumSignificantDigits
or maximumFractionDigits
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).
-
-
-
-
-
-5.3.3 Properties of the BigDecimal.prototype
object
-
-
-valueOf()
-Return the bigdecimal primitive value corresponding to this
.
-
-
-toString()
-Convert this
to a string with infinite precision in base 10.
-
-
-toPrecision(p, rnd_mode = "half-up")
-toFixed(p, rnd_mode = "half-up")
-toExponential(p, rnd_mode = "half-up")
-Convert the BigDecimal this
to string with the specified
-precision p
. There is no limit on the accepted precision
-p
. The rounding mode can be optionally
-specified. toPrecision
outputs either in decimal fixed notation
-or in decimal exponential notation with a p
digits of
-precision. toExponential
outputs in decimal exponential
-notation with p
digits after the decimal point. toFixed
-outputs in decimal notation with p
digits after the decimal
-point.
-
-
-
-
-
-6 Math mode
-
-A new math mode is enabled with the "use math"
-directive. It propagates the same way as the strict mode . 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.
-
-The following changes are made to the Javascript semantics:
-
-
- Floating point literals (i.e. number with a decimal point or an exponent) are BigFloat
by default (i.e. a l
suffix is implied). Hence typeof 1.0 === "bigfloat"
.
-
- Integer literals (i.e. numbers without a decimal point or an exponent) with or without the n
suffix are BigInt
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 2**53-1
. Hence typeof 1 === "number "
, typeof 1n === "number"
but typeof 9007199254740992 === "bigint"
.
-
- 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.
-
- 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.
-
- The ^
operator is an alias to the power operator (**
).
-
- The power operator (both ^
and **
) grammar is modified so that -2^2
is allowed and yields -4
.
-
- The logical xor operator is still available with the ^^
operator.
-
- The modulo operator (%
) returns the Euclidian remainder (always positive) instead of the truncated remainder.
-
- The integer division operator can be overloaded with Operators.updateBigIntOperators(dictionary)
.
-
- The integer power operator with a non zero negative exponent can be overloaded with Operators.updateBigIntOperators(dictionary)
.
-
-
-
-
-
-
-
-
-
-
diff --git a/doc/jsbignum.pdf b/doc/jsbignum.pdf
deleted file mode 100644
index 442a8c0..0000000
Binary files a/doc/jsbignum.pdf and /dev/null differ
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 @@
-
-
-
-
-QuickJS Javascript Engine
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-QuickJS Javascript Engine
-
-
-Table of Contents
-
-
-
-
-
-1 Introduction
-
-QuickJS is a small and embeddable Javascript engine. It supports the
-ES2020 specification
-1
-including modules, asynchronous generators, proxies and BigInt.
-
-It supports mathematical extensions such as big decimal float float
-numbers (BigDecimal), big binary floating point numbers (BigFloat),
-and operator overloading.
-
-
-1.1 Main Features
-
-
- Small and easily embeddable: just a few C files, no external dependency, 210 KiB of x86 code for a simple “hello world” program.
-
- Fast interpreter with very low startup time: runs the 69000 tests of the ECMAScript Test Suite2 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.
-
- Almost complete ES2020 support including modules, asynchronous
-generators and full Annex B support (legacy web compatibility). Many
-features from the upcoming ES2021 specification
-3 are also supported.
-
- Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2020 features.
-
- Compile Javascript sources to executables with no external dependency.
-
- Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal.
-
- Mathematical extensions: BigDecimal, BigFloat, operator overloading, bigint mode, math mode.
-
- Command line interpreter with contextual colorization and completion implemented in Javascript.
-
- Small built-in standard library with C library wrappers.
-
-
-
-
-2 Usage
-
-
-2.1 Installation
-
-A Makefile is provided to compile the engine on Linux or MacOS/X. A
-preliminary Windows support is available thru cross compilation on a
-Linux host with the MingGW tools.
-
-Edit the top of the Makefile
if you wish to select specific
-options then run make
.
-
-You can type make install
as root if you wish to install the binaries and support files to
-/usr/local
(this is not necessary to use QuickJS).
-
-
-2.2 Quick start
-
-qjs
is the command line interpreter (Read-Eval-Print Loop). You can pass
-Javascript files and/or expressions as arguments to execute them:
-
-
-
./qjs examples/hello.js
-
-
-qjsc
is the command line compiler:
-
-
-
./qjsc -o hello examples/hello.js
-./hello
-
-
-generates a hello
executable with no external dependency.
-
-
-2.3 Command line options
-
-
-2.3.1 qjs
interpreter
-
-usage: qjs [options] [file [args]]
-
-Options are:
-
--h
---help
-List options.
-
-
--e EXPR
---eval EXPR
-Evaluate EXPR.
-
-
--i
---interactive
-Go to interactive mode (it is not the default when files are provided on the command line).
-
-
--m
---module
-Load as ES6 module (default=autodetect). A module is autodetected if
-the filename extension is .mjs
or if the first keyword of the
-source is import
.
-
-
---script
-Load as ES6 script (default=autodetect).
-
-
---bignum
-Enable the bignum extensions: BigDecimal object, BigFloat object and
-the "use math"
directive.
-
-
--I file
---include file
-Include an additional file.
-
-
-
-
-Advanced options are:
-
-
---std
-Make the std
and os
modules available to the loaded
-script even if it is not a module.
-
-
--d
---dump
-Dump the memory usage stats.
-
-
--q
---quit
-just instantiate the interpreter and quit.
-
-
-
-
-
-2.3.2 qjsc
compiler
-
-usage: qjsc [options] [files]
-
-Options are:
-
--c
-Only output bytecode in a C file. The default is to output an executable file.
-
--e
-Output main()
and bytecode in a C file. The default is to output an
-executable file.
-
--o output
-Set the output filename (default = out.c or a.out ).
-
-
--N cname
-Set the C name of the generated data.
-
-
--m
-Compile as Javascript module (default=autodetect).
-
-
--D module_name
-Compile a dynamically loaded module and its dependencies. This option
-is needed when your code uses the import
keyword or the
-os.Worker
constructor because the compiler cannot statically
-find the name of the dynamically loaded modules.
-
-
--M module_name[,cname]
-Add initialization code for an external C module. See the
-c_module
example.
-
-
--x
-Byte swapped output (only used for cross compilation).
-
-
--flto
-Use link time optimization. The compilation is slower but the
-executable is smaller and faster. This option is automatically set
-when the -fno-x
options are used.
-
-
--fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint]
-Disable selected language features to produce a smaller executable file.
-
-
--fbignum
-Enable the bignum extensions: BigDecimal object, BigFloat object and
-the "use math"
directive.
-
-
-
-
-
-2.4 qjscalc
application
-
-The qjscalc
application is a superset of the qjs
-command line interpreter implementing a Javascript calculator with
-arbitrarily large integer and floating point numbers, fractions,
-complex numbers, polynomials and matrices. The source code is in
-qjscalc.js . More documentation and a web version are available at
-http://numcalc.com .
-
-
-2.5 Built-in tests
-
-Run make test
to run the few built-in tests included in the
-QuickJS archive.
-
-
-2.6 Test262 (ECMAScript Test Suite)
-
-A test262 runner is included in the QuickJS archive. The test262 tests
-can be installed in the QuickJS source directory with:
-
-
-
git clone https://github.com/tc39/test262.git test262
-cd test262
-patch -p1 < ../tests/test262.patch
-cd ..
-
-
-The patch adds the implementation specific harness
functions
-and optimizes the inefficient RegExp character classes and Unicode
-property escapes tests (the tests themselves are not modified, only a
-slow string initialization function is optimized).
-
-The tests can be run with
-
-
-The configuration files test262.conf
-(resp. test262o.conf
for the old ES5.1 tests4 ))
-contain the options to run the various tests. Tests can be excluded
-based on features or filename.
-
-The file test262_errors.txt
contains the current list of
-errors. The runner displays a message when a new error appears or when
-an existing error is corrected or modified. Use the -u
option
-to update the current list of errors (or make test2-update
).
-
-The file test262_report.txt
contains the logs of all the
-tests. It is useful to have a clearer analysis of a particular
-error. In case of crash, the last line corresponds to the failing
-test.
-
-Use the syntax ./run-test262 -c test262.conf -f filename.js
to
-run a single test. Use the syntax ./run-test262 -c test262.conf
-N
to start testing at test number N
.
-
-For more information, run ./run-test262
to see the command line
-options of the test262 runner.
-
-run-test262
accepts the -N
option to be invoked from
-test262-harness
5
-thru eshost
. Unless you want to compare QuickJS with other
-engines under the same conditions, we do not recommend to run the
-tests this way as it is much slower (typically half an hour instead of
-about 100 seconds).
-
-
-3 Specifications
-
-
-3.1 Language support
-
-
-3.1.1 ES2020 support
-
-The ES2020 specification is almost fully supported including the Annex
-B (legacy web compatibility) and the Unicode related features.
-
-The following features are not supported yet:
-
-
-
-
-3.1.2 ECMA402
-
-ECMA402 (Internationalization API) is not supported.
-
-
-3.1.3 Extensions
-
-
- The directive "use strip"
indicates that the debug information (including the source code of the functions) should not be retained to save memory. As "use strict"
, the directive can be global to a script or local to a function.
-
- The first line of a script beginning with #!
is ignored.
-
-
-
-
-3.1.4 Mathematical extensions
-
-The mathematical extensions are fully backward compatible with
-standard Javascript. See jsbignum.pdf
for more information.
-
-
- BigDecimal
support: arbitrary large floating point numbers in base 10.
-
- BigFloat
support: arbitrary large floating point numbers in base 2.
-
- Operator overloading.
-
- The directive "use bigint"
enables the bigint mode where integers are BigInt
by default.
-
- The directive "use math"
enables the math mode where the division and power operators on integers produce fractions. Floating point literals are BigFloat
by default and integers are BigInt
by default.
-
-
-
-
-3.2 Modules
-
-ES6 modules are fully supported. The default name resolution is the
-following:
-
-
- Module names with a leading .
or ..
are relative
-to the current module path.
-
- Module names without a leading .
or ..
are system
-modules, such as std
or os
.
-
- Module names ending with .so
are native modules using the
-QuickJS C API.
-
-
-
-
-3.3 Standard library
-
-The standard library is included by default in the command line
-interpreter. It contains the two modules std
and os
and
-a few global objects.
-
-
-3.3.1 Global objects
-
-
-scriptArgs
-Provides the command line arguments. The first argument is the script name.
-
-print(...args)
-Print the arguments separated by spaces and a trailing newline.
-
-console.log(...args)
-Same as print().
-
-
-
-
-
-3.3.2 std
module
-
-The std
module provides wrappers to the libc stdlib.h
-and stdio.h and a few other utilities.
-
-Available exports:
-
-
-exit(n)
-Exit the process.
-
-
-evalScript(str, options = undefined)
-Evaluate the string str
as a script (global
-eval). options
is an optional object containing the following
-optional properties:
-
-
-backtrace_barrier
-Boolean (default = false). If true, error backtraces do not list the
- stack frames below the evalScript.
-
-
-
-
-loadScript(filename)
-Evaluate the file filename
as a script (global eval).
-
-
-loadFile(filename)
-Load the file filename
and return it as a string assuming UTF-8
-encoding. Return null
in case of I/O error.
-
-
-open(filename, flags, errorObj = undefined)
-Open a file (wrapper to the libc fopen()
). Return the FILE
-object or null
in case of I/O error. If errorObj
is not
-undefined, set its errno
property to the error code or to 0 if
-no error occured.
-
-
-popen(command, flags, errorObj = undefined)
-Open a process by creating a pipe (wrapper to the libc
-popen()
). Return the FILE
-object or null
in case of I/O error. If errorObj
is not
-undefined, set its errno
property to the error code or to 0 if
-no error occured.
-
-
-fdopen(fd, flags, errorObj = undefined)
-Open a file from a file handle (wrapper to the libc
-fdopen()
). Return the FILE
-object or null
in case of I/O error. If errorObj
is not
-undefined, set its errno
property to the error code or to 0 if
-no error occured.
-
-
-tmpfile(errorObj = undefined)
-Open a temporary file. Return the FILE
-object or null
in case of I/O error. If errorObj
is not
-undefined, set its errno
property to the error code or to 0 if
-no error occured.
-
-
-puts(str)
-Equivalent to std.out.puts(str)
.
-
-
-printf(fmt, ...args)
-Equivalent to std.out.printf(fmt, ...args)
.
-
-
-sprintf(fmt, ...args)
-Equivalent to the libc sprintf().
-
-
-in
-out
-err
-Wrappers to the libc file stdin
, stdout
, stderr
.
-
-
-SEEK_SET
-SEEK_CUR
-SEEK_END
-Constants for seek().
-
-
-Error
-
-Enumeration object containing the integer value of common errors
-(additional error codes may be defined):
-
-
-EINVAL
-EIO
-EACCES
-EEXIST
-ENOSPC
-ENOSYS
-EBUSY
-ENOENT
-EPERM
-EPIPE
-
-
-
-strerror(errno)
-Return a string that describes the error errno
.
-
-
-gc()
-Manually invoke the cycle removal algorithm. The cycle removal
-algorithm is automatically started when needed, so this function is
-useful in case of specific memory constraints or for testing.
-
-
-getenv(name)
-Return the value of the environment variable name
or
-undefined
if it is not defined.
-
-
-urlGet(url, options = undefined)
-
-Download url
using the curl command line
-utility. options
is an optional object containing the following
-optional properties:
-
-
-binary
-Boolean (default = false). If true, the response is an ArrayBuffer
- instead of a string. When a string is returned, the data is assumed
- to be UTF-8 encoded.
-
-
-full
-
-Boolean (default = false). If true, return the an object contains
- the properties response
(response content),
- responseHeaders
(headers separated by CRLF), status
- (status code). response
is null
is case of protocol or
- network error. If full
is false, only the response is
- returned if the status is between 200 and 299. Otherwise null
- is returned.
-
-
-
-
-
-parseExtJSON(str)
-
-Parse str
using a superset of JSON.parse
. The
- following extensions are accepted:
-
-
- Single line and multiline comments
- unquoted properties (ASCII-only Javascript identifiers)
- trailing comma in array and object definitions
- single quoted strings
- \f
and \v
are accepted as space characters
- leading plus in numbers
- octal (0o
prefix) and hexadecimal (0x
prefix) numbers
-
-
-
-
-FILE prototype:
-
-
-close()
-Close the file. Return 0 if OK or -errno
in case of I/O error.
-
-puts(str)
-Outputs the string with the UTF-8 encoding.
-
-printf(fmt, ...args)
-Formatted printf.
-
-The same formats as the standard C library printf
are
-supported. Integer format types (e.g. %d
) truncate the Numbers
-or BigInts to 32 bits. Use the l
modifier (e.g. %ld
) to
-truncate to 64 bits.
-
-
-flush()
-Flush the buffered file.
-
-seek(offset, whence)
-Seek to a give file position (whence is
-std.SEEK_*
). offset
can be a number or a bigint. Return
-0 if OK or -errno
in case of I/O error.
-
-tell()
-Return the current file position.
-
-tello()
-Return the current file position as a bigint.
-
-eof()
-Return true if end of file.
-
-fileno()
-Return the associated OS handle.
-
-error()
-Return true if there was an error.
-
-clearerr()
-Clear the error indication.
-
-
-read(buffer, position, length)
-Read length
bytes from the file to the ArrayBuffer buffer
at byte
-position position
(wrapper to the libc fread
).
-
-
-write(buffer, position, length)
-Write length
bytes to the file from the ArrayBuffer buffer
at byte
-position position
(wrapper to the libc fread
).
-
-
-getline()
-Return the next line from the file, assuming UTF-8 encoding, excluding
-the trailing line feed.
-
-
-readAsString(max_size = undefined)
-Read max_size
bytes from the file and return them as a string
-assuming UTF-8 encoding. If max_size
is not present, the file
-is read up its end.
-
-
-getByte()
-Return the next byte from the file. Return -1 if the end of file is reached.
-
-
-putByte(c)
-Write one byte to the file.
-
-
-
-
-3.3.3 os
module
-
-The os
module provides Operating System specific functions:
-
-
- low level file access
- signals
- timers
- asynchronous I/O
- workers (threads)
-
-
-The OS functions usually return 0 if OK or an OS specific negative
-error code.
-
-Available exports:
-
-
-open(filename, flags, mode = 0o666)
-Open a file. Return a handle or < 0 if error.
-
-
-O_RDONLY
-O_WRONLY
-O_RDWR
-O_APPEND
-O_CREAT
-O_EXCL
-O_TRUNC
-POSIX open flags.
-
-
-O_TEXT
-(Windows specific). Open the file in text mode. The default is binary mode.
-
-
-close(fd)
-Close the file handle fd
.
-
-
-seek(fd, offset, whence)
-Seek in the file. Use std.SEEK_*
for
-whence
. offset
is either a number or a bigint. If
-offset
is a bigint, a bigint is returned too.
-
-
-read(fd, buffer, offset, length)
-Read length
bytes from the file handle fd
to the
-ArrayBuffer buffer
at byte position offset
.
-Return the number of read bytes or < 0 if error.
-
-
-write(fd, buffer, offset, length)
-Write length
bytes to the file handle fd
from the
-ArrayBuffer buffer
at byte position offset
.
-Return the number of written bytes or < 0 if error.
-
-
-isatty(fd)
-Return true
is fd
is a TTY (terminal) handle.
-
-
-ttyGetWinSize(fd)
-Return the TTY size as [width, height]
or null
if not available.
-
-
-ttySetRaw(fd)
-Set the TTY in raw mode.
-
-
-remove(filename)
-Remove a file. Return 0 if OK or -errno
.
-
-
-rename(oldname, newname)
-Rename a file. Return 0 if OK or -errno
.
-
-
-realpath(path)
-Return [str, err]
where str
is the canonicalized absolute
-pathname of path
and err
the error code.
-
-
-getcwd()
-Return [str, err]
where str
is the current working directory
-and err
the error code.
-
-
-chdir(path)
-Change the current directory. Return 0 if OK or -errno
.
-
-
-mkdir(path, mode = 0o777)
-Create a directory at path
. Return 0 if OK or -errno
.
-
-
-stat(path)
-lstat(path)
-
-Return [obj, err]
where obj
is an object containing the
-file status of path
. err
is the error code. The
-following fields are defined in obj
: dev, ino, mode, nlink,
-uid, gid, rdev, size, blocks, atime, mtime, ctime. The times are
-specified in milliseconds since 1970. lstat()
is the same as
-stat()
excepts that it returns information about the link
-itself.
-
-
-S_IFMT
-S_IFIFO
-S_IFCHR
-S_IFDIR
-S_IFBLK
-S_IFREG
-S_IFSOCK
-S_IFLNK
-S_ISGID
-S_ISUID
-Constants to interpret the mode
property returned by
-stat()
. They have the same value as in the C system header
-sys/stat.h .
-
-
-utimes(path, atime, mtime)
-Change the access and modification times of the file path
. The
-times are specified in milliseconds since 1970. Return 0 if OK or -errno
.
-
-
-symlink(target, linkpath)
-Create a link at linkpath
containing the string target
. Return 0 if OK or -errno
.
-
-
-readlink(path)
-Return [str, err]
where str
is the link target and err
-the error code.
-
-
-readdir(path)
-Return [array, err]
where array
is an array of strings
-containing the filenames of the directory path
. err
is
-the error code.
-
-
-setReadHandler(fd, func)
-Add a read handler to the file handle fd
. func
is called
-each time there is data pending for fd
. A single read handler
-per file handle is supported. Use func = null
to remove the
-handler.
-
-
-setWriteHandler(fd, func)
-Add a write handler to the file handle fd
. func
is
-called each time data can be written to fd
. A single write
-handler per file handle is supported. Use func = null
to remove
-the handler.
-
-
-signal(signal, func)
-Call the function func
when the signal signal
-happens. Only a single handler per signal number is supported. Use
-null
to set the default handler or undefined
to ignore
-the signal. Signal handlers can only be defined in the main thread.
-
-
-SIGINT
-SIGABRT
-SIGFPE
-SIGILL
-SIGSEGV
-SIGTERM
-POSIX signal numbers.
-
-
-kill(pid, sig)
-Send the signal sig
to the process pid
.
-
-
-exec(args[, options])
-Execute a process with the arguments args
. options
is an
-object containing optional parameters:
-
-
-block
-Boolean (default = true). If true, wait until the process is
- terminated. In this case, exec
return the exit code if positive
- or the negated signal number if the process was interrupted by a
- signal. If false, do not block and return the process id of the child.
-
-
-usePath
-Boolean (default = true). If true, the file is searched in the
- PATH
environment variable.
-
-
-file
-String (default = args[0]
). Set the file to be executed.
-
-
-cwd
-String. If present, set the working directory of the new process.
-
-
-stdin
-stdout
-stderr
-If present, set the handle in the child for stdin, stdout or stderr.
-
-
-env
-Object. If present, set the process environment from the object
- key-value pairs. Otherwise use the same environment as the current
- process.
-
-
-uid
-Integer. If present, the process uid with setuid
.
-
-
-gid
-Integer. If present, the process gid with setgid
.
-
-
-
-
-
-waitpid(pid, options)
-waitpid
Unix system call. Return the array [ret,
-status]
. ret
contains -errno
in case of error.
-
-
-WNOHANG
-Constant for the options
argument of waitpid
.
-
-
-dup(fd)
-dup
Unix system call.
-
-
-dup2(oldfd, newfd)
-dup2
Unix system call.
-
-
-pipe()
-pipe
Unix system call. Return two handles as [read_fd,
-write_fd]
or null in case of error.
-
-
-sleep(delay_ms)
-Sleep during delay_ms
milliseconds.
-
-
-setTimeout(func, delay)
-Call the function func
after delay
ms. Return a handle
-to the timer.
-
-
-clearTimeout(handle)
-Cancel a timer.
-
-
-platform
-Return a string representing the platform: "linux"
, "darwin"
,
-"win32"
or "js"
.
-
-
-Worker(module_filename)
-Constructor to create a new thread (worker) with an API close to the
-WebWorkers
. module_filename
is a string specifying the
-module filename which is executed in the newly created thread. As for
-dynamically imported module, it is relative to the current script or
-module path. Threads normally don’t share any data and communicate
-between each other with messages. Nested workers are not supported. An
-example is available in tests/test_worker.js .
-
-The worker class has the following static properties:
-
-
-parent
-In the created worker, Worker.parent
represents the parent
- worker and is used to send or receive messages.
-
-
-
-The worker instances have the following properties:
-
-
-postMessage(msg)
-
-Send a message to the corresponding worker. msg
is cloned in
- the destination worker using an algorithm similar to the HTML
- structured clone algorithm. SharedArrayBuffer
are shared
- between workers.
-
-Current limitations: Map
and Set
are not supported
- yet.
-
-
-onmessage
-
-Getter and setter. Set a function which is called each time a
- message is received. The function is called with a single
- argument. It is an object with a data
property containing the
- received message. The thread is not terminated if there is at least
- one non null
onmessage
handler.
-
-
-
-
-
-
-
-
-3.4 QuickJS C API
-
-The C API was designed to be simple and efficient. The C API is
-defined in the header quickjs.h
.
-
-
-3.4.1 Runtime and contexts
-
-JSRuntime
represents a Javascript runtime corresponding to an
-object heap. Several runtimes can exist at the same time but they
-cannot exchange objects. Inside a given runtime, no multi-threading is
-supported.
-
-JSContext
represents a Javascript context (or Realm). Each
-JSContext has its own global objects and system objects. There can be
-several JSContexts per JSRuntime and they can share objects, similar
-to frames of the same origin sharing Javascript objects in a
-web browser.
-
-
-3.4.2 JSValue
-
-JSValue
represents a Javascript value which can be a primitive
-type or an object. Reference counting is used, so it is important to
-explicitly duplicate (JS_DupValue()
, increment the reference
-count) or free (JS_FreeValue()
, decrement the reference count)
-JSValues.
-
-
-3.4.3 C functions
-
-C functions can be created with
-JS_NewCFunction()
. JS_SetPropertyFunctionList()
is a
-shortcut to easily add functions, setters and getters properties to a
-given object.
-
-Unlike other embedded Javascript engines, there is no implicit stack,
-so C functions get their parameters as normal C parameters. As a
-general rule, C functions take constant JSValue
s as parameters
-(so they don’t need to free them) and return a newly allocated (=live)
-JSValue
.
-
-
-3.4.4 Exceptions
-
-Exceptions: most C functions can return a Javascript exception. It
-must be explicitly tested and handled by the C code. The specific
-JSValue
JS_EXCEPTION
indicates that an exception
-occurred. The actual exception object is stored in the
-JSContext
and can be retrieved with JS_GetException()
.
-
-
-3.4.5 Script evaluation
-
-Use JS_Eval()
to evaluate a script or module source.
-
-If the script or module was compiled to bytecode with qjsc
, it
-can be evaluated by calling js_std_eval_binary()
. The advantage
-is that no compilation is needed so it is faster and smaller because
-the compiler can be removed from the executable if no eval
is
-required.
-
-Note: the bytecode format is linked to a given QuickJS
-version. Moreover, no security check is done before its
-execution. Hence the bytecode should not be loaded from untrusted
-sources. That’s why there is no option to output the bytecode to a
-binary file in qjsc
.
-
-
-3.4.6 JS Classes
-
-C opaque data can be attached to a Javascript object. The type of the
-C opaque data is determined with the class ID (JSClassID
) of
-the object. Hence the first step is to register a new class ID and JS
-class (JS_NewClassID()
, JS_NewClass()
). Then you can
-create objects of this class with JS_NewObjectClass()
and get or
-set the C opaque point with
-JS_GetOpaque()
/JS_SetOpaque()
.
-
-When defining a new JS class, it is possible to declare a finalizer
-which is called when the object is destroyed. A gc_mark
method
-can be provided so that the cycle removal algorithm can find the other
-objects referenced by this object. Other methods are available to
-define exotic object behaviors.
-
-The Class ID are globally allocated (i.e. for all runtimes). The
-JSClass are allocated per JSRuntime
. JS_SetClassProto()
-is used to define a prototype for a given class in a given
-JSContext. JS_NewObjectClass()
sets this prototype in the
-created object.
-
-Examples are available in quickjs-libc.c .
-
-
-3.4.7 C Modules
-
-Native ES6 modules are supported and can be dynamically or statically
-linked. Look at the test_bjson and bjson.so
-examples. The standard library quickjs-libc.c is also a good example
-of a native module.
-
-
-3.4.8 Memory handling
-
-Use JS_SetMemoryLimit()
to set a global memory allocation limit
-to a given JSRuntime.
-
-Custom memory allocation functions can be provided with
-JS_NewRuntime2()
.
-
-The maximum system stack size can be set with JS_SetMaxStackSize()
.
-
-
-3.4.9 Execution timeout and interrupts
-
-Use JS_SetInterruptHandler()
to set a callback which is
-regularly called by the engine when it is executing code. This
-callback can be used to implement an execution timeout.
-
-It is used by the command line interpreter to implement a
-Ctrl-C
handler.
-
-
-4 Internals
-
-
-4.1 Bytecode
-
-The compiler generates bytecode directly with no intermediate
-representation such as a parse tree, hence it is very fast. Several
-optimizations passes are done over the generated bytecode.
-
-A stack-based bytecode was chosen because it is simple and generates
-compact code.
-
-For each function, the maximum stack size is computed at compile time so that
-no runtime stack overflow tests are needed.
-
-A separate compressed line number table is maintained for the debug
-information.
-
-Access to closure variables is optimized and is almost as fast as local
-variables.
-
-Direct eval
in strict mode is optimized.
-
-
-4.2 Executable generation
-
-
-4.2.1 qjsc
compiler
-
-The qjsc
compiler generates C sources from Javascript files. By
-default the C sources are compiled with the system compiler
-(gcc
or clang
).
-
-The generated C source contains the bytecode of the compiled functions
-or modules. If a full complete executable is needed, it also
-contains a main()
function with the necessary C code to initialize the
-Javascript engine and to load and execute the compiled functions and
-modules.
-
-Javascript code can be mixed with C modules.
-
-In order to have smaller executables, specific Javascript features can
-be disabled, in particular eval
or the regular expressions. The
-code removal relies on the Link Time Optimization of the system
-compiler.
-
-
-4.2.2 Binary JSON
-
-qjsc
works by compiling scripts or modules and then serializing
-them to a binary format. A subset of this format (without functions or
-modules) can be used as binary JSON. The example test_bjson.js
-shows how to use it.
-
-Warning: the binary JSON format may change without notice, so it
-should not be used to store persistent data. The test_bjson.js
-example is only used to test the binary object format functions.
-
-
-4.3 Runtime
-
-
-4.3.1 Strings
-
-Strings are stored either as an 8 bit or a 16 bit array of
-characters. Hence random access to characters is always fast.
-
-The C API provides functions to convert Javascript Strings to C UTF-8 encoded
-strings. The most common case where the Javascript string contains
-only ASCII characters involves no copying.
-
-
-4.3.2 Objects
-
-The object shapes (object prototype, property names and flags) are shared
-between objects to save memory.
-
-Arrays with no holes (except at the end of the array) are optimized.
-
-TypedArray accesses are optimized.
-
-
-4.3.3 Atoms
-
-Object property names and some strings are stored as Atoms (unique
-strings) to save memory and allow fast comparison. Atoms are
-represented as a 32 bit integer. Half of the atom range is reserved for
-immediate integer literals from 0 to 2^{31}-1 .
-
-
-4.3.4 Numbers
-
-Numbers are represented either as 32-bit signed integers or 64-bit IEEE-754
-floating point values. Most operations have fast paths for the 32-bit
-integer case.
-
-
-4.3.5 Garbage collection
-
-Reference counting is used to free objects automatically and
-deterministically. A separate cycle removal pass is done when the allocated
-memory becomes too large. The cycle removal algorithm only uses the
-reference counts and the object content, so no explicit garbage
-collection roots need to be manipulated in the C code.
-
-
-4.3.6 JSValue
-
-It is a Javascript value which can be a primitive type (such as
-Number, String, ...) or an Object. NaN boxing is used in the 32-bit version
-to store 64-bit floating point numbers. The representation is
-optimized so that 32-bit integers and reference counted values can be
-efficiently tested.
-
-In 64-bit code, JSValue are 128-bit large and no NaN boxing is used. The
-rationale is that in 64-bit code memory usage is less critical.
-
-In both cases (32 or 64 bits), JSValue exactly fits two CPU registers,
-so it can be efficiently returned by C functions.
-
-
-4.3.7 Function call
-
-The engine is optimized so that function calls are fast. The system
-stack holds the Javascript parameters and local variables.
-
-
-4.4 RegExp
-
-A specific regular expression engine was developed. It is both small
-and efficient and supports all the ES2020 features including the
-Unicode properties. As the Javascript compiler, it directly generates
-bytecode without a parse tree.
-
-Backtracking with an explicit stack is used so that there is no
-recursion on the system stack. Simple quantifiers are specifically
-optimized to avoid recursions.
-
-Infinite recursions coming from quantifiers with empty terms are
-avoided.
-
-The full regexp library weights about 15 KiB (x86 code), excluding the
-Unicode library.
-
-
-4.5 Unicode
-
-A specific Unicode library was developed so that there is no
-dependency on an external large Unicode library such as ICU. All the
-Unicode tables are compressed while keeping a reasonable access
-speed.
-
-The library supports case conversion, Unicode normalization, Unicode
-script queries, Unicode general category queries and all Unicode
-binary properties.
-
-The full Unicode library weights about 45 KiB (x86 code).
-
-
-4.6 BigInt, BigFloat, BigDecimal
-
-BigInt, BigFloat and BigDecimal are implemented with the libbf
-library7 . It weights about 90
-KiB (x86 code) and provides arbitrary precision IEEE 754 floating
-point operations and transcendental functions with exact rounding.
-
-
-5 License
-
-QuickJS is released under the MIT license.
-
-Unless otherwise specified, the QuickJS sources are copyright Fabrice
-Bellard and Charlie Gordon.
-
-
-
-
-
-
-
-
diff --git a/doc/quickjs.pdf b/doc/quickjs.pdf
deleted file mode 100644
index 497964b..0000000
Binary files a/doc/quickjs.pdf and /dev/null differ
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
-#include
-#include
-#include
-#include
-#include
-#include
-
-#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;
-}
diff --git a/libbf.h b/libbf.h
index 6749b35..4576bf8 100644
--- a/libbf.h
+++ b/libbf.h
@@ -27,7 +27,7 @@
#include
#include
-#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;
diff --git a/qjscalc.js b/qjscalc.js
index 493869e..b7c5001 100644
--- a/qjscalc.js
+++ b/qjscalc.js
@@ -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_, "")
DEF(_ret_, "")
DEF(_var_, "")
+DEF(_arg_var_, "")
DEF(_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 */
diff --git a/quickjs.c b/quickjs.c
index efc1d54..a39ff8f 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -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,8 +16695,11 @@ 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);
}
@@ -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);
+ label_loop = new_label(s);
+ label_yield = new_label(s);
- emit_op(s, OP_for_await_of_start);
+ emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);
- /* 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_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);
- 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_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_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_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_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);
+ /* 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_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_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_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_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 and variables */
- for(i = 0; i < s->hoisted_def_count; i++) {
- JSHoistedDef *hf = &s->hoisted_def[i];
+ /* 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);
+ }
+ }
+
+ /* 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;
+ 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) {
+ JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
+ return -1;
+ }
+ }
+ 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;
+ }
+ }
+
+ /* 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;
- 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;
- }
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;
- }
- } else {
- s->stack_level_tab[pos] = stack_len;
- }
+ 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);
}
diff --git a/quickjs.h b/quickjs.h
index bb84829..92457a2 100644
--- a/quickjs.h
+++ b/quickjs.h
@@ -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);
diff --git a/release.sh b/release.sh
index a0bd6eb..6dc89fb 100755
--- a/release.sh
+++ b/release.sh
@@ -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"
+release_list="extras binary win_binary quickjs"
-if [ "$1" = "all" ] ; then
- extras="yes"
- binary="yes"
- quickjs="yes"
-elif [ "$1" = "binary" ] ; then
- binary="yes"
-else
- quickjs="yes"
+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 [ "$binary" = "yes" ] ; then
+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 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();