1968 lines
109 KiB
Python
1968 lines
109 KiB
Python
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()
|
||
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._ImportedModules.add(name)
|
||
if alias.asname:
|
||
self.Trans._ImportAliases[alias.asname] = name
|
||
# 同时在符号表中注册模块别名,以便 CTypeInfo.FromNode 能解析
|
||
AliasInfo = CTypeInfo()
|
||
AliasInfo.IsModuleAlias = True
|
||
AliasInfo.ResolvedModule = name
|
||
self.Trans.SymbolTable[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.get(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.get(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 name in self.Trans.SymbolTable:
|
||
self.Trans.SymbolTable[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[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()
|
||
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:
|
||
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()
|
||
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 ''
|
||
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._ImportAliases[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._ImportAliases[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[VarName] = ValueTypeInfo
|
||
if module_name:
|
||
FullName = f"{module_name}.{VarName}"
|
||
self.Trans.SymbolTable[FullName] = ValueTypeInfo
|
||
return
|
||
TTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation)
|
||
if TTypeInfo:
|
||
TTypeInfo.IsTypedef = True
|
||
TTypeInfo.Name = VarName
|
||
self.Trans.SymbolTable[VarName] = TTypeInfo
|
||
if module_name:
|
||
FullName = f"{module_name}.{VarName}"
|
||
self.Trans.SymbolTable[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[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 class_name_for_method in self.Trans.SymbolTable and self.Trans.SymbolTable[class_name_for_method].IsCpythonObject
|
||
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 FuncName not in self.Trans.SymbolTable:
|
||
FuncInfo = CTypeInfo()
|
||
FuncInfo.Name = FuncName
|
||
FuncInfo.IsFunction = True
|
||
FuncInfo.MetaList = func_meta
|
||
self.Trans.SymbolTable[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 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
|
||
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[FullName] = CInfo
|
||
self.Trans.SymbolTable[ClassName] = CInfo
|
||
if IsCenum:
|
||
EnumTypeNode = CTypeInfo()
|
||
EnumTypeNode.Name = ClassName
|
||
EnumTypeNode.BaseType = t.CEnum(ClassName)
|
||
EnumTypeNode.IsEnum = True
|
||
self.Trans.SymbolTable[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[VarName] = MemberNode
|
||
self.Trans.SymbolTable[f"{ClassName}.{VarName}"] = MemberNode
|
||
self.Trans.SymbolTable[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 SymKey not in self.Trans.SymbolTable:
|
||
FuncInfo = CTypeInfo()
|
||
FuncInfo.Name = SymKey
|
||
FuncInfo.IsFunction = True
|
||
FuncInfo.MetaList = func_meta
|
||
self.Trans.SymbolTable[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 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)
|
||
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()
|