mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
Array.prototype.join reads the length before it converts the separator. The lowering converted the separator first and let the join read the length afterwards, so a separator whose toString changed the array joined the wrong number of elements. When ToString of the separator value can run code of the program (a checked-dynamic value, an object or class instance with toString), the join now reads the length first: an array that grew joins its first elements, and the missing elements of one that shrank join as empty strings. Primitive separators keep the direct join.
29 lines
1.1 KiB
JavaScript
29 lines
1.1 KiB
JavaScript
// Array.prototype.join reads the length before it converts the separator:
|
|
// a separator whose toString changes the array joins exactly that many
|
|
// elements, and elements it removed join as empty strings. Object literal,
|
|
// class instance and untyped separators.
|
|
function joinChanging(change) {
|
|
const values = ["a", "b", "c"];
|
|
const sep = {
|
|
toString() {
|
|
change(values);
|
|
return "-";
|
|
},
|
|
};
|
|
return values.join(sep);
|
|
}
|
|
console.log(joinChanging((values) => { values.push("d"); }));
|
|
console.log(joinChanging((values) => { values.length = 1; }));
|
|
console.log(joinChanging((values) => { values.length = 0; }));
|
|
console.log(joinChanging((values) => { values[1] = "B"; }));
|
|
console.log(joinChanging(() => {}));
|
|
class Sep {
|
|
constructor(values) { this.values = values; }
|
|
toString() { this.values.length = 2; return "+"; }
|
|
}
|
|
const shrinking = ["x", "y", "z"];
|
|
console.log(shrinking.join(new Sep(shrinking)));
|
|
function untyped(values, sep) { return values.join(sep); }
|
|
const grow = ["p", "q"];
|
|
console.log(untyped(grow, { toString() { grow.push("r"); return "/"; } }));
|