Chromium Code Reviews| Index: pkg/compiler/lib/src/ssa/builder_kernel.dart |
| diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart |
| index 2143a52da0beb781bbdfed381011121a42333100..b4cd2624b895d7743b8fb687753ac55ad3f59385 100644 |
| --- a/pkg/compiler/lib/src/ssa/builder_kernel.dart |
| +++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart |
| @@ -565,6 +565,26 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| } |
| } |
| + /// Returns true if the [type] is a valid return type for an asynchronous |
| + /// function. |
| + /// |
| + /// Asynchronous functions return a `Future`, and a valid return is thus |
| + /// either dynamic, Object, or Future. |
| + /// |
| + /// We do not accept the internal Future implementation class. |
| + bool isValidAsyncReturnType(ir.DartType type) { |
| + // TODO(sigurdm): In an internal library a function could be declared: |
| + // |
| + // _FutureImpl foo async => 1; |
| + // |
| + // This should be valid (because the actual value returned from an async |
| + // function is a `_FutureImpl`), but currently false is returned in this |
| + // case. |
| + return type is ir.DynamicType || |
| + type == astAdapter.objectClass.thisType || |
| + (type is ir.InterfaceType && type == astAdapter.futureClass.thisType); |
| + } |
| + |
| @override |
| void visitReturnStatement(ir.ReturnStatement returnStatement) { |
| HInstruction value; |
| @@ -573,8 +593,22 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| } else { |
| assert(_targetFunction != null && _targetFunction is ir.FunctionNode); |
| returnStatement.expression.accept(this); |
| - value = typeBuilder.potentiallyCheckOrTrustType( |
| - pop(), astAdapter.getFunctionReturnType(_targetFunction)); |
| + value = pop(); |
| + if (_targetFunction.asyncMarker == ir.AsyncMarker.Async) { |
| + var returnType = astAdapter.getDartType(_targetFunction.returnType); |
| + if (compiler.options.enableTypeAssertions && |
| + !isValidAsyncReturnType(_targetFunction.returnType)) { |
| + generateTypeError( |
| + returnStatement, |
| + "Async function returned a Future," |
| + " was declared to return a ${_targetFunction.returnType}."); |
| + pop(); |
| + return; |
| + } |
| + } else { |
| + value = typeBuilder.potentiallyCheckOrTrustType( |
| + value, astAdapter.getFunctionReturnType(_targetFunction)); |
| + } |
| } |
| // TODO(het): Add source information |
| // TODO(het): Set a return value instead of closing the function when we |
| @@ -621,8 +655,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| @override |
| void visitForInStatement(ir.ForInStatement forInStatement) { |
| if (forInStatement.isAsync) { |
| - compiler.reporter.internalError(astAdapter.getNode(forInStatement), |
| - "Cannot compile async for-in using kernel."); |
| + _buildAsyncForIn(forInStatement); |
| } |
| // If the expression being iterated over is a JS indexable type, we can |
| // generate an optimized version of for-in that uses indexing. |
| @@ -777,6 +810,59 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| forInStatement, buildInitializer, buildCondition, () {}, buildBody); |
| } |
| + void _buildAsyncForIn(ir.ForInStatement forInStatement) { |
| + // The async-for is implemented with a StreamIterator. |
| + HInstruction streamIterator; |
| + |
| + forInStatement.iterable.accept(this); |
| + _pushStaticInvocation( |
| + astAdapter.streamIteratorConstructor, |
| + [pop(), graph.addConstantNull(closedWorld)], |
| + astAdapter.streamIteratorConstructorType); |
| + streamIterator = pop(); |
| + |
| + void buildInitializer() {} |
| + |
| + HInstruction buildCondition() { |
| + TypeMask mask = astAdapter.typeOfIteratorMoveNext(forInStatement); |
| + _pushDynamicInvocation(forInStatement, mask, [streamIterator], |
| + selector: Selectors.moveNext); |
| + HInstruction future = pop(); |
| + push(new HAwait(future, astAdapter.makeSubtypeOfObject(closedWorld))); |
| + return popBoolified(); |
| + } |
| + |
| + void buildBody() { |
| + TypeMask mask = astAdapter.typeOfIteratorCurrent(forInStatement); |
| + _pushDynamicInvocation(forInStatement, mask, [streamIterator], |
| + selector: Selectors.current); |
| + localsHandler.updateLocal( |
| + astAdapter.getLocal(forInStatement.variable), pop()); |
| + forInStatement.body.accept(this); |
| + } |
| + |
| + void buildUpdate() {} |
| + |
| + // Creates a synthetic try/finally block in case anything async goes amiss. |
| + TryCatchFinallyBuilder tryBuilder = new TryCatchFinallyBuilder(this); |
| + // Build fake try body: |
| + loopHandler.handleLoop(forInStatement, buildInitializer, buildCondition, |
| + buildUpdate, buildBody); |
| + |
| + void finalizerFunction() { |
| + _pushDynamicInvocation(forInStatement, null, [streamIterator], |
| + selector: Selectors.cancel); |
| + push(new HAwait(pop(), astAdapter.makeSubtypeOfObject(closedWorld))); |
| + pop(); |
|
sra1
2017/01/14 02:56:23
push/pop = add
Emily Fortuna
2017/01/17 20:58:21
Done.
|
| + } |
| + |
| + tryBuilder |
| + ..closeTryBody() |
| + ..buildFinallyBlock( |
| + new ir.TryFinally(null, new _FakeStatement(finalizerFunction))) |
|
sra1
2017/01/14 02:56:23
I'd rather buildFinallyBlock took a function (fina
Emily Fortuna
2017/01/17 20:58:21
Done.
|
| + ..cleanUp(); |
| + } |
| + |
| HInstruction callSetRuntimeTypeInfo( |
| HInstruction typeInfo, HInstruction newObject) { |
| // Set the runtime type information on the object. |
| @@ -2235,6 +2321,19 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder { |
| } |
| } |
| + void visitYieldStatement(ir.YieldStatement yieldStatement) { |
| + yieldStatement.expression.accept(this); |
| + add(new HYield(pop(), yieldStatement.isYieldStar)); |
| + } |
| + |
| + @override |
| + void visitAwaitExpression(ir.AwaitExpression await) { |
| + await.operand.accept(this); |
| + HInstruction awaited = pop(); |
| + // TODO(herhut): Improve this type. |
| + push(new HAwait(awaited, astAdapter.makeSubtypeOfObject(closedWorld))); |
| + } |
| + |
| @override |
| void visitRethrow(ir.Rethrow rethrowNode) { |
| HInstruction exception = rethrowableException; |
| @@ -2530,3 +2629,17 @@ class TryCatchFinallyBuilder { |
| exitBlock); |
| } |
| } |
| + |
| +/// Helper class to do the work of acting like a ir.TryFinally finalizer |
| +/// statement without actually having any source code. This is used for the |
| +/// buildAsyncForIn method but allows us to reuse code in |
| +/// TryCatchFinallyBuilder. Sure would be nice if we could have nested classes. |
| +class _FakeStatement extends ir.Statement { |
|
sra1
2017/01/14 02:56:23
I'm not keen on this. See other comments
Emily Fortuna
2017/01/17 20:58:20
Done.
|
| + Function finalizerFunc; |
| + _FakeStatement(this.finalizerFunc); |
| + |
| + accept(ir.StatementVisitor not_used) => finalizerFunc(); |
| + |
| + visitChildren(ir.Visitor v) {} |
| + transformChildren(ir.Transformer v) {} |
| +} |