Chromium Code Reviews| Index: pkg/compiler/lib/src/inferrer/node_tracer.dart |
| diff --git a/pkg/compiler/lib/src/inferrer/node_tracer.dart b/pkg/compiler/lib/src/inferrer/node_tracer.dart |
| index 7f6ad9974a5440758c830241f82708669524a7a3..f4546032b2fcbed7c8aff65deb401753440aef67 100644 |
| --- a/pkg/compiler/lib/src/inferrer/node_tracer.dart |
| +++ b/pkg/compiler/lib/src/inferrer/node_tracer.dart |
| @@ -244,77 +244,138 @@ abstract class TracerVisitor implements TypeInformationVisitor { |
| } |
| /** |
| - * Checks whether this is a call to a list adding method. The definition |
| - * of what list adding means has to stay in sync with |
| + * Checks whether this is a call to a list adding method. The definition of |
| + * what list adding means has to stay in sync with |
| * [isParameterOfListAddingMethod]. |
| */ |
| - bool isAddedToContainer(DynamicCallSiteTypeInformation info) { |
| + bool mightAddToContainer(DynamicCallSiteTypeInformation info) { |
| if (info.arguments == null) return false; |
| - var receiverType = info.receiver.type; |
| - if (!receiverType.isContainer) return false; |
| + if (info.arguments.named.isNotEmpty) return false; |
| String selectorName = info.selector.name; |
| List<TypeInformation> arguments = info.arguments.positional; |
| - return (selectorName == '[]=' && currentUser == arguments[1]) || |
| - (selectorName == 'insert' && currentUser == arguments[1]) || |
| - (selectorName == 'add' && currentUser == arguments[0]); |
| + if (arguments.length == 1) { |
| + return (selectorName == 'add' && currentUser == arguments[0]); |
| + } else if (arguments.length == 2) { |
| + return (selectorName == 'insert' && currentUser == arguments[1]); |
| + } |
| + return false; |
| } |
| - bool isIndexSetOnMap(DynamicCallSiteTypeInformation info) { |
| - if (info.arguments == null) return false; |
| - var receiverType = info.receiver.type; |
| - if (!receiverType.isMap) return false; |
| - return info.selector.name == '[]='; |
| + bool isIndexSetArgument(DynamicCallSiteTypeInformation info, int index) { |
| + String selectorName = info.selector.name; |
| + if (selectorName != '[]=') return false; |
| + assert(info.arguments.length == 2); |
| + List<TypeInformation> arguments = info.arguments.positional; |
| + return currentUser == arguments[index]; |
| } |
| /** |
| - * Checks whether this is a call to a map adding method for values. The |
| - * definition of map adding method has to stay in sync with |
| + * Checks whether the call site flows the currentUser to the key argument of |
| + * an indexing setter. This must be kept in sync with |
| * [isParameterOfMapAddingMethod]. |
| */ |
| - bool isValueAddedToMap(DynamicCallSiteTypeInformation info) { |
| - return isIndexSetOnMap(info) && currentUser == info.arguments.positional[1]; |
| + bool isIndexSetKey(DynamicCallSiteTypeInformation info) { |
| + return isIndexSetArgument(info, 0); |
| } |
| /** |
| - * Checks whether this is a call to a map adding method for keys. The |
| - * definition of map adding method has to stay in sync with |
| - * [isParameterOfMapAddingMethod]. |
| + * Checks whether the call site flows the currentUser to the value argument of |
| + * an indexing setter. This must be kept in sync with |
| + * [isParameterOfListAddingMethod] and [isParameterOfMapAddingMethod]. |
| */ |
| - bool isKeyAddedToMap(DynamicCallSiteTypeInformation info) { |
| - return isIndexSetOnMap(info) && currentUser == info.arguments.positional[0]; |
| + bool isIndexSetValue(DynamicCallSiteTypeInformation info) { |
| + return isIndexSetArgument(info, 1); |
| + } |
| + |
| + void bailoutIfReaches(bool predicate(Element e)) { |
| + for (var user in currentUser.users) { |
| + if (user is ParameterTypeInformation) { |
| + if (predicate(user.element)) { |
| + bailout('Reached suppressed parameter without precise receiver'); |
| + break; |
| + } |
| + } |
| + } |
| } |
| void visitDynamicCallSiteTypeInformation( |
| DynamicCallSiteTypeInformation info) { |
| - if (isAddedToContainer(info)) { |
| - ContainerTypeMask mask = info.receiver.type; |
| + void addsToContainer(ContainerTypeMask mask) { |
| if (mask.allocationNode != null) { |
| ListTypeInformation list = |
| inferrer.types.allocatedLists[mask.allocationNode]; |
| listsToAnalyze.add(list); |
| } else { |
| - // The [ContainerTypeMask] is a union of two containers, and |
| - // we lose track of where these containers have been allocated |
| - // at this point. |
| + // The [ContainerTypeMask] is a union of two containers, and we lose |
| + // track of where these containers have been allocated at this point. |
| bailout('Stored in too many containers'); |
| } |
| - } else if (isValueAddedToMap(info)) { |
| - MapTypeMask mask = info.receiver.type; |
| + } |
| + |
| + void addsToMapValue(MapTypeMask mask) { |
| if (mask.allocationNode != null) { |
| MapTypeInformation map = |
| inferrer.types.allocatedMaps[mask.allocationNode]; |
| mapsToAnalyze.add(map); |
| } else { |
| - // The [MapTypeMask] is a union. See comment for |
| - // [ContainerTypeMask] above. |
| + // The [MapTypeMask] is a union. See comment for [ContainerTypeMask] |
| + // above. |
| bailout('Stored in too many maps'); |
| } |
| - } else if (isKeyAddedToMap(info)) { |
| + } |
| + |
| + void addsToMapKey(MapTypeMask mask) { |
| // We do not track the use of keys from a map, so we have to bail. |
| bailout('Used as key in Map'); |
| } |
| + // "a[...] = x" could be a list (container) or map assignemnt. |
| + if (isIndexSetValue(info)) { |
| + var receiverType = info.receiver.type; |
| + if (receiverType is ContainerTypeMask) { |
| + addsToContainer(receiverType); |
| + } else if (receiverType is MapTypeMask) { |
| + addsToMapValue(receiverType); |
| + } else { |
| + // Not a container or mask, so the targets could be any methods. There |
|
Siggi Cherem (dart-lang)
2017/03/21 17:05:01
or mask => or map
sra1
2017/03/21 18:17:22
Done.
|
| + // are edges from the [currentUser] to the parameters of the targets, so |
| + // tracing will continue into the targets. Tracing stops at parameters |
| + // that match the targets corresponding to the receiverTypes above (to |
| + // prevent imprecise results from tracing the implementation), so we |
| + // need compensate if one of the targets is in the target set. If there |
| + // is an edge to a parameter matching [isParameterOfListAddingMethod] or |
| + // [isParameterOfMapAddingMethod] then the traced value is being stored |
| + // into an untraced list or map. |
| + |
| + // TODO(sra): It would be more precise to specifically match the `value' |
| + // parameters of "operator []=". |
| + bailoutIfReaches(isParameterOfListAddingMethod); |
| + bailoutIfReaches(isParameterOfMapAddingMethod); |
| + } |
| + } |
| + |
| + // Could be: m[x] = ...; |
| + if (isIndexSetKey(info)) { |
| + var receiverType = info.receiver.type; |
| + if (receiverType is MapTypeMask) { |
| + addsToMapKey(receiverType); |
| + } else { |
| + bailoutIfReaches(isParameterOfListAddingMethod); |
|
Siggi Cherem (dart-lang)
2017/03/21 17:05:01
would this case ever occur? (since the key is like
sra1
2017/03/21 18:17:22
It could happen in code that would throw an except
|
| + bailoutIfReaches(isParameterOfMapAddingMethod); |
| + } |
| + } |
| + |
| + if (mightAddToContainer(info)) { |
| + var receiverType = info.receiver.type; |
| + if (receiverType is ContainerTypeMask) { |
| + addsToContainer(receiverType); |
| + } else { |
| + // Not a container, see note above. |
| + bailoutIfReaches(isParameterOfListAddingMethod); |
| + } |
| + } |
| + |
| if (info.targetsIncludeComplexNoSuchMethod(inferrer) && |
| info.arguments != null && |
| info.arguments.contains(currentUser)) { |
| @@ -332,31 +393,29 @@ abstract class TracerVisitor implements TypeInformationVisitor { |
| /** |
| * Check whether element is the parameter of a list adding method. |
| * The definition of what a list adding method is has to stay in sync with |
| - * [isAddedToContainer]. |
| + * [mightAddToContainer]. |
| */ |
| bool isParameterOfListAddingMethod(Element element) { |
| if (!element.isRegularParameter) return false; |
| if (element.enclosingClass != compiler.backend.backendClasses.listClass) { |
| return false; |
| } |
| - Element method = element.enclosingElement; |
| - return (method.name == '[]=') || |
| - (method.name == 'add') || |
| - (method.name == 'insert'); |
| + String name = element.enclosingElement.name; |
| + return (name == '[]=') || (name == 'add') || (name == 'insert'); |
| } |
| /** |
| * Check whether element is the parameter of a list adding method. |
| * The definition of what a list adding method is has to stay in sync with |
| - * [isValueAddedToMap] and [isKeyAddedToMap]. |
| + * [isIndexSetKey] and [isIndexSetValue]. |
| */ |
| bool isParameterOfMapAddingMethod(Element element) { |
| if (!element.isRegularParameter) return false; |
| if (element.enclosingClass != compiler.backend.backendClasses.mapClass) { |
| return false; |
| } |
| - Element method = element.enclosingElement; |
| - return (method.name == '[]='); |
| + String name = element.enclosingElement.name; |
| + return (name == '[]='); |
| } |
| bool isClosure(Element element) { |
| @@ -399,8 +458,8 @@ abstract class TracerVisitor implements TypeInformationVisitor { |
| element.functionDeclaration)) { |
| bailout('Escape to code that has special backend treatment'); |
| } |
| - if (isParameterOfListAddingMethod(info.element) || |
| - isParameterOfMapAddingMethod(info.element)) { |
| + if (isParameterOfListAddingMethod(element) || |
| + isParameterOfMapAddingMethod(element)) { |
| // These elements are being handled in |
| // [visitDynamicCallSiteTypeInformation]. |
| return; |