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