fix(Handles): 多继承字段展平按继承源去重,消除菱形重复

Generator 同时继承 ClassGenMixin/ClassGenEnum/ClassGenGeneric/ClassGenVTable/ClassGenDef/ClassGenBase,而前 5 个都继承 ClassGenBase → ClassGenBase 41 字段被重复展平 6 次(struct 707 元素),class_members 索引与 struct 布局错位 → 跨 Mixin 方法 this_adjust 失效 → is_exception_name 读错位内存 strcmp 越界(ASAN heap-buffer-overflow)。

修复:HandlesClassDef 与 DeclarationGenerator 的多继承展平改为递归按继承源去重(每类只展平一次,只取自有字段;同名字段如 TempDir 跨非继承 Mixin 仍各自保留,避免 2026-08-10 历史去重缺陷回归)。
This commit is contained in:
TermiNexus committed 2026-08-18 13:13:25 +08:00
1 parent 2922af79f1
commit 81ad712475
2 files changed
+217 -111

No files matched your search

+171 -100
View File
@@ -484,7 +484,12 @@ class DeclarationGenerator:
# NoVTable 类不添加 vtable 指针(即使有方法和父类) # NoVTable 类不添加 vtable 指针(即使有方法和父类)
if has_methods and is_cvtable and not base_has_vtable and not is_novtable: if has_methods and is_cvtable and not base_has_vtable and not is_novtable:
member_types.append('i8*') member_types.append('i8*')
seen_member_names: set[str] = set() # 多继承字段展平:按继承源去重(2026-08-18)——菱形继承不再重复展平
# 祖先字段(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase,
# 而它们都继承 ClassGenBase → ClassGenBase 字段被展平 6 次,struct 膨胀
# 且 this_adjust/字段 GEP 错位)。每类只展平一次,只取自有字段。
# 与 HandlesClassDef._FlattenInherited 保持同一算法。
_seen_cls: set[str] = set()
for base in node.bases: for base in node.bases:
base_name: str | None = None base_name: str | None = None
type_args: list[str] = [] type_args: list[str] = []
@@ -505,105 +510,12 @@ class DeclarationGenerator:
if isinstance(elt, ast.Name): if isinstance(elt, ast.Name):
type_args.append(elt.id) type_args.append(elt.id)
if base_name and not self._is_marker_base(base_name): if base_name and not self._is_marker_base(base_name):
base_member_types: list[str] self._FlattenInheritedDecl(base_name, _seen_cls, member_types,
base_seen: set[str] type_args or None)
base_member_types, base_seen = self._get_inherited_members(base_name) # 自身字段收集(复用 _CollectOwnMemberTypes,与 _FlattenInheritedDecl 一致)
if type_args: _own_types: list[str]
base_member_types = self._specialize_member_types(base_member_types, type_args) _own_types, has_bitfield, total_bits = self._CollectOwnMemberTypes(node)
for mt in base_member_types: member_types.extend(_own_types)
member_types.append(mt)
seen_member_names.update(base_seen)
for item in node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
seen_member_names.add(item.target.id)
init_members: list[ast.AnnAssign] = []
for item in node.body:
if isinstance(item, ast.FunctionDef) and item.name == '__init__':
for stmt in item.body:
if (isinstance(stmt, ast.AnnAssign)
and isinstance(stmt.target, ast.Attribute)
and isinstance(stmt.target.value, ast.Name)
and stmt.target.value.id == 'self'):
init_members.append(stmt)
untyped_self_members: list[ast.Assign] = []
for m_name in [m.attr for m in init_members if isinstance(m.target, ast.Attribute)]:
seen_member_names.add(m_name)
for item in node.body:
if isinstance(item, ast.FunctionDef):
if hasattr(item, 'type_params') and item.type_params:
continue
for stmt in item.body:
if (isinstance(stmt, ast.Assign)
and len(stmt.targets) == 1
and isinstance(stmt.targets[0], ast.Attribute)
and isinstance(stmt.targets[0].value, ast.Name)
and stmt.targets[0].value.id == 'self'):
attr_name: str = stmt.targets[0].attr
if attr_name not in seen_member_names:
seen_member_names.add(attr_name)
untyped_self_members.append(stmt)
for item in list(node.body) + init_members + untyped_self_members:
if isinstance(item, ast.AnnAssign):
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
if isinstance(item.target, ast.Name) and item.target.id in ('__provides__', '__requires__', '__require_must__'):
continue
bit_width: int | None = self._get_bitfield_width(item.annotation)
if bit_width is not None:
has_bitfield = True
total_bits += bit_width
else:
VarType: str = self._get_type_str(item.annotation, embedded=True)
if IsListAnnotation(item.annotation):
slice_node: ast.AST = item.annotation.slice
is_dynamic: bool = False
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
count_node: ast.AST = slice_node.elts[1]
if isinstance(count_node, ast.Constant) and count_node.value is None:
is_dynamic = True
elif not (isinstance(count_node, ast.Constant) and isinstance(count_node.value, int) and count_node.value > 0):
if not isinstance(count_node, (ast.Name, ast.BinOp)):
is_dynamic = True
elif not isinstance(slice_node, ast.Tuple):
is_dynamic = True
if is_dynamic:
elem_type: str = self._get_type_str(slice_node if not isinstance(slice_node, ast.Tuple) else slice_node.elts[0], embedded=True)
init_len: int = 0
if isinstance(item.value, ast.List):
init_len = len(item.value.elts)
elif isinstance(item.value, ast.Constant) and isinstance(item.value.value, str):
init_len = len(item.value.value) + 1
VarType = f'[{init_len} x {elem_type}]'
else:
# 固定大小数组: t.CArray[elem_type, count] → [count x elem_type]
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
elem_type: str = self._get_type_str(slice_node.elts[0], embedded=True)
count_val: int = self._get_const_int(slice_node.elts[1])
if count_val > 0:
VarType = f'[{count_val} x {elem_type}]'
elif isinstance(slice_node, (ast.Attribute, ast.Name, ast.Subscript)):
# 单参数 t.CArray[elem_type] → 指针模式
elem_type: str = self._get_type_str(slice_node, embedded=True)
VarType = f'{elem_type}*'
member_types.append(VarType)
elif isinstance(item, ast.Assign):
if (len(item.targets) == 1
and isinstance(item.targets[0], ast.Attribute)
and isinstance(item.targets[0].value, ast.Name)
and item.targets[0].value.id == 'self'):
attr_name: str = item.targets[0].attr
if attr_name in seen_member_names:
continue
InferredType: str = 'i32'
if item.value and isinstance(item.value, ast.Constant):
if isinstance(item.value.value, float):
InferredType = 'float'
elif isinstance(item.value.value, bool):
InferredType = 'i8'
elif isinstance(item.value.value, str):
InferredType = 'i8*'
member_types.append(InferredType)
else:
member_types.append('i32')
if has_bitfield: if has_bitfield:
if total_bits <= 8: if total_bits <= 8:
member_types.insert(0, 'i8') member_types.insert(0, 'i8')
@@ -776,6 +688,165 @@ class DeclarationGenerator:
base_name, child_class_name, child_struct_type, base_name, child_class_name, child_struct_type,
seen_method_names, decls) seen_method_names, decls)
def _CollectOwnMemberTypes(self, node: ast.ClassDef) -> tuple[list[str], bool, int]:
"""收集类自身声明的字段类型(不含继承字段)。
与 _generate_class_decl 的字段收集逻辑一致(AnnAssign + __init__/方法内
self.attr 赋值)。返回 (types, has_bitfield, total_bits)。
供 _FlattenInheritedDecl 按"每类只展平一次 + 只取自有字段"使用,
消除菱形继承的重复展平(如 ClassGenBase 41 字段被 ClassGenMixin/
ClassGenEnum/.../ClassGenBase 6 个来源重复展平)。
"""
member_types: list[str] = []
has_bitfield: bool = False
total_bits: int = 0
seen_member_names: set[str] = set()
for item in node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
seen_member_names.add(item.target.id)
init_members: list[ast.AnnAssign] = []
for item in node.body:
if isinstance(item, ast.FunctionDef) and item.name == '__init__':
for stmt in item.body:
if (isinstance(stmt, ast.AnnAssign)
and isinstance(stmt.target, ast.Attribute)
and isinstance(stmt.target.value, ast.Name)
and stmt.target.value.id == 'self'):
init_members.append(stmt)
untyped_self_members: list[ast.Assign] = []
for m_name in [m.attr for m in init_members if isinstance(m.target, ast.Attribute)]:
seen_member_names.add(m_name)
for item in node.body:
if isinstance(item, ast.FunctionDef):
if hasattr(item, 'type_params') and item.type_params:
continue
for stmt in item.body:
if (isinstance(stmt, ast.Assign)
and len(stmt.targets) == 1
and isinstance(stmt.targets[0], ast.Attribute)
and isinstance(stmt.targets[0].value, ast.Name)
and stmt.targets[0].value.id == 'self'):
attr_name: str = stmt.targets[0].attr
if attr_name not in seen_member_names:
seen_member_names.add(attr_name)
untyped_self_members.append(stmt)
for item in list(node.body) + init_members + untyped_self_members:
if isinstance(item, ast.AnnAssign):
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
if isinstance(item.target, ast.Name) and item.target.id in ('__provides__', '__requires__', '__require_must__'):
continue
bit_width: int | None = self._get_bitfield_width(item.annotation)
if bit_width is not None:
has_bitfield = True
total_bits += bit_width
else:
VarType: str = self._get_type_str(item.annotation, embedded=True)
if IsListAnnotation(item.annotation):
slice_node: ast.AST = item.annotation.slice
is_dynamic: bool = False
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
count_node: ast.AST = slice_node.elts[1]
if isinstance(count_node, ast.Constant) and count_node.value is None:
is_dynamic = True
elif not (isinstance(count_node, ast.Constant) and isinstance(count_node.value, int) and count_node.value > 0):
if not isinstance(count_node, (ast.Name, ast.BinOp)):
is_dynamic = True
elif not isinstance(slice_node, ast.Tuple):
is_dynamic = True
if is_dynamic:
elem_type: str = self._get_type_str(slice_node if not isinstance(slice_node, ast.Tuple) else slice_node.elts[0], embedded=True)
init_len: int = 0
if isinstance(item.value, ast.List):
init_len = len(item.value.elts)
elif isinstance(item.value, ast.Constant) and isinstance(item.value.value, str):
init_len = len(item.value.value) + 1
VarType = f'[{init_len} x {elem_type}]'
else:
# 固定大小数组: t.CArray[elem_type, count] → [count x elem_type]
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
elem_type = self._get_type_str(slice_node.elts[0], embedded=True)
count_val: int = self._get_const_int(slice_node.elts[1])
if count_val > 0:
VarType = f'[{count_val} x {elem_type}]'
elif isinstance(slice_node, (ast.Attribute, ast.Name, ast.Subscript)):
# 单参数 t.CArray[elem_type] → 指针模式
elem_type = self._get_type_str(slice_node, embedded=True)
VarType = f'{elem_type}*'
member_types.append(VarType)
elif isinstance(item, ast.Assign):
if (len(item.targets) == 1
and isinstance(item.targets[0], ast.Attribute)
and isinstance(item.targets[0].value, ast.Name)
and item.targets[0].value.id == 'self'):
attr_name: str = item.targets[0].attr
if attr_name in seen_member_names:
continue
InferredType: str = 'i32'
if item.value and isinstance(item.value, ast.Constant):
if isinstance(item.value.value, float):
InferredType = 'float'
elif isinstance(item.value.value, bool):
InferredType = 'i8'
elif isinstance(item.value.value, str):
InferredType = 'i8*'
member_types.append(InferredType)
else:
member_types.append('i32')
return member_types, has_bitfield, total_bits
def _FlattenInheritedDecl(self, cls_name: str, seen_cls: set, member_types: list[str],
type_args: list[str] | None = None) -> None:
"""按继承源去重递归展平:继承树每个类只处理一次,每类只贡献自有字段。
菱形继承(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase,
而它们都继承 ClassGenBase)会让 ClassGenBase 字段被重复展平,导致 stub
struct 与运行时 class_members 布局错位(this_adjust 失效崩溃)。本方法与
HandlesClassDef 的 _FlattenInherited 保持同一算法。
"""
if cls_name in seen_cls:
return
seen_cls.add(cls_name)
cls_node: ast.ClassDef | None = None
for n in ast.iter_child_nodes(self._pyi_tree):
if isinstance(n, ast.ClassDef) and n.name == cls_name:
cls_node = n
break
if cls_node is None:
cls_node = self._cross_module_class_defs.get(cls_name)
if cls_node is None:
# 跨模块未知类:回退到完整展平(_get_inherited_members 递归)
mts, _ = self._get_inherited_members(cls_name)
if type_args:
mts = self._specialize_member_types(mts, type_args)
member_types.extend(mts)
return
for base in cls_node.bases:
base_name: str | None = None
base_type_args: list[str] = []
if isinstance(base, ast.Attribute):
base_name = base.attr
elif isinstance(base, ast.Name):
base_name = base.id
elif isinstance(base, ast.Subscript):
if isinstance(base.value, ast.Attribute):
base_name = base.value.attr
elif isinstance(base.value, ast.Name):
base_name = base.value.id
slice_node: ast.AST = base.slice
if isinstance(slice_node, ast.Name):
base_type_args.append(slice_node.id)
elif isinstance(slice_node, ast.Tuple):
for elt in slice_node.elts:
if isinstance(elt, ast.Name):
base_type_args.append(elt.id)
if base_name and not self._is_marker_base(base_name):
self._FlattenInheritedDecl(base_name, seen_cls, member_types,
base_type_args or None)
own, _hbf, _tbits = self._CollectOwnMemberTypes(cls_node)
if type_args:
own = self._specialize_member_types(own, type_args)
member_types.extend(own)
def _get_inherited_members(self, base_name: str) -> tuple[list[str], set[str]]: def _get_inherited_members(self, base_name: str) -> tuple[list[str], set[str]]:
member_types: list[str] = [] member_types: list[str] = []
seen_names: set[str] = set() seen_names: set[str] = set()
+46 -11
View File
@@ -658,20 +658,55 @@ class ClassHandle(BaseHandle):
# 字段重复——当前项目 Mixin 为兄弟无共享祖先;若未来出现真菱形需改为 # 字段重复——当前项目 Mixin 为兄弟无共享祖先;若未来出现真菱形需改为
# "按继承源去重"而非"按字段名去重"。 # "按继承源去重"而非"按字段名去重"。
# ============================================================ # ============================================================
# 按继承源去重(2026-08-18):菱形继承(子类同时继承 A、B、D 且
# A/B 也继承 D)会让 D 的字段被重复展平。典型:Generator 同时继承
# ClassGenMixin/ClassGenEnum/ClassGenGeneric/ClassGenVTable/ClassGenDef/
# ClassGenBase,而前 5 个都继承 ClassGenBase → ClassGenBase 41 字段
# 被展平 6 次,struct 膨胀且 this_adjust/字段 GEP 全部错位。
# 修复:递归展平整个继承树,每个类只处理一次;每类只贡献
# "非祖先字段"(class_members[类] 是含祖先的展平结果,需剔除祖先
# 部分)。同名字段(如 TempDir 在 CallGenBase/TypeGenBase 各自声明)
# 因非继承关系,两个类都贡献,各自保留——符合下方 L654 注释的
# "按继承源去重"而非"按字段名去重"。
def _FlattenInherited(cls_name: str, _seen_cls: set,
_out: list, _gen) -> None:
if cls_name in _seen_cls:
return
_seen_cls.add(cls_name)
for _gp in _gen.class_parents.get(cls_name, []) or []:
_FlattenInherited(_gp, _seen_cls, _out, _gen)
# 收集该类的全部祖先字段名(含间接),剔除自身已展平结果中
# 属于祖先的部分(避免 class_members[类] 里混合的祖先字段重复)
_anc_names: set[str] = set()
_stack: list[str] = [cls_name]
_vis: set[str] = set()
while _stack:
_c = _stack.pop()
for _gp in _gen.class_parents.get(_c, []) or []:
if _gp in _vis:
continue
_vis.add(_gp)
_stack.append(_gp)
for _mn, _mt in _gen.class_members.get(_gp, []) or []:
_anc_names.add(_mn)
for _mn, _mt in _gen.class_members.get(cls_name, []) or []:
if _mn == '__vtable__':
continue
if _mn in _anc_names:
continue
_out.append((_mn, _mt))
_pd: dict = dict(_gen.class_member_defaults.get(cls_name, {}))
if _mn in _pd:
_gen.class_member_defaults[ClassName][_mn] = _pd[_mn]
_seen_cls: set[str] = set()
_flat: list[tuple] = []
for ParentClass in ParentClasses: for ParentClass in ParentClasses:
if ParentClass not in class_graph and ParentClass not in Gen.class_members: if ParentClass not in class_graph and ParentClass not in Gen.class_members:
continue continue
parent_members: list = list(Gen.class_members.get(ParentClass, [])) _FlattenInherited(ParentClass, _seen_cls, _flat, Gen)
parent_defaults: dict = dict(Gen.class_member_defaults.get(ParentClass, {})) if _flat:
inherited: list[tuple] = [] Gen.class_members[ClassName] = Gen.class_members[ClassName] + _flat
for pm_name, pm_type in parent_members:
if pm_name == '__vtable__':
continue
inherited.append((pm_name, pm_type))
if pm_name in parent_defaults:
Gen.class_member_defaults[ClassName][pm_name] = parent_defaults[pm_name]
if inherited:
Gen.class_members[ClassName] = Gen.class_members[ClassName] + inherited
# ============================================================ # ============================================================
# 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先), # 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先),
# vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 TPV 一致), # vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 TPV 一致),