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 JS.Expression gen = new JS.Fun(jsParams, _visit(body), isGenerator: true); |
| 1339 if (JS.This.foundIn(gen)) { |
| 1340 gen = js.call('#.bind(this)', gen); |
| 1341 } |
| 1342 _asyncStarController = savedController; |
| 1343 |
| 1344 var T = _emitTypeName(rules.getExpectedReturnType(body)); |
| 1345 return js.call('dart.#(#)', [kind, [gen, T]..addAll(params)]); |
1281 } | 1346 } |
1282 | 1347 |
1283 @override | 1348 @override |
1284 JS.Statement visitFunctionDeclarationStatement( | 1349 JS.Statement visitFunctionDeclarationStatement( |
1285 FunctionDeclarationStatement node) { | 1350 FunctionDeclarationStatement node) { |
1286 var func = node.functionDeclaration; | 1351 var func = node.functionDeclaration; |
1287 if (func.isGetter || func.isSetter) { | 1352 if (func.isGetter || func.isSetter) { |
1288 return js.comment('Unimplemented function get/set statement: $node'); | 1353 return js.comment('Unimplemented function get/set statement: $node'); |
1289 } | 1354 } |
1290 | 1355 |
(...skipping 410 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1701 @override | 1766 @override |
1702 JS.Statement visitReturnStatement(ReturnStatement node) { | 1767 JS.Statement visitReturnStatement(ReturnStatement node) { |
1703 var e = node.expression; | 1768 var e = node.expression; |
1704 if (e == null) return new JS.Return(); | 1769 if (e == null) return new JS.Return(); |
1705 return (_visit(e) as JS.Expression).toReturn(); | 1770 return (_visit(e) as JS.Expression).toReturn(); |
1706 } | 1771 } |
1707 | 1772 |
1708 @override | 1773 @override |
1709 JS.Statement visitYieldStatement(YieldStatement node) { | 1774 JS.Statement visitYieldStatement(YieldStatement node) { |
1710 JS.Expression jsExpr = _visit(node.expression); | 1775 JS.Expression jsExpr = _visit(node.expression); |
1711 return jsExpr.toYieldStatement(star: node.star != null); | 1776 var star = node.star != null; |
| 1777 if (_asyncStarController != null) { |
| 1778 // async* yields are generated differently from sync* yields. `yield e` |
| 1779 // becomes: |
| 1780 // |
| 1781 // if (stream.add(e)) return; |
| 1782 // yield; |
| 1783 // |
| 1784 // `yield* e` becomes: |
| 1785 // |
| 1786 // if (stream.addStream(e)) return; |
| 1787 // yield; |
| 1788 var helperName = star ? 'addStream' : 'add'; |
| 1789 return js.statement('{ if(#.#(#)) return; #; }', [ |
| 1790 _asyncStarController, |
| 1791 helperName, |
| 1792 jsExpr, |
| 1793 new JS.Yield(null) |
| 1794 ]); |
| 1795 } |
| 1796 // A normal yield in a sync* |
| 1797 return jsExpr.toYieldStatement(star: star); |
1712 } | 1798 } |
1713 | 1799 |
1714 @override | 1800 @override |
| 1801 JS.Expression visitAwaitExpression(AwaitExpression node) { |
| 1802 return new JS.Yield(_visit(node.expression)); |
| 1803 } |
| 1804 |
| 1805 @override |
1715 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 1806 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
1716 for (var v in node.variables.variables) { | 1807 for (var v in node.variables.variables) { |
1717 _loader.loadDeclaration(v, v.element); | 1808 _loader.loadDeclaration(v, v.element); |
1718 } | 1809 } |
1719 } | 1810 } |
1720 | 1811 |
1721 _addExport(String name) { | 1812 _addExport(String name) { |
1722 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; | 1813 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; |
1723 } | 1814 } |
1724 | 1815 |
(...skipping 682 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2407 JS.While visitWhileStatement(WhileStatement node) { | 2498 JS.While visitWhileStatement(WhileStatement node) { |
2408 return new JS.While(notNull(node.condition), _visit(node.body)); | 2499 return new JS.While(notNull(node.condition), _visit(node.body)); |
2409 } | 2500 } |
2410 | 2501 |
2411 @override | 2502 @override |
2412 JS.Do visitDoStatement(DoStatement node) { | 2503 JS.Do visitDoStatement(DoStatement node) { |
2413 return new JS.Do(_visit(node.body), notNull(node.condition)); | 2504 return new JS.Do(_visit(node.body), notNull(node.condition)); |
2414 } | 2505 } |
2415 | 2506 |
2416 @override | 2507 @override |
2417 JS.ForOf visitForEachStatement(ForEachStatement node) { | 2508 JS.Statement visitForEachStatement(ForEachStatement node) { |
| 2509 if (node.awaitKeyword != null) { |
| 2510 return _emitAwaitFor(node); |
| 2511 } |
| 2512 |
2418 var init = _visit(node.identifier); | 2513 var init = _visit(node.identifier); |
2419 if (init == null) { | 2514 if (init == null) { |
2420 init = js.call('let #', node.loopVariable.identifier.name); | 2515 init = js.call('let #', node.loopVariable.identifier.name); |
2421 } | 2516 } |
2422 return new JS.ForOf(init, _visit(node.iterable), _visit(node.body)); | 2517 return new JS.ForOf(init, _visit(node.iterable), _visit(node.body)); |
2423 } | 2518 } |
2424 | 2519 |
| 2520 JS.Statement _emitAwaitFor(ForEachStatement node) { |
| 2521 // Emits `await for (var value in stream) ...`, which desugars as: |
| 2522 // |
| 2523 // var iter = new StreamIterator<T>(stream); |
| 2524 // try { |
| 2525 // while (await iter.moveNext()) { |
| 2526 // var value = iter.current; |
| 2527 // ... |
| 2528 // } |
| 2529 // } finally { |
| 2530 // await iter.cancel(); |
| 2531 // } |
| 2532 // |
| 2533 // Like the Dart VM, we call cancel() always, as it's safe to call if the |
| 2534 // stream has already been cancelled. |
| 2535 // |
| 2536 // TODO(jmesserly): we may want a helper if these become common. For now the |
| 2537 // full desugaring seems okay. |
| 2538 var context = compiler.context; |
| 2539 var dart_async = context |
| 2540 .computeLibraryElement(context.sourceFactory.forUri('dart:async')); |
| 2541 var T = node.loopVariable.element.type; |
| 2542 var StreamIterator_T = |
| 2543 dart_async.getType('StreamIterator').type.substitute4([T]); |
| 2544 |
| 2545 var createStreamIter = _emitInstanceCreationExpression( |
| 2546 StreamIterator_T.element.unnamedConstructor, StreamIterator_T, null, |
| 2547 AstBuilder.argumentList([node.iterable]), false); |
| 2548 var iter = _visit(_createTemporary('it', StreamIterator_T)); |
| 2549 |
| 2550 var init = _visit(node.identifier); |
| 2551 if (init == null) { |
| 2552 init = js.call( |
| 2553 'let # = #.current', [node.loopVariable.identifier.name, iter]); |
| 2554 } else { |
| 2555 init = js.call('# = #.current', [init, iter]); |
| 2556 } |
| 2557 return js.statement('{' |
| 2558 ' let # = #;' |
| 2559 ' try {' |
| 2560 ' while (#) { #; #; }' |
| 2561 ' } finally { #; }' |
| 2562 '}', [ |
| 2563 iter, |
| 2564 createStreamIter, |
| 2565 new JS.Yield(js.call('#.moveNext()', iter)), |
| 2566 init, |
| 2567 _visit(node.body), |
| 2568 new JS.Yield(js.call('#.cancel()', iter)) |
| 2569 ]); |
| 2570 } |
| 2571 |
2425 @override | 2572 @override |
2426 visitBreakStatement(BreakStatement node) { | 2573 visitBreakStatement(BreakStatement node) { |
2427 var label = node.label; | 2574 var label = node.label; |
2428 return new JS.Break(label != null ? label.name : null); | 2575 return new JS.Break(label != null ? label.name : null); |
2429 } | 2576 } |
2430 | 2577 |
2431 @override | 2578 @override |
2432 visitContinueStatement(ContinueStatement node) { | 2579 visitContinueStatement(ContinueStatement node) { |
2433 var label = node.label; | 2580 var label = node.label; |
2434 return new JS.Continue(label != null ? label.name : null); | 2581 return new JS.Continue(label != null ? label.name : null); |
(...skipping 417 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2852 | 2999 |
2853 /// A special kind of element created by the compiler, signifying a temporary | 3000 /// A special kind of element created by the compiler, signifying a temporary |
2854 /// variable. These objects use instance equality, and should be shared | 3001 /// variable. These objects use instance equality, and should be shared |
2855 /// everywhere in the tree where they are treated as the same variable. | 3002 /// everywhere in the tree where they are treated as the same variable. |
2856 class TemporaryVariableElement extends LocalVariableElementImpl { | 3003 class TemporaryVariableElement extends LocalVariableElementImpl { |
2857 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3004 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
2858 | 3005 |
2859 int get hashCode => identityHashCode(this); | 3006 int get hashCode => identityHashCode(this); |
2860 bool operator ==(Object other) => identical(this, other); | 3007 bool operator ==(Object other) => identical(this, other); |
2861 } | 3008 } |
OLD | NEW |