OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, 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 /** | |
8 * A set of selector names that [List] implements, that we know do not | |
9 * change the element type of the list, or let the list escape to code | |
10 * that might change the element type. | |
11 */ | |
12 Set<String> okListSelectorsSet = new Set<String>.from( | |
13 const <String>[ | |
14 // From Object. | |
15 '==', | |
16 'hashCode', | |
17 'toString', | |
18 'noSuchMethod', | |
19 'runtimeType', | |
20 | |
21 // From Iterable. | |
22 'iterator', | |
23 'map', | |
24 'where', | |
25 'expand', | |
26 'contains', | |
27 'forEach', | |
28 'reduce', | |
29 'fold', | |
30 'every', | |
31 'join', | |
32 'any', | |
33 'toList', | |
34 'toSet', | |
35 'length', | |
36 'isEmpty', | |
37 'isNotEmpty', | |
38 'take', | |
39 'takeWhile', | |
40 'skip', | |
41 'skipWhile', | |
42 'first', | |
43 'last', | |
44 'single', | |
45 'firstWhere', | |
46 'lastWhere', | |
47 'singleWhere', | |
48 'elementAt', | |
49 | |
50 // From List. | |
51 '[]', | |
52 'length', | |
53 'reversed', | |
54 'sort', | |
55 'indexOf', | |
56 'lastIndexOf', | |
57 'clear', | |
58 'remove', | |
59 'removeAt', | |
60 'removeLast', | |
61 'removeWhere', | |
62 'retainWhere', | |
63 'sublist', | |
64 'getRange', | |
65 'removeRange', | |
66 'asMap', | |
67 | |
68 // From JSArray. | |
69 'checkMutable', | |
70 'checkGrowable', | |
71 ]); | |
72 | |
73 Set<String> doNotChangeLengthSelectorsSet = new Set<String>.from( | |
74 const <String>[ | |
75 // From Object. | |
76 '==', | |
77 'hashCode', | |
78 'toString', | |
79 'noSuchMethod', | |
80 'runtimeType', | |
81 | |
82 // From Iterable. | |
83 'iterator', | |
84 'map', | |
85 'where', | |
86 'expand', | |
87 'contains', | |
88 'forEach', | |
89 'reduce', | |
90 'fold', | |
91 'every', | |
92 'join', | |
93 'any', | |
94 'toList', | |
95 'toSet', | |
96 'length', | |
97 'isEmpty', | |
98 'isNotEmpty', | |
99 'take', | |
100 'takeWhile', | |
101 'skip', | |
102 'skipWhile', | |
103 'first', | |
104 'last', | |
105 'single', | |
106 'firstWhere', | |
107 'lastWhere', | |
108 'singleWhere', | |
109 'elementAt', | |
110 | |
111 // From List. | |
112 '[]', | |
113 '[]=', | |
114 'length', | |
115 'reversed', | |
116 'sort', | |
117 'indexOf', | |
118 'lastIndexOf', | |
119 'sublist', | |
120 'getRange', | |
121 'asMap', | |
122 | |
123 // From JSArray. | |
124 'checkMutable', | |
125 'checkGrowable', | |
126 ]); | |
127 | |
128 | |
129 class ListTracerVisitor extends TracerVisitor<ListTypeInformation> { | |
130 // The [Set] of found assignments to the list. | |
131 Set<TypeInformation> assignments = new Setlet<TypeInformation>(); | |
132 bool callsGrowableMethod = false; | |
133 | |
134 ListTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); | |
135 | |
136 /** | |
137 * Returns [true] if the analysis completed successfully, [false] if it | |
138 * bailed out. In the former case, [assignments] holds a list of | |
139 * [TypeInformation] nodes that flow into the element type of this list. | |
140 */ | |
141 bool run() { | |
142 analyze(); | |
143 ListTypeInformation list = tracedType; | |
144 if (continueAnalyzing) { | |
145 if (!callsGrowableMethod && list.inferredLength == null) { | |
146 list.inferredLength = list.originalLength; | |
147 } | |
148 list.addFlowsIntoTargets(flowsInto); | |
149 return true; | |
150 } else { | |
151 callsGrowableMethod = true; | |
152 assignments = null; | |
153 return false; | |
154 } | |
155 } | |
156 | |
157 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { | |
158 bailout('Passed to a closure'); | |
159 } | |
160 | |
161 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | |
162 super.visitStaticCallSiteTypeInformation(info); | |
163 Element called = info.calledElement; | |
164 if (called.isForeign(compiler.backend) && called.name == 'JS') { | |
165 bailout('Used in JS ${info.call}'); | |
166 } | |
167 } | |
168 | |
169 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { | |
170 super.visitDynamicCallSiteTypeInformation(info); | |
171 Selector selector = info.selector; | |
172 String selectorName = selector.name; | |
173 if (currentUser == info.receiver) { | |
174 if (!okListSelectorsSet.contains(selectorName)) { | |
175 if (selector.isCall) { | |
176 int positionalLength = info.arguments.positional.length; | |
177 if (selectorName == 'add') { | |
178 if (positionalLength == 1) { | |
179 assignments.add(info.arguments.positional[0]); | |
180 } | |
181 } else if (selectorName == 'insert') { | |
182 if (positionalLength == 2) { | |
183 assignments.add(info.arguments.positional[1]); | |
184 } | |
185 } else { | |
186 bailout('Used in a not-ok selector'); | |
187 return; | |
188 } | |
189 } else if (selector.isIndexSet) { | |
190 assignments.add(info.arguments.positional[1]); | |
191 } else if (!selector.isIndex) { | |
192 bailout('Used in a not-ok selector'); | |
193 return; | |
194 } | |
195 } | |
196 if (!doNotChangeLengthSelectorsSet.contains(selectorName)) { | |
197 callsGrowableMethod = true; | |
198 } | |
199 if (selectorName == 'length' && selector.isSetter) { | |
200 callsGrowableMethod = true; | |
201 assignments.add(inferrer.types.nullType); | |
202 } | |
203 } else if (selector.isCall && | |
204 !info.targets.every((element) => element.isFunction)) { | |
205 bailout('Passed to a closure'); | |
206 return; | |
207 } | |
208 } | |
209 } | |
OLD | NEW |