diff --git a/lib/Projectrans/DeclarationGenerator.py b/lib/Projectrans/DeclarationGenerator.py index b887c5c..b38844a 100644 --- a/lib/Projectrans/DeclarationGenerator.py +++ b/lib/Projectrans/DeclarationGenerator.py @@ -484,7 +484,12 @@ class DeclarationGenerator: # NoVTable 类不添加 vtable 指针(即使有方法和父类) if has_methods and is_cvtable and not base_has_vtable and not is_novtable: 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: base_name: str | None = None type_args: list[str] = [] @@ -505,105 +510,12 @@ class DeclarationGenerator: if isinstance(elt, ast.Name): type_args.append(elt.id) if base_name and not self._is_marker_base(base_name): - base_member_types: list[str] - base_seen: set[str] - base_member_types, base_seen = self._get_inherited_members(base_name) - if type_args: - base_member_types = self._specialize_member_types(base_member_types, type_args) - for mt in base_member_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') + self._FlattenInheritedDecl(base_name, _seen_cls, member_types, + type_args or None) + # 自身字段收集(复用 _CollectOwnMemberTypes,与 _FlattenInheritedDecl 一致) + _own_types: list[str] + _own_types, has_bitfield, total_bits = self._CollectOwnMemberTypes(node) + member_types.extend(_own_types) if has_bitfield: if total_bits <= 8: member_types.insert(0, 'i8') @@ -776,6 +688,165 @@ class DeclarationGenerator: base_name, child_class_name, child_struct_type, 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]]: member_types: list[str] = [] seen_names: set[str] = set() diff --git a/lib/core/Handles/HandlesClassDef.py b/lib/core/Handles/HandlesClassDef.py index 5bb66a7..a0781ce 100644 --- a/lib/core/Handles/HandlesClassDef.py +++ b/lib/core/Handles/HandlesClassDef.py @@ -658,20 +658,55 @@ class ClassHandle(BaseHandle): # 字段重复——当前项目 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: if ParentClass not in class_graph and ParentClass not in Gen.class_members: continue - parent_members: list = list(Gen.class_members.get(ParentClass, [])) - parent_defaults: dict = dict(Gen.class_member_defaults.get(ParentClass, {})) - inherited: list[tuple] = [] - 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 + _FlattenInherited(ParentClass, _seen_cls, _flat, Gen) + if _flat: + Gen.class_members[ClassName] = Gen.class_members[ClassName] + _flat # ============================================================ # 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先), # vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 TPV 一致),