重置上传,新增了多个标准库,开始 TransPyV 自举实验

This commit is contained in:
2026-07-19 11:38:15 +08:00
parent 796222a300
commit 4e66207ba1
1041 changed files with 6597 additions and 27814 deletions

View File

@@ -476,6 +476,7 @@ class DeclarationGenerator:
seen_member_names: set[str] = set()
for base in node.bases:
base_name: str | None = None
type_args: list[str] = []
if isinstance(base, ast.Attribute):
base_name = base.attr
elif isinstance(base, ast.Name):
@@ -485,10 +486,19 @@ class DeclarationGenerator:
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):
type_args.append(slice_node.id)
elif isinstance(slice_node, ast.Tuple):
for elt in slice_node.elts:
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)
@@ -632,8 +642,124 @@ class DeclarationGenerator:
decls.append(f'declare {ret_type} @"{method_name}"({param_str})')
else:
decls.append(f'declare {ret_type} @{method_name}({param_str})')
# 为继承但未覆写的方法生成包装声明
# 这些声明让跨模块调用能正确解析子类包装函数的签名,
# 避免 stub 缺失导致默认 i32 返回类型 → 64 位指针截断
decls.extend(self._generate_inherited_method_decls(node, class_name, struct_type_name))
return decls
def _generate_inherited_method_decls(self, node: ast.ClassDef, child_class_name: str, child_struct_type: str) -> list[str]:
"""为继承但未覆写的方法生成包装声明
在 Phase 1 stub 中声明子类继承方法的签名(与父类相同,但 self 参数为子类类型)。
这样测试模块翻译时能从 temp stub 中获取正确的返回类型,
避免 Phase 2 includes 翻译滞后导致跨模块调用使用默认 i32 返回类型。
"""
decls: list[str] = []
# 收集子类自身定义的方法名(这些不需要生成包装,子类有自己的实现)
seen_method_names: set[str] = set()
for item in node.body:
if isinstance(item, ast.FunctionDef):
seen_method_names.add(item.name)
# 沿继承链收集父类方法,近祖先优先
for base in node.bases:
base_name: str | None = None
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
if not base_name or self._is_marker_base(base_name):
continue
self._CollectInheritedWrapperDecls(
base_name, child_class_name, child_struct_type,
seen_method_names, decls)
return decls
def _CollectInheritedWrapperDecls(
self, parent_name: str, child_class_name: str, child_struct_type: str,
seen_method_names: set[str], decls: list[str]) -> None:
"""递归收集父类及其祖先的方法,为未覆写的方法生成包装声明"""
# 查找父类节点:先在当前 .pyi 中查找,找不到则查跨模块类定义映射
parent_node: ast.ClassDef | None = None
for n in ast.iter_child_nodes(self._pyi_tree):
if isinstance(n, ast.ClassDef) and n.name == parent_name:
parent_node = n
break
if parent_node is None:
parent_node = self._cross_module_class_defs.get(parent_name)
if parent_node is None:
return
# 为父类的每个方法生成包装声明
for item in parent_node.body:
if isinstance(item, ast.FunctionDef):
if hasattr(item, 'type_params') and item.type_params:
continue
method_name: str = item.name
# __before_init__ 只在自身类生成,不生成继承包装
if method_name == '__before_init__':
continue
# 跳过子类已覆写的方法,以及已处理的祖先方法
if method_name in seen_method_names:
continue
seen_method_names.add(method_name)
# 构建包装方法名:{module_sha1}.{child_class}.{method_name}
wrapper_name: str = f'{child_class_name}.{method_name}'
if self.module_sha1:
wrapper_name = f"{self.module_sha1}.{wrapper_name}"
# 解析返回类型(与父类方法相同)
ret_type: str = self._get_type_str(item.returns) if item.returns else 'void'
if not ret_type:
ret_type = 'void'
elif ret_type == 'i8*':
if item.returns is None:
ret_type = 'void'
# 构建参数列表第一个参数self替换为子类类型其余与父类相同
params: list[str] = []
for arg_idx, arg in enumerate(item.args.args):
arg_type: str
if arg_idx == 0 and arg.arg == 'self':
arg_type = f'{child_struct_type}*'
else:
if arg.annotation:
arg_type = self._get_type_str(arg.annotation)
else:
arg_type = 'i8*'
arg_type = self._decay_array_to_ptr(arg_type)
params.append(arg_type)
param_str: str = ', '.join(params) if params else ''
if wrapper_name[0].isdigit():
decls.append(f'declare {ret_type} @"{wrapper_name}"({param_str})')
else:
decls.append(f'declare {ret_type} @{wrapper_name}({param_str})')
# 递归处理祖先类
for base in parent_node.bases:
base_name: str | None = None
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
if base_name and not self._is_marker_base(base_name):
self._CollectInheritedWrapperDecls(
base_name, child_class_name, child_struct_type,
seen_method_names, decls)
def _get_inherited_members(self, base_name: str) -> tuple[list[str], set[str]]:
member_types: list[str] = []
seen_names: set[str] = set()
@@ -675,6 +801,36 @@ class DeclarationGenerator:
seen_names.add(stmt.targets[0].attr)
return member_types, seen_names
def _specialize_member_types(self, member_types: list[str], type_args: list[str]) -> list[str]:
"""将基类成员类型中的泛型参数 T 替换为具体类型参数
处理泛型基类继承(如 GSListNode[BasicBlock])时,基类 _get_inherited_members
返回的成员类型中会包含未特化的 T 引用(形式为 %"SHA1.T"*)。
替换策略:使用 i8*(不透明指针)代替 T*,而非具体类型指针。
原因:跨模块场景中,具体类型指针的 SHA1 前缀可能与导入模块不一致,
导致 TransPyC 类型转换失败。i8* 作为通用指针类型,可安全赋值给
任何 Class | t.CPtr 注解的变量,运行时行为与 T* 相同(都是指针)。
Args:
member_types: 基类成员类型字符串列表
type_args: 类型参数名列表(如 ['BasicBlock']
Returns:
特化后的成员类型字符串列表
"""
if not type_args:
return member_types
result: list[str] = []
for mt in member_types:
new_mt: str = mt
# 将 %"SHA1.T"* 替换为 i8*(不透明指针)
new_mt = re.sub(r'%"[a-f0-9]+\.T"\*', 'i8*', new_mt)
# 将 %"SHA1.T"(非指针,如 embedded struct替换为 i8*
new_mt = re.sub(r'%"[a-f0-9]+\.T"', 'i8*', new_mt)
result.append(new_mt)
return result
def _llvm_type_size_str(self, type_str: str) -> int:
"""估算 LLVM 类型字符串的大小(字节),用于 REnum 变体大小比较
@@ -839,6 +995,18 @@ class DeclarationGenerator:
base: str = self._get_type_str(annotation.value)
if base == 'i8*' and isinstance(annotation.slice, ast.Constant):
return f'[{self._get_const_int(annotation.slice)} x i8]'
# 处理 t.CPtr[Type] 语法(指向 Type 的指针)
# 例如: t.CPtr[t.CPtr] -> i8** (void**), t.CPtr[t.CInt] -> i32* (int*)
# t.CPtr[t.CPtr[t.CInt]] -> i32** (int**)
ValueIsCPtr: bool = (
(isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CPtr')
or (isinstance(annotation.value, ast.Name) and annotation.value.id == 'CPtr')
)
if ValueIsCPtr and isinstance(annotation.slice, (ast.Attribute, ast.Name, ast.Subscript)):
slice_type: str = self._get_type_str(annotation.slice, embedded=True)
if slice_type and slice_type != 'void':
return f'{slice_type}*'
return 'i8*'
if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int):
return f'[{annotation.slice.value} x {base}]'
# 处理 t.CArray[elem_type, count] 注解 → [count x elem_type]

View File

@@ -537,12 +537,32 @@ class ClassHandle(BaseHandle):
Gen.class_members[ClassName] = inherited + Gen.class_members[ClassName]
if ParentClass in Gen.class_methods:
parent_methods: list = list(Gen.class_methods[ParentClass])
# 构建子类已有方法映射(方法短名 -> 方法全名)
child_method_map: dict[str, str] = {}
for m in Gen.class_methods[ClassName]:
method_name: str = m.split('.')[-1] if '.' in m else m
child_method_map[method_name] = m
# 父类方法在前(保证 vtable 布局一致性,子类多态分派索引正确),
# 子类新增方法在后。子类覆盖的方法放在父类方法的位置(用子类实现)。
new_methods: list = []
covered_names: set[str] = set()
for pm in parent_methods:
method_name: str = pm.split('.')[-1] if '.' in pm else pm
child_method: str = f"{ClassName}.{method_name}"
if child_method not in Gen.class_methods[ClassName]:
Gen.class_methods[ClassName].append(child_method)
if method_name in child_method_map:
# 子类覆盖了父类方法 — 用子类的实现,放在父类的位置
new_methods.append(child_method_map[method_name])
covered_names.add(method_name)
else:
# 父类独有方法 — 子类未覆盖,注册子类包装器
child_method: str = f"{ClassName}.{method_name}"
new_methods.append(child_method)
Gen.register_method(ClassName, child_method)
# 添加子类新增方法(不在父类中的)
for m in Gen.class_methods[ClassName]:
method_name: str = m.split('.')[-1] if '.' in m else m
if method_name not in covered_names:
new_methods.append(m)
Gen.class_methods[ClassName] = new_methods
# 提取首个裸字符串字面量作为结构体 __doc__ docstring
if (Node.body and isinstance(Node.body[0], ast.Expr)
and isinstance(Node.body[0].value, ast.Constant)
@@ -666,7 +686,19 @@ class ClassHandle(BaseHandle):
例如 Label 继承 Widget.place生成 Label.place 函数,
内部将 self 从 Label* bitcast 为 Widget*,然后调用 Widget.place。
"""
methods: list = Gen.class_methods.get(ClassName, [])
# 收集本类方法 + 沿继承链收集父类方法名
# 原代码只遍历 class_methods[ClassName],遗漏父类独有方法(如 __enter__
# 导致子类调用继承的方法时链接失败undefined reference
methods: list = list(Gen.class_methods.get(ClassName, []))
p: str | None = Gen.class_parent.get(ClassName) if hasattr(Gen, 'class_parent') else None
while p:
parent_methods: list = Gen.class_methods.get(p, [])
for pm in parent_methods:
pm_short: str = pm.split('.')[-1] if '.' in pm else pm
# 避免重复:如果子类已覆写同名方法,不添加
if not any(m.split('.')[-1] == pm_short for m in methods):
methods.append(pm)
p = Gen.class_parent.get(p)
if not methods:
return
if ClassName not in Gen.structs:

