From 74ccd31c195c11d38c631828a0b8547c7c20e908 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 28 Sep 2026 18:25:57 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E8=A3=B8=E5=90=8D=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=A4=84=E7=90=86=E5=90=8C=E5=90=8D=E5=86=B2=E7=AA=81?= =?UTF-8?q?=20+=20=E5=A3=B0=E6=98=8E=E9=98=B6=E6=AE=B5=E5=A4=9A=E8=BD=AE?= =?UTF-8?q?=EF=BC=8C=E6=B6=88=E9=99=A4=E8=B7=A8=E6=A8=A1=E5=9D=97=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E8=A7=A3=E6=9E=90=E6=AE=8B=E4=BD=99=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 承接上一条(两阶段加载)后仍有 11 个模块签名解析失败,两类根因: ① 同名冲突:平坦符号表(无前缀)一个名字只能留一个条目,不同模块可用不同类型 种类声明同名符号,且谁后插入谁占住裸名 —— stdint.vp:9 `INT: t.CTypedef = t.CInt` 与 argparse.vp:33 `define INT = 1`(sig: `INT: t.CDefine = 1`)撞名 ⇒ posix 的 `oflag: INT` 解析失败;llvmlite/__function.vp:77 `class Function` 与 ast/tokens.vp:95 枚举成员 `Function: t.State` 撞名 ⇒ 5 个模块的 `func: rawptr[Function]` 失败;ast/exprs.vp:18 `class Name` 同型(ast/parser.vp)。 修法:_resolve_name 在平坦条目不是 typedef/类/枚举时,跨 _namespaces 找回同名 typedef / 类 / 枚举(注解位置只可能是这三种)。 ② 声明阶段的顺序依赖:typedef 右侧可引用其它模块的 typedef(win32base 的 `HANDLE: t.CTypedef = VOIDPTR`,VOIDPTR 在 stdint)⇒ 单轮仍与顺序有关。 修法:Phase2Translator 声明阶段多轮(未解析 typedef 的模块下一轮重 stage, 无进展即停,_CountUnresolvedTypedefs 判定),签名阶段仍是全部声明入表后统一跑。 ③ 顺带修:两阶段是两个 TypeResolver 实例,签名阶段也须注入本模块类型名 (TypeResolver.inject_known_type_names 供两阶段共用)。 验证(TransPyV 全量 127 个 sig 模拟 ①→②):commit 失败数 11 → 0; posix.open ret=i32 params=[i8*,i32]、read ret=i64 params=[i32,i8*,i64] 解析正确。 --- lib/Projectrans/Phase2Translator.py | 50 ++++++++++++++++--- lib/core/LLVMTypeMapper.py | 75 +++++++++++++++++++++-------- lib/core/SymbolTypeResolver.py | 33 ++++++++----- 3 files changed, 119 insertions(+), 39 deletions(-) diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index 0cf4031..f1e0a96 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -15,6 +15,7 @@ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_compl from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.SymbolData import ModuleSymbols from lib.includes import t from lib.core.SymbolUtils import AnnotationContainsName, ExpandSoftKeywords from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod @@ -404,6 +405,23 @@ def _ExtractCDefineConstantsFromPyi(pyi_path: str, all_dc: dict[str, object]) -> _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) +def _CountUnresolvedTypedefs(module_symbols: object) -> int: + """该模块"有值注解却解析不出类型"的 typedef 个数(供声明阶段判断是否要下一轮)。 + + typedef 右侧可以引用**其它模块**的 typedef:例 win32base 的 + `HANDLE: t.CTypedef = VOIDPTR`(VOIDPTR 在 stdint)——本模块先 stage 时右侧 + 解析为空(original_type_kind/original_class 均为 None),要等依赖模块入表后 + 重 stage 才能补全。返回 0 表示本模块声明已完整(不再需要重跑)。 + """ + miss: int = 0 + for td in getattr(module_symbols, 'typedefs', []): + if getattr(td, 'value', None) is None and getattr(td, 'annotation', None) is None: + continue + if getattr(td, 'original_type_kind', None) is None and getattr(td, 'original_class', None) is None: + miss += 1 + return miss + + def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp_dir: str, output_dir: str, include_dirs: list[str], triple: str | None, datalayout: str | None, sha1_map: dict[str, str], sig_files: dict[str, str], @@ -3990,15 +4008,33 @@ class Phase2Translator: if short_name != module_name: base_trans.translator._source_module_sig_files[short_name] = sig_path - # ① 声明阶段(全部模块) - _staged_modules = [] - for _stage_path, _stage_mod in staged: - _stage_ms = base_trans.translator.SymbolTable.StageModuleSymbols(_stage_path, _stage_mod) - if _stage_ms is not None: - _staged_modules.append((_stage_ms, _stage_mod, _stage_path)) + # ① 声明阶段(全部模块;多轮直到"本轮没有新增可解析的声明") + # 单轮仍会与模块顺序有关:typedef 右侧可以引用**其它模块**的 typedef, + # 例 win32base 的 `HANDLE: t.CTypedef = VOIDPTR`,VOIDPTR 在 stdint + # ⇒ 本模块先 stage 时右侧解析为空;下一轮把它重 stage 一遍(重 stage 覆盖 + # 登记,条目随依赖入表而变完整)。无进展即停,最多再跑一轮。 + _stage_map: dict[tuple[str, str], ModuleSymbols] = {} + _round_pending = list(staged) + _prev_miss: int | None = None + while _round_pending: + _miss_total: int = 0 + _next_round: list[tuple[str, str]] = [] + for _stage_path, _stage_mod in _round_pending: + _stage_ms = base_trans.translator.SymbolTable.StageModuleSymbols(_stage_path, _stage_mod) + if _stage_ms is None: + continue + _stage_map[(_stage_path, _stage_mod)] = _stage_ms + _miss: int = _CountUnresolvedTypedefs(_stage_ms) + _miss_total += _miss + if _miss: + _next_round.append((_stage_path, _stage_mod)) + if not _next_round or (_prev_miss is not None and _miss_total >= _prev_miss): + break + _prev_miss = _miss_total + _round_pending = _next_round # ② 签名阶段(全部模块;此时任一模块的签名都能看到全部声明) - for _commit_ms, _commit_mod, _commit_path in _staged_modules: + for (_commit_path, _commit_mod), _commit_ms in _stage_map.items(): base_trans.translator.SymbolTable.CommitModuleSymbols(_commit_ms, _commit_mod, _commit_path) # 所有 includes 模块符号已加载,现在处理包重导出 diff --git a/lib/core/LLVMTypeMapper.py b/lib/core/LLVMTypeMapper.py index ea62f90..f5750a6 100644 --- a/lib/core/LLVMTypeMapper.py +++ b/lib/core/LLVMTypeMapper.py @@ -74,7 +74,35 @@ class LLVMTypeMapper: 展开,否则会误进 fail-fast。本地导入避免与 TypeAnnotationResolver 成环。 """ from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR - return _TAR._lookup_builtins_typedef(name, self._symtab) + return _TAR._lookup_builtins_typedef(name, self._symtab) + + def _typedef_entry_to_llvm(self, entry: CTypeInfo) -> str: + """typedef 条目 → LLVM 类型串(解析不出返回 '',由调用方决定后续分支)。 + + 原为 _resolve_name 内联的 typedef 分支,抽出后供"裸名解析"与"同名冲突时 + 跨命名空间找回 typedef"两处复用(口径必须一致)。 + """ + if not entry or not entry.IsTypedef: + return '' + # 优先用 OriginalType 解析 + if entry.OriginalType: + if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.IsFuncPtr: + return 'i8*' + if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.BaseType: + # 用 CTypeRegistry 生成归一化 LLVM 字符串(void*→i8*),struct/enum 回退 ToString() + resolved: str = self._ctype_to_llvm_str(entry.OriginalType) + return resolved if resolved else entry.OriginalType.ToString() + if isinstance(entry.OriginalType, str): + # 字符串形式的 OriginalType(如 'CUnsignedInt *')需转换为 LLVM 类型字符串 + llvm_str: str = self._ctype_str_to_llvm(entry.OriginalType) + if llvm_str: + return llvm_str + # OriginalType 为空或字符串无法转换时,回退到 BaseType + if entry.BaseType and (not isinstance(entry.BaseType, (_t._CTypedef,)) or entry.PtrCount > 0): + resolved_b: str = self._ctype_to_llvm_str(entry) + if resolved_b: + return resolved_b + return '' # ================================================================== # 公共 API @@ -241,26 +269,9 @@ class LLVMTypeMapper: if node.id in self._symtab: entry: CTypeInfo = self._symtab[node.id] if entry and entry.IsTypedef: - # 优先用 OriginalType 解析 - if entry.OriginalType: - if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.IsFuncPtr: - return 'i8*' - elif isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.BaseType: - # 用 CTypeRegistry 生成归一化 LLVM 字符串(void*→i8*),struct/enum 回退 ToString() - resolved: str = self._ctype_to_llvm_str(entry.OriginalType) - if resolved: - return resolved - return entry.OriginalType.ToString() - elif isinstance(entry.OriginalType, str): - # 字符串形式的 OriginalType(如 'CUnsignedInt *')需转换为 LLVM 类型字符串 - llvm_str: str = self._ctype_str_to_llvm(entry.OriginalType) - if llvm_str: - return llvm_str - # OriginalType 为空或字符串无法转换时,回退到 BaseType - if entry.BaseType and (not isinstance(entry.BaseType, (_t._CTypedef,)) or entry.PtrCount > 0): - resolved = self._ctype_to_llvm_str(entry) - if resolved: - return resolved + resolved_td: str = self._typedef_entry_to_llvm(entry) + if resolved_td: + return resolved_td # --- 本模块声明的类型名 ------------------------------------------- # 类名:裸类引用自身即指针 —— 与 _resolve_attribute 的 '%struct.X*' 同口径 # (下游 TypeConvert._type_str_to_llvm 按短名命中已注册 struct ⇒ %"sha1.X"*)。 @@ -278,6 +289,28 @@ class LLVMTypeMapper: return f'%struct.{node.id}*' if entry_loaded.IsEnum and not entry_loaded.IsRenum: return f'i{EnumIntWidth(entry_loaded)}' + # --- 同名冲突:平坦条目被 define / 枚举成员 / 函数占位时找回类型 ---- + # 平坦表(无前缀)一个名字只能留一个条目,而不同模块可能用**不同类型种类** + # 声明同名符号(谁后插入谁占住裸名),两个真实例子: + # · stdint.vp:9 `INT: t.CTypedef = t.CInt` vs argparse.vp:33 + # `define INT = 1`(sig 里是 `INT: t.CDefine = 1`)—— 注解 `oflag: INT` + # · llvmlite/__function.vp:77 `class Function` vs ast/tokens.vp:95 + # 枚举成员 `Function: t.State` —— 注解 `func: rawptr[Function]` + # 注解位置只可能是 typedef / 类 / 枚举 ⇒ 平坦条目不是这三种时,跨命名空间 + # (各模块的声明登记在 _namespaces[模块名] 下)再找同名条目。 + _cand_sym: CTypeInfo | None + for _ns_syms in getattr(self._symtab, '_namespaces', {}).values(): + _cand_sym = _ns_syms.get(node.id) + if _cand_sym is None: + continue + if getattr(_cand_sym, 'IsTypedef', False): + _resolved_ns: str = self._typedef_entry_to_llvm(_cand_sym) + if _resolved_ns: + return _resolved_ns + elif getattr(_cand_sym, 'IsStruct', False): + return f'%struct.{node.id}*' + elif getattr(_cand_sym, 'IsEnum', False) and not getattr(_cand_sym, 'IsRenum', False): + return f'i{EnumIntWidth(_cand_sym)}' # --- builtins.vp 隐式 CTypedef ---------------------------------- # int64/uint64/size_t 等:用户文件无需 import 即用,符号表必定 miss, # 必须在此按权威定义展开(与 TypeConvert.L454-468 同一口径), diff --git a/lib/core/SymbolTypeResolver.py b/lib/core/SymbolTypeResolver.py index a0eb37d..cf4630f 100644 --- a/lib/core/SymbolTypeResolver.py +++ b/lib/core/SymbolTypeResolver.py @@ -40,17 +40,8 @@ class TypeResolver: # 构建 class_members 查找表(typedef Postdefinition 需要) class_members: dict[str, dict[str, CTypeInfo]] = {cls.name: cls.members for cls in module_symbols.classes} - # 注入"本模块声明的类型名"(函数/typedef 签名解析需要)。 - # 本模块类名在 Pass 3 插入符号表之前**不可见** ⇒ 若不注入,裸类引用 - # (rawptr[Constant] / Constant)在 _resolve_name 里解析不出,旧口径退化成 - # i32(rawptr[X] → i32*),类信息丢失后经跨模块符号表传到调用点,报错会 - # 指向源码(形参 i32* 对不上实参 %"sha1.X"*)而掩盖根因。 - # 枚举名按 type_kind == 'enum' 单独登记宽度(IsStruct 口径不含枚举)。 - self._type_mapper.set_known_type_names( - {cls.name for cls in module_symbols.classes if cls.type_kind != 'enum'}, - {cls.name: (cls.enum_width or 32) for cls in module_symbols.classes - if cls.type_kind == 'enum'}, - ) + # 注入"本模块声明的类型名"(typedef 签名解析需要) + self.inject_known_type_names(module_symbols) # 解析 typedef(按顺序记录,供后续 typedef 引用同模块已解析的 typedef) td: TypedefSymbolData @@ -59,8 +50,28 @@ class TypeResolver: self._resolved_typedefs[td.name] = td.original_class return module_symbols + def inject_known_type_names(self, module_symbols: ModuleSymbols) -> None: + """把本模块声明的类名 / 枚举宽度注入类型映射器。 + + 本模块类名在 Pass 3 插入符号表之前**不可见** ⇒ 若不注入,裸类引用 + (rawptr[Constant] / Constant)在 _resolve_name 里解析不出,旧口径退化成 + i32(rawptr[X] → i32*),类信息丢失后经跨模块符号表传到调用点,报错会 + 指向源码(形参 i32* 对不上实参 %"sha1.X"*)而掩盖根因。 + 枚举名按 type_kind == 'enum' 单独登记宽度(IsStruct 口径不含枚举)。 + + 声明阶段与签名阶段**各自**都要注入:两阶段加载用的是两个 TypeResolver + 实例(映射器随实例走),签名阶段缺了它就没有"本模块裸类名"这条来源。 + """ + self._type_mapper.set_known_type_names( + {cls.name for cls in module_symbols.classes if cls.type_kind != 'enum'}, + {cls.name: (cls.enum_width or 32) for cls in module_symbols.classes + if cls.type_kind == 'enum'}, + ) + def resolve_signatures(self, module_symbols: ModuleSymbols) -> ModuleSymbols: """阶段 2:解析函数签名与 define 值(依赖其它模块的声明已插入符号表)""" + # 本模块类型名注入(见 inject_known_type_names:两阶段是两个实例) + self.inject_known_type_names(module_symbols) # 解析函数签名 func: FuncSymbolData for func in module_symbols.functions: