You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1706 lines
59 KiB

================
Exotic behaviors
================
This section covers the standard algorithms with exotic behaviors inlined.
For each algorithm, a single algorithm with all exotic behaviors inlined
is presented. Calls to other internal algorithms are not inlined; the
purpose is to clarify how the exotic behaviors can be implemented
reasonably.
Note: the ``String`` object has no exotic behaviors as such, but the
``length`` and array index properties are implemented as virtual properties,
so they are inlined into the algorithms below.
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
-----------------
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.
4. If ``X`` is a data property, then
a. Set ``D.[[Value]]`` to the value of ``X``\ 's ``[[Value]]`` 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.
b. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` 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.
8. Return ``D``.
Adding String object exotic behavior
------------------------------------
Now consider the ``String`` variant in E5 Section 15.5.5.2. Step 2 states that if
the default algorithm returns a descriptor (not undefined), the exotic behavior
does not execute at all. That, is the exotic algorithm is skipped if ``O`` has
an "own property" for key ``P``.
If the default algorithm fails to find an own property, the variant kicks in
checking for a valid array index key which is inside the string length. If so,
it returns a single character data property descriptor. The descriptor has
``[[Writable]]`` and ``[[Configurable]]`` set to ``false`` which means that
the property cannot be written or deleted -- the property is thus perfect for
implementation as a virtual property backed to an immutable internal string
value.
.. note:: Ecmascript 5.1 no longer requires the numbered index to be a valid
array index, any number-like value will do. This allows strings
longer than 4G. The algorithms here don't reflect this correctly.
The ``String`` object ``length`` property is an ordinary (non-exotic)
property, see E5 Section 15.5.5.1. However, it is non-writable and
non-configurable (and even non-enumerable), so it too is nice and easy
to implement as a exotic property. We'll thus incorporate the ``length``
property into the algorithm.
Finally note that from an implementation perspective it might be easier
to check for the exotic (virtual) properties before looking at the actual
ones (i.e. reverse the order of checking). This seems perfectly OK to do,
because *if* the property name matches a virtual property, the object cannot
have a "normal" property of the same name: the initial ``String`` object
does not have such properties, and since the virtual properties cannot be
deleted, they prevent the insertion of normal "own properties" of the same
name. Hence, if the virtual properties are checked for first and the check
matches, the object is guaranteed not to have a normal property of the same
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``:
a. If ``O`` is not a ``String`` instance, return ``undefined``.
b. (``String`` object exotic behavior.)
Let ``str`` be the String value of the ``[[PrimitiveValue]]``
internal property of ``O`` and ``len`` be the number of
characters in ``str``.
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``.
e. Let ``index`` be ``ToUint32(P)``.
f. If ``len`` <= ``index``, return ``undefined``.
g. Let ``resultStr`` be a string of length 1, containing one character
from ``str``, specifically the character at position ``index``, where
the first (leftmost) character in ``str`` is considered to be at
position 0, the next one at position 1, and so on.
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.
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.
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.
b. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` 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.
8. Return ``D``.
Adding arguments object exotic behavior
---------------------------------------
Next, consider the exotic ``[[GetOwnProperty]]`` behavior for a non-strict
arguments object described in E5 Section 10.6. The exotic behavior only
applies if the object *did* contain the own property ``P``, and possibly
modifies the looked up value if the key ``P`` matches a numeric index
magically "bound" to a formal.
Note that the property descriptors for such variables are initially data
property descriptors, so the default algorithm will find a data property
descriptor (and not an accessor property descriptor). If the property is
later converted to an accessor, the magical variable binding is also
dropped. So, if the exotic behavior activates, the property is always
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``:
a. If ``O`` is not a ``String`` instance, return ``undefined``.
b. (``String`` object exotic behavior.)
Let ``str`` be the String value of the ``[[PrimitiveValue]]``
internal property of ``O`` and ``len`` be the number of
characters in ``str``.
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``.
e. Else let ``index`` be ``ToUint32(P)``.
f. If ``len`` <= ``index``, return ``undefined``.
g. Let ``resultStr`` be a string of length 1, containing one character
from ``str``, specifically the character at position ``index``, where
the first (leftmost) character in ``str`` is considered to be at
position 0, the next one at position 1, and so on.
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.
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.
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.
b. Set ``D.[[Set]]`` to the value of ``X``\ 's ``[[Set]]`` 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.
8. If ``O`` is an ``arguments`` object which contains a ``[[ParameterMap]]``
internal property:
a. (Arguments object exotic behavior.) Let ``map`` be the value of
the ``[[ParameterMap]]`` internal property of the arguments object.
b. Let ``isMapped`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``map`` passing ``P`` as the argument.
c. If the value of ``isMapped`` is not ``undefined``, then:
1. Set ``D.[[Value]]`` to the result of calling the ``[[Get]]``
internal method of ``map`` passing ``P`` as the argument.
9. Return ``D``.
Notes:
* Step 1.b: if the object is a ``String`` object, there is no need for the
arguments object exotic behavior check in step 8: an object can never be
a ``String`` object and an arguments object simultaenously.
* Step 8: arguments objects for strict mode functions don't have the exotic
behavior (or a ``[[ParameterMap]]``). Arguments objects for non-strict
functions don't always have exotic behavior either: they only do, if there
is at least one mapped variable. If so, ``[[ParameterMap]]`` is added, and
exotic behavior is enabled. See the main algorithm in E5 Section 10.6,
step 12.
* Step 8.c.1: this step invokes an internal getter function which looks up
the magically bound variable. See E5 Section 10.6, 11.c.ii, and the
*MakeArgGetter* concept. A practical implementation may not create such
internal functions (we don't).
* Step 8.c.1: the rules of maintaining the ``[[ParameterMap]]`` ensures that
at this point the property is always a data property, so setting the
``[[Value]]`` is correct. If a magically bound value is converted into an
accessor, the property is deleted from the ``[[ParameterMap]]`` so it no
longer has exotic behavior.
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``:
a. If ``O`` is not a ``String`` instance, return ``undefined``.
b. (``String`` object exotic behavior.)
Let ``str`` be the String value of the ``[[PrimitiveValue]]``
internal property of ``O`` and ``len`` be the number of
characters in ``str``.
c. If ``P`` is ``"length"``:
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):
1. Let ``index`` be ``ToUint32(P)``.
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)
* ``[[Enumerable]]: true``
* ``[[Writable]]: false``
* ``[[Configurable]]: false``
e. Return ``undefined``.
2. Let ``D`` be a newly created Property Descriptor filled as follows:
a. If ``X`` is a data property:
1. Set ``D.[[Value]]`` to the value of ``X``\ 's ``[[Value]]`` 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.
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.
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:
a. (Arguments object exotic behavior.) Let ``map`` be the value of
the ``[[ParameterMap]]`` internal property of the arguments object.
b. If the result of calling the ``[[GetOwnProperty]]`` internal method
of ``map`` passing ``P`` as the argument is not ``undefined``, then:
1. Set ``D.[[Value]]`` to the result of calling the ``[[Get]]``
internal method of ``map`` passing ``P`` as the argument.
4. Return ``D``.
Notes:
* Step 3 can be skipped for accessors.
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
-----------------
(Note that E5 Section 8.12.3 has broken numbering; fixed below.)
1. Let ``desc`` be the result of calling the ``[[GetProperty]]`` internal
method of ``O`` with property name ``P``.
2. If ``desc`` is ``undefined``, return ``undefined``.
3. If ``IsDataDescriptor(desc)`` is ``true``, return ``desc.[[Value]]``.
4. Otherwise, ``IsAccessorDescriptor(desc)`` must be ``true`` so, let
``getter`` be ``desc.[[Get]]``.
5. If ``getter`` is ``undefined``, return ``undefined``.
6. Return the result calling the ``[[Call]]`` internal method of ``getter``
providing ``O`` as the ``this`` value and providing no arguments.
Adding Function object exotic behavior
--------------------------------------
Consider the ``Function`` variant in E5 Section 15.3.5.4. The behavior only
applies if ``P`` is ``caller`` and the resulting return *value* of the default
function is a strict mode function.
The exotic behavior does not need to be checked in steps 2 or 5 of the
default algorithm, because ``undefined`` is never a strict mode function
value.
So, we can reformulate into:
1. Let ``desc`` be the result of calling the ``[[GetProperty]]`` internal
method of ``O`` with property name ``P``.
2. If ``desc`` is ``undefined``, return ``undefined``.
3. If ``IsDataDescriptor(desc)`` is ``true``:
a. Let ``res`` be ``desc.[[Value]]``.
4. Otherwise, ``IsAccessorDescriptor(desc)`` must be ``true``:
a. Let ``getter`` be ``desc.[[Get]]``.
b. If ``getter`` is ``undefined``, return ``undefined``.
c. Else let ``res`` be the result of calling the ``[[Call]]`` internal
method of ``getter`` providing ``O`` as the ``this`` value and
providing no arguments.
5. If ``O`` is a ``Function`` object, ``P`` is ``"caller"``, and ``res``
is a strict mode ``Function`` object, throw a ``TypeError`` exception.
6. Return ``res``.
Adding arguments object exotic behavior
---------------------------------------
Next, consider the exotic ``[[Get]]`` behavior for a non-strict arguments
object described in E5 Section 10.6. To be exact, the exotic behaviors
are only enabled for objects with a non-empty initial ``[[ParameterMap]]``
(see E5 Section 10.6, main algorithm, step 12).
There are two exotic behaviors:
1. If the property name ``P`` is magically bound to an identifier
(through the ``[[ParameterMap]]``) the default ``[[Get]]`` is
bypassed entirely and the property value is read.
(Note that the property ``P`` *must* be a data property in this
case, so no side effects are lost by this behavior.)
2. If the property name ``P`` is *not bound* to an identifier,
the ``"caller"`` property has exotic behavior essentially
identical to that of ``Function``.
These can be incorporated as follows:
1. If ``O`` is an ``arguments`` object which contains a ``[[ParameterMap]]``
internal property:
a. (Arguments object exotic behavior.) Let ``map`` be the value of
the ``[[ParameterMap]]`` internal property of the arguments object.
b. Let ``isMapped`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``map`` passing ``P`` as the argument.
c. If the value of ``isMapped`` is not ``undefined``, then:
1. Return the result of calling the ``[[Get]]`` internal method of
``map`` passing ``P`` as the argument.
2. Let ``desc`` be the result of calling the ``[[GetProperty]]`` internal
method of ``O`` with property name ``P``.
3. If ``desc`` is ``undefined``, return ``undefined``.
4. If ``IsDataDescriptor(desc)`` is ``true``:
a. Let ``res`` be ``desc.[[Value]]``.
5. Otherwise, ``IsAccessorDescriptor(desc)`` must be ``true``:
a. Let ``getter`` be ``desc.[[Get]]``.
b. If ``getter`` is ``undefined``, return ``undefined``.
c. Else let ``res`` be the result of calling the ``[[Call]]`` internal
method of ``getter`` providing ``O`` as the ``this`` value and
providing no arguments.
6. If ``O`` is a ``Function`` object or an ``arguments`` object which
contains a ``[[ParameterMap]]`` internal property:
a. (Arguments or Function object exotic behavior.)
If ``P`` is ``"caller"`` and ``res`` is a strict mode ``Function``
object, throw a ``TypeError`` exception.
7. Return ``res``.
Note:
* Step 1 can match only when ``P`` is a "numeric" property name, and
the property value is an own data property. Magically bound properties
are initially own data properties, and if they're changed to accessors
(or deleted), the binding is removed. Because of this, the arguments
exotic behavior could just as well be moved to the end of the algorithm.
Final version
-------------
Final version with some cleanup and simplification:
1. If ``O`` is an ``arguments`` object which contains a ``[[ParameterMap]]``
internal property:
a. (Arguments object exotic behavior.) Let ``map`` be the value of
the ``[[ParameterMap]]`` internal property of the arguments object.
b. If the result of calling the ``[[GetOwnProperty]]`` internal method
of ``map`` passing ``P`` as the argument is not ``undefined``:
1. Return the result of calling the ``[[Get]]`` internal method of
``map`` passing ``P`` as the argument.
2. Let ``desc`` be the result of calling the ``[[GetProperty]]`` internal
method of ``O`` with property name ``P``.
3. If ``desc`` is ``undefined``, return ``undefined``.
4. If ``IsDataDescriptor(desc)`` is ``true``:
a. Let ``res`` be ``desc.[[Value]]``.
5. Otherwise, ``IsAccessorDescriptor(desc)`` must be ``true``:
a. Let ``getter`` be ``desc.[[Get]]``.
b. If ``getter`` is ``undefined``, return ``undefined``.
c. Else let ``res`` be the result of calling the ``[[Call]]`` internal
method of ``getter`` providing ``O`` as the ``this`` value and
providing no arguments.
6. If ``O`` is a ``Function`` object or an ``arguments`` object which
contains a ``[[ParameterMap]]`` internal property:
a. (Arguments or Function object exotic behavior.)
If ``P`` is ``"caller"`` and ``res`` is a strict mode ``Function``
object, throw a ``TypeError`` exception.
7. Return ``res``.
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
``[[DefineOwnProperty]]`` through ``[[GetOwnProperty]]`` which
returns a property descriptor prohibiting any property value or
attribute changes. However, no explicit checks are needed for
these (virtual) properties.
This is by the far the most complex property algorithm, especially
with exotic behaviors incorporated. The algorithm itself is
complex, but the ``Array`` variant actually makes multiple calls to
the default variant which is even trickier for "inlining".
Default algorithm
-----------------
1. Let ``current`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``O`` with property name ``P``.
2. Let ``extensible`` be the value of the ``[[Extensible]]`` internal
property of ``O``.
3. If ``current`` is ``undefined`` and ``extensible`` is ``false``,
then Reject.
4. If ``current`` is ``undefined`` and ``extensible`` is ``true``, then
a. If ``IsGenericDescriptor(Desc)`` or ``IsDataDescriptor(Desc)`` is
``true``, then
1. Create an own data property named ``P`` of object ``O`` whose
``[[Value]]``, ``[[Writable]]``, ``[[Enumerable]]`` and
``[[Configurable]]`` attribute values are described by ``Desc``.
If the value of an attribute field of ``Desc`` is absent, the
attribute of the newly created property is set to its default
value.
b. Else, ``Desc`` must be an accessor Property Descriptor so,
1. Create an own accessor property named ``P`` of object ``O`` whose
``[[Get]]``, ``[[Set]]``, ``[[Enumerable]]`` and ``[[Configurable]]``
attribute values are described by ``Desc``. If the value of an
attribute field of ``Desc`` is absent, the attribute of the newly
created property is set to its default value.
c. Return ``true``.
5. Return ``true`` if every field in ``Desc`` is absent.
6. Return ``true``, if every field in ``Desc`` also occurs in ``current``
and the value of every field in ``Desc`` is the same value as the
corresponding field in ``current`` when compared using the ``SameValue``
algorithm (E5 Section 9.12).
7. If the ``[[Configurable]]`` field of ``current`` is ``false`` then
a. Reject, if the ``[[Configurable]]`` field of ``Desc`` is true.
b. Reject, if the ``[[Enumerable]]`` field of ``Desc`` is present and
the ``[[Enumerable]]`` fields of ``current`` and ``Desc`` are the
Boolean negation of each other.
8. If ``IsGenericDescriptor(Desc)`` is ``true``, then no further validation
is required.
9. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
have different results, then
a. Reject, if the ``[[Configurable]]`` field of ``current`` is ``false``.
b. If ``IsDataDescriptor(current)`` is true, then
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
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
default values.
10. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
are both true, then
a. If the ``[[Configurable]]`` field of ``current`` is ``false``, then
1. Reject, if the ``[[Writable]]`` field of ``current`` is ``false``
and the ``[[Writable]]`` field of ``Desc`` is ``true``.
2. If the ``[[Writable]]`` field of ``current`` is ``false``, then
a. Reject, if the ``[[Value]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Value]], current.[[Value]])`` is ``false``.
b. else, the ``[[Configurable]]`` field of ``current`` is ``true``, so
any change is acceptable.
11. Else, ``IsAccessorDescriptor(current)`` and ``IsAccessorDescriptor(Desc)``
are both ``true`` so,
a. If the ``[[Configurable]]`` field of ``current`` is ``false``, then
1. Reject, if the ``[[Set]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Set]], current.[[Set]])`` is ``false``.
2. Reject, if the ``[[Get]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Get]], current.[[Get]])`` is ``false``.
12. For each attribute field of ``Desc`` that is present, set the
correspondingly named attribute of the property named ``P`` of object
``O`` to the value of the field.
13. Return ``true``.
Notes:
* The default attributes are *not* the same as when ``[[Put]]`` creates a
new property. The defaults here are "false" (and NULL for getter/setter),
see E5 Section 8.6.1, Table 7).
* Step 10.a.1 allows a non-configurable property to change from writable to
non-writable, but not vice versa.
* Step 10.b is not necessary (it is more of an assertion), and there is no
corresponding step 11.b mentioning the same thing. This step can be removed
from the description.
* There are multiple exit points for both Reject (throw or return false) and
true. For incorporating inline exotic behaviors, these are turned to
"gotos" below.
Default algorithm reformulated
------------------------------
Let's first do a little bit of reformulation (see above):
1. Let ``current`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``O`` with property name ``P``.
2. Let ``extensible`` be the value of the ``[[Extensible]]`` internal
property of ``O``.
3. If ``current`` is ``undefined``:
a. If ``extensible`` is ``false``, then goto REJECT.
b. If ``IsGenericDescriptor(Desc)`` or ``IsDataDescriptor(Desc)`` is
``true``, then
1. Create an own data property named ``P`` of object ``O`` whose
``[[Value]]``, ``[[Writable]]``, ``[[Enumerable]]`` and
``[[Configurable]]`` attribute values are described by ``Desc``.
If the value of an attribute field of ``Desc`` is absent, the
attribute of the newly created property is set to its default
value.
c. Else, ``Desc`` must be an accessor Property Descriptor so,
1. Create an own accessor property named ``P`` of object ``O`` whose
``[[Get]]``, ``[[Set]]``, ``[[Enumerable]]`` and ``[[Configurable]]``
attribute values are described by ``Desc``. If the value of an
attribute field of ``Desc`` is absent, the attribute of the newly
created property is set to its default value.
d. Goto SUCCESS.
4. Goto SUCCESS, if every field in ``Desc`` also occurs in ``current``
and the value of every field in ``Desc`` is the same value as the
corresponding field in ``current`` when compared using the ``SameValue``
algorithm (E5 Section 9.12). (This also covers the case where
every field in ``Desc`` is absent.)
5. If the ``[[Configurable]]`` field of ``current`` is ``false`` then
a. Goto REJECT, if the ``[[Configurable]]`` field of ``Desc`` is true.
b. Goto REJECT, if the ``[[Enumerable]]`` field of ``Desc`` is present
and the ``[[Enumerable]]`` fields of ``current`` and ``Desc`` are the
Boolean negation of each other.
6. If ``IsGenericDescriptor(Desc)`` is ``true``, then goto VALIDATED.
7. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
have different results, then
a. Goto REJECT, if the ``[[Configurable]]`` field of ``current`` is
``false``.
b. If ``IsDataDescriptor(current)`` is true, then
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
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
default values.
d. Goto VALIDATED.
8. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
are both true, then
a. If the ``[[Configurable]]`` field of ``current`` is ``false``, then
1. Goto REJECT, if the ``[[Writable]]`` field of ``current`` is
``false`` and the ``[[Writable]]`` field of ``Desc`` is ``true``.
2. Goto REJECT, If the ``[[Writable]]`` field of ``current`` is
``false``, and the ``[[Value]]`` field of ``Desc`` is present, and
``SameValue(Desc.[[Value]], current.[[Value]])`` is ``false``.
b. Goto VALIDATED.
9. Else, ``IsAccessorDescriptor(current)`` and ``IsAccessorDescriptor(Desc)``
are both ``true`` so,
a. If the ``[[Configurable]]`` field of ``current`` is ``false``, then
1. Goto REJECT, if the ``[[Set]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Set]], current.[[Set]])`` is ``false``.
2. Goto REJECT, if the ``[[Get]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Get]], current.[[Get]])`` is ``false``.
b. Goto VALIDATED.
10. **VALIDATED:** For each attribute field of ``Desc`` that is present,
set the correspondingly named attribute of the property named ``P``
of object ``O`` to the value of the field.
11. **SUCCESS:** Return ``true``.
12. **REJECT**: If ``Throw`` is ``true``, then throw a ``TypeError``
exception, otherwise return ``false``.
Analysis of Array object [[DefineOwnProperty]]
----------------------------------------------
The ``Array`` variant for ``[[DefineOwnProperty]]`` is described in
E5 Section 15.4.5.1. The variant *seems* to be essentially a pre-check
for ``length`` and array index properties before the default algorithm
runs (see steps 1-4 of the variant).
However, it's much more complex than that, because the variant algorithm
makes multiple calls to the default algorithm.
Let's look at the variant algorithm first (here we assume ``O`` is an
``Array`` with exotic behavior, so no check is made for exotic behavior):
1. Let ``oldLenDesc`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``O`` passing ``"length"`` as the argument. The
result will never be ``undefined`` or an accessor descriptor because
``Array`` objects are created with a length data property that cannot
be deleted or reconfigured.
2. Let ``oldLen`` be ``oldLenDesc.[[Value]]``.
(Note that ``oldLen`` is guaranteed to be a unsigned 32-bit integer.)
3. If ``P`` is ``"length"``, then
a. If the ``[[Value]]`` field of ``Desc`` is absent, then
1. Return the result of calling the default ``[[DefineOwnProperty]]``
internal method (E5 Section 8.12.9) on ``O`` passing ``"length"``,
``Desc``, and ``Throw`` as arguments.
b. Let ``newLenDesc`` be a copy of ``Desc``.
c. Let ``newLen`` be ``ToUint32(Desc.[[Value]])``.
d. If ``newLen`` is not equal to ``ToNumber(Desc.[[Value]])``, throw a
``RangeError`` exception.
e. Set ``newLenDesc.[[Value]]`` to ``newLen``.
f. If ``newLen`` >= ``oldLen``, then
1. Return the result of calling the default ``[[DefineOwnProperty]]``
internal method (E5 Section 8.12.9) on ``O`` passing ``"length"``,
``newLenDesc``, and ``Throw`` as arguments.
g. Reject if ``oldLenDesc.[[Writable]]`` is ``false``.
h. If ``newLenDesc.[[Writable]]`` is absent or has the value ``true``,
let ``newWritable`` be ``true``.
i. Else,
1. Need to defer setting the ``[[Writable]]`` attribute to ``false`` in
case any elements cannot be deleted.
2. Let ``newWritable`` be ``false``.
3. Set ``newLenDesc.[[Writable]]`` to ``true``.
j. Let ``succeeded`` be the result of calling the default
``[[DefineOwnProperty]]`` internal method (E5 Section 8.12.9) on ``O``
passing ``"length"``, ``newLenDesc``, and ``Throw`` as arguments.
k. If ``succeeded`` is ``false``, return ``false``.
l. While ``newLen`` < ``oldLen`` repeat,
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``
as arguments.
3. If ``canDelete`` is ``false``, then:
a. Set ``newLenDesc.[[Value]`` to ``oldLen+1``.
b. If ``newWritable`` is ``false``, set ``newLenDesc.[[Writable]`` to
``false``.
c. Call the default ``[[DefineOwnProperty]]`` internal method (E5
Section 8.12.9) on ``O`` passing ``"length"``, ``newLenDesc``, and
``false`` as arguments.
d. Reject.
m. If ``newWritable`` is ``false``, then
1. Call the default ``[[DefineOwnProperty]]`` internal method (E5 Section
8.12.9) on ``O`` passing ``"length"``, Property Descriptor
``{[[Writable]]: false}``, and ``false`` as arguments. This call will
always return ``true``.
n. Return ``true``.
4. Else if ``P`` is an array index (E5 Section 15.4), then:
a. Let ``index`` be ``ToUint32(P)``.
b. Reject if ``index`` >= ``oldLen`` and ``oldLenDesc.[[Writable]]`` is
``false``.
c. Let ``succeeded`` be the result of calling the default
``[[DefineOwnProperty]]`` internal method (E5 Section 8.12.9) on ``O``
passing ``P``, ``Desc``, and ``false`` as arguments.
d. Reject if ``succeeded`` is ``false``.
e. If ``index`` >= ``oldLen``:
1. Set ``oldLenDesc.[[Value]]`` to ``index + 1``.
2. Call the default ``[[DefineOwnProperty]]`` internal method (E5 Section
8.12.9) on ``O`` passing ``"length"``, ``oldLenDesc``, and ``false``
as arguments. This call will always return ``true``.
f. Return ``true``.
5. Return the result of calling the default ``[[DefineOwnProperty]]``
internal method (E5 Section 8.12.9) on ``O`` passing ``P``, ``Desc``,
and ``Throw`` as arguments.
Notes:
* In E5 Section 15.4.5.1 step 3.l.ii - 3.l.iii the temporary variable
``cannotDelete`` seems to be misused; it should probably be ``canDelete``
and the check in step iii should read "if ``canDelete`` is ``false`` ...".
* Step 5 is the default behavior, assuming nothing "captured" the call
before.
* Unfortunately steps 3 and 4 call the default ``[[DefineOwnProperty]]``
internally (multiple times). We'd like to avoid this, to get a
non-recursive implementation. This requires some major restatements.
Let's look at the calls to the default ``[[DefineOwnProperty]]`` (other
than step 5) to see what could be done about them.
First, for ``P`` == ``length``:
* Step 3.a.1:
If ``Desc.[[Value]]`` is absent, call the default algorithm.
This is equivalent to:
+ Jumping to step 5.
* Step 3.f.1:
If ``newLen`` validation succeeds and new length is not shorter
than previous, call the default algorithm with a modified
property descriptor, ``newLenDesc``. The new property descriptor
is a copy of the original, with ``[[Value]]`` changed to the
normalized and numeric (32-bit unsigned integer) length value.
This is equivalent to:
+ Doing length validation and coercion
+ Checking that the new length is not shorter than previous;
and if so, forcing ``Desc.[[Value]]`` to ``newLen``, and
then jumping to step 5.
+ Note: the caller's view of ``Desc`` must not change, so ``Desc``
cannot be a "pass by reference" value.
* Step 3.f.j:
Here ``newLen`` validation has succeeded, and the new length is shorter
than previous. Also, ``Desc.[[Writable]]`` may have been fudged.
The changes so far are "committed" to ``"length"`` property using the
default call.
Note that this call also has the important effect of checking that
the default algorithm is expected to succeed before we touch any of
the array elements.
This is equivalent to:
+ Doing the ``newWritable`` fudging to ``Desc``, and keeping
``newWritable`` for later.
+ Jumping to step 5.
+ Adding a post-step to the default algorithm for steps 3.k - 3.m.
* Step 3.l.3.c:
Here we've started to "shorten" the array but run into a non-deletable
element. The ``"length"`` property is updated with the actual final
length, and ``Desc.[[Writable]]`` is fudged back to its original,
requested value.
This is equivalent to:
+ Fudging both ``[[Value]]`` and ``[[Writable]]`` of ``Desc``.
+ Jumping to step 5.
* Step 3.m:
Here a pending write protection is finally implemented by calling
the default ``[[DefineOwnProperty]]`` with a property descriptor
requesting only that the property be changed to non-writable.
This is equivalent to:
+ Adding a "pending write protect" flag and jumping to 5.
+ Modifying the standard algorithm to recognize a "pending
write protect" after standard property modifications and
checks are complete.
Then, for the case when ``P`` is a valid array index:
* Step 4.c:
The index has been coerced and validated; the algorithm rejects if the
array index would require that the array ``length`` be increased but
``length`` is write protected.
This is equivalent to:
+ Doing the pre-checks for index vs. ``length``.
+ Jumping to step 5.
+ Adding a post-step to the standard algorithm to handle steps 4.d - 4.f.
* Step 4.e.2:
This is a step which happens after the default algorithm has finished
without errors. If so, and the array index extended the array ``length``,
the array ``length`` is updated to reflect this. This is expected to
always succeed.
This is equivalent to:
+ Adding a post-step to the standard algorithm.
A draft of modifications to the standard algorithm to avoid recursive
calls could be something like:
* Add a pre-step with:
+ Check for ``P`` == ``length``, and:
- If ``Desc.[[Value]]`` missing, use default algorithm
- ``newLen`` validation and updating of ``Desc.[[Value]]``
- If new length is not shorter than old length, default algorithm
with the modified ``Desc`` can be used
- Possible fudging of ``Desc.[[Writable]]`` and check for
setting ``pendingWriteProtect`` (set if ``newWritable``
is ``false``)
- If new length is shorter than old length, run the default
algorithm successfully first before touching array elements
+ Check for ``P`` being a valid array index, and:
- Pre-checks for index vs. ``length``
* Modify the standard algorithm:
+ Continuing with the post-step if the standard algorithm succeeds.
* Add a post-step with:
+ Check whether we have a pending array "shortening", i.e.
``P`` was ``"length"``, and the new length is shorter than
old.
- A complex algorithm for shortening the array needs to run.
This algorithm may either indicate success or failure, and
returns the actual final length of the array which may
differ from the requested one if a non-configurable element
prevents deletion.
+ Check for ``pendingWriteProtect``; if so, write protect the
target property (this is for step 3.m).
+ Check whether ``P`` was an array index which should increase
the length of the array.
- If so, we've already checked in the pre-step that the length
can be updated. So, update the pending new length value.
The algorithm for shortening the array is not inlined (it is a separate
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
---------------------------------------
Incorporating the approach for adding a pre- and post-processing phase
we get something like:
1. Set ``pendingWriteProtect`` to ``false``.
2. If ``O`` is not an ``Array`` object, goto SKIPARRAY.
3. Let ``oldLenDesc`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``O`` passing ``"length"`` as the argument. The
result will never be ``undefined`` or an accessor descriptor because
``Array`` objects are created with a length data property that cannot
be deleted or reconfigured.
4. Let ``oldLen`` be ``oldLenDesc.[[Value]]``.
(Note that ``oldLen`` is guaranteed to be a unsigned 32-bit integer.)
5. If ``P`` is ``"length"``, then
a. If the ``[[Value]]`` field of ``Desc`` is absent, then goto SKIPARRAY.
b. Let ``newLen`` be ``ToUint32(Desc.[[Value]])``.
c. If ``newLen`` is not equal to ``ToNumber(Desc.[[Value]])``, goto
REJECTRANGE.
d. Set ``Desc.[[Value]]`` to ``newLen``.
e. If ``newLen`` >= ``oldLen``, then goto SKIPARRAY.
f. Goto REJECT if ``oldLenDesc.[[Writable]]`` is ``false``.
g. If ``Desc.[[Writable]]`` has the value ``false``:
1. Need to defer setting the ``[[Writable]]`` attribute to ``false``
in case any elements cannot be deleted.
2. Set ``pendingWriteProtect`` to ``true``.
3. Set ``Desc.[[Writable]]`` to ``true``.
h. Goto SKIPARRAY. (Rest of the processing happens in the post-step.)
6. Else if ``P`` is an array index (E5 Section 15.4), then:
a. Let ``index`` be ``ToUint32(P)``.
b. Goto REJECT if ``index`` >= ``oldLen`` and ``oldLenDesc.[[Writable]]``
is ``false``.
c. Goto SKIPARRAY. (Rest of the processing happens in the post-step.)
7. **SKIPARRAY**:
Let ``current`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``O`` with property name ``P``.
8. Let ``extensible`` be the value of the ``[[Extensible]]`` internal
property of ``O``.
9. If ``current`` is ``undefined``:
a. If ``extensible`` is ``false``, then goto REJECT.
b. If ``IsGenericDescriptor(Desc)`` or ``IsDataDescriptor(Desc)`` is
``true``, then
1. Create an own data property named ``P`` of object ``O`` whose
``[[Value]]``, ``[[Writable]]``, ``[[Enumerable]]`` and
``[[Configurable]]`` attribute values are described by ``Desc``.
If the value of an attribute field of ``Desc`` is absent, the
attribute of the newly created property is set to its default
value.
c. Else, ``Desc`` must be an accessor Property Descriptor so,
1. Create an own accessor property named ``P`` of object ``O`` whose
``[[Get]]``, ``[[Set]]``, ``[[Enumerable]]`` and ``[[Configurable]]``
attribute values are described by ``Desc``. If the value of an
attribute field of ``Desc`` is absent, the attribute of the newly
created property is set to its default value.
d. Goto SUCCESS.
10. Goto SUCCESS, if every field in ``Desc`` also occurs in ``current``
and the value of every field in ``Desc`` is the same value as the
corresponding field in ``current`` when compared using the ``SameValue``
algorithm (E5 Section 9.12). (This also covers the case where
every field in ``Desc`` is absent.)
11. If the ``[[Configurable]]`` field of ``current`` is ``false`` then
a. Goto REJECT, if the ``[[Configurable]]`` field of ``Desc`` is true.
b. Goto REJECT, if the ``[[Enumerable]]`` field of ``Desc`` is present
and the ``[[Enumerable]]`` fields of ``current`` and ``Desc`` are the
Boolean negation of each other.
12. If ``IsGenericDescriptor(Desc)`` is ``true``, then goto VALIDATED.
13. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
have different results, then
a. Goto REJECT, if the ``[[Configurable]]`` field of ``current`` is
``false``.
b. If ``IsDataDescriptor(current)`` is true, then
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
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
default values.
d. Goto VALIDATED.
14. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
are both true, then
a. If the ``[[Configurable]]`` field of ``current`` is ``false``, then
1. Goto REJECT, if the ``[[Writable]]`` field of ``current`` is
``false`` and the ``[[Writable]]`` field of ``Desc`` is ``true``.
2. Goto REJECT, If the ``[[Writable]]`` field of ``current`` is
``false``, and the ``[[Value]]`` field of ``Desc`` is present, and
``SameValue(Desc.[[Value]], current.[[Value]])`` is ``false``.
b. Goto VALIDATED.
15. Else, ``IsAccessorDescriptor(current)`` and ``IsAccessorDescriptor(Desc)``
are both ``true`` so,
a. If the ``[[Configurable]]`` field of ``current`` is ``false``, then
1. Goto REJECT, if the ``[[Set]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Set]], current.[[Set]])`` is ``false``.
2. Goto REJECT, if the ``[[Get]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Get]], current.[[Get]])`` is ``false``.
b. Goto VALIDATED.
16. **VALIDATED:**
For each attribute field of ``Desc`` that is present, set the
correspondingly named attribute of the property named ``P`` of object
``O`` to the value of the field.
17. **SUCCESS:**
If ``O`` is an ``Array`` object:
a. If ``P`` is ``"length"``, and ``newLen`` < ``oldLen``, then:
1. Let ``shortenSucceeded``, ``finalLen`` be the result of calling the
internal helper ``ShortenArray()`` with ``oldLen`` and ``newLen``.
2. Update the property (``"length"``) value to ``finalLen``.
3. If ``pendingWriteProtect`` is ``true``, update the property
(``"length"``) to have ``[[Writable]] = false``.
4. Goto REJECT, if ``shortenSucceeded`` is ``false``.
b. If ``P`` is an array index and ``index`` >= ``oldLen``:
1. Update the ``"length"`` property of ``O`` to the value ``index + 1``.
This always succeeds, because we've checked in the pre-step that the
``"length"`` is writable, and since ``P`` is an array index property,
the length must still be writable here.
18. Return ``true``.
19. **REJECT**:
If ``Throw`` is ``true``, then throw a ``TypeError`` exception,
otherwise return ``false``.
20. **REJECTRANGE**:
Throw a ``RangeError`` exception. Note that this is unconditional
(thrown even if ``Throw`` is ``false``).
Adding arguments object exotic behavior
---------------------------------------
The exotic ``[[DefineOwnProperty]]`` behavior for an arguments object
containing a ``[[ParameterMap]]`` is described in E5 Section 10.6.
The variant algorithm essentially first runs the default algorithm.
If the default algorithm finishes successfully, the variant will then
maintain the parameter map and possibly perform a setter call.
This is easy to incorporate and results in:
1. Set ``pendingWriteProtect`` to ``false``.
2. If ``O`` is not an ``Array`` object, goto SKIPARRAY.
3. Let ``oldLenDesc`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``O`` passing ``"length"`` as the argument. The
result will never be ``undefined`` or an accessor descriptor because
``Array`` objects are created with a length data property that cannot
be deleted or reconfigured.
4. Let ``oldLen`` be ``oldLenDesc.[[Value]]``.
(Note that ``oldLen`` is guaranteed to be a unsigned 32-bit integer.)
5. If ``P`` is ``"length"``, then
a. If the ``[[Value]]`` field of ``Desc`` is absent, then goto SKIPARRAY.
b. Let ``newLen`` be ``ToUint32(Desc.[[Value]])``.
c. If ``newLen`` is not equal to ``ToNumber(Desc.[[Value]])``, goto
REJECTRANGE.
d. Set ``Desc.[[Value]]`` to ``newLen``.
e. If ``newLen`` >= ``oldLen``, then goto SKIPARRAY.
f. Goto REJECT if ``oldLenDesc.[[Writable]]`` is ``false``.
g. If ``Desc.[[Writable]]`` has the value ``false``:
1. Need to defer setting the ``[[Writable]]`` attribute to ``false``
in case any elements cannot be deleted.
2. Set ``pendingWriteProtect`` to ``true``.
3. Set ``Desc.[[Writable]]`` to ``true``.
h. Goto SKIPARRAY. (Rest of the processing happens in the post-step.)
6. Else if ``P`` is an array index (E5 Section 15.4), then:
a. Let ``index`` be ``ToUint32(P)``.
b. Goto REJECT if ``index`` >= ``oldLen`` and ``oldLenDesc.[[Writable]]``
is ``false``.
c. Goto SKIPARRAY. (Rest of the processing happens in the post-step.)
7. **SKIPARRAY**:
Let ``current`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``O`` with property name ``P``.
8. Let ``extensible`` be the value of the ``[[Extensible]]`` internal
property of ``O``.
9. If ``current`` is ``undefined``:
a. If ``extensible`` is ``false``, then goto REJECT.
b. If ``IsGenericDescriptor(Desc)`` or ``IsDataDescriptor(Desc)`` is
``true``, then
1. Create an own data property named ``P`` of object ``O`` whose
``[[Value]]``, ``[[Writable]]``, ``[[Enumerable]]`` and
``[[Configurable]]`` attribute values are described by ``Desc``.
If the value of an attribute field of ``Desc`` is absent, the
attribute of the newly created property is set to its default
value.
c. Else, ``Desc`` must be an accessor Property Descriptor so,
1. Create an own accessor property named ``P`` of object ``O`` whose
``[[Get]]``, ``[[Set]]``, ``[[Enumerable]]`` and ``[[Configurable]]``
attribute values are described by ``Desc``. If the value of an
attribute field of ``Desc`` is absent, the attribute of the newly
created property is set to its default value.
d. Goto SUCCESS.
10. Goto SUCCESS, if every field in ``Desc`` also occurs in ``current``
and the value of every field in ``Desc`` is the same value as the
corresponding field in ``current`` when compared using the ``SameValue``
algorithm (E5 Section 9.12). (This also covers the case where
every field in ``Desc`` is absent.)
11. If the ``[[Configurable]]`` field of ``current`` is ``false`` then
a. Goto REJECT, if the ``[[Configurable]]`` field of ``Desc`` is true.
b. Goto REJECT, if the ``[[Enumerable]]`` field of ``Desc`` is present
and the ``[[Enumerable]]`` fields of ``current`` and ``Desc`` are the
Boolean negation of each other.
12. If ``IsGenericDescriptor(Desc)`` is ``true``, then goto VALIDATED.
13. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
have different results, then
a. Goto REJECT, if the ``[[Configurable]]`` field of ``current`` is
``false``.
b. If ``IsDataDescriptor(current)`` is true, then
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
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
default values.
d. Goto VALIDATED.
14. Else, if ``IsDataDescriptor(current)`` and ``IsDataDescriptor(Desc)``
are both true, then
a. If the ``[[Configurable]]`` field of ``current`` is ``false``, then
1. Goto REJECT, if the ``[[Writable]]`` field of ``current`` is
``false`` and the ``[[Writable]]`` field of ``Desc`` is ``true``.
2. Goto REJECT, If the ``[[Writable]]`` field of ``current`` is
``false``, and the ``[[Value]]`` field of ``Desc`` is present, and
``SameValue(Desc.[[Value]], current.[[Value]])`` is ``false``.
b. Goto VALIDATED.
15. Else, ``IsAccessorDescriptor(current)`` and ``IsAccessorDescriptor(Desc)``
are both ``true`` so,
a. If the ``[[Configurable]]`` field of ``current`` is ``false``, then
1. Goto REJECT, if the ``[[Set]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Set]], current.[[Set]])`` is ``false``.
2. Goto REJECT, if the ``[[Get]]`` field of ``Desc`` is present and
``SameValue(Desc.[[Get]], current.[[Get]])`` is ``false``.
b. Goto VALIDATED.
16. **VALIDATED:**
For each attribute field of ``Desc`` that is present, set the
correspondingly named attribute of the property named ``P`` of object
``O`` to the value of the field.
17. **SUCCESS:**
If ``O`` is an ``Array`` object:
a. If ``P`` is ``"length"``, and ``newLen`` < ``oldLen``, then:
1. Let ``shortenSucceeded``, ``finalLen`` be the result of calling the
internal helper ``ShortenArray()`` with ``oldLen`` and ``newLen``.
2. Update the property (``"length"``) value to ``finalLen``.
3. If ``pendingWriteProtect`` is ``true``, update the property
(``"length"``) to have ``[[Writable]] = false``.
4. Goto REJECT, if ``shortenSucceeded`` is ``false``.
b. If ``P`` is an array index and ``index`` >= ``oldLen``:
1. Update the ``"length"`` property of ``O`` to the value ``index + 1``.
This always succeeds, because we've checked in the pre-step that the
``"length"`` is writable, and since ``P`` is an array index property,
the length must still be writable here.
18. If ``O`` is an arguments object which has a ``[[ParameterMap]]``
internal property:
a. Let ``map`` be the value of the ``[[ParameterMap]]`` internal property
of the arguments object.
b. If the result of calling the ``[[GetOwnProperty]]`` internal method
of ``map`` passing ``P`` as the argument is not ``undefined``, then:
1. If ``IsAccessorDescriptor(Desc)`` is ``true``, then:
a. Call the ``[[Delete]]`` internal method of ``map`` passing ``P``,
and ``false`` as the arguments. (This removes the magic binding
for ``P``.)
2. Else (``Desc`` may be generic or data descriptor):
a. If ``Desc.[[Value]]`` is present, then:
1. Call the ``[[Put]]`` internal method of ``map`` passing ``P``,
``Desc.[[Value]]``, and ``Throw`` as the arguments. (This
updates the bound variable value.)
b. If ``Desc.[[Writable]]`` is present and its value is ``false``,
then:
1. Call the ``[[Delete]]`` internal method of ``map`` passing ``P``
and ``false`` as arguments. (This removes the magic binding
for ``P``, and must happen after a possible update of the
variable value.)
19. Return ``true``.
20. **REJECT**:
If ``Throw`` is ``true``, then throw a ``TypeError`` exception,
otherwise return ``false``.
21. **REJECTRANGE**:
Throw a ``RangeError`` exception. Note that this is unconditional
(thrown even if ``Throw`` is ``false``).
Final version
-------------
(See above, currently no additional cleanup.)
Delete
======
Related E5 sections:
* E5 Section 8.12.7: default algorithm
* E5 Section 10.5: arguments object
Default algorithm
-----------------
1. Let ``desc`` be the result of calling the ``[[GetOwnProperty]]`` internal
method of ``O`` with property name ``P``.
2. If ``desc`` is ``undefined``, then return ``true``.
3. If ``desc.[[Configurable]]`` is ``true``, then
a. Remove the own property with name ``P`` from ``O``.
b. Return ``true``.
4. Else if ``Throw`` is true, then throw a ``TypeError`` exception.
5. Return ``false``.
Adding arguments object exotic behavior
---------------------------------------
The exotic ``[[Delete]]`` behavior for an arguments object containing a
``[[ParameterMap]]`` is described in E5 Section 10.6.
The variant algorithm essentially first runs the default algorithm.
If the default algorithm finishes successfully, the variant will then
possibly delete a magic variable binding.
This is easy to incorporate and results in:
1. Let ``desc`` be the result of calling the ``[[GetOwnProperty]]`` internal
method of ``O`` with property name ``P``.
2. If ``desc`` is ``undefined``, then goto SUCCESS.
3. If ``desc.[[Configurable]]`` is ``true``, then
a. Remove the own property with name ``P`` from ``O``.
b. Goto SUCCESS.
4. Else if ``Throw`` is true, then throw a ``TypeError`` exception.
5. Return ``false``.
6. **SUCCESS:**
If ``O`` is an arguments object which has a ``[[ParameterMap]]``
internal property:
a. Let ``map`` be the value of the ``[[ParameterMap]]`` internal property
of the arguments object.
b. If the result of calling the ``[[GetOwnProperty]]`` internal method
of ``map`` passing ``P`` as the argument is not ``undefined``, then:
1. Call the ``[[Delete]]`` internal method of ``map`` passing ``P``,
and ``false`` as the arguments. (This removes the magic binding
for ``P``.)
7. Return ``true``.
Notes:
* In steps 2, if ``desc`` is ``undefined``, it seems unnecessary to go to
step 6 to check the arguments parameter map. Can a magically bound
property exist in the parameter map with the underlying property having
been deleted somehow?
Final version
-------------
(See above, currently no additional cleanup.)
HasInstance
===========
Background
----------
The ``[[HasInstance]]`` internal method is referred to in the following
parts of the E5 specification:
* Section 8.6.2: ``[[HasInstance]]`` is introduced as a ``SpecOp(any)``
-> ``Boolean`` internal method. Only ``Function`` objects have a
``[[HasInstance]]`` method.
* Section 11.8.6: the ``instanceof`` operator, which is the only "caller"
for ``[[HasInstance]]`` in the E5 specification.
* Section 13.2: when ``Function`` objects are created, ``[[HasInstance]]``
is set to the algorithm in Section 15.3.5.3.
* Section 15.3.4.5: when bound functions are created using
``Function.prototype.bind()``, ``[[HasInstance]]`` is set to the
algorithm in Section 15.3.4.5.3.
* Section 15.3.4.5.3: ``[[HasInstance]]`` for bound functions.
* Section 15.3.5.3: ``[[HasInstance]]`` for ordinary (non-bound)
functions.
The ``[[HasInstance]]`` for ordinary functions is (``F`` is the function
object and ``V`` is the argument value, "V instanceof F"):
1. If ``Type(V)`` is not an ``Object``, return ``false``.
2. Let ``O`` be the result of calling the ``[[Get]]`` internal method of
``F`` with property name ``"prototype"``.
(Note: this is the external prototype, not the internal one.)
3. If ``Type(O)`` is not ``Object``, throw a ``TypeError`` exception.
4. Repeat
a. Let ``V`` be the value of the ``[[Prototype]]`` internal property of
``V``.
b. If ``V`` is ``null``, return ``false``.
c. If ``O`` and ``V`` refer to the same object, return ``true``.
Notes:
* In step 2, we're fetching the *external prototype*, which may have any
values. It might also have been changed after the instance was created.
* Step 4.a steps the internal prototype chain once before the first check.
The ``[[HasInstance]]`` for bound functions is:
1. Let ``target`` be the value of ``F``\ 's ``[[TargetFunction]]`` internal
property.
2. If ``target`` has no ``[[HasInstance]]`` internal method, a ``TypeError``
exception is thrown.
3. Return the result of calling the ``[[HasInstance]]`` internal method of
``target`` providing ``V`` as the argument.
Notes:
* In step 3, the ``target`` may be another bound function, so we may need
to follow an arbitrary number of bound functions before ending up with an
actual function object.
Combined algorithm
------------------
The two ``[[HasInstance]]`` methods (for bound and non-bound functions)
can be combined to yield:
1. While ``F`` is a bound function:
a. Set ``F`` to the value of ``F``\ 's ``[[TargetFunction]]`` internal
property.
b. If ``F`` has no ``[[HasInstance]]`` internal method, throw a
``TypeError`` exception.
(Note: ``F`` can be another bound function, so we loop until we find
the non-bound actual function.)
2. If ``Type(V)`` is not an ``Object``, return ``false``.
3. Let ``O`` be the result of calling the ``[[Get]]`` internal method of
``F`` with property name ``"prototype"``.
(Note: this is the external prototype, not the internal one.)
4. If ``Type(O)`` is not ``Object``, throw a ``TypeError`` exception.
5. Repeat
a. Let ``V`` be the value of the ``[[Prototype]]`` internal property of
``V``.
b. If ``V`` is ``null``, return ``false``.
c. If ``O`` and ``V`` refer to the same object, return ``true``.
Final version
-------------
(See above, currently no additional cleanup.)