重置上传,新增了多个标准库,开始 TransPyV 自举实验
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user