diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index 404c13b..61386d5 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -144,6 +144,46 @@ def _ReportDeclarationParseFailure(FilePath: str, Exc: BaseException, Consequenc _vlog().warning(Message, "Exception") +def PrependInheritedBlock(Gen: object, ClassName: str, Block: list) -> bool: + """把祖先字段块 `Block` 前置到 `Gen.class_members[ClassName]`(幂等,且**不删块内条目**)。 + + 为什么单独成函数:这段判据是"继承段与 body 继承前缀一一对应"的唯一保障,必须可 + 独立验证(见本仓"按'表头是否已等于块'判幂等"的三条性质)。 + + 判据与性质: + ① **不删块内条目**。块里每一项都对应 body 的一个继承槽;只要块内某项被删, + 被删项之后的继承字段的表内下标就会整体前移 k,而查询侧回退路径用 + `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型不同的槽。 + 旧实现按"名字已在整表内"过滤整块,正是这样删掉了**与子类自有字段重名**的 + 继承项(现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段 + LastBuiltinsTypedefResult 下标 32→27,写进 i32 槽 → "指针隐式转整数已禁止")。 + ② **幂等**。本函数会被多个加载路径重复调用(_LoadModuleDeclarationsFromFile / + _LoadDeclarationsFromStubLlvm / _TryLoadClassMembersFromPyi 懒加载);判据是 + "表头前 len(Block) 项的名字序列是否等于 Block 的名字序列"——相等即整块已前置, + 直接返回 False(不再前置),避免 2026-08-10 那类重复字段 + (BasicBlock: [Next, Next, Name, ...] → 与 struct elements 数量不匹配 → 偏移错乱)。 + ③ **同名并存**。子类自有字段与继承项同名时,两者都保留(定义端 + HandlesClassDef 的 body 同样是"继承前缀 + 自有段",两侧槽数一致); + 源码层面的这种重名已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下, + 此处只是不让它把布局判据带偏。 + + 已知残留:若此前只前置过**部分**祖先块(父类表当时较短),本次会整块再前置一次, + 该前缀出现重复条目。调用点上方刚对父类做过 force 重载(块内容稳定),故该情形仅在 + "父类表被中途改写"时出现,且重复条目会在成员槽失败报文的"名序"里直接可见。 + + 返回 True 表示本次真的前置了。 + """ + _Members: list = getattr(Gen, 'class_members', {}).get(ClassName) or [] + _BlockNames: list = [nm for nm, _ in Block] + if not _BlockNames: + return False + _HeadNames: list = [nm for nm, _ in _Members[:len(_BlockNames)]] + if _HeadNames == _BlockNames: + return False + getattr(Gen, 'class_members')[ClassName] = list(Block) + list(_Members) + return True + + class ImportHandle(BaseHandle): _struct_Load_cache = {} _pyi_cache = {} @@ -2511,14 +2551,19 @@ class ImportHandle(BaseHandle): # 加载路径重复调用(_LoadModuleDeclarationsFromFile / _LoadDeclarationsFromStubLlvm # / _TryLoadClassMembersFromPyi 懒加载),此前调用已前置的父类字段会被再次 # 前置 → class_members 出现重复字段(如 BasicBlock: [Next, Next, Name, ...]) - # → 与 struct elements 数量不匹配 → 属性偏移错乱。按字段名过滤已存在的 - # 父类字段;跨父类重名字段(不同父类各自声明)仍全部保留。 - _cur_member_names_pre: set = {n for n, _ in Gen.class_members.get(ClassName, [])} - _filtered_inherited: list[tuple] = [ - (nm2, tp2) for nm2, tp2 in _all_inherited if nm2 not in _cur_member_names_pre - ] - if _filtered_inherited: - Gen.class_members[ClassName] = _filtered_inherited + Gen.class_members[ClassName] + # → 与 struct elements 数量不匹配 → 属性偏移错乱。 + # 【判据改为"前置段是否已等于祖先块"(2026-09-30)】旧判据是 + # `nm2 not in {整表所有名字}`——它会把**与子类自有字段重名的继承项**删掉, + # 于是继承段与 body 的继承前缀失去一一对应:被删项**之后**的继承字段表内 + # 下标整体前移 k(k = 被删项数),而查询侧回退路径用 `base + 表内下标` + # (FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。现场: + # TypeGenCType 重复声明 5 个父类已有字段后,继承字段 LastBuiltinsTypedefResult + # 下标 32→27,写到 i32 槽 → "指针隐式转整数已禁止"。 + # 新判据(幂等且不删块内条目):只问"表头前 len(块) 项的名字序列是否等于 + # 祖先块的名字序列"——相等 ⇒ 整块已前置(本次是重复调用)⇒ 不再前置; + # 否则整块前置。**任何情况下都不删块内条目**,因此"同名自有字段 + 同名继承项" + # 会并存(body 里本来就是两个槽,定义端 HandlesClassDef 同样两者都在)。 + PrependInheritedBlock(Gen, ClassName, _all_inherited) # 父类字段已处理(可能此前已前置),跳过占位符路径避免叠加 _any_parent_loaded = True if not _any_parent_loaded and _ParentClasses: