Skip to content

Commit 8331642

Browse files
rishipalcopybara-github
authored andcommitted
Generate new vars only after function declarations to preserve normalization during rewriteArrowFunctions pass
PiperOrigin-RevId: 623378338
1 parent 26781e8 commit 8331642

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

src/com/google/javascript/jscomp/Es6RewriteArrowFunction.java

+37-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.google.javascript.jscomp;
1717

1818
import static com.google.common.base.Preconditions.checkNotNull;
19+
import static com.google.common.base.Preconditions.checkState;
1920

2021
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2122
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
@@ -126,7 +127,7 @@ private void addVarDeclarations(NodeTraversal t, ThisAndArgumentsContext context
126127
makeTreeNonIndexable(thisVar);
127128

128129
if (context.lastSuperStatement == null) {
129-
scopeBody.addChildToFront(thisVar);
130+
insertVarDeclaration(thisVar, scopeBody);
130131
} else {
131132
// Not safe to reference `this` until after super() has been called.
132133
// TODO(bradfordcsmith): Some complex cases still aren't covered, like
@@ -140,13 +141,47 @@ private void addVarDeclarations(NodeTraversal t, ThisAndArgumentsContext context
140141
Node argumentsVar =
141142
astFactory.createArgumentsAliasDeclaration(ARGUMENTS_VAR + "$" + context.uniqueId);
142143
NodeUtil.addFeatureToScript(t.getCurrentScript(), Feature.CONST_DECLARATIONS, compiler);
143-
scopeBody.addChildToFront(argumentsVar);
144+
145+
insertVarDeclaration(argumentsVar, scopeBody);
144146

145147
argumentsVar.srcrefTreeIfMissing(scopeBody);
146148
compiler.reportChangeToEnclosingScope(argumentsVar);
147149
}
148150
}
149151

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+
150185
private void makeTreeNonIndexable(Node n) {
151186
n.makeNonIndexable();
152187
for (Node child = n.getFirstChild(); child != null; child = child.getNext()) {

test/com/google/javascript/jscomp/Es6RewriteArrowFunctionTest.java

+26
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,32 @@ public void testCapturingThisInMultipleArrowsInSameFunctionScope() {
381381
"})"));
382382
}
383383

384+
@Test
385+
public void testGeneratedVariableDeclarations_placedAfterFunctionDeclarations() {
386+
testArrowRewriting(
387+
lines(
388+
"({",
389+
" x: 0,",
390+
" y: 'a',",
391+
" f: function() {",
392+
" function foo() { this.x;}",
393+
" var a2 = () => this.y;",
394+
" },",
395+
"})"),
396+
lines(
397+
"({",
398+
" x: 0,",
399+
" y: 'a',",
400+
" f: function() {",
401+
// stays hoisted
402+
" function foo() { this.x; }",
403+
// variable declarations placed after function declarations
404+
" const $jscomp$this$UID$1 = this;",
405+
" var a2 = function() { return $jscomp$this$UID$1.y; };",
406+
" },",
407+
"})"));
408+
}
409+
384410
@Test
385411
public void testPassingMultipleArrowsInSameFreeScopeAsMethodParams() {
386412
testArrowRewriting(

0 commit comments

Comments
 (0)