From 31b2712a0a074ce5c61ff0020b81b08dc0852446 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Fri, 11 Sep 2026 16:22:36 -0500 Subject: [PATCH] refactor(codemode): give errors a real prototype chain and JS error types --- packages/codemode/interpreter-support.md | 38 ++- packages/codemode/src/data.ts | 7 + packages/codemode/src/interpreter/errors.ts | 51 ++-- packages/codemode/src/interpreter/globals.ts | 7 +- packages/codemode/src/interpreter/host.ts | 2 +- .../codemode/src/interpreter/intrinsics.ts | 51 ++++ packages/codemode/src/interpreter/methods.ts | 32 +- packages/codemode/src/interpreter/model.ts | 18 +- packages/codemode/src/interpreter/objects.ts | 14 +- packages/codemode/src/interpreter/promises.ts | 19 +- packages/codemode/src/interpreter/runner.ts | 8 +- packages/codemode/src/interpreter/runtime.ts | 58 ++-- packages/codemode/src/interpreter/scope.ts | 12 +- packages/codemode/src/stdlib/array.ts | 8 +- packages/codemode/src/stdlib/collections.ts | 19 +- packages/codemode/src/stdlib/date.ts | 4 +- packages/codemode/src/stdlib/json.ts | 9 +- packages/codemode/src/stdlib/math.ts | 6 +- packages/codemode/src/stdlib/number.ts | 4 +- packages/codemode/src/stdlib/object.ts | 24 +- packages/codemode/src/stdlib/regexp.ts | 16 +- packages/codemode/src/stdlib/url.ts | 27 +- packages/codemode/src/stdlib/value.ts | 29 +- packages/codemode/src/stdlib/web.ts | 8 +- packages/codemode/test/parity.test.ts | 40 ++- packages/codemode/test/test262/run.ts | 31 +- packages/codemode/test/test262/skipped.txt | 274 ++---------------- packages/codemode/test/web-wpt.test.ts | 11 +- 28 files changed, 317 insertions(+), 510 deletions(-) create mode 100644 packages/codemode/src/interpreter/intrinsics.ts diff --git a/packages/codemode/interpreter-support.md b/packages/codemode/interpreter-support.md index cbb2960188e3..5844a8f06c2e 100644 --- a/packages/codemode/interpreter-support.md +++ b/packages/codemode/interpreter-support.md @@ -74,6 +74,7 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262 - [ ] Destructuring a key that member access resolves through the owning built-in, such as `const { constructor } = error`, reads `undefined`. - [ ] Member expressions as `for...in` targets (`for (x.y in obj)`). +- [ ] `IteratorClose` during destructuring should throw a `TypeError` when `return()` yields a non-object. ## Statements and control flow @@ -128,8 +129,8 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262 - [ ] A named function expression's name is not bound inside its own body. - [ ] Redeclaring a function in the same scope is rejected; in JavaScript the last declaration wins. - [ ] A line terminator between `async function` and the function name. -- [ ] Async generator functions evaluate parameter defaults and destructuring at the first `next()` rather than at the - call, so their errors are not thrown synchronously. +- [ ] Generator and async generator functions evaluate parameter defaults and destructuring at the first `next()` + rather than at the call, so their errors are not thrown synchronously. - [x] Synchronous and async generator declarations/expressions, `yield`, and `yield*`, including lazy bodies, `next(value)`, `return(value)`, `throw(value)`, exhaustion, promise adoption, async request ordering, `try`/`catch`/`finally`, and sync/async iterator symbols. Async `yield*` awaits values while adapting a sync @@ -173,8 +174,12 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262 - [x] Plain, arithmetic, bitwise, and logical assignment operators. - [x] Property deletion on plain data objects and arrays, including computed and optional forms; deleting an array index creates a hole without changing its length. -- [ ] Operators, `switch` discriminants, and coercion helpers such as `String` and `isNaN` applied to functions and - namespaces; JavaScript coerces them, the interpreter rejects non-data operands. +- [ ] Operators, `switch` discriminants, template interpolation, and coercion helpers such as `String` and `isNaN` + applied to functions and namespaces; JavaScript coerces them, the interpreter rejects non-data operands. +- [ ] ToPrimitive on object operands: operators, `Error(message)`, `Date` arguments, and `parseInt` radix should call + `valueOf`/`toString` in spec order and surface their throws. +- [ ] Property keys follow ToPropertyKey: `x[null]`, `x[true]`, and objects (via `toString`) become string keys; only + strings and numbers are accepted. ## Promises and tools @@ -239,15 +244,17 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262 targets for index keys only; a primitive target is a `TypeError` rather than a boxed object. - [x] `Object.keys` over arrays and tool references. - [x] Object identity is preserved by in-CodeMode Object helpers. -- [x] `__proto__`, `constructor`, and `prototype` are ordinary own data keys. `x.constructor` without an own key resolves - to the owning built-in (`[].constructor === Array`, `new TypeError().constructor === TypeError`); prototype objects - are not observable, so `[].__proto__` and `Object.prototype` read as `undefined` and `o.__proto__ = x` sets an own - field. +- [x] `__proto__`, `constructor`, and `prototype` are ordinary own data keys. Errors inherit `constructor` from a real + `Error.prototype` → `TypeError.prototype` chain, so `new TypeError().constructor === TypeError` holds through + member access and destructuring alike; `x.constructor` on other values without an own key resolves to the owning + built-in (`[].constructor === Array`). Prototype objects are not observable, so `[].__proto__` and + `Object.prototype` read as `undefined` and `o.__proto__ = x` sets an own field. - [x] Circular references are rejected when created (`o.self = o`, `array.push(array)`), not at serialization as in JS. - [x] `Object.is` for supported data values. - [x] `Object.groupBy` over finite collections and custom synchronous iterators/generators, with string-key coercion and plain-object results. -- [ ] `Object.prototype` methods on values: `toString`, `toLocaleString`, `valueOf`, and `hasOwnProperty`. +- [ ] `Object.prototype` methods on values: `toString`, `toLocaleString`, `valueOf`, `hasOwnProperty`, and + `propertyIsEnumerable`. ## Arrays @@ -298,6 +305,8 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262 `repeat` still requires a finite non-negative count. - [x] Native no-argument parity for `match()`, `matchAll()`, and `search()`; all behave as an empty pattern. Present arguments must still be a regular expression or string pattern. +- [ ] `String.raw`. +- [ ] `match`, `search`, and `split` accept any value and coerce it (objects via `toString`), like JavaScript. ## Numbers and Math @@ -352,6 +361,8 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262 - [x] `Date.prototype.toUTCString` and its `toGMTString` alias. - [x] `toDateString` and `toTimeString` in the host's local timezone. - [x] Native one-argument Date coercion for supported values, including booleans, null, arrays, and plain objects. +- [ ] Date setters and multi-argument construction coerce object arguments through `valueOf`/`toString` and surface + their throws. - [x] Native Date loose-equality and default primitive-coercion semantics, using CodeMode's deterministic ISO string representation for the string primitive. - [x] Native `RangeError` branding for invalid `toISOString()` calls. @@ -396,8 +407,8 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262 ## Web platform helpers -- [x] `atob` and `btoa` with forgiving-base64 decoding and WebIDL string conversion; invalid input throws an Error - named `InvalidCharacterError`, since there is no `DOMException`. +- [x] `atob` and `btoa` with forgiving-base64 decoding and WebIDL string conversion; invalid input throws a + `TypeError`, since there is no `DOMException`. - [x] `crypto.randomUUID()`. - [ ] `crypto.getRandomValues` and `crypto.subtle`, `TextEncoder`/`TextDecoder`, and `Blob`: these need a binary value type, which the JSON-like data model does not have yet. @@ -421,5 +432,6 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262 - [x] Caught errors do not distinguish user throws, interpreter failures, and tool failures; a program sees one Error-shaped value with `name` and `message` in `catch`, rejection handlers, and `Promise.allSettled` reasons. This is deliberate: the program should handle a failure the same way regardless of where it originated. -- [ ] Failures raised by the interpreter itself carry the generic `Error` name where JavaScript throws a `TypeError`, - `RangeError`, or `ReferenceError`, so `e instanceof TypeError` and `e.constructor === TypeError` are false. +- [x] Failures raised by the interpreter are `TypeError`s unless JavaScript names them otherwise (`RangeError`, + `ReferenceError`, `SyntaxError`, `URIError`), so `e instanceof TypeError` and `e.constructor === TypeError` + hold. Unsupported syntax reached at runtime is a `SyntaxError`; awaited tool failures stay plain `Error`. diff --git a/packages/codemode/src/data.ts b/packages/codemode/src/data.ts index 84051172d34d..324995690f1f 100644 --- a/packages/codemode/src/data.ts +++ b/packages/codemode/src/data.ts @@ -2,9 +2,11 @@ export * as Data from "./data.js" import type { DiagnosticKind } from "./codemode.js" import { + get, ownEntries, parseArrayIndex, ProgramArray, + ProgramError, ProgramFunction, ProgramObject, set, @@ -120,6 +122,11 @@ const copy = (value: unknown, label: string, mode: Mode, depth: number, seen: Se } if (value instanceof ProgramObject) { const copied: Record = {} + // Errors serialize as { name, message, ...own }: both may be inherited, and neither is enumerable in JS. + if (value instanceof ProgramError) { + define(copied, "name", copy(get(value, "name"), label, mode, depth + 1, seen)) + define(copied, "message", copy(get(value, "message"), label, mode, depth + 1, seen)) + } for (const [key, item] of ownEntries(value)) { const next = copy(item, label, mode, depth + 1, seen) if (next === undefined && mode === "json") continue diff --git a/packages/codemode/src/interpreter/errors.ts b/packages/codemode/src/interpreter/errors.ts index 3367e2183c0c..16e1722a4e1a 100644 --- a/packages/codemode/src/interpreter/errors.ts +++ b/packages/codemode/src/interpreter/errors.ts @@ -5,15 +5,10 @@ import { toData, ToolRuntimeError } from "../data.js" import { type AstNode, formatLocation, InterpreterRuntimeError, ProgramThrow, sourceLocation } from "./model.js" import { containsRuntimeReference } from "./references.js" import { type HostCall, HostFunction } from "./host.js" -import { get, ProgramError, ProgramObject } from "./objects.js" +import { createErrorValue, type ErrorType, isErrorType } from "./intrinsics.js" +import { get, hasPrototype, ProgramArray, ProgramError, ProgramObject, set } from "./objects.js" import { type Runner } from "./runner.js" -import { - coerceToString, - createAggregateErrorValue, - createErrorValue, - errorBrandName, - errorConstructors, -} from "../stdlib/value.js" +import { coerceToString } from "../stdlib/value.js" export const normalizeError = (error: unknown): Diagnostic => { if (error instanceof InterpreterRuntimeError) { @@ -77,15 +72,19 @@ export const normalizeError = (error: unknown): Diagnostic => { } } -export const caughtErrorValue = (thrown: unknown): unknown => { +export const caughtErrorValue = (runner: Runner, thrown: unknown): unknown => { if (thrown instanceof ProgramThrow) return thrown.value - if (thrown instanceof InterpreterRuntimeError) return createErrorValue(thrown.errorName, thrown.message) - const name = thrown instanceof Error && errorConstructors.has(thrown.name) ? thrown.name : "Error" - return createErrorValue(name, normalizeError(thrown).message) + const prototypes = runner.intrinsics.errors + if (thrown instanceof InterpreterRuntimeError) return createErrorValue(prototypes[thrown.type], thrown.message) + const type = thrown instanceof Error && isErrorType(thrown.name) ? thrown.name : "Error" + return createErrorValue(prototypes[type], normalizeError(thrown).message) } -const constructErrorValue = (name: string, args: Array): ProgramError => - createErrorValue(name, args[0] === undefined ? "" : coerceToString(args[0])) +export const createAggregateErrorValue = (runner: Runner, errors: Array, message: string) => { + const value = createErrorValue(runner.intrinsics.errors.AggregateError, message) + set(value, "errors", new ProgramArray(errors)) + return value +} const constructAggregateErrorValue = ( runner: Runner, @@ -95,33 +94,31 @@ const constructAggregateErrorValue = ( Effect.gen(function* () { const cursor = yield* runner.syncIterator(args[0], node) if (cursor === undefined) { - throw new InterpreterRuntimeError("new AggregateError(...) expects a synchronous iterable of errors.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("new AggregateError(...) expects a synchronous iterable of errors.", node) } const errors: Array = [] while (true) { const step = yield* cursor.next if (step.done) { - return createAggregateErrorValue(errors, args[1] === undefined ? "" : coerceToString(args[1])) + return createAggregateErrorValue(runner, errors, args[1] === undefined ? "" : coerceToString(args[1])) } errors.push(step.value) } }) /** An error constructor such as `Error` or `TypeError`; callable with or without `new`, like JS. */ -export const errorGlobal = (name: string, runner: Runner) => { +export const errorGlobal = (type: ErrorType, runner: Runner) => { + const prototype = runner.intrinsics.errors[type] const construct: HostCall = (args, node) => - name === "AggregateError" + type === "AggregateError" ? constructAggregateErrorValue(runner, args, node) - : Effect.sync(() => constructErrorValue(name, args)) - return new HostFunction({ - name, + : Effect.sync(() => createErrorValue(prototype, args[0] === undefined ? undefined : coerceToString(args[0]))) + const fn = new HostFunction({ + name: type, call: construct, construct, - instanceOf: (value) => { - const brand = errorBrandName(value) - return brand !== undefined && (name === "Error" || brand === name) - }, + instanceOf: (value) => hasPrototype(value, prototype), }) + set(prototype, "constructor", fn) + return fn } diff --git a/packages/codemode/src/interpreter/globals.ts b/packages/codemode/src/interpreter/globals.ts index 91dc7551c23b..ca24e42c4aba 100644 --- a/packages/codemode/src/interpreter/globals.ts +++ b/packages/codemode/src/interpreter/globals.ts @@ -10,10 +10,11 @@ import { objectGlobal } from "../stdlib/object.js" import { regexpGlobal } from "../stdlib/regexp.js" import { stringGlobal } from "../stdlib/string.js" import { uriGlobal, urlGlobal, urlSearchParamsGlobal } from "../stdlib/url.js" -import { coercion, errorConstructors } from "../stdlib/value.js" +import { coercion } from "../stdlib/value.js" import { atobGlobal, btoaGlobal, cryptoGlobal } from "../stdlib/web.js" import { ToolReference } from "../tool-runtime.js" import { errorGlobal } from "./errors.js" +import { errorTypes } from "./intrinsics.js" import { HostFunction } from "./host.js" import { AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol } from "./model.js" import { promiseGlobal, type PromiseRuntime } from "./promises.js" @@ -35,7 +36,7 @@ const symbolGlobal = new HostFunction({ throw new InterpreterRuntimeError( "Symbol is not callable; only Symbol.asyncIterator and Symbol.iterator are available.", node, - ).as("TypeError") + ) }), callback: false, members: { asyncIterator: AsyncIteratorSymbol, iterator: IteratorSymbol }, @@ -75,5 +76,5 @@ export const globals = (host: Host): ReadonlyArray [name, errorGlobal(name, host.runner)] as const), + ...errorTypes.map((type) => [type, errorGlobal(type, host.runner)] as const), ] diff --git a/packages/codemode/src/interpreter/host.ts b/packages/codemode/src/interpreter/host.ts index 4ca1e5be8fca..dd8e28980c6d 100644 --- a/packages/codemode/src/interpreter/host.ts +++ b/packages/codemode/src/interpreter/host.ts @@ -75,5 +75,5 @@ export const requiresNew = (name: string): HostCall => (_, node) => Effect.sync(() => { - throw new InterpreterRuntimeError(`Constructor ${name} requires 'new'.`, node).as("TypeError") + throw new InterpreterRuntimeError(`Constructor ${name} requires 'new'.`, node) }) diff --git a/packages/codemode/src/interpreter/intrinsics.ts b/packages/codemode/src/interpreter/intrinsics.ts new file mode 100644 index 000000000000..703589989f0d --- /dev/null +++ b/packages/codemode/src/interpreter/intrinsics.ts @@ -0,0 +1,51 @@ +import { ProgramError, ProgramObject, set } from "./objects.js" + +export const errorTypes = [ + "Error", + "TypeError", + "RangeError", + "SyntaxError", + "ReferenceError", + "EvalError", + "URIError", + "AggregateError", +] as const + +export type ErrorType = (typeof errorTypes)[number] + +export const isErrorType = (name: string): name is ErrorType => (errorTypes as ReadonlyArray).includes(name) + +/** The built-in prototype objects of one runtime. Constructors attach themselves as `constructor` when created. */ +export type Intrinsics = { + readonly errors: Readonly> +} + +export const createErrorValue = (prototype: ProgramObject, message: string | undefined): ProgramError => { + const value = new ProgramError(prototype) + if (message !== undefined) set(value, "message", message) + return value +} + +export const createIntrinsics = (): Intrinsics => { + const error = new ProgramObject() + set(error, "name", "Error") + set(error, "message", "") + const derived = (type: ErrorType) => { + const proto = new ProgramObject(error) + set(proto, "name", type) + set(proto, "message", "") + return proto + } + return { + errors: { + Error: error, + TypeError: derived("TypeError"), + RangeError: derived("RangeError"), + SyntaxError: derived("SyntaxError"), + ReferenceError: derived("ReferenceError"), + EvalError: derived("EvalError"), + URIError: derived("URIError"), + AggregateError: derived("AggregateError"), + }, + } +} diff --git a/packages/codemode/src/interpreter/methods.ts b/packages/codemode/src/interpreter/methods.ts index 40b9ea95272e..4d9476092d08 100644 --- a/packages/codemode/src/interpreter/methods.ts +++ b/packages/codemode/src/interpreter/methods.ts @@ -7,7 +7,7 @@ import { invokeURLMethod, uriArgument } from "../stdlib/url.js" import { coerceToNumber, coerceToString } from "../stdlib/value.js" import { compareText } from "../tool-runtime.js" import { Values } from "../values.js" -import { type AstNode, IntrinsicReference, InterpreterRuntimeError } from "./model.js" +import { type AstNode, InterpreterRuntimeError, IntrinsicReference, rangeError } from "./model.js" import { get, ProgramArray, ProgramObject, record } from "./objects.js" import { containsOpaqueReference, rejectCircularInsertion, typeofValue } from "./references.js" import { applyCollectionCallback, isSupportedCallback, type Runner, toPrimitive } from "./runner.js" @@ -111,7 +111,7 @@ const invokeStringMethod = (value: string, name: string, args: Array, n throw new InterpreterRuntimeError( `String.${name} cannot take a regular expression; use regex.test(string) or String.search instead.`, node, - ).as("TypeError") + ) } } @@ -143,10 +143,10 @@ const invokeStringMethod = (value: string, name: string, args: Array, n try { result = value.normalize(form) } catch { - throw new InterpreterRuntimeError( + throw rangeError( `String.normalize expects the form "NFC", "NFD", "NFKC", or "NFKD" (got ${JSON.stringify(form)}).`, node, - ).as("RangeError") + ) } break } @@ -233,7 +233,7 @@ const invokeStringMethod = (value: string, name: string, args: Array, n case "repeat": { const count = num(0) if (!Number.isFinite(count) || count < 0) - throw new InterpreterRuntimeError("String.repeat expects a finite non-negative count.", node).as("RangeError") + throw rangeError("String.repeat expects a finite non-negative count.", node) result = value.repeat(count) break } @@ -519,19 +519,17 @@ const loadSetRecord = (runner: Runner, source: unknown, name: string, node }) } if (!(source instanceof ProgramObject)) { - throw new InterpreterRuntimeError(`Set.${name} expects a Set-like object.`, node).as("TypeError") + throw new InterpreterRuntimeError(`Set.${name} expects a Set-like object.`, node) } return Effect.gen(function* () { const size = yield* coerceNumericArgument(runner, get(source, "size"), node) if (Number.isNaN(size)) { - throw new InterpreterRuntimeError(`Set.${name} received a Set-like object with an invalid size.`, node).as( - "TypeError", - ) + throw new InterpreterRuntimeError(`Set.${name} received a Set-like object with an invalid size.`, node) } const has = get(source, "has") const keys = get(source, "keys") if (!isSupportedCallback(has) || !isSupportedCallback(keys)) { - throw new InterpreterRuntimeError(`Set.${name} expects callable 'has' and 'keys' methods.`, node).as("TypeError") + throw new InterpreterRuntimeError(`Set.${name} expects callable 'has' and 'keys' methods.`, node) } return { size: Math.max(Math.trunc(size), 0), @@ -539,7 +537,7 @@ const loadSetRecord = (runner: Runner, source: unknown, name: string, node keys: () => Effect.flatMap(runner.invokeCallable(keys, [], node), (result) => { if (result instanceof ProgramArray) return Effect.succeed(result.items) - throw new InterpreterRuntimeError(`Set.${name} expected 'keys' to return an iterator.`, node).as("TypeError") + throw new InterpreterRuntimeError(`Set.${name} expected 'keys' to return an iterator.`, node) }), } }) @@ -558,7 +556,7 @@ const invokeURLSearchParamsMethod = ( throw new InterpreterRuntimeError( `URLSearchParams.${name} requires ${count} argument${count === 1 ? "" : "s"}.`, node, - ).as("TypeError") + ) } } switch (name) { @@ -690,7 +688,7 @@ const invokeArrayMethod = ( const index = optNumber(args[0], "index") ?? 0 const resolved = index < 0 ? target.length + index : index if (resolved < 0 || resolved >= target.length) { - throw new InterpreterRuntimeError("Array.with index is out of range.", node) + throw rangeError("Array.with index is out of range.", node) } const copied = [...target] copied[resolved] = args[1] @@ -820,9 +818,7 @@ const invokeArrayMethod = ( if (args.length < 2) { while (start < length && !(start in target)) start += 1 if (start === length) - throw new InterpreterRuntimeError("Array.reduce of an empty array with no initial value.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("Array.reduce of an empty array with no initial value.", node) accumulator = target[start] start += 1 } @@ -838,9 +834,7 @@ const invokeArrayMethod = ( if (args.length < 2) { while (start >= 0 && !(start in target)) start -= 1 if (start < 0) - throw new InterpreterRuntimeError("Array.reduceRight of an empty array with no initial value.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("Array.reduceRight of an empty array with no initial value.", node) accumulator = target[start] start -= 1 } diff --git a/packages/codemode/src/interpreter/model.ts b/packages/codemode/src/interpreter/model.ts index 66cf894bb2fd..650433646664 100644 --- a/packages/codemode/src/interpreter/model.ts +++ b/packages/codemode/src/interpreter/model.ts @@ -1,4 +1,5 @@ import type { Node } from "acorn" +import type { ErrorType } from "./intrinsics.js" import type { Effect } from "effect" import type { DiagnosticKind } from "../codemode.js" import type { ProgramObject } from "./objects.js" @@ -80,25 +81,29 @@ export const OptionalShortCircuit: unique symbol = Symbol("codemode.optional-sho export class InterpreterRuntimeError extends Error { readonly node?: AstNode - errorName = "Error" constructor( message: string, node?: AstNode, readonly kind: DiagnosticKind = "ExecutionFailure", readonly suggestions?: ReadonlyArray, + /** The JS error class a program sees when it catches this failure. */ + readonly type: ErrorType = "TypeError", ) { super(message) this.name = "InterpreterRuntimeError" if (node) this.node = node } - - as(errorName: string): this { - this.errorName = errorName - return this - } } +const failure = (type: ErrorType) => (message: string, node?: AstNode) => + new InterpreterRuntimeError(message, node, "ExecutionFailure", undefined, type) + +export const rangeError = failure("RangeError") +export const referenceError = failure("ReferenceError") +export const syntaxError = failure("SyntaxError") +export const uriError = failure("URIError") + // Orient the agent rather than enumerate JavaScript; interpreter-support.md is the full matrix. export const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes, this, getters/setters, tagged templates, BigInt, and custom Symbols. Use plain functions and data objects instead." @@ -109,6 +114,7 @@ export const unsupportedSyntax = (kind: string, node: AstNode): InterpreterRunti node, "UnsupportedSyntax", [supportedSyntaxMessage], + "SyntaxError", ) export const isRecord = (value: unknown): value is Record => diff --git a/packages/codemode/src/interpreter/objects.ts b/packages/codemode/src/interpreter/objects.ts index 536e60ee2a6a..582ce5db185d 100644 --- a/packages/codemode/src/interpreter/objects.ts +++ b/packages/codemode/src/interpreter/objects.ts @@ -13,11 +13,8 @@ export class ProgramArray extends ProgramObject { } } -export class ProgramError extends ProgramObject { - constructor(readonly errorName: string) { - super() - } -} +/** An object with the [[ErrorData]] slot: what `Error.prototype.toString` and the host boundary recognize as an error. */ +export class ProgramError extends ProgramObject {} export class ProgramFunction extends ProgramObject { readonly length: number @@ -78,6 +75,13 @@ export const get = (target: ProgramObject, key: PropertyKey): unknown => { return undefined } +export const hasPrototype = (value: unknown, proto: ProgramObject): boolean => { + for (let current = value instanceof ProgramObject ? value.proto : null; current !== null; current = current.proto) { + if (current === proto) return true + } + return false +} + export const has = (target: ProgramObject, key: PropertyKey): boolean => { for (let current: ProgramObject | null = target; current !== null; current = current.proto) { if (hasOwn(current, key)) return true diff --git a/packages/codemode/src/interpreter/promises.ts b/packages/codemode/src/interpreter/promises.ts index 78f36893ccb2..43e987699ac6 100644 --- a/packages/codemode/src/interpreter/promises.ts +++ b/packages/codemode/src/interpreter/promises.ts @@ -3,9 +3,8 @@ import type { Diagnostic } from "../codemode.js" import { type AstNode, InterpreterRuntimeError, ProgramThrow, PromiseInstanceMethodReference } from "./model.js" import { get, ProgramArray, ProgramFunction, ProgramObject, record } from "./objects.js" import { HostFunction, requiresNew, sync } from "./host.js" -import { caughtErrorValue, normalizeError } from "./errors.js" +import { caughtErrorValue, createAggregateErrorValue, normalizeError } from "./errors.js" import { typeofValue } from "./references.js" -import { createAggregateErrorValue } from "../stdlib/value.js" import { Values } from "../values.js" import { applyCollectionCallback, isSupportedCallback, type Runner, type SupportedCallback } from "./runner.js" @@ -95,7 +94,7 @@ export class PromiseRuntime { } export const selfResolutionError = (node?: AstNode): InterpreterRuntimeError => - new InterpreterRuntimeError("Chaining cycle detected: a promise cannot resolve with itself.", node).as("TypeError") + new InterpreterRuntimeError("Chaining cycle detected: a promise cannot resolve with itself.", node) export const resolvePromiseValue = ( runner: Runner, @@ -154,9 +153,7 @@ const invokePromiseMethod = ( Effect.gen(function* () { const cursor = yield* runner.syncIterator(args[0], node) if (cursor === undefined) { - throw new InterpreterRuntimeError(`Promise.${name} expects an array or other synchronous iterable.`, node).as( - "TypeError", - ) + throw new InterpreterRuntimeError(`Promise.${name} expects an array or other synchronous iterable.`, node) } const items: Array = [] while (true) { @@ -186,7 +183,7 @@ const invokePromiseMethod = ( continue } if (Cause.hasInterruptsOnly(exit.cause)) return yield* Effect.failCause(exit.cause) - outcomes.push(record({ status: "rejected", reason: caughtErrorValue(Cause.squash(exit.cause)) })) + outcomes.push(record({ status: "rejected", reason: caughtErrorValue(runner, Cause.squash(exit.cause)) })) } yield* Effect.yieldNow return new ProgramArray(outcomes) @@ -204,13 +201,13 @@ const invokePromiseMethod = ( Effect.flatMap(promises.await(item), (exit) => { if (Exit.isSuccess(exit)) return Effect.fail(new PromiseAnyFulfilled(exit.value)) if (Cause.hasInterruptsOnly(exit.cause)) return Effect.failCause(exit.cause) - return Effect.succeed(caughtErrorValue(Cause.squash(exit.cause))) + return Effect.succeed(caughtErrorValue(runner, Cause.squash(exit.cause))) }), ) return yield* settleAfterTurn( Effect.all(flipped, { concurrency: "unbounded" }).pipe( Effect.flatMap((reasons) => - Effect.fail(new ProgramThrow(createAggregateErrorValue(reasons, "All promises were rejected"))), + Effect.fail(new ProgramThrow(createAggregateErrorValue(runner, reasons, "All promises were rejected"))), ), Effect.catch((error) => error instanceof PromiseAnyFulfilled ? Effect.succeed(error.value) : Effect.fail(error), @@ -248,7 +245,7 @@ const constructPromise = ( throw new InterpreterRuntimeError( "new Promise(...) expects an executor function (e.g. new Promise((resolve, reject) => { ... })).", node, - ).as("TypeError") + ) } return Effect.gen(function* () { const deferred = Deferred.makeUnsafe() @@ -311,7 +308,7 @@ const chainReaction = ( const exit = yield* reactionExit(promises, source) const handler = Exit.isSuccess(exit) ? onFulfilled : onRejected if (handler === undefined) return yield* exit - const input = Exit.isSuccess(exit) ? exit.value : caughtErrorValue(Cause.squash(exit.cause)) + const input = Exit.isSuccess(exit) ? exit.value : caughtErrorValue(runner, Cause.squash(exit.cause)) const result = yield* applyCollectionCallback(runner, handler, method, node)([input]) return yield* resolvePromiseValue(runner, result, node, self) }), diff --git a/packages/codemode/src/interpreter/runner.ts b/packages/codemode/src/interpreter/runner.ts index 6145b46f39b0..53aff3fd2e5a 100644 --- a/packages/codemode/src/interpreter/runner.ts +++ b/packages/codemode/src/interpreter/runner.ts @@ -3,6 +3,7 @@ import { Values } from "../values.js" import { coerceToString } from "../stdlib/value.js" import { HostFunction } from "./host.js" import { type AstNode, InterpreterRuntimeError, IntrinsicReference } from "./model.js" +import type { Intrinsics } from "./intrinsics.js" import { get, has, ProgramFunction, ProgramObject } from "./objects.js" import { typeofValue } from "./references.js" @@ -11,7 +12,7 @@ export type IteratorCursor = { readonly close: Effect.Effect } -/** Everything a host function needs to call back into the program. */ +/** Everything a host function needs from the realm: calling back into the program and its intrinsic objects. */ export type Runner = { readonly invokeFunction: (fn: ProgramFunction, args: Array) => Effect.Effect readonly invokeCallable: ( @@ -21,6 +22,7 @@ export type Runner = { ) => Effect.Effect readonly settlePromise: (promise: Values.Promise) => Effect.Effect readonly syncIterator: (value: unknown, node: AstNode) => Effect.Effect | undefined, unknown, R> + readonly intrinsics: Intrinsics } export const preserveConsumerError = ( @@ -53,7 +55,7 @@ export const toPrimitive = ( const result = yield* runner.invokeCallable(callable, [], node) if (result === null || (typeof result !== "object" && typeof result !== "function")) return result } - throw new InterpreterRuntimeError("Cannot convert object to primitive value.", node).as("TypeError") + throw new InterpreterRuntimeError("Cannot convert object to primitive value.", node) }) } @@ -81,7 +83,7 @@ export const applyCollectionCallback = ( node, ) } - throw new InterpreterRuntimeError(`${name} expects a function callback.`, node).as("TypeError") + throw new InterpreterRuntimeError(`${name} expects a function callback.`, node) } return (callbackArgs) => runner.invokeCallable(callback, callbackArgs, node) } diff --git a/packages/codemode/src/interpreter/runtime.ts b/packages/codemode/src/interpreter/runtime.ts index 720d6d79b6e6..d5cff8295d35 100644 --- a/packages/codemode/src/interpreter/runtime.ts +++ b/packages/codemode/src/interpreter/runtime.ts @@ -51,19 +51,21 @@ import { CodeModeGenerator, ComputedValue, GeneratorMethodReference, - GeneratorReturn, type GeneratorRequestKind, - IntrinsicReference, + GeneratorReturn, InterpreterRuntimeError, + IntrinsicReference, IteratorSymbol, type MemberReference, OptionalShortCircuit, - PromiseInstanceMethodReference, ProgramThrow, + PromiseInstanceMethodReference, + rangeError, type StatementResult, unsupportedSyntax, } from "./model.js" import { caughtErrorValue } from "./errors.js" +import { createIntrinsics } from "./intrinsics.js" import { globals, type Host } from "./globals.js" import { HostFunction, HostNamespace } from "./host.js" import { invokeIntrinsic } from "./methods.js" @@ -97,7 +99,7 @@ import { constructRegExp, regexpMethods, regexpProperties } from "../stdlib/rege import { stringMethods } from "../stdlib/string.js" import { uriArgument, urlMethods, urlProperties, urlSearchParamsMethods, urlWritableProperties } from "../stdlib/url.js" import { enumerableSource } from "../stdlib/object.js" -import { coerceToNumber, coerceToString, compoundOperators, errorBrandName } from "../stdlib/value.js" +import { coerceToNumber, coerceToString, compoundOperators } from "../stdlib/value.js" import { Values } from "../values.js" // What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating. @@ -141,7 +143,7 @@ const constructorName = (value: unknown): string | undefined => { if (value instanceof Values.URLSearchParams) return "URLSearchParams" if (value instanceof Values.Promise) return "Promise" if (!(value instanceof ProgramObject) || value instanceof ProgramFunction) return undefined - return errorBrandName(value) ?? "Object" + return "Object" } const instanceofValue = (lhs: unknown, rhs: unknown, node: AstNode): boolean => { @@ -295,6 +297,7 @@ export class Runtime { invokeCallable: (callable, args, node) => this.root.invokeCallable(callable, args, node), settlePromise: (promise) => this.root.settlePromise(promise), syncIterator: (value, node) => this.root.syncIterator(value, node), + intrinsics: createIntrinsics(), } this.builtins = new Map([...globals(this), ...extraGlobals(this)]) for (const [name, value] of this.builtins) globalScope.set(name, { mutable: false, value }) @@ -647,7 +650,7 @@ class Frame { throw new InterpreterRuntimeError( `${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams, or custom iterator value.`, node, - ).as("TypeError") + ) } const close = () => iterator @@ -871,7 +874,7 @@ class Frame { private requireIteratorObject(value: unknown, context: string, node: AstNode): ProgramObject { if (value instanceof ProgramObject) return value - throw new InterpreterRuntimeError(`${context} must be an object.`, node).as("TypeError") + throw new InterpreterRuntimeError(`${context} must be an object.`, node) } private requireIterator(value: unknown, node: AstNode): ProgramObject | CodeModeGenerator { @@ -882,7 +885,7 @@ class Frame { private requireIteratorMethod(value: unknown, context: string, node: AstNode): unknown { if (typeofValue(value) === "function") return value - throw new InterpreterRuntimeError(`${context} must be a function.`, node).as("TypeError") + throw new InterpreterRuntimeError(`${context} must be a function.`, node) } // for...in over null/undefined iterates nothing, like JS. @@ -994,7 +997,7 @@ class Frame { return Effect.failCause(cause) } - const caught = caughtErrorValue(Cause.squash(cause)) + const caught = caughtErrorValue(self.runtime.runner, Cause.squash(cause)) const parameter = handler.param self.scopes.push() return Effect.gen(function* () { @@ -1179,9 +1182,7 @@ class Frame { return Effect.gen(function* () { const cursor = yield* self.syncIterator(value, pattern) if (cursor === undefined) { - throw new InterpreterRuntimeError("Array destructuring requires a supported iterable value.", pattern).as( - "TypeError", - ) + throw new InterpreterRuntimeError("Array destructuring requires a supported iterable value.", pattern) } let done = false for (const element of pattern.elements) { @@ -1314,7 +1315,7 @@ class Frame { : callee instanceof HostFunction ? `new ${name}(...) is not supported; call ${name}(...) without new instead.` : `${name} is not a constructor.` - throw new InterpreterRuntimeError(message, node).as("TypeError") + throw new InterpreterRuntimeError(message, node) } const args = yield* self.evaluateCallArguments(node.arguments) return yield* construct(args, node) @@ -1609,9 +1610,7 @@ class Frame { } if (callable instanceof HostFunction) return yield* (callable as HostFunction).call(args, node) if (callable === undefined || callable === null) { - throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee ?? node).as( - "TypeError", - ) + throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee ?? node) } throw new InterpreterRuntimeError("Only tools are callable here.", callee ?? node) }) @@ -1628,9 +1627,7 @@ class Frame { const spread = yield* self.evaluateExpression(argNode.argument) const cursor = yield* self.syncIterator(spread, argNode) if (cursor === undefined) - throw new InterpreterRuntimeError("Spread arguments require a synchronous iterable.", argNode).as( - "TypeError", - ) + throw new InterpreterRuntimeError("Spread arguments require a synchronous iterable.", argNode) while (true) { const step = yield* cursor.next if (step.done) break @@ -1695,7 +1692,7 @@ class Frame { const generator = new CodeModeGenerator(asynchronous, (kind, value, node) => { const request = { kind, value, response: Deferred.makeUnsafe() } if (!asynchronous && state.active) { - return Effect.fail(new InterpreterRuntimeError("Generator is already running.", node).as("TypeError")) + return Effect.fail(new InterpreterRuntimeError("Generator is already running.", node)) } if (asynchronous && (state.completed || (!state.started && kind !== "next"))) { state.started = true @@ -1865,17 +1862,14 @@ class Frame { } if (error instanceof ProgramThrow) { yield* cursor.close - throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node) } return yield* Effect.failCause(resumed.cause) } } const iterator = yield* self.customIterator(value, node, self.generatorAsync) - if (!iterator) - throw new InterpreterRuntimeError("yield* requires a compatible iterable value.", node).as("TypeError") + if (!iterator) throw new InterpreterRuntimeError("yield* requires a compatible iterable value.", node) let kind: GeneratorRequestKind = "next" let input: unknown = undefined while (true) { @@ -1888,9 +1882,7 @@ class Frame { if (method === undefined || method === null) { if (kind === "return") return yield* Effect.fail(new GeneratorReturn(input)) yield* self.closeIterator(iterator, node, self.generatorAsync) - throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node) } const called = yield* self.invokeCallable( self.requireIteratorMethod(method, `Iterator ${kind}`, node), @@ -1986,7 +1978,7 @@ class Frame { const spread = yield* self.evaluateExpression(element.argument) const cursor = yield* self.syncIterator(spread, element) if (cursor === undefined) - throw new InterpreterRuntimeError("Array spread requires a synchronous iterable.", element).as("TypeError") + throw new InterpreterRuntimeError("Array spread requires a synchronous iterable.", element) while (true) { const step = yield* cursor.next if (step.done) break @@ -2262,7 +2254,7 @@ class Frame { if (reference.target instanceof Values.URL) { const property = key as string if (!urlWritableProperties.has(property)) { - throw new InterpreterRuntimeError(`URL.${property} is read-only.`, node).as("TypeError") + throw new InterpreterRuntimeError(`URL.${property} is read-only.`, node) } try { const url = reference.target.url as unknown as Record @@ -2270,7 +2262,7 @@ class Frame { return } catch (error) { if (error instanceof InterpreterRuntimeError || error instanceof ToolRuntimeError) throw error - throw new InterpreterRuntimeError(`URL.${property} received an invalid value.`, node).as("TypeError") + throw new InterpreterRuntimeError(`URL.${property} received an invalid value.`, node) } } if (reference.target instanceof Values.RegExp) { @@ -2285,8 +2277,8 @@ class Frame { node, ) if (set(target, key, next)) return - if (target instanceof ProgramArray) throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError") - throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node).as("TypeError") + if (target instanceof ProgramArray) throw rangeError("Invalid array length", node) + throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node) } private toPropertyKey(value: unknown, node: AstNode): PropertyKey { diff --git a/packages/codemode/src/interpreter/scope.ts b/packages/codemode/src/interpreter/scope.ts index ae3257de3fd2..ccf4c6813d2c 100644 --- a/packages/codemode/src/interpreter/scope.ts +++ b/packages/codemode/src/interpreter/scope.ts @@ -1,4 +1,4 @@ -import { type AstNode, type Binding, InterpreterRuntimeError } from "./model.js" +import { type AstNode, type Binding, InterpreterRuntimeError, referenceError } from "./model.js" export class ScopeStack { private readonly scopes: Array> @@ -36,11 +36,11 @@ export class ScopeStack { const binding = this.resolve(name) if (!binding) { - throw new InterpreterRuntimeError(`Unknown identifier '${name}'.`, node).as("ReferenceError") + throw referenceError(`Unknown identifier '${name}'.`, node) } if (binding.initialized === false) { - throw new InterpreterRuntimeError(`Cannot access '${name}' before initialization.`, node).as("ReferenceError") + throw referenceError(`Cannot access '${name}' before initialization.`, node) } return binding.value @@ -50,15 +50,15 @@ export class ScopeStack { const binding = this.resolve(name) if (!binding) { - throw new InterpreterRuntimeError(`Unknown identifier '${name}'.`, node).as("ReferenceError") + throw referenceError(`Unknown identifier '${name}'.`, node) } if (binding.initialized === false) { - throw new InterpreterRuntimeError(`Cannot access '${name}' before initialization.`, node).as("ReferenceError") + throw referenceError(`Cannot access '${name}' before initialization.`, node) } if (!binding.mutable) { - throw new InterpreterRuntimeError(`Cannot assign to constant '${name}'.`, node).as("TypeError") + throw new InterpreterRuntimeError(`Cannot assign to constant '${name}'.`, node) } binding.value = value diff --git a/packages/codemode/src/stdlib/array.ts b/packages/codemode/src/stdlib/array.ts index a97fd630a2c7..a731d2eb4a7c 100644 --- a/packages/codemode/src/stdlib/array.ts +++ b/packages/codemode/src/stdlib/array.ts @@ -1,6 +1,6 @@ import { Effect } from "effect" import { HostFunction, sync, syncCall } from "../interpreter/host.js" -import { type AstNode, CodeModeGenerator, InterpreterRuntimeError } from "../interpreter/model.js" +import { type AstNode, CodeModeGenerator, InterpreterRuntimeError, rangeError } from "../interpreter/model.js" import { get, ProgramArray, ProgramObject } from "../interpreter/objects.js" import { describeValue } from "../interpreter/references.js" import { applyCollectionCallback, preserveConsumerError, type Runner } from "../interpreter/runner.js" @@ -10,7 +10,7 @@ const constructArray = (args: Array, node: AstNode): ProgramArray => { const first = args[0] if (typeof first !== "number") return new ProgramArray([first]) if (!Number.isInteger(first) || first < 0 || first > 4294967295) { - throw new InterpreterRuntimeError("Invalid array length.", node).as("RangeError") + throw rangeError("Invalid array length.", node) } // Sparse like JS: Array(3) has holes, and combinator loops already skip them. return new ProgramArray(new Array(first)) @@ -41,9 +41,7 @@ const arrayFrom = (runner: Runner, args: Array, node: AstNode): E const cursor = yield* runner.syncIterator(source, node) if (cursor === undefined) { if (source instanceof CodeModeGenerator) { - throw new InterpreterRuntimeError("Array.from expects a synchronous iterable or array-like value.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("Array.from expects a synchronous iterable or array-like value.", node) } const arrayLike = arrayLikeSource(source, node) const values: Array = [] diff --git a/packages/codemode/src/stdlib/collections.ts b/packages/codemode/src/stdlib/collections.ts index ef3f182c4497..7742a040e01a 100644 --- a/packages/codemode/src/stdlib/collections.ts +++ b/packages/codemode/src/stdlib/collections.ts @@ -89,15 +89,13 @@ export const groupBy = (runner: Runner, namespace: "Map" | "Object") => call: (args, node) => { const source = args[0] if (source === null || source === undefined) { - throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node).as("TypeError") + throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node) } const apply = applyCollectionCallback(runner, args[1], `${namespace}.groupBy`, node) return Effect.gen(function* () { const cursor = yield* runner.syncIterator(source, node) if (cursor === undefined) { - throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node).as( - "TypeError", - ) + throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node) } if (namespace === "Map") { const result = new Values.Map() @@ -139,10 +137,7 @@ const constructMap = (runner: Runner, init: unknown, node: AstNode) => { return Effect.gen(function* () { const cursor = yield* runner.syncIterator(init, node) if (cursor === undefined) { - throw new InterpreterRuntimeError( - "new Map(...) expects an iterable of [key, value] pairs or no argument.", - node, - ).as("TypeError") + throw new InterpreterRuntimeError("new Map(...) expects an iterable of [key, value] pairs or no argument.", node) } while (true) { const step = yield* cursor.next @@ -151,9 +146,7 @@ const constructMap = (runner: Runner, init: unknown, node: AstNode) => { cursor, Effect.sync(() => { if (!(step.value instanceof ProgramObject)) { - throw new InterpreterRuntimeError("new Map(...) expects [key, value] pairs as entry objects.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("new Map(...) expects [key, value] pairs as entry objects.", node) } target.map.set(getOwn(step.value, 0), getOwn(step.value, 1)) }), @@ -168,9 +161,7 @@ const constructSet = (runner: Runner, init: unknown, node: AstNode) => { return Effect.gen(function* () { const cursor = yield* runner.syncIterator(init, node) if (cursor === undefined) { - throw new InterpreterRuntimeError("new Set(...) expects a synchronous iterable or no argument.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("new Set(...) expects a synchronous iterable or no argument.", node) } while (true) { const step = yield* cursor.next diff --git a/packages/codemode/src/stdlib/date.ts b/packages/codemode/src/stdlib/date.ts index 307f0d2da956..f09ac58c37aa 100644 --- a/packages/codemode/src/stdlib/date.ts +++ b/packages/codemode/src/stdlib/date.ts @@ -1,6 +1,6 @@ import { Effect } from "effect" import { HostFunction, sync } from "../interpreter/host.js" -import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js" +import { type AstNode, InterpreterRuntimeError, rangeError } from "../interpreter/model.js" import { type Runner, toPrimitive } from "../interpreter/runner.js" import { Values } from "../values.js" import { coerceToNumber, coerceToString } from "./value.js" @@ -99,7 +99,7 @@ export const invokeDateMethod = ( case "valueOf": return value.time case "toISOString": - if (!Number.isFinite(value.time)) throw new InterpreterRuntimeError("Invalid time value.", node).as("RangeError") + if (!Number.isFinite(value.time)) throw rangeError("Invalid time value.", node) return hosted.toISOString() case "toJSON": return Number.isFinite(value.time) ? hosted.toISOString() : null diff --git a/packages/codemode/src/stdlib/json.ts b/packages/codemode/src/stdlib/json.ts index a3cb1744b32b..75438324eb10 100644 --- a/packages/codemode/src/stdlib/json.ts +++ b/packages/codemode/src/stdlib/json.ts @@ -1,7 +1,7 @@ import { Effect } from "effect" import { HostFunction, HostNamespace } from "../interpreter/host.js" import { applyCollectionCallback, type Runner } from "../interpreter/runner.js" -import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js" +import { type AstNode, InterpreterRuntimeError, syntaxError } from "../interpreter/model.js" import { typeofValue } from "../interpreter/references.js" import { fromData, toData, toProgram } from "../data.js" import { get, ownKeys, ProgramArray, ProgramObject, record, remove, set } from "../interpreter/objects.js" @@ -21,10 +21,10 @@ const parse = (runner: Runner, args: Array, node: AstNode): Effec try { return fromData(JSON.parse(text), "JSON.parse result") } catch (error) { - throw new InterpreterRuntimeError( + throw syntaxError( `JSON.parse received invalid JSON: ${error instanceof Error ? error.message : String(error)}`, node, - ).as("SyntaxError") + ) } })() if (typeofValue(args[1]) !== "function") return Effect.succeed(parsed) @@ -72,8 +72,7 @@ const stringify = (runner: Runner, args: Array, node: AstNode): E if (typeof value === "number") return Number.isFinite(value) ? value : null if (value === null || typeof value === "string" || typeof value === "boolean") return value if (!(value instanceof ProgramObject)) return {} - if (stack.has(value)) - throw new InterpreterRuntimeError("Converting circular structure to JSON.", node).as("TypeError") + if (stack.has(value)) throw new InterpreterRuntimeError("Converting circular structure to JSON.", node) stack.add(value) if (value instanceof ProgramArray) { const result: Array = [] diff --git a/packages/codemode/src/stdlib/math.ts b/packages/codemode/src/stdlib/math.ts index 54f0b522824f..10a7378d843d 100644 --- a/packages/codemode/src/stdlib/math.ts +++ b/packages/codemode/src/stdlib/math.ts @@ -42,7 +42,7 @@ const sumPrecise = (runner: Runner) => Effect.gen(function* () { const cursor = yield* runner.syncIterator(args[0], node) if (cursor === undefined) { - throw new InterpreterRuntimeError("Math.sumPrecise expects a synchronous iterable.", node).as("TypeError") + throw new InterpreterRuntimeError("Math.sumPrecise expects a synchronous iterable.", node) } const numbers: Array = [] while (true) { @@ -52,9 +52,7 @@ const sumPrecise = (runner: Runner) => cursor, Effect.sync(() => { if (typeof step.value !== "number") { - throw new InterpreterRuntimeError("Math.sumPrecise expects an iterable of numbers.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("Math.sumPrecise expects an iterable of numbers.", node) } numbers.push(step.value) }), diff --git a/packages/codemode/src/stdlib/number.ts b/packages/codemode/src/stdlib/number.ts index 3d20fc3fcf92..bad750cab6af 100644 --- a/packages/codemode/src/stdlib/number.ts +++ b/packages/codemode/src/stdlib/number.ts @@ -1,6 +1,6 @@ import { toProgram } from "../data.js" import { sync } from "../interpreter/host.js" -import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js" +import { type AstNode, InterpreterRuntimeError, rangeError } from "../interpreter/model.js" import { coercion, coerceToString } from "./value.js" export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString", "valueOf"]) @@ -28,7 +28,7 @@ export const invokeNumberMethod = (value: number, name: string, args: Array 36)) { - throw new InterpreterRuntimeError("Number.toString radix must be between 2 and 36.", node) + throw rangeError("Number.toString radix must be between 2 and 36.", node) } result = value.toString(radix) break diff --git a/packages/codemode/src/stdlib/object.ts b/packages/codemode/src/stdlib/object.ts index 799b591bb980..442108d7698a 100644 --- a/packages/codemode/src/stdlib/object.ts +++ b/packages/codemode/src/stdlib/object.ts @@ -1,7 +1,13 @@ import { Effect } from "effect" import { toProgram } from "../data.js" import { HostFunction, sync, syncCall } from "../interpreter/host.js" -import { type AstNode, AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol } from "../interpreter/model.js" +import { + type AstNode, + AsyncIteratorSymbol, + InterpreterRuntimeError, + IteratorSymbol, + rangeError, +} from "../interpreter/model.js" import { getOwn, hasOwn, ownEntries, ownKeys, ProgramArray, ProgramObject, set } from "../interpreter/objects.js" import { containsOpaqueReference, @@ -18,9 +24,7 @@ import { coerceToString } from "./value.js" // ToObject for enumeration. export const enumerableSource = (label: string, value: unknown, node: AstNode): ProgramObject => { if (value === null || value === undefined) { - throw new InterpreterRuntimeError(`${label} cannot convert ${describeValue(value)} to an object.`, node).as( - "TypeError", - ) + throw new InterpreterRuntimeError(`${label} cannot convert ${describeValue(value)} to an object.`, node) } if (value instanceof Values.Promise) { throw new InterpreterRuntimeError( @@ -48,7 +52,7 @@ export const objectAssign = (args: Array, node: AstNode): unknown => { throw new InterpreterRuntimeError( `Object.assign expects a data object or array target, received ${describeValue(target)}.`, node, - ).as("TypeError") + ) } const seen = new Set() for (const source of args.slice(1)) { @@ -58,7 +62,7 @@ export const objectAssign = (args: Array, node: AstNode): unknown => { if (typeof key === "symbol" && key !== AsyncIteratorSymbol && key !== IteratorSymbol) continue rejectCircularInsertion(target, getOwn(from, key), "Object.assign result", node, seen) if (!set(target, key, getOwn(from, key))) { - throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError") + throw rangeError("Invalid array length", node) } } } @@ -74,9 +78,7 @@ const objectFromEntries = ( return Effect.gen(function* () { const cursor = yield* runner.syncIterator(source, node) if (cursor === undefined) { - throw new InterpreterRuntimeError("Object.fromEntries expects a synchronous iterable of entries.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("Object.fromEntries expects a synchronous iterable of entries.", node) } while (true) { const step = yield* cursor.next @@ -85,9 +87,7 @@ const objectFromEntries = ( cursor, Effect.sync(() => { if (!(step.value instanceof ProgramObject) || containsOpaqueReference(step.value)) { - throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] entry objects.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] entry objects.", node) } set(out, coerceToString(getOwn(step.value, 0)), getOwn(step.value, 1)) }), diff --git a/packages/codemode/src/stdlib/regexp.ts b/packages/codemode/src/stdlib/regexp.ts index 3d50625de6f5..c6e9c9e3b1a5 100644 --- a/packages/codemode/src/stdlib/regexp.ts +++ b/packages/codemode/src/stdlib/regexp.ts @@ -1,5 +1,5 @@ import { sync, syncCall } from "../interpreter/host.js" -import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js" +import { type AstNode, InterpreterRuntimeError, syntaxError } from "../interpreter/model.js" import { ProgramArray, record, set } from "../interpreter/objects.js" import { Values } from "../values.js" import { coerceToNumber, coerceToString } from "./value.js" @@ -34,10 +34,10 @@ export const toHostRegex = (arg: unknown, method: string, node: AstNode, extraFl try { return new RegExp(arg, extraFlags) } catch (error) { - throw new InterpreterRuntimeError( + throw syntaxError( `String.${method} received the string ${JSON.stringify(arg)}, which is not a valid regular expression pattern (${regexFailureReason(error)}). ${escapeRegexHint}`, node, - ).as("SyntaxError") + ) } } throw new InterpreterRuntimeError( @@ -60,22 +60,22 @@ export const constructRegExp = (args: Array, node: AstNode): Values.Reg const pattern = first instanceof Values.RegExp ? first.regex.source : first === undefined ? "" : coerceToString(first) const flagsArg = args[1] if (flagsArg !== undefined && typeof flagsArg !== "string") { - throw new InterpreterRuntimeError( + throw syntaxError( `RegExp flags must be a string of flag characters (e.g. "g", "gi"), not ${flagsArg === null ? "null" : typeof flagsArg}.`, node, - ).as("SyntaxError") + ) } const flags = flagsArg ?? (first instanceof Values.RegExp ? first.regex.flags : "") try { return new Values.RegExp(pattern, flags) } catch (error) { const reason = regexFailureReason(error) - throw new InterpreterRuntimeError( + throw syntaxError( /flag/i.test(reason) ? `new RegExp(...) received invalid flags ${JSON.stringify(flags)} (${reason}). Valid flags are d, g, i, m, s, u, v, and y.` : `new RegExp(...) received ${JSON.stringify(pattern)}, which is not a valid regular expression pattern (${reason}). ${escapeRegexHint}`, node, - ).as("SyntaxError") + ) } } @@ -86,7 +86,7 @@ export const regexpGlobal = sync("RegExp", constructRegExp, { members: { escape: sync("RegExp.escape", (args, node) => { if (typeof args[0] !== "string") { - throw new InterpreterRuntimeError("RegExp.escape expects a string.", node).as("TypeError") + throw new InterpreterRuntimeError("RegExp.escape expects a string.", node) } return RegExp.escape(args[0]) }), diff --git a/packages/codemode/src/stdlib/url.ts b/packages/codemode/src/stdlib/url.ts index 2e4cdff0f3b5..0b29f4f60ab0 100644 --- a/packages/codemode/src/stdlib/url.ts +++ b/packages/codemode/src/stdlib/url.ts @@ -1,7 +1,7 @@ import { Effect } from "effect" import { toProgram } from "../data.js" import { HostFunction, requiresNew, sync, syncCall } from "../interpreter/host.js" -import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js" +import { type AstNode, InterpreterRuntimeError, uriError } from "../interpreter/model.js" import { ownEntries, ProgramObject } from "../interpreter/objects.js" import { isRuntimeReference } from "../interpreter/references.js" import { preserveConsumerError, type Runner } from "../interpreter/runner.js" @@ -68,10 +68,10 @@ export const uriGlobal = (name: UriFunction) => try { return uriFunctions[name](value) } catch (error) { - throw new InterpreterRuntimeError( + throw uriError( `${name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`, node, - ).as("URIError") + ) } }) @@ -81,7 +81,7 @@ export const urlArgument = (value: unknown, label: string): string => const urlStatic = (name: "canParse" | "parse") => sync(`URL.${name}`, (args, node) => { if (args.length === 0) { - throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node).as("TypeError") + throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node) } const input = urlArgument(args[0], `URL.${name} input`) const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`) @@ -95,9 +95,7 @@ const urlStatic = (name: "canParse" | "parse") => const constructURL = (args: Array, node: AstNode): Values.URL => { if (args.length === 0) { - throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node) } const input = urlArgument(args[0], "new URL input") const base = args[1] === undefined ? undefined : urlArgument(args[1], "new URL base") @@ -107,7 +105,7 @@ const constructURL = (args: Array, node: AstNode): Values.URL => { throw new InterpreterRuntimeError( `new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`, node, - ).as("TypeError") + ) } } @@ -127,9 +125,7 @@ const readURLSearchParamsPair = ( Effect.gen(function* () { const cursor = yield* runner.syncIterator(value, node) if (cursor === undefined) { - throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as( - "TypeError", - ) + throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node) } const items: Array = [] while (true) { @@ -165,10 +161,7 @@ const constructURLSearchParams = ( const step = yield* cursor.next if (step.done) { if (entries.some((entry) => entry.length !== 2)) { - throw new InterpreterRuntimeError( - "new URLSearchParams(...) expects iterable [name, value] pairs.", - node, - ).as("TypeError") + throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node) } return new Values.URLSearchParams( new URLSearchParams(entries.map((entry): [string, string] => [entry[0] ?? "", entry[1] ?? ""])), @@ -181,14 +174,14 @@ const constructURLSearchParams = ( throw new InterpreterRuntimeError( "new URLSearchParams(...) expects a query string, data object, or synchronous iterable pairs.", node, - ).as("TypeError") + ) } if (Values.isValue(init)) return new Values.URLSearchParams(new URLSearchParams()) if (!(init instanceof ProgramObject)) { throw new InterpreterRuntimeError( "new URLSearchParams(...) expects a query string, data object, iterable pairs, or URLSearchParams.", node, - ).as("TypeError") + ) } return new Values.URLSearchParams( new URLSearchParams(Object.fromEntries(ownEntries(init).map(([key, value]) => [key, coerceToString(value)]))), diff --git a/packages/codemode/src/stdlib/value.ts b/packages/codemode/src/stdlib/value.ts index fbaf431dca52..57955edb44de 100644 --- a/packages/codemode/src/stdlib/value.ts +++ b/packages/codemode/src/stdlib/value.ts @@ -1,38 +1,11 @@ import { type HostFunction, sync, type SyncOptions } from "../interpreter/host.js" import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js" import { toProgram } from "../data.js" -import { get, ProgramArray, ProgramError, set } from "../interpreter/objects.js" +import { get, ProgramArray, ProgramError } from "../interpreter/objects.js" import { Values } from "../values.js" -export const errorConstructors = new Set([ - "Error", - "TypeError", - "RangeError", - "SyntaxError", - "ReferenceError", - "EvalError", - "URIError", - "AggregateError", -]) - export const compoundOperators = new Set(["+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", ">>>="]) -export const createErrorValue = (name: string, message: string): ProgramError => { - const value = new ProgramError(name) - set(value, "name", name) - set(value, "message", message) - return value -} - -export const createAggregateErrorValue = (errors: Array, message: string): ProgramError => { - const value = createErrorValue("AggregateError", message) - set(value, "errors", new ProgramArray(errors)) - return value -} - -export const errorBrandName = (value: unknown): string | undefined => - value instanceof ProgramError ? value.errorName : undefined - export const coerceToString = (value: unknown): string => { if (value === null) return "null" if (value === undefined) return "undefined" diff --git a/packages/codemode/src/stdlib/web.ts b/packages/codemode/src/stdlib/web.ts index c67db4a17bbe..0e2046182711 100644 --- a/packages/codemode/src/stdlib/web.ts +++ b/packages/codemode/src/stdlib/web.ts @@ -2,16 +2,16 @@ import { HostNamespace, sync } from "../interpreter/host.js" import { InterpreterRuntimeError } from "../interpreter/model.js" import { coerceToString } from "./value.js" -// WebIDL DOMString conversion: a missing argument is a TypeError, anything else stringifies. +// WebIDL DOMString conversion: a missing argument is a TypeError, anything else stringifies. Invalid input is a +// TypeError as well; browsers throw a DOMException named InvalidCharacterError, which CodeMode does not have. const base64 = (name: "atob" | "btoa") => sync(name, (args, node) => { - if (args.length === 0) - throw new InterpreterRuntimeError(`${name} requires 1 argument (a string)`, node).as("TypeError") + if (args.length === 0) throw new InterpreterRuntimeError(`${name} requires 1 argument (a string)`, node) const input = coerceToString(args[0]) try { return name === "atob" ? atob(input) : btoa(input) } catch { - throw new InterpreterRuntimeError("The string contains invalid characters.", node).as("InvalidCharacterError") + throw new InterpreterRuntimeError("The string contains invalid characters.", node) } }) diff --git a/packages/codemode/test/parity.test.ts b/packages/codemode/test/parity.test.ts index d9df1fdf9e6b..9a8431b551a3 100644 --- a/packages/codemode/test/parity.test.ts +++ b/packages/codemode/test/parity.test.ts @@ -392,11 +392,35 @@ describe("Error values and instanceof", () => { ]) }) - test("diagnostics without a specific real-JS analogue are named plain Error", async () => { - expect(await value(`try { JSON.parse(5) } catch (e) { return [e.name, e instanceof Error] }`)).toEqual([ - "Error", + test("interpreter failures are TypeErrors; unsupported syntax is a SyntaxError", async () => { + expect(await value(`try { null.x } catch (e) { return [e.name, e instanceof TypeError] }`)).toEqual([ + "TypeError", true, ]) + expect(await value(`try { tools + 1 } catch (e) { return e.name }`)).toBe("TypeError") + expect(await value(`try { class A {} } catch (e) { return [e.name, e instanceof SyntaxError] }`)).toEqual([ + "SyntaxError", + true, + ]) + }) + + test("errors inherit constructor and instanceof through a real prototype chain", async () => { + expect( + await value(` + const { constructor } = new RangeError("r") + let caught + try { const { a } = null } catch (e) { caught = e } + return [ + constructor === RangeError, + new RangeError("r") instanceof Error, + new RangeError("r") instanceof TypeError, + caught.constructor === TypeError, + ({ ...caught }).constructor === Object, + "constructor" in caught, + Object.keys(new RangeError("r")), + ] + `), + ).toEqual([true, true, false, true, true, true, ["message"]]) }) test("Promise.allSettled rejection reasons are Error values", async () => { @@ -421,15 +445,19 @@ describe("Error values and instanceof", () => { ]) }) - test("error values still serialize as plain { name, message } data", async () => { + test("errors serialize as { name, message } by brand; name is inherited and message is own", async () => { expect(await value(`return new Error("m")`)).toEqual({ name: "Error", message: "m" }) expect(await value(`return JSON.stringify(new Error("m"))`)).toBe('{"name":"Error","message":"m"}') - expect(await value(`try { throw new Error("m") } catch (e) { return Object.keys(e) }`)).toEqual(["name", "message"]) + expect(await value(`try { throw new Error("m") } catch (e) { return [Object.keys(e), e.name] }`)).toEqual([ + ["message"], + "Error", + ]) + expect(await value(`return Object.keys(new Error())`)).toEqual([]) }) test("spreading an error loses the brand, like losing the prototype in JS", async () => { expect(await value(`const e = new Error("m"); return ({ ...e }) instanceof Error`)).toBe(false) - expect(await value(`const e = new Error("m"); return { ...e }`)).toEqual({ name: "Error", message: "m" }) + expect(await value(`const e = new Error("m"); return { ...e }`)).toEqual({ message: "m" }) }) test("typeof Error is function; an unknown instanceof right-hand side is a catchable error", async () => { diff --git a/packages/codemode/test/test262/run.ts b/packages/codemode/test/test262/run.ts index 7d82de149e1d..04b35cbc428f 100644 --- a/packages/codemode/test/test262/run.ts +++ b/packages/codemode/test/test262/run.ts @@ -8,8 +8,8 @@ import { executeProgram } from "../../src/interpreter/execute.js" import type { Host } from "../../src/interpreter/globals.js" import { HostFunction } from "../../src/interpreter/host.js" import { ProgramThrow } from "../../src/interpreter/model.js" -import { get, ProgramArray, ProgramObject } from "../../src/interpreter/objects.js" -import { createErrorValue, errorBrandName } from "../../src/stdlib/value.js" +import { createErrorValue } from "../../src/interpreter/intrinsics.js" +import { get, hasPrototype, ProgramArray, ProgramObject, set } from "../../src/interpreter/objects.js" import { ToolRuntime } from "../../src/tool-runtime.js" export const root = import.meta.dir @@ -65,7 +65,11 @@ export const run = async (file: string): Promise => { } const harness = (host: Host, onDone: (error: unknown) => void): ReadonlyArray => { - const fail = (message: string) => Effect.fail(new ProgramThrow(createErrorValue("Test262Error", message))) + const test262Prototype = new ProgramObject() + set(test262Prototype, "name", "Test262Error") + const test262 = (args: Array) => + createErrorValue(test262Prototype, args[0] === undefined ? "" : String(args[0])) + const fail = (message: string) => Effect.fail(new ProgramThrow(test262([message]))) const prefix = (message: unknown) => (message === undefined ? "" : `${String(message)} `) const compare = (a: unknown, b: unknown) => a instanceof ProgramArray && @@ -74,11 +78,12 @@ const harness = (host: Host, onDone: (error: unknown) => void): ReadonlyAr a.items.every((value, i) => Object.is(value, b.items[i])) const test262Error = new HostFunction({ name: "Test262Error", - call: (args) => Effect.succeed(createErrorValue("Test262Error", args[0] === undefined ? "" : String(args[0]))), - construct: (args) => Effect.succeed(createErrorValue("Test262Error", args[0] === undefined ? "" : String(args[0]))), - instanceOf: (value) => errorBrandName(value) === "Test262Error", + call: (args) => Effect.succeed(test262(args)), + construct: (args) => Effect.succeed(test262(args)), + instanceOf: (value) => hasPrototype(value, test262Prototype), members: { thrower: new HostFunction({ name: "Test262Error.thrower", call: (args) => fail(String(args[0])) }) }, }) + set(test262Prototype, "constructor", test262Error) const compareArray = new HostFunction({ name: "compareArray", call: (args) => Effect.succeed(compare(args[0], args[1])), @@ -124,13 +129,11 @@ const harness = (host: Host, onDone: (error: unknown) => void): ReadonlyAr Effect.matchCauseEffect({ onFailure: (cause) => { if (cause.reasons.some(Cause.isInterruptReason)) return Effect.failCause(cause) - const thrown = caughtErrorValue(Cause.squash(cause)) - if (thrown === null || typeof thrown !== "object") { - return fail(`${prefix(args[2])}Thrown value was not an object!`) - } - const actual = errorBrandName(thrown) - if (actual === expected) return Effect.void - return fail(`${prefix(args[2])}Expected a ${expected} but got a ${actual ?? "non-error object"}`) + const thrown = caughtErrorValue(host.runner, Cause.squash(cause)) + if (!(thrown instanceof ProgramObject)) return fail(`${prefix(args[2])}Thrown value was not an object!`) + const actual = get(thrown, "constructor") + if (actual === args[0]) return Effect.void + return fail(`${prefix(args[2])}Expected a ${expected} but got a ${show(actual)}`) }, onSuccess: () => fail(`${prefix(args[2])}Expected a ${expected} to be thrown but no exception was thrown at all`), @@ -162,5 +165,5 @@ const show = (value: unknown): string => { if (value instanceof HostFunction) return value.name if (!(value instanceof ProgramObject)) return String(value) const message = get(value, "message") - return typeof message === "string" ? `${errorBrandName(value) ?? "object"}: ${message}` : "object" + return typeof message === "string" ? `${String(get(value, "name") ?? "object")}: ${message}` : "object" } diff --git a/packages/codemode/test/test262/skipped.txt b/packages/codemode/test/test262/skipped.txt index 6a6f50855b8e..26ef55b4f90c 100644 --- a/packages/codemode/test/test262/skipped.txt +++ b/packages/codemode/test/test262/skipped.txt @@ -4,23 +4,23 @@ built-ins/Array/prototype/copyWithin/coerced-values-start-change-target.js # Ar built-ins/Array/prototype/copyWithin/coerced-values-start.js # Array.copyWithin expects start to be a number. built-ins/Array/prototype/copyWithin/coerced-values-target.js # Array.copyWithin expects target index to be a number. built-ins/Array/prototype/copyWithin/fill-holes.js # .hasOwnProperty is not a function. -built-ins/Array/prototype/copyWithin/return-abrupt-from-end.js # Expected a Test262Error but got a Error -built-ins/Array/prototype/copyWithin/return-abrupt-from-start.js # Expected a Test262Error but got a Error -built-ins/Array/prototype/copyWithin/return-abrupt-from-target.js # Expected a Test262Error but got a Error +built-ins/Array/prototype/copyWithin/return-abrupt-from-end.js # Expected a Test262Error but got a TypeError +built-ins/Array/prototype/copyWithin/return-abrupt-from-start.js # Expected a Test262Error but got a TypeError +built-ins/Array/prototype/copyWithin/return-abrupt-from-target.js # Expected a Test262Error but got a TypeError built-ins/Array/prototype/entries/iteration-mutable.js # .next is not a function. built-ins/Array/prototype/entries/iteration.js # .next is not a function. built-ins/Array/prototype/every/15.4.4.16-7-7.js # .hasOwnProperty is not a function. built-ins/Array/prototype/every/15.4.4.16-8-13.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator. built-ins/Array/prototype/fill/coerced-indexes.js # Array.fill expects start to be a number. -built-ins/Array/prototype/fill/return-abrupt-from-end.js # Expected a Test262Error but got a Error -built-ins/Array/prototype/fill/return-abrupt-from-start.js # Expected a Test262Error but got a Error +built-ins/Array/prototype/fill/return-abrupt-from-end.js # Expected a Test262Error but got a TypeError +built-ins/Array/prototype/fill/return-abrupt-from-start.js # Expected a Test262Error but got a TypeError built-ins/Array/prototype/filter/15.4.4.20-10-4.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator. built-ins/Array/prototype/filter/create-ctor-non-object.js # null value Expected a TypeError to be thrown but no exception was thrown at all built-ins/Array/prototype/flat/non-numeric-depth-should-not-throw.js # Array.flat expects depth to be a number. built-ins/Array/prototype/forEach/15.4.4.18-8-12.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator. built-ins/Array/prototype/includes/length-zero-returns-false.js # Array.includes expects a value and optional start index. built-ins/Array/prototype/includes/no-arg.js # Array.includes expects a value and optional start index. -built-ins/Array/prototype/includes/return-abrupt-tointeger-fromindex.js # Expected a Test262Error but got a Error +built-ins/Array/prototype/includes/return-abrupt-tointeger-fromindex.js # Expected a Test262Error but got a TypeError built-ins/Array/prototype/includes/tointeger-fromindex.js # Array.includes expects start index to be a number. built-ins/Array/prototype/indexOf/15.4.4.14-5-1.js # Array.indexOf expects start index to be a number. built-ins/Array/prototype/indexOf/15.4.4.14-5-15.js # Array.indexOf expects start index to be a number. @@ -32,7 +32,7 @@ built-ins/Array/prototype/indexOf/15.4.4.14-5-20.js # Array.indexOf expects sta built-ins/Array/prototype/indexOf/15.4.4.14-5-21.js # Array.indexOf expects start index to be a number. built-ins/Array/prototype/indexOf/15.4.4.14-5-22.js # Array.indexOf expects start index to be a number. built-ins/Array/prototype/indexOf/15.4.4.14-5-23.js # Array.indexOf expects start index to be a number. -built-ins/Array/prototype/indexOf/15.4.4.14-5-24.js # Expected a TypeError but got a Error +built-ins/Array/prototype/indexOf/15.4.4.14-5-24.js # toStringAccessed built-ins/Array/prototype/indexOf/15.4.4.14-5-3.js # Array.indexOf expects start index to be a number. built-ins/Array/prototype/indexOf/15.4.4.14-5-5.js # Array.indexOf expects start index to be a number. built-ins/Array/prototype/indexOf/15.4.4.14-9-7.js # Array assignment result contains a circular value. @@ -56,7 +56,7 @@ built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-20.js # Array.lastIndexOf exp built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-21.js # Array.lastIndexOf expects start index to be a number. built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-22.js # Array.lastIndexOf expects start index to be a number. built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-23.js # Array.lastIndexOf expects start index to be a number. -built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-24.js # Expected a TypeError but got a Error +built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-24.js # toStringAccessed built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-3.js # Array.lastIndexOf expects start index to be a number. built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-4.js # a.lastIndexOf(2,undefined) Expected SameValue(«1», «-1») to be true built-ins/Array/prototype/lastIndexOf/15.4.4.15-5-5.js # Array.lastIndexOf expects start index to be a number. @@ -135,179 +135,19 @@ language/statements/async-generator/dstr/obj-ptrn-prop-obj-value-null.js # Expe language/statements/async-generator/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError to be thrown but no exception was thrown at all language/statements/async-generator/rest-params-trailing-comma-early-error.js # expected SyntaxError but the program ran language/statements/async-generator/return-undefined-implicit-and-explicit.js # Actual ["tick 1", "tick 2", "g1 ret", "g2 ret", "g3 ret", "g4 ret"] and expected ["tick 1", "g1 ret" -language/statements/const/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/const/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/const/dstr/obj-init-null.js # Expected a TypeError but got a Error -language/statements/const/dstr/obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/const/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/const/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error language/statements/for-await-of/async-func-decl-dstr-array-elem-init-in.js # Failed to parse TypeScript: '…' expected. -language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-array-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-array-undefined-hole.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-array-undefined-own.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-array-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-obj-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-obj-undefined-hole.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-obj-undefined-own.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-nested-obj-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-put-const.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-put-unresolvable-strict.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-decl-dstr-array-elem-trlg-iter-elision-iter-nrml-close-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-decl-dstr-obj-empty-bool.js # Object destructuring requires a data object or array value, received a boolean. -language/statements/for-await-of/async-func-decl-dstr-obj-empty-num.js # Object destructuring requires a data object or array value, received a number. -language/statements/for-await-of/async-func-decl-dstr-obj-empty-string.js # Object destructuring requires a data object or array value, received a string. -language/statements/for-await-of/async-func-decl-dstr-obj-rest-number.js # Object destructuring requires a data object or array value, received a number. -language/statements/for-await-of/async-func-decl-dstr-obj-rest-str-val.js # Object destructuring requires a data object or array value, received a string. -language/statements/for-await-of/async-func-dstr-const-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-const-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-const-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-let-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-let-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-let-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-var-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-var-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-func-dstr-var-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true +language/statements/for-await-of/async-func-decl-dstr-obj-empty-bool.js # TypeError: Object destructuring requires a data object or array value, received a boolean. +language/statements/for-await-of/async-func-decl-dstr-obj-empty-num.js # TypeError: Object destructuring requires a data object or array value, received a number. +language/statements/for-await-of/async-func-decl-dstr-obj-empty-string.js # TypeError: Object destructuring requires a data object or array value, received a string. +language/statements/for-await-of/async-func-decl-dstr-obj-rest-number.js # TypeError: Object destructuring requires a data object or array value, received a number. +language/statements/for-await-of/async-func-decl-dstr-obj-rest-str-val.js # TypeError: Object destructuring requires a data object or array value, received a string. language/statements/for-await-of/async-gen-decl-dstr-array-elem-init-in.js # Failed to parse TypeScript: '…' expected. -language/statements/for-await-of/async-gen-decl-dstr-array-elem-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-iter-nrml-close-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-iter-nrml-close-null.js # Expected SameValue(«undefined», «TypeError») to be true language/statements/for-await-of/async-gen-decl-dstr-array-elem-iter-rtrn-close-null.js # "Promise incorrectly fulfilled." -language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-array-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-array-undefined-hole.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-array-undefined-own.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-array-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-obj-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-obj-undefined-hole.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-obj-undefined-own.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-nested-obj-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-put-const.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-put-unresolvable-strict.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-elision-iter-nrml-close-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-elision-iter-nrml-close-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-nrml-close-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-thrw-close-skip.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-decl-dstr-obj-empty-bool.js # Object destructuring requires a data object or array value, received a boolean. -language/statements/for-await-of/async-gen-decl-dstr-obj-empty-num.js # Object destructuring requires a data object or array value, received a number. -language/statements/for-await-of/async-gen-decl-dstr-obj-empty-string.js # Object destructuring requires a data object or array value, received a string. -language/statements/for-await-of/async-gen-decl-dstr-obj-rest-number.js # Object destructuring requires a data object or array value, received a number. -language/statements/for-await-of/async-gen-decl-dstr-obj-rest-str-val.js # Object destructuring requires a data object or array value, received a string. -language/statements/for-await-of/async-gen-dstr-const-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-let-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-init-iter-get-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-ary-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-obj-val-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elem-obj-val-undef.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-elision-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-rest-id-elision-next-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-ary-ptrn-rest-id-iter-step-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-init-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-init-undefined.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-list-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-ary-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-eval-err.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-id-init-throws.js # Expected SameValue(«undefined», «Test262Error») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-id-init-unresolvable.js # Expected SameValue(«undefined», «ReferenceError») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-obj-value-null.js # Expected SameValue(«undefined», «TypeError») to be true -language/statements/for-await-of/async-gen-dstr-var-obj-ptrn-prop-obj-value-undef.js # Expected SameValue(«undefined», «TypeError») to be true +language/statements/for-await-of/async-gen-decl-dstr-obj-empty-bool.js # TypeError: Object destructuring requires a data object or array value, received a boolean. +language/statements/for-await-of/async-gen-decl-dstr-obj-empty-num.js # TypeError: Object destructuring requires a data object or array value, received a number. +language/statements/for-await-of/async-gen-decl-dstr-obj-empty-string.js # TypeError: Object destructuring requires a data object or array value, received a string. +language/statements/for-await-of/async-gen-decl-dstr-obj-rest-number.js # TypeError: Object destructuring requires a data object or array value, received a number. +language/statements/for-await-of/async-gen-decl-dstr-obj-rest-str-val.js # TypeError: Object destructuring requires a data object or array value, received a string. language/statements/for-in/12.6.4-1.js # .hasOwnProperty is not a function. language/statements/for-in/head-lhs-member.js # Unsupported for...in binding. language/statements/for-of/Array.prototype.Symbol.iterator.js # The called value is not a function. @@ -315,10 +155,6 @@ language/statements/for-of/dstr/array-elem-init-in.js # Failed to parse TypeScr language/statements/for-of/dstr/array-elem-iter-rtrn-close-err.js # Iterator next must be a function. language/statements/for-of/dstr/array-elem-iter-rtrn-close-null.js # Expected a TypeError to be thrown but no exception was thrown at all language/statements/for-of/dstr/array-elem-iter-thrw-close-err.js # Expected SameValue(«1», «0») to be true -language/statements/for-of/dstr/array-elem-nested-obj-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/array-elem-nested-obj-undefined-hole.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/array-elem-nested-obj-undefined-own.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/array-elem-nested-obj-undefined.js # Expected a TypeError but got a Error language/statements/for-of/dstr/array-elem-trlg-iter-list-rtrn-close-err.js # Expected a Test262Error to be thrown but no exception was thrown at all language/statements/for-of/dstr/array-elem-trlg-iter-list-rtrn-close-null.js # Expected a TypeError to be thrown but no exception was thrown at all language/statements/for-of/dstr/array-elem-trlg-iter-list-thrw-close-err.js # Expected SameValue(«1», «0») to be true @@ -329,58 +165,14 @@ language/statements/for-of/dstr/array-rest-iter-rtrn-close-err.js # Iterator ne language/statements/for-of/dstr/array-rest-iter-rtrn-close-null.js # Iterator next must be a function. language/statements/for-of/dstr/array-rest-iter-thrw-close-err.js # Expected SameValue(«11», «0») to be true language/statements/for-of/dstr/array-rest-lref-err.js # Expected SameValue(«1», «0») to be true -language/statements/for-of/dstr/const-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/const-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/const-obj-init-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/const-obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/const-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/const-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/let-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/let-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/let-obj-init-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/let-obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/let-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/let-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error language/statements/for-of/dstr/obj-empty-bool.js # Object destructuring requires a data object or array value, received a boolean. -language/statements/for-of/dstr/obj-empty-null.js # Expected a TypeError but got a Error language/statements/for-of/dstr/obj-empty-num.js # Object destructuring requires a data object or array value, received a number. language/statements/for-of/dstr/obj-empty-string.js # Object destructuring requires a data object or array value, received a string. -language/statements/for-of/dstr/obj-empty-undef.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/obj-prop-name-evaluation-error.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/obj-prop-nested-obj-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/obj-prop-nested-obj-undefined-own.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/obj-prop-nested-obj-undefined.js # Expected a TypeError but got a Error language/statements/for-of/dstr/obj-rest-number.js # Object destructuring requires a data object or array value, received a number. language/statements/for-of/dstr/obj-rest-str-val.js # Object destructuring requires a data object or array value, received a string. -language/statements/for-of/dstr/obj-rest-val-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/obj-rest-val-undefined.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/var-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/var-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/var-obj-init-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/var-obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/var-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/for-of/dstr/var-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error language/statements/for-of/throw-from-catch.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct language/statements/for-of/throw-from-finally.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct language/statements/for-of/throw.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct -language/statements/for/dstr/const-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/const-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/for/dstr/const-obj-init-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/const-obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/for/dstr/const-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/const-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error -language/statements/for/dstr/let-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/let-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/for/dstr/let-obj-init-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/let-obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/for/dstr/let-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/let-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error -language/statements/for/dstr/var-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/var-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/for/dstr/var-obj-init-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/var-obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/for/dstr/var-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/for/dstr/var-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error language/statements/function/13.2-19-b-3gs.js # Expected a TypeError to be thrown but no exception was thrown at all language/statements/function/13.2-2-s.js # Expected a TypeError to be thrown but no exception was thrown at all language/statements/function/S13.2.1_A5_T1.js # .toString is not a function. @@ -390,18 +182,6 @@ language/statements/function/S13_A6_T1.js # Identifier '…' has already been d language/statements/function/S14_A2.js # Unknown identifier '…'. language/statements/function/S14_A5_T1.js # Identifier '…' has already been declared. language/statements/function/S14_A5_T2.js # Identifier '…' has already been declared. -language/statements/function/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/function/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/function/dstr/dflt-ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/function/dstr/dflt-ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/function/dstr/dflt-obj-init-null.js # Expected a TypeError but got a Error -language/statements/function/dstr/dflt-obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/function/dstr/dflt-obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/function/dstr/dflt-obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error -language/statements/function/dstr/obj-init-null.js # Expected a TypeError but got a Error -language/statements/function/dstr/obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/function/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/function/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error language/statements/function/rest-params-trailing-comma-early-error.js # expected SyntaxError but the program ran language/statements/generators/dflt-params-abrupt.js # Expected a Test262Error to be thrown but no exception was thrown at all language/statements/generators/dflt-params-ref-later.js # Expected a ReferenceError to be thrown but no exception was thrown at all @@ -452,12 +232,6 @@ language/statements/generators/has-instance.js # The right-hand side of '…' m language/statements/generators/rest-params-trailing-comma-early-error.js # expected SyntaxError but the program ran language/statements/labeled/value-await-non-module-escaped.js # Failed to parse TypeScript: Keywords cannot contain escape characters. language/statements/labeled/value-await-non-module.js # Failed to parse TypeScript: Expression expected. -language/statements/let/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/let/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/let/dstr/obj-init-null.js # Expected a TypeError but got a Error -language/statements/let/dstr/obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/let/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/let/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error language/statements/return/S12.9_A1_T1.js # expected SyntaxError but the program ran language/statements/return/S12.9_A1_T10.js # expected SyntaxError but the program ran language/statements/return/S12.9_A1_T2.js # expected SyntaxError but the program ran @@ -472,15 +246,3 @@ language/statements/switch/S12.11_A1_T4.js # Switch discriminants must be data language/statements/switch/scope-lex-open-dflt.js # Switch discriminants must be data values. language/statements/try/S12.14_A19_T1.js # .toString is not a function. language/statements/try/S12.14_A19_T2.js # .toString is not a function. -language/statements/try/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/try/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/try/dstr/obj-init-null.js # Expected a TypeError but got a Error -language/statements/try/dstr/obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/try/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/try/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error -language/statements/variable/dstr/ary-ptrn-elem-obj-val-null.js # Expected a TypeError but got a Error -language/statements/variable/dstr/ary-ptrn-elem-obj-val-undef.js # Expected a TypeError but got a Error -language/statements/variable/dstr/obj-init-null.js # Expected a TypeError but got a Error -language/statements/variable/dstr/obj-init-undefined.js # Expected a TypeError but got a Error -language/statements/variable/dstr/obj-ptrn-prop-obj-value-null.js # Expected a TypeError but got a Error -language/statements/variable/dstr/obj-ptrn-prop-obj-value-undef.js # Expected a TypeError but got a Error diff --git a/packages/codemode/test/web-wpt.test.ts b/packages/codemode/test/web-wpt.test.ts index 2b0ca481ba8c..41f53ec5c542 100644 --- a/packages/codemode/test/web-wpt.test.ts +++ b/packages/codemode/test/web-wpt.test.ts @@ -6,8 +6,7 @@ * * Copyright © web-platform-tests contributors. Governed by the 3-Clause BSD license in LICENSE.wpt. * - * `assert_throws_dom("InvalidCharacterError", …)` becomes a check on `error.name`: CodeMode has no - * DOMException, so the name is carried on a plain Error. + * `assert_throws_dom("InvalidCharacterError", …)` becomes a check for a TypeError: CodeMode has no DOMException. */ import { describe, expect, test } from "bun:test" import { Effect } from "effect" @@ -58,7 +57,7 @@ const referenceEncoder = ` function testBtoa(input) { var expected = mybtoa(input) if (expected === "INVALID_CHARACTER_ERR") { - try { btoa(input) } catch (error) { return error.name === "InvalidCharacterError" ? "ok" : error.name } + try { btoa(input) } catch (error) { return error instanceof TypeError ? "ok" : error.name } return "did not throw" } if (btoa(input) !== expected) return "btoa mismatch" @@ -102,7 +101,7 @@ describe("atob WPT parity (fetch/data-urls/resources/base64.json)", () => { [-0, null], ] - test(`${base64Cases.length} forgiving-base64 inputs decode to the expected bytes or throw InvalidCharacterError`, async () => { + test(`${base64Cases.length} forgiving-base64 inputs decode to the expected bytes or throw a TypeError`, async () => { expect( await value(` const cases = ${JSON.stringify(base64Cases)} @@ -113,7 +112,7 @@ describe("atob WPT parity (fetch/data-urls/resources/base64.json)", () => { const bytes = Array.from({ length: result.length }, (_, i) => result.charCodeAt(i)) return JSON.stringify(bytes) === JSON.stringify(output) ? [] : [[input, bytes]] } catch (error) { - return output === null && error.name === "InvalidCharacterError" ? [] : [[input, error.name]] + return output === null && error instanceof TypeError ? [] : [[input, error.name]] } }) `), @@ -138,7 +137,7 @@ describe("atob WPT parity (fetch/data-urls/resources/base64.json)", () => { const bytes = output.map((_, i) => result.charCodeAt(i)) return JSON.stringify(bytes) === JSON.stringify(output) ? [] : [[String(input), bytes]] } catch (error) { - return output === null && error.name === "InvalidCharacterError" ? [] : [[String(input), error.name]] + return output === null && error instanceof TypeError ? [] : [[String(input), error.name]] } }) `),