@opencode/codemode 0.0.0-dev-19530 → 2.0.0
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/data.d.ts +5 -6
- package/dist/data.js +44 -47
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interpreter/errors.d.ts +3 -5
- package/dist/interpreter/errors.js +22 -51
- package/dist/interpreter/execute.js +3 -5
- package/dist/interpreter/globals.js +47 -69
- package/dist/interpreter/host.d.ts +41 -0
- package/dist/interpreter/host.js +44 -0
- package/dist/interpreter/methods.d.ts +4 -0
- package/dist/interpreter/methods.js +837 -0
- package/dist/interpreter/model.d.ts +36 -13
- package/dist/interpreter/model.js +45 -15
- package/dist/interpreter/objects.d.ts +13 -106
- package/dist/interpreter/objects.js +65 -192
- package/dist/interpreter/promises.d.ts +13 -12
- package/dist/interpreter/promises.js +54 -59
- package/dist/interpreter/references.d.ts +0 -1
- package/dist/interpreter/references.js +33 -20
- package/dist/interpreter/runner.d.ts +11 -15
- package/dist/interpreter/runner.js +21 -21
- package/dist/interpreter/runtime.d.ts +3 -3
- package/dist/interpreter/runtime.js +327 -165
- package/dist/interpreter/scope.js +6 -6
- package/dist/stdlib/array.d.ts +2 -4
- package/dist/stdlib/array.js +32 -424
- package/dist/stdlib/collections.d.ts +7 -3
- package/dist/stdlib/collections.js +119 -291
- package/dist/stdlib/console.d.ts +2 -3
- package/dist/stdlib/console.js +30 -35
- package/dist/stdlib/date.d.ts +7 -1
- package/dist/stdlib/date.js +188 -93
- package/dist/stdlib/json.d.ts +2 -2
- package/dist/stdlib/json.js +27 -27
- package/dist/stdlib/math.d.ts +2 -2
- package/dist/stdlib/math.js +76 -96
- package/dist/stdlib/number.d.ts +4 -3
- package/dist/stdlib/number.js +60 -95
- package/dist/stdlib/object.d.ts +4 -4
- package/dist/stdlib/object.js +54 -128
- package/dist/stdlib/regexp.d.ts +8 -6
- package/dist/stdlib/regexp.js +68 -76
- package/dist/stdlib/string.d.ts +2 -2
- package/dist/stdlib/string.js +50 -213
- package/dist/stdlib/url.d.ts +13 -5
- package/dist/stdlib/url.js +102 -202
- package/dist/stdlib/value.d.ts +9 -5
- package/dist/stdlib/value.js +38 -16
- package/dist/stdlib/web.d.ts +4 -4
- package/dist/stdlib/web.js +10 -12
- package/dist/tool-runtime.d.ts +1 -2
- package/dist/tool-runtime.js +2 -2
- package/dist/values.d.ts +37 -0
- package/dist/values.js +56 -0
- package/package.json +1 -1
- package/dist/interpreter/generators.d.ts +0 -4
- package/dist/interpreter/generators.js +0 -25
- package/dist/interpreter/intrinsics.d.ts +0 -13
- package/dist/interpreter/intrinsics.js +0 -82
- package/dist/interpreter/native.d.ts +0 -19
- package/dist/interpreter/native.js +0 -40
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { Cause, Deferred, Effect, Exit } from "effect";
|
|
2
|
-
import { toProgram } from "../data.js";
|
|
2
|
+
import { ToolRuntimeError, toProgram } from "../data.js";
|
|
3
3
|
import { ToolReference } from "../tool-runtime.js";
|
|
4
|
-
import { AsyncIteratorSymbol, GeneratorReturn, InterpreterRuntimeError, IteratorSymbol,
|
|
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
|
-
import {
|
|
7
|
+
import { HostFunction, HostNamespace } from "./host.js";
|
|
8
|
+
import { invokeIntrinsic } from "./methods.js";
|
|
9
|
+
import { assign, get, has, ownKeys, parseArrayIndex, ProgramArray, ProgramFunction, ProgramObject, record, remove, set, } from "./objects.js";
|
|
8
10
|
import { preserveConsumerError } from "./runner.js";
|
|
9
|
-
import { PromiseRuntime, resolvePromise, resolvePromiseValue } from "./promises.js";
|
|
10
|
-
import { containsOpaqueReference, describeValue, rejectCircularInsertion, typeofValue } from "./references.js";
|
|
11
|
+
import { invokePromiseInstanceMethod, PromiseRuntime, resolvePromise, resolvePromiseValue } from "./promises.js";
|
|
12
|
+
import { containsOpaqueReference, describeValue, isRuntimeReference, rejectCircularInsertion, typeofValue, } from "./references.js";
|
|
11
13
|
import { ScopeStack } from "./scope.js";
|
|
12
|
-
import {
|
|
14
|
+
import { arrayMethods, mapMethods, setMethods } from "../stdlib/collections.js";
|
|
15
|
+
import { dateMethods } from "../stdlib/date.js";
|
|
16
|
+
import { numberMethods } from "../stdlib/number.js";
|
|
17
|
+
import { constructRegExp, regexpMethods, regexpProperties } from "../stdlib/regexp.js";
|
|
18
|
+
import { stringMethods } from "../stdlib/string.js";
|
|
19
|
+
import { uriArgument, urlMethods, urlProperties, urlSearchParamsMethods, urlWritableProperties } from "../stdlib/url.js";
|
|
13
20
|
import { enumerableSource } from "../stdlib/object.js";
|
|
14
|
-
import { coerceToNumber, coerceToString, compoundOperators } from "../stdlib/value.js";
|
|
21
|
+
import { coerceToNumber, coerceToString, compoundOperators, errorBrandName } from "../stdlib/value.js";
|
|
22
|
+
import { Values } from "../values.js";
|
|
15
23
|
// What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating.
|
|
16
24
|
// Unlabelled break ends this loop; a label the loop does not carry propagates outward.
|
|
17
25
|
const loopExit = (result, labels) => {
|
|
@@ -42,16 +50,37 @@ const calleeDescription = (callee) => {
|
|
|
42
50
|
}
|
|
43
51
|
return "The called value";
|
|
44
52
|
};
|
|
45
|
-
|
|
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
|
+
};
|
|
46
80
|
const instanceofValue = (lhs, rhs, node) => {
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const prototype = get(rhs, "prototype");
|
|
51
|
-
if (!(prototype instanceof ProgramObject)) {
|
|
52
|
-
throw new InterpreterRuntimeError("The right-hand side of 'instanceof' has no 'prototype' object.", node);
|
|
53
|
-
}
|
|
54
|
-
return hasPrototype(lhs, prototype);
|
|
81
|
+
if (rhs instanceof HostFunction && rhs.instanceOf !== undefined)
|
|
82
|
+
return rhs.instanceOf(lhs);
|
|
83
|
+
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);
|
|
55
84
|
};
|
|
56
85
|
const collectPatternNames = (pattern, out = []) => {
|
|
57
86
|
switch (pattern.type) {
|
|
@@ -141,6 +170,10 @@ const loopDeclaration = (left, statement) => {
|
|
|
141
170
|
lexical: kind !== "var",
|
|
142
171
|
};
|
|
143
172
|
};
|
|
173
|
+
const isOpaqueMemberReference = (value) => value instanceof ToolReference ||
|
|
174
|
+
value instanceof PromiseInstanceMethodReference ||
|
|
175
|
+
value instanceof IntrinsicReference ||
|
|
176
|
+
value instanceof GeneratorMethodReference;
|
|
144
177
|
const promiseResolutionNode = { type: "PromiseResolution", start: 0, end: 0 };
|
|
145
178
|
/** One program execution: the tool bridge, promise scheduler, captured logs, and the global scope built once. */
|
|
146
179
|
export class Runtime {
|
|
@@ -148,29 +181,29 @@ export class Runtime {
|
|
|
148
181
|
search;
|
|
149
182
|
toolKeys;
|
|
150
183
|
promises;
|
|
151
|
-
prototypes;
|
|
152
184
|
logs;
|
|
153
185
|
runner;
|
|
186
|
+
/** Built-in globals by name, unaffected by program shadowing. */
|
|
187
|
+
builtins;
|
|
154
188
|
root;
|
|
155
|
-
constructor(executeTool, search, toolKeys, promises,
|
|
189
|
+
constructor(executeTool, search, toolKeys, promises, logs = [], extraGlobals = () => []) {
|
|
156
190
|
this.executeTool = executeTool;
|
|
157
191
|
this.search = search;
|
|
158
192
|
this.toolKeys = toolKeys;
|
|
159
193
|
this.promises = promises;
|
|
160
|
-
this.prototypes = prototypes;
|
|
161
194
|
this.logs = logs;
|
|
162
195
|
const globalScope = new Map();
|
|
163
196
|
// Calling back into the program never reads frame state, so any frame serves; the root is always alive.
|
|
164
197
|
this.root = new Frame(this, new ScopeStack([globalScope]));
|
|
165
198
|
this.runner = {
|
|
166
|
-
|
|
199
|
+
invokeFunction: (fn, args) => this.root.invokeFunction(fn, args),
|
|
200
|
+
invokeCallable: (callable, args, node) => this.root.invokeCallable(callable, args, node),
|
|
167
201
|
settlePromise: (promise) => this.root.settlePromise(promise),
|
|
168
202
|
syncIterator: (value, node) => this.root.syncIterator(value, node),
|
|
169
|
-
prototypes,
|
|
170
203
|
};
|
|
171
|
-
|
|
204
|
+
this.builtins = new Map([...globals(this), ...extraGlobals(this)]);
|
|
205
|
+
for (const [name, value] of this.builtins)
|
|
172
206
|
globalScope.set(name, { mutable: false, value });
|
|
173
|
-
}
|
|
174
207
|
}
|
|
175
208
|
run(program) {
|
|
176
209
|
return this.root.run(program);
|
|
@@ -216,7 +249,10 @@ class Frame {
|
|
|
216
249
|
}
|
|
217
250
|
// Fork at the call site so admission and hooks occur when the call is made.
|
|
218
251
|
createToolCallPromise(path, args) {
|
|
219
|
-
return this.
|
|
252
|
+
return this.createPromise(Effect.suspend(() => this.runtime.executeTool(path, args)));
|
|
253
|
+
}
|
|
254
|
+
createPromise(effect) {
|
|
255
|
+
return this.runtime.promises.create(effect);
|
|
220
256
|
}
|
|
221
257
|
// Fiber exits make settlement idempotent; yielding prevents inline continuation.
|
|
222
258
|
settlePromise(promise) {
|
|
@@ -289,7 +325,7 @@ class Frame {
|
|
|
289
325
|
}).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
|
|
290
326
|
}
|
|
291
327
|
createFunction(node, name = node.type === "ArrowFunctionExpression" ? "" : (node.id?.name ?? "")) {
|
|
292
|
-
return new ProgramFunction(
|
|
328
|
+
return new ProgramFunction(name, node.params, node.body, this.scopes.capture(), node.async, node.generator);
|
|
293
329
|
}
|
|
294
330
|
// NamedEvaluation: an anonymous function definition takes the name of what it is assigned to.
|
|
295
331
|
evaluateNamed(node, name) {
|
|
@@ -469,7 +505,7 @@ class Frame {
|
|
|
469
505
|
const iterator = yield* self.customIterator(right, node, awaiting);
|
|
470
506
|
const cursor = iterator === undefined ? yield* self.syncIterator(right, node) : undefined;
|
|
471
507
|
if (iterator === undefined && cursor === undefined) {
|
|
472
|
-
throw new InterpreterRuntimeError(`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams, or custom iterator value.`, node);
|
|
508
|
+
throw new InterpreterRuntimeError(`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams, or custom iterator value.`, node).as("TypeError");
|
|
473
509
|
}
|
|
474
510
|
const close = () => iterator
|
|
475
511
|
? self.closeIterator(iterator, node, awaiting)
|
|
@@ -543,21 +579,20 @@ class Frame {
|
|
|
543
579
|
? value.items[Symbol.iterator]()
|
|
544
580
|
: typeof value === "string"
|
|
545
581
|
? value[Symbol.iterator]()
|
|
546
|
-
: value instanceof
|
|
582
|
+
: value instanceof Values.Map
|
|
547
583
|
? value.map.entries()
|
|
548
|
-
: value instanceof
|
|
584
|
+
: value instanceof Values.Set
|
|
549
585
|
? value.set.values()
|
|
550
|
-
: value instanceof
|
|
586
|
+
: value instanceof Values.URLSearchParams
|
|
551
587
|
? value.params.entries()
|
|
552
588
|
: undefined;
|
|
553
589
|
if (iterator !== undefined) {
|
|
554
|
-
const proto = this.runtime.prototypes.Array;
|
|
555
590
|
return Effect.succeed({
|
|
556
591
|
next: Effect.sync(() => {
|
|
557
592
|
const step = iterator.next();
|
|
558
593
|
return {
|
|
559
594
|
done: Boolean(step.done),
|
|
560
|
-
value: Array.isArray(step.value) ? new ProgramArray(
|
|
595
|
+
value: Array.isArray(step.value) ? new ProgramArray(step.value) : step.value,
|
|
561
596
|
};
|
|
562
597
|
}),
|
|
563
598
|
close: Effect.void,
|
|
@@ -572,6 +607,15 @@ class Frame {
|
|
|
572
607
|
});
|
|
573
608
|
}
|
|
574
609
|
customIterator(value, node, allowAsync = true) {
|
|
610
|
+
if (value instanceof CodeModeGenerator) {
|
|
611
|
+
if (value.asynchronous && !allowAsync)
|
|
612
|
+
return Effect.undefined;
|
|
613
|
+
return Effect.succeed({
|
|
614
|
+
iterator: value,
|
|
615
|
+
next: new GeneratorMethodReference(value, "next"),
|
|
616
|
+
asynchronous: value.asynchronous,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
575
619
|
if (!(value instanceof ProgramObject))
|
|
576
620
|
return Effect.undefined;
|
|
577
621
|
const asyncMethod = allowAsync ? get(value, AsyncIteratorSymbol) : undefined;
|
|
@@ -579,11 +623,13 @@ class Frame {
|
|
|
579
623
|
if (method === undefined || method === null)
|
|
580
624
|
return Effect.undefined;
|
|
581
625
|
const self = this;
|
|
582
|
-
return Effect.map(this.invokeCallable(this.requireIteratorMethod(method, "Iterator method", node),
|
|
583
|
-
const object = self.
|
|
626
|
+
return Effect.map(this.invokeCallable(this.requireIteratorMethod(method, "Iterator method", node), [], node), (iterator) => {
|
|
627
|
+
const object = self.requireIterator(iterator, node);
|
|
584
628
|
return {
|
|
585
629
|
iterator: object,
|
|
586
|
-
next:
|
|
630
|
+
next: object instanceof CodeModeGenerator
|
|
631
|
+
? new GeneratorMethodReference(object, "next")
|
|
632
|
+
: self.requireIteratorMethod(get(object, "next"), "Iterator next", node),
|
|
587
633
|
asynchronous: asyncMethod !== undefined && asyncMethod !== null,
|
|
588
634
|
};
|
|
589
635
|
});
|
|
@@ -592,10 +638,10 @@ class Frame {
|
|
|
592
638
|
const self = this;
|
|
593
639
|
return Effect.gen(function* () {
|
|
594
640
|
if (iterator.asynchronous) {
|
|
595
|
-
const object = self.requireIteratorObject(yield* self.awaitValue(yield* self.invokeCallable(iterator.next,
|
|
641
|
+
const object = self.requireIteratorObject(yield* self.awaitValue(yield* self.invokeCallable(iterator.next, [], node)), "Iterator next() result", node);
|
|
596
642
|
return { done: Boolean(get(object, "done")), value: get(object, "value") };
|
|
597
643
|
}
|
|
598
|
-
const called = yield* Effect.exit(self.invokeCallable(iterator.next,
|
|
644
|
+
const called = yield* Effect.exit(self.invokeCallable(iterator.next, [], node));
|
|
599
645
|
if (!Exit.isSuccess(called)) {
|
|
600
646
|
if (awaiting)
|
|
601
647
|
yield* Effect.yieldNow;
|
|
@@ -619,17 +665,19 @@ class Frame {
|
|
|
619
665
|
});
|
|
620
666
|
}
|
|
621
667
|
closeIterator(iterator, node, awaiting = true) {
|
|
622
|
-
const close =
|
|
668
|
+
const close = iterator.iterator instanceof CodeModeGenerator
|
|
669
|
+
? new GeneratorMethodReference(iterator.iterator, "return")
|
|
670
|
+
: get(iterator.iterator, "return");
|
|
623
671
|
if (close === undefined || close === null)
|
|
624
672
|
return iterator.asynchronous || !awaiting ? Effect.void : Effect.yieldNow;
|
|
625
673
|
const self = this;
|
|
626
674
|
return Effect.gen(function* () {
|
|
627
675
|
const method = self.requireIteratorMethod(close, "Iterator return", node);
|
|
628
676
|
if (iterator.asynchronous) {
|
|
629
|
-
self.requireIteratorObject(yield* self.awaitValue(yield* self.invokeCallable(method,
|
|
677
|
+
self.requireIteratorObject(yield* self.awaitValue(yield* self.invokeCallable(method, [], node)), "Iterator return() result", node);
|
|
630
678
|
return;
|
|
631
679
|
}
|
|
632
|
-
const called = yield* Effect.exit(self.invokeCallable(method,
|
|
680
|
+
const called = yield* Effect.exit(self.invokeCallable(method, [], node));
|
|
633
681
|
if (!Exit.isSuccess(called)) {
|
|
634
682
|
if (awaiting)
|
|
635
683
|
yield* Effect.yieldNow;
|
|
@@ -648,12 +696,17 @@ class Frame {
|
|
|
648
696
|
requireIteratorObject(value, context, node) {
|
|
649
697
|
if (value instanceof ProgramObject)
|
|
650
698
|
return value;
|
|
651
|
-
throw new InterpreterRuntimeError(`${context} must be an object.`, node);
|
|
699
|
+
throw new InterpreterRuntimeError(`${context} must be an object.`, node).as("TypeError");
|
|
700
|
+
}
|
|
701
|
+
requireIterator(value, node) {
|
|
702
|
+
return value instanceof CodeModeGenerator
|
|
703
|
+
? value
|
|
704
|
+
: this.requireIteratorObject(value, "Iterator method result", node);
|
|
652
705
|
}
|
|
653
706
|
requireIteratorMethod(value, context, node) {
|
|
654
707
|
if (typeofValue(value) === "function")
|
|
655
708
|
return value;
|
|
656
|
-
throw new InterpreterRuntimeError(`${context} must be a function.`, node);
|
|
709
|
+
throw new InterpreterRuntimeError(`${context} must be a function.`, node).as("TypeError");
|
|
657
710
|
}
|
|
658
711
|
// for...in over null/undefined iterates nothing, like JS.
|
|
659
712
|
enumerableKeys(value, node) {
|
|
@@ -661,7 +714,7 @@ class Frame {
|
|
|
661
714
|
return [...this.runtime.toolKeys(value.path)];
|
|
662
715
|
if (value === null || value === undefined)
|
|
663
716
|
return [];
|
|
664
|
-
return
|
|
717
|
+
return ownKeys(enumerableSource("for...in", value, node)).filter((key) => typeof key === "string");
|
|
665
718
|
}
|
|
666
719
|
evaluateForInStatement(node, labels) {
|
|
667
720
|
const left = node.left;
|
|
@@ -749,7 +802,7 @@ class Frame {
|
|
|
749
802
|
if (cause.reasons.some(Cause.isInterruptReason) || Cause.squash(cause) instanceof GeneratorReturn || !handler) {
|
|
750
803
|
return Effect.failCause(cause);
|
|
751
804
|
}
|
|
752
|
-
const caught = caughtErrorValue(
|
|
805
|
+
const caught = caughtErrorValue(Cause.squash(cause));
|
|
753
806
|
const parameter = handler.param;
|
|
754
807
|
self.scopes.push();
|
|
755
808
|
return Effect.gen(function* () {
|
|
@@ -815,14 +868,14 @@ class Frame {
|
|
|
815
868
|
const consumed = new Set();
|
|
816
869
|
for (const property of pattern.properties) {
|
|
817
870
|
if (property.type === "RestElement") {
|
|
818
|
-
const rest = new ProgramObject(
|
|
871
|
+
const rest = new ProgramObject();
|
|
819
872
|
assign(rest, value, consumed);
|
|
820
873
|
yield* self.declarePattern(property.argument, rest, mutable, property, initialize);
|
|
821
874
|
continue;
|
|
822
875
|
}
|
|
823
876
|
const key = yield* self.destructuringPropertyKey(property);
|
|
824
877
|
consumed.add(typeof key === "symbol" ? key : String(key));
|
|
825
|
-
yield* self.declarePattern(property.value,
|
|
878
|
+
yield* self.declarePattern(property.value, self.destructuringPropertyValue(value, key), mutable, property, initialize);
|
|
826
879
|
}
|
|
827
880
|
return;
|
|
828
881
|
}
|
|
@@ -855,14 +908,14 @@ class Frame {
|
|
|
855
908
|
const consumed = new Set();
|
|
856
909
|
for (const property of pattern.properties) {
|
|
857
910
|
if (property.type === "RestElement") {
|
|
858
|
-
const rest = new ProgramObject(
|
|
911
|
+
const rest = new ProgramObject();
|
|
859
912
|
assign(rest, value, consumed);
|
|
860
913
|
yield* self.assignPattern(property.argument, rest, property);
|
|
861
914
|
continue;
|
|
862
915
|
}
|
|
863
916
|
const key = yield* self.destructuringPropertyKey(property);
|
|
864
917
|
consumed.add(typeof key === "symbol" ? key : String(key));
|
|
865
|
-
yield* self.assignPattern(property.value,
|
|
918
|
+
yield* self.assignPattern(property.value, self.destructuringPropertyValue(value, key), property);
|
|
866
919
|
}
|
|
867
920
|
return;
|
|
868
921
|
}
|
|
@@ -882,14 +935,14 @@ class Frame {
|
|
|
882
935
|
return Effect.gen(function* () {
|
|
883
936
|
const cursor = yield* self.syncIterator(value, pattern);
|
|
884
937
|
if (cursor === undefined) {
|
|
885
|
-
throw new InterpreterRuntimeError("Array destructuring requires a supported iterable value.", pattern);
|
|
938
|
+
throw new InterpreterRuntimeError("Array destructuring requires a supported iterable value.", pattern).as("TypeError");
|
|
886
939
|
}
|
|
887
940
|
let done = false;
|
|
888
941
|
for (const element of pattern.elements) {
|
|
889
942
|
if (done) {
|
|
890
943
|
if (element === null)
|
|
891
944
|
continue;
|
|
892
|
-
yield* consume(element.type === "RestElement" ? element.argument : element, element.type === "RestElement" ? new ProgramArray(
|
|
945
|
+
yield* consume(element.type === "RestElement" ? element.argument : element, element.type === "RestElement" ? new ProgramArray() : undefined, element);
|
|
893
946
|
if (element.type === "RestElement")
|
|
894
947
|
return;
|
|
895
948
|
continue;
|
|
@@ -908,7 +961,7 @@ class Frame {
|
|
|
908
961
|
if (!done)
|
|
909
962
|
rest.push(next.value);
|
|
910
963
|
}
|
|
911
|
-
yield* consume(element.argument, new ProgramArray(
|
|
964
|
+
yield* consume(element.argument, new ProgramArray(rest), element);
|
|
912
965
|
return;
|
|
913
966
|
}
|
|
914
967
|
const consumed = consume(element, step.done ? undefined : step.value, pattern);
|
|
@@ -932,13 +985,22 @@ class Frame {
|
|
|
932
985
|
return Effect.succeed(String(keyNode.value));
|
|
933
986
|
throw unsupportedSyntax(keyNode.type, keyNode);
|
|
934
987
|
}
|
|
988
|
+
destructuringPropertyValue(source, key) {
|
|
989
|
+
if (!(source instanceof ProgramArray))
|
|
990
|
+
return get(source, key);
|
|
991
|
+
if (has(source, key))
|
|
992
|
+
return get(source, key);
|
|
993
|
+
if (typeof key === "string" && arrayMethods.has(key))
|
|
994
|
+
return new IntrinsicReference(source, key);
|
|
995
|
+
return undefined;
|
|
996
|
+
}
|
|
935
997
|
evaluateExpression(node) {
|
|
936
998
|
switch (node.type) {
|
|
937
999
|
case "Literal": {
|
|
938
1000
|
const regex = node.regex;
|
|
939
1001
|
if (regex)
|
|
940
|
-
return Effect.sync(() => constructRegExp(
|
|
941
|
-
return Effect.sync(() => toProgram(
|
|
1002
|
+
return Effect.sync(() => constructRegExp([regex.pattern, regex.flags], node));
|
|
1003
|
+
return Effect.sync(() => toProgram(node.value, "Literal"));
|
|
942
1004
|
}
|
|
943
1005
|
case "Identifier":
|
|
944
1006
|
return Effect.sync(() => this.scopes.get(node.name, node));
|
|
@@ -996,7 +1058,7 @@ class Frame {
|
|
|
996
1058
|
return Effect.gen(function* () {
|
|
997
1059
|
const callee = yield* self.evaluateExpression(node.callee);
|
|
998
1060
|
// Globals are built with this interpreter's R; `instanceof` cannot recover the type argument.
|
|
999
|
-
const construct = callee instanceof
|
|
1061
|
+
const construct = callee instanceof HostFunction ? callee.construct : undefined;
|
|
1000
1062
|
if (construct === undefined) {
|
|
1001
1063
|
// `new` itself is supported, so a non-constructible callee is a TypeError like JS rather than
|
|
1002
1064
|
// unsupported syntax. Built-ins like Number are real constructors in JS, so do not claim
|
|
@@ -1004,13 +1066,13 @@ class Frame {
|
|
|
1004
1066
|
const name = calleeDescription(node.callee);
|
|
1005
1067
|
const message = callee instanceof ProgramFunction
|
|
1006
1068
|
? `${name} cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.`
|
|
1007
|
-
: callee instanceof
|
|
1069
|
+
: callee instanceof HostFunction
|
|
1008
1070
|
? `new ${name}(...) is not supported; call ${name}(...) without new instead.`
|
|
1009
1071
|
: `${name} is not a constructor.`;
|
|
1010
|
-
throw new InterpreterRuntimeError(message, node);
|
|
1072
|
+
throw new InterpreterRuntimeError(message, node).as("TypeError");
|
|
1011
1073
|
}
|
|
1012
1074
|
const args = yield* self.evaluateCallArguments(node.arguments);
|
|
1013
|
-
return yield* construct(args,
|
|
1075
|
+
return yield* construct(args, node);
|
|
1014
1076
|
});
|
|
1015
1077
|
}
|
|
1016
1078
|
evaluateBinaryExpression(node) {
|
|
@@ -1024,7 +1086,7 @@ class Frame {
|
|
|
1024
1086
|
const rhs = yield* self.evaluateExpression(node.right);
|
|
1025
1087
|
if (operator === "instanceof")
|
|
1026
1088
|
return instanceofValue(lhs, rhs, node);
|
|
1027
|
-
return toProgram(self.
|
|
1089
|
+
return toProgram(self.applyBinaryOperator(operator, lhs, rhs, node), "Binary expression result");
|
|
1028
1090
|
});
|
|
1029
1091
|
}
|
|
1030
1092
|
applyBinaryOperator(operator, lhs, rhs, node) {
|
|
@@ -1041,7 +1103,7 @@ class Frame {
|
|
|
1041
1103
|
// Null-prototype data needs explicit primitive coercion; identity and `in` retain raw objects.
|
|
1042
1104
|
// Dates use their default string hint for addition and loose equality, and epoch time elsewhere.
|
|
1043
1105
|
const coerceOperand = (operand) => {
|
|
1044
|
-
if (operand instanceof
|
|
1106
|
+
if (operand instanceof Values.Date) {
|
|
1045
1107
|
return operator === "+" || operator === "==" || operator === "!=" ? coerceToString(operand) : operand.time;
|
|
1046
1108
|
}
|
|
1047
1109
|
return operand !== null && typeof operand === "object" ? coerceToString(operand) : operand;
|
|
@@ -1126,7 +1188,7 @@ class Frame {
|
|
|
1126
1188
|
if (containsOpaqueReference(value)) {
|
|
1127
1189
|
throw new InterpreterRuntimeError("Unary operators require data values.", node, "InvalidDataValue");
|
|
1128
1190
|
}
|
|
1129
|
-
const operand = value instanceof
|
|
1191
|
+
const operand = value instanceof Values.Date
|
|
1130
1192
|
? value.time
|
|
1131
1193
|
: value !== null && typeof value === "object"
|
|
1132
1194
|
? coerceToString(value)
|
|
@@ -1145,7 +1207,7 @@ class Frame {
|
|
|
1145
1207
|
default:
|
|
1146
1208
|
throw new InterpreterRuntimeError(`Unsupported unary operator '${operator}'.`, node);
|
|
1147
1209
|
}
|
|
1148
|
-
return toProgram(
|
|
1210
|
+
return toProgram(result, "Unary expression result");
|
|
1149
1211
|
});
|
|
1150
1212
|
}
|
|
1151
1213
|
evaluateAssignmentExpression(node) {
|
|
@@ -1166,7 +1228,7 @@ class Frame {
|
|
|
1166
1228
|
if (operator !== "=") {
|
|
1167
1229
|
const current = self.scopes.get(name, left);
|
|
1168
1230
|
const rightValue = yield* self.evaluateExpression(node.right);
|
|
1169
|
-
const next = toProgram(self.
|
|
1231
|
+
const next = toProgram(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result");
|
|
1170
1232
|
return self.scopes.set(name, next, left);
|
|
1171
1233
|
}
|
|
1172
1234
|
const rightValue = yield* self.evaluateNamed(node.right, name);
|
|
@@ -1176,7 +1238,7 @@ class Frame {
|
|
|
1176
1238
|
return yield* self.modifyMember(left, (current) => Effect.map(self.evaluateExpression(node.right), (rightValue) => {
|
|
1177
1239
|
if (operator === "=")
|
|
1178
1240
|
return { write: true, next: rightValue, result: rightValue };
|
|
1179
|
-
const next = toProgram(self.
|
|
1241
|
+
const next = toProgram(self.applyCompoundAssignment(operator, current, rightValue, node), "Assignment result");
|
|
1180
1242
|
return { write: true, next, result: next };
|
|
1181
1243
|
}));
|
|
1182
1244
|
}
|
|
@@ -1241,37 +1303,23 @@ class Frame {
|
|
|
1241
1303
|
}
|
|
1242
1304
|
throw new InterpreterRuntimeError("Update target must be an Identifier or MemberExpression.", argument);
|
|
1243
1305
|
}
|
|
1244
|
-
// EvaluateCall: a member callee supplies its base object as `this`; anything else calls with undefined.
|
|
1245
1306
|
evaluateCallExpression(node) {
|
|
1246
1307
|
const callee = node.callee;
|
|
1247
1308
|
const self = this;
|
|
1248
1309
|
return Effect.gen(function* () {
|
|
1249
1310
|
if (callee.type === "Super")
|
|
1250
1311
|
throw unsupportedSyntax(callee.type, callee);
|
|
1251
|
-
const
|
|
1252
|
-
? yield* self.readMethod(callee)
|
|
1253
|
-
: { callable: yield* self.evaluateExpression(callee), thisValue: undefined };
|
|
1312
|
+
const callable = yield* self.evaluateExpression(callee);
|
|
1254
1313
|
if (callable === OptionalShortCircuit)
|
|
1255
1314
|
return OptionalShortCircuit;
|
|
1256
1315
|
if ((callable === null || callable === undefined) && node.optional)
|
|
1257
1316
|
return OptionalShortCircuit;
|
|
1258
1317
|
const args = yield* self.evaluateCallArguments(node.arguments);
|
|
1259
|
-
return yield* self.invokeCallable(callable,
|
|
1260
|
-
});
|
|
1261
|
-
}
|
|
1262
|
-
readMethod(node) {
|
|
1263
|
-
return Effect.map(this.getMemberReference(node), (reference) => {
|
|
1264
|
-
if (reference === OptionalShortCircuit)
|
|
1265
|
-
return { callable: OptionalShortCircuit, thisValue: undefined };
|
|
1266
|
-
if (reference instanceof ToolReference)
|
|
1267
|
-
return { callable: reference, thisValue: undefined };
|
|
1268
|
-
if ("value" in reference)
|
|
1269
|
-
return { callable: reference.value, thisValue: undefined };
|
|
1270
|
-
return { callable: this.readReference(reference, node), thisValue: reference.receiver };
|
|
1318
|
+
return yield* self.invokeCallable(callable, args, node, callee);
|
|
1271
1319
|
});
|
|
1272
1320
|
}
|
|
1273
1321
|
// The single dispatch for every invocation: call expressions and callbacks share it.
|
|
1274
|
-
invokeCallable(callable,
|
|
1322
|
+
invokeCallable(callable, args, node, callee) {
|
|
1275
1323
|
const self = this;
|
|
1276
1324
|
return Effect.gen(function* () {
|
|
1277
1325
|
if (callable instanceof ToolReference) {
|
|
@@ -1280,11 +1328,27 @@ class Frame {
|
|
|
1280
1328
|
}
|
|
1281
1329
|
return yield* self.createToolCallPromise(callable.path, args);
|
|
1282
1330
|
}
|
|
1283
|
-
if (callable instanceof ProgramFunction)
|
|
1331
|
+
if (callable instanceof ProgramFunction) {
|
|
1284
1332
|
return yield* self.invokeFunction(callable, args);
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1333
|
+
}
|
|
1334
|
+
if (callable instanceof GeneratorMethodReference) {
|
|
1335
|
+
if (callable.kind === "iterator")
|
|
1336
|
+
return callable.generator;
|
|
1337
|
+
const requested = callable.generator.request(callable.kind, args[0], node);
|
|
1338
|
+
return callable.generator.asynchronous ? yield* self.createPromise(requested) : yield* requested;
|
|
1339
|
+
}
|
|
1340
|
+
if (callable instanceof IntrinsicReference) {
|
|
1341
|
+
return yield* invokeIntrinsic(self.runtime.runner, callable, args, node);
|
|
1342
|
+
}
|
|
1343
|
+
if (callable instanceof PromiseInstanceMethodReference) {
|
|
1344
|
+
return yield* invokePromiseInstanceMethod(self.runtime.runner, self.runtime.promises, callable, args, node);
|
|
1345
|
+
}
|
|
1346
|
+
if (callable instanceof HostFunction)
|
|
1347
|
+
return yield* callable.call(args, node);
|
|
1348
|
+
if (callable === undefined || callable === null) {
|
|
1349
|
+
throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee ?? node).as("TypeError");
|
|
1350
|
+
}
|
|
1351
|
+
throw new InterpreterRuntimeError("Only tools are callable here.", callee ?? node);
|
|
1288
1352
|
});
|
|
1289
1353
|
}
|
|
1290
1354
|
evaluateCallArguments(argNodes) {
|
|
@@ -1296,7 +1360,7 @@ class Frame {
|
|
|
1296
1360
|
const spread = yield* self.evaluateExpression(argNode.argument);
|
|
1297
1361
|
const cursor = yield* self.syncIterator(spread, argNode);
|
|
1298
1362
|
if (cursor === undefined)
|
|
1299
|
-
throw new InterpreterRuntimeError("Spread arguments require a synchronous iterable.", argNode);
|
|
1363
|
+
throw new InterpreterRuntimeError("Spread arguments require a synchronous iterable.", argNode).as("TypeError");
|
|
1300
1364
|
while (true) {
|
|
1301
1365
|
const step = yield* cursor.next;
|
|
1302
1366
|
if (step.done)
|
|
@@ -1312,7 +1376,6 @@ class Frame {
|
|
|
1312
1376
|
});
|
|
1313
1377
|
}
|
|
1314
1378
|
invokeFunction(fn, args) {
|
|
1315
|
-
const self = this;
|
|
1316
1379
|
const invocation = new Frame(this.runtime, new ScopeStack([...fn.capturedScopes, new Map()]));
|
|
1317
1380
|
const run = Effect.gen(function* () {
|
|
1318
1381
|
// Seed all parameters first so defaults cannot fall through to same-named outer bindings.
|
|
@@ -1324,7 +1387,7 @@ class Frame {
|
|
|
1324
1387
|
}
|
|
1325
1388
|
for (const [index, parameter] of fn.parameters.entries()) {
|
|
1326
1389
|
if (parameter.type === "RestElement") {
|
|
1327
|
-
yield* invocation.declarePattern(parameter.argument, new ProgramArray(
|
|
1390
|
+
yield* invocation.declarePattern(parameter.argument, new ProgramArray(args.slice(index)), true, parameter, true);
|
|
1328
1391
|
break;
|
|
1329
1392
|
}
|
|
1330
1393
|
yield* invocation.declarePattern(parameter, args[index], true, parameter, true);
|
|
@@ -1347,12 +1410,10 @@ class Frame {
|
|
|
1347
1410
|
const state = { started: false, completed: false, draining: false, pending: [], pendingIndex: 0 };
|
|
1348
1411
|
invocation.generatorState = state;
|
|
1349
1412
|
invocation.generatorAsync = asynchronous;
|
|
1350
|
-
const
|
|
1351
|
-
const result = (value, done) => record(protos.Object, { value, done });
|
|
1352
|
-
const request = (kind, value, node) => {
|
|
1413
|
+
const generator = new CodeModeGenerator(asynchronous, (kind, value, node) => {
|
|
1353
1414
|
const request = { kind, value, response: Deferred.makeUnsafe() };
|
|
1354
1415
|
if (!asynchronous && state.active) {
|
|
1355
|
-
return Effect.fail(new InterpreterRuntimeError("Generator is already running.", node));
|
|
1416
|
+
return Effect.fail(new InterpreterRuntimeError("Generator is already running.", node).as("TypeError"));
|
|
1356
1417
|
}
|
|
1357
1418
|
if (asynchronous && (state.completed || (!state.started && kind !== "next"))) {
|
|
1358
1419
|
state.started = true;
|
|
@@ -1368,13 +1429,13 @@ class Frame {
|
|
|
1368
1429
|
if (state.completed) {
|
|
1369
1430
|
if (kind === "throw")
|
|
1370
1431
|
return Effect.fail(new ProgramThrow(value));
|
|
1371
|
-
return Effect.succeed(
|
|
1432
|
+
return Effect.succeed(record({ value: kind === "return" ? value : undefined, done: true }));
|
|
1372
1433
|
}
|
|
1373
1434
|
if (!state.started && kind !== "next") {
|
|
1374
1435
|
state.completed = true;
|
|
1375
1436
|
if (kind === "throw")
|
|
1376
1437
|
return Effect.fail(new ProgramThrow(value));
|
|
1377
|
-
return Effect.succeed(
|
|
1438
|
+
return Effect.succeed(record({ value, done: true }));
|
|
1378
1439
|
}
|
|
1379
1440
|
state.pending.push(request);
|
|
1380
1441
|
if (state.available) {
|
|
@@ -1394,7 +1455,7 @@ class Frame {
|
|
|
1394
1455
|
const active = state.active;
|
|
1395
1456
|
state.active = undefined;
|
|
1396
1457
|
if (active) {
|
|
1397
|
-
Deferred.doneUnsafe(active.response, Exit.isSuccess(exit) ? Exit.succeed(
|
|
1458
|
+
Deferred.doneUnsafe(active.response, Exit.isSuccess(exit) ? Exit.succeed(record({ value: exit.value, done: true })) : exit);
|
|
1398
1459
|
}
|
|
1399
1460
|
yield* invocation.completeGeneratorRequests(state, asynchronous);
|
|
1400
1461
|
state.completed = true;
|
|
@@ -1402,13 +1463,11 @@ class Frame {
|
|
|
1402
1463
|
return Effect.andThen(this.runtime.promises.fork(body), Deferred.await(request.response));
|
|
1403
1464
|
}
|
|
1404
1465
|
return Deferred.await(request.response);
|
|
1405
|
-
};
|
|
1406
|
-
const generator = new ProgramGenerator(asynchronous ? protos.AsyncGenerator : protos.Generator, asynchronous, request);
|
|
1466
|
+
});
|
|
1407
1467
|
return generator;
|
|
1408
1468
|
}
|
|
1409
1469
|
completeGeneratorRequests(state, asynchronous) {
|
|
1410
1470
|
const self = this;
|
|
1411
|
-
const result = (value, done) => record(self.runtime.prototypes.Object, { value, done });
|
|
1412
1471
|
return Effect.gen(function* () {
|
|
1413
1472
|
while (true) {
|
|
1414
1473
|
const pending = self.dequeueGeneratorRequest(state);
|
|
@@ -1420,10 +1479,10 @@ class Frame {
|
|
|
1420
1479
|
}
|
|
1421
1480
|
if (asynchronous && pending.kind === "return") {
|
|
1422
1481
|
const resolved = yield* Effect.exit(self.awaitValue(pending.value));
|
|
1423
|
-
Deferred.doneUnsafe(pending.response, Exit.isSuccess(resolved) ? Exit.succeed(
|
|
1482
|
+
Deferred.doneUnsafe(pending.response, Exit.isSuccess(resolved) ? Exit.succeed(record({ value: resolved.value, done: true })) : resolved);
|
|
1424
1483
|
continue;
|
|
1425
1484
|
}
|
|
1426
|
-
Deferred.doneUnsafe(pending.response, Exit.succeed(
|
|
1485
|
+
Deferred.doneUnsafe(pending.response, Exit.succeed(record({ value: pending.kind === "return" ? pending.value : undefined, done: true })));
|
|
1427
1486
|
}
|
|
1428
1487
|
});
|
|
1429
1488
|
}
|
|
@@ -1464,7 +1523,7 @@ class Frame {
|
|
|
1464
1523
|
const state = this.generatorState;
|
|
1465
1524
|
if (!state?.active)
|
|
1466
1525
|
throw new InterpreterRuntimeError("Generator has no active request.", node);
|
|
1467
|
-
Deferred.doneUnsafe(state.active.response, Exit.succeed(record(
|
|
1526
|
+
Deferred.doneUnsafe(state.active.response, Exit.succeed(record({ value, done: false })));
|
|
1468
1527
|
state.active = undefined;
|
|
1469
1528
|
return Effect.flatMap(this.takeGeneratorRequest(state), (request) => {
|
|
1470
1529
|
state.active = request;
|
|
@@ -1482,9 +1541,9 @@ class Frame {
|
|
|
1482
1541
|
return Effect.gen(function* () {
|
|
1483
1542
|
if (value instanceof ProgramArray ||
|
|
1484
1543
|
typeof value === "string" ||
|
|
1485
|
-
value instanceof
|
|
1486
|
-
value instanceof
|
|
1487
|
-
value instanceof
|
|
1544
|
+
value instanceof Values.Map ||
|
|
1545
|
+
value instanceof Values.Set ||
|
|
1546
|
+
value instanceof Values.URLSearchParams) {
|
|
1488
1547
|
const cursor = yield* self.syncIterator(value, node);
|
|
1489
1548
|
if (!cursor)
|
|
1490
1549
|
throw new InterpreterRuntimeError("Built-in iterator is unavailable.", node);
|
|
@@ -1502,25 +1561,29 @@ class Frame {
|
|
|
1502
1561
|
}
|
|
1503
1562
|
if (error instanceof ProgramThrow) {
|
|
1504
1563
|
yield* cursor.close;
|
|
1505
|
-
throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node);
|
|
1564
|
+
throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node).as("TypeError");
|
|
1506
1565
|
}
|
|
1507
1566
|
return yield* Effect.failCause(resumed.cause);
|
|
1508
1567
|
}
|
|
1509
1568
|
}
|
|
1510
1569
|
const iterator = yield* self.customIterator(value, node, self.generatorAsync);
|
|
1511
1570
|
if (!iterator)
|
|
1512
|
-
throw new InterpreterRuntimeError("yield* requires a compatible iterable value.", node);
|
|
1571
|
+
throw new InterpreterRuntimeError("yield* requires a compatible iterable value.", node).as("TypeError");
|
|
1513
1572
|
let kind = "next";
|
|
1514
1573
|
let input = undefined;
|
|
1515
1574
|
while (true) {
|
|
1516
|
-
const method = kind === "next"
|
|
1575
|
+
const method = kind === "next"
|
|
1576
|
+
? iterator.next
|
|
1577
|
+
: iterator.iterator instanceof CodeModeGenerator
|
|
1578
|
+
? new GeneratorMethodReference(iterator.iterator, kind)
|
|
1579
|
+
: get(iterator.iterator, kind);
|
|
1517
1580
|
if (method === undefined || method === null) {
|
|
1518
1581
|
if (kind === "return")
|
|
1519
1582
|
return yield* Effect.fail(new GeneratorReturn(input));
|
|
1520
1583
|
yield* self.closeIterator(iterator, node, self.generatorAsync);
|
|
1521
|
-
throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node);
|
|
1584
|
+
throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node).as("TypeError");
|
|
1522
1585
|
}
|
|
1523
|
-
const called = yield* self.invokeCallable(self.requireIteratorMethod(method, `Iterator ${kind}`, node),
|
|
1586
|
+
const called = yield* self.invokeCallable(self.requireIteratorMethod(method, `Iterator ${kind}`, node), [input], node);
|
|
1524
1587
|
const result = self.requireIteratorObject(iterator.asynchronous ? yield* self.awaitValue(called) : called, `Iterator ${kind}() result`, node);
|
|
1525
1588
|
const done = Boolean(get(result, "done"));
|
|
1526
1589
|
const resultValue = self.generatorAsync && !iterator.asynchronous
|
|
@@ -1547,7 +1610,7 @@ class Frame {
|
|
|
1547
1610
|
});
|
|
1548
1611
|
}
|
|
1549
1612
|
evaluateObjectExpression(node) {
|
|
1550
|
-
const objectValue = new ProgramObject(
|
|
1613
|
+
const objectValue = new ProgramObject();
|
|
1551
1614
|
const self = this;
|
|
1552
1615
|
return Effect.gen(function* () {
|
|
1553
1616
|
for (const property of node.properties) {
|
|
@@ -1555,7 +1618,7 @@ class Frame {
|
|
|
1555
1618
|
const spread = yield* self.evaluateExpression(property.argument);
|
|
1556
1619
|
if (spread === null || spread === undefined)
|
|
1557
1620
|
continue;
|
|
1558
|
-
assign(objectValue, enumerableSource(
|
|
1621
|
+
assign(objectValue, enumerableSource("Object spread", spread, property));
|
|
1559
1622
|
continue;
|
|
1560
1623
|
}
|
|
1561
1624
|
if (property.kind !== "init") {
|
|
@@ -1580,7 +1643,7 @@ class Frame {
|
|
|
1580
1643
|
: key === AsyncIteratorSymbol
|
|
1581
1644
|
? "[Symbol.asyncIterator]"
|
|
1582
1645
|
: String(key);
|
|
1583
|
-
|
|
1646
|
+
set(objectValue, key, yield* self.evaluateNamed(property.value, name));
|
|
1584
1647
|
}
|
|
1585
1648
|
return objectValue;
|
|
1586
1649
|
});
|
|
@@ -1599,7 +1662,7 @@ class Frame {
|
|
|
1599
1662
|
const spread = yield* self.evaluateExpression(element.argument);
|
|
1600
1663
|
const cursor = yield* self.syncIterator(spread, element);
|
|
1601
1664
|
if (cursor === undefined)
|
|
1602
|
-
throw new InterpreterRuntimeError("Array spread requires a synchronous iterable.", element);
|
|
1665
|
+
throw new InterpreterRuntimeError("Array spread requires a synchronous iterable.", element).as("TypeError");
|
|
1603
1666
|
while (true) {
|
|
1604
1667
|
const step = yield* cursor.next;
|
|
1605
1668
|
if (step.done)
|
|
@@ -1611,7 +1674,7 @@ class Frame {
|
|
|
1611
1674
|
values.push(yield* self.evaluateExpression(element));
|
|
1612
1675
|
}
|
|
1613
1676
|
}
|
|
1614
|
-
return new ProgramArray(
|
|
1677
|
+
return new ProgramArray(values);
|
|
1615
1678
|
});
|
|
1616
1679
|
}
|
|
1617
1680
|
evaluateTemplateLiteral(node) {
|
|
@@ -1629,7 +1692,7 @@ class Frame {
|
|
|
1629
1692
|
output += quasi.value.cooked;
|
|
1630
1693
|
if (index < expressions.length) {
|
|
1631
1694
|
const raw = yield* self.evaluateExpression(expressions[index]);
|
|
1632
|
-
output += coerceToString(toProgram(
|
|
1695
|
+
output += coerceToString(toProgram(raw, "Template interpolation"));
|
|
1633
1696
|
}
|
|
1634
1697
|
}
|
|
1635
1698
|
return output;
|
|
@@ -1644,7 +1707,7 @@ class Frame {
|
|
|
1644
1707
|
}
|
|
1645
1708
|
return this.applyBinaryOperator(operator.slice(0, -1), current, incoming, node);
|
|
1646
1709
|
}
|
|
1647
|
-
getMemberReference(node) {
|
|
1710
|
+
getMemberReference(node, operation = "read") {
|
|
1648
1711
|
const objectNode = node.object;
|
|
1649
1712
|
const propertyNode = node.property;
|
|
1650
1713
|
if (objectNode.type === "Super")
|
|
@@ -1669,49 +1732,123 @@ class Frame {
|
|
|
1669
1732
|
}
|
|
1670
1733
|
return new ToolReference([...objectValue.path, key]);
|
|
1671
1734
|
}
|
|
1672
|
-
if (objectValue instanceof
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1735
|
+
if (objectValue instanceof HostFunction || objectValue instanceof HostNamespace) {
|
|
1736
|
+
// Unknown static members read as undefined so feature detection works like native JS.
|
|
1737
|
+
return new ComputedValue(objectValue.member(key, propertyNode));
|
|
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
|
+
}
|
|
1676
1747
|
if (typeof objectValue === "string") {
|
|
1677
1748
|
if (key === "length")
|
|
1678
|
-
return
|
|
1749
|
+
return new ComputedValue(objectValue.length);
|
|
1679
1750
|
const index = typeof key === "symbol" ? undefined : parseArrayIndex(key);
|
|
1680
1751
|
if (index !== undefined)
|
|
1681
|
-
return
|
|
1682
|
-
|
|
1752
|
+
return new ComputedValue(objectValue[index]);
|
|
1753
|
+
if (typeof key === "string" && stringMethods.has(key))
|
|
1754
|
+
return new IntrinsicReference(objectValue, key);
|
|
1755
|
+
return new ComputedValue(undefined);
|
|
1756
|
+
}
|
|
1757
|
+
if (typeof objectValue === "number") {
|
|
1758
|
+
if (typeof key === "string" && numberMethods.has(key))
|
|
1759
|
+
return new IntrinsicReference(objectValue, key);
|
|
1760
|
+
return new ComputedValue(undefined);
|
|
1761
|
+
}
|
|
1762
|
+
if (objectValue instanceof Values.Date) {
|
|
1763
|
+
if (typeof key === "string" && dateMethods.has(key))
|
|
1764
|
+
return new IntrinsicReference(objectValue, key);
|
|
1765
|
+
return new ComputedValue(undefined);
|
|
1766
|
+
}
|
|
1767
|
+
if (objectValue instanceof Values.RegExp) {
|
|
1768
|
+
if (key === "lastIndex")
|
|
1769
|
+
return { target: objectValue, key };
|
|
1770
|
+
if (typeof key === "string" && regexpProperties.has(key)) {
|
|
1771
|
+
return new ComputedValue(objectValue.regex[key]);
|
|
1772
|
+
}
|
|
1773
|
+
if (typeof key === "string" && regexpMethods.has(key))
|
|
1774
|
+
return new IntrinsicReference(objectValue, key);
|
|
1775
|
+
return new ComputedValue(undefined);
|
|
1776
|
+
}
|
|
1777
|
+
if (objectValue instanceof Values.Map) {
|
|
1778
|
+
if (key === "size")
|
|
1779
|
+
return new ComputedValue(objectValue.map.size);
|
|
1780
|
+
if (typeof key === "string" && mapMethods.has(key))
|
|
1781
|
+
return new IntrinsicReference(objectValue, key);
|
|
1782
|
+
return new ComputedValue(undefined);
|
|
1783
|
+
}
|
|
1784
|
+
if (objectValue instanceof Values.Set) {
|
|
1785
|
+
if (key === "size")
|
|
1786
|
+
return new ComputedValue(objectValue.set.size);
|
|
1787
|
+
if (typeof key === "string" && setMethods.has(key))
|
|
1788
|
+
return new IntrinsicReference(objectValue, key);
|
|
1789
|
+
return new ComputedValue(undefined);
|
|
1790
|
+
}
|
|
1791
|
+
if (objectValue instanceof Values.URL) {
|
|
1792
|
+
if (key === "searchParams") {
|
|
1793
|
+
return new ComputedValue(objectValue.searchParams);
|
|
1794
|
+
}
|
|
1795
|
+
if (typeof key === "string" && urlMethods.has(key))
|
|
1796
|
+
return new IntrinsicReference(objectValue, key);
|
|
1797
|
+
if (typeof key === "string" && urlProperties.has(key))
|
|
1798
|
+
return { target: objectValue, key };
|
|
1799
|
+
return new ComputedValue(undefined);
|
|
1800
|
+
}
|
|
1801
|
+
if (objectValue instanceof Values.URLSearchParams) {
|
|
1802
|
+
if (key === "size")
|
|
1803
|
+
return new ComputedValue(objectValue.params.size);
|
|
1804
|
+
if (typeof key === "string" && urlSearchParamsMethods.has(key)) {
|
|
1805
|
+
return new IntrinsicReference(objectValue, key);
|
|
1806
|
+
}
|
|
1807
|
+
return new ComputedValue(undefined);
|
|
1808
|
+
}
|
|
1809
|
+
// Reject unknown promise properties so a missing await cannot hide.
|
|
1810
|
+
if (objectValue instanceof Values.Promise) {
|
|
1811
|
+
if (key === "then" || key === "catch" || key === "finally") {
|
|
1812
|
+
return new PromiseInstanceMethodReference(objectValue, key);
|
|
1813
|
+
}
|
|
1814
|
+
throw new InterpreterRuntimeError("This value is an un-awaited Promise; await it first - e.g. `const result = await tools.ns.tool(...)`.", objectNode, "InvalidDataValue");
|
|
1815
|
+
}
|
|
1816
|
+
if (objectValue instanceof CodeModeGenerator) {
|
|
1817
|
+
if (key === "next" || key === "return" || key === "throw") {
|
|
1818
|
+
return new GeneratorMethodReference(objectValue, key);
|
|
1819
|
+
}
|
|
1820
|
+
if ((key === IteratorSymbol && !objectValue.asynchronous) ||
|
|
1821
|
+
(key === AsyncIteratorSymbol && objectValue.asynchronous)) {
|
|
1822
|
+
return new GeneratorMethodReference(objectValue, "iterator");
|
|
1823
|
+
}
|
|
1824
|
+
return new ComputedValue(undefined);
|
|
1683
1825
|
}
|
|
1684
|
-
if (
|
|
1685
|
-
return { target:
|
|
1686
|
-
if (
|
|
1687
|
-
|
|
1688
|
-
if (objectValue === null || objectValue === undefined) {
|
|
1689
|
-
throw new InterpreterRuntimeError(`Cannot read properties of ${objectValue} (reading '${String(key)}').`, objectNode);
|
|
1826
|
+
if (objectValue instanceof ProgramObject)
|
|
1827
|
+
return { target: objectValue, key };
|
|
1828
|
+
if (isRuntimeReference(objectValue)) {
|
|
1829
|
+
throw new InterpreterRuntimeError(`Cannot read properties of ${describeValue(objectValue)}; only data values expose properties.`, objectNode, "InvalidDataValue");
|
|
1690
1830
|
}
|
|
1691
1831
|
throw new InterpreterRuntimeError("Cannot access a property on a non-object value.", objectNode);
|
|
1692
1832
|
});
|
|
1693
1833
|
}
|
|
1694
|
-
readReference(reference, node) {
|
|
1695
|
-
// Reject unknown promise properties so a missing await cannot hide.
|
|
1696
|
-
if (reference.target instanceof ProgramPromise && !has(reference.target, reference.key)) {
|
|
1697
|
-
throw new InterpreterRuntimeError("This value is an un-awaited Promise; await it first - e.g. `const result = await tools.ns.tool(...)`.", node.object, "InvalidDataValue");
|
|
1698
|
-
}
|
|
1699
|
-
try {
|
|
1700
|
-
return get(reference.target, reference.key, reference.receiver);
|
|
1701
|
-
}
|
|
1702
|
-
catch (error) {
|
|
1703
|
-
throw locate(error, node);
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
1834
|
readMember(node) {
|
|
1835
|
+
const self = this;
|
|
1707
1836
|
return Effect.map(this.getMemberReference(node), (reference) => {
|
|
1708
1837
|
if (reference === OptionalShortCircuit)
|
|
1709
1838
|
return OptionalShortCircuit;
|
|
1710
|
-
if (reference instanceof
|
|
1711
|
-
return reference;
|
|
1712
|
-
if ("value" in reference)
|
|
1839
|
+
if (reference instanceof ComputedValue)
|
|
1713
1840
|
return reference.value;
|
|
1714
|
-
|
|
1841
|
+
if (reference === undefined || isOpaqueMemberReference(reference))
|
|
1842
|
+
return reference;
|
|
1843
|
+
const value = self.readReferenceValue(reference, reference.key);
|
|
1844
|
+
if (value === undefined &&
|
|
1845
|
+
reference.target instanceof ProgramArray &&
|
|
1846
|
+
typeof reference.key === "string" &&
|
|
1847
|
+
arrayMethods.has(reference.key) &&
|
|
1848
|
+
!has(reference.target, reference.key)) {
|
|
1849
|
+
return new IntrinsicReference(reference.target, reference.key);
|
|
1850
|
+
}
|
|
1851
|
+
return value;
|
|
1715
1852
|
});
|
|
1716
1853
|
}
|
|
1717
1854
|
writeMember(node, value) {
|
|
@@ -1722,50 +1859,75 @@ class Frame {
|
|
|
1722
1859
|
if (target.type !== "MemberExpression") {
|
|
1723
1860
|
throw new InterpreterRuntimeError("Only data fields may be deleted.", argument);
|
|
1724
1861
|
}
|
|
1725
|
-
return Effect.map(this.getMemberReference(target), (reference) => {
|
|
1862
|
+
return Effect.map(this.getMemberReference(target, "delete"), (reference) => {
|
|
1726
1863
|
if (reference === OptionalShortCircuit)
|
|
1727
1864
|
return true;
|
|
1728
|
-
if (reference instanceof
|
|
1865
|
+
if (reference instanceof ComputedValue ||
|
|
1866
|
+
reference === undefined ||
|
|
1867
|
+
isOpaqueMemberReference(reference) ||
|
|
1868
|
+
reference.target instanceof Values.URL) {
|
|
1729
1869
|
throw new InterpreterRuntimeError("Only data fields may be deleted.", target, "InvalidDataValue");
|
|
1730
1870
|
}
|
|
1731
|
-
if (
|
|
1732
|
-
return
|
|
1733
|
-
|
|
1871
|
+
if (reference.target instanceof Values.RegExp) {
|
|
1872
|
+
return Reflect.deleteProperty(reference.target.regex, reference.key);
|
|
1873
|
+
}
|
|
1874
|
+
return remove(reference.target, reference.key);
|
|
1734
1875
|
});
|
|
1735
1876
|
}
|
|
1736
1877
|
// Resolve side-effecting object and key expressions exactly once.
|
|
1737
1878
|
modifyMember(node, compute) {
|
|
1738
1879
|
const self = this;
|
|
1739
1880
|
return Effect.gen(function* () {
|
|
1740
|
-
const reference = yield* self.getMemberReference(node);
|
|
1741
|
-
if (reference === OptionalShortCircuit ||
|
|
1881
|
+
const reference = yield* self.getMemberReference(node, "write");
|
|
1882
|
+
if (reference === OptionalShortCircuit ||
|
|
1883
|
+
reference instanceof ComputedValue ||
|
|
1884
|
+
reference === undefined ||
|
|
1885
|
+
isOpaqueMemberReference(reference)) {
|
|
1742
1886
|
throw new InterpreterRuntimeError("Only data fields may be assigned.", node);
|
|
1743
1887
|
}
|
|
1744
|
-
if (reference.receiver !== reference.target) {
|
|
1745
|
-
throw new InterpreterRuntimeError(`Cannot create property '${String(reference.key)}' on ${typeof reference.receiver} '${String(reference.receiver)}'.`, node);
|
|
1746
|
-
}
|
|
1747
1888
|
const key = reference.key;
|
|
1748
|
-
const { write, next, result } = yield* compute(self.
|
|
1889
|
+
const { write, next, result } = yield* compute(self.readReferenceValue(reference, key));
|
|
1749
1890
|
if (write)
|
|
1750
|
-
self.assignToReference(reference
|
|
1891
|
+
self.assignToReference(reference, key, next, node);
|
|
1751
1892
|
return result;
|
|
1752
1893
|
});
|
|
1753
1894
|
}
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1895
|
+
readReferenceValue(reference, key) {
|
|
1896
|
+
if (reference.target instanceof Values.URL) {
|
|
1897
|
+
return Reflect.get(reference.target.url, key);
|
|
1898
|
+
}
|
|
1899
|
+
if (reference.target instanceof Values.RegExp)
|
|
1900
|
+
return reference.target.lastIndex;
|
|
1901
|
+
return get(reference.target, key);
|
|
1902
|
+
}
|
|
1903
|
+
assignToReference(reference, key, next, node) {
|
|
1904
|
+
if (reference.target instanceof Values.URL) {
|
|
1905
|
+
const property = key;
|
|
1906
|
+
if (!urlWritableProperties.has(property)) {
|
|
1907
|
+
throw new InterpreterRuntimeError(`URL.${property} is read-only.`, node).as("TypeError");
|
|
1908
|
+
}
|
|
1757
1909
|
try {
|
|
1758
|
-
|
|
1910
|
+
const url = reference.target.url;
|
|
1911
|
+
url[property] = uriArgument(next, `URL.${property} value`);
|
|
1912
|
+
return;
|
|
1759
1913
|
}
|
|
1760
1914
|
catch (error) {
|
|
1761
|
-
|
|
1915
|
+
if (error instanceof InterpreterRuntimeError || error instanceof ToolRuntimeError)
|
|
1916
|
+
throw error;
|
|
1917
|
+
throw new InterpreterRuntimeError(`URL.${property} received an invalid value.`, node).as("TypeError");
|
|
1762
1918
|
}
|
|
1763
|
-
}
|
|
1764
|
-
if (
|
|
1919
|
+
}
|
|
1920
|
+
if (reference.target instanceof Values.RegExp) {
|
|
1921
|
+
reference.target.lastIndex = next;
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
const target = reference.target;
|
|
1925
|
+
rejectCircularInsertion(target, next, target instanceof ProgramArray ? "Array assignment result" : "Object assignment result", node);
|
|
1926
|
+
if (set(target, key, next))
|
|
1765
1927
|
return;
|
|
1766
|
-
if (target instanceof ProgramArray
|
|
1767
|
-
throw
|
|
1768
|
-
throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node);
|
|
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");
|
|
1769
1931
|
}
|
|
1770
1932
|
toPropertyKey(value, node) {
|
|
1771
1933
|
if (typeof value === "string" || typeof value === "number") {
|