重置上传,新增了多个标准库,开始 TransPyV 自举实验
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1601,6 +1601,15 @@ class FunctionHandle(BaseHandle):
|
||||
}
|
||||
if IsExternFunc or IsStateFunc:
|
||||
return
|
||||
# 治本修复:如果函数已经有 body(blocks 不为空),跳过重复编译。
|
||||
# 否则会导致同一函数被追加第二个 entry block(llvmlite 自动重命名为 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -856,9 +856,10 @@ class HandlesTypeMerge(BaseHandle):
|
||||
if TypeItem.OriginalType:
|
||||
Result.OriginalType = TypeItem.OriginalType
|
||||
continue
|
||||
# 统计 t.CPtr(CVoid + PtrCount=1)
|
||||
# 统计 t.CPtr(CVoid + 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user