@opencode/codemode 0.0.0-dev-19437 → 0.0.0-dev-19438

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,53 @@ 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
+ const collectVarNames = (node, out = []) => {
112
+ if (!node)
113
+ return out;
114
+ switch (node.type) {
115
+ case "VariableDeclaration":
116
+ if (node.kind === "var")
117
+ for (const declaration of node.declarations)
118
+ collectPatternNames(declaration.id, out);
119
+ break;
120
+ case "BlockStatement":
121
+ for (const statement of node.body)
122
+ collectVarNames(statement, out);
123
+ break;
124
+ case "IfStatement":
125
+ collectVarNames(node.consequent, out);
126
+ collectVarNames(node.alternate, out);
127
+ break;
128
+ case "ForStatement":
129
+ if (node.init?.type === "VariableDeclaration")
130
+ collectVarNames(node.init, out);
131
+ collectVarNames(node.body, out);
132
+ break;
133
+ case "ForInStatement":
134
+ case "ForOfStatement":
135
+ if (node.left.type === "VariableDeclaration")
136
+ collectVarNames(node.left, out);
137
+ collectVarNames(node.body, out);
138
+ break;
139
+ case "WhileStatement":
140
+ case "DoWhileStatement":
141
+ case "LabeledStatement":
142
+ collectVarNames(node.body, out);
143
+ break;
144
+ case "SwitchStatement":
145
+ for (const item of node.cases)
146
+ for (const statement of item.consequent)
147
+ collectVarNames(statement, out);
148
+ break;
149
+ case "TryStatement":
150
+ collectVarNames(node.block, out);
151
+ collectVarNames(node.handler?.body, out);
152
+ collectVarNames(node.finalizer, out);
153
+ break;
154
+ }
155
+ return out;
156
+ };
110
157
  const loopDeclaration = (left, statement) => {
111
158
  if (left.type !== "VariableDeclaration")
112
159
  return undefined;
@@ -183,6 +230,7 @@ class Frame {
183
230
  return Effect.gen(function* () {
184
231
  self.predeclareLexical(program.body);
185
232
  self.hoistFunctions(program.body);
233
+ self.hoistVars(program.body);
186
234
  let value = undefined;
187
235
  for (const [index, statement] of program.body.entries()) {
188
236
  if (index === program.body.length - 1 && statement.type === "ExpressionStatement") {
@@ -290,6 +338,18 @@ class Frame {
290
338
  this.scopes.declare(node.id.name, this.createFunction(node), true, node);
291
339
  }
292
340
  }
341
+ // Hoisted `var` bindings start undefined, or copy a same-named parameter. Function bodies hoist
342
+ // into their own scope above the parameters so closures in parameter defaults keep seeing outer names.
343
+ hoistVars(statements, parameters) {
344
+ const scope = this.scopes.current();
345
+ for (const statement of statements) {
346
+ for (const name of collectVarNames(statement)) {
347
+ if (scope.has(name))
348
+ continue;
349
+ scope.set(name, { mutable: true, value: parameters?.get(name)?.value, initialized: true });
350
+ }
351
+ }
352
+ }
293
353
  predeclareLexical(statements) {
294
354
  for (const statement of statements) {
295
355
  if (statement.type !== "VariableDeclaration")
@@ -451,18 +511,20 @@ class Frame {
451
511
  }
452
512
  const assignment = left.type === "VariableDeclaration" ? undefined : left;
453
513
  const evaluateBody = (value) => Effect.gen(function* () {
454
- if (declared) {
514
+ if (declared?.lexical) {
455
515
  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);
516
+ self.predeclarePattern(declared.pattern, declared.mutable, left);
517
+ yield* self.declarePattern(declared.pattern, value, declared.mutable, left, true);
518
+ }
519
+ else if (declared) {
520
+ yield* self.assignPattern(declared.pattern, value, left);
459
521
  }
460
522
  else if (assignment) {
461
523
  yield* self.assignPattern(assignment, value, left);
462
524
  }
463
525
  return yield* self.evaluateStatement(node.body);
464
526
  }).pipe(Effect.ensuring(Effect.sync(() => {
465
- if (declared)
527
+ if (declared?.lexical)
466
528
  self.scopes.pop();
467
529
  })));
468
530
  while (true) {
@@ -662,18 +724,20 @@ class Frame {
662
724
  const assignmentName = left.type === "Identifier" ? left.name : undefined;
663
725
  for (const key of keys) {
664
726
  const result = yield* Effect.gen(function* () {
665
- if (declared) {
727
+ if (declared?.lexical) {
666
728
  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);
729
+ self.predeclarePattern(declared.pattern, declared.mutable, left);
730
+ yield* self.declarePattern(declared.pattern, key, declared.mutable, left, true);
731
+ }
732
+ else if (declared) {
733
+ yield* self.assignPattern(declared.pattern, key, left);
670
734
  }
671
735
  else if (assignmentName) {
672
736
  self.scopes.set(assignmentName, key, left);
673
737
  }
674
738
  return yield* self.evaluateStatement(node.body);
675
739
  }).pipe(Effect.ensuring(Effect.sync(() => {
676
- if (declared)
740
+ if (declared?.lexical)
677
741
  self.scopes.pop();
678
742
  })));
679
743
  const exit = loopExit(result, labels);
@@ -759,8 +823,14 @@ class Frame {
759
823
  throw new InterpreterRuntimeError("Unsupported variable declaration shape.", declaration);
760
824
  }
761
825
  const init = declaration.init;
826
+ // `var x` alone is a no-op: the binding was hoisted on function entry.
827
+ if (kind === "var") {
828
+ if (init)
829
+ yield* self.assignPattern(declaration.id, yield* self.evaluateExpression(init), declaration);
830
+ continue;
831
+ }
762
832
  const value = init ? yield* self.evaluateExpression(init) : undefined;
763
- yield* self.declarePattern(declaration.id, value, kind !== "const", declaration, kind !== "var");
833
+ yield* self.declarePattern(declaration.id, value, kind !== "const", declaration, true);
764
834
  }
765
835
  });
766
836
  }
@@ -1318,6 +1388,8 @@ class Frame {
1318
1388
  yield* invocation.declarePattern(parameter, args[index], true, parameter, true);
1319
1389
  }
1320
1390
  if (fn.body.type === "BlockStatement") {
1391
+ invocation.scopes.push();
1392
+ invocation.hoistVars(fn.body.body, paramScope);
1321
1393
  const result = yield* invocation.evaluateStatement(fn.body);
1322
1394
  return result.kind === "return" ? result.value : undefined;
1323
1395
  }
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-19438",
5
5
  "description": "Effect-native confined code execution over schema-described tools",
6
6
  "type": "module",
7
7
  "license": "MIT",