|
|
@ -37,26 +37,42 @@ Related sections of E5 specification |
|
|
|
For raw property algorithms: |
|
|
|
|
|
|
|
* E5 Section 8.12: the default algorithms |
|
|
|
|
|
|
|
* E5 Section 8.6.2: one paragraph lists exotic behaviors (page 33, PDF page 43) |
|
|
|
|
|
|
|
* E5 Section 10.6: arguments object |
|
|
|
|
|
|
|
* E5 Section 15.5.5.2: String object |
|
|
|
|
|
|
|
* E5 Section 15.3.4.5.3, 15.3.5.3, 15.3.5.4: Function object |
|
|
|
|
|
|
|
* E5 Section 15.4.5.1: Array object |
|
|
|
|
|
|
|
For other algorithms: |
|
|
|
|
|
|
|
* E5 Section 8.10.4: FromPropertyDescriptor |
|
|
|
|
|
|
|
* E5 Section 8.10.5: ToPropertyDescriptor |
|
|
|
|
|
|
|
* E5 Section 8.7.1: GetValue |
|
|
|
|
|
|
|
* E5 Section 8.7.2: PutValue |
|
|
|
|
|
|
|
* E5 Section 11.2.1: property accessor expression |
|
|
|
|
|
|
|
* E5 Section 11.13: assignment operators (note that several other places |
|
|
|
also "evaluate" property accessor expressions) |
|
|
|
|
|
|
|
* E5 Section 11.4.1: ``delete`` operator |
|
|
|
|
|
|
|
* E5 Section 11.8.6: ``instanceof`` operator |
|
|
|
|
|
|
|
* E5 Section 11.8.7: ``in`` operator |
|
|
|
|
|
|
|
* E5 Section 15.2.3.3: ``Object.getOwnPropertyDescriptor()`` |
|
|
|
|
|
|
|
* E5 Section 15.2.3.6: ``Object.defineProperty()`` |
|
|
|
|
|
|
|
* E5 Section 15.2.3.7: ``Object.defineProperties()`` |
|
|
|
|
|
|
|
Algorithm overview |
|
|
@ -359,13 +375,17 @@ internal name which corresponds to the bytecode instruction: |
|
|
|
* ``Object.getOwnPropertyDescriptor()`` |
|
|
|
|
|
|
|
+ But not ``[[GetOwnProperty]]`` or ``[[GetProperty]]`` directly |
|
|
|
|
|
|
|
+ Not "fast path" so implementation should be compact |
|
|
|
|
|
|
|
+ Only throwing variant (``Throw`` is ``true``) |
|
|
|
|
|
|
|
* ``Object.defineProperty()`` and ``Object.defineProperties()`` |
|
|
|
|
|
|
|
+ But not ``[[DefineOwnProperty]]`` directly |
|
|
|
|
|
|
|
+ Not "fast path" so implementation should be compact |
|
|
|
|
|
|
|
+ Only throwing variant (``Throw`` is ``true``) |
|
|
|
|
|
|
|
These are used to implement the basic property related run-time operations |
|
|
@ -402,31 +422,51 @@ Notes |
|
|
|
``GetValue()`` from various places: |
|
|
|
|
|
|
|
+ Array initializer |
|
|
|
|
|
|
|
+ Object initializer |
|
|
|
|
|
|
|
+ Grouping operator (parentheses) |
|
|
|
|
|
|
|
+ Property accessor (e.g. ``x['foo']['bar']``) |
|
|
|
|
|
|
|
+ ``new`` operator |
|
|
|
|
|
|
|
+ Function calls |
|
|
|
|
|
|
|
+ Postfix and prefix increment/decrement |
|
|
|
|
|
|
|
+ ``void`` operator |
|
|
|
|
|
|
|
+ Unary operators (plus, minus, bitwise and logical NOT) |
|
|
|
|
|
|
|
+ Binary operators (additive and multiplicative expressions, bitwise, |
|
|
|
logical, and comparison operations) |
|
|
|
|
|
|
|
+ ``instanceof`` operator |
|
|
|
|
|
|
|
+ ``in`` operator |
|
|
|
|
|
|
|
+ Conditional operator (``?:``) |
|
|
|
|
|
|
|
+ Simple and compound assignment (right hand side) |
|
|
|
|
|
|
|
+ Comma operator (``,``) |
|
|
|
|
|
|
|
+ Variable declaration initializer |
|
|
|
|
|
|
|
+ ``if``, ``do-while``, ``while``, ``for``, ``for-in``, ``with`` statements |
|
|
|
|
|
|
|
+ ``throw`` statement |
|
|
|
|
|
|
|
* Property references are used as left-hand-side values and written using |
|
|
|
``PutValue()`` from various places: |
|
|
|
|
|
|
|
+ Postfix and prefix increment/decrement |
|
|
|
|
|
|
|
+ Simple and compound assignment |
|
|
|
|
|
|
|
+ Variable declaration initializer |
|
|
|
|
|
|
|
+ ``for`` and ``for-in`` statements |
|
|
|
|
|
|
|
Exotic behaviors |
|
|
@ -448,7 +488,9 @@ GetOwnProperty |
|
|
|
Related E5 sections: |
|
|
|
|
|
|
|
* E5 Section 8.12.1: default algorithm |
|
|
|
|
|
|
|
* E5 Section 15.5.5: ``String`` |
|
|
|
|
|
|
|
* E5 Section 10.5: arguments object |
|
|
|
|
|
|
|
Default algorithm |
|
|
@ -530,8 +572,11 @@ after the normal property check is as follows: |
|
|
|
c. If ``P`` is ``"length"``, return a Property Descriptor with the values: |
|
|
|
|
|
|
|
* ``[[Value]]: len`` (a number) |
|
|
|
|
|
|
|
* ``[[Enumerable]]: false`` |
|
|
|
|
|
|
|
* ``[[Writable]]: false`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: false`` |
|
|
|
|
|
|
|
d. If ``P`` is not an array index (E5 Section 15.4), return ``undefined``. |
|
|
@ -548,8 +593,11 @@ after the normal property check is as follows: |
|
|
|
h. Return a Property Descriptor with the values: |
|
|
|
|
|
|
|
* ``[[Value]]: resultStr`` |
|
|
|
|
|
|
|
* ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
* ``[[Writable]]: false`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: false`` |
|
|
|
|
|
|
|
2. Let ``D`` be a newly created Property Descriptor with no fields. |
|
|
@ -604,8 +652,11 @@ The exotic behavior can be appended to the above algorithm as follows: |
|
|
|
c. If ``P`` is ``"length"``, return a Property Descriptor with the values: |
|
|
|
|
|
|
|
* ``[[Value]]: len`` (a number) |
|
|
|
|
|
|
|
* ``[[Enumerable]]: false`` |
|
|
|
|
|
|
|
* ``[[Writable]]: false`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: false`` |
|
|
|
|
|
|
|
d. If ``P`` is not an array index (E5 Section 15.4), return ``undefined``. |
|
|
@ -622,8 +673,11 @@ The exotic behavior can be appended to the above algorithm as follows: |
|
|
|
h. Return a Property Descriptor with the values: |
|
|
|
|
|
|
|
* ``[[Value]]: resultStr`` |
|
|
|
|
|
|
|
* ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
* ``[[Writable]]: false`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: false`` |
|
|
|
|
|
|
|
2. Let ``D`` be a newly created Property Descriptor with no fields. |
|
|
@ -706,8 +760,11 @@ Final version with some cleanup and simplification: |
|
|
|
1. Return a Property Descriptor with the values: |
|
|
|
|
|
|
|
* ``[[Value]]: len`` (a primitive number) |
|
|
|
|
|
|
|
* ``[[Enumerable]]: false`` |
|
|
|
|
|
|
|
* ``[[Writable]]: false`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: false`` |
|
|
|
|
|
|
|
d. If ``P`` is an array index (E5 Section 15.4): |
|
|
@ -718,8 +775,11 @@ Final version with some cleanup and simplification: |
|
|
|
|
|
|
|
* ``[[Value]]:`` a primitive string of length 1, containing one character |
|
|
|
from ``str`` at position ``index`` (zero based index) |
|
|
|
|
|
|
|
* ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
* ``[[Writable]]: false`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: false`` |
|
|
|
|
|
|
|
e. Return ``undefined``. |
|
|
@ -768,7 +828,9 @@ Get |
|
|
|
Related E5 sections: |
|
|
|
|
|
|
|
* E5 Section 8.12.3: default algorithm |
|
|
|
|
|
|
|
* E5 Section 10.5: arguments object |
|
|
|
|
|
|
|
* E5 Section 15.3.5.4: ``Function`` |
|
|
|
|
|
|
|
Default algorithm |
|
|
@ -951,7 +1013,9 @@ DefineOwnProperty |
|
|
|
Related E5 sections: |
|
|
|
|
|
|
|
* E5 Section 8.12.9: default algorithm |
|
|
|
|
|
|
|
* E5 Section 15.4.5: ``Array`` |
|
|
|
|
|
|
|
* E5 Section 10.5: arguments object |
|
|
|
|
|
|
|
Note that ``String`` exotic properties are taken into account by |
|
|
@ -1345,7 +1409,7 @@ First, for ``P`` == ``length``: |
|
|
|
|
|
|
|
This is equivalent to: |
|
|
|
|
|
|
|
- Jumping to step 5. |
|
|
|
+ Jumping to step 5. |
|
|
|
|
|
|
|
* Step 3.f.1: |
|
|
|
If ``newLen`` validation succeeds and new length is not shorter |
|
|
@ -1489,11 +1553,13 @@ helper in the implementation too) as it's relatively tricky. It is |
|
|
|
instead isolated into ``ShortenArray()`` internal helper with inputs: |
|
|
|
|
|
|
|
* old length |
|
|
|
|
|
|
|
* new length |
|
|
|
|
|
|
|
and outputs: |
|
|
|
|
|
|
|
* success flag (``false`` if some element couldn't be deleted) |
|
|
|
|
|
|
|
* final array length to be updated into ``"length"`` property |
|
|
|
|
|
|
|
Adding ``Array`` object exotic behavior |
|
|
@ -1922,6 +1988,7 @@ Delete |
|
|
|
Related E5 sections: |
|
|
|
|
|
|
|
* E5 Section 8.12.7: default algorithm |
|
|
|
|
|
|
|
* E5 Section 10.5: arguments object |
|
|
|
|
|
|
|
Default algorithm |
|
|
@ -1979,7 +2046,7 @@ This is easy to incorporate and results in: |
|
|
|
b. If the result of calling the ``[[GetOwnProperty]]`` internal method |
|
|
|
of ``map`` passing ``P`` as the argument is not ``undefined``, then: |
|
|
|
|
|
|
|
a. Call the ``[[Delete]]`` internal method of ``map`` passing ``P``, |
|
|
|
1. Call the ``[[Delete]]`` internal method of ``map`` passing ``P``, |
|
|
|
and ``false`` as the arguments. (This removes the magic binding |
|
|
|
for ``P``.) |
|
|
|
|
|
|
@ -2320,8 +2387,11 @@ The following inlines ``[[GetOwnProperty]]`` with all exotic behaviors: |
|
|
|
1. Return a Property Descriptor with the values: |
|
|
|
|
|
|
|
* ``[[Value]]: len`` (a primitive number) |
|
|
|
|
|
|
|
* ``[[Enumerable]]: false`` |
|
|
|
|
|
|
|
* ``[[Writable]]: false`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: false`` |
|
|
|
|
|
|
|
d. If ``P`` is an array index (E5 Section 15.4): |
|
|
@ -2330,10 +2400,13 @@ The following inlines ``[[GetOwnProperty]]`` with all exotic behaviors: |
|
|
|
|
|
|
|
2. If ``index`` < ``len``, return a Property Descriptor with the values: |
|
|
|
|
|
|
|
* ``[[Value]]:`` a primitive string of length 1, containing one character |
|
|
|
from ``str`` at position ``index`` (zero based index) |
|
|
|
* ``[[Value]]:`` a primitive string of length 1, containing one |
|
|
|
character from ``str`` at position ``index`` (zero based index) |
|
|
|
|
|
|
|
* ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
* ``[[Writable]]: false`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: false`` |
|
|
|
|
|
|
|
e. Goto NOTFOUND. |
|
|
@ -2615,24 +2688,35 @@ exists. The property value is updated with a call to |
|
|
|
We can assume that: |
|
|
|
|
|
|
|
* The property exists (checked by ``[[Put]]``) |
|
|
|
|
|
|
|
* The property is a data property (checked by ``[[Put]]``) |
|
|
|
|
|
|
|
* The property cannot be non-writable (checked by ``[[Put]]``, using |
|
|
|
``[[CanPut]]``) |
|
|
|
|
|
|
|
* The property descriptor is a data descriptor |
|
|
|
|
|
|
|
* The property descriptor is of the form: ``{ [[Value]]: val }`` |
|
|
|
|
|
|
|
* Because the property exists, the ``length`` of an ``Array`` object |
|
|
|
cannot change by a write to an array index; however, a write to |
|
|
|
``"length"`` may delete array elements |
|
|
|
|
|
|
|
More specifically, we know that in the ``[[DefineOwnProperty]]`` algorithm: |
|
|
|
|
|
|
|
* ``current`` is not ``undefined`` |
|
|
|
* ``IsGenericDescriptor(current)`` is ``false`` |
|
|
|
* ``IsDataDescriptor(current)`` is ``true`` |
|
|
|
* ``IsAccessorDescriptor(current)`` is ``false`` |
|
|
|
* ``IsGenericDescriptor(Desc)`` is ``false`` |
|
|
|
* ``IsDataDescriptor(Desc)`` is ``true`` |
|
|
|
* ``IsAccessorDescriptor(Desc)`` is ``false`` |
|
|
|
* ``current`` is not ``undefined`` |
|
|
|
|
|
|
|
* ``IsGenericDescriptor(current)`` is ``false`` |
|
|
|
|
|
|
|
* ``IsDataDescriptor(current)`` is ``true`` |
|
|
|
|
|
|
|
* ``IsAccessorDescriptor(current)`` is ``false`` |
|
|
|
|
|
|
|
* ``IsGenericDescriptor(Desc)`` is ``false`` |
|
|
|
|
|
|
|
* ``IsDataDescriptor(Desc)`` is ``true`` |
|
|
|
|
|
|
|
* ``IsAccessorDescriptor(Desc)`` is ``false`` |
|
|
|
|
|
|
|
Taking the ``[[DefineOwnProperty]]`` with all exotic behaviors included, |
|
|
|
using the above assumptions, eliminating any unnecessary steps, cleaning |
|
|
@ -2765,13 +2849,19 @@ See E5 Section 8.12.5, step 6. |
|
|
|
We can assume that: |
|
|
|
|
|
|
|
* The property does not exist (checked by ``[[Put]]``) |
|
|
|
|
|
|
|
* The object is extensible (checked by ``[[Put]]``) |
|
|
|
|
|
|
|
* The property descriptor is a data descriptor |
|
|
|
|
|
|
|
* The property descriptor has the fields: |
|
|
|
|
|
|
|
+ ``[[Value]]: val`` |
|
|
|
|
|
|
|
+ ``[[Writable]]: true`` |
|
|
|
|
|
|
|
+ ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
+ ``[[Configurable]]: true`` |
|
|
|
|
|
|
|
+ If the object is an ``Array``, the property name ``P`` cannot be |
|
|
@ -2902,8 +2992,11 @@ We'll refine one more time, by eliminating references to ``Desc`` and using |
|
|
|
are: |
|
|
|
|
|
|
|
* ``[[Value]]: val`` |
|
|
|
|
|
|
|
* ``[[Writable]]: true`` |
|
|
|
|
|
|
|
* ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: true`` |
|
|
|
|
|
|
|
3. Return ``true``. |
|
|
@ -2935,8 +3028,11 @@ Because user code has not had any access to the object, we can narrow down |
|
|
|
the possibilities a great deal. Here we assume that: |
|
|
|
|
|
|
|
* Object is extensible |
|
|
|
|
|
|
|
* Property does not exist |
|
|
|
|
|
|
|
* Property does not have exotic behavior and is not virtual |
|
|
|
|
|
|
|
* Property descriptor is a data descriptor, which is fully populated |
|
|
|
|
|
|
|
With these assumptions, eliminating any unnecessary steps, the algorithm is |
|
|
@ -3338,13 +3434,13 @@ Property descriptor algorithms |
|
|
|
|
|
|
|
E5 Section 8.10 describes descriptor related algorithms: |
|
|
|
|
|
|
|
* ``IsAccessorDescriptor(desc)``: ``true``, if ``desc`` contains *either* |
|
|
|
* ``IsAccessorDescriptor(desc)``: ``true``, if ``desc`` contains *either* |
|
|
|
``[[Set]]`` or ``[[Get]]`` |
|
|
|
|
|
|
|
* ``IsDataDescriptor(desc)``: ``true``, if ``desc`` contains *either* |
|
|
|
* ``IsDataDescriptor(desc)``: ``true``, if ``desc`` contains *either* |
|
|
|
``[[Value]]`` or ``[[Writable]]`` |
|
|
|
|
|
|
|
* ``IsGenericDescriptor(desc)``: ``true`` if both |
|
|
|
* ``IsGenericDescriptor(desc)``: ``true`` if both |
|
|
|
``IsAccessorDescriptor(desc)`` and ``IsGenericDescriptor`` are |
|
|
|
``false``; concretely: |
|
|
|
|
|
|
@ -3362,10 +3458,10 @@ A property descriptor may not be both a data descriptor and access descriptor |
|
|
|
``Object.defineProperty()`` may naturally contain e.g. ``"set"`` and |
|
|
|
``"value"`` keys. In this case: |
|
|
|
|
|
|
|
* ``defineProperty()`` uses ``ToPropertyDescriptor()`` to convert the |
|
|
|
* ``defineProperty()`` uses ``ToPropertyDescriptor()`` to convert the |
|
|
|
Ecmascript object into an internal property descriptor |
|
|
|
|
|
|
|
* ``ToPropertyDescriptor()`` creates a property descriptor and throws a |
|
|
|
* ``ToPropertyDescriptor()`` creates a property descriptor and throws a |
|
|
|
``TypeError`` if the descriptor contains conflicting fields |
|
|
|
|
|
|
|
``ToPropertyDescriptor()`` also coerces the values in its argument |
|
|
@ -3698,7 +3794,9 @@ This leads to externally visible behavior, illustrated in the following:: |
|
|
|
Behavior in Ecmascript implementations seems to vary: |
|
|
|
|
|
|
|
* NodeJS / V8: prints 'string' and 'object' as expected |
|
|
|
|
|
|
|
* Rhino: prints 'object' and 'object' |
|
|
|
|
|
|
|
* Smjs: prints 'object' and 'object' |
|
|
|
|
|
|
|
``GetValue()`` allows the caller to skip creation of the coerced object |
|
|
@ -3890,7 +3988,9 @@ we know that it is a ``Boolean`` instance, a ``Number`` instance, or a ``String` |
|
|
|
instance (see E5 Section 9.9). The "own properties" of these are: |
|
|
|
|
|
|
|
* ``Boolean``: none |
|
|
|
|
|
|
|
* ``Number``: none |
|
|
|
|
|
|
|
* ``String``: ``"length"`` and index properties for string characters |
|
|
|
|
|
|
|
So, the coercion can be skipped safely for everything except ``String``\ s. |
|
|
@ -5045,8 +5145,11 @@ The resulting algorithm is: |
|
|
|
are: |
|
|
|
|
|
|
|
* ``[[Value]]: V`` |
|
|
|
|
|
|
|
* ``[[Writable]]: true`` |
|
|
|
|
|
|
|
* ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: true`` |
|
|
|
|
|
|
|
14. Return. |
|
|
@ -5092,7 +5195,9 @@ be created or an existing one updated. |
|
|
|
For the possible coerced values, the own properties are: |
|
|
|
|
|
|
|
* ``Boolean``: none |
|
|
|
|
|
|
|
* ``Number``: none |
|
|
|
|
|
|
|
* ``String``: ``"length"`` and index properties for string characters |
|
|
|
|
|
|
|
These can be checked explicitly when coercing (and reject the attempt before |
|
|
@ -5266,8 +5371,11 @@ Avoiding temporaries altogether: |
|
|
|
are: |
|
|
|
|
|
|
|
* ``[[Value]]: V`` |
|
|
|
|
|
|
|
* ``[[Writable]]: true`` |
|
|
|
|
|
|
|
* ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: true`` |
|
|
|
|
|
|
|
13. Return. |
|
|
@ -5456,8 +5564,11 @@ Addressing the array ``length`` issue: |
|
|
|
are: |
|
|
|
|
|
|
|
* ``[[Value]]: V`` |
|
|
|
|
|
|
|
* ``[[Writable]]: true`` |
|
|
|
|
|
|
|
* ``[[Enumerable]]: true`` |
|
|
|
|
|
|
|
* ``[[Configurable]]: true`` |
|
|
|
|
|
|
|
14. If ``pendingLength`` > ``0``: |
|
|
@ -5605,7 +5716,9 @@ discarded, there are only two possible user visible effects: |
|
|
|
* The return value of ``[[Delete]]``, which is: |
|
|
|
|
|
|
|
+ ``true``, if the property does not exist |
|
|
|
|
|
|
|
+ ``true``, if the property exists and could be deleted |
|
|
|
|
|
|
|
+ ``false``, if the property exists, cannot be deleted, and |
|
|
|
``Throw`` is ``false`` (if ``Throw`` is ``true``, an error is |
|
|
|
thrown instead) |
|
|
@ -5618,7 +5731,9 @@ discarded, there are only two possible user visible effects: |
|
|
|
The coerced temporary object can be: |
|
|
|
|
|
|
|
* a ``Boolean`` instance: no own properties |
|
|
|
|
|
|
|
* a ``Number`` instance: no own properties |
|
|
|
|
|
|
|
* a ``String`` instance: has ``length`` and array indices (inside string |
|
|
|
length) as own properties, all non-configurable |
|
|
|
|
|
|
@ -5643,6 +5758,7 @@ entirely: |
|
|
|
1. If ``P`` is length or an array index inside the ``O`` string length: |
|
|
|
|
|
|
|
a. If ``currStrict`` is ``true``, throw a ``TypeError`` |
|
|
|
|
|
|
|
b. Else, return ``false`` |
|
|
|
|
|
|
|
2. Else, return ``true`` |
|
|
@ -5665,6 +5781,7 @@ Step 4 can be simplified a bit: |
|
|
|
a. If ``P`` is length or an array index inside the ``O`` string length: |
|
|
|
|
|
|
|
1. If ``currStrict`` is ``true``, throw a ``TypeError`` |
|
|
|
|
|
|
|
2. Else, return ``false`` |
|
|
|
|
|
|
|
5. Return ``true`` |
|
|
@ -6875,4 +6992,3 @@ Future work |
|
|
|
* Review the algorithms for robustness against internal errors such as |
|
|
|
out of memory. The order of operations should be chosen to minimize |
|
|
|
any inconsistency in state if an internal error occurs. |
|
|
|
|
|
|
|