@opencode/codemode 0.0.0-dev-19437 → 0.0.0-dev-19439
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/interpreter/runtime.js +90 -18
- package/package.json +1 -1
|
@@ -107,6 +107,55 @@ const collectPatternNames = (pattern, out = []) => {
|
|
|
107
107
|
}
|
|
108
108
|
return out;
|
|
109
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
|
+
};
|
|
110
159
|
const loopDeclaration = (left, statement) => {
|
|
111
160
|
if (left.type !== "VariableDeclaration")
|
|
112
161
|
return undefined;
|
|
@@ -183,6 +232,7 @@ class Frame {
|
|
|
183
232
|
return Effect.gen(function* () {
|
|
184
233
|
self.predeclareLexical(program.body);
|
|
185
234
|
self.hoistFunctions(program.body);
|
|
235
|
+
self.hoistVars(program.body);
|
|
186
236
|
let value = undefined;
|
|
187
237
|
for (const [index, statement] of program.body.entries()) {
|
|
188
238
|
if (index === program.body.length - 1 && statement.type === "ExpressionStatement") {
|
|
@@ -290,6 +340,19 @@ class Frame {
|
|
|
290
340
|
this.scopes.declare(node.id.name, this.createFunction(node), true, node);
|
|
291
341
|
}
|
|
292
342
|
}
|
|
343
|
+
// Hoisted `var` bindings start undefined, or copy a same-named parameter. Function bodies hoist
|
|
344
|
+
// into their own scope above the parameters so closures in parameter defaults keep seeing outer names.
|
|
345
|
+
hoistVars(statements, parameters) {
|
|
346
|
+
const names = varNames.get(statements) ??
|
|
347
|
+
statements.reduce((out, statement) => collectVarNames(statement, out), []);
|
|
348
|
+
varNames.set(statements, names);
|
|
349
|
+
const scope = this.scopes.current();
|
|
350
|
+
for (const name of names) {
|
|
351
|
+
if (scope.has(name))
|
|
352
|
+
continue;
|
|
353
|
+
scope.set(name, { mutable: true, value: parameters?.get(name)?.value, initialized: true });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
293
356
|
predeclareLexical(statements) {
|
|
294
357
|
for (const statement of statements) {
|
|
295
358
|
if (statement.type !== "VariableDeclaration")
|
|
@@ -325,7 +388,9 @@ class Frame {
|
|
|
325
388
|
self.scopes.push();
|
|
326
389
|
return yield* Effect.gen(function* () {
|
|
327
390
|
const cases = node.cases;
|
|
328
|
-
|
|
391
|
+
const statements = cases.flatMap((branch) => branch.consequent);
|
|
392
|
+
self.predeclareLexical(statements);
|
|
393
|
+
self.hoistFunctions(statements);
|
|
329
394
|
let defaultIndex;
|
|
330
395
|
let selected;
|
|
331
396
|
for (const [index, branch] of cases.entries()) {
|
|
@@ -451,18 +516,20 @@ class Frame {
|
|
|
451
516
|
}
|
|
452
517
|
const assignment = left.type === "VariableDeclaration" ? undefined : left;
|
|
453
518
|
const evaluateBody = (value) => Effect.gen(function* () {
|
|
454
|
-
if (declared) {
|
|
519
|
+
if (declared?.lexical) {
|
|
455
520
|
self.scopes.push();
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
521
|
+
self.predeclarePattern(declared.pattern, declared.mutable, left);
|
|
522
|
+
yield* self.declarePattern(declared.pattern, value, declared.mutable, left, true);
|
|
523
|
+
}
|
|
524
|
+
else if (declared) {
|
|
525
|
+
yield* self.assignPattern(declared.pattern, value, left);
|
|
459
526
|
}
|
|
460
527
|
else if (assignment) {
|
|
461
528
|
yield* self.assignPattern(assignment, value, left);
|
|
462
529
|
}
|
|
463
530
|
return yield* self.evaluateStatement(node.body);
|
|
464
531
|
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
465
|
-
if (declared)
|
|
532
|
+
if (declared?.lexical)
|
|
466
533
|
self.scopes.pop();
|
|
467
534
|
})));
|
|
468
535
|
while (true) {
|
|
@@ -662,18 +729,20 @@ class Frame {
|
|
|
662
729
|
const assignmentName = left.type === "Identifier" ? left.name : undefined;
|
|
663
730
|
for (const key of keys) {
|
|
664
731
|
const result = yield* Effect.gen(function* () {
|
|
665
|
-
if (declared) {
|
|
732
|
+
if (declared?.lexical) {
|
|
666
733
|
self.scopes.push();
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
734
|
+
self.predeclarePattern(declared.pattern, declared.mutable, left);
|
|
735
|
+
yield* self.declarePattern(declared.pattern, key, declared.mutable, left, true);
|
|
736
|
+
}
|
|
737
|
+
else if (declared) {
|
|
738
|
+
yield* self.assignPattern(declared.pattern, key, left);
|
|
670
739
|
}
|
|
671
740
|
else if (assignmentName) {
|
|
672
741
|
self.scopes.set(assignmentName, key, left);
|
|
673
742
|
}
|
|
674
743
|
return yield* self.evaluateStatement(node.body);
|
|
675
744
|
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
676
|
-
if (declared)
|
|
745
|
+
if (declared?.lexical)
|
|
677
746
|
self.scopes.pop();
|
|
678
747
|
})));
|
|
679
748
|
const exit = loopExit(result, labels);
|
|
@@ -759,8 +828,14 @@ class Frame {
|
|
|
759
828
|
throw new InterpreterRuntimeError("Unsupported variable declaration shape.", declaration);
|
|
760
829
|
}
|
|
761
830
|
const init = declaration.init;
|
|
831
|
+
// `var x` alone is a no-op: the binding was hoisted on function entry.
|
|
832
|
+
if (kind === "var") {
|
|
833
|
+
if (init)
|
|
834
|
+
yield* self.assignPattern(declaration.id, yield* self.evaluateExpression(init), declaration);
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
762
837
|
const value = init ? yield* self.evaluateExpression(init) : undefined;
|
|
763
|
-
yield* self.declarePattern(declaration.id, value, kind !== "const", declaration,
|
|
838
|
+
yield* self.declarePattern(declaration.id, value, kind !== "const", declaration, true);
|
|
764
839
|
}
|
|
765
840
|
});
|
|
766
841
|
}
|
|
@@ -1318,6 +1393,8 @@ class Frame {
|
|
|
1318
1393
|
yield* invocation.declarePattern(parameter, args[index], true, parameter, true);
|
|
1319
1394
|
}
|
|
1320
1395
|
if (fn.body.type === "BlockStatement") {
|
|
1396
|
+
invocation.scopes.push();
|
|
1397
|
+
invocation.hoistVars(fn.body.body, paramScope);
|
|
1321
1398
|
const result = yield* invocation.evaluateStatement(fn.body);
|
|
1322
1399
|
return result.kind === "return" ? result.value : undefined;
|
|
1323
1400
|
}
|
|
@@ -1327,12 +1404,7 @@ class Frame {
|
|
|
1327
1404
|
return Effect.succeed(this.createGenerator(invocation, run, fn.async));
|
|
1328
1405
|
if (!fn.async)
|
|
1329
1406
|
return run;
|
|
1330
|
-
|
|
1331
|
-
const box = {};
|
|
1332
|
-
return Effect.map(this.createPromise(Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runtime.runner, value, fn.body, box))), (promise) => {
|
|
1333
|
-
box.promise = promise;
|
|
1334
|
-
return promise;
|
|
1335
|
-
});
|
|
1407
|
+
return this.runtime.promises.createWithSelf((self) => Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runtime.runner, value, fn.body, self)));
|
|
1336
1408
|
}
|
|
1337
1409
|
createGenerator(invocation, run, asynchronous) {
|
|
1338
1410
|
const state = { started: false, completed: false, draining: false, pending: [], pendingIndex: 0 };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@opencode/codemode",
|
|
4
|
-
"version": "0.0.0-dev-
|
|
4
|
+
"version": "0.0.0-dev-19439",
|
|
5
5
|
"description": "Effect-native confined code execution over schema-described tools",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|