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.includes import t import ast import os import hashlib import llvmlite.ir as ir class ImportHandle(BaseHandle): _stub_cache = {} _stub_cache_dir = None _struct_Load_cache = {} _pyi_cache = {} _pyi_cache_dir = None _project_root_cache = None def _reset_file_cache(self): self._struct_Load_cache.clear() def _find_project_root(self): if self._project_root_cache is not None: return self._project_root_cache import os current_file = getattr(self.Trans, 'CurrentFile', '') or '' if current_file: current_dir = os.path.dirname(os.path.abspath(current_file)) search_dir = current_dir for _ in range(10): if os.path.isdir(os.path.join(search_dir, 'temp')) and os.path.isdir(os.path.join(search_dir, 'output')): self._project_root_cache = search_dir return search_dir parent = os.path.dirname(search_dir) if parent == search_dir: break search_dir = parent self._project_root_cache = current_dir return current_dir self._project_root_cache = os.getcwd() return os.getcwd() def _EmitImportDeclarationsLlvm(self, Node, Gen): if not getattr(self.Trans, '_ImportedModules', None): self.Trans._ImportedModules = set() for alias in Node.names: name = alias.name if name in ('c', 't'): continue self.Trans._ImportedModules.add(name) if alias.asname: self.Trans.SymbolTable.import_aliases[alias.asname] = name # 同时在符号表中注册模块别名,以便 CTypeInfo.FromNode 能解析 AliasInfo = CTypeInfo() AliasInfo.IsModuleAlias = True AliasInfo.ResolvedModule = name self.Trans.SymbolTable.insert(alias.asname, AliasInfo) current_module = self._get_current_module_name() 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""" if not Node.names: return for alias in Node.names: name = alias.name asname = alias.asname # For 'from X import Y' (no asname), register CDefine constants # into _define_constants so they can be found by _HandleNameLlvm if not asname or asname == name: # Check if this name is a CDefine constant in SymbolTable sym_info = self.Trans.SymbolTable.lookup(name) if sym_info and sym_info.IsDefine and sym_info.DefineValue is not None: define_constants = vars(Gen).setdefault('_define_constants', {}) if name not in define_constants: define_constants[name] = sym_info.DefineValue # Also check with module prefix for prefix_key in [f"{module}.{name}", name]: sym_info2 = self.Trans.SymbolTable.lookup(prefix_key) if sym_info2 and sym_info2.IsDefine and sym_info2.DefineValue is not None: define_constants = vars(Gen).setdefault('_define_constants', {}) if name not in define_constants: define_constants[name] = sym_info2.DefineValue break continue if not hasattr(self.Trans, '_t_c_imported_names'): self.Trans._t_c_imported_names = {} self.Trans._t_c_imported_names[asname] = (module, name) if name in Gen.functions: Gen.functions[asname] = Gen.functions[name] if name in Gen.structs: Gen.structs[asname] = Gen.structs[name] if self.Trans.SymbolTable.has(name): self.Trans.SymbolTable.insert(asname, self.Trans.SymbolTable[name]) if name in Gen.variables: Gen.variables[asname] = Gen.variables[name] if name in Gen.global_vars: Gen.global_vars[asname] = Gen.global_vars[name] for func_name in list(Gen.functions.keys()): if func_name.startswith(f'{name}.'): method_suffix = func_name[len(name):] Gen.functions[f'{asname}{method_suffix}'] = Gen.functions[func_name] for key in list(self.Trans.SymbolTable.keys()): if key.startswith(f'{name}.'): suffix = key[len(name):] self.Trans.SymbolTable.insert(f'{asname}{suffix}', self.Trans.SymbolTable[key]) for meta_dict in (Gen.class_members, Gen.class_member_defaults, Gen.class_member_signeds, Gen.class_member_bitfields, Gen.class_member_byteorders, Gen.class_member_bitoffsets, Gen.class_methods): if name in meta_dict: meta_dict[asname] = meta_dict[name] if name in Gen.class_vtable: Gen.class_vtable.add(asname) if name in Gen.class_packed: Gen.class_packed.add(asname) struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) if name in struct_sha1_map: struct_sha1_map[asname] = struct_sha1_map[name] def _EmitImportFromDeclarationsLlvm(self, Node, Gen): module = Node.module if Node.module else '' if module in ('c', 't'): if not getattr(self.Trans, '_ImportedModules', None): self.Trans._ImportedModules = set() 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: name = alias.name asname = alias.asname or name self.Trans._t_c_imported_names[asname] = (module, name) return if not getattr(self.Trans, '_ImportedModules', None): self.Trans._ImportedModules = set() current_module = self._get_current_module_name() if Node.level and Node.level > 0: current_file = getattr(self.Trans, 'CurrentFile', '') or '' current_dir = os.path.dirname(os.path.abspath(current_file)) parent_dir = current_dir for _ in range(Node.level - 1): parent_dir = os.path.dirname(parent_dir) if module: SearchExtensions = ['.pyi', '.py'] sub_path_base = os.path.join(parent_dir, module) for ext in SearchExtensions: candidate = sub_path_base + ext if os.path.isfile(candidate): self.Trans._ImportedModules.add(module) self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module, register_module_name=current_module) self._RegisterFromImportAliases(Node, Gen, module) return ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) # 尝试完整模块名和短模块名 full_mod_name = f"{current_module}.{module}" if current_module else 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._ImportedModules.add(module) self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module, register_module_name=current_module) self._RegisterFromImportAliases(Node, Gen, module) return else: mod_dir = parent_dir SearchExtensions = ['.pyi', '.py'] pkg_name = '' if not module: # 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 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._ImportedModules.add(alias.name) self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=current_module) if alias.asname: self.Trans.SymbolTable.import_aliases[alias.asname] = alias.name found_sub = True break if not found_sub: # SHA1 map fallback for submodule 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._ImportedModules.add(sub_ModulePath) self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=current_module) if alias.asname: self.Trans.SymbolTable.import_aliases[alias.asname] = alias.name self._RegisterFromImportAliases(Node, Gen, module or pkg_name) return self.Trans._ImportedModules.add(module) self._EmitModuleDeclarationsLlvm(module, Gen, register_module_name=current_module) self._RegisterFromImportAliases(Node, Gen, module) def _LoadModuleDeclarationsFromFile(self, pyi_path, Gen, module_name=None, register_module_name=None, actual_module_name=None, reexport_package=None): if not pyi_path or not os.path.isfile(pyi_path): return try: with open(pyi_path, 'r', encoding='utf-8') as f: ModuleCode = f.read() ModuleTree = ast.parse(ModuleCode) for node in ModuleTree.body: if isinstance(node, ast.FunctionDef): if node.name in ('va_start', 'va_arg', 'va_end'): continue file_module_name = os.path.splitext(os.path.basename(pyi_path))[0] effective_register = register_module_name or module_name 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, # 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, ModulePath=pyi_path) elif isinstance(node, ast.Assign): 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'): self.Trans.ClassHandler._generic_class_templates = {} type_params = [tp.name for tp in node.type_params] self.Trans.ClassHandler._generic_class_templates[node.name] = { 'node': node, 'type_params': type_params, } self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name, actual_module_name=actual_module_name) elif isinstance(node, ast.ImportFrom): if node.level > 0: current_dir = os.path.dirname(os.path.abspath(pyi_path)) parent_dir = current_dir for _ in range(node.level - 1): parent_dir = os.path.dirname(parent_dir) if node.module: SearchExtensions = ['.pyi', '.py'] sub_path_base = os.path.join(parent_dir, node.module) found_sub = False 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 self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen) 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) else: self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=reexport_package or module_name) found_sub = True break # SHA1 map 回退 if not found_sub: actual_module = f"{register_module_name}.{node.module}" if register_module_name else 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, '_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) else: self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=reexport_package or module_name) else: mod_dir = parent_dir SearchExtensions = ['.pyi', '.py'] # Load __init__.py to make package-level names available self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name) 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 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: self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) found_alias = True break # SHA1 map 回退 if not found_alias: 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, '_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: self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) except Exception as _e: if __import__('lib.constants.config', fromlist=['mode']).mode == "strict": self.Trans.LogWarning(f"异常被忽略: {_e}") def _get_current_module_name(self): current_file = getattr(self.Trans, 'CurrentFile', '') or '' if current_file: return os.path.splitext(os.path.basename(current_file))[0] return None def _RegisterSubModuleSha1(self, candidate_path, actual_module, short_module, Gen): """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, '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 _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) ModulePath = module_name.replace('.', os.sep) + '.pyi' PackageInitPath = module_name.replace('.', os.sep) + os.sep + '__init__.pyi' ProjectRoot = os.path.dirname(os.path.abspath(getattr(self.Trans, 'CurrentFile', '') or '')) or os.getcwd() import inspect HandlesFile = os.path.abspath(inspect.getfile(self.__class__)) TransPyCRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(HandlesFile)))) FullModulePath = None SearchDirs = [os.path.join(TransPyCRoot, 'includes'), os.path.join(TransPyCRoot, 'lib', 'includes')] + list(self.Trans.LibraryPaths) for LibPath in SearchDirs: SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath CandidatePath = os.path.join(SearchPath, ModulePath) if os.path.isfile(CandidatePath): FullModulePath = CandidatePath break CandidatePath = os.path.join(SearchPath, PackageInitPath) if os.path.isfile(CandidatePath): FullModulePath = CandidatePath break ModuleParts = ModulePath.split(os.sep) if len(ModuleParts) > 1: LibPathBasename = os.path.basename(LibPath.rstrip('/\\')) if LibPathBasename == ModuleParts[0]: RemainingPath = os.sep.join(ModuleParts[1:]) CandidatePath = os.path.join(SearchPath, RemainingPath) if os.path.isfile(CandidatePath): FullModulePath = CandidatePath break if not FullModulePath: ModulePathPy = module_name.replace('.', os.sep) + '.py' PackageInitPathPy = module_name.replace('.', os.sep) + os.sep + '__init__.py' SearchDirsForPy = [os.path.join(TransPyCRoot, 'includes'), os.path.join(TransPyCRoot, 'lib', 'includes')] + list(self.Trans.LibraryPaths) for LibPath in SearchDirsForPy: SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath CandidatePath = os.path.join(SearchPath, ModulePathPy) if os.path.isfile(CandidatePath): FullModulePath = CandidatePath break CandidatePath = os.path.join(SearchPath, PackageInitPathPy) if os.path.isfile(CandidatePath): FullModulePath = CandidatePath break if not FullModulePath or not os.path.isfile(FullModulePath): source_sig_files = getattr(self.Trans, '_source_module_sig_files', None) if source_sig_files: sig_path = source_sig_files.get(module_name) if sig_path and os.path.isfile(sig_path): FullModulePath = sig_path else: for key, val in source_sig_files.items(): if key.endswith('.' + module_name) or key == module_name: FullModulePath = val break if not FullModulePath or not os.path.isfile(FullModulePath): if module_name in ('pyzlib', 'zdeflate', 'zinflate', 'zchecksum', 'zdef', 'zhuff'): pass if not FullModulePath: CurrentFile = getattr(self.Trans, 'CurrentFile', None) or '' if CurrentFile: CurrentFileDir = os.path.dirname(os.path.abspath(CurrentFile)) SearchFromDirs = [CurrentFileDir, os.getcwd()] else: SearchFromDirs = [os.getcwd()] SearchDirsForSubmodule = SearchFromDirs + [os.path.join(TransPyCRoot, 'includes'), os.path.join(TransPyCRoot, 'lib', 'includes')] + list(self.Trans.LibraryPaths) for SearchPath in SearchDirsForSubmodule: if os.path.isdir(SearchPath): for root, dirs, files in os.walk(SearchPath): if '__pycache__' in root: continue if module_name + '.py' in files: FullModulePath = os.path.join(root, module_name + '.py') break if module_name + '.pyi' in files: FullModulePath = os.path.join(root, module_name + '.pyi') break if FullModulePath: break self._LoadDeclarationsFromStubLlvm(module_name, Gen) if module_name == 'pyzlib': pass try: with open(FullModulePath, 'r', encoding='utf-8') as f: ModuleCode = f.read() ModuleTree = ast.parse(ModuleCode) if module_name == 'pyzlib': func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)] has_functions_or_classes = False for node in ModuleTree.body: if module_name == 'pyzlib': node_type = type(node).__name__ name = getattr(node, 'name', getattr(node, 'attr', '?')) try: if isinstance(node, ast.FunctionDef): if node.name in ('va_start', 'va_arg', 'va_end'): continue effective_register = register_module_name or module_name 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, ModulePath=FullModulePath) has_functions_or_classes = True elif isinstance(node, ast.Assign): 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: if not hasattr(self.Trans.ClassHandler, '_generic_class_templates'): self.Trans.ClassHandler._generic_class_templates = {} type_params = [tp.name for tp in node.type_params] self.Trans.ClassHandler._generic_class_templates[node.name] = { 'node': node, 'type_params': type_params, } self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name) has_functions_or_classes = True elif isinstance(node, ast.ImportFrom): if node.level > 0: current_dir = os.path.dirname(os.path.abspath(FullModulePath)) parent_dir = current_dir for _ in range(node.level - 1): parent_dir = os.path.dirname(parent_dir) if node.module: SearchExtensions = ['.pyi', '.py'] sub_path_base = os.path.join(parent_dir, node.module) found_sub = False for ext in SearchExtensions: candidate = sub_path_base + ext 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, '_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) else: self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=module_name) has_functions_or_classes = True found_sub = True break # SHA1 map 回退:当文件查找失败时,从 SHA1 映射中查找子模块 stub if not found_sub: actual_module = f"{module_name}.{node.module}" if module_name else 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, '_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) else: self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=module_name) has_functions_or_classes = True else: mod_dir = parent_dir SearchExtensions = ['.pyi', '.py'] # Load __init__.py to make package-level names available self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name or module_name) 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"{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: self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=module_name) has_functions_or_classes = True found_alias = True break # SHA1 map 回退 if not found_alias: 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, '_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: self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=module_name) has_functions_or_classes = True except Exception as e: if module_name == 'pyzlib': node_type = type(node).__name__ name = getattr(node, 'name', getattr(node, 'attr', '?')) continue if not has_functions_or_classes and module_name: self._LoadDeclarationsFromStubLlvm(module_name, Gen) elif 'includes' in FullModulePath and module_name: self._LoadDeclarationsFromStubLlvm(module_name, Gen) except Exception as e: if module_name: self._LoadDeclarationsFromStubLlvm(module_name, Gen) 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) IsPtr = False is_string_val = False if VarName in Gen.module.globals: existing = Gen.module.globals[VarName] if isinstance(existing, ir.GlobalVariable): old_pointee = existing.type.pointee if isinstance(existing.type, ir.PointerType) else None if isinstance(old_pointee, ir.IdentifiedStructType): if old_pointee.elements is not None and len(old_pointee.elements) > 0: return elif isinstance(old_pointee, ir.ArrayType): return if Node.annotation: if isinstance(Node.annotation, ast.Subscript) and isinstance(Node.annotation.value, ast.Name) and Node.annotation.value.id == 'list': slice_node = Node.annotation.slice if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: elem_type_node = slice_node.elts[0] count_node = slice_node.elts[1] ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable) if ElemTypeInfo: elem_type = Gen._ctype_to_llvm(ElemTypeInfo) if isinstance(elem_type, ir.VoidType): elem_type = ir.IntType(8) if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int): var_type = ir.ArrayType(elem_type, count_node.value) elif isinstance(count_node, ast.Constant) and count_node.value is None: var_type = ir.ArrayType(elem_type, 0) else: var_type = Gen._ctype_to_llvm(ElemTypeInfo) IsPtr = False else: TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) if TypeInfo: var_type = Gen._ctype_to_llvm(TypeInfo) 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): if isinstance(side, ast.Subscript) and isinstance(side.value, ast.Name) and side.value.id == 'list': list_node = side break if list_node: slice_node = list_node.slice if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: elem_type_node = slice_node.elts[0] count_node = slice_node.elts[1] ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable) if ElemTypeInfo: elem_type = Gen._ctype_to_llvm(ElemTypeInfo) if isinstance(elem_type, ir.VoidType): elem_type = ir.IntType(8) if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int): var_type = ir.ArrayType(elem_type, count_node.value) elif isinstance(count_node, ast.Constant) and count_node.value is None: var_type = ir.ArrayType(elem_type, 0) else: var_type = Gen._ctype_to_llvm(ElemTypeInfo) IsPtr = False else: TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) if TypeInfo: var_type = Gen._ctype_to_llvm(TypeInfo) IsPtr = TypeInfo.IsPtr else: TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) if TypeInfo: var_type = Gen._ctype_to_llvm(TypeInfo) 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 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)) str_gv_name = VarName + '_str' if str_gv_name not in Gen.module.globals: str_gv = ir.GlobalVariable(Gen.module, arr_type, name=str_gv_name) str_gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) str_gv.linkage = 'internal' ptr_type = ir.PointerType(ir.IntType(8)) if VarName not in Gen.module.globals: ptr_gv = ir.GlobalVariable(Gen.module, ptr_type, name=VarName) str_gv = Gen.module.globals[str_gv_name] ptr_gv.initializer = ir.Constant.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)]) ptr_gv.linkage = 'internal' Gen.variables[VarName] = None return # 检查是否是 CDefine 常量或 CTypedef 类型别名 IsCDefine = False IsCTypedef = False if Node.annotation: if isinstance(Node.annotation, ast.Attribute): if getattr(Node.annotation.value, 'id', None) == 't' and Node.annotation.attr == 'CDefine': IsCDefine = True elif getattr(Node.annotation.value, 'id', None) == 't' and Node.annotation.attr == 'CTypedef': IsCTypedef = True elif isinstance(Node.annotation, ast.Name): if Node.annotation.id == 'CDefine': IsCDefine = True elif Node.annotation.id == 'CTypedef': IsCTypedef = True if IsCTypedef: if Node.value: ValueTypeInfo = None if isinstance(Node.value, ast.BinOp) and isinstance(Node.value.op, ast.BitOr): ValueTypeInfo = self.Trans.TypeMergeHandler.MergeTypes(Node.value) else: ValueTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.value) if isinstance(ValueTypeInfo, CTypeInfo) and (ValueTypeInfo.BaseType or ValueTypeInfo.PtrCount > 0): ValueTypeInfo.IsTypedef = True ValueTypeInfo.Name = VarName self.Trans.SymbolTable.insert(VarName, ValueTypeInfo) if module_name: FullName = f"{module_name}.{VarName}" self.Trans.SymbolTable.insert(FullName, ValueTypeInfo) return TTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation) if TTypeInfo: TTypeInfo.IsTypedef = True TTypeInfo.Name = VarName self.Trans.SymbolTable.insert(VarName, TTypeInfo) if module_name: FullName = f"{module_name}.{VarName}" self.Trans.SymbolTable.insert(FullName, TTypeInfo) return if IsCDefine: # CDefine 宏不生成 LLVM 全局变量,只注册到 SymbolTable # 引用时会直接替换为立即数 DefineValue = None if Node.value: DefineValue = self._ExtractConstValue(Node.value) # 注册不带前缀的键名(例如 Z_NO_COMPRESSION) self._RegisterCDefineSymbol(VarName, DefineValue, Node.lineno, ModulePath or 'unknown') # 如果提供了模块名,也注册带模块前缀的键名 if module_name: FullName = f"{module_name}.{VarName}" 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: InitVal = self._ExtractValue(Node.value, var_type, VarName) if InitVal is not None: gv.initializer = InitVal gv.linkage = 'available_externally' else: gv.linkage = 'external' else: gv.linkage = 'external' elif isinstance(node, ast.Assign) and Node.targets and isinstance(Node.targets[0], ast.Name): VarName = Node.targets[0].id if VarName.startswith('_'): return if VarName in Gen.module.globals: return gv = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName) if Node.value and ModulePath: InitVal = self._ExtractValue(Node.value, ir.IntType(32), VarName) if InitVal is not None: gv.initializer = InitVal gv.linkage = 'available_externally' else: gv.linkage = 'external' else: gv.linkage = 'external' def _ExtractValue(self, value_node, target_type, var_name=None): """Extract constant value from AST node for global variable initializer""" # 处理负号 if isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.USub): if isinstance(value_node.operand, ast.Constant) and isinstance(value_node.operand.value, int): neg_val = -value_node.operand.value if isinstance(target_type, ir.IntType): return ir.Constant(target_type, neg_val) return ir.Constant(ir.IntType(32), neg_val) # 处理正号 elif isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.UAdd): return self._ExtractValue(value_node.operand, target_type) if isinstance(value_node, ast.Constant): if isinstance(value_node.value, bool): if isinstance(target_type, ir.IntType): return ir.Constant(target_type, 1 if value_node.value else 0) return ir.Constant(ir.IntType(32), 1 if value_node.value else 0) elif isinstance(value_node.value, int): if isinstance(target_type, ir.IntType): return ir.Constant(target_type, value_node.value) return ir.Constant(ir.IntType(32), value_node.value) elif isinstance(value_node.value, str): str_val = value_node.value + '\x00' str_bytes = str_val.encode('utf-8') arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes)) Gen = self.Trans.LlvmGen gv_name = var_name + '_str' if var_name else f"str_global_{id(value_node)}" if gv_name in Gen.module.globals: gv = Gen.module.globals[gv_name] else: gv = ir.GlobalVariable(Gen.module, arr_type, name=gv_name) gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) gv.linkage = 'internal' ptr_type = ir.PointerType(ir.IntType(8)) return ir.Constant(ptr_type, gv.reference) elif isinstance(value_node.value, bool): return ir.Constant(ir.IntType(32), 1 if value_node.value else 0) elif value_node.value is None: if isinstance(target_type, ir.BaseStructType): return ir.Constant(target_type, None) if isinstance(target_type, ir.PointerType): return ir.Constant(target_type, None) return ir.Constant(target_type, 0) elif isinstance(value_node, ast.Name): if value_node.id == 'None': if isinstance(target_type, ir.BaseStructType): return ir.Constant(target_type, None) if isinstance(target_type, ir.PointerType): return ir.Constant(target_type, None) return ir.Constant(target_type, 0) return None def _ExtractConstValue(self, value_node): """Extract constant value from AST node for SymbolTable registration 支持常量、一元运算符、二元运算符和常量引用 """ import ast if isinstance(value_node, ast.Constant): return value_node.value elif isinstance(value_node, ast.BinOp): left = self._ExtractConstValue(value_node.left) right = self._ExtractConstValue(value_node.right) if left is None or right is None: return None if isinstance(value_node.op, ast.Add): return left + right elif isinstance(value_node.op, ast.Sub): return left - right elif isinstance(value_node.op, ast.Mult): return left * right elif isinstance(value_node.op, ast.Div): return left // right if isinstance(left, int) and isinstance(right, int) else left / right elif isinstance(value_node.op, ast.FloorDiv): return left // right elif isinstance(value_node.op, ast.Mod): return left % right elif isinstance(value_node.op, ast.Pow): return left ** right elif isinstance(value_node.op, ast.LShift): return left << right elif isinstance(value_node.op, ast.RShift): return left >> right elif isinstance(value_node.op, ast.BitOr): return left | right elif isinstance(value_node.op, ast.BitXor): return left ^ right elif isinstance(value_node.op, ast.BitAnd): return left & right elif isinstance(value_node, ast.UnaryOp): operand = self._ExtractConstValue(value_node.operand) if operand is None: return None if isinstance(value_node.op, ast.USub): return -operand elif isinstance(value_node.op, ast.UAdd): return +operand elif isinstance(value_node.op, ast.Invert): return ~operand elif isinstance(value_node, ast.Name): if value_node.id in getattr(self.Trans, 'SymbolTable', {}): info = self.Trans.SymbolTable[value_node.id] if info.IsDefine and info.DefineValue is not None: return info.DefineValue elif isinstance(value_node, ast.Call): if isinstance(value_node.func, ast.Attribute): if getattr(value_node.func.value, 'id', None) == 't': if value_node.args: return self._ExtractConstValue(value_node.args[0]) elif isinstance(value_node.func, ast.Name): if value_node.args: 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 _RegisterCDefineSymbol(self, FullName, DefineValue, lineno, FilePath): """Register CDefine constant to SymbolTable""" from lib.core.Handles.HandlesBase import CTypeInfo info = CTypeInfo() info.IsDefine = True info.DefineValue = DefineValue info.Lineno = lineno info.file = FilePath # 直接添加到 SymbolTable 字典 self.Trans.SymbolTable.insert(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 _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): Gen._export_funcs.add(FuncName) # t.State 标记的外部 C 函数必须保持原始名称,以便链接器解析 if FuncName in Gen.functions: return CReturnTypes = [] if Node.decorator_list: for decorator in Node.decorator_list: if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): if decorator.func.attr == 'CReturn': for arg in decorator.args: CReturnTypes.append(arg) IsPtr = False ReturnTypeInfo = None if Node.returns: try: if isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): ReturnTypeInfo = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.returns) if not isinstance(ReturnTypeInfo, CTypeInfo): ReturnTypeInfo = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable) else: ReturnTypeInfo = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable) if ReturnTypeInfo and ReturnTypeInfo.IsStr: ReturnTypeInfo = CTypeInfo() ReturnTypeInfo.BaseType = t.CChar() ReturnTypeInfo.PtrCount = 1 if ReturnTypeInfo is None: ReturnTypeInfo = CTypeInfo() ReturnTypeInfo.BaseType = t.CInt() IsPtr = ReturnTypeInfo.IsPtr except Exception: # 回退:返回类型解析失败时使用默认 CInt ReturnTypeInfo = CTypeInfo() ReturnTypeInfo.BaseType = t.CInt() else: ReturnTypeInfo = CTypeInfo() ReturnTypeInfo.BaseType = t.CInt() ReturnType = Gen._ctype_to_llvm(ReturnTypeInfo) if (isinstance(ReturnType, ir.VoidType) or (isinstance(ReturnType, ir.PointerType) and isinstance(ReturnType.pointee, ir.VoidType))) and Node.returns: RetAnnName = None if isinstance(Node.returns, ast.Name): RetAnnName = Node.returns.id elif isinstance(Node.returns, ast.Attribute): RetAnnName = Node.returns.attr elif isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): left = Node.returns.left if isinstance(left, ast.Name): RetAnnName = left.id elif isinstance(left, ast.Attribute): RetAnnName = left.attr if RetAnnName and RetAnnName in Gen.structs: ReturnType = ir.PointerType(Gen.structs[RetAnnName]) elif IsPtr or RetAnnName: ReturnType = ir.PointerType(ir.IntType(8)) ParamTypes = [] is_method = is_class_method or '.__' in FuncName class_name_for_method = FuncName.split('.')[0] if '.' in FuncName else None class_is_cpython = class_name_for_method and self.Trans.SymbolTable.is_struct(class_name_for_method) for i, Arg in enumerate(Node.args.args): if i == 0 and is_method: # self parameter of a method should always be a pointer to the struct ParamType = ir.PointerType(Gen.structs.get(class_name_for_method or FuncName.split('.')[0], ir.IntType(8))) elif Arg.annotation: try: if isinstance(Arg.annotation, ast.BinOp) and isinstance(Arg.annotation.op, ast.BitOr): ParamTypeInfo = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Arg.annotation) if ParamTypeInfo is None or not isinstance(ParamTypeInfo, CTypeInfo): ParamTypeInfo = CTypeInfo.FromNode(Arg.annotation, self.Trans.SymbolTable) else: ParamTypeInfo = CTypeInfo.FromNode(Arg.annotation, self.Trans.SymbolTable) if ParamTypeInfo is None: ParamTypeInfo = CTypeInfo() ParamTypeInfo.BaseType = t.CInt() ParamIsPtr = ParamTypeInfo.IsPtr if i == 0 and is_method and Arg.arg == 'self' and class_is_cpython: ParamIsPtr = True ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) if ParamTypeInfo.IsStr: ParamTypeInfo = CTypeInfo() ParamTypeInfo.BaseType = t.CChar() ParamTypeInfo.PtrCount = 1 ParamIsPtr = True ParamType = Gen._ctype_to_llvm(ParamTypeInfo) except Exception: # 回退:参数类型解析失败时使用默认 i32 ParamType = ir.IntType(32) if isinstance(ParamType, ir.VoidType) or (isinstance(ParamType, ir.PointerType) and isinstance(ParamType.pointee, ir.VoidType)): AnnName = None if isinstance(Arg.annotation, ast.Name): AnnName = Arg.annotation.id elif isinstance(Arg.annotation, ast.Attribute): AnnName = Arg.annotation.attr elif isinstance(Arg.annotation, ast.BinOp) and isinstance(Arg.annotation.op, ast.BitOr): left = Arg.annotation.left if isinstance(left, ast.Name): AnnName = left.id elif isinstance(left, ast.Attribute): AnnName = left.attr if AnnName and AnnName in Gen.structs: ParamType = ir.PointerType(Gen.structs[AnnName]) else: ParamType = ir.PointerType(ir.IntType(8)) else: ParamType = ir.IntType(32) ParamTypes.append(ParamType) if CReturnTypes: for _ in CReturnTypes: ParamTypes.append(ir.PointerType(ir.IntType(8))) IsVariadic = Node.args.vararg is not None FuncType = ir.FunctionType(ReturnType, ParamTypes, var_arg=IsVariadic) _is_export = False if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.IsState: _is_export = True if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.Storage and isinstance(ReturnTypeInfo.Storage, t.CExport) and not ReturnTypeInfo.IsState: Gen._export_funcs.add(FuncName) _is_export = True if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.Storage and isinstance(ReturnTypeInfo.Storage, t.CExtern): Gen._export_funcs.add(FuncName) _is_export = True if not _is_export and Node.returns: _is_export = self._check_annotation_for_state(Node.returns) MangledName = Gen._mangle_func_name(FuncName, module_name=source_module_name) func = ir.Function(Gen.module, FuncType, name=MangledName) Gen.functions[MangledName] = func Gen.functions[FuncName] = func func_meta = FuncMeta.NONE if Node.decorator_list: for d in Node.decorator_list: if isinstance(d, ast.Name): if d.id == 'staticmethod': func_meta |= FuncMeta.STATIC_METHOD elif d.id == 'property': func_meta |= FuncMeta.PROPERTY_GETTER elif d.id == 'classmethod': func_meta |= FuncMeta.CLASS_METHOD elif isinstance(d, ast.Attribute): 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 not self.Trans.SymbolTable.has(FuncName): FuncInfo = CTypeInfo() FuncInfo.Name = FuncName FuncInfo.IsFunction = True FuncInfo.MetaList = func_meta self.Trans.SymbolTable.insert(FuncName, FuncInfo) else: 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 not self.Trans.SymbolTable.has(FullSymKey): FullFuncInfo = CTypeInfo() FullFuncInfo.Name = FullSymKey FullFuncInfo.IsFunction = True FullFuncInfo.MetaList = func_meta self.Trans.SymbolTable.insert(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 self.Trans.SymbolTable.has(BasePropKey): 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.insert(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 if reexport_module_names: for reexport_mod in reexport_module_names: if reexport_mod and reexport_mod != source_module_name: ReexportName2 = Gen._mangle_func_name(FuncName, module_name=reexport_mod) if ReexportName2 not in Gen.functions: Gen.functions[ReexportName2] = func def _EmitExternalClassDeclLlvm(self, Node, Gen, module_name=None, actual_module_name=None): ClassName = Node.name if hasattr(Node, 'type_params') and Node.type_params: return IsCenum = False IsCpythonObject = False IsCVTable = False if Node.bases: for base in Node.bases: if getattr(base, 'attr', None): if base.attr == 'CEnum' or base.attr == 'Enum' or base.attr == 'REnum': IsCenum = True break elif getattr(base, 'id', None): if base.id == 'CEnum' or base.id == 'Enum' or base.id == 'REnum': IsCenum = True break if not IsCenum and getattr(Node, 'decorator_list', None): for decorator in Node.decorator_list: if isinstance(decorator, ast.Attribute): if getattr(decorator.value, 'id', None) == 't': if decorator.attr == 'Object': IsCpythonObject = True elif decorator.attr == 'CVTable': IsCVTable = True elif isinstance(decorator, ast.Call): if isinstance(decorator.func, ast.Attribute): if getattr(decorator.func.value, 'id', None) == 't': if decorator.func.attr == 'Object': IsCpythonObject = True elif decorator.func.attr == 'CVTable': IsCVTable = True elif isinstance(decorator.func, ast.Name): if decorator.func.id == 'Object': IsCpythonObject = True elif decorator.func.id == 'CVTable': IsCVTable = True if ClassName.startswith('_') and not IsCpythonObject and not IsCenum: return IsPacked = False if getattr(Node, 'decorator_list', None): for decorator in Node.decorator_list: if isinstance(decorator, ast.Call): if isinstance(decorator.func, ast.Attribute): if getattr(decorator.func.value, 'id', None) == 'c' and decorator.func.attr == 'Attribute': for arg in decorator.args: if isinstance(arg, ast.Attribute): if isinstance(arg.value, ast.Attribute): if getattr(arg.value.value, 'id', None) == 't' and arg.value.attr == 'attr' and arg.attr == 'packed': IsPacked = True if IsPacked: Gen.class_packed.add(ClassName) if IsCpythonObject: CInfo = CTypeInfo() CInfo.Name = ClassName CInfo.IsCpythonObject = True CInfo.IsStruct = True if module_name: FullName = f"{module_name}.{ClassName}" self.Trans.SymbolTable.insert(FullName, CInfo) self.Trans.SymbolTable.insert(ClassName, CInfo) if IsCenum: EnumTypeNode = CTypeInfo() EnumTypeNode.Name = ClassName EnumTypeNode.BaseType = t.CEnum(ClassName) EnumTypeNode.IsEnum = True self.Trans.SymbolTable.insert(ClassName, EnumTypeNode) for item in Node.body: VarName = None if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): VarName = item.target.id elif isinstance(item, ast.Assign): for target in item.targets: if isinstance(target, ast.Name): VarName = target.id break if VarName is None: continue value = 0 if isinstance(item, ast.Assign) and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): value = item.value.value MemberNode = CTypeInfo() MemberNode.Name = VarName MemberNode.BaseType = t.CEnum(ClassName) MemberNode.value = value MemberNode.EnumName = ClassName MemberNode.Lineno = item.lineno MemberNode.IsEnumMember = True self.Trans.SymbolTable.insert(VarName, MemberNode) self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode) self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode) return self._TryLoadStructFromStub(ClassName, Gen) source_sha1 = None 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'): Gen.class_sha1_map = {} Gen.class_sha1_map[ClassName] = source_sha1 Gen._get_or_create_struct(ClassName, source_sha1=source_sha1, packed=IsPacked) if ClassName not in Gen.class_members: Gen.class_members[ClassName] = [] if ClassName not in Gen.class_member_defaults: Gen.class_member_defaults[ClassName] = {} if ClassName not in Gen.class_member_signeds: Gen.class_member_signeds[ClassName] = {} if ClassName not in Gen.class_member_bitfields: Gen.class_member_bitfields[ClassName] = {} if ClassName not in Gen.class_member_byteorders: Gen.class_member_byteorders[ClassName] = {} if ClassName not in Gen.class_member_bitoffsets: Gen.class_member_bitoffsets[ClassName] = {} has_methods = False for item in Node.body: if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): VarName = item.target.id try: TypeInfo = CTypeInfo.FromNode(item.annotation, self.Trans.SymbolTable) if TypeInfo is None: TypeInfo = CTypeInfo() TypeInfo.BaseType = t.CInt() MemberType = Gen._ctype_to_llvm(TypeInfo) if isinstance(MemberType, ir.VoidType): MemberType = ir.PointerType(ir.IntType(8)) if TypeInfo and TypeInfo.IsBitField: Gen.class_member_bitfields[ClassName][VarName] = TypeInfo.BitWidth else: Gen.class_members[ClassName].append((VarName, MemberType)) Gen.class_member_bitfields[ClassName][VarName] = 0 if TypeInfo and TypeInfo.ByteOrder: Gen.class_member_byteorders[ClassName][VarName] = TypeInfo.ByteOrder else: Gen.class_member_byteorders[ClassName][VarName] = "" if item.value: const = self._BuildScalarConstant(item.value, MemberType, Gen) if const: Gen.class_member_defaults[ClassName][VarName] = const except Exception: # 回退:类成员类型解析失败时使用默认 i32 Gen.class_members[ClassName].append((VarName, ir.IntType(32))) Gen.class_member_signeds[ClassName][VarName] = None Gen.class_member_bitfields[ClassName][VarName] = 0 elif isinstance(item, ast.FunctionDef): has_methods = True MethodName = item.name FuncFullName = f"{ClassName}.__init__" if MethodName == "__init__" else f"{ClassName}.__call__" if MethodName == "__call__" else f"{ClassName}.{MethodName}" is_item_static = any(isinstance(d, ast.Name) and d.id == 'staticmethod' for d in item.decorator_list) is_item_property = any(isinstance(d, ast.Name) and d.id == 'property' for d in item.decorator_list) is_item_classmethod = any(isinstance(d, ast.Name) and d.id == 'classmethod' for d in item.decorator_list) 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) # 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=DeclFuncName, args=item.args, body=item.body, decorator_list=item.decorator_list, returns=item.returns ) self._EmitExternalFuncDeclLlvm(FuncDeclNode, Gen, is_class_method=not is_item_static and not is_item_classmethod, source_module_name=module_name) func_meta = FuncMeta.NONE if is_item_static: func_meta |= FuncMeta.STATIC_METHOD if is_item_property: func_meta |= FuncMeta.PROPERTY_GETTER if is_item_classmethod: func_meta |= FuncMeta.CLASS_METHOD if is_item_prop_setter: func_meta |= FuncMeta.PROPERTY_SETTER if is_item_prop_getter: func_meta |= FuncMeta.PROPERTY_GETTER if is_item_prop_deleter: func_meta |= FuncMeta.PROPERTY_DELETER # setter/deleter 的 SymKey 使用带后缀的函数名 SymKey = DeclFuncName if not self.Trans.SymbolTable.has(SymKey): FuncInfo = CTypeInfo() FuncInfo.Name = SymKey FuncInfo.IsFunction = True FuncInfo.MetaList = func_meta self.Trans.SymbolTable.insert(SymKey, FuncInfo) else: 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 self.Trans.SymbolTable.has(FuncFullName): 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.insert(FuncFullName, PropInfo) if has_methods: if IsCVTable: Gen._cross_module_vtable_classes.add(ClassName) Gen.class_vtable.add(ClassName) NewFuncName = f'{ClassName}.__before_init__' if not Gen._has_function(NewFuncName) and (IsCpythonObject or IsCVTable): source_sha1 = None 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: StructPtrType = ir.PointerType(StructType) NewFuncType = ir.FunctionType(ir.VoidType(), [StructPtrType]) NewFunc = ir.Function(Gen.module, NewFuncType, name=MangledName) Gen.functions[NewFuncName] = NewFunc bitfields = Gen.class_member_bitfields.get(ClassName, {}) if any(v > 0 for v in bitfields.values()): current_bit_offset = 0 for name, _ in Gen.class_members.get(ClassName, []): bw = bitfields.get(name, 0) Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset current_bit_offset += bw def _TryLoadStructFromStub(self, class_name: str, Gen): cache_key = class_name if cache_key in self._struct_Load_cache: return self._struct_Load_cache[cache_key] = True import os, re ProjectRoot = self._find_project_root() temp_dir = os.path.join(ProjectRoot, 'temp') if not os.path.isdir(temp_dir): return if self._stub_cache_dir != temp_dir: self._stub_cache.clear() self._stub_cache_dir = temp_dir for filename in os.listdir(temp_dir): if not filename.endswith('.stub.ll'): 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() match = re.match(r'%"?((?:[a-f0-9]+\.)?(\w+))"?\s*=\s*type\s*(.*)', stripped) if match: full_name = match.group(1) short_name = match.group(2) struct_body = match.group(3).strip() 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 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 for full_name, struct_body, source_sha1 in self._stub_cache[class_name]: is_packed = struct_body.startswith('<{') and struct_body.endswith('}>') if struct_body and struct_body != 'opaque': elem_types = [] if is_packed: inner = struct_body[2:-1].strip() else: inner = struct_body.strip('{}') if inner: for elem in inner.split(','): elem = elem.strip() if elem: et = self._parse_llvm_type(elem, Gen, source_sha1=source_sha1) 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.is_opaque: st.set_body(*elem_types) if is_packed: Gen.class_packed.add(class_name) 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 def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen): import os ProjectRoot = self._find_project_root() temp_dir = os.path.join(ProjectRoot, 'temp') if class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0: return pyi_basename = stub_filename.replace('.stub.ll', '.pyi') pyi_path = os.path.join(temp_dir, pyi_basename) if self._pyi_cache_dir != temp_dir: self._pyi_cache.clear() self._pyi_cache_dir = temp_dir if pyi_path not in self._pyi_cache: if not os.path.isfile(pyi_path): self._pyi_cache[pyi_path] = None return try: with open(pyi_path, 'r', encoding='utf-8') as f: self._pyi_cache[pyi_path] = ast.parse(f.read()) except Exception: self._pyi_cache[pyi_path] = None return pyi_tree = self._pyi_cache[pyi_path] if pyi_tree is None: return source_sha1 = stub_filename.replace('.stub.ll', '') module_name = None 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.ModuleSha1Map.items(): if mod_sha1 == source_sha1: module_name = mod_name break if module_name is None and hasattr(Gen, 'module_sha1') and Gen.module_sha1 == source_sha1: module_name = '__self__' found = False for node in pyi_tree.body: if isinstance(node, ast.ClassDef) and node.name == class_name: self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name) found = True break if not found: return if class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0: return return def _BuildScalarConstant(self, value_node, target_type, Gen): """Build a scalar constant value from AST node""" if isinstance(target_type, ir.BaseStructType): return None if isinstance(value_node, ast.Constant): if isinstance(value_node.value, int): return ir.Constant(target_type, value_node.value) elif isinstance(value_node.value, str): str_val = value_node.value + '\x00' str_bytes = str_val.encode('utf-8') arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes)) gv_name = f"str_const_{id(value_node)}" gv = ir.GlobalVariable(Gen.module, arr_type, name=gv_name) gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) gv.linkage = 'internal' ptr_type = ir.PointerType(ir.IntType(8)) return ir.Constant(ptr_type, gv.reference) elif isinstance(value_node.value, bool): return ir.Constant(target_type, 1 if value_node.value else 0) elif isinstance(value_node, ast.Name): if value_node.id == 'True': return ir.Constant(target_type, 1) elif value_node.id == 'False': if isinstance(target_type, ir.PointerType): return ir.Constant(target_type, None) return ir.Constant(target_type, 0) return None def _LookupStubFuncType(self, func_name, Gen): """从 stub.ll 文件中查找指定函数的 LLVM 类型""" import os import re import llvmlite.ir as ir 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): return None if func_name not in self._stub_func_cache: for filename in os.listdir(temp_dir): if not filename.endswith('.stub.ll'): 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: 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 ret_str, params_str, is_variadic, source_sha1 = self._stub_func_cache[func_name] ret_type = self._parse_llvm_type(ret_str, Gen, source_sha1=source_sha1, create_structs=False) if ret_type is None: ret_type = ir.IntType(32) param_types = [] if params_str and params_str != 'void': depth = 0 current = '' for ch in params_str: if ch == ',' and depth == 0: p = self._strip_llvm_param_name(current.strip()) if p and p != '...': pt = self._parse_llvm_type(p, Gen, source_sha1=source_sha1, create_structs=False) if pt is not None: param_types.append(pt) else: param_types.append(ir.PointerType(ir.IntType(8))) current = '' else: if ch == '{': depth += 1 elif ch == '}': depth -= 1 current += ch p = self._strip_llvm_param_name(current.strip()) if p and p != '...': pt = self._parse_llvm_type(p, Gen, source_sha1=source_sha1, create_structs=False) if pt is not None: param_types.append(pt) else: param_types.append(ir.PointerType(ir.IntType(8))) return ir.FunctionType(ret_type, param_types, var_arg=is_variadic) def _strip_llvm_param_name(self, param_str): """剥离 LLVM IR 参数字符串中的参数名,仅保留类型部分""" import re param_str = param_str.strip() result = re.sub(r'\s+%[\w.]+\s*$', '', param_str) return result.strip() def _parse_simple_llvm_type(self, type_str): """解析简单的 LLVM 类型字符串(不创建结构体,避免副作用)""" import llvmlite.ir as ir type_str = type_str.strip() type_map = { 'void': ir.VoidType(), 'i1': ir.IntType(1), 'i8': ir.IntType(8), 'i16': ir.IntType(16), 'i32': ir.IntType(32), 'i64': ir.IntType(64), 'float': ir.FloatType(), 'double': ir.DoubleType(), } if type_str in type_map: return type_map[type_str] if type_str.endswith('*'): pointee = type_str[:-1].strip() pointee_type = self._parse_simple_llvm_type(pointee) if pointee_type: return ir.PointerType(pointee_type) return ir.PointerType(ir.IntType(8)) if type_str.startswith('[') and ']' in type_str: import re arr_match = re.match(r'\[(\d+)\s*x\s+(.+)\]', type_str) if arr_match: size = int(arr_match.group(1)) elem_type = self._parse_simple_llvm_type(arr_match.group(2).strip()) if elem_type: return ir.ArrayType(elem_type, size) if type_str.startswith('%'): return ir.PointerType(ir.IntType(8)) return None def _LoadDeclarationsFromStubLlvm(self, module_name: str, Gen): """从 stub.ll 文件加载函数声明和结构体定义到 Gen""" import os import re import llvmlite.ir as ir ProjectRoot = self._find_project_root() temp_dir = os.path.join(ProjectRoot, 'temp') if not os.path.isdir(temp_dir): return all_stub_files = [f for f in os.listdir(temp_dir) if f.endswith('.stub.ll')] source_sig_files = getattr(self.Trans, '_source_module_sig_files', {}) target_sha1 = None if module_name: for key, val in source_sig_files.items(): if key == module_name or key.endswith('.' + module_name): target_sha1 = os.path.basename(val).replace('.pyi', '') break if not target_sha1: ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) target_sha1 = ModuleSha1Map.get(module_name) if target_sha1: target_stub = f"{target_sha1}.stub.ll" if target_stub in all_stub_files: stub_files = [target_stub] else: stub_files = [] else: stub_files = [] needed_sha1s = set() for filename in stub_files: stub_path = os.path.join(temp_dir, filename) try: with open(stub_path, 'r', encoding='utf-8') as f: content = f.read() for line in content.splitlines(): stripped = line.strip() if stripped.startswith('%') and '=' in stripped and 'type' in stripped: for m in re.finditer(r'%\"?([a-f0-9]{16})\.', stripped): needed_sha1s.add(m.group(1)) elif stripped.startswith('declare '): for m in re.finditer(r'%\"?([a-f0-9]{16})\.', stripped): needed_sha1s.add(m.group(1)) 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) Load_order = preLoad_files + stub_files 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 try: with open(stub_path, 'r', encoding='utf-8') as f: content = f.read() for line in content.splitlines(): stripped = line.strip() if stripped.startswith('%') and '=' in stripped and 'type' in stripped: struct_def_match = re.match(r'%"?((?:[a-f0-9]+\.)?[\w.]+)"?\s*=\s*type\s*(.*)', stripped) if struct_def_match: raw_name = struct_def_match.group(1) if '.' in raw_name: clean_name = raw_name.split('.', 1)[1] else: clean_name = raw_name struct_body = struct_def_match.group(2).strip() if struct_body and struct_body != 'opaque': elem_types = [] is_packed = struct_body.startswith('<{') and struct_body.endswith('}>') if is_packed: inner = struct_body[2:-1].strip() else: inner = struct_body.strip('{}') if inner: for elem in inner.split(','): elem = elem.strip() if elem: elem_type = self._parse_llvm_type(elem, Gen, source_sha1=source_sha1) if elem_type is not None: elem_types.append(elem_type) else: elem_types.append(ir.IntType(32)) st = Gen._get_or_create_struct(clean_name, source_sha1=source_sha1, packed=is_packed) if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): try: st.set_body(*elem_types) 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: continue func_name_match = stripped.split('@', 1) if len(func_name_match) < 2: continue func_name = func_name_match[1].split('(', 1)[0].strip().strip('"') if not module_name: continue try: func_type = self._parse_llvm_declare(stripped, Gen) if func_type is not None: if func_name in Gen.functions: existing_func = Gen.functions[func_name] existing_ftype = getattr(existing_func, 'ftype', None) or existing_func.type.pointee if str(existing_ftype) != str(func_type): func = ir.Function(Gen.module, func_type, name=func_name) Gen.functions[func_name] = func if '.' in func_name: short_name = func_name.split('.', 1)[1] Gen.functions[short_name] = func else: func = ir.Function(Gen.module, func_type, name=func_name) Gen.functions[func_name] = func if '.' in func_name: short_name = func_name.split('.', 1)[1] Gen.functions[short_name] = func 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 语句""" import re import llvmlite.ir as ir match = re.match(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$', declare_line) if not match: return None ret_type_str = match.group(1).strip() params_str = match.group(3).strip() ret_type = self._parse_llvm_type(ret_type_str, Gen) if ret_type is None: return None param_types = [] if params_str and params_str != 'void': depth = 0 current = '' for ch in params_str: if ch == ',' and depth == 0: p = self._strip_llvm_param_name(current.strip()) if p and p != '...': pt = self._parse_llvm_type(p, Gen) if pt is not None: param_types.append(pt) current = '' else: if ch == '{': depth += 1 elif ch == '}': depth -= 1 current += ch p = self._strip_llvm_param_name(current.strip()) if p and p != '...': pt = self._parse_llvm_type(p, Gen) if pt is not None: param_types.append(pt) is_variadic = '...' in params_str return ir.FunctionType(ret_type, param_types, var_arg=is_variadic) def _parse_llvm_type(self, type_str: str, Gen, source_sha1=None, create_structs=True): """解析 LLVM 类型字符串""" import re import llvmlite.ir as ir type_str = type_str.strip() type_map = { 'void': ir.VoidType(), 'i1': ir.IntType(1), 'i8': ir.IntType(8), 'i16': ir.IntType(16), 'i32': ir.IntType(32), 'i64': ir.IntType(64), 'float': ir.FloatType(), 'double': ir.DoubleType(), } if type_str in type_map: return type_map[type_str] if type_str.startswith('{') and type_str.endswith('}'): inner = type_str[1:-1].strip() field_types = [] depth = 0 current = '' for ch in inner: if ch == ',' and depth == 0: field_types.append(current.strip()) current = '' else: if ch == '{': depth += 1 elif ch == '}': depth -= 1 current += ch if current.strip(): field_types.append(current.strip()) llvm_fields = [] for ft in field_types: parsed = self._parse_llvm_type(ft, Gen, source_sha1=source_sha1, create_structs=create_structs) if parsed is not None: llvm_fields.append(parsed) else: return None if llvm_fields: return ir.LiteralStructType(llvm_fields) return None if type_str.endswith('*'): pointee = type_str[:-1].strip() pointee_type = self._parse_llvm_type(pointee, Gen, source_sha1=source_sha1, create_structs=create_structs) if pointee_type is not None: return ir.PointerType(pointee_type) return ir.PointerType(ir.IntType(8)) if type_str.startswith('[') and ']' in type_str: arr_match = re.match(r'\[(\d+)\s*x\s+(.+)\]', type_str) if arr_match: size = int(arr_match.group(1)) elem_type = self._parse_llvm_type(arr_match.group(2).strip(), Gen, source_sha1=source_sha1, create_structs=create_structs) if elem_type is not None: return ir.ArrayType(elem_type, size) typedef_names = {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'} struct_match = re.match(r'%"?((?:[a-f0-9]+\.)?[\w.]+)"?', type_str) if struct_match: raw_name = struct_match.group(1) actual_source_sha1 = source_sha1 if '.' in raw_name: parts = raw_name.split('.', 1) if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): actual_source_sha1 = parts[0] struct_name = parts[1] else: struct_name = raw_name if struct_name not in typedef_names: if create_structs: return Gen._get_or_create_struct(struct_name, source_sha1=actual_source_sha1) else: existing = Gen.structs.get(struct_name) if existing is not None: return existing return None return ir.IntType(32) return None def _strip_param_name(self, param_str): """从 LLVM 参数字符串中剥离参数名,只保留类型部分 例如: '%"sha1.struct_name"* %param_name' -> '%"sha1.struct_name"*' 'i8* %path' -> 'i8*' 'i32' -> 'i32' """ import re param_str = param_str.strip() stripped = re.sub(r'\s+%[\w.]+\s*$', '', param_str) return stripped.strip()