Host the bare compareArray from the upstream assert.js harness

harness/assert.js defines `compareArray(a, b)` beside `assert`: a plain
function that RETURNS a boolean, whose throwing counterpart is
`assert.compareArray` (already hosted). The adapter now injects the bare
name into every prepared test and implements the upstream comparison:
read both `.length`s, so a nullish operand throws the same TypeError,
return false on a length mismatch, otherwise compare elements by
SameValue.

That also admits the compareArray.js include when the only use is a
direct call. A value reference or property read stays excluded, because
it would need the upstream function object and its `format` expando.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Malte Ubl
2026-10-29 05:34:44 -07:00
co-authored by Claude Opus 5.5
parent 94e921728c
commit a4c19ae4b4
4 changed files with 59 additions and 3 deletions
+10 -2
View File
@@ -250,14 +250,22 @@ test("built-in error assertions are admitted without permitting constructor alia
expect(exclusion(alias, metadata(alias), "strict")).toBe("harness:assert-surface");
});
test("the compareArray include only admits the implemented assertion form", () => {
test("the compareArray include admits the assertion and the bare call form only", () => {
const accepted = source(
"includes: [compareArray.js]",
"assert.compareArray([1, NaN], [1, NaN]);",
);
expect(exclusion(accepted, metadata(accepted), "strict")).toBeUndefined();
const globalHelper = source("includes: [compareArray.js]", "compareArray([1], [1]);");
expect(exclusion(globalHelper, metadata(globalHelper), "strict")).toBe(
expect(exclusion(globalHelper, metadata(globalHelper), "strict")).toBeUndefined();
// assert.js supplies compareArray, so a test needs no include to call it.
const withoutInclude = source("description: intl", "assert(!compareArray([1], [2]));");
expect(exclusion(withoutInclude, metadata(withoutInclude), "strict")).toBeUndefined();
const valueReference = source(
"includes: [compareArray.js]",
"const compare = compareArray; assert(compare([1], [1]));",
);
expect(exclusion(valueReference, metadata(valueReference), "strict")).toBe(
"harness:compareArray-surface",
);
});
+20
View File
@@ -189,6 +189,26 @@ assert.notSameValue(1, "1");
source: "assert.compareArray([1, 2], [1, 3]);",
},
{ name: "array signed zero mismatch", status: "fail", source: "assert.compareArray([0], [-0]);" },
{
name: "bare compareArray returns true for matching contents",
status: "pass",
source: "assert(compareArray([1, NaN, -0], [1, NaN, -0]));",
},
{
name: "bare compareArray returns false for a length or element mismatch",
status: "pass",
source: "assert(!compareArray([1], [1, 2])); assert(!compareArray([1, 2], [1, 3]));",
},
{
name: "bare compareArray compares operands without a length as equal",
status: "pass",
source: "assert(compareArray(1, 2)); assert(compareArray({}, {}));",
},
{
name: "bare compareArray does not throw on a mismatch",
status: "fail",
source: "assert(compareArray([1], [2]));",
},
{ name: "assert requires true, not truthiness", status: "fail", source: "assert(1);" },
{
name: "exact built-in exception",
+22
View File
@@ -60,6 +60,28 @@ export function assert(value: unknown, message?: unknown): void {
if (value !== true) throw new Test262Error(text(message, "Expected true"));
}
function lengthOf(value: unknown): unknown {
return (value as { length?: unknown }).length;
}
function elementAt(value: unknown, index: number): unknown {
return (value as Record<number, unknown>)[index];
}
/** The bare `compareArray(a, b)` the upstream `harness/assert.js` defines
* alongside `assert`. It RETURNS a boolean (the throwing assertion is
* `assert.compareArray`), and it has no shape checks of its own: it reads
* `.length` off both operands, so a null operand throws TypeError there and
* here, and two operands without a length of their own compare equal. */
export function compareArray(actual: unknown, expected: unknown): boolean {
const length = lengthOf(actual);
if (lengthOf(expected) !== length) return false;
for (let i = 0; typeof length === "number" && i < length; i++) {
if (!same(elementAt(expected, i), elementAt(actual, i))) return false;
}
return true;
}
export namespace assert {
export function sameValue(actual: unknown, expected: unknown, message?: unknown): void {
if (!same(actual, expected)) throw new Test262Error(text(message, "Expected SameValue"));
+7 -1
View File
@@ -169,7 +169,11 @@ export function exclusion(source, meta, variant) {
// Supported assertion calls; aliases, mutations, and reflection stay out.
} else reason = "harness:assert-surface";
} else if (meta.includes.includes("compareArray.js") && ts.isIdentifier(node) && node.text === "compareArray" &&
!(ts.isCallExpression(node.parent) && node.parent.expression === node) &&
!(ts.isPropertyAccessExpression(node.parent) && node.parent.name === node && ts.isIdentifier(node.parent.expression) && node.parent.expression.text === "assert")) {
// assert.js defines the bare compareArray; only its direct call form is
// hosted (a value reference or property read would need the upstream
// function object and its format expando).
reason = "harness:compareArray-surface";
} else if (ts.isIdentifier(node) && node.text === "Test262Error") {
if (!ts.isNewExpression(node.parent) || node.parent.expression !== node) reason = "harness:Test262Error-surface";
@@ -192,7 +196,9 @@ export function exclusion(source, meta, variant) {
}
export function prepare(source, asyncTest = false, marker = completion, variant = "strict", includes = []) {
const names = asyncTest ? "assert, Test262Error, $DONE" : "assert, Test262Error";
const names = asyncTest
? "assert, compareArray, Test262Error, $DONE"
: "assert, compareArray, Test262Error";
const end = asyncTest ? "" : `;console.log(${JSON.stringify(marker)});\n`;
const helpers =
(includes.includes("propertyHelper.js") ? `${propertyHelperSource}\n` : "") +