@@ -27,9 +27,19 @@ type EmitContext struct {
27
27
emitHelpers collections.OrderedSet [* EmitHelper ]
28
28
}
29
29
30
+ type environmentFlags int
31
+
32
+ const (
33
+ environmentFlagsNone environmentFlags = 0
34
+ environmentFlagsInParameters environmentFlags = 1 << 0 // currently visiting a parameter list
35
+ environmentFlagsVariablesHoistedInParameters environmentFlags = 1 << 1 // a temp variable was hoisted while visiting a parameter list
36
+ )
37
+
30
38
type varScope struct {
31
- variables []* ast.VariableDeclarationNode
32
- functions []* ast.FunctionDeclarationNode
39
+ variables []* ast.VariableDeclarationNode
40
+ functions []* ast.FunctionDeclarationNode
41
+ flags environmentFlags
42
+ initializationStatements []* ast.Node
33
43
}
34
44
35
45
func NewEmitContext () * EmitContext {
@@ -105,12 +115,18 @@ func (c *EmitContext) StartVariableEnvironment() {
105
115
func (c * EmitContext ) EndVariableEnvironment () []* ast.Statement {
106
116
scope := c .varScopeStack .Pop ()
107
117
var statements []* ast.Statement
118
+ if len (scope .functions ) > 0 {
119
+ statements = slices .Clone (scope .functions )
120
+ }
108
121
if len (scope .variables ) > 0 {
109
122
varDeclList := c .Factory .NewVariableDeclarationList (ast .NodeFlagsNone , c .Factory .NewNodeList (scope .variables ))
110
123
varStatement := c .Factory .NewVariableStatement (nil /*modifiers*/ , varDeclList )
111
124
c .SetEmitFlags (varStatement , EFCustomPrologue )
112
125
statements = append (statements , varStatement )
113
126
}
127
+ if len (scope .initializationStatements ) > 0 {
128
+ statements = append (statements , scope .initializationStatements ... )
129
+ }
114
130
return append (statements , c .EndLexicalEnvironment ()... )
115
131
}
116
132
@@ -148,6 +164,9 @@ func (c *EmitContext) AddVariableDeclaration(name *ast.IdentifierNode) {
148
164
c .SetEmitFlags (varDecl , EFNoNestedSourceMaps )
149
165
scope := c .varScopeStack .Peek ()
150
166
scope .variables = append (scope .variables , varDecl )
167
+ if scope .flags & environmentFlagsInParameters != 0 {
168
+ scope .flags |= environmentFlagsVariablesHoistedInParameters
169
+ }
151
170
}
152
171
153
172
// Adds a hoisted function declaration to the current VariableEnvironment
@@ -721,11 +740,135 @@ func (c *EmitContext) VisitVariableEnvironment(nodes *ast.StatementList, visitor
721
740
722
741
func (c * EmitContext ) VisitParameters (nodes * ast.ParameterList , visitor * ast.NodeVisitor ) * ast.ParameterList {
723
742
c .StartVariableEnvironment ()
743
+ scope := c .varScopeStack .Peek ()
744
+ oldFlags := scope .flags
745
+ scope .flags |= environmentFlagsInParameters
724
746
nodes = visitor .VisitNodes (nodes )
747
+
748
+ // As of ES2015, any runtime execution of that occurs in for a parameter (such as evaluating an
749
+ // initializer or a binding pattern), occurs in its own lexical scope. As a result, any expression
750
+ // that we might transform that introduces a temporary variable would fail as the temporary variable
751
+ // exists in a different lexical scope. To address this, we move any binding patterns and initializers
752
+ // in a parameter list to the body if we detect a variable being hoisted while visiting a parameter list
753
+ // when the emit target is greater than ES2015. (Which is now all targets.)
754
+ if scope .flags & environmentFlagsVariablesHoistedInParameters != 0 {
755
+ nodes = c .addDefaultValueAssignmentsIfNeeded (nodes )
756
+ }
757
+ scope .flags = oldFlags
725
758
// !!! c.suspendVariableEnvironment()
726
759
return nodes
727
760
}
728
761
762
+ func (c * EmitContext ) addDefaultValueAssignmentsIfNeeded (nodeList * ast.ParameterList ) * ast.ParameterList {
763
+ if nodeList == nil {
764
+ return nodeList
765
+ }
766
+ var result []* ast.Node
767
+ nodes := nodeList .Nodes
768
+ for i , parameter := range nodes {
769
+ updated := c .addDefaultValueAssignmentIfNeeded (parameter .AsParameterDeclaration ())
770
+ if updated != parameter {
771
+ if result == nil {
772
+ result = slices .Clone (nodes )
773
+ }
774
+ result [i ] = updated
775
+ }
776
+ }
777
+ if result != nil {
778
+ res := c .Factory .NewNodeList (result )
779
+ res .Loc = nodeList .Loc
780
+ return res
781
+ }
782
+ return nodeList
783
+ }
784
+
785
+ func (c * EmitContext ) addDefaultValueAssignmentIfNeeded (parameter * ast.ParameterDeclaration ) * ast.Node {
786
+ // A rest parameter cannot have a binding pattern or an initializer,
787
+ // so let's just ignore it.
788
+ if parameter .DotDotDotToken != nil {
789
+ return parameter .AsNode ()
790
+ } else if ast .IsBindingPattern (parameter .Name ()) {
791
+ return c .addDefaultValueAssignmentForBindingPattern (parameter )
792
+ } else if parameter .Initializer != nil {
793
+ return c .addDefaultValueAssignmentForInitializer (parameter , parameter .Name (), parameter .Initializer )
794
+ }
795
+ return parameter .AsNode ()
796
+ }
797
+
798
+ func (c * EmitContext ) addDefaultValueAssignmentForBindingPattern (parameter * ast.ParameterDeclaration ) * ast.Node {
799
+ var initNode * ast.Node
800
+ if parameter .Initializer != nil {
801
+ initNode = c .Factory .NewConditionalExpression (
802
+ c .Factory .NewStrictEqualityExpression (
803
+ c .Factory .NewGeneratedNameForNode (parameter .AsNode ()),
804
+ c .Factory .NewVoidZeroExpression (),
805
+ ),
806
+ c .Factory .NewToken (ast .KindQuestionToken ),
807
+ parameter .Initializer ,
808
+ c .Factory .NewToken (ast .KindColonToken ),
809
+ c .Factory .NewGeneratedNameForNode (parameter .AsNode ()),
810
+ )
811
+ } else {
812
+ initNode = c .Factory .NewGeneratedNameForNode (parameter .AsNode ())
813
+ }
814
+ c .AddInitializationStatement (c .Factory .NewVariableStatement (
815
+ nil ,
816
+ c .Factory .NewVariableDeclarationList (ast .NodeFlagsNone , c .Factory .NewNodeList ([]* ast.Node {c .Factory .NewVariableDeclaration (
817
+ parameter .Name (),
818
+ nil ,
819
+ parameter .Type ,
820
+ initNode ,
821
+ )})),
822
+ ))
823
+ return c .Factory .UpdateParameterDeclaration (
824
+ parameter ,
825
+ parameter .Modifiers (),
826
+ parameter .DotDotDotToken ,
827
+ c .Factory .NewGeneratedNameForNode (parameter .AsNode ()),
828
+ parameter .QuestionToken ,
829
+ parameter .Type ,
830
+ nil ,
831
+ )
832
+ }
833
+
834
+ func (c * EmitContext ) addDefaultValueAssignmentForInitializer (parameter * ast.ParameterDeclaration , name * ast.Node , initializer * ast.Node ) * ast.Node {
835
+ c .AddEmitFlags (initializer , EFNoSourceMap | EFNoComments )
836
+ nameClone := name .Clone (c .Factory )
837
+ c .AddEmitFlags (nameClone , EFNoSourceMap )
838
+ initAssignment := c .Factory .NewAssignmentExpression (
839
+ nameClone ,
840
+ initializer ,
841
+ )
842
+ initAssignment .Loc = parameter .Loc
843
+ c .AddEmitFlags (initAssignment , EFNoComments )
844
+ initBlock := c .Factory .NewBlock (c .Factory .NewNodeList ([]* ast.Node {c .Factory .NewExpressionStatement (initAssignment )}), false )
845
+ initBlock .Loc = parameter .Loc
846
+ c .AddEmitFlags (initBlock , EFSingleLine | EFNoTrailingSourceMap | EFNoTokenSourceMaps | EFNoComments )
847
+ c .AddInitializationStatement (c .Factory .NewIfStatement (
848
+ c .Factory .NewTypeCheck (name .Clone (c .Factory ), "undefined" ),
849
+ initBlock ,
850
+ nil ,
851
+ ))
852
+ return c .Factory .UpdateParameterDeclaration (
853
+ parameter ,
854
+ parameter .Modifiers (),
855
+ parameter .DotDotDotToken ,
856
+ parameter .Name (),
857
+ parameter .QuestionToken ,
858
+ parameter .Type ,
859
+ nil ,
860
+ )
861
+ }
862
+
863
+ func (c * EmitContext ) AddInitializationStatement (node * ast.Node ) {
864
+ scope := c .varScopeStack .Peek ()
865
+ if scope == nil {
866
+ panic ("Tried to add an initialization statement without a surrounding variable scope" )
867
+ }
868
+ c .AddEmitFlags (node , EFCustomPrologue )
869
+ scope .initializationStatements = append (scope .initializationStatements , node )
870
+ }
871
+
729
872
func (c * EmitContext ) VisitFunctionBody (node * ast.BlockOrExpression , visitor * ast.NodeVisitor ) * ast.BlockOrExpression {
730
873
// !!! c.resumeVariableEnvironment()
731
874
updated := visitor .VisitNode (node )
0 commit comments