Skip to content

Commit

Permalink
AsyncIter Parser and Code Generation
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=202970151
  • Loading branch information
MatthewMerrill authored and lauraharker committed Jul 2, 2018
1 parent f055891 commit bfce7ca
Show file tree
Hide file tree
Showing 14 changed files with 370 additions and 35 deletions.
15 changes: 15 additions & 0 deletions src/com/google/javascript/jscomp/AstValidator.java
Expand Up @@ -185,6 +185,9 @@ public void validateStatement(Node n, boolean isAmbient) {
case FOR_OF:
validateForOf(n);
return;
case FOR_AWAIT_OF:
validateForAwaitOf(n);
return;
case WHILE:
validateWhile(n);
return;
Expand Down Expand Up @@ -908,6 +911,9 @@ private void validateFunctionFeatures(Node n) {
if (n.isAsyncFunction()) {
validateFeature(Feature.ASYNC_FUNCTIONS, n);
}
if (n.isAsyncFunction() && n.isGeneratorFunction()) {
validateFeature(Feature.ASYNC_GENERATORS, n);
}
}

private void validateFunctionBody(Node n, boolean noBlock) {
Expand Down Expand Up @@ -1172,6 +1178,15 @@ private void validateForOf(Node n) {
validateBlock(n.getLastChild());
}

private void validateForAwaitOf(Node n) {
validateFeature(Feature.FOR_AWAIT_OF, n);
validateNodeType(Token.FOR_AWAIT_OF, n);
validateChildCount(n);
validateVarOrAssignmentTarget(n.getFirstChild());
validateExpression(n.getSecondChild());
validateBlock(n.getLastChild());
}

private void validateVarOrOptionalExpression(Node n) {
if (NodeUtil.isNameDeclaration(n)) {
validateNameDeclarationHelper(n.getToken(), n);
Expand Down
33 changes: 25 additions & 8 deletions src/com/google/javascript/jscomp/CodeGenerator.java
Expand Up @@ -547,15 +547,15 @@ protected void add(Node n, Context context) {
add("static ");
}

if (n.isMemberFunctionDef() && n.getFirstChild().isAsyncFunction()) {
add("async ");
}

if (!n.isMemberVariableDef() && n.getFirstChild().isGeneratorFunction()) {
checkState(type == Token.MEMBER_FUNCTION_DEF, n);
add("*");
}

if (n.isMemberFunctionDef() && n.getFirstChild().isAsyncFunction()) {
add("async ");
}

switch (type) {
case GETTER_DEF:
// Get methods have no parameters.
Expand Down Expand Up @@ -703,6 +703,20 @@ protected void add(Node n, Context context) {
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
break;

case FOR_AWAIT_OF:
Preconditions.checkState(childCount == 3, n);
add("for await");
cc.maybeInsertSpace();
add("(");
add(first);
cc.maybeInsertSpace();
add("of");
cc.maybeInsertSpace();
add(first.getNext());
add(")");
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
break;

case DO:
Preconditions.checkState(childCount == 2, n);
add("do");
Expand Down Expand Up @@ -1014,10 +1028,13 @@ protected void add(Node n, Context context) {
add("get ");
} else if (n.getBooleanProp(Node.COMPUTED_PROP_SETTER)) {
add("set ");
} else if (last.getBooleanProp(Node.GENERATOR_FN)) {
add("*");
} else if (last.isAsyncFunction()) {
add("async");
} else {
if (last.isAsyncFunction()) {
add("async");
}
if (last.getBooleanProp(Node.GENERATOR_FN)) {
add("*");
}
}
add("[");
add(first);
Expand Down
Expand Up @@ -167,6 +167,7 @@ void populate() {

case FOR:
case FOR_OF:
case FOR_AWAIT_OF:
case FOR_IN:
case SWITCH:
scanVars(n, null, scope);
Expand Down
12 changes: 11 additions & 1 deletion src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -1518,6 +1518,7 @@ static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) {
case AWAIT:
case FOR_IN: // assigns to a loop LHS
case FOR_OF: // assigns to a loop LHS
case FOR_AWAIT_OF: // assigns to a loop LHS
return true;
case CALL:
return NodeUtil.functionCallHasSideEffects(n, compiler);
Expand Down Expand Up @@ -2414,7 +2415,7 @@ static boolean isVanillaFunction(Node n) {
}

public static boolean isEnhancedFor(Node n) {
return n.isForOf() || n.isForIn();
return n.isForOf() || n.isForAwaitOf() || n.isForIn();
}

public static boolean isAnyFor(Node n) {
Expand All @@ -2429,6 +2430,7 @@ static boolean isLoopStructure(Node n) {
case FOR:
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
case DO:
case WHILE:
return true;
Expand All @@ -2447,6 +2449,7 @@ static Node getLoopCodeBlock(Node n) {
case FOR:
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
case WHILE:
return n.getLastChild();
case DO:
Expand Down Expand Up @@ -2481,6 +2484,7 @@ public static boolean isControlStructure(Node n) {
case FOR:
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
case DO:
case WHILE:
case WITH:
Expand Down Expand Up @@ -2510,6 +2514,7 @@ static boolean isControlStructureCodeBlock(Node parent, Node n) {
case FOR:
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
case WHILE:
case LABEL:
case WITH:
Expand Down Expand Up @@ -2543,6 +2548,7 @@ static Node getConditionExpression(Node n) {
return n.getSecondChild();
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
case CASE:
return null;
default:
Expand Down Expand Up @@ -2577,6 +2583,7 @@ static boolean createsBlockScope(Node n) {
case FOR:
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
case SWITCH:
case CLASS:
return true;
Expand Down Expand Up @@ -3311,6 +3318,7 @@ public static boolean isLValue(Node n) {
case FOR:
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
return parent.getFirstChild() == n;
case ARRAY_PATTERN:
case STRING_KEY:
Expand Down Expand Up @@ -3511,6 +3519,7 @@ private static Node getEnclosingTarget(Node targetNode) {

case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
// e.g. `for ({length} in obj) {}` // targetNode is `{length}`
// e.g. `for ({length} of obj) {}` // targetNode is `{length}`
checkState(targetIsFirstChild, targetNode);
Expand Down Expand Up @@ -4240,6 +4249,7 @@ private static void getLhsNodesHelper(Node n, List<Node> lhsNodes) {
return;
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
// Enhanced for loops assign to variables in their first child
// e.g.
// for (some.prop in someObj) {...
Expand Down
61 changes: 61 additions & 0 deletions src/com/google/javascript/jscomp/RewriteAsyncIteration.java
@@ -0,0 +1,61 @@
/*
* Copyright 2014 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;

import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
import com.google.javascript.rhino.Node;

/**
* Noop pass reporting an error if scripts attempt to transpile asynchronous generators or
* for-await-of loops - these features are not yet ready to be transpiled.
*/
public final class RewriteAsyncIteration extends NodeTraversal.AbstractPostOrderCallback
implements HotSwapCompilerPass {

static final DiagnosticType CANNOT_CONVERT_ASYNC_ITERATION_YET =
DiagnosticType.error(
"JSC_CANNOT_CONVERT_ASYNC_ITERATION_YET",
"Cannot convert async iteration/generators yet.");

private static final FeatureSet transpiledFeatures =
FeatureSet.BARE_MINIMUM.with(Feature.ASYNC_GENERATORS, Feature.FOR_AWAIT_OF);

private final AbstractCompiler compiler;

public RewriteAsyncIteration(AbstractCompiler compiler) {
this.compiler = compiler;
}

@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
TranspilationPasses.processTranspile(compiler, scriptRoot, transpiledFeatures, this);
TranspilationPasses.maybeMarkFeaturesAsTranspiledAway(compiler, transpiledFeatures);
}

@Override
public void process(Node externs, Node root) {
TranspilationPasses.processTranspile(compiler, root, transpiledFeatures, this);
TranspilationPasses.maybeMarkFeaturesAsTranspiledAway(compiler, transpiledFeatures);
}

@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isForAwaitOf() || (n.isAsyncFunction() && n.isGeneratorFunction())) {
compiler.report(JSError.make(n, CANNOT_CONVERT_ASYNC_ITERATION_YET));
}
}
}
20 changes: 20 additions & 0 deletions src/com/google/javascript/jscomp/TranspilationPasses.java
Expand Up @@ -16,6 +16,8 @@

package com.google.javascript.jscomp;

import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES2018;
import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES2018_MODULES;
import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES6;
import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES7;
import static com.google.javascript.jscomp.parsing.parser.FeatureSet.ES8;
Expand Down Expand Up @@ -72,6 +74,11 @@ public static void addPreTypecheckTranspilationPasses(
// parameter can be removed.
static void addPreTypecheckTranspilationPasses(
List<PassFactory> passes, CompilerOptions options, boolean doEs6ExternsCheck) {

if (options.needsTranspilationFrom(ES2018)) {
passes.add(rewriteAsyncIteration);
}

if (options.needsTranspilationFrom(ES_NEXT)) {
passes.add(rewriteObjRestSpread);
}
Expand Down Expand Up @@ -195,6 +202,19 @@ protected FeatureSet featureSet() {
}
};

private static final PassFactory rewriteAsyncIteration =
new HotSwapPassFactory("rewriteAsyncIteration") {
@Override
protected HotSwapCompilerPass create(final AbstractCompiler compiler) {
return new RewriteAsyncIteration(compiler);
}

@Override
protected FeatureSet featureSet() {
return ES2018_MODULES;
}
};

private static final PassFactory rewriteObjRestSpread =
new HotSwapPassFactory("rewriteObjRestSpread") {
@Override
Expand Down
19 changes: 19 additions & 0 deletions src/com/google/javascript/jscomp/parsing/IRFactory.java
Expand Up @@ -78,6 +78,7 @@
import com.google.javascript.jscomp.parsing.parser.trees.ExportSpecifierTree;
import com.google.javascript.jscomp.parsing.parser.trees.ExpressionStatementTree;
import com.google.javascript.jscomp.parsing.parser.trees.FinallyTree;
import com.google.javascript.jscomp.parsing.parser.trees.ForAwaitOfStatementTree;
import com.google.javascript.jscomp.parsing.parser.trees.ForInStatementTree;
import com.google.javascript.jscomp.parsing.parser.trees.ForOfStatementTree;
import com.google.javascript.jscomp.parsing.parser.trees.ForStatementTree;
Expand Down Expand Up @@ -489,6 +490,7 @@ private static boolean isBreakTarget(Node n) {
case FOR:
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
case WHILE:
case DO:
case SWITCH:
Expand All @@ -503,6 +505,7 @@ private static boolean isContinueTarget(Node n) {
case FOR:
case FOR_IN:
case FOR_OF:
case FOR_AWAIT_OF:
case WHILE:
case DO:
return true;
Expand Down Expand Up @@ -1355,6 +1358,16 @@ Node processForOf(ForOfStatementTree loopNode) {
transformBlock(loopNode.body));
}

Node processForAwaitOf(ForAwaitOfStatementTree loopNode) {
maybeWarnForFeature(loopNode, Feature.FOR_AWAIT_OF);
Node initializer = transform(loopNode.initializer);
return newNode(
Token.FOR_AWAIT_OF,
initializer,
transform(loopNode.collection),
transformBlock(loopNode.body));
}

Node processForLoop(ForStatementTree loopNode) {
Node node = newNode(
Token.FOR,
Expand Down Expand Up @@ -1416,6 +1429,10 @@ Node processFunction(FunctionDeclarationTree functionTree) {
maybeWarnForFeature(functionTree, Feature.ASYNC_FUNCTIONS);
}

if (isGenerator && isAsync) {
maybeWarnForFeature(functionTree, Feature.ASYNC_GENERATORS);
}

IdentifierToken name = functionTree.name;
Node newName;
if (name != null) {
Expand Down Expand Up @@ -2981,6 +2998,8 @@ public Node process(ParseTree node) {
return processAwait(node.asAwaitExpression());
case FOR_OF_STATEMENT:
return processForOf(node.asForOfStatement());
case FOR_AWAIT_OF_STATEMENT:
return processForAwaitOf(node.asForAwaitOfStatement());

case EXPORT_DECLARATION:
return processExportDecl(node.asExportDeclaration());
Expand Down
Expand Up @@ -153,6 +153,10 @@ public enum Feature {
OBJECT_LITERALS_WITH_SPREAD("object literals with spread", LangVersion.ES_NEXT),
OBJECT_PATTERN_REST("object pattern rest", LangVersion.ES_NEXT),

// https://github.com/tc39/proposal-async-iteration
ASYNC_GENERATORS("async generator functions", LangVersion.ES2018),
FOR_AWAIT_OF("for-await-of loop", LangVersion.ES2018),

// ES6 typed features that are not at all implemented in browsers
ACCESSIBILITY_MODIFIER("accessibility modifier", LangVersion.TYPESCRIPT),
AMBIENT_DECLARATION("ambient declaration", LangVersion.TYPESCRIPT),
Expand Down

0 comments on commit bfce7ca

Please sign in to comment.