OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 library types; | |
6 | |
7 import '../dart2jslib.dart' hide Selector, TypedSelector; | |
8 import '../elements/elements.dart'; | |
9 import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer; | |
10 import '../tree/tree.dart'; | |
11 import '../util/util.dart'; | |
12 import '../universe/universe.dart'; | |
13 import '../inferrer/concrete_types_inferrer.dart' show ConcreteTypesInferrer; | |
14 | |
15 part 'container_type_mask.dart'; | |
16 part 'dictionary_type_mask.dart'; | |
17 part 'flat_type_mask.dart'; | |
18 part 'forwarding_type_mask.dart'; | |
19 part 'map_type_mask.dart'; | |
20 part 'type_mask.dart'; | |
21 part 'union_type_mask.dart'; | |
22 part 'value_type_mask.dart'; | |
23 | |
24 /** | |
25 * Common super class for our type inferrers. | |
26 */ | |
27 abstract class TypesInferrer { | |
28 void analyzeMain(Element element); | |
29 TypeMask getReturnTypeOfElement(Element element); | |
30 TypeMask getTypeOfElement(Element element); | |
31 TypeMask getTypeOfNode(Element owner, Node node); | |
32 TypeMask getTypeOfSelector(Selector selector); | |
33 void clear(); | |
34 bool isCalledOnce(Element element); | |
35 bool isFixedArrayCheckedForGrowable(Node node); | |
36 } | |
37 | |
38 /** | |
39 * The types task infers guaranteed types globally. | |
40 */ | |
41 class TypesTask extends CompilerTask { | |
42 static final bool DUMP_BAD_CPA_RESULTS = false; | |
43 static final bool DUMP_GOOD_CPA_RESULTS = false; | |
44 | |
45 final String name = 'Type inference'; | |
46 final ClassWorld classWorld; | |
47 TypesInferrer typesInferrer; | |
48 ConcreteTypesInferrer concreteTypesInferrer; | |
49 | |
50 TypesTask(Compiler compiler) | |
51 : this.classWorld = compiler.world, | |
52 super(compiler) { | |
53 typesInferrer = new TypeGraphInferrer(compiler); | |
54 if (compiler.enableConcreteTypeInference) { | |
55 concreteTypesInferrer = new ConcreteTypesInferrer(compiler); | |
56 } | |
57 } | |
58 | |
59 TypeMask dynamicTypeCache; | |
60 TypeMask nonNullTypeCache; | |
61 TypeMask nullTypeCache; | |
62 TypeMask intTypeCache; | |
63 TypeMask uint32TypeCache; | |
64 TypeMask uint31TypeCache; | |
65 TypeMask positiveIntTypeCache; | |
66 TypeMask doubleTypeCache; | |
67 TypeMask numTypeCache; | |
68 TypeMask boolTypeCache; | |
69 TypeMask functionTypeCache; | |
70 TypeMask listTypeCache; | |
71 TypeMask constListTypeCache; | |
72 TypeMask fixedListTypeCache; | |
73 TypeMask growableListTypeCache; | |
74 TypeMask mapTypeCache; | |
75 TypeMask constMapTypeCache; | |
76 TypeMask stringTypeCache; | |
77 TypeMask typeTypeCache; | |
78 | |
79 TypeMask get dynamicType { | |
80 if (dynamicTypeCache == null) { | |
81 dynamicTypeCache = | |
82 new TypeMask.subclass(classWorld.objectClass, classWorld); | |
83 } | |
84 return dynamicTypeCache; | |
85 } | |
86 | |
87 TypeMask get nonNullType { | |
88 if (nonNullTypeCache == null) { | |
89 nonNullTypeCache = | |
90 new TypeMask.nonNullSubclass(classWorld.objectClass, classWorld); | |
91 } | |
92 return nonNullTypeCache; | |
93 } | |
94 | |
95 TypeMask get intType { | |
96 if (intTypeCache == null) { | |
97 intTypeCache = new TypeMask.nonNullSubclass( | |
98 compiler.backend.intImplementation, compiler.world); | |
99 } | |
100 return intTypeCache; | |
101 } | |
102 | |
103 TypeMask get uint32Type { | |
104 if (uint32TypeCache == null) { | |
105 uint32TypeCache = new TypeMask.nonNullSubclass( | |
106 compiler.backend.uint32Implementation, compiler.world); | |
107 } | |
108 return uint32TypeCache; | |
109 } | |
110 | |
111 TypeMask get uint31Type { | |
112 if (uint31TypeCache == null) { | |
113 uint31TypeCache = new TypeMask.nonNullExact( | |
114 compiler.backend.uint31Implementation, compiler.world); | |
115 } | |
116 return uint31TypeCache; | |
117 } | |
118 | |
119 TypeMask get positiveIntType { | |
120 if (positiveIntTypeCache == null) { | |
121 positiveIntTypeCache = new TypeMask.nonNullSubclass( | |
122 compiler.backend.positiveIntImplementation, compiler.world); | |
123 } | |
124 return positiveIntTypeCache; | |
125 } | |
126 | |
127 TypeMask get doubleType { | |
128 if (doubleTypeCache == null) { | |
129 doubleTypeCache = new TypeMask.nonNullExact( | |
130 compiler.backend.doubleImplementation, compiler.world); | |
131 } | |
132 return doubleTypeCache; | |
133 } | |
134 | |
135 TypeMask get numType { | |
136 if (numTypeCache == null) { | |
137 numTypeCache = new TypeMask.nonNullSubclass( | |
138 compiler.backend.numImplementation, compiler.world); | |
139 } | |
140 return numTypeCache; | |
141 } | |
142 | |
143 TypeMask get boolType { | |
144 if (boolTypeCache == null) { | |
145 boolTypeCache = new TypeMask.nonNullExact( | |
146 compiler.backend.boolImplementation, compiler.world); | |
147 } | |
148 return boolTypeCache; | |
149 } | |
150 | |
151 TypeMask get functionType { | |
152 if (functionTypeCache == null) { | |
153 functionTypeCache = new TypeMask.nonNullSubtype( | |
154 compiler.backend.functionImplementation, classWorld); | |
155 } | |
156 return functionTypeCache; | |
157 } | |
158 | |
159 TypeMask get listType { | |
160 if (listTypeCache == null) { | |
161 listTypeCache = new TypeMask.nonNullExact( | |
162 compiler.backend.listImplementation, compiler.world); | |
163 } | |
164 return listTypeCache; | |
165 } | |
166 | |
167 TypeMask get constListType { | |
168 if (constListTypeCache == null) { | |
169 constListTypeCache = new TypeMask.nonNullExact( | |
170 compiler.backend.constListImplementation, compiler.world); | |
171 } | |
172 return constListTypeCache; | |
173 } | |
174 | |
175 TypeMask get fixedListType { | |
176 if (fixedListTypeCache == null) { | |
177 fixedListTypeCache = new TypeMask.nonNullExact( | |
178 compiler.backend.fixedListImplementation, compiler.world); | |
179 } | |
180 return fixedListTypeCache; | |
181 } | |
182 | |
183 TypeMask get growableListType { | |
184 if (growableListTypeCache == null) { | |
185 growableListTypeCache = new TypeMask.nonNullExact( | |
186 compiler.backend.growableListImplementation, compiler.world); | |
187 } | |
188 return growableListTypeCache; | |
189 } | |
190 | |
191 TypeMask get mapType { | |
192 if (mapTypeCache == null) { | |
193 mapTypeCache = new TypeMask.nonNullSubtype( | |
194 compiler.backend.mapImplementation, classWorld); | |
195 } | |
196 return mapTypeCache; | |
197 } | |
198 | |
199 TypeMask get constMapType { | |
200 if (constMapTypeCache == null) { | |
201 constMapTypeCache = new TypeMask.nonNullSubtype( | |
202 compiler.backend.constMapImplementation, classWorld); | |
203 } | |
204 return constMapTypeCache; | |
205 } | |
206 | |
207 TypeMask get stringType { | |
208 if (stringTypeCache == null) { | |
209 stringTypeCache = new TypeMask.nonNullExact( | |
210 compiler.backend.stringImplementation, compiler.world); | |
211 } | |
212 return stringTypeCache; | |
213 } | |
214 | |
215 TypeMask get typeType { | |
216 if (typeTypeCache == null) { | |
217 typeTypeCache = new TypeMask.nonNullExact( | |
218 compiler.backend.typeImplementation, compiler.world); | |
219 } | |
220 return typeTypeCache; | |
221 } | |
222 | |
223 TypeMask get nullType { | |
224 if (nullTypeCache == null) { | |
225 // TODO(johnniwinther): Assert that the null type has been resolved. | |
226 nullTypeCache = const TypeMask.empty(); | |
227 } | |
228 return nullTypeCache; | |
229 } | |
230 | |
231 /** Helper method for [intersection]. */ | |
232 TypeMask _intersection(TypeMask type1, TypeMask type2) { | |
233 if (type1 == null) return type2; | |
234 if (type2 == null) return type1; | |
235 return type1.intersection(type2, classWorld); | |
236 } | |
237 | |
238 /** Computes the intersection of [type1] and [type2] */ | |
239 TypeMask intersection(TypeMask type1, TypeMask type2, element) { | |
240 TypeMask result = _intersection(type1, type2); | |
241 if (DUMP_BAD_CPA_RESULTS && better(type1, type2)) { | |
242 print("CPA is worse for $element: $type1 /\\ $type2 = $result"); | |
243 } | |
244 if (DUMP_GOOD_CPA_RESULTS && better(type2, type1)) { | |
245 print("CPA is better for $element: $type1 /\\ $type2 = $result"); | |
246 } | |
247 return result; | |
248 } | |
249 | |
250 /** Returns true if [type1] is strictly bettern than [type2]. */ | |
251 bool better(TypeMask type1, TypeMask type2) { | |
252 if (type1 == null) return false; | |
253 if (type2 == null) { | |
254 return (type1 != null) && | |
255 (type1 != dynamicType); | |
256 } | |
257 return (type1 != type2) && | |
258 type2.containsMask(type1, classWorld) && | |
259 !type1.containsMask(type2, classWorld); | |
260 } | |
261 | |
262 /** | |
263 * Called when resolution is complete. | |
264 */ | |
265 void onResolutionComplete(Element mainElement) { | |
266 measure(() { | |
267 typesInferrer.analyzeMain(mainElement); | |
268 if (concreteTypesInferrer != null) { | |
269 bool success = concreteTypesInferrer.analyzeMain(mainElement); | |
270 if (!success) { | |
271 // If the concrete type inference bailed out, we pretend it didn't | |
272 // happen. In the future we might want to record that it failed but | |
273 // use the partial results as hints. | |
274 concreteTypesInferrer = null; | |
275 } | |
276 } | |
277 }); | |
278 typesInferrer.clear(); | |
279 } | |
280 | |
281 /** | |
282 * Return the (inferred) guaranteed type of [element] or null. | |
283 */ | |
284 TypeMask getGuaranteedTypeOfElement(Element element) { | |
285 return measure(() { | |
286 TypeMask guaranteedType = typesInferrer.getTypeOfElement(element); | |
287 return (concreteTypesInferrer == null) | |
288 ? guaranteedType | |
289 : intersection(guaranteedType, | |
290 concreteTypesInferrer.getTypeOfElement(element), | |
291 element); | |
292 }); | |
293 } | |
294 | |
295 TypeMask getGuaranteedReturnTypeOfElement(Element element) { | |
296 return measure(() { | |
297 TypeMask guaranteedType = | |
298 typesInferrer.getReturnTypeOfElement(element); | |
299 return (concreteTypesInferrer == null) | |
300 ? guaranteedType | |
301 : intersection(guaranteedType, | |
302 concreteTypesInferrer.getReturnTypeOfElement(element), | |
303 element); | |
304 }); | |
305 } | |
306 | |
307 /** | |
308 * Return the (inferred) guaranteed type of [node] or null. | |
309 * [node] must be an AST node of [owner]. | |
310 */ | |
311 TypeMask getGuaranteedTypeOfNode(owner, node) { | |
312 return measure(() { | |
313 TypeMask guaranteedType = typesInferrer.getTypeOfNode(owner, node); | |
314 return (concreteTypesInferrer == null) | |
315 ? guaranteedType | |
316 : intersection(guaranteedType, | |
317 concreteTypesInferrer.getTypeOfNode(owner, node), | |
318 node); | |
319 }); | |
320 } | |
321 | |
322 /** | |
323 * Return the (inferred) guaranteed type of [selector] or null. | |
324 */ | |
325 TypeMask getGuaranteedTypeOfSelector(Selector selector) { | |
326 return measure(() { | |
327 TypeMask guaranteedType = | |
328 typesInferrer.getTypeOfSelector(selector); | |
329 return (concreteTypesInferrer == null) | |
330 ? guaranteedType | |
331 : intersection(guaranteedType, | |
332 concreteTypesInferrer.getTypeOfSelector(selector), | |
333 selector); | |
334 }); | |
335 } | |
336 } | |
OLD | NEW |