Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(832)

Unified Diff: pkg/compiler/lib/src/js/rewrite_async.dart

Issue 839323003: Implementation of async-await transformation on js ast. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/compiler/dart2js/async_await_js_transform_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/js/rewrite_async.dart
diff --git a/pkg/compiler/lib/src/js/rewrite_async.dart b/pkg/compiler/lib/src/js/rewrite_async.dart
new file mode 100644
index 0000000000000000000000000000000000000000..cf2a31c274685d03eabfba63ccd98344b1d63ee5
--- /dev/null
+++ b/pkg/compiler/lib/src/js/rewrite_async.dart
@@ -0,0 +1,1292 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library rewrite_async;
+
+import "js.dart";
+import "../helpers/helpers.dart";
+import 'dart:collection';
+import "dart:math" show max;
+import 'package:compiler/src/util/util.dart';
+
+class AsyncFinder extends BaseVisitor {
+ @override
+ visitFun(Fun node) {
+ switch (node.asyncModifier) {
+ case const AsyncModifier.sync():
+ node.visitChildren(this);
+ break;
+ case const AsyncModifier.async():
+
+ node.visitChildren(this);
+ break;
+ case const AsyncModifier.syncStar():
+ throw "Sync* is not supported yet";
+ case const AsyncModifier.asyncStar():
+ throw "Async* is not supported yet";
+ }
+ }
+
+}
+
+class AnalysisPrinter extends BaseVisitor {
+ int indent = 0;
+ var awaits;
+ visitNode(Node node) {
+ indent ++;
+ node.visitChildren(this);
+ indent --;
+ }
+}
+
+class Analysis extends NodeVisitor {
+ Set<Node> awaits = new Set<Node>();
+
+ Map<Node, Node> targets = new Map<Node, Node>();
+ List<Node> loopsAndSwitches = new List<Node>();
+ List<LabeledStatement> labelledStatements = new List<LabeledStatement>();
+
+ bool visit(Node node) {
+ bool result = node.accept(this);
floitsch 2015/01/20 16:06:15 don't call it "result".
sigurdm 2015/02/02 10:20:15 Called it containsAwait
+ if (result) {
+ awaits.add(node);
+ }
+ return result;
+ }
+
+ analyze(Fun node) {
+ visit(node.body);
+ }
+
+ @override
+ visitAccess(PropertyAccess node) {
+ bool x = visit(node.receiver);
+ bool y = visit(node.selector);
+ return x || y;
+ }
+
+ @override
+ visitArrayHole(ArrayHole node) {
+ // TODO: implement visitArrayHole
floitsch 2015/01/20 16:06:15 should be trivial. There is no expression in the h
sigurdm 2015/02/02 10:20:14 Done.
+ }
+
+ @override
+ visitArrayInitializer(ArrayInitializer node) {
+ // TODO: implement visitArrayInitializer
+ }
+
+ @override
+ visitAssignment(Assignment node) {
+ bool x = visit(node.leftHandSide);
floitsch 2015/01/20 16:06:15 x -> left y -> right
sigurdm 2015/02/02 10:20:15 Done.
+ bool y = node.value == null
+ ? false
+ : visit(node.value);
+ return x || y;
+ }
+
+ @override
+ visitAwait(Await node) {
+ visit(node.expression);
+ return true;
+ }
+
+ @override
+ visitBinary(Binary node) {
+ bool x = visit(node.left);
+ bool y = visit(node.right);
+ return x || y;
+ }
+
+ @override
+ visitBlob(Blob node) {
+ return false;
+ }
+
+ @override
+ visitBlock(Block node) {
+ bool result = false;
+ for (var s in node.statements) {
+ if (visit(s)) result = true;
+ }
+ return result;
+ }
+
+ @override
+ visitBreak(Break node) {
+ if (node.targetLabel != null) {
+ targets[node] = labelledStatements.lastWhere(
+ (LabeledStatement stm) => stm.label == node.targetLabel);
+ } else {
+ targets[node] = loopsAndSwitches.last;
+ }
+ return false;
+ }
+
+ @override
+ visitCall(Call node) {
+ bool result = visit(node.target);
+ for (var s in node.arguments) {
+ if (visit(s)) result = true;
+ }
+ return result;
+ }
+
+ @override
+ visitCase(Case node) {
+ bool x = visit(node.expression);
+ bool y = visit(node.body);
+ return x || y;
+ }
+
+ @override
+ visitCatch(Catch node) {
+ bool x = visit(node.declaration);
+ bool y = visit(node.body);
+ return x || y;
+ }
+
+ @override
+ visitComment(Comment node) {
+ return false;
+ }
+
+ @override
+ visitConditional(Conditional node) {
+ bool x = visit(node.condition);
+ bool y = visit(node.then);
+ bool z = visit(node.otherwise);
+ return x || y || z;
+ }
+
+ @override
+ visitContinue(Continue node) {
+ if (node.targetLabel != null) {
+ targets[node] = labelledStatements.lastWhere(
+ (LabeledStatement stm) => stm.label == node.targetLabel);
+ } else {
+ targets[node] = loopsAndSwitches.lastWhere(
+ (Node node) => node is! Switch);
+ }
+ return false;
+ }
+
+ @override
+ visitDefault(Default node) {
+ return visit(node.body);
+ }
+
+ @override
+ visitDo(Do node) {
+ loopsAndSwitches.add(node);
+ bool x = visit(node.body);
+ bool y = visit(node.condition);
+ loopsAndSwitches.removeLast();
+ return x || y;
+ }
+
+ @override
+ visitEmptyStatement(EmptyStatement node) {
+ return false;
+ }
+
+ @override
+ visitExpressionStatement(ExpressionStatement node) {
+ return visit(node.expression);
+ }
+
+ @override
+ visitFor(For node) {
+ loopsAndSwitches.add(node);
floitsch 2015/01/20 16:06:15 I would add the loopsAndSwitches only for the body
sigurdm 2015/02/02 10:20:15 Good point Done
+ bool x = visit(node.init);
+ bool y = visit(node.condition);
+ bool z = visit(node.update);
+ bool u = visit(node.body);
+ loopsAndSwitches.removeLast();
+ return x || y || z || u;
+ }
+
+ @override
+ visitForIn(ForIn node) {
+ loopsAndSwitches.add(node);
+ loopsAndSwitches.removeLast();
+ // TODO: implement visitForIn
+ throw "BADDD";
+ }
+
+ @override
+ visitFun(Fun node) {
+ return false;
+ }
+
+ @override
+ visitFunctionDeclaration(FunctionDeclaration node) {
+ return false;
+ }
+
+ @override
+ visitIf(If node) {
+ bool x = visit(node.condition);
+ bool y = visit(node.then);
+ bool z = visit(node.otherwise);
+ return x || y || z;
+ }
+
+ @override
+ visitInterpolatedExpression(InterpolatedExpression node) {
+ // TODO: implement visitInterpolatedExpression
+ }
+
+ @override
+ visitInterpolatedLiteral(InterpolatedLiteral node) {
+ // TODO: implement visitInterpolatedLiteral
+ }
+
+ @override
+ visitInterpolatedParameter(InterpolatedParameter node) {
+ // TODO: implement visitInterpolatedParameter
+ }
+
+ @override
+ visitInterpolatedSelector(InterpolatedSelector node) {
+ // TODO: implement visitInterpolatedSelector
+ }
+
+ @override
+ visitInterpolatedStatement(InterpolatedStatement node) {
+ // TODO: implement visitInterpolatedStatement
+ }
+
+ @override
+ visitLabeledStatement(LabeledStatement node) {
+ labelledStatements.add(node);
+ bool result = visit(node.body);
+ labelledStatements.removeLast();
+ return result;
+ }
+
+ @override
+ visitLiteralBool(LiteralBool node) {
+ return false;
+ }
+
+ @override
+ visitLiteralExpression(LiteralExpression node) {
+ // TODO: implement visitLiteralExpression
+ }
+
+ @override
+ visitLiteralNull(LiteralNull node) {
+ return false;
+ }
+
+ @override
+ visitLiteralNumber(LiteralNumber node) {
+ return false;
+ }
+
+ @override
+ visitLiteralStatement(LiteralStatement node) {
+ // TODO: implement visitLiteralStatement
+ }
+
+ @override
+ visitLiteralString(LiteralString node) {
+ return false;
+ }
+
+ @override
+ visitNamedFunction(NamedFunction node) {
+ return false;
+ }
+
+ @override
+ visitNew(New node) {
+ return visitCall(node);
+ }
+
+ @override
+ visitObjectInitializer(ObjectInitializer node) {
+ bool result = false;
+ for (Property property in node.properties) {
+ if (visit(property)) result = true;
+ }
+ return result;
+ }
+
+ @override
+ visitParameter(Parameter node) {
+ }
+
+ @override
+ visitPostfix(Postfix node) {
+ return visit(node.argument);
+ }
+
+ @override
+ visitPrefix(Prefix node) {
+ return visit(node.argument);
+ }
+
+ @override
+ visitProgram(Program node) {
+ throw "Unexpected";
+ }
+
+ @override
+ visitProperty(Property node) {
+ return visit(node.value);
+ }
+
+ @override
+ visitRegExpLiteral(RegExpLiteral node) {
+ return false;
+ }
+
+ @override
+ visitReturn(Return node) {
+ if (node.value == null) return false;
+ return visit(node.value);
+ }
+
+ @override
+ visitSwitch(Switch node) {
+ loopsAndSwitches.add(node);
+ bool result = visit(node.key);
+ for (SwitchClause clause in node.cases) {
+ if (visit(clause)) result = true;
+ }
+ loopsAndSwitches.removeLast();
+ return result;
+ }
+
+ @override
+ visitThis(This node) {
+ return false;
+ }
+
+ @override
+ visitThrow(Throw node) {
+ return visit(node.expression);
+ }
+
+ @override
+ visitTry(Try node) {
+ bool x = visit(node.body);
+ bool y = node.catchPart == null ? false : visit(node.catchPart);
+ bool z = node.finallyPart == null ? false : visit(node.finallyPart);
+ return x || y || z;
+ }
+
+ @override
+ visitVariableDeclaration(VariableDeclaration node) {
+ return false;
+ }
+
+ @override
+ visitVariableDeclarationList(VariableDeclarationList node) {
+ bool result = false;
+ for (VariableInitialization init in node.declarations) {
+ if (visit(init)) result = true;
+ }
+ return result;
+ }
+
+ @override
+ visitVariableInitialization(VariableInitialization node) {
+ return visitAssignment(node);
+ }
+
+ @override
+ visitVariableUse(VariableUse node) {
+ return false;
+ }
+
+ @override
+ visitWhile(While node) {
+ loopsAndSwitches.add(node);
+ // Be careful to avoid short-circuit.
+ bool x = visit(node.condition);
+ bool y = visit(node.body);
+ loopsAndSwitches.removeLast();
+ return x || y;
+ }
+}
+
+class AsyncRewriter extends NodeVisitor {
+
+ // The set of all nodes containing an await nested somewhere.
+ Set<Node> __containsAwait;
+
+ // Local variables are hoisted to the top of the function, so we collect them
+ // here.
+ List<VariableDeclaration> localVariables = new List<VariableDeclaration>();
+
+ Map<Node, Node> __targets = new Map<Node, Node>();
+ Map<Node, int> continueLabels = new Map<Node, int>();
+ Map<Node, int> breakLabels = new Map<Node, int>();
+
+ List<Pair<String, String>> variableRenamings =
+ new List<Pair<String, String>>();
+ Set<String> usedNames = new Set<String>();
+
+ List<int> errorHandlerLabels = new List<int>();
+
+ // TODO(sigurdm): use namer for these.
+ String resultName = "__result";
+ String helperName = "__helper";
+ String gotoName = "__goto";
+ String handlerName = "__handler";
+ String errorName = "__error";
+
+ int __currentLabel = 0;
+
+ int highWaterMark = 0;
+ int currentTempVarIndex = 0;
+
+ int allocateTempVar() {
+ assert(highWaterMark >= currentTempVarIndex);
+ currentTempVarIndex++;
+ highWaterMark = max(currentTempVarIndex, highWaterMark);
+ return currentTempVarIndex;
+ }
+
+ deallocateTempVar([howMany = 1]) {
+ currentTempVarIndex -= howMany;
+ }
+
+ Expression useTempVar(int i) {
+ return new VariableUse("__temp$i");
+ }
+
+
+ String freshName(String originalName) {
+ String result = "__$originalName";
+ int counter = 1;
+ while (usedNames.contains(result)) {
+ result = "__$originalName$counter";
+ ++counter;
+ }
+ usedNames.add(result);
+ return result;
+ }
+
+ /// We collect all the pieces in this map, and output a switch with a case
+ /// for each label.
+ /// The order is important, therefore the type is explicitly LinkedHashMap.
floitsch 2015/01/20 16:06:15 New line before.
sigurdm 2015/02/02 10:20:15 Done.
+ LinkedHashMap<int, List<Statement>> labelledParts =
+ new LinkedHashMap<int, List<Statement>>();
+
+ // Description of each label for readability of the non-minified output.
+ Map<int, String> labelComments = new Map<int, String>();
+
+ // True if the function has any try blocks containing await.
+ bool tryBlocks = false;
+
+ List<Statement> currentStatementBuffer;
+
+ void addStatement(Statement node) {
+ currentStatementBuffer.add(node);
+ }
+
+ void addExpressionStatement(Expression node) {
+ addStatement(new ExpressionStatement(node));
+ }
+
+ Expression methodCall(Expression callee,
+ String methodName,
+ List<Expression> args) {
+ return new Call(new PropertyAccess.field(callee, methodName), args);
+ }
+
+ int newLabel([String comment]) {
+ int result = __currentLabel;
+ __currentLabel++;
+ if (comment != null) {
+ labelComments[result] = comment;
+ }
+ return result;
+ }
+
+ void beginLabel(int label) {
+ assert(!labelledParts.containsKey(label));
+ labelledParts[label] = currentStatementBuffer = new List<Statement>();
+ addStatement(new Comment(labelComments[label]));
+ }
+
+ /// Returns a statement assigning to the `__goto` variable. This should be
+ /// followed by a break for the goto to be executed. Use [gotoWithBreak] or
+ /// [addGoto] for this.
+ Statement goto(int label) {
+ return new ExpressionStatement(
+ new Assignment(new VariableUse(gotoName), new LiteralNumber("$label")));
+ }
+
+ /// Returns a block with the same effect as [addGoto].
+ Block gotoAndBreak(int label) {
+ List<Statement> statements = new List<Statement>();
+ if (labelComments.containsKey(label)) {
+ statements.add(new Comment("goto ${labelComments[label]}"));
+ }
+ statements.add(goto(label));
+ statements.add(new Break(null));
+ return new Block(statements);
+ }
+
+ /// Add a goto to [label] including the break.
+ /// Will also insert a comment describing the label if available.
+ void addGoto(int label) {
+ if (labelComments.containsKey(label)) {
+ addStatement(new Comment("goto ${labelComments[label]}"));
+ }
+ addStatement(goto(label));
+ addStatement(new Break(null));
+ }
+
+ bool transform(Node node) {
floitsch 2015/01/20 16:06:14 shouldTransform
sigurdm 2015/02/02 10:20:15 Done.
+ return node is Break || node is Continue || __containsAwait.contains(node);
+ }
+
+ /// Main entry point.
+ Fun rewrite(Fun node) {
+ assert(node.asyncModifier == const AsyncModifier.async());
+ Analysis analysis = new Analysis();
+ analysis.analyze(node);
+ __containsAwait = analysis.awaits;
+ __targets = analysis.targets;
+ new AnalysisPrinter()..awaits = analysis.awaits
+ ..visitNode(node);
+ return node.accept(this);
+ }
+
+ visitStatement(Statement node) {
+ node.accept(this);
+ }
+
+ void visitExpressionIgnoreResult(Expression node) {
+ addExpressionStatement(node.accept(this));
+ }
+
+ Expression visitExpression(Expression node) {
+ return node.accept(this);
+ }
+
+ Expression _storeIfNecessary(Expression result) {
+ if (result is Literal) return result;
floitsch 2015/01/20 16:06:15 I guess this will be wrong, once you have list-lit
sigurdm 2015/02/02 10:20:14 Actually list-literals are not Literal, but Object
+ Expression tempVar = useTempVar(allocateTempVar());
+ addExpressionStatement(new Assignment(tempVar, result));
+ return tempVar;
+ }
+
+ withExpression(Expression node, fn(Expression result), {bool store}) {
+ int oldTempVarIndex = currentTempVarIndex;
+ Expression visited = visitExpression(node);
+ if (store) {
+ visited = _storeIfNecessary(visited);
+ }
+ var result = fn(visited);
+ currentTempVarIndex = oldTempVarIndex;
+ return result;
+ }
+
+ withExpression2(Expression node1, Expression node2,
+ fn(Expression result1, Expression result2)) {
+ int oldTempVarIndex = currentTempVarIndex;
+ Expression r1 = visitExpression(node1);
+ if (transform(node2)) {
+ r1 = _storeIfNecessary(r1);
+ }
+ Expression r2 = visitExpression(node2);
+ var result = fn(r1, r2);
+ currentTempVarIndex = oldTempVarIndex;
+ return result;
+ }
+
+ withExpressions(List<Node> nodes, fn(List<Node> results)) {
+ int oldTempVarIndex = currentTempVarIndex;
+ // Find last occurence of a 'transform' expression in [nodes].
+ // All expressions before that must be stored in temp-vars.
+ int lastTransformIndex = 0;
+ for (int i = nodes.length - 1; i >= 0; --i) {
+ if (transform(nodes[i])) {
+ lastTransformIndex = i;
+ break;
+ }
+ }
+ List<Node> visited = nodes.take(lastTransformIndex).map((Node node) {
+ return _storeIfNecessary(visitExpression(node));
+ }).toList();
+ visited.addAll(nodes.skip(lastTransformIndex).map((Node node) {
+ return visitExpression(node);
+ }));
+ var result = fn(visited);
+ currentTempVarIndex = oldTempVarIndex;
+ return result;
+ }
+
+ // [[Fun(a1, ..., an) S ]] =>
+ // Fun(a1, ..., an) {
+ // vars(S); // Declaration of local variables of S.
+ // var __goto = 0;
+ // var __handler = null; // The current error handler.
+ // Fun helper(__result) {
+ // while(true) {
+ // try {
+ // switch(__goto) {
+ // case 0:
+ // [[S]];
+ // }
+ // } catch(__error) {
+ // if (handler === null) throw error;
+ // __goto = __handler;
+ // __result = __error;
+ // }
+ // }
+ // }
+ // }
+ visitAsyncFun(Fun node) {
+ beginLabel(newLabel("Function start"));
+ visitStatement(node.body);
+ List<SwitchClause> clauses = labelledParts.keys.map((label) {
+ return new Case(new LiteralNumber("$label"),
+ new Block(labelledParts[label]));
+ }).toList();
+ Statement helperBody = new Switch(new VariableUse(gotoName),
+ clauses);
+ if (tryBlocks) {
+ helperBody = new Try(new Block([helperBody]),
floitsch 2015/01/20 16:06:15 consider using the `js` templates. You will actual
sigurdm 2015/02/02 10:20:14 Yeah - it is better like that.
+ new Catch((new VariableDeclaration(errorName)),
+ new Block([new If.noElse(new Binary("===",
+ new VariableUse(handlerName), new LiteralNull()),
+ new Throw(new VariableUse(errorName))),
+ new ExpressionStatement(new Assignment(
+ new VariableUse(resultName),
+ new VariableUse(errorName))),
+ new ExpressionStatement(new Assignment(
+ new VariableUse(gotoName),
+ new VariableUse(handlerName))),
+ ])), null);
+ }
+ helperBody = new While(new LiteralBool(true), helperBody);
+ helperBody = new Block([helperBody]);
+ List<VariableInitialization> inits =
+ [new VariableInitialization(new VariableDeclaration(gotoName),
+ new LiteralNumber("0")),
+ new VariableInitialization(new VariableDeclaration(handlerName),
+ new LiteralNull())];
+ inits.addAll(localVariables.map(
+ (VariableDeclaration decl) => new VariableInitialization(decl, null)));
+ VariableDeclarationList varDecl = new VariableDeclarationList(inits);
+ inits.addAll(new Iterable.generate(highWaterMark, (int i) {
+ return new VariableInitialization(
+ new VariableDeclaration("__temp${i + 1}"),
+ null);
+ }));
+ Fun helper = new Fun([new Parameter(resultName)], helperBody);
+ return new Fun(node.params,
+ new Block([new ExpressionStatement(varDecl),
+ new FunctionDeclaration(new VariableDeclaration(helperName),
+ helper),
+ new Return(
+ new New(new PropertyAccess.field(
+ new VariableUse("Future"), "microtask"),
+ [new VariableUse(helperName)]))]));
+ }
+
+ @override
+ visitFun(Fun node) {
+ switch (node.asyncModifier) {
+ case const AsyncModifier.sync():
+ return node;
+ case const AsyncModifier.async():
+ return visitAsyncFun(node);
+ case const AsyncModifier.syncStar():
+ throw "Sync* is not supported yet";
+ case const AsyncModifier.asyncStar():
+ throw "Async* is not supported yet";
+ }
+
+ }
+
+ @override
+ visitAccess(PropertyAccess node) {
+ return withExpression2(node.receiver, node.selector,
+ (receiver, selector) => new PropertyAccess(receiver, selector));
+ }
+
+ unreachable(Node node) {
+ throw("The transformer should never meet: $node");
floitsch 2015/01/20 16:06:15 If the function is intendend to stay, make it have
sigurdm 2015/02/02 10:20:14 Done.
+ }
+
+ @override
+ visitArrayHole(ArrayHole node) {
+ return node;
+ }
+
+ @override
+ visitArrayInitializer(ArrayInitializer node) {
+ withExpressions(node.elements, (elements) {
+ return new ArrayInitializer(elements);
+ });
+ }
+
+ // C([[e1 = e2]] =>
+ // C([[e1]] = [[e2]])
+ @override
+ visitAssignment(Assignment node) {
+ Expression leftHandSide = node.leftHandSide;
+ if (leftHandSide is VariableUse) {
+ return withExpression(node.value, (Expression value) {
+ return new Assignment(leftHandSide, value);
+ }, store: false);
+ } else if (leftHandSide is PropertyAccess) {
+ return withExpressions(
+ [leftHandSide.receiver, leftHandSide.selector, node.value],
+ (evaluated) {
+ return new Assignment(
+ new PropertyAccess(evaluated[0], evaluated[1]),
+ evaluated[2]);
+ });
+ } else {
+ throw "Unexpected assignment left hand side $leftHandSide";
+ }
+ }
+
+ // C([[await e]]) =>
+ // __goto = `newLabel(afterCall)
+ // [[e]].then(__helper, onError: (error) {__goto = `currentHandler } );
+ // case afterCall:
+ // C(__result);
+ @override
+ Expression visitAwait(Await node) {
+ int afterWait = newLabel("Returning from await.");
+ withExpression(node.expression, (Expression expression) {
+ addStatement(goto(afterWait));
+ addStatement(new Return(new Call(
+ new PropertyAccess.field(expression, "then"),
+ [new VariableUse(helperName),
+ new Fun([new Parameter(errorName)],
+ new Block([new ExpressionStatement(
+ new Assignment(new VariableUse(gotoName),
+ currentErrorHandler)),
+ new ExpressionStatement(new Call(
+ new VariableUse(helperName),
+ [new VariableUse(errorName)]))]))])));
+ }, store: false);
+ beginLabel(afterWait);
+ return new VariableUse(resultName);
+ }
+
+ Expression get currentErrorHandler {
+ return errorHandlerLabels.isEmpty
+ ? new LiteralNull()
+ : new LiteralNumber("${errorHandlerLabels.last}");
+ }
+
+ // TODO short-circuiting operators!!
+ @override
+ Expression visitBinary(Binary node) {
+ return withExpression2(node.left, node.right,
+ (left, right) => new Binary(node.op, left, right));
+ }
+
+ @override
+ visitBlob(Blob node) {
+ return node;
+ }
+
+ @override
+ void visitBlock(Block node) {
+ for (Statement statement in node.statements) {
+ visitStatement(statement);
+ }
+ }
+
+ @override
+ void visitBreak(Break node) {
+ int target = breakLabels[__targets[node]];
+ if (target != null) {
+ addGoto(target);
+ } else {
+ addStatement(node);
+ }
+ }
+
+ @override
+ Expression visitCall(Call node) {
+ bool storeTarget = node.arguments.any(transform);
+ return withExpression(node.target, (target) {
+ return withExpressions(node.arguments, (List<Expression> arguments) {
+ return new Call(target, arguments);
+ });
+ }, store: storeTarget);
+ }
+
+ @override
+ visitCase(Case node) {
+ return unreachable(node);
+ }
+
+ @override
+ visitCatch(Catch node) {
+ return unreachable(node);
+ }
+
+ @override
+ visitComment(Comment node) {
+ addStatement(node);
+ }
+
+ @override
+ Expression visitConditional(Conditional node) {
+ if (!transform(node.then) && !transform(node.otherwise)) {
+ return withExpression(node.condition, (Expression condition) {
+ return new Conditional(condition, node.then, node.otherwise);
+ });
+ }
+ int thenLabel = newLabel("then");
+ int joinLabel = newLabel("join");
+ int elseLabel = newLabel("else");
+ withExpression(node.condition, (Expression condition) {
+ addExpressionStatement(new Assignment(new VariableUse(gotoName),
+ new Conditional(condition, new LiteralNumber("$thenLabel"),
+ new LiteralNumber("$elseLabel"))));
+ }, store: false);
+ addStatement(new Break(null));
+ beginLabel(thenLabel);
+ withExpression(node.then, (Expression value) {
+ addExpressionStatement(new Assignment(
+ new VariableUse(resultName),
+ value));
+ }, store: false);
+ addGoto(joinLabel);
+ beginLabel(elseLabel);
+ withExpression(node.otherwise, (Expression value) {
+ addExpressionStatement(new Assignment(
+ new VariableUse(resultName),
+ value));
+ }, store: false);
+ beginLabel(joinLabel);
+ return new VariableUse(resultName);
+ }
+
+ @override
+ visitContinue(Continue node) {
+ int target = continueLabels[__targets[node]];
+ if (target != null) {
+ addGoto(target);
+ } else {
+ addStatement(node);
+ }
+ }
+
+ @override
+ visitDefault(Default node) => unreachable(node);
+
+ @override
+ visitDo(Do node) {
+ throw "TODO";
+ }
+
+ @override
+ visitEmptyStatement(EmptyStatement node) => node;
+
+ @override
+ visitExpressionStatement(ExpressionStatement node) {
+ if (node.expression is VariableDeclarationList) {
+ // Treat VariableDeclarationList as a statement.
+ visitVariableDeclarationList(node.expression);
+ } else {
+ visitExpressionIgnoreResult(node.expression);
+ }
+ }
+
+ @override
+ visitFor(For node) {
+ throw "TODO";
+ }
+
+ @override
+ visitForIn(ForIn node) {
+ throw "TODO";
+ }
+
+ @override
+ visitFunctionDeclaration(FunctionDeclaration node) {
+ return node;
+ }
+
+ Block translateInBlock(Statement node) {
+ List<Statement> oldBuffer = currentStatementBuffer;
+ List<Statement> resultBuffer = currentStatementBuffer = new List();
+ visitStatement(node);
+ currentStatementBuffer = oldBuffer;
+ return new Block(resultBuffer);
+ }
+
+ @override
+ visitIf(If node) {
+ if (!transform(node.then) && !transform(node.otherwise)) {
+ withExpression(node.condition, (Expression condition) {
+ addStatement(new If(condition,
+ translateInBlock(node.then),
+ translateInBlock(node.otherwise)));
+ }, store: false);
+ return;
+ }
+ int thenLabel = newLabel("then");
+ int joinLabel = newLabel("join");
+ int elseLabel = node.otherwise is EmptyStatement
+ ? joinLabel
+ : newLabel("else");
+
+ withExpression(node.condition, (Expression condition) {
+ addExpressionStatement(new Assignment(new VariableUse(gotoName),
+ new Conditional(condition, new LiteralNumber("$thenLabel"),
+ new LiteralNumber("$elseLabel"))));
+ }, store: false);
+ addStatement(new Break(null));
+ beginLabel(thenLabel);
+ visitStatement(node.then);
+ if (node.otherwise is EmptyStatement) {
+ addGoto(joinLabel);
+ beginLabel(elseLabel);
+ visitStatement(node.otherwise);
+ }
+ beginLabel(joinLabel);
+ }
+
+ @override
+ visitInterpolatedExpression(InterpolatedExpression node) {
+ throw "Unsupported";
+ }
+
+ @override
+ visitInterpolatedLiteral(InterpolatedLiteral node) => throw "Unsupported";
+
+ @override
+ visitInterpolatedParameter(InterpolatedParameter node) => throw "Unsupported";
+
+ @override
+ visitInterpolatedSelector(InterpolatedSelector node) => throw "Unsupported";
+
+ @override
+ visitInterpolatedStatement(InterpolatedStatement node) => throw "Unsupported";
+
+ @override
+ visitLabeledStatement(LabeledStatement node) {
+ int breakLabel = newLabel("break ${node.label}");
+ int continueLabel = newLabel("continue ${node.label}");
+ breakLabels[node] = breakLabel;
+ continueLabels[node] = continueLabel;
+
+ beginLabel(continueLabel);
+ visitStatement(node.body);
+ beginLabel(breakLabel);
+ }
+
+ @override
+ visitLiteralBool(LiteralBool node) => node;
+
+ @override
+ visitLiteralExpression(LiteralExpression node) => throw "Unsupported";
+
+ @override
+ visitLiteralNull(LiteralNull node) => node;
+
+ @override
+ visitLiteralNumber(LiteralNumber node) => node;
+
+ @override
+ visitLiteralStatement(LiteralStatement node) => throw "Unsupported";
+
+ @override
+ visitLiteralString(LiteralString node) => node;
+
+ @override
+ visitNamedFunction(NamedFunction node) {
+ throw "TODO";
+ }
+
+ @override
+ visitNew(New node) {
+ bool storeTarget = node.arguments.any(transform);
+ return withExpression(node.target, (target) {
+ return withExpressions(node.arguments, (List<Expression> arguments) {
+ return new New(target, arguments);
+ });
+ }, store: storeTarget);
+ }
+
+ @override
+ visitObjectInitializer(ObjectInitializer node) {
+ return withExpressions(node.properties, (List<Node> properties) {
+ return new ObjectInitializer(properties);
+ });
+ }
+
+ @override
+ visitParameter(Parameter node) {
+ throw "Unexpected";
+ }
+
+ @override
+ visitPostfix(Postfix node) {
+ return withExpression(node.argument,
+ (Expression argument) => new Postfix(node.op, argument), store: false);
+ }
+
+ @override
+ visitPrefix(Prefix node) {
+ return withExpression(node.argument,
+ (Expression argument) => new Prefix(node.op, argument), store: false);
+ }
+
+ @override
+ visitProgram(Program node) => throw "Unsupported";
+
+ @override
+ visitProperty(Property node) {
+ return withExpression(node.value,
+ (Expression value) => new Property(node.name, value), store: false);
+ }
+
+ @override
+ visitRegExpLiteral(RegExpLiteral node) => node;
+
+ @override
+ visitReturn(Return node) {
+ if (node.value == null) {
+ addStatement(node);
+ } else {
+ withExpression(node.value, (Expression value) {
+ addStatement(new Return(value));
+ }, store: false);
+ }
+ }
+
+ // [[switch(e) {
+ // case i: Si;
+ // ...
+ // default: Sd;
+ // }]] =>
+ // case #before
+ // switch([[e]]) {
+ // case i: __goto = #i;
+ // ...
+ // default: __goto = #defaultLabel;
+ // }
+ // case #i:
+ // [[Si, breakLabels|#after, continueLabels|#before]]
+ // // Notice fallthough/
+ // ...
+ // case #defaultLabel:
+ // [[Si, breakLabels|#after, continueLabels|#before]]
+ // case #afterSwitch:
+ @override
+ visitSwitch(Switch node) {
+ if (!node.cases.any(transform)) {
+ // If only the key has an await, translation can be simplified.
+ withExpression(node.key, (Expression key) {
+ List<SwitchClause> cases = node.cases.map((SwitchClause clause) {
+ if (clause is Case) {
+ return new Case(clause.expression, translateInBlock(clause.body));
+ } else if (clause is Default) {
+ return new Default(translateInBlock(clause.body));
+ }
+ }).toList();
+ addStatement(new Switch(key, cases));
+ });
+ return;
+ }
+ int before = newLabel("switch");
+ int after = newLabel("after switch");
+ breakLabels[node] = after;
+
+ beginLabel(before);
+ List<int> labels = new List<int>(node.cases.length);
+
+ if (!node.cases.every(
+ (SwitchClause x) => !(x is Case && transform(x.expression)))){
+ int defaultIndex = null; // Null means no default was found.
+ // If there is an await in one of the keys, we have to use a chain of ifs.
+
+ withExpression(node.key, (Expression key) {
+ int i = 0;
+ for (SwitchClause clause in node.cases) {
+ if (clause is Default) {
+ // The default case is handled last.
+ defaultIndex = i;
+ labels[i] = newLabel("default");
+ continue;
+ } else if (clause is Case) {
+ labels[i] = newLabel("case");
+ withExpression(clause.expression, (expression) {
+ addStatement(new If.noElse(new Binary("===", key, expression),
+ gotoAndBreak(labels[i])));
+ }, store: false);
+ }
+ i++;
+ }
+ }, store: true);
+
+ if (defaultIndex == null) {
+ addGoto(after);
+ } else {
+ addGoto(labels[defaultIndex]);
+ }
+
+ } else {
+ bool hasDefault = false;
+ int i = 0;
+ List<SwitchClause> clauses = new List<SwitchClause>();
+ for (SwitchClause clause in node.cases) {
+ if (clause is Case) {
+ labels[i] = newLabel("case");
+ clauses.add(new Case(clause.expression, gotoAndBreak(labels[i])));
+ } else if (i is Default) {
+ labels[i] = newLabel("default");
+ clauses.add(new Default(gotoAndBreak(labels[i])));
+ hasDefault = true;
+ } else {
+ throw "Unknown switchclause $i";
+ }
+ i++;
+ }
+ withExpression(node.key, (Expression key) {
+ addStatement(new Switch(key, clauses));
+ }, store: false);
+ if (!hasDefault) {
+ addGoto(after);
+ }
+ }
+ for (int i = 0; i < labels.length; i++) {
+ beginLabel(labels[i]);
+ visitStatement(node.cases[i].body);
+ }
+ beginLabel(after);
+ }
+
+ @override
+ visitThis(This node) => node;
+
+ @override
+ visitThrow(Throw node) {
+ withExpression(node.expression, (Expression expression) {
+ addStatement(new Throw(expression));
+ }, store: false);
+ }
+
+ setHandler() {
+ addExpressionStatement(
+ new Assignment(new VariableUse(handlerName), currentErrorHandler));
+ }
+
+ @override
+ void visitTry(Try node) {
+ if (!transform(node)) {
+ Block body = translateInBlock(node.body);
+ Catch catchPart = node.catchPart == null
+ ? null
+ : new Catch(node.catchPart.declaration,
+ translateInBlock(node.catchPart.body));
+ Block finallyPart = node.finallyPart == null
+ ? null
+ : translateInBlock(node.finallyPart);
+ addStatement(new Try(body,
+ catchPart,
+ finallyPart));
+ return;
+ }
+ tryBlocks = true;
+ int handlerLabel = newLabel("catch");
+ int finallyLabel = newLabel("finally");
+ errorHandlerLabels.add(handlerLabel);
+ setHandler();
+ visitStatement(node.body);
+ errorHandlerLabels.removeLast();
+ setHandler();
+ addGoto(finallyLabel);
+ beginLabel(handlerLabel);
+ if (node.catchPart != null) {
+ String errorRename = freshName(node.catchPart.declaration.name);
+ localVariables.add(new VariableDeclaration(errorRename));
+ variableRenamings.add(
+ new Pair(node.catchPart.declaration.name, errorRename));
+ addExpressionStatement(new Assignment(
+ new VariableUse(errorRename),
+ new VariableUse(resultName)));
+ visitStatement(node.catchPart.body);
+ variableRenamings.removeLast();
+ }
+ beginLabel(finallyLabel);
+ if (node.finallyPart != null) {
+ visitStatement(node.finallyPart);
+ }
+ }
+
+ @override
+ visitVariableDeclaration(VariableDeclaration node) {
+ throw "TODO";
+ }
+
+ @override
+ void visitVariableDeclarationList(VariableDeclarationList node) {
+ // Declaration of local variables is hoisted outside the helper
+ // But the initialization is done here.
+ for (VariableInitialization initialization in node.declarations) {
+ VariableDeclaration declaration = initialization.declaration;
+ localVariables.add(declaration);
+ if (initialization.value != null) {
+ withExpression(initialization.value, (Expression value) {
+ addStatement(new ExpressionStatement(
+ new Assignment(declaration, value)));
+ }, store: false);
+ }
+ }
+ }
+
+ @override
+ visitVariableInitialization(VariableInitialization node) {
+ // These should be handled by visitVariableDeclarationList.
+ throw "Unexpected variableInitialization";
+ }
+
+ @override
+ visitVariableUse(VariableUse node) {
+ String renaming = variableRenamings.lastWhere(
+ (Pair renaming) => renaming.a == node.name, orElse: () {
+ return new Pair(null, null);
+ }).b;
+ if (renaming == null) return node;
+ return new VariableUse(renaming);
+ }
+
+ @override
+ visitWhile(While node) {
+ if (!transform(node.body)) {
+ // If only the condition has an await, translation can be simplified.
+ withExpression(node.condition, (Expression condition) {
+ addStatement(new While(condition, translateInBlock(node.body)));
+ }, store: false);
+ return;
+ }
+ int continueLabel = newLabel("while condition");
+ continueLabels[node] = continueLabel;
+ beginLabel(continueLabel);
+
+ int bodyLabel = newLabel("while body");
+ int after = newLabel("after while");
+ breakLabels[node] = after;
+ withExpression(node.condition, (Expression cond) {
+ addExpressionStatement(new Assignment(new VariableUse(gotoName),
+ new Conditional(cond, new LiteralNumber("$bodyLabel"),
+ new LiteralNumber("$after"))));
+ }, store: false);
+ addStatement(new Break(null));
+ beginLabel(bodyLabel);
+ visitStatement(node.body);
+ addGoto(continueLabel);
+ beginLabel(after);
+ }
+}
« no previous file with comments | « no previous file | tests/compiler/dart2js/async_await_js_transform_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698