@@ -54,6 +54,8 @@ public final class Es6RewriteBlockScopedDeclaration extends AbstractPostOrderCal
54
54
FeatureSet .BARE_MINIMUM .with (Feature .LET_DECLARATIONS , Feature .CONST_DECLARATIONS );
55
55
private final UniqueIdSupplier uniqueIdSupplier ;
56
56
57
+ private static final String LOOP_PARAM_NAME_PREFIX = "$jscomp$loop_param$" ;
58
+
57
59
public Es6RewriteBlockScopedDeclaration (AbstractCompiler compiler ) {
58
60
this .compiler = compiler ;
59
61
this .uniqueIdSupplier = compiler .getUniqueIdSupplier ();
@@ -470,7 +472,7 @@ private void transformLoopClosure() {
470
472
return ;
471
473
}
472
474
473
- createWrapperFunctions ();
475
+ Set < Node > wrapperFunctions = createWrapperFunctions ();
474
476
475
477
for (Node loopNode : loopObjectMap .keySet ()) {
476
478
// Introduce objects to reflect the captured scope variables.
@@ -485,6 +487,7 @@ private void transformLoopClosure() {
485
487
Node updateLoopObject =
486
488
astFactory .createAssign (createLoopObjectNameNode (loopObject ), objectLitNextIteration );
487
489
Node objectLit =
490
+ // This is the only time we generate a loop object variable for a loopNode.
488
491
IR .var (createLoopObjectNameNode (loopObject ), astFactory .createObjectLit ())
489
492
.srcrefTree (loopNode );
490
493
addNodeBeforeLoop (objectLit , loopNode );
@@ -498,26 +501,82 @@ private void transformLoopClosure() {
498
501
499
502
changeLoopLocalVariablesToProperties (loopNode , loopObject );
500
503
}
504
+
505
+ // At this point, all local variables in the loop have been changed to property accesses on
506
+ // the "loopObject" name. For the wrapper functions that we introduced in the loop, we must
507
+ // change the name references in their body to refer to their parameter name instead of the
508
+ // "loopObject" name.
509
+ // TODO(bradfordcsmith): This is inefficient. We should really choose the names first, then
510
+ // make the changes, instead of changing the same variable references twice.
511
+ updateNamesInWrapperFunctions (wrapperFunctions );
512
+ }
513
+
514
+ /**
515
+ * Before:
516
+ *
517
+ * <pre>{@code
518
+ * var arr = [];
519
+ * var LOOP$0 = {};
520
+ * var i = 0;
521
+ * for (; i < 10; LOOP$0 = {y: LOOP$0.y}, i++) {
522
+ * LOOP$0.y = i;
523
+ * arr.push((function(LOOP$0$PARAM$1) {
524
+ * return function() { return LOOP$0.y; }; <---- must use param name
525
+ * })(LOOP$0));
526
+ * }
527
+ *
528
+ * }</pre>
529
+ *
530
+ * After:
531
+ *
532
+ * <pre>{@code
533
+ * var arr = [];
534
+ * var LOOP$0 = {};
535
+ * var i = 0;
536
+ * for (; i < 10; LOOP$0 = {y: LOOP$0.y}, i++) {
537
+ * LOOP$0.y = i;
538
+ * arr.push((function(LOOP$0$PARAM$1) {
539
+ * return function() { return LOOP$0$PARAM$1.y; }; <--- changed
540
+ * })(LOOP$0));
541
+ * }
542
+ *
543
+ * }</pre>
544
+ */
545
+ private void updateNamesInWrapperFunctions (Set <Node > wrapperFunctions ) {
546
+ for (Node func : wrapperFunctions ) {
547
+ // get the param names here (and the loopObject names from it)
548
+ Node paramList = func .getSecondChild ();
549
+
550
+ for (Node param = paramList .getFirstChild (); param != null ; param = param .getNext ()) {
551
+ String loopObjectName =
552
+ param .getString ().substring (0 , param .getString ().indexOf (LOOP_PARAM_NAME_PREFIX ));
553
+ updateNames (/* block */ func .getLastChild (), param , loopObjectName );
554
+ }
555
+ }
501
556
}
502
557
503
558
/** Create wrapper functions and call them. */
504
- private void createWrapperFunctions () {
559
+ private Set <Node > createWrapperFunctions () {
560
+ Set <Node > wrapperFunctions = new LinkedHashSet <>();
505
561
for (Node functionOrObjectLit : nodesRequiringLoopObjectsClosureMap .keySet ()) {
506
562
Node returnNode = IR .returnNode ();
507
563
Set <LoopObject > objects = nodesRequiringLoopObjectsClosureMap .get (functionOrObjectLit );
508
- Node [] objectNames = new Node [objects .size ()];
564
+ Node [] parameterNames = new Node [objects .size ()];
509
565
Node [] objectNamesForCall = new Node [objects .size ()];
510
- int i = 0 ;
566
+ int loopObjectIndex = 0 ;
511
567
for (LoopObject object : objects ) {
512
- objectNames [i ] = createLoopObjectNameNode (object );
513
- objectNamesForCall [i ] = createLoopObjectNameNode (object );
514
- i ++;
568
+ // This name for parameter should be unique to preserve normalization
569
+ parameterNames [loopObjectIndex ] =
570
+ createUniqueParameterNameToUseWithinLoop (functionOrObjectLit , object );
571
+ // this name must be the same as the loop object name
572
+ objectNamesForCall [loopObjectIndex ] = createLoopObjectNameNode (object );
573
+ loopObjectIndex ++;
515
574
}
516
575
517
576
Node iife =
518
577
astFactory .createFunction (
519
578
"" ,
520
- IR .paramList (objectNames ),
579
+ IR .paramList (parameterNames ),
521
580
IR .block (returnNode ),
522
581
type (StandardColors .TOP_OBJECT ));
523
582
compiler .reportChangeToChangeScope (iife );
@@ -531,9 +590,29 @@ private void createWrapperFunctions() {
531
590
functionOrObjectLit );
532
591
replacement = call .srcrefTreeIfMissing (functionOrObjectLit );
533
592
functionOrObjectLit .replaceWith (replacement );
593
+ wrapperFunctions .add (iife );
534
594
returnNode .addChildToFront (functionOrObjectLit );
535
595
compiler .reportChangeToEnclosingScope (replacement );
536
596
}
597
+ return wrapperFunctions ;
598
+ }
599
+
600
+ /*
601
+ * Renames all references within the functionOrObjectLit body from the old loopObjectName
602
+ * (e.g. "$jscomp$loop$1") to the given loopObjectParamName
603
+ * (e.g. $jscomp$loop$1$jscomp$loop_param$m123..456).
604
+ */
605
+ private void updateNames (Node node , Node paramName , String loopObjectName ) {
606
+
607
+ if (node .isName () && !node .getString ().isEmpty () /* not an anonymous function name */ ) {
608
+ if (node .getString ().contains (loopObjectName )) {
609
+ node .setString (paramName .getString ());
610
+ return ;
611
+ }
612
+ }
613
+ for (Node child = node .getFirstChild (); child != null ; child = child .getNext ()) {
614
+ updateNames (child , paramName , loopObjectName );
615
+ }
537
616
}
538
617
539
618
/**
@@ -692,6 +771,21 @@ private Node createLoopVarReferenceReplacement(
692
771
return replacement ;
693
772
}
694
773
774
+ /**
775
+ * Gets a name other than the loop object name to use for parameter names of the new wrapper
776
+ * functions being added in the loop.
777
+ *
778
+ * <p>For a loop object "$jscomp$loop$0", this creates a unique parameter name like
779
+ * "$jscomp$loop$0$jscomp$loop_param$1".
780
+ */
781
+ private Node createUniqueParameterNameToUseWithinLoop (Node node , LoopObject loopObject ) {
782
+ return astFactory .createName (
783
+ loopObject .name
784
+ + LOOP_PARAM_NAME_PREFIX
785
+ + uniqueIdSupplier .getUniqueId (compiler .getInput (NodeUtil .getInputId (node ))),
786
+ type (StandardColors .TOP_OBJECT ));
787
+ }
788
+
695
789
private Node createLoopObjectNameNode (LoopObject loopObject ) {
696
790
return astFactory .createName (loopObject .name , type (StandardColors .TOP_OBJECT ));
697
791
}
0 commit comments