@opencode/codemode 0.0.0-beta-19507 → 0.0.0-dev-19276
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.
- package/README.md +0 -6
- package/dist/codemode.d.ts +12 -9
- package/dist/codemode.js +9 -5
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/interpreter/errors.d.ts +6 -4
- package/dist/interpreter/errors.js +10 -25
- package/dist/interpreter/execute.d.ts +3 -4
- package/dist/interpreter/execute.js +18 -11
- package/dist/interpreter/iterator.d.ts +13 -0
- package/dist/interpreter/iterator.js +4 -0
- package/dist/interpreter/methods.d.ts +16 -3
- package/dist/interpreter/methods.js +290 -101
- package/dist/interpreter/model.d.ts +79 -10
- package/dist/interpreter/model.js +102 -2
- package/dist/interpreter/promises.d.ts +13 -15
- package/dist/interpreter/promises.js +52 -70
- package/dist/interpreter/references.d.ts +0 -1
- package/dist/interpreter/references.js +77 -57
- package/dist/interpreter/runtime.d.ts +95 -16
- package/dist/interpreter/runtime.js +960 -549
- package/dist/openapi/spec.js +6 -3
- package/dist/stdlib/collections.d.ts +1 -6
- package/dist/stdlib/collections.js +1 -117
- package/dist/stdlib/console.d.ts +2 -3
- package/dist/stdlib/console.js +28 -39
- package/dist/stdlib/date.d.ts +4 -5
- package/dist/stdlib/date.js +12 -34
- package/dist/stdlib/json.d.ts +6 -3
- package/dist/stdlib/json.js +63 -40
- package/dist/stdlib/math.d.ts +7 -3
- package/dist/stdlib/math.js +153 -85
- package/dist/stdlib/number.d.ts +4 -2
- package/dist/stdlib/number.js +37 -30
- package/dist/stdlib/object.d.ts +6 -6
- package/dist/stdlib/object.js +87 -84
- package/dist/stdlib/promise.d.ts +2 -0
- package/dist/stdlib/promise.js +1 -0
- package/dist/stdlib/regexp.d.ts +7 -6
- package/dist/stdlib/regexp.js +34 -48
- package/dist/stdlib/string.d.ts +3 -1
- package/dist/stdlib/string.js +17 -20
- package/dist/stdlib/url.d.ts +6 -10
- package/dist/stdlib/url.js +25 -102
- package/dist/stdlib/value.d.ts +7 -8
- package/dist/stdlib/value.js +56 -56
- package/dist/tool-runtime.d.ts +15 -16
- package/dist/tool-runtime.js +150 -13
- package/dist/values.d.ts +16 -22
- package/dist/values.js +17 -23
- package/package.json +1 -1
- package/dist/data.d.ts +0 -25
- package/dist/data.js +0 -153
- package/dist/interpreter/globals.d.ts +0 -13
- package/dist/interpreter/globals.js +0 -63
- package/dist/interpreter/host.d.ts +0 -41
- package/dist/interpreter/host.js +0 -44
- package/dist/interpreter/objects.d.ts +0 -37
- package/dist/interpreter/objects.js +0 -151
- package/dist/interpreter/runner.d.ts +0 -24
- package/dist/interpreter/runner.js +0 -45
- package/dist/stdlib/array.d.ts +0 -3
- package/dist/stdlib/array.js +0 -68
- package/dist/stdlib/web.d.ts +0 -4
- package/dist/stdlib/web.js +0 -20
|
@@ -1,223 +1,200 @@
|
|
|
1
1
|
import { Cause, Deferred, Effect, Exit } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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 {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
39
|
-
return callee
|
|
40
|
-
if (callee
|
|
41
|
-
const object = callee
|
|
42
|
-
const property = callee
|
|
43
|
-
const key =
|
|
44
|
-
? property
|
|
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
|
|
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
|
|
82
|
-
|
|
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
|
|
92
|
+
out.push(getString(pattern, "name"));
|
|
89
93
|
break;
|
|
90
94
|
case "AssignmentPattern":
|
|
91
|
-
collectPatternNames(pattern
|
|
95
|
+
collectPatternNames(getNode(pattern, "left"), out);
|
|
92
96
|
break;
|
|
93
97
|
case "RestElement":
|
|
94
|
-
collectPatternNames(pattern
|
|
98
|
+
collectPatternNames(getNode(pattern, "argument"), out);
|
|
95
99
|
break;
|
|
96
100
|
case "ArrayPattern":
|
|
97
|
-
for (const element of pattern
|
|
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
|
|
104
|
-
|
|
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
|
|
163
|
-
if (
|
|
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
|
|
122
|
+
const kind = getString(left, "kind");
|
|
167
123
|
return {
|
|
168
|
-
pattern:
|
|
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
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
146
|
+
invokeSearch;
|
|
182
147
|
toolKeys;
|
|
183
|
-
promises;
|
|
184
148
|
logs;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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.
|
|
162
|
+
this.invokeSearch = invokeSearch;
|
|
192
163
|
this.toolKeys = toolKeys;
|
|
193
|
-
this.promises = promises;
|
|
194
164
|
this.logs = logs;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
|
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.
|
|
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.
|
|
228
|
+
return this.createPromise(Effect.suspend(() => this.executeTool(path, args)));
|
|
253
229
|
}
|
|
254
230
|
createPromise(effect) {
|
|
255
|
-
return this.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
291
|
+
const body = getArray(node, "body");
|
|
316
292
|
self.predeclareLexical(body);
|
|
317
293
|
self.hoistFunctions(body);
|
|
318
|
-
for (const
|
|
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
|
|
328
|
-
return new
|
|
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
|
|
339
|
-
if (
|
|
308
|
+
for (const statementValue of statements) {
|
|
309
|
+
if (!isRecord(statementValue) || statementValue.type !== "FunctionDeclaration")
|
|
340
310
|
continue;
|
|
341
|
-
|
|
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
|
|
359
|
-
if (
|
|
316
|
+
for (const statementValue of statements) {
|
|
317
|
+
if (!isRecord(statementValue) || statementValue.type !== "VariableDeclaration")
|
|
360
318
|
continue;
|
|
361
|
-
const
|
|
319
|
+
const statement = statementValue;
|
|
320
|
+
const kind = getString(statement, "kind");
|
|
362
321
|
if (kind === "var")
|
|
363
322
|
continue;
|
|
364
|
-
for (const
|
|
365
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
417
|
-
const result = yield* self.evaluateStatement(
|
|
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(
|
|
435
|
-
const
|
|
436
|
-
if (
|
|
437
|
-
|
|
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
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
|
|
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
|
|
458
|
-
const testNode = node
|
|
459
|
-
const updateNode = node
|
|
460
|
-
|
|
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
|
|
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
|
|
484
|
-
if (
|
|
485
|
-
return
|
|
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
|
|
496
|
-
const left = node
|
|
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
|
|
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
|
-
|
|
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
|
|
527
|
+
if (declared) {
|
|
521
528
|
self.scopes.push();
|
|
522
|
-
|
|
523
|
-
|
|
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(
|
|
536
|
+
return yield* self.evaluateStatement(body);
|
|
532
537
|
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
533
|
-
if (declared
|
|
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
|
|
552
|
-
if (
|
|
556
|
+
const result = bodyExit.value;
|
|
557
|
+
if (result.kind === "return") {
|
|
553
558
|
yield* close();
|
|
554
|
-
return
|
|
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.
|
|
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
|
|
579
|
-
? value
|
|
593
|
+
const iterator = Array.isArray(value)
|
|
594
|
+
? value[Symbol.iterator]()
|
|
580
595
|
: typeof value === "string"
|
|
581
596
|
? value[Symbol.iterator]()
|
|
582
|
-
: value instanceof
|
|
597
|
+
: value instanceof CodeModeMap
|
|
583
598
|
? value.map.entries()
|
|
584
|
-
: value instanceof
|
|
599
|
+
: value instanceof CodeModeSet
|
|
585
600
|
? value.set.values()
|
|
586
|
-
: value instanceof
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
:
|
|
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(() =>
|
|
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
|
|
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
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (value
|
|
716
|
-
return
|
|
717
|
-
|
|
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
|
|
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
|
|
729
|
-
const
|
|
730
|
-
|
|
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
|
|
759
|
+
if (declared) {
|
|
737
760
|
self.scopes.push();
|
|
738
|
-
|
|
739
|
-
|
|
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(
|
|
768
|
+
return yield* self.evaluateStatement(body);
|
|
748
769
|
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
749
|
-
if (declared
|
|
770
|
+
if (declared)
|
|
750
771
|
self.scopes.pop();
|
|
751
772
|
})));
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
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
|
-
|
|
794
|
+
const labelNode = getOptionalNode(node, "label");
|
|
795
|
+
return labelNode ? { kind: "break", label: getString(labelNode, "name") } : { kind: "break" };
|
|
764
796
|
}
|
|
765
797
|
evaluateContinueStatement(node) {
|
|
766
|
-
|
|
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
|
|
773
|
-
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
|
-
|
|
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
|
|
797
|
-
const handler = node
|
|
798
|
-
const finalizer = node
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
835
|
-
|
|
836
|
-
|
|
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
|
|
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.
|
|
861
|
-
yield* self.declarePattern(pattern
|
|
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 (
|
|
866
|
-
throw new InterpreterRuntimeError(
|
|
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
|
|
897
|
+
for (const propertyValue of getArray(pattern, "properties")) {
|
|
898
|
+
const property = asNode(propertyValue, "properties");
|
|
870
899
|
if (property.type === "RestElement") {
|
|
871
|
-
const rest =
|
|
872
|
-
|
|
873
|
-
|
|
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
|
|
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
|
|
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.
|
|
901
|
-
yield* self.assignPattern(pattern
|
|
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 (
|
|
906
|
-
throw new InterpreterRuntimeError(
|
|
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
|
|
946
|
+
for (const propertyValue of getArray(pattern, "properties")) {
|
|
947
|
+
const property = asNode(propertyValue, "properties");
|
|
910
948
|
if (property.type === "RestElement") {
|
|
911
|
-
const rest =
|
|
912
|
-
|
|
913
|
-
|
|
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
|
|
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
|
|
981
|
+
for (const [index, item] of getArray(pattern, "elements").entries()) {
|
|
942
982
|
if (done) {
|
|
943
|
-
if (
|
|
983
|
+
if (item === null)
|
|
944
984
|
continue;
|
|
945
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
979
|
-
if (property
|
|
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
|
-
|
|
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
|
|
990
|
-
return get(source, key);
|
|
991
|
-
if (
|
|
992
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
|
1162
|
-
return Effect.flatMap(this.evaluateExpression(node
|
|
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
|
|
1406
|
+
return left ? this.evaluateExpression(getNode(node, "right")) : Effect.succeed(left);
|
|
1165
1407
|
if (operator === "||")
|
|
1166
|
-
return left ? Effect.succeed(left) : this.evaluateExpression(node
|
|
1408
|
+
return left ? Effect.succeed(left) : this.evaluateExpression(getNode(node, "right"));
|
|
1167
1409
|
if (operator === "??")
|
|
1168
|
-
return left !== null && left !== undefined
|
|
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
|
|
1174
|
-
const argument = node
|
|
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
|
|
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
|
|
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
|
|
1454
|
+
return boundedData(result, "Unary expression result");
|
|
1211
1455
|
});
|
|
1212
1456
|
}
|
|
1213
1457
|
evaluateAssignmentExpression(node) {
|
|
1214
|
-
const left = node
|
|
1215
|
-
const operator = node
|
|
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
|
|
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
|
|
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
|
|
1231
|
-
const next =
|
|
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.
|
|
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
|
|
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 =
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
1274
|
-
const argument = node
|
|
1275
|
-
const prefix = node
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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.
|
|
1589
|
+
return yield* invokeIntrinsic(self.runner, callable, args, node);
|
|
1342
1590
|
}
|
|
1343
|
-
if (callable instanceof
|
|
1344
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
1871
|
+
if (Array.isArray(value) ||
|
|
1543
1872
|
typeof value === "string" ||
|
|
1544
|
-
value instanceof
|
|
1545
|
-
value instanceof
|
|
1546
|
-
value instanceof
|
|
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
|
-
:
|
|
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(
|
|
1917
|
+
const done = Boolean(result.done);
|
|
1589
1918
|
const resultValue = self.generatorAsync && !iterator.asynchronous
|
|
1590
|
-
? yield* self.awaitAsyncFromSyncValue(iterator,
|
|
1591
|
-
:
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
1969
|
+
const keyNode = getNode(property, "key");
|
|
1970
|
+
const valueNode = getNode(property, "value");
|
|
1971
|
+
const computed = getBoolean(property, "computed");
|
|
1628
1972
|
let key;
|
|
1629
|
-
if (
|
|
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
|
|
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
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
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
|
|
1656
|
-
if (
|
|
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
|
|
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
|
|
2021
|
+
return values;
|
|
1678
2022
|
});
|
|
1679
2023
|
}
|
|
1680
2024
|
evaluateTemplateLiteral(node) {
|
|
1681
|
-
const quasis = node
|
|
1682
|
-
const expressions = node
|
|
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
|
-
|
|
1689
|
-
if (typeof
|
|
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 +=
|
|
2036
|
+
output += rawValue.cooked;
|
|
1693
2037
|
if (index < expressions.length) {
|
|
1694
|
-
const raw = yield* self.evaluateExpression(expressions[index]);
|
|
1695
|
-
output += coerceToString(
|
|
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
|
|
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
|
|
1712
|
-
const propertyNode = node
|
|
1713
|
-
|
|
1714
|
-
|
|
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) &&
|
|
2064
|
+
if ((objectValue === null || objectValue === undefined) && optional)
|
|
1723
2065
|
return OptionalShortCircuit;
|
|
1724
|
-
const key =
|
|
2066
|
+
const key = computed
|
|
1725
2067
|
? self.toPropertyKey(yield* self.evaluateExpression(propertyNode), propertyNode)
|
|
1726
2068
|
: propertyNode.type === "Identifier"
|
|
1727
|
-
? propertyNode
|
|
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
|
|
1736
|
-
|
|
1737
|
-
|
|
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
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
typeof reference.key === "string"
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
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
|
|
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
|
|
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
|
|
2270
|
+
if (reference.target instanceof CodeModeRegExp) {
|
|
1872
2271
|
return Reflect.deleteProperty(reference.target.regex, reference.key);
|
|
1873
2272
|
}
|
|
1874
|
-
return
|
|
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
|
|
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
|
|
2302
|
+
if (reference.target instanceof CodeModeURL) {
|
|
1897
2303
|
return Reflect.get(reference.target.url, key);
|
|
1898
2304
|
}
|
|
1899
|
-
if (reference.target instanceof
|
|
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
|
|
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
|
|
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,
|
|
1926
|
-
|
|
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") {
|