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

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