1 | """
|
2 | pass_state.py
|
3 | """
|
4 | from __future__ import print_function
|
5 |
|
6 | from collections import defaultdict
|
7 |
|
8 | from mycpp.util import log
|
9 |
|
10 | _ = log
|
11 |
|
12 |
|
13 | class Virtual(object):
|
14 | """
|
15 | See unit test for example usage.
|
16 | """
|
17 |
|
18 | def __init__(self) -> None:
|
19 | self.methods: dict[str, list[str]] = defaultdict(list)
|
20 | self.subclasses: dict[str, list[str]] = defaultdict(list)
|
21 | self.virtuals: list[tuple[str, str]] = []
|
22 | self.has_vtable: dict[str, bool] = {}
|
23 | self.can_reorder_fields: dict[str, bool] = {}
|
24 |
|
25 | # _Executor -> vm::_Executor
|
26 | self.base_class_unique: dict[str, str] = {}
|
27 |
|
28 | # These are called on the Forward Declare pass
|
29 | def OnMethod(self, class_name: str, method_name: str) -> None:
|
30 | #log('OnMethod %s %s', class_name, method_name)
|
31 |
|
32 | # __init__ and so forth don't count
|
33 | if method_name.startswith('__') and method_name.endswith('__'):
|
34 | return
|
35 |
|
36 | self.methods[class_name].append(method_name)
|
37 |
|
38 | def OnSubclass(self, base_class: str, subclass: str) -> None:
|
39 | if '::' in base_class:
|
40 | # Hack for
|
41 | #
|
42 | # class _Executor: pass
|
43 | # versus
|
44 | # class MyExecutor(vm._Executor): pass
|
45 | base_key = base_class.split('::')[1]
|
46 |
|
47 | # Fail if we have two base classes in different namespaces with the same
|
48 | # name.
|
49 | if base_key in self.base_class_unique:
|
50 | # Make sure we don't have collisions
|
51 | assert self.base_class_unique[base_key] == base_class
|
52 | else:
|
53 | self.base_class_unique[base_key] = base_class
|
54 |
|
55 | else:
|
56 | base_key = base_class
|
57 |
|
58 | self.subclasses[base_key].append(subclass)
|
59 |
|
60 | def Calculate(self) -> None:
|
61 | """
|
62 | Call this after the forward declare pass.
|
63 |
|
64 | TODO: Are there bugs based on conflicting class names?
|
65 | """
|
66 | for base_class, subclasses in self.subclasses.items():
|
67 | self.can_reorder_fields[base_class] = False
|
68 |
|
69 | for subclass in subclasses:
|
70 | self.can_reorder_fields[subclass] = False
|
71 |
|
72 | b_methods = self.methods[base_class]
|
73 | s_methods = self.methods[subclass]
|
74 | overlapping = set(b_methods) & set(s_methods)
|
75 | for method in overlapping:
|
76 | self.virtuals.append((base_class, method))
|
77 | self.virtuals.append((subclass, method))
|
78 | if overlapping:
|
79 | self.has_vtable[base_class] = True
|
80 | self.has_vtable[subclass] = True
|
81 |
|
82 | # These is called on the Decl pass
|
83 | def IsVirtual(self, class_name: str, method_name: str) -> bool:
|
84 | return (class_name, method_name) in self.virtuals
|
85 |
|
86 | def HasVTable(self, class_name: str) -> bool:
|
87 | return class_name in self.has_vtable
|
88 |
|
89 | def CanReorderFields(self, class_name: str) -> bool:
|
90 | if class_name in self.can_reorder_fields:
|
91 | return self.can_reorder_fields[class_name]
|
92 | else:
|
93 | return True # by default they can be reordered
|