-
Notifications
You must be signed in to change notification settings - Fork 18
Account for resize-in-the-middle for TA methods #75
Conversation
This came out of a discussion with @marjakh, who convinced me the NaN behavior with floats is too weird for not a lot of gain. PTAL @marjakh and @jugglinmike. I plan to present the two alternative semantics to TC39 next plenary, leaning towards this one for its simplicity. @jugglinmike, incidentally this would make #72 much simpler to fix. Just make sure to only copy the in-bounds portion of the source buffer, no need to do the weird Get-and-Set dance we chatted about. I believe this behavior is superior to the one in #74 despite not being consistent with the Array versions for several reasons:
|
Concretely, the difference in behavior between #74 and this PR is: // Adopted from a test written by @marjakh
const rab = new ArrayBuffer(4 * 8, { maxByteLength: 1024 });
const ta = new Float64Array(rab, 0, 4);
for (let i = 0; i < 4; ++i) {
ta[i] = i;
}
class MyArray extends Uint32Array {
constructor(...params) {
super(...params);
}
static get [Symbol.species]() {
rab.resize(2 * 8);
return Uint32Array;
}
};
const a = new MyArray(rab);
const sliced = a.slice();
assert(a.length === 2); // Since it's length-tracking
assert(sliced.length === 4); // Since snapshotted length
assert(sliced[0] === 0);
assert(sliced[1] === 1);
if (hasPR74Behavior) {
// This is because the slice loop does the equivalent of sliced[2] = a[2].
assert(Number.isNaN(sliced[2]));
assert(Number.isNaN(sliced[3]));
}
if (hasPR75Behavior) {
// This is because the slice loop stops at index 1 due to resize.
assert(sliced[2] === 0);
assert(sliced[3] === 0);
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your argument about why this approach is preferable. I'll wait to update gh-72 until we get consensus at TC39. Same goes for the additions to Test262 that @marjakh has requested.
Curiously, your illustrative example has the same bug I originally had in my test and which confused me for several hours. :) With the code in your example: and since the MyArray ctor resizes unconditionally, the array is already resized before we call slice(). (Also FYI @jugglinmike , in case you'll later write the test262 test based on this example.) |
Oh wow yeah, let me make the example more sneaky... Edit: Changed it to resize inside a |
This reached consensus at the October 2021 TC39. |
Closes #73 At a high-level: - Lengths are cached at the beginning of methods. - Only in-bounds elements are copied. Together, this means that the result TAs have the snapshotted length at the beginning of the method. If the source TA's buffer shrunk due to the species constructor, the elements between [newShrunkLen, originalLen) in the result TA remain 0. This is an alternative solution to #74. In #74, elements between [newShrunkLen, originalLen) are set to the result of OOB [[Get]]s on the source TA. While doing so is maximally consistent with the Array versions of these methods, it turns out to be really weird for floating-point TAs. For OOB [[Get]]s on TAs returns `undefined`, and setting `undefined` in Float32Arrays and Float64Arrays coerces to NaN. This means extra complexity and code implementations have to do for an edge case (resize-in-the-middle) that's bad practice anyway.
6289d75
to
adb0e3e
Compare
Rebased. |
Closes #73
At a high-level:
Together, this means that the result TAs have the snapshotted length at
the beginning of the method. If the source TA's buffer shrunk due to
the species constructor, the elements between [newShrunkLen,
originalLen) in the result TA remain 0.
This is an alternative solution to #74. In #74, elements between
[newShrunkLen, originalLen) are set to the result of OOB [[Get]]s on the
source TA. While doing so is maximally consistent with the Array
versions of these methods, it turns out to be really weird for
floating-point TAs. For OOB [[Get]]s on TAs returns
undefined
, andsetting
undefined
in Float32Arrays and Float64Arrays coerces to NaN.This means extra complexity and code implementations have to do for an
edge case (resize-in-the-middle) that's bad practice anyway.