Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(308)

Side by Side Diff: lib/src/compiler/code_generator.dart

Issue 1962823002: fix #552, Object members on native types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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';
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698