mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-14 10:35:47 +08:00
Array.prototype.push.call({length: 1, 0: "x"}, "y") and its siblings
answered "not supported yet": the checked-dynamic array dispatch only ran
the methods of a receiver that is a real array, though the algorithms are
generic in the language. PR #2505 implemented them inside scr_dyn_invoke.c,
which every program that links that unit then differed on (373 DIFFERS,
108 runtime-only programs), so it was withheld.
The algorithms now live in their own gated unit, scr_dyn_array_like.c,
reached through a distinct libCall (dyn.arrayLikeProtoCall) that only a
JavaScript source emits. A native array receiver, or a live typed-ref array,
delegates to the invoke unit's dispatch, so the array path keeps one
implementation and its exact bytes. Methods: concat, copyWithin, fill,
flat, flatMap, join, pop, push, reverse, shift, splice, toReversed,
toSorted, toSpliced, unshift, with.
Each algorithm starts with `? ToObject(this value)`, so a nullish
receiver is a TypeError before any length is read — the check the invoke
unit answers for its own receivers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
3.6 KiB
JavaScript
73 lines
3.6 KiB
JavaScript
// Array.prototype's methods are generic: they run over any object with a
|
|
// length, reading and writing ordinary properties. A call on such an
|
|
// object used to answer "not supported yet" for every method other than
|
|
// at/slice/indexOf/includes and the callback methods.
|
|
const resolved = [];
|
|
const report = (label, value) => resolved.push(label + '=' + value);
|
|
|
|
const box = { length: 3, 0: 'a', 1: 'b', 2: 'c' };
|
|
report('join', Array.prototype.join.call(box, '-'));
|
|
report('push', Array.prototype.push.call(box, 'd'));
|
|
report('after-push', Array.prototype.join.call(box, ''));
|
|
report('pop', Array.prototype.pop.call(box));
|
|
report('length', box.length);
|
|
report('shift', Array.prototype.shift.call(box));
|
|
report('unshift', Array.prototype.unshift.call(box, 'x', 'y'));
|
|
report('after-unshift', Array.prototype.join.call(box, ','));
|
|
report('reverse', Array.prototype.join.call(Array.prototype.reverse.call(box), '/'));
|
|
report('concat', Array.prototype.concat.call(box, ['z']).join('+'));
|
|
report('slice', Array.prototype.slice.call(box).join('|'));
|
|
|
|
const filler = { length: 4, 0: 1, 1: 2, 2: 3, 3: 4 };
|
|
report('fill', Array.prototype.join.call(Array.prototype.fill.call(filler, 0, 1, 3), ''));
|
|
report('copyWithin', Array.prototype.join.call(Array.prototype.copyWithin.call(filler, 0, 2), ''));
|
|
|
|
const spliced = { length: 4, 0: 'a', 1: 'b', 2: 'c', 3: 'd' };
|
|
report('splice', Array.prototype.splice.call(spliced, 1, 2, 'X').join(''));
|
|
report('after-splice', Array.prototype.join.call(spliced, ''));
|
|
report('splice-none', Array.prototype.splice.call(spliced).length);
|
|
|
|
const ordered = { length: 3, 0: 3, 1: 1, 2: 2 };
|
|
report('toSorted', Array.prototype.toSorted.call(ordered).join(''));
|
|
report('toReversed', Array.prototype.toReversed.call(ordered).join(''));
|
|
report('toSpliced', Array.prototype.toSpliced.call(ordered, 1, 1).join(''));
|
|
report('with', Array.prototype.with.call(ordered, 0, 9).join(''));
|
|
report('with-negative', Array.prototype.with.call(ordered, -1, 9).join(''));
|
|
report('unsorted', Array.prototype.join.call(ordered, ''));
|
|
|
|
const nested = { length: 3, 0: [1, 2], 1: 3, 2: [4, [5]] };
|
|
report('flat', Array.prototype.flat.call(nested, 1).join(''));
|
|
report('flat-deep', Array.prototype.flat.call(nested, 2).join(''));
|
|
report('flatMap', Array.prototype.flatMap.call(nested, (value) => [value, value]).length);
|
|
|
|
// Holes and absent indexes stay holes.
|
|
const sparse = { length: 3, 0: 'a', 2: 'c' };
|
|
const copied = Array.prototype.toReversed.call(sparse);
|
|
report('holes', '1' in copied ? 'present' : 'absent');
|
|
report('holes-join', copied.join(''));
|
|
|
|
// Writes beyond the receiver's length extend it, as the length assignment
|
|
// at the end of push/splice does.
|
|
const extended = { length: 1, 0: 'only' };
|
|
Array.prototype.push.call(extended, 'next');
|
|
report('extended', JSON.stringify(extended));
|
|
|
|
// Every one of the algorithms starts with `? ToObject(this value)`, so a
|
|
// nullish receiver is a TypeError before any length is read.
|
|
const attempt = (label, run) => {
|
|
try {
|
|
report(label, 'no-throw ' + JSON.stringify(run()));
|
|
} catch (error) {
|
|
report(label, error.constructor.name + ': ' + error.message);
|
|
}
|
|
};
|
|
attempt('null-join', () => Array.prototype.join.call(null));
|
|
attempt('undefined-push', () => Array.prototype.push.call(undefined, 1));
|
|
attempt('null-copyWithin', () => Array.prototype.copyWithin.call(null, 0, 0));
|
|
attempt('undefined-fill', () => Array.prototype.fill.call(undefined, 0));
|
|
attempt('null-toReversed', () => Array.prototype.toReversed.call(null));
|
|
attempt('undefined-with', () => Array.prototype.with.call(undefined, 0, 1));
|
|
attempt('null-splice', () => Array.prototype.splice.call(null, 0, 1));
|
|
|
|
console.log(resolved.join('\n'));
|