@@ -170,6 +170,49 @@ const collectPatternNames = (pattern: Pattern, out: Array<string> = []): Array<s
170170 return out
171171}
172172
173+ // `var` names declared anywhere in a function body except inside nested functions, which own theirs.
174+ const collectVarNames = (
175+ node : Statement | ModuleDeclaration | null | undefined ,
176+ out : Array < string > = [ ] ,
177+ ) : Array < string > => {
178+ if ( ! node ) return out
179+ switch ( node . type ) {
180+ case "VariableDeclaration" :
181+ if ( node . kind === "var" ) for ( const declaration of node . declarations ) collectPatternNames ( declaration . id , out )
182+ break
183+ case "BlockStatement" :
184+ for ( const statement of node . body ) collectVarNames ( statement , out )
185+ break
186+ case "IfStatement" :
187+ collectVarNames ( node . consequent , out )
188+ collectVarNames ( node . alternate , out )
189+ break
190+ case "ForStatement" :
191+ if ( node . init ?. type === "VariableDeclaration" ) collectVarNames ( node . init , out )
192+ collectVarNames ( node . body , out )
193+ break
194+ case "ForInStatement" :
195+ case "ForOfStatement" :
196+ if ( node . left . type === "VariableDeclaration" ) collectVarNames ( node . left , out )
197+ collectVarNames ( node . body , out )
198+ break
199+ case "WhileStatement" :
200+ case "DoWhileStatement" :
201+ case "LabeledStatement" :
202+ collectVarNames ( node . body , out )
203+ break
204+ case "SwitchStatement" :
205+ for ( const item of node . cases ) for ( const statement of item . consequent ) collectVarNames ( statement , out )
206+ break
207+ case "TryStatement" :
208+ collectVarNames ( node . block , out )
209+ collectVarNames ( node . handler ?. body , out )
210+ collectVarNames ( node . finalizer , out )
211+ break
212+ }
213+ return out
214+ }
215+
173216const loopDeclaration = ( left : VariableDeclaration | Pattern , statement : "for...of" | "for...in" ) => {
174217 if ( left . type !== "VariableDeclaration" ) return undefined
175218 const declaration = left . declarations . length === 1 ? left . declarations [ 0 ] : undefined
@@ -276,6 +319,7 @@ class Frame<R> {
276319 return Effect . gen ( function * ( ) {
277320 self . predeclareLexical ( program . body )
278321 self . hoistFunctions ( program . body )
322+ self . hoistVars ( program . body )
279323 let value : unknown = undefined
280324 for ( const [ index , statement ] of program . body . entries ( ) ) {
281325 if ( index === program . body . length - 1 && statement . type === "ExpressionStatement" ) {
@@ -399,6 +443,18 @@ class Frame<R> {
399443 }
400444 }
401445
446+ // Hoisted `var` bindings start undefined, or copy a same-named parameter. Function bodies hoist
447+ // into their own scope above the parameters so closures in parameter defaults keep seeing outer names.
448+ private hoistVars ( statements : ReadonlyArray < Statement | ModuleDeclaration > , parameters ?: Map < string , Binding > ) : void {
449+ const scope = this . scopes . current ( )
450+ for ( const statement of statements ) {
451+ for ( const name of collectVarNames ( statement ) ) {
452+ if ( scope . has ( name ) ) continue
453+ scope . set ( name , { mutable : true , value : parameters ?. get ( name ) ?. value , initialized : true } )
454+ }
455+ }
456+ }
457+
402458 private predeclareLexical ( statements : ReadonlyArray < Statement | ModuleDeclaration > ) : void {
403459 for ( const statement of statements ) {
404460 if ( statement . type !== "VariableDeclaration" ) continue
@@ -589,18 +645,20 @@ class Frame<R> {
589645
590646 const evaluateBody = ( value : unknown ) =>
591647 Effect . gen ( function * ( ) {
592- if ( declared ) {
648+ if ( declared ?. lexical ) {
593649 self . scopes . push ( )
594- if ( declared . lexical ) self . predeclarePattern ( declared . pattern , declared . mutable , left )
595- yield * self . declarePattern ( declared . pattern , value , declared . mutable , left , declared . lexical )
650+ self . predeclarePattern ( declared . pattern , declared . mutable , left )
651+ yield * self . declarePattern ( declared . pattern , value , declared . mutable , left , true )
652+ } else if ( declared ) {
653+ yield * self . assignPattern ( declared . pattern , value , left )
596654 } else if ( assignment ) {
597655 yield * self . assignPattern ( assignment , value , left )
598656 }
599657 return yield * self . evaluateStatement ( node . body )
600658 } ) . pipe (
601659 Effect . ensuring (
602660 Effect . sync ( ( ) => {
603- if ( declared ) self . scopes . pop ( )
661+ if ( declared ?. lexical ) self . scopes . pop ( )
604662 } ) ,
605663 ) ,
606664 )
@@ -834,18 +892,20 @@ class Frame<R> {
834892
835893 for ( const key of keys ) {
836894 const result = yield * Effect . gen ( function * ( ) {
837- if ( declared ) {
895+ if ( declared ?. lexical ) {
838896 self . scopes . push ( )
839- if ( declared . lexical ) self . predeclarePattern ( declared . pattern , declared . mutable , left )
840- yield * self . declarePattern ( declared . pattern , key , declared . mutable , left , declared . lexical )
897+ self . predeclarePattern ( declared . pattern , declared . mutable , left )
898+ yield * self . declarePattern ( declared . pattern , key , declared . mutable , left , true )
899+ } else if ( declared ) {
900+ yield * self . assignPattern ( declared . pattern , key , left )
841901 } else if ( assignmentName ) {
842902 self . scopes . set ( assignmentName , key , left )
843903 }
844904 return yield * self . evaluateStatement ( node . body )
845905 } ) . pipe (
846906 Effect . ensuring (
847907 Effect . sync ( ( ) => {
848- if ( declared ) self . scopes . pop ( )
908+ if ( declared ?. lexical ) self . scopes . pop ( )
849909 } ) ,
850910 ) ,
851911 )
@@ -952,8 +1012,13 @@ class Frame<R> {
9521012 }
9531013
9541014 const init = declaration . init
1015+ // `var x` alone is a no-op: the binding was hoisted on function entry.
1016+ if ( kind === "var" ) {
1017+ if ( init ) yield * self . assignPattern ( declaration . id , yield * self . evaluateExpression ( init ) , declaration )
1018+ continue
1019+ }
9551020 const value = init ? yield * self . evaluateExpression ( init ) : undefined
956- yield * self . declarePattern ( declaration . id , value , kind !== "const" , declaration , kind !== "var" )
1021+ yield * self . declarePattern ( declaration . id , value , kind !== "const" , declaration , true )
9571022 }
9581023 } )
9591024 }
@@ -1574,6 +1639,8 @@ class Frame<R> {
15741639 }
15751640
15761641 if ( fn . body . type === "BlockStatement" ) {
1642+ invocation . scopes . push ( )
1643+ invocation . hoistVars ( fn . body . body , paramScope )
15771644 const result = yield * invocation . evaluateStatement ( fn . body )
15781645 return result . kind === "return" ? result . value : undefined
15791646 }
0 commit comments