Index: lib/src/checker/resolver.dart |
diff --git a/lib/src/checker/resolver.dart b/lib/src/checker/resolver.dart |
index 8a35e0d153243515af63a9bc370e5b866547f738..24efb989ff12f9c0d90a7d83cd3b12a4a02d8afa 100644 |
--- a/lib/src/checker/resolver.dart |
+++ b/lib/src/checker/resolver.dart |
@@ -664,12 +664,72 @@ class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
} |
} |
+ Map<String, DartType> _objectMemberMap = null; |
+ |
+ Map<String, DartType> _getObjectMemberMap() { |
+ if (_objectMemberMap == null) { |
+ _objectMemberMap = new Map<String, DartType>(); |
+ var objectType = _typeProvider.objectType; |
+ var element = objectType.element; |
+ // Only record methods (including getters) with no parameters. As parameters are contravariant wrt |
+ // type, using Object's version may be too strict. |
+ // Add instance methods. |
+ element.methods |
+ .where((method) => !method.isStatic && method.parameters.isEmpty) |
+ .forEach((method) { |
+ _objectMemberMap[method.name] = method.type; |
+ }); |
+ // Add getters. |
+ element.accessors |
+ .where((member) => !member.isStatic && member.isGetter) |
+ .forEach((member) { |
+ _objectMemberMap[member.name] = member.type.returnType; |
+ }); |
+ } |
+ return _objectMemberMap; |
+ } |
+ |
+ List<DartType> _sealedTypes = null; |
+ |
+ bool _isSealed(DartType t) { |
+ if (_sealedTypes == null) { |
+ // TODO(vsm): Use the analyzer's list - see dartbug.com/23125. |
+ _sealedTypes = <DartType>[ |
+ _typeProvider.nullType, |
+ _typeProvider.numType, |
+ _typeProvider.intType, |
+ _typeProvider.doubleType, |
+ _typeProvider.boolType, |
+ _typeProvider.stringType |
+ ]; |
+ } |
+ return _sealedTypes.contains(t); |
+ } |
+ |
@override // to propagate types to identifiers |
visitMethodInvocation(MethodInvocation node) { |
// TODO(sigmund): follow up with analyzer team - why is this needed? |
visitSimpleIdentifier(node.methodName); |
super.visitMethodInvocation(node); |
+ // Search for Object methods. |
+ var objectMap = _getObjectMemberMap(); |
+ var name = node.methodName.name; |
+ if (node.staticType.isDynamic && |
+ objectMap.containsKey(name) && |
+ isDynamicTarget(node.target)) { |
+ var type = objectMap[name]; |
+ if (type is FunctionType && node.argumentList.arguments.isEmpty) { |
+ node.target.staticType = _typeProvider.objectType; |
+ node.methodName.staticType = type; |
+ // Only infer the type of the overall expression if we have an exact |
+ // type - e.g., a sealed type. Otherwise, it may be too strict. |
+ if (_isSealed(type.returnType)) { |
+ node.staticType = type.returnType; |
+ } |
+ } |
+ } |
+ |
var e = node.methodName.staticElement; |
if (e is FunctionElement && |
e.library.name == '_foreign_helper' && |
@@ -690,6 +750,39 @@ class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
} |
} |
+ void _inferObjectAccess( |
+ Expression node, Expression target, SimpleIdentifier id) { |
+ // Search for Object accesses. |
+ var objectMap = _getObjectMemberMap(); |
+ var name = id.name; |
+ if (node.staticType.isDynamic && |
+ objectMap.containsKey(name) && |
+ isDynamicTarget(target)) { |
+ target.staticType = _typeProvider.objectType; |
+ var type = objectMap[name]; |
+ id.staticType = type; |
+ // Only infer the type of the overall expression if we have an exact |
+ // type - e.g., a sealed type. Otherwise, it may be too strict. |
+ if (_isSealed(type)) { |
+ node.staticType = type; |
+ } |
+ } |
+ } |
+ |
+ @override |
+ visitPropertyAccess(PropertyAccess node) { |
+ super.visitPropertyAccess(node); |
+ |
+ _inferObjectAccess(node, node.target, node.propertyName); |
+ } |
+ |
+ @override |
+ visitPrefixedIdentifier(PrefixedIdentifier node) { |
+ super.visitPrefixedIdentifier(node); |
+ |
+ _inferObjectAccess(node, node.prefix, node.identifier); |
+ } |
+ |
@override |
visitConditionalExpression(ConditionalExpression node) { |
// TODO(vsm): The static type of a conditional should be the LUB of the |