fix(codemode): align array callback behavior (#36584)
This commit is contained in:
@@ -171,7 +171,6 @@ ultimate source of truth.
|
|||||||
- [ ] `Array.prototype.toSpliced`.
|
- [ ] `Array.prototype.toSpliced`.
|
||||||
- [ ] Canonical index handling: a key such as `"01"` must not alias index `1`.
|
- [ ] Canonical index handling: a key such as `"01"` must not alias index `1`.
|
||||||
- [ ] Complete sparse-array parity. Promise combinators do consume holes as `undefined` members, as in JS.
|
- [ ] Complete sparse-array parity. Promise combinators do consume holes as `undefined` members, as in JS.
|
||||||
- [ ] Correct `findLast` return behavior when its predicate mutates the examined element.
|
|
||||||
|
|
||||||
## Strings
|
## Strings
|
||||||
|
|
||||||
|
|||||||
@@ -727,16 +727,16 @@ const invokeArrayMethod = <R>(
|
|||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
case "reduce": {
|
case "reduce": {
|
||||||
let accumulator: unknown
|
let start = 0
|
||||||
let start: number
|
let accumulator = args[1]
|
||||||
if (args.length >= 2) {
|
if (args.length < 2) {
|
||||||
accumulator = args[1]
|
while (start < length && !(start in target)) start += 1
|
||||||
start = 0
|
if (start === length)
|
||||||
} else {
|
throw new InterpreterRuntimeError("Array.reduce of an empty array with no initial value.", node).as(
|
||||||
if (length === 0)
|
"TypeError",
|
||||||
throw new InterpreterRuntimeError("Array.reduce of an empty array with no initial value.", node)
|
)
|
||||||
accumulator = target[0]
|
accumulator = target[start]
|
||||||
start = 1
|
start += 1
|
||||||
}
|
}
|
||||||
for (let index = start; index < length; index += 1) {
|
for (let index = start; index < length; index += 1) {
|
||||||
if (!(index in target)) continue
|
if (!(index in target)) continue
|
||||||
@@ -745,16 +745,16 @@ const invokeArrayMethod = <R>(
|
|||||||
return accumulator
|
return accumulator
|
||||||
}
|
}
|
||||||
case "reduceRight": {
|
case "reduceRight": {
|
||||||
let accumulator: unknown
|
let start = length - 1
|
||||||
let start: number
|
let accumulator = args[1]
|
||||||
if (args.length >= 2) {
|
if (args.length < 2) {
|
||||||
accumulator = args[1]
|
while (start >= 0 && !(start in target)) start -= 1
|
||||||
start = length - 1
|
if (start < 0)
|
||||||
} else {
|
throw new InterpreterRuntimeError("Array.reduceRight of an empty array with no initial value.", node).as(
|
||||||
if (length === 0)
|
"TypeError",
|
||||||
throw new InterpreterRuntimeError("Array.reduceRight of an empty array with no initial value.", node)
|
)
|
||||||
accumulator = target[length - 1]
|
accumulator = target[start]
|
||||||
start = length - 2
|
start -= 1
|
||||||
}
|
}
|
||||||
for (let index = start; index >= 0; index -= 1) {
|
for (let index = start; index >= 0; index -= 1) {
|
||||||
if (!(index in target)) continue
|
if (!(index in target)) continue
|
||||||
@@ -764,7 +764,8 @@ const invokeArrayMethod = <R>(
|
|||||||
}
|
}
|
||||||
case "findLast":
|
case "findLast":
|
||||||
for (let index = length - 1; index >= 0; index -= 1) {
|
for (let index = length - 1; index >= 0; index -= 1) {
|
||||||
if (yield* apply([target[index], index, target])) return target[index]
|
const item = target[index]
|
||||||
|
if (yield* apply([item, index, target])) return item
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
case "findLastIndex":
|
case "findLastIndex":
|
||||||
|
|||||||
@@ -26,9 +26,11 @@
|
|||||||
* - test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js
|
* - test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js
|
||||||
* - test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js
|
* - test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js
|
||||||
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js
|
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js
|
||||||
|
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-c-ii-20.js
|
||||||
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js
|
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js
|
||||||
* - test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js
|
* - test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js
|
||||||
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js
|
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js
|
||||||
|
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-c-ii-20.js
|
||||||
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js
|
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js
|
||||||
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js
|
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js
|
||||||
* - test/built-ins/Array/prototype/flatMap/depth-always-one.js
|
* - test/built-ins/Array/prototype/flatMap/depth-always-one.js
|
||||||
@@ -210,6 +212,11 @@ const cases = [
|
|||||||
code: `let calls = 0; const result = [1].reduce(() => { calls += 1; return 2 }); return [result, calls]`,
|
code: `let calls = 0; const result = [1].reduce(() => { calls += 1; return 2 }); return [result, calls]`,
|
||||||
expected: [1, 0],
|
expected: [1, 0],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-c-ii-20.js",
|
||||||
|
code: `let accessed = false; const result = [11].reduce((previous) => { accessed = true; return previous === undefined }, undefined); return [result, accessed]`,
|
||||||
|
expected: [true, true],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js",
|
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js",
|
||||||
code: `const input = [1, 2, 3, 4, 5]; input.reduce(() => 1); return input`,
|
code: `const input = [1, 2, 3, 4, 5]; input.reduce(() => 1); return input`,
|
||||||
@@ -225,6 +232,11 @@ const cases = [
|
|||||||
code: `let calls = 0; const result = [1].reduceRight(() => { calls += 1; return 2 }); return [result, calls]`,
|
code: `let calls = 0; const result = [1].reduceRight(() => { calls += 1; return 2 }); return [result, calls]`,
|
||||||
expected: [1, 0],
|
expected: [1, 0],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-c-ii-20.js",
|
||||||
|
code: `let accessed = false; const result = [11].reduceRight((previous) => { accessed = true; return previous === undefined }, undefined); return [result, accessed]`,
|
||||||
|
expected: [true, true],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js",
|
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js",
|
||||||
code: `const input = [1, 2, 3, 4, 5]; input.reduceRight(() => 1); return input`,
|
code: `const input = [1, 2, 3, 4, 5]; input.reduceRight(() => 1); return input`,
|
||||||
@@ -323,3 +335,46 @@ describe("Test262 Array callback adaptations", () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("Array callback regressions", () => {
|
||||||
|
test("reduce and reduceRight find the first present element", async () => {
|
||||||
|
expect(
|
||||||
|
await value(`
|
||||||
|
const left = []
|
||||||
|
left[2] = 3
|
||||||
|
const right = []
|
||||||
|
right[0] = 4
|
||||||
|
right[3] = 1
|
||||||
|
right.pop()
|
||||||
|
return [left.reduce((a, b) => a + b), right.reduceRight((a, b) => a + b)]
|
||||||
|
`),
|
||||||
|
).toEqual([3, 4])
|
||||||
|
})
|
||||||
|
|
||||||
|
test("reduce and reduceRight reject arrays containing only holes", async () => {
|
||||||
|
expect(
|
||||||
|
await value(`
|
||||||
|
const values = []
|
||||||
|
values[2] = 1
|
||||||
|
values.pop()
|
||||||
|
let left
|
||||||
|
let right
|
||||||
|
try { values.reduce((a, b) => a + b) } catch (error) { left = error.name }
|
||||||
|
try { values.reduceRight((a, b) => a + b) } catch (error) { right = error.name }
|
||||||
|
return [left, right]
|
||||||
|
`),
|
||||||
|
).toEqual(["TypeError", "TypeError"])
|
||||||
|
})
|
||||||
|
|
||||||
|
test("findLast returns the value observed before predicate mutation", async () => {
|
||||||
|
expect(
|
||||||
|
await value(`
|
||||||
|
const values = [1]
|
||||||
|
return values.findLast((item, index, array) => {
|
||||||
|
array[index] = 2
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
`),
|
||||||
|
).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user