View File

@@ -792,7 +792,10 @@ class ExprBuiltinHandle(BaseHandle):
elif dst.type.width > 64:
dst = Gen.builder.trunc(dst, ir.IntType(64), name="trunc_memset_dst")
dst = Gen.builder.inttoptr(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_int2ptr")
if isinstance(val.type, ir.IntType) and val.type.width != 32:
if isinstance(val.type, ir.PointerType):
# 0 字面量被推断为 null 指针i8*memset 第二参数需要 i32
val = ir.Constant(ir.IntType(32), 0)
elif isinstance(val.type, ir.IntType) and val.type.width != 32:
if val.type.width < 32:
val = Gen.builder.zext(val, ir.IntType(32), name="zext_memset_val")
else:

View File

@@ -1872,69 +1872,180 @@ class ExprCallHandle(BaseHandle):
self._check_eh_return(result, FuncName, Gen, called_func=func)
return result
@staticmethod
def _has_cvtable_decorator(node: ast.ClassDef) -> bool:
"""检查 ClassDef 是否有 @t.CVTable 装饰器(或 @CVTable"""
for decorator in node.decorator_list:
if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'CVTable':
return True
if isinstance(decorator, ast.Call):
inner = decorator.func
if isinstance(inner, ast.Attribute) and getattr(inner.value, 'id', None) == 't' and inner.attr == 'CVTable':
return True
if isinstance(inner, ast.Name) and inner.id == 'CVTable':
return True
if isinstance(decorator, ast.Name) and decorator.id == 'CVTable':
return True
return False
@staticmethod
def _has_novtable_decorator(node: ast.ClassDef) -> bool:
"""检查 ClassDef 是否有 @t.NoVTable 装饰器"""
for decorator in node.decorator_list:
if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'NoVTable':
return True
if isinstance(decorator, ast.Name) and decorator.id == 'NoVTable':
return True
return False
def _try_register_vtable_ondemand(self, ClassName: str, class_sha1: str, Gen: "LlvmGeneratorMixin") -> None:
"""按需 vtable 注册:从 .pyi stub 检查 @t.CVTable 并注册到 vtable 集合。
当 vcall 决策点发现 ClassName 不在 vtable 集合中时调用。
读取 temp/{sha1}.pyi 文件,查找 ClassName 的 ClassDef
检查 @t.CVTable 装饰器,注册 vtable 和方法列表。
沿继承链递归收集方法,保证 vtable 布局与 prescan 的 _EmitClassLlvm 一致:
- 基类方法在前(固定索引,保证多态分派正确)
- 子类新增方法在后
- 子类覆盖的方法放在父类的位置(用子类实现)
与 prescan 的 _resolve_class_flags 逻辑一致:
- 类自身有 @t.CVTable → 注册
- 类自身有 @t.NoVTable → 不注册
- 类继承 @t.CVTable 基类(且无 @t.NoVTable→ 注册(自动启用 CVTable
"""
try:
import_handler = self.Trans.ImportHandler
project_root = import_handler._find_project_root()
pyi_path = os.path.join(project_root, 'temp', f'{class_sha1}.pyi')
if not os.path.isfile(pyi_path):
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
# 缓存已读取的 .pyi 文件sha1 -> class_map
pyi_cache: dict[str, dict[str, ast.ClassDef]] = {}
def load_pyi(sha1: str) -> dict[str, ast.ClassDef] | None:
if sha1 in pyi_cache:
return pyi_cache[sha1]
pyi_path = os.path.join(project_root, 'temp', f'{sha1}.pyi')
if not os.path.isfile(pyi_path):
return None
with open(pyi_path, 'r', encoding='utf-8') as f:
pyi_code = f.read()
tree = ast.parse(pyi_code)
cmap: dict[str, ast.ClassDef] = {}
for node in ast.iter_child_nodes(tree):
if isinstance(node, ast.ClassDef):
cmap[node.name] = node
pyi_cache[sha1] = cmap
return cmap
def collect_class_methods(class_name: str, sha1: str, visited: set[str]) -> tuple[list[str], bool, bool, str | None]:
"""递归收集类的方法列表(基类方法在前,子类方法在后)。
Returns:
(methods, is_cvtable, is_novtable, parent_name)
"""
if class_name in visited:
return ([], False, False, None)
visited.add(class_name)
class_map = load_pyi(sha1)
if class_map is None:
return ([], False, False, None)
target_node = class_map.get(class_name)
if target_node is None:
return ([], False, False, None)
is_cvtable = self._has_cvtable_decorator(target_node)
is_novtable = self._has_novtable_decorator(target_node)
# 收集自身方法
self_methods: list[str] = []
for item in target_node.body:
if isinstance(item, ast.FunctionDef):
method_name = item.name
if method_name in ('__new__', '__init__', '__before_init__'):
continue
if method_name == '__call__':
self_methods.append(f"{class_name}.__call__")
else:
self_methods.append(f"{class_name}.{method_name}")
# 收集基类方法
parent_methods: list[str] = []
parent_name: str | None = None
for base in target_node.bases:
base_name: str | None = None
if hasattr(base, 'id'):
base_name = base.id
elif hasattr(base, 'attr'):
base_name = base.attr
if not base_name or base_name in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'):
continue
if parent_name is None:
parent_name = base_name
# 检查同文件内的基类
base_node = class_map.get(base_name)
if base_node is not None:
base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, sha1, visited)
if base_novtable:
pass # 父类是 NoVTable不继承 vtable
elif base_cvtable:
is_cvtable = True
parent_methods.extend(base_methods)
else:
# 跨模块基类 — 查找基类的 sha1
base_sha1 = struct_sha1_map.get(base_name)
if base_sha1:
base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, base_sha1, visited)
if base_novtable:
pass
elif base_cvtable:
is_cvtable = True
parent_methods.extend(base_methods)
else:
# 无法找到基类的 sha1
if base_name in Gen._cross_module_novtable:
pass # 父类是 NoVTable不继承 vtable
# 合并方法列表:基类方法在前,子类方法在后
# 子类覆盖的方法放在父类的位置
child_method_map: dict[str, str] = {}
for m in self_methods:
mn = m.split('.')[-1] if '.' in m else m
child_method_map[mn] = m
merged: list[str] = []
covered_names: set[str] = set()
for pm in parent_methods:
mn = pm.split('.')[-1] if '.' in pm else pm
if mn in child_method_map:
merged.append(child_method_map[mn])
covered_names.add(mn)
else:
# 父类独有方法 — 用子类名(虚分派时函数查找会沿继承链回退)
merged.append(f"{class_name}.{mn}")
for m in self_methods:
mn = m.split('.')[-1] if '.' in m else m
if mn not in covered_names:
merged.append(m)
return (merged, is_cvtable, is_novtable, parent_name)
methods, is_cvtable, is_novtable, parent_name = collect_class_methods(ClassName, class_sha1, set())
if is_novtable:
return
with open(pyi_path, 'r', encoding='utf-8') as f:
pyi_code = f.read()
pyi_tree = ast.parse(pyi_code)
# 查找目标 ClassDef
target_node: ast.ClassDef | None = None
for node in ast.iter_child_nodes(pyi_tree):
if isinstance(node, ast.ClassDef) and node.name == ClassName:
target_node = node
break
if target_node is None:
return
# 检查 @t.CVTable 装饰器
is_cvtable: bool = False
for decorator in target_node.decorator_list:
if isinstance(decorator, ast.Attribute) and getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'CVTable':
is_cvtable = True
break
if isinstance(decorator, ast.Call):
inner = decorator.func
if isinstance(inner, ast.Attribute) and getattr(inner.value, 'id', None) == 't' and inner.attr == 'CVTable':
is_cvtable = True
break
if isinstance(inner, ast.Name) and inner.id == 'CVTable':
is_cvtable = True
break
if isinstance(decorator, ast.Name) and decorator.id == 'CVTable':
is_cvtable = True
break
if not is_cvtable:
return
# 注册到 vtable 集合(共享集合)
Gen._cross_module_vtable_classes.add(ClassName)
Gen.class_vtable.add(ClassName)
# 预注册方法列表(与 prescan 逻辑一致)
if ClassName not in Gen.class_methods:
Gen.class_methods[ClassName] = []
for item in target_node.body:
if isinstance(item, ast.FunctionDef):
method_name = item.name
# 构造函数不放入 vtable避免子类与基类 vtable 大小不一致破坏多态分派)
if method_name in ('__new__', '__init__', '__before_init__'):
continue
if method_name == '__init__':
full_name = f"{ClassName}.__init__"
elif method_name == '__call__':
full_name = f"{ClassName}.__call__"
else:
full_name = f"{ClassName}.{method_name}"
if full_name not in Gen.class_methods[ClassName]:
Gen.class_methods[ClassName].append(full_name)
# 设置方法列表(与 prescan 逻辑一致:基类方法在前
Gen.class_methods[ClassName] = methods
# 设置 class_parent用于虚分派时的函数查找回退
if parent_name and ClassName not in Gen.class_parent:
Gen.class_parent[ClassName] = parent_name
except Exception:
pass

