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

Unified Diff: frog/await/transformation.dart

Issue 10548047: Remove frog from the repository. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Move test and update apidoc.gyp. Created 8 years, 6 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 | « frog/await/samples/mintmaker/mintmaker.dart ('k') | frog/block_scope.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: frog/await/transformation.dart
diff --git a/frog/await/transformation.dart b/frog/await/transformation.dart
deleted file mode 100644
index 33f90ea8172c7c26d3658af2c269fa90a1a93c4d..0000000000000000000000000000000000000000
--- a/frog/await/transformation.dart
+++ /dev/null
@@ -1,628 +0,0 @@
-// Copyright (c) 2011, 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.
-
-/**
- * Desugarizes all await calls in a single function. This visitor assumes that
- * the tree is already normalized with [AwaitNormalizer]. For this reason it
- * only needs to recurse on statements and not on expressions. Like in
- * [AwaitChecker], nested functions have to be processed separately.
- */
-class AwaitProcessor implements TreeVisitor {
-
- /**
- * Name of the variable introduced on asynchronous functions (of type
- * [Completer] used to create a future of the function's result.
- */
- // TODO(sigmund): fix frog to make it possible to switch to '_a:res'. The
- // mangling code currently breaks across closure-boundaries.
- static final _PREFIX = '_a_';
- static final _COMPLETER_NAME = _PREFIX + 'res';
- static final _THEN_PARAM = _PREFIX + 'v';
- static final _EXCEPTION_HANDLER_PARAM = _PREFIX + 'e';
- static final _IGNORED_THEN_PARAM = _PREFIX + 'ignored_param';
- static final _CONTINUATION_PREFIX = _PREFIX + 'after_';
-
- static final _COMPLETE_METHOD = 'complete';
- static final _COMPLETE_EXCEPTION_METHOD = 'completeException';
-
-
- /** The continuation when visiting a particular statement. */
- Queue<Statement> continuation;
-
- /** Closures to call when a future throws an exception (in reverse order). */
- List<Identifier> exceptionHandlers;
-
- /** All try-catch blocks enclosing the current statement. */
- List<TryStatement> enclosingTrys;
-
- /** Counter to ensure created closure names are unique. */
- int continuationClosures = 0;
-
- /** Nodes containing await expressions (determined by [AwaitChecker]). */
- final NodeSet haveAwait;
-
- AwaitProcessor(this.haveAwait)
- : continuation = new Queue<Statement>(),
- exceptionHandlers = [],
- enclosingTrys = [];
-
- visitVariableDefinition(VariableDefinition node) {
- if (!haveAwait.contains(node)) return node;
- final values = node.values;
- // After normalization, variable declarations with await can only occur at
- // the top-level and there is no other declaration.
- assert(values != null && values.length == 1
- && values[0] is AwaitExpression);
- final param = new Identifier(_THEN_PARAM, node.span);
- // T x = await t; ... becomes:
- // t.then((v) { T x = v; ... }
- continuation.addFirst(new VariableDefinition(
- node.modifiers, node.type, node.names,
- [new VarExpression(param, node.span)], node.span));
- return new ExpressionStatement(
- _desugarAwaitCall(values[0], param), node.span);
- }
-
- visitFunctionDefinition(FunctionDefinition node) {
- if (!haveAwait.contains(node)) return node;
- // TODO(sigmund): consider making this part of the normalizer
- // make implicit return explicit:
- if (node.body is BlockStatement) {
- BlockStatement block = node.body;
- if (block.body.last() is! ReturnStatement) {
- continuation.addFirst(
- _callCompleter(_makeNull(node.span), node.span));
- }
- }
-
- Statement newBody = node.body.visit(this);
- // TODO(sigmund): extract type arg and put it in completer
- // We update the body in-place to make it easier to update nested functions
- // without having to rewrite the containing function's AST.
- node.body = new BlockStatement([
- _declareCompleter(null, node.span),
- _wrapInTryCatch(newBody, node == _mainMethod.definition),
- _returnFuture(node.span)], newBody.span);
- return node;
- }
-
- visitReturnStatement(ReturnStatement node) {
- continuation.clear();
- return _callCompleter(node.value, node.span);
- }
-
- visitThrowStatement(ThrowStatement node) {
- // instead of calling the exception handler here, we take a different
- // approach and use throw directly. This helps make the try-catch
- // transformation simpler.
- continuation.clear();
- return node;
- }
-
- visitAssertStatement(AssertStatement node) {
- return node;
- }
-
- visitBreakStatement(BreakStatement node) {
- // TODO(sigmund): implement
- return node;
- }
-
- visitContinueStatement(ContinueStatement node) {
- // TODO(sigmund): implement
- return node;
- }
-
- visitIfStatement(IfStatement node) {
- if (!haveAwait.contains(node)) return node;
- // TODO(sigmund): consider whether we should create this continuation
- // closure when there are few statements following (e.g a simple expression
- // statement, no loops, etc).
- String afterIf = _newClosureName(_CONTINUATION_PREFIX + "_if");
- Statement def = _makeContinuation(afterIf, node.span);
-
- final trueContinuation = new Queue();
- trueContinuation.addFirst(_callNoArg(afterIf, node.span));
- continuation = trueContinuation;
- Statement tRes = node.trueBranch.visit(this);
-
- Statement fRes = null;
- if (node.falseBranch != null) {
- final falseContinuation = new Queue();
- falseContinuation.addFirst(_callNoArg(afterIf, node.span));
- continuation = falseContinuation;
- fRes = node.falseBranch.visit(this);
- continuation = new Queue();
- } else {
- continuation = new Queue();
- continuation.addFirst(_callNoArg(afterIf, node.span));
- }
-
- continuation.addFirst(new IfStatement(node.test, tRes, fRes, node.span));
- return def;
- }
-
- visitWhileStatement(WhileStatement node) {
- if (!haveAwait.contains(node)) return node;
-
- String afterWhile = _newClosureName(_CONTINUATION_PREFIX + "_while");
- Statement def = _makeContinuation(afterWhile, node.span);
- String repeatWhile = _newClosureName(_PREFIX + "_while");
-
- final bodyContinuation = new Queue();
- bodyContinuation.addFirst(_callNoArg(repeatWhile, node.span));
- continuation = bodyContinuation;
- Statement body = node.body.visit(this);
-
- continuation = new Queue();
- continuation.addFirst(_callNoArg(afterWhile, node.span));
- continuation.addFirst(new IfStatement(node.test, body, null, node.span));
- Statement defLoop = _makeContinuation(repeatWhile, node.span);
-
- continuation = new Queue();
- continuation.addFirst(_callNoArg(repeatWhile, node.span));
- continuation.addFirst(defLoop);
- return def;
- }
-
- visitDoStatement(DoStatement node) {
- if (!haveAwait.contains(node)) return node;
- // TODO(sigmund): implement
- return node;
- }
-
- visitForStatement(ForStatement node) {
- if (!haveAwait.contains(node)) return node;
- // TODO(sigmund): implement
- // Note: this is harder than while loops because of dart's special semantics
- // capturing the loop variable.
- return node;
- }
-
- visitForInStatement(ForInStatement node) {
- if (!haveAwait.contains(node)) return node;
- // TODO(sigmund): implement
- // Note: this is harder than while loops because of dart's special semantics
- // capturing the loop variable.
- return node;
- }
-
- visitTryStatement(TryStatement node) {
- if (!haveAwait.contains(node)) return node;
- // TODO(sigmund): pending to do on try-catch blocks
- // - consider when await shows in catch blocks, but not in the try block
- // - support exceptions after the await
- // - handle nested try blocks
- // - support finally
- // - consider throws within catch blocks, e.g:
- // try { a } catch (E e1) { throw new E2(); } catch (E2 e2) { -- }
-
- String afterTry = _newClosureName(_CONTINUATION_PREFIX + "_try");
- Statement afterTryDef = _makeContinuation(afterTry, node.span);
-
- final defs = []; // closures for each catch block (avoid duplicating code).
- final catches = []; // catch clauses of the transformed try-catch block
-
- // Catch blocks are passed as an exception handler on de-sugared awaits:
- final handlerName = new Identifier(
- _newClosureName(_PREFIX + "exception_handler"), node.span);
- // TODO(sigmund): add trace argument (library change in Future<T>)
- final handlerArg = new Identifier(_EXCEPTION_HANDLER_PARAM, node.span);
- final handlerBody = [];
-
- // Transform each catch-block internally.
- bool untypedCatch = false; // When true, the exception handler is smaller.
- for (CatchNode n in node.catches) {
- String fname = _newClosureName(_PREFIX + "catch");
-
- // Code in transformed catch-block:
- continuation = new Queue();
- continuation.addFirst(_callNoArg(afterTry, node.span));
- continuation.addFirst(n.body.visit(this));
-
- defs.add(_makeCatchFunction(n, fname));
- catches.add(new CatchNode(n.exception, n.trace,
- new BlockStatement(
- [_callCatchFunction(n, fname), _returnFuture(n.span)], n.span),
- n.span));
-
- // Code in exception handler:
- if (!untypedCatch) {
- final exceptionHandlerCases = [
- _callCatchFunctionHelper(fname, handlerArg, null, n.span),
- _returnBoolean(n.span, true)];
- if (n.exception.type == null) {
- handlerBody.addAll(exceptionHandlerCases);
- untypedCatch = true;
- } else {
- handlerBody.add(new IfStatement(
- new IsExpression(true,
- new VarExpression(handlerArg, n.span),
- n.exception.type, n.span),
- new BlockStatement(exceptionHandlerCases, n.span),
- null, n.span));
- }
- }
- }
-
- if (!untypedCatch) {
- handlerBody.add(_returnBoolean(node.span, false));
- }
-
- // Declare the exception handler
- final handlerDef = new FunctionDefinition([], null, handlerName,
- [new FormalNode(false, false, null, handlerArg, null, node.span)],
- null, null, new BlockStatement(handlerBody, node.span), node.span);
-
- // Transform the try body, tracking the enclosing try blocks and
- // exception handlers.
- enclosingTrys.addLast(new TryStatement(
- null /* this is replaced with code in [_desugarAwaitCall] */,
- catches, node.finallyBlock, node.span));
- exceptionHandlers.addLast(handlerName);
- continuation = new Queue();
- continuation.addFirst(_callNoArg(afterTry, node.span));
- Statement body = node.body.visit(this);
- exceptionHandlers.removeLast();
- enclosingTrys.removeLast();
-
- continuation = new Queue();
- continuation.addAll(defs);
- continuation.add(handlerDef);
- continuation.add(new TryStatement(body,
- catches, node.finallyBlock, node.span));
- continuation.add(_callNoArg(afterTry, node.span));
- return afterTryDef;
- }
-
- /**
- * Create a closure representing the catch block. The closure takes either 1
- * or 2 args, depending on whether [n] has a `trace` declaration.
- */
- Statement _makeCatchFunction(CatchNode n, String fname) {
- if (n.trace == null) {
- return _makeContinuation1(fname,
- n.exception.type, n.exception.name, n.span);
- } else {
- return _makeContinuation2(fname,
- n.exception.type, n.exception.name,
- n.trace.type, n.trace.name, n.span);
- }
- }
-
- /** Calls the catch function declared for [n] using [_makeCatchFunction]. */
- Statement _callCatchFunction(CatchNode n, String fname) {
- return _callCatchFunctionHelper(fname, n.exception.name,
- n.trace == null ? null : n.trace.name, n.span);
- }
-
- /** Helper to call a catch function declared using [_makeCatchFunction]. */
- Statement _callCatchFunctionHelper(
- String fname, Identifier exception, Identifier trace, SourceSpan span) {
- if (trace == null) {
- return _call1Arg(fname,
- new VarExpression(exception, exception.span), span);
- } else {
- return _call2Args(fname, new VarExpression(exception, exception.span),
- new VarExpression(trace, trace.span), span);
- }
- }
-
- visitSwitchStatement(SwitchStatement node) {
- if (!haveAwait.contains(node)) return node;
- // TODO(sigmund): implement
- return node;
- }
-
- visitBlockStatement(BlockStatement node) {
- if (!haveAwait.contains(node) && continuation.isEmpty()) return node;
- // TODO(sigmund): test also when !continuation.isEmpty();
- for (int i = node.body.length - 1; i >= 0; i--) {
- final res = node.body[i].visit(this);
- // Note: we can't inline [res] in the line below because [continuation]
- // might be redefined and we want to use the latest reference after we
- // finish visiting [node.body].
- continuation.addFirst(res);
- }
-
- List<Statement> newBody = [];
- newBody.addAll(continuation);
- continuation.clear();
- return new BlockStatement(newBody, node.span);
- }
-
- visitLabeledStatement(LabeledStatement node) {
- if (!haveAwait.contains(node)) return node;
- // TODO(sigmund): implement
- return node;
- }
-
- visitExpressionStatement(ExpressionStatement node) {
- if (!haveAwait.contains(node)) return node;
- // After normalization, expression statements with await can only occur at
- // the top-level or as the rhs of simple assignments (= but not +=).
- if (node.body is AwaitExpression) {
- return new ExpressionStatement(
- _desugarAwaitCall(node.body,
- new Identifier(_IGNORED_THEN_PARAM, node.span)), node.span);
- } else {
- assert(node.body is BinaryExpression);
- BinaryExpression bin = node.body;
- assert(bin.op.kind == TokenKind.ASSIGN);
- assert(bin.y is AwaitExpression);
- final param = new Identifier(_THEN_PARAM, node.span);
- continuation.addFirst(new ExpressionStatement(
- new BinaryExpression(
- bin.op, bin.x, new VarExpression(param, node.span), node.span),
- node.span));
- return new ExpressionStatement(
- _desugarAwaitCall(bin.y, param), node.span);
- }
- }
-
- visitEmptyStatement(EmptyStatement node) {
- if (!haveAwait.contains(node)) return node;
- return node;
- }
-
- visitArgumentNode(ArgumentNode node) {
- if (!haveAwait.contains(node)) return node;
- return node;
- }
-
- visitCatchNode(CatchNode node) {
- // shouldn't reach here.
- world.fatal("'catch' should be handled with the enclosing try statement",
- node.span);
- }
-
- visitCaseNode(CaseNode node) {
- if (!haveAwait.contains(node)) return node;
- return node;
- }
-
- /**
- * Converts an await expression into several statements: calling
- * [:Future.then:] and propatating errors. This implementation assumes that
- * await calls are within blocks (after normalization).
- */
- CallExpression _desugarAwaitCall(AwaitExpression node, Identifier param) {
- List<Statement> afterAwait = [];
- afterAwait.addAll(continuation);
- if (afterAwait.last() is ReturnStatement) {
- // The only reason there is a `return` is because there was another await
- // and we introduced it. Such `return` is not needed in the callback.
- afterAwait.removeLast();
- }
- // Within try-blocks, we wrap the continuation in a try-catch block:
- Statement thenBody = new BlockStatement(afterAwait, node.span);
- for (int i = enclosingTrys.length - 1; i >= 0; i--) {
- final templateTry = enclosingTrys[i];
- thenBody = new TryStatement(thenBody,
- templateTry.catches, templateTry.finallyBlock, templateTry.span);
- }
-
- // A lambda function that executes the continuation.
- final thenArg = new LambdaExpression(
- new FunctionDefinition([], null, null,
- [new FormalNode(
- false, false, null /* infer type from body? */,
- param, null, param.span)
- ], null, null,
- thenBody, node.span),
- node.span);
-
- continuation.clear();
- continuation.addFirst(_returnFuture(node.span));
- // Within try-blocks, we also add an exception handler to propagate errors.
- for (final handlerName in exceptionHandlers) {
- continuation.addFirst(new ExpressionStatement(new CallExpression(
- new DotExpression(node.body,
- new Identifier('handleException', node.span), node.span),
- [new ArgumentNode(null,
- new VarExpression(handlerName, node.span),
- node.span)],
- node.span), node.span));
- }
- return _callThen(node.body, thenArg, node.span);
- }
-
- /** Make the call expression: [: future.then(arg); :] */
- CallExpression _callThen(Expression future, Expression arg, SourceSpan span) {
- return new CallExpression(
- new DotExpression(future, new Identifier('then', span), span),
- [new ArgumentNode(null, arg, span)], span);
- }
-
- /** Make the statement: [: final Completer<T> v = new Completer<T>(); :]. */
- VariableDefinition _declareCompleter(Type argType, SourceSpan span) {
- final name = new Identifier('Completer', span);
- final ctorName = new Identifier('', span);
- var typeRef = new NameTypeReference(false, name, [ctorName], span);
- var type = world.corelib.types['Completer'];
- if (argType != null) {
- typeRef = new GenericTypeReference(typeRef,
- new TypeReference(span, argType), 0, span);
- typeRef.type = type.getOrMakeConcreteType([argType]);
- } else {
- typeRef.type = type;
- }
- final def = new VariableDefinition([new Token.fake(TokenKind.FINAL, span)],
- typeRef,
- [new Identifier(_COMPLETER_NAME, span)],
- [new NewExpression(false, typeRef, null, [], span)],
- span);
- return def;
- }
-
- /**
- * Wrap [s] in a try-catch block that propagates errors through the future
- * that is returned from the asynchronous function. If the function that we
- * are generating is `main`, we add a noop listener on the resulting future.
- * Without it, we would have to make it illegal to use `await` in main. This
- * is because futures swallow exceptions if their values are never used.
- */
- Statement _wrapInTryCatch(Statement s, bool isMain) {
- final ex = new Identifier("ex", s.span);
- Statement catchStatement =
- _callCompleterException(new VarExpression(ex, ex.span), s.span);
- if (isMain) {
- final future = new DotExpression(
- new VarExpression(new Identifier(_COMPLETER_NAME, s.span), s.span),
- new Identifier("future", s.span), s.span);
- final noopHandler = new LambdaExpression(
- new FunctionDefinition([], null, null,
- [new FormalNode(false, false, null,
- new Identifier(_IGNORED_THEN_PARAM, s.span), null, s.span)
- ], null, null,
- new BlockStatement([], s.span), s.span), s.span);
- catchStatement = new BlockStatement([
- // _a_res.future.then((_ignored_param) { });
- new ExpressionStatement(
- _callThen(future, noopHandler, s.span), s.span),
- catchStatement], s.span);
- }
- return new TryStatement(s, [
- new CatchNode(new DeclaredIdentifier(null, ex, ex.span), null,
- catchStatement, s.span)], null, s.span);
- }
-
- /** Make the statement: [: _a$res.complete(value); :]. */
- _callCompleter(Expression value, SourceSpan span) {
- return _callTarget1Arg(_COMPLETER_NAME, _COMPLETE_METHOD, value, span);
- }
-
- /** Make the statement: [: _a$res.completeException(value); :]. */
- _callCompleterException(Expression value, SourceSpan span) {
- return _callTarget1Arg(
- _COMPLETER_NAME, _COMPLETE_EXCEPTION_METHOD, value, span);
- }
-
- /** Make the statement: [: return _a$res.future; :]. */
- _returnFuture(SourceSpan span) {
- return new ReturnStatement(
- new DotExpression(
- new VarExpression(new Identifier(_COMPLETER_NAME, span), span),
- new Identifier("future", span), span), span);
- }
-
- /** Create a unique name for a continuation. */
- String _newClosureName(String name) {
- continuationClosures++;
- return '${name}_$continuationClosures';
- }
-
- /** Return the current continuation as a statement block for a method body. */
- Statement _continuationAsBody(SourceSpan span) {
- if (continuation.length == 1 && continuation.first() is BlockStatement) {
- // No need to wrap a single block statement within a block statement:
- return continuation.first();
- } else {
- List<Statement> continuationBlock = [];
- continuationBlock.addAll(continuation);
- return new BlockStatement(continuationBlock, span);
- }
- }
-
- /** Create a closure that contains the continuation statements. */
- FunctionDefinition _makeContinuation(String mName, SourceSpan span) {
- return new FunctionDefinition([], null,
- new Identifier(mName, span), [], null, null,
- _continuationAsBody(span), span);
- }
-
- /** Create a 1-arg closure that contains the continuation statements. */
- FunctionDefinition _makeContinuation1(String mName,
- TypeReference arg1Type, Identifier arg1Name, SourceSpan span) {
- return new FunctionDefinition([], null,
- new Identifier(mName, span),
- [new FormalNode(false, false, arg1Type, arg1Name, null, arg1Name.span)],
- null, null, _continuationAsBody(span), span);
- }
-
- /** Create a 2-arg closure that contains the continuation statements. */
- FunctionDefinition _makeContinuation2(
- String mName, TypeReference arg1Type, Identifier arg1Name,
- TypeReference arg2Type, Identifier arg2Name, SourceSpan span) {
- return new FunctionDefinition([], null,
- new Identifier(mName, span),
- [new FormalNode(false, false, arg1Type, arg1Name, null, arg1Name.span),
- new FormalNode(false, false, arg2Type, arg2Name, null, arg2Name.span)],
- null, null, _continuationAsBody(span), span);
- }
-
- /** Make a statement invoking a function in scope. */
- Statement _callNoArg(String mName, SourceSpan span) {
- return new ExpressionStatement(new CallExpression(
- new VarExpression(new Identifier(mName, span), span), [], span), span);
- }
-
- /** Make the statement: [: target.method(value); :]. */
- Statement _callTarget1Arg(
- String target, String method, Expression value, SourceSpan span) {
- if (value == null) value = _makeNull(span);
- return new ExpressionStatement(new CallExpression(
- new DotExpression(
- new VarExpression(new Identifier(target, span), span),
- new Identifier(method, span), span),
- [new ArgumentNode(null, value, value.span)], span), span);
- }
-
- /** Make the statement: [: f(value); :]. */
- Statement _call1Arg(String f, Expression value, SourceSpan span) {
- if (value == null) value = _makeNull(span);
- return new ExpressionStatement(new CallExpression(
- new VarExpression(new Identifier(f, span), span),
- [new ArgumentNode(null, value, value.span)], span), span);
- }
-
- /** Make the statement: [: f(a, b); :]. */
- Statement _call2Args(String f, Expression a, Expression b,
- SourceSpan span) {
- if (a == null) a = _makeNull(span);
- if (b == null) b = _makeNull(span);
- return new ExpressionStatement(new CallExpression(
- new VarExpression(new Identifier(f, span), span),
- [new ArgumentNode(null, a, a.span),
- new ArgumentNode(null, b, b.span)], span), span);
- }
-
- /** Make a return statement for a boolean value. */
- Statement _returnBoolean(SourceSpan span, bool value) {
- return new ReturnStatement(
- new LiteralExpression(Value.fromBool(value, span), span), span);
- }
-
- Expression _makeNull(SourceSpan span) {
- return new LiteralExpression(Value.fromNull(span), span);
- }
-}
-
-// TODO(sigmund): create the following tests:
-// - await within the body of getter or setter properties
-// - await in each valid AST construct
-// - exceptions - make some of these fail and propagate errors.
-// - methods with and without returns (are returns added implicitly)
-// - await within assignmetns, but not declarations (see what happens with
-// x = await t;
-// x = await y;
-// or
-// x += await t;
-// x += await y;
-// (does it matter that ExpressionStatement will shadow the variable in the
-// callback function to then?)
-// - floating ifs/for loops, etc - make sure we don't need to normalize the ast
-// to disallow 'floating' ifs or to insert a block statement when returning from
-// ifs (rather than appending code to the existing continuation list). for
-// instance:
-// if (a) if (b) await t; 2;
-// becomes:
-// if (a) { if (b) { await t; } } 2;
-// which becomes becomes:
-// _2() { 2; } if (a) { if (b) { t.then((_) { _2(); } } } _2();
-// (seems that 'a' doesn't need { })
-// - 'if/while' with only one statement in the continuation (check the
-// optimization that copies code rather than adding a continuation closure).
-// - await not inside a block (could break error propagation if normalization is
-// done incorrectly).
« no previous file with comments | « frog/await/samples/mintmaker/mintmaker.dart ('k') | frog/block_scope.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698