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

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
« no previous file with comments | « lib/src/codegen/ast_builder.dart ('k') | lib/src/codegen/js_metalet.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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
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
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
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 }
OLDNEW
« no previous file with comments | « lib/src/codegen/ast_builder.dart ('k') | lib/src/codegen/js_metalet.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698