OILS / mycpp / pass_state.py View on Github | oilshell.org

93 lines, 49 significant
1"""
2pass_state.py
3"""
4from __future__ import print_function
5
6from collections import defaultdict
7
8from mycpp.util import log
9
10_ = log
11
12
13class 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