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