mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-23 02:36:57 -04:00
250 lines
6.5 KiB
TypeScript
250 lines
6.5 KiB
TypeScript
/*
|
|
* Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
|
|
* - test/language/statements/labeled/continue.js
|
|
* - test/language/statements/break/S12.8_A4_T1.js
|
|
* - test/language/statements/continue/simple-and-labeled.js
|
|
* - test/language/statements/continue/labeled-continue.js
|
|
* - test/language/statements/continue/nested-let-bound-for-loops-labeled-continue.js
|
|
*
|
|
* Copyright (C) 2009 the Sputnik authors. All rights reserved.
|
|
* Copyright (C) 2014, 2016 the V8 project authors. All rights reserved.
|
|
* Test262 portions are governed by the BSD license in LICENSE.test262.
|
|
*/
|
|
import { describe, expect, test } from "bun:test"
|
|
import { Effect } from "effect"
|
|
import { CodeMode } from "../src/index.js"
|
|
|
|
const execute = (code: string) => Effect.runPromise(CodeMode.execute({ code, tools: {} }))
|
|
|
|
const value = async (code: string) => {
|
|
const result = await execute(code)
|
|
if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
|
|
return result.value
|
|
}
|
|
|
|
describe("Test262 labeled break adaptations", () => {
|
|
test("break exits the matching labeled statement", async () => {
|
|
expect(
|
|
await value(`
|
|
const visited = []
|
|
target: {
|
|
visited.push(1)
|
|
break target
|
|
visited.push(2)
|
|
}
|
|
visited.push(3)
|
|
return visited
|
|
`),
|
|
).toEqual([1, 3])
|
|
})
|
|
|
|
test("break crosses nested loops and switches without being consumed early", async () => {
|
|
expect(
|
|
await value(`
|
|
const visited = []
|
|
outer: for (const item of [1, 2, 3]) {
|
|
switch (item) {
|
|
case 2:
|
|
break outer
|
|
default:
|
|
visited.push(item)
|
|
}
|
|
}
|
|
return visited
|
|
`),
|
|
).toEqual([1])
|
|
})
|
|
|
|
test("break targets every supported loop form", async () => {
|
|
expect(
|
|
await value(`
|
|
const counts = { while: 0, doWhile: 0, for: 0, forOf: 0, forIn: 0 }
|
|
whileLabel: while (true) {
|
|
counts.while++
|
|
break whileLabel
|
|
}
|
|
doLabel: do {
|
|
counts.doWhile++
|
|
break doLabel
|
|
} while (true)
|
|
forLabel: for (;;) {
|
|
counts.for++
|
|
break forLabel
|
|
}
|
|
forOfLabel: for (const item of [1, 2]) {
|
|
counts.forOf++
|
|
break forOfLabel
|
|
}
|
|
forInLabel: for (const key in { a: 1, b: 2 }) {
|
|
counts.forIn++
|
|
break forInLabel
|
|
}
|
|
return counts
|
|
`),
|
|
).toEqual({ while: 1, doWhile: 1, for: 1, forOf: 1, forIn: 1 })
|
|
})
|
|
|
|
test("nested labels consume only their matching break", async () => {
|
|
expect(
|
|
await value(`
|
|
const visited = []
|
|
outer: {
|
|
inner: {
|
|
visited.push(1)
|
|
break outer
|
|
}
|
|
visited.push(2)
|
|
}
|
|
visited.push(3)
|
|
return visited
|
|
`),
|
|
).toEqual([1, 3])
|
|
})
|
|
})
|
|
|
|
describe("Test262 labeled continue adaptations", () => {
|
|
test("continue targets its labeled for loop", async () => {
|
|
expect(
|
|
await value(`
|
|
let count = 0
|
|
label: for (let index = 0; index < 10; index++) {
|
|
count++
|
|
continue label
|
|
}
|
|
return count
|
|
`),
|
|
).toBe(10)
|
|
})
|
|
|
|
test("continue escapes an inner loop for the targeted outer loop", async () => {
|
|
expect(
|
|
await value(`
|
|
let count = 0
|
|
outer: for (let x = 0; x < 10; x++) {
|
|
let y = 0
|
|
while (true) {
|
|
y++
|
|
count++
|
|
if (y === 1) continue outer
|
|
throw new Error("inner loop consumed the outer continue")
|
|
}
|
|
}
|
|
return count
|
|
`),
|
|
).toBe(10)
|
|
})
|
|
|
|
test("multiple labels can target the same iteration statement", async () => {
|
|
expect(
|
|
await value(`
|
|
const visited = []
|
|
outer: inner: for (const item of [1, 2, 3]) {
|
|
visited.push(item)
|
|
continue outer
|
|
}
|
|
return visited
|
|
`),
|
|
).toEqual([1, 2, 3])
|
|
})
|
|
|
|
test("labeled continue works for every supported loop form", async () => {
|
|
expect(
|
|
await value(`
|
|
const counts = { while: 0, doWhile: 0, forOf: 0, forIn: 0 }
|
|
let index = 0
|
|
whileLabel: while (index++ < 2) {
|
|
counts.while++
|
|
continue whileLabel
|
|
}
|
|
|
|
index = 0
|
|
doLabel: do {
|
|
counts.doWhile++
|
|
if (index++ < 1) continue doLabel
|
|
} while (index < 2)
|
|
|
|
forOfLabel: for (const item of [1, 2]) {
|
|
counts.forOf += item
|
|
continue forOfLabel
|
|
}
|
|
|
|
forInLabel: for (const key in { a: 1, b: 2 }) {
|
|
counts.forIn++
|
|
continue forInLabel
|
|
}
|
|
return counts
|
|
`),
|
|
).toEqual({ while: 2, doWhile: 2, forOf: 3, forIn: 2 })
|
|
})
|
|
|
|
test("labeled continues preserve per-iteration lexical bindings", async () => {
|
|
expect(
|
|
await value(`
|
|
const classic = []
|
|
classicLabel: for (let index = 0; index < 3; index++) {
|
|
classic.push(() => index)
|
|
continue classicLabel
|
|
}
|
|
|
|
const nested = []
|
|
nestedLabel: for (let outer = 0; outer < 3; outer++) {
|
|
for (let inner = 0; inner < 2; inner++) {
|
|
nested.push(() => outer)
|
|
continue nestedLabel
|
|
}
|
|
}
|
|
|
|
const forOf = []
|
|
forOfLabel: for (const item of [1, 2, 3]) {
|
|
forOf.push(() => item)
|
|
continue forOfLabel
|
|
}
|
|
return [classic.map((read) => read()), nested.map((read) => read()), forOf.map((read) => read())]
|
|
`),
|
|
).toEqual([
|
|
[0, 1, 2],
|
|
[0, 1, 2],
|
|
[1, 2, 3],
|
|
])
|
|
})
|
|
|
|
test("finally completions override labeled loop control", async () => {
|
|
expect(
|
|
await value(`
|
|
const visited = []
|
|
breakWins: for (const item of [1, 2, 3]) {
|
|
try {
|
|
continue breakWins
|
|
} finally {
|
|
visited.push(item)
|
|
break breakWins
|
|
}
|
|
}
|
|
|
|
continueWins: for (const item of [4, 5, 6]) {
|
|
try {
|
|
break continueWins
|
|
} finally {
|
|
visited.push(item)
|
|
continue continueWins
|
|
}
|
|
}
|
|
return visited
|
|
`),
|
|
).toEqual([1, 4, 5, 6])
|
|
})
|
|
|
|
test("a label on a non-iteration statement is not a continue target", async () => {
|
|
const result = await execute(`
|
|
do {
|
|
target: {
|
|
continue target
|
|
}
|
|
} while (false)
|
|
`)
|
|
expect(result.ok).toBe(false)
|
|
if (result.ok) return
|
|
expect(result.error.kind).toBe("ParseError")
|
|
})
|
|
})
|