View File

@@ -1601,6 +1601,15 @@ class FunctionHandle(BaseHandle):
}
if IsExternFunc or IsStateFunc:
return
# 治本修复:如果函数已经有 bodyblocks 不为空),跳过重复编译。
# 否则会导致同一函数被追加第二个 entry blockllvmlite 自动重命名为 entry.1
# 产生非法 IR两个 entry block + 第二个 block 无前驱成为死代码。
# 触发场景:泛型特化路径 _EmitGenericSpecialization 先调用 _EmitFunctionForwardDeclLlvm
# 再调用 _EmitFunctionLlvm若特化在多个调用点被重复触发如 list[str] 在 split/upper 等多处使用),
# 同一 MangledName 会进入此函数两次,第二次会重复追加 entry block。
existing_blocks: list = getattr(func, 'blocks', [])
if existing_blocks:
return func
EntryBlock = func.append_basic_block(name="entry")
Gen.builder = ir.IRBuilder(EntryBlock)
Gen.func = func
@@ -1612,6 +1621,15 @@ class FunctionHandle(BaseHandle):
# 保存当前的 var_type_info以便函数结束时恢复
saved_var_type_info = Gen.var_type_info.copy()
saved_var_type_assignments = Gen.var_type_assignments.copy()
# 治本修复:保存并清空 _local_heap_ptrs / _var_to_heap_ptr。
# 当 with 上下文中的代码(如 s1.split(','))触发内联编译 list[str].__new__ 时,
# 若不清空__new__ 方法会继承外层 with 的 _local_heap_ptrs含 %call_enter 条目),
# 方法结束时 _emit_local_heap_frees 会尝试 bitcast/free %call_enter
# 但该值在 __new__ 作用域中不存在 → llc 报错 'use of undefined value'。
saved_local_heap_ptrs = list(Gen._local_heap_ptrs)
saved_var_to_heap_ptr = dict(Gen._var_to_heap_ptr)
Gen._local_heap_ptrs = []
Gen._var_to_heap_ptr = {}
# 函数内部定义的变量会在赋值时添加到 var_type_info 中
# 这样函数内部定义的元类型变量(如 a = t.CInt32T就能被正确处理
for stmt in Node.body:
@@ -1810,8 +1828,9 @@ class FunctionHandle(BaseHandle):
Gen.var_type_info = saved_var_type_info
Gen.var_type_assignments = saved_var_type_assignments
Gen._stop_iter_flag_param = None
Gen._local_heap_ptrs = []
Gen._var_to_heap_ptr = {}
# 恢复外层作用域的 _local_heap_ptrs / _var_to_heap_ptr
Gen._local_heap_ptrs = saved_local_heap_ptrs
Gen._var_to_heap_ptr = saved_var_to_heap_ptr
Gen._variadic_info = None
self.Trans._CurrentCpythonObjectClass = saved_cpython_class
self.Trans.CurrentCReturnTypes = None

