diff --git a/.gitignore b/.gitignore
index 9f66f9b5..c6b3fc91 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,9 @@
*.swp
*.o
*.strip
-/duk.O2.*
+/_*
+/duk.*
+/libduktape.*
/build/
/dist/
/site/
diff --git a/Makefile b/Makefile
index bb9b5f4f..853ccf2f 100644
--- a/Makefile
+++ b/Makefile
@@ -1144,6 +1144,15 @@ codepolicycheck:
--check-cpp-comment \
--dump-vim-commands \
src/*.c src/*.h src/*.h.in tests/api/*.c
+ @$(PYTHON) util/check_code_policy.py \
+ $(CODEPOLICYOPTS) \
+ --check-carriage-returns \
+ --check-fixme \
+ --check-non-ascii \
+ --check-trailing-whitespace \
+ --check-mixed-indent \
+ --dump-vim-commands \
+ src/*.py
@$(PYTHON) util/check_code_policy.py \
$(CODEPOLICYOPTS) \
--check-debug-log-calls \
@@ -1210,6 +1219,11 @@ codepolicycheck:
--check-nonleading-tab \
--dump-vim-commands \
website/api/*.yaml website/api/*.html
+ @$(PYTHON) util/check_code_policy.py \
+ $(CODEPOLICYOPTS) \
+ --check-carriage-returns \
+ --dump-vim-commands \
+ doc/*.rst
.PHONY: codepolicycheckvim
codepolicycheckvim:
diff --git a/RELEASES.rst b/RELEASES.rst
index 1c83b72b..6e5a0b4b 100644
--- a/RELEASES.rst
+++ b/RELEASES.rst
@@ -1680,6 +1680,14 @@ Planned
* Make coroutine support optional (DUK_USE_COROUTINE_SUPPORT); disabling
coroutines reduces code footprint by about 2kB (GH-829)
+* Add an internal type for representing Array instances (duk_harray) to
+ simplify array operations and improve performance; this also changes the
+ key order of Object.getOwnPropertyNames() for sparse arrays (arrays whose
+ internal "array part" has been abandoned) (GH-703)
+
+* Rename debugger protocol artificial property "bound" to "boundfunction"
+ for consistency with an internal flag rename (GH-703)
+
* Add an extra module (extras/duk-v1-compat) providing many Duktape 1.x API
calls removed in Duktape 2.x (multiple Github issues)
@@ -1721,6 +1729,10 @@ Planned
* Fix -Wshift-sign-overflow warnings on some Clang versions for signed left
shifts whose result was used as unsigned (GH-812, GH-813)
+* Fix JSON.stringify (and JX/JC encode) fast path to allow arrays whose
+ .length is larger than the internal array part size (created e.g. when
+ calling new Array(10)) without falling back to the slow path (GH-703)
+
* Internal performance improvement: avoid one extra shift when computing
reg/const pointers in the bytecode executor (GH-674)
diff --git a/config/examples/low_memory.yaml b/config/examples/low_memory.yaml
index 4472e443..b38425b9 100644
--- a/config/examples/low_memory.yaml
+++ b/config/examples/low_memory.yaml
@@ -18,6 +18,7 @@ DUK_USE_ERRCREATE: false
DUK_USE_ERRTHROW: false
DUK_USE_VERBOSE_ERRORS: false
DUK_USE_PARANOID_ERRORS: true
+DUK_USE_VERBOSE_EXECUTOR_ERRORS: false # <100 bytes footprint
DUK_USE_DEBUGGER_SUPPORT: false # must be disabled if DUK_USE_PC2LINE is disabled
DUK_USE_PC2LINE: false
DUK_USE_LEXER_SLIDING_WINDOW: false
@@ -79,5 +80,7 @@ DUK_USE_REGEXP_CANON_WORKAROUND: false
#DUK_USE_ROM_GLOBAL_INHERIT: true # select inherit or clone; inherit recommended
#DUK_USE_ROM_GLOBAL_CLONE: false
-# Reduce footprint by dropping coroutine support (about 2kB).
+# Consider these:
#DUK_USE_COROUTINE_SUPPORT: false
+#DUK_USE_SOURCE_NONBMP: false # <300 bytes footprint
+#DUK_USE_ES6_PROXY: false # roughly 2kB footprint
diff --git a/doc/buffers.rst b/doc/buffers.rst
index fa8fcb05..97864f86 100644
--- a/doc/buffers.rst
+++ b/doc/buffers.rst
@@ -199,33 +199,33 @@ Summary of buffer-related values
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
| Type | Specification | .length | .byteLength | .byteOffset | .BYTES_PER_ELEMENT | .buffer | [index] | Element type | Read coercion | Write coercion | Endianness | Accessor methods | Notes |
+===================+===============+================+=============+=============+====================+=========+=========+==============+===============+=====================+=============+==================+===================================+
-| plain buffer | Duktape | yes (bytes) | yes | yes | yes | no | yes | uint8 | uint8 | ToUint32() & 0xff | n/a | no | |
+| plain buffer | Duktape | yes (bytes) | yes | yes | yes | no | yes | uint8 | uint8 | ToUint32() & 0xff | n/a | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
| Duktape.Buffer | Duktape | yes (bytes) | yes | yes | 1 | no | yes | uint8 | uint8 | ToUint32() & 0xff | n/a | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Buffer | Node.js | yes (bytes) | yes | yes | 1 | no | yes | uint8 | uint8 | ToUint32() & 0xff | n/a | yes | Based on Node.js v0.12.1. |
+| Buffer | Node.js | yes (bytes) | yes | yes | 1 | no | yes | uint8 | uint8 | ToUint32() & 0xff | n/a | yes | Based on Node.js v0.12.1. |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
| ArrayBuffer | TypedArray | yes (bytes) | yes | yes | 1 | no | yes | uint8 | uint8 | ToUint32() & 0xff | n/a | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
| DataView | TypedArray | yes (bytes) | yes | yes | 1 | yes | yes | uint8 | uint8 | ToUint32() & 0xff | n/a | yes | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Int8Array | TypedArray | yes (bytes) | yes | yes | 1 | yes | yes | int8 | int8 | ToUint32() & 0xff | n/a | no | |
+| Int8Array | TypedArray | yes (bytes) | yes | yes | 1 | yes | yes | int8 | int8 | ToUint32() & 0xff | n/a | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
| Uint8Array | TypedArray | yes (bytes) | yes | yes | 1 | yes | yes | uint8 | uint8 | ToUint32() & 0xff | n/a | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Uint8ClampedArray | TypedArray | yes (bytes) | yes | yes | 1 | yes | yes | uint8 | uint8 | special | n/a | no | Write: special clamp/round. |
+| Uint8ClampedArray | TypedArray | yes (bytes) | yes | yes | 1 | yes | yes | uint8 | uint8 | special | n/a | no | Write: special clamp/round. |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Int16Array | TypedArray | yes (elements) | yes | yes | 2 | yes | yes | int16 | int16 | ToUint32() & 0xffff | host | no | |
+| Int16Array | TypedArray | yes (elements) | yes | yes | 2 | yes | yes | int16 | int16 | ToUint32() & 0xffff | host | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Uint16Array | TypedArray | yes (elements) | yes | yes | 2 | yes | yes | uint16 | uint16 | ToUint32() & 0xffff | host | no | |
+| Uint16Array | TypedArray | yes (elements) | yes | yes | 2 | yes | yes | uint16 | uint16 | ToUint32() & 0xffff | host | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Int32Array | TypedArray | yes (elements) | yes | yes | 4 | yes | yes | int32 | int32 | ToUint32() | host | no | |
+| Int32Array | TypedArray | yes (elements) | yes | yes | 4 | yes | yes | int32 | int32 | ToUint32() | host | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Uint32Array | TypedArray | yes (elements) | yes | yes | 4 | yes | yes | uint32 | uint32 | ToUint32() | host | no | |
+| Uint32Array | TypedArray | yes (elements) | yes | yes | 4 | yes | yes | uint32 | uint32 | ToUint32() | host | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Float32Array | TypedArray | yes (elements) | yes | yes | 4 | yes | yes | float | float | cast to float | host | no | |
+| Float32Array | TypedArray | yes (elements) | yes | yes | 4 | yes | yes | float | float | cast to float | host | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
-| Float64Array | TypedArray | yes (elements) | yes | yes | 8 | yes | yes | double | double | cast to double | host | no | |
+| Float64Array | TypedArray | yes (elements) | yes | yes | 8 | yes | yes | double | double | cast to double | host | no | |
+-------------------+---------------+----------------+-------------+-------------+--------------------+---------+---------+--------------+---------------+---------------------+-------------+------------------+-----------------------------------+
Notes:
@@ -593,10 +593,10 @@ Specification notes
Steps for unsigned byte (octet) clamped coercion:
- - Set x to min(max(x, 0), 2^8 − 1).
+ - Set x to min(max(x, 0), 2^8 - 1).
- Round x to the nearest integer, choosing the even integer if it lies
- halfway between two, and choosing +0 rather than −0.
+ halfway between two, and choosing +0 rather than -0.
- Return the IDL octet value that represents the same numeric value as x.
diff --git a/doc/bytecode.rst b/doc/bytecode.rst
index bad1990d..8fb4734a 100644
--- a/doc/bytecode.rst
+++ b/doc/bytecode.rst
@@ -179,7 +179,7 @@ For example, for a simple Mandelbrot function (``mandel()`` in
+===========================+================+======================+
| Original source | 884 | 371 |
+---------------------------+----------------+----------------------+
-| Bytecode dump | 809 | 504 |
+| Bytecode dump | 809 | 504 |
+---------------------------+----------------+----------------------+
| UglifyJS2-minified source | 364 | 267 |
+---------------------------+----------------+----------------------+
diff --git a/doc/code-issues.rst b/doc/code-issues.rst
index 30de114c..758460fe 100644
--- a/doc/code-issues.rst
+++ b/doc/code-issues.rst
@@ -1663,14 +1663,6 @@ Types used inside Duktape
be larger than 32 bits while individual activations could be limited to
a signed 32 bit index space.
-* **FIXME:** normal vs. fast variables: use tight values in structs,
- "fast" values as e.g. loop counters in fast paths (character / byte
- iteration loops etc)
-
-* **FIXME**: flags field type (storage vs. internal APIs)
-
-* **FIXME**: avoid casting when unnecessary
-
Formatting considerations
-------------------------
diff --git a/doc/compiler.rst b/doc/compiler.rst
index cbe9d80c..fbe3a3ed 100644
--- a/doc/compiler.rst
+++ b/doc/compiler.rst
@@ -1303,7 +1303,7 @@ Statement types generating an empty value indirectly:
Some examples:
+--------------------+-------------+-------------------------------------------------------------------------+
-| Eval argument | Eval result | Notes |
+| Eval argument | Eval result | Notes |
+====================+=============+=========================================================================+
| "1+2;" | 3 | Normal case, expression statement generates implicit return value. |
+--------------------+-------------+-------------------------------------------------------------------------+
@@ -1690,7 +1690,7 @@ ReferenceError at runtime.
A valid left-hand-side expression (such as an identifier) may also be
wrapped in one or more parentheses (i.e., an arbitrary number of tokens)::
- for ( (((i))) in [ 'foo', 'bar' ] ) { }
+ for ( (((i))) in [ 'foo', 'bar' ] ) { }
print(i);
// -> prints 1
diff --git a/doc/debugger.rst b/doc/debugger.rst
index e19d9a76..452cc610 100644
--- a/doc/debugger.rst
+++ b/doc/debugger.rst
@@ -2057,7 +2057,7 @@ The flags field is an unsigned integer bitmask with the following bits:
| Bitmask | Description |
+=========+=================================================================+
| 0x01 | Property attribute: writable, matches |
-| | DUK_PROPDESC_FLAG_WRITABLE. |
+| | DUK_PROPDESC_FLAG_WRITABLE. |
+---------+-----------------------------------------------------------------+
| 0x02 | Property attribute: enumerable, |
| | matches DUK_PROPDESC_FLAG_ENUMERABLE. |
diff --git a/doc/error-objects.rst b/doc/error-objects.rst
index 3e881031..7c740e5c 100644
--- a/doc/error-objects.rst
+++ b/doc/error-objects.rst
@@ -445,7 +445,7 @@ The following SyntaxError illustrates all the relevant file/line sources::
input:3 <-- file/line of source text (SyntaxError)
duk_js_compiler.c:3612 <-- __FILE__ / __LINE__ of DUK_ERROR() call site
eval native strict directeval preventsyield <-- innermost activation, eval() function
- global input:1 preventsyield <-- second innermost activation, caller of eval()
+ global input:1 preventsyield <-- second innermost activation, caller of eval()
input 3 <-- .fileName and .lineNumber blames source text for SyntaxError
From the application point of view the most relevant file/line is usually the
diff --git a/doc/function-objects.rst b/doc/function-objects.rst
index cbb3a40e..85631b31 100644
--- a/doc/function-objects.rst
+++ b/doc/function-objects.rst
@@ -413,4 +413,3 @@ As an example, the bitstream for the diffs [+0, +2, +9, -3, +0] would be::
Typically the pc2line data is about 10-15% of the size of the corresponding
bytecode, a very modest addition to footprint compared to the 100% addition
of a straight table approach.
-
diff --git a/doc/hobject-alg-defineproperty.rst b/doc/hobject-alg-defineproperty.rst
index cb0fd5f2..99e6e3ca 100644
--- a/doc/hobject-alg-defineproperty.rst
+++ b/doc/hobject-alg-defineproperty.rst
@@ -220,16 +220,16 @@ with exotic behaviors, we get:
1. Convert the property named ``P`` of object ``O`` from a data property
to an accessor property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
c. Else,
1. Convert the property named ``P`` of object ``O`` from an accessor
property to a data property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
d. Goto VALIDATED.
@@ -489,16 +489,16 @@ Some cleanup
1. Convert the property named ``P`` of object ``O`` from a data property
to an accessor property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
c. Else,
1. Convert the property named ``P`` of object ``O`` from an accessor
property to a data property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
d. Goto VALIDATED.
diff --git a/doc/hobject-alg-exoticbehaviors.rst b/doc/hobject-alg-exoticbehaviors.rst
index 60fac0e6..eacaac34 100644
--- a/doc/hobject-alg-exoticbehaviors.rst
+++ b/doc/hobject-alg-exoticbehaviors.rst
@@ -26,27 +26,27 @@ Related E5 sections:
Default algorithm
-----------------
-1. If ``O`` doesn’t have an own property with name ``P``, return ``undefined``.
+1. If ``O`` doesn't have an own property with name ``P``, return ``undefined``.
2. Let ``D`` be a newly created Property Descriptor with no fields.
-3. Let ``X`` be ``O``\ ’s own property named P.
+3. Let ``X`` be ``O``\ 's own property named P.
4. If ``X`` is a data property, then
- a. Set ``D.[[Value]]`` to the value of ``X``\ ’s ``[[Value]]`` attribute.
+ a. Set ``D.[[Value]]`` to the value of ``X``\ 's ``[[Value]]`` attribute.
- b. Set ``D.[[Writable]]`` to the value of ``X``\ ’s ``[[Writable]]`` attribute.
+ b. Set ``D.[[Writable]]`` to the value of ``X``\ 's ``[[Writable]]`` attribute.
5. Else ``X`` is an accessor property, so
- a. Set ``D.[[Get]]`` to the value of ``X``\ ’s ``[[Get]]`` attribute.
+ a. Set ``D.[[Get]]`` to the value of ``X``\ 's ``[[Get]]`` attribute.
- b. Set ``D.[[Set]]`` to the value of ``X``\ ’s ``[[Set]]`` attribute.
+ b. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` attribute.
-6. Set ``D.[[Enumerable]]`` to the value of ``X``\ ’s ``[[Enumerable]]`` attribute.
+6. Set ``D.[[Enumerable]]`` to the value of ``X``\ 's ``[[Enumerable]]`` attribute.
-7. Set ``D.[[Configurable]]`` to the value of ``X``\ ’s ``[[Configurable]]`` attribute.
+7. Set ``D.[[Configurable]]`` to the value of ``X``\ 's ``[[Configurable]]`` attribute.
8. Return ``D``.
@@ -90,7 +90,7 @@ name. (Whether this is useful in an implementation is another issue.)
The combined algorithm, assuming the the virtual properties are checked
after the normal property check is as follows:
-1. If ``O`` doesn’t have an own property with name ``P``:
+1. If ``O`` doesn't have an own property with name ``P``:
a. If ``O`` is not a ``String`` instance, return ``undefined``.
@@ -132,23 +132,23 @@ after the normal property check is as follows:
2. Let ``D`` be a newly created Property Descriptor with no fields.
-3. Let ``X`` be ``O``\ ’s own property named ``P``.
+3. Let ``X`` be ``O``\ 's own property named ``P``.
4. If ``X`` is a data property, then
- a. Set ``D.[[Value]]`` to the value of ``X``\ ’s ``[[Value]]`` attribute.
+ a. Set ``D.[[Value]]`` to the value of ``X``\ 's ``[[Value]]`` attribute.
- b. Set ``D.[[Writable]]`` to the value of ``X``\ ’s ``[[Writable]]`` attribute.
+ b. Set ``D.[[Writable]]`` to the value of ``X``\ 's ``[[Writable]]`` attribute.
5. Else ``X`` is an accessor property, so
- a. Set ``D.[[Get]]`` to the value of ``X``\ ’s ``[[Get]]`` attribute.
+ a. Set ``D.[[Get]]`` to the value of ``X``\ 's ``[[Get]]`` attribute.
- b. Set ``D.[[Set]]`` to the value of ``X``\ ’s ``[[Set]]`` attribute.
+ b. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` attribute.
-6. Set ``D.[[Enumerable]]`` to the value of ``X``\ ’s ``[[Enumerable]]`` attribute.
+6. Set ``D.[[Enumerable]]`` to the value of ``X``\ 's ``[[Enumerable]]`` attribute.
-7. Set ``D.[[Configurable]]`` to the value of ``X``\ ’s ``[[Configurable]]`` attribute.
+7. Set ``D.[[Configurable]]`` to the value of ``X``\ 's ``[[Configurable]]`` attribute.
8. Return ``D``.
@@ -170,7 +170,7 @@ a data property.
The exotic behavior can be appended to the above algorithm as follows:
-1. If ``O`` doesn’t have an own property with name ``P``:
+1. If ``O`` doesn't have an own property with name ``P``:
a. If ``O`` is not a ``String`` instance, return ``undefined``.
@@ -212,23 +212,23 @@ The exotic behavior can be appended to the above algorithm as follows:
2. Let ``D`` be a newly created Property Descriptor with no fields.
-3. Let ``X`` be ``O``\ ’s own property named ``P``.
+3. Let ``X`` be ``O``\ 's own property named ``P``.
4. If ``X`` is a data property, then
- a. Set ``D.[[Value]]`` to the value of ``X``\ ’s ``[[Value]]`` attribute.
+ a. Set ``D.[[Value]]`` to the value of ``X``\ 's ``[[Value]]`` attribute.
- b. Set ``D.[[Writable]]`` to the value of ``X``\ ’s ``[[Writable]]`` attribute.
+ b. Set ``D.[[Writable]]`` to the value of ``X``\ 's ``[[Writable]]`` attribute.
5. Else ``X`` is an accessor property, so
- a. Set ``D.[[Get]]`` to the value of ``X``\ ’s ``[[Get]]`` attribute.
+ a. Set ``D.[[Get]]`` to the value of ``X``\ 's ``[[Get]]`` attribute.
- b. Set ``D.[[Set]]`` to the value of ``X``\ ’s ``[[Set]]`` attribute.
+ b. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` attribute.
-6. Set ``D.[[Enumerable]]`` to the value of ``X``\ ’s ``[[Enumerable]]`` attribute.
+6. Set ``D.[[Enumerable]]`` to the value of ``X``\ 's ``[[Enumerable]]`` attribute.
-7. Set ``D.[[Configurable]]`` to the value of ``X``\ ’s ``[[Configurable]]`` attribute.
+7. Set ``D.[[Configurable]]`` to the value of ``X``\ 's ``[[Configurable]]`` attribute.
8. If ``O`` is an ``arguments`` object which contains a ``[[ParameterMap]]``
internal property:
@@ -275,8 +275,8 @@ Final version
Final version with some cleanup and simplification:
-1. Let ``X`` be ``O``\ ’s own property named ``P``.
- If ``O`` doesn’t have an own property with name ``P``:
+1. Let ``X`` be ``O``\ 's own property named ``P``.
+ If ``O`` doesn't have an own property with name ``P``:
a. If ``O`` is not a ``String`` instance, return ``undefined``.
@@ -318,21 +318,21 @@ Final version with some cleanup and simplification:
a. If ``X`` is a data property:
- 1. Set ``D.[[Value]]`` to the value of ``X``\ ’s ``[[Value]]`` attribute.
+ 1. Set ``D.[[Value]]`` to the value of ``X``\ 's ``[[Value]]`` attribute.
- 2. Set ``D.[[Writable]]`` to the value of ``X``\ ’s ``[[Writable]]`` attribute.
+ 2. Set ``D.[[Writable]]`` to the value of ``X``\ 's ``[[Writable]]`` attribute.
b. Else ``X`` is an accessor property:
- 1. Set ``D.[[Get]]`` to the value of ``X``\ ’s ``[[Get]]`` attribute.
+ 1. Set ``D.[[Get]]`` to the value of ``X``\ 's ``[[Get]]`` attribute.
- 2. Set ``D.[[Set]]`` to the value of ``X``\ ’s ``[[Set]]`` attribute.
+ 2. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` attribute.
c. For either type of property:
- 1. Set ``D.[[Enumerable]]`` to the value of ``X``\ ’s ``[[Enumerable]]`` attribute.
+ 1. Set ``D.[[Enumerable]]`` to the value of ``X``\ 's ``[[Enumerable]]`` attribute.
- 2. Set ``D.[[Configurable]]`` to the value of ``X``\ ’s ``[[Configurable]]`` attribute.
+ 2. Set ``D.[[Configurable]]`` to the value of ``X``\ 's ``[[Configurable]]`` attribute.
3. If ``O`` is an ``arguments`` object which contains a ``[[ParameterMap]]``
internal property:
@@ -620,16 +620,16 @@ Default algorithm
1. Convert the property named ``P`` of object ``O`` from a data property
to an accessor property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
c. Else,
1. Convert the property named ``P`` of object ``O`` from an accessor
property to a data property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
10. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
@@ -743,16 +743,16 @@ Let's first do a little bit of reformulation (see above):
1. Convert the property named ``P`` of object ``O`` from a data property
to an accessor property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
c. Else,
1. Convert the property named ``P`` of object ``O`` from an accessor
property to a data property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
d. Goto VALIDATED.
@@ -861,7 +861,7 @@ Let's look at the variant algorithm first (here we assume ``O`` is an
l. While ``newLen`` < ``oldLen`` repeat,
- 1. Set ``oldLen`` to ``oldLen – 1``.
+ 1. Set ``oldLen`` to ``oldLen - 1``.
2. Let ``canDelete`` be the result of calling the ``[[Delete]]``
internal method of ``O`` passing ``ToString(oldLen)`` and ``false``
@@ -1203,16 +1203,16 @@ we get something like:
1. Convert the property named ``P`` of object ``O`` from a data property
to an accessor property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
c. Else,
1. Convert the property named ``P`` of object ``O`` from an accessor
property to a data property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
d. Goto VALIDATED.
@@ -1398,16 +1398,16 @@ This is easy to incorporate and results in:
1. Convert the property named ``P`` of object ``O`` from a data property
to an accessor property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
c. Else,
1. Convert the property named ``P`` of object ``O`` from an accessor
property to a data property. Preserve the existing values of the
- converted property’s ``[[Configurable]]`` and ``[[Enumerable]]``
- attributes and set the rest of the property’s attributes to their
+ converted property's ``[[Configurable]]`` and ``[[Enumerable]]``
+ attributes and set the rest of the property's attributes to their
default values.
d. Goto VALIDATED.
@@ -1651,7 +1651,7 @@ Notes:
The ``[[HasInstance]]`` for bound functions is:
-1. Let ``target`` be the value of ``F``\ ’s ``[[TargetFunction]]`` internal
+1. Let ``target`` be the value of ``F``\ 's ``[[TargetFunction]]`` internal
property.
2. If ``target`` has no ``[[HasInstance]]`` internal method, a ``TypeError``
diff --git a/doc/hobject-alg-preliminaries.rst b/doc/hobject-alg-preliminaries.rst
index 24908adc..e6d6cd57 100644
--- a/doc/hobject-alg-preliminaries.rst
+++ b/doc/hobject-alg-preliminaries.rst
@@ -158,25 +158,25 @@ The inlined form for default ``[[GetOwnProperty]]`` is essentially:
a. Let ``D`` be a newly created Property Descriptor with no fields.
- b. Let ``X`` be ``curr``\ ’s own property named P.
+ b. Let ``X`` be ``curr``\ 's own property named P.
c. If ``X`` is a data property, then
- 1. Set ``D.[[Value]]`` to the value of ``X``\ ’s ``[[Value]]``
+ 1. Set ``D.[[Value]]`` to the value of ``X``\ 's ``[[Value]]``
attribute.
- 2. Set ``D.[[Writable]]`` to the value of ``X``\ ’s ``[[Writable]]``
+ 2. Set ``D.[[Writable]]`` to the value of ``X``\ 's ``[[Writable]]``
attribute.
d. Else ``X`` is an accessor property, so
- 1. Set ``D.[[Get]]`` to the value of ``X``\ ’s ``[[Get]]`` attribute.
+ 1. Set ``D.[[Get]]`` to the value of ``X``\ 's ``[[Get]]`` attribute.
- 2. Set ``D.[[Set]]`` to the value of ``X``\ ’s ``[[Set]]`` attribute.
+ 2. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` attribute.
- e. Set ``D.[[Enumerable]]`` to the value of ``X``\ ’s ``[[Enumerable]]`` attribute.
+ e. Set ``D.[[Enumerable]]`` to the value of ``X``\ 's ``[[Enumerable]]`` attribute.
- f. Set ``D.[[Configurable]]`` to the value of ``X``\ ’s ``[[Configurable]]`` attribute.
+ f. Set ``D.[[Configurable]]`` to the value of ``X``\ 's ``[[Configurable]]`` attribute.
g. Return ``D``.
@@ -197,8 +197,8 @@ The following inlines ``[[GetOwnProperty]]`` with all exotic behaviors:
1. ``curr`` = ``O``
2. **NEXT:**
- Let ``X`` be ``curr``\ ’s own property named ``P``.
- If ``curr`` doesn’t have an own property with name ``P``:
+ Let ``X`` be ``curr``\ 's own property named ``P``.
+ If ``curr`` doesn't have an own property with name ``P``:
a. If ``curr`` is not a ``String`` instance, goto NOTFOUND.
@@ -240,21 +240,21 @@ The following inlines ``[[GetOwnProperty]]`` with all exotic behaviors:
a. If ``X`` is a data property:
- 1. Set ``D.[[Value]]`` to the value of ``X``\ ’s ``[[Value]]`` attribute.
+ 1. Set ``D.[[Value]]`` to the value of ``X``\ 's ``[[Value]]`` attribute.
- 2. Set ``D.[[Writable]]`` to the value of ``X``\ ’s ``[[Writable]]`` attribute.
+ 2. Set ``D.[[Writable]]`` to the value of ``X``\ 's ``[[Writable]]`` attribute.
b. Else ``X`` is an accessor property:
- 1. Set ``D.[[Get]]`` to the value of ``X``\ ’s ``[[Get]]`` attribute.
+ 1. Set ``D.[[Get]]`` to the value of ``X``\ 's ``[[Get]]`` attribute.
- 2. Set ``D.[[Set]]`` to the value of ``X``\ ’s ``[[Set]]`` attribute.
+ 2. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` attribute.
c. For either type of property:
- 1. Set ``D.[[Enumerable]]`` to the value of ``X``\ ’s ``[[Enumerable]]`` attribute.
+ 1. Set ``D.[[Enumerable]]`` to the value of ``X``\ 's ``[[Enumerable]]`` attribute.
- 2. Set ``D.[[Configurable]]`` to the value of ``X``\ ’s ``[[Configurable]]`` attribute.
+ 2. Set ``D.[[Configurable]]`` to the value of ``X``\ 's ``[[Configurable]]`` attribute.
4. If ``curr`` is an ``arguments`` object which contains a ``[[ParameterMap]]``
internal property:
@@ -318,8 +318,8 @@ exotic behaviors:
2. ``curr`` = ``O``
3. **NEXT:**
- Let ``X`` be ``curr``\ ’s own property named ``P``.
- If ``curr`` doesn’t have an own property with name ``P``:
+ Let ``X`` be ``curr``\ 's own property named ``P``.
+ If ``curr`` doesn't have an own property with name ``P``:
a. If ``curr`` is not a ``String`` instance, goto NOTFOUND.
@@ -349,7 +349,7 @@ exotic behaviors:
4. If ``X`` is a data property:
- a. Set ``res`` to the value of ``X``\ ’s ``[[Value]]`` attribute.
+ a. Set ``res`` to the value of ``X``\ 's ``[[Value]]`` attribute.
b. Goto FOUND1
@@ -1331,7 +1331,7 @@ and is as follows:
``Obj`` with argument ``"value"`` is ``true``, then:
a. Let ``value`` be the result of calling the ``[[Get]]`` internal method
- of ``Obj`` with argument ``“value”``.
+ of ``Obj`` with argument ``"value"``.
b. Set the ``[[Value]]`` field of ``desc`` to ``value``.
diff --git a/doc/hobject-design.rst b/doc/hobject-design.rst
index fe73882b..08c0da20 100644
--- a/doc/hobject-design.rst
+++ b/doc/hobject-design.rst
@@ -385,7 +385,7 @@ advantage of its non-mutability). The requirement is stated in a
footnote in E5 Section 8.6.2:
NOTE This specification defines no ECMAScript language operators or
- built-in functions that permit a program to modify an object’s
+ built-in functions that permit a program to modify an object's
``[[Class]]`` or ``[[Prototype]]`` internal properties or to change
the value of [[Extensible]] from false to true.
diff --git a/doc/hobject-enumeration.rst b/doc/hobject-enumeration.rst
index d1566aad..dfb958d9 100644
--- a/doc/hobject-enumeration.rst
+++ b/doc/hobject-enumeration.rst
@@ -31,6 +31,9 @@ Apparently the behavior relied on is roughly:
* The properties of the object itself are enumerated first, followed by
its prototype's properties, and so on
+ES6 has specific enumeration requirements which aren't yet implemented
+by Duktape (as of version 2.0).
+
Specification (E5)
==================
@@ -269,4 +272,3 @@ Test 4 shows that 'length' is not exotic for an object which has an
array as a prototype. Exotic semantics of 'length' do not apply to
the object because the property write goes to the object, which is not
an array. This also explains the result of test 3.
-
diff --git a/doc/identifier-handling.rst b/doc/identifier-handling.rst
index 656990e2..d0b190cb 100644
--- a/doc/identifier-handling.rst
+++ b/doc/identifier-handling.rst
@@ -787,7 +787,7 @@ and a ``strict`` flag:
a. Return a value of type Reference whose base value is ``undefined``, whose
referenced name is ``name``, and whose strict mode flag is ``strict``.
-2. Let ``envRec`` be ``lex``\ ‘s environment record.
+2. Let ``envRec`` be ``lex``\ 's environment record.
3. Let ``exists`` be the result of calling the ``HasBinding(N)`` concrete
method of ``envRec`` passing ``name`` as the argument ``N``.
@@ -799,7 +799,7 @@ and a ``strict`` flag:
5. Else
- a. Let ``outer`` be the value of ``lex``\ ’s outer environment reference.
+ a. Let ``outer`` be the value of ``lex``\ 's outer environment reference.
b. Return the result of calling ``GetIdentifierReference`` passing
``outer``, ``name``, and ``strict`` as arguments.
@@ -822,7 +822,7 @@ Eliminating recursion
a. Return a value of type Reference whose base value is ``undefined``, whose
referenced name is ``name``, and whose strict mode flag is ``strict``.
-2. Let ``envRec`` be ``lex``\ ‘s environment record.
+2. Let ``envRec`` be ``lex``\ 's environment record.
3. Let ``exists`` be the result of calling the ``HasBinding(N)`` concrete
method of ``envRec`` passing ``name`` as the argument ``N``.
diff --git a/doc/low-memory.rst b/doc/low-memory.rst
index a8cac923..e56f53d8 100644
--- a/doc/low-memory.rst
+++ b/doc/low-memory.rst
@@ -124,7 +124,7 @@ but see for example:
Example using GCC: compile the Duktape command line utility without removing
unused API symbols::
- $ gcc -o duk -Os -pedantic -std=c99 -Wall -fstrict-aliasing \
+ $ gcc -o duk -Os -pedantic -std=c99 -Wall -fstrict-aliasing \
-fomit-frame-pointer -I./src -DDUK_OPT_SELF_TESTS src/duktape.c \
examples/cmdline/duk_cmdline.c -lm
$ size duk
@@ -715,7 +715,7 @@ Use the following command to run the optimization::
$ rm -rf /tmp/out; mkdir /tmp/out
$ python examples/alloc-logging/pool_simulator.py \
- --out-dir /tmp/out \
+ --out-dir /tmp/out \
--alloc-log /tmp/duk-alloc-log.txt \
--pool-config examples/alloc-logging/pool_config_1.json \
--out-pool-config /tmp/tight_borrow.json \
diff --git a/doc/release-notes-v2-0.rst b/doc/release-notes-v2-0.rst
index cbf6d3f5..54f0bbdd 100644
--- a/doc/release-notes-v2-0.rst
+++ b/doc/release-notes-v2-0.rst
@@ -428,6 +428,19 @@ To upgrade:
- Convert debug level options from ``DUK_USE_{D,DD,DDD}PRINT`` to the
equivalent ``DUK_USE_DEBUG_LEVEL`` (0, 1, or 2).
+Other debugger changes
+----------------------
+
+* Artificial properties renamed for consistency with internal renaming:
+
+ - ``compiledfunction`` -> ``compfunc``
+
+ - ``nativefunction`` -> ``natfunc``
+
+ - ``bufferobject`` -> ``bufobj``
+
+ - ``bound`` -> ``boundfunc``
+
Fatal error and panic handling reworked
---------------------------------------
diff --git a/doc/unicode-support.rst b/doc/unicode-support.rst
index 92375f6e..96b50212 100644
--- a/doc/unicode-support.rst
+++ b/doc/unicode-support.rst
@@ -298,4 +298,3 @@ Java behavior:
+--------+----------+----------+----------+----------+----------+----------+----------+----------+
| U+0131 | U+0049 | U+0049 | U+0049 | U+0049 | U+0131 | U+0131 | U+0131 | U+0131 |
+--------+----------+----------+----------+----------+----------+----------+----------+----------+
-
diff --git a/doc/uri.rst b/doc/uri.rst
index 5edc462f..e1379094 100644
--- a/doc/uri.rst
+++ b/doc/uri.rst
@@ -10,7 +10,7 @@ URI syntax
E5.1 Annex F::
- 15.1.3: Added notes clarifying that ECMAScript‘s URI syntax is based upon
+ 15.1.3: Added notes clarifying that ECMAScript's URI syntax is based upon
RFC 2396 and not the newer RFC 3986. In the algorithm for Decode, a step
was removed that immediately preceded the current step 4.d.vii.10.a
because it tested for a condition that cannot occur.
diff --git a/src/builtins.yaml b/src/builtins.yaml
index ceaf8081..37bd5194 100644
--- a/src/builtins.yaml
+++ b/src/builtins.yaml
@@ -654,9 +654,10 @@ objects:
# in E5 Section 15.4.5.2 (and this matches the behavior of e.g. V8).
properties:
- - key: "length"
- value: 0
- attributes: "w"
+ # With duk_harray added to the internal representation, .length is now virtual.
+ #- key: "length"
+ # value: 0
+ # attributes: "w"
- key: "constructor"
value:
type: object
diff --git a/src/duk_api_bytecode.c b/src/duk_api_bytecode.c
index 9d3f86ab..b7aa6583 100644
--- a/src/duk_api_bytecode.c
+++ b/src/duk_api_bytecode.c
@@ -442,7 +442,7 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t
/* assert just a few critical flags */
DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT);
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&h_fun->obj));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&h_fun->obj));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&h_fun->obj));
DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&h_fun->obj));
DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&h_fun->obj));
@@ -650,7 +650,7 @@ DUK_EXTERNAL void duk_dump_function(duk_context *ctx) {
*/
func = duk_require_hcompfunc(ctx, -1);
DUK_ASSERT(func != NULL);
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&func->obj));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&func->obj));
/* Estimating the result size beforehand would be costly, so
* start with a reasonable size and extend as needed.
diff --git a/src/duk_api_call.c b/src/duk_api_call.c
index c677e07a..f3525465 100644
--- a/src/duk_api_call.c
+++ b/src/duk_api_call.c
@@ -301,13 +301,13 @@ DUK_EXTERNAL void duk_new(duk_context *ctx, duk_idx_t nargs) {
*/
goto not_constructable;
}
- if (!DUK_HOBJECT_HAS_BOUND(cons)) {
+ if (!DUK_HOBJECT_HAS_BOUNDFUNC(cons)) {
break;
}
duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET); /* -> [... cons target] */
duk_remove(ctx, -2); /* -> [... target] */
}
- DUK_ASSERT(cons != NULL && !DUK_HOBJECT_HAS_BOUND(cons));
+ DUK_ASSERT(cons != NULL && !DUK_HOBJECT_HAS_BOUNDFUNC(cons));
/* [... constructor arg1 ... argN final_cons] */
diff --git a/src/duk_api_internal.h b/src/duk_api_internal.h
index 6b1ff731..d0d4437a 100644
--- a/src/duk_api_internal.h
+++ b/src/duk_api_internal.h
@@ -95,8 +95,7 @@ DUK_INTERNAL_DECL duk_hstring *duk_to_hstring(duk_context *ctx, duk_idx_t idx);
#if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */
DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_context *ctx, duk_idx_t idx);
#endif
-DUK_INTERNAL_DECL void duk_to_object_class_string_top(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_push_hobject_class_string(duk_context *ctx, duk_hobject *h);
+DUK_INTERNAL_DECL void duk_push_class_string_tval(duk_context *ctx, duk_tval *tv);
DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped); /* out_clamped=NULL, RangeError if outside range */
DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped(duk_context *ctx, duk_idx_t idx, duk_int_t minval, duk_int_t maxval);
@@ -135,6 +134,9 @@ DUK_INTERNAL_DECL duk_idx_t duk_push_compiledfunction(duk_context *ctx);
DUK_INTERNAL_DECL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs);
DUK_INTERNAL_DECL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs);
+DUK_INTERNAL_DECL duk_harray *duk_push_harray(duk_context *ctx);
+DUK_INTERNAL_DECL duk_harray *duk_push_harray_with_size(duk_context *ctx, duk_uint32_t size);
+
DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz);
DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv);
DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv);
@@ -170,6 +172,11 @@ DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_context *ctx, duk_idx_t
/* Set object 'length'. */
DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t length);
+DUK_EXTERNAL_DECL void duk_pack(duk_context *ctx, duk_idx_t count);
+#if 0
+DUK_EXTERNAL_DECL void duk_unpack(duk_context *ctx);
+#endif
+
/* Raw internal valstack access macros: access is unsafe so call site
* must have a guarantee that the index is valid. When that is the case,
* using these macro results in faster and smaller code than duk_get_tval().
diff --git a/src/duk_api_object.c b/src/duk_api_object.c
index 9a69eed6..f124f99d 100644
--- a/src/duk_api_object.c
+++ b/src/duk_api_object.c
@@ -248,7 +248,7 @@ DUK_INTERNAL duk_bool_t duk_has_prop_stridx(duk_context *ctx, duk_idx_t obj_idx,
return duk_has_prop(ctx, obj_idx);
}
-/* Define own property without inheritance looks and such. This differs from
+/* Define own property without inheritance lookups and such. This differs from
* [[DefineOwnProperty]] because special behaviors (like Array 'length') are
* not invoked by this method. The caller must be careful to invoke any such
* behaviors if necessary.
diff --git a/src/duk_api_stack.c b/src/duk_api_stack.c
index 8f11dec4..d56a1a87 100644
--- a/src/duk_api_stack.c
+++ b/src/duk_api_stack.c
@@ -2095,47 +2095,70 @@ DUK_INTERNAL duk_hstring *duk_safe_to_hstring(duk_context *ctx, duk_idx_t idx) {
}
#endif
-/* Coerce top into Object.prototype.toString() output. */
-DUK_INTERNAL void duk_to_object_class_string_top(duk_context *ctx) {
+/* Push Object.prototype.toString() output for 'tv'. */
+DUK_INTERNAL void duk_push_class_string_tval(duk_context *ctx, duk_tval *tv) {
duk_hthread *thr;
- duk_uint_t typemask;
+ duk_small_uint_t stridx;
duk_hstring *h_strclass;
DUK_ASSERT_CTX_VALID(ctx);
thr = (duk_hthread *) ctx;
DUK_UNREF(thr);
- typemask = duk_get_type_mask(ctx, -1);
- if (typemask & DUK_TYPE_MASK_UNDEFINED) {
- h_strclass = DUK_HTHREAD_STRING_UC_UNDEFINED(thr);
- } else if (typemask & DUK_TYPE_MASK_NULL) {
- h_strclass = DUK_HTHREAD_STRING_UC_NULL(thr);
- } else {
- duk_hobject *h_obj;
-
- duk_to_object(ctx, -1);
- h_obj = duk_get_hobject(ctx, -1);
- DUK_ASSERT(h_obj != NULL);
+ switch (DUK_TVAL_GET_TAG(tv)) {
+ case DUK_TAG_UNUSED: /* Treat like 'undefined', shouldn't happen. */
+ case DUK_TAG_UNDEFINED: {
+ stridx = DUK_STRIDX_UC_UNDEFINED;
+ break;
+ }
+ case DUK_TAG_NULL: {
+ stridx = DUK_STRIDX_UC_NULL;
+ break;
+ }
+ case DUK_TAG_BOOLEAN: {
+ stridx = DUK_STRIDX_UC_BOOLEAN;
+ break;
+ }
+ case DUK_TAG_POINTER: {
+ stridx = DUK_STRIDX_UC_POINTER;
+ break;
+ }
+ case DUK_TAG_LIGHTFUNC: {
+ stridx = DUK_STRIDX_UC_FUNCTION;
+ break;
+ }
+ case DUK_TAG_STRING: {
+ stridx = DUK_STRIDX_UC_STRING;
+ break;
+ }
+ case DUK_TAG_OBJECT: {
+ duk_hobject *h;
+ duk_small_uint_t classnum;
- h_strclass = DUK_HOBJECT_GET_CLASS_STRING(thr->heap, h_obj);
+ h = DUK_TVAL_GET_OBJECT(tv);
+ DUK_ASSERT(h != NULL);
+ classnum = DUK_HOBJECT_GET_CLASS_NUMBER(h);
+ stridx = DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum);
+ break;
+ }
+ case DUK_TAG_BUFFER: {
+ /* XXX: needs a plain buffer fix */
+ stridx = DUK_STRIDX_UC_BUFFER;
+ break;
}
+#if defined(DUK_USE_FASTINT)
+ case DUK_TAG_FASTINT:
+ /* Fall through to generic number case. */
+#endif
+ default: {
+ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); /* number (maybe fastint) */
+ stridx = DUK_STRIDX_UC_NUMBER;
+ break;
+ }
+ }
+ h_strclass = DUK_HTHREAD_GET_STRING(thr, stridx);
DUK_ASSERT(h_strclass != NULL);
- duk_pop(ctx);
- duk_push_sprintf(ctx, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass));
-}
-
-DUK_INTERNAL void duk_push_hobject_class_string(duk_context *ctx, duk_hobject *h) {
- duk_hthread *thr;
- duk_hstring *h_strclass;
-
- DUK_ASSERT_CTX_VALID(ctx);
- DUK_ASSERT(h != NULL);
- thr = (duk_hthread *) ctx;
- DUK_UNREF(thr);
-
- h_strclass = DUK_HOBJECT_GET_CLASS_STRING(thr->heap, h);
- DUK_ASSERT(h_strclass != NULL);
duk_push_sprintf(ctx, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass));
}
@@ -2839,7 +2862,7 @@ DUK_EXTERNAL duk_bool_t duk_is_function(duk_context *ctx, duk_idx_t idx) {
idx,
DUK_HOBJECT_FLAG_COMPFUNC |
DUK_HOBJECT_FLAG_NATFUNC |
- DUK_HOBJECT_FLAG_BOUND);
+ DUK_HOBJECT_FLAG_BOUNDFUNC);
}
DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_context *ctx, duk_idx_t idx) {
@@ -2860,7 +2883,7 @@ DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_context *ctx, duk_idx_t idx) {
DUK_ASSERT_CTX_VALID(ctx);
return duk__obj_flag_any_default_false(ctx,
idx,
- DUK_HOBJECT_FLAG_BOUND);
+ DUK_HOBJECT_FLAG_BOUNDFUNC);
}
DUK_EXTERNAL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t idx) {
@@ -3513,42 +3536,67 @@ DUK_EXTERNAL duk_idx_t duk_push_object(duk_context *ctx) {
DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) {
duk_hthread *thr = (duk_hthread *) ctx;
- duk_hobject *obj;
+ duk_uint_t flags;
+ duk_harray *obj;
duk_idx_t ret;
+ duk_tval *tv_slot;
DUK_ASSERT_CTX_VALID(ctx);
- ret = duk_push_object_helper(ctx,
- DUK_HOBJECT_FLAG_EXTENSIBLE |
- DUK_HOBJECT_FLAG_ARRAY_PART |
- DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY),
- DUK_BIDX_ARRAY_PROTOTYPE);
-
- obj = duk_require_hobject(ctx, ret);
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_ARRAY_PART |
+ DUK_HOBJECT_FLAG_EXOTIC_ARRAY |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY);
- /*
- * An array must have a 'length' property (E5 Section 15.4.5.2).
- * The special array behavior flag must only be enabled once the
- * length property has been added.
- *
- * The internal property must be a number (and preferably a
- * fastint if fastint support is enabled).
- */
+ obj = duk_harray_alloc(thr->heap, flags);
+ if (!obj) {
+ DUK_ERROR_ALLOC_FAILED(thr);
+ }
- duk_push_int(ctx, 0);
-#if defined(DUK_USE_FASTINT)
- DUK_ASSERT(DUK_TVAL_IS_FASTINT(duk_require_tval(ctx, -1)));
-#endif
+ /* XXX: since prototype is NULL, could save a check */
+ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_ARRAY_PROTOTYPE]);
- duk_hobject_define_property_internal(thr,
- obj,
- DUK_HTHREAD_STRING_LENGTH(thr),
- DUK_PROPDESC_FLAGS_W);
- DUK_HOBJECT_SET_EXOTIC_ARRAY(obj);
+ tv_slot = thr->valstack_top;
+ DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
+ DUK_HOBJECT_INCREF(thr, obj); /* XXX: could preallocate with refcount = 1 */
+ ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
+ thr->valstack_top++;
+ DUK_ASSERT(obj->length == 0); /* Array .length starts at zero. */
return ret;
}
+DUK_INTERNAL duk_harray *duk_push_harray(duk_context *ctx) {
+ /* XXX: API call could do this directly, cast to void in API macro. */
+ duk_hthread *thr;
+ duk_harray *a;
+
+ thr = (duk_hthread *) ctx;
+ (void) duk_push_array(ctx);
+ DUK_ASSERT(DUK_TVAL_IS_OBJECT(thr->valstack_top - 1));
+ a = (duk_harray *) DUK_TVAL_GET_OBJECT(thr->valstack_top - 1);
+ DUK_ASSERT(a != NULL);
+ return a;
+}
+
+/* Push a duk_harray with preallocated size (.length also set to match size).
+ * Caller may then populate array part of the duk_harray directly.
+ */
+DUK_INTERNAL duk_harray *duk_push_harray_with_size(duk_context *ctx, duk_uint32_t size) {
+ duk_harray *a;
+
+ a = duk_push_harray(ctx);
+
+ duk_hobject_realloc_props((duk_hthread *) ctx,
+ (duk_hobject *) a,
+ 0,
+ size,
+ 0,
+ 0);
+ a->length = size;
+ return a;
+}
+
DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_hthread *obj;
@@ -3601,6 +3649,7 @@ DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) {
}
/* default prototype (Note: 'obj' must be reachable) */
+ /* XXX: since prototype is NULL, could save a check */
DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]);
/* Initial stack size satisfies the stack spare constraints so there
@@ -4229,6 +4278,72 @@ DUK_EXTERNAL void duk_pop_3(duk_context *ctx) {
duk_pop_n(ctx, 3);
}
+/*
+ * Pack and unpack (pack value stack entries into an array and vice versa)
+ */
+
+/* XXX: pack index range? array index offset? */
+DUK_EXTERNAL void duk_pack(duk_context *ctx, duk_idx_t count) {
+ duk_hthread *thr;
+ duk_harray *a;
+ duk_tval *tv_src;
+ duk_tval *tv_dst;
+ duk_tval *tv_curr;
+ duk_tval *tv_limit;
+ duk_idx_t top;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+
+ top = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
+ if (count > top) {
+ DUK_ERROR_RANGE_INVALID_COUNT(thr);
+ }
+
+ a = duk_push_harray_with_size(ctx, (duk_uint32_t) count); /* XXX: uninitialized would be OK */
+ DUK_ASSERT(a != NULL);
+ DUK_ASSERT(DUK_HOBJECT_GET_ASIZE((duk_hobject *) a) == (duk_uint32_t) count);
+ DUK_ASSERT(count == 0 || DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a) != NULL);
+ a->length = count;
+
+ /* Copy value stack values directly to the array part without
+ * any refcount updates: net refcount changes are zero.
+ */
+
+ tv_src = thr->valstack_top - count - 1;
+ tv_dst = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a);
+ DUK_MEMCPY((void *) tv_dst, (const void *) tv_src, count * sizeof(duk_tval));
+
+ /* Overwrite result array to final value stack location and wipe
+ * the rest; no refcount operations needed.
+ */
+
+ tv_dst = tv_src; /* when count == 0, same as tv_src (OK) */
+ tv_src = thr->valstack_top - 1;
+ DUK_TVAL_SET_TVAL(tv_dst, tv_src);
+
+ tv_curr = tv_dst + 1;
+ tv_limit = thr->valstack_top;
+ while (tv_curr != tv_limit) {
+ /* Wipe policy: keep as 'undefined'. */
+ DUK_TVAL_SET_UNDEFINED(tv_curr);
+ tv_curr++;
+ }
+ thr->valstack_top = tv_dst + 1;
+}
+
+#if 0
+/* XXX: unpack to position? */
+DUK_EXTERNAL void duk_unpack(duk_context *ctx) {
+ /* - dense with length <= a_part
+ * - dense with length > a_part
+ * - sparse
+ * - array-like but not actually an array?
+ * - how to deal with 'unused' values (gaps); inherit or ignore?
+ */
+}
+#endif
+
/*
* Error throwing
*/
@@ -4569,7 +4684,7 @@ DUK_LOCAL const char *duk__push_string_tval_readable(duk_context *ctx, duk_tval
return duk_push_string_tval_readable(ctx, tv);
}
}
- duk_push_hobject_class_string(ctx, h);
+ duk_push_class_string_tval(ctx, tv);
break;
}
case DUK_TAG_BUFFER: {
diff --git a/src/duk_bi_array.c b/src/duk_bi_array.c
index 64c87b3f..18913d0c 100644
--- a/src/duk_bi_array.c
+++ b/src/duk_bi_array.c
@@ -55,6 +55,7 @@ DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_context *ctx) {
duk_uint32_t len;
(void) duk_push_this_coercible_to_object(ctx);
+ DUK_ASSERT_HOBJECT_VALID(duk_get_hobject(ctx, -1));
duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LENGTH);
len = duk_to_uint32(ctx, -1);
@@ -80,12 +81,12 @@ DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_context *ctx) {
DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_context *ctx) {
duk_idx_t nargs;
+ duk_harray *a;
duk_double_t d;
duk_uint32_t len;
- duk_idx_t i;
+ duk_uint32_t len_prealloc;
nargs = duk_get_top(ctx);
- duk_push_array(ctx);
if (nargs == 1 && duk_is_number(ctx, 0)) {
/* XXX: expensive check (also shared elsewhere - so add a shared internal API call?) */
@@ -95,25 +96,17 @@ DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_context *ctx) {
return DUK_RET_RANGE_ERROR;
}
- /* XXX: if 'len' is low, may want to ensure array part is kept:
- * the caller is likely to want a dense array.
+ /* For small lengths create a dense preallocated array.
+ * For large arrays preallocate an initial part.
*/
- duk_push_u32(ctx, len);
- duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); /* [ ToUint32(len) array ToUint32(len) ] -> [ ToUint32(len) array ] */
+ len_prealloc = len < 64 ? len : 64;
+ a = duk_push_harray_with_size(ctx, len_prealloc);
+ DUK_ASSERT(a != NULL);
+ a->length = len;
return 1;
}
- /* XXX: optimize by creating array into correct size directly, and
- * operating on the array part directly; values can be memcpy()'d from
- * value stack directly as long as refcounts are increased.
- */
- for (i = 0; i < nargs; i++) {
- duk_dup(ctx, i);
- duk_xdef_prop_index_wec(ctx, -2, (duk_uarridx_t) i);
- }
-
- duk_push_u32(ctx, (duk_uint32_t) nargs);
- duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
+ duk_pack(ctx, nargs);
return 1;
}
@@ -303,6 +296,7 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) {
count = 0;
idx = 0;
for (;;) {
+ DUK_DDD(DUK_DDDPRINT("join idx=%ld", (long) idx));
if (count >= DUK__ARRAY_MID_JOIN_LIMIT || /* intermediate join to avoid valstack overflow */
idx >= len) { /* end of loop (careful with len==0) */
/* [ sep ToObject(this) len sep str0 ... str(count-1) ] */
diff --git a/src/duk_bi_duktape.c b/src/duk_bi_duktape.c
index b2a7a3af..4adc956e 100644
--- a/src/duk_bi_duktape.c
+++ b/src/duk_bi_duktape.c
@@ -63,12 +63,16 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) {
case DUK_HTYPE_OBJECT: {
duk_hobject *h_obj = (duk_hobject *) h;
duk_small_uint_t hdr_size;
- if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
+ if (DUK_HOBJECT_IS_ARRAY(h_obj)) {
+ hdr_size = (duk_small_uint_t) sizeof(duk_harray);
+ } else if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
hdr_size = (duk_small_uint_t) sizeof(duk_hcompfunc);
} else if (DUK_HOBJECT_IS_NATFUNC(h_obj)) {
hdr_size = (duk_small_uint_t) sizeof(duk_hnatfunc);
} else if (DUK_HOBJECT_IS_THREAD(h_obj)) {
hdr_size = (duk_small_uint_t) sizeof(duk_hthread);
+ } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
+ hdr_size = (duk_small_uint_t) sizeof(duk_hbufobj);
} else {
hdr_size = (duk_small_uint_t) sizeof(duk_hobject);
}
diff --git a/src/duk_bi_function.c b/src/duk_bi_function.c
index bec1b6cd..c985d476 100644
--- a/src/duk_bi_function.c
+++ b/src/duk_bi_function.c
@@ -133,11 +133,11 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) {
/* Indicate function type in the function body using a dummy
* directive.
*/
- if (DUK_HOBJECT_HAS_COMPFUNC(obj)) {
+ if (DUK_HOBJECT_IS_COMPFUNC(obj)) {
duk_push_sprintf(ctx, "function %s() {\"ecmascript\"}", (const char *) func_name);
- } else if (DUK_HOBJECT_HAS_NATFUNC(obj)) {
+ } else if (DUK_HOBJECT_IS_NATFUNC(obj)) {
duk_push_sprintf(ctx, "function %s() {\"native\"}", (const char *) func_name);
- } else if (DUK_HOBJECT_HAS_BOUND(obj)) {
+ } else if (DUK_HOBJECT_IS_BOUNDFUNC(obj)) {
duk_push_sprintf(ctx, "function %s() {\"bound\"}", (const char *) func_name);
} else {
goto type_error;
@@ -274,7 +274,7 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) {
/* create bound function object */
duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
- DUK_HOBJECT_FLAG_BOUND |
+ DUK_HOBJECT_FLAG_BOUNDFUNC |
DUK_HOBJECT_FLAG_CONSTRUCTABLE |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION),
DUK_BIDX_FUNCTION_PROTOTYPE);
diff --git a/src/duk_bi_json.c b/src/duk_bi_json.c
index d0e07378..8fcd6998 100644
--- a/src/duk_bi_json.c
+++ b/src/duk_bi_json.c
@@ -2455,55 +2455,54 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du
arr_len = (duk_uint_fast32_t) duk_hobject_get_length(js_ctx->thr, obj);
asize = (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj);
- if (arr_len > asize) {
- /* Array length is larger than 'asize'. This shouldn't
- * happen in practice. Bail out just in case.
- */
- DUK_DD(DUK_DDPRINT("arr_len > asize, abort fast path"));
- goto abort_fastpath;
- }
/* Array part may be larger than 'length'; if so, iterate
- * only up to array 'length'.
+ * only up to array 'length'. Array part may also be smaller
+ * than 'length' in some cases.
*/
for (i = 0; i < arr_len; i++) {
- DUK_ASSERT(i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj));
-
- tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(js_ctx->thr->heap, obj, i);
+ duk_tval *tv_arrval;
+ duk_hstring *h_tmp;
+ duk_bool_t has_inherited;
if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth);
}
- if (DUK_UNLIKELY(DUK_TVAL_IS_UNUSED(tv_val))) {
- /* Gap in array; check for inherited property,
- * bail out if one exists. This should be enough
- * to support gappy arrays for all practical code.
- */
- duk_hstring *h_tmp;
- duk_bool_t has_inherited;
-
- /* XXX: refactor into an internal helper, pretty awkward */
- duk_push_uint((duk_context *) js_ctx->thr, (duk_uint_t) i);
- h_tmp = duk_to_hstring((duk_context *) js_ctx->thr, -1);
- DUK_ASSERT(h_tmp != NULL);
- has_inherited = duk_hobject_hasprop_raw(js_ctx->thr, obj, h_tmp);
- duk_pop((duk_context *) js_ctx->thr);
-
- if (has_inherited) {
- DUK_D(DUK_DPRINT("gap in array, conflicting inherited property, abort fast path"));
- goto abort_fastpath;
+ if (DUK_LIKELY(i < asize)) {
+ tv_arrval = DUK_HOBJECT_A_GET_VALUE_PTR(js_ctx->thr->heap, obj, i);
+ if (DUK_LIKELY(!DUK_TVAL_IS_UNUSED(tv_arrval))) {
+ /* Expected case: element is present. */
+ if (duk__json_stringify_fast_value(js_ctx, tv_arrval) == 0) {
+ DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
+ }
+ goto elem_done;
}
+ }
- /* Ordinary gap, undefined encodes to 'null' in
- * standard JSON (and no JX/JC support here now).
- */
- DUK_D(DUK_DPRINT("gap in array, no conflicting inherited property, remain on fast path"));
- DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
- } else {
- if (duk__json_stringify_fast_value(js_ctx, tv_val) == 0) {
- DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
- }
+ /* Gap in array; check for inherited property,
+ * bail out if one exists. This should be enough
+ * to support gappy arrays for all practical code.
+ */
+
+ /* XXX: refactor into an internal helper, pretty awkward */
+ duk_push_uint((duk_context *) js_ctx->thr, (duk_uint_t) i);
+ h_tmp = duk_to_hstring((duk_context *) js_ctx->thr, -1);
+ DUK_ASSERT(h_tmp != NULL);
+ has_inherited = duk_hobject_hasprop_raw(js_ctx->thr, obj, h_tmp);
+ duk_pop((duk_context *) js_ctx->thr);
+ if (has_inherited) {
+ DUK_D(DUK_DPRINT("gap in array, conflicting inherited property, abort fast path"));
+ goto abort_fastpath;
}
+
+ /* Ordinary gap, undefined encodes to 'null' in
+ * standard JSON (and no JX/JC support here now).
+ */
+ DUK_D(DUK_DPRINT("gap in array, no conflicting inherited property, remain on fast path"));
+ DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
+ /* fall through */
+
+ elem_done:
DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
emitted = 1;
}
diff --git a/src/duk_bi_object.c b/src/duk_bi_object.c
index 28509ab3..b17b73ca 100644
--- a/src/duk_bi_object.c
+++ b/src/duk_bi_object.c
@@ -523,8 +523,9 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx) {
}
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_context *ctx) {
- duk_push_this(ctx);
- duk_to_object_class_string_top(ctx);
+ duk_tval *tv;
+ tv = DUK_HTHREAD_THIS_PTR((duk_hthread *) ctx);
+ duk_push_class_string_tval(ctx, tv);
return 1;
}
diff --git a/src/duk_debug_vsnprintf.c b/src/duk_debug_vsnprintf.c
index 6f01c7ab..ed67af88 100644
--- a/src/duk_debug_vsnprintf.c
+++ b/src/duk_debug_vsnprintf.c
@@ -437,8 +437,8 @@ DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) {
if (DUK_HOBJECT_HAS_CONSTRUCTABLE(h)) {
DUK__COMMA(); duk_fb_sprintf(fb, "__constructable:true");
}
- if (DUK_HOBJECT_HAS_BOUND(h)) {
- DUK__COMMA(); duk_fb_sprintf(fb, "__bound:true");
+ if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) {
+ DUK__COMMA(); duk_fb_sprintf(fb, "__boundfunc:true");
}
if (DUK_HOBJECT_HAS_COMPFUNC(h)) {
DUK__COMMA(); duk_fb_sprintf(fb, "__compfunc:true");
@@ -489,7 +489,12 @@ DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) {
DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_proxyobj:true");
}
}
- if (st->internal && DUK_HOBJECT_IS_COMPFUNC(h)) {
+
+ if (st->internal && DUK_HOBJECT_IS_ARRAY(h)) {
+ duk_harray *a = (duk_harray *) h;
+ DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) a->length);
+ DUK__COMMA(); duk_fb_sprintf(fb, "__length_nonwritable:%ld", (long) a->length_nonwritable);
+ } else if (st->internal && DUK_HOBJECT_IS_COMPFUNC(h)) {
duk_hcompfunc *f = (duk_hcompfunc *) h;
DUK__COMMA(); duk_fb_put_cstring(fb, "__data:");
duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f));
diff --git a/src/duk_debugger.c b/src/duk_debugger.c
index c4181815..976591e8 100644
--- a/src/duk_debugger.c
+++ b/src/duk_debugger.c
@@ -1947,7 +1947,7 @@ DUK_LOCAL duk_uint_t duk__debug_getinfo_hstring_masks[] = {
DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = {
"extensible",
"constructable",
- "bound",
+ "boundfunc",
"compfunc",
"natfunc",
"bufobj",
@@ -1969,7 +1969,7 @@ DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = {
DUK_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks[] = {
DUK_HOBJECT_FLAG_EXTENSIBLE,
DUK_HOBJECT_FLAG_CONSTRUCTABLE,
- DUK_HOBJECT_FLAG_BOUND,
+ DUK_HOBJECT_FLAG_BOUNDFUNC,
DUK_HOBJECT_FLAG_COMPFUNC,
DUK_HOBJECT_FLAG_NATFUNC,
DUK_HOBJECT_FLAG_BUFOBJ,
diff --git a/src/duk_error_augment.c b/src/duk_error_augment.c
index eef2538e..c51d047e 100644
--- a/src/duk_error_augment.c
+++ b/src/duk_error_augment.c
@@ -159,7 +159,11 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
duk_context *ctx = (duk_context *) thr;
duk_small_uint_t depth;
duk_int_t i, i_min;
- duk_uarridx_t arr_idx;
+ duk_int_t arr_size;
+ duk_harray *a;
+ duk_tval *tv;
+ duk_hstring *s;
+ duk_uint32_t u32;
duk_double_t d;
DUK_ASSERT(thr != NULL);
@@ -179,20 +183,41 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
DUK_DDD(DUK_DDDPRINT("adding traceback to object: %!T",
(duk_tval *) duk_get_tval(ctx, -1)));
- duk_push_array(ctx); /* XXX: specify array size, as we know it */
- arr_idx = 0;
+ /* Preallocate array to correct size, so that we can just write out
+ * the _Tracedata values into the array part.
+ */
+ depth = DUK_USE_TRACEBACK_DEPTH;
+ arr_size = (thr_callstack->callstack_top <= depth ? thr_callstack->callstack_top : depth) * 2;
+ if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
+ arr_size += 2;
+ }
+ if (c_filename) {
+ /* We need the C filename to be interned before getting the
+ * array part pointer to avoid any GC interference while the
+ * array part is populated.
+ */
+ duk_push_string(ctx, c_filename);
+ arr_size += 2;
+ }
+
+ DUK_D(DUK_DPRINT("preallocated _Tracedata to %ld items", (long) arr_size));
+ a = duk_push_harray_with_size(ctx, (duk_uint32_t) arr_size); /* XXX: call which returns array part pointer directly */
+ DUK_ASSERT(a != NULL);
+ tv = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a);
+ DUK_ASSERT(tv != NULL || arr_size == 0);
/* Compiler SyntaxErrors (and other errors) come first, and are
* blamed by default (not flagged "noblame").
*/
if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
- duk_push_hstring(ctx, thr->compile_ctx->h_filename);
- duk_xdef_prop_index_wec(ctx, -2, arr_idx);
- arr_idx++;
-
- duk_push_uint(ctx, (duk_uint_t) thr->compile_ctx->curr_token.start_line); /* (flags<<32) + (line), flags = 0 */
- duk_xdef_prop_index_wec(ctx, -2, arr_idx);
- arr_idx++;
+ s = thr->compile_ctx->h_filename;
+ DUK_TVAL_SET_STRING(tv, s);
+ DUK_HSTRING_INCREF(thr, s);
+ tv++;
+
+ u32 = (duk_uint32_t) thr->compile_ctx->curr_token.start_line; /* (flags<<32) + (line), flags = 0 */
+ DUK_TVAL_SET_U32(tv, u32);
+ tv++;
}
/* Filename/line from C macros (__FILE__, __LINE__) are added as an
@@ -200,31 +225,20 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
* the line and flags.
*/
- /* XXX: optimize: allocate an array part to the necessary size (upwards
- * estimate) and fill in the values directly into the array part; finally
- * update 'length'.
- */
-
- /* XXX: using duk_put_prop_index() would cause obscure error cases when Array.prototype
- * has write-protected array index named properties. This was seen as DoubleErrors
- * in e.g. some test262 test cases. Using duk_xdef_prop_index() is better but heavier.
- * The best fix is to fill in the tracedata directly into the array part. There are
- * no side effect concerns if the array part is allocated directly and only INCREFs
- * happen after that.
- */
-
- /* [ ... error arr ] */
+ /* [ ... error c_filename? arr ] */
if (c_filename) {
- duk_push_string(ctx, c_filename);
- duk_xdef_prop_index_wec(ctx, -2, arr_idx);
- arr_idx++;
+ DUK_ASSERT(DUK_TVAL_IS_STRING(thr->valstack_top - 2));
+ s = DUK_TVAL_GET_STRING(thr->valstack_top - 2); /* interned c_filename */
+ DUK_ASSERT(s != NULL);
+ DUK_TVAL_SET_STRING(tv, s);
+ DUK_HSTRING_INCREF(thr, s);
+ tv++;
d = (noblame_fileline ? ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) +
(duk_double_t) c_line;
- duk_push_number(ctx, d);
- duk_xdef_prop_index_wec(ctx, -2, arr_idx);
- arr_idx++;
+ DUK_TVAL_SET_DOUBLE(tv, d);
+ tv++;
}
/* traceback depth doesn't take into account the filename/line
@@ -234,11 +248,12 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
i_min = (thr_callstack->callstack_top > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0);
DUK_ASSERT(i_min >= 0);
- /* [ ... error arr ] */
+ /* [ ... error c_filename? arr ] */
DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX); /* callstack limits */
for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) {
duk_uint32_t pc;
+ duk_tval *tv_src;
/*
* Note: each API operation potentially resizes the callstack,
@@ -253,9 +268,11 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
DUK_ASSERT_DISABLE(thr_callstack->callstack[i].pc >= 0); /* unsigned */
/* Add function object. */
- duk_push_tval(ctx, &(thr_callstack->callstack + i)->tv_func);
- duk_xdef_prop_index_wec(ctx, -2, arr_idx);
- arr_idx++;
+ tv_src = &(thr_callstack->callstack + i)->tv_func; /* object (function) or lightfunc */
+ DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_src) || DUK_TVAL_IS_LIGHTFUNC(tv_src));
+ DUK_TVAL_SET_TVAL(tv, tv_src);
+ DUK_TVAL_INCREF(thr, tv);
+ tv++;
/* Add a number containing: pc, activation flags.
*
@@ -266,14 +283,18 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack,
DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */
DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */
d = ((duk_double_t) thr_callstack->callstack[i].flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc;
- duk_push_number(ctx, d); /* -> [... arr num] */
- duk_xdef_prop_index_wec(ctx, -2, arr_idx);
- arr_idx++;
+ DUK_TVAL_SET_DOUBLE(tv, d);
+ tv++;
}
- /* XXX: set with duk_hobject_set_length() when tracedata is filled directly */
- duk_push_uint(ctx, (duk_uint_t) arr_idx);
- duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC);
+ DUK_ASSERT((duk_uint32_t) (tv - DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a)) == a->length);
+ DUK_ASSERT(a->length == (duk_uint32_t) arr_size);
+
+ /* [ ... error c_filename? arr ] */
+
+ if (c_filename) {
+ duk_remove(ctx, -2);
+ }
/* [ ... error arr ] */
diff --git a/src/duk_error_macros.c b/src/duk_error_macros.c
index 6eca34d3..a209a841 100644
--- a/src/duk_error_macros.c
+++ b/src/duk_error_macros.c
@@ -71,6 +71,8 @@ DUK_INTERNAL void duk_err_type_invalid_args(duk_hthread *thr, const char *filena
/* The file/line arguments are NULL and 0, they're ignored by DUK_ERROR_RAW()
* when non-verbose errors are used.
*/
+
+DUK_NORETURN(DUK_LOCAL_DECL void duk__err_shared(duk_hthread *thr, duk_uint_t code));
DUK_LOCAL void duk__err_shared(duk_hthread *thr, duk_uint_t code) {
DUK_ERROR_RAW(thr, NULL, 0, code, NULL);
}
diff --git a/src/duk_forwdecl.h b/src/duk_forwdecl.h
index f3e4bc8b..44d42ffc 100644
--- a/src/duk_forwdecl.h
+++ b/src/duk_forwdecl.h
@@ -18,6 +18,7 @@ struct duk_jmpbuf;
/* duk_tval intentionally skipped */
struct duk_heaphdr;
struct duk_heaphdr_string;
+struct duk_harray;
struct duk_hstring;
struct duk_hstring_external;
struct duk_hobject;
@@ -73,6 +74,7 @@ typedef struct duk_jmpbuf duk_jmpbuf;
/* duk_tval intentionally skipped */
typedef struct duk_heaphdr duk_heaphdr;
typedef struct duk_heaphdr_string duk_heaphdr_string;
+typedef struct duk_harray duk_harray;
typedef struct duk_hstring duk_hstring;
typedef struct duk_hstring_external duk_hstring_external;
typedef struct duk_hobject duk_hobject;
diff --git a/src/duk_harray.h b/src/duk_harray.h
new file mode 100644
index 00000000..7c8af3af
--- /dev/null
+++ b/src/duk_harray.h
@@ -0,0 +1,42 @@
+/*
+ * Heap Array object representation. Used for actual Array instances.
+ *
+ * All objects with the exotic array behavior (which must coincide with having
+ * internal class array) MUST be duk_harrays. No other object can be a
+ * duk_harray. However, duk_harrays may not always have an array part.
+ */
+
+#ifndef DUK_HARRAY_H_INCLUDED
+#define DUK_HARRAY_H_INCLUDED
+
+#define DUK_ASSERT_HARRAY_VALID(h) do { \
+ DUK_ASSERT((h) != NULL); \
+ DUK_ASSERT(DUK_HOBJECT_IS_ARRAY((duk_hobject *) (h))); \
+ DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY((duk_hobject *) (h))); \
+ } while (0)
+
+#define DUK_HARRAY_LENGTH_WRITABLE(h) (!(h)->length_nonwritable)
+#define DUK_HARRAY_LENGTH_NONWRITABLE(h) ((h)->length_nonwritable)
+#define DUK_HARRAY_SET_LENGTH_WRITABLE(h) do { (h)->length_nonwritable = 0; } while (0)
+#define DUK_HARRAY_SET_LENGTH_NONWRITABLE(h) do { (h)->length_nonwritable = 1; } while (0)
+
+struct duk_harray {
+ /* Shared object part. */
+ duk_hobject obj;
+
+ /* Array .length. */
+ duk_uint32_t length;
+
+ /* Array .length property attributes. The property is always
+ * non-enumerable and non-configurable. It's initially writable
+ * but per Object.defineProperty() rules it can be made non-writable
+ * even if it is non-configurable. Thus we need to track the
+ * writability explicitly.
+ *
+ * XXX: this field to be eliminated and moved into duk_hobject
+ * flags field to save space.
+ */
+ duk_bool_t length_nonwritable;
+};
+
+#endif /* DUK_HARRAY_H_INCLUDED */
diff --git a/src/duk_heap_alloc.c b/src/duk_heap_alloc.c
index e921fc41..25eac338 100644
--- a/src/duk_heap_alloc.c
+++ b/src/duk_heap_alloc.c
@@ -608,9 +608,11 @@ DUK_LOCAL void duk__dump_type_sizes(void) {
DUK__DUMPSZ(duk_hstring);
DUK__DUMPSZ(duk_hstring_external);
DUK__DUMPSZ(duk_hobject);
+ DUK__DUMPSZ(duk_harray);
DUK__DUMPSZ(duk_hcompfunc);
DUK__DUMPSZ(duk_hnatfunc);
DUK__DUMPSZ(duk_hthread);
+ DUK__DUMPSZ(duk_hbufobj);
DUK__DUMPSZ(duk_hbuffer);
DUK__DUMPSZ(duk_hbuffer_fixed);
DUK__DUMPSZ(duk_hbuffer_dynamic);
diff --git a/src/duk_heap_markandsweep.c b/src/duk_heap_markandsweep.c
index cc0d4e9a..adbd38f8 100644
--- a/src/duk_heap_markandsweep.c
+++ b/src/duk_heap_markandsweep.c
@@ -69,7 +69,13 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) {
duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h));
- if (DUK_HOBJECT_IS_COMPFUNC(h)) {
+ /* XXX: rearrange bits to allow a switch case to be used here? */
+ /* XXX: add a fast path for objects (and arrays)? */
+ if (DUK_HOBJECT_IS_ARRAY(h)) {
+ duk_harray *a = (duk_harray *) h;
+ /* No special marking. */
+ (void) a;
+ } else if (DUK_HOBJECT_IS_COMPFUNC(h)) {
duk_hcompfunc *f = (duk_hcompfunc *) h;
duk_tval *tv, *tv_end;
duk_hobject **fn, **fn_end;
diff --git a/src/duk_heap_refcount.c b/src/duk_heap_refcount.c
index beaa41a5..5f6e0abc 100644
--- a/src/duk_heap_refcount.c
+++ b/src/duk_heap_refcount.c
@@ -84,7 +84,13 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h)
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h));
- if (DUK_HOBJECT_IS_COMPFUNC(h)) {
+ /* XXX: rearrange bits to allow a switch case to be used here? */
+ /* XXX: add a fast path for objects (and arrays)? */
+ if (DUK_HOBJECT_IS_ARRAY(h)) {
+ duk_harray *a = (duk_harray *) h;
+ /* No special ref updates. */
+ (void) a;
+ } else if (DUK_HOBJECT_IS_COMPFUNC(h)) {
duk_hcompfunc *f = (duk_hcompfunc *) h;
duk_tval *tv, *tv_end;
duk_hobject **funcs, **funcs_end;
diff --git a/src/duk_hobject.h b/src/duk_hobject.h
index 29a6173c..4aafcc92 100644
--- a/src/duk_hobject.h
+++ b/src/duk_hobject.h
@@ -32,12 +32,16 @@
#ifndef DUK_HOBJECT_H_INCLUDED
#define DUK_HOBJECT_H_INCLUDED
-/* Object flag. There are currently 26 flag bits available. Make sure
+/* Object flag. There are currently 25 flag bits available. Make sure
* this stays in sync with debugger object inspection code.
*/
+
+/* XXX: some flags are object subtype specific (e.g. common to all function
+ * subtypes, duk_harray, etc) and could be reused for different subtypes.
+ */
#define DUK_HOBJECT_FLAG_EXTENSIBLE DUK_HEAPHDR_USER_FLAG(0) /* object is extensible */
#define DUK_HOBJECT_FLAG_CONSTRUCTABLE DUK_HEAPHDR_USER_FLAG(1) /* object is constructable */
-#define DUK_HOBJECT_FLAG_BOUND DUK_HEAPHDR_USER_FLAG(2) /* object established using Function.prototype.bind() */
+#define DUK_HOBJECT_FLAG_BOUNDFUNC DUK_HEAPHDR_USER_FLAG(2) /* object established using Function.prototype.bind() */
#define DUK_HOBJECT_FLAG_COMPFUNC DUK_HEAPHDR_USER_FLAG(4) /* object is a compiled function (duk_hcompfunc) */
#define DUK_HOBJECT_FLAG_NATFUNC DUK_HEAPHDR_USER_FLAG(5) /* object is a native function (duk_hnatfunc) */
#define DUK_HOBJECT_FLAG_BUFOBJ DUK_HEAPHDR_USER_FLAG(6) /* object is a buffer object (duk_hbufobj) (always exotic) */
@@ -156,7 +160,8 @@
#define DUK_HOBJECT_IS_OBJENV(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_OBJENV)
#define DUK_HOBJECT_IS_DECENV(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_DECENV)
#define DUK_HOBJECT_IS_ENV(h) (DUK_HOBJECT_IS_OBJENV((h)) || DUK_HOBJECT_IS_DECENV((h)))
-#define DUK_HOBJECT_IS_ARRAY(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_ARRAY)
+#define DUK_HOBJECT_IS_ARRAY(h) DUK_HOBJECT_HAS_EXOTIC_ARRAY((h)) /* Rely on class Array <=> exotic Array */
+#define DUK_HOBJECT_IS_BOUNDFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
#define DUK_HOBJECT_IS_COMPFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
#define DUK_HOBJECT_IS_NATFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
#define DUK_HOBJECT_IS_BUFOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
@@ -167,12 +172,12 @@
DUK_HOBJECT_FLAG_NATFUNC)
#define DUK_HOBJECT_IS_FUNCTION(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \
- DUK_HOBJECT_FLAG_BOUND | \
+ DUK_HOBJECT_FLAG_BOUNDFUNC | \
DUK_HOBJECT_FLAG_COMPFUNC | \
DUK_HOBJECT_FLAG_NATFUNC)
#define DUK_HOBJECT_IS_CALLABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \
- DUK_HOBJECT_FLAG_BOUND | \
+ DUK_HOBJECT_FLAG_BOUNDFUNC | \
DUK_HOBJECT_FLAG_COMPFUNC | \
DUK_HOBJECT_FLAG_NATFUNC)
@@ -183,12 +188,18 @@
DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC | \
DUK_HOBJECT_FLAG_BUFOBJ | \
DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
-
#define DUK_HOBJECT_HAS_EXOTIC_BEHAVIOR(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS)
+/* object has any virtual properties (not counting Proxy behavior) */
+#define DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \
+ DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | \
+ DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC | \
+ DUK_HOBJECT_FLAG_BUFOBJ)
+#define DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS)
+
#define DUK_HOBJECT_HAS_EXTENSIBLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
#define DUK_HOBJECT_HAS_CONSTRUCTABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
-#define DUK_HOBJECT_HAS_BOUND(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUND)
+#define DUK_HOBJECT_HAS_BOUNDFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
#define DUK_HOBJECT_HAS_COMPFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
#define DUK_HOBJECT_HAS_NATFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
#define DUK_HOBJECT_HAS_BUFOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
@@ -208,7 +219,7 @@
#define DUK_HOBJECT_SET_EXTENSIBLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
#define DUK_HOBJECT_SET_CONSTRUCTABLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
-#define DUK_HOBJECT_SET_BOUND(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUND)
+#define DUK_HOBJECT_SET_BOUNDFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
#define DUK_HOBJECT_SET_COMPFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
#define DUK_HOBJECT_SET_NATFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
#define DUK_HOBJECT_SET_BUFOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
@@ -228,7 +239,7 @@
#define DUK_HOBJECT_CLEAR_EXTENSIBLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
#define DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
-#define DUK_HOBJECT_CLEAR_BOUND(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUND)
+#define DUK_HOBJECT_CLEAR_BOUNDFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
#define DUK_HOBJECT_CLEAR_COMPFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
#define DUK_HOBJECT_CLEAR_NATFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
#define DUK_HOBJECT_CLEAR_BUFOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
@@ -303,6 +314,9 @@
DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT32ARRAY || \
DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_FLOAT32ARRAY || \
DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_FLOAT64ARRAY)); \
+ /* Object is an Array <=> object has exotic array behavior */ \
+ DUK_ASSERT((DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_ARRAY && DUK_HOBJECT_HAS_EXOTIC_ARRAY((h))) || \
+ (DUK_HOBJECT_GET_CLASS_NUMBER((h)) != DUK_HOBJECT_CLASS_ARRAY && !DUK_HOBJECT_HAS_EXOTIC_ARRAY((h)))); \
} while (0)
/*
@@ -831,11 +845,20 @@ DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc(duk_heap *heap, duk_uint_t hobj
#if 0 /* unused */
DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc_checked(duk_hthread *thr, duk_uint_t hobject_flags);
#endif
+DUK_INTERNAL_DECL duk_harray *duk_harray_alloc(duk_heap *heap, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hcompfunc *duk_hcompfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hnatfunc *duk_hnatfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags);
-DUK_INTERNAL duk_hbufobj *duk_hbufobj_alloc(duk_heap *heap, duk_uint_t hobject_flags);
+DUK_INTERNAL_DECL duk_hbufobj *duk_hbufobj_alloc(duk_heap *heap, duk_uint_t hobject_flags);
DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_flags);
+/* resize */
+DUK_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr,
+ duk_hobject *obj,
+ duk_uint32_t new_e_size,
+ duk_uint32_t new_a_size,
+ duk_uint32_t new_h_size,
+ duk_bool_t abandon_array);
+
/* low-level property functions */
DUK_INTERNAL_DECL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx);
DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_hstring *key);
diff --git a/src/duk_hobject_alloc.c b/src/duk_hobject_alloc.c
index 9b294ac3..7a5785d9 100644
--- a/src/duk_hobject_alloc.c
+++ b/src/duk_hobject_alloc.c
@@ -190,3 +190,23 @@ DUK_INTERNAL duk_hobject *duk_hobject_alloc_checked(duk_hthread *thr, duk_uint_t
return res;
}
#endif
+
+/*
+ * Allocate a new array.
+ */
+
+DUK_INTERNAL duk_harray *duk_harray_alloc(duk_heap *heap, duk_uint_t hobject_flags) {
+ duk_harray *res;
+
+ res = (duk_harray *) DUK_ALLOC(heap, sizeof(duk_harray));
+ if (!res) {
+ return NULL;
+ }
+ DUK_MEMZERO(res, sizeof(duk_harray));
+
+ duk__init_object_parts(heap, &res->obj, hobject_flags);
+
+ DUK_ASSERT(res->length == 0);
+
+ return res;
+}
diff --git a/src/duk_hobject_enum.c b/src/duk_hobject_enum.c
index afceeaca..eae14fab 100644
--- a/src/duk_hobject_enum.c
+++ b/src/duk_hobject_enum.c
@@ -394,6 +394,16 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint
/* [enum_target res] */
}
+ if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(curr)) {
+ /* Array .length comes after numeric indices. */
+
+ if (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) {
+ duk_push_hstring_stridx(ctx, DUK_STRIDX_LENGTH);
+ duk_push_true(ctx);
+ duk_put_prop(ctx, -3);
+ }
+ }
+
/*
* Entries part
*/
diff --git a/src/duk_hobject_props.c b/src/duk_hobject_props.c
index 0c765584..e1384fb5 100644
--- a/src/duk_hobject_props.c
+++ b/src/duk_hobject_props.c
@@ -82,7 +82,6 @@ DUK_LOCAL_DECL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hob
DUK_LOCAL_DECL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags);
DUK_LOCAL_DECL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_uint32_t arr_idx, duk_propdesc *out_desc, duk_small_uint_t flags);
-DUK_LOCAL duk_uint32_t duk__get_old_array_length(duk_hthread *thr, duk_hobject *obj, duk_propdesc *temp_desc);
/*
* Misc helpers
@@ -484,13 +483,12 @@ DUK_LOCAL duk_bool_t duk__proxy_check_prop(duk_hthread *thr, duk_hobject *obj, d
* will become invalid after this call.
*/
-DUK_LOCAL
-void duk__realloc_props(duk_hthread *thr,
- duk_hobject *obj,
- duk_uint32_t new_e_size,
- duk_uint32_t new_a_size,
- duk_uint32_t new_h_size,
- duk_bool_t abandon_array) {
+DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr,
+ duk_hobject *obj,
+ duk_uint32_t new_e_size,
+ duk_uint32_t new_a_size,
+ duk_uint32_t new_h_size,
+ duk_bool_t abandon_array) {
duk_context *ctx = (duk_context *) thr;
#ifdef DUK_USE_MARK_AND_SWEEP
duk_small_uint_t prev_mark_and_sweep_base_flags;
@@ -967,7 +965,7 @@ DUK_LOCAL void duk__grow_props_for_new_entry_item(duk_hthread *thr, duk_hobject
new_a_size = DUK_HOBJECT_GET_ASIZE(obj);
DUK_ASSERT(new_e_size >= old_e_used + 1); /* duk__get_min_grow_e() is always >= 1 */
- duk__realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
+ duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
}
/* Grow array part for a new highest array index. */
@@ -987,7 +985,7 @@ DUK_LOCAL void duk__grow_props_for_array_item(duk_hthread *thr, duk_hobject *obj
new_a_size = highest_arr_idx + duk__get_min_grow_a(highest_arr_idx);
DUK_ASSERT(new_a_size >= highest_arr_idx + 1); /* duk__get_min_grow_a() is always >= 1 */
- duk__realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
+ duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
}
/* Abandon array part, moving array entries into entries part.
@@ -1030,7 +1028,7 @@ DUK_LOCAL void duk__abandon_array_checked(duk_hthread *thr, duk_hobject *obj) {
(void *) obj, (long) e_used, (long) a_used, (long) a_size,
(long) new_e_size, (long) new_a_size, (long) new_h_size));
- duk__realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 1);
+ duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 1);
}
/*
@@ -1095,7 +1093,7 @@ DUK_INTERNAL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj)
DUK_DD(DUK_DDPRINT("compacting hobject -> new e_size %ld, new a_size=%ld, new h_size=%ld, abandon_array=%ld",
(long) e_size, (long) a_size, (long) h_size, (long) abandon_array));
- duk__realloc_props(thr, obj, e_size, a_size, h_size, abandon_array);
+ duk_hobject_realloc_props(thr, obj, e_size, a_size, h_size, abandon_array);
}
/*
@@ -1672,13 +1670,40 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob
}
/*
- * Not found as a concrete property, check whether a String object
- * virtual property matches.
+ * Not found as a concrete property, check for virtual properties.
*/
prop_not_found_concrete:
- if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj)) {
+ if (!DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(obj)) {
+ /* Quick skip. */
+ goto prop_not_found;
+ }
+
+ if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
+ duk_harray *a;
+
+ DUK_DDD(DUK_DDDPRINT("array object exotic property get for key: %!O, arr_idx: %ld",
+ (duk_heaphdr *) key, (long) arr_idx));
+
+ a = (duk_harray *) obj;
+ DUK_ASSERT_HARRAY_VALID(a);
+
+ if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
+ DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length exotic behavior"));
+
+ if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
+ duk_push_uint(ctx, (duk_uint_t) a->length);
+ }
+ out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
+ if (DUK_HARRAY_LENGTH_WRITABLE(a)) {
+ out_desc->flags |= DUK_PROPDESC_FLAG_WRITABLE;
+ }
+
+ DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
+ return 1; /* cannot be arguments exotic */
+ }
+ } else if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj)) {
DUK_DDD(DUK_DDDPRINT("string object exotic property get for key: %!O, arr_idx: %ld",
(duk_heaphdr *) key, (long) arr_idx));
@@ -1827,6 +1852,7 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob
* Not found as concrete or virtual
*/
+ prop_not_found:
DUK_DDD(DUK_DDDPRINT("-> not found (virtual, entry part, or array part)"));
return 0;
@@ -2033,8 +2059,9 @@ DUK_LOCAL duk_tval *duk__getprop_shallow_fastpath_array_tval(duk_hthread *thr, d
return NULL;
}
-DUK_LOCAL duk_bool_t duk__putprop_shallow_fastpath_array_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_tval *tv_val, duk_propdesc *temp_desc) {
+DUK_LOCAL duk_bool_t duk__putprop_shallow_fastpath_array_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_tval *tv_val) {
duk_tval *tv;
+ duk_harray *a;
duk_uint32_t idx;
duk_uint32_t old_len, new_len;
@@ -2045,6 +2072,9 @@ DUK_LOCAL duk_bool_t duk__putprop_shallow_fastpath_array_tval(duk_hthread *thr,
}
DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); /* caller ensures */
+ a = (duk_harray *) obj;
+ DUK_ASSERT_HARRAY_VALID(a);
+
#if defined(DUK_USE_FASTINT)
if (DUK_TVAL_IS_FASTINT(tv_key)) {
idx = duk__tval_fastint_to_arr_idx(tv_key);
@@ -2068,24 +2098,19 @@ DUK_LOCAL duk_bool_t duk__putprop_shallow_fastpath_array_tval(duk_hthread *thr,
DUK_ASSERT(idx != 0xffffffffUL);
DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);
- old_len = duk__get_old_array_length(thr, obj, temp_desc);
+ old_len = a->length;
if (idx >= old_len) {
DUK_DDD(DUK_DDDPRINT("write new array entry requires length update "
"(arr_idx=%ld, old_len=%ld)",
(long) idx, (long) old_len));
- if (!(temp_desc->flags & DUK_PROPDESC_FLAG_WRITABLE)) {
+ if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) {
DUK_ERROR_TYPE(thr, DUK_STR_NOT_WRITABLE);
return 0; /* not reachable */
}
new_len = idx + 1;
- /* No resize has occurred so temp_desc->e_idx is still OK */
- tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, temp_desc->e_idx);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
- DUK_TVAL_SET_U32(tv, new_len); /* no need for decref/incref because value is a number */
- } else {
- ;
+ ((duk_harray *) obj)->length = new_len;
}
tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, idx);
@@ -2340,6 +2365,8 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj,
curr = DUK_TVAL_GET_OBJECT(tv_obj);
DUK_ASSERT(curr != NULL);
+ /* XXX: array .length fast path (important in e.g. loops)? */
+
tmp = duk__getprop_shallow_fastpath_array_tval(thr, curr, tv_key);
if (tmp) {
duk_push_tval(ctx, tmp);
@@ -2683,7 +2710,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj,
* only refers to the value being a "strict mode Function
* object" which is ambiguous.
*/
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(orig));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(orig));
h = duk_get_hobject(ctx, -1); /* NULL if not an object */
if (h &&
@@ -2866,51 +2893,6 @@ DUK_INTERNAL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *o
* Used by duk_hobject_putprop().
*/
-DUK_LOCAL duk_uint32_t duk__get_old_array_length(duk_hthread *thr, duk_hobject *obj, duk_propdesc *temp_desc) {
- duk_bool_t rc;
- duk_tval *tv;
- duk_uint32_t res;
-
- DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
- /* This function is only called for objects with array exotic behavior.
- * The [[DefineOwnProperty]] algorithm for arrays requires that
- * 'length' can never have a value outside the unsigned 32-bit range,
- * attempt to write such a value is a RangeError. Here we can thus
- * assert for this. When Duktape internals go around the official
- * property write interface (doesn't happen often) this assumption is
- * easy to accidentally break, so such code must be written carefully.
- * See test-bi-array-push-maxlen.js.
- */
-
- rc = duk__get_own_propdesc_raw(thr, obj, DUK_HTHREAD_STRING_LENGTH(thr), DUK__NO_ARRAY_INDEX, temp_desc, 0 /*flags*/); /* don't push value */
- DUK_UNREF(rc);
- DUK_ASSERT(rc != 0); /* arrays MUST have a 'length' property */
- DUK_ASSERT(temp_desc->e_idx >= 0);
-
- tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, temp_desc->e_idx);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); /* array 'length' is always a number, as we coerce it */
- DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) >= 0.0);
- DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (double) 0xffffffffUL);
- DUK_ASSERT((duk_double_t) (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv) == DUK_TVAL_GET_NUMBER(tv));
-#if defined(DUK_USE_FASTINT)
- /* Downgrade checks are not made everywhere, so 'length' is not always
- * a fastint (it is a number though). This can be removed once length
- * is always guaranteed to be a fastint.
- */
- DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv) || DUK_TVAL_IS_DOUBLE(tv));
- if (DUK_TVAL_IS_FASTINT(tv)) {
- res = (duk_uint32_t) DUK_TVAL_GET_FASTINT_U32(tv);
- } else {
- res = (duk_uint32_t) DUK_TVAL_GET_DOUBLE(tv);
- }
-#else
- res = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv);
-#endif /* DUK_USE_FASTINT */
-
- return res;
-}
-
DUK_LOCAL duk_uint32_t duk__to_new_array_length_checked(duk_hthread *thr) {
duk_context *ctx = (duk_context *) thr;
duk_uint32_t res;
@@ -2921,7 +2903,7 @@ DUK_LOCAL duk_uint32_t duk__to_new_array_length_checked(duk_hthread *thr) {
* outside the 32-bit range. Negative zero is accepted as zero.
*/
- /* XXX: fastint */
+ /* XXX: fastint; avoid value stack! */
d = duk_to_number(ctx, -1);
res = (duk_uint32_t) d;
@@ -2977,6 +2959,9 @@ duk_bool_t duk__handle_put_array_length_smaller(duk_hthread *thr,
DUK_ASSERT(out_result_len != NULL);
DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
+ DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj));
+ DUK_ASSERT(DUK_HOBJECT_IS_ARRAY(obj));
+
if (DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
/*
* All defined array-indexed properties are in the array part
@@ -3125,11 +3110,10 @@ duk_bool_t duk__handle_put_array_length_smaller(duk_hthread *thr,
/* XXX: is valstack top best place for argument? */
DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject *obj) {
duk_context *ctx = (duk_context *) thr;
- duk_propdesc desc;
+ duk_harray *a;
duk_uint32_t old_len;
duk_uint32_t new_len;
duk_uint32_t result_len;
- duk_tval *tv;
duk_bool_t rc;
DUK_DDD(DUK_DDDPRINT("handling a put operation to array 'length' exotic property, "
@@ -3142,13 +3126,18 @@ DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject
DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
+ DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj));
+ DUK_ASSERT(DUK_HOBJECT_IS_ARRAY(obj));
+ a = (duk_harray *) obj;
+ DUK_ASSERT_HARRAY_VALID(a);
+
DUK_ASSERT(duk_is_valid_index(ctx, -1));
/*
* Get old and new length
*/
- old_len = duk__get_old_array_length(thr, obj, &desc);
+ old_len = a->length;
duk_dup(ctx, -1); /* [in_val in_val] */
new_len = duk__to_new_array_length_checked(thr); /* -> [in_val] */
DUK_DDD(DUK_DDDPRINT("old_len=%ld, new_len=%ld", (long) old_len, (long) new_len));
@@ -3157,7 +3146,7 @@ DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject
* Writability check
*/
- if (!(desc.flags & DUK_PROPDESC_FLAG_WRITABLE)) {
+ if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) {
DUK_DDD(DUK_DDDPRINT("length is not writable, fail"));
return 0;
}
@@ -3169,14 +3158,7 @@ DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject
if (new_len >= old_len) {
DUK_DDD(DUK_DDDPRINT("new length is higher than old length, just update length, no deletions"));
-
- DUK_ASSERT(desc.e_idx >= 0);
- DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, desc.e_idx));
- tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, desc.e_idx);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
- /* no decref needed for a number */
- DUK_TVAL_SET_U32(tv, new_len);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ a->length = new_len;
return 1;
}
@@ -3193,13 +3175,7 @@ DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject
rc = duk__handle_put_array_length_smaller(thr, obj, old_len, new_len, 0 /*force_flag*/, &result_len);
DUK_ASSERT(result_len >= new_len && result_len <= old_len);
- DUK_ASSERT(desc.e_idx >= 0);
- DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, desc.e_idx));
- tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, desc.e_idx);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
- /* no decref needed for a number */
- DUK_TVAL_SET_U32(tv, result_len);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ a->length = result_len;
/* XXX: shrink array allocation or entries compaction here? */
@@ -3362,7 +3338,9 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
* tests/ecmascript/test-misc-array-fast-write.js
*/
- if (duk__putprop_shallow_fastpath_array_tval(thr, orig, tv_key, tv_val, &desc) != 0) {
+ /* XXX: array .length? */
+
+ if (duk__putprop_shallow_fastpath_array_tval(thr, orig, tv_key, tv_val) != 0) {
DUK_DDD(DUK_DDDPRINT("array fast path success"));
return 1;
}
@@ -3639,7 +3617,37 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
}
if (desc.flags & DUK_PROPDESC_FLAG_VIRTUAL) {
DUK_DD(DUK_DDPRINT("found existing own (non-inherited) virtual property, property is writable"));
- if (DUK_HOBJECT_IS_BUFOBJ(curr)) {
+
+ if (DUK_HOBJECT_IS_ARRAY(curr)) {
+ /*
+ * Write to 'length' of an array is a very complex case
+ * handled in a helper which updates both the array elements
+ * and writes the new 'length'. The write may result in an
+ * unconditional RangeError or a partial write (indicated
+ * by a return code).
+ *
+ * Note: the helper has an unnecessary writability check
+ * for 'length', we already know it is writable.
+ */
+ DUK_ASSERT(key == DUK_HTHREAD_STRING_LENGTH(thr)); /* only virtual array property */
+
+ DUK_DDD(DUK_DDDPRINT("writing existing 'length' property to array exotic, invoke complex helper"));
+
+ /* XXX: the helper currently assumes stack top contains new
+ * 'length' value and the whole calling convention is not very
+ * compatible with what we need.
+ */
+
+ duk_push_tval(ctx, tv_val); /* [key val] */
+ rc = duk__handle_put_array_length(thr, orig);
+ duk_pop(ctx); /* [key val] -> [key] */
+ if (!rc) {
+ goto fail_array_length_partial;
+ }
+
+ /* key is 'length', cannot match argument exotic behavior */
+ goto success_no_arguments_exotic;
+ } else if (DUK_HOBJECT_IS_BUFOBJ(curr)) {
duk_hbufobj *h_bufobj;
duk_uint_t byte_off;
duk_small_uint_t elem_size;
@@ -3679,6 +3687,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
}
}
+ DUK_D(DUK_DPRINT("should not happen, key %!O", key));
goto fail_internal; /* should not happen */
}
DUK_DD(DUK_DDPRINT("put to existing own plain property, property is writable"));
@@ -3741,36 +3750,8 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
DUK_ASSERT((desc.flags & DUK_PROPDESC_FLAG_VIRTUAL) == 0);
DUK_ASSERT(desc.a_idx >= 0 || desc.e_idx >= 0);
- if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(orig) &&
- key == DUK_HTHREAD_STRING_LENGTH(thr)) {
- /*
- * Write to 'length' of an array is a very complex case
- * handled in a helper which updates both the array elements
- * and writes the new 'length'. The write may result in an
- * unconditional RangeError or a partial write (indicated
- * by a return code).
- *
- * Note: the helper has an unnecessary writability check
- * for 'length', we already know it is writable.
- */
-
- DUK_DDD(DUK_DDDPRINT("writing existing 'length' property to array exotic, invoke complex helper"));
-
- /* XXX: the helper currently assumes stack top contains new
- * 'length' value and the whole calling convention is not very
- * compatible with what we need.
- */
-
- duk_push_tval(ctx, tv_val); /* [key val] */
- rc = duk__handle_put_array_length(thr, orig);
- duk_pop(ctx); /* [key val] -> [key] */
- if (!rc) {
- goto fail_array_length_partial;
- }
-
- /* key is 'length', cannot match argument exotic behavior */
- goto success_no_arguments_exotic;
- }
+ /* Array own property .length is handled above. */
+ DUK_ASSERT(!(DUK_HOBJECT_IS_ARRAY(orig) && key == DUK_HTHREAD_STRING_LENGTH(thr)));
if (desc.e_idx >= 0) {
tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, orig, desc.e_idx);
@@ -3816,6 +3797,9 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
DUK_ASSERT(orig != NULL);
+ /* Array own property .length is handled above. */
+ DUK_ASSERT(!(DUK_HOBJECT_IS_ARRAY(orig) && key == DUK_HTHREAD_STRING_LENGTH(thr)));
+
#if defined(DUK_USE_ROM_OBJECTS)
/* This should not happen because DUK_TAG_OBJECT case checks
* for this already, but check just in case.
@@ -3836,15 +3820,19 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
arr_idx != DUK__NO_ARRAY_INDEX) {
/* automatic length update */
duk_uint32_t old_len;
+ duk_harray *a;
+
+ a = (duk_harray *) orig;
+ DUK_ASSERT_HARRAY_VALID(a);
- old_len = duk__get_old_array_length(thr, orig, &desc);
+ old_len = a->length;
if (arr_idx >= old_len) {
DUK_DDD(DUK_DDDPRINT("write new array entry requires length update "
"(arr_idx=%ld, old_len=%ld)",
(long) arr_idx, (long) old_len));
- if (!(desc.flags & DUK_PROPDESC_FLAG_WRITABLE)) {
+ if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) {
DUK_DD(DUK_DDPRINT("attempt to extend array, but array 'length' is not writable"));
goto fail_not_writable;
}
@@ -4003,18 +3991,12 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
* may realloc and compact properties and hence change e_idx.
*/
+ DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(orig));
+
DUK_DDD(DUK_DDDPRINT("write successful, pending array length update to: %ld",
(long) new_array_length));
- rc = duk__get_own_propdesc_raw(thr, orig, DUK_HTHREAD_STRING_LENGTH(thr), DUK__NO_ARRAY_INDEX, &desc, 0 /*flags*/); /* don't push value */
- DUK_UNREF(rc);
- DUK_ASSERT(rc != 0);
- DUK_ASSERT(desc.e_idx >= 0);
-
- tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, orig, desc.e_idx);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
- /* no need for decref/incref because value is a number */
- DUK_TVAL_SET_U32(tv, new_array_length);
+ ((duk_harray *) orig)->length = new_array_length;
}
/*
@@ -4562,6 +4544,18 @@ DUK_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hob
DUK_DDD(DUK_DDDPRINT("property already exists but is virtual -> skip as requested"));
goto pop_exit;
}
+ if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
+ duk_uint32_t new_len;
+#if defined(DUK_USE_DEBUG)
+ duk_uint32_t prev_len;
+ prev_len = ((duk_harray *) obj)->length;
+#endif
+ new_len = duk__to_new_array_length_checked(thr); /* pops length */
+ ((duk_harray *) obj)->length = new_len;
+ DUK_D(DUK_DPRINT("internal define property for array .length: %ld -> %ld",
+ (long) prev_len, (long) ((duk_harray *) obj)->length));
+ goto no_pop_exit;
+ }
DUK_DDD(DUK_DDDPRINT("property already exists but is virtual -> failure"));
goto error_virtual;
}
@@ -4606,6 +4600,7 @@ DUK_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hob
pop_exit:
duk_pop(ctx); /* remove in_val */
+ no_pop_exit:
return;
error_internal:
@@ -4743,6 +4738,16 @@ DUK_INTERNAL void duk_hobject_set_length_zero(duk_hthread *thr, duk_hobject *obj
DUK_INTERNAL duk_uint32_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj) {
duk_context *ctx = (duk_context *) thr;
duk_double_t val;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(obj != NULL);
+
+ /* Fast path for Arrays. */
+ if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
+ return ((duk_harray *) obj)->length;
+ }
+
+ /* Slow path, .length can be e.g. accessor, obj can be a Proxy, etc. */
duk_push_hobject(ctx, obj);
duk_push_hstring_stridx(ctx, DUK_STRIDX_LENGTH);
(void) duk_hobject_getprop(thr,
@@ -4750,6 +4755,8 @@ DUK_INTERNAL duk_uint32_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *
DUK_GET_TVAL_NEGIDX(ctx, -1));
val = duk_to_number(ctx, -1);
duk_pop_n(ctx, 3);
+
+ /* XXX: better check */
if (val >= 0.0 && val < DUK_DOUBLE_2TO32) {
return (duk_uint32_t) val;
}
@@ -5080,6 +5087,8 @@ void duk_hobject_define_property_helper(duk_context *ctx,
}
if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
+ duk_harray *a;
+
/* E5 Section 15.4.5.1, step 3, steps a - i are implemented here, j - n at the end */
if (!has_value) {
DUK_DDD(DUK_DDDPRINT("exotic array behavior for 'length', but no value in descriptor -> normal behavior"));
@@ -5092,9 +5101,11 @@ void duk_hobject_define_property_helper(duk_context *ctx,
* Get old and new length
*/
- /* Note: reuse 'curr' as a temp propdesc */
- arrlen_old_len = duk__get_old_array_length(thr, obj, &curr);
+ a = (duk_harray *) obj;
+ DUK_ASSERT_HARRAY_VALID(a);
+ arrlen_old_len = a->length;
+ DUK_ASSERT(idx_value >= 0);
duk_dup(ctx, idx_value);
arrlen_new_len = duk__to_new_array_length_checked(thr);
duk_push_u32(ctx, arrlen_new_len);
@@ -5110,8 +5121,7 @@ void duk_hobject_define_property_helper(duk_context *ctx,
DUK_DDD(DUK_DDDPRINT("new length is smaller than previous => exotic post behavior"));
/* XXX: consolidated algorithm step 15.f -> redundant? */
- if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE) && !force_flag) {
- /* Note: 'curr' refers to 'length' propdesc */
+ if (DUK_HARRAY_LENGTH_NONWRITABLE(a) && !force_flag) {
goto fail_not_writable_array_length;
}
@@ -5128,17 +5138,20 @@ void duk_hobject_define_property_helper(duk_context *ctx,
/* E5 Section 15.4.5.1, step 4 */
duk_uint32_t old_len;
+ duk_harray *a;
- /* Note: use 'curr' as a temp propdesc */
- old_len = duk__get_old_array_length(thr, obj, &curr);
+ a = (duk_harray *) obj;
+ DUK_ASSERT_HARRAY_VALID(a);
+
+ old_len = a->length;
if (arr_idx >= old_len) {
DUK_DDD(DUK_DDDPRINT("defineProperty requires array length update "
"(arr_idx=%ld, old_len=%ld)",
(long) arr_idx, (long) old_len));
- if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE)) {
- /* Note: 'curr' refers to 'length' propdesc */
+ if (DUK_HARRAY_LENGTH_NONWRITABLE(a) && !force_flag) {
+ /* With force flag allow writing. */
goto fail_not_writable_array_length;
}
@@ -5178,6 +5191,17 @@ void duk_hobject_define_property_helper(duk_context *ctx,
goto fail_not_extensible;
}
+#if defined(DUK_USE_ROM_OBJECTS)
+ /* ROM objects are never extensible but force flag may
+ * allow us to come here anyway.
+ */
+ DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj) || !DUK_HOBJECT_HAS_EXTENSIBLE(obj));
+ if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
+ DUK_D(DUK_DPRINT("attempt to define property on a read-only target object"));
+ goto fail_not_configurable;
+ }
+#endif
+
/* XXX: share final setting code for value and flags? difficult because
* refcount code is different. Share entry allocation? But can't allocate
* until array index checked.
@@ -5384,15 +5408,19 @@ void duk_hobject_define_property_helper(duk_context *ctx,
}
}
- /* Reject attempt to change virtual properties: not part of the
- * standard algorithm, applies currently to e.g. virtual index
- * properties of buffer objects (which are virtual but writable).
- * (Cannot "force" modification of a virtual property.)
+ /* Virtual properties don't have backing so they can't mostly be
+ * edited. Some virtual properties are, however, writable: for
+ * example, virtual index properties of buffer objects and Array
+ * instance .length. These are not configurable so the checks
+ * above mostly cover attempts to change them, except when the
+ * duk_def_prop() call is used with DUK_DEFPROP_FORCE; even in
+ * that case we can't forcibly change the property attributes
+ * because they don't have concrete backing.
*/
- if (curr.flags & DUK_PROPDESC_FLAG_VIRTUAL) {
- goto fail_virtual;
- }
+ /* XXX: for ROM objects too it'd be best if value modify was
+ * allowed if the value matches SameValue.
+ */
/* Reject attempt to change a read-only object. */
#if defined(DUK_USE_ROM_OBJECTS)
if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
@@ -5436,7 +5464,12 @@ void duk_hobject_define_property_helper(duk_context *ctx,
DUK_ASSERT(rc != 0);
DUK_ASSERT(curr.e_idx >= 0 && curr.a_idx < 0);
}
+ if (curr.e_idx < 0) {
+ DUK_ASSERT(curr.a_idx < 0 && curr.e_idx < 0);
+ goto fail_virtual; /* safeguard for virtual property */
+ }
+ DUK_ASSERT(curr.e_idx >= 0);
DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx);
@@ -5473,8 +5506,11 @@ void duk_hobject_define_property_helper(duk_context *ctx,
goto fail_not_configurable;
}
- /* curr is accessor -> cannot be in array part */
- DUK_ASSERT(curr.e_idx >= 0 && curr.a_idx < 0);
+ /* curr is accessor -> cannot be in array part. */
+ DUK_ASSERT(curr.a_idx < 0);
+ if (curr.e_idx < 0) {
+ goto fail_virtual; /* safeguard; no virtual accessors now */
+ }
DUK_DDD(DUK_DDDPRINT("convert property to data property"));
@@ -5580,6 +5616,7 @@ void duk_hobject_define_property_helper(duk_context *ctx,
DUK_ASSERT(curr.flags == DUK_PROPDESC_FLAGS_WEC); /* must have been, since in array part */
DUK_ASSERT(!has_set);
DUK_ASSERT(!has_get);
+ DUK_ASSERT(idx_value >= 0); /* must be: if attributes match and we get here the value must differ (otherwise no change) */
tv2 = duk_require_tval(ctx, idx_value);
tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, curr.a_idx);
@@ -5598,15 +5635,45 @@ void duk_hobject_define_property_helper(duk_context *ctx,
DUK_DDD(DUK_DDDPRINT("updating existing property in entry part"));
- /* array case is handled comprehensively above */
- DUK_ASSERT(curr.e_idx >= 0 && curr.a_idx < 0);
+ /* Array case is handled comprehensively above: either in entry
+ * part or a virtual property.
+ */
+ DUK_ASSERT(curr.a_idx < 0);
DUK_DDD(DUK_DDDPRINT("update existing property attributes"));
- DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, curr.e_idx, new_flags);
+ if (curr.e_idx >= 0) {
+ DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, curr.e_idx, new_flags);
+ } else {
+ /* For Array .length the only allowed transition is for .length
+ * to become non-writable.
+ */
+ if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
+ duk_harray *a;
+ a = (duk_harray *) obj;
+ DUK_D(DUK_DPRINT("Object.defineProperty() attribute update for duk_harray .length -> %02lx", (unsigned long) new_flags));
+ DUK_ASSERT_HARRAY_VALID(a);
+ if ((new_flags & DUK_PROPDESC_FLAGS_EC) != (curr.flags & DUK_PROPDESC_FLAGS_EC)) {
+ DUK_D(DUK_DPRINT("Object.defineProperty() attempt to change virtual array .length enumerable or configurable attribute, fail"));
+ goto fail_virtual;
+ }
+ if (new_flags & DUK_PROPDESC_FLAG_WRITABLE) {
+ DUK_HARRAY_SET_LENGTH_WRITABLE(a);
+ } else {
+ DUK_HARRAY_SET_LENGTH_NONWRITABLE(a);
+ }
+ }
+ }
if (has_set) {
duk_hobject *tmp;
+ /* Virtual properties are non-configurable but with a 'force'
+ * flag we might come here so check explicitly for virtual.
+ */
+ if (curr.e_idx < 0) {
+ goto fail_virtual;
+ }
+
DUK_DDD(DUK_DDDPRINT("update existing property setter"));
DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
@@ -5619,6 +5686,10 @@ void duk_hobject_define_property_helper(duk_context *ctx,
if (has_get) {
duk_hobject *tmp;
+ if (curr.e_idx < 0) {
+ goto fail_virtual;
+ }
+
DUK_DDD(DUK_DDDPRINT("update existing property getter"));
DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
@@ -5632,11 +5703,29 @@ void duk_hobject_define_property_helper(duk_context *ctx,
duk_tval *tv1, *tv2;
DUK_DDD(DUK_DDDPRINT("update existing property value"));
- DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
- tv2 = duk_require_tval(ctx, idx_value);
- tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx);
- DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects */
+ if (curr.e_idx >= 0) {
+ DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
+ tv2 = duk_require_tval(ctx, idx_value);
+ tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx);
+ DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects */
+ } else {
+ DUK_ASSERT(curr.a_idx < 0); /* array part case handled comprehensively previously */
+
+ DUK_D(DUK_DPRINT("Object.defineProperty(), value update for virtual property"));
+ /* XXX: Uint8Array and other typed array virtual writes not currently
+ * handled.
+ */
+ if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
+ duk_harray *a;
+ a = (duk_harray *) obj;
+ DUK_D(DUK_DPRINT("Object.defineProperty() value update for duk_harray .length -> %ld", (long) arrlen_new_len));
+ DUK_ASSERT_HARRAY_VALID(a);
+ a->length = arrlen_new_len;
+ } else {
+ goto fail_virtual; /* should not happen */
+ }
+ }
}
/*
@@ -5656,10 +5745,12 @@ void duk_hobject_define_property_helper(duk_context *ctx,
/* [obj key desc value get set curr_value] */
if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) {
- if (arridx_new_array_length > 0) {
- duk_tval *tmp;
- duk_bool_t rc;
+ duk_harray *a;
+
+ a = (duk_harray *) obj;
+ DUK_ASSERT_HARRAY_VALID(a);
+ if (arridx_new_array_length > 0) {
/*
* Note: zero works as a "no update" marker because the new length
* can never be zero after a new property is written.
@@ -5670,31 +5761,19 @@ void duk_hobject_define_property_helper(duk_context *ctx,
DUK_DDD(DUK_DDDPRINT("defineProperty successful, pending array length update to: %ld",
(long) arridx_new_array_length));
- /* Note: reuse 'curr' */
- rc = duk__get_own_propdesc_raw(thr, obj, DUK_HTHREAD_STRING_LENGTH(thr), DUK__NO_ARRAY_INDEX, &curr, 0 /*flags*/); /* don't push value */
- DUK_UNREF(rc);
- DUK_ASSERT(rc != 0);
- DUK_ASSERT(curr.e_idx >= 0);
-
- tmp = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tmp));
- /* no need for decref/incref because value is a number */
- DUK_TVAL_SET_U32(tmp, arridx_new_array_length);
+ a->length = arridx_new_array_length;
}
+
if (key == DUK_HTHREAD_STRING_LENGTH(thr) && arrlen_new_len < arrlen_old_len) {
/*
* E5 Section 15.4.5.1, steps 3.k - 3.n. The order at the end combines
* the error case 3.l.iii and the success case 3.m-3.n.
- *
- * Note: 'length' is always in entries part, so no array abandon issues for
- * 'writable' update.
*/
/* XXX: investigate whether write protect can be handled above, if we
* just update length here while ignoring its protected status
*/
- duk_tval *tmp;
duk_uint32_t result_len;
duk_bool_t rc;
@@ -5706,23 +5785,14 @@ void duk_hobject_define_property_helper(duk_context *ctx,
/* update length (curr points to length, and we assume it's still valid) */
DUK_ASSERT(result_len >= arrlen_new_len && result_len <= arrlen_old_len);
- DUK_ASSERT(curr.e_idx >= 0);
- DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
- tmp = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tmp));
- /* no decref needed for a number */
- DUK_TVAL_SET_U32(tmp, result_len);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tmp));
+ a->length = result_len;
if (pending_write_protect) {
DUK_DDD(DUK_DDDPRINT("setting array length non-writable (pending writability update)"));
- DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(thr->heap, obj, curr.e_idx);
+ DUK_HARRAY_SET_LENGTH_NONWRITABLE(a);
}
- /*
- * XXX: shrink array allocation or entries compaction here?
- */
-
+ /* XXX: shrink array allocation or entries compaction here? */
if (!rc) {
goto fail_array_length_partial;
}
diff --git a/src/duk_hthread.h b/src/duk_hthread.h
index d904c644..4b423d81 100644
--- a/src/duk_hthread.h
+++ b/src/duk_hthread.h
@@ -181,6 +181,16 @@
DUK_ASSERT_CTX_VSSIZE((ctx)); \
} while (0)
+/*
+ * Misc
+ */
+
+/* Fast access to 'this' binding. Assumes there's a call in progress. */
+#define DUK_HTHREAD_THIS_PTR(thr) \
+ (DUK_ASSERT_EXPR((thr) != NULL), \
+ DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), \
+ (thr)->valstack_bottom - 1)
+
/*
* Struct defines
*/
diff --git a/src/duk_hthread_builtins.c b/src/duk_hthread_builtins.c
index 35767598..1f463207 100644
--- a/src/duk_hthread_builtins.c
+++ b/src/duk_hthread_builtins.c
@@ -299,17 +299,17 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
/* Cast converts magic to 16-bit signed value */
magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0 /*def_value*/);
((duk_hnatfunc *) h)->magic = magic;
+ } else if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
+ duk_push_array(ctx);
} else {
- /* XXX: ARRAY_PART for Array prototype? */
-
duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE,
-1); /* no prototype or class yet */
- h = duk_require_hobject(ctx, -1);
- DUK_ASSERT(h != NULL);
}
+ h = duk_require_hobject(ctx, -1);
+ DUK_ASSERT(h != NULL);
DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num);
if (i < DUK_NUM_BUILTINS) {
@@ -327,22 +327,22 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
* expected of an Array instance which are different: writable,
* non-enumerable, non-configurable (E5 Section 15.4.5.2).
*
- * This is currently determined implicitly based on class; there are
- * no attribute flags in the init data.
+ * Because Array .length is virtual, it's not encoded in the init
+ * data separately.
*/
+ DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_ARRAY); /* .length is virtual */
duk_push_int(ctx, len);
duk_xdef_prop_stridx(ctx,
-2,
DUK_STRIDX_LENGTH,
- (class_num == DUK_HOBJECT_CLASS_ARRAY ? /* only Array.prototype matches */
- DUK_PROPDESC_FLAGS_W : DUK_PROPDESC_FLAGS_NONE));
+ DUK_PROPDESC_FLAGS_NONE);
}
/* enable exotic behaviors last */
if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
- DUK_HOBJECT_SET_EXOTIC_ARRAY(h);
+ DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)); /* set by duk_push_array() */
}
if (class_num == DUK_HOBJECT_CLASS_STRING) {
DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h);
@@ -352,11 +352,11 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h));
/* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(h));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(h));
DUK_ASSERT(!DUK_HOBJECT_HAS_COMPFUNC(h));
/* DUK_HOBJECT_FLAG_NATFUNC varies */
DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h));
- DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h)); /* currently, even for Array.prototype */
+ DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h) || class_num == DUK_HOBJECT_CLASS_ARRAY);
/* DUK_HOBJECT_FLAG_STRICT varies */
DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(h) || /* all native functions have NEWENV */
DUK_HOBJECT_HAS_NEWENV(h));
diff --git a/src/duk_internal.h b/src/duk_internal.h
index d28dac5b..222b856f 100644
--- a/src/duk_internal.h
+++ b/src/duk_internal.h
@@ -62,6 +62,7 @@ DUK_USE_USER_DECLARE()
#include "duk_hnatfunc.h"
#include "duk_hbufobj.h"
#include "duk_hthread.h"
+#include "duk_harray.h"
#include "duk_hbuffer.h"
#include "duk_heap.h"
#include "duk_debugger.h"
diff --git a/src/duk_js_call.c b/src/duk_js_call.c
index 5922a6e4..657ea1df 100644
--- a/src/duk_js_call.c
+++ b/src/duk_js_call.c
@@ -435,7 +435,7 @@ DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr,
break;
} else if (DUK_TVAL_IS_OBJECT(tv_func)) {
func = DUK_TVAL_GET_OBJECT(tv_func);
- if (!DUK_HOBJECT_HAS_BOUND(func)) {
+ if (!DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
/* Normal non-bound function. */
break;
}
@@ -506,7 +506,7 @@ DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr,
if (DUK_TVAL_IS_OBJECT(tv_func)) {
func = DUK_TVAL_GET_OBJECT(tv_func);
DUK_ASSERT(func != NULL);
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func) ||
DUK_HOBJECT_HAS_NATFUNC(func));
}
@@ -568,7 +568,7 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func)
DUK_ASSERT(thr != NULL);
DUK_ASSERT(func != NULL);
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func)); /* bound chain resolved */
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound chain resolved */
DUK_ASSERT(thr->callstack_top >= 1);
if (DUK_HOBJECT_HAS_STRICT(func)) {
@@ -734,7 +734,7 @@ DUK_LOCAL duk_hobject *duk__nonbound_func_lookup(duk_context *ctx,
if (!DUK_HOBJECT_IS_CALLABLE(func)) {
goto not_callable_error;
}
- if (DUK_HOBJECT_HAS_BOUND(func)) {
+ if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
duk__handle_bound_chain_for_call(thr, idx_func, out_num_stack_args, call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL);
/* The final object may be a normal function or a lightfunc.
@@ -756,7 +756,7 @@ DUK_LOCAL duk_hobject *duk__nonbound_func_lookup(duk_context *ctx,
DUK_ASSERT((DUK_TVAL_IS_OBJECT(tv_func) && DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(tv_func))) ||
DUK_TVAL_IS_LIGHTFUNC(tv_func));
- DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));
+ DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) ||
DUK_HOBJECT_IS_NATFUNC(func)));
@@ -1310,7 +1310,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
DUK_TVAL_SET_TVAL(&tv_func_copy, tv_func);
tv_func = &tv_func_copy; /* local copy to avoid relookups */
- DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));
+ DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) ||
DUK_HOBJECT_IS_NATFUNC(func)));
@@ -1348,7 +1348,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
thr->callstack_top++;
DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
DUK_ASSERT(thr->valstack_top > thr->valstack_bottom); /* at least effective 'this' */
- DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));
+ DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
act->flags = 0;
@@ -1455,7 +1455,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
* Delayed creation (on demand) is handled in duk_js_var.c.
*/
- DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func)); /* bound function chain has already been resolved */
+ DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound function chain has already been resolved */
if (DUK_LIKELY(func != NULL)) {
if (DUK_LIKELY(DUK_HOBJECT_HAS_NEWENV(func))) {
@@ -2406,7 +2406,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
/* XXX: tv_func is not actually needed */
DUK_ASSERT(func != NULL);
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(func));
duk__coerce_effective_this_binding(thr, func, idx_func + 1);
@@ -2482,7 +2482,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
/* 'act' already set above */
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(func));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
@@ -2603,7 +2603,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
thr->callstack_top++;
DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(func));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
@@ -2654,7 +2654,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
/* XXX: unify handling with native call. */
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func)); /* bound function chain has already been resolved */
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound function chain has already been resolved */
if (!DUK_HOBJECT_HAS_NEWENV(func)) {
/* use existing env (e.g. for non-strict eval); cannot have
diff --git a/src/duk_js_compiler.c b/src/duk_js_compiler.c
index 93561437..e06a2516 100644
--- a/src/duk_js_compiler.c
+++ b/src/duk_js_compiler.c
@@ -7763,6 +7763,7 @@ DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer
thr->compile_ctx = prev_ctx; /* must restore reliably before returning */
if (safe_rc != DUK_EXEC_SUCCESS) {
+ DUK_D(DUK_DPRINT("compilation failed: %!T", duk_get_tval(ctx, -1)));
duk_throw(ctx);
}
diff --git a/src/duk_js_executor.c b/src/duk_js_executor.c
index 9c727a4d..be493cbb 100644
--- a/src/duk_js_executor.c
+++ b/src/duk_js_executor.c
@@ -3445,7 +3445,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_func));
obj_func = DUK_TVAL_GET_OBJECT(tv_func);
DUK_ASSERT(obj_func != NULL);
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(obj_func));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(obj_func));
/*
* Other cases, use C recursion.
diff --git a/src/duk_js_ops.c b/src/duk_js_ops.c
index a1e9d9d6..45e70b47 100644
--- a/src/duk_js_ops.c
+++ b/src/duk_js_ops.c
@@ -1084,7 +1084,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_
DUK_ERROR_TYPE(thr, "invalid instanceof rval");
}
- if (!DUK_HOBJECT_HAS_BOUND(func)) {
+ if (!DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
break;
}
@@ -1107,7 +1107,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_
* to execute E5 Section 15.3.5.3.
*/
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func));
/* [ ... lval rval(func) ] */
diff --git a/src/duk_js_var.c b/src/duk_js_var.c
index 75247096..2fa07ac9 100644
--- a/src/duk_js_var.c
+++ b/src/duk_js_var.c
@@ -176,7 +176,7 @@ void duk_js_push_closure(duk_hthread *thr,
DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj));
DUK_HOBJECT_SET_CONSTRUCTABLE(&fun_clos->obj); /* Note: not set in template (has no "prototype") */
DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(&fun_clos->obj));
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&fun_clos->obj));
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&fun_clos->obj));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&fun_clos->obj));
DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&fun_clos->obj));
DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&fun_clos->obj));
@@ -346,7 +346,7 @@ void duk_js_push_closure(duk_hthread *thr,
/* [ ... closure template formals ] */
DUK_ASSERT(duk_has_prop_stridx(ctx, -1, DUK_STRIDX_LENGTH));
DUK_ASSERT(duk_get_length(ctx, -1) <= DUK_UINT_MAX); /* formal arg limits */
- len_value = (duk_uint_t) duk_get_length(ctx, -1);
+ len_value = (duk_uint_t) duk_get_length(ctx, -1); /* could access duk_harray directly, not important */
} else {
/* XXX: what to do if _Formals is not empty but compiler has
* optimized it away -- read length from an explicit property
@@ -529,7 +529,7 @@ void duk_js_init_activation_environment_records_delayed(duk_hthread *thr,
func = DUK_ACT_GET_FUNC(act);
DUK_ASSERT(func != NULL);
- DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func)); /* bound functions are never in act 'func' */
+ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound functions are never in act 'func' */
/*
* Delayed initialization only occurs for 'NEWENV' functions.
diff --git a/src/extract_caseconv.py b/src/extract_caseconv.py
index 1552fbb5..a5a67f17 100644
--- a/src/extract_caseconv.py
+++ b/src/extract_caseconv.py
@@ -442,4 +442,3 @@ def main():
if __name__ == '__main__':
main()
-
diff --git a/src/genbuildparams.py b/src/genbuildparams.py
index 6faa6765..d78ffe0e 100644
--- a/src/genbuildparams.py
+++ b/src/genbuildparams.py
@@ -42,4 +42,3 @@ if __name__ == '__main__':
f.write('\n')
f.write('#endif /* DUK_BUILDPARAMS_H_INCLUDED */\n')
f.close()
-
diff --git a/src/genbuiltins.py b/src/genbuiltins.py
index 1494adde..02ec5356 100644
--- a/src/genbuiltins.py
+++ b/src/genbuiltins.py
@@ -2130,6 +2130,8 @@ def rom_emit_object_initializer_types_and_macros(genc):
# RAM structure which has no dynamic or variable size parts.
genc.emitLine('typedef struct duk_romobj duk_romobj; ' + \
'struct duk_romobj { duk_hobject hdr; };')
+ genc.emitLine('typedef struct duk_romarr duk_romarr; ' + \
+ 'struct duk_romarr { duk_harray hdr; };')
genc.emitLine('typedef struct duk_romfun duk_romfun; ' + \
'struct duk_romfun { duk_hnatfunc hdr; };')
@@ -2150,14 +2152,18 @@ def rom_emit_object_initializer_types_and_macros(genc):
#genc.emitLine('#endif')
genc.emitLine('#define DUK__ROMOBJ_INIT(heaphdr_flags,refcount,props,props_enc16,iproto,iproto_enc16,esize,enext,asize,hsize) \\')
genc.emitLine('\t{ { { (heaphdr_flags), (refcount), 0, 0, (props_enc16) }, (iproto_enc16), (esize), (enext), (asize) } }')
+ genc.emitLine('#define DUK__ROMARR_INIT(heaphdr_flags,refcount,props,props_enc16,iproto,iproto_enc16,esize,enext,asize,hsize,length) \\')
+ genc.emitLine('\t{ { { { (heaphdr_flags), (refcount), 0, 0, (props_enc16) }, (iproto_enc16), (esize), (enext), (asize) }, (length), 0 /*length_nonwritable*/ } }')
genc.emitLine('#define DUK__ROMFUN_INIT(heaphdr_flags,refcount,props,props_enc16,iproto,iproto_enc16,esize,enext,asize,hsize,nativefunc,nargs,magic) \\')
genc.emitLine('\t{ { { { (heaphdr_flags), (refcount), 0, 0, (props_enc16) }, (iproto_enc16), (esize), (enext), (asize) }, (nativefunc), (duk_int16_t) (nargs), (duk_int16_t) (magic) } }')
- genc.emitLine('#else')
+ genc.emitLine('#else /* DUK_USE_HEAPPTR16 */')
genc.emitLine('#define DUK__ROMOBJ_INIT(heaphdr_flags,refcount,props,props_enc16,iproto,iproto_enc16,esize,enext,asize,hsize) \\')
genc.emitLine('\t{ { { (heaphdr_flags), (refcount), NULL, NULL }, (duk_uint8_t *) DUK_LOSE_CONST(props), (duk_hobject *) DUK_LOSE_CONST(iproto), (esize), (enext), (asize), (hsize) } }')
+ genc.emitLine('#define DUK__ROMARR_INIT(heaphdr_flags,refcount,props,props_enc16,iproto,iproto_enc16,esize,enext,asize,hsize,length) \\')
+ genc.emitLine('\t{ { { { (heaphdr_flags), (refcount), NULL, NULL }, (duk_uint8_t *) DUK_LOSE_CONST(props), (duk_hobject *) DUK_LOSE_CONST(iproto), (esize), (enext), (asize), (hsize) }, (length), 0 /*length_nonwritable*/ } }')
genc.emitLine('#define DUK__ROMFUN_INIT(heaphdr_flags,refcount,props,props_enc16,iproto,iproto_enc16,esize,enext,asize,hsize,nativefunc,nargs,magic) \\')
genc.emitLine('\t{ { { { (heaphdr_flags), (refcount), NULL, NULL }, (duk_uint8_t *) DUK_LOSE_CONST(props), (duk_hobject *) DUK_LOSE_CONST(iproto), (esize), (enext), (asize), (hsize) }, (nativefunc), (duk_int16_t) (nargs), (duk_int16_t) (magic) } }')
- genc.emitLine('#endif')
+ genc.emitLine('#endif /* DUK_USE_HEAPPTR16 */')
# Emit duk_tval structs. This gets a bit messier with packed/unpacked
# duk_tval, endianness variants, pointer sizes, etc.
@@ -2418,6 +2424,8 @@ def rom_emit_objects(genc, meta, bi_str_map):
# See commentary above for duk_prop_%d forward declarations.
if obj.get('callable', False):
genc.emitLine('DUK_EXTERNAL_DECL const duk_romfun duk_obj_%d;' % idx)
+ elif obj.get('class') == 'Array':
+ genc.emitLine('DUK_EXTERNAL_DECL const duk_romarr duk_obj_%d;' % idx)
else:
genc.emitLine('DUK_EXTERNAL_DECL const duk_romobj duk_obj_%d;' % idx)
genc.emitLine('')
@@ -2433,6 +2441,8 @@ def rom_emit_objects(genc, meta, bi_str_map):
if isfunc:
tmp = 'DUK_EXTERNAL const duk_romfun duk_obj_%d = ' % idx
+ elif obj.get('class') == 'Array':
+ tmp = 'DUK_EXTERNAL const duk_romarr duk_obj_%d = ' % idx
else:
tmp = 'DUK_EXTERNAL const duk_romobj duk_obj_%d = ' % idx
@@ -2443,6 +2453,8 @@ def rom_emit_objects(genc, meta, bi_str_map):
flags.append('DUK_HOBJECT_FLAG_NEWENV')
if obj.get('constructable', False):
flags.append('DUK_HOBJECT_FLAG_CONSTRUCTABLE')
+ if obj.get('class') == 'Array':
+ flags.append('DUK_HOBJECT_FLAG_EXOTIC_ARRAY')
flags.append('DUK_HOBJECT_CLASS_AS_FLAGS(%d)' % class_to_number(obj['class'])) # XXX: use constant, not number
refcount = 1 # refcount is faked to be always 1
@@ -2484,6 +2496,11 @@ def rom_emit_objects(genc, meta, bi_str_map):
('|'.join(flags), refcount, props, props_enc16, \
iproto, iproto_enc16, e_size, e_next, a_size, h_size, \
nativefunc, nargs, magic)
+ elif obj.get('class') == 'Array':
+ arrlen = 0
+ tmp += 'DUK__ROMARR_INIT(%s,%d,%s,%d,%s,%d,%d,%d,%d,%d,%d);' % \
+ ('|'.join(flags), refcount, props, props_enc16, \
+ iproto, iproto_enc16, e_size, e_next, a_size, h_size, arrlen)
else:
tmp += 'DUK__ROMOBJ_INIT(%s,%d,%s,%d,%s,%d,%d,%d,%d,%d);' % \
('|'.join(flags), refcount, props, props_enc16, \
diff --git a/src/genhashsizes.py b/src/genhashsizes.py
index ba34b7fc..867e2856 100644
--- a/src/genhashsizes.py
+++ b/src/genhashsizes.py
@@ -122,4 +122,3 @@ while True:
if is_prime(i):
print 'highest 32-bit prime is: %d (0x%08x)' % (i, i)
break
-
diff --git a/src/gennumdigits.py b/src/gennumdigits.py
index f259f2e5..acd7af6a 100644
--- a/src/gennumdigits.py
+++ b/src/gennumdigits.py
@@ -42,5 +42,3 @@ for radix in xrange(2, 36+1):
print repr(digits_table)
print repr(limits_table)
-
-
diff --git a/src/genobjsizereport.py b/src/genobjsizereport.py
index 2c98254b..0d0e5e62 100644
--- a/src/genobjsizereport.py
+++ b/src/genobjsizereport.py
@@ -71,4 +71,3 @@ if __name__ == '__main__':
# $ python genobjsizereport.py *.o
main()
-
diff --git a/tests/api/test-def-prop-virtual.c b/tests/api/test-def-prop-virtual.c
new file mode 100644
index 00000000..c9c40bfd
--- /dev/null
+++ b/tests/api/test-def-prop-virtual.c
@@ -0,0 +1,156 @@
+/*
+ * Test DUK_DEFPROP_FORCE for virtual properties.
+ */
+
+/*===
+*** test_array_length_enumerable_noforce (duk_safe_call)
+set array .length enumerable
+==> rc=1, result='TypeError: not configurable'
+*** test_array_length_enumerable_force (duk_safe_call)
+set array .length enumerable
+==> rc=1, result='TypeError: property is virtual'
+*** test_array_length_configurable_noforce (duk_safe_call)
+set array .length configurable
+==> rc=1, result='TypeError: not configurable'
+*** test_array_length_configurable_force (duk_safe_call)
+set array .length configurable
+==> rc=1, result='TypeError: property is virtual'
+*** test_array_length_overwrite_same_noforce (duk_safe_call)
+["foo","bar","quux"]
+final top: 0
+==> rc=0, result='undefined'
+*** test_array_length_overwrite_same_force (duk_safe_call)
+["foo","bar","quux"]
+final top: 0
+==> rc=0, result='undefined'
+*** test_array_length_overwrite_bigger_noforce (duk_safe_call)
+==> rc=1, result='TypeError: not configurable'
+*** test_array_length_overwrite_bigger_force (duk_safe_call)
+["foo","bar","quux",null,null]
+final top: 0
+==> rc=0, result='undefined'
+*** test_array_length_overwrite_smaller_noforce (duk_safe_call)
+==> rc=1, result='TypeError: array length non-writable'
+*** test_array_length_overwrite_smaller_force (duk_safe_call)
+["foo"]
+final top: 0
+==> rc=0, result='undefined'
+===*/
+
+static duk_ret_t test__array_length_enumerable(duk_context *ctx, int force) {
+ duk_push_array(ctx);
+
+ duk_push_string(ctx, "length");
+ printf("set array .length enumerable\n");
+ fflush(stdout);
+ duk_def_prop(ctx, -2, DUK_DEFPROP_SET_ENUMERABLE | (force ? DUK_DEFPROP_FORCE : 0));
+
+ duk_eval_string(ctx,
+ "(function (v) { print(Duktape.enc('jx', Object.getOwnPropertyDescriptor(v, 'length' ))); })");
+ duk_dup(ctx, -2);
+ duk_call(ctx, 1);
+
+ duk_pop_2(ctx);
+ printf("final top: %ld\n", (long) duk_get_top(ctx));
+ return 0;
+}
+
+static duk_ret_t test_array_length_enumerable_noforce(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__array_length_enumerable(ctx, 0);
+}
+static duk_ret_t test_array_length_enumerable_force(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__array_length_enumerable(ctx, 1);
+}
+
+static duk_ret_t test__array_length_configurable(duk_context *ctx, int force) {
+ duk_push_array(ctx);
+
+ duk_push_string(ctx, "length");
+ printf("set array .length configurable\n");
+ fflush(stdout);
+ duk_def_prop(ctx, -2, DUK_DEFPROP_SET_CONFIGURABLE | (force ? DUK_DEFPROP_FORCE : 0));
+
+ duk_eval_string(ctx,
+ "(function (v) { print(Duktape.enc('jx', Object.getOwnPropertyDescriptor(v, 'length' ))); })");
+ duk_dup(ctx, -2);
+ duk_call(ctx, 1);
+
+ duk_pop_2(ctx);
+ printf("final top: %ld\n", (long) duk_get_top(ctx));
+ return 0;
+}
+
+static duk_ret_t test_array_length_configurable_noforce(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__array_length_configurable(ctx, 0);
+}
+static duk_ret_t test_array_length_configurable_force(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__array_length_configurable(ctx, 1);
+}
+
+static duk_ret_t test__length_overwrite(duk_context *ctx, int force, int new_len) {
+ duk_eval_string(ctx,
+ "(function () {\n"
+ " var arr = [ 'foo', 'bar', 'quux' ];\n"
+ " Object.defineProperty(arr, 'length', { writable: false });\n"
+ " return arr;\n"
+ "})()\n");
+
+ /* Array .length is not writable; with DUK_DEFPROP_FORCE we can still
+ * write it.
+ */
+ duk_push_string(ctx, "length");
+ duk_push_int(ctx, new_len);
+ duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | (force ? DUK_DEFPROP_FORCE : 0));
+
+ duk_eval_string(ctx,
+ "(function (v) { print(Duktape.enc('jx', v)); })");
+ duk_dup(ctx, -2);
+ duk_call(ctx, 1);
+
+ duk_pop_2(ctx);
+
+ printf("final top: %ld\n", (long) duk_get_top(ctx));
+ return 0;
+}
+
+static duk_ret_t test_array_length_overwrite_same_noforce(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__length_overwrite(ctx, 0, 3);
+}
+static duk_ret_t test_array_length_overwrite_same_force(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__length_overwrite(ctx, 1, 3);
+}
+static duk_ret_t test_array_length_overwrite_bigger_noforce(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__length_overwrite(ctx, 0, 5);
+}
+static duk_ret_t test_array_length_overwrite_bigger_force(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__length_overwrite(ctx, 1, 5);
+}
+static duk_ret_t test_array_length_overwrite_smaller_noforce(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__length_overwrite(ctx, 0, 1);
+}
+static duk_ret_t test_array_length_overwrite_smaller_force(duk_context *ctx, void *udata) {
+ (void) udata;
+ return test__length_overwrite(ctx, 1, 1);
+}
+
+void test(duk_context *ctx) {
+ TEST_SAFE_CALL(test_array_length_enumerable_noforce);
+ TEST_SAFE_CALL(test_array_length_enumerable_force);
+ TEST_SAFE_CALL(test_array_length_configurable_noforce);
+ TEST_SAFE_CALL(test_array_length_configurable_force);
+ TEST_SAFE_CALL(test_array_length_overwrite_same_noforce);
+ TEST_SAFE_CALL(test_array_length_overwrite_same_force);
+ TEST_SAFE_CALL(test_array_length_overwrite_bigger_noforce);
+ TEST_SAFE_CALL(test_array_length_overwrite_bigger_force);
+ TEST_SAFE_CALL(test_array_length_overwrite_smaller_noforce);
+ TEST_SAFE_CALL(test_array_length_overwrite_smaller_force);
+}
diff --git a/tests/api/test-def-prop.c b/tests/api/test-def-prop.c
index 79f153e3..a3ead7e6 100644
--- a/tests/api/test-def-prop.c
+++ b/tests/api/test-def-prop.c
@@ -162,38 +162,38 @@ final top: 1
final top: 1
==> rc=0, result='undefined'
*** test_fail_array_smaller (duk_safe_call)
+"length" {value:4,writable:true,enumerable:false,configurable:false} no-getter no-setter
"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter
"1" {value:"bar",writable:true,enumerable:true,configurable:true} no-getter no-setter
"2" {value:"quux",writable:true,enumerable:true,configurable:false} no-getter no-setter
"3" {value:"baz",writable:true,enumerable:true,configurable:true} no-getter no-setter
-"length" {value:4,writable:true,enumerable:false,configurable:false} no-getter no-setter
==> rc=1, result='TypeError: array length write failed'
*** test_force_array_smaller (duk_safe_call)
+"length" {value:4,writable:true,enumerable:false,configurable:false} no-getter no-setter
"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter
"1" {value:"bar",writable:true,enumerable:true,configurable:true} no-getter no-setter
"2" {value:"quux",writable:true,enumerable:true,configurable:false} no-getter no-setter
"3" {value:"baz",writable:true,enumerable:true,configurable:true} no-getter no-setter
-"length" {value:4,writable:true,enumerable:false,configurable:false} no-getter no-setter
-"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter
"length" {value:1,writable:true,enumerable:false,configurable:false} no-getter no-setter
+"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter
json: ["foo"]
final top: 1
==> rc=0, result='undefined'
*** test_fail_array_smaller_nonwritablelength (duk_safe_call)
+"length" {value:4,writable:false,enumerable:false,configurable:false} no-getter no-setter
"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter
"1" {value:"bar",writable:true,enumerable:true,configurable:true} no-getter no-setter
"2" {value:"quux",writable:true,enumerable:true,configurable:false} no-getter no-setter
"3" {value:"baz",writable:true,enumerable:true,configurable:true} no-getter no-setter
-"length" {value:4,writable:false,enumerable:false,configurable:false} no-getter no-setter
==> rc=1, result='TypeError: array length non-writable'
*** test_force_array_smaller_nonwritablelength (duk_safe_call)
+"length" {value:4,writable:false,enumerable:false,configurable:false} no-getter no-setter
"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter
"1" {value:"bar",writable:true,enumerable:true,configurable:true} no-getter no-setter
"2" {value:"quux",writable:true,enumerable:true,configurable:false} no-getter no-setter
"3" {value:"baz",writable:true,enumerable:true,configurable:true} no-getter no-setter
-"length" {value:4,writable:false,enumerable:false,configurable:false} no-getter no-setter
-"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter
"length" {value:1,writable:false,enumerable:false,configurable:false} no-getter no-setter
+"0" {value:"foo",writable:true,enumerable:true,configurable:true} no-getter no-setter
json: ["foo"]
final top: 1
==> rc=0, result='undefined'
@@ -927,6 +927,11 @@ static duk_ret_t test_force_accessor2data(duk_context *ctx, void *udata) {
/* Make array smaller, ignoring non-configurable elements. */
static duk_ret_t test_force_array_smaller_raw(duk_context *ctx, duk_bool_t length_writable, duk_bool_t forced) {
+ /* The array will have a non-configurable element (not very common)
+ * which causes the array part to be abandoned and affects enumeration
+ * order; in Duktape 2.0 'length' will enumerate before the properties
+ * because .length is virtual.
+ */
if (length_writable) {
duk_eval_string(ctx,
"(function () {\n"
diff --git a/tests/ecmascript/test-bi-json-enc-fastpath.js b/tests/ecmascript/test-bi-json-enc-fastpath.js
index 29d11756..f2df48b0 100644
--- a/tests/ecmascript/test-bi-json-enc-fastpath.js
+++ b/tests/ecmascript/test-bi-json-enc-fastpath.js
@@ -59,12 +59,13 @@ top level value test
6 "foo"
7 {"foo":123}
8 ["foo"]
-9 undefined
-10 "1970-01-01T00:00:00.123Z"
-11 undefined
+9 [null,null,null,null,null,null,null,null,null,null]
+10 undefined
+11 "1970-01-01T00:00:00.123Z"
12 undefined
13 undefined
-14 {"type":"Buffer","data":[65,66,67,68,69,70,71,72]}
+14 undefined
+15 {"type":"Buffer","data":[65,66,67,68,69,70,71,72]}
===*/
/* Top level value */
@@ -73,6 +74,7 @@ function jsonStringifyFastPathTopLevelValueTest() {
var values = [
undefined, null, true, false, 123, 123.456, 'foo',
{ foo: 123 }, [ 'foo' ],
+ new Array(10), // .length is larger than underlying array part length
function myfunc() {},
new Date(123),
Duktape.dec('hex', 'deadbeef'),
diff --git a/tests/ecmascript/test-dev-duk-harray.js b/tests/ecmascript/test-dev-duk-harray.js
new file mode 100644
index 00000000..3ace1091
--- /dev/null
+++ b/tests/ecmascript/test-dev-duk-harray.js
@@ -0,0 +1,423 @@
+/*
+ * Dev testcases when adding duk_harray
+ *
+ * https://github.com/svaarala/duktape/pull/703
+ */
+
+/*===
+array literal test
+3
+1,2,3
+===*/
+
+function arrayLiteralTest() {
+ var arr;
+
+ arr = [ 1, 2, 3 ];
+ print(arr.length);
+ print(arr);
+}
+
+try {
+ print('array literal test');
+ arrayLiteralTest();
+} catch (e) {
+ print(e.stack || e);
+}
+
+/*===
+array constructor test
+10 [null,null,null,null,null,null,null,null,null,null]
+10 [null,null,null,null,null,"foo",null,null,null,null]
+6 [1,2,3,"foo","bar","quux"]
+6 [1,2,3,"foo","bar","baz"]
+256
+===*/
+
+function arrayConstructorTest() {
+ var arr;
+ var i;
+
+ // Create Array using numeric argument
+ arr = new Array(10);
+ print(arr.length, JSON.stringify(arr));
+ arr[5] = 'foo';
+ print(arr.length, JSON.stringify(arr));
+ for (i = 0; i < 256; i++) {
+ arr = new Array(i);
+ if (arr.length !== i) {
+ throw new Error('failed for index ' + i);
+ }
+ }
+
+ // Create Array from initializer arguments
+ arr = new Array(1, 2, 3, 'foo', 'bar', 'quux');
+ print(arr.length, JSON.stringify(arr));
+ arr[5] = 'baz';
+ print(arr.length, JSON.stringify(arr));
+
+ // Create Array with a lot of initializer arguments (more than value stack reserve).
+ arr = new Array(
+ // 256
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+ );
+ print(arr.length);
+}
+
+try {
+ print('array constructor test');
+ arrayConstructorTest();
+} catch (e) {
+ print(e.stack || e);
+}
+
+/*===
+array enumeration test
+0,1,2,3,length
+0,1,2,3
+0,1,2,3,4,length
+0,1,2,3,4
+0,1,2,3,4,length,foo
+0,1,2,3,4,foo
+===*/
+
+function arrayEnumerationTest() {
+ var arr;
+
+ arr = [ 1,2,3,4 ];
+ print(Object.getOwnPropertyNames(arr));
+ print(Object.keys(arr));
+ arr[4] = 5;
+ print(Object.getOwnPropertyNames(arr));
+ print(Object.keys(arr));
+ arr.foo = 'bar';
+ print(Object.getOwnPropertyNames(arr));
+ print(Object.keys(arr));
+}
+
+try {
+ print('array enumeration test');
+ arrayEnumerationTest();
+} catch (e) {
+ print(e.stack || e);
+}
+
+/*===
+array .length property descriptor test
+6 true false false
+6 [1,2,3,4,5,6]
+5 [1,2,3,4,5]
+5 [1,2,3,4,5]
+5 [1,2,3,4,5]
+5 false false false
+defineProperty
+5 [1,2,3,4,5]
+5 [1,2,3,4,5]
+TypeError: array length non-writable
+TypeError: not configurable
+TypeError: not configurable
+TypeError: not configurable
+TypeError: not configurable
+2 [null,null]
+===*/
+
+function arrayLengthPropertyDescriptorTest() {
+ var arr, pd;
+
+ // Make .length non-writable.
+ arr = [ 1, 2, 3, 4, 5, 6 ];
+ pd = Object.getOwnPropertyDescriptor(arr, 'length');
+ print(pd.value, pd.writable, pd.enumerable, pd.configurable);
+ print(arr.length, JSON.stringify(arr));
+ arr.length = 5;
+ print(arr.length, JSON.stringify(arr));
+ Object.defineProperty(arr, 'length', { writable: false });
+ print(arr.length, JSON.stringify(arr));
+ arr.length = 4;
+ print(arr.length, JSON.stringify(arr));
+ pd = Object.getOwnPropertyDescriptor(arr, 'length');
+ print(pd.value, pd.writable, pd.enumerable, pd.configurable);
+ print('defineProperty');
+ try {
+ Object.defineProperty(arr, 'length', { value: 5 }); // no change, ok
+ print(arr.length, JSON.stringify(arr));
+ } catch (e) {
+ print(e.stack || e);
+ }
+ try {
+ Object.defineProperty(arr, 'length', { value: 5, writable: false, enumerable: false, configurable: false }); // no change, ok
+ print(arr.length, JSON.stringify(arr));
+ } catch (e) {
+ print(e.stack || e);
+ }
+ try {
+ Object.defineProperty(arr, 'length', { value: 4 }); // Not OK
+ print('never here');
+ print(arr.length, JSON.stringify(arr));
+ } catch (e) {
+ //print(e.stack);
+ print(e);
+ }
+ try {
+ Object.defineProperty(arr, 'length', { writable: true }); // Not OK
+ print('never here');
+ print(arr.length, JSON.stringify(arr));
+ } catch (e) {
+ //print(e.stack);
+ print(e);
+ }
+ try {
+ Object.defineProperty(arr, 'length', { enumerable: true }); // Not OK
+ print('never here');
+ print(arr.length, JSON.stringify(arr));
+ } catch (e) {
+ //print(e.stack);
+ print(e);
+ }
+ try {
+ Object.defineProperty(arr, 'length', { configurable: true }); // Not OK
+ print('never here');
+ print(arr.length, JSON.stringify(arr));
+ } catch (e) {
+ //print(e.stack);
+ print(e);
+ }
+ try {
+ Object.defineProperty(arr, 'length', { set: function () {}, get: function () {} }); // Not OK
+ print('never here');
+ print(arr.length, JSON.stringify(arr));
+ } catch (e) {
+ //print(e.stack);
+ print(e);
+ }
+
+ // Set .length using Object.defineProperty().
+ arr = [];
+ Object.defineProperty(arr, 'length', { value: 2 });
+ print(arr.length, JSON.stringify(arr));
+}
+
+try {
+ print('array .length property descriptor test');
+ arrayLengthPropertyDescriptorTest();
+} catch (e) {
+ print(e.stack || e);
+}
+
+/*===
+array length test
+false
+TypeError: not configurable
+4
+4
+true
+0,1,2,length
+true
+
+1,2,3,4,5,6,7,8,9,10
+1,2,3,4,5,foo,7,8,9,10
+TypeError: not writable
+1,2,3,4,5,foo,7,8,9,10
+===*/
+
+function arrayLengthTest() {
+ var arr;
+ var obj;
+
+ // Attempt to delete Array .length fails; result false in non-strict
+ // mode, error in strict mode.
+ arr = [ 1, 2, 3, 4, 5 ];
+ print(delete arr.length);
+ try {
+ (function deleteTest() { 'use strict'; delete arr.length; })();
+ } catch (e) {
+ //print(e.stack);
+ print(e);
+ }
+
+ // Attempt to write a non-writable Array .length.
+ arr = [ 1, 2, 3, 4 ];
+ Object.defineProperty(arr, 'length', { writable: false });
+ print(arr.length);
+ arr.length = 2;
+ print(arr.length);
+
+ // Property existence for Array .length.
+ arr = [ 1, 2, 3 ];
+ print('length' in arr);
+ print(Object.getOwnPropertyNames(arr));
+ obj = Object.create(arr); // inherit from 'arr'
+ print('length' in obj);
+ print(Object.getOwnPropertyNames(obj));
+
+ // Writing to an Array when .length is write protected.
+ arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ Object.defineProperty(arr, 'length', { writable: false });
+ print(arr);
+ arr[5] = 'foo'; // OK, length not changed
+ print(arr);
+ try {
+ arr[10] = 'foo';
+ } catch (e) {
+ //print(e.stack);
+ print(e);
+ }
+ print(arr);
+}
+
+try {
+ print('array length test');
+ arrayLengthTest();
+} catch (e) {
+ print(e.stack || e);
+}
+
+/*===
+enumeration order for sparse arrays
+0,1,2,length
+0,1,2
+0,1,2,length,foo
+0,1,2,foo
+length,0,1,2,foo
+0,1,2,foo
+length,0,1,2,foo,bar
+0,1,2,foo,bar
+===*/
+
+/* The enumeration order for sparse arrays (= arrays whose array part has been
+ * abandoned) changes with the introduction of duk_harray. The enumeration
+ * order is: (1) array part, (2) virtual .length property, (3) entry part.
+ * The virtual .length only comes into play when also non-enumerable own
+ * properties are listed, e.g. Object.getOwnPropertyNames() is used.
+ *
+ * For a three-member dense array this would result in the enumeration order
+ * 0,1,2,length. When that array becomes sparse, Duktape 1.x would still
+ * enumerate it as 0,1,2,length because .length was stored explicitly and
+ * could thus be moved to the entry part. Duktape 2.x has a virtual Array
+ * .length and the sparse array will thus enumerate as length,0,1,2.
+ *
+ * ((o) Duktape 1.5.0 (v1.4.0-421-g8e90d3d-dirty)
+ * duk> a = [1,2,3]; a[100] = 1; a.length = 3;
+ * = 3
+ * duk> Object.getOwnPropertyNames(a)
+ * = 0,1,2,length
+ *
+ * ((o) Duktape [linenoise] 1.99.99 (v1.5.0-168-g86373ab-dirty)
+ * duk> a = [1,2,3]; a[100] = 1; a.length = 3;
+ * = 3
+ * duk> Object.getOwnPropertyNames(a)
+ * = length,0,1,2
+ */
+
+function sparseArrayEnumTest() {
+ var arr;
+
+ arr = [ 1, 2, 3 ];
+ print(Object.getOwnPropertyNames(arr));
+ print(Object.keys(arr));
+
+ arr.foo = 'bar';
+ print(Object.getOwnPropertyNames(arr));
+ print(Object.keys(arr));
+
+ arr[100] = 1; arr.length = 3; // make sparse
+ print(Object.getOwnPropertyNames(arr));
+ print(Object.keys(arr));
+
+ arr.bar = 'quux';
+ print(Object.getOwnPropertyNames(arr));
+ print(Object.keys(arr));
+}
+
+try {
+ print('enumeration order for sparse arrays');
+ sparseArrayEnumTest();
+} catch (e) {
+ print(e.stack || e);
+}
+
+/*===
+Array.prototype test
+[object Array]
+[object Array]
+0 foo bar quux undefined
+0 foo undefined undefined undefined
+0 undefined undefined undefined undefined
+dummy
+undefined
+===*/
+
+function arrayPrototypeTest() {
+ var arr;
+
+ // Array.prototype is an Array instance.
+ // This is useful to check for ROM built-ins.
+ print(Object.prototype.toString.call([]));
+ print(Object.prototype.toString.call(Array.prototype));
+
+ // It can also be written to (unusual but required).
+ Array.prototype.push('foo', 'bar', 'quux');
+ arr = [];
+ print(arr.length, arr[0], arr[1], arr[2], arr[3]);
+ Array.prototype.length = 1;
+ print(arr.length, arr[0], arr[1], arr[2], arr[3]);
+ Array.prototype.length = 0;
+ print(arr.length, arr[0], arr[1], arr[2], arr[3]);
+
+ // Array.prototype is dense by default; make it sparse.
+ //print(Duktape.enc('jx', Duktape.info(Array.prototype)));
+ Array.prototype[1000] = 'dummy';
+ //print(Duktape.enc('jx', Duktape.info(Array.prototype)));
+ arr = [];
+ print(arr[1000]);
+ Array.prototype.length = 0;
+ //print(Duktape.enc('jx', Duktape.info(Array.prototype)));
+ print(arr[1000]);
+}
+
+try {
+ print('Array.prototype test');
+ arrayPrototypeTest();
+} catch (e) {
+ print(e.stack || e);
+}
+
+/*===
+100
+1000000000
+===*/
+
+function arrayMiscTest() {
+ var arr;
+
+ // Creating an Array with a small count creates a dense array
+ // with a preallocated array part.
+ arr = new Array(100);
+ print(arr.length);
+
+ // Creating an Array with a huge count creates a dense array
+ // but with no preallocated array part.
+ arr = new Array(1e9);
+ print(arr.length);
+}
+
+try {
+ arrayMiscTest();
+} catch (e) {
+ print(e.stack || e);
+}
diff --git a/tests/ecmascript/test-dev-enum-abandoned-array.js b/tests/ecmascript/test-dev-enum-abandoned-array.js
new file mode 100644
index 00000000..6548d673
--- /dev/null
+++ b/tests/ecmascript/test-dev-enum-abandoned-array.js
@@ -0,0 +1,65 @@
+/*
+ * Enumeration order for abandoned array part changed in Duktape 2.x.
+ * This testcase illustrates the change. The change is related to array
+ * instance .length which is non-enumerable, so that the change only affects
+ * Object.getOwnPropertyNames() and duk_enum() calls which request enumeration
+ * of non-enumerable properties.
+ */
+
+/*===
+with array part
+- 0
+- 1
+- 2
+- length
+- myProperty
+without array part
+- length
+- 0
+- 1
+- 2
+- myProperty
+array index added after abandoning array part
+- length
+- 0
+- 1
+- 2
+- myProperty
+- 3
+===*/
+
+function test() {
+ var arr = [ 'foo', 'bar', 'quux' ];
+ arr.myProperty = true;
+
+ // When array part is present the array index properties enumerate first,
+ // then the virtual .length property, and finally any other properties.
+
+ print('with array part');
+ Object.getOwnPropertyNames(arr).forEach(function (k) { print('-', k); });
+
+ arr[100] = 'dummy'; // abandon array part
+ arr.length = 3;
+
+ // When array part is not present, the virtual .length property enumerates
+ // first, followed by index properties moved into the entry part, followed
+ // by other properties (and any array index writes which happen after the
+ // array part is abandoned.
+ //
+ // In Duktape 1.x the .length property is concrete and would be enumerated
+ // after the index properties moved into the entry part. Array indexes
+ // added after array part abandonment would still appear last.
+
+ print('without array part');
+ Object.getOwnPropertyNames(arr).forEach(function (k) { print('-', k); });
+
+ arr.push('baz');
+ print('array index added after abandoning array part');
+ Object.getOwnPropertyNames(arr).forEach(function (k) { print('-', k); });
+}
+
+try {
+ test();
+} catch (e) {
+ print(e.stack || e);
+}
diff --git a/tests/ecmascript/test-dev-rom-builtins-1.js b/tests/ecmascript/test-dev-rom-builtins-1.js
index 56085cb3..c0ad0ad4 100644
--- a/tests/ecmascript/test-dev-rom-builtins-1.js
+++ b/tests/ecmascript/test-dev-rom-builtins-1.js
@@ -383,19 +383,29 @@ function romObjectRegExpTest() {
/*===
--- Error.prototype setter/getter
+fileName
{get:{_func:true},set:{_func:true},enumerable:false,configurable:false}
get length: 0, set length: 0
+lineNumber
{get:{_func:true},set:{_func:true},enumerable:false,configurable:false}
get length: 0, set length: 0
+stack
{get:{_func:true},set:{_func:true},enumerable:false,configurable:false}
get length: 0, set length: 0
===*/
function romObjectErrorPrototypeAccessorTest() {
function test(key) {
- var pd = Object.getOwnPropertyDescriptor(Error.prototype, key);
- print(Duktape.enc('jx', pd));
- print('get length: ' + pd.get.length + ', set length: ' + pd.set.length);
+ print(key);
+ try {
+ var pd = Object.getOwnPropertyDescriptor(Error.prototype, key);
+ print(Duktape.enc('jx', pd));
+ print('get length: ' + pd.get.length + ', set length: ' + pd.set.length);
+ } catch (e) {
+ print(typeof e, typeof e.message, typeof e.name, e.message, e.name);
+ print(e instanceof Error);
+ print(e.stack || e);
+ }
}
test('fileName');
@@ -415,6 +425,21 @@ function romObjectAccessorTest() {
print(global.__proto__ == Object.getPrototypeOf(global));
}
+/*===
+--- Array.prototype test
+[object Array]
+[object Array]
+0
+===*/
+
+function romObjectArrayPrototypeTest() {
+ // Array.prototype is also an Array instance; this affects the
+ // ROM init data and is thus useful to check.
+ print(Object.prototype.toString.call([]));
+ print(Object.prototype.toString.call(Array.prototype));
+ print(Array.prototype.length);
+}
+
// Read-only code paths related to object properties which aren't covered:
//
// duk_hobject_props.c:duk_realloc_props(): assert, can't be exercised directly.
@@ -463,6 +488,9 @@ try {
print('--- Accessor test')
romObjectAccessorTest();
+
+ print('--- Array.prototype test');
+ romObjectArrayPrototypeTest();
} catch (e) {
print(e.stack || e);
}
diff --git a/tests/perf/test-array-append.js b/tests/perf/test-array-append.js
new file mode 100644
index 00000000..5b03c131
--- /dev/null
+++ b/tests/perf/test-array-append.js
@@ -0,0 +1,24 @@
+/*
+ * Basic array append-by-index performance
+ */
+
+if (typeof print !== 'function') { print = console.log; }
+
+function test() {
+ var arr;
+ var i, j;
+
+ for (i = 0; i < 1e3; i++) {
+ arr = [];
+ for (j = 0; j < 1e4; j++) {
+ arr[j] = 'foo';
+ }
+ }
+}
+
+try {
+ test();
+} catch (e) {
+ print(e.stack || e);
+ throw e;
+}
diff --git a/tests/perf/test-array-cons-list.js b/tests/perf/test-array-cons-list.js
new file mode 100644
index 00000000..c2b93763
--- /dev/null
+++ b/tests/perf/test-array-cons-list.js
@@ -0,0 +1,24 @@
+/*
+ * Create Array using new Array() value list
+ */
+
+if (typeof print !== 'function') { print = console.log; }
+
+function test() {
+ var arr;
+ var i;
+
+ for (i = 0; i < 1e6; i++) {
+ arr = new Array('foo', 'bar', 'quux', 'baz', 'quuux',
+ '1', '2', '3', '4', '5',
+ 'foo', 'bar', 'quux', 'baz', 'quuux',
+ '1', '2', '3', '4', '5');
+ }
+}
+
+try {
+ test();
+} catch (e) {
+ print(e.stack || e);
+ throw e;
+}
diff --git a/tests/perf/test-array-literal.js b/tests/perf/test-array-literal.js
new file mode 100644
index 00000000..7c3fc517
--- /dev/null
+++ b/tests/perf/test-array-literal.js
@@ -0,0 +1,24 @@
+/*
+ * Create Array using a literal
+ */
+
+if (typeof print !== 'function') { print = console.log; }
+
+function test() {
+ var arr;
+ var i;
+
+ for (i = 0; i < 1e6; i++) {
+ arr = [ 'foo', 'bar', 'quux', 'baz', 'quuux',
+ '1', '2', '3', '4', '5',
+ 'foo', 'bar', 'quux', 'baz', 'quuux',
+ '1', '2', '3', '4', '5' ];
+ }
+}
+
+try {
+ test();
+} catch (e) {
+ print(e.stack || e);
+ throw e;
+}
diff --git a/tests/perf/test-array-push.js b/tests/perf/test-array-push.js
new file mode 100644
index 00000000..a3b58a33
--- /dev/null
+++ b/tests/perf/test-array-push.js
@@ -0,0 +1,24 @@
+/*
+ * Basic array.push() performance
+ */
+
+if (typeof print !== 'function') { print = console.log; }
+
+function test() {
+ var arr;
+ var i, j;
+
+ for (i = 0; i < 1e3; i++) {
+ arr = [];
+ for (j = 0; j < 1e4; j++) {
+ arr.push('foo');
+ }
+ }
+}
+
+try {
+ test();
+} catch (e) {
+ print(e.stack || e);
+ throw e;
+}
diff --git a/tests/perf/test-array-read-lenloop.js b/tests/perf/test-array-read-lenloop.js
new file mode 100644
index 00000000..036eaf3b
--- /dev/null
+++ b/tests/perf/test-array-read-lenloop.js
@@ -0,0 +1,29 @@
+/*
+ * Basic array read loop performance, loop to .length
+ */
+
+if (typeof print !== 'function') { print = console.log; }
+
+function test() {
+ var arr = [];
+ var ign;
+ var i, j;
+
+ while (arr.length < 1000) {
+ arr.push('foo');
+ }
+
+ for (i = 0; i < 3e4; i++) {
+ // Typical loop idiom where index check is against .length.
+ for (j = 0; j < arr.length; j++) {
+ ign = arr[j];
+ }
+ }
+}
+
+try {
+ test();
+} catch (e) {
+ print(e.stack || e);
+ throw e;
+}
diff --git a/tests/perf/test-error-create.js b/tests/perf/test-error-create.js
new file mode 100644
index 00000000..12113315
--- /dev/null
+++ b/tests/perf/test-error-create.js
@@ -0,0 +1,50 @@
+/*
+ * Create Error creation (tracedata)
+ */
+
+if (typeof print !== 'function') { print = console.log; }
+
+// Avoid tailcalls on purpose to ensure traceback is reasonably long.
+
+function fun0() {
+ var res = new Error('aiee');
+ return res;
+}
+
+function fun1() {
+ var res = fun0();
+ return res;
+}
+
+function fun2() {
+ var res = fun1();
+ return res;
+}
+
+function fun3() {
+ var res = fun2();
+ return res;
+}
+
+function fun4() {
+ var res = fun3();
+ return res;
+}
+
+function test() {
+ var err;
+ var i;
+
+ for (i = 0; i < 1e6; i++) {
+ err = fun4();
+ }
+
+ print(err.stack || err);
+}
+
+try {
+ test();
+} catch (e) {
+ print(e.stack || e);
+ throw e;
+}
diff --git a/tests/perf/test-object-literal.js b/tests/perf/test-object-literal.js
new file mode 100644
index 00000000..28b9c728
--- /dev/null
+++ b/tests/perf/test-object-literal.js
@@ -0,0 +1,42 @@
+/*
+ * Create Object using a literal
+ */
+
+if (typeof print !== 'function') { print = console.log; }
+
+function test() {
+ var obj;
+ var i;
+
+ for (i = 0; i < 1e6; i++) {
+ obj = {
+ key1: 'val1',
+ key2: 'val2',
+ key3: 'val3',
+ key4: 'val4',
+ key5: 'val5',
+ key6: 'val6',
+ key7: 'val7',
+ key8: 'val8',
+ key9: 'val9',
+ key10: 'val10',
+ key11: 'val11',
+ key12: 'val12',
+ key13: 'val13',
+ key14: 'val14',
+ key15: 'val15',
+ key16: 'val16',
+ key17: 'val17',
+ key18: 'val18',
+ key19: 'val19',
+ key20: 'val20'
+ };
+ }
+}
+
+try {
+ test();
+} catch (e) {
+ print(e.stack || e);
+ throw e;
+}
diff --git a/util/make_dist.py b/util/make_dist.py
index ce3436a2..9fdc902c 100644
--- a/util/make_dist.py
+++ b/util/make_dist.py
@@ -331,6 +331,7 @@ copy_files([
'duk_error_misc.c',
'duk_error_throw.c',
'duk_forwdecl.h',
+ 'duk_harray.h',
'duk_hbuffer_alloc.c',
'duk_hbuffer.h',
'duk_hbuffer_ops.c',
diff --git a/website/api/duk_def_prop.yaml b/website/api/duk_def_prop.yaml
index b07ccaf8..80152acb 100644
--- a/website/api/duk_def_prop.yaml
+++ b/website/api/duk_def_prop.yaml
@@ -35,8 +35,9 @@ summary: |
Object.defineProperty()
and is useful for e.g. sandboxing setup.
In some cases even a forced change is not possible and will cause an error to be
thrown. For instance, properties implemented internally as virtual properties
- (such as string "length" and index properties) cannot be modified because they
- don't have concrete internal storage and thus no way of recording changes.
.length
and index properties)
+ or may have limitations (such as array .length
property which cannot
+ be made configurable or enumerable due to internal limitations).
Some examples (see further examples below):
undefined
is pushed instead.
+ If the current function was called via one or more bound functions or a + Proxy object, the function returned from this call is the final, resolved + function (not the bound function or the Proxy).
+This function allows a C function to gain access to its function object. Since multiple function objects can internally point to the same C function, a function object is a convenient place for function parameterization and