@opencode/codemode 0.0.0-beta-19422 → 0.0.0-beta-19500
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/dist/codemode.d.ts +1 -1
- package/dist/codemode.js +1 -1
- package/dist/data.d.ts +4 -6
- package/dist/data.js +44 -18
- package/dist/interpreter/errors.js +3 -4
- package/dist/interpreter/execute.d.ts +2 -1
- package/dist/interpreter/execute.js +2 -2
- package/dist/interpreter/globals.js +4 -0
- package/dist/interpreter/methods.js +64 -55
- package/dist/interpreter/model.d.ts +3 -11
- package/dist/interpreter/model.js +0 -14
- package/dist/interpreter/objects.d.ts +37 -0
- package/dist/interpreter/objects.js +151 -0
- package/dist/interpreter/promises.d.ts +3 -0
- package/dist/interpreter/promises.js +22 -27
- package/dist/interpreter/references.js +9 -11
- package/dist/interpreter/runner.d.ts +4 -3
- package/dist/interpreter/runner.js +9 -6
- package/dist/interpreter/runtime.d.ts +4 -1
- package/dist/interpreter/runtime.js +269 -239
- package/dist/stdlib/array.js +12 -14
- package/dist/stdlib/collections.js +10 -9
- package/dist/stdlib/console.js +16 -14
- package/dist/stdlib/date.js +6 -0
- package/dist/stdlib/json.js +21 -36
- package/dist/stdlib/object.d.ts +2 -0
- package/dist/stdlib/object.js +38 -62
- package/dist/stdlib/regexp.d.ts +2 -1
- package/dist/stdlib/regexp.js +14 -20
- package/dist/stdlib/string.js +5 -0
- package/dist/stdlib/url.js +3 -3
- package/dist/stdlib/value.d.ts +3 -3
- package/dist/stdlib/value.js +25 -26
- package/dist/stdlib/web.d.ts +4 -0
- package/dist/stdlib/web.js +20 -0
- package/dist/tool-runtime.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Cause, Deferred, Effect, Exit } from "effect";
|
|
2
2
|
import { ToolRuntimeError, toProgram } from "../data.js";
|
|
3
3
|
import { ToolReference } from "../tool-runtime.js";
|
|
4
|
-
import { AsyncIteratorSymbol,
|
|
4
|
+
import { AsyncIteratorSymbol, CodeModeGenerator, ComputedValue, GeneratorMethodReference, GeneratorReturn, IntrinsicReference, InterpreterRuntimeError, IteratorSymbol, OptionalShortCircuit, PromiseInstanceMethodReference, ProgramThrow, unsupportedSyntax, } from "./model.js";
|
|
5
5
|
import { caughtErrorValue } from "./errors.js";
|
|
6
6
|
import { globals } from "./globals.js";
|
|
7
7
|
import { HostFunction, HostNamespace } from "./host.js";
|
|
8
8
|
import { invokeIntrinsic } from "./methods.js";
|
|
9
|
+
import { assign, get, has, ownKeys, parseArrayIndex, ProgramArray, ProgramFunction, ProgramObject, record, remove, set, } from "./objects.js";
|
|
9
10
|
import { preserveConsumerError } from "./runner.js";
|
|
10
11
|
import { invokePromiseInstanceMethod, PromiseRuntime, resolvePromise, resolvePromiseValue } from "./promises.js";
|
|
11
12
|
import { containsOpaqueReference, describeValue, isRuntimeReference, rejectCircularInsertion, typeofValue, } from "./references.js";
|
|
@@ -16,15 +17,22 @@ import { numberMethods } from "../stdlib/number.js";
|
|
|
16
17
|
import { constructRegExp, regexpMethods, regexpProperties } from "../stdlib/regexp.js";
|
|
17
18
|
import { stringMethods } from "../stdlib/string.js";
|
|
18
19
|
import { uriArgument, urlMethods, urlProperties, urlSearchParamsMethods, urlWritableProperties } from "../stdlib/url.js";
|
|
19
|
-
import {
|
|
20
|
+
import { enumerableSource } from "../stdlib/object.js";
|
|
21
|
+
import { coerceToNumber, coerceToString, compoundOperators, errorBrandName } from "../stdlib/value.js";
|
|
20
22
|
import { Values } from "../values.js";
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
// What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating.
|
|
24
|
+
// Unlabelled break ends this loop; a label the loop does not carry propagates outward.
|
|
25
|
+
const loopExit = (result, labels) => {
|
|
26
|
+
if (result.kind === "return")
|
|
27
|
+
return result;
|
|
28
|
+
if (result.kind === "break") {
|
|
29
|
+
if (result.label !== undefined && !labels?.has(result.label))
|
|
30
|
+
return result;
|
|
31
|
+
return { kind: "none" };
|
|
32
|
+
}
|
|
33
|
+
if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label))
|
|
34
|
+
return result;
|
|
35
|
+
return undefined;
|
|
28
36
|
};
|
|
29
37
|
const calleeDescription = (callee) => {
|
|
30
38
|
if (callee?.type === "Identifier")
|
|
@@ -42,6 +50,33 @@ const calleeDescription = (callee) => {
|
|
|
42
50
|
}
|
|
43
51
|
return "The called value";
|
|
44
52
|
};
|
|
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
|
+
};
|
|
45
80
|
const instanceofValue = (lhs, rhs, node) => {
|
|
46
81
|
if (rhs instanceof HostFunction && rhs.instanceOf !== undefined)
|
|
47
82
|
return rhs.instanceOf(lhs);
|
|
@@ -72,6 +107,55 @@ const collectPatternNames = (pattern, out = []) => {
|
|
|
72
107
|
}
|
|
73
108
|
return out;
|
|
74
109
|
};
|
|
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
|
+
};
|
|
75
159
|
const loopDeclaration = (left, statement) => {
|
|
76
160
|
if (left.type !== "VariableDeclaration")
|
|
77
161
|
return undefined;
|
|
@@ -90,12 +174,6 @@ const isOpaqueMemberReference = (value) => value instanceof ToolReference ||
|
|
|
90
174
|
value instanceof PromiseInstanceMethodReference ||
|
|
91
175
|
value instanceof IntrinsicReference ||
|
|
92
176
|
value instanceof GeneratorMethodReference;
|
|
93
|
-
const copyIteratorSymbols = (source, target, consumed) => {
|
|
94
|
-
for (const symbol of IteratorSymbols) {
|
|
95
|
-
if (!consumed?.has(symbol) && Object.hasOwn(source, symbol))
|
|
96
|
-
Reflect.set(target, symbol, Reflect.get(source, symbol));
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
177
|
const promiseResolutionNode = { type: "PromiseResolution", start: 0, end: 0 };
|
|
100
178
|
/** One program execution: the tool bridge, promise scheduler, captured logs, and the global scope built once. */
|
|
101
179
|
export class Runtime {
|
|
@@ -105,8 +183,10 @@ export class Runtime {
|
|
|
105
183
|
promises;
|
|
106
184
|
logs;
|
|
107
185
|
runner;
|
|
186
|
+
/** Built-in globals by name, unaffected by program shadowing. */
|
|
187
|
+
builtins;
|
|
108
188
|
root;
|
|
109
|
-
constructor(executeTool, search, toolKeys, promises, logs = []) {
|
|
189
|
+
constructor(executeTool, search, toolKeys, promises, logs = [], extraGlobals = () => []) {
|
|
110
190
|
this.executeTool = executeTool;
|
|
111
191
|
this.search = search;
|
|
112
192
|
this.toolKeys = toolKeys;
|
|
@@ -121,7 +201,8 @@ export class Runtime {
|
|
|
121
201
|
settlePromise: (promise) => this.root.settlePromise(promise),
|
|
122
202
|
syncIterator: (value, node) => this.root.syncIterator(value, node),
|
|
123
203
|
};
|
|
124
|
-
|
|
204
|
+
this.builtins = new Map([...globals(this), ...extraGlobals(this)]);
|
|
205
|
+
for (const [name, value] of this.builtins)
|
|
125
206
|
globalScope.set(name, { mutable: false, value });
|
|
126
207
|
}
|
|
127
208
|
run(program) {
|
|
@@ -145,6 +226,7 @@ class Frame {
|
|
|
145
226
|
return Effect.gen(function* () {
|
|
146
227
|
self.predeclareLexical(program.body);
|
|
147
228
|
self.hoistFunctions(program.body);
|
|
229
|
+
self.hoistVars(program.body);
|
|
148
230
|
let value = undefined;
|
|
149
231
|
for (const [index, statement] of program.body.entries()) {
|
|
150
232
|
if (index === program.body.length - 1 && statement.type === "ExpressionStatement") {
|
|
@@ -242,8 +324,15 @@ class Frame {
|
|
|
242
324
|
return { kind: "none" };
|
|
243
325
|
}).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
|
|
244
326
|
}
|
|
245
|
-
createFunction(node) {
|
|
246
|
-
return new
|
|
327
|
+
createFunction(node, name = node.type === "ArrowFunctionExpression" ? "" : (node.id?.name ?? "")) {
|
|
328
|
+
return new ProgramFunction(name, node.params, node.body, this.scopes.capture(), node.async, node.generator);
|
|
329
|
+
}
|
|
330
|
+
// NamedEvaluation: an anonymous function definition takes the name of what it is assigned to.
|
|
331
|
+
evaluateNamed(node, name) {
|
|
332
|
+
if (node.type === "ArrowFunctionExpression" || (node.type === "FunctionExpression" && !node.id)) {
|
|
333
|
+
return Effect.sync(() => this.createFunction(node, name));
|
|
334
|
+
}
|
|
335
|
+
return this.evaluateExpression(node);
|
|
247
336
|
}
|
|
248
337
|
hoistFunctions(statements) {
|
|
249
338
|
for (const node of statements) {
|
|
@@ -252,6 +341,19 @@ class Frame {
|
|
|
252
341
|
this.scopes.declare(node.id.name, this.createFunction(node), true, node);
|
|
253
342
|
}
|
|
254
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 });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
255
357
|
predeclareLexical(statements) {
|
|
256
358
|
for (const statement of statements) {
|
|
257
359
|
if (statement.type !== "VariableDeclaration")
|
|
@@ -287,7 +389,9 @@ class Frame {
|
|
|
287
389
|
self.scopes.push();
|
|
288
390
|
return yield* Effect.gen(function* () {
|
|
289
391
|
const cases = node.cases;
|
|
290
|
-
|
|
392
|
+
const statements = cases.flatMap((branch) => branch.consequent);
|
|
393
|
+
self.predeclareLexical(statements);
|
|
394
|
+
self.hoistFunctions(statements);
|
|
291
395
|
let defaultIndex;
|
|
292
396
|
let selected;
|
|
293
397
|
for (const [index, branch] of cases.entries()) {
|
|
@@ -328,20 +432,9 @@ class Frame {
|
|
|
328
432
|
const self = this;
|
|
329
433
|
return Effect.gen(function* () {
|
|
330
434
|
while (yield* self.evaluateExpression(node.test)) {
|
|
331
|
-
const
|
|
332
|
-
if (
|
|
333
|
-
|
|
334
|
-
return result;
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
if (result.kind === "break") {
|
|
338
|
-
if (result.label !== undefined && !labels?.has(result.label))
|
|
339
|
-
return result;
|
|
340
|
-
return { kind: "none" };
|
|
341
|
-
}
|
|
342
|
-
if (result.kind === "return") {
|
|
343
|
-
return result;
|
|
344
|
-
}
|
|
435
|
+
const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
|
|
436
|
+
if (exit !== undefined)
|
|
437
|
+
return exit;
|
|
345
438
|
}
|
|
346
439
|
return { kind: "none" };
|
|
347
440
|
});
|
|
@@ -350,20 +443,9 @@ class Frame {
|
|
|
350
443
|
const self = this;
|
|
351
444
|
return Effect.gen(function* () {
|
|
352
445
|
do {
|
|
353
|
-
const
|
|
354
|
-
if (
|
|
355
|
-
|
|
356
|
-
return result;
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
if (result.kind === "break") {
|
|
360
|
-
if (result.label !== undefined && !labels?.has(result.label))
|
|
361
|
-
return result;
|
|
362
|
-
return { kind: "none" };
|
|
363
|
-
}
|
|
364
|
-
if (result.kind === "return") {
|
|
365
|
-
return result;
|
|
366
|
-
}
|
|
446
|
+
const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
|
|
447
|
+
if (exit !== undefined)
|
|
448
|
+
return exit;
|
|
367
449
|
} while (yield* self.evaluateExpression(node.test));
|
|
368
450
|
return { kind: "none" };
|
|
369
451
|
});
|
|
@@ -398,24 +480,13 @@ class Frame {
|
|
|
398
480
|
};
|
|
399
481
|
nextIteration();
|
|
400
482
|
while (testNode ? yield* self.evaluateExpression(testNode) : true) {
|
|
401
|
-
const
|
|
402
|
-
if (
|
|
403
|
-
return
|
|
404
|
-
}
|
|
405
|
-
if (result.kind === "break") {
|
|
406
|
-
if (result.label !== undefined && !labels?.has(result.label))
|
|
407
|
-
return result;
|
|
408
|
-
return { kind: "none" };
|
|
409
|
-
}
|
|
410
|
-
if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label))
|
|
411
|
-
return result;
|
|
483
|
+
const exit = loopExit(yield* self.evaluateStatement(node.body), labels);
|
|
484
|
+
if (exit !== undefined)
|
|
485
|
+
return exit;
|
|
412
486
|
nextIteration();
|
|
413
487
|
if (updateNode) {
|
|
414
488
|
yield* self.evaluateExpression(updateNode);
|
|
415
489
|
}
|
|
416
|
-
if (result.kind === "continue") {
|
|
417
|
-
continue;
|
|
418
|
-
}
|
|
419
490
|
}
|
|
420
491
|
return { kind: "none" };
|
|
421
492
|
}).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
|
|
@@ -446,18 +517,20 @@ class Frame {
|
|
|
446
517
|
}
|
|
447
518
|
const assignment = left.type === "VariableDeclaration" ? undefined : left;
|
|
448
519
|
const evaluateBody = (value) => Effect.gen(function* () {
|
|
449
|
-
if (declared) {
|
|
520
|
+
if (declared?.lexical) {
|
|
450
521
|
self.scopes.push();
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
522
|
+
self.predeclarePattern(declared.pattern, declared.mutable, left);
|
|
523
|
+
yield* self.declarePattern(declared.pattern, value, declared.mutable, left, true);
|
|
524
|
+
}
|
|
525
|
+
else if (declared) {
|
|
526
|
+
yield* self.assignPattern(declared.pattern, value, left);
|
|
454
527
|
}
|
|
455
528
|
else if (assignment) {
|
|
456
529
|
yield* self.assignPattern(assignment, value, left);
|
|
457
530
|
}
|
|
458
531
|
return yield* self.evaluateStatement(node.body);
|
|
459
532
|
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
460
|
-
if (declared)
|
|
533
|
+
if (declared?.lexical)
|
|
461
534
|
self.scopes.pop();
|
|
462
535
|
})));
|
|
463
536
|
while (true) {
|
|
@@ -475,20 +548,10 @@ class Frame {
|
|
|
475
548
|
}
|
|
476
549
|
return yield* Effect.failCause(bodyExit.cause);
|
|
477
550
|
}
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
551
|
+
const exit = loopExit(bodyExit.value, labels);
|
|
552
|
+
if (exit !== undefined) {
|
|
480
553
|
yield* close();
|
|
481
|
-
return
|
|
482
|
-
}
|
|
483
|
-
if (result.kind === "break") {
|
|
484
|
-
yield* close();
|
|
485
|
-
if (result.label !== undefined && !labels?.has(result.label))
|
|
486
|
-
return result;
|
|
487
|
-
return { kind: "none" };
|
|
488
|
-
}
|
|
489
|
-
if (result.kind === "continue" && result.label !== undefined && !labels?.has(result.label)) {
|
|
490
|
-
yield* close();
|
|
491
|
-
return result;
|
|
554
|
+
return exit;
|
|
492
555
|
}
|
|
493
556
|
}
|
|
494
557
|
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
@@ -512,8 +575,8 @@ class Frame {
|
|
|
512
575
|
});
|
|
513
576
|
}
|
|
514
577
|
syncIterator(value, node) {
|
|
515
|
-
const iterator =
|
|
516
|
-
? value[Symbol.iterator]()
|
|
578
|
+
const iterator = value instanceof ProgramArray
|
|
579
|
+
? value.items[Symbol.iterator]()
|
|
517
580
|
: typeof value === "string"
|
|
518
581
|
? value[Symbol.iterator]()
|
|
519
582
|
: value instanceof Values.Map
|
|
@@ -527,7 +590,10 @@ class Frame {
|
|
|
527
590
|
return Effect.succeed({
|
|
528
591
|
next: Effect.sync(() => {
|
|
529
592
|
const step = iterator.next();
|
|
530
|
-
return {
|
|
593
|
+
return {
|
|
594
|
+
done: Boolean(step.done),
|
|
595
|
+
value: Array.isArray(step.value) ? new ProgramArray(step.value) : step.value,
|
|
596
|
+
};
|
|
531
597
|
}),
|
|
532
598
|
close: Effect.void,
|
|
533
599
|
});
|
|
@@ -550,10 +616,10 @@ class Frame {
|
|
|
550
616
|
asynchronous: value.asynchronous,
|
|
551
617
|
});
|
|
552
618
|
}
|
|
553
|
-
if (!
|
|
619
|
+
if (!(value instanceof ProgramObject))
|
|
554
620
|
return Effect.undefined;
|
|
555
|
-
const asyncMethod = allowAsync ?
|
|
556
|
-
const method = asyncMethod ??
|
|
621
|
+
const asyncMethod = allowAsync ? get(value, AsyncIteratorSymbol) : undefined;
|
|
622
|
+
const method = asyncMethod ?? get(value, IteratorSymbol);
|
|
557
623
|
if (method === undefined || method === null)
|
|
558
624
|
return Effect.undefined;
|
|
559
625
|
const self = this;
|
|
@@ -563,7 +629,7 @@ class Frame {
|
|
|
563
629
|
iterator: object,
|
|
564
630
|
next: object instanceof CodeModeGenerator
|
|
565
631
|
? new GeneratorMethodReference(object, "next")
|
|
566
|
-
: self.requireIteratorMethod(object
|
|
632
|
+
: self.requireIteratorMethod(get(object, "next"), "Iterator next", node),
|
|
567
633
|
asynchronous: asyncMethod !== undefined && asyncMethod !== null,
|
|
568
634
|
};
|
|
569
635
|
});
|
|
@@ -573,7 +639,7 @@ class Frame {
|
|
|
573
639
|
return Effect.gen(function* () {
|
|
574
640
|
if (iterator.asynchronous) {
|
|
575
641
|
const object = self.requireIteratorObject(yield* self.awaitValue(yield* self.invokeCallable(iterator.next, [], node)), "Iterator next() result", node);
|
|
576
|
-
return { done: Boolean(object
|
|
642
|
+
return { done: Boolean(get(object, "done")), value: get(object, "value") };
|
|
577
643
|
}
|
|
578
644
|
const called = yield* Effect.exit(self.invokeCallable(iterator.next, [], node));
|
|
579
645
|
if (!Exit.isSuccess(called)) {
|
|
@@ -583,7 +649,7 @@ class Frame {
|
|
|
583
649
|
}
|
|
584
650
|
const captured = yield* Effect.exit(Effect.sync(() => {
|
|
585
651
|
const object = self.requireIteratorObject(called.value, "Iterator next() result", node);
|
|
586
|
-
return { done: Boolean(object
|
|
652
|
+
return { done: Boolean(get(object, "done")), value: get(object, "value") };
|
|
587
653
|
}));
|
|
588
654
|
if (!Exit.isSuccess(captured)) {
|
|
589
655
|
if (awaiting)
|
|
@@ -601,7 +667,7 @@ class Frame {
|
|
|
601
667
|
closeIterator(iterator, node, awaiting = true) {
|
|
602
668
|
const close = iterator.iterator instanceof CodeModeGenerator
|
|
603
669
|
? new GeneratorMethodReference(iterator.iterator, "return")
|
|
604
|
-
: iterator.iterator
|
|
670
|
+
: get(iterator.iterator, "return");
|
|
605
671
|
if (close === undefined || close === null)
|
|
606
672
|
return iterator.asynchronous || !awaiting ? Effect.void : Effect.yieldNow;
|
|
607
673
|
const self = this;
|
|
@@ -617,7 +683,7 @@ class Frame {
|
|
|
617
683
|
yield* Effect.yieldNow;
|
|
618
684
|
return yield* Effect.failCause(called.cause);
|
|
619
685
|
}
|
|
620
|
-
const captured = yield* Effect.exit(Effect.sync(() => self.requireIteratorObject(called.value, "Iterator return() result", node)
|
|
686
|
+
const captured = yield* Effect.exit(Effect.sync(() => get(self.requireIteratorObject(called.value, "Iterator return() result", node), "value")));
|
|
621
687
|
if (!Exit.isSuccess(captured)) {
|
|
622
688
|
if (awaiting)
|
|
623
689
|
yield* Effect.yieldNow;
|
|
@@ -628,7 +694,7 @@ class Frame {
|
|
|
628
694
|
});
|
|
629
695
|
}
|
|
630
696
|
requireIteratorObject(value, context, node) {
|
|
631
|
-
if (
|
|
697
|
+
if (value instanceof ProgramObject)
|
|
632
698
|
return value;
|
|
633
699
|
throw new InterpreterRuntimeError(`${context} must be an object.`, node).as("TypeError");
|
|
634
700
|
}
|
|
@@ -642,17 +708,13 @@ class Frame {
|
|
|
642
708
|
return value;
|
|
643
709
|
throw new InterpreterRuntimeError(`${context} must be a function.`, node).as("TypeError");
|
|
644
710
|
}
|
|
645
|
-
|
|
646
|
-
|
|
711
|
+
// for...in over null/undefined iterates nothing, like JS.
|
|
712
|
+
enumerableKeys(value, node) {
|
|
713
|
+
if (value instanceof ToolReference)
|
|
647
714
|
return [...this.runtime.toolKeys(value.path)];
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
}
|
|
652
|
-
if (value !== null && typeof value === "object" && !isRuntimeReference(value)) {
|
|
653
|
-
return Object.keys(value);
|
|
654
|
-
}
|
|
655
|
-
return undefined;
|
|
715
|
+
if (value === null || value === undefined)
|
|
716
|
+
return [];
|
|
717
|
+
return ownKeys(enumerableSource("for...in", value, node)).filter((key) => typeof key === "string");
|
|
656
718
|
}
|
|
657
719
|
evaluateForInStatement(node, labels) {
|
|
658
720
|
const left = node.left;
|
|
@@ -664,43 +726,32 @@ class Frame {
|
|
|
664
726
|
if (declared?.lexical)
|
|
665
727
|
self.predeclarePattern(declared.pattern, declared.mutable, left);
|
|
666
728
|
const right = yield* self.evaluateExpression(node.right);
|
|
667
|
-
const keys = self.enumerableKeys(right);
|
|
668
|
-
if (keys === undefined) {
|
|
669
|
-
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);
|
|
670
|
-
}
|
|
729
|
+
const keys = self.enumerableKeys(right, node.right);
|
|
671
730
|
if (left.type !== "Identifier" && left.type !== "VariableDeclaration") {
|
|
672
731
|
throw new InterpreterRuntimeError("Unsupported for...in binding.", left);
|
|
673
732
|
}
|
|
674
733
|
const assignmentName = left.type === "Identifier" ? left.name : undefined;
|
|
675
734
|
for (const key of keys) {
|
|
676
735
|
const result = yield* Effect.gen(function* () {
|
|
677
|
-
if (declared) {
|
|
736
|
+
if (declared?.lexical) {
|
|
678
737
|
self.scopes.push();
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
738
|
+
self.predeclarePattern(declared.pattern, declared.mutable, left);
|
|
739
|
+
yield* self.declarePattern(declared.pattern, key, declared.mutable, left, true);
|
|
740
|
+
}
|
|
741
|
+
else if (declared) {
|
|
742
|
+
yield* self.assignPattern(declared.pattern, key, left);
|
|
682
743
|
}
|
|
683
744
|
else if (assignmentName) {
|
|
684
745
|
self.scopes.set(assignmentName, key, left);
|
|
685
746
|
}
|
|
686
747
|
return yield* self.evaluateStatement(node.body);
|
|
687
748
|
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
688
|
-
if (declared)
|
|
749
|
+
if (declared?.lexical)
|
|
689
750
|
self.scopes.pop();
|
|
690
751
|
})));
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (result.kind === "break") {
|
|
695
|
-
if (result.label !== undefined && !labels?.has(result.label))
|
|
696
|
-
return result;
|
|
697
|
-
return { kind: "none" };
|
|
698
|
-
}
|
|
699
|
-
if (result.kind === "continue") {
|
|
700
|
-
if (result.label !== undefined && !labels?.has(result.label))
|
|
701
|
-
return result;
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
752
|
+
const exit = loopExit(result, labels);
|
|
753
|
+
if (exit !== undefined)
|
|
754
|
+
return exit;
|
|
704
755
|
}
|
|
705
756
|
return { kind: "none" };
|
|
706
757
|
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
@@ -781,8 +832,16 @@ class Frame {
|
|
|
781
832
|
throw new InterpreterRuntimeError("Unsupported variable declaration shape.", declaration);
|
|
782
833
|
}
|
|
783
834
|
const init = declaration.init;
|
|
784
|
-
|
|
785
|
-
|
|
835
|
+
// `var x` alone is a no-op: the binding was hoisted on function entry.
|
|
836
|
+
const id = declaration.id;
|
|
837
|
+
const evaluate = (init) => id.type === "Identifier" ? self.evaluateNamed(init, id.name) : self.evaluateExpression(init);
|
|
838
|
+
if (kind === "var") {
|
|
839
|
+
if (init)
|
|
840
|
+
yield* self.assignPattern(id, yield* evaluate(init), declaration);
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
const value = init ? yield* evaluate(init) : undefined;
|
|
844
|
+
yield* self.declarePattern(declaration.id, value, kind !== "const", declaration, true);
|
|
786
845
|
}
|
|
787
846
|
});
|
|
788
847
|
}
|
|
@@ -798,23 +857,19 @@ class Frame {
|
|
|
798
857
|
return;
|
|
799
858
|
}
|
|
800
859
|
if (pattern.type === "AssignmentPattern") {
|
|
801
|
-
const resolved = value === undefined ? yield* self.
|
|
860
|
+
const resolved = value === undefined ? yield* self.evaluateDefault(pattern) : value;
|
|
802
861
|
yield* self.declarePattern(pattern.left, resolved, mutable, node, initialize);
|
|
803
862
|
return;
|
|
804
863
|
}
|
|
805
864
|
if (pattern.type === "ObjectPattern") {
|
|
806
|
-
if (value
|
|
865
|
+
if (!(value instanceof ProgramObject)) {
|
|
807
866
|
throw new InterpreterRuntimeError(`Object destructuring requires a data object or array value, received ${describeValue(value)}.`, pattern, "InvalidDataValue");
|
|
808
867
|
}
|
|
809
868
|
const consumed = new Set();
|
|
810
869
|
for (const property of pattern.properties) {
|
|
811
870
|
if (property.type === "RestElement") {
|
|
812
|
-
const rest =
|
|
813
|
-
|
|
814
|
-
if (!consumed.has(key))
|
|
815
|
-
rest[key] = item;
|
|
816
|
-
}
|
|
817
|
-
copyIteratorSymbols(value, rest, consumed);
|
|
871
|
+
const rest = new ProgramObject();
|
|
872
|
+
assign(rest, value, consumed);
|
|
818
873
|
yield* self.declarePattern(property.argument, rest, mutable, property, initialize);
|
|
819
874
|
continue;
|
|
820
875
|
}
|
|
@@ -842,30 +897,25 @@ class Frame {
|
|
|
842
897
|
return;
|
|
843
898
|
}
|
|
844
899
|
if (pattern.type === "AssignmentPattern") {
|
|
845
|
-
const resolved = value === undefined ? yield* self.
|
|
900
|
+
const resolved = value === undefined ? yield* self.evaluateDefault(pattern) : value;
|
|
846
901
|
yield* self.assignPattern(pattern.left, resolved, node);
|
|
847
902
|
return;
|
|
848
903
|
}
|
|
849
904
|
if (pattern.type === "ObjectPattern") {
|
|
850
|
-
if (value
|
|
905
|
+
if (!(value instanceof ProgramObject)) {
|
|
851
906
|
throw new InterpreterRuntimeError(`Object destructuring requires a data object or array value, received ${describeValue(value)}.`, pattern, "InvalidDataValue");
|
|
852
907
|
}
|
|
853
|
-
const source = value;
|
|
854
908
|
const consumed = new Set();
|
|
855
909
|
for (const property of pattern.properties) {
|
|
856
910
|
if (property.type === "RestElement") {
|
|
857
|
-
const rest =
|
|
858
|
-
|
|
859
|
-
if (!consumed.has(key))
|
|
860
|
-
rest[key] = item;
|
|
861
|
-
}
|
|
862
|
-
copyIteratorSymbols(source, rest, consumed);
|
|
911
|
+
const rest = new ProgramObject();
|
|
912
|
+
assign(rest, value, consumed);
|
|
863
913
|
yield* self.assignPattern(property.argument, rest, property);
|
|
864
914
|
continue;
|
|
865
915
|
}
|
|
866
916
|
const key = yield* self.destructuringPropertyKey(property);
|
|
867
917
|
consumed.add(typeof key === "symbol" ? key : String(key));
|
|
868
|
-
yield* self.assignPattern(property.value, self.destructuringPropertyValue(
|
|
918
|
+
yield* self.assignPattern(property.value, self.destructuringPropertyValue(value, key), property);
|
|
869
919
|
}
|
|
870
920
|
return;
|
|
871
921
|
}
|
|
@@ -875,6 +925,11 @@ class Frame {
|
|
|
875
925
|
throw new InterpreterRuntimeError(`Unsupported assignment pattern '${pattern.type}'.`, node);
|
|
876
926
|
});
|
|
877
927
|
}
|
|
928
|
+
evaluateDefault(pattern) {
|
|
929
|
+
return pattern.left.type === "Identifier"
|
|
930
|
+
? this.evaluateNamed(pattern.right, pattern.left.name)
|
|
931
|
+
: this.evaluateExpression(pattern.right);
|
|
932
|
+
}
|
|
878
933
|
destructureArrayPattern(pattern, value, consume) {
|
|
879
934
|
const self = this;
|
|
880
935
|
return Effect.gen(function* () {
|
|
@@ -887,7 +942,7 @@ class Frame {
|
|
|
887
942
|
if (done) {
|
|
888
943
|
if (element === null)
|
|
889
944
|
continue;
|
|
890
|
-
yield* consume(element.type === "RestElement" ? element.argument : element, element.type === "RestElement" ?
|
|
945
|
+
yield* consume(element.type === "RestElement" ? element.argument : element, element.type === "RestElement" ? new ProgramArray() : undefined, element);
|
|
891
946
|
if (element.type === "RestElement")
|
|
892
947
|
return;
|
|
893
948
|
continue;
|
|
@@ -906,7 +961,7 @@ class Frame {
|
|
|
906
961
|
if (!done)
|
|
907
962
|
rest.push(next.value);
|
|
908
963
|
}
|
|
909
|
-
yield* consume(element.argument, rest, element);
|
|
964
|
+
yield* consume(element.argument, new ProgramArray(rest), element);
|
|
910
965
|
return;
|
|
911
966
|
}
|
|
912
967
|
const consumed = consume(element, step.done ? undefined : step.value, pattern);
|
|
@@ -931,14 +986,10 @@ class Frame {
|
|
|
931
986
|
throw unsupportedSyntax(keyNode.type, keyNode);
|
|
932
987
|
}
|
|
933
988
|
destructuringPropertyValue(source, key) {
|
|
934
|
-
if (!
|
|
935
|
-
return
|
|
936
|
-
if (key
|
|
937
|
-
return source
|
|
938
|
-
if (typeof key === "number")
|
|
939
|
-
return source[key];
|
|
940
|
-
if (Object.hasOwn(source, key))
|
|
941
|
-
return Reflect.get(source, key);
|
|
989
|
+
if (!(source instanceof ProgramArray))
|
|
990
|
+
return get(source, key);
|
|
991
|
+
if (has(source, key))
|
|
992
|
+
return get(source, key);
|
|
942
993
|
if (typeof key === "string" && arrayMethods.has(key))
|
|
943
994
|
return new IntrinsicReference(source, key);
|
|
944
995
|
return undefined;
|
|
@@ -1013,7 +1064,7 @@ class Frame {
|
|
|
1013
1064
|
// unsupported syntax. Built-ins like Number are real constructors in JS, so do not claim
|
|
1014
1065
|
// otherwise; say `new` is unsupported for them and point at the plain call.
|
|
1015
1066
|
const name = calleeDescription(node.callee);
|
|
1016
|
-
const message = callee instanceof
|
|
1067
|
+
const message = callee instanceof ProgramFunction
|
|
1017
1068
|
? `${name} cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.`
|
|
1018
1069
|
: callee instanceof HostFunction
|
|
1019
1070
|
? `new ${name}(...) is not supported; call ${name}(...) without new instead.`
|
|
@@ -1043,6 +1094,9 @@ class Frame {
|
|
|
1043
1094
|
return lhs === rhs;
|
|
1044
1095
|
if (operator === "!==")
|
|
1045
1096
|
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
|
+
}
|
|
1046
1100
|
if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
|
|
1047
1101
|
throw new InterpreterRuntimeError("Binary operators require data values.", node, "InvalidDataValue");
|
|
1048
1102
|
}
|
|
@@ -1095,11 +1149,10 @@ class Frame {
|
|
|
1095
1149
|
case ">>>":
|
|
1096
1150
|
return l >>> r;
|
|
1097
1151
|
case "in":
|
|
1098
|
-
if (rhs
|
|
1152
|
+
if (!(rhs instanceof ProgramObject)) {
|
|
1099
1153
|
throw new InterpreterRuntimeError("The 'in' operator requires a data object on the right-hand side.", node);
|
|
1100
1154
|
}
|
|
1101
|
-
|
|
1102
|
-
return Object.hasOwn(rhs, coerceOperand(lhs));
|
|
1155
|
+
return has(rhs, coerceOperand(lhs));
|
|
1103
1156
|
default:
|
|
1104
1157
|
throw new InterpreterRuntimeError(`Unsupported binary operator '${operator}'.`, node);
|
|
1105
1158
|
}
|
|
@@ -1178,7 +1231,7 @@ class Frame {
|
|
|
1178
1231
|
const next = toProgram(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result");
|
|
1179
1232
|
return self.scopes.set(name, next, left);
|
|
1180
1233
|
}
|
|
1181
|
-
const rightValue = yield* self.
|
|
1234
|
+
const rightValue = yield* self.evaluateNamed(node.right, name);
|
|
1182
1235
|
return self.scopes.set(name, rightValue, left);
|
|
1183
1236
|
}
|
|
1184
1237
|
if (left.type === "MemberExpression") {
|
|
@@ -1201,7 +1254,7 @@ class Frame {
|
|
|
1201
1254
|
const current = self.scopes.get(name, left);
|
|
1202
1255
|
if (!shouldAssign(current))
|
|
1203
1256
|
return current;
|
|
1204
|
-
const rightValue = yield* self.
|
|
1257
|
+
const rightValue = yield* self.evaluateNamed(node.right, name);
|
|
1205
1258
|
return self.scopes.set(name, rightValue, left);
|
|
1206
1259
|
});
|
|
1207
1260
|
}
|
|
@@ -1275,7 +1328,7 @@ class Frame {
|
|
|
1275
1328
|
}
|
|
1276
1329
|
return yield* self.createToolCallPromise(callable.path, args);
|
|
1277
1330
|
}
|
|
1278
|
-
if (callable instanceof
|
|
1331
|
+
if (callable instanceof ProgramFunction) {
|
|
1279
1332
|
return yield* self.invokeFunction(callable, args);
|
|
1280
1333
|
}
|
|
1281
1334
|
if (callable instanceof GeneratorMethodReference) {
|
|
@@ -1334,12 +1387,14 @@ class Frame {
|
|
|
1334
1387
|
}
|
|
1335
1388
|
for (const [index, parameter] of fn.parameters.entries()) {
|
|
1336
1389
|
if (parameter.type === "RestElement") {
|
|
1337
|
-
yield* invocation.declarePattern(parameter.argument, args.slice(index), true, parameter, true);
|
|
1390
|
+
yield* invocation.declarePattern(parameter.argument, new ProgramArray(args.slice(index)), true, parameter, true);
|
|
1338
1391
|
break;
|
|
1339
1392
|
}
|
|
1340
1393
|
yield* invocation.declarePattern(parameter, args[index], true, parameter, true);
|
|
1341
1394
|
}
|
|
1342
1395
|
if (fn.body.type === "BlockStatement") {
|
|
1396
|
+
invocation.scopes.push();
|
|
1397
|
+
invocation.hoistVars(fn.body.body, paramScope);
|
|
1343
1398
|
const result = yield* invocation.evaluateStatement(fn.body);
|
|
1344
1399
|
return result.kind === "return" ? result.value : undefined;
|
|
1345
1400
|
}
|
|
@@ -1349,12 +1404,7 @@ class Frame {
|
|
|
1349
1404
|
return Effect.succeed(this.createGenerator(invocation, run, fn.async));
|
|
1350
1405
|
if (!fn.async)
|
|
1351
1406
|
return run;
|
|
1352
|
-
|
|
1353
|
-
const box = {};
|
|
1354
|
-
return Effect.map(this.createPromise(Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runtime.runner, value, fn.body, box))), (promise) => {
|
|
1355
|
-
box.promise = promise;
|
|
1356
|
-
return promise;
|
|
1357
|
-
});
|
|
1407
|
+
return this.runtime.promises.createWithSelf((self) => Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runtime.runner, value, fn.body, self)));
|
|
1358
1408
|
}
|
|
1359
1409
|
createGenerator(invocation, run, asynchronous) {
|
|
1360
1410
|
const state = { started: false, completed: false, draining: false, pending: [], pendingIndex: 0 };
|
|
@@ -1379,13 +1429,13 @@ class Frame {
|
|
|
1379
1429
|
if (state.completed) {
|
|
1380
1430
|
if (kind === "throw")
|
|
1381
1431
|
return Effect.fail(new ProgramThrow(value));
|
|
1382
|
-
return Effect.succeed({ value: kind === "return" ? value : undefined, done: true });
|
|
1432
|
+
return Effect.succeed(record({ value: kind === "return" ? value : undefined, done: true }));
|
|
1383
1433
|
}
|
|
1384
1434
|
if (!state.started && kind !== "next") {
|
|
1385
1435
|
state.completed = true;
|
|
1386
1436
|
if (kind === "throw")
|
|
1387
1437
|
return Effect.fail(new ProgramThrow(value));
|
|
1388
|
-
return Effect.succeed({ value, done: true });
|
|
1438
|
+
return Effect.succeed(record({ value, done: true }));
|
|
1389
1439
|
}
|
|
1390
1440
|
state.pending.push(request);
|
|
1391
1441
|
if (state.available) {
|
|
@@ -1405,7 +1455,7 @@ class Frame {
|
|
|
1405
1455
|
const active = state.active;
|
|
1406
1456
|
state.active = undefined;
|
|
1407
1457
|
if (active) {
|
|
1408
|
-
Deferred.doneUnsafe(active.response, Exit.isSuccess(exit) ? Exit.succeed({ value: exit.value, done: true }) : exit);
|
|
1458
|
+
Deferred.doneUnsafe(active.response, Exit.isSuccess(exit) ? Exit.succeed(record({ value: exit.value, done: true })) : exit);
|
|
1409
1459
|
}
|
|
1410
1460
|
yield* invocation.completeGeneratorRequests(state, asynchronous);
|
|
1411
1461
|
state.completed = true;
|
|
@@ -1429,10 +1479,10 @@ class Frame {
|
|
|
1429
1479
|
}
|
|
1430
1480
|
if (asynchronous && pending.kind === "return") {
|
|
1431
1481
|
const resolved = yield* Effect.exit(self.awaitValue(pending.value));
|
|
1432
|
-
Deferred.doneUnsafe(pending.response, Exit.isSuccess(resolved) ? Exit.succeed({ value: resolved.value, done: true }) : resolved);
|
|
1482
|
+
Deferred.doneUnsafe(pending.response, Exit.isSuccess(resolved) ? Exit.succeed(record({ value: resolved.value, done: true })) : resolved);
|
|
1433
1483
|
continue;
|
|
1434
1484
|
}
|
|
1435
|
-
Deferred.doneUnsafe(pending.response, Exit.succeed({ value: pending.kind === "return" ? pending.value : undefined, done: true }));
|
|
1485
|
+
Deferred.doneUnsafe(pending.response, Exit.succeed(record({ value: pending.kind === "return" ? pending.value : undefined, done: true })));
|
|
1436
1486
|
}
|
|
1437
1487
|
});
|
|
1438
1488
|
}
|
|
@@ -1473,7 +1523,7 @@ class Frame {
|
|
|
1473
1523
|
const state = this.generatorState;
|
|
1474
1524
|
if (!state?.active)
|
|
1475
1525
|
throw new InterpreterRuntimeError("Generator has no active request.", node);
|
|
1476
|
-
Deferred.doneUnsafe(state.active.response, Exit.succeed({ value, done: false }));
|
|
1526
|
+
Deferred.doneUnsafe(state.active.response, Exit.succeed(record({ value, done: false })));
|
|
1477
1527
|
state.active = undefined;
|
|
1478
1528
|
return Effect.flatMap(this.takeGeneratorRequest(state), (request) => {
|
|
1479
1529
|
state.active = request;
|
|
@@ -1489,7 +1539,7 @@ class Frame {
|
|
|
1489
1539
|
delegateYield(value, node) {
|
|
1490
1540
|
const self = this;
|
|
1491
1541
|
return Effect.gen(function* () {
|
|
1492
|
-
if (
|
|
1542
|
+
if (value instanceof ProgramArray ||
|
|
1493
1543
|
typeof value === "string" ||
|
|
1494
1544
|
value instanceof Values.Map ||
|
|
1495
1545
|
value instanceof Values.Set ||
|
|
@@ -1526,7 +1576,7 @@ class Frame {
|
|
|
1526
1576
|
? iterator.next
|
|
1527
1577
|
: iterator.iterator instanceof CodeModeGenerator
|
|
1528
1578
|
? new GeneratorMethodReference(iterator.iterator, kind)
|
|
1529
|
-
: iterator.iterator
|
|
1579
|
+
: get(iterator.iterator, kind);
|
|
1530
1580
|
if (method === undefined || method === null) {
|
|
1531
1581
|
if (kind === "return")
|
|
1532
1582
|
return yield* Effect.fail(new GeneratorReturn(input));
|
|
@@ -1535,10 +1585,10 @@ class Frame {
|
|
|
1535
1585
|
}
|
|
1536
1586
|
const called = yield* self.invokeCallable(self.requireIteratorMethod(method, `Iterator ${kind}`, node), [input], node);
|
|
1537
1587
|
const result = self.requireIteratorObject(iterator.asynchronous ? yield* self.awaitValue(called) : called, `Iterator ${kind}() result`, node);
|
|
1538
|
-
const done = Boolean(result
|
|
1588
|
+
const done = Boolean(get(result, "done"));
|
|
1539
1589
|
const resultValue = self.generatorAsync && !iterator.asynchronous
|
|
1540
|
-
? yield* self.awaitAsyncFromSyncValue(iterator, result
|
|
1541
|
-
: result
|
|
1590
|
+
? yield* self.awaitAsyncFromSyncValue(iterator, get(result, "value"), node, kind !== "return" && !done)
|
|
1591
|
+
: get(result, "value");
|
|
1542
1592
|
if (done) {
|
|
1543
1593
|
if (kind === "return")
|
|
1544
1594
|
return yield* Effect.fail(new GeneratorReturn(resultValue));
|
|
@@ -1560,20 +1610,15 @@ class Frame {
|
|
|
1560
1610
|
});
|
|
1561
1611
|
}
|
|
1562
1612
|
evaluateObjectExpression(node) {
|
|
1563
|
-
const objectValue =
|
|
1613
|
+
const objectValue = new ProgramObject();
|
|
1564
1614
|
const self = this;
|
|
1565
1615
|
return Effect.gen(function* () {
|
|
1566
1616
|
for (const property of node.properties) {
|
|
1567
1617
|
if (property.type === "SpreadElement") {
|
|
1568
1618
|
const spread = yield* self.evaluateExpression(property.argument);
|
|
1569
|
-
if (spread === null || spread === undefined
|
|
1619
|
+
if (spread === null || spread === undefined)
|
|
1570
1620
|
continue;
|
|
1571
|
-
|
|
1572
|
-
throw new InterpreterRuntimeError(`Object spread requires a data object, received ${describeValue(spread)}.`, property, "InvalidDataValue");
|
|
1573
|
-
}
|
|
1574
|
-
for (const [key, value] of Object.entries(spread))
|
|
1575
|
-
objectValue[key] = value;
|
|
1576
|
-
copyIteratorSymbols(spread, objectValue);
|
|
1621
|
+
assign(objectValue, enumerableSource("Object spread", spread, property));
|
|
1577
1622
|
continue;
|
|
1578
1623
|
}
|
|
1579
1624
|
if (property.kind !== "init") {
|
|
@@ -1593,7 +1638,12 @@ class Frame {
|
|
|
1593
1638
|
else {
|
|
1594
1639
|
throw new InterpreterRuntimeError("Unsupported object property key shape.", keyNode);
|
|
1595
1640
|
}
|
|
1596
|
-
|
|
1641
|
+
const name = key === IteratorSymbol
|
|
1642
|
+
? "[Symbol.iterator]"
|
|
1643
|
+
: key === AsyncIteratorSymbol
|
|
1644
|
+
? "[Symbol.asyncIterator]"
|
|
1645
|
+
: String(key);
|
|
1646
|
+
set(objectValue, key, yield* self.evaluateNamed(property.value, name));
|
|
1597
1647
|
}
|
|
1598
1648
|
return objectValue;
|
|
1599
1649
|
});
|
|
@@ -1624,7 +1674,7 @@ class Frame {
|
|
|
1624
1674
|
values.push(yield* self.evaluateExpression(element));
|
|
1625
1675
|
}
|
|
1626
1676
|
}
|
|
1627
|
-
return values;
|
|
1677
|
+
return new ProgramArray(values);
|
|
1628
1678
|
});
|
|
1629
1679
|
}
|
|
1630
1680
|
evaluateTemplateLiteral(node) {
|
|
@@ -1686,6 +1736,14 @@ class Frame {
|
|
|
1686
1736
|
// Unknown static members read as undefined so feature detection works like native JS.
|
|
1687
1737
|
return new ComputedValue(objectValue.member(key, propertyNode));
|
|
1688
1738
|
}
|
|
1739
|
+
// Values have no prototype chain, so `.constructor` resolves to the owning built-in directly.
|
|
1740
|
+
if (operation === "read" &&
|
|
1741
|
+
key === "constructor" &&
|
|
1742
|
+
!(objectValue instanceof ProgramObject && has(objectValue, key))) {
|
|
1743
|
+
const name = constructorName(objectValue);
|
|
1744
|
+
if (name !== undefined)
|
|
1745
|
+
return new ComputedValue(self.runtime.builtins.get(name));
|
|
1746
|
+
}
|
|
1689
1747
|
if (typeof objectValue === "string") {
|
|
1690
1748
|
if (key === "length")
|
|
1691
1749
|
return new ComputedValue(objectValue.length);
|
|
@@ -1765,28 +1823,16 @@ class Frame {
|
|
|
1765
1823
|
}
|
|
1766
1824
|
return new ComputedValue(undefined);
|
|
1767
1825
|
}
|
|
1826
|
+
if (objectValue instanceof ProgramObject)
|
|
1827
|
+
return { target: objectValue, key };
|
|
1768
1828
|
if (isRuntimeReference(objectValue)) {
|
|
1769
1829
|
throw new InterpreterRuntimeError(`Cannot read properties of ${describeValue(objectValue)}; only data values expose properties.`, objectNode, "InvalidDataValue");
|
|
1770
1830
|
}
|
|
1771
|
-
|
|
1772
|
-
throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode);
|
|
1773
|
-
}
|
|
1774
|
-
if (Array.isArray(objectValue)) {
|
|
1775
|
-
if (operation === "delete")
|
|
1776
|
-
return { target: objectValue, key };
|
|
1777
|
-
const index = typeof key === "symbol" ? undefined : parseArrayIndex(key);
|
|
1778
|
-
if (key !== "length" && !(typeof key === "string" && arrayMethods.has(key)) && index === undefined) {
|
|
1779
|
-
if (typeof key === "string" && Object.hasOwn(objectValue, key)) {
|
|
1780
|
-
return new ComputedValue(objectValue[key]);
|
|
1781
|
-
}
|
|
1782
|
-
return new ComputedValue(undefined);
|
|
1783
|
-
}
|
|
1784
|
-
return { target: objectValue, key: index ?? key };
|
|
1785
|
-
}
|
|
1786
|
-
return { target: objectValue, key };
|
|
1831
|
+
throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode);
|
|
1787
1832
|
});
|
|
1788
1833
|
}
|
|
1789
1834
|
readMember(node) {
|
|
1835
|
+
const self = this;
|
|
1790
1836
|
return Effect.map(this.getMemberReference(node), (reference) => {
|
|
1791
1837
|
if (reference === OptionalShortCircuit)
|
|
1792
1838
|
return OptionalShortCircuit;
|
|
@@ -1794,19 +1840,15 @@ class Frame {
|
|
|
1794
1840
|
return reference.value;
|
|
1795
1841
|
if (reference === undefined || isOpaqueMemberReference(reference))
|
|
1796
1842
|
return reference;
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
if (reference.target instanceof Values.URL) {
|
|
1807
|
-
return Reflect.get(reference.target.url, reference.key);
|
|
1808
|
-
}
|
|
1809
|
-
return Reflect.get(reference.target, reference.key);
|
|
1843
|
+
const value = self.readReferenceValue(reference, reference.key);
|
|
1844
|
+
if (value === undefined &&
|
|
1845
|
+
reference.target instanceof ProgramArray &&
|
|
1846
|
+
typeof reference.key === "string" &&
|
|
1847
|
+
arrayMethods.has(reference.key) &&
|
|
1848
|
+
!has(reference.target, reference.key)) {
|
|
1849
|
+
return new IntrinsicReference(reference.target, reference.key);
|
|
1850
|
+
}
|
|
1851
|
+
return value;
|
|
1810
1852
|
});
|
|
1811
1853
|
}
|
|
1812
1854
|
writeMember(node, value) {
|
|
@@ -1829,27 +1871,20 @@ class Frame {
|
|
|
1829
1871
|
if (reference.target instanceof Values.RegExp) {
|
|
1830
1872
|
return Reflect.deleteProperty(reference.target.regex, reference.key);
|
|
1831
1873
|
}
|
|
1832
|
-
return
|
|
1874
|
+
return remove(reference.target, reference.key);
|
|
1833
1875
|
});
|
|
1834
1876
|
}
|
|
1835
1877
|
// Resolve side-effecting object and key expressions exactly once.
|
|
1836
1878
|
modifyMember(node, compute) {
|
|
1837
1879
|
const self = this;
|
|
1838
1880
|
return Effect.gen(function* () {
|
|
1839
|
-
const reference = yield* self.getMemberReference(node);
|
|
1881
|
+
const reference = yield* self.getMemberReference(node, "write");
|
|
1840
1882
|
if (reference === OptionalShortCircuit ||
|
|
1841
1883
|
reference instanceof ComputedValue ||
|
|
1842
1884
|
reference === undefined ||
|
|
1843
1885
|
isOpaqueMemberReference(reference)) {
|
|
1844
1886
|
throw new InterpreterRuntimeError("Only data fields may be assigned.", node);
|
|
1845
1887
|
}
|
|
1846
|
-
if (Array.isArray(reference.target)) {
|
|
1847
|
-
if (reference.key === "length")
|
|
1848
|
-
throw new InterpreterRuntimeError("Array length cannot be assigned.", node);
|
|
1849
|
-
if (typeof reference.key === "string" && arrayMethods.has(reference.key)) {
|
|
1850
|
-
throw new InterpreterRuntimeError("Array methods cannot be assigned.", node);
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
1888
|
const key = reference.key;
|
|
1854
1889
|
const { write, next, result } = yield* compute(self.readReferenceValue(reference, key));
|
|
1855
1890
|
if (write)
|
|
@@ -1863,18 +1898,9 @@ class Frame {
|
|
|
1863
1898
|
}
|
|
1864
1899
|
if (reference.target instanceof Values.RegExp)
|
|
1865
1900
|
return reference.target.lastIndex;
|
|
1866
|
-
return
|
|
1901
|
+
return get(reference.target, key);
|
|
1867
1902
|
}
|
|
1868
1903
|
assignToReference(reference, key, next, node) {
|
|
1869
|
-
if (Array.isArray(reference.target)) {
|
|
1870
|
-
const target = reference.target;
|
|
1871
|
-
if (typeof key !== "number" || parseArrayIndex(key) === undefined) {
|
|
1872
|
-
throw new InterpreterRuntimeError("Array assignment index must be a valid array index.", node, "InvalidDataValue");
|
|
1873
|
-
}
|
|
1874
|
-
rejectCircularInsertion(target, next, "Array assignment result", node);
|
|
1875
|
-
target[key] = next;
|
|
1876
|
-
return;
|
|
1877
|
-
}
|
|
1878
1904
|
if (reference.target instanceof Values.URL) {
|
|
1879
1905
|
const property = key;
|
|
1880
1906
|
if (!urlWritableProperties.has(property)) {
|
|
@@ -1896,8 +1922,12 @@ class Frame {
|
|
|
1896
1922
|
return;
|
|
1897
1923
|
}
|
|
1898
1924
|
const target = reference.target;
|
|
1899
|
-
rejectCircularInsertion(target, next, "Object assignment result", node);
|
|
1900
|
-
|
|
1925
|
+
rejectCircularInsertion(target, next, target instanceof ProgramArray ? "Array assignment result" : "Object assignment result", node);
|
|
1926
|
+
if (set(target, key, next))
|
|
1927
|
+
return;
|
|
1928
|
+
if (target instanceof ProgramArray)
|
|
1929
|
+
throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError");
|
|
1930
|
+
throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node).as("TypeError");
|
|
1901
1931
|
}
|
|
1902
1932
|
toPropertyKey(value, node) {
|
|
1903
1933
|
if (typeof value === "string" || typeof value === "number") {
|