View File

@@ -375,10 +375,22 @@ class ImportHandle(BaseHandle):
SearchExtensions = ['.pyi', '.py']
sub_path_base = os.path.join(parent_dir, node.module)
found_sub = False
# 治本修复:用包目录名(如 'json')而非 register_module_name当前编译模块名如 '_dict')。
# 当 _dict.py 导入 json 包,递归处理 json/__init__.py 中的 `from .__parser import parse` 时,
# register_module_name='_dict' 会导致 actual_module='_dict.__parser'
# 使 _mangle_func_name 查找失败回退到 self.module_sha1生成错误的 mangled name。
# 包名推导优先级:
# 1. module_name如果它在 ModuleSha1Map 中,说明是已注册的包名,如 'json'
# — 处理 sha1 命名的 .pyi 文件parent_dir 是 temp 目录时)
# 2. os.path.basename(parent_dir)(原始路径推导,如 'json'
if module_name and module_name in Gen.ModuleSha1Map:
pkg_basename = module_name
else:
pkg_basename = os.path.basename(parent_dir)
for ext in SearchExtensions:
candidate = sub_path_base + ext
if os.path.isfile(candidate):
actual_module = f"{register_module_name}.{node.module}" if register_module_name else node.module
actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module
self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen)
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
@@ -392,7 +404,7 @@ class ImportHandle(BaseHandle):
break
# SHA1 map 回退
if not found_sub:
actual_module = f"{register_module_name}.{node.module}" if register_module_name else node.module
actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module)
if target_sha1:
@@ -413,12 +425,18 @@ class ImportHandle(BaseHandle):
SearchExtensions = ['.pyi', '.py']
# Load __init__.py to make package-level names available
self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name)
# 治本修复:用包目录名(如 'json')而非 register_module_name当前编译模块名如 '_dict')。
# 同 if node.module 分支的修复逻辑。
if module_name and module_name in Gen.ModuleSha1Map:
pkg_basename = module_name
else:
pkg_basename = os.path.basename(mod_dir)
for alias in node.names:
found_alias = False
for ext in SearchExtensions:
sub_path = os.path.join(mod_dir, alias.name + ext)
if os.path.isfile(sub_path):
sub_ModulePath = f"{register_module_name}.{alias.name}" if register_module_name else alias.name
sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(sub_ModulePath)
@@ -430,7 +448,7 @@ class ImportHandle(BaseHandle):
break
# SHA1 map 回退
if not found_alias:
sub_ModulePath = f"{register_module_name}.{alias.name}" if register_module_name else alias.name
sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name)
if target_sha1:
@@ -1538,6 +1556,23 @@ class ImportHandle(BaseHandle):
Gen.class_member_bitoffsets[ClassName] = {}
if ClassName not in Gen.class_methods:
Gen.class_methods[ClassName] = []
# 先计算 source_sha1确保首次 _TryLoadStructFromStub 调用就能使用正确的 sha1
# 优先使用传入的 source_sha1来自 _TryLoadClassMembersFromPyi 的直接调用,
# 当前模块未直接导入定义模块时 module_name 查找会失败,需依赖传入的 source_sha1
if not source_sha1:
if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map:
source_sha1 = Gen.ModuleSha1Map[actual_module_name]
elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map:
source_sha1 = Gen.ModuleSha1Map[module_name]
# 提前注册 struct使用导入的 source_sha1 创建 opaque struct
# 避免后续 _TryLoadStructFromStub 触发嵌套编译时StructGen 用本地 sha1
# 创建缺少基类字段(如 GSListNode.Next的本地类型定义。
Gen._get_or_create_struct(ClassName, source_sha1=source_sha1, packed=IsPacked)
# Track which module defines this class (for cross-module name mangling)
if source_sha1:
if not hasattr(Gen, 'class_sha1_map'):
Gen.class_sha1_map = {}
Gen.class_sha1_map[ClassName] = source_sha1
# --- 预扫描预注册:修复编译时序问题 ---
# _TryLoadStructFromStub → _parse_llvm_type → _specialize_generic_class
# 可能触发 list[str].__new__ 编译(其中调用 pool.alloc(48)
@@ -1560,24 +1595,10 @@ class ImportHandle(BaseHandle):
if _has_methods_prescan and IsCVTable:
Gen._cross_module_vtable_classes.add(ClassName)
Gen.class_vtable.add(ClassName)
# 先计算 source_sha1确保首次 _TryLoadStructFromStub 调用就能使用正确的 sha1
# 优先使用传入的 source_sha1来自 _TryLoadClassMembersFromPyi 的直接调用,
# 当前模块未直接导入定义模块时 module_name 查找会失败,需依赖传入的 source_sha1
if not source_sha1:
if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map:
source_sha1 = Gen.ModuleSha1Map[actual_module_name]
elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map:
source_sha1 = Gen.ModuleSha1Map[module_name]
# 首次加载就传入 source_sha1避免从其他模块的 output stub 中加载到错误的结构体定义
# (如 HandlesExprCall.py 的 output stub 中 43f27dda2e5b5923.Value 只有 5 字段,
# 而正确的 aaf8ba449d1e64c1.Value 有 6 字段)
self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1)
# Track which module defines this class (for cross-module name mangling)
if source_sha1:
if not hasattr(Gen, 'class_sha1_map'):
Gen.class_sha1_map = {}
Gen.class_sha1_map[ClassName] = source_sha1
Gen._get_or_create_struct(ClassName, source_sha1=source_sha1, packed=IsPacked)
# 如果 _get_or_create_struct 因 sha1 冲突创建了新的 opaque struct
# 需用 source_sha1 重新加载正确的 stub body。
if source_sha1:
@@ -1727,11 +1748,32 @@ class ImportHandle(BaseHandle):
# 直接从 AST 提取泛型基类名(如 GSListNode[Value] -> GSListNode
# 后续继承字段展平逻辑会从 .pyi 加载该基类的字段。
if isinstance(_base.value, ast.Name):
_base_name = _base.value.id
_base_short_name: str = _base.value.id
elif isinstance(_base.value, ast.Attribute):
_base_name = _base.value.attr
_base_short_name: str = _base.value.attr
else:
continue
# 尝试手动构造特化名并检查是否已有特化 class_members。
# 这解决了 _ResolveGenericSlice 失败但特化已通过其他路径
# (如 _parse_llvm_type触发的情况。特化 class_members 中
# 的 T 已被正确替换为具体类型(如 BasicBlock避免使用
# 未特化的 GSListNode 导致 T* 占位类型残留。
_slice_name: str | None = None
if isinstance(_base.slice, ast.Name):
_slice_name = _base.slice.id
elif isinstance(_base.slice, ast.Attribute):
_slice_name = _base.slice.attr
elif isinstance(_base.slice, ast.Subscript):
# 嵌套泛型(如 GSList[GSListNode[T]]),暂不处理
pass
_manual_spec_name: str | None = None
if _slice_name is not None:
_manual_spec_name = f"{_base_short_name}[{_slice_name}]"
if _manual_spec_name is not None and _manual_spec_name in Gen.class_members:
# 特化 class_members 已存在直接使用T 已被替换)
_base_name = _manual_spec_name
else:
_base_name = _base_short_name
else:
continue
if _base_name == ClassName:
@@ -1960,6 +2002,17 @@ class ImportHandle(BaseHandle):
st.set_body(*elem_types)
if is_packed:
Gen.class_packed.add(class_name)
# 同步设置短名 struct 的 body当存在 sha1 冲突时_get_or_create_struct
# 返回 full_key struct但代码可能通过短名引用旧 struct由 setup_from_symbol_table
# 早期创建的 opaque 占位符,使用本地 sha1 前缀)。需要同步设置短名 struct
# 的 body确保 GEP 指令能正确访问字段(修复跨模块类型定义冲突导致的
# 字段索引错位问题)。
short_st = Gen.structs.get(class_name)
if short_st is not None and short_st is not st and isinstance(short_st, ir.IdentifiedStructType) and short_st.is_opaque:
try:
short_st.set_body(*elem_types)
except Exception:
pass # set_body 可能因已设置而失败,忽略
except Exception as _e:
if _config_mode == "strict":
raise
@@ -2028,48 +2081,75 @@ class ImportHandle(BaseHandle):
return self.Trans._BuildScalarConstantUnified(value_node, target_type, Gen)
def _LookupStubFuncType(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None:
"""从 stub.ll 文件中查找指定函数的 LLVM 类型"""
"""从 stub.ll 文件中查找指定函数的 LLVM 类型
扫描 output/ 和 temp/ 两个目录的 .stub.ll 文件。
关键修复output/Phase 2 完整结果)必须覆盖 temp/Phase 1 不完整结果),
否则继承包装方法(在 Phase 2 的 _generate_inherited_method_wrappers 中生成)
将无法被解析,导致跨模块调用使用默认 i32 返回类型 → 64 位指针截断。
"""
if not getattr(self, '_stub_func_cache', None):
self._stub_func_cache = {}
ProjectRoot = self._find_project_root()
temp_dir = os.path.join(ProjectRoot, 'temp')
if not os.path.isdir(temp_dir):
output_dir = os.path.join(ProjectRoot, 'output')
# 文件数签名:检测 stub 新增(增量编译时 output 逐步填充完整签名)
output_count = sum(1 for f in os.listdir(output_dir) if f.endswith('.stub.ll')) if os.path.isdir(output_dir) else 0
temp_count = sum(1 for f in os.listdir(temp_dir) if f.endswith('.stub.ll')) if os.path.isdir(temp_dir) else 0
signature = (output_count, temp_count)
if getattr(self, '_stub_func_cache_signature', None) != signature or not self._stub_func_cache:
# 签名变化或缓存为空,重建缓存
self._stub_func_cache = {}
self._stub_func_cache_signature = signature
if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir):
return None
if func_name not in self._stub_func_cache:
for filename in os.listdir(temp_dir):
if not filename.endswith('.stub.ll'):
# 先扫描 temp/Phase 1不完整签名再扫描 output/Phase 2完整签名覆盖之
# 这样 output 中存在的继承包装方法会覆盖 temp 中的同名旧声明(若有),
# 也填补 temp 中缺失的函数声明
for scan_dir in [temp_dir, output_dir]:
if not os.path.isdir(scan_dir):
continue
stub_path = os.path.join(temp_dir, filename)
source_sha1 = filename.replace('.stub.ll', '')
try:
with open(stub_path, 'r', encoding='utf-8') as f:
content = f.read()
for line in content.splitlines():
stripped = line.strip()
if not stripped.startswith('declare '):
continue
name_match = stripped.split('@', 1)
if len(name_match) < 2:
continue
stub_func_name = name_match[1].split('(', 1)[0].strip().strip('"')
match = re.match(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$', stripped)
if match:
stub_ret = match.group(1).strip()
stub_params = match.group(3).strip()
self._stub_func_cache[stub_func_name] = (stub_ret, stub_params, '...' in stub_params, source_sha1)
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
for filename in os.listdir(scan_dir):
if not filename.endswith('.stub.ll'):
continue
stub_path = os.path.join(scan_dir, filename)
# 预检查文件存在性os.listdir 与 open 之间文件可能被并发写入删除
if not os.path.isfile(stub_path):
continue
source_sha1 = filename.replace('.stub.ll', '')
try:
with open(stub_path, 'r', encoding='utf-8') as f:
content = f.read()
for line in content.splitlines():
stripped = line.strip()
if not stripped.startswith('declare '):
continue
name_match = stripped.split('@', 1)
if len(name_match) < 2:
continue
stub_func_name = name_match[1].split('(', 1)[0].strip().strip('"')
match = re.match(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$', stripped)
if match:
stub_ret = match.group(1).strip()
stub_params = match.group(3).strip()
self._stub_func_cache[stub_func_name] = (stub_ret, stub_params, '...' in stub_params, source_sha1)
except FileNotFoundError:
continue
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
if func_name not in self._stub_func_cache:
return None
@@ -2110,7 +2190,8 @@ class ImportHandle(BaseHandle):
def _strip_llvm_param_name(self, param_str: str) -> str:
"""剥离 LLVM IR 参数字符串中的参数名,仅保留类型部分"""
param_str = param_str.strip()
result = re.sub(r'\s+%[\w.]+\s*$', '', param_str)
# 支持 %name 和 %"quoted.name" 两种参数名形式
result = re.sub(r'\s+%("[^"]*"|[\w.]+)\s*$', '', param_str)
return result.strip()
def _parse_simple_llvm_type(self, type_str: str) -> ir.Type | None:
@@ -2573,6 +2654,12 @@ class ImportHandle(BaseHandle):
existing = Gen.structs.get(struct_name)
if existing is not None:
return existing
# 尝试用 source_sha1 前缀的全名查找
if actual_source_sha1:
full_key = f"{actual_source_sha1}.{struct_name}"
existing_full = Gen.structs.get(full_key)
if existing_full is not None:
return existing_full
return None
return ir.IntType(32)

View File

@@ -856,9 +856,10 @@ class HandlesTypeMerge(BaseHandle):
if TypeItem.OriginalType:
Result.OriginalType = TypeItem.OriginalType
continue
# 统计 t.CPtrCVoid + PtrCount=1
# 统计 t.CPtrCVoid + PtrCount>0
# 注意: t.CPtr[t.CPtr] 的 PtrCount=2双重指针需要完整累加
if TypeItem.PtrCount > 0 and TypeItem.BaseType and (TypeItem.BaseType is t.CVoid or isinstance(TypeItem.BaseType, t.CVoid)):
cptr_count += 1
cptr_count += TypeItem.PtrCount
# BaseType 保持不变,只计数,后续统一累加
if TypeItem.IsRenum and TypeItem.Name:
Result.IsRenum = True

View File

@@ -8,6 +8,28 @@ import llvmlite.ir as ir
class WithHandle(BaseHandle):
def _ResolveInheritedMethod(self, ClassName: str, MethodName: str, Gen: "Translator.LlvmGen") -> tuple[str | None, "ir.Function | None"]:
"""沿继承链查找方法,返回 (定义方法的类名, 函数对象)
当子类未覆写 __enter__/__exit__ 等方法时,沿 Gen.class_parent 链查找父类实现。
_generate_inherited_method_wrappers 只为 class_methods[ClassName] 中的方法生成包装,
不包括父类独有方法,因此 with 语句需要单独处理继承查找。
"""
# 先检查子类自身
FullName: str = f'{ClassName}.{MethodName}'
func = Gen._find_function(FullName) if hasattr(Gen, '_find_function') else Gen.functions.get(FullName)
if func:
return (ClassName, func)
# 沿继承链查找
p: str | None = Gen.class_parent.get(ClassName) if hasattr(Gen, 'class_parent') else None
while p:
parent_full: str = f'{p}.{MethodName}'
parent_func = Gen._find_function(parent_full) if hasattr(Gen, '_find_function') else Gen.functions.get(parent_full)
if parent_func:
return (p, parent_func)
p = Gen.class_parent.get(p)
return (None, None)
def _HandleWithLlvm(self, Node: ast.With) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
for item in Node.items:
@@ -60,10 +82,16 @@ class WithHandle(BaseHandle):
ctx_is_var_ref: bool = isinstance(context_expr, ast.Name)
enter_result: ir.Value | None = None
if ClassName:
EnterFuncName: str = f'{ClassName}.__enter__'
if EnterFuncName in Gen.functions:
# 沿继承链查找 __enter__子类可能未覆写使用父类实现
enter_class, enter_func = self._ResolveInheritedMethod(ClassName, '__enter__', Gen)
if enter_func is not None:
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
enter_call: ir.CallInstr = Gen.builder.call(Gen.functions[EnterFuncName], [ctx_Loaded], name="call_enter")
# 父类方法需要 self 为父类指针类型bitcast 子类指针
if enter_class != ClassName and enter_class in Gen.structs:
parent_ptr_type = ir.PointerType(Gen.structs[enter_class])
if ctx_Loaded.type != parent_ptr_type:
ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_enter_self")
enter_call: ir.CallInstr = Gen.builder.call(enter_func, [ctx_Loaded], name="call_enter")
enter_result = enter_call
if isinstance(enter_call.type, ir.PointerType):
pointee: ir.Type = enter_call.type.pointee
@@ -139,10 +167,16 @@ class WithHandle(BaseHandle):
if Gen._with_provides_stack:
Gen._with_provides_stack.pop()
if ClassName:
ExitFuncName: str = f'{ClassName}.__exit__'
if ExitFuncName in Gen.functions:
# 沿继承链查找 __exit__子类可能未覆写使用父类实现
exit_class, exit_func = self._ResolveInheritedMethod(ClassName, '__exit__', Gen)
if exit_func is not None:
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
Gen.builder.call(Gen.functions[ExitFuncName], [ctx_Loaded], name="call_exit")
# 父类方法需要 self 为父类指针类型bitcast 子类指针
if exit_class != ClassName and exit_class in Gen.structs:
parent_ptr_type = ir.PointerType(Gen.structs[exit_class])
if ctx_Loaded.type != parent_ptr_type:
ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_exit_self")
Gen.builder.call(exit_func, [ctx_Loaded], name="call_exit")
Gen._unregister_local_heap_ptr(original_ctx_val)
if ctx_val is not original_ctx_val:
Gen._unregister_local_heap_ptr(ctx_val)

View File

@@ -389,12 +389,24 @@ class BaseGenMixin:
return int(llvm_str[1:])
# 4. 特殊名称(非整数类型,但需要标记为 typedef 以跳过 opaque 定义)
# 注意:如果 name 已注册为 struct用户定义了同名 class如 includes/_dict.py 中的 dict
# 则不应视为 typedef应返回 None 让调用方走 struct 创建路径。
# 解决时序问题_parse_llvm_declare 从 stub.ll 解析时 dict 可能尚未注册到 Gen.structs
# 但 SymbolTable 在 .pyi 解析阶段已就绪,可通过 is_struct 检查区分 class 和 typedef。
_SPECIAL_TYPEDEFS: set[str] = {
'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion',
'CStruct', 'enum', 'REnum', 'renum',
'list', 'dict', 'tuple', 'set', 'str', 'bytes', 'array', 'type', 'CArray',
}
if name in _SPECIAL_TYPEDEFS:
if name in self.structs:
return None # 已注册为 struct不视为 typedef
if self.SymbolTable is not None:
try:
if self.SymbolTable.is_struct(name):
return None # SymbolTable 中标记为 struct不视为 typedef
except Exception:
pass
return 0 # 标记为 typedef但位宽为 0非整数
return None

View File

@@ -248,7 +248,15 @@ class StructGenMixin:
if is_packed:
self.structs[ClassName].packed = True
continue
mangled_name: str = self._mangle_name(ClassName)
# 修复跨模块类型定义冲突:如果类是从其他模块导入的(已在 class_sha1_map 注册),
# 必须使用导入的 source_sha1 作为前缀,而非本地模块的 sha1。
# 否则会创建缺少基类字段(如 GSListNode.Next的本地类型定义
# 导致字段索引错位 → 访问违规崩溃。
_imported_sha1: str = self.class_sha1_map.get(ClassName, '')
if _imported_sha1:
mangled_name: str = f"{_imported_sha1}.{ClassName}"
else:
mangled_name: str = self._mangle_name(ClassName)
struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, mangled_name, packed=is_packed)
self.structs[ClassName] = struct_type
@@ -361,7 +369,18 @@ class StructGenMixin:
_vtable_first_ok: bool = True
if _has_vtable_precheck and len(existing_st.elements) > 0:
_vtable_first_ok = isinstance(existing_st.elements[0], ir.PointerType)
if len(existing_st.elements) >= _expected_count_pre and _vtable_first_ok:
# 检测 T* 占位类型(未特化的泛型类型参数)。
# 当 struct 包含 T* 字段时(如 BasicBlock 继承 GSListNode[BasicBlock] 但 stub 未特化),
# 说明 stub 使用了未特化的基类,需清除重新生成。
_has_t_placeholder_pre: bool = False
for _elem_pre in existing_st.elements:
if isinstance(_elem_pre, ir.PointerType) and isinstance(_elem_pre.pointee, ir.IdentifiedStructType):
_pname_pre: str = _elem_pre.pointee.name
_short_pre: str = _pname_pre.rsplit('.', 1)[-1] if '.' in _pname_pre else _pname_pre
if _short_pre == 'T':
_has_t_placeholder_pre = True
break
if len(existing_st.elements) >= _expected_count_pre and _vtable_first_ok and not _has_t_placeholder_pre:
if ClassName in self.class_packed and not existing_st.packed:
existing_st.packed = True
continue
@@ -438,7 +457,12 @@ class StructGenMixin:
# 由 _TryLoadStructFromStub 设置完整 body。
member_types = []
else:
member_types = [ir.IntType(8)]
# 无 vtable 且 class_members 为空:常见于 register_method 早期
# 调用 _generate_structs此时 class_members[ClassName]=[] 但尚未
# 填充实际字段)。保持 opaque避免 set_body(*[i8]) 永久损坏 struct
# llvmlite 的 set_body 只能调用一次)。后续 _generate_structs 调用
# 会从 class_members 或 stub 设置正确 body。
member_types = []
# 获取之前创建的 struct 并设置成员
struct_type: ir.IdentifiedStructType = self.structs[ClassName]
@@ -454,10 +478,26 @@ class StructGenMixin:
import_handler: Any = self._import_handler_ref
if import_handler is not None and ClassName not in self._classes_in_progress:
try:
import_handler._TryLoadStructFromStub(ClassName, self)
# 传入 source_sha1 确保从正确的 stub 文件加载完整定义
_src_sha1: str = self.class_sha1_map.get(ClassName, '')
import_handler._TryLoadStructFromStub(ClassName, self, source_sha1=_src_sha1 or None)
except Exception:
pass
struct_type = self.structs.get(ClassName, struct_type)
# 检测 stub body 中的 T* 占位类型(未特化的泛型类型参数)。
# 若 stub 使用了未特化的基类(如 GSListNode 而非 GSListNode[BasicBlock]
# 清除 _elements 让 set_body 用正确的 class_members 重新设置。
if struct_type.elements is not None and member_types:
_has_t_placeholder_post: bool = False
for _elem_post in struct_type.elements:
if isinstance(_elem_post, ir.PointerType) and isinstance(_elem_post.pointee, ir.IdentifiedStructType):
_pname_post: str = _elem_post.pointee.name
_short_post: str = _pname_post.rsplit('.', 1)[-1] if '.' in _pname_post else _pname_post
if _short_post == 'T':
_has_t_placeholder_post = True
break
if _has_t_placeholder_post:
struct_type._elements = None
if struct_type.elements is None:
# 防御性检查member_types 为空时不调用 set_body。
# set_body(*[]) 会设置 elements=() 使 is_opaque=False
@@ -485,18 +525,20 @@ class StructGenMixin:
# _shared_class_vtable 和 _shared_cross_module_vtable导致 class_vtable 与
# _cross_module_vtable_classes 内容相同,无法用以区分本模块类与跨模块类。
# _generate_structs 会为所有 class_methods 的类创建空 struct type导致 self.structs
# 也包含所有类。唯一可靠的过滤条件是 self.functions_EmitClassLlvm 在本函数之前
# 调用LlvmGenerator.py L178-182本模块定义的类方法已加入 self.functions
# import 声明的跨模块类方法也在 self.functions 中。若 self.functions 中没有任何
# 以 "{ClassName}." 开头的键,说明本模块未定义也未导入该类的方法,虚表会保持
# 全 null虚表分派时调用 null 指针引发段错误。此时应跳过虚表创建,让链接器
# 从定义该类的模块解析虚表符号
# 也包含所有类。过滤条件是 self.functions只考虑本模块定义的函数define有函数体
# 跳过跨模块的 declare无函数体。跨模块类的 declare 通过 HandlesImports 加入
# self.functions短名如 "MemManager.alloc"),但其 is_declaration=True。
# 若只存在 declare 而无 define,说明本模块未定义该类,虚表应由定义模块创建。
# vtable linkage='internal',跨模块调用通过对象的 vtable 指针(首字段)完成分派,
# 不依赖本模块的 vtable 全局变量
has_local_method: bool = False
prefix: str = f"{ClassName}."
for func_name in self.functions:
for func_name, func_obj in self.functions.items():
if func_name.startswith(prefix):
has_local_method = True
break
# 跳过跨模块的 declare无函数体只考虑本模块定义的函数
if not getattr(func_obj, 'is_declaration', False):
has_local_method = True
break
if not has_local_method:
continue
VtableType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods))

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
import sys
import types
from typing import Any, ClassVar, Optional, TypeVar, Generic, TypeAlias
from typing import Any, ClassVar, Optional, TypeVar, Generic, TypeAlias, Callable
from lib.constants.config import mode as _ConfigMode
from lib.core.VLogger import get_logger as _vlog