OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 part of type_graph_inferrer; | |
6 | |
7 Set<String> okMapSelectorsSet = new Set.from( | |
8 const <String>[ | |
9 // From Object. | |
10 "==", | |
11 "hashCode", | |
12 "toString", | |
13 "noSuchMethod", | |
14 "runtimeType", | |
15 // From Map | |
16 "[]", | |
17 "isEmpty", | |
18 "isNotEmpty", | |
19 "keys", | |
20 "length", | |
21 "values", | |
22 "clear", | |
23 "containsKey", | |
24 "containsValue", | |
25 "forEach", | |
26 "remove"]); | |
27 | |
28 class MapTracerVisitor extends TracerVisitor<MapTypeInformation> { | |
29 // These lists are used to keep track of newly discovered assignments to | |
30 // the map. Note that elements at corresponding indices are expected to | |
31 // belong to the same assignment operation. | |
32 List<TypeInformation> keyAssignments = <TypeInformation>[]; | |
33 List<TypeInformation> valueAssignments = <TypeInformation>[]; | |
34 // This list is used to keep track of assignments of entire maps to | |
35 // this map. | |
36 List<MapTypeInformation> mapAssignments = <MapTypeInformation>[]; | |
37 | |
38 MapTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); | |
39 | |
40 /** | |
41 * Returns [true] if the analysis completed successfully, [false] | |
42 * if it bailed out. In the former case, [keyAssignments] and | |
43 * [valueAssignments] hold a list of [TypeInformation] nodes that | |
44 * flow into the key and value types of this map. | |
45 */ | |
46 bool run() { | |
47 analyze(); | |
48 MapTypeInformation map = tracedType; | |
49 if (continueAnalyzing) { | |
50 map.addFlowsIntoTargets(flowsInto); | |
51 return true; | |
52 } | |
53 keyAssignments = valueAssignments = mapAssignments = null; | |
54 return false; | |
55 } | |
56 | |
57 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { | |
58 bailout('Passed to a closure'); | |
59 } | |
60 | |
61 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | |
62 super.visitStaticCallSiteTypeInformation(info); | |
63 Element called = info.calledElement; | |
64 if (called.isForeign(compiler.backend) && called.name == 'JS') { | |
65 bailout('Used in JS ${info.call}'); | |
66 } | |
67 } | |
68 | |
69 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { | |
70 super.visitDynamicCallSiteTypeInformation(info); | |
71 Selector selector = info.selector; | |
72 String selectorName = selector.name; | |
73 if (currentUser == info.receiver) { | |
74 if (!okMapSelectorsSet.contains(selectorName)) { | |
75 if (selector.isCall) { | |
76 int positionalLength = info.arguments.positional.length; | |
77 if (selectorName == 'addAll') { | |
78 // All keys and values from the argument flow into | |
79 // the map. | |
80 TypeInformation map = info.arguments.positional[0]; | |
81 if (map is MapTypeInformation) { | |
82 inferrer.analyzeMapAndEnqueue(map); | |
83 mapAssignments.add(map); | |
84 } else { | |
85 // If we could select a component from a [TypeInformation], | |
86 // like the keytype or valuetype in this case, we could | |
87 // propagate more here. | |
88 // TODO(herhut): implement selection on [TypeInformation]. | |
89 bailout('Adding map with unknown typeinfo to current map'); | |
90 } | |
91 } else if (selectorName == 'putIfAbsent') { | |
92 // The first argument is a new key, the result type of | |
93 // the second argument becomes a new value. | |
94 // Unfortunately, the type information does not | |
95 // explicitly track the return type, yet, so we have | |
96 // to go to dynamic. | |
97 // TODO(herhut,16507): Use return type of closure in | |
98 // Map.putIfAbsent. | |
99 keyAssignments.add(info.arguments.positional[0]); | |
100 valueAssignments.add(inferrer.types.dynamicType); | |
101 } else { | |
102 // It would be nice to handle [Map.keys] and [Map.values], too. | |
103 // However, currently those calls do not trigger the creation | |
104 // of a [ListTypeInformation], so I have nowhere to propagate | |
105 // that information. | |
106 // TODO(herhut): add support for Map.keys and Map.values. | |
107 bailout('Map used in a not-ok selector [$selectorName]'); | |
108 return; | |
109 } | |
110 } else if (selector.isIndexSet) { | |
111 keyAssignments.add(info.arguments.positional[0]); | |
112 valueAssignments.add(info.arguments.positional[1]); | |
113 } else if (!selector.isIndex) { | |
114 bailout('Map used in a not-ok selector [$selectorName]'); | |
115 return; | |
116 } | |
117 } | |
118 } else if (selector.isCall && | |
119 !info.targets.every((element) => element.isFunction)) { | |
120 bailout('Passed to a closure'); | |
121 return; | |
122 } | |
123 } | |
124 } | |
OLD | NEW |