Skip to content

Commit d39a1ab

Browse files
rishipalcopybara-github
authored andcommitted
Move es6RewriteRestAndSpread pass post normalization
PiperOrigin-RevId: 619204806
1 parent f9a5c54 commit d39a1ab

File tree

4 files changed

+74
-17
lines changed

4 files changed

+74
-17
lines changed

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

+3
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,9 @@ Node createArgumentsAliasDeclaration(String aliasName) {
570570
* (e.g. because you are creating a new variable), you can use this method. However, if you've
571571
* just created the variable declaration, you could also just clone the {@code NAME} node from it
572572
* to create the new reference.
573+
*
574+
* <p>This method assumes that if the AST is normalized and the name starts with "$jscomp", then
575+
* it must be a constant name.
573576
*/
574577
Node createName(String name, Type type) {
575578
Node result = IR.name(name);

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

+28-13
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,12 @@ private void visitRestParam(NodeTraversal t, Node restParam, Node paramList) {
9393
return;
9494
}
9595

96+
// Now that the restParam is deleted, create a let declaration by making a new NAME node of the
97+
// same name `paramName`
9698
Node let =
9799
astFactory
98100
.createSingleLetNameDeclaration(
99-
paramName,
101+
paramName, // creates a new NAME node with name `paramName`
100102
astFactory.createCall(
101103
astFactory.createGetPropWithUnknownType(
102104
astFactory.createQName(this.namespace, "$jscomp.getRestArguments"),
@@ -105,24 +107,37 @@ private void visitRestParam(NodeTraversal t, Node restParam, Node paramList) {
105107
astFactory.createNumber(restIndex),
106108
astFactory.createArgumentsReference()))
107109
.srcrefTreeIfMissing(functionBody);
108-
if (paramName.startsWith("$jscomp$destructuring$var")) {
109-
// Currently this pass is before normalization. Hence, the createSingleLetNameDeclaration
110-
// creates a non-const names. But the earlier Es6RewriteDesctructuring pass creates these
111-
// names as const names.
112-
// TODO(b/197349249): When this pass moves post normalization, stop explictly marking these
113-
// names as const in this if-block.
114-
checkState(
115-
nameNode.getBooleanProp(Node.IS_CONSTANT_NAME),
116-
"The $jscomp$destructuring$var[num] name must've been marked as const by the"
117-
+ " Es6RewriteDesctructuring pass");
118-
let.getFirstChild().putBooleanProp(Node.IS_CONSTANT_NAME, true);
110+
Node insertBeforePoint = getInsertBeforePoint(functionBody);
111+
if (insertBeforePoint != null) {
112+
let.insertBefore(insertBeforePoint);
113+
} else {
114+
// functionBody only contains hoisted function declarations
115+
functionBody.addChildToBack(let);
119116
}
120-
functionBody.addChildToFront(let);
121117
NodeUtil.addFeatureToScript(t.getCurrentScript(), Feature.LET_DECLARATIONS, compiler);
122118
compiler.ensureLibraryInjected("es6/util/restarguments", /* force= */ false);
123119
t.reportCodeChange();
124120
}
125121

122+
/**
123+
* Gets an insertion point in the function body before which we want to insert the new let
124+
* declaration. If there's not good insertion point (e.g. the function is empty or only contains
125+
* inner function declarations), return null.
126+
*/
127+
private Node getInsertBeforePoint(Node functionBody) {
128+
checkState(functionBody.getParent().isFunction());
129+
Node current = functionBody.getFirstChild();
130+
131+
// Do not insert the let declaration before any hoisted function declarations in this function
132+
// body as those function declarations are hoisted by normalization. We must maintain
133+
// normalization.
134+
while (current != null && NodeUtil.isFunctionDeclaration(current)) {
135+
current = current.getNext();
136+
}
137+
138+
return current;
139+
}
140+
126141
/**
127142
* Processes array literals or calls to eliminate spreads.
128143
*

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,6 @@ public static void addEarlyOptimizationTranspilationPasses(
209209
if (options.needsTranspilationOf(Feature.CLASSES)) {
210210
passes.maybeAdd(es6RewriteClass);
211211
}
212-
if (options.needsTranspilationFrom(
213-
FeatureSet.BARE_MINIMUM.with(Feature.REST_PARAMETERS, Feature.SPREAD_EXPRESSIONS))) {
214-
passes.maybeAdd(es6RewriteRestAndSpread);
215-
}
216212
}
217213

218214
/**
@@ -226,6 +222,11 @@ public static void addPostNormalizationTranspilationPasses(
226222
// TODO(b/197349249): Move passes from `addEarlyOptimizationTranspilationPasses()` to here
227223
// until that method can be deleted as a no-op.
228224

225+
if (options.needsTranspilationFrom(
226+
FeatureSet.BARE_MINIMUM.with(Feature.REST_PARAMETERS, Feature.SPREAD_EXPRESSIONS))) {
227+
passes.maybeAdd(es6RewriteRestAndSpread);
228+
}
229+
229230
if (options.needsTranspilationFrom(
230231
FeatureSet.BARE_MINIMUM.with(
231232
Feature.COMPUTED_PROPERTIES, Feature.MEMBER_DECLARATIONS, Feature.TEMPLATE_LITERALS))) {

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

+38
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,44 @@ public void testUsedRestParameterAtPositionTwo() {
569569
assertThat(getLastCompiler().getInjected()).containsExactly("es6/util/restarguments");
570570
}
571571

572+
@Test
573+
public void testUsedRestParameterAtPositionTwo_maintainsNormalization() {
574+
test(
575+
"function f(zero, one, ...two) { function inner() {} return two; }",
576+
lines(
577+
"function f(zero, one) {",
578+
" function inner() {}", // stays hoisted
579+
" let two = $jscomp.getRestArguments.apply(2, arguments);",
580+
" return two;",
581+
"}"));
582+
assertThat(getLastCompiler().getInjected()).containsExactly("es6/util/restarguments");
583+
}
584+
585+
@Test
586+
public void testUsedRestParameterAtPositionTwo_maintainsNormalization_withoutReturn() {
587+
test(
588+
"function f(zero, one, ...two) { function inner() {} two; }",
589+
lines(
590+
"function f(zero, one) {",
591+
" function inner() {}", // stays hoisted
592+
" let two = $jscomp.getRestArguments.apply(2, arguments);",
593+
" two;",
594+
"}"));
595+
assertThat(getLastCompiler().getInjected()).containsExactly("es6/util/restarguments");
596+
}
597+
598+
@Test
599+
public void testUnusedRestParameterAtPositionTwo_noGoodInsertionPoint() {
600+
test(
601+
"function f(zero, one, ...two) { function inner() {} }",
602+
lines(
603+
"function f(zero, one) {",
604+
" function inner() {}", // stays hoisted
605+
" let two = $jscomp.getRestArguments.apply(2, arguments);", // declaration inserted
606+
"}"));
607+
assertThat(getLastCompiler().getInjected()).containsExactly("es6/util/restarguments");
608+
}
609+
572610
@Test
573611
public void testUnusedRestParameterAtPositionZeroWithTypingOnFunction() {
574612
test("/** @param {...number} zero */ function f(...zero) {}", "function f() {}");

0 commit comments

Comments
 (0)