archives

« Bugzilla Issues Index

#2546 — Array.prototype.copyWithin specifies two differerent default values for 'end'


The (non-normative?) text at the top of 22.1.3.3 Array.prototype.copyWithin states "(target, start, end = this.length)" and "The end argument is optional with the length of the this object as its default value.".

However, the actual algorithm sets end to ToLength(ToObject(this).length) not this.length. In particular, if this.length is negative, ToLength will make it zero, so there will be two different values for `end` depending on whether you think it is given its default value before executing the algorithm or not. (Aka, whether you pay attention to the method signature and first paragraph, or just pay attention to the algorithm as stated.)

The easiest fix is just to eliminate the `= this.length` from the signature and possibly `with the length of the this object as its default value` from the second sentence. That's assuming the algorithm is correct.

If instead the heading and paragraph text is correct, then step 12 should read:

"If end is undefined, let relativeEnd be ToInteger(lenVal); else let relativeEnd be ToInteger(end)."

That is, refer to lenVal, not len, and perform ToInteger instead of ToLength.


The algorithm is always normative.

As for any method that deals with generic array-like objects, "the length of the Array" should be be interpreted as meaning the result of applying ToLength to the value of the array's 'length' property.

The signature line isn't intended to be normative, but just a sort hand description of typical usage.

I'm somewhat conflicted about whether I should change the heading signature line. Clearly it caused some confusion for you and we what to avoid creating that sort of confusion. On the other hand, saying copyWithin(target, start [, end]) would be a less informative summary of the intended usage.

I probably should say somewhere that heading signatures like this are non-normative, but not everybody will read that so the source of potential confusion will still be there.


I wonder what the impact of actually changing the algorithm to use ToInteger for the end parameter would be? This is totally a corner case, you'd only see it if you had an object with a negative length property. But it would eliminate the possible confusion, and allow us to use the more descriptive signature without caveats.


It's still necessary to say that default parameter expressions are not normative and are not supposed to be evaluated. This is required because the default expression may contain code which executes side effects, in this case, for example, `this.length` may execute arbitrary code if there is a getter for the "length" property on the thisValue object.


Discussion in http://esdiscuss.org/topic/changing-behavior-of-array-copywithin-when-used-on-array-like-with-negative-length

André Bargull proposes a concrete diff at https://gist.github.com/anba/6c75c34c72d4ffaa8de7
which avoids double-invocation of `valueOf`/etc side-effects in case `this.length` is object-valued.

A quick audit of the spec shows that `Array#fill` might have the same issue. `Array#lastIndexOf` also uses `ToLength` instead of `ToInteger` but it uses ES5-style default parameters. The spec text can probably be made consistent without affecting actual behavior.

The `copyWithin`, `fill`, `lastIndexOf`, and `subarray` methods of `%TypedArray%.prototype` are spec'ed with default arguments equal to `this.length`, but there's no way `length` can be negative for a typed array.


Claude Pache points out that `Array#slice` also contains a similar default for `end`.


Fixed in rev23 editor's draft.

Removed all "default value"notation usage in function signatures so there is no confusion with the normative algorithm texts.

added Bug 2582 for a abstract operation for converting relative index positions to absolute


fixed in rev23 draft