| 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 | 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 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 | 178 |
| 179 // TODO(jmesserly): scriptTag, directives. | 179 // TODO(jmesserly): scriptTag, directives. |
| 180 var body = <JS.Statement>[]; | 180 var body = <JS.Statement>[]; |
| 181 for (var child in node.declarations) { | 181 for (var child in node.declarations) { |
| 182 // Attempt to group adjacent fields/properties. | 182 // Attempt to group adjacent fields/properties. |
| 183 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | 183 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
| 184 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | 184 if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
| 185 | 185 |
| 186 var code = _visit(child); | 186 var code = _visit(child); |
| 187 if (code != null) { | 187 if (code != null) { |
| 188 if (_pendingStatements.isNotEmpty) { | 188 _flushPendingStatements(body); |
| 189 body.addAll(_pendingStatements); | |
| 190 _pendingStatements.clear(); | |
| 191 } | |
| 192 body.add(code); | 189 body.add(code); |
| 193 } | 190 } |
| 194 } | 191 } |
| 195 | 192 |
| 196 // Flush any unwritten fields/properties. | 193 // Flush any unwritten fields/properties. |
| 197 _flushLazyFields(body); | 194 _flushLazyFields(body); |
| 198 _flushLibraryProperties(body); | 195 _flushLibraryProperties(body); |
| 199 | 196 |
| 200 assert(_pendingStatements.isEmpty); | 197 assert(_pendingStatements.isEmpty); |
| 201 return _statement(body); | 198 return _statement(body); |
| (...skipping 917 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1119 if (type.isVoid) { | 1116 if (type.isVoid) { |
| 1120 return js.call('dart.void'); | 1117 return js.call('dart.void'); |
| 1121 } else if (type.isDynamic) { | 1118 } else if (type.isDynamic) { |
| 1122 return js.call('dart.dynamic'); | 1119 return js.call('dart.dynamic'); |
| 1123 } | 1120 } |
| 1124 | 1121 |
| 1125 var name = type.name; | 1122 var name = type.name; |
| 1126 var element = type.element; | 1123 var element = type.element; |
| 1127 if (name == '' || lowerTypedef && type is FunctionType) { | 1124 if (name == '' || lowerTypedef && type is FunctionType) { |
| 1128 if (type is FunctionType) { | 1125 if (type is FunctionType) { |
| 1129 // TODO(vsm): Support all parameter types. | |
| 1130 var returnType = type.returnType; | 1126 var returnType = type.returnType; |
| 1131 var parameterTypes = type.normalParameterTypes; | 1127 var parameterTypes = type.normalParameterTypes; |
| 1132 var optionalTypes = type.optionalParameterTypes; | 1128 var optionalTypes = type.optionalParameterTypes; |
| 1133 var namedTypes = type.namedParameterTypes; | 1129 var namedTypes = type.namedParameterTypes; |
| 1134 if (namedTypes.isEmpty) { | 1130 if (namedTypes.isEmpty) { |
| 1135 if (optionalTypes.isEmpty) { | 1131 if (optionalTypes.isEmpty) { |
| 1136 return js.call('dart.functionType(#, #)', [ | 1132 return js.call('dart.functionType(#, #)', [ |
| 1137 _emitTypeName(returnType), | 1133 _emitTypeName(returnType), |
| 1138 _emitTypeNames(parameterTypes) | 1134 _emitTypeNames(parameterTypes) |
| 1139 ]); | 1135 ]); |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1269 if (target == null || isLibraryPrefix(target)) { | 1265 if (target == null || isLibraryPrefix(target)) { |
| 1270 if (rules.isDynamicCall(node.methodName)) { | 1266 if (rules.isDynamicCall(node.methodName)) { |
| 1271 code = 'dart.$DCALL(#, #)'; | 1267 code = 'dart.$DCALL(#, #)'; |
| 1272 } else { | 1268 } else { |
| 1273 code = '#(#)'; | 1269 code = '#(#)'; |
| 1274 } | 1270 } |
| 1275 return js.call( | 1271 return js.call( |
| 1276 code, [_visit(node.methodName), _visit(node.argumentList)]); | 1272 code, [_visit(node.methodName), _visit(node.argumentList)]); |
| 1277 } | 1273 } |
| 1278 | 1274 |
| 1275 var type = getStaticType(target); |
| 1276 var name = node.methodName.name; |
| 1277 var memberName = _emitMemberName(name, type: type); |
| 1278 |
| 1279 if (rules.isDynamicTarget(target)) { | 1279 if (rules.isDynamicTarget(target)) { |
| 1280 code = 'dart.$DSEND(#, #, #)'; | 1280 code = 'dart.$DSEND(#, #, #)'; |
| 1281 } else if (rules.isDynamicCall(node.methodName)) { | 1281 } else if (rules.isDynamicCall(node.methodName)) { |
| 1282 // This is a dynamic call to a statically know target. For example: | 1282 // This is a dynamic call to a statically know target. For example: |
| 1283 // class Foo { Function bar; } | 1283 // class Foo { Function bar; } |
| 1284 // new Foo().bar(); // dynamic call | 1284 // new Foo().bar(); // dynamic call |
| 1285 code = 'dart.$DCALL(#.#, #)'; | 1285 code = 'dart.$DCALL(#.#, #)'; |
| 1286 } else if (_requiresStaticDispatch(target, name)) { |
| 1287 assert(rules.objectMembers[name] is FunctionType); |
| 1288 // Object methods require a helper for null checks. |
| 1289 return js.call('dart.#(#, #)', [ |
| 1290 memberName, |
| 1291 _visit(target), |
| 1292 _visit(node.argumentList) |
| 1293 ]); |
| 1286 } else { | 1294 } else { |
| 1287 code = '#.#(#)'; | 1295 code = '#.#(#)'; |
| 1288 } | 1296 } |
| 1289 return js.call(code, [ | 1297 |
| 1290 _visit(target), | 1298 return js.call( |
| 1291 _emitMemberName(node.methodName.name, type: getStaticType(target)), | 1299 code, [_visit(target), memberName, _visit(node.argumentList)]); |
| 1292 _visit(node.argumentList) | |
| 1293 ]); | |
| 1294 } | 1300 } |
| 1295 | 1301 |
| 1296 /// Emits code for the `JS(...)` builtin. | 1302 /// Emits code for the `JS(...)` builtin. |
| 1297 _emitForeignJS(MethodInvocation node) { | 1303 _emitForeignJS(MethodInvocation node) { |
| 1298 var e = node.methodName.staticElement; | 1304 var e = node.methodName.staticElement; |
| 1299 if (e is FunctionElement && | 1305 if (e is FunctionElement && |
| 1300 e.library.name == '_foreign_helper' && | 1306 e.library.name == '_foreign_helper' && |
| 1301 e.name == 'JS') { | 1307 e.name == 'JS') { |
| 1302 var args = node.argumentList.arguments; | 1308 var args = node.argumentList.arguments; |
| 1303 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` | 1309 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1435 return new JS.VariableInitialization(name, _visitInitializer(node)); | 1441 return new JS.VariableInitialization(name, _visitInitializer(node)); |
| 1436 } | 1442 } |
| 1437 | 1443 |
| 1438 JS.Expression _visitInitializer(VariableDeclaration node) { | 1444 JS.Expression _visitInitializer(VariableDeclaration node) { |
| 1439 var value = _visit(node.initializer); | 1445 var value = _visit(node.initializer); |
| 1440 // explicitly initialize to null, to avoid getting `undefined`. | 1446 // explicitly initialize to null, to avoid getting `undefined`. |
| 1441 // TODO(jmesserly): do this only for vars that aren't definitely assigned. | 1447 // TODO(jmesserly): do this only for vars that aren't definitely assigned. |
| 1442 return value != null ? value : new JS.LiteralNull(); | 1448 return value != null ? value : new JS.LiteralNull(); |
| 1443 } | 1449 } |
| 1444 | 1450 |
| 1451 void _flushPendingStatements(List<JS.Statement> body) { |
| 1452 if (_pendingStatements.isNotEmpty) { |
| 1453 body.addAll(_pendingStatements); |
| 1454 _pendingStatements.clear(); |
| 1455 } |
| 1456 } |
| 1457 |
| 1445 void _flushLazyFields(List<JS.Statement> body) { | 1458 void _flushLazyFields(List<JS.Statement> body) { |
| 1446 var code = _emitLazyFields(_exportsVar, _lazyFields); | 1459 var code = _emitLazyFields(_exportsVar, _lazyFields); |
| 1447 if (code != null) body.add(code); | 1460 if (code != null) { |
| 1461 // Ensure symbols for private fields are defined. |
| 1462 _flushPendingStatements(body); |
| 1463 body.add(code); |
| 1464 } |
| 1448 _lazyFields.clear(); | 1465 _lazyFields.clear(); |
| 1449 } | 1466 } |
| 1450 | 1467 |
| 1451 JS.Statement _emitLazyFields( | 1468 JS.Statement _emitLazyFields( |
| 1452 JS.Expression objExpr, List<VariableDeclaration> fields) { | 1469 JS.Expression objExpr, List<VariableDeclaration> fields) { |
| 1453 if (fields.isEmpty) return null; | 1470 if (fields.isEmpty) return null; |
| 1454 | 1471 |
| 1455 var methods = []; | 1472 var methods = []; |
| 1456 for (var node in fields) { | 1473 for (var node in fields) { |
| 1457 var name = node.name.name; | 1474 var name = node.name.name; |
| 1458 methods.add(new JS.Method(_propertyName(name), | 1475 var element = node.element; |
| 1459 js.call('function() { return #; }', _visit(node.initializer)), | 1476 var access = _emitMemberName(name, type: element.type, isStatic: true); |
| 1477 methods.add(new JS.Method( |
| 1478 access, js.call('function() { return #; }', _visit(node.initializer)), |
| 1460 isGetter: true)); | 1479 isGetter: true)); |
| 1461 | 1480 |
| 1462 // TODO(jmesserly): use a dummy setter to indicate writable. | 1481 // TODO(jmesserly): use a dummy setter to indicate writable. |
| 1463 if (!node.isFinal) { | 1482 if (!node.isFinal) { |
| 1464 methods.add(new JS.Method( | 1483 methods.add( |
| 1465 _propertyName(name), js.call('function(_) {}'), isSetter: true)); | 1484 new JS.Method(access, js.call('function(_) {}'), isSetter: true)); |
| 1466 } | 1485 } |
| 1467 } | 1486 } |
| 1468 | 1487 |
| 1469 return js.statement( | 1488 return js.statement( |
| 1470 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); | 1489 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
| 1471 } | 1490 } |
| 1472 | 1491 |
| 1473 void _flushLibraryProperties(List<JS.Statement> body) { | 1492 void _flushLibraryProperties(List<JS.Statement> body) { |
| 1474 if (_properties.isEmpty) return; | 1493 if (_properties.isEmpty) return; |
| 1475 body.add(js.statement('dart.copyProperties(#, { # });', [ | 1494 body.add(js.statement('dart.copyProperties(#, { # });', [ |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1518 | 1537 |
| 1519 bool _isNonNullableExpression(Expression expr) { | 1538 bool _isNonNullableExpression(Expression expr) { |
| 1520 // If the type is non-nullable, no further checking needed. | 1539 // If the type is non-nullable, no further checking needed. |
| 1521 if (rules.isNonNullableType(getStaticType(expr))) return true; | 1540 if (rules.isNonNullableType(getStaticType(expr))) return true; |
| 1522 | 1541 |
| 1523 // TODO(vsm): Revisit whether we really need this when we get | 1542 // TODO(vsm): Revisit whether we really need this when we get |
| 1524 // better non-nullability in the type system. | 1543 // better non-nullability in the type system. |
| 1525 | 1544 |
| 1526 if (expr is Literal && expr is! NullLiteral) return true; | 1545 if (expr is Literal && expr is! NullLiteral) return true; |
| 1527 if (expr is IsExpression) return true; | 1546 if (expr is IsExpression) return true; |
| 1547 if (expr is ThisExpression) return true; |
| 1548 if (expr is SuperExpression) return true; |
| 1528 if (expr is ParenthesizedExpression) { | 1549 if (expr is ParenthesizedExpression) { |
| 1529 return _isNonNullableExpression(expr.expression); | 1550 return _isNonNullableExpression(expr.expression); |
| 1530 } | 1551 } |
| 1531 if (expr is Conversion) { | 1552 if (expr is Conversion) { |
| 1532 return _isNonNullableExpression(expr.expression); | 1553 return _isNonNullableExpression(expr.expression); |
| 1533 } | 1554 } |
| 1534 DartType type = null; | 1555 DartType type = null; |
| 1535 if (expr is BinaryExpression) { | 1556 if (expr is BinaryExpression) { |
| 1536 type = getStaticType(expr.leftOperand); | 1557 type = getStaticType(expr.leftOperand); |
| 1537 } else if (expr is PrefixExpression) { | 1558 } else if (expr is PrefixExpression) { |
| (...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1858 return _visit(node.identifier); | 1879 return _visit(node.identifier); |
| 1859 } else { | 1880 } else { |
| 1860 return _emitGet(node.prefix, node.identifier); | 1881 return _emitGet(node.prefix, node.identifier); |
| 1861 } | 1882 } |
| 1862 } | 1883 } |
| 1863 | 1884 |
| 1864 @override | 1885 @override |
| 1865 visitPropertyAccess(PropertyAccess node) => | 1886 visitPropertyAccess(PropertyAccess node) => |
| 1866 _emitGet(_getTarget(node), node.propertyName); | 1887 _emitGet(_getTarget(node), node.propertyName); |
| 1867 | 1888 |
| 1889 bool _requiresStaticDispatch(Expression target, String memberName) { |
| 1890 var type = getStaticType(target); |
| 1891 if (!rules.objectMembers.containsKey(memberName)) { |
| 1892 return false; |
| 1893 } |
| 1894 if (!type.isObject && |
| 1895 !_isJSBuiltinType(type) && |
| 1896 _isNonNullableExpression(target)) { |
| 1897 return false; |
| 1898 } |
| 1899 return true; |
| 1900 } |
| 1901 |
| 1868 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1902 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
| 1869 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { | 1903 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
| 1870 var name = _emitMemberName(memberId.name, type: getStaticType(target)); | 1904 var name = _emitMemberName(memberId.name, type: getStaticType(target)); |
| 1871 if (rules.isDynamicTarget(target)) { | 1905 if (rules.isDynamicTarget(target)) { |
| 1872 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); | 1906 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); |
| 1873 } | 1907 } |
| 1874 | 1908 |
| 1875 String code; | 1909 String code; |
| 1876 var member = memberId.staticElement; | 1910 var member = memberId.staticElement; |
| 1877 if (member != null && member is MethodElement) { | 1911 if (member != null && member is MethodElement) { |
| 1878 // Tear-off methods: explicitly bind it. | 1912 // Tear-off methods: explicitly bind it. |
| 1913 if (_requiresStaticDispatch(target, memberId.name)) { |
| 1914 return js.call('dart.#.bind(#)', [name, _visit(target)]); |
| 1915 } |
| 1879 if (isStateless(target, target)) { | 1916 if (isStateless(target, target)) { |
| 1880 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]); | 1917 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]); |
| 1881 } | 1918 } |
| 1882 code = 'dart.bind(#, #)'; | 1919 code = 'dart.bind(#, #)'; |
| 1920 } else if (_requiresStaticDispatch(target, memberId.name)) { |
| 1921 return js.call('dart.#(#)', [name, _visit(target)]); |
| 1883 } else { | 1922 } else { |
| 1884 code = '#.#'; | 1923 code = '#.#'; |
| 1885 } | 1924 } |
| 1886 | 1925 |
| 1887 return js.call(code, [_visit(target), name]); | 1926 return js.call(code, [_visit(target), name]); |
| 1888 } | 1927 } |
| 1889 | 1928 |
| 1890 /// Emits a generic send, like an operator method. | 1929 /// Emits a generic send, like an operator method. |
| 1891 /// | 1930 /// |
| 1892 /// **Please note** this function does not support method invocation syntax | 1931 /// **Please note** this function does not support method invocation syntax |
| (...skipping 576 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2469 // TODO(jmesserly): validate the library. See issue #135. | 2508 // TODO(jmesserly): validate the library. See issue #135. |
| 2470 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2509 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
| 2471 | 2510 |
| 2472 bool _isJsPeerInterface(DartObjectImpl value) => | 2511 bool _isJsPeerInterface(DartObjectImpl value) => |
| 2473 value.type.name == 'JsPeerInterface'; | 2512 value.type.name == 'JsPeerInterface'; |
| 2474 | 2513 |
| 2475 // TODO(jacobr): we would like to do something like the following | 2514 // TODO(jacobr): we would like to do something like the following |
| 2476 // but we don't have summary support yet. | 2515 // but we don't have summary support yet. |
| 2477 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2516 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
| 2478 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2517 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
| OLD | NEW |