可用的回归测试通过的标准版本

This commit is contained in:
2026-06-18 00:39:43 +08:00
parent bffb0cb6b7
commit e02c867edf
365 changed files with 22562 additions and 24532 deletions

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.includes import t
import ast
import os
@@ -13,13 +13,13 @@ import llvmlite.ir as ir
class ImportHandle(BaseHandle):
_stub_cache = {}
_stub_cache_dir = None
_struct_load_cache = {}
_struct_Load_cache = {}
_pyi_cache = {}
_pyi_cache_dir = None
_project_root_cache = None
def _reset_file_cache(self):
self._struct_load_cache.clear()
self._struct_Load_cache.clear()
def _find_project_root(self):
if self._project_root_cache is not None:
@@ -43,17 +43,17 @@ class ImportHandle(BaseHandle):
return os.getcwd()
def _EmitImportDeclarationsLlvm(self, Node, Gen):
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
if not getattr(self.Trans, '_import_aliases', None):
self.Trans._import_aliases = {}
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
if not getattr(self.Trans, '_ImportAliases', None):
self.Trans._ImportAliases = {}
for alias in Node.names:
name = alias.name
if name in ('c', 't'):
continue
self.Trans._imported_modules.add(name)
self.Trans._ImportedModules.add(name)
if alias.asname:
self.Trans._import_aliases[alias.asname] = name
self.Trans._ImportAliases[alias.asname] = name
# 同时在符号表中注册模块别名,以便 CTypeInfo.FromNode 能解析
AliasInfo = CTypeInfo()
AliasInfo.IsModuleAlias = True
@@ -63,7 +63,7 @@ class ImportHandle(BaseHandle):
self._EmitModuleDeclarationsLlvm(name, Gen, register_module_name=current_module)
def _RegisterFromImportAliases(self, Node, Gen, module):
"""Register 'from module import y as z' aliases after module declarations are loaded"""
"""Register 'from module import y as z' aliases after module declarations are Loaded"""
if not Node.names:
return
for alias in Node.names:
@@ -125,11 +125,11 @@ class ImportHandle(BaseHandle):
def _EmitImportFromDeclarationsLlvm(self, Node, Gen):
module = Node.module if Node.module else ''
if module in ('c', 't'):
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
if not getattr(self.Trans, '_import_aliases', None):
self.Trans._import_aliases = {}
self.Trans._imported_modules.add(module)
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
if not getattr(self.Trans, '_ImportAliases', None):
self.Trans._ImportAliases = {}
self.Trans._ImportedModules.add(module)
if not hasattr(self.Trans, '_t_c_imported_names'):
self.Trans._t_c_imported_names = {}
for alias in Node.names:
@@ -137,10 +137,10 @@ class ImportHandle(BaseHandle):
asname = alias.asname or name
self.Trans._t_c_imported_names[asname] = (module, name)
return
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
if not getattr(self.Trans, '_import_aliases', None):
self.Trans._import_aliases = {}
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
if not getattr(self.Trans, '_ImportAliases', None):
self.Trans._ImportAliases = {}
current_module = self._get_current_module_name()
if Node.level and Node.level > 0:
current_file = getattr(self.Trans, 'CurrentFile', '') or ''
@@ -154,20 +154,20 @@ class ImportHandle(BaseHandle):
for ext in SearchExtensions:
candidate = sub_path_base + ext
if os.path.isfile(candidate):
self.Trans._imported_modules.add(module)
self.Trans._ImportedModules.add(module)
self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module, register_module_name=current_module)
self._RegisterFromImportAliases(Node, Gen, module)
return
module_sha1_map = getattr(Gen, 'module_sha1_map', {})
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
# 尝试完整模块名和短模块名
full_mod_name = f"{current_module}.{module}" if current_module else module
target_sha1 = module_sha1_map.get(full_mod_name) or module_sha1_map.get(module)
target_sha1 = ModuleSha1Map.get(full_mod_name) or ModuleSha1Map.get(module)
if target_sha1:
temp_dir = getattr(Gen, '_temp_dir', None)
if temp_dir:
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.pyi")
if os.path.isfile(sha1_pyi):
self.Trans._imported_modules.add(module)
self.Trans._ImportedModules.add(module)
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module, register_module_name=current_module)
self._RegisterFromImportAliases(Node, Gen, module)
return
@@ -177,39 +177,39 @@ class ImportHandle(BaseHandle):
SearchExtensions = ['.pyi', '.py']
pkg_name = ''
if not module:
# When 'from . import name' is used, load the package's __init__
# When 'from . import name' is used, Load the package's __init__
# to make package-level names (functions, classes, constants) available
pkg_name = self._LoadPackageInitForRelativeImport(mod_dir, Gen, current_module)
# Also try to load each alias as a submodule
# Also try to Load each alias as a submodule
for alias in Node.names:
found_sub = False
for ext in SearchExtensions:
sub_path = os.path.join(mod_dir, alias.name + ext)
if os.path.isfile(sub_path):
self.Trans._imported_modules.add(alias.name)
self.Trans._ImportedModules.add(alias.name)
self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=current_module)
if alias.asname:
self.Trans._import_aliases[alias.asname] = alias.name
self.Trans._ImportAliases[alias.asname] = alias.name
found_sub = True
break
if not found_sub:
# SHA1 map fallback for submodule
module_sha1_map = getattr(Gen, 'module_sha1_map', {})
sub_module_path = f"{pkg_name}.{alias.name}" if pkg_name else alias.name
target_sha1 = module_sha1_map.get(sub_module_path) or module_sha1_map.get(alias.name)
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
sub_ModulePath = f"{pkg_name}.{alias.name}" if pkg_name else alias.name
target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name)
if target_sha1:
temp_dir = getattr(Gen, '_temp_dir', None)
if temp_dir:
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.pyi")
if os.path.isfile(sha1_pyi):
self.Trans._imported_modules.add(sub_module_path)
self.Trans._ImportedModules.add(sub_ModulePath)
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=current_module)
if alias.asname:
self.Trans._import_aliases[alias.asname] = alias.name
self.Trans._ImportAliases[alias.asname] = alias.name
self._RegisterFromImportAliases(Node, Gen, module or pkg_name)
return
self.Trans._imported_modules.add(module)
self.Trans._ImportedModules.add(module)
self._EmitModuleDeclarationsLlvm(module, Gen, register_module_name=current_module)
self._RegisterFromImportAliases(Node, Gen, module)
@@ -229,15 +229,15 @@ class ImportHandle(BaseHandle):
reexport_names = []
if module_name and module_name != file_module_name and module_name != effective_register:
reexport_names.append(module_name)
# When loading a submodule for 'from .xxx import yyy' inside a package's __init__.py,
# When Loading a submodule for 'from .xxx import yyy' inside a package's __init__.py,
# re-export functions under the package name so 'pkg.func()' resolves correctly
if reexport_package and reexport_package != file_module_name and reexport_package not in reexport_names:
reexport_names.append(reexport_package)
self._EmitExternalFuncDeclLlvm(node, Gen, source_module_name=file_module_name, register_module_name=effective_register, reexport_module_names=reexport_names if reexport_names else None)
elif isinstance(node, ast.AnnAssign):
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, module_path=pyi_path)
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=pyi_path)
elif isinstance(node, ast.Assign):
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, module_path=pyi_path)
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=pyi_path)
elif isinstance(node, ast.ClassDef):
if hasattr(node, 'type_params') and node.type_params:
if not hasattr(self.Trans.ClassHandler, '_generic_class_templates'):
@@ -263,9 +263,9 @@ class ImportHandle(BaseHandle):
if os.path.isfile(candidate):
actual_module = f"{register_module_name}.{node.module}" if register_module_name else node.module
self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen)
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
self.Trans._imported_modules.add(actual_module)
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(actual_module)
for alias in node.names:
if alias.name == '*':
self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
@@ -276,16 +276,16 @@ class ImportHandle(BaseHandle):
# SHA1 map 回退
if not found_sub:
actual_module = f"{register_module_name}.{node.module}" if register_module_name else node.module
module_sha1_map = getattr(Gen, 'module_sha1_map', {})
target_sha1 = module_sha1_map.get(actual_module) or module_sha1_map.get(node.module)
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module)
if target_sha1:
temp_dir = getattr(Gen, '_temp_dir', None)
if temp_dir:
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.pyi")
if os.path.isfile(sha1_pyi):
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
self.Trans._imported_modules.add(actual_module)
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(actual_module)
for alias in node.names:
if alias.name == '*':
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
@@ -301,10 +301,10 @@ class ImportHandle(BaseHandle):
for ext in SearchExtensions:
sub_path = os.path.join(mod_dir, alias.name + ext)
if os.path.isfile(sub_path):
sub_module_path = f"{register_module_name}.{alias.name}" if register_module_name else alias.name
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
self.Trans._imported_modules.add(sub_module_path)
sub_ModulePath = f"{register_module_name}.{alias.name}" if register_module_name else alias.name
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(sub_ModulePath)
if alias.name == '*':
self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
else:
@@ -313,17 +313,17 @@ class ImportHandle(BaseHandle):
break
# SHA1 map 回退
if not found_alias:
sub_module_path = f"{register_module_name}.{alias.name}" if register_module_name else alias.name
module_sha1_map = getattr(Gen, 'module_sha1_map', {})
target_sha1 = module_sha1_map.get(sub_module_path) or module_sha1_map.get(alias.name)
sub_ModulePath = f"{register_module_name}.{alias.name}" if register_module_name else alias.name
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name)
if target_sha1:
temp_dir = getattr(Gen, '_temp_dir', None)
if temp_dir:
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.pyi")
if os.path.isfile(sha1_pyi):
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
self.Trans._imported_modules.add(sub_module_path)
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(sub_ModulePath)
if alias.name == '*':
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
else:
@@ -339,52 +339,56 @@ class ImportHandle(BaseHandle):
return None
def _RegisterSubModuleSha1(self, candidate_path, actual_module, short_module, Gen):
"""Register a submodule's SHA1 in module_sha1_map so cross-module class references work"""
"""Register a submodule's SHA1 in ModuleSha1Map so cross-module class references work"""
try:
with open(candidate_path, 'r', encoding='utf-8') as f:
sub_content = f.read()
sub_sha1 = hashlib.sha1(sub_content.encode('utf-8')).hexdigest()[:16]
if hasattr(Gen, 'module_sha1_map'):
Gen.module_sha1_map[actual_module] = sub_sha1
if short_module and short_module not in Gen.module_sha1_map:
Gen.module_sha1_map[short_module] = sub_sha1
except Exception:
pass
if hasattr(Gen, 'ModuleSha1Map'):
Gen.ModuleSha1Map[actual_module] = sub_sha1
if short_module and short_module not in Gen.ModuleSha1Map:
Gen.ModuleSha1Map[short_module] = sub_sha1
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
def _LoadPackageInitForRelativeImport(self, mod_dir, Gen, register_module_name):
"""Load __init__.py from the package directory for 'from . import name' resolution.
This makes package-level names (functions, classes, constants) available.
Returns the package name."""
pkg_name = os.path.basename(mod_dir)
init_Loaded = False
# 跳过当前正在编译的文件,避免重复加载导致 class_members 重复
current_sha1 = getattr(Gen, 'module_sha1', None)
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
pkg_sha1 = ModuleSha1Map.get(pkg_name)
if pkg_sha1 and pkg_sha1 == current_sha1:
return pkg_name
# Try SHA1 map first (correct mangled names from stub)
if pkg_sha1:
temp_dir = getattr(Gen, '_temp_dir', None)
if temp_dir:
sha1_pyi = os.path.join(temp_dir, f"{pkg_sha1}.pyi")
if os.path.isfile(sha1_pyi):
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=pkg_name, register_module_name=register_module_name)
init_Loaded = True
# Fallback: try Loading __init__.py directly from the package directory
if not init_Loaded:
for ext in ['.pyi', '.py']:
init_path = os.path.join(mod_dir, '__init__' + ext)
if os.path.isfile(init_path):
self._LoadModuleDeclarationsFromFile(init_path, Gen, module_name=pkg_name, register_module_name=register_module_name)
init_Loaded = True
break
return pkg_name
def _LoadPackageInitForRelativeImport(self, mod_dir, Gen, register_module_name):
"""Load __init__.py from the package directory for 'from . import name' resolution.
This makes package-level names (functions, classes, constants) available.
Returns the package name."""
pkg_name = os.path.basename(mod_dir)
init_loaded = False
# 跳过当前正在编译的文件,避免重复加载导致 class_members 重复
current_sha1 = getattr(Gen, 'module_sha1', None)
module_sha1_map = getattr(Gen, 'module_sha1_map', {})
pkg_sha1 = module_sha1_map.get(pkg_name)
if pkg_sha1 and pkg_sha1 == current_sha1:
return pkg_name
# Try SHA1 map first (correct mangled names from stub)
if pkg_sha1:
temp_dir = getattr(Gen, '_temp_dir', None)
if temp_dir:
sha1_pyi = os.path.join(temp_dir, f"{pkg_sha1}.pyi")
if os.path.isfile(sha1_pyi):
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=pkg_name, register_module_name=register_module_name)
init_loaded = True
# Fallback: try loading __init__.py directly from the package directory
if not init_loaded:
for ext in ['.pyi', '.py']:
init_path = os.path.join(mod_dir, '__init__' + ext)
if os.path.isfile(init_path):
self._LoadModuleDeclarationsFromFile(init_path, Gen, module_name=pkg_name, register_module_name=register_module_name)
init_loaded = True
break
return pkg_name
def _EmitModuleDeclarationsLlvm(self, module_name, Gen, register_module_name=None):
if module_name in ('pyzlib',):
source_sig_files = getattr(self.Trans, '_source_module_sig_files', None)
@@ -489,10 +493,10 @@ class ImportHandle(BaseHandle):
self._EmitExternalFuncDeclLlvm(node, Gen, source_module_name=module_name, register_module_name=effective_register)
has_functions_or_classes = True
elif isinstance(node, ast.AnnAssign):
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, module_path=FullModulePath)
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=FullModulePath)
has_functions_or_classes = True
elif isinstance(node, ast.Assign):
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, module_path=FullModulePath)
self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=FullModulePath)
has_functions_or_classes = True
elif isinstance(node, ast.ClassDef):
if hasattr(node, 'type_params') and node.type_params:
@@ -520,9 +524,9 @@ class ImportHandle(BaseHandle):
if os.path.isfile(candidate):
actual_module = f"{module_name}.{node.module}" if module_name else node.module
self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen)
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
self.Trans._imported_modules.add(actual_module)
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(actual_module)
for alias in node.names:
if alias.name == '*':
self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name)
@@ -534,17 +538,17 @@ class ImportHandle(BaseHandle):
# SHA1 map 回退:当文件查找失败时,从 SHA1 映射中查找子模块 stub
if not found_sub:
actual_module = f"{module_name}.{node.module}" if module_name else node.module
module_sha1_map = getattr(Gen, 'module_sha1_map', {})
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
# 尝试完整模块名和短模块名
target_sha1 = module_sha1_map.get(actual_module) or module_sha1_map.get(node.module)
target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module)
if target_sha1:
temp_dir = getattr(Gen, '_temp_dir', None)
if temp_dir:
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.pyi")
if os.path.isfile(sha1_pyi):
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
self.Trans._imported_modules.add(actual_module)
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(actual_module)
for alias in node.names:
if alias.name == '*':
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name)
@@ -561,10 +565,10 @@ class ImportHandle(BaseHandle):
for ext in SearchExtensions:
sub_path = os.path.join(mod_dir, alias.name + ext)
if os.path.isfile(sub_path):
sub_module_path = f"{module_name}.{alias.name}" if module_name else alias.name
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
self.Trans._imported_modules.add(sub_module_path)
sub_ModulePath = f"{module_name}.{alias.name}" if module_name else alias.name
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(sub_ModulePath)
if alias.name == '*':
self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name)
else:
@@ -574,17 +578,17 @@ class ImportHandle(BaseHandle):
break
# SHA1 map 回退
if not found_alias:
sub_module_path = f"{module_name}.{alias.name}" if module_name else alias.name
module_sha1_map = getattr(Gen, 'module_sha1_map', {})
target_sha1 = module_sha1_map.get(sub_module_path) or module_sha1_map.get(alias.name)
sub_ModulePath = f"{module_name}.{alias.name}" if module_name else alias.name
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name)
if target_sha1:
temp_dir = getattr(Gen, '_temp_dir', None)
if temp_dir:
sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.pyi")
if os.path.isfile(sha1_pyi):
if not getattr(self.Trans, '_imported_modules', None):
self.Trans._imported_modules = set()
self.Trans._imported_modules.add(sub_module_path)
if not getattr(self.Trans, '_ImportedModules', None):
self.Trans._ImportedModules = set()
self.Trans._ImportedModules.add(sub_ModulePath)
if alias.name == '*':
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name)
else:
@@ -603,13 +607,13 @@ class ImportHandle(BaseHandle):
if module_name:
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
def _EmitExternalGlobalDeclLlvm(self, Node, Gen, module_name=None, module_path=None):
def _EmitExternalGlobalDeclLlvm(self, Node, Gen, module_name=None, ModulePath=None):
"""Emit external global variable declaration from imported module"""
if isinstance(Node, ast.AnnAssign) and isinstance(Node.target, ast.Name):
VarName = Node.target.id
# 不再跳过 _ 开头的全局变量,跨模块引用需要声明它们
var_type = ir.IntType(32)
is_ptr = False
IsPtr = False
is_string_val = False
if VarName in Gen.module.globals:
existing = Gen.module.globals[VarName]
@@ -637,12 +641,12 @@ class ImportHandle(BaseHandle):
var_type = ir.ArrayType(elem_type, 0)
else:
var_type = Gen._ctype_to_llvm(ElemTypeInfo)
is_ptr = False
IsPtr = False
else:
TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
if TypeInfo:
var_type = Gen._ctype_to_llvm(TypeInfo)
is_ptr = TypeInfo.IsPtr
IsPtr = TypeInfo.IsPtr
elif isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr):
list_node = None
for side in (Node.annotation.left, Node.annotation.right):
@@ -665,20 +669,20 @@ class ImportHandle(BaseHandle):
var_type = ir.ArrayType(elem_type, 0)
else:
var_type = Gen._ctype_to_llvm(ElemTypeInfo)
is_ptr = False
IsPtr = False
else:
TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
if TypeInfo:
var_type = Gen._ctype_to_llvm(TypeInfo)
is_ptr = TypeInfo.IsPtr
IsPtr = TypeInfo.IsPtr
else:
TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
if TypeInfo:
var_type = Gen._ctype_to_llvm(TypeInfo)
is_ptr = TypeInfo.IsPtr
IsPtr = TypeInfo.IsPtr
if isinstance(var_type, ir.IdentifiedStructType) and (var_type.elements is None or len(var_type.elements) == 0):
var_type = ir.IntType(32)
if Node.value and module_path and is_ptr and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str):
if Node.value and ModulePath and IsPtr and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str):
str_val = Node.value.value + '\x00'
str_bytes = str_val.encode('utf-8')
arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes))
@@ -743,13 +747,21 @@ class ImportHandle(BaseHandle):
if Node.value:
DefineValue = self._ExtractConstValue(Node.value)
# 注册不带前缀的键名(例如 Z_NO_COMPRESSION
self._RegisterCDefineSymbol(VarName, DefineValue, Node.lineno, module_path or 'unknown')
self._RegisterCDefineSymbol(VarName, DefineValue, Node.lineno, ModulePath or 'unknown')
# 如果提供了模块名,也注册带模块前缀的键名
if module_name:
FullName = f"{module_name}.{VarName}"
self._RegisterCDefineSymbol(FullName, DefineValue, Node.lineno, module_path or 'unknown')
self._RegisterCDefineSymbol(FullName, DefineValue, Node.lineno, ModulePath or 'unknown')
return # 不再继续生成 LLVM 全局变量
# t.State 标记的全局变量只声明不定义external linkage避免 DSO/non-DSO 重定义
if TypeInfo and TypeInfo.IsState:
if VarName not in Gen.module.globals:
gv = ir.GlobalVariable(Gen.module, var_type, name=VarName)
gv.linkage = 'external'
Gen._export_funcs.add(VarName)
return
gv = ir.GlobalVariable(Gen.module, var_type, name=VarName)
if Node.value:
@@ -768,7 +780,7 @@ class ImportHandle(BaseHandle):
if VarName in Gen.module.globals:
return
gv = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName)
if Node.value and module_path:
if Node.value and ModulePath:
InitVal = self._ExtractValue(Node.value, ir.IntType(32), VarName)
if InitVal is not None:
gv.initializer = InitVal
@@ -893,40 +905,40 @@ class ImportHandle(BaseHandle):
return self._ExtractConstValue(value_node.args[0])
return None
def _ExtractTypedefOriginalType(self, value_node):
import ast
from lib.includes.t import CTypeRegistry
if isinstance(value_node, ast.Attribute):
if getattr(value_node.value, 'id', None) == 't':
llvm_str = CTypeRegistry.NameToLLVM(value_node.attr)
if llvm_str:
return llvm_str
elif isinstance(value_node, ast.Name):
llvm_str = CTypeRegistry.NameToLLVM(value_node.id)
if llvm_str:
return llvm_str
elif isinstance(value_node, ast.BinOp) and isinstance(value_node.op, ast.BitOr):
left = self._ExtractTypedefOriginalType(value_node.left)
right = self._ExtractTypedefOriginalType(value_node.right)
parts = []
if left:
parts.append(left)
if right:
parts.append(right)
if parts:
return '|'.join(parts)
elif isinstance(value_node, ast.Call):
if isinstance(value_node.func, ast.Attribute):
if getattr(value_node.func.value, 'id', None) == 't':
llvm_str = CTypeRegistry.NameToLLVM(value_node.func.attr)
if llvm_str:
return llvm_str
elif isinstance(value_node.func, ast.Name):
llvm_str = CTypeRegistry.NameToLLVM(value_node.func.id)
if llvm_str:
return llvm_str
return None
def _ExtractTypedefOriginalType(self, value_node):
import ast
from lib.includes.t import CTypeRegistry
if isinstance(value_node, ast.Attribute):
if getattr(value_node.value, 'id', None) == 't':
llvm_str = CTypeRegistry.NameToLLVM(value_node.attr)
if llvm_str:
return llvm_str
elif isinstance(value_node, ast.Name):
llvm_str = CTypeRegistry.NameToLLVM(value_node.id)
if llvm_str:
return llvm_str
elif isinstance(value_node, ast.BinOp) and isinstance(value_node.op, ast.BitOr):
left = self._ExtractTypedefOriginalType(value_node.left)
right = self._ExtractTypedefOriginalType(value_node.right)
parts = []
if left:
parts.append(left)
if right:
parts.append(right)
if parts:
return '|'.join(parts)
elif isinstance(value_node, ast.Call):
if isinstance(value_node.func, ast.Attribute):
if getattr(value_node.func.value, 'id', None) == 't':
llvm_str = CTypeRegistry.NameToLLVM(value_node.func.attr)
if llvm_str:
return llvm_str
elif isinstance(value_node.func, ast.Name):
llvm_str = CTypeRegistry.NameToLLVM(value_node.func.id)
if llvm_str:
return llvm_str
return None
def _RegisterCDefineSymbol(self, FullName, DefineValue, lineno, FilePath):
"""Register CDefine constant to SymbolTable"""
from lib.core.Handles.HandlesBase import CTypeInfo
@@ -938,19 +950,19 @@ class ImportHandle(BaseHandle):
# 直接添加到 SymbolTable 字典
self.Trans.SymbolTable[FullName] = info
def _check_annotation_for_state(self, annotation) -> bool:
import ast
if isinstance(annotation, ast.Attribute):
if hasattr(annotation, 'attr') and annotation.attr in ('CExport', 'CExtern', 'State'):
return True
if hasattr(annotation, 'value') and isinstance(annotation.value, ast.Name) and annotation.value.id == 't' and annotation.attr == 'State':
return True
elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return self._check_annotation_for_state(annotation.left) or self._check_annotation_for_state(annotation.right)
elif isinstance(annotation, ast.Name):
return annotation.id in ('CExport', 'CExtern', 'State')
return False
def _check_annotation_for_state(self, annotation) -> bool:
import ast
if isinstance(annotation, ast.Attribute):
if hasattr(annotation, 'attr') and annotation.attr in ('CExport', 'CExtern', 'State'):
return True
if hasattr(annotation, 'value') and isinstance(annotation.value, ast.Name) and annotation.value.id == 't' and annotation.attr == 'State':
return True
elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return self._check_annotation_for_state(annotation.left) or self._check_annotation_for_state(annotation.right)
elif isinstance(annotation, ast.Name):
return annotation.id in ('CExport', 'CExtern', 'State')
return False
def _EmitExternalFuncDeclLlvm(self, Node, Gen, is_class_method=False, source_module_name=None, register_module_name=None, reexport_module_names=None):
FuncName = Node.name
if Node.returns and self._check_annotation_for_state(Node.returns):
@@ -1086,13 +1098,15 @@ class ImportHandle(BaseHandle):
elif d.id == 'classmethod':
func_meta |= FuncMeta.CLASS_METHOD
elif isinstance(d, ast.Attribute):
if isinstance(d.value, ast.Name) and d.value.id == 'property':
if d.attr == 'setter':
func_meta |= FuncMeta.PROPERTY_SETTER
elif d.attr == 'getter':
func_meta |= FuncMeta.PROPERTY_GETTER
elif d.attr == 'deleter':
func_meta |= FuncMeta.PROPERTY_DELETER
if isinstance(d.value, ast.Name):
# 支持 @property.setter 和 @propname.setter 两种形式
if d.value.id == 'property' or d.attr in ('setter', 'getter', 'deleter'):
if d.attr == 'setter':
func_meta |= FuncMeta.PROPERTY_SETTER
elif d.attr == 'getter':
func_meta |= FuncMeta.PROPERTY_GETTER
elif d.attr == 'deleter':
func_meta |= FuncMeta.PROPERTY_DELETER
if FuncName not in self.Trans.SymbolTable:
FuncInfo = CTypeInfo()
FuncInfo.Name = FuncName
@@ -1103,6 +1117,28 @@ class ImportHandle(BaseHandle):
existing = self.Trans.SymbolTable[FuncName]
if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE:
existing.MetaList = func_meta
# 同时注册带模块前缀的符号,以便 module.func_name 查找能命中
if source_module_name and source_module_name not in ('c', 't'):
FullSymKey = f"{source_module_name}.{FuncName}"
if FullSymKey not in self.Trans.SymbolTable:
FullFuncInfo = CTypeInfo()
FullFuncInfo.Name = FullSymKey
FullFuncInfo.IsFunction = True
FullFuncInfo.MetaList = func_meta
self.Trans.SymbolTable[FullSymKey] = FullFuncInfo
# property setter/deleter: 在原始 PropKey不带后缀下注册 MetaList
if FuncMeta.PROPERTY_SETTER in func_meta or FuncMeta.PROPERTY_DELETER in func_meta:
BasePropKey = FuncName.replace('$set', '').replace('$del', '')
if BasePropKey in self.Trans.SymbolTable:
base_existing = self.Trans.SymbolTable[BasePropKey]
if func_meta != FuncMeta.NONE:
base_existing.MetaList = base_existing.MetaList | func_meta
else:
PropInfo = CTypeInfo()
PropInfo.Name = BasePropKey
PropInfo.IsFunction = True
PropInfo.MetaList = func_meta
self.Trans.SymbolTable[BasePropKey] = PropInfo
if register_module_name and register_module_name != source_module_name:
ReexportName = Gen._mangle_func_name(FuncName, module_name=register_module_name)
Gen.functions[ReexportName] = func
@@ -1207,10 +1243,10 @@ class ImportHandle(BaseHandle):
return
self._TryLoadStructFromStub(ClassName, Gen)
source_sha1 = None
if actual_module_name and hasattr(Gen, 'module_sha1_map') and actual_module_name in Gen.module_sha1_map:
source_sha1 = Gen.module_sha1_map[actual_module_name]
elif module_name and hasattr(Gen, 'module_sha1_map') and module_name in Gen.module_sha1_map:
source_sha1 = Gen.module_sha1_map[module_name]
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]
# Track which module defines this class (for cross-module name mangling)
if source_sha1:
if not hasattr(Gen, 'class_sha1_map'):
@@ -1268,9 +1304,15 @@ class ImportHandle(BaseHandle):
is_item_prop_setter = any(isinstance(d, ast.Attribute) and d.attr == 'setter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list)
is_item_prop_getter = any(isinstance(d, ast.Attribute) and d.attr == 'getter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list)
is_item_prop_deleter = any(isinstance(d, ast.Attribute) and d.attr == 'deleter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list)
if FuncFullName not in Gen.functions:
# setter/deleter 使用不同的函数名后缀
DeclFuncName = FuncFullName
if is_item_prop_setter:
DeclFuncName = FuncFullName + '$set'
elif is_item_prop_deleter:
DeclFuncName = FuncFullName + '$del'
if DeclFuncName not in Gen.functions:
FuncDeclNode = ast.FunctionDef(
name=FuncFullName,
name=DeclFuncName,
args=item.args,
body=item.body,
decorator_list=item.decorator_list,
@@ -1290,10 +1332,11 @@ class ImportHandle(BaseHandle):
func_meta |= FuncMeta.PROPERTY_GETTER
if is_item_prop_deleter:
func_meta |= FuncMeta.PROPERTY_DELETER
SymKey = FuncFullName
# setter/deleter 的 SymKey 使用带后缀的函数名
SymKey = DeclFuncName
if SymKey not in self.Trans.SymbolTable:
FuncInfo = CTypeInfo()
FuncInfo.Name = FuncFullName
FuncInfo.Name = SymKey
FuncInfo.IsFunction = True
FuncInfo.MetaList = func_meta
self.Trans.SymbolTable[SymKey] = FuncInfo
@@ -1301,6 +1344,18 @@ class ImportHandle(BaseHandle):
existing = self.Trans.SymbolTable[SymKey]
if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE:
existing.MetaList = func_meta
# setter/deleter: 同时在原始 PropKey不带后缀下注册 MetaList
if is_item_prop_setter or is_item_prop_deleter:
if FuncFullName in self.Trans.SymbolTable:
base_existing = self.Trans.SymbolTable[FuncFullName]
if func_meta != FuncMeta.NONE:
base_existing.MetaList = base_existing.MetaList | func_meta
else:
PropInfo = CTypeInfo()
PropInfo.Name = FuncFullName
PropInfo.IsFunction = True
PropInfo.MetaList = func_meta
self.Trans.SymbolTable[FuncFullName] = PropInfo
if has_methods:
if IsCVTable:
Gen._cross_module_vtable_classes.add(ClassName)
@@ -1308,8 +1363,8 @@ class ImportHandle(BaseHandle):
NewFuncName = f'{ClassName}.__before_init__'
if not Gen._has_function(NewFuncName) and (IsCpythonObject or IsCVTable):
source_sha1 = None
if module_name and hasattr(Gen, 'module_sha1_map') and module_name in Gen.module_sha1_map:
source_sha1 = Gen.module_sha1_map[module_name]
if module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map:
source_sha1 = Gen.ModuleSha1Map[module_name]
MangledName = Gen._mangle_name(NewFuncName) if not source_sha1 else f"{source_sha1}.{NewFuncName}"
StructType = Gen.structs.get(ClassName)
if StructType:
@@ -1327,9 +1382,9 @@ class ImportHandle(BaseHandle):
def _TryLoadStructFromStub(self, class_name: str, Gen):
cache_key = class_name
if cache_key in self._struct_load_cache:
if cache_key in self._struct_Load_cache:
return
self._struct_load_cache[cache_key] = True
self._struct_Load_cache[cache_key] = True
import os, re
ProjectRoot = self._find_project_root()
@@ -1358,9 +1413,13 @@ class ImportHandle(BaseHandle):
self._stub_cache.setdefault(short_name, []).append((full_name, struct_body, source_sha1))
if '.' in full_name:
self._stub_cache.setdefault(full_name, []).append((full_name, struct_body, source_sha1))
except Exception:
pass
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
if class_name not in self._stub_cache:
return
@@ -1380,12 +1439,16 @@ class ImportHandle(BaseHandle):
elem_types.append(et if et else ir.IntType(32))
try:
st = Gen._get_or_create_struct(class_name, source_sha1=source_sha1, packed=is_packed)
if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0):
if isinstance(st, ir.IdentifiedStructType) and st.is_opaque:
st.set_body(*elem_types)
if is_packed:
Gen.class_packed.add(class_name)
except Exception:
pass
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
stub_filename = source_sha1 + '.stub.ll'
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
return
@@ -1419,13 +1482,13 @@ class ImportHandle(BaseHandle):
return
source_sha1 = stub_filename.replace('.stub.ll', '')
module_name = None
if hasattr(Gen, 'module_sha1_map'):
for mod_name, mod_sha1 in Gen.module_sha1_map.items():
if hasattr(Gen, 'ModuleSha1Map'):
for mod_name, mod_sha1 in Gen.ModuleSha1Map.items():
if mod_sha1 == source_sha1 and '.' not in mod_name:
module_name = mod_name
break
if module_name is None:
for mod_name, mod_sha1 in Gen.module_sha1_map.items():
for mod_name, mod_sha1 in Gen.ModuleSha1Map.items():
if mod_sha1 == source_sha1:
module_name = mod_name
break
@@ -1513,9 +1576,13 @@ class ImportHandle(BaseHandle):
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:
pass
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
if func_name not in self._stub_func_cache:
return None
@@ -1622,8 +1689,8 @@ class ImportHandle(BaseHandle):
target_sha1 = os.path.basename(val).replace('.pyi', '')
break
if not target_sha1:
module_sha1_map = getattr(Gen, 'module_sha1_map', {})
target_sha1 = module_sha1_map.get(module_name)
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
target_sha1 = ModuleSha1Map.get(module_name)
if target_sha1:
target_stub = f"{target_sha1}.stub.ll"
@@ -1648,21 +1715,25 @@ class ImportHandle(BaseHandle):
elif stripped.startswith('declare '):
for m in re.finditer(r'%\"?([a-f0-9]{16})\.', stripped):
needed_sha1s.add(m.group(1))
except Exception:
pass
preload_files = []
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
preLoad_files = []
for sha1 in needed_sha1s:
dep_stub = f"{sha1}.stub.ll"
if dep_stub in all_stub_files and dep_stub not in stub_files:
preload_files.append(dep_stub)
preLoad_files.append(dep_stub)
load_order = preload_files + stub_files
Load_order = preLoad_files + stub_files
for filename in load_order:
for filename in Load_order:
stub_path = os.path.join(temp_dir, filename)
source_sha1 = filename.replace('.stub.ll', '')
is_preload = filename in preload_files
is_preLoad = filename in preLoad_files
try:
with open(stub_path, 'r', encoding='utf-8') as f:
content = f.read()
@@ -1699,14 +1770,18 @@ class ImportHandle(BaseHandle):
if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0):
try:
st.set_body(*elem_types)
except Exception:
pass
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
continue
if not stripped.startswith('declare '):
continue
if is_preload:
if is_preLoad:
continue
func_name_match = stripped.split('@', 1)
@@ -1736,10 +1811,18 @@ class ImportHandle(BaseHandle):
if '.' in func_name:
short_name = func_name.split('.', 1)[1]
Gen.functions[short_name] = func
except Exception:
pass
except Exception:
pass
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
def _parse_llvm_declare(self, declare_line: str, Gen):
"""解析 LLVM declare 语句"""