fix(TPC): _get_member_offset 短名规范化+跨模块懒加载+fast-fail

class_members/class_vtable/class_parent 以短名注册而调用方可能传 sha1 全名,且 class_members 未随 struct 就绪,导致查表全 miss 静默返回 None → 变量 alloca 未初始化 → 运行期读 0xffffffffffffffff 崩溃(HandlesImports.HandleImport imp.names)。修复:统一短名解析、主动懒加载 class_members、找不到字段改为 fast-fail raise 暴露 class_members/struct 真实状态。
This commit is contained in:
TermiNexus committed 2026-08-10 18:09:05 +08:00
1 parent 2129430446
commit cb557c84ce
1 file changed
+35 -1
+35 -1
View File
@@ -237,6 +237,13 @@ class FuncGenMixin:
# 此处检测冲突,加载正确模块的 class_members 并以 sha1 前缀全名缓存。
_ResolvedClass: str = ClassName if ClassName else ''
if ClassName:
# 统一短名解析(治本修复 2026-08-10):struct/class_members/class_vtable/
# class_parent 均以短名注册(跨模块加载 HandlesImports L1604),而调用方
# 可能传入带 sha1 前缀的全名(如 "f47f88ae6545965a.Import"),导致下方
# 查表全部 miss 返回 None → _gep_and_Load_member 静默返回 None →
# AnnAssign InitValue 为 None → 变量 alloca 未初始化 → 运行期读垃圾指针。
ClassName = self._extract_short_name(ClassName)
_ResolvedClass = ClassName
_ClassSha1Map: dict[str, str] = getattr(self, 'class_sha1_map', {})
_ExpectedSha1: str | None = _ClassSha1Map.get(ClassName)
if _ExpectedSha1:
@@ -289,6 +296,18 @@ class FuncGenMixin:
isinstance(_first_elem.pointee, ir.IntType) and _first_elem.pointee.width == 8)
if not _has_parent or _is_vtable_ptr:
base = 1
if ClassName and ClassName not in self.class_members and self._Trans and hasattr(self._Trans, 'ImportHandler'):
# 跨模块 class_members 懒加载:struct 已从 stub 加载但 class_members
# 可能尚未从 .vpi 填充(vpi/stub 就绪时序不一致),主动按类 sha1 加载。
_Sha1sForLoad: dict[str, str] = getattr(self, 'class_sha1_map', {})
for _mod_name_lz, _mod_sha1_lz in _Sha1sForLoad.items():
try:
self._Trans.ImportHandler._TryLoadClassMembersFromPyi(
ClassName, f"{_mod_sha1_lz}.stub.ll", self)
except Exception:
continue
if ClassName in self.class_members and len(self.class_members[ClassName]) > 0:
break
if _ResolvedClass and _ResolvedClass in self.class_members:
for i, (name, _) in enumerate(self.class_members[_ResolvedClass]):
if name == field_name:
@@ -444,7 +463,22 @@ class FuncGenMixin:
if _num_inherited_final == 1:
# 单继承字段,偏移 = base(vtable 之后第一个位置)
return base + 0
return None
# fast-fail(治本修复 2026-08-10):字段未找到不再静默返回 None。
# 静默 None 会让调用方(_gep_and_Load_member L26-27)直接 return None,
# AnnAssign InitValue 变 None → 变量 alloca 未初始化 → 运行期读垃圾指针
# (如 HandlesImports.HandleImport 的 imp.names → 0xffffffffffffffff 崩溃)。
# 抛出编译错误,暴露 class_members/struct 的真实状态便于定位根因。
_members_diag: list = self.class_members.get(ClassName, []) if ClassName else []
_struct_diag: str = 'not-registered'
if ClassName and ClassName in self.structs:
_st_diag = self.structs[ClassName]
if isinstance(_st_diag, ir.IdentifiedStructType):
_struct_diag = f"{len(_st_diag.elements)} elems" if _st_diag.elements is not None else 'opaque'
_member_names_diag: list = [n for n, _ in _members_diag][:16]
raise KeyError(
f"[fast-fail] 属性 '{field_name}' 在类 '{ClassName}' 中找不到"
f"(class_members={len(_members_diag)} 个: {_member_names_diag}, struct={_struct_diag})"
)
def _resolve_class_for_var(self, var_name: str) -> str | None:
for ClassName in self.class_methods: