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.
177 lines
5.1 KiB
177 lines
5.1 KiB
================================================================
|
|
DELPROP: exposed property deletion algorithm ("delete" operator)
|
|
================================================================
|
|
|
|
Background
|
|
==========
|
|
|
|
Properties are deleted in Ecmascript code with the ``delete`` operator, e.g.::
|
|
|
|
delete foo.bar;
|
|
|
|
This involves:
|
|
|
|
* A property accessor reference (E5 Section 11.2.1)
|
|
|
|
* ``delete`` semantics (E5 Section 11.4.1)
|
|
|
|
* A call to ``[[Delete]]``
|
|
|
|
The property accessor coercions are the same as for ``GetValue``:
|
|
|
|
* The base reference is checked with ``CheckObjectCoercible()``
|
|
|
|
* The property name is coerced to a string
|
|
|
|
The ``delete`` expression will then:
|
|
|
|
* Coerce the base value to an object
|
|
|
|
* Call the ``[[Delete]]`` algorithm
|
|
|
|
Note that if the base value is not an object, a temporary object will be
|
|
created by coercion. Since a deletion always operates on the "own
|
|
properties" of an object, the deletion can only have side effects (error
|
|
throwing) side effects. Any other effects will be lost with the temporary
|
|
object. This is discussed in more detail below, for the deletion algorithm.
|
|
|
|
Notes:
|
|
|
|
* ``[[Delete]]`` only checks for the property ``P`` in the original object
|
|
``O``, and does not follow the prototype chain
|
|
|
|
* In particular, an inherited property ``P`` which would prevent a ``[[Put]]``
|
|
does not affect the outcome of ``[[Delete]]``
|
|
|
|
First draft
|
|
===========
|
|
|
|
Starting from the property accessor, then applying ``delete`` (and skipping any
|
|
unused steps):
|
|
|
|
1. Call ``CheckObjectCoercible`` for the base value. In practice, throw a
|
|
``TypeError`` if the base value is ``null`` or ``undefined``.
|
|
|
|
2. Coerce property name to string using ``ToString()``.
|
|
|
|
3. Coerce base value to object using ``ToObject()`` and call ``[[Delete]]``
|
|
with the coerced object, the coerced key, and a "Throw" flag set if
|
|
the property reference is contained in strict mode code.
|
|
|
|
More formally, suppose ``O`` is the base value, ``P`` is the property name
|
|
value, and ``currStrict`` is ``true`` if the property deletion expression
|
|
occurred in strict code:
|
|
|
|
1. If ``O`` is ``null`` or ``undefined``, throw a ``TypeError``
|
|
|
|
2. ``P`` = ``ToString(P)``
|
|
|
|
3. ``O`` = ``ToObject(O)``
|
|
|
|
4. Call ``O.[[Delete]](P, currStrict)``, and return its result
|
|
|
|
Avoiding object coercion
|
|
========================
|
|
|
|
We want to avoid the object coercion; let's first make it more explicit:
|
|
|
|
1. If ``O`` is ``null`` or ``undefined``, throw a ``TypeError``
|
|
|
|
2. ``P`` = ``ToString(P)``
|
|
|
|
3. If ``O`` is an object, call ``[[Delete]](O, P, currStrict)``, and
|
|
return its result
|
|
|
|
4. Else ``O`` is primitive:
|
|
|
|
a. ``O`` = ``ToObject(O)`` (create temporary object)
|
|
|
|
b. Call ``O.[[Delete]](P, currStrict)``, and return its result
|
|
|
|
Avoiding temporary objects
|
|
==========================
|
|
|
|
Note that a ``[[Delete]]`` only operates on the "own properties" of the
|
|
target object. When the base value is not an object, the deletion operates
|
|
only on the temporary object. Since the temporary object is immediately
|
|
discarded, there are only two possible user visible effects:
|
|
|
|
* The return value of ``[[Delete]]``, which is:
|
|
|
|
+ ``true``, if the property does not exist
|
|
|
|
+ ``true``, if the property exists and could be deleted
|
|
|
|
+ ``false``, if the property exists, cannot be deleted, and
|
|
``Throw`` is ``false`` (if ``Throw`` is ``true``, an error is
|
|
thrown instead)
|
|
|
|
* Errors thrown by ``[[Delete]]``, which happens if:
|
|
|
|
+ The (own) property exists, the property is non-configurable, and the
|
|
Throw flag is set, i.e. we're evaluating ``delete`` in strict code
|
|
|
|
The coerced temporary object can be:
|
|
|
|
* a ``Boolean`` instance: no own properties
|
|
|
|
* a ``Number`` instance: no own properties
|
|
|
|
* a ``String`` instance: has ``length`` and array indices (inside string
|
|
length) as own properties, all non-configurable
|
|
|
|
Given these, the algorithm can be changed to avoid creation of temporaries
|
|
entirely:
|
|
|
|
1. If ``O`` is ``null`` or ``undefined``, throw a ``TypeError``
|
|
|
|
2. ``P`` = ``ToString(P)``
|
|
|
|
3. If ``O`` is an object, call ``[[Delete]](O, P, currStrict)`` and
|
|
return its result
|
|
|
|
4. Else ``O`` is primitive:
|
|
|
|
a. If ``O`` is a boolean, return ``true``
|
|
|
|
b. If ``O`` is a number, return ``true``
|
|
|
|
c. If ``O`` is a string:
|
|
|
|
1. If ``P`` is length or an array index inside the ``O`` string length:
|
|
|
|
a. If ``currStrict`` is ``true``, throw a ``TypeError``
|
|
|
|
b. Else, return ``false``
|
|
|
|
2. Else, return ``true``
|
|
|
|
d. Return ``true``
|
|
(This step should never be reached, as the checks above are
|
|
comprehensive.)
|
|
|
|
Step 4 can be simplified a bit:
|
|
|
|
1. If ``O`` is ``null`` or ``undefined``, throw a ``TypeError``
|
|
|
|
2. ``P`` = ``ToString(P)``
|
|
|
|
3. If ``O`` is an object, call ``[[Delete]](O, P, currStrict)`` and
|
|
return its result
|
|
|
|
4. If ``O`` is a string:
|
|
|
|
a. If ``P`` is length or an array index inside the ``O`` string length:
|
|
|
|
1. If ``currStrict`` is ``true``, throw a ``TypeError``
|
|
|
|
2. Else, return ``false``
|
|
|
|
5. Return ``true``
|
|
|
|
Fast path for array indices
|
|
===========================
|
|
|
|
It would be straightforward to add a fast path for array indices, but there
|
|
is no fast path in the current implementation for array index deletion.
|
|
The index is always string coerced and interned.
|
|
|