 Chromium Code Reviews
 Chromium Code Reviews Issue 1243503007:
  fixes #221, initial sync*, async, async* implementation  (Closed) 
  Base URL: git@github.com:dart-lang/dev_compiler.git@master
    
  
    Issue 1243503007:
  fixes #221, initial sync*, async, async* implementation  (Closed) 
  Base URL: git@github.com:dart-lang/dev_compiler.git@master| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a | 
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. | 
| 4 | 4 | 
| 5 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; | 
| 6 | 6 | 
| 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 
| 8 | 8 | 
| 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 
| 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 60 /// Information that is precomputed for this library, indicates which fields | 60 /// Information that is precomputed for this library, indicates which fields | 
| 61 /// need storage slots. | 61 /// need storage slots. | 
| 62 final HashSet<FieldElement> _fieldsNeedingStorage; | 62 final HashSet<FieldElement> _fieldsNeedingStorage; | 
| 63 | 63 | 
| 64 /// The variable for the target of the current `..` cascade expression. | 64 /// The variable for the target of the current `..` cascade expression. | 
| 65 SimpleIdentifier _cascadeTarget; | 65 SimpleIdentifier _cascadeTarget; | 
| 66 | 66 | 
| 67 /// The variable for the current catch clause | 67 /// The variable for the current catch clause | 
| 68 SimpleIdentifier _catchParameter; | 68 SimpleIdentifier _catchParameter; | 
| 69 | 69 | 
| 70 /// In an async* function, this represents the stream controller parameter. | |
| 71 JS.TemporaryId _asyncStarController; | |
| 72 | |
| 70 /// Imported libraries, and the temporaries used to refer to them. | 73 /// Imported libraries, and the temporaries used to refer to them. | 
| 71 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 74 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 
| 72 final _exports = new Set<String>(); | 75 final _exports = new Set<String>(); | 
| 73 final _lazyFields = <VariableDeclaration>[]; | 76 final _lazyFields = <VariableDeclaration>[]; | 
| 74 final _properties = <FunctionDeclaration>[]; | 77 final _properties = <FunctionDeclaration>[]; | 
| 75 final _privateNames = new HashMap<String, JS.TemporaryId>(); | 78 final _privateNames = new HashMap<String, JS.TemporaryId>(); | 
| 76 final _moduleItems = <JS.Statement>[]; | 79 final _moduleItems = <JS.Statement>[]; | 
| 77 final _temps = new HashMap<Element, JS.TemporaryId>(); | 80 final _temps = new HashMap<Element, JS.TemporaryId>(); | 
| 78 final _qualifiedIds = new HashMap<Element, JS.MaybeQualifiedId>(); | 81 final _qualifiedIds = new HashMap<Element, JS.MaybeQualifiedId>(); | 
| 79 final _qualifiedGenericIds = new HashMap<Element, JS.MaybeQualifiedId>(); | 82 final _qualifiedGenericIds = new HashMap<Element, JS.MaybeQualifiedId>(); | 
| (...skipping 1078 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1158 | 1161 | 
| 1159 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1162 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 
| 1160 if (node.isAbstract || _externalOrNative(node)) { | 1163 if (node.isAbstract || _externalOrNative(node)) { | 
| 1161 return null; | 1164 return null; | 
| 1162 } | 1165 } | 
| 1163 | 1166 | 
| 1164 var params = _visit(node.parameters) as List<JS.Parameter>; | 1167 var params = _visit(node.parameters) as List<JS.Parameter>; | 
| 1165 if (params == null) params = <JS.Parameter>[]; | 1168 if (params == null) params = <JS.Parameter>[]; | 
| 1166 | 1169 | 
| 1167 return new JS.Method( | 1170 return new JS.Method( | 
| 1168 _elementMemberName(node.element), _emitJsFunction(params, node.body), | 1171 _elementMemberName(node.element), _emitFunctionBody(params, node.body), | 
| 1169 isGetter: node.isGetter, | 1172 isGetter: node.isGetter, | 
| 1170 isSetter: node.isSetter, | 1173 isSetter: node.isSetter, | 
| 1171 isStatic: node.isStatic); | 1174 isStatic: node.isStatic); | 
| 1172 } | 1175 } | 
| 1173 | 1176 | 
| 1174 @override | 1177 @override | 
| 1175 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1178 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 
| 1176 assert(node.parent is CompilationUnit); | 1179 assert(node.parent is CompilationUnit); | 
| 1177 | 1180 | 
| 1178 if (_externalOrNative(node)) return null; | 1181 if (_externalOrNative(node)) return null; | 
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1245 } | 1248 } | 
| 1246 | 1249 | 
| 1247 @override | 1250 @override | 
| 1248 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1251 JS.Expression visitFunctionExpression(FunctionExpression node) { | 
| 1249 var params = _visit(node.parameters) as List<JS.Parameter>; | 1252 var params = _visit(node.parameters) as List<JS.Parameter>; | 
| 1250 if (params == null) params = <JS.Parameter>[]; | 1253 if (params == null) params = <JS.Parameter>[]; | 
| 1251 | 1254 | 
| 1252 var parent = node.parent; | 1255 var parent = node.parent; | 
| 1253 var inStmt = parent.parent is FunctionDeclarationStatement; | 1256 var inStmt = parent.parent is FunctionDeclarationStatement; | 
| 1254 if (parent is FunctionDeclaration) { | 1257 if (parent is FunctionDeclaration) { | 
| 1255 return _emitJsFunction(params, node.body); | 1258 return _emitFunctionBody(params, node.body); | 
| 1256 } else { | 1259 } else { | 
| 1257 String code; | 1260 String code; | 
| 1258 AstNode body; | 1261 JS.Node jsBody; | 
| 1259 var nodeBody = node.body; | 1262 var body = node.body; | 
| 1260 if (nodeBody is ExpressionFunctionBody) { | 1263 if (body.isGenerator || body.isAsynchronous) { | 
| 1261 code = '(#) => #'; | 1264 code = '(#) => #'; | 
| 1262 body = nodeBody.expression; | 1265 jsBody = _emitGeneratorFunctionBody(params, body); | 
| 1266 } else if (body is ExpressionFunctionBody) { | |
| 1267 code = '(#) => #'; | |
| 1268 jsBody = _visit(body.expression); | |
| 1263 } else { | 1269 } else { | 
| 1264 code = '(#) => { #; }'; | 1270 code = '(#) => { #; }'; | 
| 1265 body = nodeBody; | 1271 jsBody = _visit(body); | 
| 1266 } | 1272 } | 
| 1267 var clos = js.call(code, [params, _visit(body)]); | 1273 var clos = js.call(code, [params, jsBody]); | 
| 1268 if (!inStmt) { | 1274 if (!inStmt) { | 
| 1269 var type = getStaticType(node); | 1275 var type = getStaticType(node); | 
| 1270 return _emitFunctionTagged(clos, type, | 1276 return _emitFunctionTagged(clos, type, | 
| 1271 topLevel: _executesAtTopLevel(node)); | 1277 topLevel: _executesAtTopLevel(node)); | 
| 1272 } | 1278 } | 
| 1273 return clos; | 1279 return clos; | 
| 1274 } | 1280 } | 
| 1275 } | 1281 } | 
| 1276 | 1282 | 
| 1277 JS.Fun _emitJsFunction(List<JS.Parameter> params, FunctionBody body) { | 1283 JS.Fun _emitFunctionBody(List<JS.Parameter> params, FunctionBody body) { | 
| 1278 // TODO(jmesserly): async/async* | 1284 // sync*, async, async* | 
| 1279 var syncStar = body.isSynchronous && body.star != null; | 1285 if (body.isAsynchronous || body.isGenerator) { | 
| 1280 return new JS.Fun(params, _visit(body), isGenerator: syncStar); | 1286 return new JS.Fun(params, js.statement( | 
| 1287 '{ return #; }', [_emitGeneratorFunctionBody(params, body)])); | |
| 1288 } | |
| 1289 // normal function (sync) | |
| 1290 return new JS.Fun(params, _visit(body)); | |
| 1291 } | |
| 1292 | |
| 1293 JS.Expression _emitGeneratorFunctionBody( | |
| 1294 List<JS.Parameter> params, FunctionBody body) { | |
| 1295 var kind = body.isSynchronous ? 'sync' : 'async'; | |
| 1296 if (body.isGenerator) kind += 'Star'; | |
| 1297 | |
| 1298 // Transforms `sync*` `async` and `async*` function bodies | |
| 1299 // using ES6 generators. | |
| 1300 // | |
| 1301 // `sync*` wraps a generator in a Dart Iterable<T>: | |
| 1302 // | |
| 1303 // function name(<args>) { | |
| 1304 // return dart.syncStar(function*(<args>) { | |
| 1305 // <body> | |
| 1306 // }, T, <args>).bind(this); | |
| 1307 // } | |
| 1308 // | |
| 1309 // We need to include <args> in case any are mutated, so each `.iterator` | |
| 1310 // gets the same initial values. | |
| 1311 // | |
| 1312 // TODO(jmesserly): we could omit the args for the common case where args | |
| 1313 // are not mutated inside the generator. | |
| 1314 // | |
| 1315 // In the future, we might be able to simplify this, see: | |
| 1316 // https://github.com/dart-lang/dev_compiler/issues/247. | |
| 1317 // | |
| 1318 // `async` works the same, but uses the `dart.async` helper. | |
| 1319 // | |
| 1320 // In the body of a `sync*` and `async`, `yield`/`await` are both generated | |
| 1321 // simply as `yield`. | |
| 1322 // | |
| 1323 // `async*` uses the `dart.asyncStar` helper, and also has an extra `stream` | |
| 1324 // argument to the generator, which is used for passing values to the | |
| 1325 // _AsyncStarStreamController implementation type. | |
| 1326 // `yield` is specially generated inside `async*`, see visitYieldStatement. | |
| 1327 // `await` is generated as `yield`. | |
| 1328 // runtime/_generators.js has an example of what the code is generated as. | |
| 1329 var savedController = _asyncStarController; | |
| 1330 List jsParams; | |
| 1331 if (kind == 'asyncStar') { | |
| 1332 _asyncStarController = new JS.TemporaryId('stream'); | |
| 1333 jsParams = [_asyncStarController]..addAll(params); | |
| 1334 } else { | |
| 1335 _asyncStarController = null; | |
| 1336 jsParams = params; | |
| 1337 } | |
| 1338 var jsThis = new _JsThisFinder(); | |
| 1339 JS.Expression gen = new JS.Fun(jsParams, _visit(body), isGenerator: true); | |
| 1340 gen.accept(jsThis); | |
| 1341 if (jsThis.found) { | |
| 1342 gen = js.call('#.bind(this)', gen); | |
| 1343 } | |
| 1344 _asyncStarController = savedController; | |
| 1345 | |
| 1346 var T = _emitTypeName(rules.getExpectedReturnType(body)); | |
| 1347 return js.call('dart.#(#)', [kind, [gen, T]..addAll(params)]); | |
| 1281 } | 1348 } | 
| 1282 | 1349 | 
| 1283 @override | 1350 @override | 
| 1284 JS.Statement visitFunctionDeclarationStatement( | 1351 JS.Statement visitFunctionDeclarationStatement( | 
| 1285 FunctionDeclarationStatement node) { | 1352 FunctionDeclarationStatement node) { | 
| 1286 var func = node.functionDeclaration; | 1353 var func = node.functionDeclaration; | 
| 1287 if (func.isGetter || func.isSetter) { | 1354 if (func.isGetter || func.isSetter) { | 
| 1288 return js.comment('Unimplemented function get/set statement: $node'); | 1355 return js.comment('Unimplemented function get/set statement: $node'); | 
| 1289 } | 1356 } | 
| 1290 | 1357 | 
| (...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1703 @override | 1770 @override | 
| 1704 JS.Statement visitReturnStatement(ReturnStatement node) { | 1771 JS.Statement visitReturnStatement(ReturnStatement node) { | 
| 1705 var e = node.expression; | 1772 var e = node.expression; | 
| 1706 if (e == null) return new JS.Return(); | 1773 if (e == null) return new JS.Return(); | 
| 1707 return (_visit(e) as JS.Expression).toReturn(); | 1774 return (_visit(e) as JS.Expression).toReturn(); | 
| 1708 } | 1775 } | 
| 1709 | 1776 | 
| 1710 @override | 1777 @override | 
| 1711 JS.Statement visitYieldStatement(YieldStatement node) { | 1778 JS.Statement visitYieldStatement(YieldStatement node) { | 
| 1712 JS.Expression jsExpr = _visit(node.expression); | 1779 JS.Expression jsExpr = _visit(node.expression); | 
| 1713 return jsExpr.toYieldStatement(star: node.star != null); | 1780 var star = node.star != null; | 
| 1781 if (_asyncStarController != null) { | |
| 1782 // async* yields are generated differently from sync* yields. `yield e` | |
| 1783 // becomes: | |
| 1784 // | |
| 1785 // if (stream.add(e)) return; | |
| 1786 // yield; | |
| 1787 // | |
| 1788 // `yield* e` becomes: | |
| 1789 // | |
| 1790 // if (stream.addStream(e)) return; | |
| 1791 // yield; | |
| 1792 var helperName = star ? 'addStream' : 'add'; | |
| 1793 return js.statement('{ if(#.#(#)) return; #; }', [ | |
| 1794 _asyncStarController, | |
| 1795 helperName, | |
| 1796 jsExpr, | |
| 1797 new JS.Yield(null) | |
| 1798 ]); | |
| 1799 } | |
| 1800 // A normal yield in a sync* | |
| 1801 return jsExpr.toYieldStatement(star: star); | |
| 1714 } | 1802 } | 
| 1715 | 1803 | 
| 1716 @override | 1804 @override | 
| 1805 JS.Expression visitAwaitExpression(AwaitExpression node) { | |
| 1806 return new JS.Yield(_visit(node.expression)); | |
| 1807 } | |
| 1808 | |
| 1809 @override | |
| 1717 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 1810 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 
| 1718 for (var v in node.variables.variables) { | 1811 for (var v in node.variables.variables) { | 
| 1719 _loader.loadDeclaration(v, v.element); | 1812 _loader.loadDeclaration(v, v.element); | 
| 1720 } | 1813 } | 
| 1721 } | 1814 } | 
| 1722 | 1815 | 
| 1723 _addExport(String name) { | 1816 _addExport(String name) { | 
| 1724 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; | 1817 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; | 
| 1725 } | 1818 } | 
| 1726 | 1819 | 
| (...skipping 682 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2409 JS.While visitWhileStatement(WhileStatement node) { | 2502 JS.While visitWhileStatement(WhileStatement node) { | 
| 2410 return new JS.While(notNull(node.condition), _visit(node.body)); | 2503 return new JS.While(notNull(node.condition), _visit(node.body)); | 
| 2411 } | 2504 } | 
| 2412 | 2505 | 
| 2413 @override | 2506 @override | 
| 2414 JS.Do visitDoStatement(DoStatement node) { | 2507 JS.Do visitDoStatement(DoStatement node) { | 
| 2415 return new JS.Do(_visit(node.body), notNull(node.condition)); | 2508 return new JS.Do(_visit(node.body), notNull(node.condition)); | 
| 2416 } | 2509 } | 
| 2417 | 2510 | 
| 2418 @override | 2511 @override | 
| 2419 JS.ForOf visitForEachStatement(ForEachStatement node) { | 2512 visitForEachStatement(ForEachStatement node) { | 
| 
vsm
2015/07/27 21:03:24
Return "JS.Statement"?
 
Jennifer Messerly
2015/07/27 21:46:35
Done.
 | |
| 2513 if (node.awaitKeyword != null) { | |
| 2514 return _emitAwaitFor(node); | |
| 2515 } | |
| 2516 | |
| 2420 var init = _visit(node.identifier); | 2517 var init = _visit(node.identifier); | 
| 2421 if (init == null) { | 2518 if (init == null) { | 
| 2422 init = js.call('let #', node.loopVariable.identifier.name); | 2519 init = js.call('let #', node.loopVariable.identifier.name); | 
| 2423 } | 2520 } | 
| 2424 return new JS.ForOf(init, _visit(node.iterable), _visit(node.body)); | 2521 return new JS.ForOf(init, _visit(node.iterable), _visit(node.body)); | 
| 2425 } | 2522 } | 
| 2426 | 2523 | 
| 2524 JS.Statement _emitAwaitFor(ForEachStatement node) { | |
| 2525 // Emits `await for (var value in stream) ...`, which desugars as: | |
| 2526 // | |
| 2527 // var iter = new StreamIterator<T>(stream); | |
| 2528 // try { | |
| 2529 // while (await iter.moveNext()) { | |
| 2530 // var value = iter.current; | |
| 2531 // ... | |
| 2532 // } | |
| 2533 // } finally { | |
| 2534 // await iter.cancel(); | |
| 2535 // } | |
| 2536 // | |
| 2537 // Like the Dart VM, we call cancel() always, as it's safe to call if the | |
| 2538 // stream has already been cancelled. | |
| 2539 // | |
| 2540 // TODO(jmesserly): we may want a helper if these become common. For now the | |
| 2541 // full desugaring seems okay. | |
| 2542 var context = compiler.context; | |
| 2543 var dart_async = context | |
| 2544 .computeLibraryElement(context.sourceFactory.forUri('dart:async')); | |
| 2545 var T = node.loopVariable.element.type; | |
| 2546 var StreamIterator_T = | |
| 2547 dart_async.getType('StreamIterator').type.substitute4([T]); | |
| 2548 | |
| 2549 var createStreamIter = _emitInstanceCreationExpression( | |
| 2550 StreamIterator_T.element.unnamedConstructor, StreamIterator_T, null, | |
| 2551 AstBuilder.argumentList([node.iterable]), false); | |
| 2552 var iter = _visit(_createTemporary('it', StreamIterator_T)); | |
| 2553 | |
| 2554 var init = _visit(node.identifier); | |
| 2555 if (init == null) { | |
| 2556 init = js.call( | |
| 2557 'let # = #.current', [node.loopVariable.identifier.name, iter]); | |
| 2558 } else { | |
| 2559 init = js.call('# = #.current', [init, iter]); | |
| 2560 } | |
| 2561 return js.statement('{' | |
| 2562 ' let # = #;' | |
| 2563 ' try {' | |
| 2564 ' while (#) { #; #; }' | |
| 2565 ' } finally { #; }' | |
| 2566 '}', [ | |
| 2567 iter, | |
| 2568 createStreamIter, | |
| 2569 new JS.Yield(js.call('#.moveNext()', iter)), | |
| 2570 init, | |
| 2571 _visit(node.body), | |
| 2572 new JS.Yield(js.call('#.cancel()', iter)) | |
| 2573 ]); | |
| 2574 } | |
| 2575 | |
| 2427 @override | 2576 @override | 
| 2428 visitBreakStatement(BreakStatement node) { | 2577 visitBreakStatement(BreakStatement node) { | 
| 2429 var label = node.label; | 2578 var label = node.label; | 
| 2430 return new JS.Break(label != null ? label.name : null); | 2579 return new JS.Break(label != null ? label.name : null); | 
| 2431 } | 2580 } | 
| 2432 | 2581 | 
| 2433 @override | 2582 @override | 
| 2434 visitContinueStatement(ContinueStatement node) { | 2583 visitContinueStatement(ContinueStatement node) { | 
| 2435 var label = node.label; | 2584 var label = node.label; | 
| 2436 return new JS.Continue(label != null ? label.name : null); | 2585 return new JS.Continue(label != null ? label.name : null); | 
| (...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2862 | 3011 | 
| 2863 class _JsThisFinder extends JS.BaseVisitor { | 3012 class _JsThisFinder extends JS.BaseVisitor { | 
| 2864 bool found = false; | 3013 bool found = false; | 
| 2865 visitThis(JS.This node) { | 3014 visitThis(JS.This node) { | 
| 2866 found = true; | 3015 found = true; | 
| 2867 } | 3016 } | 
| 2868 visitNode(JS.Node node) { | 3017 visitNode(JS.Node node) { | 
| 2869 if (!found) super.visitNode(node); | 3018 if (!found) super.visitNode(node); | 
| 2870 } | 3019 } | 
| 2871 } | 3020 } | 
| OLD | NEW |