Skip to content

Commit

Permalink
Support transpiling for-of loops with an arbitrary lhs expression as …
Browse files Browse the repository at this point in the history
…the initializer

e.g.
for (this.foo of someArray) { ...

and remove an assertion that the user only passes names and name declarations in the initializer.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=201246324
  • Loading branch information
lauraharker authored and brad4d committed Jun 20, 2018
1 parent f2d9c31 commit 784b4af
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 16 deletions.
35 changes: 19 additions & 16 deletions src/com/google/javascript/jscomp/Es6ForOfConverter.java
Expand Up @@ -18,7 +18,6 @@
import static com.google.javascript.jscomp.Es6ToEs3Util.createType; import static com.google.javascript.jscomp.Es6ToEs3Util.createType;
import static com.google.javascript.jscomp.Es6ToEs3Util.withType; import static com.google.javascript.jscomp.Es6ToEs3Util.withType;


import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.parsing.parser.FeatureSet; import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature; import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
import com.google.javascript.rhino.IR; import com.google.javascript.rhino.IR;
Expand Down Expand Up @@ -46,6 +45,7 @@ public final class Es6ForOfConverter implements NodeTraversal.Callback, HotSwapC
private final JSType unknownType; private final JSType unknownType;
private final JSType stringType; private final JSType stringType;
private final JSType booleanType; private final JSType booleanType;
private final DefaultNameGenerator namer;


private static final String ITER_BASE = "$jscomp$iter$"; private static final String ITER_BASE = "$jscomp$iter$";


Expand All @@ -59,6 +59,7 @@ public Es6ForOfConverter(AbstractCompiler compiler) {
this.unknownType = createType(addTypes, registry, JSTypeNative.UNKNOWN_TYPE); this.unknownType = createType(addTypes, registry, JSTypeNative.UNKNOWN_TYPE);
this.stringType = createType(addTypes, registry, JSTypeNative.STRING_TYPE); this.stringType = createType(addTypes, registry, JSTypeNative.STRING_TYPE);
this.booleanType = createType(addTypes, registry, JSTypeNative.BOOLEAN_TYPE); this.booleanType = createType(addTypes, registry, JSTypeNative.BOOLEAN_TYPE);
namer = new DefaultNameGenerator();
} }


@Override @Override
Expand Down Expand Up @@ -126,18 +127,16 @@ private void visitForOf(Node node, Node parent) {
IR.getprop(iterName.cloneTree(), withStringType(IR.string("next"))), IR.getprop(iterName.cloneTree(), withStringType(IR.string("next"))),
iteratorNextType)), iteratorNextType)),
iIterableResultType); iIterableResultType);
String variableName; String iteratorResultName = ITER_RESULT;
Token declType; if (NodeUtil.isNameDeclaration(variable)) {
if (variable.isName()) { iteratorResultName += variable.getFirstChild().getString();
declType = Token.NAME; } else if (variable.isName()) {
variableName = variable.getQualifiedName(); iteratorResultName += variable.getString();
} else { } else {
Preconditions.checkState(NodeUtil.isNameDeclaration(variable), // give arbitrary lhs expressions an arbitrary name
"Expected var, let, or const. Got %s", variable); iteratorResultName += namer.generateNextName();
declType = variable.getToken();
variableName = variable.getFirstChild().getQualifiedName();
} }
Node iterResult = withType(IR.name(ITER_RESULT + variableName), iIterableResultType); Node iterResult = withType(IR.name(iteratorResultName), iIterableResultType);
iterResult.makeNonIndexable(); iterResult.makeNonIndexable();


Node call = Es6ToEs3Util.makeIterator(compiler, iterable); Node call = Es6ToEs3Util.makeIterator(compiler, iterable);
Expand Down Expand Up @@ -182,21 +181,25 @@ private void visitForOf(Node node, Node parent) {
withType(IR.assign(iterResult.cloneTree(), getNext.cloneTree()), iIterableResultType); withType(IR.assign(iterResult.cloneTree(), getNext.cloneTree()), iIterableResultType);


Node declarationOrAssign; Node declarationOrAssign;
if (declType == Token.NAME) { if (!NodeUtil.isNameDeclaration(variable)) {
declarationOrAssign = declarationOrAssign =
withType( withType(
IR.assign( IR.assign(
withType(IR.name(variableName).useSourceInfoFrom(variable), typeParam), withType(variable.cloneTree().setJSDocInfo(null), typeParam),
withType( withType(
IR.getprop(iterResult.cloneTree(), withStringType(IR.string("value"))), IR.getprop(iterResult.cloneTree(), withStringType(IR.string("value"))),
typeParam)), typeParam)),
typeParam); typeParam);
declarationOrAssign.setJSDocInfo(varJSDocInfo); declarationOrAssign.setJSDocInfo(varJSDocInfo);
declarationOrAssign = IR.exprResult(declarationOrAssign); declarationOrAssign = IR.exprResult(declarationOrAssign);
} else { } else {
declarationOrAssign = new Node( Token declarationType = variable.getToken(); // i.e. VAR, CONST, or LET.
declType, declarationOrAssign =
withType(IR.name(variableName).useSourceInfoFrom(variable.getFirstChild()), typeParam)); new Node(
declarationType,
IR.name(variable.getFirstChild().getString())
.useSourceInfoFrom(variable.getFirstChild()))
.setJSType(typeParam);
declarationOrAssign.getFirstChild().addChildToBack( declarationOrAssign.getFirstChild().addChildToBack(
withType( withType(
IR.getprop(iterResult.cloneTree(), withStringType(IR.string("value"))), IR.getprop(iterResult.cloneTree(), withStringType(IR.string("value"))),
Expand Down
34 changes: 34 additions & 0 deletions test/com/google/javascript/jscomp/Es6ForOfConverterTest.java
Expand Up @@ -193,6 +193,40 @@ public void testForOfOnNonIterable() {
TypeValidator.TYPE_MISMATCH_WARNING); TypeValidator.TYPE_MISMATCH_WARNING);
} }


public void testForOfWithQualifiedNameInitializer() {
// TODO(b/79532975): handle this case in the type checker and remove disableTypeCheck();
disableTypeCheck();
disableTypeInfoValidation();
test(
"var obj = {a: 0}; for (obj.a of [1,2,3]) { console.log(obj.a); }",
lines(
"var obj = {a: 0};",
"for (var $jscomp$iter$0 = $jscomp.makeIterator([1,2,3]),",
" $jscomp$key$a = $jscomp$iter$0.next();",
" !$jscomp$key$a.done; $jscomp$key$a = $jscomp$iter$0.next()) {",
" obj.a = $jscomp$key$a.value;",
" {",
" console.log(obj.a);",
" }",
"}"));
}

public void testForOfWithComplexInitializer() {
// TODO(b/79532975): handle this case in the type checker and remove disableTypeCheck();
disableTypeCheck();
disableTypeInfoValidation();
test(
"function f() { return {}; } for (f()['x' + 1] of [1,2,3]) {}",
lines(
"function f() { return {}; }",
"for (var $jscomp$iter$0 = $jscomp.makeIterator([1,2,3]),",
" $jscomp$key$a = $jscomp$iter$0.next();",
" !$jscomp$key$a.done; $jscomp$key$a = $jscomp$iter$0.next()) {",
" f()['x' + 1] = $jscomp$key$a.value;",
" {}",
"}"));
}

@Override @Override
protected Compiler createCompiler() { protected Compiler createCompiler() {
return new NoninjectingCompiler(); return new NoninjectingCompiler();
Expand Down

0 comments on commit 784b4af

Please sign in to comment.