Section 15.4.5.1 ([[ThrowablePut]] on an array) has several bugs:
the algorithm has not been changed to take account of property
descriptors. For example, it attempts to directly get and set
values of properties, rather than getting and setting [[Value]]
attributes or calling setter functions.
it creates new properties with empty attributes, rather than
with attributes [[Writable]]: true, [[Enumerable]]: true,
[[Configurable]]: true, as the algorithm of 8.6.2.10 step 7 does.
it may change the value of the 'length' property even if that
property has been made non-[[Writable]] using
"Object.defineProperty(array, 'length', {writable: false});"
(which is possible even though 'length' is non-[[Configurable]]).
it may delete array index properties that are non-[[Configurable]]
when the 'length' property is set.
Suggested fix:
====
15.4.5.1 [[ThrowablePut]] ( P, V, Throw )
Array objects use a variation of the [[ThrowablePut]] method used for other
native ECMAScript objects (8.6.2.10).
Assume A is an Array object, P is a string, and Throw is a boolean flag.
In the following algorithm, the term "Reject" means "If Throw is true, then
throw a TypeError exception, otherwise return."
When the [[ThrowablePut]] method of A is called with property P, value V,
and Boolean flag Throw, the following steps are taken:
Call the [[CanPut]] method of A with argument P.
If Result(1) is false, then Reject.
Let OldLength be the [[Value]] attribute of A's own "length" property
(which necessarily exists and is a data property holding an integer
value).
If P is "length", then go to step 13.
If P is an array index, ToUint32(P) is not less than OldLength, and
the [[Writable]] attribute of A's own "length" property is false,
then Reject.
Call the [[GetOwnProperty]] method of A with argument P.
If IsDataDescriptor(Result(6)) is true, then
a. Set the [[Value]] attribute of property P of A to V.
b. Go to step 11.
Call the [[GetProperty]] method of A with argument P.
If IsAccessorDescriptor(Result(8)) is true, then
a. Get Result(8).[[Setter]] which cannot be undefined.
b. Call the [[Call]] method of Result(9a) providing A as the
this value and providing V as the sole argument.
c. Go to step 11.
Create a data property named P on object A whose attributes are
[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true,
[[Configurable]]: true.
If P is an array index and ToUint32(P) is not less than OldLength,
then
a. Set the [[Value]] attribute of A's own "length" property to
ToUint32(P)+1.
Return.
Let NewLength be ToUint32(V).
If NewLength is not equal to ToNumber(V), throw a RangeError exception.
For every integer k that is less than OldLength but not less than
NewLength:
a. If an own property of A named ToString(k) exists and its
[[Configurable]] attribute is false, then Reject.
For every integer k that is less than OldLength but not less than
NewLength:
a. If an own property of A named ToString(k) exists, then remove it.
Set the [[Value]] attribute of A's own "length" property to NewLength.
Return.
====
The overhead of step 8, and of steps 5-8 of the [[CanPut]] algorithm,
could be eliminated if Array.prototype and Object.prototype were not
allowed to have setters (assuming that the [[Prototype]] property of
an array cannot be changed after creation).
An implementation could eliminate the overhead of step 15 in the common
case by maintaining a flag in the array header that keeps track of
whether the array has any non-[[Configurable]] array index properties.
Also note that [[CanPut]] is only used by the two versions of
[[ThrowablePut]], and it would arguably be clearer to fold [[CanPut]]
into the [[ThrowablePut]] algorithms, since they are doing the same
prototype chain search and checking the same descriptors.
Steps 3 and 4 of [[CanPut]] are redundant.
Section 15.4.5.1 ([[ThrowablePut]] on an array) has several bugs:
- the algorithm has not been changed to take account of property
descriptors. For example, it attempts to directly get and set
values of properties, rather than getting and setting [[Value]]
attributes or calling setter functions.
- it creates new properties with empty attributes, rather than
with attributes [[Writable]]: true, [[Enumerable]]: true,
[[Configurable]]: true, as the algorithm of 8.6.2.10 step 7 does.
- it may change the value of the 'length' property even if that
property has been made non-[[Writable]] using
"Object.defineProperty(array, 'length', {writable: false});"
(which is possible even though 'length' is non-[[Configurable]]).
- it may delete array index properties that are non-[[Configurable]]
when the 'length' property is set.
Suggested fix:
====
15.4.5.1 [[ThrowablePut]] ( P, V, Throw )
Array objects use a variation of the [[ThrowablePut]] method used for other
native ECMAScript objects (8.6.2.10).
Assume A is an Array object, P is a string, and Throw is a boolean flag.
In the following algorithm, the term "Reject" means "If Throw is true, then
throw a TypeError exception, otherwise return."
When the [[ThrowablePut]] method of A is called with property P, value V,
and Boolean flag Throw, the following steps are taken:
1. Call the [[CanPut]] method of A with argument P.
2. If Result(1) is false, then Reject.
3. Let OldLength be the [[Value]] attribute of A's own "length" property
(which necessarily exists and is a data property holding an integer
value).
4. If P is "length", then go to step 13.
5. If P is an array index, ToUint32(P) is not less than OldLength, and
the [[Writable]] attribute of A's own "length" property is false,
then Reject.
6. Call the [[GetOwnProperty]] method of A with argument P.
7. If IsDataDescriptor(Result(6)) is true, then
a. Set the [[Value]] attribute of property P of A to V.
b. Go to step 11.
8. Call the [[GetProperty]] method of A with argument P.
9. If IsAccessorDescriptor(Result(8)) is true, then
a. Get Result(8).[[Setter]] which cannot be undefined.
b. Call the [[Call]] method of Result(9a) providing A as the
this value and providing V as the sole argument.
c. Go to step 11.
10. Create a data property named P on object A whose attributes are
[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true,
[[Configurable]]: true.
11. If P is an array index and ToUint32(P) is not less than OldLength,
then
a. Set the [[Value]] attribute of A's own "length" property to
ToUint32(P)+1.
12. Return.
13. Let NewLength be ToUint32(V).
14. If NewLength is not equal to ToNumber(V), throw a RangeError exception.
15. For every integer k that is less than OldLength but not less than
NewLength:
a. If an own property of A named ToString(k) exists and its
[[Configurable]] attribute is false, then Reject.
16. For every integer k that is less than OldLength but not less than
NewLength:
a. If an own property of A named ToString(k) exists, then remove it.
17. Set the [[Value]] attribute of A's own "length" property to NewLength.
18. Return.
====
The overhead of step 8, and of steps 5-8 of the [[CanPut]] algorithm,
could be eliminated if Array.prototype and Object.prototype were not
allowed to have setters (assuming that the [[Prototype]] property of
an array cannot be changed after creation).
An implementation could eliminate the overhead of step 15 in the common
case by maintaining a flag in the array header that keeps track of
whether the array has any non-[[Configurable]] array index properties.
Also note that [[CanPut]] is only used by the two versions of
[[ThrowablePut]], and it would arguably be clearer to fold [[CanPut]]
into the [[ThrowablePut]] algorithms, since they are doing the same
prototype chain search and checking the same descriptors.
Steps 3 and 4 of [[CanPut]] are redundant.
--
David-Sarah Hopwood
Section 15.4.5.1 ([[ThrowablePut]] on an array) has several bugs:
the algorithm has not been changed to take account of property descriptors. For example, it attempts to directly get and set values of properties, rather than getting and setting [[Value]] attributes or calling setter functions.
it creates new properties with empty attributes, rather than with attributes [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true, as the algorithm of 8.6.2.10 step 7 does.
it may change the value of the 'length' property even if that property has been made non-[[Writable]] using "Object.defineProperty(array, 'length', {writable: false});" (which is possible even though 'length' is non-[[Configurable]]).
it may delete array index properties that are non-[[Configurable]] when the 'length' property is set.
Suggested fix:
==== 15.4.5.1 [[ThrowablePut]] ( P, V, Throw )
Array objects use a variation of the [[ThrowablePut]] method used for other native ECMAScript objects (8.6.2.10).
Assume A is an Array object, P is a string, and Throw is a boolean flag. In the following algorithm, the term "Reject" means "If Throw is true, then throw a TypeError exception, otherwise return."
When the [[ThrowablePut]] method of A is called with property P, value V, and Boolean flag Throw, the following steps are taken:
The overhead of step 8, and of steps 5-8 of the [[CanPut]] algorithm, could be eliminated if Array.prototype and Object.prototype were not allowed to have setters (assuming that the [[Prototype]] property of an array cannot be changed after creation).
An implementation could eliminate the overhead of step 15 in the common case by maintaining a flag in the array header that keeps track of whether the array has any non-[[Configurable]] array index properties.
Also note that [[CanPut]] is only used by the two versions of [[ThrowablePut]], and it would arguably be clearer to fold [[CanPut]] into the [[ThrowablePut]] algorithms, since they are doing the same prototype chain search and checking the same descriptors. Steps 3 and 4 of [[CanPut]] are redundant.