16
16
package com .google .javascript .jscomp ;
17
17
18
18
import static com .google .common .base .Preconditions .checkNotNull ;
19
+ import static com .google .common .base .Preconditions .checkState ;
19
20
20
21
import com .google .errorprone .annotations .CanIgnoreReturnValue ;
21
22
import com .google .javascript .jscomp .parsing .parser .FeatureSet ;
@@ -126,7 +127,7 @@ private void addVarDeclarations(NodeTraversal t, ThisAndArgumentsContext context
126
127
makeTreeNonIndexable (thisVar );
127
128
128
129
if (context .lastSuperStatement == null ) {
129
- scopeBody . addChildToFront (thisVar );
130
+ insertVarDeclaration (thisVar , scopeBody );
130
131
} else {
131
132
// Not safe to reference `this` until after super() has been called.
132
133
// TODO(bradfordcsmith): Some complex cases still aren't covered, like
@@ -140,13 +141,47 @@ private void addVarDeclarations(NodeTraversal t, ThisAndArgumentsContext context
140
141
Node argumentsVar =
141
142
astFactory .createArgumentsAliasDeclaration (ARGUMENTS_VAR + "$" + context .uniqueId );
142
143
NodeUtil .addFeatureToScript (t .getCurrentScript (), Feature .CONST_DECLARATIONS , compiler );
143
- scopeBody .addChildToFront (argumentsVar );
144
+
145
+ insertVarDeclaration (argumentsVar , scopeBody );
144
146
145
147
argumentsVar .srcrefTreeIfMissing (scopeBody );
146
148
compiler .reportChangeToEnclosingScope (argumentsVar );
147
149
}
148
150
}
149
151
152
+ private void insertVarDeclaration (Node varDeclaration , Node scopeBody ) {
153
+ if (scopeBody .getParent ().isFunction ()) {
154
+ // for functions, we must find the correct insertion point to preserve normalization
155
+ Node insertBeforePoint = getInsertBeforePoint (scopeBody );
156
+ if (insertBeforePoint != null ) {
157
+ varDeclaration .insertBefore (insertBeforePoint );
158
+ } else {
159
+ // functionBody only contains hoisted function declarations
160
+ scopeBody .addChildToBack (varDeclaration );
161
+ }
162
+ } else {
163
+ scopeBody .addChildToFront (varDeclaration );
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Gets an insertion point in the function body before which we want to insert the new let
169
+ * declaration. If there's not good insertion point (e.g. the function is empty or only contains
170
+ * inner function declarations), return null.
171
+ */
172
+ private Node getInsertBeforePoint (Node functionBody ) {
173
+ checkState (functionBody .getParent ().isFunction ());
174
+ Node current = functionBody .getFirstChild ();
175
+
176
+ // Do not insert the let declaration before any hoisted function declarations in this function
177
+ // body as those function declarations are hoisted by normalization. We must maintain
178
+ // normalization.
179
+ while (current != null && NodeUtil .isFunctionDeclaration (current )) {
180
+ current = current .getNext ();
181
+ }
182
+ return current ;
183
+ }
184
+
150
185
private void makeTreeNonIndexable (Node n ) {
151
186
n .makeNonIndexable ();
152
187
for (Node child = n .getFirstChild (); child != null ; child = child .getNext ()) {
0 commit comments