mirror of https://github.com/svaarala/duktape.git
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.
1705 lines
59 KiB
1705 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.)
|
|
|