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 |