Utility for generating distinct C names from Python names. Since C names can't use '.' (or unicode), some care is required to make C names generated from Python names unique. Also, we want to avoid generating overly long C names since they make the generated code harder to read.
| 4 | |
| 5 | |
| 6 | class NameGenerator: |
| 7 | """Utility for generating distinct C names from Python names. |
| 8 | |
| 9 | Since C names can't use '.' (or unicode), some care is required to |
| 10 | make C names generated from Python names unique. Also, we want to |
| 11 | avoid generating overly long C names since they make the generated |
| 12 | code harder to read. |
| 13 | |
| 14 | Note that we don't restrict ourselves to a 32-character distinguishing |
| 15 | prefix guaranteed by the C standard since all the compilers we care |
| 16 | about at the moment support longer names without issues. |
| 17 | |
| 18 | For names that are exported in a shared library (not static) use |
| 19 | exported_name() instead. |
| 20 | |
| 21 | Summary of the approach: |
| 22 | |
| 23 | * Generate a unique name prefix from suffix of fully-qualified |
| 24 | module name used for static names. If only compiling a single |
| 25 | module, this can be empty. For example, if the modules are |
| 26 | 'foo.bar' and 'foo.baz', the prefixes can be 'bar_' and 'baz_', |
| 27 | respectively. If the modules are 'bar.foo' and 'baz.foo', the |
| 28 | prefixes will be 'bar_foo_' and 'baz_foo_'. |
| 29 | |
| 30 | * Replace '.' in the Python name with '___' in the C name. (And |
| 31 | replace the unlikely but possible '___' with '___3_'. This |
| 32 | collides '___' with '.3_', but this is OK because names |
| 33 | may not start with a digit.) |
| 34 | |
| 35 | The generated should be internal to a build and thus the mapping is |
| 36 | arbitrary. Just generating names '1', '2', ... would be correct, |
| 37 | though not very usable. The generated names may be visible in CPU |
| 38 | profiles and when debugging using native debuggers. |
| 39 | """ |
| 40 | |
| 41 | def __init__(self, groups: Iterable[list[str]], *, separate: bool = False) -> None: |
| 42 | """Initialize with a list of modules in each compilation group. |
| 43 | |
| 44 | The names of modules are used to shorten names referring to |
| 45 | modules, for convenience. Arbitrary module |
| 46 | names are supported for generated names, but uncompiled modules |
| 47 | will use long names. |
| 48 | |
| 49 | If separate is True, assume separate compilation. This implies |
| 50 | that we don't have knowledge of all sources that will be linked |
| 51 | together. In this case we won't trim module prefixes, since we |
| 52 | don't have enough information to determine common module prefixes. |
| 53 | """ |
| 54 | self.module_map: dict[str, str] = {} |
| 55 | for names in groups: |
| 56 | if not separate: |
| 57 | self.module_map.update(make_module_translation_map(names)) |
| 58 | else: |
| 59 | for name in names: |
| 60 | self.module_map[name] = name + "." |
| 61 | self.translations: dict[tuple[str, str], str] = {} |
| 62 | self.used_names: set[str] = set() |
| 63 |
no outgoing calls
searching dependent graphs…