Chromium Code Reviews| 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 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 61 final HashSet<FieldElement> _fieldsNeedingStorage; | 61 final HashSet<FieldElement> _fieldsNeedingStorage; |
| 62 | 62 |
| 63 /// The variable for the target of the current `..` cascade expression. | 63 /// The variable for the target of the current `..` cascade expression. |
| 64 SimpleIdentifier _cascadeTarget; | 64 SimpleIdentifier _cascadeTarget; |
| 65 | 65 |
| 66 /// The variable for the current catch clause | 66 /// The variable for the current catch clause |
| 67 SimpleIdentifier _catchParameter; | 67 SimpleIdentifier _catchParameter; |
| 68 | 68 |
| 69 ConstantEvaluator _constEvaluator; | 69 ConstantEvaluator _constEvaluator; |
| 70 | 70 |
| 71 /// Map of fields / properties / methods on Object. | |
| 72 Map<String, DartType> _objectMembers; | |
| 73 | |
| 71 final _exports = new Set<String>(); | 74 final _exports = new Set<String>(); |
| 72 final _lazyFields = <VariableDeclaration>[]; | 75 final _lazyFields = <VariableDeclaration>[]; |
| 73 final _properties = <FunctionDeclaration>[]; | 76 final _properties = <FunctionDeclaration>[]; |
| 74 final _privateNames = new HashMap<String, JSTemporary>(); | 77 final _privateNames = new HashMap<String, JSTemporary>(); |
| 75 final _extensionMethodNames = new HashSet<String>(); | 78 final _extensionMethodNames = new HashSet<String>(); |
| 76 final _pendingStatements = <JS.Statement>[]; | 79 final _pendingStatements = <JS.Statement>[]; |
| 77 final _temps = new HashMap<Element, JSTemporary>(); | 80 final _temps = new HashMap<Element, JSTemporary>(); |
| 78 | 81 |
| 79 /// The name for the library's exports inside itself. | 82 /// The name for the library's exports inside itself. |
| 80 /// This much be a constant because we interpolate it into template strings, | 83 /// This much be a constant because we interpolate it into template strings, |
| 81 /// and otherwise it would break caching for them. | 84 /// and otherwise it would break caching for them. |
| 82 /// `exports` was chosen as the most similar to ES module patterns. | 85 /// `exports` was chosen as the most similar to ES module patterns. |
| 83 final JSTemporary _exportsVar = new JSTemporary('exports'); | 86 final JSTemporary _exportsVar = new JSTemporary('exports'); |
| 84 final JSTemporary _namedArgTemp = new JSTemporary('opts'); | 87 final JSTemporary _namedArgTemp = new JSTemporary('opts'); |
| 85 | 88 |
| 86 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | 89 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or |
| 87 /// [ClassTypeAlias]. | 90 /// [ClassTypeAlias]. |
| 88 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); | 91 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); |
| 89 | 92 |
| 90 /// Memoized results of [_lazyClass]. | 93 /// Memoized results of [_lazyClass]. |
| 91 final _lazyClassMemo = new HashMap<Element, bool>(); | 94 final _lazyClassMemo = new HashMap<Element, bool>(); |
| 92 | 95 |
| 93 /// Memoized results of [_inLibraryCycle]. | 96 /// Memoized results of [_inLibraryCycle]. |
| 94 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | 97 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); |
| 95 | 98 |
| 96 JSCodegenVisitor(this.options, this.rules, this.libraryInfo, | 99 JSCodegenVisitor(this.options, this.rules, this.libraryInfo, |
| 97 this._extensionMethods, this._fieldsNeedingStorage); | 100 this._extensionMethods, this._fieldsNeedingStorage) { |
| 101 _objectMembers = getObjectMemberMap(rules.provider); | |
|
Jennifer Messerly
2015/04/23 19:01:42
thought here, TypeRules is our type (src/checker/r
vsm
2015/04/23 20:35:55
Yeah, I suppose that's better than here.
Done.
| |
| 102 } | |
| 98 | 103 |
| 99 LibraryElement get currentLibrary => libraryInfo.library; | 104 LibraryElement get currentLibrary => libraryInfo.library; |
| 100 TypeProvider get types => rules.provider; | 105 TypeProvider get types => rules.provider; |
| 101 | 106 |
| 102 JS.Program emitLibrary(LibraryUnit library) { | 107 JS.Program emitLibrary(LibraryUnit library) { |
| 103 String jsDefaultValue = null; | 108 String jsDefaultValue = null; |
| 104 | 109 |
| 105 // Modify the AST to make coercions explicit. | 110 // Modify the AST to make coercions explicit. |
| 106 new CoercionReifier(library, rules, options).reify(); | 111 new CoercionReifier(library, rules, options).reify(); |
| 107 | 112 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 178 | 183 |
| 179 // TODO(jmesserly): scriptTag, directives. | 184 // TODO(jmesserly): scriptTag, directives. |
| 180 var body = <JS.Statement>[]; | 185 var body = <JS.Statement>[]; |
| 181 for (var child in node.declarations) { | 186 for (var child in node.declarations) { |
| 182 // Attempt to group adjacent fields/properties. | 187 // Attempt to group adjacent fields/properties. |
| 183 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | 188 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
| 184 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | 189 if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
| 185 | 190 |
| 186 var code = _visit(child); | 191 var code = _visit(child); |
| 187 if (code != null) { | 192 if (code != null) { |
| 188 if (_pendingStatements.isNotEmpty) { | 193 _flushPendingStatements(body); |
| 189 body.addAll(_pendingStatements); | |
| 190 _pendingStatements.clear(); | |
| 191 } | |
| 192 body.add(code); | 194 body.add(code); |
| 193 } | 195 } |
| 194 } | 196 } |
| 195 | 197 |
| 196 // Flush any unwritten fields/properties. | 198 // Flush any unwritten fields/properties. |
| 197 _flushLazyFields(body); | 199 _flushLazyFields(body); |
| 198 _flushLibraryProperties(body); | 200 _flushLibraryProperties(body); |
| 199 | 201 |
| 200 assert(_pendingStatements.isEmpty); | 202 assert(_pendingStatements.isEmpty); |
| 201 return _statement(body); | 203 return _statement(body); |
| (...skipping 917 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1119 if (type.isVoid) { | 1121 if (type.isVoid) { |
| 1120 return js.call('dart.void'); | 1122 return js.call('dart.void'); |
| 1121 } else if (type.isDynamic) { | 1123 } else if (type.isDynamic) { |
| 1122 return js.call('dart.dynamic'); | 1124 return js.call('dart.dynamic'); |
| 1123 } | 1125 } |
| 1124 | 1126 |
| 1125 var name = type.name; | 1127 var name = type.name; |
| 1126 var element = type.element; | 1128 var element = type.element; |
| 1127 if (name == '' || lowerTypedef && type is FunctionType) { | 1129 if (name == '' || lowerTypedef && type is FunctionType) { |
| 1128 if (type is FunctionType) { | 1130 if (type is FunctionType) { |
| 1129 // TODO(vsm): Support all parameter types. | |
| 1130 var returnType = type.returnType; | 1131 var returnType = type.returnType; |
| 1131 var parameterTypes = type.normalParameterTypes; | 1132 var parameterTypes = type.normalParameterTypes; |
| 1132 var optionalTypes = type.optionalParameterTypes; | 1133 var optionalTypes = type.optionalParameterTypes; |
| 1133 var namedTypes = type.namedParameterTypes; | 1134 var namedTypes = type.namedParameterTypes; |
| 1134 if (namedTypes.isEmpty) { | 1135 if (namedTypes.isEmpty) { |
| 1135 if (optionalTypes.isEmpty) { | 1136 if (optionalTypes.isEmpty) { |
| 1136 return js.call('dart.functionType(#, #)', [ | 1137 return js.call('dart.functionType(#, #)', [ |
| 1137 _emitTypeName(returnType), | 1138 _emitTypeName(returnType), |
| 1138 _emitTypeNames(parameterTypes) | 1139 _emitTypeNames(parameterTypes) |
| 1139 ]); | 1140 ]); |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1269 if (target == null || isLibraryPrefix(target)) { | 1270 if (target == null || isLibraryPrefix(target)) { |
| 1270 if (rules.isDynamicCall(node.methodName)) { | 1271 if (rules.isDynamicCall(node.methodName)) { |
| 1271 code = 'dart.$DCALL(#, #)'; | 1272 code = 'dart.$DCALL(#, #)'; |
| 1272 } else { | 1273 } else { |
| 1273 code = '#(#)'; | 1274 code = '#(#)'; |
| 1274 } | 1275 } |
| 1275 return js.call( | 1276 return js.call( |
| 1276 code, [_visit(node.methodName), _visit(node.argumentList)]); | 1277 code, [_visit(node.methodName), _visit(node.argumentList)]); |
| 1277 } | 1278 } |
| 1278 | 1279 |
| 1280 var type = getStaticType(target); | |
| 1281 var name = node.methodName.name; | |
| 1282 var memberName = _emitMemberName(name, type: type); | |
| 1283 | |
| 1279 if (rules.isDynamicTarget(target)) { | 1284 if (rules.isDynamicTarget(target)) { |
| 1280 code = 'dart.$DSEND(#, #, #)'; | 1285 code = 'dart.$DSEND(#, #, #)'; |
| 1281 } else if (rules.isDynamicCall(node.methodName)) { | 1286 } else if (rules.isDynamicCall(node.methodName)) { |
| 1282 // This is a dynamic call to a statically know target. For example: | 1287 // This is a dynamic call to a statically know target. For example: |
| 1283 // class Foo { Function bar; } | 1288 // class Foo { Function bar; } |
| 1284 // new Foo().bar(); // dynamic call | 1289 // new Foo().bar(); // dynamic call |
| 1285 code = 'dart.$DCALL(#.#, #)'; | 1290 code = 'dart.$DCALL(#.#, #)'; |
| 1291 } else if (_objectMembers.containsKey(name)) { | |
|
Jennifer Messerly
2015/04/23 19:05:58
Is this only if `target` is nullable**? A test cas
vsm
2015/04/23 20:35:55
I added a check in. We need both checks as all ty
| |
| 1292 assert(_objectMembers[name] is FunctionType); | |
| 1293 // Object methods require a helper for null checks. | |
| 1294 return js.call('dart.#(#, #)', [ | |
| 1295 memberName, | |
| 1296 _visit(target), | |
| 1297 _visit(node.argumentList) | |
| 1298 ]); | |
| 1286 } else { | 1299 } else { |
| 1287 code = '#.#(#)'; | 1300 code = '#.#(#)'; |
| 1288 } | 1301 } |
| 1289 return js.call(code, [ | 1302 |
| 1290 _visit(target), | 1303 return js.call( |
| 1291 _emitMemberName(node.methodName.name, type: getStaticType(target)), | 1304 code, [_visit(target), memberName, _visit(node.argumentList)]); |
| 1292 _visit(node.argumentList) | |
| 1293 ]); | |
| 1294 } | 1305 } |
| 1295 | 1306 |
| 1296 /// Emits code for the `JS(...)` builtin. | 1307 /// Emits code for the `JS(...)` builtin. |
| 1297 _emitForeignJS(MethodInvocation node) { | 1308 _emitForeignJS(MethodInvocation node) { |
| 1298 var e = node.methodName.staticElement; | 1309 var e = node.methodName.staticElement; |
| 1299 if (e is FunctionElement && | 1310 if (e is FunctionElement && |
| 1300 e.library.name == '_foreign_helper' && | 1311 e.library.name == '_foreign_helper' && |
| 1301 e.name == 'JS') { | 1312 e.name == 'JS') { |
| 1302 var args = node.argumentList.arguments; | 1313 var args = node.argumentList.arguments; |
| 1303 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` | 1314 // 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)); | 1446 return new JS.VariableInitialization(name, _visitInitializer(node)); |
| 1436 } | 1447 } |
| 1437 | 1448 |
| 1438 JS.Expression _visitInitializer(VariableDeclaration node) { | 1449 JS.Expression _visitInitializer(VariableDeclaration node) { |
| 1439 var value = _visit(node.initializer); | 1450 var value = _visit(node.initializer); |
| 1440 // explicitly initialize to null, to avoid getting `undefined`. | 1451 // explicitly initialize to null, to avoid getting `undefined`. |
| 1441 // TODO(jmesserly): do this only for vars that aren't definitely assigned. | 1452 // TODO(jmesserly): do this only for vars that aren't definitely assigned. |
| 1442 return value != null ? value : new JS.LiteralNull(); | 1453 return value != null ? value : new JS.LiteralNull(); |
| 1443 } | 1454 } |
| 1444 | 1455 |
| 1456 void _flushPendingStatements(List<JS.Statement> body) { | |
| 1457 if (_pendingStatements.isNotEmpty) { | |
| 1458 body.addAll(_pendingStatements); | |
| 1459 _pendingStatements.clear(); | |
| 1460 } | |
| 1461 } | |
| 1462 | |
| 1445 void _flushLazyFields(List<JS.Statement> body) { | 1463 void _flushLazyFields(List<JS.Statement> body) { |
| 1446 var code = _emitLazyFields(_exportsVar, _lazyFields); | 1464 var code = _emitLazyFields(_exportsVar, _lazyFields); |
| 1447 if (code != null) body.add(code); | 1465 if (code != null) { |
| 1466 // Ensure symbols for private fields are defined. | |
| 1467 _flushPendingStatements(body); | |
| 1468 body.add(code); | |
| 1469 } | |
| 1448 _lazyFields.clear(); | 1470 _lazyFields.clear(); |
| 1449 } | 1471 } |
| 1450 | 1472 |
| 1451 JS.Statement _emitLazyFields( | 1473 JS.Statement _emitLazyFields( |
| 1452 JS.Expression objExpr, List<VariableDeclaration> fields) { | 1474 JS.Expression objExpr, List<VariableDeclaration> fields) { |
| 1453 if (fields.isEmpty) return null; | 1475 if (fields.isEmpty) return null; |
| 1454 | 1476 |
| 1455 var methods = []; | 1477 var methods = []; |
| 1456 for (var node in fields) { | 1478 for (var node in fields) { |
| 1457 var name = node.name.name; | 1479 var name = node.name.name; |
| 1458 methods.add(new JS.Method(_propertyName(name), | 1480 var element = node.element; |
| 1459 js.call('function() { return #; }', _visit(node.initializer)), | 1481 var access = _emitMemberName(name, type: element.type, isStatic: true); |
| 1482 methods.add(new JS.Method( | |
| 1483 access, js.call('function() { return #; }', _visit(node.initializer)), | |
| 1460 isGetter: true)); | 1484 isGetter: true)); |
| 1461 | 1485 |
| 1462 // TODO(jmesserly): use a dummy setter to indicate writable. | 1486 // TODO(jmesserly): use a dummy setter to indicate writable. |
| 1463 if (!node.isFinal) { | 1487 if (!node.isFinal) { |
| 1464 methods.add(new JS.Method( | 1488 methods.add( |
| 1465 _propertyName(name), js.call('function(_) {}'), isSetter: true)); | 1489 new JS.Method(access, js.call('function(_) {}'), isSetter: true)); |
| 1466 } | 1490 } |
| 1467 } | 1491 } |
| 1468 | 1492 |
| 1469 return js.statement( | 1493 return js.statement( |
| 1470 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); | 1494 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
| 1471 } | 1495 } |
| 1472 | 1496 |
| 1473 void _flushLibraryProperties(List<JS.Statement> body) { | 1497 void _flushLibraryProperties(List<JS.Statement> body) { |
| 1474 if (_properties.isEmpty) return; | 1498 if (_properties.isEmpty) return; |
| 1475 body.add(js.statement('dart.copyProperties(#, { # });', [ | 1499 body.add(js.statement('dart.copyProperties(#, { # });', [ |
| (...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1869 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { | 1893 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
| 1870 var name = _emitMemberName(memberId.name, type: getStaticType(target)); | 1894 var name = _emitMemberName(memberId.name, type: getStaticType(target)); |
| 1871 if (rules.isDynamicTarget(target)) { | 1895 if (rules.isDynamicTarget(target)) { |
| 1872 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); | 1896 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); |
| 1873 } | 1897 } |
| 1874 | 1898 |
| 1875 String code; | 1899 String code; |
| 1876 var member = memberId.staticElement; | 1900 var member = memberId.staticElement; |
| 1877 if (member != null && member is MethodElement) { | 1901 if (member != null && member is MethodElement) { |
| 1878 // Tear-off methods: explicitly bind it. | 1902 // Tear-off methods: explicitly bind it. |
| 1879 if (isStateless(target, target)) { | 1903 if (isStateless(target, target) && |
| 1904 !_objectMembers.containsKey(memberId.name)) { | |
| 1880 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]); | 1905 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]); |
| 1881 } | 1906 } |
| 1882 code = 'dart.bind(#, #)'; | 1907 code = 'dart.bind(#, #)'; |
| 1908 } else if (_objectMembers.containsKey(memberId.name)) { | |
| 1909 code = 'dart.#(#)'; | |
| 1910 return js.call(code, [name, _visit(target)]); | |
| 1883 } else { | 1911 } else { |
| 1884 code = '#.#'; | 1912 code = '#.#'; |
| 1885 } | 1913 } |
| 1886 | 1914 |
| 1887 return js.call(code, [_visit(target), name]); | 1915 return js.call(code, [_visit(target), name]); |
| 1888 } | 1916 } |
| 1889 | 1917 |
| 1890 /// Emits a generic send, like an operator method. | 1918 /// Emits a generic send, like an operator method. |
| 1891 /// | 1919 /// |
| 1892 /// **Please note** this function does not support method invocation syntax | 1920 /// **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. | 2497 // TODO(jmesserly): validate the library. See issue #135. |
| 2470 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2498 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
| 2471 | 2499 |
| 2472 bool _isJsPeerInterface(DartObjectImpl value) => | 2500 bool _isJsPeerInterface(DartObjectImpl value) => |
| 2473 value.type.name == 'JsPeerInterface'; | 2501 value.type.name == 'JsPeerInterface'; |
| 2474 | 2502 |
| 2475 // TODO(jacobr): we would like to do something like the following | 2503 // TODO(jacobr): we would like to do something like the following |
| 2476 // but we don't have summary support yet. | 2504 // but we don't have summary support yet. |
| 2477 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2505 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
| 2478 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2506 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
| OLD | NEW |