"""集中化 sys.path 引导模块 在首次导入时一次性设置好编译器运行所需的 sys.path: - 项目根目录:让 ``import TransPyC``、``from StubGen import``、动态 importlib 能解析 - lib/includes:让用户注解模块中的 ``import t`` / ``import c`` 能解析 入口点(TransPyC.py / Projectrans.py)应在最开头 ``import lib._bootstrap``。 lib 内部模块也可 ``import lib._bootstrap`` 作为安全网,重复导入是无副作用的 no-op。 """ from __future__ import annotations import os import sys _BOOTSTRAPPED: bool = False def _bootstrap() -> None: global _BOOTSTRAPPED if _BOOTSTRAPPED: return _BOOTSTRAPPED = True # lib/_bootstrap.py 所在目录 _LibDir: str = os.path.dirname(os.path.abspath(__file__)) # 项目根目录 = lib 的父目录 _ProjectRoot: str = os.path.dirname(_LibDir) # lib/includes 目录(编译器内部类型系统 t.py / c.py) _IncludesDir: str = os.path.join(_LibDir, 'includes') if _ProjectRoot not in sys.path: sys.path.insert(0, _ProjectRoot) if _IncludesDir not in sys.path: sys.path.append(_IncludesDir) # ============================================================ # LLVM IR 兼容性补丁:llvmlite 不允许 void* 指针类型 # # LLVM IR 规范中不存在 void*,C 的 void* 在 IR 层对应的是 i8*。 # 但编译链(REnum / Python 推理)可能错误地构造 ir.PointerType(ir.VoidType()), # 这会导致 llc 报 "pointers to void are invalid - use i8* instead" 直接失败。 # # 这里在入口对 ir.PointerType.__new__ 和 __init__ 做 monkey patch: # 所有传入 VoidType 的 pointee(无论多少级 Ptr 嵌套)都自动替换为 i8。 # 这样 _nonlocal_types / 闭包环境 / 任何代码路径产生的 Ptr(Void) 都会被静默修正。 # ============================================================ try: import llvmlite.ir as _llvmlite_ir except Exception: # noqa: BLE001 _llvmlite_ir = None # type: ignore[assignment] if _llvmlite_ir is not None: _I8 = _llvmlite_ir.IntType(8) def _IsBadPointee(pointee) -> bool: """只在明确传入了 VoidType 或 Ptr(...Void...) 这类坏 pointee 时返回 True; None 由 llvmlite 内部 super 调用使用,不能替换!""" if pointee is None: return False cur = pointee while isinstance(cur, _llvmlite_ir.PointerType): if isinstance(getattr(cur, 'pointee', None), _llvmlite_ir.VoidType): return True cur = getattr(cur, 'pointee', None) if isinstance(pointee, _llvmlite_ir.VoidType): return True return False # llvmlite 内部:PointerType(pointee=Int8) 实际返回的是内部类 _TypedPointerType 实例, # 它的 __init__ 会 super 调 PointerType.__init__(pointee=None, addrspace), # 因此我们只在明确看到 VoidType 传入(pointee is not None)时才做替换。 def _PatchPointerInitAndNew(cls) -> None: try: orig_new = cls.__new__ orig_init = cls.__init__ except Exception: # noqa: BLE001 return if getattr(orig_init, '__void_patch_applied__', False): return def _patched_new(klass, pointee=None, addrspace=None, *args, **kwargs): if _IsBadPointee(pointee): return orig_new(klass, _I8, addrspace, *args, **kwargs) return orig_new(klass, pointee, addrspace, *args, **kwargs) def _patched_init(self_self, pointee=None, addrspace=None, *args, **kwargs): if _IsBadPointee(pointee): return orig_init(self_self, _I8, addrspace, *args, **kwargs) return orig_init(self_self, pointee, addrspace, *args, **kwargs) _patched_init.__void_patch_applied__ = True # type: ignore[attr-defined] try: cls.__new__ = _patched_new # type: ignore[method-assign] cls.__init__ = _patched_init # type: ignore[method-assign] except Exception: # noqa: BLE001 pass import inspect as _inspect # 收集所有继承自 PointerType 的类(含 _TypedPointerType) _all_ir_classes: list = [ obj for _name, obj in vars(_llvmlite_ir).items() if _inspect.isclass(obj) ] for _cls in _all_ir_classes: if issubclass(_cls, _llvmlite_ir.PointerType) or _cls is _llvmlite_ir.PointerType: _PatchPointerInitAndNew(_cls) # 同步 patch as_pointer() 的结果(它走的是 PointerType 构造器,但保险起见) _as_pointer_patched = set() def _PatchAsPointer(holder_cls) -> None: if not hasattr(holder_cls, 'as_pointer') or id(holder_cls.as_pointer) in _as_pointer_patched: return _orig = holder_cls.as_pointer def _PatchedAsPointer(self_self, *aa, **kk): res = _orig(self_self, *aa, **kk) rcur = res while isinstance(rcur, _llvmlite_ir.PointerType): if isinstance(getattr(rcur, 'pointee', None), _llvmlite_ir.VoidType): return _I8.as_pointer() rcur = getattr(rcur, 'pointee', None) return res try: holder_cls.as_pointer = _PatchedAsPointer # type: ignore[attr-defined] _as_pointer_patched.add(id(_orig)) except Exception: # noqa: BLE001 pass for _obj in _all_ir_classes: if hasattr(_obj, 'as_pointer') and callable(getattr(_obj, 'as_pointer', None)): _PatchAsPointer(_obj) _bootstrap()