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.

1541 lines
50 KiB

==========================
Preliminary algorithm work
==========================
In this section we look at the internal algorithms and do some preliminary
work of restating them by: inlining algorithms, merging algorithms, looking
at algorithm behavior with some fixed parameters, etc. Tricky issues of
algorithms are also discussed to some extent.
The purpose of this section is to provide raw material for the sections
dealing with actual exposed algorithms.
CanPut
======
``[[CanPut]]`` indicates whether a ``[[Put]]`` would cause an error or not.
An error is possible in the following cases for object ``O``, property ``P``:
* ``O`` has ``P`` as own property, it is a plain property, and
``[[Writable]]`` is false
* ``O`` has ``P`` as own property, it is an accessor property, and is
missing the ``[[Set]]`` function
* ``P`` is found in ``O``\ 's prototype chain (not in ``O``), it is a plain
property, and either ``O.[[Extensible]]`` or property ``[[Writable]]``
is false
* ``P`` is found in ``O``\ 's prototype chain (not in ``O``), it is an
accessor property, and is missing the ``[[Set]]`` function
* ``P`` is not found in ``O``\ 's prototype chain, and ``O.[[Extensible]]``
is false
The algorithm in E5 Section 8.12.4 deals with the "own property" case first
and then looks up the property again from the prototype chain. If a
property is found, the only difference is between steps 2.b and 8.a: the
``[[Extensible]]`` property of the original object ``O`` must be checked
if the property is found in an ancestor, as a ``[[Put]]`` would actually go
into ``O``, extending its set of properties.
The following simplified (and restated) variant should be equivalent and
requires only one prototype chain lookup:
1. ``desc`` = ``O.[[GetProperty]](P)``.
2. If ``desc`` is ``undefined``, return ``O.[[Extensible]]``.
3. If ``IsAccessorDescriptor(desc)``:
a. If ``desc.[[Set]]`` is ``undefined``, return ``false``.
b. Else, return ``true``.
4. Else, ``desc`` must be a data descriptor:
a. (**CHANGED:**) If ``desc`` was not found in the original object ``O``,
and ``O.[[Extensible]]`` is ``false``, return ``false``.
b. Return ``desc.[[Writable]]``.
The step denoted with CHANGED reconciles steps 2.b and 8.a of the original
algorithm. The "found in the original object ``O``" part can be implemented
in many ways:
* Compare object pointers of original object vs. object where property was
found: works if an object occurs at most once in a prototype chain (which
should always be the case)
* The prototype chain lookup ``[[GetProperty]]`` also returns an "inherited"
flag
GetProperty
===========
``[[GetProperty]]`` is a very straightforward wrapper over
``[[GetOwnProperty]]`` which follows the prototype chain. Like
``[[GetOwnProperty]]``, it returns a descriptor.
There is no exotic behavior for ``[[GetProperty]]``, the exotic behaviors
only affect ``[[GetOwnProperty]]`` which is called during ``[[GetProperty]]``.
Original algorithm
------------------
1. Let ``prop`` be the result of calling the ``[[GetOwnProperty]]`` internal
method of ``O`` with property name ``P``.
2. If ``prop`` is not ``undefined``, return ``prop``.
3. Let ``proto`` be the value of the ``[[Prototype]]`` internal property of
``O``.
4. If ``proto`` is ``null``, return ``undefined``.
5. Return the result of calling the ``[[GetProperty]]`` internal method of
``proto`` with argument ``P``.
Eliminating recursion
---------------------
This is better unwound into a loop (using ``desc`` instead of ``prop``, as
it is more descriptive):
1. Let ``curr`` be ``O``.
2. While ``curr`` is not ``null``:
a. Let ``desc`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``curr`` with property name ``P``.
b. If ``desc`` is not ``undefined``, return ``desc``.
c. Let ``curr`` be the value of the ``[[Prototype]]`` internal property of
``curr``.
3. Return ``undefined``.
Less nested form
----------------
The following is a less "nested" form (note that ``curr`` is guaranteed to
be non-null in the first loop):
1. Let ``curr`` be ``O``.
2. **NEXT:**
Let ``desc`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``curr`` with property name ``P``.
3. If ``desc`` is not ``undefined``, return ``desc``.
4. Let ``curr`` be the value of the ``[[Prototype]]`` internal property of
``curr``.
5. If ``curr`` is not ``null``, goto NEXT.
6. Return ``undefined``
.. note:: A maximum prototype chain depth should be imposed as a safeguard
against loops. Note that while it should be impossible to create
prototype loops with Ecmascript code alone, creating them from C
code *is* possible.
GetProperty with default GetOwnProperty inlined
-----------------------------------------------
``[[GetOwnProperty]]`` is just creating the descriptor from whatever form
properties are stored. It has exotic behaviors, so the resulting function
is a bit complicated.
The inlined form for default ``[[GetOwnProperty]]`` is essentially:
1. ``curr`` = ``O``
2. **NEXT:**
If ``curr`` has own property ``P``:
a. Let ``D`` be a newly created Property Descriptor with no fields.
b. Let ``X`` be ``curr``\ ’s own property named P.
c. If ``X`` is a data property, then
1. Set ``D.[[Value]]`` to the value of ``X``\ ’s ``[[Value]]``
attribute.
2. Set ``D.[[Writable]]`` to the value of ``X``\ ’s ``[[Writable]]``
attribute.
d. Else ``X`` is an accessor property, so
1. Set ``D.[[Get]]`` to the value of ``X``\ ’s ``[[Get]]`` attribute.
2. Set ``D.[[Set]]`` to the value of ``X``\ ’s ``[[Set]]`` attribute.
e. Set ``D.[[Enumerable]]`` to the value of ``X``\ ’s ``[[Enumerable]]`` attribute.
f. Set ``D.[[Configurable]]`` to the value of ``X``\ ’s ``[[Configurable]]`` attribute.
g. Return ``D``.
3. Let ``curr`` be the value of the ``[[Prototype]]`` internal property of
``curr``.
4. If ``curr`` is not ``null``, goto NEXT.
5. Return ``undefined``
This is a relatively useless form, because exotic behaviors are missing.
GetProperty with complete GetOwnProperty inlined
------------------------------------------------
The following inlines ``[[GetOwnProperty]]`` with all exotic behaviors:
1. ``curr`` = ``O``
2. **NEXT:**
Let ``X`` be ``curr``\ ’s own property named ``P``.
If ``curr`` doesn’t have an own property with name ``P``:
a. If ``curr`` is not a ``String`` instance, goto NOTFOUND.
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. Goto NOTFOUND.
3. 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.
4. If ``curr`` 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.
5. Return ``D``.
6. **NOTFOUND:**
Let ``curr`` be the value of the ``[[Prototype]]`` internal property of
``curr``.
7. If ``curr`` is not ``null``, goto NEXT.
8. Return ``undefined``
.. note:: This implementation is currently *not* used. The implementation for
``[[GetOwnProperty]]`` is a separate helper. See ``duk_hobject_props.c``,
helper functions: ``get_own_property_desc()`` and ``get_property_desc()``.
Get
===
``[[Get]]`` is straightforward; it gets a property descriptor with
``[[GetProperty]]`` and then coerces it to a value.
Get with GetProperty inlined
============================
``[[Get]]`` was covered above when discussion exotic behaviors, so we'll
skip discussing it again here.
``[[Get]]`` is essentially a ``[[GetProperty]]`` followed by coercion of
the descriptor into a value. For a data descriptor, simply return its
``[[Value]]``. For a property accessor, simply call its ``[[Get]]``
function. The descriptor does not need to be created at all, as we're
just interested in the final value.
The following combines both ``[[GetOwnProperty]]`` and ``[[Get]]`` with
exotic behaviors:
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. ``curr`` = ``O``
3. **NEXT:**
Let ``X`` be ``curr``\ ’s own property named ``P``.
If ``curr`` doesn’t have an own property with name ``P``:
a. If ``curr`` is not a ``String`` instance, goto NOTFOUND.
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 ``len`` (a primitive number).
(No need to check for arguments object exotic
behavior or ``"caller"`` property exotic behavior.)
d. If ``P`` is an array index (E5 Section 15.4):
1. Let ``index`` be ``ToUint32(P)``.
2. If ``index`` < ``len``:
a. Return a primitive string of length 1, containing one character
from ``str`` at position ``index`` (zero based index).
(No need to check for arguments object exotic behavior or
``"caller"`` property exotic behavior.)
e. Goto NOTFOUND.
4. If ``X`` is a data property:
a. Set ``res`` to the value of ``X``\ ’s ``[[Value]]`` attribute.
b. Goto FOUND1
5. Else ``X`` is an accessor property:
a. Let ``getter`` be ``X``\ 's ``[[Get]]`` attribute.
b. If ``getter`` is ``undefined``:
1. Return ``undefined``.
(Note: arguments object exotic behavior for mapped variables cannot
apply: if the property is an accessor, it can never be in the arguments
object ``[[ParameterMap]]``. Also, the ``"caller"`` exotic behavior
does not apply, since the result ``undefined`` is not a strict mode
function. Thus, no "goto FOUND1" here.)
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.
d. Goto FOUND2.
(Note: arguments object exotic behavior for mapped variables cannot
apply: if the property is an accessor, it can never be in the arguments
object ``[[ParameterMap]]``. However, the ``"caller"`` exotic behavior
might apply, at FOUND2.)
6. **FOUND1**:
If ``curr`` 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 ``res`` to the result of calling the ``[[Get]]`` internal method
of ``map`` passing ``P`` as the argument.
7. **FOUND2**:
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.
8. Return ``res``.
9. **NOTFOUND:**
Let ``curr`` be the value of the ``[[Prototype]]`` internal property of
``curr``.
10. If ``curr`` is not ``null``, goto NEXT.
11. Return ``undefined``.
(Note: no need for exotic behavior checks here; e.g. result is not a
strict mode function.)
.. note:: The step 5.c gives the object as the ``this`` binding for the
getter call. When properties are actually accessed from Ecmascript
code, the wrappers (property accessor evaluation, ``GetValue()``)
have a different behavior: the primitive (uncoerced) object is
given as the ``this`` binding.
DefineOwnProperty callers
=========================
``[[DefineOwnProperty]]`` is defined in E5 Section 8.12.9.
It is a complex algorithm which allows the value and attributes of property
``P`` of object ``O`` to be changed. It is used for ``[[Put]]`` which is
performance relevant and should thus be "inlined" to the extent possible
(see special case analysis below). It is also used generically when
initializing newly created objects etc, which can also use a simplified
version.
Note: ``[[DefineOwnProperty]]`` allows some counterintuitive property
attributes changes to be made. The callers in the specification are
supposed to "guard" against these. For instance:
* A property which is non-configurable but writable *can* be changed
to non-writable (but not vice versa). Non-configurability does not
guarantee that changes cannot be made.
* A property which is configurable but not writable can have its value
changed by a ``[[DefineOwnProperty]]`` call. This is allowed because
a caller could simply change the property to writable, change its
value, and then change it back to non-writable (this is possible
because the property is configurable). The ``[[Put]]`` algorithms
prevents writing to a non-writable but configurable property with an
explicit check, ``[[CanPut]]``.
``[[DefineOwnProperty]]`` is referenced by the following property-related
internal algorithms:
* ``FromPropertyDescriptor``, E5 Section 8.10.4
* ``[[Put]]``, E5 Section 8.12.5
* Array's exotic ``[[DefineOwnProperty]]`` relies on the default one, E5
Section 15.4.5.1
* Argument object's exotic ``[[DefineOwnProperty]]`` relies on the default
one, E5 Section 10.6
It is used less fundamentally in many places, e.g. to initialize values
(list probably not complete):
* ``CreateMutableBinding``, E5 Section 10.2.1.2.2
* Arguments object setup, E5 Section 10.6
* Array initializer, E5 Section 11.1.4
* Object initializer, E5 Section 11.1.5
* Function object creation, E5 Section 13.2
* ``[[ThrowTypeError]]`` function object, E5 Section 13.2.3
* ``Object.getOwnPropertyNames``, E5 Section 15.2.3.4
* ``Object.defineProperty``, E5 Section 15.2.3.6
* ``Object.seal``, E5 Section 15.2.3.8
* ``Object.freeze``, E5 Section 15.2.3.9
* ``Object.keys``, E5 Section 15.2.3.14
* ``Function.prototype.bind``, E5 Section 15.3.4.5
* ``Array.prototype.concat``, E5 Section 15.4.4.4
* ``Array.prototype.slice``, E5 Section 15.4.4.10
* ``Array.prototype.splice``, E5 Section 15.4.4.12
* ``Array.prototype.map``, E5 Section 15.4.4.19
* ``Array.prototype.filter``, E5 Section 15.4.4.20
* ``String.prototype.match``, E5 Section 15.5.4.10
* ``String.prototype.split``, E5 Section 15.5.4.14
* ``RegExp.prototype.exec``, E5 Section 15.10.6.2
* ``JSON.parse``, E5 Section 15.12.2
* ``JSON.stringify``, E5 Section 15.12.3
DefineOwnProperty for an existing property in Put
=================================================
This case arises when a ``[[Put]]`` is performed and the property already
exists. The property value is updated with a call to
``[[DefineOwnProperty]]`` with a property descriptor only containing
``[[Value]]``. See E5 Section 8.12.5, step 3.
We can assume that:
* The property exists (checked by ``[[Put]]``)
* The property is a data property (checked by ``[[Put]]``)
* The property cannot be non-writable (checked by ``[[Put]]``, using
``[[CanPut]]``)
* The property descriptor is a data descriptor
* The property descriptor is of the form: ``{ [[Value]]: val }``
* Because the property exists, the ``length`` of an ``Array`` object
cannot change by a write to an array index; however, a write to
``"length"`` may delete array elements
More specifically, we know that in the ``[[DefineOwnProperty]]`` algorithm:
* ``current`` is not ``undefined``
* ``IsGenericDescriptor(current)`` is ``false``
* ``IsDataDescriptor(current)`` is ``true``
* ``IsAccessorDescriptor(current)`` is ``false``
* ``IsGenericDescriptor(Desc)`` is ``false``
* ``IsDataDescriptor(Desc)`` is ``true``
* ``IsAccessorDescriptor(Desc)`` is ``false``
Taking the ``[[DefineOwnProperty]]`` with all exotic behaviors included,
using the above assumptions, eliminating any unnecessary steps, cleaning
up and clarifying, we get:
1. If ``O`` is an ``Array`` object, and ``P`` is ``"length"``, then:
a. Let ``newLen`` be ``ToUint32(Desc.[[Value]])``.
b. If ``newLen`` is not equal to ``ToNumber(Desc.[[Value]])``, throw
a ``RangeError`` exception. Note that this is unconditional (thrown
even if ``Throw`` is ``false``).
c. 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.
d. Let ``oldLen`` be ``oldLenDesc.[[Value]]``. (Note that ``oldLen``
is guaranteed to be a unsigned 32-bit integer.)
e. If ``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. Goto REJECT, if ``shortenSucceeded`` is ``false``.
4. Return.
f. Update the property (``"length"``) value to ``newLen``.
g. Return.
2. Set the ``[[Value]]`` attribute of the property named ``P`` of object
``O`` to the value of ``Desc.[[Value]]``. (Since it is side effect
free to update the value with the same value, no check for that case
is needed.)
3. 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 ``[[Put]]`` internal method of ``map`` passing ``P``,
``Desc.[[Value]]``, and ``Throw`` as the arguments. (This
updates the bound variable value.)
4. Return ``true``.
Note that step 1 combines the pre-step and post-step for an ``Array``
object ``length`` exotic behavior. This is only possible if we know
beforehand that the ``"length"`` property is writable (so that the
write never fails and we always reach the post-step).
We'll refine one more time, by eliminating references to ``Desc`` and using
``val`` to refer to ``Desc.[[Value]]``:
1. If ``O`` is an ``Array`` object, and ``P`` is ``"length"``, then:
a. Let ``newLen`` be ``ToUint32(val)``.
b. If ``newLen`` is not equal to ``ToNumber(val)``, throw a ``RangeError``
exception. Note that this is unconditional (thrown even if ``Throw``
is ``false``).
c. 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.
d. Let ``oldLen`` be ``oldLenDesc.[[Value]]``. (Note that ``oldLen``
is guaranteed to be a unsigned 32-bit integer.)
e. If ``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. Goto REJECT, if ``shortenSucceeded`` is ``false``.
4. Return.
f. Update the property (``"length"``) value to ``newLen``.
g. Return.
2. Set the ``[[Value]]`` attribute of the property named ``P`` of object
``O`` to ``val``. (Since it is side effect free to update the value
with the same value, no check for that case is needed.)
3. 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 ``[[Put]]`` internal method of ``map`` passing ``P``,
``val``, and ``Throw`` as the arguments. (This updates the bound
variable value.)
4. Return ``true``.
We'll need this variant later when creating an inlined version for the full
property write processing.
DefineOwnProperty for a non-existent property in Put
====================================================
This case arises when a ``[[Put]]`` is performed and the property does not
already exist as an "own property", and no setter in an ancestor captured
the write. The property is created with a call to ``[[DefineOwnProperty]]``
with a property descriptor containing a ``[[Value]]``, and the following
set to ``true``: ``[[Writable]]``, ``[[Enumerable]]``, ``[[Configurable]]``.
See E5 Section 8.12.5, step 6.
We can assume that:
* The property does not exist (checked by ``[[Put]]``)
* The object is extensible (checked by ``[[Put]]``)
* The property descriptor is a data descriptor
* The property descriptor has the fields:
+ ``[[Value]]: val``
+ ``[[Writable]]: true``
+ ``[[Enumerable]]: true``
+ ``[[Configurable]]: true``
+ If the object is an ``Array``, the property name ``P`` cannot be
``"length"`` (as that would exist)
More specifically, we know that in the ``[[DefineOwnProperty]]`` algorithm:
* ``current`` is ``undefined``
Taking the ``[[DefineOwnProperty]]`` with all exotic behaviors included,
using the above assumptions, and then eliminating any unnecessary steps,
cleaning up and clarifying, we get:
1. If ``O`` is an ``Array`` object and ``P`` is an array index (E5 Section
15.4), then:
a. 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.
b. Let ``oldLen`` be ``oldLenDesc.[[Value]]``.
(Note that ``oldLen`` is guaranteed to be a unsigned 32-bit integer.)
c. Let ``index`` be ``ToUint32(P)``.
d. Goto REJECT if ``index`` >= ``oldLen`` and ``oldLenDesc.[[Writable]]``
is ``false``.
2. Create an own data property named ``P`` of object ``O`` whose
``[[Value]]``, ``[[Writable]]``, ``[[Enumerable]]`` and
``[[Configurable]]`` attribute values are described by ``Desc``.
3. If ``O`` is an ``Array`` object, ``P`` is an array index and
``index`` >= ``oldLen``:
a. 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.
4. 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 ``[[Put]]`` internal method of ``map`` passing ``P``,
``Desc.[[Value]]``, and ``Throw`` as the arguments. (This
updates the bound variable value.)
5. Return ``true``.
6. **REJECT**:
If ``Throw`` is ``true``, then throw a ``TypeError`` exception,
otherwise return ``false``.
This can be refined further by noticing that the arguments object exotic
behavior cannot be triggered if the property does not exist: all magically
bound properties exist initially, and if they are deleted, the magic
variable binding is also deleted.
We can also change the order of property creation and the postponed array
``length`` write because they are both guaranteed to succeed.
So, we get:
1. If ``O`` is an ``Array`` object and ``P`` is an array index (E5 Section
15.4), then:
a. 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.
b. Let ``oldLen`` be ``oldLenDesc.[[Value]]``.
(Note that ``oldLen`` is guaranteed to be a unsigned 32-bit integer.)
c. Let ``index`` be ``ToUint32(P)``.
d. If ``index`` >= ``oldLen``:
1. Goto REJECT ``oldLenDesc.[[Writable]]`` is ``false``.
2. Update the ``"length"`` property of ``O`` to the value ``index + 1``.
This always succeeds.
2. Create an own data property named ``P`` of object ``O`` whose
``[[Value]]``, ``[[Writable]]``, ``[[Enumerable]]`` and
``[[Configurable]]`` attribute values are described by ``Desc``.
3. Return ``true``.
4. **REJECT**:
If ``Throw`` is ``true``, then throw a ``TypeError`` exception,
otherwise return ``false``.
We'll refine one more time, by eliminating references to ``Desc`` and using
``val`` to refer to ``Desc.[[Value]]``:
1. If ``O`` is an ``Array`` object and ``P`` is an array index (E5 Section
15.4), then:
a. 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.
b. Let ``oldLen`` be ``oldLenDesc.[[Value]]``.
(Note that ``oldLen`` is guaranteed to be a unsigned 32-bit integer.)
c. Let ``index`` be ``ToUint32(P)``.
d. If ``index`` >= ``oldLen``:
1. Goto REJECT ``oldLenDesc.[[Writable]]`` is ``false``.
2. Update the ``"length"`` property of ``O`` to the value ``index + 1``.
This always succeeds.
2. Create an own data property named ``P`` of object ``O`` whose attributes
are:
* ``[[Value]]: val``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true``
3. Return ``true``.
4. **REJECT**:
If ``Throw`` is ``true``, then throw a ``TypeError`` exception,
otherwise return ``false``.
Notes:
* If step 2 fails due to an out-of-memory or other internal error, we
may have updated ``length`` already. So, switching steps 2 and
1.d.2 might be prudent (the check in step 1.d.1 *must* be executed
before writing anything though).
We'll need this variant later when creating an inlined version for the full
property write processing.
DefineOwnProperty for (some) internal object initialization
===========================================================
This case occurs when internal objects or results objects are created by the
implementation. We can't simply use a normal property write internally,
because we need to set the property attributes to whatever combination is
required by the context (many different property attribute variants are
used throughout the specification).
Because user code has not had any access to the object, we can narrow down
the possibilities a great deal. Here we assume that:
* Object is extensible
* Property does not exist
* Property does not have exotic behavior and is not virtual
* Property descriptor is a data descriptor, which is fully populated
With these assumptions, eliminating any unnecessary steps, the algorithm is
simply:
1. Create an own data property named ``P`` of object ``O`` whose
``[[Value]]``, ``[[Writable]]``, ``[[Enumerable]]`` and
``[[Configurable]]`` attribute values are described by ``Desc``.
2. Return ``true``.
This doesn't cover all the initialization cases, but simply illustraes that
very constrained cases are very simple.
Put
===
"Reject" below is shorthand for:
* If ``Throw`` is ``true``, then throw a ``TypeError`` exception; else return.
Original algorithm
------------------
For object ``O``, property ``P``, and value ``V``:
1. If the result of calling the ``[[CanPut]]`` internal method of ``O`` with
argument ``P`` is false, then
a. If ``Throw`` is ``true``, then throw a ``TypeError`` exception.
b. Else return.
2. Let ``ownDesc`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``O`` with argument ``P``.
3. If ``IsDataDescriptor(ownDesc)`` is ``true``, then
a. Let ``valueDesc`` be the Property Descriptor ``{[[Value]]: V}``.
b. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``valueDesc``, and ``Throw`` as arguments.
c. Return.
4. Let ``desc`` be the result of calling the ``[[GetProperty]]`` internal
method of ``O`` with argument ``P``. This may be either an own or inherited
accessor property descriptor or an inherited data property descriptor.
5. If ``IsAccessorDescriptor(desc)`` is ``true``, then
a. Let ``setter`` be ``desc.[[Set]]`` which cannot be ``undefined``.
b. Call the ``[[Call]]`` internal method of setter providing ``O`` as the
``this`` value and providing ``V`` as the sole argument.
6. Else, create a named data property named ``P`` on object ``O`` as follows
a. Let ``newDesc`` be the Property Descriptor:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
b. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``newDesc``, and ``Throw`` as arguments.
7. Return.
Notes:
* Step 5.a: ``setter`` cannot be ``undefined`` at this point because
``[[CanPut]]`` has checked it (and throws an exception if it is
``undefined``).
Minimizing prototype traversal
------------------------------
The ``ownDesc`` check is necessary because a ``[[Put]]`` on an existing own
property is a change of value; a ``[[Put]]`` on an inherited plain property
is an addition of a new property on the *original* target object (not the
ancestor where the inherited property was found).
To minimize prototype traversal, these can be combined as follows (with
some cleanup):
1. If the result of calling the ``[[CanPut]]`` internal method of ``O`` with
argument ``P`` is false, then Reject.
2. Let ``desc`` be the result of calling the ``[[GetProperty]]`` internal
method of ``O`` with argument ``P``.
(Note: here we assume that we also get to know whether the property was
found in ``O`` or in its ancestor.)
3. If ``IsAccessorDescriptor(desc)`` is ``true``, then:
a. Call the ``[[Call]]`` internal method of ``desc.[[Set]]`` providing
``O`` as the ``this`` value and providing ``V`` as the sole argument.
(Note: ``desc.[[Set]]`` cannot be ``undefined``, as this is checked by
``[[CanPut]]``.)
4. Else if ``desc`` was found in ``O`` directly (as an "own data property"),
then:
a. Let ``valueDesc`` be the Property Descriptor ``{[[Value]]: V}``.
b. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``valueDesc``, and ``Throw`` as arguments.
5. Else ``desc`` is an inherited data property or ``undefined``, then:
a. Let ``newDesc`` be the Property Descriptor:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
b. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``newDesc``, and ``Throw`` as arguments.
6. Return.
This still travels the prototype chain twice: once for ``[[CanPut]]``, and
a second time for the actual ``[[Put]]``. ``[[CanPut]]`` can be inlined
quite easily, as it does very similar checks as ``[[Put]]``.
The result is:
1. Let ``desc`` be the result of calling the ``[[GetProperty]]`` internal
method of ``O`` with argument ``P``.
(Note: here we assume that we also get to know whether the property was
found in ``O`` or in its ancestor.)
2. If ``IsAccessorDescriptor(desc)`` is ``true``, then:
a. If ``desc.[[Set]]`` is ``undefined``, Reject.
b. Call the ``[[Call]]`` internal method of ``desc.[[Set]]`` providing
``O`` as the ``this`` value and providing ``V`` as the sole argument.
3. Else if ``desc`` is an inherited (data) property, then:
a. If ``O.[[Extensible]]`` is ``false``, Reject.
b. If ``desc.[[Writable]]`` is ``false``, Reject.
c. Let ``newDesc`` be the Property Descriptor:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
d. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``newDesc``, and ``Throw`` as arguments.
4. Else if ``desc`` was not found (is ``undefined``):
a. If ``O.[[Extensible]]`` is ``false``, Reject.
b. Let ``newDesc`` be the Property Descriptor:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
c. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``newDesc``, and ``Throw`` as arguments.
5. Else ``desc`` was found in ``O`` directly (as an "own data property"),
then:
a. If ``desc.[[Writable]]`` is ``false``, Reject.
b. Let ``valueDesc`` be the Property Descriptor ``{[[Value]]: V}``.
b. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``valueDesc``, and ``Throw`` as arguments.
6. Return.
The above can be further refined to (making also the modification required
to ``[[GetProperty]]`` explicit):
1. Let ``desc`` and ``inherited`` be the result of calling the
``[[GetProperty]]`` internal method of ``O`` with argument ``P``.
2. If ``IsAccessorDescriptor(desc)`` is ``true``, then:
a. If ``desc.[[Set]]`` is ``undefined``, Reject.
b. Call the ``[[Call]]`` internal method of ``desc.[[Set]]`` providing
``O`` as the ``this`` value and providing ``V`` as the sole argument.
3. Else if ``desc`` is not ``undefined`` and ``inherited`` is ``false``
(own data property), then:
a. If ``desc.[[Writable]]`` is ``false``, Reject.
b. Let ``valueDesc`` be the Property Descriptor ``{[[Value]]: V}``.
b. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``valueDesc``, and ``Throw`` as arguments.
3. Else ``desc`` is an inherited (data) property or ``undefined``:
a. If ``O.[[Extensible]]`` is ``false``, Reject.
b. If ``desc`` is not ``undefined`` and ``desc.[[Writable]]`` is
``false``, Reject.
(In other words: ``desc`` was inherited and is non-writable.)
c. Let ``newDesc`` be the Property Descriptor:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
d. Call the ``[[DefineOwnProperty]]`` internal method of ``O`` passing
``P``, ``newDesc``, and ``Throw`` as arguments.
4. Return.
This can be further improved in actual C code.
Inlining GetProperty
--------------------
When actually implementing, it's useful to "inline" the ``[[GetProperty]]``
loop, which changes the code structure quite a bit:
1. Set ``curr`` to ``O``.
2. While ``curr`` !== ``null``:
a. If ``O`` does not have own property ``P``:
1. Set ``curr`` to ``curr.[[Prototype]]``
1. Continue (while loop)
b. Let ``desc`` be the descriptor for own property ``P``
c. If ``IsDataDescriptor(desc)``:
1. If ``curr`` != ``O`` (property is an inherited data property):
(Note: assumes there are no prototype loops.)
a. If ``O.[[Extensible]`` is ``false``, Reject.
b. If ``desc.[[Writable]]`` is ``false``, Reject.
c. Let ``newDesc`` be a property descriptor with values:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
d. Call ``O.[[DefineOwnProperty]](P, newDesc, Throw)``.
2. Else (property is an own data property):
a. If ``desc.[[Writable]]`` is ``false``, Reject.
b. Let ``valueDesc`` be ``{ [[Value]]: V }``.
c. Call ``O.[[DefineOwnProperty]](P, valueDesc, Throw)``.
e. Else (property is an accessor):
1. If ``desc.[[Set]]`` is ``undefined``, Reject.
2. Call the ``[[Call]]`` internal method of ``desc.[[Set]]`` providing
``O`` as the ``this`` value and providing ``V`` as the sole argument.
f. Return.
3. Property was not found in the prototype chain:
a. If ``O.[[Extensible]]`` is ``false``, Reject.
b. Let ``newDesc`` be a property descriptor with values:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
c. Call ``O.[[DefineOwnProperty]](P, newDesc, Throw)``.
Less nested form
----------------
The following is a less "nested" form (note that ``curr`` is guaranteed to
be non-null in the first loop):
1. Let ``curr`` be ``O``.
2. **NEXT:**
Let ``desc`` be the result of calling the ``[[GetOwnProperty]]``
internal method of ``curr`` with property name ``P``.
3. If ``desc`` is ``undefined``:
a. Let ``curr`` be the value of the ``[[Prototype]]`` internal property
of ``curr``.
b. If ``curr`` is not ``null``, goto NEXT.
c. If ``O.[[Extensible]]`` is ``false``, Reject.
d. Let ``newDesc`` be a property descriptor with values:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
e. Call ``O.[[DefineOwnProperty]](P, newDesc, Throw)``.
f. Return.
4. If ``IsDataDescriptor(desc)``:
a. If ``curr`` != ``O`` (property is an inherited data property):
(Note: assumes there are no prototype loops.)
1. If ``O.[[Extensible]`` is ``false``, Reject.
2. If ``desc.[[Writable]]`` is ``false``, Reject.
3. Let ``newDesc`` be a property descriptor with values:
* ``[[Value]]: V``
* ``[[Writable]]: true``
* ``[[Enumerable]]: true``
* ``[[Configurable]]: true}``
4. Call ``O.[[DefineOwnProperty]](P, newDesc, Throw)``.
b. Else (property is an own data property):
1. If ``desc.[[Writable]]`` is ``false``, Reject.
2. Let ``valueDesc`` be ``{ [[Value]]: V }``.
3. Call ``O.[[DefineOwnProperty]](P, valueDesc, Throw)``.
5. Else (property is an accessor):
a. If ``desc.[[Set]]`` is ``undefined``, Reject.
b. Call the ``[[Call]]`` internal method of ``desc.[[Set]]`` providing
``O`` as the ``this`` value and providing ``V`` as the sole argument.
6. Return.
Note about PutValue
-------------------
Note that ``PutValue()`` has a ``[[Put]]`` variant with two exotic
behaviors related to object coercion. The above algorithm does not
take those into account.
Property descriptor algorithms
==============================
E5 Section 8.10 describes descriptor related algorithms:
* ``IsAccessorDescriptor(desc)``: ``true``, if ``desc`` contains *either*
``[[Set]]`` or ``[[Get]]``
* ``IsDataDescriptor(desc)``: ``true``, if ``desc`` contains *either*
``[[Value]]`` or ``[[Writable]]``
* ``IsGenericDescriptor(desc)``: ``true`` if both
``IsAccessorDescriptor(desc)`` and ``IsGenericDescriptor`` are
``false``; concretely:
* ``desc`` contains none of the following: ``[[Set]]``, ``[[Get]]``,
``[[Value]]``, ``[[Writable]]``
* ``desc`` may contain: ``[[Enumerable]]``, ``[[Configurable]]``
A property descriptor may be fully populated or not. If fully populated,
it is either a data descriptor or an access descriptor, not a generic
descriptor.
A property descriptor may not be both a data descriptor and access descriptor
(this is stated in E5 Section 8.10). However, an argument to e.g.
``Object.defineProperty()`` may naturally contain e.g. ``"set"`` and
``"value"`` keys. In this case:
* ``defineProperty()`` uses ``ToPropertyDescriptor()`` to convert the
Ecmascript object into an internal property descriptor
* ``ToPropertyDescriptor()`` creates a property descriptor and throws a
``TypeError`` if the descriptor contains conflicting fields
``ToPropertyDescriptor()`` also coerces the values in its argument
Ecmascript object (e.g. it uses ``ToBoolean()`` for the flags).
The behavior of ``ToPropertyDescriptor()`` is probably easiest to "inline"
into wherever it is needed. The E5 specification refers to
``ToPropertyDescriptor`` only in ``Object.defineProperty()`` and
``Object.defineProperties()``.
The current implementation does not have partial internal property
descriptors (internal property value and attributes are always fully
populated).
ToPropertyDescriptor
====================
The ``ToPropertyDescriptor()`` algorithm is specified in E5 Section 8.10.5
and is as follows:
1. If ``Type(Obj)`` is not ``Object`` throw a ``TypeError`` exception.
2. Let ``desc`` be the result of creating a new Property Descriptor that
initially has no fields.
3. If the result of calling the ``[[HasProperty]]`` internal method of
``Obj`` with argument ``"enumerable"`` is ``true``, then:
a. Let ``enum`` be the result of calling the ``[[Get]]`` internal method
of ``Obj`` with ``"enumerable"``.
b. Set the ``[[Enumerable]]`` field of ``desc`` to ``ToBoolean(enum)``.
4. If the result of calling the ``[[HasProperty]]`` internal method of
``Obj`` with argument ``"configurable"`` is ``true``, then:
a. Let ``conf`` be the result of calling the ``[[Get]]`` internal method
of ``Obj`` with argument ``"configurable"``.
b. Set the ``[[Configurable]]`` field of ``desc`` to ``ToBoolean(conf)``.
5. If the result of calling the ``[[HasProperty]]`` internal method of
``Obj`` with argument ``"value"`` is ``true``, then:
a. Let ``value`` be the result of calling the ``[[Get]]`` internal method
of ``Obj`` with argument ``“value”``.
b. Set the ``[[Value]]`` field of ``desc`` to ``value``.
6. If the result of calling the ``[[HasProperty]]`` internal method of
``Obj`` with argument ``"writable"`` is ``true``, then:
a. Let ``writable`` be the result of calling the ``[[Get]]`` internal
method of ``Obj`` with argument ``"writable"``.
b. Set the ``[[Writable]]`` field of ``desc`` to ``ToBoolean(writable)``.
7. If the result of calling the ``[[HasProperty]]`` internal method of
``Obj`` with argument ``"get"`` is ``true``, then:
a. Let ``getter`` be the result of calling the ``[[Get]]`` internal
method of ``Obj`` with argument ``"get"``.
b. If ``IsCallable(getter)`` is ``false`` and ``getter`` is not
``undefined``, then throw a ``TypeError`` exception.
c. Set the ``[[Get]]`` field of ``desc`` to ``getter``.
8. If the result of calling the ``[[HasProperty]]`` internal method of
``Obj`` with argument ``"set"`` is ``true``, then:
a. Let ``setter`` be the result of calling the ``[[Get]]`` internal
method of ``Obj`` with argument ``"set"``.
b. If ``IsCallable(setter)`` is ``false`` and ``setter`` is not
``undefined``, then throw a TypeError exception.
c. Set the ``[[Set]]`` field of ``desc`` to ``setter``.
9. If either ``desc.[[Get]]`` or ``desc.[[Set]]`` are present, then:
a. If either ``desc.[[Value]]`` or ``desc.[[Writable]]`` are present,
then throw a ``TypeError`` exception.
10. Return ``desc``.
Notes:
* Since ``[[Get]]`` is used to read the descriptor value fields, they can
be inherited from a parent object, and they can also be accessors.
* Setter/getter values must be either callable or ``undefined`` if they are
present. In particular, ``null`` is not an allowed value.
* Any call to ``[[Get]]`` may cause an exception (e.g. if the property is
an accessor with a throwing getter). In addition, there are explicit
exceptions for object type check and setter/getter check. The order of
checking and coercion thus matters, at least if the errors thrown have
a message indicating the failing check. All the exceptions are of the
same type (``TypeError``), so a chance in ordering is not strictly a
compliance issue (there are no guaranteed error messages).
* ``ToBoolean()`` has no side effects and is guaranteed to succeed.
The algorithm in the specification is expressed quite verbosely; the
following is a reformulation with less text, the target object has also
been renamed to ``O``:
1. If ``Type(O)`` is not ``Object`` throw a ``TypeError`` exception.
2. Let ``desc`` be a new, empty Property Descriptor.
3. If ``O.[[HasProperty]]("enumerable")`` === ``true``, then
set ``desc.[[Enumerable]]`` to ``ToBoolean(O.[[Get]]("enumerable"))``.
4. If ``O.[[HasProperty]]("configurable")`` === ``true``, then
set ``desc.[[Configurable]]`` to ``ToBoolean(O.[[Get]]("configurable"))``.
5. If ``O.[[HasProperty]]("value")`` === ``true``, then
set ``desc.[[Value]]`` to ``O.[[Get]]("value")``.
6. If ``O.[[HasProperty]]("writable")`` === ``true``, then
set ``desc.[[Writable]]`` to ``ToBoolean(O.[[Get]]("writable"))``.
7. If ``O.[[HasProperty]]("get")`` === ``true``, then:
a. Set ``desc.[[Get]]`` to ``O.[[Get]]("get")``.
b. If ``desc.[[Get]]`` !== ``undefined`` and
``IsCallable(desc.[[Get]])`` === ``false``, then
throw a ``TypeError`` exception.
8. If ``O.[[HasProperty]]("set")`` === ``true``, then:
a. Set ``desc.[[Set]]`` to ``O.[[Get]]("set")``.
b. If ``desc.[[Set]]`` !== ``undefined`` and
``IsCallable(desc.[[Set]])`` === ``false``, then
throw a ``TypeError`` exception.
9. If either ``desc.[[Get]]`` or ``desc.[[Set]]`` are present, then:
a. If either ``desc.[[Value]]`` or ``desc.[[Writable]]`` are present,
then throw a ``TypeError`` exception.
10. Return ``desc``.
NormalizePropertyDescriptor
===========================
This algorithm is not defined in the E5 specification, but is used as an
internal helper for implementing ``Object.defineProperties()`` and
``Object.defineProperty()``.
The algorithm is a variant of ``ToPropertyDescriptor()`` which, instead of
an internal descriptor, outputs an equivalent Ecmascript property descriptor
which has been fully validated, and contains only "own" data properties.
If the resulting Ecmascript object, ``desc``, is later given to
``ToPropertyDescriptor()``:
* The call cannot fail.
* The call will yield the same internal descriptor as if given the
original object.
* There can be no user visible side effects, because ``desc`` only
contains plain (own) values.
For instance, if the input property descriptor were::
{
get value() { return "test"; },
writable: 0.0,
configurable: "nonempty",
enumerable: new Date(),
additional: "ignored" // ignored, not relevant to a descriptor
}
the normalized descriptor would be::
{
value: "test",
writable: false,
configurable: true,
enumerable: true
}
(The example doesn't illustrate the fact that inherited properties are
converted to "own" properties.)
The algorithm is as follows:
1. If ``Type(O)`` is not ``Object`` throw a ``TypeError`` exception.
2. Let ``desc`` be a new, empty Object.
3. If ``O.[[HasProperty]]("enumerable")`` === ``true``, then
call ``desc.[[Put]]`` with the arguments
``"enumerable"``, ``ToBoolean(O.[[Get]]("enumerable"))`` and ``true``.
4. If ``O.[[HasProperty]]("configurable")`` === ``true``, then
call ``desc.[[Put]]`` with the arguments
``"configurable"``, ``ToBoolean(O.[[Get]]("configurable"))`` and ``true``.
5. If ``O.[[HasProperty]]("value")`` === ``true``, then
call ``desc.[[Put]]`` with the arguments
``"value"``, ``O.[[Get]]("value")`` and ``true``.
6. If ``O.[[HasProperty]]("writable")`` === ``true``, then
call ``desc.[[Put]]`` with the arguments
``"writable"``, ``ToBoolean(O.[[Get]]("writable"))`` and ``true``.
7. If ``O.[[HasProperty]]("get")`` === ``true``, then:
a. Let ``getter`` be ``O.[[Get]]("get")``.
b. If ``getter`` !== ``undefined`` and
``IsCallable(getter)`` === ``false``, then
throw a ``TypeError`` exception.
c. Call ``desc.[[Put]]`` with the arguments
``"get"``, ``getter`` and ``true``.
8. If ``O.[[HasProperty]]("set")`` === ``true``, then:
a. Let ``setter`` be ``O.[[Get]]("set")``.
b. If ``setter`` !== ``undefined`` and
``IsCallable(setter)`` === ``false``, then
throw a ``TypeError`` exception.
c. Call ``desc.[[Put]]`` with the arguments
``"set"``, ``setter`` and ``true``.
9. Validation:
a. Let ``g`` be ``desc.[[HasProperty]]("get")``.
b. Let ``s`` be ``desc.[[HasProperty]]("set")``.
c. Let ``v`` be ``desc.[[HasProperty]]("value")``.
d. Let ``w`` be ``desc.[[HasProperty]]("writable")``.
e. If ``(g || s) && (v || w)`` then throw a ``TypeError`` exception.
10. Return ``desc``.
Notes:
* The third argument to ``desc.[[Put]]`` is the ``Throw`` flag. The value
is irrelevant as the ``[[Put]]`` calls cannot fail.