@@ -223,10 +223,18 @@ private void visitSuper(NodeTraversal t, ConstructorData constructorData) {
223
223
// `this` -> `$jscomp$super$this` throughout the constructor body,
224
224
// except for super() calls.
225
225
updateThisToSuperThis (typeOfThis , constructorBody , superCalls , uniqueSuperThisName );
226
- // Start constructor with `var $jscomp$super$this;`
227
- constructorBody .addChildToFront (
226
+ // Start constructor with `var $jscomp$super$this;`, but place it after any hoisted
227
+ // function declarations
228
+ Node declaration =
228
229
IR .var (astFactory .createName (uniqueSuperThisName , typeOfThis ))
229
- .srcrefTree (constructorBody ));
230
+ .srcrefTree (constructorBody );
231
+ Node insertBeforePoint = getInsertBeforePoint (constructorBody );
232
+ if (insertBeforePoint != null ) {
233
+ declaration .insertBefore (insertBeforePoint );
234
+ } else {
235
+ // functionBody only contains hoisted function declarations
236
+ constructorBody .addChildToBack (declaration );
237
+ }
230
238
// End constructor with `return $jscomp$super$this;`
231
239
constructorBody .addChildToBack (
232
240
IR .returnNode (astFactory .createName (uniqueSuperThisName , typeOfThis ))
@@ -249,6 +257,24 @@ private void visitSuper(NodeTraversal t, ConstructorData constructorData) {
249
257
}
250
258
}
251
259
260
+ /**
261
+ * Gets an insertion point in the function body before which we want to insert the new let
262
+ * declaration. If there's not good insertion point (e.g. the function is empty or only contains
263
+ * inner function declarations), return null.
264
+ */
265
+ private Node getInsertBeforePoint (Node functionBody ) {
266
+ checkState (functionBody .getParent ().isFunction ());
267
+ Node current = functionBody .getFirstChild ();
268
+
269
+ // Do not insert the let declaration before any hoisted function declarations in this function
270
+ // body as those function declarations are hoisted by normalization. We must maintain
271
+ // normalization.
272
+ while (current != null && NodeUtil .isFunctionDeclaration (current )) {
273
+ current = current .getNext ();
274
+ }
275
+ return current ;
276
+ }
277
+
252
278
/**
253
279
* Change calls to `super` to use `$jscomp.construct` instead.
254
280
*
@@ -299,11 +325,20 @@ private void convertSuperCallsToJsCompConstructCalls(
299
325
// `this` -> `$jscomp$super$this` throughout the constructor body,
300
326
// except for super() calls.
301
327
updateThisToSuperThis (typeOfThis , constructorBody , superCalls , uniqueSuperThisName );
302
- // Start constructor with `var $jscomp$super$this;`
303
- constructorBody .addChildToFront (
328
+ // Start constructor with `var $jscomp$super$this;`, but place it after any hoisted
329
+ // function declarations
330
+ Node declaration =
304
331
astFactory
305
332
.createSingleVarNameDeclaration (uniqueSuperThisName )
306
- .srcrefTree (constructorBody ));
333
+ .srcrefTree (constructorBody );
334
+ Node insertBeforePoint = getInsertBeforePoint (constructorBody );
335
+ if (insertBeforePoint != null ) {
336
+ declaration .insertBefore (insertBeforePoint );
337
+ } else {
338
+ // functionBody only contains hoisted function declarations
339
+ constructorBody .addChildToBack (declaration );
340
+ }
341
+
307
342
// End constructor with `return $jscomp$super$this;`
308
343
constructorBody .addChildToBack (
309
344
astFactory
0 commit comments