@opencode/codemode 2.0.11 → 2.0.13
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 -4
- package/dist/interpreter/callback.d.ts +11 -14
- package/dist/interpreter/callback.js +23 -7
- package/dist/interpreter/errors.d.ts +3 -3
- package/dist/interpreter/errors.js +2 -2
- package/dist/interpreter/execute.d.ts +2 -1
- package/dist/interpreter/extensions.d.ts +2 -1
- package/dist/interpreter/extensions.js +7 -35
- package/dist/interpreter/globals.d.ts +2 -1
- package/dist/interpreter/globals.js +1 -1
- package/dist/interpreter/interpreter.d.ts +9 -20
- package/dist/interpreter/interpreter.js +124 -122
- package/dist/interpreter/intrinsics.d.ts +4 -2
- package/dist/interpreter/intrinsics.js +12 -0
- package/dist/interpreter/model.d.ts +7 -8
- package/dist/interpreter/model.js +3 -3
- package/dist/interpreter/native.d.ts +5 -5
- package/dist/interpreter/objects.d.ts +148 -47
- package/dist/interpreter/objects.js +224 -26
- package/dist/interpreter/promises.d.ts +7 -7
- package/dist/interpreter/promises.js +32 -11
- package/dist/interpreter/references.d.ts +8 -7
- package/dist/interpreter/references.js +10 -46
- package/dist/interpreter/scope.d.ts +5 -4
- package/dist/stdlib/array.d.ts +2 -1
- package/dist/stdlib/array.js +37 -52
- package/dist/stdlib/bytes.js +14 -24
- package/dist/stdlib/collections.js +52 -30
- package/dist/stdlib/console.d.ts +2 -2
- package/dist/stdlib/console.js +6 -50
- package/dist/stdlib/date.js +6 -2
- package/dist/stdlib/headers.js +10 -22
- package/dist/stdlib/iterator.d.ts +1 -1
- package/dist/stdlib/iterator.js +294 -6
- package/dist/stdlib/json.js +3 -5
- package/dist/stdlib/math.js +5 -17
- package/dist/stdlib/number.js +9 -21
- package/dist/stdlib/object.d.ts +3 -4
- package/dist/stdlib/object.js +7 -18
- package/dist/stdlib/regexp.d.ts +3 -3
- package/dist/stdlib/regexp.js +1 -2
- package/dist/stdlib/string.js +12 -20
- package/dist/stdlib/url.d.ts +2 -1
- package/dist/stdlib/url.js +6 -16
- package/dist/stdlib/value.d.ts +0 -3
- package/dist/stdlib/value.js +2 -74
- package/dist/stdlib/web.js +1 -2
- package/dist/tool-runtime.d.ts +2 -0
- package/dist/tool-runtime.js +39 -27
- package/package.json +1 -1
|
@@ -4,15 +4,16 @@ import { ToolReference } from "../tool-runtime.js";
|
|
|
4
4
|
import { AsyncIteratorSymbol, CallSite, GeneratorReturn, IteratorSymbol, OptionalShortCircuit, invalidData, Throw, rangeError, typeError, unsupportedSyntax, } from "./model.js";
|
|
5
5
|
import { checkStringLength } from "./limits.js";
|
|
6
6
|
import { locate, materialize } from "./errors.js";
|
|
7
|
+
import { primitivePrototype } from "./intrinsics.js";
|
|
7
8
|
import { globals } from "./globals.js";
|
|
8
|
-
import { assign, Callable, define, get, has, hasPrototype, keys, Native, parseArrayIndex, Arr,
|
|
9
|
+
import { assign, Callable, define, get, hostCursor, IteratorObj, has, hidden, hasPrototype, keys, Native, parseArrayIndex, Arr, Fn, GeneratorObj, Obj, PromiseObj, record, remove, set, coerceToNumber, coerceToString, } from "./objects.js";
|
|
9
10
|
import { preserveConsumerError } from "./callback.js";
|
|
10
11
|
import { Pending, resolvePromise, resolvePromiseValue } from "./promises.js";
|
|
11
12
|
import { containsOpaqueReference, describeValue, rejectCircularInsertion, typeofValue } from "./references.js";
|
|
12
13
|
import { ScopeStack } from "./scope.js";
|
|
13
14
|
import { constructRegExp } from "../stdlib/regexp.js";
|
|
14
15
|
import { enumerableSource } from "../stdlib/object.js";
|
|
15
|
-
import {
|
|
16
|
+
import { compoundOperators } from "../stdlib/value.js";
|
|
16
17
|
// What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating.
|
|
17
18
|
// Unlabelled break ends this loop; a label the loop does not carry propagates outward.
|
|
18
19
|
const loopExit = (result, labels) => {
|
|
@@ -27,23 +28,37 @@ const loopExit = (result, labels) => {
|
|
|
27
28
|
return result;
|
|
28
29
|
return undefined;
|
|
29
30
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
// Native engines name the callee (`search(...).catch is not a function`), including call chains. Returns
|
|
32
|
+
// undefined when a link cannot be named, so a chain is either named completely or not at all.
|
|
33
|
+
const calleeDescription = (node) => {
|
|
34
|
+
if (node?.type === "Identifier")
|
|
35
|
+
return node.name;
|
|
36
|
+
if (node?.type === "CallExpression") {
|
|
37
|
+
const target = calleeDescription(node.callee);
|
|
38
|
+
return target === undefined ? undefined : `${target}(...)`;
|
|
39
|
+
}
|
|
40
|
+
if (node?.type !== "MemberExpression")
|
|
41
|
+
return undefined;
|
|
42
|
+
const property = node.property;
|
|
43
|
+
const key = !node.computed && property.type === "Identifier"
|
|
44
|
+
? property.name
|
|
45
|
+
: property.type === "Literal" && typeof property.value === "string"
|
|
46
|
+
? property.value
|
|
47
|
+
: undefined;
|
|
48
|
+
if (key === undefined)
|
|
49
|
+
return undefined;
|
|
50
|
+
const object = calleeDescription(node.object);
|
|
51
|
+
return object === undefined ? undefined : `${object}.${key}`;
|
|
45
52
|
};
|
|
46
53
|
// OrdinaryHasInstance: walk the left operand's chain looking for the constructor's `prototype`.
|
|
54
|
+
// acorn types every literal as possibly a BigInt or RegExp; regex literals become RegExp objects before this is asked.
|
|
55
|
+
const literal = (node) => {
|
|
56
|
+
if (typeof node.value === "bigint")
|
|
57
|
+
throw typeError("BigInt literals are not supported.", node);
|
|
58
|
+
if (node.value instanceof RegExp)
|
|
59
|
+
throw unsupportedSyntax("RegExpLiteral", node);
|
|
60
|
+
return node.value;
|
|
61
|
+
};
|
|
47
62
|
const instanceofValue = (lhs, rhs, node) => {
|
|
48
63
|
if (!(rhs instanceof Callable)) {
|
|
49
64
|
throw typeError("The right-hand side of 'instanceof' is not callable.", node);
|
|
@@ -173,6 +188,9 @@ export class Interpreter {
|
|
|
173
188
|
iterate(value) {
|
|
174
189
|
return this.root.iterate(value);
|
|
175
190
|
}
|
|
191
|
+
iterateDirect(value) {
|
|
192
|
+
return this.root.iterateDirect(value);
|
|
193
|
+
}
|
|
176
194
|
/** Runs one host tool: arguments cross as JSON and the result comes back as program values. */
|
|
177
195
|
tool(run, args) {
|
|
178
196
|
const ctx = this;
|
|
@@ -300,7 +318,16 @@ class Frame {
|
|
|
300
318
|
}).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
|
|
301
319
|
}
|
|
302
320
|
createFunction(node, name = node.type === "ArrowFunctionExpression" ? "" : (node.id?.name ?? "")) {
|
|
303
|
-
|
|
321
|
+
const builtins = this.ctx.builtins;
|
|
322
|
+
const fn = new Fn(builtins.Function, name, node.params, node.body, this.scopes.capture(), node.async, node.generator);
|
|
323
|
+
// Each generator function gets its own prototype, so `g() instanceof g` holds as in JS.
|
|
324
|
+
if (node.generator)
|
|
325
|
+
define(fn, "prototype", new Obj(node.async ? builtins.AsyncGenerator : builtins.Generator), hidden);
|
|
326
|
+
// The body of a named function expression sees its own name, read-only.
|
|
327
|
+
if (node.type === "FunctionExpression" && node.id) {
|
|
328
|
+
fn.capturedScopes.push(new Map([[node.id.name, { mutable: false, value: fn, initialized: true }]]));
|
|
329
|
+
}
|
|
330
|
+
return fn;
|
|
304
331
|
}
|
|
305
332
|
// NamedEvaluation: an anonymous function definition takes the name of what it is assigned to.
|
|
306
333
|
evaluateNamed(node, name) {
|
|
@@ -309,11 +336,12 @@ class Frame {
|
|
|
309
336
|
}
|
|
310
337
|
return this.evaluateExpression(node);
|
|
311
338
|
}
|
|
339
|
+
// Repeated `function` declarations and `var` clashes are legal: the last declaration wins.
|
|
312
340
|
hoistFunctions(statements) {
|
|
313
341
|
for (const node of statements) {
|
|
314
342
|
if (node.type !== "FunctionDeclaration")
|
|
315
343
|
continue;
|
|
316
|
-
this.scopes.
|
|
344
|
+
this.scopes.current().set(node.id.name, { mutable: true, value: this.createFunction(node), initialized: true });
|
|
317
345
|
}
|
|
318
346
|
}
|
|
319
347
|
// Hoisted `var` bindings start undefined, or copy a same-named parameter. Function bodies hoist
|
|
@@ -477,10 +505,10 @@ class Frame {
|
|
|
477
505
|
if (declared?.lexical)
|
|
478
506
|
self.predeclarePattern(declared.pattern, declared.mutable, left);
|
|
479
507
|
const right = yield* self.evaluateExpression(node.right);
|
|
480
|
-
const cursor = self.
|
|
508
|
+
const cursor = self.builtinCursor(right);
|
|
481
509
|
const iterator = cursor === undefined ? yield* self.customIterator(right, node, awaiting) : undefined;
|
|
482
510
|
if (iterator === undefined && cursor === undefined) {
|
|
483
|
-
throw invalidData(`${awaiting ? "for await...of" : "for...of"} requires an
|
|
511
|
+
throw invalidData(`${awaiting ? "for await...of" : "for...of"} requires an iterable value, received ${describeValue(right)}.`, node);
|
|
484
512
|
}
|
|
485
513
|
const close = () => iterator
|
|
486
514
|
? self.closeIterator(iterator, node, awaiting)
|
|
@@ -518,10 +546,9 @@ class Frame {
|
|
|
518
546
|
const bodyExit = yield* Effect.exit(evaluateBody(step.value));
|
|
519
547
|
if (!Exit.isSuccess(bodyExit)) {
|
|
520
548
|
// Process interruption must remain prompt; user cleanup cannot extend a timeout.
|
|
521
|
-
if (
|
|
522
|
-
yield* Effect.
|
|
523
|
-
|
|
524
|
-
return yield* Effect.failCause(bodyExit.cause);
|
|
549
|
+
if (Cause.hasInterruptsOnly(bodyExit.cause))
|
|
550
|
+
return yield* Effect.failCause(bodyExit.cause);
|
|
551
|
+
return yield* preserveConsumerError(close(), Effect.failCause(bodyExit.cause));
|
|
525
552
|
}
|
|
526
553
|
const exit = loopExit(bodyExit.value, labels);
|
|
527
554
|
if (exit !== undefined) {
|
|
@@ -550,49 +577,41 @@ class Frame {
|
|
|
550
577
|
});
|
|
551
578
|
}
|
|
552
579
|
iterate(value, node) {
|
|
553
|
-
const cursor = this.
|
|
580
|
+
const cursor = this.builtinCursor(value);
|
|
554
581
|
if (cursor !== undefined)
|
|
555
582
|
return Effect.succeed(cursor);
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
583
|
+
return Effect.map(this.customIterator(value, node, false), (iterator) => iterator === undefined ? undefined : this.customCursor(iterator, node));
|
|
584
|
+
}
|
|
585
|
+
/** GetIteratorDirect: drive an iterator by its own `next`, without asking for `[Symbol.iterator]`. */
|
|
586
|
+
iterateDirect(value, node) {
|
|
587
|
+
if (value instanceof IteratorObj)
|
|
588
|
+
return value.cursor;
|
|
589
|
+
if (!(value instanceof Obj)) {
|
|
590
|
+
throw typeError(`An iterator must be an object, received ${describeValue(value)}.`, node);
|
|
591
|
+
}
|
|
592
|
+
return this.customCursor({
|
|
593
|
+
iterator: value,
|
|
594
|
+
next: this.requireIteratorMethod(get(value, "next"), "Iterator next", node),
|
|
595
|
+
asynchronous: false,
|
|
596
|
+
}, node);
|
|
563
597
|
}
|
|
564
|
-
|
|
565
|
-
const iterator = value instanceof Arr
|
|
566
|
-
? value.items[Symbol.iterator]()
|
|
567
|
-
: typeof value === "string"
|
|
568
|
-
? value[Symbol.iterator]()
|
|
569
|
-
: value instanceof MapObj
|
|
570
|
-
? value.map.entries()
|
|
571
|
-
: value instanceof SetObj
|
|
572
|
-
? value.set.values()
|
|
573
|
-
: value instanceof URLSearchParamsObj
|
|
574
|
-
? value.params.entries()
|
|
575
|
-
: value instanceof HeadersObj
|
|
576
|
-
? value.headers.entries()
|
|
577
|
-
: value instanceof Bytes
|
|
578
|
-
? value.bytes.values()
|
|
579
|
-
: value instanceof IteratorObj
|
|
580
|
-
? value.iterator
|
|
581
|
-
: undefined;
|
|
582
|
-
if (iterator === undefined)
|
|
583
|
-
return undefined;
|
|
584
|
-
const proto = this.ctx.builtins.Array;
|
|
598
|
+
customCursor(iterator, node) {
|
|
585
599
|
return {
|
|
586
|
-
next:
|
|
587
|
-
|
|
588
|
-
return {
|
|
589
|
-
done: Boolean(step.done),
|
|
590
|
-
value: Array.isArray(step.value) ? new Arr(proto, step.value) : step.value,
|
|
591
|
-
};
|
|
592
|
-
}),
|
|
593
|
-
close: Effect.void,
|
|
600
|
+
next: this.nextIteratorResult(iterator, node, false),
|
|
601
|
+
close: Effect.suspend(() => this.closeIterator(iterator, node, false)),
|
|
594
602
|
};
|
|
595
603
|
}
|
|
604
|
+
builtinCursor(value) {
|
|
605
|
+
// Natives build their cursors without knowing R, like `lift` in native.ts.
|
|
606
|
+
if (value instanceof IteratorObj)
|
|
607
|
+
return value.cursor;
|
|
608
|
+
const iterator = typeof value === "string"
|
|
609
|
+
? value[Symbol.iterator]()
|
|
610
|
+
: value instanceof Obj
|
|
611
|
+
? value.iterator(this.ctx.builtins)
|
|
612
|
+
: undefined;
|
|
613
|
+
return iterator === undefined ? undefined : hostCursor(iterator);
|
|
614
|
+
}
|
|
596
615
|
customIterator(value, node, allowAsync = true) {
|
|
597
616
|
if (!(value instanceof Obj))
|
|
598
617
|
return Effect.undefined;
|
|
@@ -934,7 +953,7 @@ class Frame {
|
|
|
934
953
|
return;
|
|
935
954
|
}
|
|
936
955
|
const consumed = consume(element, step.done ? undefined : step.value, pattern);
|
|
937
|
-
yield* step.done ? consumed : preserveConsumerError(cursor, consumed);
|
|
956
|
+
yield* step.done ? consumed : preserveConsumerError(cursor.close, consumed);
|
|
938
957
|
}
|
|
939
958
|
if (!done)
|
|
940
959
|
yield* cursor.close;
|
|
@@ -946,7 +965,7 @@ class Frame {
|
|
|
946
965
|
}
|
|
947
966
|
const keyNode = property.key;
|
|
948
967
|
if (property.computed) {
|
|
949
|
-
return Effect.map(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value
|
|
968
|
+
return Effect.map(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value));
|
|
950
969
|
}
|
|
951
970
|
if (keyNode.type === "Identifier")
|
|
952
971
|
return Effect.succeed(keyNode.name);
|
|
@@ -960,9 +979,7 @@ class Frame {
|
|
|
960
979
|
const regex = node.regex;
|
|
961
980
|
if (regex)
|
|
962
981
|
return Effect.sync(() => constructRegExp(this.ctx.builtins, [regex.pattern, regex.flags]));
|
|
963
|
-
|
|
964
|
-
throw typeError("BigInt literals are not supported.", node);
|
|
965
|
-
return Effect.succeed(node.value);
|
|
982
|
+
return Effect.succeed(literal(node));
|
|
966
983
|
}
|
|
967
984
|
case "Identifier":
|
|
968
985
|
return Effect.sync(() => this.scopes.get(node.name, node));
|
|
@@ -1025,7 +1042,7 @@ class Frame {
|
|
|
1025
1042
|
// `new` itself is supported, so a non-constructible callee is a TypeError like JS rather than
|
|
1026
1043
|
// unsupported syntax. Built-ins like Number are real constructors in JS, so do not claim
|
|
1027
1044
|
// otherwise; say `new` is unsupported for them and point at the plain call.
|
|
1028
|
-
const name = calleeDescription(node.callee);
|
|
1045
|
+
const name = calleeDescription(node.callee) ?? "The called value";
|
|
1029
1046
|
const message = callee instanceof Fn
|
|
1030
1047
|
? `${name} cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.`
|
|
1031
1048
|
: callee instanceof Native
|
|
@@ -1062,14 +1079,9 @@ class Frame {
|
|
|
1062
1079
|
if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
|
|
1063
1080
|
throw invalidData("Binary operators require data values.", node);
|
|
1064
1081
|
}
|
|
1065
|
-
//
|
|
1066
|
-
|
|
1067
|
-
const coerceOperand = (operand) =>
|
|
1068
|
-
if (operand instanceof DateObj) {
|
|
1069
|
-
return operator === "+" || operator === "==" || operator === "!=" ? coerceToString(operand) : operand.time;
|
|
1070
|
-
}
|
|
1071
|
-
return operand !== null && typeof operand === "object" ? coerceToString(operand) : operand;
|
|
1072
|
-
};
|
|
1082
|
+
// Addition and loose equality use the default hint; every other operator asks for a number.
|
|
1083
|
+
const hint = operator === "+" || operator === "==" || operator === "!=" ? "default" : "number";
|
|
1084
|
+
const coerceOperand = (operand) => (operand instanceof Obj ? operand.toPrimitive(hint) : operand);
|
|
1073
1085
|
const bothObjects = lhs !== null && typeof lhs === "object" && rhs !== null && typeof rhs === "object";
|
|
1074
1086
|
const l = coerceOperand(lhs);
|
|
1075
1087
|
const r = coerceOperand(rhs);
|
|
@@ -1154,11 +1166,7 @@ class Frame {
|
|
|
1154
1166
|
if (containsOpaqueReference(value)) {
|
|
1155
1167
|
throw invalidData("Unary operators require data values.", node);
|
|
1156
1168
|
}
|
|
1157
|
-
const operand = value instanceof
|
|
1158
|
-
? value.time
|
|
1159
|
-
: value !== null && typeof value === "object"
|
|
1160
|
-
? coerceToString(value)
|
|
1161
|
-
: value;
|
|
1169
|
+
const operand = value instanceof Obj ? value.toPrimitive("number") : value;
|
|
1162
1170
|
let result;
|
|
1163
1171
|
switch (operator) {
|
|
1164
1172
|
case "+":
|
|
@@ -1312,7 +1320,7 @@ class Frame {
|
|
|
1312
1320
|
if (callable instanceof Native) {
|
|
1313
1321
|
return yield* self.native(() => callable.call(thisValue, args), node);
|
|
1314
1322
|
}
|
|
1315
|
-
throw typeError(`${calleeDescription(callee)} is not a function.`, callee ?? node);
|
|
1323
|
+
throw typeError(`${calleeDescription(callee) ?? "The called value"} is not a function.`, callee ?? node);
|
|
1316
1324
|
});
|
|
1317
1325
|
}
|
|
1318
1326
|
// Built-ins throw without a location, synchronously or inside their Effect; the call site supplies it.
|
|
@@ -1351,14 +1359,14 @@ class Frame {
|
|
|
1351
1359
|
if (depth > MAX_CALL_DEPTH)
|
|
1352
1360
|
throw rangeError("Maximum call stack size exceeded", node);
|
|
1353
1361
|
const invocation = new Frame(this.ctx, new ScopeStack([...fn.capturedScopes, new Map()]), depth);
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
for (const
|
|
1358
|
-
|
|
1359
|
-
paramScope.set(name, { mutable: true, value: undefined, initialized: false });
|
|
1360
|
-
}
|
|
1362
|
+
// Seed all parameters first so defaults cannot fall through to same-named outer bindings.
|
|
1363
|
+
const paramScope = invocation.scopes.current();
|
|
1364
|
+
for (const parameter of fn.parameters) {
|
|
1365
|
+
for (const name of collectPatternNames(parameter)) {
|
|
1366
|
+
paramScope.set(name, { mutable: true, value: undefined, initialized: false });
|
|
1361
1367
|
}
|
|
1368
|
+
}
|
|
1369
|
+
const bind = Effect.gen(function* () {
|
|
1362
1370
|
for (const [index, parameter] of fn.parameters.entries()) {
|
|
1363
1371
|
if (parameter.type === "RestElement") {
|
|
1364
1372
|
yield* invocation.declarePattern(parameter.argument, new Arr(self.ctx.builtins.Array, args.slice(index)), true, parameter, true);
|
|
@@ -1366,6 +1374,8 @@ class Frame {
|
|
|
1366
1374
|
}
|
|
1367
1375
|
yield* invocation.declarePattern(parameter, args[index], true, parameter, true);
|
|
1368
1376
|
}
|
|
1377
|
+
});
|
|
1378
|
+
const body = Effect.gen(function* () {
|
|
1369
1379
|
if (fn.body.type === "BlockStatement") {
|
|
1370
1380
|
invocation.scopes.push();
|
|
1371
1381
|
invocation.hoistVars(fn.body.body, paramScope);
|
|
@@ -1374,14 +1384,17 @@ class Frame {
|
|
|
1374
1384
|
}
|
|
1375
1385
|
return yield* invocation.evaluateExpression(fn.body);
|
|
1376
1386
|
});
|
|
1387
|
+
// Generators bind parameters at the call and defer only the body to the first `next()`, as in JS.
|
|
1377
1388
|
if (fn.generator)
|
|
1378
|
-
return Effect.
|
|
1389
|
+
return Effect.map(bind, () => this.createGenerator(invocation, body, fn));
|
|
1390
|
+
const run = Effect.andThen(bind, body);
|
|
1379
1391
|
if (!fn.async)
|
|
1380
1392
|
return run;
|
|
1381
1393
|
return this.ctx.pending.createWithSelf((self) => Effect.flatMap(run, (value) => resolvePromiseValue(invocation.ctx, value, self)));
|
|
1382
1394
|
});
|
|
1383
1395
|
}
|
|
1384
|
-
createGenerator(invocation, run,
|
|
1396
|
+
createGenerator(invocation, run, fn) {
|
|
1397
|
+
const asynchronous = fn.async;
|
|
1385
1398
|
const state = { started: false, completed: false, draining: false, pending: [], pendingIndex: 0 };
|
|
1386
1399
|
invocation.generatorState = state;
|
|
1387
1400
|
invocation.generatorAsync = asynchronous;
|
|
@@ -1440,8 +1453,8 @@ class Frame {
|
|
|
1440
1453
|
}
|
|
1441
1454
|
return Deferred.await(request.response);
|
|
1442
1455
|
};
|
|
1443
|
-
const
|
|
1444
|
-
return
|
|
1456
|
+
const proto = get(fn, "prototype");
|
|
1457
|
+
return new GeneratorObj(proto instanceof Obj ? proto : builtins.Generator, asynchronous, request);
|
|
1445
1458
|
}
|
|
1446
1459
|
completeGeneratorRequests(state, asynchronous) {
|
|
1447
1460
|
const self = this;
|
|
@@ -1517,16 +1530,8 @@ class Frame {
|
|
|
1517
1530
|
delegateYield(value, node) {
|
|
1518
1531
|
const self = this;
|
|
1519
1532
|
return Effect.gen(function* () {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
value instanceof MapObj ||
|
|
1523
|
-
value instanceof SetObj ||
|
|
1524
|
-
value instanceof URLSearchParamsObj ||
|
|
1525
|
-
value instanceof HeadersObj ||
|
|
1526
|
-
value instanceof Bytes) {
|
|
1527
|
-
const cursor = yield* self.iterate(value, node);
|
|
1528
|
-
if (!cursor)
|
|
1529
|
-
throw typeError("Built-in iterator is unavailable.", node);
|
|
1533
|
+
const cursor = self.builtinCursor(value);
|
|
1534
|
+
if (cursor !== undefined) {
|
|
1530
1535
|
while (true) {
|
|
1531
1536
|
const step = yield* cursor.next;
|
|
1532
1537
|
if (step.done)
|
|
@@ -1603,13 +1608,13 @@ class Frame {
|
|
|
1603
1608
|
const keyNode = property.key;
|
|
1604
1609
|
let key;
|
|
1605
1610
|
if (property.computed) {
|
|
1606
|
-
key = self.toPropertyKey(yield* self.evaluateExpression(keyNode)
|
|
1611
|
+
key = self.toPropertyKey(yield* self.evaluateExpression(keyNode));
|
|
1607
1612
|
}
|
|
1608
1613
|
else if (keyNode.type === "Identifier") {
|
|
1609
1614
|
key = keyNode.name;
|
|
1610
1615
|
}
|
|
1611
1616
|
else if (keyNode.type === "Literal") {
|
|
1612
|
-
key = self.toPropertyKey(keyNode
|
|
1617
|
+
key = self.toPropertyKey(literal(keyNode));
|
|
1613
1618
|
}
|
|
1614
1619
|
else {
|
|
1615
1620
|
throw typeError("Unsupported object property key shape.", keyNode);
|
|
@@ -1700,10 +1705,10 @@ class Frame {
|
|
|
1700
1705
|
if ((objectValue === null || objectValue === undefined) && node.optional)
|
|
1701
1706
|
return OptionalShortCircuit;
|
|
1702
1707
|
const key = node.computed
|
|
1703
|
-
? self.toPropertyKey(yield* self.evaluateExpression(propertyNode)
|
|
1708
|
+
? self.toPropertyKey(yield* self.evaluateExpression(propertyNode))
|
|
1704
1709
|
: propertyNode.type === "Identifier"
|
|
1705
1710
|
? propertyNode.name
|
|
1706
|
-
: self.toPropertyKey(yield* self.evaluateExpression(propertyNode)
|
|
1711
|
+
: self.toPropertyKey(yield* self.evaluateExpression(propertyNode));
|
|
1707
1712
|
if (objectValue instanceof ToolReference) {
|
|
1708
1713
|
if (typeof key !== "string") {
|
|
1709
1714
|
throw typeError("Tool paths must use string property names.", propertyNode);
|
|
@@ -1712,20 +1717,17 @@ class Frame {
|
|
|
1712
1717
|
}
|
|
1713
1718
|
if (objectValue instanceof Obj)
|
|
1714
1719
|
return { target: objectValue, key, receiver: objectValue };
|
|
1715
|
-
//
|
|
1716
|
-
const builtins = self.ctx.builtins;
|
|
1720
|
+
// Strings own length and indexes; every other primitive property reads through the wrapper prototype.
|
|
1717
1721
|
if (typeof objectValue === "string") {
|
|
1718
1722
|
if (key === "length")
|
|
1719
1723
|
return { value: objectValue.length };
|
|
1720
1724
|
const index = typeof key === "symbol" ? undefined : parseArrayIndex(key);
|
|
1721
1725
|
if (index !== undefined)
|
|
1722
1726
|
return { value: objectValue[index] };
|
|
1723
|
-
return { target: builtins.String, key, receiver: objectValue };
|
|
1724
1727
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
return { target: builtins.Boolean, key, receiver: objectValue };
|
|
1728
|
+
const proto = primitivePrototype(self.ctx.builtins, objectValue);
|
|
1729
|
+
if (proto !== undefined)
|
|
1730
|
+
return { target: proto, key, receiver: objectValue };
|
|
1729
1731
|
if (objectValue === null || objectValue === undefined) {
|
|
1730
1732
|
throw typeError(`Cannot read properties of ${objectValue} (reading '${String(key)}').`, objectNode);
|
|
1731
1733
|
}
|
|
@@ -1764,9 +1766,11 @@ class Frame {
|
|
|
1764
1766
|
}
|
|
1765
1767
|
evaluateDeleteExpression(argument) {
|
|
1766
1768
|
const target = argument.type === "ChainExpression" ? argument.expression : argument;
|
|
1767
|
-
if (target.type
|
|
1769
|
+
if (target.type === "Identifier")
|
|
1768
1770
|
throw typeError("Only data fields may be deleted.", argument);
|
|
1769
|
-
|
|
1771
|
+
// `delete <non-reference>` evaluates the operand and is true, as in JS.
|
|
1772
|
+
if (target.type !== "MemberExpression")
|
|
1773
|
+
return Effect.map(this.evaluateExpression(target), () => true);
|
|
1770
1774
|
return Effect.map(this.getMemberReference(target), (reference) => {
|
|
1771
1775
|
if (reference === OptionalShortCircuit)
|
|
1772
1776
|
return true;
|
|
@@ -1812,12 +1816,10 @@ class Frame {
|
|
|
1812
1816
|
throw rangeError("Invalid array length", node);
|
|
1813
1817
|
throw typeError(`Cannot assign to read only property '${String(key)}'.`, node);
|
|
1814
1818
|
}
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
}
|
|
1819
|
-
if (value === AsyncIteratorSymbol || value === IteratorSymbol)
|
|
1819
|
+
// ToPropertyKey: anything else becomes its string form, so `counts[row.category]` works when the field is null.
|
|
1820
|
+
toPropertyKey(value) {
|
|
1821
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "symbol")
|
|
1820
1822
|
return value;
|
|
1821
|
-
|
|
1823
|
+
return coerceToString(value);
|
|
1822
1824
|
}
|
|
1823
1825
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { ErrorObj, Obj } from "./objects.js";
|
|
1
|
+
import { ErrorObj, Obj, type Value } from "./objects.js";
|
|
2
2
|
export declare const errorTypes: readonly ["Error", "TypeError", "RangeError", "SyntaxError", "ReferenceError", "EvalError", "URIError", "AggregateError"];
|
|
3
3
|
export type ErrorType = (typeof errorTypes)[number];
|
|
4
4
|
export declare const isErrorType: (name: string) => name is ErrorType;
|
|
5
|
-
declare const builtins: readonly ["Object", "Function", "Array", "String", "Number", "Boolean", "Date", "RegExp", "Map", "Set", "URL", "URLSearchParams", "Headers", "Uint8Array", "TextEncoder", "TextDecoder", "Promise", "Iterator", "AsyncIterator", "Generator", "AsyncGenerator"];
|
|
5
|
+
declare const builtins: readonly ["Object", "Function", "Array", "String", "Number", "Boolean", "Date", "RegExp", "Map", "Set", "URL", "URLSearchParams", "Headers", "Uint8Array", "TextEncoder", "TextDecoder", "Promise", "Iterator", "IteratorHelper", "AsyncIterator", "Generator", "AsyncGenerator"];
|
|
6
6
|
/**
|
|
7
7
|
* The built-in prototype objects of one runtime, allocated empty in dependency order. The globals populate them
|
|
8
8
|
* and attach their constructors when the runtime is built.
|
|
9
9
|
*/
|
|
10
10
|
export type Builtins = Readonly<Record<(typeof builtins)[number] | ErrorType, Obj>>;
|
|
11
11
|
export declare const createErrorValue: (prototype: Obj, message: string | undefined) => ErrorObj;
|
|
12
|
+
/** The prototype a primitive reads its methods from without being boxed; none for null, undefined, and symbols. */
|
|
13
|
+
export declare const primitivePrototype: (builtins: Builtins, value: Value) => Obj | undefined;
|
|
12
14
|
export declare const createBuiltins: () => Builtins;
|
|
13
15
|
export {};
|
|
@@ -30,6 +30,7 @@ const builtins = [
|
|
|
30
30
|
"TextDecoder",
|
|
31
31
|
"Promise",
|
|
32
32
|
"Iterator",
|
|
33
|
+
"IteratorHelper",
|
|
33
34
|
"AsyncIterator",
|
|
34
35
|
"Generator",
|
|
35
36
|
"AsyncGenerator",
|
|
@@ -40,6 +41,16 @@ export const createErrorValue = (prototype, message) => {
|
|
|
40
41
|
define(value, "message", message, hidden);
|
|
41
42
|
return value;
|
|
42
43
|
};
|
|
44
|
+
/** The prototype a primitive reads its methods from without being boxed; none for null, undefined, and symbols. */
|
|
45
|
+
export const primitivePrototype = (builtins, value) => {
|
|
46
|
+
if (typeof value === "string")
|
|
47
|
+
return builtins.String;
|
|
48
|
+
if (typeof value === "number")
|
|
49
|
+
return builtins.Number;
|
|
50
|
+
if (typeof value === "boolean")
|
|
51
|
+
return builtins.Boolean;
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
43
54
|
export const createBuiltins = () => {
|
|
44
55
|
const object = new Obj(null);
|
|
45
56
|
// Function.prototype is itself callable and returns undefined.
|
|
@@ -75,6 +86,7 @@ export const createBuiltins = () => {
|
|
|
75
86
|
TextDecoder: plain(),
|
|
76
87
|
Promise: plain(),
|
|
77
88
|
Iterator: iterator,
|
|
89
|
+
IteratorHelper: new Obj(iterator),
|
|
78
90
|
AsyncIterator: asyncIterator,
|
|
79
91
|
Generator: new Obj(iterator),
|
|
80
92
|
AsyncGenerator: new Obj(asyncIterator),
|
|
@@ -2,7 +2,7 @@ import type { Node } from "acorn";
|
|
|
2
2
|
import { Context } from "effect";
|
|
3
3
|
import type { ErrorType } from "./intrinsics.js";
|
|
4
4
|
import type { DiagnosticKind } from "../codemode.js";
|
|
5
|
-
import type { ErrorObj } from "./objects.js";
|
|
5
|
+
import type { ErrorObj, Value } from "./objects.js";
|
|
6
6
|
/** Any parsed node; the interpreter narrows on `type` and reads `loc` for diagnostics. */
|
|
7
7
|
export type AstNode = Node;
|
|
8
8
|
/** The program call a built-in is running under: where to locate failures born inside it, and how deep the stack is there. */
|
|
@@ -12,14 +12,14 @@ export declare const CallSite: Context.Reference<{
|
|
|
12
12
|
}>;
|
|
13
13
|
export type Binding = {
|
|
14
14
|
mutable: boolean;
|
|
15
|
-
value:
|
|
15
|
+
value: Value;
|
|
16
16
|
initialized?: boolean;
|
|
17
17
|
};
|
|
18
18
|
export type StatementResult = {
|
|
19
19
|
kind: "none";
|
|
20
20
|
} | {
|
|
21
21
|
kind: "return";
|
|
22
|
-
value:
|
|
22
|
+
value: Value;
|
|
23
23
|
} | {
|
|
24
24
|
kind: "break";
|
|
25
25
|
label?: string;
|
|
@@ -32,12 +32,12 @@ export declare const AsyncIteratorSymbol: unique symbol;
|
|
|
32
32
|
export declare const IteratorSymbol: unique symbol;
|
|
33
33
|
export declare const IteratorSymbols: readonly [typeof AsyncIteratorSymbol, typeof IteratorSymbol];
|
|
34
34
|
export declare class Throw {
|
|
35
|
-
readonly value:
|
|
36
|
-
constructor(value:
|
|
35
|
+
readonly value: Value;
|
|
36
|
+
constructor(value: Value);
|
|
37
37
|
}
|
|
38
38
|
export declare class GeneratorReturn {
|
|
39
|
-
readonly value:
|
|
40
|
-
constructor(value:
|
|
39
|
+
readonly value: Value;
|
|
40
|
+
constructor(value: Value);
|
|
41
41
|
}
|
|
42
42
|
export declare const OptionalShortCircuit: unique symbol;
|
|
43
43
|
/**
|
|
@@ -64,7 +64,6 @@ export declare const syntaxError: (message: string, node?: AstNode) => PendingTh
|
|
|
64
64
|
export declare const uriError: (message: string, node?: AstNode) => PendingThrow;
|
|
65
65
|
export declare const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes, this, getters/setters, tagged templates, BigInt, and custom Symbols. Use plain functions and data objects instead.";
|
|
66
66
|
export declare const unsupportedSyntax: (kind: string, node: AstNode) => PendingThrow;
|
|
67
|
-
export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
|
|
68
67
|
export declare const sourceLocation: (node: AstNode) => {
|
|
69
68
|
readonly line: number;
|
|
70
69
|
readonly column: number;
|
|
@@ -51,10 +51,10 @@ export const uriError = failure("URIError");
|
|
|
51
51
|
// Orient the agent rather than enumerate JavaScript; interpreter-support.md is the full matrix.
|
|
52
52
|
export const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes, this, getters/setters, tagged templates, BigInt, and custom Symbols. Use plain functions and data objects instead.";
|
|
53
53
|
export const unsupportedSyntax = (kind, node) => new PendingThrow("SyntaxError", `Syntax '${kind}' is not supported. ${supportedSyntaxMessage}`, node, "UnsupportedSyntax", [supportedSyntaxMessage]);
|
|
54
|
-
|
|
54
|
+
// Acorn lines are 1-based and its columns are 0-based. Diagnostics use 1-based columns of the submitted source.
|
|
55
55
|
export const sourceLocation = (node) => ({
|
|
56
|
-
line:
|
|
57
|
-
column:
|
|
56
|
+
line: node.loc?.start.line ?? 1,
|
|
57
|
+
column: (node.loc?.start.column ?? 0) + 1,
|
|
58
58
|
});
|
|
59
59
|
export const formatLocation = (node) => {
|
|
60
60
|
if (!node?.loc)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import type { Builtins } from "./intrinsics.js";
|
|
3
|
-
import { type Callable, Native, type NativeOptions, Obj } from "./objects.js";
|
|
4
|
-
/** A native function body: a
|
|
5
|
-
export type Impl = (thisValue:
|
|
3
|
+
import { type Callable, Native, type NativeOptions, Obj, type Value } from "./objects.js";
|
|
4
|
+
/** A native function body: a value, a thrown `PendingThrow`, or an Effect of a value. */
|
|
5
|
+
export type Impl = (thisValue: Value, args: Array<Value>) => Value | Effect.Effect<Value, unknown, unknown>;
|
|
6
6
|
export declare const native: <R>(builtins: Builtins, options: NativeOptions<R>) => Native<R>;
|
|
7
7
|
export declare const fn: <R>(builtins: Builtins, name: string, length: number, impl: Impl) => Native<R>;
|
|
8
8
|
export type Method = readonly [name: string, length: number, impl: Impl];
|
|
9
9
|
export declare const methods: (builtins: Builtins, target: Obj, table: ReadonlyArray<Method>) => void;
|
|
10
|
-
export declare const constants: (target: Obj, table: Record<string,
|
|
10
|
+
export declare const constants: (target: Obj, table: Record<string, Value>) => void;
|
|
11
11
|
/** A constructor wired to its prototype: `C.prototype === proto` and `proto.constructor === C`. */
|
|
12
12
|
export declare const constructor: <R>(builtins: Builtins, proto: Obj, options: NativeOptions<R>) => Native<R>;
|
|
13
13
|
/** The `call` of a constructor that JS requires to be invoked with `new`. */
|
|
@@ -15,4 +15,4 @@ export declare const requiresNew: (name: string) => () => Effect.Effect<never, u
|
|
|
15
15
|
/** The instance prototype for `new` via `newTarget.prototype`, falling back to the built-in's own. */
|
|
16
16
|
export declare const prototypeFrom: (newTarget: Callable, fallback: Obj) => Obj;
|
|
17
17
|
/** Narrows a method receiver to the built-in it belongs to, or throws the TypeError JS would. */
|
|
18
|
-
export declare const receiver: <T extends Obj>(cls: abstract new (...args: never) => T, thisValue:
|
|
18
|
+
export declare const receiver: <T extends Obj>(cls: abstract new (...args: never) => T, thisValue: Value, method: string) => T;
|