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(CompilationUnit unit, Map<Element, AstNode> map)
{ |
323 var map = <Element, AstNode>{}; | |
324 for (var declaration in unit.declarations) { | 332 for (var declaration in unit.declarations) { |
325 if (declaration is TopLevelVariableDeclaration) { | 333 if (declaration is TopLevelVariableDeclaration) { |
326 for (var field in declaration.variables.variables) { | 334 for (var field in declaration.variables.variables) { |
327 map[field.element] = field; | 335 map[field.element] = field; |
328 } | 336 } |
329 } else { | 337 } else { |
330 map[declaration.element] = declaration; | 338 map[declaration.element] = declaration; |
331 } | 339 } |
332 } | 340 } |
333 return map; | |
334 } | 341 } |
335 | 342 |
336 void _emitModuleItem(AstNode node) { | 343 void _emitModuleItem(AstNode node) { |
337 // TODO(jmesserly): ideally we could do this at a smaller granularity. | 344 // 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 | 345 // We'll need to be consistent about when we're generating functions, and |
339 // only run this on the outermost function. | 346 // only run this on the outermost function. |
340 inferNullableTypes(node); | 347 inferNullableTypes(node); |
341 | 348 |
342 var code = _visit(node); | 349 var code = _visit(node); |
343 if (code != null) _moduleItems.add(code); | 350 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, | 961 JS.Method _emitSuperAccessorWrapper(MethodDeclaration method, |
955 InterfaceType type, List<ClassElement> superclasses) { | 962 InterfaceType type, List<ClassElement> superclasses) { |
956 var methodElement = method.element as PropertyAccessorElement; | 963 var methodElement = method.element as PropertyAccessorElement; |
957 var field = methodElement.variable; | 964 var field = methodElement.variable; |
958 if (!field.isSynthetic) return null; | 965 if (!field.isSynthetic) return null; |
959 var propertyOverrideResult = checkForPropertyOverride( | 966 var propertyOverrideResult = checkForPropertyOverride( |
960 methodElement.variable, superclasses, _extensionTypes); | 967 methodElement.variable, superclasses, _extensionTypes); |
961 | 968 |
962 // Generate a corresponding virtual getter / setter. | 969 // Generate a corresponding virtual getter / setter. |
963 var name = _elementMemberName(methodElement, | 970 var name = _elementMemberName(methodElement, |
964 allowExtensions: _extensionTypes.isNativeClass(type.element)); | 971 useExtension: _extensionTypes.isNativeClass(type.element)); |
965 if (method.isGetter) { | 972 if (method.isGetter) { |
966 // Generate a setter | 973 // Generate a setter |
967 if (field.setter != null || !propertyOverrideResult.foundSetter) | 974 if (field.setter != null || !propertyOverrideResult.foundSetter) |
968 return null; | 975 return null; |
969 var fn = js.call('function(value) { super[#] = value; }', [name]); | 976 var fn = js.call('function(value) { super[#] = value; }', [name]); |
970 return new JS.Method(name, fn, isSetter: true); | 977 return new JS.Method(name, fn, isSetter: true); |
971 } else { | 978 } else { |
972 // Generate a getter | 979 // Generate a getter |
973 if (field.getter != null || !propertyOverrideResult.foundGetter) | 980 if (field.getter != null || !propertyOverrideResult.foundGetter) |
974 return null; | 981 return null; |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1108 | 1115 |
1109 /// If a concrete class implements one of our extensions, we might need to | 1116 /// If a concrete class implements one of our extensions, we might need to |
1110 /// add forwarders. | 1117 /// add forwarders. |
1111 void _defineExtensionMembers(List<ExecutableElement> extensions, | 1118 void _defineExtensionMembers(List<ExecutableElement> extensions, |
1112 JS.Expression className, List<JS.Statement> body) { | 1119 JS.Expression className, List<JS.Statement> body) { |
1113 // If a concrete class implements one of our extensions, we might need to | 1120 // If a concrete class implements one of our extensions, we might need to |
1114 // add forwarders. | 1121 // add forwarders. |
1115 if (extensions.isNotEmpty) { | 1122 if (extensions.isNotEmpty) { |
1116 var methodNames = <JS.Expression>[]; | 1123 var methodNames = <JS.Expression>[]; |
1117 for (var e in extensions) { | 1124 for (var e in extensions) { |
1118 methodNames.add(_elementMemberName(e, allowExtensions: false)); | 1125 methodNames.add(_elementMemberName(e, useExtension: false)); |
1119 } | 1126 } |
1120 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ | 1127 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ |
1121 className, | 1128 className, |
1122 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) | 1129 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) |
1123 ])); | 1130 ])); |
1124 } | 1131 } |
1125 } | 1132 } |
1126 | 1133 |
1127 /// Emit the signature on the class recording the runtime type information | 1134 /// Emit the signature on the class recording the runtime type information |
1128 void _emitClassSignature( | 1135 void _emitClassSignature( |
(...skipping 17 matching lines...) Expand all Loading... |
1146 for (MethodDeclaration node in methods) { | 1153 for (MethodDeclaration node in methods) { |
1147 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 1154 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
1148 var name = node.name.name; | 1155 var name = node.name.name; |
1149 var element = node.element; | 1156 var element = node.element; |
1150 var inheritedElement = | 1157 var inheritedElement = |
1151 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 1158 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
1152 if (inheritedElement != null && inheritedElement.type == element.type) { | 1159 if (inheritedElement != null && inheritedElement.type == element.type) { |
1153 continue; | 1160 continue; |
1154 } | 1161 } |
1155 var memberName = _elementMemberName(element, | 1162 var memberName = _elementMemberName(element, |
1156 allowExtensions: _extensionTypes.isNativeClass(classElem)); | 1163 useExtension: _extensionTypes.isNativeClass(classElem)); |
1157 var parts = _emitFunctionTypeParts(element.type); | 1164 var parts = _emitFunctionTypeParts(element.type); |
1158 var property = | 1165 var property = |
1159 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 1166 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
1160 if (node.isStatic) { | 1167 if (node.isStatic) { |
1161 tStatics.add(property); | 1168 tStatics.add(property); |
1162 sNames.add(memberName); | 1169 sNames.add(memberName); |
1163 } else { | 1170 } else { |
1164 tMethods.add(property); | 1171 tMethods.add(property); |
1165 } | 1172 } |
1166 } | 1173 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1202 /// Ensure `dartx.` symbols we will use are present. | 1209 /// Ensure `dartx.` symbols we will use are present. |
1203 void _initExtensionSymbols( | 1210 void _initExtensionSymbols( |
1204 ClassElement classElem, | 1211 ClassElement classElem, |
1205 List<MethodDeclaration> methods, | 1212 List<MethodDeclaration> methods, |
1206 List<FieldDeclaration> fields, | 1213 List<FieldDeclaration> fields, |
1207 List<JS.Statement> body) { | 1214 List<JS.Statement> body) { |
1208 if (_extensionTypes.hasNativeSubtype(classElem.type)) { | 1215 if (_extensionTypes.hasNativeSubtype(classElem.type)) { |
1209 var dartxNames = <JS.Expression>[]; | 1216 var dartxNames = <JS.Expression>[]; |
1210 for (var m in methods) { | 1217 for (var m in methods) { |
1211 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { | 1218 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { |
1212 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); | 1219 dartxNames.add(_elementMemberName(m.element, useExtension: false)); |
1213 } | 1220 } |
1214 } | 1221 } |
1215 for (var f in fields) { | 1222 for (var f in fields) { |
1216 if (!f.isStatic) { | 1223 if (!f.isStatic) { |
1217 for (var d in f.fields.variables) { | 1224 for (var d in f.fields.variables) { |
1218 if (d.element.isPublic) { | 1225 if (d.element.isPublic) { |
1219 dartxNames.add( | 1226 dartxNames.add( |
1220 _elementMemberName(d.element.getter, allowExtensions: false)); | 1227 _elementMemberName(d.element.getter, useExtension: false)); |
1221 } | 1228 } |
1222 } | 1229 } |
1223 } | 1230 } |
1224 } | 1231 } |
1225 if (dartxNames.isNotEmpty) { | 1232 if (dartxNames.isNotEmpty) { |
1226 body.add(js.statement('dart.defineExtensionNames(#)', | 1233 body.add(js.statement('dart.defineExtensionNames(#)', |
1227 [new JS.ArrayInitializer(dartxNames, multiline: true)])); | 1234 [new JS.ArrayInitializer(dartxNames, multiline: true)])); |
1228 } | 1235 } |
1229 } | 1236 } |
1230 } | 1237 } |
1231 | 1238 |
1232 List<ExecutableElement> _extensionsToImplement(ClassElement element) { | 1239 List<ExecutableElement> _extensionsToImplement(ClassElement element) { |
1233 var members = <ExecutableElement>[]; | 1240 var members = <ExecutableElement>[]; |
1234 if (_extensionTypes.isNativeClass(element)) return members; | 1241 if (_extensionTypes.isNativeClass(element)) return members; |
1235 | 1242 |
1236 // Collect all extension types we implement. | 1243 // Collect all extension types we implement. |
1237 var type = element.type; | 1244 var type = element.type; |
1238 var types = _extensionTypes.collectNativeInterfaces(element); | 1245 var types = _extensionTypes.collectNativeInterfaces(element); |
1239 if (types.isEmpty) return members; | 1246 if (types.isEmpty) return members; |
1240 | 1247 |
1241 // Collect all possible extension method names. | 1248 // Collect all possible extension method names. |
1242 var extensionMembers = new HashSet<String>(); | 1249 var extensionMembers = new HashSet<String>(); |
1243 for (var t in types) { | 1250 for (var t in types) { |
1244 for (var m in [t.methods, t.accessors].expand((e) => e)) { | 1251 for (var m in [t.methods, t.accessors].expand((e) => e)) { |
1245 if (!m.isStatic) extensionMembers.add(m.name); | 1252 if (!m.isStatic && m.isPublic) extensionMembers.add(m.name); |
1246 } | 1253 } |
1247 } | 1254 } |
1248 | 1255 |
1249 // Collect all of extension methods this type implements. | 1256 // Collect all of extension methods this type implements. |
1250 for (var m in [type.methods, type.accessors].expand((e) => e)) { | 1257 for (var m in [type.methods, type.accessors].expand((e) => e)) { |
1251 if (!m.isStatic && !m.isAbstract && extensionMembers.contains(m.name)) { | 1258 if (!m.isStatic && !m.isAbstract && extensionMembers.contains(m.name)) { |
1252 members.add(m); | 1259 members.add(m); |
1253 } | 1260 } |
1254 } | 1261 } |
1255 return members; | 1262 return members; |
(...skipping 413 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1669 // call sites, but it's cleaner to instead transform the operator method
. | 1676 // call sites, but it's cleaner to instead transform the operator method
. |
1670 fn = _alwaysReturnLastParameter(fn); | 1677 fn = _alwaysReturnLastParameter(fn); |
1671 } | 1678 } |
1672 | 1679 |
1673 fn = _makeGenericFunction(fn); | 1680 fn = _makeGenericFunction(fn); |
1674 } | 1681 } |
1675 | 1682 |
1676 return annotate( | 1683 return annotate( |
1677 new JS.Method( | 1684 new JS.Method( |
1678 _elementMemberName(node.element, | 1685 _elementMemberName(node.element, |
1679 allowExtensions: _extensionTypes.isNativeClass(type.element)), | 1686 useExtension: _extensionTypes.isNativeClass(type.element)), |
1680 fn, | 1687 fn, |
1681 isGetter: node.isGetter, | 1688 isGetter: node.isGetter, |
1682 isSetter: node.isSetter, | 1689 isSetter: node.isSetter, |
1683 isStatic: node.isStatic), | 1690 isStatic: node.isStatic), |
1684 node, | 1691 node, |
1685 node.element); | 1692 node.element); |
1686 } | 1693 } |
1687 | 1694 |
1688 /// Transform the function so the last parameter is always returned. | 1695 /// Transform the function so the last parameter is always returned. |
1689 /// | 1696 /// |
(...skipping 782 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2472 JS.Expression jsTarget = _visit(target); | 2479 JS.Expression jsTarget = _visit(target); |
2473 if (DynamicInvoke.get(target)) { | 2480 if (DynamicInvoke.get(target)) { |
2474 if (typeArgs != null) { | 2481 if (typeArgs != null) { |
2475 return js.call('dart.dgsend(#, #, #, #)', | 2482 return js.call('dart.dgsend(#, #, #, #)', |
2476 [jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]); | 2483 [jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]); |
2477 } else { | 2484 } else { |
2478 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); | 2485 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); |
2479 } | 2486 } |
2480 } | 2487 } |
2481 if (_isObjectMemberCall(target, name)) { | 2488 if (_isObjectMemberCall(target, name)) { |
2482 // Object methods require a helper for null checks & native types. | |
2483 assert(typeArgs == null); // Object methods don't take type args. | 2489 assert(typeArgs == null); // Object methods don't take type args. |
2484 return js.call('dart.#(#, #)', [memberName, jsTarget, args]); | 2490 return js.call('dart.#(#, #)', [name, jsTarget, args]); |
2485 } | 2491 } |
2486 | 2492 |
2487 jsTarget = new JS.PropertyAccess(jsTarget, memberName); | 2493 jsTarget = new JS.PropertyAccess(jsTarget, memberName); |
2488 | 2494 |
2489 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); | 2495 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
2490 | 2496 |
2491 if (DynamicInvoke.get(node.methodName)) { | 2497 if (DynamicInvoke.get(node.methodName)) { |
2492 // This is a dynamic call to a statically known target. For example: | 2498 // This is a dynamic call to a statically known target. For example: |
2493 // class Foo { Function bar; } | 2499 // class Foo { Function bar; } |
2494 // new Foo().bar(); // dynamic call | 2500 // new Foo().bar(); // dynamic call |
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2827 // Static and instance fields are handled elsewhere. | 2833 // Static and instance fields are handled elsewhere. |
2828 assert(node.element is TopLevelVariableElement); | 2834 assert(node.element is TopLevelVariableElement); |
2829 return _emitTopLevelField(node); | 2835 return _emitTopLevelField(node); |
2830 } | 2836 } |
2831 | 2837 |
2832 var name = | 2838 var name = |
2833 new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type)); | 2839 new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type)); |
2834 return new JS.VariableInitialization(name, _visitInitializer(node)); | 2840 return new JS.VariableInitialization(name, _visitInitializer(node)); |
2835 } | 2841 } |
2836 | 2842 |
2837 bool _isFinalJSDecl(AstNode field) => | |
2838 field is VariableDeclaration && | |
2839 field.isFinal && | |
2840 _isJSInvocation(field.initializer); | |
2841 | |
2842 /// Try to emit a constant static field. | 2843 /// Try to emit a constant static field. |
2843 /// | 2844 /// |
2844 /// If the field's initializer does not cause side effects, and if all of | 2845 /// If the field's initializer does not cause side effects, and if all of |
2845 /// dependencies are safe to refer to while we are initializing the class, | 2846 /// dependencies are safe to refer to while we are initializing the class, |
2846 /// then we can initialize it eagerly: | 2847 /// then we can initialize it eagerly: |
2847 /// | 2848 /// |
2848 /// // Baz must be const constructor, and the name "Baz" must be defined | 2849 /// // Baz must be const constructor, and the name "Baz" must be defined |
2849 /// // by this point. | 2850 /// // by this point. |
2850 /// Foo.bar = dart.const(new Baz(42)); | 2851 /// Foo.bar = dart.const(new Baz(42)); |
2851 /// | 2852 /// |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2897 jsInit = _visitInitializer(field); | 2898 jsInit = _visitInitializer(field); |
2898 _loader.finishTopLevel(element); | 2899 _loader.finishTopLevel(element); |
2899 eagerInit = _loader.isLoaded(element); | 2900 eagerInit = _loader.isLoaded(element); |
2900 } else { | 2901 } else { |
2901 // TODO(jmesserly): we're visiting the initializer here, and again | 2902 // TODO(jmesserly): we're visiting the initializer here, and again |
2902 // later on when we emit lazy fields. That seems busted. | 2903 // later on when we emit lazy fields. That seems busted. |
2903 jsInit = _visitInitializer(field); | 2904 jsInit = _visitInitializer(field); |
2904 eagerInit = false; | 2905 eagerInit = false; |
2905 } | 2906 } |
2906 | 2907 |
2907 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 2908 // Treat dart:runtime stuff as safe to eagerly evaluate. |
2908 // runtime helpers. | 2909 // TODO(jmesserly): it'd be nice to avoid this special case. |
2909 // TODO(jmesserly): we don't track dependencies correctly for these. | 2910 var isJSTopLevel = field.isFinal && _isDartRuntime(element.library); |
2910 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | |
2911 if (eagerInit || isJSTopLevel) { | 2911 if (eagerInit || isJSTopLevel) { |
2912 // Remember that we emitted it this way, so re-export can take advantage | 2912 // Remember that we emitted it this way, so re-export can take advantage |
2913 // of this fact. | 2913 // of this fact. |
2914 _eagerTopLevelFields.add(element); | 2914 _eagerTopLevelFields.add(element); |
2915 | 2915 |
2916 return annotate( | 2916 return annotate( |
2917 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), | 2917 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), |
2918 field, | 2918 field, |
2919 element); | 2919 element); |
2920 } | 2920 } |
(...skipping 672 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3593 } | 3593 } |
3594 } | 3594 } |
3595 | 3595 |
3596 /// Everything in Dart is an Object and supports the 4 members on Object, | 3596 /// Everything in Dart is an Object and supports the 4 members on Object, |
3597 /// so we have to use a runtime helper to handle values such as `null` and | 3597 /// so we have to use a runtime helper to handle values such as `null` and |
3598 /// native types. | 3598 /// native types. |
3599 /// | 3599 /// |
3600 /// For example `null.toString()` is legal in Dart, so we need to generate | 3600 /// For example `null.toString()` is legal in Dart, so we need to generate |
3601 /// that as `dart.toString(obj)`. | 3601 /// that as `dart.toString(obj)`. |
3602 bool _isObjectMemberCall(Expression target, String memberName) { | 3602 bool _isObjectMemberCall(Expression target, String memberName) { |
3603 // Is this a member on `Object`? | 3603 if (!isObjectMember(memberName)) { |
3604 if (!isObjectProperty(memberName)) { | |
3605 return false; | 3604 return false; |
3606 } | 3605 } |
3607 | 3606 |
3608 // Check if the target could be `null`, is dynamic, or may be an extension | 3607 // Check if the target could be `null`, is dynamic, or may be an extension |
3609 // native type. In all of those cases we need defensive code generation. | 3608 // native type. In all of those cases we need defensive code generation. |
3610 var type = getStaticType(target); | 3609 var type = getStaticType(target); |
3611 return isNullable(target) || | 3610 return isNullable(target) || |
3612 type.isDynamic || | 3611 type.isDynamic || |
3613 (_extensionTypes.hasNativeSubtype(type) && target is! SuperExpression); | 3612 (_extensionTypes.hasNativeSubtype(type) && target is! SuperExpression); |
3614 } | 3613 } |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3672 // subclasses cannot override x. | 3671 // subclasses cannot override x. |
3673 jsTarget = new JS.This(); | 3672 jsTarget = new JS.This(); |
3674 } | 3673 } |
3675 | 3674 |
3676 JS.Expression result; | 3675 JS.Expression result; |
3677 if (member != null && member is MethodElement && !isStatic) { | 3676 if (member != null && member is MethodElement && !isStatic) { |
3678 // Tear-off methods: explicitly bind it. | 3677 // Tear-off methods: explicitly bind it. |
3679 if (isSuper) { | 3678 if (isSuper) { |
3680 result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]); | 3679 result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]); |
3681 } else if (_isObjectMemberCall(target, memberName)) { | 3680 } else if (_isObjectMemberCall(target, memberName)) { |
3682 result = js.call('dart.bind(#, #, dart.#)', [jsTarget, name, name]); | 3681 result = js.call('dart.bind(#, #, dart.#)', |
| 3682 [jsTarget, _propertyName(memberName), memberName]); |
3683 } else { | 3683 } else { |
3684 result = js.call('dart.bind(#, #)', [jsTarget, name]); | 3684 result = js.call('dart.bind(#, #)', [jsTarget, name]); |
3685 } | 3685 } |
3686 } else if (_isObjectMemberCall(target, memberName)) { | 3686 } else if (_isObjectMemberCall(target, memberName)) { |
3687 result = js.call('dart.#(#)', [name, jsTarget]); | 3687 result = js.call('dart.#(#)', [memberName, jsTarget]); |
3688 } else { | 3688 } else { |
3689 result = js.call('#.#', [jsTarget, name]); | 3689 result = js.call('#.#', [jsTarget, name]); |
3690 } | 3690 } |
3691 if (typeArgs == null) { | 3691 if (typeArgs == null) { |
3692 return result; | 3692 return result; |
3693 } | 3693 } |
3694 return js.call('dart.gbind(#, #)', [result, typeArgs]); | 3694 return js.call('dart.gbind(#, #)', [result, typeArgs]); |
3695 } | 3695 } |
3696 | 3696 |
3697 /// Emits a generic send, like an operator method. | 3697 /// Emits a generic send, like an operator method. |
(...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4145 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 4145 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
4146 if (nodes == null || nodes.isEmpty) return null; | 4146 if (nodes == null || nodes.isEmpty) return null; |
4147 return new JS.Expression.binary( | 4147 return new JS.Expression.binary( |
4148 _visitList(nodes) as List<JS.Expression>, operator); | 4148 _visitList(nodes) as List<JS.Expression>, operator); |
4149 } | 4149 } |
4150 | 4150 |
4151 /// Like [_emitMemberName], but for declaration sites. | 4151 /// Like [_emitMemberName], but for declaration sites. |
4152 /// | 4152 /// |
4153 /// Unlike call sites, we always have an element available, so we can use it | 4153 /// Unlike call sites, we always have an element available, so we can use it |
4154 /// directly rather than computing the relevant options for [_emitMemberName]. | 4154 /// directly rather than computing the relevant options for [_emitMemberName]. |
4155 JS.Expression _elementMemberName(ExecutableElement e, | 4155 JS.Expression _elementMemberName(ExecutableElement e, {bool useExtension}) { |
4156 {bool allowExtensions: true}) { | |
4157 String name; | 4156 String name; |
4158 if (e is PropertyAccessorElement) { | 4157 if (e is PropertyAccessorElement) { |
4159 name = e.variable.name; | 4158 name = e.variable.name; |
4160 } else { | 4159 } else { |
4161 name = e.name; | 4160 name = e.name; |
4162 } | 4161 } |
4163 return _emitMemberName(name, | 4162 return _emitMemberName(name, |
4164 type: (e.enclosingElement as ClassElement).type, | 4163 type: (e.enclosingElement as ClassElement).type, |
4165 unary: e.parameters.isEmpty, | 4164 unary: e.parameters.isEmpty, |
4166 isStatic: e.isStatic, | 4165 isStatic: e.isStatic, |
4167 allowExtensions: allowExtensions); | 4166 useExtension: useExtension); |
4168 } | 4167 } |
4169 | 4168 |
4170 /// This handles member renaming for private names and operators. | 4169 /// This handles member renaming for private names and operators. |
4171 /// | 4170 /// |
4172 /// Private names are generated using ES6 symbols: | 4171 /// Private names are generated using ES6 symbols: |
4173 /// | 4172 /// |
4174 /// // At the top of the module: | 4173 /// // At the top of the module: |
4175 /// let _x = Symbol('_x'); | 4174 /// let _x = Symbol('_x'); |
4176 /// let _y = Symbol('_y'); | 4175 /// let _y = Symbol('_y'); |
4177 /// ... | 4176 /// ... |
(...skipping 27 matching lines...) Expand all Loading... |
4205 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 4204 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
4206 /// for this transformation to happen, otherwise binary minus is assumed. | 4205 /// for this transformation to happen, otherwise binary minus is assumed. |
4207 /// | 4206 /// |
4208 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 4207 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
4209 /// helper, that checks for null. The user defined method is called '=='. | 4208 /// helper, that checks for null. The user defined method is called '=='. |
4210 /// | 4209 /// |
4211 JS.Expression _emitMemberName(String name, | 4210 JS.Expression _emitMemberName(String name, |
4212 {DartType type, | 4211 {DartType type, |
4213 bool unary: false, | 4212 bool unary: false, |
4214 bool isStatic: false, | 4213 bool isStatic: false, |
4215 bool allowExtensions: true}) { | 4214 bool useExtension}) { |
4216 // Static members skip the rename steps. | 4215 // Static members skip the rename steps. |
4217 if (isStatic) return _propertyName(name); | 4216 if (isStatic) return _propertyName(name); |
4218 | 4217 |
4219 if (name.startsWith('_')) { | 4218 if (name.startsWith('_')) { |
4220 return _emitPrivateNameSymbol(currentLibrary, name); | 4219 return _emitPrivateNameSymbol(currentLibrary, name); |
4221 } | 4220 } |
4222 | 4221 |
4223 if (name == '[]') { | 4222 if (name == '[]') { |
4224 name = 'get'; | 4223 name = 'get'; |
4225 } else if (name == '[]=') { | 4224 } else if (name == '[]=') { |
4226 name = 'set'; | 4225 name = 'set'; |
4227 } else if (name == '-' && unary) { | 4226 } else if (name == '-' && unary) { |
4228 name = 'unary-'; | 4227 name = 'unary-'; |
4229 } else if (name == 'constructor' || name == 'prototype') { | 4228 } else if (name == 'constructor' || name == 'prototype') { |
4230 // This uses an illegal (in Dart) character for a member, avoiding the | 4229 // This uses an illegal (in Dart) character for a member, avoiding the |
4231 // conflict. We could use practically any character for this. | 4230 // conflict. We could use practically any character for this. |
4232 name = '+$name'; | 4231 name = '+$name'; |
4233 } | 4232 } |
4234 | 4233 |
4235 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 4234 var result = _propertyName(name); |
4236 var baseType = type; | 4235 |
4237 while (baseType is TypeParameterType) { | 4236 if (useExtension == null) { |
4238 baseType = baseType.element.bound; | 4237 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
4239 } | 4238 var baseType = type; |
4240 if (allowExtensions && | 4239 while (baseType is TypeParameterType) { |
4241 baseType != null && | 4240 baseType = baseType.element.bound; |
4242 _extensionTypes.hasNativeSubtype(baseType) && | 4241 } |
4243 !isObjectProperty(name)) { | 4242 useExtension = baseType != null && |
4244 return js.call('dartx.#', _propertyName(name)); | 4243 _extensionTypes.hasNativeSubtype(baseType) && |
| 4244 !isObjectMember(name); |
4245 } | 4245 } |
4246 | 4246 |
4247 return _propertyName(name); | 4247 return useExtension ? js.call('dartx.#', result) : result; |
4248 } | 4248 } |
4249 | 4249 |
4250 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { | 4250 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { |
4251 return _privateNames | 4251 return _privateNames |
4252 .putIfAbsent(library, () => new HashMap()) | 4252 .putIfAbsent(library, () => new HashMap()) |
4253 .putIfAbsent(name, () { | 4253 .putIfAbsent(name, () { |
4254 var id = new JS.TemporaryId(name); | 4254 var id = new JS.TemporaryId(name); |
4255 _moduleItems.add( | 4255 _moduleItems.add( |
4256 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); | 4256 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); |
4257 return id; | 4257 return id; |
(...skipping 25 matching lines...) Expand all Loading... |
4283 } | 4283 } |
4284 | 4284 |
4285 /// Returns true if this is any kind of object represented by `Number` in JS. | 4285 /// Returns true if this is any kind of object represented by `Number` in JS. |
4286 /// | 4286 /// |
4287 /// In practice, this is 4 types: num, int, double, and JSNumber. | 4287 /// In practice, this is 4 types: num, int, double, and JSNumber. |
4288 /// | 4288 /// |
4289 /// JSNumber is the type that actually "implements" all numbers, hence it's | 4289 /// JSNumber is the type that actually "implements" all numbers, hence it's |
4290 /// a subtype of int and double (and num). It's in our "dart:_interceptors". | 4290 /// a subtype of int and double (and num). It's in our "dart:_interceptors". |
4291 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); | 4291 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); |
4292 | 4292 |
4293 bool _isObjectGetter(String name) { | 4293 /// Return true if this is one of the methods/properties on all Dart Objects |
4294 PropertyAccessorElement element = types.objectType.element.getGetter(name); | 4294 /// (toString, hashCode, noSuchMethod, runtimeType). |
4295 return (element != null && !element.isStatic); | 4295 /// |
4296 } | 4296 /// Operator == is excluded, as it is handled as part of the equality binary |
4297 | 4297 /// operator. |
4298 bool _isObjectMethod(String name) { | 4298 bool isObjectMember(String name) { |
4299 MethodElement element = types.objectType.element.getMethod(name); | 4299 // We could look these up on Object, but we have hard coded runtime helpers |
4300 return (element != null && !element.isStatic); | 4300 // so it's not really providing any benefit. |
4301 } | 4301 switch (name) { |
4302 | 4302 case 'hashCode': |
4303 bool isObjectProperty(String name) { | 4303 case 'toString': |
4304 return _isObjectGetter(name) || _isObjectMethod(name); | 4304 case 'noSuchMethod': |
| 4305 case 'runtimeType': |
| 4306 return true; |
| 4307 } |
| 4308 return false; |
4305 } | 4309 } |
4306 | 4310 |
4307 // TODO(leafp): Various analyzer pieces computed similar things. | 4311 // TODO(leafp): Various analyzer pieces computed similar things. |
4308 // Share this logic somewhere? | 4312 // Share this logic somewhere? |
4309 DartType _getExpectedReturnType(ExecutableElement element) { | 4313 DartType _getExpectedReturnType(ExecutableElement element) { |
4310 FunctionType functionType = element.type; | 4314 FunctionType functionType = element.type; |
4311 if (functionType == null) { | 4315 if (functionType == null) { |
4312 return DynamicTypeImpl.instance; | 4316 return DynamicTypeImpl.instance; |
4313 } | 4317 } |
4314 var type = functionType.returnType; | 4318 var type = functionType.returnType; |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4394 } | 4398 } |
4395 | 4399 |
4396 bool isLibraryPrefix(Expression node) => | 4400 bool isLibraryPrefix(Expression node) => |
4397 node is SimpleIdentifier && node.staticElement is PrefixElement; | 4401 node is SimpleIdentifier && node.staticElement is PrefixElement; |
4398 | 4402 |
4399 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 4403 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
4400 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 4404 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
4401 | 4405 |
4402 bool _isDartRuntime(LibraryElement l) => | 4406 bool _isDartRuntime(LibraryElement l) => |
4403 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 4407 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
OLD | NEW |