| 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
|