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:
1 parent
2922af79f1
commit
81ad712475
2 files changed
+217
-111
No files matched your search
@@ -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()
|
||||||
|
|||||||
@@ -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 一致),
|
||||||
|
|||||||
Reference in new issue
Block a user