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 import 'dart:collection' show HashMap, HashSet; | 5 import 'dart:collection' show HashMap, HashSet; |
6 import 'dart:math' show min, max; | 6 import 'dart:math' show min, max; |
7 | 7 |
8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 // TODO(jmesserly): find a cleaner design for this. | 217 // TODO(jmesserly): find a cleaner design for this. |
218 if (_isDartRuntime(library)) { | 218 if (_isDartRuntime(library)) { |
219 items.add(new JS.ExportDeclaration( | 219 items.add(new JS.ExportDeclaration( |
220 js.call('const # = Object.create(null)', [_dartxVar]))); | 220 js.call('const # = Object.create(null)', [_dartxVar]))); |
221 } | 221 } |
222 } | 222 } |
223 | 223 |
224 // Collect all Element -> Node mappings, in case we need to forward declare | 224 // Collect all Element -> Node mappings, in case we need to forward declare |
225 // any nodes. | 225 // any nodes. |
226 var nodes = new HashMap<Element, AstNode>.identity(); | 226 var nodes = new HashMap<Element, AstNode>.identity(); |
227 compilationUnits.map(_collectElements).forEach(nodes.addAll); | 227 var sdkBootstrappingFns = new List<FunctionElement>(); |
| 228 for (var unit in compilationUnits) { |
| 229 if (_isDartRuntime(unit.element.library)) { |
| 230 sdkBootstrappingFns.addAll(unit.element.functions); |
| 231 } |
| 232 _collectElements(unit, nodes); |
| 233 } |
228 _loader = new ElementLoader(_emitModuleItem, nodes); | 234 _loader = new ElementLoader(_emitModuleItem, nodes); |
229 | 235 |
230 // Add implicit dart:core dependency so it is first. | 236 // Add implicit dart:core dependency so it is first. |
231 emitLibraryName(dartCoreLibrary); | 237 emitLibraryName(dartCoreLibrary); |
232 | 238 |
| 239 // Emit SDK bootstrapping functions first, if any. |
| 240 sdkBootstrappingFns.forEach(_loader.emitDeclaration); |
| 241 |
233 // Visit each compilation unit and emit its code. | 242 // Visit each compilation unit and emit its code. |
234 // | 243 // |
235 // NOTE: declarations are not necessarily emitted in this order. | 244 // NOTE: declarations are not necessarily emitted in this order. |
236 // Order will be changed as needed so the resulting code can execute. | 245 // Order will be changed as needed so the resulting code can execute. |
237 // This is done by forward declaring items. | 246 // This is done by forward declaring items. |
238 compilationUnits.forEach(visitCompilationUnit); | 247 compilationUnits.forEach(visitCompilationUnit); |
239 | 248 |
240 // Declare imports | 249 // Declare imports |
241 _finishImports(items); | 250 _finishImports(items); |
242 | 251 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
312 imports.add(new JS.NameSpecifier(_runtimeLibVar)); | 321 imports.add(new JS.NameSpecifier(_runtimeLibVar)); |
313 imports.add(new JS.NameSpecifier(_dartxVar)); | 322 imports.add(new JS.NameSpecifier(_dartxVar)); |
314 } | 323 } |
315 items.add(new JS.ImportDeclaration( | 324 items.add(new JS.ImportDeclaration( |
316 namedImports: imports, from: js.string(module, "'"))); | 325 namedImports: imports, from: js.string(module, "'"))); |
317 }); | 326 }); |
318 } | 327 } |
319 | 328 |
320 /// Collect toplevel elements and nodes we need to emit, and returns | 329 /// Collect toplevel elements and nodes we need to emit, and returns |
321 /// an ordered map of these. | 330 /// an ordered map of these. |
322 static Map<Element, AstNode> _collectElements(CompilationUnit unit) { | 331 static void _collectElements( |
323 var map = <Element, AstNode>{}; | 332 CompilationUnit unit, Map<Element, AstNode> map) { |
324 for (var declaration in unit.declarations) { | 333 for (var declaration in unit.declarations) { |
325 if (declaration is TopLevelVariableDeclaration) { | 334 if (declaration is TopLevelVariableDeclaration) { |
326 for (var field in declaration.variables.variables) { | 335 for (var field in declaration.variables.variables) { |
327 map[field.element] = field; | 336 map[field.element] = field; |
328 } | 337 } |
329 } else { | 338 } else { |
330 map[declaration.element] = declaration; | 339 map[declaration.element] = declaration; |
331 } | 340 } |
332 } | 341 } |
333 return map; | |
334 } | 342 } |
335 | 343 |
336 void _emitModuleItem(AstNode node) { | 344 void _emitModuleItem(AstNode node) { |
337 // TODO(jmesserly): ideally we could do this at a smaller granularity. | 345 // TODO(jmesserly): ideally we could do this at a smaller granularity. |
338 // We'll need to be consistent about when we're generating functions, and | 346 // We'll need to be consistent about when we're generating functions, and |
339 // only run this on the outermost function. | 347 // only run this on the outermost function. |
340 inferNullableTypes(node); | 348 inferNullableTypes(node); |
341 | 349 |
342 var code = _visit(node); | 350 var code = _visit(node); |
343 if (code != null) _moduleItems.add(code); | 351 if (code != null) _moduleItems.add(code); |
(...skipping 610 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
954 JS.Method _emitSuperAccessorWrapper(MethodDeclaration method, | 962 JS.Method _emitSuperAccessorWrapper(MethodDeclaration method, |
955 InterfaceType type, List<ClassElement> superclasses) { | 963 InterfaceType type, List<ClassElement> superclasses) { |
956 var methodElement = method.element as PropertyAccessorElement; | 964 var methodElement = method.element as PropertyAccessorElement; |
957 var field = methodElement.variable; | 965 var field = methodElement.variable; |
958 if (!field.isSynthetic) return null; | 966 if (!field.isSynthetic) return null; |
959 var propertyOverrideResult = checkForPropertyOverride( | 967 var propertyOverrideResult = checkForPropertyOverride( |
960 methodElement.variable, superclasses, _extensionTypes); | 968 methodElement.variable, superclasses, _extensionTypes); |
961 | 969 |
962 // Generate a corresponding virtual getter / setter. | 970 // Generate a corresponding virtual getter / setter. |
963 var name = _elementMemberName(methodElement, | 971 var name = _elementMemberName(methodElement, |
964 allowExtensions: _extensionTypes.isNativeClass(type.element)); | 972 useExtension: _extensionTypes.isNativeClass(type.element)); |
965 if (method.isGetter) { | 973 if (method.isGetter) { |
966 // Generate a setter | 974 // Generate a setter |
967 if (field.setter != null || !propertyOverrideResult.foundSetter) | 975 if (field.setter != null || !propertyOverrideResult.foundSetter) |
968 return null; | 976 return null; |
969 var fn = js.call('function(value) { super[#] = value; }', [name]); | 977 var fn = js.call('function(value) { super[#] = value; }', [name]); |
970 return new JS.Method(name, fn, isSetter: true); | 978 return new JS.Method(name, fn, isSetter: true); |
971 } else { | 979 } else { |
972 // Generate a getter | 980 // Generate a getter |
973 if (field.getter != null || !propertyOverrideResult.foundGetter) | 981 if (field.getter != null || !propertyOverrideResult.foundGetter) |
974 return null; | 982 return null; |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1109 | 1117 |
1110 /// If a concrete class implements one of our extensions, we might need to | 1118 /// If a concrete class implements one of our extensions, we might need to |
1111 /// add forwarders. | 1119 /// add forwarders. |
1112 void _defineExtensionMembers(List<ExecutableElement> extensions, | 1120 void _defineExtensionMembers(List<ExecutableElement> extensions, |
1113 JS.Expression className, List<JS.Statement> body) { | 1121 JS.Expression className, List<JS.Statement> body) { |
1114 // If a concrete class implements one of our extensions, we might need to | 1122 // If a concrete class implements one of our extensions, we might need to |
1115 // add forwarders. | 1123 // add forwarders. |
1116 if (extensions.isNotEmpty) { | 1124 if (extensions.isNotEmpty) { |
1117 var methodNames = <JS.Expression>[]; | 1125 var methodNames = <JS.Expression>[]; |
1118 for (var e in extensions) { | 1126 for (var e in extensions) { |
1119 methodNames.add(_elementMemberName(e, allowExtensions: false)); | 1127 methodNames.add(_elementMemberName(e, useExtension: false)); |
1120 } | 1128 } |
1121 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ | 1129 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ |
1122 className, | 1130 className, |
1123 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) | 1131 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) |
1124 ])); | 1132 ])); |
1125 } | 1133 } |
1126 } | 1134 } |
1127 | 1135 |
1128 /// Emit the signature on the class recording the runtime type information | 1136 /// Emit the signature on the class recording the runtime type information |
1129 void _emitClassSignature( | 1137 void _emitClassSignature( |
(...skipping 17 matching lines...) Expand all Loading... |
1147 for (MethodDeclaration node in methods) { | 1155 for (MethodDeclaration node in methods) { |
1148 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 1156 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
1149 var name = node.name.name; | 1157 var name = node.name.name; |
1150 var element = node.element; | 1158 var element = node.element; |
1151 var inheritedElement = | 1159 var inheritedElement = |
1152 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 1160 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
1153 if (inheritedElement != null && inheritedElement.type == element.type) { | 1161 if (inheritedElement != null && inheritedElement.type == element.type) { |
1154 continue; | 1162 continue; |
1155 } | 1163 } |
1156 var memberName = _elementMemberName(element, | 1164 var memberName = _elementMemberName(element, |
1157 allowExtensions: _extensionTypes.isNativeClass(classElem)); | 1165 useExtension: _extensionTypes.isNativeClass(classElem)); |
1158 var parts = _emitFunctionTypeParts(element.type); | 1166 var parts = _emitFunctionTypeParts(element.type); |
1159 var property = | 1167 var property = |
1160 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 1168 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
1161 if (node.isStatic) { | 1169 if (node.isStatic) { |
1162 tStatics.add(property); | 1170 tStatics.add(property); |
1163 sNames.add(memberName); | 1171 sNames.add(memberName); |
1164 } else { | 1172 } else { |
1165 tMethods.add(property); | 1173 tMethods.add(property); |
1166 } | 1174 } |
1167 } | 1175 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1203 /// Ensure `dartx.` symbols we will use are present. | 1211 /// Ensure `dartx.` symbols we will use are present. |
1204 void _initExtensionSymbols( | 1212 void _initExtensionSymbols( |
1205 ClassElement classElem, | 1213 ClassElement classElem, |
1206 List<MethodDeclaration> methods, | 1214 List<MethodDeclaration> methods, |
1207 List<FieldDeclaration> fields, | 1215 List<FieldDeclaration> fields, |
1208 List<JS.Statement> body) { | 1216 List<JS.Statement> body) { |
1209 if (_extensionTypes.hasNativeSubtype(classElem.type)) { | 1217 if (_extensionTypes.hasNativeSubtype(classElem.type)) { |
1210 var dartxNames = <JS.Expression>[]; | 1218 var dartxNames = <JS.Expression>[]; |
1211 for (var m in methods) { | 1219 for (var m in methods) { |
1212 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { | 1220 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { |
1213 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); | 1221 dartxNames.add(_elementMemberName(m.element, useExtension: false)); |
1214 } | 1222 } |
1215 } | 1223 } |
1216 for (var f in fields) { | 1224 for (var f in fields) { |
1217 if (!f.isStatic) { | 1225 if (!f.isStatic) { |
1218 for (var d in f.fields.variables) { | 1226 for (var d in f.fields.variables) { |
1219 if (d.element.isPublic) { | 1227 if (d.element.isPublic) { |
1220 dartxNames.add( | 1228 dartxNames.add( |
1221 _elementMemberName(d.element.getter, allowExtensions: false)); | 1229 _elementMemberName(d.element.getter, useExtension: false)); |
1222 } | 1230 } |
1223 } | 1231 } |
1224 } | 1232 } |
1225 } | 1233 } |
1226 if (dartxNames.isNotEmpty) { | 1234 if (dartxNames.isNotEmpty) { |
1227 body.add(js.statement('dart.defineExtensionNames(#)', | 1235 body.add(js.statement('dart.defineExtensionNames(#)', |
1228 [new JS.ArrayInitializer(dartxNames, multiline: true)])); | 1236 [new JS.ArrayInitializer(dartxNames, multiline: true)])); |
1229 } | 1237 } |
1230 } | 1238 } |
1231 } | 1239 } |
1232 | 1240 |
1233 List<ExecutableElement> _extensionsToImplement(ClassElement element) { | 1241 List<ExecutableElement> _extensionsToImplement(ClassElement element) { |
1234 var members = <ExecutableElement>[]; | 1242 var members = <ExecutableElement>[]; |
1235 if (_extensionTypes.isNativeClass(element)) return members; | 1243 if (_extensionTypes.isNativeClass(element)) return members; |
1236 | 1244 |
1237 // Collect all extension types we implement. | 1245 // Collect all extension types we implement. |
1238 var type = element.type; | 1246 var type = element.type; |
1239 var types = _extensionTypes.collectNativeInterfaces(element); | 1247 var types = _extensionTypes.collectNativeInterfaces(element); |
1240 if (types.isEmpty) return members; | 1248 if (types.isEmpty) return members; |
1241 | 1249 |
1242 // Collect all possible extension method names. | 1250 // Collect all possible extension method names. |
1243 var extensionMembers = new HashSet<String>(); | 1251 var extensionMembers = new HashSet<String>(); |
1244 for (var t in types) { | 1252 for (var t in types) { |
1245 for (var m in [t.methods, t.accessors].expand((e) => e)) { | 1253 for (var m in [t.methods, t.accessors].expand((e) => e)) { |
1246 if (!m.isStatic) extensionMembers.add(m.name); | 1254 if (!m.isStatic && m.isPublic) extensionMembers.add(m.name); |
1247 } | 1255 } |
1248 } | 1256 } |
1249 | 1257 |
1250 // Collect all of extension methods this type implements. | 1258 // Collect all of extension methods this type implements. |
1251 for (var m in [type.methods, type.accessors].expand((e) => e)) { | 1259 for (var m in [type.methods, type.accessors].expand((e) => e)) { |
1252 if (!m.isStatic && !m.isAbstract && extensionMembers.contains(m.name)) { | 1260 if (!m.isStatic && !m.isAbstract && extensionMembers.contains(m.name)) { |
1253 members.add(m); | 1261 members.add(m); |
1254 } | 1262 } |
1255 } | 1263 } |
1256 return members; | 1264 return members; |
(...skipping 413 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1670 // call sites, but it's cleaner to instead transform the operator method
. | 1678 // call sites, but it's cleaner to instead transform the operator method
. |
1671 fn = _alwaysReturnLastParameter(fn); | 1679 fn = _alwaysReturnLastParameter(fn); |
1672 } | 1680 } |
1673 | 1681 |
1674 fn = _makeGenericFunction(fn); | 1682 fn = _makeGenericFunction(fn); |
1675 } | 1683 } |
1676 | 1684 |
1677 return annotate( | 1685 return annotate( |
1678 new JS.Method( | 1686 new JS.Method( |
1679 _elementMemberName(node.element, | 1687 _elementMemberName(node.element, |
1680 allowExtensions: _extensionTypes.isNativeClass(type.element)), | 1688 useExtension: _extensionTypes.isNativeClass(type.element)), |
1681 fn, | 1689 fn, |
1682 isGetter: node.isGetter, | 1690 isGetter: node.isGetter, |
1683 isSetter: node.isSetter, | 1691 isSetter: node.isSetter, |
1684 isStatic: node.isStatic), | 1692 isStatic: node.isStatic), |
1685 node, | 1693 node, |
1686 node.element); | 1694 node.element); |
1687 } | 1695 } |
1688 | 1696 |
1689 /// Transform the function so the last parameter is always returned. | 1697 /// Transform the function so the last parameter is always returned. |
1690 /// | 1698 /// |
(...skipping 794 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2485 JS.Expression jsTarget = _visit(target); | 2493 JS.Expression jsTarget = _visit(target); |
2486 if (DynamicInvoke.get(target)) { | 2494 if (DynamicInvoke.get(target)) { |
2487 if (typeArgs != null) { | 2495 if (typeArgs != null) { |
2488 return js.call('dart.dgsend(#, #, #, #)', | 2496 return js.call('dart.dgsend(#, #, #, #)', |
2489 [jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]); | 2497 [jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]); |
2490 } else { | 2498 } else { |
2491 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); | 2499 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); |
2492 } | 2500 } |
2493 } | 2501 } |
2494 if (_isObjectMemberCall(target, name)) { | 2502 if (_isObjectMemberCall(target, name)) { |
2495 // Object methods require a helper for null checks & native types. | |
2496 assert(typeArgs == null); // Object methods don't take type args. | 2503 assert(typeArgs == null); // Object methods don't take type args. |
2497 return js.call('dart.#(#, #)', [memberName, jsTarget, args]); | 2504 return js.call('dart.#(#, #)', [name, jsTarget, args]); |
2498 } | 2505 } |
2499 | 2506 |
2500 jsTarget = new JS.PropertyAccess(jsTarget, memberName); | 2507 jsTarget = new JS.PropertyAccess(jsTarget, memberName); |
2501 | 2508 |
2502 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); | 2509 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
2503 | 2510 |
2504 if (DynamicInvoke.get(node.methodName)) { | 2511 if (DynamicInvoke.get(node.methodName)) { |
2505 // This is a dynamic call to a statically known target. For example: | 2512 // This is a dynamic call to a statically known target. For example: |
2506 // class Foo { Function bar; } | 2513 // class Foo { Function bar; } |
2507 // new Foo().bar(); // dynamic call | 2514 // new Foo().bar(); // dynamic call |
(...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2838 // Static and instance fields are handled elsewhere. | 2845 // Static and instance fields are handled elsewhere. |
2839 assert(node.element is TopLevelVariableElement); | 2846 assert(node.element is TopLevelVariableElement); |
2840 return _emitTopLevelField(node); | 2847 return _emitTopLevelField(node); |
2841 } | 2848 } |
2842 | 2849 |
2843 var name = | 2850 var name = |
2844 new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type)); | 2851 new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type)); |
2845 return new JS.VariableInitialization(name, _visitInitializer(node)); | 2852 return new JS.VariableInitialization(name, _visitInitializer(node)); |
2846 } | 2853 } |
2847 | 2854 |
2848 bool _isFinalJSDecl(AstNode field) => | |
2849 field is VariableDeclaration && | |
2850 field.isFinal && | |
2851 _isJSInvocation(field.initializer); | |
2852 | |
2853 /// Try to emit a constant static field. | 2855 /// Try to emit a constant static field. |
2854 /// | 2856 /// |
2855 /// If the field's initializer does not cause side effects, and if all of | 2857 /// If the field's initializer does not cause side effects, and if all of |
2856 /// dependencies are safe to refer to while we are initializing the class, | 2858 /// dependencies are safe to refer to while we are initializing the class, |
2857 /// then we can initialize it eagerly: | 2859 /// then we can initialize it eagerly: |
2858 /// | 2860 /// |
2859 /// // Baz must be const constructor, and the name "Baz" must be defined | 2861 /// // Baz must be const constructor, and the name "Baz" must be defined |
2860 /// // by this point. | 2862 /// // by this point. |
2861 /// Foo.bar = dart.const(new Baz(42)); | 2863 /// Foo.bar = dart.const(new Baz(42)); |
2862 /// | 2864 /// |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2908 jsInit = _visitInitializer(field); | 2910 jsInit = _visitInitializer(field); |
2909 _loader.finishTopLevel(element); | 2911 _loader.finishTopLevel(element); |
2910 eagerInit = _loader.isLoaded(element); | 2912 eagerInit = _loader.isLoaded(element); |
2911 } else { | 2913 } else { |
2912 // TODO(jmesserly): we're visiting the initializer here, and again | 2914 // TODO(jmesserly): we're visiting the initializer here, and again |
2913 // later on when we emit lazy fields. That seems busted. | 2915 // later on when we emit lazy fields. That seems busted. |
2914 jsInit = _visitInitializer(field); | 2916 jsInit = _visitInitializer(field); |
2915 eagerInit = false; | 2917 eagerInit = false; |
2916 } | 2918 } |
2917 | 2919 |
2918 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 2920 // Treat dart:runtime stuff as safe to eagerly evaluate. |
2919 // runtime helpers. | 2921 // TODO(jmesserly): it'd be nice to avoid this special case. |
2920 // TODO(jmesserly): we don't track dependencies correctly for these. | 2922 var isJSTopLevel = field.isFinal && _isDartRuntime(element.library); |
2921 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | |
2922 if (eagerInit || isJSTopLevel) { | 2923 if (eagerInit || isJSTopLevel) { |
2923 // Remember that we emitted it this way, so re-export can take advantage | 2924 // Remember that we emitted it this way, so re-export can take advantage |
2924 // of this fact. | 2925 // of this fact. |
2925 _eagerTopLevelFields.add(element); | 2926 _eagerTopLevelFields.add(element); |
2926 | 2927 |
2927 return annotate( | 2928 return annotate( |
2928 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), | 2929 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), |
2929 field, | 2930 field, |
2930 element); | 2931 element); |
2931 } | 2932 } |
(...skipping 672 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3604 } | 3605 } |
3605 } | 3606 } |
3606 | 3607 |
3607 /// Everything in Dart is an Object and supports the 4 members on Object, | 3608 /// Everything in Dart is an Object and supports the 4 members on Object, |
3608 /// so we have to use a runtime helper to handle values such as `null` and | 3609 /// so we have to use a runtime helper to handle values such as `null` and |
3609 /// native types. | 3610 /// native types. |
3610 /// | 3611 /// |
3611 /// For example `null.toString()` is legal in Dart, so we need to generate | 3612 /// For example `null.toString()` is legal in Dart, so we need to generate |
3612 /// that as `dart.toString(obj)`. | 3613 /// that as `dart.toString(obj)`. |
3613 bool _isObjectMemberCall(Expression target, String memberName) { | 3614 bool _isObjectMemberCall(Expression target, String memberName) { |
3614 // Is this a member on `Object`? | 3615 if (!isObjectMember(memberName)) { |
3615 if (!isObjectProperty(memberName)) { | |
3616 return false; | 3616 return false; |
3617 } | 3617 } |
3618 | 3618 |
3619 // Check if the target could be `null`, is dynamic, or may be an extension | 3619 // Check if the target could be `null`, is dynamic, or may be an extension |
3620 // native type. In all of those cases we need defensive code generation. | 3620 // native type. In all of those cases we need defensive code generation. |
3621 var type = getStaticType(target); | 3621 var type = getStaticType(target); |
3622 return isNullable(target) || | 3622 return isNullable(target) || |
3623 type.isDynamic || | 3623 type.isDynamic || |
3624 (_extensionTypes.hasNativeSubtype(type) && target is! SuperExpression); | 3624 (_extensionTypes.hasNativeSubtype(type) && target is! SuperExpression); |
3625 } | 3625 } |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3683 // subclasses cannot override x. | 3683 // subclasses cannot override x. |
3684 jsTarget = new JS.This(); | 3684 jsTarget = new JS.This(); |
3685 } | 3685 } |
3686 | 3686 |
3687 JS.Expression result; | 3687 JS.Expression result; |
3688 if (member != null && member is MethodElement && !isStatic) { | 3688 if (member != null && member is MethodElement && !isStatic) { |
3689 // Tear-off methods: explicitly bind it. | 3689 // Tear-off methods: explicitly bind it. |
3690 if (isSuper) { | 3690 if (isSuper) { |
3691 result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]); | 3691 result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]); |
3692 } else if (_isObjectMemberCall(target, memberName)) { | 3692 } else if (_isObjectMemberCall(target, memberName)) { |
3693 result = js.call('dart.bind(#, #, dart.#)', [jsTarget, name, name]); | 3693 result = js.call('dart.bind(#, #, dart.#)', |
| 3694 [jsTarget, _propertyName(memberName), memberName]); |
3694 } else { | 3695 } else { |
3695 result = js.call('dart.bind(#, #)', [jsTarget, name]); | 3696 result = js.call('dart.bind(#, #)', [jsTarget, name]); |
3696 } | 3697 } |
3697 } else if (_isObjectMemberCall(target, memberName)) { | 3698 } else if (_isObjectMemberCall(target, memberName)) { |
3698 result = js.call('dart.#(#)', [name, jsTarget]); | 3699 result = js.call('dart.#(#)', [memberName, jsTarget]); |
3699 } else { | 3700 } else { |
3700 result = js.call('#.#', [jsTarget, name]); | 3701 result = js.call('#.#', [jsTarget, name]); |
3701 } | 3702 } |
3702 if (typeArgs == null) { | 3703 if (typeArgs == null) { |
3703 return result; | 3704 return result; |
3704 } | 3705 } |
3705 return js.call('dart.gbind(#, #)', [result, typeArgs]); | 3706 return js.call('dart.gbind(#, #)', [result, typeArgs]); |
3706 } | 3707 } |
3707 | 3708 |
3708 /// Emits a generic send, like an operator method. | 3709 /// Emits a generic send, like an operator method. |
(...skipping 445 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4154 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 4155 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
4155 if (nodes == null || nodes.isEmpty) return null; | 4156 if (nodes == null || nodes.isEmpty) return null; |
4156 return new JS.Expression.binary( | 4157 return new JS.Expression.binary( |
4157 _visitList(nodes) as List<JS.Expression>, operator); | 4158 _visitList(nodes) as List<JS.Expression>, operator); |
4158 } | 4159 } |
4159 | 4160 |
4160 /// Like [_emitMemberName], but for declaration sites. | 4161 /// Like [_emitMemberName], but for declaration sites. |
4161 /// | 4162 /// |
4162 /// Unlike call sites, we always have an element available, so we can use it | 4163 /// Unlike call sites, we always have an element available, so we can use it |
4163 /// directly rather than computing the relevant options for [_emitMemberName]. | 4164 /// directly rather than computing the relevant options for [_emitMemberName]. |
4164 JS.Expression _elementMemberName(ExecutableElement e, | 4165 JS.Expression _elementMemberName(ExecutableElement e, {bool useExtension}) { |
4165 {bool allowExtensions: true}) { | |
4166 String name; | 4166 String name; |
4167 if (e is PropertyAccessorElement) { | 4167 if (e is PropertyAccessorElement) { |
4168 name = e.variable.name; | 4168 name = e.variable.name; |
4169 } else { | 4169 } else { |
4170 name = e.name; | 4170 name = e.name; |
4171 } | 4171 } |
4172 return _emitMemberName(name, | 4172 return _emitMemberName(name, |
4173 type: (e.enclosingElement as ClassElement).type, | 4173 type: (e.enclosingElement as ClassElement).type, |
4174 unary: e.parameters.isEmpty, | 4174 unary: e.parameters.isEmpty, |
4175 isStatic: e.isStatic, | 4175 isStatic: e.isStatic, |
4176 allowExtensions: allowExtensions); | 4176 useExtension: useExtension); |
4177 } | 4177 } |
4178 | 4178 |
4179 /// This handles member renaming for private names and operators. | 4179 /// This handles member renaming for private names and operators. |
4180 /// | 4180 /// |
4181 /// Private names are generated using ES6 symbols: | 4181 /// Private names are generated using ES6 symbols: |
4182 /// | 4182 /// |
4183 /// // At the top of the module: | 4183 /// // At the top of the module: |
4184 /// let _x = Symbol('_x'); | 4184 /// let _x = Symbol('_x'); |
4185 /// let _y = Symbol('_y'); | 4185 /// let _y = Symbol('_y'); |
4186 /// ... | 4186 /// ... |
(...skipping 27 matching lines...) Expand all Loading... |
4214 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 4214 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
4215 /// for this transformation to happen, otherwise binary minus is assumed. | 4215 /// for this transformation to happen, otherwise binary minus is assumed. |
4216 /// | 4216 /// |
4217 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 4217 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
4218 /// helper, that checks for null. The user defined method is called '=='. | 4218 /// helper, that checks for null. The user defined method is called '=='. |
4219 /// | 4219 /// |
4220 JS.Expression _emitMemberName(String name, | 4220 JS.Expression _emitMemberName(String name, |
4221 {DartType type, | 4221 {DartType type, |
4222 bool unary: false, | 4222 bool unary: false, |
4223 bool isStatic: false, | 4223 bool isStatic: false, |
4224 bool allowExtensions: true}) { | 4224 bool useExtension}) { |
4225 // Static members skip the rename steps. | 4225 // Static members skip the rename steps. |
4226 if (isStatic) return _propertyName(name); | 4226 if (isStatic) return _propertyName(name); |
4227 | 4227 |
4228 if (name.startsWith('_')) { | 4228 if (name.startsWith('_')) { |
4229 return _emitPrivateNameSymbol(currentLibrary, name); | 4229 return _emitPrivateNameSymbol(currentLibrary, name); |
4230 } | 4230 } |
4231 | 4231 |
4232 if (name == '[]') { | 4232 if (name == '[]') { |
4233 name = 'get'; | 4233 name = 'get'; |
4234 } else if (name == '[]=') { | 4234 } else if (name == '[]=') { |
4235 name = 'set'; | 4235 name = 'set'; |
4236 } else if (name == '-' && unary) { | 4236 } else if (name == '-' && unary) { |
4237 name = 'unary-'; | 4237 name = 'unary-'; |
4238 } else if (name == 'constructor' || name == 'prototype') { | 4238 } else if (name == 'constructor' || name == 'prototype') { |
4239 // This uses an illegal (in Dart) character for a member, avoiding the | 4239 // This uses an illegal (in Dart) character for a member, avoiding the |
4240 // conflict. We could use practically any character for this. | 4240 // conflict. We could use practically any character for this. |
4241 name = '+$name'; | 4241 name = '+$name'; |
4242 } | 4242 } |
4243 | 4243 |
4244 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 4244 var result = _propertyName(name); |
4245 var baseType = type; | 4245 |
4246 while (baseType is TypeParameterType) { | 4246 if (useExtension == null) { |
4247 baseType = baseType.element.bound; | 4247 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
4248 } | 4248 var baseType = type; |
4249 if (allowExtensions && | 4249 while (baseType is TypeParameterType) { |
4250 baseType != null && | 4250 baseType = baseType.element.bound; |
4251 _extensionTypes.hasNativeSubtype(baseType) && | 4251 } |
4252 !isObjectProperty(name)) { | 4252 useExtension = baseType != null && |
4253 return js.call('dartx.#', _propertyName(name)); | 4253 _extensionTypes.hasNativeSubtype(baseType) && |
| 4254 !isObjectMember(name); |
4254 } | 4255 } |
4255 | 4256 |
4256 return _propertyName(name); | 4257 return useExtension ? js.call('dartx.#', result) : result; |
4257 } | 4258 } |
4258 | 4259 |
4259 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { | 4260 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { |
4260 return _privateNames | 4261 return _privateNames |
4261 .putIfAbsent(library, () => new HashMap()) | 4262 .putIfAbsent(library, () => new HashMap()) |
4262 .putIfAbsent(name, () { | 4263 .putIfAbsent(name, () { |
4263 var id = new JS.TemporaryId(name); | 4264 var id = new JS.TemporaryId(name); |
4264 _moduleItems.add( | 4265 _moduleItems.add( |
4265 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); | 4266 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); |
4266 return id; | 4267 return id; |
(...skipping 25 matching lines...) Expand all Loading... |
4292 } | 4293 } |
4293 | 4294 |
4294 /// Returns true if this is any kind of object represented by `Number` in JS. | 4295 /// Returns true if this is any kind of object represented by `Number` in JS. |
4295 /// | 4296 /// |
4296 /// In practice, this is 4 types: num, int, double, and JSNumber. | 4297 /// In practice, this is 4 types: num, int, double, and JSNumber. |
4297 /// | 4298 /// |
4298 /// JSNumber is the type that actually "implements" all numbers, hence it's | 4299 /// JSNumber is the type that actually "implements" all numbers, hence it's |
4299 /// a subtype of int and double (and num). It's in our "dart:_interceptors". | 4300 /// a subtype of int and double (and num). It's in our "dart:_interceptors". |
4300 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); | 4301 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); |
4301 | 4302 |
4302 bool _isObjectGetter(String name) { | 4303 /// Return true if this is one of the methods/properties on all Dart Objects |
4303 PropertyAccessorElement element = types.objectType.element.getGetter(name); | 4304 /// (toString, hashCode, noSuchMethod, runtimeType). |
4304 return (element != null && !element.isStatic); | 4305 /// |
4305 } | 4306 /// Operator == is excluded, as it is handled as part of the equality binary |
4306 | 4307 /// operator. |
4307 bool _isObjectMethod(String name) { | 4308 bool isObjectMember(String name) { |
4308 MethodElement element = types.objectType.element.getMethod(name); | 4309 // We could look these up on Object, but we have hard coded runtime helpers |
4309 return (element != null && !element.isStatic); | 4310 // so it's not really providing any benefit. |
4310 } | 4311 switch (name) { |
4311 | 4312 case 'hashCode': |
4312 bool isObjectProperty(String name) { | 4313 case 'toString': |
4313 return _isObjectGetter(name) || _isObjectMethod(name); | 4314 case 'noSuchMethod': |
| 4315 case 'runtimeType': |
| 4316 return true; |
| 4317 } |
| 4318 return false; |
4314 } | 4319 } |
4315 | 4320 |
4316 // TODO(leafp): Various analyzer pieces computed similar things. | 4321 // TODO(leafp): Various analyzer pieces computed similar things. |
4317 // Share this logic somewhere? | 4322 // Share this logic somewhere? |
4318 DartType _getExpectedReturnType(ExecutableElement element) { | 4323 DartType _getExpectedReturnType(ExecutableElement element) { |
4319 FunctionType functionType = element.type; | 4324 FunctionType functionType = element.type; |
4320 if (functionType == null) { | 4325 if (functionType == null) { |
4321 return DynamicTypeImpl.instance; | 4326 return DynamicTypeImpl.instance; |
4322 } | 4327 } |
4323 var type = functionType.returnType; | 4328 var type = functionType.returnType; |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4403 } | 4408 } |
4404 | 4409 |
4405 bool isLibraryPrefix(Expression node) => | 4410 bool isLibraryPrefix(Expression node) => |
4406 node is SimpleIdentifier && node.staticElement is PrefixElement; | 4411 node is SimpleIdentifier && node.staticElement is PrefixElement; |
4407 | 4412 |
4408 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 4413 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
4409 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 4414 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
4410 | 4415 |
4411 bool _isDartRuntime(LibraryElement l) => | 4416 bool _isDartRuntime(LibraryElement l) => |
4412 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 4417 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
OLD | NEW |