chore: merge v2 into promise-combinators

This commit is contained in:
Aiden Cline 2026-07-09 11:49:12 -05:00
commit 02ce4a330f
794 changed files with 33863 additions and 20365 deletions

View file

@ -4,6 +4,7 @@
- Do not add a speculative generic permission or approval policy. A host omits tools it does not expose and enforces domain authorization inside each provided tool.
- Keep Code Mode unaware of host session, channel, and conversation models. The hosting application supplies trusted execution scope around it.
- Tool schemas are the model-facing Interface. Keep arguments minimal and natural to the operation; never add unrelated IDs as ambient capability tokens.
- When interpreter behavior or support changes, update `interpreter-support.md` and direct tests in the same PR. Update `codemode.md` when the package design, integration status, or rationale changes.
## OpenAPI

View file

@ -237,23 +237,21 @@ A host cannot define its own `$codemode` top-level namespace.
## Supported Programs
CodeMode executes a deliberately bounded JavaScript subset. It supports:
CodeMode executes a deliberately bounded JavaScript subset. See the
[interpreter support checklist](./interpreter-support.md) for the complete, checkable language and standard-library
matrix, known semantic gaps, and intentional exclusions.
- Plain data literals, property access, assignment, destructuring, and sequence expressions (the comma operator, evaluated left to right with the final value returned).
- `if`, conditional expressions, `switch`, `for`, `for...of` (arrays, strings, Maps, Sets, including assignment-form destructuring such as `for ([key, value] of entries)`), `for...in` (own keys of plain objects, index strings of arrays, and namespace/tool names of `tools` references - anything else is an error suggesting `for...of` or `Object.keys`, rather than real JS's surprising behavior of indices for strings and zero iterations for Maps/Sets), `while`, and `do...while`.
- Arrow functions and function declarations with closures, defaults, rest parameters, and destructuring.
- Optional chaining, nullish coalescing, templates, spread (arrays, strings, Maps, Sets), and `try`/`catch`.
- Common array, string, number, `Object`, `Math`, and `JSON` operations, including primitive-number `valueOf`, the standard non-finite `Number` constants, and host-backed `Math.random`. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
- `Date` - `Date.now()`/`Date.parse()`/`Date.UTC()`, `new Date(...)`, the getter methods, and date arithmetic/comparison via the time value. Dates stringify as ISO (`toString` included, for determinism across host timezones).
- Regular expressions - `/literals/` and `new RegExp(...)` with `test`/`exec` (stateful `lastIndex` for `g`), plus string `match`/`matchAll`/`replace`/`replaceAll`/`split`/`search` with patterns. Match results are arrays carrying `index` and named `groups` as own properties (`input` is omitted). `replace` and `replaceAll` accept function replacers with captures, offset, input, and named groups; callbacks run sequentially, may await tool calls, and have their results coerced to strings. Invalid patterns, invalid flags, and missing-`g` calls fail with catchable errors that say what was wrong and how to fix it (escaping hints, the exact `/pattern/g` to write). Patterns run on the host engine, so pathological backtracking is bounded only by the execution timeout.
- `Map` and `Set` - construction from entries/arrays/strings, `get`/`set`/`add`/`has`/`delete`/`clear`/`size`/`forEach`, and `keys`/`values`/`entries` returning **arrays** (not iterators).
- URL helpers - `URL` resolution and mutation, linked `URLSearchParams`, `URL.canParse`/`URL.parse`, URI and URI-component encoding/decoding, and query parameter construction, lookup, mutation, sorting, callbacks, and materialization. URLSearchParams iteration methods return arrays, matching the Map/Set convention.
- First-class promises - an un-awaited `tools.ns.tool(...)` is a promise value whose call starts immediately on an execution-owned fiber; `await` resolves it (awaiting a non-promise value is a no-op, and `return tools.ns.tool(...)` resolves like an async-function return). `Promise.all`, `Promise.allSettled`, and `Promise.race` accept any array mixing promises and plain values (built inline, beforehand, or via spread); `Promise.resolve`/`Promise.reject` construct settled promises. `Promise.allSettled` rejection reasons are the same plain `{ name?, message }` data a `catch` binding sees, and `Promise.race` leaves losing calls running. At most 8 tool calls run concurrently. Before successful completion, CodeMode awaits still-running promises without marking their rejections handled and returns every unobserved ordinary rejection in `Success.unhandledRejections`, in promise-creation order. A fatal program failure instead cancels outstanding work; timeout and host interruption do the same.
- `throw value` and `throw new Error(message)` for explicit program failure. `Error` (and `TypeError`/`RangeError`/`SyntaxError`/`ReferenceError`/`EvalError`/`URIError`) are real constructors, callable with or without `new`; error values are plain `{ name, message }` data that additionally satisfy `instanceof Error` (a specific type matches itself and `Error`, as in JS). Every caught failure - thrown errors, interpreter runtime errors, and tool failures - is `instanceof Error` in a `catch` block; a thrown non-error value (`throw "text"`) is not, matching JS. Caught failures carry the `name` the equivalent real-JS failure would have - `JSON.parse` and invalid regex patterns produce a `SyntaxError` (satisfying `instanceof SyntaxError`), an unknown identifier a `ReferenceError`, assigning to a constant a `TypeError`, a bad `normalize` form a `RangeError`; failures with no specific analogue (including tool failures) are named `"Error"`. `instanceof` also recognizes `Date`, `RegExp`, `Map`, `Set`, `URL`, `URLSearchParams`, `Array`, `Object`, and `Promise`; any other right-hand side is a catchable error.
At a high level, it supports:
Inside a program, standard-library values stay live everywhere: the internal data checkpoints (`Object.*` helpers, spread, coercion inputs) preserve the instances, so `Object.values({ d: date })[0].getTime()` and a spread copy of an object holding a Map keep working. Only at the host boundary (final result, tool arguments, `JSON.stringify`) do they serialize exactly as `JSON.stringify` would: Date and URL become strings (an invalid Date becomes `null`), while RegExp, Map, Set, and URLSearchParams become `{}`. Promise values never cross a data boundary: an un-awaited promise in a result or tool argument produces a diagnostic that says to await it, instead of serializing to `{}`.
- Plain data, property access and assignment, destructuring, functions, conditionals, loops, spread, optional chaining,
and structured error handling.
- Allowlisted Array, String, Number, Object, Math, JSON, console, Date, RegExp, Map, Set, URL, and URLSearchParams APIs.
- Eager supervised tool promises, direct `await`, and the supported `Promise` combinators for concurrent work.
- Live standard-library values inside the sandbox and predictable JSON-like serialization at tool/result boundaries.
- Actionable diagnostics for unsupported syntax, invalid data, tool failures, limits, and execution failures.
It does not expose `eval`, dynamic imports, modules, classes, generators, timers, host globals, prototype mutation, custom promise constructors (`new Promise`), promise chaining (`.then`/`.catch`/`.finally` - `await` with `try`/`catch` is the supported style), or arbitrary method calls. Unsupported syntax returns an `UnsupportedSyntax` diagnostic with a source location when available.
It does not expose ambient host authority or arbitrary JavaScript execution. Unsupported syntax returns an
`UnsupportedSyntax` diagnostic with a source location when available.
CodeMode is an orchestration language, not a general JavaScript runtime.

View file

@ -6,7 +6,8 @@ It records current behavior, intentional boundaries, durable rationale, and mate
Completed implementation history, branch names, test counts, and closed findings belong in git, not here. Remove
completed work instead of preserving checked-off chronology.
Detailed package API documentation lives in [README.md](./README.md). OpenAPI-specific follow-ups live in
Detailed package API documentation lives in [README.md](./README.md), and the checkable language/runtime matrix lives
in [interpreter-support.md](./interpreter-support.md). OpenAPI-specific follow-ups live in
[src/openapi/TODO.md](./src/openapi/TODO.md).
## How CodeMode Works
@ -146,31 +147,5 @@ represent accurately rather than guessing semantics.
## Remaining Work
Keep only material unresolved work here. Small isolated defects should be GitHub issues; adapter-only work belongs in
the adapter TODO. Delete entries when completed.
### DSL expansion
The supported JavaScript subset should grow when common model-generated code improves tool orchestration. These are
current omissions to implement, not intentional product boundaries.
- [ ] Design proper multi-stage promise pipelines. Supporting `.then`, `.catch`, and `.finally` should preserve promise
assimilation, cancellation, failure handling, and concurrent per-item pipelines rather than adding syntax-only
shims. Consider `Promise.any` in the same pass.
- [ ] Support async iteration and `for await...of`. Define behavior first for the runtime's supported promise and
collection values, then extend it to bounded host streams when a stream boundary exists.
- [ ] Support callback-bearing standard-library variants that models commonly generate: the mapper argument to
`Array.from(...)` and replacers for `JSON.stringify(...)`, including Effect-aware callbacks where needed.
- [ ] Add `Object.is` after runtime method and tool references have stable identity semantics.
- [ ] Add deterministic modern collection conveniences where they improve orchestration: `Object.groupBy`, Set
composition methods, and `Array.prototype.toSpliced`.
- [ ] Decide whether iterable `Math.sumPrecise` belongs in the runtime.
- [ ] Refine diagnostics so user throws, expected tool failures, unexpected host/tool defects, and genuine interpreter
defects are distinguishable without leaking private causes.
### Tool and result contracts
- [ ] Design explicit tagged representations and size rules before allowing Blob, File, ArrayBuffer, typed arrays, or
host streams to cross the sandbox boundary.
- [ ] Define one consistent policy for tool path segments named `__proto__`, `constructor`, or `prototype`. They must
either be safely callable, rejected before catalog generation, or use one documented escaping rule.
The [interpreter support checklist](./interpreter-support.md) owns concrete DSL, standard-library, semantic-correctness,
diagnostic, and data-boundary work. OpenAPI adapter work remains in [src/openapi/TODO.md](./src/openapi/TODO.md).

View file

@ -0,0 +1,296 @@
# CodeMode Interpreter Support
This is the checkable support matrix for CodeMode's confined JavaScript interpreter. It tracks the language and
standard-library surface that programs can use today, plus concrete gaps that may be implemented later.
- `[x]` means the feature is implemented at the scope described here.
- `[ ]` means the feature is unavailable, incomplete, or intentionally divergent as described.
- A checked item does not promise complete ECMAScript edge-case parity. Known differences are listed next to the
supported surface or under [Known semantic gaps](#known-semantic-gaps).
- [Intentional exclusions](#intentional-exclusions) are boundaries, not backlog.
When behavior changes, update this file and the tests in the same change. The implementation and tests remain the
ultimate source of truth.
## Source and execution model
- [x] JavaScript parsed with the latest syntax accepted by Acorn, then restricted by the interpreter allowlist.
- [x] Erasable TypeScript syntax, including type annotations, type declarations, assertions, and non-null assertions.
TypeScript is transpiled first; the emitted JavaScript must still use the supported subset.
- [x] Top-level `await` and `return` through the program's implicit async-function scope.
- [x] Explicit `return`, final top-level expression as a REPL-style result, and `null` when no value is produced.
- [x] JSON-like host boundaries with `undefined` and non-finite numbers normalized to `null`.
- [x] Live Date, RegExp, Map, Set, URL, and URLSearchParams values inside the sandbox.
- [x] Tool calls through the host-provided `tools` tree only.
- [x] Cooperative timeout, tool-call accounting, output bounding, and a maximum of eight concurrent tool calls.
- [ ] Full JavaScript or TypeScript compatibility. CodeMode is a bounded orchestration language.
## Values and literals
- [x] `null`, `undefined`, booleans, finite and non-finite numbers, and strings.
- [x] Array literals, including holes and spread from arrays, strings, Maps, Sets, and URLSearchParams.
- [x] Object literals with shorthand, computed string/number keys, and object spread.
- [x] Template literals with interpolation.
- [x] Regular-expression literals.
- [x] `NaN` and `Infinity` globals.
- [ ] BigInt literals and values.
- [ ] Symbols.
- [ ] Tagged template literals.
- [ ] Getters and setters in object literals.
## Bindings and destructuring
- [x] `const`, `let`, and accepted `var` declarations.
- [x] Object and array destructuring in declarations, parameters, assignment expressions, and `for...of` bindings.
- [x] Nested patterns, defaults, elisions, and rest elements.
- [x] Assignment to identifiers, object fields, array indexes, and writable URL fields.
- [x] Function declarations are hoisted within their interpreted scope.
- [x] Parameter defaults observe a temporal dead zone for later parameters.
- [ ] JavaScript-correct `var` function scope, hoisting, and redeclaration. Accepted `var` currently behaves like a
lexical declaration; prefer `let` or `const`.
- [ ] Complete `let`/`const` temporal-dead-zone and declaration-hoisting semantics.
- [ ] Computed object destructuring keys such as `const { [field]: value } = record`.
- [ ] Object destructuring from arrays, such as `const { length } = values`.
- [ ] Iterable array destructuring from Map, Set, string, or URLSearchParams values.
- [ ] Dynamic property deletion with `delete object[key]`.
## Statements and control flow
- [x] Blocks and empty statements.
- [x] `if`/`else` and conditional expressions.
- [x] `switch`, including default clauses and fallthrough.
- [x] `for`, `while`, and `do...while`.
- [x] `for...of` over arrays, strings, Maps, Sets, and URLSearchParams.
- [x] `for...in` over own keys of plain objects, arrays, and tool references.
- [x] Unlabeled `break` and `continue`.
- [x] `try`, `catch`, optional catch bindings, and `finally`.
- [x] `throw` with arbitrary values.
- [ ] Labeled statements, labeled `break`, and labeled `continue`.
- [ ] `for await...of` and async iteration.
- [ ] `with` and `debugger` statements.
## Functions and callbacks
- [x] Function declarations, function expressions, and arrow functions.
- [x] Synchronous and `async` functions.
- [x] Closures, recursion, default parameters, rest parameters, and destructured parameters.
- [x] Expression and block function bodies.
- [x] User callbacks for the supported Array, Map, Set, URLSearchParams, sort, and string-replacement APIs.
- [x] `Boolean`, `Number`, `String`, `parseInt`, `parseFloat`, and URI helpers as callbacks where applicable.
- [x] Async string replacement callbacks; replacements are evaluated sequentially.
- [ ] `this`, `super`, constructor functions, or function prototype methods such as `call`, `apply`, and `bind`.
- [ ] Classes and private fields.
- [ ] Generator functions and `yield`.
- [ ] Async predicates, reducers, and comparators with automatic awaiting. Async mapping can be joined explicitly with
`Promise.all`, but a promise is not a meaningful predicate or sort result.
- [ ] General built-in callable references as callbacks, such as `values.map(Math.abs)` or
`records.map(JSON.stringify)`.
## Expressions and operators
- [x] Property access with dot or computed bracket syntax.
- [x] Optional property access and optional calls.
- [x] Function/tool calls and spread arguments.
- [x] Sequence expressions (the comma operator).
- [x] `await` for sandbox promises; awaiting a plain value is a no-op.
- [x] `new` for Error types, Date, RegExp, Map, Set, URL, and URLSearchParams.
- [x] Arithmetic operators: `+`, `-`, `*`, `/`, `%`, and `**`.
- [x] Equality and ordering: `==`, `!=`, `===`, `!==`, `<`, `<=`, `>`, and `>=`.
- [x] Bitwise operators: `&`, `|`, `^`, `~`, `<<`, `>>`, and `>>>`.
- [x] Logical operators: `&&`, `||`, `??`, and `!`, with short-circuiting.
- [x] Unary `+`, unary `-`, `typeof`, `instanceof`, and own-property-only `in`.
- [x] Prefix and postfix `++` and `--`.
- [x] Plain, arithmetic, bitwise, and logical assignment operators.
- [ ] Unary `void` and `delete`.
- [ ] Arbitrary constructors and `new Promise(...)`.
## Promises and tools
- [x] Tool calls start eagerly and return supervised, run-once sandbox promises.
- [x] Direct `await`, repeated awaits, and implicit resolution when a promise is returned from a function/program.
- [x] `Promise.resolve` and `Promise.reject`.
- [x] `Promise.all`, `Promise.allSettled`, and `Promise.race` over supported collections containing promises and plain
values.
- [x] `Promise.all` preserves result order and rejects on the first observed failure.
- [x] `Promise.allSettled` returns plain fulfilled/rejected outcome records.
- [x] `Promise.race` interrupts losing in-flight tool calls.
- [x] Un-awaited calls are drained before execution ends; unhandled failures become diagnostics.
- [x] `try`/`catch` can handle awaited tool and promise failures.
- [ ] Real promise values from `Promise.all`, `Promise.allSettled`, and `Promise.race`. These calls currently settle
before returning, so separately constructed combinator batches do not overlap as normal JavaScript promises do.
- [ ] `Promise.any`.
- [ ] Promise chaining with `.then`, `.catch`, and `.finally`.
- [ ] Custom promise construction with `new Promise(...)`.
- [ ] Async iterables, host streams, and stream consumption.
## Objects and properties
- [x] Own-field reads and writes on plain data objects.
- [x] Computed property names and object spread.
- [x] `Object.keys`, `Object.values`, `Object.entries`, `Object.hasOwn`, `Object.assign`, and `Object.fromEntries`.
- [x] `Object.keys` over arrays and tool references.
- [x] Object identity is preserved by in-sandbox Object helpers.
- [x] Blocked access to `__proto__`, `constructor`, and `prototype`.
- [ ] `Object.is`; runtime and tool-reference identity semantics need to be defined first.
- [ ] `Object.groupBy`.
- [ ] Object creation, descriptors, freezing/sealing, prototype APIs, and reflection APIs.
- [ ] A final policy for legal data/tool keys named `__proto__`, `constructor`, or `prototype`.
## Arrays
- [x] Static methods: `Array.isArray`, `Array.of`, and `Array.from`.
- [x] Iteration/transformation: `map`, `filter`, `flatMap`, and `forEach`.
- [x] Searching/tests: `find`, `findIndex`, `findLast`, `findLastIndex`, `some`, `every`, `includes`, `indexOf`, and
`lastIndexOf`.
- [x] Aggregation: `reduce` and `reduceRight`.
- [x] Ordering: `sort`, `toSorted`, `reverse`, and `toReversed`.
- [x] Access/copying: `at`, `slice`, `concat`, `flat`, `with`, and `join`.
- [x] Mutation: `push`, `pop`, `shift`, `unshift`, `splice`, `fill`, and `copyWithin`.
- [x] Materialized iteration helpers: `keys`, `values`, and `entries` return arrays rather than iterators.
- [x] `length`, numeric indexing, index assignment, spread, and `for...of`.
- [ ] The mapper and `thisArg` forms of `Array.from`.
- [ ] `Array.prototype.toSpliced`.
- [ ] Canonical index handling: a key such as `"01"` must not alias index `1`.
- [ ] Complete sparse-array parity.
- [ ] Correct `findLast` return behavior when its predicate mutates the examined element.
## Strings
- [x] Case/normalization: `toLowerCase`, `toUpperCase`, `normalize`.
- [x] Trimming: `trim`, `trimStart`, `trimEnd`, `trimLeft`, and `trimRight`.
- [x] Searching/tests: `includes`, `startsWith`, `endsWith`, `indexOf`, `lastIndexOf`, and `search`.
- [x] Slicing/access: `slice`, `substring`, `substr`, `at`, `charAt`, `charCodeAt`, and `codePointAt`.
- [x] Construction/transformation: `split`, `concat`, `repeat`, `padStart`, `padEnd`, `replace`, and `replaceAll`.
- [x] Regular-expression integration: `match`, materialized `matchAll`, `replace`, `replaceAll`, `split`, and `search`.
- [x] `localeCompare`; locale and options arguments are currently ignored.
- [x] `toString`, `length`, numeric indexing, spread, and `for...of` by Unicode code point.
- [x] Static `String.fromCharCode` and `String.fromCodePoint`.
- [ ] Locale/options-aware `localeCompare` and locale formatting APIs.
- [ ] Exact native coercion across every string method; CodeMode often requires explicit strings/numbers.
- [ ] Native no-argument parity for `match()` and `search()`.
## Numbers and Math
- [x] Coercion functions: `Number`, `parseInt`, and `parseFloat`.
- [x] Number predicates/parsers: `Number.isInteger`, `Number.isFinite`, `Number.isNaN`, `Number.isSafeInteger`,
`Number.parseInt`, and `Number.parseFloat`.
- [x] Number formatting: `toFixed`, `toPrecision`, `toExponential`, `toString`, and `valueOf`.
- [x] Number constants: `MAX_SAFE_INTEGER`, `MIN_SAFE_INTEGER`, `MAX_VALUE`, `MIN_VALUE`, `EPSILON`, `NaN`,
`POSITIVE_INFINITY`, and `NEGATIVE_INFINITY`.
- [x] Math constants: `PI`, `E`, `LN2`, `LN10`, `LOG2E`, `LOG10E`, `SQRT2`, and `SQRT1_2`.
- [x] Math methods: `random`, `max`, `min`, `abs`, `acos`, `acosh`, `asin`, `asinh`, `atan`, `atan2`, `atanh`,
`floor`, `ceil`, `round`, `trunc`, `sign`, `sqrt`, `cbrt`, `pow`, `hypot`, `cos`, `cosh`, `sin`, `sinh`,
`tan`, `tanh`, `log`, `log2`, `log10`, `log1p`, `exp`, `expm1`, `f16round`, `fround`, `clz32`, and `imul`.
- [ ] Native zero-argument behavior for `Number()` and `String()`; they currently do not produce `0` and `""`.
- [ ] Safe interpreter coercion for `++` and `--` rather than host `Number(...)` coercion.
- [ ] Reliable feature detection for unknown static members.
- [ ] `Math.sumPrecise`.
- [ ] Global coercing `isFinite` and `isNaN`.
## JSON and console
- [x] `JSON.parse` and `JSON.stringify`.
- [x] Numeric/string indentation for `JSON.stringify`.
- [x] Captured `console.log`, `console.info`, `console.debug`, `console.warn`, and `console.error`.
- [x] Captured `console.dir` and `console.table`.
- [ ] `JSON.parse` reviver callbacks.
- [ ] `JSON.stringify` function/array replacers.
- [ ] Other console methods, timers, counters, groups, and host console access.
## Date
- [x] `Date.now`, `Date.parse`, and `Date.UTC`.
- [x] `new Date()` from the current time, epoch milliseconds, a date string, another Date, or local components.
- [x] `getTime`, `valueOf`, `toISOString`, `toJSON`, and deterministic ISO `toString`.
- [x] Local getters: `getFullYear`, `getMonth`, `getDate`, `getDay`, `getHours`, `getMinutes`, `getSeconds`, and
`getMilliseconds`.
- [x] UTC getters: `getUTCFullYear`, `getUTCMonth`, `getUTCDate`, `getUTCDay`, `getUTCHours`, `getUTCMinutes`,
`getUTCSeconds`, and `getUTCMilliseconds`.
- [x] `getTimezoneOffset`, arithmetic, relational comparison, and `instanceof Date`.
- [x] Date values serialize to ISO strings; invalid dates serialize to `null`.
- [ ] Date setters.
- [ ] `toUTCString`, locale methods, and other Date formatting methods.
- [ ] Exact native constructor coercion, local-time, and loose-equality semantics.
- [ ] Native `RangeError` branding for invalid `toISOString()` calls.
- [ ] Temporal and Intl date/time APIs.
## Regular expressions
- [x] Literal and `new RegExp(pattern, flags)` construction.
- [x] `test`, `exec`, and `toString`.
- [x] Readable `source`, `flags`, `lastIndex`, `global`, `ignoreCase`, `multiline`, `sticky`, `unicode`, and `dotAll`.
- [x] Captures, named groups, match indexes, and stateful global matching.
- [x] Integration with supported String methods, including async function replacers.
- [ ] Writable `lastIndex`.
- [ ] Exposed metadata for the `d` and `v` flags.
- [ ] `RegExp.escape`.
- [ ] Protection from pathological host-regex backtracking beyond the cooperative execution timeout.
## Map and Set
- [x] `new Map()` from entry arrays or another Map.
- [x] Map `get`, `set`, `has`, `delete`, `clear`, `size`, and `forEach`.
- [x] `new Set()` from arrays, strings, or another Set.
- [x] Set `add`, `has`, `delete`, `clear`, `size`, and `forEach`.
- [x] Materialized `keys`, `values`, and `entries` arrays for Map and Set.
- [x] Spread, `for...of`, `Array.from`, and `Object.fromEntries` integration.
- [x] Map and Set values serialize to `{}` at host/JSON boundaries.
- [ ] Set composition methods such as `union`, `intersection`, `difference`, and relation predicates.
- [ ] WeakMap and WeakSet.
- [ ] Native iterator objects and custom iterators.
## URL and URI helpers
- [x] `encodeURI`, `encodeURIComponent`, `decodeURI`, and `decodeURIComponent`.
- [x] `new URL(input, base)`, `URL.canParse`, and `URL.parse`.
- [x] URL `toString`, `toJSON`, and linked `searchParams`.
- [x] Readable URL fields: `href`, `origin`, `protocol`, `username`, `password`, `host`, `hostname`, `port`,
`pathname`, `search`, and `hash`.
- [x] Writable URL fields except `origin`.
- [x] `new URLSearchParams()` from query strings, data objects, pairs, Maps, and URLSearchParams.
- [x] URLSearchParams `append`, `delete`, `get`, `getAll`, `has`, `set`, `sort`, `forEach`, `keys`, `values`,
`entries`, `toString`, and `size`.
- [x] URL values serialize to their href; URLSearchParams serialize to `{}`.
## Errors and diagnostics
- [x] `Error`, `TypeError`, `RangeError`, `SyntaxError`, `ReferenceError`, `EvalError`, and `URIError`, callable with
or without `new`.
- [x] Error `name`/`message`, error inheritance through `instanceof`, and plain-data serialization.
- [x] `instanceof` for Date, RegExp, Map, Set, URL, URLSearchParams, Array, Object, Promise, and Error types.
- [x] Catchable interpreter failures and awaited tool failures.
- [x] Source locations on unsupported-syntax diagnostics when available.
- [x] Sanitized model-visible diagnostics and explicit safe `ToolError` messages.
- [ ] Distinct public categories for user throws, tool refusal, tool internal failure, invalid returned data, compile
failures, and genuine interpreter defects.
- [ ] Preservation of detailed recoverable failure categories inside `catch` and `Promise.allSettled`.
## Known semantic gaps
These are actionable implementation items. Check them off only when behavior and direct tests land.
- [ ] Return real promises from `Promise.all`, `Promise.allSettled`, and `Promise.race`.
- [ ] Bound pending tool-call admission/allocation in addition to execution concurrency.
- [ ] Guarantee every advertised tool path is executable, including dotted and blocked path segments.
- [ ] Define safe outbound handling for non-finite numbers and `undefined` so invalid values cannot silently become
`null` in render-only or OpenAPI tool calls.
- [ ] Make regular-expression execution genuinely timeout-safe, or narrow the timeout guarantee explicitly.
- [ ] Complete lexical declaration and destructuring semantics listed above.
- [ ] Make callback acceptance and async callback behavior consistent across built-ins.
- [ ] Reject every unsupported callback argument explicitly rather than silently ignoring it.
- [ ] Resolve the built-in correctness gaps listed in the Array, String, Number, Date, and RegExp sections.
- [ ] Make tool search tokenization Unicode-aware.
- [ ] Design explicit tagged representations and size limits before adding binary values or streams.
## Intentional exclusions
These constraints preserve CodeMode's confinement and host-neutral scope. They are not TODO items.
- Ambient filesystem, process, environment, credential, network, or application access.
- `fetch`, timers, crypto, or other host globals unless a future host explicitly supplies a bounded capability.
- Static imports, dynamic imports, modules, npm packages, and module loading.
- `eval`, `Function(...)`, arbitrary host execution, and prototype mutation.
- Generic permission prompts, authorization policy, persistence, replay, or exactly-once side effects.
- Arbitrary method dispatch outside the documented allowlists.
- Automatic parsing of text tool results as JSON.
- Full browser, Node.js, Bun, or ECMAScript runtime compatibility.

View file

@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/codemode",
"version": "1.17.14",
"version": "1.17.15",
"description": "Effect-native confined code execution over schema-described tools",
"private": true,
"type": "module",

View file

@ -0,0 +1,28 @@
Test262: ECMAScript Test Suite ("Software") is protected by copyright and is being
made available under the "BSD License", included below. This Software may be subject to third party rights (rights
from parties other than Ecma International), including patent rights, and no licenses under such third party rights
are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA
CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://www.ecma-international.org/ipr FOR
INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS*.
Copyright (C) 2012 Ecma International
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
* Ecma International Standards hereafter means Ecma International Standards as well as Ecma Technical Reports

View file

@ -0,0 +1,325 @@
/*
* Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
* - test/built-ins/Array/prototype/map/15.4.4.19-8-1.js
* - test/built-ins/Array/prototype/map/15.4.4.19-8-2.js
* - test/built-ins/Array/prototype/map/15.4.4.19-8-b-1.js
* - test/built-ins/Array/prototype/filter/15.4.4.20-9-1.js
* - test/built-ins/Array/prototype/filter/15.4.4.20-9-2.js
* - test/built-ins/Array/prototype/filter/15.4.4.20-9-b-1.js
* - test/built-ins/Array/prototype/find/predicate-call-parameters.js
* - test/built-ins/Array/prototype/find/predicate-not-called-on-empty-array.js
* - test/built-ins/Array/prototype/find/return-found-value-predicate-result-is-true.js
* - test/built-ins/Array/prototype/find/return-undefined-if-predicate-returns-false-value.js
* - test/built-ins/Array/prototype/findIndex/predicate-call-parameters.js
* - test/built-ins/Array/prototype/findIndex/return-index-predicate-result-is-true.js
* - test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js
* - test/built-ins/Array/prototype/findLast/predicate-call-parameters.js
* - test/built-ins/Array/prototype/findLast/return-found-value-predicate-result-is-true.js
* - test/built-ins/Array/prototype/findLast/return-undefined-if-predicate-returns-false-value.js
* - test/built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js
* - test/built-ins/Array/prototype/findLastIndex/return-index-predicate-result-is-true.js
* - test/built-ins/Array/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js
* - test/built-ins/Array/prototype/some/15.4.4.17-7-1.js
* - test/built-ins/Array/prototype/some/15.4.4.17-8-1.js
* - test/built-ins/Array/prototype/every/15.4.4.16-7-1.js
* - test/built-ins/Array/prototype/every/15.4.4.16-8-1.js
* - test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js
* - test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js
* - test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js
* - test/built-ins/Array/prototype/flatMap/depth-always-one.js
* - test/built-ins/Array/prototype/flatMap/mapperfunction-throws.js
* - test/built-ins/Array/prototype/sort/S15.4.4.11_A1.1_T1.js
* - test/built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T1.js
* - test/built-ins/Array/prototype/sort/stability-5-elements.js
* - test/built-ins/Array/prototype/toSorted/comparefn-controls-sort.js
* - test/built-ins/Array/prototype/toSorted/comparefn-default.js
* - test/built-ins/Array/prototype/toSorted/immutable.js
* - test/built-ins/Array/prototype/toSorted/zero-or-one-element.js
*
* Copyright (C) 2015 the V8 project authors. All rights reserved.
* Copyright (C) 2018 Mathias Bynens. All rights reserved.
* Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
* Copyright (C) 2021 Igalia, S.L. All rights reserved.
* Copyright (C) 2021 Microsoft. All rights reserved.
* Copyright (C) 2025 Google. All rights reserved.
* Copyright (C) 2026 Garham Lee. All rights reserved.
* Copyright (c) 2012 Ecma International. All rights reserved.
* Copyright 2009 the Sputnik authors. All rights reserved.
* Test262 portions are governed by the BSD license in LICENSE.test262.
*/
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { CodeMode } from "../src/index.js"
const value = async (code: string) => {
const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
return result.value
}
const cases = [
{
path: "test/built-ins/Array/prototype/map/15.4.4.19-8-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const result = input.map((value) => { input[2] = 3; input[5] = 6; return 1 }); return [result.length, result[5] === undefined]`,
expected: [5, true],
},
{
path: "test/built-ins/Array/prototype/map/15.4.4.19-8-2.js",
code: `const input = [1, 2, 3, 4, 5]; const result = input.map((value) => { input[4] = -1; return value > 0 ? 1 : 0 }); return [result.length, result[4]]`,
expected: [5, 0],
},
{
path: "test/built-ins/Array/prototype/map/15.4.4.19-8-b-1.js",
code: `const input = []; input[10] = 0; input.pop(); input[1] = undefined; let calls = 0; const result = input.map(() => { calls += 1; return 1 }); return [result.length, calls, 0 in result, 1 in result]`,
expected: [10, 1, false, true],
},
{
path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const result = input.filter(() => { input[2] = 3; input[5] = 6; return true }); return result`,
expected: [1, 2, 3, 4, 5],
},
{
path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-2.js",
code: `const input = [1, 2, 3, 4, 5]; return input.filter((value) => { input[2] = -1; input[4] = -1; return value > 0 })`,
expected: [1, 2, 4],
},
{
path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-b-1.js",
code: `const input = []; input[9] = 0; input.pop(); input[1] = undefined; let calls = 0; const result = input.filter(() => { calls += 1; return false }); return [result, calls]`,
expected: [[], 1],
},
{
path: "test/built-ins/Array/prototype/find/predicate-call-parameters.js",
code: `const input = [10, 20]; const seen = []; input.find((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
expected: [
[10, 0, true],
[20, 1, true],
],
},
{
path: "test/built-ins/Array/prototype/find/return-found-value-predicate-result-is-true.js",
code: `return [1, 2, 3].find((value) => value > 1)`,
expected: 2,
},
{
path: "test/built-ins/Array/prototype/find/return-undefined-if-predicate-returns-false-value.js",
code: `return [1, 2, 3].find((value) => value > 4) === undefined`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/find/predicate-not-called-on-empty-array.js",
code: `let calls = 0; const result = [].find(() => { calls += 1; return true }); return [result === undefined, calls]`,
expected: [true, 0],
},
{
path: "test/built-ins/Array/prototype/findIndex/predicate-call-parameters.js",
code: `const input = [10, 20]; const seen = []; input.findIndex((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
expected: [
[10, 0, true],
[20, 1, true],
],
},
{
path: "test/built-ins/Array/prototype/findIndex/return-index-predicate-result-is-true.js",
code: `return [1, 2, 3].findIndex((value) => value > 1)`,
expected: 1,
},
{
path: "test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js",
code: `return [1, 2, 3].findIndex((value) => value > 4)`,
expected: -1,
},
{
path: "test/built-ins/Array/prototype/findLast/predicate-call-parameters.js",
code: `const input = [10, 20]; const seen = []; input.findLast((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
expected: [
[20, 1, true],
[10, 0, true],
],
},
{
path: "test/built-ins/Array/prototype/findLast/return-found-value-predicate-result-is-true.js",
code: `return [1, 2, 3].findLast((value) => value < 3)`,
expected: 2,
},
{
path: "test/built-ins/Array/prototype/findLast/return-undefined-if-predicate-returns-false-value.js",
code: `return [1, 2, 3].findLast((value) => value > 4) === undefined`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js",
code: `const input = [10, 20]; const seen = []; input.findLastIndex((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
expected: [
[20, 1, true],
[10, 0, true],
],
},
{
path: "test/built-ins/Array/prototype/findLastIndex/return-index-predicate-result-is-true.js",
code: `return [1, 2, 3].findLastIndex((value) => value < 3)`,
expected: 1,
},
{
path: "test/built-ins/Array/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js",
code: `return [1, 2, 3].findLastIndex((value) => value > 4)`,
expected: -1,
},
{
path: "test/built-ins/Array/prototype/some/15.4.4.17-7-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const seen = []; const result = input.some((value) => { input[2] = 3; seen.push(value); return false }); return [result, seen.includes(3)]`,
expected: [false, true],
},
{
path: "test/built-ins/Array/prototype/some/15.4.4.17-8-1.js",
code: `return [].some(() => true)`,
expected: false,
},
{
path: "test/built-ins/Array/prototype/every/15.4.4.16-7-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const seen = []; const result = input.every((value) => { input[2] = 3; seen.push(value); return true }); return [result, seen.includes(3)]`,
expected: [true, true],
},
{
path: "test/built-ins/Array/prototype/every/15.4.4.16-8-1.js",
code: `return [].every(() => false)`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; let calls = 0; input.forEach(() => { calls += 1; input[2] = 3; input[5] = 6 }); return calls`,
expected: 5,
},
{
path: "test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js",
code: `const input = [1, 2, 3]; const seen = []; input.forEach((value, index) => { seen.push(value); if (index === 0) input.pop() }); return seen`,
expected: [1, 2],
},
{
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = "5"; return input.reduce((accumulator, value) => { input[2] = 3; input[5] = 6; return accumulator + value })`,
expected: "105",
},
{
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js",
code: `let calls = 0; const result = [1].reduce(() => { calls += 1; return 2 }); return [result, calls]`,
expected: [1, 0],
},
{
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js",
code: `const input = [1, 2, 3, 4, 5]; input.reduce(() => 1); return input`,
expected: [1, 2, 3, 4, 5],
},
{
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js",
code: `const input = ["1", 2]; input[3] = 4; input[4] = "5"; return input.reduceRight((accumulator, value) => { input[2] = 3; input[5] = 6; return accumulator + value })`,
expected: "54321",
},
{
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js",
code: `let calls = 0; const result = [1].reduceRight(() => { calls += 1; return 2 }); return [result, calls]`,
expected: [1, 0],
},
{
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js",
code: `const input = [1, 2, 3, 4, 5]; input.reduceRight(() => 1); return input`,
expected: [1, 2, 3, 4, 5],
},
{
path: "test/built-ins/Array/prototype/flatMap/depth-always-one.js",
code: `return [1, 2, 3].flatMap((value) => [[value * 2]])`,
expected: [[2], [4], [6]],
},
{
path: "test/built-ins/Array/prototype/flatMap/mapperfunction-throws.js",
code: `try { [1, 2].flatMap(() => { throw "stop" }) } catch (error) { return error === "stop" } return false`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/sort/S15.4.4.11_A1.1_T1.js",
code: `const input = []; input[2] = 0; input.pop(); input.sort(); return [input.length, input[0] === undefined, input[1] === undefined]`,
expected: [2, true, true],
},
{
path: "test/built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T1.js",
code: `return ["z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n", "M", "L", "K", "J", "I", "H", "G", "F", "E", "D", "C", "B", "A"].sort()`,
expected: [
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
],
},
{
path: "test/built-ins/Array/prototype/sort/stability-5-elements.js",
code: `const input = [{ n: "A", r: 2 }, { n: "B", r: 3 }, { n: "C", r: 2 }, { n: "D", r: 3 }, { n: "E", r: 3 }]; return input.sort((left, right) => right.r - left.r).map((item) => item.n).join("")`,
expected: "BDEAC",
},
{
path: "test/built-ins/Array/prototype/toSorted/comparefn-controls-sort.js",
code: `const mixed = [333, 33, 3, 222, 22, 2, 111, 11, 1]; return [[1, 2, 3, 4].toSorted((a, b) => a - b), [4, 3, 2, 1].toSorted((a, b) => a - b), mixed.toSorted((a, b) => a - b), [1, 2, 3, 4].toSorted((a, b) => b - a), [4, 3, 2, 1].toSorted((a, b) => b - a), mixed.toSorted((a, b) => b - a)]`,
expected: [
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 11, 22, 33, 111, 222, 333],
[4, 3, 2, 1],
[4, 3, 2, 1],
[333, 222, 111, 33, 22, 11, 3, 2, 1],
],
},
{
path: "test/built-ins/Array/prototype/toSorted/comparefn-default.js",
code: `return [[1, 2, 3, 4].toSorted(), [4, 3, 2, 1].toSorted(), ["a", 2, 1, "z"].toSorted(), [333, 33, 3, 222, 22, 2, 111, 11, 1].toSorted()]`,
expected: [
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, "a", "z"],
[1, 11, 111, 2, 22, 222, 3, 33, 333],
],
},
{
path: "test/built-ins/Array/prototype/toSorted/immutable.js",
code: `const input = [2, 0, 1]; const result = input.toSorted(); return [input, result !== input]`,
expected: [[2, 0, 1], true],
},
{
path: "test/built-ins/Array/prototype/toSorted/zero-or-one-element.js",
code: `const zero = []; const one = [1]; const zeroResult = zero.toSorted(); const oneResult = one.toSorted(); return [zeroResult, oneResult, zeroResult !== zero, oneResult !== one]`,
expected: [[], [1], true, true],
},
] as const
describe("Test262 Array callback adaptations", () => {
for (const item of cases) {
test(item.path, async () => {
expect(await value(item.code)).toEqual(item.expected)
})
}
})

View file

@ -0,0 +1,323 @@
/*
* Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
* - test/built-ins/Array/prototype/includes/samevaluezero.js
* - test/built-ins/Array/prototype/includes/using-fromindex.js
* - test/built-ins/Array/prototype/join/S15.4.4.5_A3.1_T1.js
* - test/built-ins/Array/prototype/join/S15.4.4.5_A3.2_T1.js
* - test/built-ins/Array/prototype/slice/S15.4.4.10_A1.2_T2.js
* - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T1.js
* - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T2.js
* - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T3.js
* - test/built-ins/Array/prototype/indexOf/fromindex-zero-conversion.js
* - test/built-ins/Array/prototype/indexOf/length-zero-returns-minus-one.js
* - test/built-ins/Array/prototype/lastIndexOf/fromindex-zero-conversion.js
* - test/built-ins/Array/prototype/lastIndexOf/length-zero-returns-minus-one.js
* - test/built-ins/Array/prototype/at/returns-item.js
* - test/built-ins/Array/prototype/at/returns-item-relative-index.js
* - test/built-ins/Array/prototype/at/returns-undefined-for-out-of-range-index.js
* - test/built-ins/Array/prototype/flat/null-undefined-elements.js
* - test/built-ins/Array/prototype/flat/positive-infinity.js
* - test/built-ins/Array/prototype/reverse/S15.4.4.8_A1_T1.js
* - test/built-ins/Array/prototype/toReversed/immutable.js
* - test/built-ins/Array/prototype/toReversed/zero-or-one-element.js
* - test/built-ins/Array/prototype/with/immutable.js
* - test/built-ins/Array/prototype/with/index-negative.js
* - test/built-ins/Array/prototype/push/S15.4.4.7_A1_T1.js
* - test/built-ins/Array/prototype/pop/S15.4.4.6_A1.1_T1.js
* - test/built-ins/Array/prototype/shift/S15.4.4.9_A1.1_T1.js
* - test/built-ins/Array/prototype/unshift/S15.4.4.13_A1_T1.js
* - test/built-ins/Array/prototype/splice/S15.4.4.12_A1.1_T1.js
* - test/built-ins/Array/prototype/splice/S15.4.4.12_A1.2_T1.js
* - test/built-ins/Array/prototype/splice/called_with_one_argument.js
* - test/built-ins/Array/prototype/fill/fill-values.js
* - test/built-ins/Array/prototype/fill/fill-values-custom-start-and-end.js
* - test/built-ins/Array/prototype/fill/return-this.js
* - test/built-ins/Array/prototype/copyWithin/non-negative-target-start-and-end.js
* - test/built-ins/Array/prototype/copyWithin/return-this.js
* - test/built-ins/Array/prototype/keys/iteration.js
* - test/built-ins/Array/prototype/values/iteration.js
* - test/built-ins/Array/prototype/entries/iteration.js
* - test/built-ins/Array/isArray/15.4.3.2-0-3.js
* - test/built-ins/Array/isArray/15.4.3.2-0-4.js
* - test/built-ins/Array/from/from-array.js
* - test/built-ins/Array/from/from-string.js
* - test/built-ins/Array/from/array-like-has-length-but-no-indexes-with-values.js
* - test/built-ins/Array/of/creates-a-new-array-from-arguments.js
*
* Copyright (C) 2015 André Bargull. All rights reserved.
* Copyright (C) 2015 the V8 project authors. All rights reserved.
* Copyright (C) 2016 the V8 project authors. All rights reserved.
* Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
* Copyright (C) 2020 Alexey Shvayka. All rights reserved.
* Copyright (C) 2020 Rick Waldron. All rights reserved.
* Copyright (C) 2021 Igalia, S.L. All rights reserved.
* Copyright (c) 2012 Ecma International. All rights reserved.
* Copyright (c) 2014 Hank Yates. All rights reserved.
* Copyright (c) 2015 the V8 project authors. All rights reserved.
* Copyright (c) 2021 Rick Waldron. All rights reserved.
* Copyright 2009 the Sputnik authors. All rights reserved.
* Copyright 2015 Microsoft Corporation. All rights reserved.
* Copyright 2016 The V8 project authors. All rights reserved.
* Test262 portions are governed by the BSD license in LICENSE.test262.
*/
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { CodeMode } from "../src/index.js"
const value = async (code: string) => {
const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
return result.value
}
const cases = [
{
path: "test/built-ins/Array/prototype/includes/samevaluezero.js",
code: `const input = [42, 0, 1, NaN]; return [input.includes(42), input.includes("42"), input.includes([42]), input.includes(true), input.includes(NaN), input.includes(0), input.includes(-0), input.includes(null), input.includes("")]`,
expected: [true, false, false, false, true, true, true, false, false],
},
{
path: "test/built-ins/Array/prototype/includes/using-fromindex.js",
code: `const input = ["a", "b", "c"]; return [input.includes("a", 0), input.includes("a", 1), input.includes("a", -4), input.includes("a", -3), input.includes("a", -2), input.includes("b", 0), input.includes("b", 1), input.includes("b", 2), input.includes("b", -3), input.includes("b", -2), input.includes("b", -1), input.includes("c", 0), input.includes("c", 2), input.includes("c", 3), input.includes("c", -3), input.includes("c", -1)]`,
expected: [true, false, true, true, false, true, true, false, true, true, false, true, true, false, true, true],
},
{
path: "test/built-ins/Array/prototype/join/S15.4.4.5_A3.1_T1.js",
code: `return [[0, 1, 2, 3].join("&"), [0, 1, 2, 3].join("")]`,
expected: ["0&1&2&3", "0123"],
},
{
path: "test/built-ins/Array/prototype/join/S15.4.4.5_A3.2_T1.js",
code: `return [
["", "", ""].join(""),
["&", "&", "&"].join("&"),
[true, true, true].join(),
[null, null, null].join(),
[undefined, undefined, undefined].join(),
[Infinity, Infinity, Infinity].join(),
[NaN, NaN, NaN].join(),
]`,
expected: ["", "&&&&&", "true,true,true", ",,", ",,", "Infinity,Infinity,Infinity", "NaN,NaN,NaN"],
},
{
path: "test/built-ins/Array/prototype/slice/S15.4.4.10_A1.2_T2.js",
code: `return [0, 1, 2, 3, 4].slice(-1, 5)`,
expected: [4],
},
{
path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T1.js",
code: `return [].concat([0, 1], [2, 3, 4])`,
expected: [0, 1, 2, 3, 4],
},
{
path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T2.js",
code: `const object = { value: 1 }; const result = [0].concat(object, [1, 2], -1, true, "NaN"); return [result, result[1] === object]`,
expected: [[0, { value: 1 }, 1, 2, -1, true, "NaN"], true],
},
{
path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T3.js",
code: `const input = [0, 1]; const result = input.concat(); return [result, result !== input]`,
expected: [[0, 1], true],
},
{
path: "test/built-ins/Array/prototype/indexOf/fromindex-zero-conversion.js",
code: `const result = [true].indexOf(true, -0); return [result, 1 / result === Infinity]`,
expected: [0, true],
},
{
path: "test/built-ins/Array/prototype/indexOf/length-zero-returns-minus-one.js",
code: `return [].indexOf(1)`,
expected: -1,
},
{
path: "test/built-ins/Array/prototype/lastIndexOf/fromindex-zero-conversion.js",
code: `const result = [true].lastIndexOf(true, -0); return [result, 1 / result === Infinity]`,
expected: [0, true],
},
{
path: "test/built-ins/Array/prototype/lastIndexOf/length-zero-returns-minus-one.js",
code: `return [].lastIndexOf(1)`,
expected: -1,
},
{
path: "test/built-ins/Array/prototype/at/returns-item.js",
code: `const input = [1, 2, 3, 4, undefined, 5]; return [input.at(0), input.at(1), input.at(2), input.at(3), input.at(4) === undefined, input.at(5)]`,
expected: [1, 2, 3, 4, true, 5],
},
{
path: "test/built-ins/Array/prototype/at/returns-item-relative-index.js",
code: `const input = [1, 2, 3, 4, undefined, 5]; return [input.at(0), input.at(-1), input.at(-2) === undefined, input.at(-3), input.at(-4)]`,
expected: [1, 5, true, 4, 3],
},
{
path: "test/built-ins/Array/prototype/at/returns-undefined-for-out-of-range-index.js",
code: `const input = []; return [input.at(-2) === undefined, input.at(0) === undefined, input.at(1) === undefined]`,
expected: [true, true, true],
},
{
path: "test/built-ins/Array/prototype/flat/null-undefined-elements.js",
code: `const result = [1, [null, [undefined]]].flat(2); return [result.length, result[0], result[1] === null, result[2] === undefined]`,
expected: [3, 1, true, true],
},
{
path: "test/built-ins/Array/prototype/flat/positive-infinity.js",
code: `return [1, [2, [3, [4]]]].flat(Infinity)`,
expected: [1, 2, 3, 4],
},
{
path: "test/built-ins/Array/prototype/reverse/S15.4.4.8_A1_T1.js",
code: `const empty = []; const one = [1]; const input = [1, 2]; const emptyResult = empty.reverse(); const oneResult = one.reverse(); const result = input.reverse(); return [emptyResult === empty, oneResult === one, result === input, input]`,
expected: [true, true, true, [2, 1]],
},
{
path: "test/built-ins/Array/prototype/toReversed/immutable.js",
code: `const input = [0, 1, 2]; const result = input.toReversed(); return [input, result !== input]`,
expected: [[0, 1, 2], true],
},
{
path: "test/built-ins/Array/prototype/toReversed/zero-or-one-element.js",
code: `const zero = []; const one = [1]; const zeroResult = zero.toReversed(); const oneResult = one.toReversed(); return [zeroResult, oneResult, zeroResult !== zero, oneResult !== one]`,
expected: [[], [1], true, true],
},
{
path: "test/built-ins/Array/prototype/with/immutable.js",
code: `const input = [0, 1, 2]; const result = input.with(1, 3); return [input, result !== input, input.with(1, 1) !== input]`,
expected: [[0, 1, 2], true, true],
},
{
path: "test/built-ins/Array/prototype/with/index-negative.js",
code: `const input = [0, 1, 2]; return [input.with(-1, 4), input.with(-3, 4)]`,
expected: [
[0, 1, 4],
[4, 1, 2],
],
},
{
path: "test/built-ins/Array/prototype/push/S15.4.4.7_A1_T1.js",
code: `const input = []; return [input.push(1), input.push(), input.push(-1), input]`,
expected: [1, 1, 2, [1, -1]],
},
{
path: "test/built-ins/Array/prototype/pop/S15.4.4.6_A1.1_T1.js",
code: `const input = []; return [input.pop() === undefined, input.length]`,
expected: [true, 0],
},
{
path: "test/built-ins/Array/prototype/shift/S15.4.4.9_A1.1_T1.js",
code: `const input = []; return [input.shift() === undefined, input.length]`,
expected: [true, 0],
},
{
path: "test/built-ins/Array/prototype/unshift/S15.4.4.13_A1_T1.js",
code: `const input = []; return [input.unshift(1), input[0], input.unshift(), input.unshift(-1), input]`,
expected: [1, 1, 1, 2, [-1, 1]],
},
{
path: "test/built-ins/Array/prototype/splice/S15.4.4.12_A1.1_T1.js",
code: `const input = [0, 1, 2, 3]; const removed = input.splice(0, 3); return [input, removed]`,
expected: [[3], [0, 1, 2]],
},
{
path: "test/built-ins/Array/prototype/splice/S15.4.4.12_A1.2_T1.js",
code: `const input = [0, 1]; const removed = input.splice(-2, -1); return [input, removed]`,
expected: [[0, 1], []],
},
{
path: "test/built-ins/Array/prototype/splice/called_with_one_argument.js",
code: `const input = ["first", "second", "third"]; const removed = input.splice(1); return [input, removed]`,
expected: [["first"], ["second", "third"]],
},
{
path: "test/built-ins/Array/prototype/fill/fill-values-custom-start-and-end.js",
code: `const input = [0, 0, 0, 0, 0]; input.fill(8, -3, 4); const sparse = []; sparse[4] = 0; sparse.fill(8, 1, 3); return [[0, 0, 0].fill(8, 1, 2), input, [0, 0, 0, 0, 0].fill(8, -2, -1), [0, 0, 0, 0, 0].fill(8, -1, -3), [0 in sparse, sparse[1], sparse[2], 3 in sparse, sparse[4]]]`,
expected: [
[0, 8, 0],
[0, 0, 8, 8, 0],
[0, 0, 0, 8, 0],
[0, 0, 0, 0, 0],
[false, 8, 8, false, 0],
],
},
{
path: "test/built-ins/Array/prototype/fill/return-this.js",
code: `const input = []; return input.fill(1) === input`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/fill/fill-values.js",
code: `const omitted = [0, 0].fill(); return [[].fill(8), omitted.map((value) => value === undefined), [0, 0, 0].fill(8)]`,
expected: [[], [true, true], [8, 8, 8]],
},
{
path: "test/built-ins/Array/prototype/copyWithin/non-negative-target-start-and-end.js",
code: `return [[0, 1, 2, 3].copyWithin(0, 0, 0), [0, 1, 2, 3].copyWithin(0, 0, 2), [0, 1, 2, 3].copyWithin(0, 1, 2), [0, 1, 2, 3].copyWithin(1, 0, 2), [0, 1, 2, 3, 4, 5].copyWithin(1, 3, 5)]`,
expected: [
[0, 1, 2, 3],
[0, 1, 2, 3],
[1, 1, 2, 3],
[0, 0, 1, 3],
[0, 3, 4, 3, 4, 5],
],
},
{
path: "test/built-ins/Array/prototype/copyWithin/return-this.js",
code: `const input = [0, 1, 2, 3]; const result = input.copyWithin(1, 0, 2); return [input, result === input]`,
expected: [[0, 0, 1, 3], true],
},
{
path: "test/built-ins/Array/prototype/keys/iteration.js",
code: `return ["a", "b", "c"].keys()`,
expected: [0, 1, 2],
},
{
path: "test/built-ins/Array/prototype/values/iteration.js",
code: `return ["a", "b", "c"].values()`,
expected: ["a", "b", "c"],
},
{
path: "test/built-ins/Array/prototype/entries/iteration.js",
code: `return ["a", "b"].entries()`,
expected: [
[0, "a"],
[1, "b"],
],
},
{
path: "test/built-ins/Array/isArray/15.4.3.2-0-3.js",
code: `return [Array.isArray([]), Array.isArray([1]), Array.isArray(Array.of(1))]`,
expected: [true, true, true],
},
{
path: "test/built-ins/Array/isArray/15.4.3.2-0-4.js",
code: `return [Array.isArray(42), Array.isArray({}), Array.isArray(null), Array.isArray("array")]`,
expected: [false, false, false, false],
},
{
path: "test/built-ins/Array/from/from-array.js",
code: `const input = [0, "foo", undefined, Infinity]; const result = Array.from(input); return [result.length, result[0], result[1], result[2] === undefined, result[3] === Infinity, result !== input, result instanceof Array]`,
expected: [4, 0, "foo", true, true, true, true],
},
{
path: "test/built-ins/Array/from/from-string.js",
code: `return Array.from("Test")`,
expected: ["T", "e", "s", "t"],
},
{
path: "test/built-ins/Array/from/array-like-has-length-but-no-indexes-with-values.js",
code: `const result = Array.from({ length: 5 }); const mapped = result.map(() => 1); return [result.length, result.map((value) => value === undefined), mapped.length, mapped]`,
expected: [5, [true, true, true, true, true], 5, [1, 1, 1, 1, 1]],
},
{
path: "test/built-ins/Array/of/creates-a-new-array-from-arguments.js",
code: `const mixed = Array.of(undefined, false, null, undefined); return [Array.of("Mike", "Rick", "Leo"), mixed.length, mixed[0] === undefined, mixed[1], mixed[2], mixed[3] === undefined, Array.of()]`,
expected: [["Mike", "Rick", "Leo"], 4, true, false, null, true, []],
},
] as const
describe("Test262 Array core adaptations", () => {
for (const item of cases) {
test(item.path, async () => {
expect(await value(item.code)).toEqual(item.expected)
})
}
})

File diff suppressed because it is too large Load diff

View file

@ -177,13 +177,13 @@ describe("OpenAPI.fromSpec", () => {
const spec = await opencodeSpec()
const result = OpenAPI.fromSpec({ spec, baseUrl })
expect(result.skipped).toHaveLength(5)
expect(result.skipped).toHaveLength(4)
expect(result.skipped).toContainEqual({
method: "GET",
path: "/api/pty/{ptyID}/connect",
reason: "WebSocket operations are not supported",
})
expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(3)
expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(2)
expect(result.skipped).toContainEqual({
method: "GET",
path: "/api/fs/read/*",
@ -210,11 +210,11 @@ describe("OpenAPI.fromSpec", () => {
if (!Tool.isDefinition(instructionPut)) throw new Error("v2.session.instructions.entry.put was not generated")
expect(inputTypeScript(instructionPut)).toBe("{ sessionID: string; key: string; value: unknown }")
expect(toolAt(result.tools, "v2_session_instructions_entry_put_2")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connect")).toBeUndefined()
expect(Tool.isDefinition(toolAt(result.tools, "v2.pty.connect"))).toBe(false)
expect(toolAt(result.tools, "v2.session.log")).toBeUndefined()
expect(toolAt(result.tools, "v2.event.subscribe")).toBeUndefined()
expect(toolAt(result.tools, "v2.fs.read")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connectToken")).not.toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connect.token")).not.toBeUndefined()
})
test("preserves operation path sanitization and collision handling", () => {

View file

@ -42,11 +42,6 @@ describe("H2: string property access reads as undefined (not a throw)", () => {
test("unknown property on a number is undefined", async () => {
expect(await value(`return (5).foo ?? "n"`)).toBe("n")
})
test("supported string methods still work", async () => {
expect(await value(`return "AB".toLowerCase()`)).toBe("ab")
expect(await value(`return "hello".length`)).toBe(5)
})
})
describe("H3: array property access reads as undefined (not a throw)", () => {
@ -63,8 +58,7 @@ describe("H3: array property access reads as undefined (not a throw)", () => {
expect(await value(`return [1,2,3].toSpliced === undefined`)).toBe(true)
})
test("supported array methods and indexing still work", async () => {
expect(await value(`return [1,2,3].map(x => x + 1)`)).toEqual([2, 3, 4])
test("array indexing still works", async () => {
expect(await value(`return [1,2,3][9] === undefined`)).toBe(true)
expect(await value(`return [1,2,3][9]`)).toBeNull()
})
@ -202,9 +196,6 @@ describe("Error values and instanceof", () => {
"TypeError",
true,
])
expect(await value(`try { "a".normalize("NOPE") } catch (e) { return [e.name, e instanceof RangeError] }`)).toEqual(
["RangeError", true],
)
expect(await value(`try { "a".match("(") } catch (e) { return [e.name, e instanceof SyntaxError] }`)).toEqual([
"SyntaxError",
true,
@ -263,55 +254,18 @@ describe("Error values and instanceof", () => {
})
})
describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
test("sort and reverse mutate and return the receiver", async () => {
describe("CodeMode-specific array behavior", () => {
test("sort with a comparator mutates and returns the receiver", async () => {
expect(
await value(`
const sorted = [3, 1, 2]
const sortResult = sorted.sort((a, b) => a - b)
const reversed = [1, 2, 3]
const reverseResult = reversed.reverse()
return { sorted, sameSort: sorted === sortResult, reversed, sameReverse: reversed === reverseResult }
const input = [3, 1, 2]
const result = input.sort((a, b) => a - b)
return { input, same: input === result }
`),
).toEqual({ sorted: [1, 2, 3], sameSort: true, reversed: [3, 2, 1], sameReverse: true })
).toEqual({ input: [1, 2, 3], same: true })
})
test("array callbacks receive the receiver and observe later mutations", async () => {
expect(
await value(`
const values = [1, 2, 3]
const seen = values.map((value, index, receiver) => {
if (index === 0) values[1] = 9
return [value, receiver === values]
})
return seen
`),
).toEqual([
[1, true],
[9, true],
[3, true],
])
expect(
await value(`
const values = [1, 2, 3]
const seen = []
values.forEach((value, index) => {
seen.push(value)
if (index === 0) values.pop()
})
return seen
`),
).toEqual([1, 2])
})
test("splice removes in place and returns the removed elements", async () => {
expect(await value(`const a = [1,2,3,4]; const removed = a.splice(1, 2); return { removed, a }`)).toEqual({
removed: [2, 3],
a: [1, 4],
})
})
test("splice inserts new elements at the cut", async () => {
test("splice can replace and insert elements", async () => {
expect(await value(`const a = ["a","d"]; a.splice(1, 0, "b", "c"); return a`)).toEqual(["a", "b", "c", "d"])
expect(await value(`const a = [1,2,3]; const removed = a.splice(1, 1, "x"); return { removed, a }`)).toEqual({
removed: [2],
@ -319,32 +273,12 @@ describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
})
})
test("splice with one argument removes to the end; negative start counts back", async () => {
expect(await value(`const a = [1,2,3]; const removed = a.splice(1); return { removed, a }`)).toEqual({
removed: [2, 3],
a: [1],
})
expect(await value(`const a = [1,2,3]; const removed = a.splice(-1); return { removed, a }`)).toEqual({
removed: [3],
a: [1, 2],
})
})
test("splice rejects inserting a container into itself", async () => {
const err = await error(`const a = [1]; a.splice(0, 0, [a]); return a`)
expect(err.kind).toBe("InvalidDataValue")
expect(err.message).toContain("circular")
})
test("fill overwrites a range and returns the mutated array", async () => {
expect(await value(`const a = [1,2,3,4]; return a.fill(0, 1, 3)`)).toEqual([1, 0, 0, 4])
expect(await value(`return [1,2,3].fill("z")`)).toEqual(["z", "z", "z"])
})
test("copyWithin copies a range in place", async () => {
expect(await value(`return [1,2,3,4,5].copyWithin(0, 3)`)).toEqual([4, 5, 3, 4, 5])
})
test("keys/values/entries return arrays usable with for...of and spread", async () => {
expect(await value(`return [...["x","y","z"].keys()]`)).toEqual([0, 1, 2])
expect(await value(`return ["x","y"].values()`)).toEqual(["x", "y"])
@ -359,16 +293,9 @@ describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
})
})
describe("string methods: localeCompare, normalize, trim aliases", () => {
describe("CodeMode-specific string behavior", () => {
test("localeCompare orders strings for sorting", async () => {
expect(await value(`return ["b","a","c"].sort((x, y) => x.localeCompare(y))`)).toEqual(["a", "b", "c"])
expect(await value(`return "a".localeCompare("a")`)).toBe(0)
})
test("normalize applies unicode normalization forms", async () => {
expect(await value(`return "\\u0065\\u0301".normalize("NFC").length`)).toBe(1)
expect(await value(`return "\\u00e9".normalize("NFD").length`)).toBe(2)
expect(await value(`return "x".normalize() === "x"`)).toBe(true)
})
test("an invalid normalize form is a clear catchable error", async () => {
@ -453,11 +380,6 @@ describe("H5: builtin coercion functions work as array callbacks", () => {
expect(await value(`return [1, 2, 3].map(String)`)).toEqual(["1", "2", "3"])
})
test("arrow callbacks still work (no regression)", async () => {
expect(await value(`return [1, 2, 3, 4].filter(x => x % 2 === 0)`)).toEqual([2, 4])
expect(await value(`return [1, 2, 3].reduce((a, b) => a + b, 0)`)).toBe(6)
})
test("a non-callable callback is still rejected", async () => {
const err = await error(`return [1,2,3].map(42)`)
expect(err.message).toContain("callback")

View file

@ -154,9 +154,7 @@ describe("RegExp", () => {
).toEqual(["1", "22"])
})
test("string match: non-global carries index, global lists all matches", async () => {
expect(await value(`const m = "a1b22".match(/\\d+/); return [m[0], m.index]`)).toEqual(["1", 1])
expect(await value(`return "a1b22".match(/\\d+/g)`)).toEqual(["1", "22"])
test("an unmatched string pattern returns null", async () => {
expect(await value(`return "abc".match(/\\d/)`)).toBeNull()
})
@ -164,13 +162,6 @@ describe("RegExp", () => {
expect(await value(`return "a1b22".matchAll(/(\\d+)/g).map((m) => m[1])`)).toEqual(["1", "22"])
})
test("replace and replaceAll with patterns and $1 substitution", async () => {
expect(await value(`return "a1b2".replace(/\\d/, "#")`)).toBe("a#b2")
expect(await value(`return "a1b2".replace(/\\d/g, "#")`)).toBe("a#b#")
expect(await value(`return "a1b2".replaceAll(/\\d/g, "#")`)).toBe("a#b#")
expect(await value(`return "hi bob".replace(/b(o)b/, "[$1]")`)).toBe("hi [o]")
})
test("function replacers receive captures, offsets, input, and named groups", async () => {
expect(
await value(`
@ -236,12 +227,6 @@ describe("RegExp", () => {
expect(await value(`try { "a".replaceAll(/a/, "b"); return "no" } catch { return "caught" }`)).toBe("caught")
})
test("split and search accept patterns", async () => {
expect(await value(`return "a1b22c".split(/\\d+/)`)).toEqual(["a", "b", "c"])
expect(await value(`return "ab42".search(/\\d/)`)).toBe(2)
expect(await value(`return "ab".search(/\\d/)`)).toBe(-1)
})
test("new RegExp constructs from strings; invalid patterns are catchable", async () => {
expect(await value(`return new RegExp("a+", "i").test("AAA")`)).toBe(true)
expect(await value(`try { new RegExp("("); return "no" } catch { return "caught" }`)).toBe("caught")
@ -650,9 +635,7 @@ describe("stdlib integration", () => {
true,
)
expect(
await value(
`try { Object.fromEntries(new Map([["fn", Math.max]])); return false } catch { return true }`,
),
await value(`try { Object.fromEntries(new Map([["fn", Math.max]])); return false } catch { return true }`),
).toBe(true)
})

View file

@ -0,0 +1,580 @@
/*
* Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
* - test/built-ins/String/prototype/toLowerCase/S15.5.4.16_A2_T1.js
* - test/built-ins/String/prototype/toLowerCase/special_casing.js
* - test/built-ins/String/prototype/toLowerCase/special_casing_conditional.js
* - test/built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js
* - test/built-ins/String/prototype/toLowerCase/supplementary_plane.js
* - test/built-ins/String/prototype/toUpperCase/S15.5.4.18_A2_T1.js
* - test/built-ins/String/prototype/toUpperCase/special_casing.js
* - test/built-ins/String/prototype/toUpperCase/supplementary_plane.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-1.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-2.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-3.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-4.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-5.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-6.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-7.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-8.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-9.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-10.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-11.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-12.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-13.js
* - test/built-ins/String/prototype/trim/15.5.4.20-3-14.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-1.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-2.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-3.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-4.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-5.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-6.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-8.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-10.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-11.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-12.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-13.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-14.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-16.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-18.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-19.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-20.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-21.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-22.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-24.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-27.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-28.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-29.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-30.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-32.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-34.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-35.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-36.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-37.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-38.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-39.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-40.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-41.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-42.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-43.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-44.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-45.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-46.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-47.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-48.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-49.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-50.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-51.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-52.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-53.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-54.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-55.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-56.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-57.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-58.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-59.js
* - test/built-ins/String/prototype/trim/15.5.4.20-4-60.js
* - test/built-ins/String/prototype/trim/u180e.js
* - test/built-ins/String/prototype/trimStart/this-value-whitespace.js
* - test/built-ins/String/prototype/trimStart/this-value-line-terminator.js
* - test/built-ins/String/prototype/trimEnd/this-value-whitespace.js
* - test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js
* - test/built-ins/String/prototype/repeat/repeat-string-n-times.js
* - test/built-ins/String/prototype/repeat/empty-string-returns-empty.js
* - test/built-ins/String/prototype/repeat/count-is-zero-returns-empty-string.js
* - test/built-ins/String/prototype/repeat/count-coerced-to-zero-returns-empty-string.js
* - test/built-ins/String/prototype/padStart/fill-string-empty.js
* - test/built-ins/String/prototype/padStart/normal-operation.js
* - test/built-ins/String/prototype/padStart/fill-string-omitted.js
* - test/built-ins/String/prototype/padStart/max-length-not-greater-than-string.js
* - test/built-ins/String/prototype/padEnd/fill-string-empty.js
* - test/built-ins/String/prototype/padEnd/normal-operation.js
* - test/built-ins/String/prototype/padEnd/fill-string-omitted.js
* - test/built-ins/String/prototype/padEnd/max-length-not-greater-than-string.js
* - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T4.js
* - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T7.js
* - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T8.js
* - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T1.js
* - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T2.js
* - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T3.js
* - test/built-ins/String/prototype/charAt/S9.4_A1.js
* - test/built-ins/String/prototype/charAt/S9.4_A2.js
* - test/built-ins/String/prototype/charAt/pos-rounding.js
* - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T4.js
* - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T7.js
* - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T8.js
* - test/built-ins/String/prototype/charCodeAt/pos-rounding.js
* - test/built-ins/String/prototype/codePointAt/return-single-code-unit.js
* - test/built-ins/String/prototype/codePointAt/return-first-code-unit.js
* - test/built-ins/String/prototype/codePointAt/return-utf16-decode.js
* - test/built-ins/String/prototype/codePointAt/return-code-unit-coerced-position.js
* - test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-less-than-zero.js
* - test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-equal-or-more-than-size.js
* - test/built-ins/String/prototype/at/returns-code-unit.js
* - test/built-ins/String/prototype/at/returns-item.js
* - test/built-ins/String/prototype/at/returns-item-relative-index.js
* - test/built-ins/String/prototype/at/returns-undefined-for-out-of-range-index.js
* - test/built-ins/String/prototype/at/index-non-numeric-argument-tointeger.js
* - test/built-ins/String/prototype/concat/S15.5.4.6_A1_T4.js
* - test/built-ins/String/prototype/toString/string-primitive.js
* - test/built-ins/String/prototype/normalize/return-normalized-string.js
* - test/built-ins/String/prototype/normalize/return-normalized-string-using-default-parameter.js
* - test/built-ins/String/prototype/normalize/form-is-not-valid-throws.js
* - test/built-ins/String/prototype/localeCompare/15.5.4.9_CE.js
* - test/built-ins/String/fromCharCode/S15.5.3.2_A2.js
* - test/built-ins/String/fromCharCode/S15.5.3.2_A3_T1.js
* - test/built-ins/String/fromCharCode/S9.7_A1.js
* - test/built-ins/String/fromCharCode/S9.7_A2.1.js
* - test/built-ins/String/fromCharCode/S9.7_A2.2.js
* - test/built-ins/String/fromCharCode/S9.7_A3.2_T1.js
* - test/built-ins/String/fromCodePoint/arguments-is-empty.js
* - test/built-ins/String/fromCodePoint/return-string-value.js
* - test/built-ins/String/fromCodePoint/argument-is-not-integer.js
* - test/built-ins/String/fromCodePoint/number-is-out-of-range.js
*
* Copyright 2009 the Sputnik authors. All rights reserved.
* Copyright (C) 2009 the Sputnik authors. All rights reserved.
* Copyright (c) 2012 Ecma International. All rights reserved.
* Copyright 2012 Norbert Lindenberg. All rights reserved.
* Copyright 2012 Mozilla Corporation. All rights reserved.
* Copyright 2013 Microsoft Corporation. All rights reserved.
* Copyright (C) 2015 the V8 project authors. All rights reserved.
* Copyright (C) 2015 André Bargull. All rights reserved.
* Copyright (C) 2016 the V8 project authors. All rights reserved.
* Copyright (C) 2016 André Bargull. All rights reserved.
* Copyright (C) 2016 Jordan Harband. All rights reserved.
* Copyright (C) 2016 Mathias Bynens. All rights reserved.
* Copyright (c) 2017 Valerie Young. All rights reserved.
* Copyright (C) 2017 Valerie Young. All rights reserved.
* Copyright (C) 2020 Rick Waldron. All rights reserved.
* Copyright (C) 2022 Richard Gibson. All rights reserved.
* Test262 portions are governed by the BSD license in LICENSE.test262.
*/
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { CodeMode } from "../src/index.js"
type Argument = string | number | undefined
type Outcome = "undefined" | "length" | "RangeError"
type Assertion = {
label: string
input?: string
args?: ReadonlyArray<Argument>
expected?: string | number
outcome?: Outcome
}
type Vector = {
path: string
method: string
static?: boolean
assertions: ReadonlyArray<Assertion>
}
const value = async (code: string) => {
const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
return result.value
}
const literal = (input: Argument) => {
if (input === undefined) return "undefined"
if (typeof input === "string") return JSON.stringify(input)
if (Number.isNaN(input)) return "NaN"
if (input === Infinity) return "Infinity"
if (input === -Infinity) return "-Infinity"
if (Object.is(input, -0)) return "-0"
return JSON.stringify(input)
}
const vectors: Array<Vector> = []
const add = (path: string, method: string, assertions: ReadonlyArray<Assertion>, staticMethod = false) => {
vectors.push({ path, method, assertions, static: staticMethod })
}
const assertion = (label: string, input: string, expected: string | number, args: ReadonlyArray<Argument> = []) => ({
label,
input,
args,
expected,
})
add("test/built-ins/String/prototype/toLowerCase/S15.5.4.16_A2_T1.js", "toLowerCase", [
assertion("#1 direct value", "Hello, WoRlD!", "hello, world!"),
assertion("#2 String value", "Hello, WoRlD!", "hello, world!"),
])
add("test/built-ins/String/prototype/toLowerCase/special_casing.js", "toLowerCase", [
assertion(
"103 SpecialCasing mappings",
"\u00DF\u0130\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F88\u1F89\u1F8A\u1F8B\u1F8C\u1F8D\u1F8E\u1F8F\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F98\u1F99\u1F9A\u1F9B\u1F9C\u1F9D\u1F9E\u1F9F\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA8\u1FA9\u1FAA\u1FAB\u1FAC\u1FAD\u1FAE\u1FAF\u1FB3\u1FBC\u1FC3\u1FCC\u1FF3\u1FFC\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7",
"\u00DF\u0069\u0307\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FB3\u1FB3\u1FC3\u1FC3\u1FF3\u1FF3\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7",
),
])
add("test/built-ins/String/prototype/toLowerCase/special_casing_conditional.js", "toLowerCase", [
assertion("single sigma", "\u03A3", "\u03C3"),
assertion("preceded by cased", "A\u03A3", "a\u03C2"),
assertion("preceded by supplementary cased", "\uD835\uDCA2\u03A3", "\uD835\uDCA2\u03C2"),
assertion("preceded by full stop", "A.\u03A3", "a.\u03C2"),
assertion("preceded by soft hyphen", "A\u00AD\u03A3", "a\u00AD\u03C2"),
assertion("preceded by combining mark", "A\uD834\uDE42\u03A3", "a\uD834\uDE42\u03C2"),
assertion("preceded by uncased combining mark", "\u0345\u03A3", "\u0345\u03C3"),
assertion("preceded by cased and combining mark", "\u0391\u0345\u03A3", "\u03B1\u0345\u03C2"),
assertion("followed by cased", "A\u03A3B", "a\u03C3b"),
assertion("followed by supplementary cased", "A\u03A3\uD835\uDCA2", "a\u03C3\uD835\uDCA2"),
assertion("followed by full stop and cased", "A\u03A3.b", "a\u03C3.b"),
assertion("followed by soft hyphen and cased", "A\u03A3\u00ADB", "a\u03C3\u00ADb"),
assertion("followed by combining mark and cased", "A\u03A3\uD834\uDE42B", "a\u03C3\uD834\uDE42b"),
assertion("followed by uncased combining mark", "A\u03A3\u0345", "a\u03C2\u0345"),
assertion("followed by combining mark and cased Greek", "A\u03A3\u0345\u0391", "a\u03C3\u0345\u03B1"),
])
add("test/built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js", "toLowerCase", [
assertion("preceded by U+180E", "A\u180E\u03A3", "a\u180E\u03C2"),
assertion("preceded by U+180E and followed by cased", "A\u180E\u03A3B", "a\u180E\u03C3b"),
assertion("followed by U+180E", "A\u03A3\u180E", "a\u03C2\u180E"),
assertion("followed by U+180E and cased", "A\u03A3\u180EB", "a\u03C3\u180Eb"),
assertion("surrounded by U+180E", "A\u180E\u03A3\u180E", "a\u180E\u03C2\u180E"),
assertion("surrounded by U+180E and followed by cased", "A\u180E\u03A3\u180EB", "a\u180E\u03C3\u180Eb"),
])
add("test/built-ins/String/prototype/toLowerCase/supplementary_plane.js", "toLowerCase", [
assertion(
"40 Deseret mappings",
"\uD801\uDC00\uD801\uDC01\uD801\uDC02\uD801\uDC03\uD801\uDC04\uD801\uDC05\uD801\uDC06\uD801\uDC07\uD801\uDC08\uD801\uDC09\uD801\uDC0A\uD801\uDC0B\uD801\uDC0C\uD801\uDC0D\uD801\uDC0E\uD801\uDC0F\uD801\uDC10\uD801\uDC11\uD801\uDC12\uD801\uDC13\uD801\uDC14\uD801\uDC15\uD801\uDC16\uD801\uDC17\uD801\uDC18\uD801\uDC19\uD801\uDC1A\uD801\uDC1B\uD801\uDC1C\uD801\uDC1D\uD801\uDC1E\uD801\uDC1F\uD801\uDC20\uD801\uDC21\uD801\uDC22\uD801\uDC23\uD801\uDC24\uD801\uDC25\uD801\uDC26\uD801\uDC27",
"\uD801\uDC28\uD801\uDC29\uD801\uDC2A\uD801\uDC2B\uD801\uDC2C\uD801\uDC2D\uD801\uDC2E\uD801\uDC2F\uD801\uDC30\uD801\uDC31\uD801\uDC32\uD801\uDC33\uD801\uDC34\uD801\uDC35\uD801\uDC36\uD801\uDC37\uD801\uDC38\uD801\uDC39\uD801\uDC3A\uD801\uDC3B\uD801\uDC3C\uD801\uDC3D\uD801\uDC3E\uD801\uDC3F\uD801\uDC40\uD801\uDC41\uD801\uDC42\uD801\uDC43\uD801\uDC44\uD801\uDC45\uD801\uDC46\uD801\uDC47\uD801\uDC48\uD801\uDC49\uD801\uDC4A\uD801\uDC4B\uD801\uDC4C\uD801\uDC4D\uD801\uDC4E\uD801\uDC4F",
),
])
add("test/built-ins/String/prototype/toUpperCase/S15.5.4.18_A2_T1.js", "toUpperCase", [
assertion("#1 direct value", "Hello, WoRlD!", "HELLO, WORLD!"),
assertion("#2 String value", "Hello, WoRlD!", "HELLO, WORLD!"),
])
add("test/built-ins/String/prototype/toUpperCase/special_casing.js", "toUpperCase", [
assertion(
"103 SpecialCasing mappings",
"\u00DF\u0130\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F88\u1F89\u1F8A\u1F8B\u1F8C\u1F8D\u1F8E\u1F8F\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F98\u1F99\u1F9A\u1F9B\u1F9C\u1F9D\u1F9E\u1F9F\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA8\u1FA9\u1FAA\u1FAB\u1FAC\u1FAD\u1FAE\u1FAF\u1FB3\u1FBC\u1FC3\u1FCC\u1FF3\u1FFC\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7",
"\u0053\u0053\u0130\u0046\u0046\u0046\u0049\u0046\u004C\u0046\u0046\u0049\u0046\u0046\u004C\u0053\u0054\u0053\u0054\u0535\u0552\u0544\u0546\u0544\u0535\u0544\u053B\u054E\u0546\u0544\u053D\u02BC\u004E\u0399\u0308\u0301\u03A5\u0308\u0301\u004A\u030C\u0048\u0331\u0054\u0308\u0057\u030A\u0059\u030A\u0041\u02BE\u03A5\u0313\u03A5\u0313\u0300\u03A5\u0313\u0301\u03A5\u0313\u0342\u0391\u0342\u0397\u0342\u0399\u0308\u0300\u0399\u0308\u0301\u0399\u0342\u0399\u0308\u0342\u03A5\u0308\u0300\u03A5\u0308\u0301\u03A1\u0313\u03A5\u0342\u03A5\u0308\u0342\u03A9\u0342\u1F08\u0399\u1F09\u0399\u1F0A\u0399\u1F0B\u0399\u1F0C\u0399\u1F0D\u0399\u1F0E\u0399\u1F0F\u0399\u1F08\u0399\u1F09\u0399\u1F0A\u0399\u1F0B\u0399\u1F0C\u0399\u1F0D\u0399\u1F0E\u0399\u1F0F\u0399\u1F28\u0399\u1F29\u0399\u1F2A\u0399\u1F2B\u0399\u1F2C\u0399\u1F2D\u0399\u1F2E\u0399\u1F2F\u0399\u1F28\u0399\u1F29\u0399\u1F2A\u0399\u1F2B\u0399\u1F2C\u0399\u1F2D\u0399\u1F2E\u0399\u1F2F\u0399\u1F68\u0399\u1F69\u0399\u1F6A\u0399\u1F6B\u0399\u1F6C\u0399\u1F6D\u0399\u1F6E\u0399\u1F6F\u0399\u1F68\u0399\u1F69\u0399\u1F6A\u0399\u1F6B\u0399\u1F6C\u0399\u1F6D\u0399\u1F6E\u0399\u1F6F\u0399\u0391\u0399\u0391\u0399\u0397\u0399\u0397\u0399\u03A9\u0399\u03A9\u0399\u1FBA\u0399\u0386\u0399\u1FCA\u0399\u0389\u0399\u1FFA\u0399\u038F\u0399\u0391\u0342\u0399\u0397\u0342\u0399\u03A9\u0342\u0399",
),
])
add("test/built-ins/String/prototype/toUpperCase/supplementary_plane.js", "toUpperCase", [
assertion(
"40 Deseret mappings",
"\uD801\uDC28\uD801\uDC29\uD801\uDC2A\uD801\uDC2B\uD801\uDC2C\uD801\uDC2D\uD801\uDC2E\uD801\uDC2F\uD801\uDC30\uD801\uDC31\uD801\uDC32\uD801\uDC33\uD801\uDC34\uD801\uDC35\uD801\uDC36\uD801\uDC37\uD801\uDC38\uD801\uDC39\uD801\uDC3A\uD801\uDC3B\uD801\uDC3C\uD801\uDC3D\uD801\uDC3E\uD801\uDC3F\uD801\uDC40\uD801\uDC41\uD801\uDC42\uD801\uDC43\uD801\uDC44\uD801\uDC45\uD801\uDC46\uD801\uDC47\uD801\uDC48\uD801\uDC49\uD801\uDC4A\uD801\uDC4B\uD801\uDC4C\uD801\uDC4D\uD801\uDC4E\uD801\uDC4F",
"\uD801\uDC00\uD801\uDC01\uD801\uDC02\uD801\uDC03\uD801\uDC04\uD801\uDC05\uD801\uDC06\uD801\uDC07\uD801\uDC08\uD801\uDC09\uD801\uDC0A\uD801\uDC0B\uD801\uDC0C\uD801\uDC0D\uD801\uDC0E\uD801\uDC0F\uD801\uDC10\uD801\uDC11\uD801\uDC12\uD801\uDC13\uD801\uDC14\uD801\uDC15\uD801\uDC16\uD801\uDC17\uD801\uDC18\uD801\uDC19\uD801\uDC1A\uD801\uDC1B\uD801\uDC1C\uD801\uDC1D\uD801\uDC1E\uD801\uDC1F\uD801\uDC20\uD801\uDC21\uD801\uDC22\uD801\uDC23\uD801\uDC24\uD801\uDC25\uD801\uDC26\uD801\uDC27",
),
])
const whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
const lineTerminators = "\u000A\u000D\u2028\u2029"
const trim = (file: string, input: string, expected: string) =>
add(`test/built-ins/String/prototype/trim/${file}`, "trim", [assertion("upstream assertion", input, expected)])
trim("15.5.4.20-3-1.js", lineTerminators, "")
trim("15.5.4.20-3-2.js", whitespace, "")
trim("15.5.4.20-3-3.js", whitespace + lineTerminators, "")
trim("15.5.4.20-3-4.js", whitespace + lineTerminators + "abc", "abc")
trim("15.5.4.20-3-5.js", "abc" + whitespace + lineTerminators, "abc")
trim("15.5.4.20-3-6.js", whitespace + lineTerminators + "abc" + whitespace + lineTerminators, "abc")
trim("15.5.4.20-3-7.js", "ab" + whitespace + lineTerminators + "cd", "ab" + whitespace + lineTerminators + "cd")
trim("15.5.4.20-3-8.js", "\0\u0000", "\0\u0000")
trim("15.5.4.20-3-9.js", "\0", "\0")
trim("15.5.4.20-3-10.js", "\u0000", "\u0000")
trim("15.5.4.20-3-11.js", "\0\u0000abc", "\0\u0000abc")
trim("15.5.4.20-3-12.js", "abc\0\u0000", "abc\0\u0000")
trim("15.5.4.20-3-13.js", "\0\u0000abc\0\u0000", "\0\u0000abc\0\u0000")
trim("15.5.4.20-3-14.js", "a\0\u0000bc", "a\0\u0000bc")
trim("15.5.4.20-4-1.js", "\u0009a bc \u0009", "a bc")
trim("15.5.4.20-4-2.js", " \u0009abc \u0009", "abc")
trim("15.5.4.20-4-3.js", "\u0009abc", "abc")
trim("15.5.4.20-4-4.js", "\u000Babc", "abc")
trim("15.5.4.20-4-5.js", "\u000Cabc", "abc")
trim("15.5.4.20-4-6.js", "\u0020abc", "abc")
trim("15.5.4.20-4-8.js", "\u00A0abc", "abc")
trim("15.5.4.20-4-10.js", "\uFEFFabc", "abc")
trim("15.5.4.20-4-11.js", "abc\u0009", "abc")
trim("15.5.4.20-4-12.js", "abc\u000B", "abc")
trim("15.5.4.20-4-13.js", "abc\u000C", "abc")
trim("15.5.4.20-4-14.js", "abc\u0020", "abc")
trim("15.5.4.20-4-16.js", "abc\u00A0", "abc")
trim("15.5.4.20-4-18.js", "abc\uFEFF", "abc")
trim("15.5.4.20-4-19.js", "\u0009abc\u0009", "abc")
trim("15.5.4.20-4-20.js", "\u000Babc\u000B", "abc")
trim("15.5.4.20-4-21.js", "\u000Cabc\u000C", "abc")
trim("15.5.4.20-4-22.js", "\u0020abc\u0020", "abc")
trim("15.5.4.20-4-24.js", "\u00A0abc\u00A0", "abc")
trim("15.5.4.20-4-27.js", "\u0009\u0009", "")
trim("15.5.4.20-4-28.js", "\u000B\u000B", "")
trim("15.5.4.20-4-29.js", "\u000C\u000C", "")
trim("15.5.4.20-4-30.js", "\u0020\u0020", "")
trim("15.5.4.20-4-32.js", "\u00A0\u00A0", "")
trim("15.5.4.20-4-34.js", "\uFEFF\uFEFF", "")
trim("15.5.4.20-4-35.js", "ab\u0009c", "ab\u0009c")
trim("15.5.4.20-4-36.js", "ab\u000Bc", "ab\u000Bc")
trim("15.5.4.20-4-37.js", "ab\u000Cc", "ab\u000Cc")
trim("15.5.4.20-4-38.js", "ab\u0020c", "ab\u0020c")
trim("15.5.4.20-4-39.js", "ab\u0085c", "ab\u0085c")
trim("15.5.4.20-4-40.js", "ab\u00A0c", "ab\u00A0c")
trim("15.5.4.20-4-41.js", "ab\u200Bc", "ab\u200Bc")
trim("15.5.4.20-4-42.js", "ab\uFEFFc", "ab\uFEFFc")
trim("15.5.4.20-4-43.js", "\u000Aabc", "abc")
trim("15.5.4.20-4-44.js", "\u000Dabc", "abc")
trim("15.5.4.20-4-45.js", "\u2028abc", "abc")
trim("15.5.4.20-4-46.js", "\u2029abc", "abc")
trim("15.5.4.20-4-47.js", "abc\u000A", "abc")
trim("15.5.4.20-4-48.js", "abc\u000D", "abc")
trim("15.5.4.20-4-49.js", "abc\u2028", "abc")
trim("15.5.4.20-4-50.js", "abc\u2029", "abc")
trim("15.5.4.20-4-51.js", "\u000Aabc\u000A", "abc")
trim("15.5.4.20-4-52.js", "\u000Dabc\u000D", "abc")
trim("15.5.4.20-4-53.js", "\u2028abc\u2028", "abc")
trim("15.5.4.20-4-54.js", "\u2029abc\u2029", "abc")
trim("15.5.4.20-4-55.js", "\u000A\u000A", "")
trim("15.5.4.20-4-56.js", "\u000D\u000D", "")
trim("15.5.4.20-4-57.js", "\u2028\u2028", "")
trim("15.5.4.20-4-58.js", "\u2029\u2029", "")
trim("15.5.4.20-4-59.js", "\u2029 abc", "abc")
trim("15.5.4.20-4-60.js", " ", "")
add("test/built-ins/String/prototype/trim/u180e.js", "trim", [
assertion("trailing U+180E", "_\u180E", "_\u180E"),
assertion("only U+180E", "\u180E", "\u180E"),
assertion("leading U+180E", "\u180E_", "\u180E_"),
])
const directionalWhitespace = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF"
add("test/built-ins/String/prototype/trimStart/this-value-whitespace.js", "trimStart", [
assertion("all whitespace", directionalWhitespace + "a" + directionalWhitespace + "b" + directionalWhitespace, "a" + directionalWhitespace + "b" + directionalWhitespace),
])
add("test/built-ins/String/prototype/trimStart/this-value-line-terminator.js", "trimStart", [
assertion("all line terminators", lineTerminators + "a" + lineTerminators + "b" + lineTerminators, "a" + lineTerminators + "b" + lineTerminators),
])
add("test/built-ins/String/prototype/trimEnd/this-value-whitespace.js", "trimEnd", [
assertion("all whitespace", directionalWhitespace + "a" + directionalWhitespace + "b" + directionalWhitespace, directionalWhitespace + "a" + directionalWhitespace + "b"),
])
add("test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js", "trimEnd", [
assertion("all line terminators", lineTerminators + "a" + lineTerminators + "b" + lineTerminators, lineTerminators + "a" + lineTerminators + "b"),
])
add("test/built-ins/String/prototype/repeat/repeat-string-n-times.js", "repeat", [
assertion("repeat once", "abc", "abc", [1]),
assertion("repeat three times", "abc", "abcabcabc", [3]),
{ label: "repeat 10000 times length", input: ".", args: [10000], expected: 10000, outcome: "length" },
])
add("test/built-ins/String/prototype/repeat/empty-string-returns-empty.js", "repeat", [
assertion("count 1", "", "", [1]),
assertion("count 3", "", "", [3]),
assertion("maximum 32-bit count", "", "", [0xffffffff]),
])
add("test/built-ins/String/prototype/repeat/count-is-zero-returns-empty-string.js", "repeat", [
assertion("zero", "foo", "", [0]),
])
add("test/built-ins/String/prototype/repeat/count-coerced-to-zero-returns-empty-string.js", "repeat", [
assertion("fraction truncates to zero", "abc", "", [0.9]),
])
add("test/built-ins/String/prototype/padStart/fill-string-empty.js", "padStart", [assertion("empty fill", "abc", "abc", [5, ""])])
add("test/built-ins/String/prototype/padStart/normal-operation.js", "padStart", [
assertion("truncated multi-character fill", "abc", "defdabc", [7, "def"]),
assertion("single-character fill", "abc", "**abc", [5, "*"]),
assertion("truncated surrogate pair", "abc", "\uD83D\uDCA9\uD83Dabc", [6, "\uD83D\uDCA9"]),
])
add("test/built-ins/String/prototype/padStart/fill-string-omitted.js", "padStart", [
assertion("omitted fill", "abc", " abc", [5]),
assertion("undefined fill", "abc", " abc", [5, undefined]),
])
add("test/built-ins/String/prototype/padStart/max-length-not-greater-than-string.js", "padStart", [
assertion("NaN", "abc", "abc", [NaN, "def"]),
assertion("negative infinity", "abc", "abc", [-Infinity, "def"]),
assertion("zero", "abc", "abc", [0, "def"]),
assertion("negative one", "abc", "abc", [-1, "def"]),
assertion("equal length", "abc", "abc", [3, "def"]),
assertion("fraction truncates", "abc", "abc", [3.9999, "def"]),
])
add("test/built-ins/String/prototype/padEnd/fill-string-empty.js", "padEnd", [assertion("empty fill", "abc", "abc", [5, ""])])
add("test/built-ins/String/prototype/padEnd/normal-operation.js", "padEnd", [
assertion("truncated multi-character fill", "abc", "abcdefd", [7, "def"]),
assertion("single-character fill", "abc", "abc**", [5, "*"]),
assertion("truncated surrogate pair", "abc", "abc\uD83D\uDCA9\uD83D", [6, "\uD83D\uDCA9"]),
])
add("test/built-ins/String/prototype/padEnd/fill-string-omitted.js", "padEnd", [
assertion("omitted fill", "abc", "abc ", [5]),
assertion("undefined fill", "abc", "abc ", [5, undefined]),
])
add("test/built-ins/String/prototype/padEnd/max-length-not-greater-than-string.js", "padEnd", [
assertion("NaN", "abc", "abc", [NaN, "def"]),
assertion("negative infinity", "abc", "abc", [-Infinity, "def"]),
assertion("zero", "abc", "abc", [0, "def"]),
assertion("negative one", "abc", "abc", [-1, "def"]),
assertion("equal length", "abc", "abc", [3, "def"]),
assertion("fraction truncates", "abc", "abc", [3.9999, "def"]),
])
add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T4.js", "charAt", [assertion("omitted position", "lego", "l")])
add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T7.js", "charAt", [assertion("undefined position", "lego", "l", [undefined])])
add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T8.js", "charAt", [assertion("undefined position", "42", "4", [undefined])])
add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T1.js", "charAt", ["A", "B", "C", "A", "B", "C"].map((expected, position) => assertion(`position ${position}`, "ABCABC", expected, [position])))
add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T2.js", "charAt", [-2, -1].map((position) => assertion(`position ${position}`, "ABCABC", "", [position])))
add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T3.js", "charAt", [6, 7].map((position) => assertion(`position ${position}`, "ABCABC", "", [position])))
add("test/built-ins/String/prototype/charAt/S9.4_A1.js", "charAt", [assertion("NaN position", "abc", "a", [NaN])])
add("test/built-ins/String/prototype/charAt/S9.4_A2.js", "charAt", [
assertion("positive zero", "abc", "a", [0]),
assertion("negative zero", "abc", "a", [-0]),
])
add("test/built-ins/String/prototype/charAt/pos-rounding.js", "charAt", [
assertion("-0.99999", "abc", "a", [-0.99999]),
assertion("-0.00001", "abc", "a", [-0.00001]),
assertion("0.00001", "abc", "a", [0.00001]),
assertion("0.99999", "abc", "a", [0.99999]),
assertion("1.00001", "abc", "b", [1.00001]),
assertion("1.99999", "abc", "b", [1.99999]),
])
add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T4.js", "charCodeAt", [assertion("omitted position", "smart", 0x73)])
add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T7.js", "charCodeAt", [assertion("undefined position", "lego", 0x6c, [undefined])])
add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T8.js", "charCodeAt", [assertion("undefined position", "42", 0x34, [undefined])])
add("test/built-ins/String/prototype/charCodeAt/pos-rounding.js", "charCodeAt", [
assertion("-0.99999", "abc", 0x61, [-0.99999]),
assertion("-0.00001", "abc", 0x61, [-0.00001]),
assertion("0.00001", "abc", 0x61, [0.00001]),
assertion("0.99999", "abc", 0x61, [0.99999]),
assertion("1.00001", "abc", 0x62, [1.00001]),
assertion("1.99999", "abc", 0x62, [1.99999]),
])
add("test/built-ins/String/prototype/codePointAt/return-single-code-unit.js", "codePointAt", [
assertion("a", "abc", 97, [0]), assertion("b", "abc", 98, [1]), assertion("c", "abc", 99, [2]),
assertion("ordinary BMP", "\uAAAA\uBBBB", 0xaaaa, [0]), assertion("before high-surrogate range", "\uD7FF\uAAAA", 0xd7ff, [0]),
assertion("low surrogate", "\uDC00\uAAAA", 0xdc00, [0]), assertion("trailing D800", "123\uD800", 0xd800, [3]),
assertion("trailing DAAA", "123\uDAAA", 0xdaaa, [3]), assertion("trailing DBFF", "123\uDBFF", 0xdbff, [3]),
])
add("test/built-ins/String/prototype/codePointAt/return-first-code-unit.js", "codePointAt", [
assertion("D800 before DBFF", "\uD800\uDBFF", 0xd800, [0]), assertion("D800 before E000", "\uD800\uE000", 0xd800, [0]),
assertion("DAAA before DBFF", "\uDAAA\uDBFF", 0xdaaa, [0]), assertion("DAAA before E000", "\uDAAA\uE000", 0xdaaa, [0]),
assertion("DBFF before DBFF", "\uDBFF\uDBFF", 0xdbff, [0]), assertion("DBFF before E000", "\uDBFF\uE000", 0xdbff, [0]),
assertion("D800 before NUL", "\uD800\u0000", 0xd800, [0]), assertion("D800 before FFFF", "\uD800\uFFFF", 0xd800, [0]),
assertion("DAAA before NUL", "\uDAAA\u0000", 0xdaaa, [0]), assertion("DAAA before FFFF", "\uDAAA\uFFFF", 0xdaaa, [0]),
assertion("DBFF before FFFF", "\uDBFF\uFFFF", 0xdbff, [0]),
])
add("test/built-ins/String/prototype/codePointAt/return-utf16-decode.js", "codePointAt", [
assertion("U+10000", "\uD800\uDC00", 65536, [0]), assertion("U+101D0", "\uD800\uDDD0", 66000, [0]),
assertion("U+103FF", "\uD800\uDFFF", 66559, [0]), assertion("U+BA800", "\uDAAA\uDC00", 763904, [0]),
assertion("U+BA9D0", "\uDAAA\uDDD0", 764368, [0]), assertion("U+BABFF", "\uDAAA\uDFFF", 764927, [0]),
assertion("U+10FC00", "\uDBFF\uDC00", 1113088, [0]), assertion("U+10FDD0", "\uDBFF\uDDD0", 1113552, [0]),
assertion("U+10FFFF", "\uDBFF\uDFFF", 1114111, [0]),
])
add("test/built-ins/String/prototype/codePointAt/return-code-unit-coerced-position.js", "codePointAt", [
assertion("NaN", "\uD800\uDC00", 65536, [NaN]), assertion("undefined", "\uD800\uDC00", 65536, [undefined]),
])
add("test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-less-than-zero.js", "codePointAt", [
{ label: "negative one", input: "abc", args: [-1], outcome: "undefined" },
{ label: "negative infinity", input: "abc", args: [-Infinity], outcome: "undefined" },
])
add("test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-equal-or-more-than-size.js", "codePointAt", [
{ label: "equal to size", input: "abc", args: [3], outcome: "undefined" },
{ label: "greater than size", input: "abc", args: [4], outcome: "undefined" },
{ label: "positive infinity", input: "abc", args: [Infinity], outcome: "undefined" },
])
add("test/built-ins/String/prototype/at/returns-code-unit.js", "at", [
assertion("position 0", "12\uD80034", "1", [0]), assertion("position 1", "12\uD80034", "2", [1]),
assertion("unpaired surrogate", "12\uD80034", "\uD800", [2]), assertion("position 3", "12\uD80034", "3", [3]),
assertion("position 4", "12\uD80034", "4", [4]),
])
add("test/built-ins/String/prototype/at/returns-item.js", "at", ["1", "2", "3", "4", "5"].map((expected, position) => assertion(`position ${position}`, "12345", expected, [position])))
add("test/built-ins/String/prototype/at/returns-item-relative-index.js", "at", [
assertion("zero", "12345", "1", [0]), assertion("negative one", "12345", "5", [-1]),
assertion("negative three", "12345", "3", [-3]), assertion("negative four", "12345", "2", [-4]),
])
add("test/built-ins/String/prototype/at/returns-undefined-for-out-of-range-index.js", "at", [-2, 0, 1].map((position) => ({ label: `position ${position}`, input: "", args: [position], outcome: "undefined" })))
add("test/built-ins/String/prototype/at/index-non-numeric-argument-tointeger.js", "at", [assertion("undefined", "01", "0", [undefined])])
add("test/built-ins/String/prototype/concat/S15.5.4.6_A1_T4.js", "concat", [assertion("no arguments", "lego", "lego")])
add("test/built-ins/String/prototype/toString/string-primitive.js", "toString", [
assertion("empty string", "", ""), assertion("non-empty string", "str", "str"),
])
add("test/built-ins/String/prototype/normalize/return-normalized-string.js", "normalize", [
assertion("NFC short", "\u1E9B\u0323", "\u1E9B\u0323", ["NFC"]),
assertion("NFD short", "\u1E9B\u0323", "\u017F\u0323\u0307", ["NFD"]),
assertion("NFKC short", "\u1E9B\u0323", "\u1E69", ["NFKC"]),
assertion("NFKD short", "\u1E9B\u0323", "\u0073\u0323\u0307", ["NFKD"]),
assertion("NFC long", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFC"]),
assertion("NFD long", "\u00C5\u2ADC\u0958\u2126\u0344", "A\u030A\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFD"]),
assertion("NFKC long", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFKC"]),
assertion("NFKD long", "\u00C5\u2ADC\u0958\u2126\u0344", "A\u030A\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFKD"]),
])
add("test/built-ins/String/prototype/normalize/return-normalized-string-using-default-parameter.js", "normalize", [
assertion("omitted", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301"),
assertion("undefined", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", [undefined]),
])
add("test/built-ins/String/prototype/normalize/form-is-not-valid-throws.js", "normalize", [
{ label: "bar", input: "foo", args: ["bar"], outcome: "RangeError" },
{ label: "NFC1", input: "foo", args: ["NFC1"], outcome: "RangeError" },
])
add("test/built-ins/String/prototype/localeCompare/15.5.4.9_CE.js", "localeCompare", [
assertion("D70", "o\u0308", 0, ["ö"]), assertion("reordered diaeresis", "ä\u0323", 0, ["a\u0323\u0308"]),
assertion("reordered marks", "a\u0308\u0323", 0, ["a\u0323\u0308"]), assertion("precomposed dot below", "ạ\u0308", 0, ["a\u0323\u0308"]),
assertion("breve after diaeresis", "ä\u0306", 0, ["a\u0308\u0306"]), assertion("diaeresis after breve", "ă\u0308", 0, ["a\u0306\u0308"]),
assertion("Hangul", "\u1111\u1171\u11B6", 0, ["퓛"]), assertion("angstrom compatibility", "Å", 0, ["Å"]),
assertion("angstrom decomposed", "Å", 0, ["A\u030A"]), assertion("reordered horn and dot", "x\u031B\u0323", 0, ["x\u0323\u031B"]),
assertion("Vietnamese precomposed 1", "ự", 0, ["ụ\u031B"]), assertion("Vietnamese decomposed", "ự", 0, ["u\u031B\u0323"]),
assertion("Vietnamese precomposed 2", "ự", 0, ["ư\u0323"]), assertion("Vietnamese reordered", "ự", 0, ["u\u0323\u031B"]),
assertion("cedilla", "Ç", 0, ["C\u0327"]), assertion("q reordered", "q\u0307\u0323", 0, ["q\u0323\u0307"]),
assertion("Hangul syllable", "가", 0, ["\u1100\u1161"]), assertion("ohm", "Ω", 0, ["Ω"]),
assertion("angstrom", "Å", 0, ["A\u030A"]), assertion("circumflex", "ô", 0, ["o\u0302"]),
assertion("s with marks", "ṩ", 0, ["s\u0323\u0307"]), assertion("d composed plus dot", "ḋ\u0323", 0, ["d\u0323\u0307"]),
assertion("d two precompositions", "ḋ\u0323", 0, ["ḍ\u0307"]),
])
add("test/built-ins/String/fromCharCode/S15.5.3.2_A2.js", "fromCharCode", [{ label: "no arguments", expected: "" }], true)
add("test/built-ins/String/fromCharCode/S15.5.3.2_A3_T1.js", "fromCharCode", [{ label: "ABBA", args: [65, 66, 66, 65], expected: "ABBA" }], true)
add("test/built-ins/String/fromCharCode/S9.7_A1.js", "fromCharCode", [
{ label: "NaN", args: [NaN], expected: 0 }, { label: "zero", args: [0], expected: 0 }, { label: "negative zero", args: [-0], expected: 0 },
{ label: "positive infinity", args: [Infinity], expected: 0 }, { label: "negative infinity", args: [-Infinity], expected: 0 },
], true)
add("test/built-ins/String/fromCharCode/S9.7_A2.1.js", "fromCharCode", [
[0, 0], [1, 1], [-1, 65535], [65535, 65535], [65534, 65534], [65536, 0], [4294967295, 65535], [4294967294, 65534], [4294967296, 0],
].map(([input, expected]) => ({ label: String(input), args: [input!], expected })), true)
add("test/built-ins/String/fromCharCode/S9.7_A2.2.js", "fromCharCode", [
[-32767, 32769], [-32768, 32768], [-32769, 32767], [-65535, 1], [-65536, 0], [-65537, 65535], [65535, 65535], [65536, 0], [65537, 1], [131071, 65535], [131072, 0], [131073, 1],
].map(([input, expected]) => ({ label: String(input), args: [input!], expected })), true)
add("test/built-ins/String/fromCharCode/S9.7_A3.2_T1.js", "fromCharCode", [
{ label: "positive fraction", args: [1.2345], expected: 1 }, { label: "negative fraction", args: [-5.4321], expected: 65531 },
], true)
add("test/built-ins/String/fromCodePoint/arguments-is-empty.js", "fromCodePoint", [{ label: "no arguments", expected: "" }], true)
add("test/built-ins/String/fromCodePoint/return-string-value.js", "fromCodePoint", [
{ label: "NUL", args: [0], expected: "\x00" }, { label: "asterisk", args: [42], expected: "*" },
{ label: "AZ", args: [65, 90], expected: "AZ" }, { label: "Cyrillic", args: [0x404], expected: "\u0404" },
{ label: "hex supplementary", args: [0x2f804], expected: "\uD87E\uDC04" }, { label: "decimal supplementary", args: [194564], expected: "\uD87E\uDC04" },
{ label: "mixed supplementary", args: [0x1d306, 0x61, 0x1d307], expected: "\uD834\uDF06a\uD834\uDF07" },
{ label: "maximum code point", args: [1114111], expected: "\uDBFF\uDFFF" },
], true)
add("test/built-ins/String/fromCodePoint/argument-is-not-integer.js", "fromCodePoint", [
{ label: "fraction", args: [3.14], outcome: "RangeError" }, { label: "fraction after valid", args: [42, 3.14], outcome: "RangeError" },
], true)
add("test/built-ins/String/fromCodePoint/number-is-out-of-range.js", "fromCodePoint", [
{ label: "negative one", args: [-1], outcome: "RangeError" }, { label: "negative after valid", args: [1, -1], outcome: "RangeError" },
{ label: "above maximum", args: [1114112], outcome: "RangeError" }, { label: "infinity", args: [Infinity], outcome: "RangeError" },
], true)
describe("Test262-adapted core String behavior", () => {
for (const vector of vectors) {
test(vector.path, async () => {
const results = vector.assertions.map((item) => {
const args = (item.args ?? []).map(literal).join(", ")
const expression = vector.static
? `String.${vector.method}(${args})`
: `${JSON.stringify(item.input)}.${vector.method}(${args})`
const observed = vector.static && vector.method === "fromCharCode" && typeof item.expected === "number"
? `${expression}.charCodeAt(0)`
: expression
const checked = item.outcome === "undefined"
? `${observed} === undefined`
: item.outcome === "length"
? `${observed}.length`
: item.outcome === "RangeError"
? `(() => { try { ${observed}; return false } catch (error) { return error instanceof RangeError } })()`
: observed
return `{ label: ${JSON.stringify(item.label)}, value: ${checked} }`
})
const expected = vector.assertions.map((item) => ({
label: item.label,
value: item.outcome === undefined || item.outcome === "length" ? item.expected! : true,
}))
expect(await value(`return [${results.join(",")}]`)).toEqual(expected)
})
}
})

View file

@ -0,0 +1,625 @@
/*
* Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
* - test/built-ins/String/prototype/split/separator-regexp.js
* - test/built-ins/String/prototype/split/arguments-are-regexp-s-and-3-and-instance-is-string-a-b-c-de-f.js
* - test/built-ins/String/prototype/split/argument-is-regexp-s-and-instance-is-string-a-b-c-de-f.js
* - test/built-ins/String/prototype/split/argument-is-regexp-d-and-instance-is-string-dfe23iu-34-65.js
* - test/built-ins/String/prototype/split/argument-is-regexp-reg-exp-d-and-instance-is-string-dfe23iu-34-65.js
* - test/built-ins/String/prototype/split/argument-is-regexp-a-z-and-instance-is-string-abc.js
* - test/built-ins/String/prototype/split/argument-is-reg-exp-a-z-and-instance-is-string-abc.js
* - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-undefined-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-0-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-1-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-2-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-3-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-4-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/argument-is-regexp-l-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/argument-is-new-reg-exp-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-0-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-1-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-2-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-3-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-4-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-undefined-and-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-2-instance-is-string-one-two-three-four-five.js
* - test/built-ins/String/prototype/split/separator-regexp-comma-instance-is-string-one-1-two-2-four-4.js
* - test/built-ins/String/prototype/split/argument-is-regexp-x-and-instance-is-string-a-b-c-de-f.js
* - test/built-ins/String/prototype/replace/regexp-capture-by-index.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A1_T17.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T1.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T2.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T3.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T4.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T5.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T6.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T7.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T8.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T9.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T10.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T1.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T2.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T3.js
* - test/built-ins/String/prototype/replace/S15.5.4.11_A5_T1.js
* - test/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js
* - test/built-ins/String/prototype/replaceAll/searchValue-empty-string.js
* - test/built-ins/String/prototype/replaceAll/searchValue-empty-string-this-empty-string.js
* - test/built-ins/String/prototype/replaceAll/replaceValue-value-replaces-string.js
* - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024.js
* - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0024.js
* - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0026.js
* - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0060.js
* - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0027.js
* - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024N.js
* - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024NN.js
* - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x003C.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A1_T14.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A2_T2.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A2_T3.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A2_T4.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A2_T5.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A2_T6.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A2_T7.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A2_T8.js
* - test/built-ins/String/prototype/match/S15.5.4.10_A2_T12.js
* - test/built-ins/String/prototype/matchAll/regexp-prototype-matchAll-v-u-flag.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A1_T14.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A2_T1.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A2_T2.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A2_T3.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A2_T4.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A2_T5.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A2_T6.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A2_T7.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A3_T1.js
* - test/built-ins/String/prototype/search/S15.5.4.12_A3_T2.js
*
* Copyright 2009 the Sputnik authors. All rights reserved.
* Copyright (C) 2019 Leo Balter. All rights reserved.
* Copyright (C) 2020 Rick Waldron. All rights reserved.
* Copyright (C) 2023 Richard Gibson. All rights reserved.
* Copyright (C) 2024 Tan Meng. All rights reserved.
* Test262 portions are governed by the BSD license in LICENSE.test262.
*/
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { CodeMode } from "../src/index.js"
type Vector = {
readonly path: string
readonly code: string
readonly expected: CodeMode.DataValue
}
const value = async (code: string) => {
const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
return result.value
}
const run = (name: string, vectors: ReadonlyArray<Vector>) => {
describe(name, () => {
for (const vector of vectors) {
test(vector.path, async () => {
expect(await value(vector.code)).toEqual(vector.expected)
})
}
})
}
run("Test262-adapted regexp split behavior", [
{
path: "test/built-ins/String/prototype/split/separator-regexp.js",
code: `
return [
"x".split(/^/), "x".split(/$/), "x".split(/.?/), "x".split(/.*/), "x".split(/.+/),
"x".split(/.*?/), "x".split(/.{1}/), "x".split(/.{1,}/), "x".split(/.{1,2}/),
"x".split(/()/), "x".split(/./), "x".split(/(?:)/), "x".split(/(...)/),
"x".split(/(|)/), "x".split(/[]/), "x".split(/[^]/), "x".split(/[.-.]/),
"x".split(/\\0/), "x".split(/\\b/), "x".split(/\\B/), "x".split(/\\d/),
"x".split(/\\D/), "x".split(/\\n/), "x".split(/\\r/), "x".split(/\\s/),
"x".split(/\\S/), "x".split(/\\v/), "x".split(/\\w/), "x".split(/\\W/),
]
`,
expected: [
["x"], ["x"], ["", ""], ["", ""], ["", ""], ["x"], ["", ""], ["", ""], ["", ""],
["x"], ["", ""], ["x"], ["x"], ["x"], ["x"], ["", ""], ["x"], ["x"], ["x"],
["x"], ["x"], ["", ""], ["x"], ["x"], ["x"], ["", ""], ["x"], ["", ""], ["x"],
],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-regexp-s-and-3-and-instance-is-string-a-b-c-de-f.js",
code: `return "a b c de f".split(/\\s/, 3)`,
expected: ["a", "b", "c"],
},
{
path: "test/built-ins/String/prototype/split/argument-is-regexp-s-and-instance-is-string-a-b-c-de-f.js",
code: `return "a b c de f".split(/\\s/)`,
expected: ["a", "b", "c", "de", "f"],
},
{
path: "test/built-ins/String/prototype/split/argument-is-regexp-d-and-instance-is-string-dfe23iu-34-65.js",
code: `return "dfe23iu 34 =+65--".split(/\\d+/)`,
expected: ["dfe", "iu ", " =+", "--"],
},
{
path: "test/built-ins/String/prototype/split/argument-is-regexp-reg-exp-d-and-instance-is-string-dfe23iu-34-65.js",
code: `return "dfe23iu 34 =+65--".split(new RegExp("\\\\d+"))`,
expected: ["dfe", "iu ", " =+", "--"],
},
{
path: "test/built-ins/String/prototype/split/argument-is-regexp-a-z-and-instance-is-string-abc.js",
code: `return "abc".split(/[a-z]/)`,
expected: ["", "", "", ""],
},
{
path: "test/built-ins/String/prototype/split/argument-is-reg-exp-a-z-and-instance-is-string-abc.js",
code: `return "abc".split(new RegExp("[a-z]"))`,
expected: ["", "", "", ""],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-undefined-and-instance-is-string-hello.js",
code: `return "hello".split(/l/, undefined)`,
expected: ["he", "", "o"],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-0-and-instance-is-string-hello.js",
code: `return "hello".split(/l/, 0)`,
expected: [],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-1-and-instance-is-string-hello.js",
code: `return "hello".split(/l/, 1)`,
expected: ["he"],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-2-and-instance-is-string-hello.js",
code: `return "hello".split(/l/, 2)`,
expected: ["he", ""],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-3-and-instance-is-string-hello.js",
code: `return "hello".split(/l/, 3)`,
expected: ["he", "", "o"],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-4-and-instance-is-string-hello.js",
code: `return "hello".split(/l/, 4)`,
expected: ["he", "", "o"],
},
{
path: "test/built-ins/String/prototype/split/argument-is-regexp-l-and-instance-is-string-hello.js",
code: `return "hello".split(/l/)`,
expected: ["he", "", "o"],
},
{
path: "test/built-ins/String/prototype/split/argument-is-new-reg-exp-and-instance-is-string-hello.js",
code: `return "hello".split(new RegExp())`,
expected: ["h", "e", "l", "l", "o"],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-0-and-instance-is-string-hello.js",
code: `return "hello".split(new RegExp(), 0)`,
expected: [],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-1-and-instance-is-string-hello.js",
code: `return "hello".split(new RegExp(), 1)`,
expected: ["h"],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-2-and-instance-is-string-hello.js",
code: `return "hello".split(new RegExp(), 2)`,
expected: ["h", "e"],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-3-and-instance-is-string-hello.js",
code: `return "hello".split(new RegExp(), 3)`,
expected: ["h", "e", "l"],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-4-and-instance-is-string-hello.js",
code: `return "hello".split(new RegExp(), 4)`,
expected: ["h", "e", "l", "l"],
},
{
path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-undefined-and-instance-is-string-hello.js",
code: `return "hello".split(new RegExp(), undefined)`,
expected: ["h", "e", "l", "l", "o"],
},
{
path: "test/built-ins/String/prototype/split/call-split-2-instance-is-string-one-two-three-four-five.js",
code: `return "one two three four five".split(/ /, 2)`,
expected: ["one", "two"],
},
{
path: "test/built-ins/String/prototype/split/separator-regexp-comma-instance-is-string-one-1-two-2-four-4.js",
code: `return "one-1,two-2,four-4".split(/,/)`,
expected: ["one-1", "two-2", "four-4"],
},
{
path: "test/built-ins/String/prototype/split/argument-is-regexp-x-and-instance-is-string-a-b-c-de-f.js",
code: `return "a b c de f".split(/X/)`,
expected: ["a b c de f"],
},
])
run("Test262-adapted replace behavior", [
{
path: "test/built-ins/String/prototype/replace/regexp-capture-by-index.js",
code: `
const str = "foo-x-bar"
const patterns = ["x", /x/, /(x)/, /(x)($^)?/, /((((((((((x))))))))))/]
const replacements = ["|$0|", "|$00|", "|$000|", "|$1|", "|$01|", "|$010|", "|$2|", "|$02|", "|$020|", "|$10|", "|$100|", "|$20|", "|$200|"]
return replacements.flatMap((replacement) => patterns.map((pattern) => str.replace(pattern, replacement)))
`,
expected: [
"foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar",
"foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar",
"foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar",
"foo-|$1|-bar", "foo-|$1|-bar", "foo-|x|-bar", "foo-|x|-bar", "foo-|x|-bar",
"foo-|$01|-bar", "foo-|$01|-bar", "foo-|x|-bar", "foo-|x|-bar", "foo-|x|-bar",
"foo-|$010|-bar", "foo-|$010|-bar", "foo-|x0|-bar", "foo-|x0|-bar", "foo-|x0|-bar",
"foo-|$2|-bar", "foo-|$2|-bar", "foo-|$2|-bar", "foo-||-bar", "foo-|x|-bar",
"foo-|$02|-bar", "foo-|$02|-bar", "foo-|$02|-bar", "foo-||-bar", "foo-|x|-bar",
"foo-|$020|-bar", "foo-|$020|-bar", "foo-|$020|-bar", "foo-|0|-bar", "foo-|x0|-bar",
"foo-|$10|-bar", "foo-|$10|-bar", "foo-|x0|-bar", "foo-|x0|-bar", "foo-|x|-bar",
"foo-|$100|-bar", "foo-|$100|-bar", "foo-|x00|-bar", "foo-|x00|-bar", "foo-|x0|-bar",
"foo-|$20|-bar", "foo-|$20|-bar", "foo-|$20|-bar", "foo-|0|-bar", "foo-|x0|-bar",
"foo-|$200|-bar", "foo-|$200|-bar", "foo-|$200|-bar", "foo-|00|-bar", "foo-|x00|-bar",
],
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A1_T17.js",
code: `return "asdf".replace(new RegExp(undefined, "g"), "1")`,
expected: "1a1s1d1f1",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T1.js",
code: `return "She sells seashells by the seashore.".replace(/sh/g, "sch")`,
expected: "She sells seaschells by the seaschore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T2.js",
code: `return "She sells seashells by the seashore.".replace(/sh/g, "$$sch")`,
expected: "She sells sea$schells by the sea$schore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T3.js",
code: `return "She sells seashells by the seashore.".replace(/sh/g, "$&sch")`,
expected: "She sells seashschells by the seashschore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T4.js",
code: `return "She sells seashells by the seashore.".replace(/sh/g, "$\`sch")`,
expected: "She sells seaShe sells seaschells by the seaShe sells seashells by the seaschore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T5.js",
code: `return "She sells seashells by the seashore.".replace(/sh/g, "$'sch")`,
expected: "She sells seaells by the seashore.schells by the seaore.schore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T6.js",
code: `return "She sells seashells by the seashore.".replace(/sh/, "sch")`,
expected: "She sells seaschells by the seashore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T7.js",
code: `return "She sells seashells by the seashore.".replace(/sh/, "$$sch")`,
expected: "She sells sea$schells by the seashore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T8.js",
code: `return "She sells seashells by the seashore.".replace(/sh/, "$&sch")`,
expected: "She sells seashschells by the seashore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T9.js",
code: `return "She sells seashells by the seashore.".replace(/sh/, "$\`sch")`,
expected: "She sells seaShe sells seaschells by the seashore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T10.js",
code: `return "She sells seashells by the seashore.".replace(/sh/, "$'sch")`,
expected: "She sells seaells by the seashore.schells by the seashore.",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T1.js",
code: `return "uid=31".replace(/(uid=)(\\d+)/, "$1115")`,
expected: "uid=115",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T2.js",
code: `return "uid=31".replace(/(uid=)(\\d+)/, "$1115")`,
expected: "uid=115",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T3.js",
code: `return "uid=31".replace(/(uid=)(\\d+)/, "$11A15")`,
expected: "uid=1A15",
},
{
path: "test/built-ins/String/prototype/replace/S15.5.4.11_A5_T1.js",
code: `return "aaaaaaaaaa,aaaaaaaaaaaaaaa".replace(/^(a+)\\1*,\\1+$/, "$1")`,
expected: "aaaaa",
},
])
run("Test262-adapted replaceAll behavior", [
{
path: "test/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js",
code: `
return [
"abc abc abc".replaceAll(new RegExp("b", "g"), "z"),
"abc abc abc".replaceAll(new RegExp("b", "gy"), "z"),
"abc abc abc".replaceAll(new RegExp("b", "giy"), "z"),
"No Uppercase!".replaceAll(new RegExp("[A-Z]", "g"), ""),
"No Uppercase?".replaceAll(new RegExp("[A-Z]", "gy"), ""),
"NO UPPERCASE!".replaceAll(new RegExp("[A-Z]", "gy"), ""),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "$2-$1"),
"abcabcabcabc".replaceAll(new RegExp("(a(.))", "g"), "$1$2$3"),
"aabacadaeafagahaiajakalamano a azaya".replaceAll(new RegExp("(((((((((((((a(.).).).).).).).).))))))", "g"), "($10)-($12)-($1)"),
"abcba".replaceAll(new RegExp("b", "g"), "$'"),
"abcba".replaceAll(new RegExp("b", "g"), "$\`"),
"abcba".replaceAll(new RegExp("(?<named>b)", "g"), "($<named>)"),
"abcba".replaceAll(new RegExp("(?<named>b)", "g"), "($<named)"),
"abcba".replaceAll(new RegExp("(?<named>b)", "g"), "($<unnamed>)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$$$)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$$)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$&)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$1)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$\`)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$')"),
"abcabcabcabc".replaceAll(new RegExp("a(?<z>b)(ca)", "g"), "($$<z>)"),
"abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($&)"),
]
`,
expected: [
"azc azc azc", "abc abc abc", "abc abc abc", "o ppercase!", "o Uppercase?", " UPPERCASE!",
"ca-bbcca-bbc", "abb$3cabb$3cabb$3cabb$3c",
"(aabaca)-(aaba)-(aabacadaea)f(agahai)-(agah)-(agahaiajak)(alaman)-(alam)-(alamano a )azaya",
"acbacaa", "aacabca", "a(b)c(b)a", "a($<named)c($<named)a", "a()c()a", "($)bc($)bc",
"($)bc($)bc", "($$)bc($$)bc", "($$)bc($$)bc", "($&)bc($&)bc", "($1)bc($1)bc",
"($`)bc($`)bc", "($')bc($')bc", "($<z>)bc($<z>)bc", "(abca)bc(abca)bc",
],
},
{
path: "test/built-ins/String/prototype/replaceAll/searchValue-empty-string.js",
code: `return ["aab c \\nx".replaceAll("", "_"), "a".replaceAll("", "_")]`,
expected: ["_a_a_b_ _c_ _ _\n_x_", "_a_"],
},
{
path: "test/built-ins/String/prototype/replaceAll/searchValue-empty-string-this-empty-string.js",
code: `return "".replaceAll("", "abc")`,
expected: "abc",
},
{
path: "test/built-ins/String/prototype/replaceAll/replaceValue-value-replaces-string.js",
code: `return ["aaab a a aac".replaceAll("aa", "z"), "aaab a a aac".replaceAll("aa", "a"), "aaab a a aac".replaceAll("a", "a"), "aaab a a aac".replaceAll("a", "z")]`,
expected: ["zab a a zc", "aab a a ac", "aaab a a aac", "zzzb z z zzc"],
},
{
path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024.js",
code: `
const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
return [str.replaceAll("ninguém", "$"), str.replaceAll("é", "$"), str.replaceAll("é", "$ -"), str.replaceAll("é", "$$$")]
`,
expected: [
"Ninguém é igual a $. Todo o ser humano é um estranho ímpar.",
"Ningu$m $ igual a ningu$m. Todo o ser humano $ um estranho ímpar.",
"Ningu$ -m $ - igual a ningu$ -m. Todo o ser humano $ - um estranho ímpar.",
"Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.",
],
},
{
path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0024.js",
code: `
const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
return [str.replaceAll("ninguém", "$$"), str.replaceAll("é", "$$"), str.replaceAll("é", "$$ -"), str.replaceAll("é", "$$&"), str.replaceAll("é", "$$$"), str.replaceAll("é", "$$$$")]
`,
expected: [
"Ninguém é igual a $. Todo o ser humano é um estranho ímpar.",
"Ningu$m $ igual a ningu$m. Todo o ser humano $ um estranho ímpar.",
"Ningu$ -m $ - igual a ningu$ -m. Todo o ser humano $ - um estranho ímpar.",
"Ningu$&m $& igual a ningu$&m. Todo o ser humano $& um estranho ímpar.",
"Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.",
"Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.",
],
},
{
path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0026.js",
code: `
const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
return [str.replaceAll("ninguém", "$&"), str.replaceAll("ninguém", "($&)"), str.replaceAll("é", "($&)"), str.replaceAll("é", "($&) $&")]
`,
expected: [
"Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar.",
"Ninguém é igual a (ninguém). Todo o ser humano é um estranho ímpar.",
"Ningu(é)m (é) igual a ningu(é)m. Todo o ser humano (é) um estranho ímpar.",
"Ningu(é) ém (é) é igual a ningu(é) ém. Todo o ser humano (é) é um estranho ímpar.",
],
},
{
path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0060.js",
code: `
const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
return [str.replaceAll("ninguém", "$\`"), str.replaceAll("Ninguém", "$\`"), str.replaceAll("ninguém", "($\`)"), str.replaceAll("é", "($\`)")]
`,
expected: [
"Ninguém é igual a Ninguém é igual a . Todo o ser humano é um estranho ímpar.",
" é igual a ninguém. Todo o ser humano é um estranho ímpar.",
"Ninguém é igual a (Ninguém é igual a ). Todo o ser humano é um estranho ímpar.",
"Ningu(Ningu)m (Ninguém ) igual a ningu(Ninguém é igual a ningu)m. Todo o ser humano (Ninguém é igual a ninguém. Todo o ser humano ) um estranho ímpar.",
],
},
{
path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0027.js",
code: `
const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar."
return [str.replaceAll("ninguém", "$'"), str.replaceAll(".", "--- $'"), str.replaceAll("é", "($')")]
`,
expected: [
"Ninguém é igual a . Todo o ser humano é um estranho ímpar.. Todo o ser humano é um estranho ímpar.",
"Ninguém é igual a ninguém--- Todo o ser humano é um estranho ímpar. Todo o ser humano é um estranho ímpar--- ",
"Ningu(m é igual a ninguém. Todo o ser humano é um estranho ímpar.)m ( igual a ninguém. Todo o ser humano é um estranho ímpar.) igual a ningu(m. Todo o ser humano é um estranho ímpar.)m. Todo o ser humano ( um estranho ímpar.) um estranho ímpar.",
],
},
{
path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024N.js",
code: `
const str = "ABC AAA ABC AAA"
return ["$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"].map((replacement) => str.replaceAll("ABC", replacement))
`,
expected: ["$1 AAA $1 AAA", "$2 AAA $2 AAA", "$3 AAA $3 AAA", "$4 AAA $4 AAA", "$5 AAA $5 AAA", "$6 AAA $6 AAA", "$7 AAA $7 AAA", "$8 AAA $8 AAA", "$9 AAA $9 AAA"],
},
{
path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024NN.js",
code: `
const str = "aaaaaaaaaaaaaaaa aaaaaaaa aaaaaaaaaaaaaaaa"
return [str.replaceAll("a", "$11"), str.replaceAll("a", "$29")]
`,
expected: [
"$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11 $11$11$11$11$11$11$11$11 $11$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11",
"$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29 $29$29$29$29$29$29$29$29 $29$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29",
],
},
{
path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x003C.js",
code: `return "aaaaaaaaaaaaaaaa aaaaaaaa aaaaaaaaaaaaaaaa".replaceAll("a", "$<")`,
expected: "$<$<$<$<$<$<$<$<$<$<$<$<$<$<$<$< $<$<$<$<$<$<$<$< $<$<$<$<$<$<$<$<$<$<$<$<$<$<$<$<",
},
])
run("Test262-adapted match behavior", [
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A1_T14.js",
code: `const match = "ABBABABAB77BBAA".match(new RegExp("77")); return [match[0], match.index]`,
expected: ["77", 9],
},
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T2.js",
code: `return "343443444".match(/34/g)`,
expected: ["34", "34", "34"],
},
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T3.js",
code: `return "123456abcde7890".match(/\\d{1}/g)`,
expected: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
},
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T4.js",
code: `return "123456abcde7890".match(/\\d{2}/g)`,
expected: ["12", "34", "56", "78", "90"],
},
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T5.js",
code: `return "123456abcde7890".match(/\\D{2}/g)`,
expected: ["ab", "cd"],
},
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T6.js",
code: `const match = "Boston, Mass. 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/); return [match[0], match[1], match[2] === undefined, match.length, match.index]`,
expected: ["02134", "02134", true, 3, 14],
},
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T7.js",
code: `return "Boston, Mass. 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/g)`,
expected: ["02134"],
},
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T8.js",
code: `const match = "Boston, MA 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/); return [match[0], match[1], match[2] === undefined, match.length, match.index]`,
expected: ["02134", "02134", true, 3, 11],
},
{
path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T12.js",
code: `return "Boston, MA 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/g)`,
expected: ["02134"],
},
])
run("Test262-adapted matchAll behavior", [
{
path: "test/built-ins/String/prototype/matchAll/regexp-prototype-matchAll-v-u-flag.js",
code: `
const text = "𠮷a𠮷b𠮷"
const collect = (regex) => {
const matches = text.matchAll(regex)
return matches.map((match) => match[0]).concat(matches.map((match) => match.index))
}
const empty = text.matchAll(/(?:)/gu)
const complex = "a𠮷b􏿿c".matchAll(/\\P{ASCII}/gu)
return [
collect(/𠮷/g),
collect(/𠮷/gu),
collect(/\\p{Script=Han}/gu),
collect(/./gu),
empty.map((match) => match[0]).concat(empty.map((match) => match.index)).length,
complex.map((match) => match[0]),
]
`,
expected: [
["𠮷", "𠮷", "𠮷", 0, 3, 6],
["𠮷", "𠮷", "𠮷", 0, 3, 6],
["𠮷", "𠮷", "𠮷", 0, 3, 6],
["𠮷", "a", "𠮷", "b", "𠮷", 0, 2, 3, 5, 6],
12,
["𠮷", "􏿿"],
],
},
])
run("Test262-adapted search behavior", [
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A1_T14.js",
code: `return "ABBABABAB77BBAA".search(new RegExp("77"))`,
expected: 9,
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T1.js",
code: `return "test string".search("string")`,
expected: 5,
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T2.js",
code: `return "test string".search("String")`,
expected: -1,
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T3.js",
code: `return "test string".search(/String/i)`,
expected: 5,
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T4.js",
code: `return "one two three four five".search(/Four/)`,
expected: -1,
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T5.js",
code: `return "one two three four five".search(/four/)`,
expected: 14,
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T6.js",
code: `return "test string".search("notexist")`,
expected: -1,
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T7.js",
code: `return "test string probe".search("string pro")`,
expected: 5,
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A3_T1.js",
code: `const text = "power of the power of the power of the great sword"; return [text.search(/the/), text.search(/the/g)]`,
expected: [9, 9],
},
{
path: "test/built-ins/String/prototype/search/S15.5.4.12_A3_T2.js",
code: `const text = "power of the power of the power of the great sword"; return [text.search(/of/), text.search(/of/g)]`,
expected: [6, 6],
},
])

View file

@ -0,0 +1,794 @@
/*
* Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
* - test/built-ins/String/prototype/split/call-split-l-0-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-l-1-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-l-2-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-l-3-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-l-4-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-l-na-n-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-l-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-ll-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-h-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-hello-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-hellothere-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-o-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-x-instance-is-string-hello.js
* - test/built-ins/String/prototype/split/call-split-x-instance-is-empty-string.js
* - test/built-ins/String/prototype/split/call-split-4-instance-is-string-one-1-two-2-four-4.js
* - test/built-ins/String/prototype/split/call-split-on-instance-is-string-one-1-two-2-four-4.js
* - test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three-four-five.js
* - test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three.js
* - test/built-ins/String/prototype/split/call-split-instance-is-string.js
* - test/built-ins/String/prototype/split/instance-is-string-one-two-three-four-five.js
* - test/built-ins/String/prototype/split/instance-is-string.js
* - test/built-ins/String/prototype/split/separator-colon-instance-is-string-one-1-two-2-four-4.js
* - test/built-ins/String/prototype/split/separator-comma-instance-is-string-one-two-three-four-five.js
* - test/built-ins/String/prototype/split/separator-empty-string-instance-is-string.js
* - test/built-ins/String/prototype/split/call-split-without-arguments-and-instance-is-empty-string.js
* - test/built-ins/String/prototype/split/separator-undef.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A1_T6.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A1_T14.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T1.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T2.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T3.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T4.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T5.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T6.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T7.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T8.js
* - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T9.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A1_T6.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A1_T14.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T1.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T2.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T3.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T4.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T5.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T6.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T7.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T8.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T9.js
* - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T10.js
* - test/annexB/built-ins/String/prototype/substr/start-negative.js
* - test/annexB/built-ins/String/prototype/substr/length-negative.js
* - test/annexB/built-ins/String/prototype/substr/length-positive.js
* - test/annexB/built-ins/String/prototype/substr/length-falsey.js
* - test/annexB/built-ins/String/prototype/substr/length-undef.js
* - test/annexB/built-ins/String/prototype/substr/surrogate-pairs.js
* - test/built-ins/String/prototype/includes/String.prototype.includes_FailMissingLetter.js
* - test/built-ins/String/prototype/includes/String.prototype.includes_SuccessNoLocation.js
* - test/built-ins/String/prototype/includes/String.prototype.includes_FailBadLocation.js
* - test/built-ins/String/prototype/includes/String.prototype.includes_FailLocation.js
* - test/built-ins/String/prototype/includes/String.prototype.includes_Success.js
* - test/built-ins/String/prototype/includes/searchstring-found-with-position.js
* - test/built-ins/String/prototype/includes/searchstring-found-without-position.js
* - test/built-ins/String/prototype/includes/searchstring-not-found-with-position.js
* - test/built-ins/String/prototype/includes/searchstring-not-found-without-position.js
* - test/built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js
* - test/built-ins/String/prototype/includes/return-true-if-searchstring-is-empty.js
* - test/built-ins/String/prototype/includes/coerced-values-of-position.js
* - test/built-ins/String/prototype/startsWith/searchstring-found-with-position.js
* - test/built-ins/String/prototype/startsWith/searchstring-found-without-position.js
* - test/built-ins/String/prototype/startsWith/searchstring-not-found-with-position.js
* - test/built-ins/String/prototype/startsWith/searchstring-not-found-without-position.js
* - test/built-ins/String/prototype/startsWith/out-of-bounds-position.js
* - test/built-ins/String/prototype/startsWith/return-true-if-searchstring-is-empty.js
* - test/built-ins/String/prototype/startsWith/coerced-values-of-position.js
* - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success.js
* - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_2.js
* - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_3.js
* - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_4.js
* - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail.js
* - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail_2.js
* - test/built-ins/String/prototype/endsWith/searchstring-found-with-position.js
* - test/built-ins/String/prototype/endsWith/searchstring-found-without-position.js
* - test/built-ins/String/prototype/endsWith/searchstring-not-found-with-position.js
* - test/built-ins/String/prototype/endsWith/searchstring-not-found-without-position.js
* - test/built-ins/String/prototype/endsWith/return-false-if-search-start-is-less-than-zero.js
* - test/built-ins/String/prototype/endsWith/return-true-if-searchstring-is-empty.js
* - test/built-ins/String/prototype/endsWith/coerced-values-of-position.js
* - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T1.js
* - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T2.js
* - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T3.js
* - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T4.js
* - test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T1.js
* - test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T3.js
* - test/built-ins/String/prototype/indexOf/position-tointeger.js
* - test/built-ins/String/prototype/indexOf/searchstring-tostring.js
* - test/built-ins/String/prototype/lastIndexOf/not-a-substring.js
*
* Copyright 2009 the Sputnik authors. All rights reserved.
* Copyright (c) 2014 Ryan Lewis. All rights reserved.
* Copyright (C) 2015 the V8 project authors. All rights reserved.
* Copyright (C) 2016 the V8 project authors. All rights reserved.
* Copyright (C) 2017 Josh Wolfe. All rights reserved.
* Copyright (C) 2020 Leo Balter. All rights reserved.
* Copyright (C) 2026 Garham Lee. All rights reserved.
* Test262 portions are governed by the BSD license in LICENSE.test262.
*/
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { CodeMode } from "../src/index.js"
const value = async (code: string) => {
const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
return result.value
}
const cases = [
{
path: "test/built-ins/String/prototype/split/call-split-l-0-instance-is-string-hello.js",
code: `const result = "hello".split("l", 0); return [result.length, result[0] === undefined]`,
expected: [0, true],
labels: [
"The value of __split.length is expected to equal the value of __expected.length",
"The value of __split[0] is expected to equal the value of __expected[0]",
],
},
{
path: "test/built-ins/String/prototype/split/call-split-l-1-instance-is-string-hello.js",
code: `const result = "hello".split("l", 1); return [result.length, result[0]]`,
expected: [1, "he"],
labels: [
"The value of __split.length is expected to equal the value of __expected.length",
"The value of __split[0] is expected to equal the value of __expected[0]",
],
},
{
path: "test/built-ins/String/prototype/split/call-split-l-2-instance-is-string-hello.js",
code: `const result = "hello".split("l", 2); return [result.length, result[0], result[1]]`,
expected: [2, "he", ""],
labels: [
"The value of __split.length is expected to equal the value of __expected.length",
"The value of __split[index] is expected to equal the value of __expected[index]",
"The value of __split[index] is expected to equal the value of __expected[index]",
],
},
{
path: "test/built-ins/String/prototype/split/call-split-l-3-instance-is-string-hello.js",
code: `const result = "hello".split("l", 3); return [result.length, result[0], result[1], result[2]]`,
expected: [3, "he", "", "o"],
labels: [
"The value of __split.length is expected to equal the value of __expected.length",
"The value of __split[index] is expected to equal the value of __expected[index]",
"The value of __split[index] is expected to equal the value of __expected[index]",
"The value of __split[index] is expected to equal the value of __expected[index]",
],
},
{
path: "test/built-ins/String/prototype/split/call-split-l-4-instance-is-string-hello.js",
code: `const result = "hello".split("l", 4); return [result.length, result[0], result[1], result[2]]`,
expected: [3, "he", "", "o"],
labels: [
"The value of __split.length is expected to equal the value of __expected.length",
"The value of __split[index] is expected to equal the value of __expected[index]",
"The value of __split[index] is expected to equal the value of __expected[index]",
"The value of __split[index] is expected to equal the value of __expected[index]",
],
},
{
path: "test/built-ins/String/prototype/split/call-split-l-na-n-instance-is-string-hello.js",
code: `const result = "hello".split("l", NaN); return [result.length, result[0] === undefined]`,
expected: [0, true],
labels: [
"The value of __split.length is expected to equal the value of __expected.length",
"The value of __split[0] is expected to equal the value of __expected[0]",
],
},
{
path: "test/built-ins/String/prototype/split/call-split-l-instance-is-string-hello.js",
code: `const result = "hello".split("l"); return [result.length, result[0], result[1], result[2]]`,
expected: [3, "he", "", "o"],
labels: [
"The value of __split.length is 3",
'The value of __split[0] is "he"',
'The value of __split[1] is ""',
'The value of __split[2] is "o"',
],
},
{
path: "test/built-ins/String/prototype/split/call-split-ll-instance-is-string-hello.js",
code: `const result = "hello".split("ll"); return [result.length, result[0], result[1]]`,
expected: [2, "he", "o"],
labels: ["The value of __split.length is 2", 'The value of __split[0] is "he"', 'The value of __split[1] is "o"'],
},
{
path: "test/built-ins/String/prototype/split/call-split-h-instance-is-string-hello.js",
code: `const result = "hello".split("h"); return [result.length, result[0], result[1]]`,
expected: [2, "", "ello"],
labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is "ello"'],
},
{
path: "test/built-ins/String/prototype/split/call-split-hello-instance-is-string-hello.js",
code: `const result = "hello".split("hello"); return [result.length, result[0], result[1]]`,
expected: [2, "", ""],
labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is ""'],
},
{
path: "test/built-ins/String/prototype/split/call-split-hellothere-instance-is-string-hello.js",
code: `const result = "hello".split("hellothere"); return [result.length, result[0]]`,
expected: [1, "hello"],
labels: ["The value of __split.length is 1", 'The value of __split[0] is "hello"'],
},
{
path: "test/built-ins/String/prototype/split/call-split-o-instance-is-string-hello.js",
code: `const result = "hello".split("o"); return [result.length, result[0], result[1]]`,
expected: [2, "hell", ""],
labels: ["The value of __split.length is 2", 'The value of __split[0] is "hell"', 'The value of __split[1] is ""'],
},
{
path: "test/built-ins/String/prototype/split/call-split-x-instance-is-string-hello.js",
code: `const result = "hello".split("x"); return [result.length, result[0]]`,
expected: [1, "hello"],
labels: ["The value of __split.length is 1", 'The value of __split[0] is "hello"'],
},
{
path: "test/built-ins/String/prototype/split/call-split-x-instance-is-empty-string.js",
code: `const result = "".split("x"); return [result.length, result[0]]`,
expected: [1, ""],
labels: ["The value of __split.length is 1", 'The value of __split[0] is ""'],
},
{
path: "test/built-ins/String/prototype/split/call-split-4-instance-is-string-one-1-two-2-four-4.js",
code: `const result = "one-1 two-2 four-4".split("-4"); return [result.length, result[0], result[1]]`,
expected: [2, "one-1 two-2 four", ""],
labels: [
"The value of __split.length is 2",
'The value of __split[0] is "one-1 two-2 four"',
'The value of __split[1] is ""',
],
},
{
path: "test/built-ins/String/prototype/split/call-split-on-instance-is-string-one-1-two-2-four-4.js",
code: `const result = "one-1 two-2 four-4".split("on"); return [result.length, result[0], result[1]]`,
expected: [2, "", "e-1 two-2 four-4"],
labels: [
"The value of __split.length is 2",
'The value of __split[0] is ""',
'The value of __split[1] is "e-1 two-2 four-4"',
],
},
{
path: "test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three-four-five.js",
code: `const result = "one two three four five".split(" "); return [result.length, ...result]`,
expected: [5, "one", "two", "three", "four", "five"],
labels: [
"The value of __split.length is 5", 'The value of __split[0] is "one"', 'The value of __split[1] is "two"',
'The value of __split[2] is "three"', 'The value of __split[3] is "four"', 'The value of __split[4] is "five"',
],
},
{
path: "test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three.js",
code: `const result = "one two three".split(""); return [result[0], result[1], result[11], result[12]]`,
expected: ["o", "n", "e", "e"],
labels: [
'The value of __split[0] is "o"', 'The value of __split[1] is "n"',
'The value of __split[11] is "e"', 'The value of __split[12] is "e"',
],
},
{
path: "test/built-ins/String/prototype/split/call-split-instance-is-string.js",
code: `const result = " ".split(" "); return [result.length, result[0], result[1]]`,
expected: [2, "", ""],
labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is ""'],
},
{
path: "test/built-ins/String/prototype/split/instance-is-string-one-two-three-four-five.js",
code: `const result = "one,two,three,four,five".split(); return [result.length, result[0]]`,
expected: [1, "one,two,three,four,five"],
labels: ["The value of __split.length is 1", 'The value of __split[0] is "one,two,three,four,five"'],
},
{
path: "test/built-ins/String/prototype/split/instance-is-string.js",
code: `const result = " ".split(); return [result.length, result[0]]`,
expected: [1, " "],
labels: ["The value of __split.length is 1", 'The value of __split[0] is " "'],
},
{
path: "test/built-ins/String/prototype/split/separator-colon-instance-is-string-one-1-two-2-four-4.js",
code: `const result = "one-1,two-2,four-4".split(":"); return [result.length, result[0]]`,
expected: [1, "one-1,two-2,four-4"],
labels: ["The value of __split.length is 1", 'The value of __split[0] is "one-1,two-2,four-4"'],
},
{
path: "test/built-ins/String/prototype/split/separator-comma-instance-is-string-one-two-three-four-five.js",
code: `const result = "one,two,three,four,five".split(","); return [result.length, ...result]`,
expected: [5, "one", "two", "three", "four", "five"],
labels: [
"The value of __split.length is 5",
'The value of __split[0] is "one"',
'The value of __split[1] is "two"',
'The value of __split[2] is "three"',
'The value of __split[3] is "four"',
'The value of __split[4] is "five"',
],
},
{
path: "test/built-ins/String/prototype/split/separator-empty-string-instance-is-string.js",
code: `const result = " ".split(""); return [result.length, result[0]]`,
expected: [1, " "],
labels: ["The value of __split.length is 1", 'The value of __split[0] is " "'],
},
{
path: "test/built-ins/String/prototype/split/call-split-without-arguments-and-instance-is-empty-string.js",
code: `const result = "".split(); return [result.length, result[0]]`,
expected: [1, ""],
labels: ["The value of __split.length is 1", 'The value of __split[0] is ""'],
},
{
path: "test/built-ins/String/prototype/split/separator-undef.js",
code: `const result = "undefined is not a function".split(); return [Array.isArray(result), result.length, result[0]]`,
expected: [true, 1, "undefined is not a function"],
labels: ["implicit separator, result is array", "implicit separator, result.length", "implicit separator, [0] is the same string"],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A1_T6.js",
code: `return ["undefined".slice(undefined, 3)]`,
expected: ["und"],
labels: ['#1: new String("undefined").slice(x,3) === "und"'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A1_T14.js",
code: `return ["report".slice(undefined)]`,
expected: ["report"],
labels: ['#1: "report".slice(function(){}()) === "report"'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T1.js",
code: `return [typeof "this is a string object".slice()]`,
expected: ["string"],
labels: ['#1: typeof __string.slice() === "string"'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T2.js",
code: `return ["this is a string object".slice(NaN, Infinity)]`,
expected: ["this is a string object"],
labels: ['#1: __string.slice(NaN, Infinity) === "this is a string object"'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T3.js",
code: `return ["".slice(1, 0)]`,
expected: [""],
labels: ['#1: __string.slice(1,0) === ""'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T4.js",
code: `return ["this is a string object".slice(Infinity, NaN)]`,
expected: [""],
labels: ['#1: __string.slice(Infinity, NaN) === ""'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T5.js",
code: `return ["this is a string object".slice(Infinity, Infinity)]`,
expected: [""],
labels: ['#1: __string.slice(Infinity, Infinity) === ""'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T6.js",
code: `return ["this is a string object".slice(-0.01, 0)]`,
expected: [""],
labels: ['#1: __string.slice(-0.01,0) === ""'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T7.js",
code: `const text = "this is a string object"; return [text.slice(text.length, text.length)]`,
expected: [""],
labels: ['#1: __string.slice(__string.length, __string.length) === ""'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T8.js",
code: `const text = "this is a string object"; return [text.slice(text.length + 1, 0)]`,
expected: [""],
labels: ['#1: __string.slice(__string.length+1, 0) === ""'],
},
{
path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T9.js",
code: `return ["this is a string object".slice(-Infinity, -Infinity)]`,
expected: [""],
labels: ['#1: __string.slice(-Infinity, -Infinity) === ""'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A1_T6.js",
code: `return ["undefined".substring(undefined, 3)]`,
expected: ["und"],
labels: ['#1: new String("undefined").substring(x,3) === "und"'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A1_T14.js",
code: `return ["report".substring(undefined)]`,
expected: ["report"],
labels: ['#1: "report".substring(function(){}()) === "report"'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T1.js",
code: `return [typeof "this is a string object".substring()]`,
expected: ["string"],
labels: ['#1: typeof __string.substring() === "string"'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T2.js",
code: `return ["this is a string object".substring(NaN, Infinity)]`,
expected: ["this is a string object"],
labels: ['#1: __string.substring(NaN, Infinity) === "this is a string object"'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T3.js",
code: `return ["".substring(1, 0)]`,
expected: [""],
labels: ['#1: __string.substring(1,0) === ""'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T4.js",
code: `return ["this is a string object".substring(Infinity, NaN)]`,
expected: ["this is a string object"],
labels: ['#1: __string.substring(Infinity, NaN) === "this is a string object"'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T5.js",
code: `return ["this is a string object".substring(Infinity, Infinity)]`,
expected: [""],
labels: ['#1: __string.substring(Infinity, Infinity) === ""'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T6.js",
code: `return ["this is a string object".substring(-0.01, 0)]`,
expected: [""],
labels: ['#1: __string.substring(-0.01,0) === ""'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T7.js",
code: `const text = "this is a string object"; return [text.substring(text.length, text.length)]`,
expected: [""],
labels: ['#1: __string.substring(__string.length, __string.length) === ""'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T8.js",
code: `const text = "this is a string object"; return [text.substring(text.length + 1, 0)]`,
expected: ["this is a string object"],
labels: ['#1: __string.substring(__string.length+1, 0) === "this is a string object"'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T9.js",
code: `return ["this is a string object".substring(-Infinity, -Infinity)]`,
expected: [""],
labels: ['#1: __string.substring(-Infinity, -Infinity) === ""'],
},
{
path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T10.js",
code: `return ["this_is_a_string object".substring(0, 8)]`,
expected: ["this_is_"],
labels: ['#1: __string.substring(0,8) === "this_is_"'],
},
{
path: "test/annexB/built-ins/String/prototype/substr/start-negative.js",
code: `return ["abc".substr(-1), "abc".substr(-2), "abc".substr(-3), "abc".substr(-4), "abc".substr(-1.1)]`,
expected: ["c", "bc", "abc", "abc", "c"],
labels: ["-1", "-2", "-3", "size + intStart < 0", "floating point rounding semantics"],
},
{
path: "test/annexB/built-ins/String/prototype/substr/length-negative.js",
code: `return [
"abc".substr(0, -1), "abc".substr(0, -2), "abc".substr(0, -3), "abc".substr(0, -4),
"abc".substr(1, -1), "abc".substr(1, -2), "abc".substr(1, -3), "abc".substr(1, -4),
"abc".substr(2, -1), "abc".substr(2, -2), "abc".substr(2, -3), "abc".substr(2, -4),
"abc".substr(3, -1), "abc".substr(3, -2), "abc".substr(3, -3), "abc".substr(3, -4),
]`,
expected: ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
labels: [
"0, -1", "0, -2", "0, -3", "0, -4", "1, -1", "1, -2", "1, -3", "1, -4",
"2, -1", "2, -2", "2, -3", "2, -4", "3, -1", "3, -2", "3, -3", "3, -4",
],
},
{
path: "test/annexB/built-ins/String/prototype/substr/length-positive.js",
code: `return [
"abc".substr(0, 1), "abc".substr(0, 2), "abc".substr(0, 3), "abc".substr(0, 4),
"abc".substr(1, 1), "abc".substr(1, 2), "abc".substr(1, 3), "abc".substr(1, 4),
"abc".substr(2, 1), "abc".substr(2, 2), "abc".substr(2, 3), "abc".substr(2, 4),
"abc".substr(3, 1), "abc".substr(3, 2), "abc".substr(3, 3), "abc".substr(3, 4),
]`,
expected: ["a", "ab", "abc", "abc", "b", "bc", "bc", "bc", "c", "c", "c", "c", "", "", "", ""],
labels: [
"0, 1", "0, 1", "0, 1", "0, 1", "1, 1", "1, 1", "1, 1", "1, 1",
"2, 1", "2, 1", "2, 1", "2, 1", "3, 1", "3, 1", "3, 1", "3, 1",
],
},
{
path: "test/annexB/built-ins/String/prototype/substr/length-falsey.js",
code: `return ["abc".substr(0, NaN), "abc".substr(1, NaN), "abc".substr(2, NaN), "abc".substr(3, NaN)]`,
expected: ["", "", "", ""],
labels: ["start: 0, length: NaN", "start: 1, length: NaN", "start: 2, length: NaN", "start: 3, length: NaN"],
},
{
path: "test/annexB/built-ins/String/prototype/substr/length-undef.js",
code: `return [
"abc".substr(0), "abc".substr(1), "abc".substr(2), "abc".substr(3),
"abc".substr(0, undefined), "abc".substr(1, undefined), "abc".substr(2, undefined), "abc".substr(3, undefined),
]`,
expected: ["abc", "bc", "c", "", "abc", "bc", "c", ""],
labels: [
"start: 0, length: unspecified", "start: 1, length: unspecified", "start: 2, length: unspecified", "start: 3, length: unspecified",
"start: 0, length: undefined", "start: 1, length: undefined", "start: 2, length: undefined", "start: 3, length: undefined",
],
},
{
path: "test/annexB/built-ins/String/prototype/substr/surrogate-pairs.js",
code: `return [
"\uD834\uDF06".substr(0), "\uD834\uDF06".substr(1), "\uD834\uDF06".substr(2),
"\uD834\uDF06".substr(0, 0), "\uD834\uDF06".substr(0, 1), "\uD834\uDF06".substr(0, 2),
]`,
expected: ["\uD834\uDF06", "\uDF06", "", "", "\uD834", "\uD834\uDF06"],
labels: ["start: 0", "start: 1", "start: 2", "end: 0", "end: 1", "end: 2"],
},
{
path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailMissingLetter.js",
code: `return ["word".includes("a", 0)]`, expected: [false], labels: ['"word".includes("a", 0)'],
},
{
path: "test/built-ins/String/prototype/includes/String.prototype.includes_SuccessNoLocation.js",
code: `return ["word".includes("w")]`, expected: [true], labels: ['"word".includes("w")'],
},
{
path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailBadLocation.js",
code: `return ["word".includes("w", 5)]`, expected: [false], labels: ['"word".includes("w", 5)'],
},
{
path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailLocation.js",
code: `return ["word".includes("o", 3)]`, expected: [false], labels: ['"word".includes("o", 3)'],
},
{
path: "test/built-ins/String/prototype/includes/String.prototype.includes_Success.js",
code: `return ["word".includes("w", 0)]`, expected: [true], labels: ['"word".includes("w", 0)'],
},
{
path: "test/built-ins/String/prototype/includes/searchstring-found-with-position.js",
code: `const text = "The future is cool!"; return [text.includes("The future", 0), text.includes(" is ", 1), text.includes("cool!", 10)]`,
expected: [true, true, true],
labels: [
'Returns true for str.includes("The future", 0)',
'Returns true for str.includes(" is ", 1)',
'Returns true for str.includes("cool!", 10)',
],
},
{
path: "test/built-ins/String/prototype/includes/searchstring-found-without-position.js",
code: `const text = "The future is cool!"; return [text.includes("The future"), text.includes("is cool!"), text.includes(text)]`,
expected: [true, true, true],
labels: [
'Returns true for str.includes("The future")',
'Returns true for str.includes("is cool!")',
"Returns true for str.includes(str)",
],
},
{
path: "test/built-ins/String/prototype/includes/searchstring-not-found-with-position.js",
code: `const text = "The future is cool!"; return [text.includes("The future", 1), text.includes(text, 1)]`,
expected: [false, false],
labels: ['Returns false on str.includes("The future", 1)', "Returns false on str.includes(str, 1)"],
},
{
path: "test/built-ins/String/prototype/includes/searchstring-not-found-without-position.js",
code: `const text = "The future is cool!"; return [text.includes("Flash"), text.includes("FUTURE")]`,
expected: [false, false], labels: ["Flash if not included", "includes is case sensitive"],
},
{
path: "test/built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js",
code: `const text = "The future is cool!"; return [
text.includes("!", text.length + 1), text.includes("!", 100), text.includes("!", Infinity), text.includes("!", text.length),
]`,
expected: [false, false, false, false],
labels: [
'str.includes("!", str.length + 1) returns false', 'str.includes("!", 100) returns false',
'str.includes("!", Infinity) returns false', 'str.includes("!", str.length) returns false',
],
},
{
path: "test/built-ins/String/prototype/includes/return-true-if-searchstring-is-empty.js",
code: `const text = "The future is cool!"; return [text.includes("", text.length), text.includes(""), text.includes("", Infinity)]`,
expected: [true, true, true],
labels: ['str.includes("", str.length) returns true', 'str.includes("") returns true', 'str.includes("", Infinity) returns true'],
},
{
path: "test/built-ins/String/prototype/includes/coerced-values-of-position.js",
code: `const text = "The future is cool!"; return [
text.includes("The future", NaN), text.includes("The future", undefined), text.includes("The future", 0.4),
text.includes("The future", -1), text.includes("The future", 1.4),
]`,
expected: [true, true, true, true, false],
labels: ["NaN coerced to 0", "undefined coerced to 0", "0.4 coerced to 0", "negative position", "1.4 coerced to 1"],
},
{
path: "test/built-ins/String/prototype/startsWith/searchstring-found-with-position.js",
code: `const text = "The future is cool!"; return [text.startsWith("The future", 0), text.startsWith("future", 4), text.startsWith(" is cool!", 10)]`,
expected: [true, true, true],
labels: [
'str.startsWith("The future", 0) === true', 'str.startsWith("future", 4) === true',
'str.startsWith(" is cool!", 10) === true',
],
},
{
path: "test/built-ins/String/prototype/startsWith/searchstring-found-without-position.js",
code: `const text = "The future is cool!"; return [text.startsWith("The "), text.startsWith("The future"), text.startsWith(text)]`,
expected: [true, true, true],
labels: ['str.startsWith("The ") === true', 'str.startsWith("The future") === true', "str.startsWith(str) === true"],
},
{
path: "test/built-ins/String/prototype/startsWith/searchstring-not-found-with-position.js",
code: `const text = "The future is cool!"; return [text.startsWith("The future", 1), text.startsWith(text, 1)]`,
expected: [false, false],
labels: ['str.startsWith("The future", 1) === false', "str.startsWith(str, 1) === false"],
},
{
path: "test/built-ins/String/prototype/startsWith/searchstring-not-found-without-position.js",
code: `const text = "The future is cool!"; return [text.startsWith("Flash"), text.startsWith("THE FUTURE"), text.startsWith("future is cool!")]`,
expected: [false, false, false],
labels: ['str.startsWith("Flash") === false', "startsWith is case sensitive", 'str.startsWith("future is cool!") === false'],
},
{
path: "test/built-ins/String/prototype/startsWith/out-of-bounds-position.js",
code: `const text = "The future is cool!"; return [
text.startsWith("!", text.length), text.startsWith("!", 100), text.startsWith("!", Infinity),
text.startsWith("The future", -1), text.startsWith("The future", -Infinity),
]`,
expected: [false, false, false, true, true],
labels: [
'str.startsWith("!", str.length) returns false', 'str.startsWith("!", 100) returns false',
'str.startsWith("!", Infinity) returns false', "position argument < 0 will search from the start of the string (-1)",
"position argument < 0 will search from the start of the string (-Infinity)",
],
},
{
path: "test/built-ins/String/prototype/startsWith/return-true-if-searchstring-is-empty.js",
code: `const text = "The future is cool!"; return [text.startsWith(""), text.startsWith("", text.length), text.startsWith("", Infinity)]`,
expected: [true, true, true],
labels: ['str.startsWith("") returns true', 'str.startsWith("", str.length) returns true', 'str.startsWith("", Infinity) returns true'],
},
{
path: "test/built-ins/String/prototype/startsWith/coerced-values-of-position.js",
code: `const text = "The future is cool!"; return [
text.startsWith("The future", NaN), text.startsWith("The future", undefined),
text.startsWith("The future", 0.4), text.startsWith("The future", 1.4),
]`,
expected: [true, true, true, false],
labels: ["NaN coerced to 0", "undefined coerced to 0", "0.4 coerced to 0", "1.4 coerced to 1"],
},
{
path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success.js",
code: `return ["word".endsWith("d")]`, expected: [true], labels: ['"word".endsWith("d")'],
},
{
path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_2.js",
code: `return ["word".endsWith("d", 4)]`, expected: [true], labels: ['"word".endsWith("d", 4)'],
},
{
path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_3.js",
code: `return ["word".endsWith("d", 25)]`, expected: [true], labels: ['"word".endsWith("d", 25)'],
},
{
path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_4.js",
code: `return ["word".endsWith("r", 3)]`, expected: [true], labels: ['"word".endsWith("r", 3)'],
},
{
path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail.js",
code: `return ["word".endsWith("r")]`, expected: [false], labels: ['"word".endsWith("r")'],
},
{
path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail_2.js",
code: `return ["word".endsWith("d", 3)]`, expected: [false], labels: ['"word".endsWith("d", 3)'],
},
{
path: "test/built-ins/String/prototype/endsWith/searchstring-found-with-position.js",
code: `const text = "The future is cool!"; return [text.endsWith("The future", 10), text.endsWith("future", 10), text.endsWith(" is cool!", text.length)]`,
expected: [true, true, true],
labels: [
'str.endsWith("The future", 10) === true', 'str.endsWith("future", 10) === true',
'str.endsWith(" is cool!", str.length) === true',
],
},
{
path: "test/built-ins/String/prototype/endsWith/searchstring-found-without-position.js",
code: `const text = "The future is cool!"; return [text.endsWith("cool!"), text.endsWith("!"), text.endsWith(text)]`,
expected: [true, true, true],
labels: ['str.endsWith("cool!") === true', 'str.endsWith("!") === true', "str.endsWith(str) === true"],
},
{
path: "test/built-ins/String/prototype/endsWith/searchstring-not-found-with-position.js",
code: `const text = "The future is cool!"; return [text.endsWith("is cool!", text.length - 1), text.endsWith("!", 1)]`,
expected: [false, false],
labels: ['str.endsWith("is cool!", str.length - 1) === false', 'str.endsWith("!", 1) === false'],
},
{
path: "test/built-ins/String/prototype/endsWith/searchstring-not-found-without-position.js",
code: `const text = "The future is cool!"; return [text.endsWith("is Flash!"), text.endsWith("IS COOL!"), text.endsWith("The future")]`,
expected: [false, false, false],
labels: ['str.endsWith("is Flash!") === false', "endsWith is case sensitive", 'str.endsWith("The future") === false'],
},
{
path: "test/built-ins/String/prototype/endsWith/return-false-if-search-start-is-less-than-zero.js",
code: `return ["web".endsWith("w", 0), "Bob".endsWith(" Bob")]`,
expected: [false, false],
labels: ['"web".endsWith("w", 0) returns false', '"Bob".endsWith(" Bob") returns false'],
},
{
path: "test/built-ins/String/prototype/endsWith/return-true-if-searchstring-is-empty.js",
code: `const text = "The future is cool!"; return [
text.endsWith(""), text.endsWith("", text.length), text.endsWith("", Infinity),
text.endsWith("", -1), text.endsWith("", -Infinity),
]`,
expected: [true, true, true, true, true],
labels: [
'str.endsWith("") returns true', 'str.endsWith("", str.length) returns true', 'str.endsWith("", Infinity) returns true',
'str.endsWith("", -1) returns true', 'str.endsWith("", -Infinity) returns true',
],
},
{
path: "test/built-ins/String/prototype/endsWith/coerced-values-of-position.js",
code: `const text = "The future is cool!"; return [
text.endsWith("", NaN), text.endsWith("", undefined), text.endsWith("The future", 10.4),
]`,
expected: [true, true, true],
labels: ["NaN coerced to 0", "undefined coerced to 0", "10.4 coerced to 10"],
},
{
path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T1.js",
code: `return ["abcd".indexOf("abcdab")]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab")===-1'],
},
{
path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T2.js",
code: `return ["abcd".indexOf("abcdab", 0)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",0)===-1'],
},
{
path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T3.js",
code: `return ["abcd".indexOf("abcdab", 99)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",99)===-1'],
},
{
path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T4.js",
code: `return ["abcd".indexOf("abcdab", NaN)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",NaN)===-1'],
},
{
path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T1.js",
code: `return ["$$abcdabcd".indexOf("ab", NaN)]`, expected: [2], labels: ['#1: "$$abcdabcd".indexOf("ab",NaN)===2'],
},
{
path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T3.js",
code: `return ["$$abcdabcd".indexOf("ab", -Infinity)]`, expected: [2], labels: ['#1: "$$abcdabcd".indexOf("ab", function(){return -Infinity;}())===2'],
},
{
path: "test/built-ins/String/prototype/indexOf/position-tointeger.js",
code: `return [
"aaaa".indexOf("aa", 0), "aaaa".indexOf("aa", 1), "aaaa".indexOf("aa", -0.9),
"aaaa".indexOf("aa", 0.9), "aaaa".indexOf("aa", 1.9), "aaaa".indexOf("aa", NaN),
"aaaa".indexOf("aa", Infinity), "aaaa".indexOf("aa", undefined),
"aaaa".indexOf("aa", 2), "aaaa".indexOf("aa", 2.9),
]`,
expected: [0, 1, 0, 0, 1, 0, -1, 0, 2, 2],
labels: [
"position 0", "position 1", "ToInteger: truncate towards 0 (-0.9)", "ToInteger: truncate towards 0 (0.9)",
"ToInteger: truncate towards 0 (1.9)", "ToInteger: NaN => 0", "position Infinity",
"ToInteger: undefined => NaN => 0", "position 2", "ToInteger: truncate towards 0 (2.9)",
],
},
{
path: "test/built-ins/String/prototype/indexOf/searchstring-tostring.js",
code: `return ["foo".indexOf(""), "__foo__".indexOf("foo")]`,
expected: [0, 2], labels: ['"foo".indexOf("")', '"__foo__".indexOf("foo")'],
},
{
path: "test/built-ins/String/prototype/lastIndexOf/not-a-substring.js",
code: `return ["abc".lastIndexOf("d")]`,
expected: [-1],
labels: ["String.prototype.lastIndexOf returns -1 when searchString is shorter than this and searchString is not a substring of this."],
},
] as const
describe("Test262-adapted String search and extraction behavior", () => {
for (const item of cases) {
test(item.path, async () => {
const actual = await value(item.code)
if (!Array.isArray(actual)) throw new Error(`expected assertion values for ${item.path}`)
expect(actual.length, "adapted assertion count").toBe(item.expected.length)
item.expected.forEach((expected, index) => expect(actual[index], item.labels[index]!).toEqual(expected))
})
}
})

View file

@ -0,0 +1,77 @@
# Test262 Array Coverage
The Array tests adapt Test262 at revision `250f204f23a9249ff204be2baec29600faae7b75`. They cover CodeMode's 35
exposed instance methods and three static methods using actual arrays, accepted argument types, deterministic behavior,
and CodeMode's materialized collection conventions. Each executable case names its exact upstream source path.
`LICENSE.test262` contains the upstream BSD terms.
This is coverage of CodeMode's bounded Array surface, not a claim of ECMAScript or Test262 conformance. One upstream
file may contain both adapted and inapplicable assertions, so a cited source means only that the represented assertions
were adapted.
## Inventory
The 38 relevant upstream API directories contain 2,837 files. The executable suite adapts assertions from 83 distinct
sources.
| API | Upstream files | Adapted sources |
| ------------------------------- | -------------: | --------------: |
| `Array.prototype.map` | 216 | 3 |
| `Array.prototype.filter` | 242 | 3 |
| `Array.prototype.find` | 23 | 4 |
| `Array.prototype.findIndex` | 23 | 3 |
| `Array.prototype.findLast` | 24 | 3 |
| `Array.prototype.findLastIndex` | 24 | 3 |
| `Array.prototype.some` | 219 | 2 |
| `Array.prototype.every` | 218 | 2 |
| `Array.prototype.includes` | 30 | 2 |
| `Array.prototype.join` | 23 | 2 |
| `Array.prototype.reduce` | 260 | 3 |
| `Array.prototype.reduceRight` | 260 | 3 |
| `Array.prototype.flatMap` | 24 | 2 |
| `Array.prototype.forEach` | 190 | 2 |
| `Array.prototype.sort` | 54 | 3 |
| `Array.prototype.toSorted` | 21 | 4 |
| `Array.prototype.slice` | 71 | 1 |
| `Array.prototype.concat` | 69 | 3 |
| `Array.prototype.indexOf` | 201 | 2 |
| `Array.prototype.lastIndexOf` | 198 | 2 |
| `Array.prototype.at` | 13 | 3 |
| `Array.prototype.flat` | 19 | 2 |
| `Array.prototype.reverse` | 18 | 1 |
| `Array.prototype.toReversed` | 17 | 2 |
| `Array.prototype.with` | 21 | 2 |
| `Array.prototype.push` | 24 | 1 |
| `Array.prototype.pop` | 23 | 1 |
| `Array.prototype.shift` | 20 | 1 |
| `Array.prototype.unshift` | 22 | 1 |
| `Array.prototype.splice` | 81 | 3 |
| `Array.prototype.fill` | 22 | 3 |
| `Array.prototype.copyWithin` | 39 | 2 |
| `Array.prototype.keys` | 12 | 1 |
| `Array.prototype.values` | 12 | 1 |
| `Array.prototype.entries` | 12 | 1 |
| `Array.from` | 47 | 3 |
| `Array.isArray` | 29 | 2 |
| `Array.of` | 16 | 1 |
## Exclusions
Assertions are not adapted when they test behavior outside CodeMode's documented Array surface:
- Function metadata, property descriptors, constructibility, prototype mutation, species constructors, or cross-realm
identity.
- Generic receivers, detached methods, `.call`, `.apply`, boxed values, custom coercion objects, Symbols, BigInts,
proxies, accessors, frozen arrays, typed arrays, or ArrayBuffers.
- `Array.from` mappers, custom iterables, constructor substitution, and iterator-closing behavior.
- Native iterator identity, `.next()`, completion records, or live iterator mutation. CodeMode deliberately materializes
`keys`, `values`, and `entries` as arrays.
- Sparse-array assertions that depend on literal elisions or inherited indexed properties. CodeMode's confined data
model does not preserve those prototype and hole semantics at every boundary.
- Argument coercions outside the accepted schema-like surface. Numeric positions must be numbers and `join` separators
must be strings.
- Exact native error brands where CodeMode exposes a safe runtime error instead.
- Async/effectful callbacks, circular-data rejection, sandbox-value identity, diagnostics, and host-boundary behavior.
Those remain covered by CodeMode-specific tests.
Handwritten tests remain where they specify CodeMode behavior rather than ordinary ECMAScript Array semantics.

View file

@ -0,0 +1,69 @@
# Test262 String Coverage
The String tests adapt Test262 at revision `250f204f23a9249ff204be2baec29600faae7b75`. They cover CodeMode's 32
exposed instance methods and two static methods using primitive receivers, accepted argument types, and deterministic
behavior. Each executable case names its exact upstream source path. `LICENSE.test262` contains the upstream BSD terms.
This is coverage of CodeMode's bounded String surface, not a claim of ECMAScript or Test262 conformance. One upstream
file may contain both adapted and inapplicable assertions, so a cited source means only that the represented assertions
were adapted.
## Inventory
The relevant upstream directories contain 1,048 files: 1,009 core built-in files, 29 Annex B files for exposed methods,
and 10 Intl `localeCompare` files. The executable suite adapts assertions from 298 distinct sources.
| API | Upstream files | Adapted sources |
| --- | ---: | ---: |
| `String.fromCharCode` | 17 | 6 |
| `String.fromCodePoint` | 11 | 4 |
| `String.prototype.at` | 11 | 5 |
| `String.prototype.charAt` | 30 | 9 |
| `String.prototype.charCodeAt` | 25 | 4 |
| `String.prototype.codePointAt` | 16 | 6 |
| `String.prototype.concat` | 22 | 1 |
| `String.prototype.endsWith` | 27 | 13 |
| `String.prototype.includes` | 27 | 12 |
| `String.prototype.indexOf` | 47 | 8 |
| `String.prototype.lastIndexOf` | 25 | 1 |
| `String.prototype.localeCompare` | 23 | 1 |
| `String.prototype.match` | 52 | 9 |
| `String.prototype.matchAll` | 26 | 1 |
| `String.prototype.normalize` | 14 | 3 |
| `String.prototype.padEnd` | 13 | 4 |
| `String.prototype.padStart` | 13 | 4 |
| `String.prototype.repeat` | 16 | 4 |
| `String.prototype.replace` | 56 | 16 |
| `String.prototype.replaceAll` | 46 | 12 |
| `String.prototype.search` | 44 | 10 |
| `String.prototype.slice` | 38 | 11 |
| `String.prototype.split` | 121 | 50 |
| `String.prototype.startsWith` | 21 | 7 |
| `String.prototype.substr` | 15 | 6 |
| `String.prototype.substring` | 46 | 12 |
| `String.prototype.toLowerCase` | 30 | 5 |
| `String.prototype.toString` | 7 | 1 |
| `String.prototype.toUpperCase` | 26 | 3 |
| `String.prototype.trim` | 129 | 66 |
| `String.prototype.trimEnd` | 23 | 2 |
| `String.prototype.trimLeft` | 4 | 0 |
| `String.prototype.trimRight` | 4 | 0 |
| `String.prototype.trimStart` | 23 | 2 |
## Exclusions
Assertions are not adapted when they test behavior outside CodeMode's documented String surface:
- Function metadata, property descriptors, constructibility, prototype mutation, or cross-realm identity.
- The `trimLeft`/`trimRight` Test262 files assert prototype function identity, which CodeMode does not expose. Their
supported call behavior remains covered by CodeMode-specific tests.
- Boxed strings, generic receivers, custom coercion objects, Symbols, BigInts, or argument types CodeMode rejects.
- Symbol-based RegExp dispatch, custom matchers, species constructors, or iterator protocol details. CodeMode materializes
`matchAll` results instead of exposing iterators.
- Locale selection and options. CodeMode deliberately uses the host default locale and ignores those arguments.
- Test262 harness behavior or setup syntax unavailable in the confined interpreter.
- Function-replacer behavior that is covered by CodeMode-specific tests for sequential callbacks, async tool calls,
result coercion, diagnostics, and sandbox boundaries.
- Assertions requiring an exact native error type when CodeMode deliberately exposes only its safe runtime error.
Handwritten tests remain where they specify CodeMode behavior rather than ordinary ECMAScript String semantics.