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

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1243503007: fixes #221, initial sync*, async, async* implementation (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 4 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 unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698