@opencode/codemode 0.0.0-beta-19507 → 0.0.0-dev-19274

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +0 -6
  2. package/dist/codemode.d.ts +12 -9
  3. package/dist/codemode.js +9 -5
  4. package/dist/index.d.ts +0 -1
  5. package/dist/index.js +0 -1
  6. package/dist/interpreter/errors.d.ts +6 -4
  7. package/dist/interpreter/errors.js +10 -25
  8. package/dist/interpreter/execute.d.ts +3 -4
  9. package/dist/interpreter/execute.js +18 -11
  10. package/dist/interpreter/iterator.d.ts +13 -0
  11. package/dist/interpreter/iterator.js +4 -0
  12. package/dist/interpreter/methods.d.ts +16 -3
  13. package/dist/interpreter/methods.js +290 -101
  14. package/dist/interpreter/model.d.ts +79 -10
  15. package/dist/interpreter/model.js +102 -2
  16. package/dist/interpreter/promises.d.ts +13 -15
  17. package/dist/interpreter/promises.js +52 -70
  18. package/dist/interpreter/references.d.ts +0 -1
  19. package/dist/interpreter/references.js +77 -57
  20. package/dist/interpreter/runtime.d.ts +95 -16
  21. package/dist/interpreter/runtime.js +960 -549
  22. package/dist/openapi/spec.js +6 -3
  23. package/dist/stdlib/collections.d.ts +1 -6
  24. package/dist/stdlib/collections.js +1 -117
  25. package/dist/stdlib/console.d.ts +2 -3
  26. package/dist/stdlib/console.js +28 -39
  27. package/dist/stdlib/date.d.ts +4 -5
  28. package/dist/stdlib/date.js +12 -34
  29. package/dist/stdlib/json.d.ts +6 -3
  30. package/dist/stdlib/json.js +63 -40
  31. package/dist/stdlib/math.d.ts +7 -3
  32. package/dist/stdlib/math.js +153 -85
  33. package/dist/stdlib/number.d.ts +4 -2
  34. package/dist/stdlib/number.js +37 -30
  35. package/dist/stdlib/object.d.ts +6 -6
  36. package/dist/stdlib/object.js +87 -84
  37. package/dist/stdlib/promise.d.ts +2 -0
  38. package/dist/stdlib/promise.js +1 -0
  39. package/dist/stdlib/regexp.d.ts +7 -6
  40. package/dist/stdlib/regexp.js +34 -48
  41. package/dist/stdlib/string.d.ts +3 -1
  42. package/dist/stdlib/string.js +17 -20
  43. package/dist/stdlib/url.d.ts +6 -10
  44. package/dist/stdlib/url.js +25 -102
  45. package/dist/stdlib/value.d.ts +7 -8
  46. package/dist/stdlib/value.js +56 -56
  47. package/dist/tool-runtime.d.ts +15 -16
  48. package/dist/tool-runtime.js +150 -13
  49. package/dist/values.d.ts +16 -22
  50. package/dist/values.js +17 -23
  51. package/package.json +1 -1
  52. package/dist/data.d.ts +0 -25
  53. package/dist/data.js +0 -153
  54. package/dist/interpreter/globals.d.ts +0 -13
  55. package/dist/interpreter/globals.js +0 -63
  56. package/dist/interpreter/host.d.ts +0 -41
  57. package/dist/interpreter/host.js +0 -44
  58. package/dist/interpreter/objects.d.ts +0 -37
  59. package/dist/interpreter/objects.js +0 -151
  60. package/dist/interpreter/runner.d.ts +0 -24
  61. package/dist/interpreter/runner.js +0 -45
  62. package/dist/stdlib/array.d.ts +0 -3
  63. package/dist/stdlib/array.js +0 -68
  64. package/dist/stdlib/web.d.ts +0 -4
  65. package/dist/stdlib/web.js +0 -20
@@ -1,223 +1,200 @@
1
1
  import { Cause, Deferred, Effect, Exit } from "effect";
2
- import { ToolRuntimeError, toProgram } from "../data.js";
3
- import { ToolReference } from "../tool-runtime.js";
4
- import { AsyncIteratorSymbol, CodeModeGenerator, ComputedValue, GeneratorMethodReference, GeneratorReturn, IntrinsicReference, InterpreterRuntimeError, IteratorSymbol, OptionalShortCircuit, PromiseInstanceMethodReference, ProgramThrow, unsupportedSyntax, } from "./model.js";
5
- import { caughtErrorValue } from "./errors.js";
6
- import { globals } from "./globals.js";
7
- import { HostFunction, HostNamespace } from "./host.js";
8
- import { invokeIntrinsic } from "./methods.js";
9
- import { assign, get, has, ownKeys, parseArrayIndex, ProgramArray, ProgramFunction, ProgramObject, record, remove, set, } from "./objects.js";
10
- import { preserveConsumerError } from "./runner.js";
11
- import { invokePromiseInstanceMethod, PromiseRuntime, resolvePromise, resolvePromiseValue } from "./promises.js";
12
- import { containsOpaqueReference, describeValue, isRuntimeReference, rejectCircularInsertion, typeofValue, } from "./references.js";
2
+ import { isBlockedMember, ToolReference, ToolRuntimeError } from "../tool-runtime.js";
3
+ import { AsyncIteratorSymbol, asNode, CodeModeFunction, CodeModeGenerator, CoercionFunction, ComputedValue, ErrorConstructorReference, GlobalMethodReference, GlobalNamespace, GeneratorMethodReference, GeneratorReturn, getArray, getBoolean, getNode, getOptionalNode, getString, IntrinsicReference, InterpreterRuntimeError, isRecord, IteratorSymbol, IteratorSymbols, JsonMethodReference, OptionalShortCircuit, PromiseCapabilityFunction, PromiseInstanceMethodReference, PromiseMethodReference, PromiseNamespace, ProgramThrow, SearchFunction, SymbolNamespace, unsupportedSyntax, UriFunction, } from "./model.js";
4
+ import { caughtErrorValue, constructAggregateErrorValue, constructErrorValue } from "./errors.js";
5
+ import { arrayStatics, invokeArrayFrom, invokeGlobalMethod, invokeGroupBy, invokeIntrinsic, } from "./methods.js";
6
+ import { preserveConsumerError } from "./iterator.js";
7
+ import { constructPromise, invokePromiseInstanceMethod, invokePromiseMethod, PromiseRuntime, resolvePromise, resolvePromiseValue, } from "./promises.js";
8
+ import { containsOpaqueReference, isRuntimeReference, rejectCircularInsertion, typeofValue } from "./references.js";
13
9
  import { ScopeStack } from "./scope.js";
14
- import { arrayMethods, mapMethods, setMethods } from "../stdlib/collections.js";
15
- import { dateMethods } from "../stdlib/date.js";
16
- import { numberMethods } from "../stdlib/number.js";
17
- import { constructRegExp, regexpMethods, regexpProperties } from "../stdlib/regexp.js";
18
- import { stringMethods } from "../stdlib/string.js";
19
- import { uriArgument, urlMethods, urlProperties, urlSearchParamsMethods, urlWritableProperties } from "../stdlib/url.js";
20
- import { enumerableSource } from "../stdlib/object.js";
21
- import { coerceToNumber, coerceToString, compoundOperators, errorBrandName } from "../stdlib/value.js";
22
- import { Values } from "../values.js";
23
- // What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating.
24
- // Unlabelled break ends this loop; a label the loop does not carry propagates outward.
25
- const loopExit = (result, labels) => {
26
- if (result.kind === "return")
27
- return result;
28
- if (result.kind === "break") {
29
- if (result.label !== undefined && !labels?.has(result.label))
30
- return result;
31
- return { kind: "none" };
32
- }
33
- if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label))
34
- return result;
35
- return undefined;
10
+ import { arrayMethods, mapMethods, mapStatics, setMethods } from "../stdlib/collections.js";
11
+ import { consoleMethods, formatConsoleMessage } from "../stdlib/console.js";
12
+ import { dateMethods, dateStatics } from "../stdlib/date.js";
13
+ import { invokeJsonMethod, jsonStatics } from "../stdlib/json.js";
14
+ import { invokeMathSumPrecise, mathConstants, mathMethods } from "../stdlib/math.js";
15
+ import { numberConstants, numberMethods, numberStatics } from "../stdlib/number.js";
16
+ import { invokeObjectFromEntries, objectMethodsPreservingIdentity, objectStatics } from "../stdlib/object.js";
17
+ import { promiseStatics } from "../stdlib/promise.js";
18
+ import { escapeRegexHint, regexpMethods, regexpProperties, regexpStatics, regexFailureReason, } from "../stdlib/regexp.js";
19
+ import { stringMethods, stringStatics } from "../stdlib/string.js";
20
+ import { urlMethods, urlProperties, urlSearchParamsMethods, urlStatics, urlWritableProperties, invokeUriFunction, uriArgument, urlArgument, } from "../stdlib/url.js";
21
+ import { boundedData, coerceToNumber, coerceToString, compoundOperators, errorBrandName, errorConstructors, invokeCoercion, valueConstructors, } from "../stdlib/value.js";
22
+ import { isCodeModeValue, CodeModeDate, CodeModeMap, CodeModePromise, CodeModeRegExp, CodeModeSet, CodeModeURL, CodeModeURLSearchParams, } from "../values.js";
23
+ const globalStaticMembers = {
24
+ Object: objectStatics,
25
+ Math: mathMethods,
26
+ Array: arrayStatics,
27
+ console: consoleMethods,
28
+ Date: dateStatics,
29
+ RegExp: regexpStatics,
30
+ Map: mapStatics,
31
+ URL: urlStatics,
32
+ };
33
+ const MAX_ARRAY_LENGTH = 4_294_967_295;
34
+ const parseArrayIndex = (key) => {
35
+ const property = String(key);
36
+ if (!/^(0|[1-9]\d*)$/.test(property))
37
+ return undefined;
38
+ const index = Number(property);
39
+ return index < MAX_ARRAY_LENGTH ? index : undefined;
36
40
  };
