重置上传,新增了多个标准库,开始 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

@@ -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))