@@ -2049,9 +2049,13 @@ public void visit(NodeTraversal t, Node n, Node parent) {
2049
2049
} else if (n .isName () && n .getString ().equals ("arguments" )) {
2050
2050
visitArguments (n );
2051
2051
} else if (n .isVar ()) {
2052
- // don't transpile var in "for (var i = 0; ; )"
2053
- if (!(parent .isVanillaFor () || parent .isForIn ()) || parent .getFirstChild () != n ) {
2054
- visitVar (n );
2052
+ if (parent .isVanillaFor ()) {
2053
+ visitVanillaForLoopVar (n );
2054
+ } else if (parent .isForIn ()) {
2055
+ visitForInLoopVar (n );
2056
+ } else {
2057
+ // NOTE: for-of loops are transpiled away before this pass
2058
+ visitVarStatement (n );
2055
2059
}
2056
2060
} // else no changes need to be made
2057
2061
}
@@ -2130,9 +2134,86 @@ void visitArguments(Node n) {
2130
2134
* a = "test", b = i + 5;
2131
2135
* </pre>
2132
2136
*/
2133
- void visitVar (Node varStatement ) {
2137
+ void visitVarStatement (Node varStatement ) {
2138
+ Node commaExpression = extractAssignmentsToCommaExpression (varStatement );
2139
+ if (commaExpression == null ) {
2140
+ varStatement .detach ();
2141
+ } else {
2142
+ varStatement .replaceWith (IR .exprResult (commaExpression ));
2143
+ }
2144
+ // Move declaration without initial values to just before the program method definition.
2145
+ hoistNode (varStatement );
2146
+ }
2147
+
2148
+ /**
2149
+ * Hoists {@code var} declarations in vanilla for loops into the closure containing the
2150
+ * generator to preserve their state across multiple invocation of state machine program.
2151
+ *
2152
+ * <p>
2153
+ *
2154
+ * <pre>
2155
+ * for (var a = "test", b = i + 5; ... ; )
2156
+ * </pre>
2157
+ *
2158
+ * is transpiled to:
2159
+ *
2160
+ * <pre>
2161
+ * var a, b;
2162
+ * for (a = "test", b = i + 5; ...; )
2163
+ * </pre>
2164
+ */
2165
+ private void visitVanillaForLoopVar (Node varDeclaration ) {
2166
+ Node commaExpression = extractAssignmentsToCommaExpression (varDeclaration );
2167
+ if (commaExpression == null ) {
2168
+ // `for (var x; ` becomes `for (; `
2169
+ varDeclaration .replaceWith (IR .empty ());
2170
+ } else {
2171
+ // `for (var i = 0, j = 0; `... becomes `for (i = 0, j = 0; `...
2172
+ varDeclaration .replaceWith (commaExpression );
2173
+ }
2174
+ // Move declaration without initial values to just before the program method definition.
2175
+ hoistNode (varDeclaration );
2176
+ }
2177
+
2178
+ /**
2179
+ * Hoists {@code var} declarations in for-in loops into the closure containing the
2180
+ * generator to preserve their state across multiple invocation of state machine program.
2181
+ *
2182
+ * <p>
2183
+ *
2184
+ * <pre>
2185
+ * for (var a in obj)
2186
+ * </pre>
2187
+ *
2188
+ * is transpiled to:
2189
+ *
2190
+ * <pre>
2191
+ * var a;
2192
+ * for (a in obj))
2193
+ * </pre>
2194
+ */
2195
+ private void visitForInLoopVar (Node varDeclaration ) {
2196
+ // `for (var varName in ` ...
2197
+ Node varName = varDeclaration .getOnlyChild ();
2198
+ checkState (!varName .hasChildren (), varName );
2199
+ Node clonedVarName = varName .cloneNode ().setJSDocInfo (null );
2200
+ // becomes `for (varName in ` ...
2201
+ varDeclaration .replaceWith (clonedVarName );
2202
+ // Move declaration without initial values to just before the program method definition.
2203
+ hoistNode (varDeclaration );
2204
+ }
2205
+
2206
+ /**
2207
+ * Removes all initializers from a var declaration and returns them as a single expression
2208
+ * of comma-separated assignments or null if there aren't any initializers.
2209
+ *
2210
+ * @param varDeclaration VAR node
2211
+ * @return null or expression node (e.g. `varName1 = 1, varName2 = y`)
2212
+ */
2213
+ @ Nullable
2214
+ private Node extractAssignmentsToCommaExpression (Node varDeclaration ) {
2134
2215
ArrayList <Node > assignments = new ArrayList <>();
2135
- for (Node varName : varStatement .children ()) {
2216
+ for (Node varName : varDeclaration .children ()) {
2136
2217
if (varName .hasChildren ()) {
2137
2218
Node copiedVarName = varName .cloneNode ().setJSDocInfo (null );
2138
2219
Node assign =
@@ -2142,22 +2223,15 @@ void visitVar(Node varStatement) {
2142
2223
assignments .add (assign );
2143
2224
}
2144
2225
}
2145
- if (assignments .isEmpty ()) {
2146
- varStatement .detach ();
2147
- } else {
2148
- Node commaExpression = null ;
2149
- for (Node assignment : assignments ) {
2150
- commaExpression =
2151
- commaExpression == null
2152
- ? assignment
2153
- : withType (IR .comma (commaExpression , assignment ), assignment .getJSType ())
2154
- .useSourceInfoFrom (assignment );
2155
- }
2156
- varStatement .replaceWith (IR .exprResult (commaExpression ));
2226
+ Node commaExpression = null ;
2227
+ for (Node assignment : assignments ) {
2228
+ commaExpression =
2229
+ commaExpression == null
2230
+ ? assignment
2231
+ : withType (IR .comma (commaExpression , assignment ), assignment .getJSType ())
2232
+ .useSourceInfoFrom (assignment );
2157
2233
}
2158
- // Place original var statement with initial values removed to just before
2159
- // the program method definition.
2160
- hoistNode (varStatement );
2234
+ return commaExpression ;
2161
2235
}
2162
2236
}
2163
2237
0 commit comments