37
41
  const calleeDescription = (callee) => {
38
- if (callee?.type === "Identifier")
39
- return callee.name;
40
- if (callee?.type === "MemberExpression") {
41
- const object = callee.object;
42
- const property = callee.property;
43
- const key = !callee.computed && property.type === "Identifier"
44
- ? property.name
42
+ if (callee.type === "Identifier")
43
+ return getString(callee, "name");
44
+ if (callee.type === "MemberExpression") {
45
+ const object = getNode(callee, "object");
46
+ const property = getNode(callee, "property");
47
+ const key = callee.computed !== true && property.type === "Identifier"
48
+ ? getString(property, "name")
45
49
  : property.type === "Literal" && typeof property.value === "string"
46
50
  ? property.value
47
51
  : undefined;
48
52
  if (object.type === "Identifier" && key !== undefined)
49
- return `${object.name}.${key}`;
53
+ return `${getString(object, "name")}.${key}`;
50
54
  }
51
55
  return "The called value";
52
56
  };
53
- const constructorName = (value) => {
54
- if (typeof value === "string")
55
- return "String";
56
- if (typeof value === "number")
57
- return "Number";
58
- if (typeof value === "boolean")
59
- return "Boolean";
60
- if (value instanceof ProgramArray)
61
- return "Array";
62
- if (value instanceof Values.Date)
63
- return "Date";
64
- if (value instanceof Values.RegExp)
65
- return "RegExp";
66
- if (value instanceof Values.Map)
67
- return "Map";
68
- if (value instanceof Values.Set)
69
- return "Set";
70
- if (value instanceof Values.URL)
71
- return "URL";
72
- if (value instanceof Values.URLSearchParams)
73
- return "URLSearchParams";
74
- if (value instanceof Values.Promise)
75
- return "Promise";
76
- if (!(value instanceof ProgramObject) || value instanceof ProgramFunction)
77
- return undefined;
78
- return errorBrandName(value) ?? "Object";
79
- };
80
57
  const instanceofValue = (lhs, rhs, node) => {
81
- if (rhs instanceof HostFunction && rhs.instanceOf !== undefined)
82
- return rhs.instanceOf(lhs);
58
+ if (rhs instanceof ErrorConstructorReference) {
59
+ const brand = errorBrandName(lhs);
60
+ return brand !== undefined && (rhs.name === "Error" || brand === rhs.name);
61
+ }
62
+ if (rhs instanceof GlobalNamespace) {
63
+ switch (rhs.name) {
64
+ case "Date":
65
+ return lhs instanceof CodeModeDate;
66
+ case "RegExp":
67
+ return lhs instanceof CodeModeRegExp;
68
+ case "Map":
69
+ return lhs instanceof CodeModeMap;
70
+ case "Set":
71
+ return lhs instanceof CodeModeSet;
72
+ case "URL":
73
+ return lhs instanceof CodeModeURL;
74
+ case "URLSearchParams":
75
+ return lhs instanceof CodeModeURLSearchParams;
76
+ case "Array":
77
+ return Array.isArray(lhs);
78
+ case "Object":
79
+ return lhs !== null && (typeof lhs === "object" || typeofValue(lhs) === "function");
80
+ }
81
+ }
82
+ if (rhs instanceof PromiseNamespace)
83
+ return lhs instanceof CodeModePromise;
84
+ if (rhs instanceof CoercionFunction && (rhs.name === "Number" || rhs.name === "String" || rhs.name === "Boolean")) {
85
+ return false;
86
+ }
83
87
  throw new InterpreterRuntimeError("The right-hand side of 'instanceof' must be a supported constructor: Error (or a specific error type like TypeError), Date, RegExp, Map, Set, URL, URLSearchParams, Array, Object, or Promise.", node);
84
88
  };
85
89
  const collectPatternNames = (pattern, out = []) => {
86
90
  switch (pattern.type) {
87
91
  case "Identifier":
88
- out.push(pattern.name);
92
+ out.push(getString(pattern, "name"));
89
93
  break;
90
94
  case "AssignmentPattern":
91
- collectPatternNames(pattern.left, out);
95
+ collectPatternNames(getNode(pattern, "left"), out);
92
96
  break;
93
97
  case "RestElement":
94
- collectPatternNames(pattern.argument, out);
98
+ collectPatternNames(getNode(pattern, "argument"), out);
95
99
  break;
96
100
  case "ArrayPattern":
97
- for (const element of pattern.elements) {
101
+ for (const element of getArray(pattern, "elements")) {
98
102
  if (element !== null)
99
- collectPatternNames(element, out);
103
+ collectPatternNames(asNode(element, "elements"), out);
100
104
  }
101
105
  break;
102
106
  case "ObjectPattern":
103
- for (const prop of pattern.properties) {
104
- collectPatternNames(prop.type === "RestElement" ? prop.argument : prop.value, out);
107
+ for (const property of getArray(pattern, "properties")) {
108
+ const prop = asNode(property, "properties");
109
+ collectPatternNames(prop.type === "RestElement" ? getNode(prop, "argument") : getNode(prop, "value"), out);
105
110
  }
106
111
  break;
107
112
  }
108
113
  return out;
109
114
  };
110
- // `var` names declared anywhere in a function body except inside nested functions, which own theirs.
111
- // Memoized per body: a function's var names never change, and hoisting runs on every call.
112
- const varNames = new WeakMap();
113
- const collectVarNames = (node, out = []) => {
114
- if (!node)
115
- return out;
116
- switch (node.type) {
117
- case "VariableDeclaration":
118
- if (node.kind === "var")
119
- for (const declaration of node.declarations)
120
- collectPatternNames(declaration.id, out);
121
- break;
122
- case "BlockStatement":
123
- for (const statement of node.body)
124
- collectVarNames(statement, out);
125
- break;
126
- case "IfStatement":
127
- collectVarNames(node.consequent, out);
128
- collectVarNames(node.alternate, out);
129
- break;
130
- case "ForStatement":
131
- if (node.init?.type === "VariableDeclaration")
132
- collectVarNames(node.init, out);
133
- collectVarNames(node.body, out);
134
- break;
135
- case "ForInStatement":
136
- case "ForOfStatement":
137
- if (node.left.type === "VariableDeclaration")
138
- collectVarNames(node.left, out);
139
- collectVarNames(node.body, out);
140
- break;
141
- case "WhileStatement":
142
- case "DoWhileStatement":
143
- case "LabeledStatement":
144
- collectVarNames(node.body, out);
145
- break;
146
- case "SwitchStatement":
147
- for (const item of node.cases)
148
- for (const statement of item.consequent)
149
- collectVarNames(statement, out);
150
- break;
151
- case "TryStatement":
152
- collectVarNames(node.block, out);
153
- collectVarNames(node.handler?.body, out);
154
- collectVarNames(node.finalizer, out);
155
- break;
156
- }
157
- return out;
158
- };
159
115
  const loopDeclaration = (left, statement) => {
160
116
  if (left.type !== "VariableDeclaration")
161
117
  return undefined;
162
- const declaration = left.declarations.length === 1 ? left.declarations[0] : undefined;
163
- if (declaration === undefined) {
118
+ const declarations = getArray(left, "declarations");
119
+ if (declarations.length !== 1) {
164
120
  throw new InterpreterRuntimeError(`${statement} supports one declared binding.`, left);
165
121
  }
166
- const kind = left.kind;
122
+ const kind = getString(left, "kind");
167
123
  return {
168
- pattern: declaration.id,
124
+ pattern: getNode(asNode(declarations[0], "declarations[0]"), "id"),
169
125
  mutable: kind !== "const",
170
126
  lexical: kind !== "var",
171
127
  };
172
128
  };
173
129
  const isOpaqueMemberReference = (value) => value instanceof ToolReference ||
130
+ value instanceof PromiseMethodReference ||
174
131
  value instanceof PromiseInstanceMethodReference ||
175
132
  value instanceof IntrinsicReference ||
133
+ value instanceof GlobalMethodReference ||
134
+ value instanceof JsonMethodReference ||
176
135
  value instanceof GeneratorMethodReference;
177
- const promiseResolutionNode = { type: "PromiseResolution", start: 0, end: 0 };
178
- /** One program execution: the tool bridge, promise scheduler, captured logs, and the global scope built once. */
179
- export class Runtime {
136
+ const copyIteratorSymbols = (source, target, consumed) => {
137
+ for (const symbol of IteratorSymbols) {
138
+ if (!consumed?.has(symbol) && Object.hasOwn(source, symbol))
139
+ Reflect.set(target, symbol, Reflect.get(source, symbol));
140
+ }
141
+ };
142
+ const promiseResolutionNode = { type: "PromiseResolution" };
143
+ export class Interpreter {
144
+ scopes;
180
145
  executeTool;
181
- search;
146
+ invokeSearch;
182
147
  toolKeys;
183
- promises;
184
148
  logs;
185
- runner;
186
- /** Built-in globals by name, unaffected by program shadowing. */
187
- builtins;
188
- root;
189
- constructor(executeTool, search, toolKeys, promises, logs = [], extraGlobals = () => []) {
149
+ promises;
150
+ generatorState;
151
+ generatorAsync = false;
152
+ runner = {
153
+ invokeFunction: (fn, args) => this.invokeFunction(fn, args),
154
+ invokeCallable: (callable, args, node) => this.invokeCallable(callable, args, node),
155
+ settlePromise: (promise) => this.settlePromise(promise),
156
+ syncIterator: (value, node) => this.syncIterator(value, node),
157
+ };
158
+ constructor(executeTool, invokeSearch, toolKeys, promises, logs = []) {
159
+ const globalScope = new Map();
160
+ this.scopes = new ScopeStack([globalScope]);
190
161
  this.executeTool = executeTool;
191
- this.search = search;
162
+ this.invokeSearch = invokeSearch;
192
163
  this.toolKeys = toolKeys;
193
- this.promises = promises;
194
164
  this.logs = logs;
195
- const globalScope = new Map();
196
- // Calling back into the program never reads frame state, so any frame serves; the root is always alive.
197
- this.root = new Frame(this, new ScopeStack([globalScope]));
198
- this.runner = {
199
- invokeFunction: (fn, args) => this.root.invokeFunction(fn, args),
200
- invokeCallable: (callable, args, node) => this.root.invokeCallable(callable, args, node),
201
- settlePromise: (promise) => this.root.settlePromise(promise),
202
- syncIterator: (value, node) => this.root.syncIterator(value, node),
203
- };
204
- this.builtins = new Map([...globals(this), ...extraGlobals(this)]);
205
- for (const [name, value] of this.builtins)
206
- globalScope.set(name, { mutable: false, value });
207
- }
208
- run(program) {
209
- return this.root.run(program);
210
- }
211
- }
212
- /** One activation: the top-level program or a single function call, evaluating against its own scope chain. */
213
- class Frame {
214
- runtime;
215
- scopes;
216
- generatorState;
217
- generatorAsync = false;
218
- constructor(runtime, scopes) {
219
- this.runtime = runtime;
220
- this.scopes = scopes;
165
+ this.promises = promises;
166
+ globalScope.set("tools", { mutable: false, value: new ToolReference([]) });
167
+ globalScope.set("search", { mutable: false, value: new SearchFunction() });
168
+ globalScope.set("Promise", { mutable: false, value: new PromiseNamespace() });
169
+ globalScope.set("Symbol", { mutable: false, value: new SymbolNamespace() });
170
+ globalScope.set("undefined", { mutable: false, value: undefined });
171
+ globalScope.set("Object", { mutable: false, value: new GlobalNamespace("Object") });
172
+ globalScope.set("Math", { mutable: false, value: new GlobalNamespace("Math") });
173
+ globalScope.set("JSON", { mutable: false, value: new GlobalNamespace("JSON") });
174
+ globalScope.set("Number", { mutable: false, value: new CoercionFunction("Number") });
175
+ globalScope.set("String", { mutable: false, value: new CoercionFunction("String") });
176
+ globalScope.set("Boolean", { mutable: false, value: new CoercionFunction("Boolean") });
177
+ globalScope.set("Array", { mutable: false, value: new GlobalNamespace("Array") });
178
+ globalScope.set("console", { mutable: false, value: new GlobalNamespace("console") });
179
+ globalScope.set("parseInt", { mutable: false, value: new CoercionFunction("parseInt") });
180
+ globalScope.set("parseFloat", { mutable: false, value: new CoercionFunction("parseFloat") });
181
+ globalScope.set("isFinite", { mutable: false, value: new CoercionFunction("isFinite") });
182
+ globalScope.set("isNaN", { mutable: false, value: new CoercionFunction("isNaN") });
183
+ globalScope.set("Date", { mutable: false, value: new GlobalNamespace("Date") });
184
+ globalScope.set("RegExp", { mutable: false, value: new GlobalNamespace("RegExp") });
185
+ globalScope.set("Map", { mutable: false, value: new GlobalNamespace("Map") });
186
+ globalScope.set("Set", { mutable: false, value: new GlobalNamespace("Set") });
187
+ globalScope.set("URL", { mutable: false, value: new GlobalNamespace("URL") });
188
+ globalScope.set("URLSearchParams", { mutable: false, value: new GlobalNamespace("URLSearchParams") });
189
+ globalScope.set("encodeURI", { mutable: false, value: new UriFunction("encodeURI") });
190
+ globalScope.set("encodeURIComponent", { mutable: false, value: new UriFunction("encodeURIComponent") });
191
+ globalScope.set("decodeURI", { mutable: false, value: new UriFunction("decodeURI") });
192
+ globalScope.set("decodeURIComponent", { mutable: false, value: new UriFunction("decodeURIComponent") });
193
+ for (const name of errorConstructors) {
194
+ globalScope.set(name, { mutable: false, value: new ErrorConstructorReference(name) });
195
+ }
196
+ globalScope.set("NaN", { mutable: false, value: NaN });
197
+ globalScope.set("Infinity", { mutable: false, value: Infinity });
221
198
  }
222
199
  run(program) {
223
200
  const self = this;
@@ -226,11 +203,10 @@ class Frame {
226
203
  return Effect.gen(function* () {
227
204
  self.predeclareLexical(program.body);
228
205
  self.hoistFunctions(program.body);
229
- self.hoistVars(program.body);
230
206
  let value = undefined;
231
207
  for (const [index, statement] of program.body.entries()) {
232
208
  if (index === program.body.length - 1 && statement.type === "ExpressionStatement") {
233
- value = yield* self.evaluateExpression(statement.expression);
209
+ value = yield* self.evaluateExpression(getNode(statement, "expression"));
234
210
  break;
235
211
  }
236
212
  const result = yield* self.evaluateStatement(statement);
@@ -243,20 +219,20 @@ class Frame {
243
219
  }
244
220
  }
245
221
  // The implicit async body adopts returned promises before copy-out.
246
- value = yield* resolvePromiseValue(self.runtime.runner, value, program);
222
+ value = yield* resolvePromiseValue(self.runner, value, program);
247
223
  return value;
248
224
  }).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
249
225
  }
250
226
  // Fork at the call site so admission and hooks occur when the call is made.
251
227
  createToolCallPromise(path, args) {
252
- return this.createPromise(Effect.suspend(() => this.runtime.executeTool(path, args)));
228
+ return this.createPromise(Effect.suspend(() => this.executeTool(path, args)));
253
229
  }
254
230
  createPromise(effect) {
255
- return this.runtime.promises.create(effect);
231
+ return this.promises.create(effect);
256
232
  }
257
233
  // Fiber exits make settlement idempotent; yielding prevents inline continuation.
258
234
  settlePromise(promise) {
259
- const promises = this.runtime.promises;
235
+ const promises = this.promises;
260
236
  return Effect.suspend(() => {
261
237
  promises.markObserved(promise);
262
238
  return Effect.flatMap(promises.await(promise), (exit) => Effect.andThen(Effect.yieldNow, exit));
@@ -265,11 +241,11 @@ class Frame {
265
241
  evaluateStatement(node) {
266
242
  switch (node.type) {
267
243
  case "ExpressionStatement":
268
- return Effect.as(this.evaluateExpression(node.expression), { kind: "none" });
244
+ return Effect.as(this.evaluateExpression(getNode(node, "expression")), { kind: "none" });
269
245
  case "VariableDeclaration":
270
246
  return Effect.map(this.evaluateVariableDeclaration(node), () => ({ kind: "none" }));
271
247
  case "ReturnStatement": {
272
- const argumentNode = node.argument;
248
+ const argumentNode = getOptionalNode(node, "argument");
273
249
  return argumentNode
274
250
  ? Effect.map(this.evaluateExpression(argumentNode), (value) => ({ kind: "return", value }))
275
251
  : Effect.succeed({ kind: "return", value: undefined });
@@ -312,10 +288,11 @@ class Frame {
312
288
  this.scopes.push();
313
289
  const self = this;
314
290
  return Effect.gen(function* () {
315
- const body = node.body;
291
+ const body = getArray(node, "body");
316
292
  self.predeclareLexical(body);
317
293
  self.hoistFunctions(body);
318
- for (const statement of body) {
294
+ for (const statementValue of body) {
295
+ const statement = asNode(statementValue, "body");
319
296
  const result = yield* self.evaluateStatement(statement);
320
297
  if (result.kind !== "none") {
321
298
  return result;
@@ -324,45 +301,28 @@ class Frame {
324
301
  return { kind: "none" };
325
302
  }).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
326
303
  }
327
- createFunction(node, name = node.type === "ArrowFunctionExpression" ? "" : (node.id?.name ?? "")) {
328
- return new ProgramFunction(name, node.params, node.body, this.scopes.capture(), node.async, node.generator);
329
- }
330
- // NamedEvaluation: an anonymous function definition takes the name of what it is assigned to.
331
- evaluateNamed(node, name) {
332
- if (node.type === "ArrowFunctionExpression" || (node.type === "FunctionExpression" && !node.id)) {
333
- return Effect.sync(() => this.createFunction(node, name));
334
- }
335
- return this.evaluateExpression(node);
304
+ createFunction(node) {
305
+ return new CodeModeFunction(getArray(node, "params").map((parameter, index) => asNode(parameter, `params[${index}]`)), getNode(node, "body"), this.scopes.capture(), node.async === true, node.generator === true);
336
306
  }
337
307
  hoistFunctions(statements) {
338
- for (const node of statements) {
339
- if (node.type !== "FunctionDeclaration")
308
+ for (const statementValue of statements) {
309
+ if (!isRecord(statementValue) || statementValue.type !== "FunctionDeclaration")
340
310
  continue;
341
- this.scopes.declare(node.id.name, this.createFunction(node), true, node);
342
- }
343
- }
344
- // Hoisted `var` bindings start undefined, or copy a same-named parameter. Function bodies hoist
345
- // into their own scope above the parameters so closures in parameter defaults keep seeing outer names.
346
- hoistVars(statements, parameters) {
347
- const names = varNames.get(statements) ??
348
- statements.reduce((out, statement) => collectVarNames(statement, out), []);
349
- varNames.set(statements, names);
350
- const scope = this.scopes.current();
351
- for (const name of names) {
352
- if (scope.has(name))
353
- continue;
354
- scope.set(name, { mutable: true, value: parameters?.get(name)?.value, initialized: true });
311
+ const node = statementValue;
312
+ this.scopes.declare(getString(getNode(node, "id"), "name"), this.createFunction(node), true, node);
355
313
  }
356
314
  }
357
315
  predeclareLexical(statements) {
358
- for (const statement of statements) {
359
- if (statement.type !== "VariableDeclaration")
316
+ for (const statementValue of statements) {
317
+ if (!isRecord(statementValue) || statementValue.type !== "VariableDeclaration")
360
318
  continue;
361
- const kind = statement.kind;
319
+ const statement = statementValue;
320
+ const kind = getString(statement, "kind");
362
321
  if (kind === "var")
363
322
  continue;
364
- for (const declaration of statement.declarations) {
365
- for (const name of collectPatternNames(declaration.id)) {
323
+ for (const declarationValue of getArray(statement, "declarations")) {
324
+ const declaration = asNode(declarationValue, "declarations");
325
+ for (const name of collectPatternNames(getNode(declaration, "id"))) {
366
326
  this.scopes.reserve(name, kind !== "const", declaration);
367
327
  }
368
328
  }
@@ -373,29 +333,30 @@ class Frame {
373
333
  this.scopes.reserve(name, mutable, node);
374
334
  }
375
335
  evaluateIfStatement(node) {
376
- return Effect.flatMap(this.evaluateExpression(node.test), (test) => test
377
- ? this.evaluateStatement(node.consequent)
378
- : node.alternate
379
- ? this.evaluateStatement(node.alternate)
336
+ const testNode = getNode(node, "test");
337
+ const consequentNode = getNode(node, "consequent");
338
+ const alternateNode = getOptionalNode(node, "alternate");
339
+ return Effect.flatMap(this.evaluateExpression(testNode), (test) => test
340
+ ? this.evaluateStatement(consequentNode)
341
+ : alternateNode
342
+ ? this.evaluateStatement(alternateNode)
380
343
  : Effect.succeed({ kind: "none" }));
381
344
  }
382
345
  evaluateSwitchStatement(node) {
383
346
  const self = this;
384
347
  return Effect.gen(function* () {
385
- const discriminant = yield* self.evaluateExpression(node.discriminant);
348
+ const discriminant = yield* self.evaluateExpression(getNode(node, "discriminant"));
386
349
  if (containsOpaqueReference(discriminant)) {
387
350
  throw new InterpreterRuntimeError("Switch discriminants must be data values.", node, "InvalidDataValue");
388
351
  }
389
352
  self.scopes.push();
390
353
  return yield* Effect.gen(function* () {
391
- const cases = node.cases;
392
- const statements = cases.flatMap((branch) => branch.consequent);
393
- self.predeclareLexical(statements);
394
- self.hoistFunctions(statements);
354
+ const cases = getArray(node, "cases").map((value, index) => asNode(value, `cases[${index}]`));
355
+ self.predeclareLexical(cases.flatMap((branch) => getArray(branch, "consequent")));
395
356
  let defaultIndex;
396
357
  let selected;
397
358
  for (const [index, branch] of cases.entries()) {
398
- const test = branch.test;
359
+ const test = getOptionalNode(branch, "test");
399
360
  if (!test) {
400
361
  defaultIndex = index;
401
362
  continue;
@@ -413,8 +374,8 @@ class Frame {
413
374
  if (start === undefined)
414
375
  return { kind: "none" };
415
376
  for (let index = start; index < cases.length; index += 1) {
416
- for (const statement of cases[index].consequent) {
417
- const result = yield* self.evaluateStatement(statement);
377
+ for (const statementValue of getArray(cases[index], "consequent")) {
378
+ const result = yield* self.evaluateStatement(asNode(statementValue, "consequent"));
418
379
  if (result.kind === "break") {
419
380
  if (result.label === undefined)
420
381
  return { kind: "none" };
@@ -429,24 +390,50 @@ class Frame {
429
390
  });
430
391
  }
431
392
  evaluateWhileStatement(node, labels) {
393
+ const testNode = getNode(node, "test");
394
+ const bodyNode = getNode(node, "body");
432
395
  const self = this;
433
396
  return Effect.gen(function* () {
434
- while (yield* self.evaluateExpression(node.test)) {
435
- const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
436
- if (exit !== undefined)
437
- return exit;
397
+ while (yield* self.evaluateExpression(testNode)) {
398
+ const result = yield* self.evaluateStatement(bodyNode);
399
+ if (result.kind === "continue") {
400
+ if (result.label !== undefined && !labels?.has(result.label))
401
+ return result;
402
+ continue;
403
+ }
404
+ if (result.kind === "break") {
405
+ if (result.label !== undefined && !labels?.has(result.label))
406
+ return result;
407
+ return { kind: "none" };
408
+ }
409
+ if (result.kind === "return") {
410
+ return result;
411
+ }
438
412
  }
439
413
  return { kind: "none" };
440
414
  });
441
415
  }
442
416
  evaluateDoWhileStatement(node, labels) {
417
+ const bodyNode = getNode(node, "body");
418
+ const testNode = getNode(node, "test");
443
419
  const self = this;
444
420
  return Effect.gen(function* () {
445
421
  do {
446
- const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
447
- if (exit !== undefined)
448
- return exit;
449
- } while (yield* self.evaluateExpression(node.test));
422
+ const result = yield* self.evaluateStatement(bodyNode);
423
+ if (result.kind === "continue") {
424
+ if (result.label !== undefined && !labels?.has(result.label))
425
+ return result;
426
+ continue;
427
+ }
428
+ if (result.kind === "break") {
429
+ if (result.label !== undefined && !labels?.has(result.label))
430
+ return result;
431
+ return { kind: "none" };
432
+ }
433
+ if (result.kind === "return") {
434
+ return result;
435
+ }
436
+ } while (yield* self.evaluateExpression(testNode));
450
437
  return { kind: "none" };
451
438
  });
452
439
  }
@@ -454,10 +441,11 @@ class Frame {
454
441
  this.scopes.push();
455
442
  const self = this;
456
443
  return Effect.gen(function* () {
457
- const initNode = node.init;
458
- const testNode = node.test;
459
- const updateNode = node.update;
460
- if (initNode?.type === "VariableDeclaration" && initNode.kind !== "var") {
444
+ const initNode = getOptionalNode(node, "init");
445
+ const testNode = getOptionalNode(node, "test");
446
+ const updateNode = getOptionalNode(node, "update");
447
+ const bodyNode = getNode(node, "body");
448
+ if (initNode?.type === "VariableDeclaration" && getString(initNode, "kind") !== "var") {
461
449
  self.predeclareLexical([initNode]);
462
450
  }
463
451
  if (initNode) {
@@ -468,7 +456,7 @@ class Frame {
468
456
  yield* self.evaluateExpression(initNode);
469
457
  }
470
458
  }
471
- const perIterationBindings = initNode?.type === "VariableDeclaration" && initNode.kind !== "var"
459
+ const perIterationBindings = initNode?.type === "VariableDeclaration" && getString(initNode, "kind") !== "var"
472
460
  ? Array.from(self.scopes.current().keys())
473
461
  : [];
474
462
  const nextIteration = () => {
@@ -480,20 +468,31 @@ class Frame {
480
468
  };
481
469
  nextIteration();
482
470
  while (testNode ? yield* self.evaluateExpression(testNode) : true) {
483
- const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
484
- if (exit !== undefined)
485
- return exit;
471
+ const result = yield* self.evaluateStatement(bodyNode);
472
+ if (result.kind === "return") {
473
+ return result;
474
+ }
475
+ if (result.kind === "break") {
476
+ if (result.label !== undefined && !labels?.has(result.label))
477
+ return result;
478
+ return { kind: "none" };
479
+ }
480
+ if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label))
481
+ return result;
486
482
  nextIteration();
487
483
  if (updateNode) {
488
484
  yield* self.evaluateExpression(updateNode);
489
485
  }
486
+ if (result.kind === "continue") {
487
+ continue;
488
+ }
490
489
  }
491
490
  return { kind: "none" };
492
491
  }).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
493
492
  }
494
493
  evaluateForOfStatement(node, labels) {
495
- const awaiting = node.await;
496
- const left = node.left;
494
+ const awaiting = getBoolean(node, "await");
495
+ const left = getNode(node, "left");
497
496
  const declared = loopDeclaration(left, "for...of");
498
497
  if (declared?.lexical)
499
498
  this.scopes.push();
@@ -501,7 +500,8 @@ class Frame {
501
500
  return Effect.gen(function* () {
502
501
  if (declared?.lexical)
503
502
  self.predeclarePattern(declared.pattern, declared.mutable, left);
504
- const right = yield* self.evaluateExpression(node.right);
503
+ const right = yield* self.evaluateExpression(getNode(node, "right"));
504
+ const body = getNode(node, "body");
505
505
  const iterator = yield* self.customIterator(right, node, awaiting);
506
506
  const cursor = iterator === undefined ? yield* self.syncIterator(right, node) : undefined;
507
507
  if (iterator === undefined && cursor === undefined) {
@@ -512,25 +512,30 @@ class Frame {
512
512
  : awaiting
513
513
  ? Effect.andThen(cursor?.close ?? Effect.void, Effect.yieldNow)
514
514
  : (cursor?.close ?? Effect.void);
515
- if (left.type === "RestElement" || left.type === "AssignmentPattern") {
515
+ let assignment;
516
+ if (left.type !== "VariableDeclaration" &&
517
+ (left.type === "Identifier" ||
518
+ left.type === "MemberExpression" ||
519
+ left.type === "ArrayPattern" ||
520
+ left.type === "ObjectPattern")) {
521
+ assignment = left;
522
+ }
523
+ else if (left.type !== "VariableDeclaration") {
516
524
  throw new InterpreterRuntimeError("Unsupported for...of binding.", left);
517
525
  }
518
- const assignment = left.type === "VariableDeclaration" ? undefined : left;
519
526
  const evaluateBody = (value) => Effect.gen(function* () {
520
- if (declared?.lexical) {
527
+ if (declared) {
521
528
  self.scopes.push();
522
- self.predeclarePattern(declared.pattern, declared.mutable, left);
523
- yield* self.declarePattern(declared.pattern, value, declared.mutable, left, true);
524
- }
525
- else if (declared) {
526
- yield* self.assignPattern(declared.pattern, value, left);
529
+ if (declared.lexical)
530
+ self.predeclarePattern(declared.pattern, declared.mutable, left);
531
+ yield* self.declarePattern(declared.pattern, value, declared.mutable, left, declared.lexical);
527
532
  }
528
533
  else if (assignment) {
529
534
  yield* self.assignPattern(assignment, value, left);
530
535
  }
531
- return yield* self.evaluateStatement(node.body);
536
+ return yield* self.evaluateStatement(body);
532
537
  }).pipe(Effect.ensuring(Effect.sync(() => {
533
- if (declared?.lexical)
538
+ if (declared)
534
539
  self.scopes.pop();
535
540
  })));
536
541
  while (true) {
@@ -548,10 +553,20 @@ class Frame {
548
553
  }
549
554
  return yield* Effect.failCause(bodyExit.cause);
550
555
  }
551
- const exit = loopExit(bodyExit.value, labels);
552
- if (exit !== undefined) {
556
+ const result = bodyExit.value;
557
+ if (result.kind === "return") {
553
558
  yield* close();
554
- return exit;
559
+ return result;
560
+ }
561
+ if (result.kind === "break") {
562
+ yield* close();
563
+ if (result.label !== undefined && !labels?.has(result.label))
564
+ return result;
565
+ return { kind: "none" };
566
+ }
567
+ if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label)) {
568
+ yield* close();
569
+ return result;
555
570
  }
556
571
  }
557
572
  }).pipe(Effect.ensuring(Effect.sync(() => {
@@ -560,7 +575,7 @@ class Frame {
560
575
  })));
561
576
  }
562
577
  awaitValue(value, node = promiseResolutionNode) {
563
- return Effect.flatMap(resolvePromise(this.runtime.runner, this.runtime.promises, value, node), (promise) => this.settlePromise(promise));
578
+ return Effect.flatMap(resolvePromise(this.runner, this.promises, value, node), (promise) => this.settlePromise(promise));
564
579
  }
565
580
  awaitAsyncFromSyncValue(iterator, value, node, closeOnRejection) {
566
581
  const self = this;
@@ -575,25 +590,22 @@ class Frame {
575
590
  });
576
591
  }
577
592
  syncIterator(value, node) {
578
- const iterator = value instanceof ProgramArray
579
- ? value.items[Symbol.iterator]()
593
+ const iterator = Array.isArray(value)
594
+ ? value[Symbol.iterator]()
580
595
  : typeof value === "string"
581
596
  ? value[Symbol.iterator]()
582
- : value instanceof Values.Map
597
+ : value instanceof CodeModeMap
583
598
  ? value.map.entries()
584
- : value instanceof Values.Set
599
+ : value instanceof CodeModeSet
585
600
  ? value.set.values()
586
- : value instanceof Values.URLSearchParams
601
+ : value instanceof CodeModeURLSearchParams
587
602
  ? value.params.entries()
588
603
  : undefined;
589
604
  if (iterator !== undefined) {
590
605
  return Effect.succeed({
591
606
  next: Effect.sync(() => {
592
607
  const step = iterator.next();
593
- return {
594
- done: Boolean(step.done),
595
- value: Array.isArray(step.value) ? new ProgramArray(step.value) : step.value,
596
- };
608
+ return { done: Boolean(step.done), value: step.value };
597
609
  }),
598
610
  close: Effect.void,
599
611
  });
@@ -616,10 +628,10 @@ class Frame {
616
628
  asynchronous: value.asynchronous,
617
629
  });
618
630
  }
619
- if (!(value instanceof ProgramObject))
631
+ if (!isRecord(value) || isRuntimeReference(value))
620
632
  return Effect.undefined;
621
- const asyncMethod = allowAsync ? get(value, AsyncIteratorSymbol) : undefined;
622
- const method = asyncMethod ?? get(value, IteratorSymbol);
633
+ const asyncMethod = allowAsync ? Reflect.get(value, AsyncIteratorSymbol) : undefined;
634
+ const method = asyncMethod ?? Reflect.get(value, IteratorSymbol);
623
635
  if (method === undefined || method === null)
624
636
  return Effect.undefined;
625
637
  const self = this;
@@ -629,7 +641,7 @@ class Frame {
629
641
  iterator: object,
630
642
  next: object instanceof CodeModeGenerator
631
643
  ? new GeneratorMethodReference(object, "next")
632
- : self.requireIteratorMethod(get(object, "next"), "Iterator next", node),
644
+ : self.requireIteratorMethod(object.next, "Iterator next", node),
633
645
  asynchronous: asyncMethod !== undefined && asyncMethod !== null,
634
646
  };
635
647
  });
@@ -639,7 +651,7 @@ class Frame {
639
651
  return Effect.gen(function* () {
640
652
  if (iterator.asynchronous) {
641
653
  const object = self.requireIteratorObject(yield* self.awaitValue(yield* self.invokeCallable(iterator.next, [], node)), "Iterator next() result", node);
642
- return { done: Boolean(get(object, "done")), value: get(object, "value") };
654
+ return { done: Boolean(object.done), value: object.value };
643
655
  }
644
656
  const called = yield* Effect.exit(self.invokeCallable(iterator.next, [], node));
645
657
  if (!Exit.isSuccess(called)) {
@@ -649,7 +661,7 @@ class Frame {
649
661
  }
650
662
  const captured = yield* Effect.exit(Effect.sync(() => {
651
663
  const object = self.requireIteratorObject(called.value, "Iterator next() result", node);
652
- return { done: Boolean(get(object, "done")), value: get(object, "value") };
664
+ return { done: Boolean(object.done), value: object.value };
653
665
  }));
654
666
  if (!Exit.isSuccess(captured)) {
655
667
  if (awaiting)
@@ -667,7 +679,7 @@ class Frame {
667
679
  closeIterator(iterator, node, awaiting = true) {
668
680
  const close = iterator.iterator instanceof CodeModeGenerator
669
681
  ? new GeneratorMethodReference(iterator.iterator, "return")
670
- : get(iterator.iterator, "return");
682
+ : iterator.iterator.return;
671
683
  if (close === undefined || close === null)
672
684
  return iterator.asynchronous || !awaiting ? Effect.void : Effect.yieldNow;
673
685
  const self = this;
@@ -683,7 +695,7 @@ class Frame {
683
695
  yield* Effect.yieldNow;
684
696
  return yield* Effect.failCause(called.cause);
685
697
  }
686
- const captured = yield* Effect.exit(Effect.sync(() => get(self.requireIteratorObject(called.value, "Iterator return() result", node), "value")));
698
+ const captured = yield* Effect.exit(Effect.sync(() => self.requireIteratorObject(called.value, "Iterator return() result", node).value));
687
699
  if (!Exit.isSuccess(captured)) {
688
700
  if (awaiting)
689
701
  yield* Effect.yieldNow;
@@ -694,7 +706,7 @@ class Frame {
694
706
  });
695
707
  }
696
708
  requireIteratorObject(value, context, node) {
697
- if (value instanceof ProgramObject)
709
+ if (isRecord(value) && !isRuntimeReference(value))
698
710
  return value;
699
711
  throw new InterpreterRuntimeError(`${context} must be an object.`, node).as("TypeError");
700
712
  }
@@ -708,16 +720,20 @@ class Frame {
708
720
  return value;
709
721
  throw new InterpreterRuntimeError(`${context} must be a function.`, node).as("TypeError");
710
722
  }
711
- // for...in over null/undefined iterates nothing, like JS.
712
- enumerableKeys(value, node) {
713
- if (value instanceof ToolReference)
714
- return [...this.runtime.toolKeys(value.path)];
715
- if (value === null || value === undefined)
716
- return [];
717
- return ownKeys(enumerableSource("for...in", value, node)).filter((key) => typeof key === "string");
723
+ enumerableKeys(value) {
724
+ if (value instanceof ToolReference) {
725
+ return [...this.toolKeys(value.path)];
726
+ }
727
+ if (Array.isArray(value)) {
728
+ return Object.keys(value);
729
+ }
730
+ if (value !== null && typeof value === "object" && !isRuntimeReference(value)) {
731
+ return Object.keys(value);
732
+ }
733
+ return undefined;
718
734
  }
719
735
  evaluateForInStatement(node, labels) {
720
- const left = node.left;
736
+ const left = getNode(node, "left");
721
737
  const declared = loopDeclaration(left, "for...in");
722
738
  if (declared?.lexical)
723
739
  this.scopes.push();
@@ -725,33 +741,48 @@ class Frame {
725
741
  return Effect.gen(function* () {
726
742
  if (declared?.lexical)
727
743
  self.predeclarePattern(declared.pattern, declared.mutable, left);
728
- const right = yield* self.evaluateExpression(node.right);
729
- const keys = self.enumerableKeys(right, node.right);
730
- if (left.type !== "Identifier" && left.type !== "VariableDeclaration") {
744
+ const right = yield* self.evaluateExpression(getNode(node, "right"));
745
+ const body = getNode(node, "body");
746
+ const keys = self.enumerableKeys(right);
747
+ if (keys === undefined) {
748
+ throw new InterpreterRuntimeError("for...in requires a plain object, array, or tools reference. Use for...of for arrays/strings/Maps/Sets, or Object.keys(value) for a key list.", node);
749
+ }
750
+ let assignmentName;
751
+ if (left.type === "Identifier") {
752
+ assignmentName = getString(left, "name");
753
+ }
754
+ else if (left.type !== "VariableDeclaration") {
731
755
  throw new InterpreterRuntimeError("Unsupported for...in binding.", left);
732
756
  }
733
- const assignmentName = left.type === "Identifier" ? left.name : undefined;
734
757
  for (const key of keys) {
735
758
  const result = yield* Effect.gen(function* () {
736
- if (declared?.lexical) {
759
+ if (declared) {
737
760
  self.scopes.push();
738
- self.predeclarePattern(declared.pattern, declared.mutable, left);
739
- yield* self.declarePattern(declared.pattern, key, declared.mutable, left, true);
740
- }
741
- else if (declared) {
742
- yield* self.assignPattern(declared.pattern, key, left);
761
+ if (declared.lexical)
762
+ self.predeclarePattern(declared.pattern, declared.mutable, left);
763
+ yield* self.declarePattern(declared.pattern, key, declared.mutable, left, declared.lexical);
743
764
  }
744
765
  else if (assignmentName) {
745
766
  self.scopes.set(assignmentName, key, left);
746
767
  }
747
- return yield* self.evaluateStatement(node.body);
768
+ return yield* self.evaluateStatement(body);
748
769
  }).pipe(Effect.ensuring(Effect.sync(() => {
749
- if (declared?.lexical)
770
+ if (declared)
750
771
  self.scopes.pop();
751
772
  })));
752
- const exit = loopExit(result, labels);
753
- if (exit !== undefined)
754
- return exit;
773
+ if (result.kind === "return") {
774
+ return result;
775
+ }
776
+ if (result.kind === "break") {
777
+ if (result.label !== undefined && !labels?.has(result.label))
778
+ return result;
779
+ return { kind: "none" };
780
+ }
781
+ if (result.kind === "continue") {
782
+ if (result.label !== undefined && !labels?.has(result.label))
783
+ return result;
784
+ continue;
785
+ }
755
786
  }
756
787
  return { kind: "none" };
757
788
  }).pipe(Effect.ensuring(Effect.sync(() => {
@@ -760,17 +791,19 @@ class Frame {
760
791
  })));
761
792
  }
762
793
  evaluateBreakStatement(node) {
763
- return node.label ? { kind: "break", label: node.label.name } : { kind: "break" };
794
+ const labelNode = getOptionalNode(node, "label");
795
+ return labelNode ? { kind: "break", label: getString(labelNode, "name") } : { kind: "break" };
764
796
  }
765
797
  evaluateContinueStatement(node) {
766
- return node.label ? { kind: "continue", label: node.label.name } : { kind: "continue" };
798
+ const labelNode = getOptionalNode(node, "label");
799
+ return labelNode ? { kind: "continue", label: getString(labelNode, "name") } : { kind: "continue" };
767
800
  }
768
801
  evaluateLabeledStatement(node) {
769
802
  const labels = new Set();
770
803
  let body = node;
771
804
  while (body.type === "LabeledStatement") {
772
- labels.add(body.label.name);
773
- body = body.body;
805
+ labels.add(getString(getNode(body, "label"), "name"));
806
+ body = getNode(body, "body");
774
807
  }
775
808
  const evaluated = (() => {
776
809
  if (body.type === "WhileStatement")
@@ -790,12 +823,13 @@ class Frame {
790
823
  : result);
791
824
  }
792
825
  evaluateThrowStatement(node) {
793
- return Effect.flatMap(this.evaluateExpression(node.argument), (value) => Effect.fail(new ProgramThrow(value)));
826
+ const argument = getNode(node, "argument");
827
+ return Effect.flatMap(this.evaluateExpression(argument), (value) => Effect.fail(new ProgramThrow(value)));
794
828
  }
795
829
  evaluateTryStatement(node) {
796
- const body = node.block;
797
- const handler = node.handler;
798
- const finalizer = node.finalizer;
830
+ const body = getNode(node, "block");
831
+ const handler = getOptionalNode(node, "handler");
832
+ const finalizer = getOptionalNode(node, "finalizer");
799
833
  const self = this;
800
834
  const attempted = Effect.matchCauseEffect(this.evaluateStatement(body), {
801
835
  onFailure: (cause) => {
@@ -803,12 +837,12 @@ class Frame {
803
837
  return Effect.failCause(cause);
804
838
  }
805
839
  const caught = caughtErrorValue(Cause.squash(cause));
806
- const parameter = handler.param;
840
+ const parameter = getOptionalNode(handler, "param");
807
841
  self.scopes.push();
808
842
  return Effect.gen(function* () {
809
843
  if (parameter)
810
844
  yield* self.declarePattern(parameter, caught, true, handler);
811
- return yield* self.evaluateStatement(handler.body);
845
+ return yield* self.evaluateStatement(getNode(handler, "body"));
812
846
  }).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
813
847
  },
814
848
  onSuccess: Effect.succeed,
@@ -824,24 +858,18 @@ class Frame {
824
858
  });
825
859
  }
826
860
  evaluateVariableDeclaration(node) {
827
- const kind = node.kind;
861
+ const kind = getString(node, "kind");
862
+ const declarations = getArray(node, "declarations");
828
863
  const self = this;
829
864
  return Effect.gen(function* () {
830
- for (const declaration of node.declarations) {
865
+ for (const declarationValue of declarations) {
866
+ const declaration = asNode(declarationValue, "declarations");
831
867
  if (declaration.type !== "VariableDeclarator") {
832
868
  throw new InterpreterRuntimeError("Unsupported variable declaration shape.", declaration);
833
869
  }
834
- const init = declaration.init;
835
- // `var x` alone is a no-op: the binding was hoisted on function entry.
836
- const id = declaration.id;
837
- const evaluate = (init) => id.type === "Identifier" ? self.evaluateNamed(init, id.name) : self.evaluateExpression(init);
838
- if (kind === "var") {
839
- if (init)
840
- yield* self.assignPattern(id, yield* evaluate(init), declaration);
841
- continue;
842
- }
843
- const value = init ? yield* evaluate(init) : undefined;
844
- yield* self.declarePattern(declaration.id, value, kind !== "const", declaration, true);
870
+ const init = getOptionalNode(declaration, "init");
871
+ const value = init ? yield* self.evaluateExpression(init) : undefined;
872
+ yield* self.declarePattern(getNode(declaration, "id"), value, kind !== "const", declaration, kind !== "var");
845
873
  }
846
874
  });
847
875
  }
@@ -849,7 +877,7 @@ class Frame {
849
877
  const self = this;
850
878
  return Effect.gen(function* () {
851
879
  if (pattern.type === "Identifier") {
852
- const name = pattern.name;
880
+ const name = getString(pattern, "name");
853
881
  if (initialize)
854
882
  self.scopes.initialize(name, value, node);
855
883
  else
@@ -857,25 +885,33 @@ class Frame {
857
885
  return;
858
886
  }
859
887
  if (pattern.type === "AssignmentPattern") {
860
- const resolved = value === undefined ? yield* self.evaluateDefault(pattern) : value;
861
- yield* self.declarePattern(pattern.left, resolved, mutable, node, initialize);
888
+ const resolved = value === undefined ? yield* self.evaluateExpression(getNode(pattern, "right")) : value;
889
+ yield* self.declarePattern(getNode(pattern, "left"), resolved, mutable, node, initialize);
862
890
  return;
863
891
  }
864
892
  if (pattern.type === "ObjectPattern") {
865
- if (!(value instanceof ProgramObject)) {
866
- throw new InterpreterRuntimeError(`Object destructuring requires a data object or array value, received ${describeValue(value)}.`, pattern, "InvalidDataValue");
893
+ if (value === null || typeof value !== "object" || isRuntimeReference(value)) {
894
+ throw new InterpreterRuntimeError("Object destructuring requires a data object or array value.", pattern, "InvalidDataValue");
867
895
  }
868
896
  const consumed = new Set();
869
- for (const property of pattern.properties) {
897
+ for (const propertyValue of getArray(pattern, "properties")) {
898
+ const property = asNode(propertyValue, "properties");
870
899
  if (property.type === "RestElement") {
871
- const rest = new ProgramObject();
872
- assign(rest, value, consumed);
873
- yield* self.declarePattern(property.argument, rest, mutable, property, initialize);
900
+ const rest = Object.create(null);
901
+ for (const [key, item] of Object.entries(value)) {
902
+ if (!consumed.has(key) && !isBlockedMember(key))
903
+ rest[key] = item;
904
+ }
905
+ copyIteratorSymbols(value, rest, consumed);
906
+ yield* self.declarePattern(getNode(property, "argument"), rest, mutable, property, initialize);
874
907
  continue;
875
908
  }
876
909
  const key = yield* self.destructuringPropertyKey(property);
910
+ if (isBlockedMember(String(key))) {
911
+ throw new InterpreterRuntimeError(`Property '${String(key)}' is not available.`, property);
912
+ }
877
913
  consumed.add(typeof key === "symbol" ? key : String(key));
878
- yield* self.declarePattern(property.value, self.destructuringPropertyValue(value, key), mutable, property, initialize);
914
+ yield* self.declarePattern(getNode(property, "value"), self.destructuringPropertyValue(value, key), mutable, property, initialize);
879
915
  }
880
916
  return;
881
917
  }
@@ -889,7 +925,7 @@ class Frame {
889
925
  const self = this;
890
926
  return Effect.gen(function* () {
891
927
  if (pattern.type === "Identifier") {
892
- self.scopes.set(pattern.name, value, pattern);
928
+ self.scopes.set(getString(pattern, "name"), value, pattern);
893
929
  return;
894
930
  }
895
931
  if (pattern.type === "MemberExpression") {
@@ -897,25 +933,34 @@ class Frame {
897
933
  return;
898
934
  }
899
935
  if (pattern.type === "AssignmentPattern") {
900
- const resolved = value === undefined ? yield* self.evaluateDefault(pattern) : value;
901
- yield* self.assignPattern(pattern.left, resolved, node);
936
+ const resolved = value === undefined ? yield* self.evaluateExpression(getNode(pattern, "right")) : value;
937
+ yield* self.assignPattern(getNode(pattern, "left"), resolved, node);
902
938
  return;
903
939
  }
904
940
  if (pattern.type === "ObjectPattern") {
905
- if (!(value instanceof ProgramObject)) {
906
- throw new InterpreterRuntimeError(`Object destructuring requires a data object or array value, received ${describeValue(value)}.`, pattern, "InvalidDataValue");
941
+ if (value === null || typeof value !== "object" || isRuntimeReference(value)) {
942
+ throw new InterpreterRuntimeError("Object destructuring requires a data object or array value.", pattern, "InvalidDataValue");
907
943
  }
944
+ const source = value;
908
945
  const consumed = new Set();
909
- for (const property of pattern.properties) {
946
+ for (const propertyValue of getArray(pattern, "properties")) {
947
+ const property = asNode(propertyValue, "properties");
910
948
  if (property.type === "RestElement") {
911
- const rest = new ProgramObject();
912
- assign(rest, value, consumed);
913
- yield* self.assignPattern(property.argument, rest, property);
949
+ const rest = Object.create(null);
950
+ for (const [key, item] of Object.entries(source)) {
951
+ if (!consumed.has(key) && !isBlockedMember(key))
952
+ rest[key] = item;
953
+ }
954
+ copyIteratorSymbols(source, rest, consumed);
955
+ yield* self.assignPattern(getNode(property, "argument"), rest, property);
914
956
  continue;
915
957
  }
916
958
  const key = yield* self.destructuringPropertyKey(property);
959
+ if (isBlockedMember(String(key))) {
960
+ throw new InterpreterRuntimeError(`Property '${String(key)}' is not available.`, property);
961
+ }
917
962
  consumed.add(typeof key === "symbol" ? key : String(key));
918
- yield* self.assignPattern(property.value, self.destructuringPropertyValue(value, key), property);
963
+ yield* self.assignPattern(getNode(property, "value"), self.destructuringPropertyValue(source, key), property);
919
964
  }
920
965
  return;
921
966
  }
@@ -925,11 +970,6 @@ class Frame {
925
970
  throw new InterpreterRuntimeError(`Unsupported assignment pattern '${pattern.type}'.`, node);
926
971
  });
927
972
  }
928
- evaluateDefault(pattern) {
929
- return pattern.left.type === "Identifier"
930
- ? this.evaluateNamed(pattern.right, pattern.left.name)
931
- : this.evaluateExpression(pattern.right);
932
- }
933
973
  destructureArrayPattern(pattern, value, consume) {
934
974
  const self = this;
935
975
  return Effect.gen(function* () {
@@ -938,19 +978,21 @@ class Frame {
938
978
  throw new InterpreterRuntimeError("Array destructuring requires a supported iterable value.", pattern).as("TypeError");
939
979
  }
940
980
  let done = false;
941
- for (const element of pattern.elements) {
981
+ for (const [index, item] of getArray(pattern, "elements").entries()) {
942
982
  if (done) {
943
- if (element === null)
983
+ if (item === null)
944
984
  continue;
945
- yield* consume(element.type === "RestElement" ? element.argument : element, element.type === "RestElement" ? new ProgramArray() : undefined, element);
985
+ const element = asNode(item, `elements[${index}]`);
986
+ yield* consume(element.type === "RestElement" ? getNode(element, "argument") : element, element.type === "RestElement" ? [] : undefined, element);
946
987
  if (element.type === "RestElement")
947
988
  return;
948
989
  continue;
949
990
  }
950
991
  const step = yield* cursor.next;
951
992
  done = step.done;
952
- if (element === null)
993
+ if (item === null)
953
994
  continue;
995
+ const element = asNode(item, `elements[${index}]`);
954
996
  if (element.type === "RestElement") {
955
997
  const rest = [];
956
998
  if (!step.done)
@@ -961,7 +1003,7 @@ class Frame {
961
1003
  if (!done)
962
1004
  rest.push(next.value);
963
1005
  }
964
- yield* consume(element.argument, new ProgramArray(rest), element);
1006
+ yield* consume(getNode(element, "argument"), rest, element);
965
1007
  return;
966
1008
  }
967
1009
  const consumed = consume(element, step.done ? undefined : step.value, pattern);
@@ -972,24 +1014,24 @@ class Frame {
972
1014
  });
973
1015
  }
974
1016
  destructuringPropertyKey(property) {
975
- if (property.type !== "Property" || property.kind !== "init") {
1017
+ if (property.type !== "Property" || getString(property, "kind") !== "init") {
976
1018
  throw new InterpreterRuntimeError("Unsupported object destructuring property.", property);
977
1019
  }
978
- const keyNode = property.key;
979
- if (property.computed) {
1020
+ const keyNode = getNode(property, "key");
1021
+ if (getBoolean(property, "computed")) {
980
1022
  return Effect.map(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value, keyNode));
981
1023
  }
982
- if (keyNode.type === "Identifier")
983
- return Effect.succeed(keyNode.name);
984
- if (keyNode.type === "Literal")
985
- return Effect.succeed(String(keyNode.value));
986
- throw unsupportedSyntax(keyNode.type, keyNode);
1024
+ return Effect.succeed(keyNode.type === "Identifier" ? getString(keyNode, "name") : String(keyNode.value));
987
1025
  }
988
1026
  destructuringPropertyValue(source, key) {
989
- if (!(source instanceof ProgramArray))
990
- return get(source, key);
991
- if (has(source, key))
992
- return get(source, key);
1027
+ if (!Array.isArray(source))
1028
+ return Reflect.get(source, key);
1029
+ if (key === "length")
1030
+ return source.length;
1031
+ if (typeof key === "number")
1032
+ return source[key];
1033
+ if (Object.hasOwn(source, key))
1034
+ return Reflect.get(source, key);
993
1035
  if (typeof key === "string" && arrayMethods.has(key))
994
1036
  return new IntrinsicReference(source, key);
995
1037
  return undefined;
@@ -998,12 +1040,13 @@ class Frame {
998
1040
  switch (node.type) {
999
1041
  case "Literal": {
1000
1042
  const regex = node.regex;
1001
- if (regex)
1002
- return Effect.sync(() => constructRegExp([regex.pattern, regex.flags], node));
1003
- return Effect.sync(() => toProgram(node.value, "Literal"));
1043
+ if (isRecord(regex) && typeof regex.pattern === "string") {
1044
+ return Effect.sync(() => this.constructRegExp([regex.pattern, typeof regex.flags === "string" ? regex.flags : ""], node));
1045
+ }
1046
+ return Effect.sync(() => boundedData(node.value, "Literal"));
1004
1047
  }
1005
1048
  case "Identifier":
1006
- return Effect.sync(() => this.scopes.get(node.name, node));
1049
+ return Effect.sync(() => this.scopes.get(getString(node, "name"), node));
1007
1050
  case "BinaryExpression":
1008
1051
  return this.evaluateBinaryExpression(node);
1009
1052
  case "LogicalExpression":
@@ -1016,8 +1059,8 @@ class Frame {
1016
1059
  const self = this;
1017
1060
  return Effect.gen(function* () {
1018
1061
  let result;
1019
- for (const expression of node.expressions) {
1020
- result = yield* self.evaluateExpression(expression);
1062
+ for (const expression of getArray(node, "expressions")) {
1063
+ result = yield* self.evaluateExpression(asNode(expression, "expressions"));
1021
1064
  }
1022
1065
  return result;
1023
1066
  });
@@ -1030,7 +1073,7 @@ class Frame {
1030
1073
  case "MemberExpression":
1031
1074
  return this.readMember(node);
1032
1075
  case "ChainExpression":
1033
- return Effect.map(this.evaluateExpression(node.expression), (value) => value === OptionalShortCircuit ? undefined : value);
1076
+ return Effect.map(this.evaluateExpression(getNode(node, "expression")), (value) => value === OptionalShortCircuit ? undefined : value);
1034
1077
  case "ObjectExpression":
1035
1078
  return this.evaluateObjectExpression(node);
1036
1079
  case "ArrayExpression":
@@ -1043,7 +1086,7 @@ class Frame {
1043
1086
  return this.evaluateUpdateExpression(node);
1044
1087
  case "AwaitExpression": {
1045
1088
  // Await always suspends, including for plain values.
1046
- return Effect.flatMap(this.evaluateExpression(node.argument), (value) => this.awaitValue(value, node));
1089
+ return Effect.flatMap(this.evaluateExpression(getNode(node, "argument")), (value) => this.awaitValue(value, node));
1047
1090
  }
1048
1091
  case "YieldExpression":
1049
1092
  return this.evaluateYieldExpression(node);
@@ -1054,39 +1097,240 @@ class Frame {
1054
1097
  }
1055
1098
  }
1056
1099
  evaluateNewExpression(node) {
1100
+ const callee = getNode(node, "callee");
1101
+ if (callee.type !== "Identifier") {
1102
+ throw unsupportedSyntax("NewExpression", node);
1103
+ }
1104
+ const name = getString(callee, "name");
1105
+ const argNodes = getArray(node, "arguments");
1106
+ const self = this;
1107
+ if (name === "Promise") {
1108
+ return Effect.flatMap(this.evaluateCallArguments(argNodes), (args) => constructPromise(self.runner, self.promises, args[0], node));
1109
+ }
1110
+ if (errorConstructors.has(name)) {
1111
+ return Effect.flatMap(this.evaluateCallArguments(argNodes), (args) => name === "AggregateError"
1112
+ ? constructAggregateErrorValue(self.runner, args, node)
1113
+ : Effect.succeed(constructErrorValue(name, args)));
1114
+ }
1115
+ // Array and Object construct identically with or without new, like JS.
1116
+ if (name === "Array") {
1117
+ return Effect.map(this.evaluateCallArguments(argNodes), (args) => self.constructArray(args, node));
1118
+ }
1119
+ if (name === "Object") {
1120
+ return Effect.map(this.evaluateCallArguments(argNodes), (args) => self.constructObject(args, node));
1121
+ }
1122
+ if (valueConstructors.has(name)) {
1123
+ return Effect.gen(function* () {
1124
+ const args = yield* self.evaluateCallArguments(argNodes);
1125
+ switch (name) {
1126
+ case "Date":
1127
+ return yield* self.constructDate(args, node);
1128
+ case "RegExp":
1129
+ return self.constructRegExp(args, node);
1130
+ case "Map":
1131
+ return yield* self.constructMap(args[0], node);
1132
+ case "Set":
1133
+ return yield* self.constructSet(args[0], node);
1134
+ case "URL":
1135
+ return self.constructURL(args, node);
1136
+ default:
1137
+ return yield* self.constructURLSearchParams(args[0], node);
1138
+ }
1139
+ });
1140
+ }
1141
+ throw unsupportedSyntax("NewExpression", node);
1142
+ }
1143
+ constructArray(args, node) {
1144
+ if (args.length !== 1)
1145
+ return [...args];
1146
+ const first = args[0];
1147
+ if (typeof first !== "number")
1148
+ return [first];
1149
+ if (!Number.isInteger(first) || first < 0 || first > 4294967295) {
1150
+ throw new InterpreterRuntimeError("Invalid array length.", node).as("RangeError");
1151
+ }
1152
+ // Sparse like JS: Array(3) has holes, and combinator loops already skip them.
1153
+ return new Array(first);
1154
+ }
1155
+ constructObject(args, node) {
1156
+ const first = args[0];
1157
+ if (first === null || first === undefined)
1158
+ return {};
1159
+ if (typeof first === "object")
1160
+ return first;
1161
+ throw new InterpreterRuntimeError(`Object(${typeof first}) wrapper objects are not supported; use the primitive value directly.`, node);
1162
+ }
1163
+ constructDate(args, node) {
1164
+ if (args.length === 0)
1165
+ return Effect.succeed(new CodeModeDate(Date.now()));
1166
+ if (args.length === 1) {
1167
+ const arg = args[0];
1168
+ if (arg instanceof CodeModeDate)
1169
+ return Effect.succeed(new CodeModeDate(arg.time));
1170
+ return Effect.map(this.toDatePrimitive(arg, node), (value) => typeof value === "string"
1171
+ ? new CodeModeDate(Date.parse(value))
1172
+ : new CodeModeDate(new Date(coerceToNumber(value)).getTime()));
1173
+ }
1174
+ const parts = args.map((arg) => coerceToNumber(arg));
1175
+ return Effect.succeed(new CodeModeDate(new Date(...parts).getTime()));
1176
+ }
1177
+ toDatePrimitive(value, node) {
1178
+ if (value === null || (typeof value !== "object" && typeof value !== "function"))
1179
+ return Effect.succeed(value);
1180
+ const object = value;
1057
1181
  const self = this;
1058
1182
  return Effect.gen(function* () {
1059
- const callee = yield* self.evaluateExpression(node.callee);
1060
- // Globals are built with this interpreter's R; `instanceof` cannot recover the type argument.
1061
- const construct = callee instanceof HostFunction ? callee.construct : undefined;
1062
- if (construct === undefined) {
1063
- // `new` itself is supported, so a non-constructible callee is a TypeError like JS rather than
1064
- // unsupported syntax. Built-ins like Number are real constructors in JS, so do not claim
1065
- // otherwise; say `new` is unsupported for them and point at the plain call.
1066
- const name = calleeDescription(node.callee);
1067
- const message = callee instanceof ProgramFunction
1068
- ? `${name} cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.`
1069
- : callee instanceof HostFunction
1070
- ? `new ${name}(...) is not supported; call ${name}(...) without new instead.`
1071
- : `${name} is not a constructor.`;
1072
- throw new InterpreterRuntimeError(message, node).as("TypeError");
1073
- }
1074
- const args = yield* self.evaluateCallArguments(node.arguments);
1075
- return yield* construct(args, node);
1183
+ if (Object.hasOwn(object, "valueOf") && typeofValue(object.valueOf) === "function") {
1184
+ const result = yield* self.runner.invokeCallable(object.valueOf, [], node);
1185
+ if (result === null || (typeof result !== "object" && typeof result !== "function"))
1186
+ return result;
1187
+ }
1188
+ if (!Object.hasOwn(object, "toString"))
1189
+ return coerceToString(value);
1190
+ if (typeofValue(object.toString) === "function") {
1191
+ const result = yield* self.runner.invokeCallable(object.toString, [], node);
1192
+ if (result === null || (typeof result !== "object" && typeof result !== "function"))
1193
+ return result;
1194
+ }
1195
+ throw new InterpreterRuntimeError("Cannot convert object to primitive value.", node).as("TypeError");
1196
+ });
1197
+ }
1198
+ constructRegExp(args, node) {
1199
+ const first = args[0];
1200
+ const pattern = first instanceof CodeModeRegExp ? first.regex.source : first === undefined ? "" : coerceToString(first);
1201
+ const flagsArg = args[1];
1202
+ if (flagsArg !== undefined && typeof flagsArg !== "string") {
1203
+ throw new InterpreterRuntimeError(`RegExp flags must be a string of flag characters (e.g. "g", "gi"), not ${flagsArg === null ? "null" : typeof flagsArg}.`, node).as("SyntaxError");
1204
+ }
1205
+ const flags = flagsArg ?? (first instanceof CodeModeRegExp ? first.regex.flags : "");
1206
+ try {
1207
+ return new CodeModeRegExp(pattern, flags);
1208
+ }
1209
+ catch (error) {
1210
+ const reason = regexFailureReason(error);
1211
+ throw new InterpreterRuntimeError(/flag/i.test(reason)
1212
+ ? `new RegExp(...) received invalid flags ${JSON.stringify(flags)} (${reason}). Valid flags are d, g, i, m, s, u, v, and y.`
1213
+ : `new RegExp(...) received ${JSON.stringify(pattern)}, which is not a valid regular expression pattern (${reason}). ${escapeRegexHint}`, node).as("SyntaxError");
1214
+ }
1215
+ }
1216
+ constructMap(init, node) {
1217
+ const target = new CodeModeMap();
1218
+ if (init === undefined || init === null)
1219
+ return Effect.succeed(target);
1220
+ const self = this;
1221
+ return Effect.gen(function* () {
1222
+ const cursor = yield* self.syncIterator(init, node);
1223
+ if (cursor === undefined) {
1224
+ throw new InterpreterRuntimeError("new Map(...) expects an iterable of [key, value] pairs or no argument.", node).as("TypeError");
1225
+ }
1226
+ while (true) {
1227
+ const step = yield* cursor.next;
1228
+ if (step.done)
1229
+ return target;
1230
+ yield* preserveConsumerError(cursor, Effect.sync(() => {
1231
+ if (!isRecord(step.value) || isRuntimeReference(step.value)) {
1232
+ throw new InterpreterRuntimeError("new Map(...) expects [key, value] pairs as entry objects.", node).as("TypeError");
1233
+ }
1234
+ target.map.set(step.value[0], step.value[1]);
1235
+ }));
1236
+ }
1237
+ });
1238
+ }
1239
+ constructSet(init, node) {
1240
+ const target = new CodeModeSet();
1241
+ if (init === undefined || init === null)
1242
+ return Effect.succeed(target);
1243
+ const self = this;
1244
+ return Effect.gen(function* () {
1245
+ const cursor = yield* self.syncIterator(init, node);
1246
+ if (cursor === undefined) {
1247
+ throw new InterpreterRuntimeError("new Set(...) expects a synchronous iterable or no argument.", node).as("TypeError");
1248
+ }
1249
+ while (true) {
1250
+ const step = yield* cursor.next;
1251
+ if (step.done)
1252
+ return target;
1253
+ target.set.add(step.value);
1254
+ }
1255
+ });
1256
+ }
1257
+ constructURL(args, node) {
1258
+ if (args.length === 0) {
1259
+ throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node).as("TypeError");
1260
+ }
1261
+ const input = urlArgument(args[0], "new URL input");
1262
+ const base = args[1] === undefined ? undefined : urlArgument(args[1], "new URL base");
1263
+ try {
1264
+ return new CodeModeURL(new URL(input, base));
1265
+ }
1266
+ catch {
1267
+ throw new InterpreterRuntimeError(`new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`, node).as("TypeError");
1268
+ }
1269
+ }
1270
+ constructURLSearchParams(init, node) {
1271
+ if (init === undefined)
1272
+ return Effect.succeed(new CodeModeURLSearchParams(new URLSearchParams()));
1273
+ if (init instanceof CodeModeURLSearchParams) {
1274
+ return Effect.succeed(new CodeModeURLSearchParams(new URLSearchParams(init.params)));
1275
+ }
1276
+ if (typeof init === "string")
1277
+ return Effect.succeed(new CodeModeURLSearchParams(new URLSearchParams(init)));
1278
+ if (init === null || typeof init === "number" || typeof init === "boolean") {
1279
+ return Effect.succeed(new CodeModeURLSearchParams(new URLSearchParams(coerceToString(init))));
1280
+ }
1281
+ const self = this;
1282
+ return Effect.gen(function* () {
1283
+ const cursor = yield* self.syncIterator(init, node);
1284
+ if (cursor !== undefined) {
1285
+ const entries = [];
1286
+ while (true) {
1287
+ const step = yield* cursor.next;
1288
+ if (step.done) {
1289
+ if (entries.some((entry) => entry.length !== 2)) {
1290
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as("TypeError");
1291
+ }
1292
+ return new CodeModeURLSearchParams(new URLSearchParams(entries.map((entry) => [entry[0] ?? "", entry[1] ?? ""])));
1293
+ }
1294
+ entries.push(yield* preserveConsumerError(cursor, self.readURLSearchParamsPair(step.value, node)));
1295
+ }
1296
+ }
1297
+ if (isRuntimeReference(init)) {
1298
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, or synchronous iterable pairs.", node).as("TypeError");
1299
+ }
1300
+ if (isCodeModeValue(init))
1301
+ return new CodeModeURLSearchParams(new URLSearchParams());
1302
+ const data = boundedData(init, "new URLSearchParams input");
1303
+ if (data === null || typeof data !== "object") {
1304
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, iterable pairs, or URLSearchParams.", node).as("TypeError");
1305
+ }
1306
+ return new CodeModeURLSearchParams(new URLSearchParams(Object.fromEntries(Object.entries(data).map(([key, value]) => [key, coerceToString(value)]))));
1307
+ });
1308
+ }
1309
+ readURLSearchParamsPair(value, node) {
1310
+ const self = this;
1311
+ return Effect.gen(function* () {
1312
+ const cursor = yield* self.syncIterator(value, node);
1313
+ if (cursor === undefined) {
1314
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as("TypeError");
1315
+ }
1316
+ const items = [];
1317
+ while (true) {
1318
+ const step = yield* cursor.next;
1319
+ if (step.done)
1320
+ return items;
1321
+ items.push(yield* preserveConsumerError(cursor, Effect.sync(() => uriArgument(step.value, "URLSearchParams pair value"))));
1322
+ }
1076
1323
  });
1077
1324
  }
1078
1325
  evaluateBinaryExpression(node) {
1079
- const operator = node.operator;
1080
- const left = node.left;
1081
- if (left.type === "PrivateIdentifier")
1082
- throw unsupportedSyntax(left.type, left);
1326
+ const operator = getString(node, "operator");
1083
1327
  const self = this;
1084
1328
  return Effect.gen(function* () {
1085
- const lhs = yield* self.evaluateExpression(left);
1086
- const rhs = yield* self.evaluateExpression(node.right);
1329
+ const lhs = yield* self.evaluateExpression(getNode(node, "left"));
1330
+ const rhs = yield* self.evaluateExpression(getNode(node, "right"));
1087
1331
  if (operator === "instanceof")
1088
1332
  return instanceofValue(lhs, rhs, node);
1089
- return toProgram(self.applyBinaryOperator(operator, lhs, rhs, node), "Binary expression result");
1333
+ return boundedData(self.applyBinaryOperator(operator, lhs, rhs, node), "Binary expression result");
1090
1334
  });
1091
1335
  }
1092
1336
  applyBinaryOperator(operator, lhs, rhs, node) {
@@ -1094,16 +1338,13 @@ class Frame {
1094
1338
  return lhs === rhs;
1095
1339
  if (operator === "!==")
1096
1340
  return lhs !== rhs;
1097
- if (operator === "in" && rhs instanceof ProgramObject && !containsOpaqueReference(lhs)) {
1098
- return has(rhs, lhs !== null && typeof lhs === "object" ? coerceToString(lhs) : lhs);
1099
- }
1100
1341
  if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
1101
1342
  throw new InterpreterRuntimeError("Binary operators require data values.", node, "InvalidDataValue");
1102
1343
  }
1103
1344
  // Null-prototype data needs explicit primitive coercion; identity and `in` retain raw objects.
1104
1345
  // Dates use their default string hint for addition and loose equality, and epoch time elsewhere.
1105
1346
  const coerceOperand = (operand) => {
1106
- if (operand instanceof Values.Date) {
1347
+ if (operand instanceof CodeModeDate) {
1107
1348
  return operator === "+" || operator === "==" || operator === "!=" ? coerceToString(operand) : operand.time;
1108
1349
  }
1109
1350
  return operand !== null && typeof operand === "object" ? coerceToString(operand) : operand;
@@ -1149,33 +1390,36 @@ class Frame {
1149
1390
  case ">>>":
1150
1391
  return l >>> r;
1151
1392
  case "in":
1152
- if (!(rhs instanceof ProgramObject)) {
1393
+ if (rhs === null || typeof rhs !== "object") {
1153
1394
  throw new InterpreterRuntimeError("The 'in' operator requires a data object on the right-hand side.", node);
1154
1395
  }
1155
- return has(rhs, coerceOperand(lhs));
1396
+ // Never expose properties inherited from host prototypes.
1397
+ return Object.hasOwn(rhs, coerceOperand(lhs));
1156
1398
  default:
1157
1399
  throw new InterpreterRuntimeError(`Unsupported binary operator '${operator}'.`, node);
1158
1400
  }
1159
1401
  }
1160
1402
  evaluateLogicalExpression(node) {
1161
- const operator = node.operator;
1162
- return Effect.flatMap(this.evaluateExpression(node.left), (left) => {
1403
+ const operator = getString(node, "operator");
1404
+ return Effect.flatMap(this.evaluateExpression(getNode(node, "left")), (left) => {
1163
1405
  if (operator === "&&")
1164
- return left ? this.evaluateExpression(node.right) : Effect.succeed(left);
1406
+ return left ? this.evaluateExpression(getNode(node, "right")) : Effect.succeed(left);
1165
1407
  if (operator === "||")
1166
- return left ? Effect.succeed(left) : this.evaluateExpression(node.right);
1408
+ return left ? Effect.succeed(left) : this.evaluateExpression(getNode(node, "right"));
1167
1409
  if (operator === "??")
1168
- return left !== null && left !== undefined ? Effect.succeed(left) : this.evaluateExpression(node.right);
1410
+ return left !== null && left !== undefined
1411
+ ? Effect.succeed(left)
1412
+ : this.evaluateExpression(getNode(node, "right"));
1169
1413
  throw new InterpreterRuntimeError(`Unsupported logical operator '${operator}'.`, node);
1170
1414
  });
1171
1415
  }
1172
1416
  evaluateUnaryExpression(node) {
1173
- const operator = node.operator;
1174
- const argument = node.argument;
1417
+ const operator = getString(node, "operator");
1418
+ const argument = getNode(node, "argument");
1175
1419
  if (operator === "delete")
1176
1420
  return this.evaluateDeleteExpression(argument);
1177
1421
  // Undeclared names short-circuit, but declared TDZ bindings must still throw.
1178
- if (operator === "typeof" && argument.type === "Identifier" && !this.scopes.resolve(argument.name)) {
1422
+ if (operator === "typeof" && argument.type === "Identifier" && !this.scopes.resolve(getString(argument, "name"))) {
1179
1423
  return Effect.succeed("undefined");
1180
1424
  }
1181
1425
  return Effect.map(this.evaluateExpression(argument), (value) => {
@@ -1188,7 +1432,7 @@ class Frame {
1188
1432
  if (containsOpaqueReference(value)) {
1189
1433
  throw new InterpreterRuntimeError("Unary operators require data values.", node, "InvalidDataValue");
1190
1434
  }
1191
- const operand = value instanceof Values.Date
1435
+ const operand = value instanceof CodeModeDate
1192
1436
  ? value.time
1193
1437
  : value !== null && typeof value === "object"
1194
1438
  ? coerceToString(value)
@@ -1207,38 +1451,38 @@ class Frame {
1207
1451
  default:
1208
1452
  throw new InterpreterRuntimeError(`Unsupported unary operator '${operator}'.`, node);
1209
1453
  }
1210
- return toProgram(result, "Unary expression result");
1454
+ return boundedData(result, "Unary expression result");
1211
1455
  });
1212
1456
  }
1213
1457
  evaluateAssignmentExpression(node) {
1214
- const left = node.left;
1215
- const operator = node.operator;
1458
+ const left = getNode(node, "left");
1459
+ const operator = getString(node, "operator");
1216
1460
  const self = this;
1217
1461
  return Effect.gen(function* () {
1218
1462
  if (operator === "??=" || operator === "||=" || operator === "&&=") {
1219
1463
  return yield* self.evaluateLogicalAssignment(node, left, operator);
1220
1464
  }
1221
1465
  if (operator === "=" && (left.type === "ObjectPattern" || left.type === "ArrayPattern")) {
1222
- const rightValue = yield* self.evaluateExpression(node.right);
1466
+ const rightValue = yield* self.evaluateExpression(getNode(node, "right"));
1223
1467
  yield* self.assignPattern(left, rightValue, node);
1224
1468
  return rightValue;
1225
1469
  }
1226
1470
  if (left.type === "Identifier") {
1227
- const name = left.name;
1471
+ const name = getString(left, "name");
1228
1472
  if (operator !== "=") {
1229
1473
  const current = self.scopes.get(name, left);
1230
- const rightValue = yield* self.evaluateExpression(node.right);
1231
- const next = toProgram(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result");
1474
+ const rightValue = yield* self.evaluateExpression(getNode(node, "right"));
1475
+ const next = boundedData(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result");
1232
1476
  return self.scopes.set(name, next, left);
1233
1477
  }
1234
- const rightValue = yield* self.evaluateNamed(node.right, name);
1478
+ const rightValue = yield* self.evaluateExpression(getNode(node, "right"));
1235
1479
  return self.scopes.set(name, rightValue, left);
1236
1480
  }
1237
1481
  if (left.type === "MemberExpression") {
1238
- return yield* self.modifyMember(left, (current) => Effect.map(self.evaluateExpression(node.right), (rightValue) => {
1482
+ return yield* self.modifyMember(left, (current) => Effect.map(self.evaluateExpression(getNode(node, "right")), (rightValue) => {
1239
1483
  if (operator === "=")
1240
1484
  return { write: true, next: rightValue, result: rightValue };
1241
- const next = toProgram(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result");
1485
+ const next = boundedData(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result");
1242
1486
  return { write: true, next, result: next };
1243
1487
  }));
1244
1488
  }
@@ -1249,18 +1493,18 @@ class Frame {
1249
1493
  const self = this;
1250
1494
  const shouldAssign = (current) => operator === "??=" ? current === null || current === undefined : operator === "||=" ? !current : Boolean(current);
1251
1495
  if (left.type === "Identifier") {
1252
- const name = left.name;
1496
+ const name = getString(left, "name");
1253
1497
  return Effect.gen(function* () {
1254
1498
  const current = self.scopes.get(name, left);
1255
1499
  if (!shouldAssign(current))
1256
1500
  return current;
1257
- const rightValue = yield* self.evaluateNamed(node.right, name);
1501
+ const rightValue = yield* self.evaluateExpression(getNode(node, "right"));
1258
1502
  return self.scopes.set(name, rightValue, left);
1259
1503
  });
1260
1504
  }
1261
1505
  if (left.type === "MemberExpression") {
1262
1506
  return self.modifyMember(left, (current) => shouldAssign(current)
1263
- ? Effect.map(self.evaluateExpression(node.right), (rightValue) => ({
1507
+ ? Effect.map(self.evaluateExpression(getNode(node, "right")), (rightValue) => ({
1264
1508
  write: true,
1265
1509
  next: rightValue,
1266
1510
  result: rightValue,
@@ -1270,9 +1514,9 @@ class Frame {
1270
1514
  throw new InterpreterRuntimeError("Assignment target must be an Identifier or MemberExpression.", left);
1271
1515
  }
1272
1516
  evaluateUpdateExpression(node) {
1273
- const operator = node.operator;
1274
- const argument = node.argument;
1275
- const prefix = node.prefix;
1517
+ const operator = getString(node, "operator");
1518
+ const argument = getNode(node, "argument");
1519
+ const prefix = getBoolean(node, "prefix");
1276
1520
  const increment = operator === "++" ? 1 : operator === "--" ? -1 : undefined;
1277
1521
  if (increment === undefined) {
1278
1522
  throw new InterpreterRuntimeError(`Unsupported update operator '${operator}'.`, node);
@@ -1287,7 +1531,7 @@ class Frame {
1287
1531
  };
1288
1532
  if (argument.type === "Identifier") {
1289
1533
  return Effect.sync(() => {
1290
- const name = argument.name;
1534
+ const name = getString(argument, "name");
1291
1535
  const current = operand(this.scopes.get(name, argument));
1292
1536
  const next = current + increment;
1293
1537
  this.scopes.set(name, next, argument);
@@ -1304,31 +1548,35 @@ class Frame {
1304
1548
  throw new InterpreterRuntimeError("Update target must be an Identifier or MemberExpression.", argument);
1305
1549
  }
1306
1550
  evaluateCallExpression(node) {
1307
- const callee = node.callee;
1551
+ const callee = getNode(node, "callee");
1552
+ const argNodes = getArray(node, "arguments");
1308
1553
  const self = this;
1309
1554
  return Effect.gen(function* () {
1310
- if (callee.type === "Super")
1311
- throw unsupportedSyntax(callee.type, callee);
1312
1555
  const callable = yield* self.evaluateExpression(callee);
1313
1556
  if (callable === OptionalShortCircuit)
1314
1557
  return OptionalShortCircuit;
1315
- if ((callable === null || callable === undefined) && node.optional)
1558
+ if ((callable === null || callable === undefined) && node.optional === true)
1316
1559
  return OptionalShortCircuit;
1317
- const args = yield* self.evaluateCallArguments(node.arguments);
1560
+ const args = yield* self.evaluateCallArguments(argNodes);
1318
1561
  return yield* self.invokeCallable(callable, args, node, callee);
1319
1562
  });
1320
1563
  }
1321
1564
  // The single dispatch for every invocation: call expressions and callbacks share it.
1322
- invokeCallable(callable, args, node, callee) {
1565
+ invokeCallable(callable, args, node, callee = node) {
1323
1566
  const self = this;
1324
1567
  return Effect.gen(function* () {
1325
1568
  if (callable instanceof ToolReference) {
1326
- if (callable.path.length === 0) {
1327
- throw new InterpreterRuntimeError("The tools root is not callable.", callee ?? node);
1328
- }
1569
+ if (callable.path.length === 0)
1570
+ throw new InterpreterRuntimeError("The tools root is not callable.", callee);
1329
1571
  return yield* self.createToolCallPromise(callable.path, args);
1330
1572
  }
1331
- if (callable instanceof ProgramFunction) {
1573
+ if (callable instanceof PromiseMethodReference) {
1574
+ return yield* invokePromiseMethod(self.runner, self.promises, callable, args, node);
1575
+ }
1576
+ if (callable instanceof PromiseInstanceMethodReference) {
1577
+ return yield* invokePromiseInstanceMethod(self.runner, self.promises, callable, args, node);
1578
+ }
1579
+ if (callable instanceof CodeModeFunction) {
1332
1580
  return yield* self.invokeFunction(callable, args);
1333
1581
  }
1334
1582
  if (callable instanceof GeneratorMethodReference) {
@@ -1338,26 +1586,103 @@ class Frame {
1338
1586
  return callable.generator.asynchronous ? yield* self.createPromise(requested) : yield* requested;
1339
1587
  }
1340
1588
  if (callable instanceof IntrinsicReference) {
1341
- return yield* invokeIntrinsic(self.runtime.runner, callable, args, node);
1589
+ return yield* invokeIntrinsic(self.runner, callable, args, node);
1342
1590
  }
1343
- if (callable instanceof PromiseInstanceMethodReference) {
1344
- return yield* invokePromiseInstanceMethod(self.runtime.runner, self.runtime.promises, callable, args, node);
1591
+ if (callable instanceof GlobalMethodReference) {
1592
+ if (callable.namespace === "console")
1593
+ return self.invokeConsole(callable.name, args, node);
1594
+ if (callable.namespace === "Object" && args[0] instanceof ToolReference) {
1595
+ return self.invokeObjectMethodOnTools(callable.name, args[0], node);
1596
+ }
1597
+ if (callable.namespace === "Object" && objectMethodsPreservingIdentity.has(callable.name)) {
1598
+ if (callable.name === "fromEntries")
1599
+ return yield* invokeObjectFromEntries(self.runner, args[0], node);
1600
+ return invokeGlobalMethod(callable, args, node);
1601
+ }
1602
+ if (callable.namespace === "Array" && callable.name === "from") {
1603
+ return yield* invokeArrayFrom(self.runner, args, node);
1604
+ }
1605
+ if ((callable.namespace === "Object" || callable.namespace === "Map") && callable.name === "groupBy") {
1606
+ return yield* invokeGroupBy(self.runner, callable.namespace, args, node);
1607
+ }
1608
+ if (callable.namespace === "Math" && callable.name === "sumPrecise") {
1609
+ return yield* invokeMathSumPrecise(self.runner, args[0], node);
1610
+ }
1611
+ if (callable.namespace === "Array" && callable.name === "of") {
1612
+ return invokeGlobalMethod(callable, args, node);
1613
+ }
1614
+ return boundedData(invokeGlobalMethod(callable, args, node), `${callable.namespace}.${callable.name} result`);
1615
+ }
1616
+ if (callable instanceof JsonMethodReference) {
1617
+ return yield* invokeJsonMethod(self.runner, callable.name, args, node);
1618
+ }
1619
+ if (callable instanceof CoercionFunction) {
1620
+ return boundedData(invokeCoercion(callable, args, node), `${callable.name} result`);
1621
+ }
1622
+ if (callable instanceof UriFunction) {
1623
+ return invokeUriFunction(callable, args, node);
1624
+ }
1625
+ if (callable instanceof SearchFunction) {
1626
+ return yield* self.invokeSearch(args);
1627
+ }
1628
+ if (callable instanceof ErrorConstructorReference) {
1629
+ if (callable.name === "AggregateError")
1630
+ return yield* constructAggregateErrorValue(self.runner, args, node);
1631
+ return constructErrorValue(callable.name, args);
1632
+ }
1633
+ if (callable instanceof GlobalNamespace) {
1634
+ // Real JS permits calling Array, Object, Date, and RegExp without new.
1635
+ if (callable.name === "Array")
1636
+ return self.constructArray(args, node);
1637
+ if (callable.name === "Object")
1638
+ return self.constructObject(args, node);
1639
+ // ISO instead of the host's locale string: CodeMode date strings are
1640
+ // deterministic and must not leak the host timezone.
1641
+ if (callable.name === "Date")
1642
+ return new Date().toISOString();
1643
+ if (callable.name === "RegExp")
1644
+ return self.constructRegExp(args, node);
1645
+ if (typeofValue(callable) === "function") {
1646
+ throw new InterpreterRuntimeError(`Constructor ${callable.name} requires 'new'.`, node).as("TypeError");
1647
+ }
1648
+ throw new InterpreterRuntimeError(`${callable.name} is not a function.`, node).as("TypeError");
1649
+ }
1650
+ if (callable instanceof PromiseNamespace) {
1651
+ throw new InterpreterRuntimeError("Constructor Promise requires 'new'.", node).as("TypeError");
1652
+ }
1653
+ if (callable instanceof SymbolNamespace) {
1654
+ throw new InterpreterRuntimeError("Symbol is not callable; only Symbol.asyncIterator and Symbol.iterator are available.", node).as("TypeError");
1655
+ }
1656
+ if (callable instanceof PromiseCapabilityFunction) {
1657
+ callable.settle(args[0]);
1658
+ return undefined;
1345
1659
  }
1346
- if (callable instanceof HostFunction)
1347
- return yield* callable.call(args, node);
1348
1660
  if (callable === undefined || callable === null) {
1349
- throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee ?? node).as("TypeError");
1661
+ throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee).as("TypeError");
1350
1662
  }
1351
- throw new InterpreterRuntimeError("Only tools are callable here.", callee ?? node);
1663
+ throw new InterpreterRuntimeError("Only tools are callable here.", callee);
1352
1664
  });
1353
1665
  }
1666
+ invokeObjectMethodOnTools(name, ref, node) {
1667
+ if (name === "keys") {
1668
+ return boundedData(this.enumerableKeys(ref), "Object.keys result");
1669
+ }
1670
+ throw new InterpreterRuntimeError(`Object.${name}(...) cannot read tool references: they are not plain data. Use Object.keys(tools) for names, or search({ query }) for signatures.`, node, "InvalidDataValue");
1671
+ }
1672
+ invokeConsole(name, args, node) {
1673
+ if (!consoleMethods.has(name))
1674
+ throw new InterpreterRuntimeError(`console.${name} is not available.`, node);
1675
+ this.logs.push(formatConsoleMessage(name, args));
1676
+ return undefined;
1677
+ }
1354
1678
  evaluateCallArguments(argNodes) {
1355
1679
  const self = this;
1356
1680
  return Effect.gen(function* () {
1357
1681
  const args = [];
1358
- for (const argNode of argNodes) {
1682
+ for (const [index, arg] of argNodes.entries()) {
1683
+ const argNode = asNode(arg, `arguments[${index}]`);
1359
1684
  if (argNode.type === "SpreadElement") {
1360
- const spread = yield* self.evaluateExpression(argNode.argument);
1685
+ const spread = yield* self.evaluateExpression(getNode(argNode, "argument"));
1361
1686
  const cursor = yield* self.syncIterator(spread, argNode);
1362
1687
  if (cursor === undefined)
1363
1688
  throw new InterpreterRuntimeError("Spread arguments require a synchronous iterable.", argNode).as("TypeError");
@@ -1376,7 +1701,8 @@ class Frame {
1376
1701
  });
1377
1702
  }
1378
1703
  invokeFunction(fn, args) {
1379
- const invocation = new Frame(this.runtime, new ScopeStack([...fn.capturedScopes, new Map()]));
1704
+ const invocation = new Interpreter(this.executeTool, this.invokeSearch, this.toolKeys, this.promises, this.logs);
1705
+ invocation.scopes = new ScopeStack([...fn.capturedScopes, new Map()]);
1380
1706
  const run = Effect.gen(function* () {
1381
1707
  // Seed all parameters first so defaults cannot fall through to same-named outer bindings.
1382
1708
  const paramScope = invocation.scopes.current();
@@ -1387,14 +1713,12 @@ class Frame {
1387
1713
  }
1388
1714
  for (const [index, parameter] of fn.parameters.entries()) {
1389
1715
  if (parameter.type === "RestElement") {
1390
- yield* invocation.declarePattern(parameter.argument, new ProgramArray(args.slice(index)), true, parameter, true);
1716
+ yield* invocation.declarePattern(getNode(parameter, "argument"), args.slice(index), true, parameter, true);
1391
1717
  break;
1392
1718
  }
1393
1719
  yield* invocation.declarePattern(parameter, args[index], true, parameter, true);
1394
1720
  }
1395
1721
  if (fn.body.type === "BlockStatement") {
1396
- invocation.scopes.push();
1397
- invocation.hoistVars(fn.body.body, paramScope);
1398
1722
  const result = yield* invocation.evaluateStatement(fn.body);
1399
1723
  return result.kind === "return" ? result.value : undefined;
1400
1724
  }
@@ -1404,7 +1728,12 @@ class Frame {
1404
1728
  return Effect.succeed(this.createGenerator(invocation, run, fn.async));
1405
1729
  if (!fn.async)
1406
1730
  return run;
1407
- return this.runtime.promises.createWithSelf((self) => Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runtime.runner, value, fn.body, self)));
1731
+ // The initial yield assigns the promise before the body can self-resolve.
1732
+ const box = {};
1733
+ return Effect.map(this.createPromise(Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runner, value, fn.body, box))), (promise) => {
1734
+ box.promise = promise;
1735
+ return promise;
1736
+ });
1408
1737
  }
1409
1738
  createGenerator(invocation, run, asynchronous) {
1410
1739
  const state = { started: false, completed: false, draining: false, pending: [], pendingIndex: 0 };
@@ -1422,20 +1751,20 @@ class Frame {
1422
1751
  if (state.draining)
1423
1752
  return Deferred.await(request.response);
1424
1753
  state.draining = true;
1425
- return Effect.andThen(this.runtime.promises.fork(invocation
1754
+ return Effect.andThen(this.promises.fork(invocation
1426
1755
  .completeGeneratorRequests(state, true)
1427
1756
  .pipe(Effect.ensuring(Effect.sync(() => (state.draining = false))))), Deferred.await(request.response));
1428
1757
  }
1429
1758
  if (state.completed) {
1430
1759
  if (kind === "throw")
1431
1760
  return Effect.fail(new ProgramThrow(value));
1432
- return Effect.succeed(record({ value: kind === "return" ? value : undefined, done: true }));
1761
+ return Effect.succeed({ value: kind === "return" ? value : undefined, done: true });
1433
1762
  }
1434
1763
  if (!state.started && kind !== "next") {
1435
1764
  state.completed = true;
1436
1765
  if (kind === "throw")
1437
1766
  return Effect.fail(new ProgramThrow(value));
1438
- return Effect.succeed(record({ value, done: true }));
1767
+ return Effect.succeed({ value, done: true });
1439
1768
  }
1440
1769
  state.pending.push(request);
1441
1770
  if (state.available) {
@@ -1455,12 +1784,12 @@ class Frame {
1455
1784
  const active = state.active;
1456
1785
  state.active = undefined;
1457
1786
  if (active) {
1458
- Deferred.doneUnsafe(active.response, Exit.isSuccess(exit) ? Exit.succeed(record({ value: exit.value, done: true })) : exit);
1787
+ Deferred.doneUnsafe(active.response, Exit.isSuccess(exit) ? Exit.succeed({ value: exit.value, done: true }) : exit);
1459
1788
  }
1460
1789
  yield* invocation.completeGeneratorRequests(state, asynchronous);
1461
1790
  state.completed = true;
1462
1791
  });
1463
- return Effect.andThen(this.runtime.promises.fork(body), Deferred.await(request.response));
1792
+ return Effect.andThen(this.promises.fork(body), Deferred.await(request.response));
1464
1793
  }
1465
1794
  return Deferred.await(request.response);
1466
1795
  });
@@ -1479,10 +1808,10 @@ class Frame {
1479
1808
  }
1480
1809
  if (asynchronous && pending.kind === "return") {
1481
1810
  const resolved = yield* Effect.exit(self.awaitValue(pending.value));
1482
- Deferred.doneUnsafe(pending.response, Exit.isSuccess(resolved) ? Exit.succeed(record({ value: resolved.value, done: true })) : resolved);
1811
+ Deferred.doneUnsafe(pending.response, Exit.isSuccess(resolved) ? Exit.succeed({ value: resolved.value, done: true }) : resolved);
1483
1812
  continue;
1484
1813
  }
1485
- Deferred.doneUnsafe(pending.response, Exit.succeed(record({ value: pending.kind === "return" ? pending.value : undefined, done: true })));
1814
+ Deferred.doneUnsafe(pending.response, Exit.succeed({ value: pending.kind === "return" ? pending.value : undefined, done: true }));
1486
1815
  }
1487
1816
  });
1488
1817
  }
@@ -1505,12 +1834,12 @@ class Frame {
1505
1834
  return request;
1506
1835
  }
1507
1836
  evaluateYieldExpression(node) {
1508
- const argument = node.argument;
1837
+ const argument = getOptionalNode(node, "argument");
1509
1838
  const self = this;
1510
1839
  return Effect.gen(function* () {
1511
1840
  if (!self.generatorState)
1512
1841
  throw new InterpreterRuntimeError("yield is only valid inside a generator.", node);
1513
- if (node.delegate) {
1842
+ if (node.delegate === true) {
1514
1843
  const value = argument ? yield* self.evaluateExpression(argument) : undefined;
1515
1844
  return yield* self.delegateYield(value, node);
1516
1845
  }
@@ -1523,7 +1852,7 @@ class Frame {
1523
1852
  const state = this.generatorState;
1524
1853
  if (!state?.active)
1525
1854
  throw new InterpreterRuntimeError("Generator has no active request.", node);
1526
- Deferred.doneUnsafe(state.active.response, Exit.succeed(record({ value, done: false })));
1855
+ Deferred.doneUnsafe(state.active.response, Exit.succeed({ value, done: false }));
1527
1856
  state.active = undefined;
1528
1857
  return Effect.flatMap(this.takeGeneratorRequest(state), (request) => {
1529
1858
  state.active = request;
@@ -1539,11 +1868,11 @@ class Frame {
1539
1868
  delegateYield(value, node) {
1540
1869
  const self = this;
1541
1870
  return Effect.gen(function* () {
1542
- if (value instanceof ProgramArray ||
1871
+ if (Array.isArray(value) ||
1543
1872
  typeof value === "string" ||
1544
- value instanceof Values.Map ||
1545
- value instanceof Values.Set ||
1546
- value instanceof Values.URLSearchParams) {
1873
+ value instanceof CodeModeMap ||
1874
+ value instanceof CodeModeSet ||
1875
+ value instanceof CodeModeURLSearchParams) {
1547
1876
  const cursor = yield* self.syncIterator(value, node);
1548
1877
  if (!cursor)
1549
1878
  throw new InterpreterRuntimeError("Built-in iterator is unavailable.", node);
@@ -1576,7 +1905,7 @@ class Frame {
1576
1905
  ? iterator.next
1577
1906
  : iterator.iterator instanceof CodeModeGenerator
1578
1907
  ? new GeneratorMethodReference(iterator.iterator, kind)
1579
- : get(iterator.iterator, kind);
1908
+ : iterator.iterator[kind];
1580
1909
  if (method === undefined || method === null) {
1581
1910
  if (kind === "return")
1582
1911
  return yield* Effect.fail(new GeneratorReturn(input));
@@ -1585,10 +1914,10 @@ class Frame {
1585
1914
  }
1586
1915
  const called = yield* self.invokeCallable(self.requireIteratorMethod(method, `Iterator ${kind}`, node), [input], node);
1587
1916
  const result = self.requireIteratorObject(iterator.asynchronous ? yield* self.awaitValue(called) : called, `Iterator ${kind}() result`, node);
1588
- const done = Boolean(get(result, "done"));
1917
+ const done = Boolean(result.done);
1589
1918
  const resultValue = self.generatorAsync && !iterator.asynchronous
1590
- ? yield* self.awaitAsyncFromSyncValue(iterator, get(result, "value"), node, kind !== "return" && !done)
1591
- : get(result, "value");
1919
+ ? yield* self.awaitAsyncFromSyncValue(iterator, result.value, node, kind !== "return" && !done)
1920
+ : result.value;
1592
1921
  if (done) {
1593
1922
  if (kind === "return")
1594
1923
  return yield* Effect.fail(new GeneratorReturn(resultValue));
@@ -1610,27 +1939,42 @@ class Frame {
1610
1939
  });
1611
1940
  }
1612
1941
  evaluateObjectExpression(node) {
1613
- const objectValue = new ProgramObject();
1942
+ const objectValue = Object.create(null);
1943
+ const properties = getArray(node, "properties");
1614
1944
  const self = this;
1615
1945
  return Effect.gen(function* () {
1616
- for (const property of node.properties) {
1946
+ for (const propertyValue of properties) {
1947
+ const property = asNode(propertyValue, "properties");
1617
1948
  if (property.type === "SpreadElement") {
1618
- const spread = yield* self.evaluateExpression(property.argument);
1619
- if (spread === null || spread === undefined)
1949
+ const spread = yield* self.evaluateExpression(getNode(property, "argument"));
1950
+ if (spread === null || spread === undefined || isCodeModeValue(spread))
1620
1951
  continue;
1621
- assign(objectValue, enumerableSource("Object spread", spread, property));
1952
+ if (typeof spread !== "object" || Array.isArray(spread) || isRuntimeReference(spread)) {
1953
+ throw new InterpreterRuntimeError("Object spread requires a data object.", property, "InvalidDataValue");
1954
+ }
1955
+ for (const [key, value] of Object.entries(spread)) {
1956
+ if (isBlockedMember(key))
1957
+ throw new InterpreterRuntimeError(`Property '${key}' is not available.`, property);
1958
+ objectValue[key] = value;
1959
+ }
1960
+ copyIteratorSymbols(spread, objectValue);
1622
1961
  continue;
1623
1962
  }
1624
- if (property.kind !== "init") {
1963
+ if (property.type !== "Property") {
1964
+ throw new InterpreterRuntimeError("Only standard object properties are supported.", property);
1965
+ }
1966
+ if (getString(property, "kind") !== "init") {
1625
1967
  throw new InterpreterRuntimeError("Only init object properties are supported.", property);
1626
1968
  }
1627
- const keyNode = property.key;
1969
+ const keyNode = getNode(property, "key");
1970
+ const valueNode = getNode(property, "value");
1971
+ const computed = getBoolean(property, "computed");
1628
1972
  let key;
1629
- if (property.computed) {
1973
+ if (computed) {
1630
1974
  key = self.toPropertyKey(yield* self.evaluateExpression(keyNode), keyNode);
1631
1975
  }
1632
1976
  else if (keyNode.type === "Identifier") {
1633
- key = keyNode.name;
1977
+ key = getString(keyNode, "name");
1634
1978
  }
1635
1979
  else if (keyNode.type === "Literal") {
1636
1980
  key = self.toPropertyKey(keyNode.value, keyNode);
@@ -1638,28 +1982,28 @@ class Frame {
1638
1982
  else {
1639
1983
  throw new InterpreterRuntimeError("Unsupported object property key shape.", keyNode);
1640
1984
  }
1641
- const name = key === IteratorSymbol
1642
- ? "[Symbol.iterator]"
1643
- : key === AsyncIteratorSymbol
1644
- ? "[Symbol.asyncIterator]"
1645
- : String(key);
1646
- set(objectValue, key, yield* self.evaluateNamed(property.value, name));
1985
+ if (isBlockedMember(String(key))) {
1986
+ throw new InterpreterRuntimeError(`Property '${String(key)}' is not available.`, keyNode);
1987
+ }
1988
+ Reflect.set(objectValue, key, yield* self.evaluateExpression(valueNode));
1647
1989
  }
1648
1990
  return objectValue;
1649
1991
  });
1650
1992
  }
1651
1993
  evaluateArrayExpression(node) {
1994
+ const elements = getArray(node, "elements");
1652
1995
  const values = [];
1653
1996
  const self = this;
1654
1997
  return Effect.gen(function* () {
1655
- for (const element of node.elements) {
1656
- if (element === null) {
1998
+ for (const elementValue of elements) {
1999
+ if (elementValue === null) {
1657
2000
  // A literal elision is a real hole, like JS: extend length without an own index.
1658
2001
  values.length += 1;
1659
2002
  continue;
1660
2003
  }
2004
+ const element = asNode(elementValue, "elements");
1661
2005
  if (element.type === "SpreadElement") {
1662
- const spread = yield* self.evaluateExpression(element.argument);
2006
+ const spread = yield* self.evaluateExpression(getNode(element, "argument"));
1663
2007
  const cursor = yield* self.syncIterator(spread, element);
1664
2008
  if (cursor === undefined)
1665
2009
  throw new InterpreterRuntimeError("Array spread requires a synchronous iterable.", element).as("TypeError");
@@ -1674,32 +2018,32 @@ class Frame {
1674
2018
  values.push(yield* self.evaluateExpression(element));
1675
2019
  }
1676
2020
  }
1677
- return new ProgramArray(values);
2021
+ return values;
1678
2022
  });
1679
2023
  }
1680
2024
  evaluateTemplateLiteral(node) {
1681
- const quasis = node.quasis;
1682
- const expressions = node.expressions;
2025
+ const quasis = getArray(node, "quasis");
2026
+ const expressions = getArray(node, "expressions");
1683
2027
  let output = "";
1684
2028
  const self = this;
1685
2029
  return Effect.gen(function* () {
1686
2030
  for (let index = 0; index < quasis.length; index += 1) {
1687
- const quasi = quasis[index];
1688
- // acorn only omits `cooked` for invalid escapes in tagged templates, which are unsupported.
1689
- if (typeof quasi.value.cooked !== "string") {
2031
+ const quasi = asNode(quasis[index], "quasis");
2032
+ const rawValue = quasi.value;
2033
+ if (!isRecord(rawValue) || typeof rawValue.cooked !== "string") {
1690
2034
  throw new InterpreterRuntimeError("Invalid template literal quasi.", quasi);
1691
2035
  }
1692
- output += quasi.value.cooked;
2036
+ output += rawValue.cooked;
1693
2037
  if (index < expressions.length) {
1694
- const raw = yield* self.evaluateExpression(expressions[index]);
1695
- output += coerceToString(toProgram(raw, "Template interpolation"));
2038
+ const raw = yield* self.evaluateExpression(asNode(expressions[index], "expressions"));
2039
+ output += coerceToString(boundedData(raw, "Template interpolation"));
1696
2040
  }
1697
2041
  }
1698
2042
  return output;
1699
2043
  });
1700
2044
  }
1701
2045
  evaluateConditionalExpression(node) {
1702
- return Effect.flatMap(this.evaluateExpression(node.test), (test) => this.evaluateExpression(test ? node.consequent : node.alternate));
2046
+ return Effect.flatMap(this.evaluateExpression(getNode(node, "test")), (test) => this.evaluateExpression(getNode(node, test ? "consequent" : "alternate")));
1703
2047
  }
1704
2048
  applyCompoundAssignment(operator, current, incoming, node) {
1705
2049
  if (!compoundOperators.has(operator)) {
@@ -1708,23 +2052,21 @@ class Frame {
1708
2052
  return this.applyBinaryOperator(operator.slice(0, -1), current, incoming, node);
1709
2053
  }
1710
2054
  getMemberReference(node, operation = "read") {
1711
- const objectNode = node.object;
1712
- const propertyNode = node.property;
1713
- if (objectNode.type === "Super")
1714
- throw unsupportedSyntax(objectNode.type, objectNode);
1715
- if (propertyNode.type === "PrivateIdentifier")
1716
- throw unsupportedSyntax(propertyNode.type, propertyNode);
2055
+ const objectNode = getNode(node, "object");
2056
+ const propertyNode = getNode(node, "property");
2057
+ const computed = getBoolean(node, "computed");
2058
+ const optional = node.optional === true;
1717
2059
  const self = this;
1718
2060
  return Effect.gen(function* () {
1719
2061
  const objectValue = yield* self.evaluateExpression(objectNode);
1720
2062
  if (objectValue === OptionalShortCircuit)
1721
2063
  return OptionalShortCircuit;
1722
- if ((objectValue === null || objectValue === undefined) && node.optional)
2064
+ if ((objectValue === null || objectValue === undefined) && optional)
1723
2065
  return OptionalShortCircuit;
1724
- const key = node.computed
2066
+ const key = computed
1725
2067
  ? self.toPropertyKey(yield* self.evaluateExpression(propertyNode), propertyNode)
1726
2068
  : propertyNode.type === "Identifier"
1727
- ? propertyNode.name
2069
+ ? getString(propertyNode, "name")
1728
2070
  : self.toPropertyKey(yield* self.evaluateExpression(propertyNode), propertyNode);
1729
2071
  if (objectValue instanceof ToolReference) {
1730
2072
  if (typeof key !== "string") {
@@ -1732,17 +2074,38 @@ class Frame {
1732
2074
  }
1733
2075
  return new ToolReference([...objectValue.path, key]);
1734
2076
  }
1735
- if (objectValue instanceof HostFunction || objectValue instanceof HostNamespace) {
1736
- // Unknown static members read as undefined so feature detection works like native JS.
1737
- return new ComputedValue(objectValue.member(key, propertyNode));
2077
+ if (objectValue instanceof PromiseNamespace) {
2078
+ if (typeof key === "string" && promiseStatics.has(key)) {
2079
+ return new PromiseMethodReference(key);
2080
+ }
2081
+ throw new InterpreterRuntimeError(`Promise.${String(key)} is not available. Available: Promise.all, Promise.allSettled, Promise.race, Promise.any, Promise.resolve, and Promise.reject; consume promises with await.`, propertyNode);
1738
2082
  }
1739
- // Values have no prototype chain, so `.constructor` resolves to the owning built-in directly.
1740
- if (operation === "read" &&
1741
- key === "constructor" &&
1742
- !(objectValue instanceof ProgramObject && has(objectValue, key))) {
1743
- const name = constructorName(objectValue);
1744
- if (name !== undefined)
1745
- return new ComputedValue(self.runtime.builtins.get(name));
2083
+ if (objectValue instanceof SymbolNamespace) {
2084
+ if (key === "asyncIterator")
2085
+ return new ComputedValue(AsyncIteratorSymbol);
2086
+ if (key === "iterator")
2087
+ return new ComputedValue(IteratorSymbol);
2088
+ return new ComputedValue(undefined);
2089
+ }
2090
+ if (objectValue instanceof GlobalNamespace) {
2091
+ if (typeof key === "string" && isBlockedMember(key)) {
2092
+ throw new InterpreterRuntimeError(`${objectValue.name}.${key} is not available.`, propertyNode);
2093
+ }
2094
+ if (typeof key !== "string")
2095
+ return new ComputedValue(undefined);
2096
+ if (objectValue.name === "Math" && mathConstants.has(key)) {
2097
+ return new ComputedValue(Math[key]);
2098
+ }
2099
+ if (objectValue.name === "JSON") {
2100
+ if (jsonStatics.has(key))
2101
+ return new JsonMethodReference(key);
2102
+ return new ComputedValue(undefined);
2103
+ }
2104
+ if (globalStaticMembers[objectValue.name]?.has(key)) {
2105
+ return new GlobalMethodReference(objectValue.name, key);
2106
+ }
2107
+ // Unknown static members read as undefined so feature detection works like native JS.
2108
+ return new ComputedValue(undefined);
1746
2109
  }
1747
2110
  if (typeof objectValue === "string") {
1748
2111
  if (key === "length")
@@ -1759,12 +2122,29 @@ class Frame {
1759
2122
  return new IntrinsicReference(objectValue, key);
1760
2123
  return new ComputedValue(undefined);
1761
2124
  }
1762
- if (objectValue instanceof Values.Date) {
2125
+ if (objectValue instanceof CoercionFunction) {
2126
+ if (typeof key === "string" && isBlockedMember(key)) {
2127
+ throw new InterpreterRuntimeError(`${objectValue.name}.${key} is not available.`, propertyNode);
2128
+ }
2129
+ if (typeof key !== "string")
2130
+ return new ComputedValue(undefined);
2131
+ if (objectValue.name === "Number" && numberConstants.has(key)) {
2132
+ return new ComputedValue(Number[key]);
2133
+ }
2134
+ if (objectValue.name === "Number" && numberStatics.has(key)) {
2135
+ return new GlobalMethodReference("Number", key);
2136
+ }
2137
+ if (objectValue.name === "String" && stringStatics.has(key)) {
2138
+ return new GlobalMethodReference("String", key);
2139
+ }
2140
+ return new ComputedValue(undefined);
2141
+ }
2142
+ if (objectValue instanceof CodeModeDate) {
1763
2143
  if (typeof key === "string" && dateMethods.has(key))
1764
2144
  return new IntrinsicReference(objectValue, key);
1765
2145
  return new ComputedValue(undefined);
1766
2146
  }
1767
- if (objectValue instanceof Values.RegExp) {
2147
+ if (objectValue instanceof CodeModeRegExp) {
1768
2148
  if (key === "lastIndex")
1769
2149
  return { target: objectValue, key };
1770
2150
  if (typeof key === "string" && regexpProperties.has(key)) {
@@ -1774,21 +2154,21 @@ class Frame {
1774
2154
  return new IntrinsicReference(objectValue, key);
1775
2155
  return new ComputedValue(undefined);
1776
2156
  }
1777
- if (objectValue instanceof Values.Map) {
2157
+ if (objectValue instanceof CodeModeMap) {
1778
2158
  if (key === "size")
1779
2159
  return new ComputedValue(objectValue.map.size);
1780
2160
  if (typeof key === "string" && mapMethods.has(key))
1781
2161
  return new IntrinsicReference(objectValue, key);
1782
2162
  return new ComputedValue(undefined);
1783
2163
  }
1784
- if (objectValue instanceof Values.Set) {
2164
+ if (objectValue instanceof CodeModeSet) {
1785
2165
  if (key === "size")
1786
2166
  return new ComputedValue(objectValue.set.size);
1787
2167
  if (typeof key === "string" && setMethods.has(key))
1788
2168
  return new IntrinsicReference(objectValue, key);
1789
2169
  return new ComputedValue(undefined);
1790
2170
  }
1791
- if (objectValue instanceof Values.URL) {
2171
+ if (objectValue instanceof CodeModeURL) {
1792
2172
  if (key === "searchParams") {
1793
2173
  return new ComputedValue(objectValue.searchParams);
1794
2174
  }
@@ -1798,7 +2178,7 @@ class Frame {
1798
2178
  return { target: objectValue, key };
1799
2179
  return new ComputedValue(undefined);
1800
2180
  }
1801
- if (objectValue instanceof Values.URLSearchParams) {
2181
+ if (objectValue instanceof CodeModeURLSearchParams) {
1802
2182
  if (key === "size")
1803
2183
  return new ComputedValue(objectValue.params.size);
1804
2184
  if (typeof key === "string" && urlSearchParamsMethods.has(key)) {
@@ -1807,7 +2187,7 @@ class Frame {
1807
2187
  return new ComputedValue(undefined);
1808
2188
  }
1809
2189
  // Reject unknown promise properties so a missing await cannot hide.
1810
- if (objectValue instanceof Values.Promise) {
2190
+ if (objectValue instanceof CodeModePromise) {
1811
2191
  if (key === "then" || key === "catch" || key === "finally") {
1812
2192
  return new PromiseInstanceMethodReference(objectValue, key);
1813
2193
  }
@@ -1823,16 +2203,31 @@ class Frame {
1823
2203
  }
1824
2204
  return new ComputedValue(undefined);
1825
2205
  }
1826
- if (objectValue instanceof ProgramObject)
1827
- return { target: objectValue, key };
1828
2206
  if (isRuntimeReference(objectValue)) {
1829
- throw new InterpreterRuntimeError(`Cannot read properties of ${describeValue(objectValue)}; only data values expose properties.`, objectNode, "InvalidDataValue");
2207
+ throw new InterpreterRuntimeError("Runtime references are opaque and do not expose properties.", objectNode, "InvalidDataValue");
2208
+ }
2209
+ if (typeof objectValue !== "object" || objectValue === null) {
2210
+ throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode);
2211
+ }
2212
+ if (typeof key === "string" && isBlockedMember(key)) {
2213
+ throw new InterpreterRuntimeError(`Property '${key}' is not available.`, propertyNode);
1830
2214
  }
1831
- throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode);
2215
+ if (Array.isArray(objectValue)) {
2216
+ if (operation === "delete")
2217
+ return { target: objectValue, key };
2218
+ const index = typeof key === "symbol" ? undefined : parseArrayIndex(key);
2219
+ if (key !== "length" && !(typeof key === "string" && arrayMethods.has(key)) && index === undefined) {
2220
+ if (typeof key === "string" && Object.hasOwn(objectValue, key)) {
2221
+ return new ComputedValue(objectValue[key]);
2222
+ }
2223
+ return new ComputedValue(undefined);
2224
+ }
2225
+ return { target: objectValue, key: index ?? key };
2226
+ }
2227
+ return { target: objectValue, key };
1832
2228
  });
1833
2229
  }
1834
2230
  readMember(node) {
1835
- const self = this;
1836
2231
  return Effect.map(this.getMemberReference(node), (reference) => {
1837
2232
  if (reference === OptionalShortCircuit)
1838
2233
  return OptionalShortCircuit;
@@ -1840,22 +2235,26 @@ class Frame {
1840
2235
  return reference.value;
1841
2236
  if (reference === undefined || isOpaqueMemberReference(reference))
1842
2237
  return reference;
1843
- const value = self.readReferenceValue(reference, reference.key);
1844
- if (value === undefined &&
1845
- reference.target instanceof ProgramArray &&
1846
- typeof reference.key === "string" &&
1847
- arrayMethods.has(reference.key) &&
1848
- !has(reference.target, reference.key)) {
1849
- return new IntrinsicReference(reference.target, reference.key);
1850
- }
1851
- return value;
2238
+ if (Array.isArray(reference.target)) {
2239
+ if (reference.key === "length")
2240
+ return reference.target.length;
2241
+ if (typeof reference.key === "string")
2242
+ return new IntrinsicReference(reference.target, reference.key);
2243
+ return Reflect.get(reference.target, reference.key);
2244
+ }
2245
+ if (reference.target instanceof CodeModeRegExp)
2246
+ return reference.target.lastIndex;
2247
+ if (reference.target instanceof CodeModeURL) {
2248
+ return Reflect.get(reference.target.url, reference.key);
2249
+ }
2250
+ return Reflect.get(reference.target, reference.key);
1852
2251
  });
1853
2252
  }
1854
2253
  writeMember(node, value) {
1855
2254
  return this.modifyMember(node, () => Effect.succeed({ write: true, next: value, result: value }));
1856
2255
  }
1857
2256
  evaluateDeleteExpression(argument) {
1858
- const target = argument.type === "ChainExpression" ? argument.expression : argument;
2257
+ const target = argument.type === "ChainExpression" ? getNode(argument, "expression") : argument;
1859
2258
  if (target.type !== "MemberExpression") {
1860
2259
  throw new InterpreterRuntimeError("Only data fields may be deleted.", argument);
1861
2260
  }
@@ -1865,26 +2264,33 @@ class Frame {
1865
2264
  if (reference instanceof ComputedValue ||
1866
2265
  reference === undefined ||
1867
2266
  isOpaqueMemberReference(reference) ||
1868
- reference.target instanceof Values.URL) {
2267
+ reference.target instanceof CodeModeURL) {
1869
2268
  throw new InterpreterRuntimeError("Only data fields may be deleted.", target, "InvalidDataValue");
1870
2269
  }
1871
- if (reference.target instanceof Values.RegExp) {
2270
+ if (reference.target instanceof CodeModeRegExp) {
1872
2271
  return Reflect.deleteProperty(reference.target.regex, reference.key);
1873
2272
  }
1874
- return remove(reference.target, reference.key);
2273
+ return Reflect.deleteProperty(reference.target, reference.key);
1875
2274
  });
1876
2275
  }
1877
2276
  // Resolve side-effecting object and key expressions exactly once.
1878
2277
  modifyMember(node, compute) {
1879
2278
  const self = this;
1880
2279
  return Effect.gen(function* () {
1881
- const reference = yield* self.getMemberReference(node, "write");
2280
+ const reference = yield* self.getMemberReference(node);
1882
2281
  if (reference === OptionalShortCircuit ||
1883
2282
  reference instanceof ComputedValue ||
1884
2283
  reference === undefined ||
1885
2284
  isOpaqueMemberReference(reference)) {
1886
2285
  throw new InterpreterRuntimeError("Only data fields may be assigned.", node);
1887
2286
  }
2287
+ if (Array.isArray(reference.target)) {
2288
+ if (reference.key === "length")
2289
+ throw new InterpreterRuntimeError("Array length cannot be assigned.", node);
2290
+ if (typeof reference.key === "string" && arrayMethods.has(reference.key)) {
2291
+ throw new InterpreterRuntimeError("Array methods cannot be assigned.", node);
2292
+ }
2293
+ }
1888
2294
  const key = reference.key;
1889
2295
  const { write, next, result } = yield* compute(self.readReferenceValue(reference, key));
1890
2296
  if (write)
@@ -1893,15 +2299,24 @@ class Frame {
1893
2299
  });
1894
2300
  }
1895
2301
  readReferenceValue(reference, key) {
1896
- if (reference.target instanceof Values.URL) {
2302
+ if (reference.target instanceof CodeModeURL) {
1897
2303
  return Reflect.get(reference.target.url, key);
1898
2304
  }
1899
- if (reference.target instanceof Values.RegExp)
2305
+ if (reference.target instanceof CodeModeRegExp)
1900
2306
  return reference.target.lastIndex;
1901
- return get(reference.target, key);
2307
+ return Reflect.get(reference.target, key);
1902
2308
  }
1903
2309
  assignToReference(reference, key, next, node) {
1904
- if (reference.target instanceof Values.URL) {
2310
+ if (Array.isArray(reference.target)) {
2311
+ const target = reference.target;
2312
+ if (typeof key !== "number" || parseArrayIndex(key) === undefined) {
2313
+ throw new InterpreterRuntimeError("Array assignment index must be a valid array index.", node, "InvalidDataValue");
2314
+ }
2315
+ rejectCircularInsertion(target, next, "Array assignment result", node);
2316
+ target[key] = next;
2317
+ return;
2318
+ }
2319
+ if (reference.target instanceof CodeModeURL) {
1905
2320
  const property = key;
1906
2321
  if (!urlWritableProperties.has(property)) {
1907
2322
  throw new InterpreterRuntimeError(`URL.${property} is read-only.`, node).as("TypeError");
@@ -1917,17 +2332,13 @@ class Frame {
1917
2332
  throw new InterpreterRuntimeError(`URL.${property} received an invalid value.`, node).as("TypeError");
1918
2333
  }
1919
2334
  }
1920
- if (reference.target instanceof Values.RegExp) {
2335
+ if (reference.target instanceof CodeModeRegExp) {
1921
2336
  reference.target.lastIndex = next;
1922
2337
  return;
1923
2338
  }
1924
2339
  const target = reference.target;
1925
- rejectCircularInsertion(target, next, target instanceof ProgramArray ? "Array assignment result" : "Object assignment result", node);
1926
- if (set(target, key, next))
1927
- return;
1928
- if (target instanceof ProgramArray)
1929
- throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError");
1930
- throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node).as("TypeError");
2340
+ rejectCircularInsertion(target, next, "Object assignment result", node);
2341
+ Reflect.set(target, key, next);
1931
2342
  }
1932
2343
  toPropertyKey(value, node) {
1933
2344
  if (typeof value === "string" || typeof value === "number") {