array.[[ThrowablePut]] bugs in Kona draft

David-Sarah Hopwood david.hopwood at industrial-designers.co.uk
Mon Nov 17 19:32:24 PST 2008


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


More information about the Es-discuss mailing list