| 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; | 7 import 'dart:collection' show HashSet, HashMap; |
| 8 import 'dart:io' show Directory, File; | 8 import 'dart:io' show Directory, File; |
| 9 | 9 |
| 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| (...skipping 895 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 906 } | 906 } |
| 907 | 907 |
| 908 @override | 908 @override |
| 909 JS.Expression visitFunctionExpression(FunctionExpression node) { | 909 JS.Expression visitFunctionExpression(FunctionExpression node) { |
| 910 var params = _visit(node.parameters); | 910 var params = _visit(node.parameters); |
| 911 if (params == null) params = []; | 911 if (params == null) params = []; |
| 912 | 912 |
| 913 if (node.parent is FunctionDeclaration) { | 913 if (node.parent is FunctionDeclaration) { |
| 914 return new JS.Fun(params, _visit(node.body)); | 914 return new JS.Fun(params, _visit(node.body)); |
| 915 } else { | 915 } else { |
| 916 var bindThis = _maybeBindThis(node.body); | |
| 917 | |
| 918 String code; | 916 String code; |
| 919 AstNode body; | 917 AstNode body; |
| 920 var nodeBody = node.body; | 918 var nodeBody = node.body; |
| 921 if (nodeBody is ExpressionFunctionBody) { | 919 if (nodeBody is ExpressionFunctionBody) { |
| 922 code = '(#) => #'; | 920 code = '(#) => #'; |
| 923 body = nodeBody.expression; | 921 body = nodeBody.expression; |
| 924 } else { | 922 } else { |
| 925 code = '(#) => { #; }'; | 923 code = '(#) => { #; }'; |
| 926 body = nodeBody; | 924 body = nodeBody; |
| 927 } | 925 } |
| 928 return js.call('($code)$bindThis', [params, _visit(body)]); | 926 return js.call(code, [params, _visit(body)]); |
| 929 } | 927 } |
| 930 } | 928 } |
| 931 | 929 |
| 932 @override | 930 @override |
| 933 JS.Statement visitFunctionDeclarationStatement( | 931 JS.Statement visitFunctionDeclarationStatement( |
| 934 FunctionDeclarationStatement node) { | 932 FunctionDeclarationStatement node) { |
| 935 var func = node.functionDeclaration; | 933 var func = node.functionDeclaration; |
| 936 if (func.isGetter || func.isSetter) { | 934 if (func.isGetter || func.isSetter) { |
| 937 return js.comment('Unimplemented function get/set statement: $node'); | 935 return js.comment('Unimplemented function get/set statement: $node'); |
| 938 } | 936 } |
| (...skipping 585 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1524 bool _canUsePrimitiveEquality(Expression left, Expression right) { | 1522 bool _canUsePrimitiveEquality(Expression left, Expression right) { |
| 1525 if (_isNull(left) || _isNull(right)) return true; | 1523 if (_isNull(left) || _isNull(right)) return true; |
| 1526 | 1524 |
| 1527 var leftType = _canonicalizeNumTypes(rules.getStaticType(left)); | 1525 var leftType = _canonicalizeNumTypes(rules.getStaticType(left)); |
| 1528 var rightType = _canonicalizeNumTypes(rules.getStaticType(right)); | 1526 var rightType = _canonicalizeNumTypes(rules.getStaticType(right)); |
| 1529 return _isJSBuiltinType(leftType) && leftType == rightType; | 1527 return _isJSBuiltinType(leftType) && leftType == rightType; |
| 1530 } | 1528 } |
| 1531 | 1529 |
| 1532 bool _isNull(Expression expr) => expr is NullLiteral; | 1530 bool _isNull(Expression expr) => expr is NullLiteral; |
| 1533 | 1531 |
| 1534 // TODO(jmesserly, vsm): Refactor this logic. | |
| 1535 SimpleIdentifier _createTemporary(String name, DartType type) { | 1532 SimpleIdentifier _createTemporary(String name, DartType type) { |
| 1536 // We use an invalid source location to signal that this is a temporary. | 1533 // We use an invalid source location to signal that this is a temporary. |
| 1537 // See [_isTemporary]. | 1534 // See [_isTemporary]. |
| 1538 // TODO(jmesserly): alternatives are | 1535 // TODO(jmesserly): alternatives are |
| 1539 // * (ab)use Element.isSynthetic, which isn't currently used for | 1536 // * (ab)use Element.isSynthetic, which isn't currently used for |
| 1540 // LocalVariableElementImpl, so we could repurpose to mean "temp". | 1537 // LocalVariableElementImpl, so we could repurpose to mean "temp". |
| 1541 // * add a new property to LocalVariableElementImpl. | 1538 // * add a new property to LocalVariableElementImpl. |
| 1542 // * create a new subtype of LocalVariableElementImpl to mark a temp. | 1539 // * create a new subtype of LocalVariableElementImpl to mark a temp. |
| 1543 var id = | 1540 var id = |
| 1544 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); | 1541 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); |
| 1545 id.staticElement = new LocalVariableElementImpl.forNode(id); | 1542 id.staticElement = new LocalVariableElementImpl.forNode(id); |
| 1546 id.staticType = type; | 1543 id.staticType = type; |
| 1547 return id; | 1544 return id; |
| 1548 } | 1545 } |
| 1549 | 1546 |
| 1550 bool _isTemporary(Element node) => node.nameOffset == -1; | 1547 bool _isTemporary(Element node) => node.nameOffset == -1; |
| 1551 | 1548 |
| 1552 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { | 1549 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { |
| 1553 var type = rules.getStaticType(expr); | 1550 var type = rules.getStaticType(expr); |
| 1554 assert(type != null); | 1551 assert(type != null); |
| 1555 var tmp = _createTemporary('x', type); | 1552 var tmp = _createTemporary('x', type); |
| 1556 | 1553 |
| 1557 // Increment and write | 1554 // Increment and write |
| 1558 var one = AstBuilder.integerLiteral(1); | 1555 var one = AstBuilder.integerLiteral(1); |
| 1559 one.staticType = rules.provider.intType; | 1556 one.staticType = rules.provider.intType; |
| 1560 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); | 1557 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); |
| 1561 increment.staticType = type; | 1558 increment.staticType = type; |
| 1562 var write = _emitAssignment(expr, increment); | 1559 var write = _emitAssignment(expr, increment); |
| 1563 | 1560 |
| 1564 var bindThis = _maybeBindThis(expr); | 1561 return js.call( |
| 1565 return js.call("((#) => (#, #))$bindThis(#)", [ | 1562 "((#) => (#, #))(#)", [_visit(tmp), write, _visit(tmp), _visit(expr)]); |
| 1566 _visit(tmp), | |
| 1567 write, | |
| 1568 _visit(tmp), | |
| 1569 _visit(expr) | |
| 1570 ]); | |
| 1571 } | 1563 } |
| 1572 | 1564 |
| 1573 @override | 1565 @override |
| 1574 JS.Expression visitPostfixExpression(PostfixExpression node) { | 1566 JS.Expression visitPostfixExpression(PostfixExpression node) { |
| 1575 var op = node.operator; | 1567 var op = node.operator; |
| 1576 var expr = node.operand; | 1568 var expr = node.operand; |
| 1577 | 1569 |
| 1578 if (node.parent is Statement) { | 1570 if (node.parent is Statement) { |
| 1579 // Prefix code is simpler. If the expr result isn't used, fall to that. | 1571 // Prefix code is simpler. If the expr result isn't used, fall to that. |
| 1580 return _emitPrefixExpression(op, expr); | 1572 return _emitPrefixExpression(op, expr); |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1655 } else { | 1647 } else { |
| 1656 // Use comma expression. For example: | 1648 // Use comma expression. For example: |
| 1657 // (sb.write(1), sb.write(2), sb) | 1649 // (sb.write(1), sb.write(2), sb) |
| 1658 var sections = _visitListToBinary(node.cascadeSections, ','); | 1650 var sections = _visitListToBinary(node.cascadeSections, ','); |
| 1659 result = new JS.Binary(',', sections, _visit(_cascadeTarget)); | 1651 result = new JS.Binary(',', sections, _visit(_cascadeTarget)); |
| 1660 } | 1652 } |
| 1661 } else { | 1653 } else { |
| 1662 // In the general case we need to capture the target expression into | 1654 // In the general case we need to capture the target expression into |
| 1663 // a temporary. This uses a lambda to get a temporary scope, and it also | 1655 // a temporary. This uses a lambda to get a temporary scope, and it also |
| 1664 // remains valid in an expression context. | 1656 // remains valid in an expression context. |
| 1665 // TODO(jmesserly): need a better way to handle temps. | |
| 1666 // TODO(jmesserly): special case for parent is ExpressionStatement? | |
| 1667 _cascadeTarget = _createTemporary('_', node.target.staticType); | 1657 _cascadeTarget = _createTemporary('_', node.target.staticType); |
| 1668 | 1658 |
| 1669 var body = _visitList(node.cascadeSections); | 1659 var body = _visitList(node.cascadeSections); |
| 1670 if (node.parent is! ExpressionStatement) { | 1660 if (node.parent is! ExpressionStatement) { |
| 1671 body.add(js.statement('return #;', _visit(_cascadeTarget))); | 1661 body.add(js.statement('return #;', _visit(_cascadeTarget))); |
| 1672 } | 1662 } |
| 1673 | 1663 |
| 1674 var bindThis = _maybeBindThis(node.cascadeSections); | 1664 result = js.call('((#) => { # })(#)', [ |
| 1675 result = js.call('((#) => { # })$bindThis(#)', [ | |
| 1676 _visit(_cascadeTarget), | 1665 _visit(_cascadeTarget), |
| 1677 body, | 1666 body, |
| 1678 _visit(node.target) | 1667 _visit(node.target) |
| 1679 ]); | 1668 ]); |
| 1680 } | 1669 } |
| 1681 | 1670 |
| 1682 _cascadeTarget = savedCascadeTemp; | 1671 _cascadeTarget = savedCascadeTemp; |
| 1683 return result; | 1672 return result; |
| 1684 } | 1673 } |
| 1685 | 1674 |
| (...skipping 484 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2170 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2159 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
| 2171 | 2160 |
| 2172 /// Choose a canonical name from the library element. | 2161 /// Choose a canonical name from the library element. |
| 2173 /// This never uses the library's name (the identifier in the `library` | 2162 /// This never uses the library's name (the identifier in the `library` |
| 2174 /// declaration) as it doesn't have any meaningful rules enforced. | 2163 /// declaration) as it doesn't have any meaningful rules enforced. |
| 2175 JS.Identifier _libraryName(LibraryElement library) { | 2164 JS.Identifier _libraryName(LibraryElement library) { |
| 2176 if (library == libraryInfo.library) return _exportsVar; | 2165 if (library == libraryInfo.library) return _exportsVar; |
| 2177 return new JS.Identifier(jsLibraryName(library)); | 2166 return new JS.Identifier(jsLibraryName(library)); |
| 2178 } | 2167 } |
| 2179 | 2168 |
| 2180 String _maybeBindThis(node) { | |
| 2181 if (currentClass == null) return ''; | |
| 2182 var visitor = _BindThisVisitor._instance; | |
| 2183 visitor._bindThis = false; | |
| 2184 node.accept(visitor); | |
| 2185 return visitor._bindThis ? '.bind(this)' : ''; | |
| 2186 } | |
| 2187 | |
| 2188 static bool _needsImplicitThis(Element e) => | 2169 static bool _needsImplicitThis(Element e) => |
| 2189 e is PropertyAccessorElement && !e.variable.isStatic || | 2170 e is PropertyAccessorElement && !e.variable.isStatic || |
| 2190 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; | 2171 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; |
| 2191 } | 2172 } |
| 2192 | 2173 |
| 2193 /// Returns true if the local variable is potentially mutated within [context]. | 2174 /// Returns true if the local variable is potentially mutated within [context]. |
| 2194 /// This accounts for closures that may have been created outside of [context]. | 2175 /// This accounts for closures that may have been created outside of [context]. |
| 2195 bool _isPotentiallyMutated(VariableElementImpl e, [AstNode context]) { | 2176 bool _isPotentiallyMutated(VariableElementImpl e, [AstNode context]) { |
| 2196 if (e.isPotentiallyMutatedInClosure) { | 2177 if (e.isPotentiallyMutatedInClosure) { |
| 2197 // TODO(jmesserly): this returns true incorrectly in some cases, because | 2178 // TODO(jmesserly): this returns true incorrectly in some cases, because |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2232 identical(parent.methodName, node)) return; | 2213 identical(parent.methodName, node)) return; |
| 2233 if (parent is ConstructorName) return; | 2214 if (parent is ConstructorName) return; |
| 2234 if (parent is Label) return; | 2215 if (parent is Label) return; |
| 2235 | 2216 |
| 2236 if (node.inSetterContext() && node.staticElement == _variable) { | 2217 if (node.inSetterContext() && node.staticElement == _variable) { |
| 2237 _potentiallyMutated = true; | 2218 _potentiallyMutated = true; |
| 2238 } | 2219 } |
| 2239 } | 2220 } |
| 2240 } | 2221 } |
| 2241 | 2222 |
| 2242 /// This is a workaround for V8 arrow function bindings being not yet | |
| 2243 /// implemented. See issue #43 | |
| 2244 // TODO(jmesserly): cleaner to handle this workaround on the JS side. | |
| 2245 class _BindThisVisitor extends RecursiveAstVisitor { | |
| 2246 static _BindThisVisitor _instance = new _BindThisVisitor(); | |
| 2247 bool _bindThis = false; | |
| 2248 | |
| 2249 @override | |
| 2250 visitSimpleIdentifier(SimpleIdentifier node) { | |
| 2251 if (JSCodegenVisitor._needsImplicitThis(node.staticElement)) { | |
| 2252 _bindThis = true; | |
| 2253 } | |
| 2254 } | |
| 2255 | |
| 2256 @override | |
| 2257 visitThisExpression(ThisExpression node) { | |
| 2258 _bindThis = true; | |
| 2259 } | |
| 2260 } | |
| 2261 | |
| 2262 class JSGenerator extends CodeGenerator { | 2223 class JSGenerator extends CodeGenerator { |
| 2263 final JSCodeOptions options; | 2224 final JSCodeOptions options; |
| 2264 | 2225 |
| 2265 JSGenerator(String outDir, Uri root, TypeRules rules, this.options) | 2226 JSGenerator(String outDir, Uri root, TypeRules rules, this.options) |
| 2266 : super(outDir, root, rules); | 2227 : super(outDir, root, rules); |
| 2267 | 2228 |
| 2268 String generateLibrary( | 2229 String generateLibrary( |
| 2269 LibraryUnit unit, LibraryInfo info, CheckerReporter reporter) { | 2230 LibraryUnit unit, LibraryInfo info, CheckerReporter reporter) { |
| 2270 var jsTree = new JSCodegenVisitor(info, rules, reporter).emitLibrary(unit); | 2231 var jsTree = new JSCodegenVisitor(info, rules, reporter).emitLibrary(unit); |
| 2271 | 2232 |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2384 | 2345 |
| 2385 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2346 // TODO(jmesserly): in many cases marking the end will be unncessary. |
| 2386 printer.mark(_location(node.end)); | 2347 printer.mark(_location(node.end)); |
| 2387 } | 2348 } |
| 2388 | 2349 |
| 2389 String _getIdentifier(AstNode node) { | 2350 String _getIdentifier(AstNode node) { |
| 2390 if (node is SimpleIdentifier) return node.name; | 2351 if (node is SimpleIdentifier) return node.name; |
| 2391 return null; | 2352 return null; |
| 2392 } | 2353 } |
| 2393 } | 2354 } |
| OLD | NEW |