Repository: tc39/ecma262

Issue #2502: ToLength suffers from off-by-one errors
Created: 2021-08-26T07:11:11Z
(found when working through semantics for #2501)
ToLength is described as returning a value "suitable for use as the length of an array-like object", but its algorithm clamps that to 253 - 1, which is the maximum possible value of an integer index and therefore one less than the maximum possible length of an array-like object. And some uses of it correspond with the description (LengthOfArrayLike and StringPad), while others correspond with the algorithm (ToIndex and RegExp retrieval of lastIndex).
ToIndex itself seems to have a similar problem, with TypedArray and ArrayBuffer and SharedArrayBuffer and InitializeTypedArrayFromArrayBuffer using its output as a length.
I believe this concern does not yet manifest in practice due to the extremely large values in question, but it should still be addressed.

Duplicate issue #2578: Normative: `Array.from` should support arrays of `2**53-1` elements, rather than just `2**53-2`
Created: 2021-11-15T15:20:11Z
Description:
All the array methods throw an error when they create an array longer than 2**53-1 (thus, when you exceed the index 2**53-2). However, Array.from(iterable) throws one element earlier:
https://tc39.es/ecma262/#sec-array.from
e. Repeat,
    i. If k ≥ 2**53 - 1, then
        1. Let error be ThrowCompletion(a newly created TypeError object).
        2. Return ? IteratorClose(iteratorRecord, error).
   ii. Let Pk be ! ToString(𝔽(k)).
  iii. Let next be ? IteratorStep(iteratorRecord).
   iv. If next is false, then
        1. Perform ? Set(A, "length", 𝔽(k), true).
        2. Return A.
    v. Let nextValue be ? IteratorValue(next).
   ... other steps to add a new element with nextValue

This code always checks if we reached the length 2**53-1, but it throws before making sure that we are actually exceeding it.  Looking at #352, it seems like the intention was to throw when exceeding 2**53-1.
I think step e.i should be moved after step e.iv.
Additionally, I think we should introduce a similar check in the Array.from(arrayLike) case (Array.from({ length: 2**53 }) should throw), but that's a bigger normative change.
eshost Output:
$ eshost -e "Array.from(function*() { for (let i = 0; i < 2**53-1; i++) yield i;}())" -t

┌────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ChakraCore     │                                                                                                                                                                                       │
├────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Hermes         │                                                                                                                                                                                       │
│                │ HermesGC: OOM: [] reason = Max heap size was exceeded (1 from category: vm_allocate_category), numCollections = 3968, heapSize = 1073741824, allocated = 1051421752, va = 1073741824. │
├────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ JavaScriptCore │                                                                                                                                                                                       │
├────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Moddable XS    │                                                                                                                                                                                       │
├────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ SpiderMonkey   │                                                                                                                                                                                       │
├────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ V8             │                                                                                                                                                                                       │
│                │ RangeError: Invalid array length                                                                                                                                                      │
└────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

I don't understand these eshost results, and in Node.js it segfaults.

Closing comment (2021-11-15T15:55:05Z): Probably related to #2502.