mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 05:38:39 +00:00
Issue #2551 - implement array.prototype.toSpliced
This commit is contained in:
@@ -345,6 +345,60 @@ function ArrayWith(index, value) {
|
||||
return A;
|
||||
}
|
||||
|
||||
// ES2023 22.1.3.35 Array.prototype.toSpliced ( start, deleteCount, ...items )
|
||||
function ArrayToSpliced(start, deleteCount) {
|
||||
// Step 1.
|
||||
var O = ToObject(this);
|
||||
|
||||
// Step 2.
|
||||
var len = ToLength(O.length);
|
||||
|
||||
// Step 3.
|
||||
var relativeStart = ToInteger(start);
|
||||
var actualStart =
|
||||
relativeStart < 0
|
||||
? std_Math_max(len + relativeStart, 0)
|
||||
: std_Math_min(relativeStart, len);
|
||||
|
||||
// Step 4.
|
||||
var insertCount = arguments.length > 2 ? arguments.length - 2 : 0;
|
||||
|
||||
// Step 5.
|
||||
var actualDeleteCount;
|
||||
if (arguments.length === 0) {
|
||||
actualDeleteCount = 0;
|
||||
} else if (arguments.length === 1) {
|
||||
actualDeleteCount = len - actualStart;
|
||||
} else {
|
||||
var dc = ToInteger(deleteCount);
|
||||
actualDeleteCount = std_Math_min(std_Math_max(dc, 0), len - actualStart);
|
||||
}
|
||||
|
||||
// Steps 6-8.
|
||||
var newLen = len + insertCount - actualDeleteCount;
|
||||
if (newLen > MAX_NUMERIC_INDEX) ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
|
||||
var A = std_Array(newLen);
|
||||
|
||||
// Steps 9-10.
|
||||
var k = 0;
|
||||
for (; k < actualStart; k++) {
|
||||
_DefineDataProperty(A, k, O[k]);
|
||||
}
|
||||
|
||||
// Step 11.
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
_DefineDataProperty(A, k++, arguments[i]);
|
||||
}
|
||||
|
||||
// Steps 12-13.
|
||||
for (var from = actualStart + actualDeleteCount; from < len; from++) {
|
||||
_DefineDataProperty(A, k++, O[from]);
|
||||
}
|
||||
|
||||
// Step 14.
|
||||
return A;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.18. */
|
||||
function ArrayForEach(callbackfn /*, thisArg*/) {
|
||||
/* Step 1. */
|
||||
|
||||
@@ -3209,6 +3209,7 @@ static const JSFunctionSpec array_methods[] = {
|
||||
JS_SELF_HOSTED_FN("findLast", "ArrayFindLast", 1,0),
|
||||
JS_SELF_HOSTED_FN("findLastIndex", "ArrayFindLastIndex", 1,0),
|
||||
JS_SELF_HOSTED_FN("toReversed", "ArrayToReversed", 0,0),
|
||||
JS_SELF_HOSTED_FN("toSpliced", "ArrayToSpliced", 2,0),
|
||||
JS_SELF_HOSTED_FN("toSorted", "ArrayToSorted", 1,0),
|
||||
JS_SELF_HOSTED_FN("with", "ArrayWith", 2,0),
|
||||
|
||||
@@ -3383,6 +3384,7 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
|
||||
!DefineProperty(cx, unscopables, cx->names().includes, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().keys, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().toReversed, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().toSpliced, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().values, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().toSorted, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().with, value))
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>Array.prototype.toSpliced test</title>
|
||||
<style>
|
||||
body { font: 14px/1.4 sans-serif; padding: 16px; }
|
||||
.pass { color: #0a0; }
|
||||
.fail { color: #c00; }
|
||||
pre { white-space: pre-wrap; }
|
||||
</style>
|
||||
<pre id="log"></pre>
|
||||
<script>
|
||||
(function() {
|
||||
const log = document.getElementById("log");
|
||||
function write(msg, cls) {
|
||||
const line = document.createElement("div");
|
||||
if (cls) line.className = cls;
|
||||
line.textContent = msg;
|
||||
log.appendChild(line);
|
||||
}
|
||||
|
||||
window.assertEq = function(actual, expected, msg) {
|
||||
if (actual !== expected) {
|
||||
throw new Error((msg ? msg + ": " : "") +
|
||||
"expected " + expected + ", got " + actual);
|
||||
}
|
||||
};
|
||||
|
||||
window.assertThrowsInstanceOf = function(fn, ctor, msg) {
|
||||
let threw = false;
|
||||
try {
|
||||
fn();
|
||||
} catch (e) {
|
||||
if (e instanceof ctor) {
|
||||
threw = true;
|
||||
} else {
|
||||
throw new Error((msg ? msg + ": " : "") +
|
||||
"threw " + e + ", expected " + ctor.name);
|
||||
}
|
||||
}
|
||||
if (!threw) {
|
||||
throw new Error((msg ? msg + ": " : "") + "did not throw");
|
||||
}
|
||||
};
|
||||
|
||||
window.reportCompare = function() {};
|
||||
|
||||
let hadError = false;
|
||||
window.addEventListener("error", function(e) {
|
||||
hadError = true;
|
||||
write("FAIL: " + e.message, "fail");
|
||||
});
|
||||
window.addEventListener("load", function() {
|
||||
write("PASS: toSpliced.js loaded", "pass");
|
||||
if (!hadError)
|
||||
write("PASS: all toSpliced tests passed", "pass");
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="toSpliced.js"></script>
|
||||
@@ -0,0 +1,113 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/ */
|
||||
|
||||
assertEq(typeof Array.prototype.toSpliced, "function");
|
||||
assertEq(Array.prototype.toSpliced.length, 2);
|
||||
|
||||
let desc = Object.getOwnPropertyDescriptor(Array.prototype, "toSpliced");
|
||||
assertEq(desc.writable, true);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, true);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
Array.prototype.toSpliced.call(null, 0, 0);
|
||||
}, TypeError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
Array.prototype.toSpliced.call(undefined, 0, 0);
|
||||
}, TypeError);
|
||||
|
||||
// Non-mutating behavior.
|
||||
let original = [1, 2, 3, 4];
|
||||
let spliced = original.toSpliced(1, 2, "a", "b");
|
||||
assertEq(original !== spliced, true);
|
||||
assertEq(original.join(","), "1,2,3,4");
|
||||
assertEq(spliced.join(","), "1,a,b,4");
|
||||
assertEq(original.toSpliced().join(","), "1,2,3,4");
|
||||
|
||||
// Start index clamping and negatives.
|
||||
assertEq([1, 2, 3].toSpliced(-1, 1, 9).join(","), "1,2,9");
|
||||
assertEq([1, 2, 3].toSpliced(-99, 1, 9).join(","), "9,2,3");
|
||||
assertEq([1, 2, 3].toSpliced(99, 1, 9).join(","), "1,2,3,9");
|
||||
|
||||
// deleteCount handling.
|
||||
assertEq([1, 2, 3].toSpliced(1).join(","), "1");
|
||||
assertEq([1, 2, 3].toSpliced(1, -3, 9).join(","), "1,9,2,3");
|
||||
|
||||
// Holes are materialized as undefined in copied segments.
|
||||
let sparse = [1, , 3, 4];
|
||||
let sparseResult = sparse.toSpliced(2, 1, "x");
|
||||
assertEq(sparseResult.length, 4);
|
||||
assertEq(sparseResult[0], 1);
|
||||
assertEq(sparseResult[1], undefined);
|
||||
assertEq(1 in sparseResult, true);
|
||||
assertEq(sparseResult[2], "x");
|
||||
assertEq(sparseResult[3], 4);
|
||||
|
||||
// Generic behavior on array-like values.
|
||||
let arrayLike = { 0: "a", 1: "b", 3: "d", length: 4 };
|
||||
let arrayLikeResult = Array.prototype.toSpliced.call(arrayLike, 1, 2, "X", "Y");
|
||||
assertEq(Array.isArray(arrayLikeResult), true);
|
||||
assertEq(arrayLikeResult.join(","), "a,X,Y,d");
|
||||
|
||||
// Element reads are ascending and skipped region is not read.
|
||||
let accessLog = [];
|
||||
let getterArr = {
|
||||
length: 5,
|
||||
get 0() {
|
||||
accessLog.push(0);
|
||||
return "z0";
|
||||
},
|
||||
get 1() {
|
||||
accessLog.push(1);
|
||||
throw new Error("must not be read");
|
||||
},
|
||||
get 2() {
|
||||
accessLog.push(2);
|
||||
throw new Error("must not be read");
|
||||
},
|
||||
get 3() {
|
||||
accessLog.push(3);
|
||||
return "z3";
|
||||
},
|
||||
get 4() {
|
||||
accessLog.push(4);
|
||||
return "z4";
|
||||
},
|
||||
};
|
||||
let getterResult = Array.prototype.toSpliced.call(getterArr, 1, 2, "X");
|
||||
assertEq(accessLog.join(","), "0,3,4");
|
||||
assertEq(getterResult.join(","), "z0,X,z3,z4");
|
||||
|
||||
// constructor / Symbol.species is ignored and constructor is not read.
|
||||
let constructorAccessed = false;
|
||||
let speciesIgnored = [1, 2, 3];
|
||||
Object.defineProperty(speciesIgnored, "constructor", {
|
||||
get: function () {
|
||||
constructorAccessed = true;
|
||||
throw new Error("constructor should not be read");
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
let speciesIgnoredResult = speciesIgnored.toSpliced(1, 1, 9);
|
||||
assertEq(constructorAccessed, false);
|
||||
assertEq(Array.isArray(speciesIgnoredResult), true);
|
||||
assertEq(speciesIgnoredResult.join(","), "1,9,3");
|
||||
|
||||
// Length limit check happens before indexed reads.
|
||||
let indexedRead = false;
|
||||
let tooLong = {
|
||||
length: 4294967296,
|
||||
get 0() {
|
||||
indexedRead = true;
|
||||
throw new Error("index getter should not run");
|
||||
},
|
||||
};
|
||||
assertThrowsInstanceOf(function () {
|
||||
Array.prototype.toSpliced.call(tooLong, 0, 0);
|
||||
}, RangeError);
|
||||
assertEq(indexedRead, false);
|
||||
|
||||
// @@unscopables must include toSpliced.
|
||||
assertEq(Array.prototype[Symbol.unscopables].toSpliced, true);
|
||||
|
||||
if (typeof reportCompare === "function") reportCompare(0, 0);
|
||||
@@ -430,6 +430,7 @@
|
||||
macro(toLocaleString, toLocaleString, "toLocaleString") \
|
||||
macro(toReversed, toReversed, "toReversed") \
|
||||
macro(toSource, toSource, "toSource") \
|
||||
macro(toSpliced, toSpliced, "toSpliced") \
|
||||
macro(toSorted, toSorted, "toSorted") \
|
||||
macro(toString, toString, "toString") \
|
||||
macro(toUTCString, toUTCString, "toUTCString") \
|
||||
|
||||
Reference in New Issue
Block a user