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 1099 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1110 | 1110 |
1111 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1111 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
1112 if (node.isAbstract || _externalOrNative(node)) { | 1112 if (node.isAbstract || _externalOrNative(node)) { |
1113 return null; | 1113 return null; |
1114 } | 1114 } |
1115 | 1115 |
1116 var params = _visit(node.parameters); | 1116 var params = _visit(node.parameters); |
1117 if (params == null) params = []; | 1117 if (params == null) params = []; |
1118 | 1118 |
1119 return new JS.Method( | 1119 return new JS.Method( |
1120 _elementMemberName(node.element), new JS.Fun(params, _visit(node.body)), | 1120 _elementMemberName(node.element), _emitJsFunction(params, node.body), |
1121 isGetter: node.isGetter, | 1121 isGetter: node.isGetter, |
1122 isSetter: node.isSetter, | 1122 isSetter: node.isSetter, |
1123 isStatic: node.isStatic); | 1123 isStatic: node.isStatic); |
1124 } | 1124 } |
1125 | 1125 |
1126 @override | 1126 @override |
1127 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1127 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
1128 assert(node.parent is CompilationUnit); | 1128 assert(node.parent is CompilationUnit); |
1129 | 1129 |
1130 if (_externalOrNative(node)) return null; | 1130 if (_externalOrNative(node)) return null; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1195 } | 1195 } |
1196 throw 'Function has non function type: $type'; | 1196 throw 'Function has non function type: $type'; |
1197 } | 1197 } |
1198 | 1198 |
1199 @override | 1199 @override |
1200 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1200 JS.Expression visitFunctionExpression(FunctionExpression node) { |
1201 var params = _visit(node.parameters); | 1201 var params = _visit(node.parameters); |
1202 if (params == null) params = []; | 1202 if (params == null) params = []; |
1203 | 1203 |
1204 var parent = node.parent; | 1204 var parent = node.parent; |
1205 var inDecl = parent is FunctionDeclaration; | |
1206 var inStmt = parent.parent is FunctionDeclarationStatement; | 1205 var inStmt = parent.parent is FunctionDeclarationStatement; |
1207 if (inDecl && !inStmt) { | 1206 if (parent is FunctionDeclaration) { |
1208 return new JS.Fun(params, _visit(node.body)); | 1207 return _emitJsFunction(params, node.body); |
1209 } else { | 1208 } else { |
1210 String code; | 1209 String code; |
1211 AstNode body; | 1210 AstNode body; |
1212 var nodeBody = node.body; | 1211 var nodeBody = node.body; |
1213 if (nodeBody is ExpressionFunctionBody) { | 1212 if (nodeBody is ExpressionFunctionBody) { |
1214 code = '(#) => #'; | 1213 code = '(#) => #'; |
1215 body = nodeBody.expression; | 1214 body = nodeBody.expression; |
1216 } else { | 1215 } else { |
1217 code = '(#) => { #; }'; | 1216 code = '(#) => { #; }'; |
1218 body = nodeBody; | 1217 body = nodeBody; |
1219 } | 1218 } |
1220 var clos = js.call(code, [params, _visit(body)]); | 1219 var clos = js.call(code, [params, _visit(body)]); |
1221 if (!inStmt) { | 1220 if (!inStmt) { |
1222 var type = getStaticType(node); | 1221 var type = getStaticType(node); |
1223 return _emitFunctionTagged(clos, type, | 1222 return _emitFunctionTagged(clos, type, |
1224 topLevel: _executesAtTopLevel(node)); | 1223 topLevel: _executesAtTopLevel(node)); |
1225 } | 1224 } |
1226 return clos; | 1225 return clos; |
1227 } | 1226 } |
1228 } | 1227 } |
1229 | 1228 |
| 1229 JS.Fun _emitJsFunction(List<JS.Parameter> params, FunctionBody body) { |
| 1230 // TODO(jmesserly): async/async* |
| 1231 var syncStar = body.isSynchronous && body.star != null; |
| 1232 return new JS.Fun(params, _visit(body), isGenerator: syncStar); |
| 1233 } |
| 1234 |
1230 @override | 1235 @override |
1231 JS.Statement visitFunctionDeclarationStatement( | 1236 JS.Statement visitFunctionDeclarationStatement( |
1232 FunctionDeclarationStatement node) { | 1237 FunctionDeclarationStatement node) { |
1233 var func = node.functionDeclaration; | 1238 var func = node.functionDeclaration; |
1234 if (func.isGetter || func.isSetter) { | 1239 if (func.isGetter || func.isSetter) { |
1235 return js.comment('Unimplemented function get/set statement: $node'); | 1240 return js.comment('Unimplemented function get/set statement: $node'); |
1236 } | 1241 } |
1237 | 1242 |
1238 // Use an => function to bind this. | 1243 var fn = _visit(func.functionExpression); |
1239 // Technically we only need to do this if the function actually closes over | 1244 var jsThis = new _JsThisFinder(); |
1240 // `this`, but it seems harmless enough to just do it always. | 1245 fn.accept(jsThis); |
| 1246 |
1241 var name = new JS.Identifier(func.name.name); | 1247 var name = new JS.Identifier(func.name.name); |
| 1248 JS.Statement declareFn; |
| 1249 if (jsThis.found) { |
| 1250 declareFn = js.statement('let # = #.bind(this);', [name, fn]); |
| 1251 } else { |
| 1252 declareFn = new JS.FunctionDeclaration(name, fn); |
| 1253 } |
| 1254 |
1242 return new JS.Block([ | 1255 return new JS.Block([ |
1243 js.statement('let # = #;', [name, _visit(func.functionExpression)]), | 1256 declareFn, |
1244 _emitFunctionTagged(name, func.element.type).toStatement() | 1257 _emitFunctionTagged(name, func.element.type).toStatement() |
1245 ]); | 1258 ]); |
1246 } | 1259 } |
1247 | 1260 |
1248 /// Writes a simple identifier. This can handle implicit `this` as well as | 1261 /// Writes a simple identifier. This can handle implicit `this` as well as |
1249 /// going through the qualified library name if necessary. | 1262 /// going through the qualified library name if necessary. |
1250 @override | 1263 @override |
1251 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { | 1264 JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { |
1252 var accessor = node.staticElement; | 1265 var accessor = node.staticElement; |
1253 if (accessor == null) { | 1266 if (accessor == null) { |
(...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1635 | 1648 |
1636 @override | 1649 @override |
1637 JS.Statement visitAssertStatement(AssertStatement node) => | 1650 JS.Statement visitAssertStatement(AssertStatement node) => |
1638 // TODO(jmesserly): only emit in checked mode. | 1651 // TODO(jmesserly): only emit in checked mode. |
1639 js.statement('dart.assert(#);', _visit(node.condition)); | 1652 js.statement('dart.assert(#);', _visit(node.condition)); |
1640 | 1653 |
1641 @override | 1654 @override |
1642 JS.Statement visitReturnStatement(ReturnStatement node) { | 1655 JS.Statement visitReturnStatement(ReturnStatement node) { |
1643 var e = node.expression; | 1656 var e = node.expression; |
1644 if (e == null) return new JS.Return(); | 1657 if (e == null) return new JS.Return(); |
1645 return _visit(e).toReturn(); | 1658 return (_visit(e) as JS.Expression).toReturn(); |
1646 } | 1659 } |
1647 | 1660 |
1648 @override | 1661 @override |
| 1662 JS.Statement visitYieldStatement(YieldStatement node) { |
| 1663 JS.Expression jsExpr = _visit(node.expression); |
| 1664 return jsExpr.toYieldStatement(star: node.star != null); |
| 1665 } |
| 1666 |
| 1667 @override |
1649 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 1668 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
1650 for (var v in node.variables.variables) { | 1669 for (var v in node.variables.variables) { |
1651 _loader.loadDeclaration(v, v.element); | 1670 _loader.loadDeclaration(v, v.element); |
1652 } | 1671 } |
1653 } | 1672 } |
1654 | 1673 |
1655 _addExport(String name) { | 1674 _addExport(String name) { |
1656 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; | 1675 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; |
1657 } | 1676 } |
1658 | 1677 |
(...skipping 913 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2572 visitInterpolationExpression(InterpolationExpression node) => | 2591 visitInterpolationExpression(InterpolationExpression node) => |
2573 _visit(node.expression); | 2592 _visit(node.expression); |
2574 | 2593 |
2575 @override | 2594 @override |
2576 visitBooleanLiteral(BooleanLiteral node) => js.boolean(node.value); | 2595 visitBooleanLiteral(BooleanLiteral node) => js.boolean(node.value); |
2577 | 2596 |
2578 @override | 2597 @override |
2579 JS.Expression visitExpression(Expression node) => | 2598 JS.Expression visitExpression(Expression node) => |
2580 _unimplementedCall('Unimplemented ${node.runtimeType}: $node'); | 2599 _unimplementedCall('Unimplemented ${node.runtimeType}: $node'); |
2581 | 2600 |
2582 @override | |
2583 JS.Statement visitYieldStatement(YieldStatement node) => | |
2584 _unimplementedCall('Unimplemented yield: $node').toStatement(); | |
2585 | |
2586 JS.Expression _unimplementedCall(String comment) { | 2601 JS.Expression _unimplementedCall(String comment) { |
2587 return js.call('dart.throw_(#)', [js.escapedString(comment)]); | 2602 return js.call('dart.throw_(#)', [js.escapedString(comment)]); |
2588 } | 2603 } |
2589 | 2604 |
2590 @override | 2605 @override |
2591 visitNode(AstNode node) { | 2606 visitNode(AstNode node) { |
2592 // TODO(jmesserly): verify this is unreachable. | 2607 // TODO(jmesserly): verify this is unreachable. |
2593 throw 'Unimplemented ${node.runtimeType}: $node'; | 2608 throw 'Unimplemented ${node.runtimeType}: $node'; |
2594 } | 2609 } |
2595 | 2610 |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2798 | 2813 |
2799 /// A special kind of element created by the compiler, signifying a temporary | 2814 /// A special kind of element created by the compiler, signifying a temporary |
2800 /// variable. These objects use instance equality, and should be shared | 2815 /// variable. These objects use instance equality, and should be shared |
2801 /// everywhere in the tree where they are treated as the same variable. | 2816 /// everywhere in the tree where they are treated as the same variable. |
2802 class TemporaryVariableElement extends LocalVariableElementImpl { | 2817 class TemporaryVariableElement extends LocalVariableElementImpl { |
2803 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2818 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
2804 | 2819 |
2805 int get hashCode => identityHashCode(this); | 2820 int get hashCode => identityHashCode(this); |
2806 bool operator ==(Object other) => identical(this, other); | 2821 bool operator ==(Object other) => identical(this, other); |
2807 } | 2822 } |
| 2823 |
| 2824 class _JsThisFinder extends JS.BaseVisitor { |
| 2825 bool found = false; |
| 2826 visitThis(JS.This node) { |
| 2827 found = true; |
| 2828 } |
| 2829 visitNode(JS.Node node) { |
| 2830 if (!found) super.visitNode(node); |
| 2831 } |
| 2832 } |
OLD | NEW |