@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.
@@ -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
- self.predeclareLexical(cases.flatMap((branch) => branch.consequent));
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
- if (declared.lexical)
457
- self.predeclarePattern(declared.pattern, declared.mutable, left);
458
- yield* self.declarePattern(declared.pattern, value, declared.mutable, left, declared.lexical);
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
- if (declared.lexical)
668
- self.predeclarePattern(declared.pattern, declared.mutable, left);
669
- yield* self.declarePattern(declared.pattern, key, declared.mutable, left, declared.lexical);
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, kind !== "var");
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
- // The initial yield assigns the promise before the body can self-resolve.
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-19437",
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",