gitignore nit* in bin/
[nit.git] / src / coloring.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Graph coloring tools
16 module coloring
17
18 import rapid_type_analysis # for type coloration
19
20 abstract class AbstractColoring[E: Object]
21
22 private var sorter: AbstractSorter[E]
23 private var reverse_sorter: AbstractSorter[E]
24
25 private var core: OrderedSet[E] = new OrderedSet[E]
26 private var crown: OrderedSet[E] = new OrderedSet[E]
27 private var border: OrderedSet[E] = new OrderedSet[E]
28
29 private var coloration_result: Map[E, Int] = new HashMap[E, Int]
30 private var conflicts_graph_cache: nullable HashMap[E, Set[E]]
31
32 init(sorter: AbstractSorter[E], reverse_sorter: AbstractSorter[E]) do
33 self.sorter = sorter
34 self.reverse_sorter = reverse_sorter
35 end
36
37 fun colorize(elements: Collection[E]): Map[E, Int] do
38 # tag each element as part of group core, crown or border
39 for e in elements do
40 tag_element(e)
41 end
42
43 #print "core: {core.join(", ")}"
44 #print "border: {border.join(", ")}"
45 #print "crown: {crown.join(", ")}"
46
47 # sort by reverse linearization order
48 reverse_sorter.sort(core)
49 reverse_sorter.sort(border)
50 reverse_sorter.sort(crown)
51
52 #print "conflicts"
53 #for k, v in conflicts_graph do
54 # if k isa MType then
55 # print "{k}: {v.join(", ")}"
56 # end
57 #end
58
59 # colorize graph
60 colorize_elements(core)
61 colorize_elements(border)
62 colorize_elements(crown)
63
64 return coloration_result
65 end
66
67 # Colorize a collection of elements
68 private fun colorize_elements(elements: Collection[E]) do
69 var min_color = 0
70
71 for element in elements do
72 var color = min_color
73 while not self.is_color_free(element, color) do
74 color += 1
75 end
76 coloration_result[element] = color
77 color = min_color
78 end
79 end
80
81 # Check if a related element to the element already use the color
82 private fun is_color_free(element: E, color: Int): Bool do
83 if conflicts_graph.has_key(element) then
84 for st in conflicts_graph[element] do
85 if coloration_result.has_key(st) and coloration_result[st] == color then return false
86 end
87 end
88 for st in self.super_elements(element) do
89 if coloration_result.has_key(st) and coloration_result[st] == color then return false
90 end
91 return true
92 end
93
94 # Tag element as core, crown or border
95 private fun tag_element(element: E) do
96 # Check if sub elements are all in single inheritance
97 var all_subelements_si = true
98 for subelem in self.sub_elements(element) do
99 if self.is_element_mi(subelem) then
100 all_subelements_si = false
101 break
102 end
103 end
104
105 # Tag as core, crown or border
106 if self.is_element_mi(element) then
107 core.add_all(self.super_elements(element))
108 core.add(element)
109 if all_subelements_si then
110 border.add(element)
111 end
112 else if not all_subelements_si then
113 core.add_all(self.super_elements(element))
114 core.add(element)
115 else
116 crown.add(element)
117 end
118 end
119
120 # Conflicts graph of elements hierarchy (two types are in conflict if they have common subelements)
121 private fun conflicts_graph: Map[E, Set[E]] do
122 if self.conflicts_graph_cache == null then
123 self.conflicts_graph_cache = new HashMap[E, HashSet[E]]
124 for t in self.core do
125 for i in self.linear_extension(t) do
126 if t == i then continue
127
128 var lin_i = self.linear_extension(i)
129
130 for j in self.linear_extension(t) do
131 if i == j or j == t then continue
132 var lin_j = self.linear_extension(j)
133
134 var d_ij = lin_i - lin_j
135 var d_ji = lin_j - lin_i
136
137 for ed1 in d_ij do
138 if not conflicts_graph_cache.has_key(ed1) then conflicts_graph_cache[ed1] = new HashSet[E]
139 # add ed1 x ed2 to conflicts graph
140 for ed2 in d_ji do conflicts_graph_cache[ed1].add(ed2)
141 end
142 for ed1 in d_ij do
143 if not conflicts_graph_cache.has_key(ed1) then conflicts_graph_cache[ed1] = new HashSet[E]
144 # add ed1 x ed2 to conflicts graph
145 for ed2 in d_ji do conflicts_graph_cache[ed1].add(ed2)
146 end
147 end
148 end
149 end
150 end
151 return conflicts_graph_cache.as(not null)
152 end
153
154 # cache for linear_extensions
155 private var linear_extensions_cache: Map[E, OrderedSet[E]] = new HashMap[E, OrderedSet[E]]
156
157 # Return a linear_extension of super_elements of the element
158 private fun linear_extension(element: E): OrderedSet[E] do
159 if not self.linear_extensions_cache.has_key(element) then
160 var lin = new OrderedSet[E]
161 lin.add(element)
162 lin.add_all(self.super_elements(element))
163 self.sorter.sort(lin)
164 self.linear_extensions_cache[element] = lin
165 end
166 return self.linear_extensions_cache[element]
167 end
168
169 # Return all super elements (directs and indirects) of an element
170 private fun super_elements(element: E): Collection[E] is abstract
171
172 # Return all sub elements (directs and indirects) of an element
173 private fun sub_elements(element: E): Collection[E] is abstract
174
175 # Is the element in multiple inheritance ?
176 private fun is_element_mi(element: E): Bool is abstract
177 end
178
179 # MClassType coloring
180 class TypeColoring
181 super AbstractColoring[MClassType]
182
183 type T: MClassType
184
185 private var mmodule: MModule
186 private var mtypes: Set[MClassType] = new HashSet[MClassType]
187
188 # caches
189 private var super_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
190 private var sub_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
191
192 init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis) do
193 super(new TypeSorter(mainmodule), new ReverseTypeSorter(mainmodule))
194 self.mmodule = mainmodule
195 self.mtypes.add_all(runtime_type_analysis.live_types)
196 self.mtypes.add_all(runtime_type_analysis.live_cast_types)
197 end
198
199 # Build type tables
200 private fun build_type_tables(mtypes: Set[T], colors: Map[T, Int]): Map[T, Array[nullable T]] do
201 var tables = new HashMap[T, Array[nullable T]]
202
203 for mtype in mtypes do
204 var table = new Array[nullable T]
205 var supers = new HashSet[T]
206 supers.add_all(self.super_elements(mtype))
207 supers.add(mtype)
208 for sup in supers do
209 var color = colors[sup]
210 if table.length <= color then
211 for i in [table.length .. color[ do
212 table[i] = null
213 end
214 end
215 table[color] = sup
216 end
217 tables[mtype] = table
218 end
219 return tables
220 end
221
222 redef fun super_elements(element) do
223 if not self.super_elements_cache.has_key(element) then
224 var supers = new HashSet[T]
225 for mtype in self.mtypes do
226 if element == mtype then continue
227 if element.is_subtype(self.mmodule, null, mtype) then
228 supers.add(mtype)
229 end
230 end
231 self.super_elements_cache[element] = supers
232 end
233 return self.super_elements_cache[element]
234 end
235
236 # Return all direct super elements of an element
237 redef fun is_element_mi(element) do
238 return self.mmodule.flatten_mclass_hierarchy[element.mclass].direct_greaters.length > 1
239 end
240
241 # Return all sub elements (directs and indirects) of an element
242 redef fun sub_elements(element) do
243 if not self.sub_elements_cache.has_key(element) then
244 var subs = new HashSet[T]
245 for mtype in self.mtypes do
246 if element == mtype then continue
247 if mtype.is_subtype(self.mmodule, null, element) then
248 subs.add(mtype)
249 end
250 end
251 self.sub_elements_cache[element] = subs
252 end
253 return self.sub_elements_cache[element]
254 end
255 end
256
257 # A sorter for linearize list of types
258 private class TypeSorter
259 super AbstractSorter[MClassType]
260
261 private var mmodule: MModule
262
263 init(mmodule: MModule) do self.mmodule = mmodule
264
265 redef fun compare(a, b) do
266 if a == b then
267 return 0
268 else if a.is_subtype(self.mmodule, null, b) then
269 return -1
270 end
271 return 1
272 end
273 end
274
275 # A sorter for reverse linearization
276 private class ReverseTypeSorter
277 super TypeSorter
278
279 redef fun compare(a, b) do
280 if a == b then
281 return 0
282 else if a.is_subtype(self.mmodule, null, b) then
283 return 1
284 end
285 return -1
286 end
287 end
288
289 # MClass coloring
290 class ClassColoring
291 super AbstractColoring[MClass]
292
293 type T: MClass
294
295 private var mmodule: MModule
296
297 # caches
298 private var super_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
299 private var parent_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
300 private var sub_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
301
302 init(mainmodule: MModule) do
303 super(new ClassSorter(mainmodule), new ReverseClassSorter(mainmodule))
304 self.mmodule = mainmodule
305 end
306
307 redef fun super_elements(element) do
308 if not self.super_elements_cache.has_key(element) then
309 var supers = new HashSet[T]
310 if self.mmodule.flatten_mclass_hierarchy.has(element) then
311 for sup in self.mmodule.flatten_mclass_hierarchy[element].greaters do
312 if element == sup then continue
313 supers.add(sup)
314 end
315 end
316 self.super_elements_cache[element] = supers
317 end
318 return self.super_elements_cache[element]
319 end
320
321 private fun parent_elements(element: T): Set[T] do
322 if not self.parent_elements_cache.has_key(element) then
323 var parents = new HashSet[T]
324 if self.mmodule.flatten_mclass_hierarchy.has(element) then
325 for parent in self.mmodule.flatten_mclass_hierarchy[element].direct_greaters do
326 if element == parent then continue
327 parents.add(parent)
328 end
329 end
330 self.parent_elements_cache[element] = parents
331 end
332 return self.parent_elements_cache[element]
333 end
334
335 # Return all sub elements (directs and indirects) of an element
336 redef fun sub_elements(element) do
337 if not self.sub_elements_cache.has_key(element) then
338 var subs = new HashSet[T]
339 if self.mmodule.flatten_mclass_hierarchy.has(element) then
340 for sub in self.mmodule.flatten_mclass_hierarchy[element].smallers do
341 subs.add(sub)
342 end
343 end
344 self.sub_elements_cache[element] = subs
345 end
346 return self.sub_elements_cache[element]
347 end
348
349 # Return all direct super elements of an element
350 redef fun is_element_mi(element) do
351 if not self.mmodule.flatten_mclass_hierarchy.has(element) then return false
352 return self.mmodule.flatten_mclass_hierarchy[element].direct_greaters.length > 1
353 end
354 end
355
356 # A sorter for linearize list of classes
357 private class ClassSorter
358 super AbstractSorter[MClass]
359
360 var mmodule: MModule
361
362 redef fun compare(a, b) do
363 if a == b then
364 return 0
365 else if self.mmodule.flatten_mclass_hierarchy[a].greaters.has(b) then
366 return -1
367 end
368 return 1
369 end
370 end
371
372 # A sorter for reverse linearization
373 private class ReverseClassSorter
374 super AbstractSorter[MClass]
375
376 var mmodule: MModule
377
378 redef fun compare(a, b) do
379 if a == b then
380 return 0
381 else if self.mmodule.flatten_mclass_hierarchy[a].greaters.has(b) then
382 return 1
383 end
384 return -1
385 end
386 end
387
388 # MProperty coloring
389 class PropertyColoring
390
391 type MPROP: MProperty
392 type MPROPDEF: MPropDef
393
394 private var class_coloring: ClassColoring
395 private var coloration_result: Map[MPROP, Int] = new HashMap[MPROP, Int]
396
397 init(class_coloring: ClassColoring) do
398 self.class_coloring = class_coloring
399 end
400
401 private fun colorize: Map[MPROP, Int] do
402 colorize_core_properties
403 colorize_crown_properties
404 return self.coloration_result
405 end
406
407 private fun build_property_tables: Map[MClass, Array[nullable MPROPDEF]] do
408 var tables = new HashMap[MClass, Array[nullable MPROPDEF]]
409
410 for mclass in self.class_coloring.coloration_result.keys do
411 var table = new Array[nullable MPROPDEF]
412 # first, fill table from parents by reverse linearization order
413 var parents = new OrderedSet[MClass]
414 parents.add_all(self.class_coloring.super_elements(mclass))
415 self.class_coloring.reverse_sorter.sort(parents)
416 for parent in parents do
417 for mproperty in self.properties(parent) do
418 var color = self.coloration_result[mproperty]
419 if table.length <= color then
420 for i in [table.length .. color[ do
421 table[i] = null
422 end
423 end
424 for mpropdef in mproperty.mpropdefs do
425 if mpropdef.mclassdef.mclass == parent then
426 table[color] = mpropdef
427 end
428 end
429 end
430 end
431
432 # then override with local properties
433 for mproperty in self.properties(mclass) do
434 var color = self.coloration_result[mproperty]
435 if table.length <= color then
436 for i in [table.length .. color[ do
437 table[i] = null
438 end
439 end
440 for mpropdef in mproperty.mpropdefs do
441 if mpropdef.mclassdef.mclass == mclass then
442 table[color] = mpropdef
443 end
444 end
445 end
446 tables[mclass] = table
447 end
448 return tables
449 end
450
451 # Colorize properties of the core hierarchy
452 private fun colorize_core_properties do
453 var mclasses = self.class_coloring.core
454 var min_color = 0
455
456 for mclass in mclasses do
457 var color = min_color
458
459 # if the class is root, get the minimal color
460 if self.class_coloring.parent_elements(mclass).length == 0 then
461 colorize_elements(self.properties(mclass), color)
462 else
463 # check last color used by parents
464 color = max_color(color, self.class_coloring.parent_elements(mclass))
465 # check max color used in conflicts
466 if self.class_coloring.conflicts_graph.has_key(mclass) then
467 color = max_color(color, self.class_coloring.conflicts_graph[mclass])
468 end
469
470 # colorize
471 colorize_elements(self.properties(mclass), color)
472 end
473 end
474 end
475
476 # Colorize properties of the crown hierarchy
477 private fun colorize_crown_properties do
478 for mclass in self.class_coloring.crown do
479 colorize_elements(self.properties(mclass), max_color(0, self.class_coloring.parent_elements(mclass)))
480 end
481 end
482
483 # Colorize a collection of properties given a starting color
484 private fun colorize_elements(elements: Collection[MPROP], start_color: Int) do
485 for element in elements do
486 if self.coloration_result.has_key(element) then continue
487 self.coloration_result[element] = start_color
488 start_color += 1
489 end
490 end
491
492 private fun max_color(min_color: Int, mclasses: Collection[MClass]): Int do
493 var max_color = min_color
494
495 for mclass in mclasses do
496 for mproperty in self.properties(mclass) do
497 var color = min_color
498 if self.coloration_result.has_key(mproperty) then
499 color = self.coloration_result[mproperty]
500 if color >= max_color then max_color = color + 1
501 end
502 end
503 end
504 return max_color
505 end
506
507 # properties cache
508 private var properties_cache: Map[MClass, Set[MPROP]] = new HashMap[MClass, Set[MPROP]]
509
510 # All 'mproperties' associated to all 'mclassdefs' of the class
511 private fun properties(mclass: MClass): Set[MPROP] do
512 if not self.properties_cache.has_key(mclass) then
513 var properties = new HashSet[MPROP]
514 var parents = self.class_coloring.super_elements(mclass)
515 for parent in parents do
516 properties.add_all(self.properties(parent))
517 end
518
519 for mclassdef in mclass.mclassdefs do
520 for mpropdef in mclassdef.mpropdefs do
521 var mproperty = mpropdef.mproperty
522 if mproperty isa MPROP then
523 properties.add(mproperty)
524 end
525 end
526 end
527 self.properties_cache[mclass] = properties
528 end
529 return properties_cache[mclass]
530 end
531 end
532
533 # MMethod coloring
534 class MethodColoring
535 super PropertyColoring
536
537 redef type MPROP: MMethod
538 redef type MPROPDEF: MMethodDef
539 end
540
541 # MAttribute coloring
542 class AttributeColoring
543 super PropertyColoring
544
545 redef type MPROP: MAttribute
546 redef type MPROPDEF: MAttributeDef
547 end
548
549 # MParameterType coloring
550 class FTColoring
551 private var class_coloring: ClassColoring
552 private var coloration_result: Map[MParameterType, Int] = new HashMap[MParameterType, Int]
553
554 init(class_coloring: ClassColoring) do
555 self.class_coloring = class_coloring
556 end
557
558 private fun colorize: Map[MParameterType, Int] do
559 colorize_core_properties
560 colorize_crown_properties
561 return self.coloration_result
562 end
563
564 # Colorize properties of the core hierarchy
565 private fun colorize_core_properties do
566 var mclasses = self.class_coloring.core
567 var min_color = 0
568
569 for mclass in mclasses do
570 var color = min_color
571
572 # if the class is root, get the minimal color
573 if self.class_coloring.parent_elements(mclass).length == 0 then
574 colorize_elements(self.fts(mclass), color)
575 else
576 # check last color used by parents
577 color = max_color(color, self.class_coloring.parent_elements(mclass))
578 # check max color used in conflicts
579 if self.class_coloring.conflicts_graph.has_key(mclass) then
580 color = max_color(color, self.class_coloring.conflicts_graph[mclass])
581 end
582 # colorize
583 colorize_elements(self.fts(mclass), color)
584 end
585 end
586 end
587
588 # Colorize properties of the crown hierarchy
589 private fun colorize_crown_properties do
590 for mclass in self.class_coloring.crown do
591 colorize_elements(self.fts(mclass), max_color(0, self.class_coloring.parent_elements(mclass)))
592 end
593 end
594
595 # Colorize a collection of properties given a starting color
596 private fun colorize_elements(elements: Collection[MParameterType], start_color: Int) do
597 for element in elements do
598 if self.coloration_result.has_key(element) then continue
599 self.coloration_result[element] = start_color
600 start_color += 1
601 end
602 end
603
604 private fun max_color(min_color: Int, mclasses: Collection[MClass]): Int do
605 var max_color = min_color
606
607 for mclass in mclasses do
608 for ft in self.fts(mclass) do
609 var color = min_color
610 if self.coloration_result.has_key(ft) then
611 color = self.coloration_result[ft]
612 if color >= max_color then max_color = color + 1
613 end
614 end
615 end
616 return max_color
617 end
618
619 # fts cache
620 private var fts_cache: Map[MClass, Set[MParameterType]] = new HashMap[MClass, Set[MParameterType]]
621
622 private fun fts(mclass: MClass): Set[MParameterType] do
623 if not self.fts_cache.has_key(mclass) then
624 var fts = new HashSet[MParameterType]
625 var mclass_type = mclass.mclass_type
626 if mclass_type isa MGenericType then
627 for ft in mclass_type.arguments do
628 fts.add(ft.as(MParameterType))
629 end
630 end
631 self.fts_cache[mclass] = fts
632 end
633 return fts_cache[mclass]
634 end
635
636 private fun build_ft_tables: Map[MClass, Array[nullable MParameterType]] do
637 var tables = new HashMap[MClass, Array[nullable MParameterType]]
638
639 for mclass in self.class_coloring.coloration_result.keys do
640 var table = new Array[nullable MParameterType]
641
642 # first, fill table from parents
643 for parent in self.class_coloring.super_elements(mclass) do
644 for ft in self.fts(parent) do
645 var color = self.coloration_result[ft]
646 if table.length <= color then
647 for i in [table.length .. color[ do
648 table[i] = null
649 end
650 end
651 table[color] = ft
652 end
653 end
654
655 # then override with local properties
656 for ft in self.fts(mclass) do
657 var color = self.coloration_result[ft]
658 if table.length <= color then
659 for i in [table.length .. color[ do
660 table[i] = null
661 end
662 end
663 table[color] = ft
664 end
665 tables[mclass] = table
666 end
667 return tables
668 end
669 end
670
671 # Utils
672
673 # An ordered set
674 private class OrderedSet[E]
675 super Array[E]
676
677 redef fun add(e) do
678 if not self.has(e) then
679 super(e)
680 end
681 end
682
683 # Return a new OrderedSet with the elements only contened in 'self' and not in 'o'
684 fun -(o: OrderedSet[E]): OrderedSet[E] do
685 var res = new OrderedSet[E]
686 for e in self do if not o.has(e) then res.add(e)
687 return res
688 end
689 end