2677 lines
156 KiB
Python
2677 lines
156 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
|
||
import ast
|
||
import inspect
|
||
import os
|
||
import re
|
||
import hashlib
|
||
import llvmlite.ir as ir
|
||
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
|
||
from lib.core.VLogger import get_logger as _vlog
|
||
from lib.constants.config import mode as _config_mode
|
||
from lib.includes import t
|
||
from lib.includes.t import CTypeRegistry
|
||
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName
|
||
|
||
|
||
class ImportHandle(BaseHandle):
|
||
_struct_Load_cache = {}
|
||
_pyi_cache = {}
|
||
_pyi_cache_dir = None
|
||
_project_root_cache = None
|
||
# 类级 stub 索引:class_name -> list of (full_name, struct_body, file_sha1, from_output)
|
||
# 一次扫描所有 .stub.ll 文件构建索引,避免 _TryLoadStructFromStub 每次调用都全量扫描
|
||
# (原 609 次调用 × ~100 stub 文件 × ~50 行 = 5.5M 正则匹配 → 一次扫描 ~5000 匹配)
|
||
_stub_index: dict[str, list[tuple[str, str, str, bool]]] = {}
|
||
_stub_index_signature: tuple[int, int] | None = None # (output_stub_count, temp_stub_count)
|
||
|
||
def __init__(self, translator: "Translator") -> None:
|
||
super().__init__(translator)
|
||
# 实例级去重集合:每个 translator 独立跟踪已加载的 .pyi 和已发射的模块声明。
|
||
# 若作为类变量共享,main.py 编译时加载的 __nodes.py 会阻止 __parser.py 编译时
|
||
# 重新加载,导致函数未注册到 __parser.py 的 Gen.functions(NamedExpr bug 根因)。
|
||
self._loaded_pyi_paths: set[str] = set()
|
||
self._emitted_module_decls: set[tuple] = set()
|
||
|
||
def _reset_file_cache(self) -> None:
|
||
self._struct_Load_cache.clear()
|
||
self._loaded_pyi_paths.clear()
|
||
self._emitted_module_decls.clear()
|
||
# stub 索引在文件间保留(同一 worker 进程内),但标记需检查是否过期
|
||
# 不清除 _stub_index,通过 _stub_index_signature 在下次使用时校验新鲜度
|
||
|
||
def _find_project_root(self) -> str:
|
||
if self._project_root_cache is not None:
|
||
return self._project_root_cache
|
||
# 优先使用 translator 上显式设置的 _temp_dir(由 Phase2Translator._translate_file 传入),
|
||
# 这样可以正确处理 include 文件——其源文件目录不在项目根目录下,
|
||
# 从源文件目录向上搜索 temp/output 会失败并回退到错误的路径。
|
||
temp_dir_attr = getattr(self.Trans, '_temp_dir', None)
|
||
if temp_dir_attr and os.path.isdir(temp_dir_attr):
|
||
project_root = os.path.dirname(os.path.abspath(temp_dir_attr))
|
||
if os.path.isdir(os.path.join(project_root, 'temp')) and os.path.isdir(os.path.join(project_root, 'output')):
|
||
self._project_root_cache = project_root
|
||
return project_root
|
||
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
|
||
# 回退到当前工作目录(编译运行时的项目根目录)
|
||
cwd = os.getcwd()
|
||
if os.path.isdir(os.path.join(cwd, 'temp')) and os.path.isdir(os.path.join(cwd, 'output')):
|
||
self._project_root_cache = cwd
|
||
return cwd
|
||
if current_file:
|
||
self._project_root_cache = current_dir
|
||
return current_dir
|
||
self._project_root_cache = cwd
|
||
return cwd
|
||
|
||
def _EmitImportDeclarationsLlvm(self, Node: ast.Import, Gen: LlvmGeneratorMixin) -> None:
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
for alias in Node.names:
|
||
name = alias.name
|
||
if name in ('c', 't'):
|
||
if alias.asname:
|
||
self.Trans.SymbolTable.import_aliases[alias.asname] = name
|
||
AliasInfo = CTypeInfo()
|
||
AliasInfo.IsModuleAlias = True
|
||
AliasInfo.ResolvedModule = name
|
||
self.Trans.SymbolTable.insert(alias.asname, AliasInfo)
|
||
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: ast.ImportFrom, Gen: LlvmGeneratorMixin, module: str) -> None:
|
||
"""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]
|
||
info = self.Trans.SymbolTable.lookup(name)
|
||
if info:
|
||
self.Trans.SymbolTable.insert(asname, info)
|
||
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: ast.ImportFrom, Gen: LlvmGeneratorMixin) -> None:
|
||
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 = {}
|
||
# 注册 t/c 类型别名到 _t_type_symbols(CType 子类)
|
||
t_type_syms = self.Trans.SymbolTable._t_type_symbols
|
||
for alias in Node.names:
|
||
name = alias.name
|
||
asname = alias.asname or name
|
||
self.Trans._t_c_imported_names[asname] = (module, name)
|
||
# 仅注册 CType 子类(CEnum/CUnion/CStruct/REnum 等)
|
||
if module == 't':
|
||
cls = getattr(t, name, None)
|
||
if isinstance(cls, type) and issubclass(cls, t.CType):
|
||
t_type_syms[asname] = cls
|
||
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 _RegisterCrossModuleDocstrings(self, pyi_path: str, Gen: LlvmGeneratorMixin, module_prefix: str | None) -> None:
|
||
"""从 {sha1}.doc.json 加载跨模块 docstring 注册到 Gen._doc_strings。
|
||
|
||
- 长名 {module_prefix}.{symbol} 总是注册
|
||
- 短名 {symbol} 只在不存在时注册(避免覆盖本模块的)
|
||
- slice_level != 0(优化开启)时注册为 None
|
||
- 跨模块 docstring 在 _get_doc_ptr 访问时创建本地常量副本
|
||
"""
|
||
if not module_prefix:
|
||
return
|
||
basename: str = os.path.basename(pyi_path)
|
||
# 若传入 .py 文件,则从 _source_module_sig_files 查找对应的 {sha1}.pyi
|
||
if basename.endswith('.py'):
|
||
source_sig_files = getattr(self.Trans, '_source_module_sig_files', None)
|
||
if source_sig_files:
|
||
sig_path = source_sig_files.get(module_prefix)
|
||
if sig_path and os.path.isfile(sig_path):
|
||
pyi_path = sig_path
|
||
basename = os.path.basename(pyi_path)
|
||
else:
|
||
return
|
||
else:
|
||
return
|
||
if not basename.endswith('.pyi'):
|
||
return
|
||
sha1: str = basename[:-len('.pyi')]
|
||
shared_doc_meta = getattr(self.Trans, '_shared_doc_meta', None)
|
||
if not shared_doc_meta:
|
||
return
|
||
docs: dict[str, str] | None = shared_doc_meta.get(sha1)
|
||
if not docs:
|
||
return
|
||
trans = getattr(Gen, '_Trans', None)
|
||
slice_level: int = getattr(trans, 'SliceLevel', 3)
|
||
optimized: bool = slice_level != 0
|
||
for symbol, doc in docs.items():
|
||
long_key: str = f"{module_prefix}.{symbol}"
|
||
if optimized:
|
||
Gen._doc_strings[long_key] = None
|
||
if symbol not in Gen._doc_strings:
|
||
Gen._doc_strings[symbol] = None
|
||
else:
|
||
Gen._doc_strings[long_key] = doc
|
||
if symbol not in Gen._doc_strings:
|
||
Gen._doc_strings[symbol] = doc
|
||
|
||
def _LoadModuleDeclarationsFromFile(self, pyi_path: str, Gen: LlvmGeneratorMixin, module_name: str | None = None, register_module_name: str | None = None, actual_module_name: str | None = None, reexport_package: str | None = None) -> None:
|
||
if not pyi_path or not os.path.isfile(pyi_path):
|
||
return
|
||
# 去重:同一 .pyi 文件只加载一次(菱形依赖下避免重复 read+parse)
|
||
abs_pyi = os.path.abspath(pyi_path)
|
||
if abs_pyi in self._loaded_pyi_paths:
|
||
return
|
||
self._loaded_pyi_paths.add(abs_pyi)
|
||
# 注册跨模块 __doc__ docstring 到 Gen._doc_strings
|
||
self._RegisterCrossModuleDocstrings(pyi_path, Gen, register_module_name or module_name)
|
||
try:
|
||
with open(pyi_path, 'r', encoding='utf-8') as f:
|
||
ModuleCode = f.read()
|
||
ModuleTree = ast.parse(ModuleCode)
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||
return
|
||
|
||
# 计算源模块名(从 .pyi 文件路径推导)。
|
||
# 治本修复:module_name 参数可能来自 alias.name(如 `from stmts import Module`
|
||
# 中的 'Module' 是类名而非模块名),直接传给 _EmitExternalClassDeclLlvm 会导致
|
||
# ModuleSha1Map 查找失败 → _self_struct_sha1=None → 使用错误的同名 struct。
|
||
# 使用文件名推导的模块名(如 'stmts')作为 source_module_name 才是正确的。
|
||
# 对于 sha1 命名的 .pyi 文件(如 e64295c03b564a53.pyi),文件名是 sha1 而非
|
||
# 模块名,需回退到 actual_module_name 或 module_name。
|
||
file_module_name = os.path.splitext(os.path.basename(pyi_path))[0]
|
||
if len(file_module_name) == 16 and all(_c in '0123456789abcdef' for _c in file_module_name):
|
||
file_module_name = actual_module_name or module_name or file_module_name
|
||
|
||
for node in ModuleTree.body:
|
||
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
|
||
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,
|
||
}
|
||
# 治本修复:使用 file_module_name(源模块名)而非 module_name(可能是别名/类名)。
|
||
# 这样 _EmitExternalClassDeclLlvm 内的 ModuleSha1Map 查找才能命中,
|
||
# _EmitExternalFuncDeclLlvm 才能拿到正确的 source_module_name → 正确的 struct sha1。
|
||
# 注意:actual_module_name 保持原值(可能为 None),不回退到 module_name(可能是别名)
|
||
self._EmitExternalClassDeclLlvm(node, Gen, module_name=file_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
|
||
# 治本修复:用包目录名(如 'json')而非 register_module_name(当前编译模块名,如 '_dict')。
|
||
# 当 _dict.py 导入 json 包,递归处理 json/__init__.py 中的 `from .__parser import parse` 时,
|
||
# register_module_name='_dict' 会导致 actual_module='_dict.__parser',
|
||
# 使 _mangle_func_name 查找失败回退到 self.module_sha1,生成错误的 mangled name。
|
||
# 包名推导优先级:
|
||
# 1. module_name(如果它在 ModuleSha1Map 中,说明是已注册的包名,如 'json')
|
||
# — 处理 sha1 命名的 .pyi 文件(parent_dir 是 temp 目录时)
|
||
# 2. os.path.basename(parent_dir)(原始路径推导,如 'json')
|
||
if module_name and module_name in Gen.ModuleSha1Map:
|
||
pkg_basename = module_name
|
||
else:
|
||
pkg_basename = os.path.basename(parent_dir)
|
||
for ext in SearchExtensions:
|
||
candidate = sub_path_base + ext
|
||
if os.path.isfile(candidate):
|
||
actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module
|
||
self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen)
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
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"{pkg_basename}.{node.module}" if pkg_basename else node.module
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module)
|
||
if target_sha1:
|
||
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)
|
||
# 治本修复:用包目录名(如 'json')而非 register_module_name(当前编译模块名,如 '_dict')。
|
||
# 同 if node.module 分支的修复逻辑。
|
||
if module_name and module_name in Gen.ModuleSha1Map:
|
||
pkg_basename = module_name
|
||
else:
|
||
pkg_basename = os.path.basename(mod_dir)
|
||
for alias in node.names:
|
||
found_alias = False
|
||
for ext in SearchExtensions:
|
||
sub_path = os.path.join(mod_dir, alias.name + ext)
|
||
if os.path.isfile(sub_path):
|
||
sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name
|
||
if not getattr(self.Trans, '_ImportedModules', None):
|
||
self.Trans._ImportedModules = set()
|
||
self.Trans._ImportedModules.add(sub_ModulePath)
|
||
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"{pkg_basename}.{alias.name}" if pkg_basename else alias.name
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name)
|
||
if target_sha1:
|
||
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:
|
||
_vlog().warning(
|
||
f"HandlesImports[_LoadModuleDeclarationsFromFile]: 忽略节点 "
|
||
f"{type(node).__name__} {getattr(node, 'name', '?')} 异常: {_e}",
|
||
exc_info=_e
|
||
)
|
||
continue
|
||
|
||
def _get_current_module_name(self) -> str | None:
|
||
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: str, actual_module: str, short_module: str, Gen: LlvmGeneratorMixin) -> None:
|
||
"""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:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
|
||
def _LoadPackageInitForRelativeImport(self, mod_dir: str, Gen: LlvmGeneratorMixin, register_module_name: str | None) -> str:
|
||
"""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: str, Gen: LlvmGeneratorMixin, register_module_name: str | None = None) -> None:
|
||
# 去重:同一模块声明只发射一次(避免多个 import 同一模块时重复加载)
|
||
decl_key = (module_name, register_module_name)
|
||
if decl_key in self._emitted_module_decls:
|
||
return
|
||
self._emitted_module_decls.add(decl_key)
|
||
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()
|
||
|
||
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
|
||
# 注册跨模块 __doc__ docstring(module_name 是被导入模块名)
|
||
if FullModulePath:
|
||
self._RegisterCrossModuleDocstrings(FullModulePath, Gen, module_name)
|
||
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', '?'))
|
||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
||
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: ast.AnnAssign | ast.Assign, Gen: LlvmGeneratorMixin, module_name: str | None = None, ModulePath: str | None = None) -> 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
|
||
TypeInfo = None
|
||
if VarName in Gen.module.globals:
|
||
existing = Gen.module.globals[VarName]
|
||
if isinstance(existing, ir.GlobalVariable):
|
||
return
|
||
if Node.annotation:
|
||
parse_result = ParseListAnnotation(Node.annotation)
|
||
if parse_result and not parse_result.is_pointer:
|
||
# list[type, count] 数组声明
|
||
elem_type_node = parse_result.elem_type_node
|
||
count_node = parse_result.count_node
|
||
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
|
||
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:
|
||
# 检查符号是否已存在且已正确解析(OriginalType 已设置),避免覆盖
|
||
existing_sym = self.Trans.SymbolTable.lookup(VarName) if hasattr(self.Trans.SymbolTable, 'lookup') else None
|
||
if existing_sym and isinstance(existing_sym, CTypeInfo) and existing_sym.IsTypedef and existing_sym.OriginalType:
|
||
return
|
||
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):
|
||
# GetCTypeInfo 对 t.CInt 等 Attribute 返回的 BaseType 可能是 CType 基类实例
|
||
# (而非具体子类如 CInt),导致 _resolve_typedef_int_width 无法解析位宽。
|
||
# 此时从 Node.value 提取类型名,设置 OriginalType 为字符串,
|
||
# 让 _resolve_typedef_int_width 通过 CTypeRegistry.GetClassByName 解析。
|
||
if ValueTypeInfo.BaseType and type(ValueTypeInfo.BaseType) is t.CType:
|
||
if isinstance(Node.value, ast.Attribute) and getattr(Node.value.value, 'id', None) == 't':
|
||
type_name: str = Node.value.attr
|
||
resolved_cls = CTypeRegistry.GetClassByName(type_name)
|
||
if resolved_cls is not None:
|
||
ValueTypeInfo.OriginalType = type_name
|
||
ValueTypeInfo.IsTypedef = True
|
||
ValueTypeInfo.Name = VarName
|
||
# 设置 OriginalType,确保 _resolve_name 能解析(MergeTypes 不为 BinOp 结果设置 OriginalType)
|
||
if not ValueTypeInfo.OriginalType:
|
||
OriginalCopy: CTypeInfo = ValueTypeInfo.Copy()
|
||
OriginalCopy.IsTypedef = False
|
||
OriginalCopy.Name = ''
|
||
ValueTypeInfo.OriginalType = OriginalCopy
|
||
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)
|
||
# 但要先检查:如果符号已经存在且是 typedef,不覆盖它
|
||
existing = self.Trans.SymbolTable.lookup(VarName)
|
||
if not (existing and existing.IsTypedef and existing.OriginalType):
|
||
self._RegisterCDefineSymbol(VarName, DefineValue, Node.lineno, ModulePath or 'unknown')
|
||
# 如果提供了模块名,也注册带模块前缀的键名
|
||
if module_name:
|
||
FullName = f"{module_name}.{VarName}"
|
||
existing_full = self.Trans.SymbolTable.lookup(FullName)
|
||
if not (existing_full and existing_full.IsTypedef and existing_full.OriginalType):
|
||
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)
|
||
# 外部全局变量声明:只声明不定义(external linkage),避免多重定义
|
||
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)
|
||
gv.linkage = 'external'
|
||
|
||
def _ExtractValue(self, value_node: ast.AST, target_type: ir.Type, var_name: str | None = None) -> ir.Constant | 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: ast.AST) -> object:
|
||
"""Extract constant value from AST node for SymbolTable registration
|
||
支持常量、一元运算符、二元运算符和常量引用
|
||
"""
|
||
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: ast.AST) -> str | None:
|
||
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: str, DefineValue: object, lineno: int, FilePath: str) -> None:
|
||
"""Register CDefine constant to SymbolTable"""
|
||
# 防御:新值为 None 但已存在非 None 值时保留旧值。
|
||
# 翻译期间声明发射调用 _ExtractConstValue(不支持 ast.Attribute 形式如 t.CSizeT().Size),
|
||
# 会返回 None 并覆盖共享符号表中已由 ConstEvaluator 正确求值的值(如 8)。
|
||
# 共享符号表在 _build_shared_symbol_data 阶段已正确求值,此处不应覆盖。
|
||
if DefineValue is None:
|
||
existing: Any = self.Trans.SymbolTable.lookup(FullName)
|
||
if existing and existing.IsDefine and existing.DefineValue is not None:
|
||
return
|
||
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: ast.AST) -> bool:
|
||
return (AnnotationContainsName(annotation, 'CExport') or
|
||
AnnotationContainsName(annotation, 'CExtern') or
|
||
AnnotationContainsName(annotation, 'State'))
|
||
|
||
def _check_decorators_for_state(self, Node: ast.FunctionDef) -> bool:
|
||
"""检查装饰器列表中是否包含 @t.CExport / @t.CExtern / @t.State(t.CExport 既能作返回值注解也能作装饰器)"""
|
||
if not Node.decorator_list:
|
||
return False
|
||
for decorator in Node.decorator_list:
|
||
if (AnnotationContainsName(decorator, 'CExport') or
|
||
AnnotationContainsName(decorator, 'CExtern') or
|
||
AnnotationContainsName(decorator, 'State')):
|
||
return True
|
||
return False
|
||
|
||
def _EmitExternalFuncDeclLlvm(self, Node: ast.FunctionDef, Gen: LlvmGeneratorMixin, is_class_method: bool = False, source_module_name: str | None = None, register_module_name: str | None = None, reexport_module_names: list[str] | None = None) -> None:
|
||
FuncName = Node.name
|
||
if (Node.returns and self._check_annotation_for_state(Node.returns)) or self._check_decorators_for_state(Node):
|
||
Gen._export_funcs.add(FuncName) # t.State 标记的外部 C 函数必须保持原始名称,以便链接器解析
|
||
if source_module_name and source_module_name not in ('c', 't'):
|
||
_src_sha1 = Gen.ModuleSha1Map.get(source_module_name)
|
||
if not _src_sha1:
|
||
_init_name = f"{source_module_name}.__init__"
|
||
if _init_name in Gen.ModuleSha1Map:
|
||
_src_sha1 = Gen.ModuleSha1Map[_init_name]
|
||
if _src_sha1:
|
||
Gen._export_extern_funcs[FuncName] = _src_sha1
|
||
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:
|
||
ReturnTypeInfo = self.ResolveAnnotationType(Node.returns)
|
||
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 as e: # 回退:返回类型解析失败时使用默认 CInt
|
||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
||
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))
|
||
# __new__ 方法应返回类指针类型(堆分配替换),与 _GetFunctionSignatureLlvm/_EmitFunctionLlvm 保持一致
|
||
# .pyi 生成器对无返回类型注解的 __new__ 默认使用 t.CInt,这里修正为 ClassName*
|
||
if '.__new__' in FuncName:
|
||
_np = FuncName.split('.')
|
||
if len(_np[0]) == 16 and all(_c in '0123456789abcdef' for _c in _np[0]):
|
||
_new_class_name = _np[1]
|
||
else:
|
||
_new_class_name = _np[0]
|
||
# 治本修复:优先用 source sha1 查找正确的 struct,避免同名类冲突时返回错误类型
|
||
_new_struct = None
|
||
_new_src_sha1: str | None = None
|
||
if source_module_name and hasattr(Gen, 'ModuleSha1Map'):
|
||
_new_src_sha1 = Gen.ModuleSha1Map.get(source_module_name)
|
||
if not _new_src_sha1:
|
||
_init_name = f"{source_module_name}.__init__"
|
||
if _init_name in Gen.ModuleSha1Map:
|
||
_new_src_sha1 = Gen.ModuleSha1Map[_init_name]
|
||
if _new_src_sha1 and _new_class_name:
|
||
_new_struct = Gen.structs.get(f"{_new_src_sha1}.{_new_class_name}")
|
||
if _new_struct is None:
|
||
_new_struct = Gen.structs.get(_new_class_name)
|
||
if _new_struct is None:
|
||
_full_name = FuncName.split('.__new__')[0]
|
||
_new_struct = Gen.structs.get(_full_name)
|
||
if _new_struct:
|
||
if isinstance(_new_struct, ir.PointerType):
|
||
ReturnType = _new_struct
|
||
else:
|
||
ReturnType = ir.PointerType(_new_struct)
|
||
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)
|
||
# 治本修复:通过 source_module_name 获取源模块 sha1,查找正确的 struct。
|
||
# 当存在同名类冲突(如 stmts.py:Module 和 __module.py:Module)时,
|
||
# Gen.structs[short_name] 可能指向错误模块的 struct(set_body 已锁定错误布局)。
|
||
# 优先查找以 sha1 为前缀的完整 key,避免使用错误的 struct 作为 self 参数类型。
|
||
_self_struct_sha1: str | None = None
|
||
if source_module_name and hasattr(Gen, 'ModuleSha1Map'):
|
||
_self_struct_sha1 = Gen.ModuleSha1Map.get(source_module_name)
|
||
if not _self_struct_sha1:
|
||
_init_name = f"{source_module_name}.__init__"
|
||
if _init_name in Gen.ModuleSha1Map:
|
||
_self_struct_sha1 = Gen.ModuleSha1Map[_init_name]
|
||
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
|
||
_self_struct = None
|
||
if _self_struct_sha1 and class_name_for_method:
|
||
_full_key = f"{_self_struct_sha1}.{class_name_for_method}"
|
||
_self_struct = Gen.structs.get(_full_key)
|
||
if _self_struct is None:
|
||
_self_struct = Gen.structs.get(class_name_for_method or FuncName.split('.')[0], ir.IntType(8))
|
||
ParamType = ir.PointerType(_self_struct)
|
||
elif Arg.annotation:
|
||
try:
|
||
ParamTypeInfo = self.ResolveAnnotationType(Arg.annotation)
|
||
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
|
||
# C 语言中数组参数退化为指针:list[type, N] → type*
|
||
if ParamTypeInfo.ArrayDims:
|
||
ParamTypeInfo.ArrayDims = []
|
||
ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1)
|
||
ParamType = Gen._ctype_to_llvm(ParamTypeInfo)
|
||
except Exception as e: # 回退:参数类型解析失败时使用默认 i32
|
||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
||
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):
|
||
AnnName = ExtractTypeNameFromBinOp(Arg.annotation)
|
||
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:
|
||
if Node.returns and self._check_annotation_for_state(Node.returns):
|
||
_is_export = True
|
||
elif self._check_decorators_for_state(Node):
|
||
_is_export = True
|
||
MangledName = Gen._mangle_func_name(FuncName, module_name=source_module_name)
|
||
func = Gen._get_or_declare_function(MangledName, FuncType)
|
||
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', '')
|
||
base_existing = self.Trans.SymbolTable.lookup(BasePropKey)
|
||
if base_existing:
|
||
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: ast.ClassDef, Gen: LlvmGeneratorMixin, module_name: str | None = None, actual_module_name: str | None = None, source_sha1: str | None = None) -> None:
|
||
ClassName = Node.name
|
||
if hasattr(Node, 'type_params') and Node.type_params:
|
||
return
|
||
IsCenum = False
|
||
IsRenum = False
|
||
IsCpythonObject = False
|
||
IsCVTable = False
|
||
IsNoVTable = False
|
||
if Node.bases:
|
||
for base in Node.bases:
|
||
if getattr(base, 'attr', None):
|
||
if base.attr == 'REnum':
|
||
IsRenum = True
|
||
break
|
||
elif base.attr == 'CEnum' or base.attr == 'Enum':
|
||
IsCenum = True
|
||
break
|
||
elif getattr(base, 'id', None):
|
||
if base.id == 'REnum':
|
||
IsRenum = True
|
||
break
|
||
elif base.id == 'CEnum' or base.id == 'Enum':
|
||
IsCenum = True
|
||
break
|
||
if not IsCenum and not IsRenum 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 decorator.attr == 'NoVTable':
|
||
IsNoVTable = True
|
||
elif decorator.attr == 'REnum':
|
||
IsRenum = True
|
||
elif decorator.attr == 'CEnum' or decorator.attr == 'Enum':
|
||
IsCenum = 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 decorator.func.attr == 'NoVTable':
|
||
IsNoVTable = True
|
||
elif isinstance(decorator.func, ast.Name):
|
||
if decorator.func.id == 'Object':
|
||
IsCpythonObject = True
|
||
elif decorator.func.id == 'CVTable':
|
||
IsCVTable = True
|
||
elif decorator.func.id == 'NoVTable':
|
||
IsNoVTable = True
|
||
if ClassName.startswith('__') and not IsCpythonObject and not IsCenum and not IsRenum:
|
||
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)
|
||
next_enum_value: int = 0
|
||
for item in Node.body:
|
||
VarName = None
|
||
ItemValue = None
|
||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
VarName = item.target.id
|
||
ItemValue = item.value
|
||
elif isinstance(item, ast.Assign):
|
||
for target in item.targets:
|
||
if isinstance(target, ast.Name):
|
||
VarName = target.id
|
||
break
|
||
ItemValue = item.value
|
||
if VarName is None:
|
||
continue
|
||
# 提取枚举值:优先用显式赋值,否则自动编号
|
||
if ItemValue is not None and isinstance(ItemValue, ast.Constant) and isinstance(ItemValue.value, int):
|
||
value = ItemValue.value
|
||
next_enum_value = ItemValue.value + 1
|
||
else:
|
||
value = next_enum_value
|
||
next_enum_value += 1
|
||
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
|
||
if IsRenum:
|
||
# REnum 跨模块导入:注册 REnum 类型 + 变体成员到 SymbolTable
|
||
# 对齐同模块 _EmitREnumLlvm 的注册逻辑
|
||
RenumTypeNode = CTypeInfo()
|
||
RenumTypeNode.Name = ClassName
|
||
RenumTypeNode.IsRenum = True
|
||
RenumTypeNode.IsEnum = True # 兼容 IsEnum 检查
|
||
# REnum 在内存布局上是 struct,必须设置 IsStruct=True 才能让
|
||
# HandlesTypeMerge 的 CPtr 吸收逻辑生效(REnumType | t.CPtr = REnumType*)
|
||
RenumTypeNode.IsStruct = True
|
||
if module_name:
|
||
FullName = f"{module_name}.{ClassName}"
|
||
self.Trans.SymbolTable.insert(FullName, RenumTypeNode)
|
||
self.Trans.SymbolTable.insert(ClassName, RenumTypeNode)
|
||
# 从 stub.ll 加载 REnum 结构体布局到 Gen.structs
|
||
self._TryLoadStructFromStub(ClassName, Gen)
|
||
# 记录 class_sha1_map(跨模块名修饰)
|
||
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]
|
||
if source_sha1:
|
||
if not hasattr(Gen, 'class_sha1_map'):
|
||
Gen.class_sha1_map = {}
|
||
Gen.class_sha1_map[ClassName] = source_sha1
|
||
# 遍历嵌套 ClassDef 注册变体成员
|
||
# REnum body 是嵌套 ClassDef(变体定义),不是 AnnAssign/Assign
|
||
next_tag: int = 0
|
||
variant_names: list[str] = []
|
||
for item in Node.body:
|
||
if isinstance(item, ast.ClassDef):
|
||
VariantName = item.name
|
||
MemberNode = CTypeInfo()
|
||
MemberNode.Name = VariantName
|
||
MemberNode.value = next_tag
|
||
MemberNode.EnumName = ClassName
|
||
MemberNode.Lineno = item.lineno
|
||
MemberNode.IsEnumMember = True
|
||
self.Trans.SymbolTable.insert(VariantName, MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode)
|
||
variant_names.append(VariantName)
|
||
next_tag += 1
|
||
# 加载嵌套变体结构体(如 LLVMType_Int)到 Gen.structs,
|
||
# 并注册 class_members 以支持 _HandleREnumConstructLlvm 的 payload 存储和
|
||
# _HandleRenumMatchLlvm 的 binding 提取。
|
||
NestedStructName: str = f"{ClassName}_{VariantName}"
|
||
self._TryLoadStructFromStub(NestedStructName, Gen)
|
||
if NestedStructName not in Gen.class_members:
|
||
Gen.class_members[NestedStructName] = []
|
||
if NestedStructName not in Gen.class_member_signeds:
|
||
Gen.class_member_signeds[NestedStructName] = {}
|
||
if Gen.class_members[NestedStructName]:
|
||
# 已有成员(_TryLoadStructFromStub 可能已加载),跳过
|
||
pass
|
||
else:
|
||
Gen.class_members[NestedStructName].append(('__tag', ir.IntType(32)))
|
||
Gen.class_member_signeds[NestedStructName]['__tag'] = None
|
||
for nested_item in item.body:
|
||
parsed = self.CollectAnnAssignMember(nested_item, Gen)
|
||
if parsed:
|
||
MName, MType, MTypeInfo = parsed
|
||
Gen.class_members[NestedStructName].append((MName, MType))
|
||
Gen.class_member_signeds[NestedStructName][MName] = None
|
||
# 当 stub 中没有嵌套变体结构体定义时(_TryLoadStructFromStub 失败),
|
||
# 使用主 REnum 结构体的 body (max_variant_struct) 创建嵌套变体结构体,
|
||
# 确保跨模块布局一致。否则 _generate_structs() Step 4 会从 class_members
|
||
# 设置最小布局 body,导致 GEP 偏移量与定义模块不匹配(offset 4 vs offset 8)。
|
||
NestedStructExisting = Gen.structs.get(NestedStructName)
|
||
NestedHasBody = (isinstance(NestedStructExisting, ir.IdentifiedStructType)
|
||
and NestedStructExisting.elements is not None
|
||
and len(NestedStructExisting.elements) > 0)
|
||
if not NestedHasBody:
|
||
MainRenumStruct = Gen.structs.get(ClassName)
|
||
if (isinstance(MainRenumStruct, ir.IdentifiedStructType)
|
||
and MainRenumStruct.elements is not None
|
||
and len(MainRenumStruct.elements) > 0):
|
||
NestedStruct = Gen._get_or_create_struct(NestedStructName, source_sha1=source_sha1)
|
||
if isinstance(NestedStruct, ir.IdentifiedStructType) and NestedStruct.is_opaque:
|
||
NestedStruct.set_body(*MainRenumStruct.elements)
|
||
elif isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
# 显式值变体(无 payload)
|
||
VariantName = item.target.id
|
||
value = next_tag
|
||
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||
value = item.value.value
|
||
MemberNode = CTypeInfo()
|
||
MemberNode.Name = VariantName
|
||
MemberNode.value = value
|
||
MemberNode.EnumName = ClassName
|
||
MemberNode.Lineno = item.lineno
|
||
MemberNode.IsEnumMember = True
|
||
self.Trans.SymbolTable.insert(VariantName, MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode)
|
||
self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode)
|
||
variant_names.append(VariantName)
|
||
next_tag = value + 1
|
||
RenumTypeNode.RenumVariants = variant_names
|
||
return
|
||
# 先初始化 class_methods/class_members 等容器,确保 _TryLoadStructFromStub
|
||
# 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods)
|
||
# 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32)
|
||
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] = {}
|
||
if ClassName not in Gen.class_methods:
|
||
Gen.class_methods[ClassName] = []
|
||
# 先计算 source_sha1,确保首次 _TryLoadStructFromStub 调用就能使用正确的 sha1
|
||
# 优先使用传入的 source_sha1(来自 _TryLoadClassMembersFromPyi 的直接调用,
|
||
# 当前模块未直接导入定义模块时 module_name 查找会失败,需依赖传入的 source_sha1)
|
||
if not source_sha1:
|
||
if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map:
|
||
source_sha1 = Gen.ModuleSha1Map[actual_module_name]
|
||
elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map:
|
||
source_sha1 = Gen.ModuleSha1Map[module_name]
|
||
# 提前注册 struct:使用导入的 source_sha1 创建 opaque struct,
|
||
# 避免后续 _TryLoadStructFromStub 触发嵌套编译时,StructGen 用本地 sha1
|
||
# 创建缺少基类字段(如 GSListNode.Next)的本地类型定义。
|
||
Gen._get_or_create_struct(ClassName, source_sha1=source_sha1, packed=IsPacked)
|
||
# Track which module defines this class (for cross-module name mangling)
|
||
if source_sha1:
|
||
if not hasattr(Gen, 'class_sha1_map'):
|
||
Gen.class_sha1_map = {}
|
||
Gen.class_sha1_map[ClassName] = source_sha1
|
||
# --- 预扫描预注册:修复编译时序问题 ---
|
||
# _TryLoadStructFromStub → _parse_llvm_type → _specialize_generic_class
|
||
# 可能触发 list[str].__new__ 编译(其中调用 pool.alloc(48)),
|
||
# 此时 MemManager 尚未注册到虚表,导致直接调用返回 NULL → 段错误。
|
||
# 修复:在 _TryLoadStructFromStub 之前预扫描方法列表并提前注册虚表。
|
||
_has_methods_prescan = False
|
||
for _item in Node.body:
|
||
if isinstance(_item, ast.FunctionDef):
|
||
_has_methods_prescan = True
|
||
_MethodName = _item.name
|
||
# 构造函数(__new__/__init__/__before_init__)不放入 vtable,
|
||
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移
|
||
# (如 Module 有 __new__/__init__ 使 vtable=7,AST 无它们 vtable=5,
|
||
# 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。
|
||
if _MethodName in ('__new__', '__init__', '__before_init__'):
|
||
continue
|
||
_FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}"
|
||
if _FuncFullName not in Gen.class_methods[ClassName]:
|
||
Gen.class_methods[ClassName].append(_FuncFullName)
|
||
if _has_methods_prescan and IsCVTable:
|
||
Gen._cross_module_vtable_classes.add(ClassName)
|
||
Gen.class_vtable.add(ClassName)
|
||
# 首次加载就传入 source_sha1,避免从其他模块的 output stub 中加载到错误的结构体定义
|
||
# (如 HandlesExprCall.py 的 output stub 中 43f27dda2e5b5923.Value 只有 5 字段,
|
||
# 而正确的 aaf8ba449d1e64c1.Value 有 6 字段)
|
||
self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1)
|
||
# 如果 _get_or_create_struct 因 sha1 冲突创建了新的 opaque struct,
|
||
# 需用 source_sha1 重新加载正确的 stub body。
|
||
if source_sha1:
|
||
self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1)
|
||
has_methods = False
|
||
# 解析 __provides__/__requires__/__require_must__ 编译期元数据(跨模块加载)
|
||
for item in Node.body:
|
||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
field_name: str = item.target.id
|
||
if field_name in ('__provides__', '__requires__', '__require_must__') and item.value:
|
||
values: list[str] = []
|
||
if isinstance(item.value, ast.List):
|
||
for elt in item.value.elts:
|
||
if isinstance(elt, ast.Constant) and isinstance(elt.value, str):
|
||
values.append(elt.value)
|
||
if field_name == '__provides__':
|
||
Gen.class_provides[ClassName] = values
|
||
elif field_name == '__requires__':
|
||
Gen.class_requires[ClassName] = values
|
||
elif field_name == '__require_must__':
|
||
Gen.class_require_must[ClassName] = values
|
||
_existing_member_names = {m[0] for m in Gen.class_members[ClassName]}
|
||
for item in Node.body:
|
||
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
|
||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||
if item.target.id in ('__provides__', '__requires__', '__require_must__'):
|
||
continue
|
||
parsed = self.CollectAnnAssignMember(item, Gen)
|
||
if parsed:
|
||
VarName, MemberType, TypeInfo = parsed
|
||
if TypeInfo is None:
|
||
if VarName not in _existing_member_names:
|
||
Gen.class_members[ClassName].append((VarName, MemberType))
|
||
_existing_member_names.add(VarName)
|
||
Gen.class_member_signeds[ClassName][VarName] = None
|
||
Gen.class_member_bitfields[ClassName][VarName] = 0
|
||
else:
|
||
if TypeInfo.IsBitField:
|
||
Gen.class_member_bitfields[ClassName][VarName] = TypeInfo.BitWidth
|
||
else:
|
||
if VarName not in _existing_member_names:
|
||
Gen.class_members[ClassName].append((VarName, MemberType))
|
||
_existing_member_names.add(VarName)
|
||
Gen.class_member_bitfields[ClassName][VarName] = 0
|
||
if 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
|
||
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}"
|
||
# 构造函数(__new__/__init__/__before_init__)不放入 vtable,
|
||
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。
|
||
# register_method 内部也会过滤,此处仅跳过 class_methods.append。
|
||
if MethodName not in ('__new__', '__init__', '__before_init__'):
|
||
if FuncFullName not in Gen.class_methods[ClassName]:
|
||
Gen.class_methods[ClassName].append(FuncFullName)
|
||
Gen.register_method(ClassName, FuncFullName)
|
||
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
|
||
)
|
||
try:
|
||
self._EmitExternalFuncDeclLlvm(FuncDeclNode, Gen, is_class_method=not is_item_static and not is_item_classmethod, source_module_name=module_name)
|
||
except Exception as e:
|
||
raise
|
||
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:
|
||
base_existing = self.Trans.SymbolTable.lookup(FuncFullName)
|
||
if base_existing:
|
||
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)
|
||
# --- 跨模块继承字段展平 ---
|
||
# _EmitExternalClassDeclLlvm 只添加了类自身定义的字段(来自 .pyi),
|
||
# 没有展平父类(如 GSListNode[T])的继承字段,导致 class_members 缺少
|
||
# 继承字段,_get_member_offset 返回错误偏移。
|
||
# 修复:先尝试从已加载的父类获取真实字段名;若父类未加载,
|
||
# 则用 struct body 元素数推断继承字段数并补充占位条目 __inherited_N。
|
||
_ParentClass: str | None = None
|
||
if Node.bases:
|
||
for _base in Node.bases:
|
||
_base_name: str | None = None
|
||
if hasattr(_base, 'id'):
|
||
_base_name = _base.id
|
||
elif hasattr(_base, 'attr'):
|
||
_base_name = _base.attr
|
||
elif isinstance(_base, ast.Subscript):
|
||
_spec_name: str | None = self.Trans.ExprCallHandle._ResolveGenericSlice(_base)
|
||
if _spec_name:
|
||
_base_name = _spec_name
|
||
else:
|
||
# _ResolveGenericSlice 失败(泛型基类未在当前模块注册为模板,
|
||
# 常见于跨模块导入:如 HandlesExprCall.py 导入 Value(GSListNode[Value]),
|
||
# 但 GSListNode 定义在 includes/linkedlist.py,尚未编译)。
|
||
# 直接从 AST 提取泛型基类名(如 GSListNode[Value] -> GSListNode),
|
||
# 后续继承字段展平逻辑会从 .pyi 加载该基类的字段。
|
||
if isinstance(_base.value, ast.Name):
|
||
_base_short_name: str = _base.value.id
|
||
elif isinstance(_base.value, ast.Attribute):
|
||
_base_short_name: str = _base.value.attr
|
||
else:
|
||
continue
|
||
# 尝试手动构造特化名并检查是否已有特化 class_members。
|
||
# 这解决了 _ResolveGenericSlice 失败但特化已通过其他路径
|
||
# (如 _parse_llvm_type)触发的情况。特化 class_members 中
|
||
# 的 T 已被正确替换为具体类型(如 BasicBlock),避免使用
|
||
# 未特化的 GSListNode 导致 T* 占位类型残留。
|
||
_slice_name: str | None = None
|
||
if isinstance(_base.slice, ast.Name):
|
||
_slice_name = _base.slice.id
|
||
elif isinstance(_base.slice, ast.Attribute):
|
||
_slice_name = _base.slice.attr
|
||
elif isinstance(_base.slice, ast.Subscript):
|
||
# 嵌套泛型(如 GSList[GSListNode[T]]),暂不处理
|
||
pass
|
||
_manual_spec_name: str | None = None
|
||
if _slice_name is not None:
|
||
_manual_spec_name = f"{_base_short_name}[{_slice_name}]"
|
||
if _manual_spec_name is not None and _manual_spec_name in Gen.class_members:
|
||
# 特化 class_members 已存在,直接使用(T 已被替换)
|
||
_base_name = _manual_spec_name
|
||
else:
|
||
_base_name = _base_short_name
|
||
else:
|
||
continue
|
||
if _base_name == ClassName:
|
||
continue
|
||
if _base_name in ('CVTable', 'Object', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'):
|
||
continue
|
||
# 总是记录父类名,即使父类 class_members 未加载。
|
||
# _get_member_offset 会沿 class_parent 链回退查找继承字段。
|
||
_ParentClass = _base_name
|
||
if not hasattr(Gen, 'class_parent'):
|
||
Gen.class_parent = {}
|
||
Gen.class_parent[ClassName] = _ParentClass
|
||
break
|
||
# 治本修复:在继承展开前,尝试从 stub/.pyi 加载父类的 class_members。
|
||
# 确保子类 class_members 能正确展平父类真实字段名(而非 __inherited_N 占位符),
|
||
# 避免 struct body 从不完整 class_members 生成导致 GEP 越界。
|
||
# 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。
|
||
if _ParentClass:
|
||
_parent_members_check: list = Gen.class_members.get(_ParentClass, [])
|
||
_parent_all_placeholders: bool = bool(_parent_members_check) and all(
|
||
n.startswith('__inherited_') for n, _ in _parent_members_check
|
||
)
|
||
if not _parent_members_check or _parent_all_placeholders:
|
||
# 清除占位条目以便 _TryLoadClassMembersFromPyi 的 guard 通过
|
||
_old_parent_members: list = _parent_members_check
|
||
if _parent_all_placeholders:
|
||
Gen.class_members[_ParentClass] = []
|
||
_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {})
|
||
_parent_loaded: bool = False
|
||
for _mod_name, _mod_sha1 in _sha1_map.items():
|
||
_stub_name: str = f"{_mod_sha1}.stub.ll"
|
||
try:
|
||
self._TryLoadClassMembersFromPyi(_ParentClass, _stub_name, Gen)
|
||
except Exception:
|
||
pass
|
||
_parent_members_check = Gen.class_members.get(_ParentClass, [])
|
||
if _parent_members_check and not all(n.startswith('__inherited_') for n, _ in _parent_members_check):
|
||
_parent_loaded = True
|
||
break
|
||
if not _parent_loaded and _old_parent_members:
|
||
# 加载失败,回滚到旧占位条目(有总比没有好)
|
||
Gen.class_members[_ParentClass] = _old_parent_members
|
||
if _ParentClass and _ParentClass in Gen.class_members:
|
||
_parent_members: list = list(Gen.class_members[_ParentClass])
|
||
_parent_defaults: dict = dict(Gen.class_member_defaults.get(_ParentClass, {}))
|
||
_existing_names: set = {m[0] for m in Gen.class_members[ClassName]}
|
||
_inherited: list[tuple] = []
|
||
for _pm_name, _pm_type in _parent_members:
|
||
if _pm_name not in _existing_names:
|
||
_inherited.append((_pm_name, _pm_type))
|
||
if _pm_name in _parent_defaults:
|
||
Gen.class_member_defaults[ClassName][_pm_name] = _parent_defaults[_pm_name]
|
||
Gen.class_members[ClassName] = _inherited + Gen.class_members[ClassName]
|
||
else:
|
||
# 父类未加载到 class_members(常见于泛型特化基类如 GSListNode[Value],
|
||
# 其特化不在 .pyi 中)。用 struct body 元素数推断继承字段数。
|
||
_st = Gen.structs.get(ClassName)
|
||
if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None:
|
||
_num_elements = len(_st.elements)
|
||
# 如果类有 vtable ptr(struct 首元素),需减去 1
|
||
# 否则 vtable ptr 会被误当成 __inherited_0 占位条目,
|
||
# 导致子类继承时多出一个字段(如 Name(AST) 从 9 字段变成 10)
|
||
_has_vtable_ptr = IsCVTable or (has_methods and not IsNoVTable)
|
||
if _has_vtable_ptr and _num_elements > 0:
|
||
_num_elements -= 1
|
||
_num_members = len(Gen.class_members[ClassName])
|
||
if _num_elements > _num_members:
|
||
_num_inherited = _num_elements - _num_members
|
||
_inherited_ph: list[tuple] = [(f'__inherited_{i}', _st.elements[i]) for i in range(_num_inherited)]
|
||
Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName]
|
||
if has_methods:
|
||
if IsCVTable:
|
||
Gen._cross_module_vtable_classes.add(ClassName)
|
||
Gen.class_vtable.add(ClassName)
|
||
if IsNoVTable:
|
||
Gen._cross_module_novtable.add(ClassName)
|
||
# __before_init__ 对所有跨模块结构体声明(定义侧无条件生成,导入侧也应无条件声明)
|
||
NewFuncName = f'{ClassName}.__before_init__'
|
||
if not Gen._has_function(NewFuncName):
|
||
# 复用前面已查找的 source_sha1(优先 actual_module_name 再 module_name),
|
||
# 通过 class_sha1_map 获取,避免此处只查 module_name 导致子模块(如 w32.win32file)查找失败
|
||
source_sha1 = getattr(Gen, 'class_sha1_map', {}).get(ClassName)
|
||
MangledName = Gen._mangle_name(NewFuncName) if not source_sha1 else f"{source_sha1}.{NewFuncName}"
|
||
StructType = Gen.structs.get(ClassName)
|
||
if StructType is not None:
|
||
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 _build_stub_index(self) -> None:
|
||
"""扫描所有 .stub.ll 文件构建 class_name → 条目列表索引
|
||
|
||
一次扫描替代 _TryLoadStructFromStub 的 609 次全量扫描。
|
||
通过文件数签名检测新增 stub,支持增量编译。
|
||
"""
|
||
ProjectRoot = self._find_project_root()
|
||
temp_dir = os.path.join(ProjectRoot, 'temp')
|
||
output_dir = os.path.join(ProjectRoot, 'output')
|
||
if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir):
|
||
return
|
||
|
||
# 文件数签名:检测 stub 文件新增(增量编译时 output 逐步填充)
|
||
output_count = sum(1 for f in os.listdir(output_dir) if f.endswith('.stub.ll')) if os.path.isdir(output_dir) else 0
|
||
temp_count = sum(1 for f in os.listdir(temp_dir) if f.endswith('.stub.ll')) if os.path.isdir(temp_dir) else 0
|
||
signature = (output_count, temp_count)
|
||
if self._stub_index_signature == signature and self._stub_index:
|
||
return # 索引未过期
|
||
|
||
# 重建索引
|
||
self._stub_index.clear()
|
||
for scan_dir, is_output in [(output_dir, True), (temp_dir, False)]:
|
||
if not os.path.isdir(scan_dir):
|
||
continue
|
||
for filename in os.listdir(scan_dir):
|
||
if not filename.endswith('.stub.ll'):
|
||
continue
|
||
stub_path = os.path.join(scan_dir, filename)
|
||
# 预检查文件存在性:os.listdir 与 open 之间文件可能被 --clean 或
|
||
# 并发写入删除(独立 profile 脚本曾触发 FileNotFoundError)
|
||
if not os.path.isfile(stub_path):
|
||
continue
|
||
file_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()
|
||
# 索引键:short_name 和 full_name 都映射到同一条目
|
||
self._stub_index.setdefault(short_name, []).append((full_name, struct_body, file_sha1, is_output))
|
||
if full_name != short_name:
|
||
self._stub_index.setdefault(full_name, []).append((full_name, struct_body, file_sha1, is_output))
|
||
except FileNotFoundError:
|
||
# 竞态:isfile 检查后文件被删除,跳过即可(不应触发 strict raise)
|
||
continue
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"构建 stub 索引失败: {_e}", "Exception")
|
||
self._stub_index_signature = signature
|
||
|
||
def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None) -> None:
|
||
# 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义,
|
||
# 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致
|
||
# 跨模块 struct 类型对象无法分别 set_body(每个模块有独立的 self.structs)。
|
||
# 治本修复:当 source_sha1 指定时(同名类冲突情况),使用 sha1 前缀全名作为
|
||
# 缓存键,确保不同模块的同名类(如 stmts.py:Module 和 __module.py:Module)
|
||
# 不会互相阻止 stub 加载。
|
||
cache_key = (id(Gen), f"{source_sha1}.{class_name}" if source_sha1 else class_name)
|
||
if cache_key in self._struct_Load_cache:
|
||
return
|
||
# 先设置缓存防止递归重入,但如果加载失败则清除缓存允许后续重试
|
||
self._struct_Load_cache[cache_key] = True
|
||
|
||
# 使用 stub 索引替代全量扫描(609 次 × 100 文件 → 1 次扫描 + O(1) 查询)
|
||
self._build_stub_index()
|
||
entries = self._stub_index.get(class_name, [])
|
||
|
||
if not entries:
|
||
del self._struct_Load_cache[cache_key]
|
||
return
|
||
|
||
# 选择最佳条目:
|
||
# 优先级 0: 当 source_sha1 指定时,优先选择 type_sha1 匹配的条目(治本修复:
|
||
# 同名类冲突时确保选择正确模块的 struct 定义)
|
||
# 优先级 1: 类型定义模块自身的 stub(file_sha1 == full_name 中的 sha1 前缀)
|
||
# 定义模块的 stub 包含完整的字段展平(含跨模块继承字段)
|
||
# 优先级 2: output(Phase2 生成)优于 temp(Phase1 生成)
|
||
# 优先级 3: 非 opaque(具体定义)优于 opaque
|
||
def _entry_priority(e: tuple[str, str, str, bool]) -> tuple[int, int, int, int]:
|
||
full_name, struct_body, file_sha1, from_output = e
|
||
type_sha1 = file_sha1
|
||
if '.' in full_name:
|
||
parts = full_name.split('.', 1)
|
||
if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]):
|
||
type_sha1 = parts[0]
|
||
sha1_match = 0 if (source_sha1 and type_sha1 == source_sha1) else 1
|
||
is_defining = 0 if type_sha1 == file_sha1 else 1
|
||
is_temp = 0 if from_output else 1
|
||
is_opaque = 0 if (struct_body and struct_body != 'opaque') else 1
|
||
return (sha1_match, is_defining, is_temp, is_opaque)
|
||
|
||
best_entry = min(entries, key=_entry_priority)
|
||
full_name, struct_body, file_sha1, from_output = best_entry
|
||
|
||
# Bug 1 修复:从 full_name 提取 sha1 前缀(类型定义模块的 sha1),
|
||
# 而非使用 file_sha1(stub 文件所在模块的 sha1,可能只是导入方而非定义方)。
|
||
# 例如 output/35ffd4b9fbd082d1.stub.ll (main.py) 中包含
|
||
# %"5cde8c6813a2b6f3.Function" = type {...},
|
||
# 此时 file_sha1='35ffd4b9fbd082d1' (main.py) 但类型实际定义在
|
||
# __function.py (sha1='5cde8c6813a2b6f3'),必须用后者作为 source_sha1。
|
||
actual_sha1 = file_sha1
|
||
if '.' in full_name:
|
||
parts = full_name.split('.', 1)
|
||
if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]):
|
||
actual_sha1 = parts[0]
|
||
|
||
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('{}').strip()
|
||
if inner:
|
||
for elem in inner.split(','):
|
||
elem = elem.strip()
|
||
if elem:
|
||
et = self._parse_llvm_type(elem, Gen, source_sha1=actual_sha1)
|
||
elem_types.append(et if et else ir.IntType(32))
|
||
try:
|
||
st = Gen._get_or_create_struct(class_name, source_sha1=actual_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)
|
||
# 同步设置短名 struct 的 body:当存在 sha1 冲突时,_get_or_create_struct
|
||
# 返回 full_key struct,但代码可能通过短名引用旧 struct(由 setup_from_symbol_table
|
||
# 早期创建的 opaque 占位符,使用本地 sha1 前缀)。需要同步设置短名 struct
|
||
# 的 body,确保 GEP 指令能正确访问字段(修复跨模块类型定义冲突导致的
|
||
# 字段索引错位问题)。
|
||
short_st = Gen.structs.get(class_name)
|
||
if short_st is not None and short_st is not st and isinstance(short_st, ir.IdentifiedStructType) and short_st.is_opaque:
|
||
try:
|
||
short_st.set_body(*elem_types)
|
||
except Exception:
|
||
pass # set_body 可能因已设置而失败,忽略
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
stub_filename = actual_sha1 + '.stub.ll'
|
||
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
|
||
|
||
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin) -> None:
|
||
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 as e:
|
||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
||
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__'
|
||
# module_name 反查失败时(当前模块未直接导入定义模块),
|
||
# 仍需注册类成员和 vtable 信息。传 source_sha1 让 _EmitExternalClassDeclLlvm
|
||
# 正确设置 class_sha1_map 和注册 vtable。
|
||
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, source_sha1=source_sha1)
|
||
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: ast.AST, target_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Constant | None:
|
||
"""Build a scalar constant value from AST node, delegated to unified method."""
|
||
return self.Trans._BuildScalarConstantUnified(value_node, target_type, Gen)
|
||
|
||
def _LookupStubFuncType(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None:
|
||
"""从 stub.ll 文件中查找指定函数的 LLVM 类型
|
||
|
||
扫描 output/ 和 temp/ 两个目录的 .stub.ll 文件。
|
||
关键修复:output/(Phase 2 完整结果)必须覆盖 temp/(Phase 1 不完整结果),
|
||
否则继承包装方法(在 Phase 2 的 _generate_inherited_method_wrappers 中生成)
|
||
将无法被解析,导致跨模块调用使用默认 i32 返回类型 → 64 位指针截断。
|
||
"""
|
||
|
||
if not getattr(self, '_stub_func_cache', None):
|
||
self._stub_func_cache = {}
|
||
|
||
ProjectRoot = self._find_project_root()
|
||
temp_dir = os.path.join(ProjectRoot, 'temp')
|
||
output_dir = os.path.join(ProjectRoot, 'output')
|
||
|
||
# 文件数签名:检测 stub 新增(增量编译时 output 逐步填充完整签名)
|
||
output_count = sum(1 for f in os.listdir(output_dir) if f.endswith('.stub.ll')) if os.path.isdir(output_dir) else 0
|
||
temp_count = sum(1 for f in os.listdir(temp_dir) if f.endswith('.stub.ll')) if os.path.isdir(temp_dir) else 0
|
||
signature = (output_count, temp_count)
|
||
if getattr(self, '_stub_func_cache_signature', None) != signature or not self._stub_func_cache:
|
||
# 签名变化或缓存为空,重建缓存
|
||
self._stub_func_cache = {}
|
||
self._stub_func_cache_signature = signature
|
||
|
||
if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir):
|
||
return None
|
||
|
||
if func_name not in self._stub_func_cache:
|
||
# 先扫描 temp/(Phase 1,不完整签名),再扫描 output/(Phase 2,完整签名)覆盖之
|
||
# 这样 output 中存在的继承包装方法会覆盖 temp 中的同名旧声明(若有),
|
||
# 也填补 temp 中缺失的函数声明
|
||
for scan_dir in [temp_dir, output_dir]:
|
||
if not os.path.isdir(scan_dir):
|
||
continue
|
||
for filename in os.listdir(scan_dir):
|
||
if not filename.endswith('.stub.ll'):
|
||
continue
|
||
|
||
stub_path = os.path.join(scan_dir, filename)
|
||
# 预检查文件存在性:os.listdir 与 open 之间文件可能被并发写入删除
|
||
if not os.path.isfile(stub_path):
|
||
continue
|
||
source_sha1 = filename.replace('.stub.ll', '')
|
||
try:
|
||
with open(stub_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
for line in content.splitlines():
|
||
stripped = line.strip()
|
||
if not stripped.startswith('declare '):
|
||
continue
|
||
|
||
name_match = stripped.split('@', 1)
|
||
if len(name_match) < 2:
|
||
continue
|
||
|
||
stub_func_name = name_match[1].split('(', 1)[0].strip().strip('"')
|
||
|
||
match = re.match(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$', stripped)
|
||
if match:
|
||
stub_ret = match.group(1).strip()
|
||
stub_params = match.group(3).strip()
|
||
self._stub_func_cache[stub_func_name] = (stub_ret, stub_params, '...' in stub_params, source_sha1)
|
||
except FileNotFoundError:
|
||
continue
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
|
||
if func_name not in self._stub_func_cache:
|
||
return None
|
||
|
||
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: str) -> str:
|
||
"""剥离 LLVM IR 参数字符串中的参数名,仅保留类型部分"""
|
||
param_str = param_str.strip()
|
||
# 支持 %name 和 %"quoted.name" 两种参数名形式
|
||
result = re.sub(r'\s+%("[^"]*"|[\w.]+)\s*$', '', param_str)
|
||
return result.strip()
|
||
|
||
def _parse_simple_llvm_type(self, type_str: str) -> ir.Type | None:
|
||
"""解析简单的 LLVM 类型字符串(不创建结构体,避免副作用)"""
|
||
|
||
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:
|
||
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: LlvmGeneratorMixin) -> None:
|
||
"""从 stub.ll 文件加载函数声明和结构体定义到 Gen"""
|
||
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:
|
||
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)
|
||
# 治本修复:从 raw_name 提取类型定义的实际 sha1 前缀,
|
||
# 而非使用 stub 文件的 sha1。同一 stub 文件可能包含多个模块的
|
||
# struct 定义(前向引用),必须用类型名中的 sha1 作为 source_sha1,
|
||
# 否则同名类冲突检测会失效(如 __module.py stub 中的
|
||
# e64295c03b564a53.Module 前向引用会被错误地用 6e3f70566907c6cf 作为 sha1)。
|
||
actual_type_sha1 = source_sha1
|
||
if '.' in raw_name:
|
||
_rn_parts = raw_name.split('.', 1)
|
||
if len(_rn_parts[0]) == 16 and all(_c in '0123456789abcdef' for _c in _rn_parts[0]):
|
||
actual_type_sha1 = _rn_parts[0]
|
||
clean_name = _rn_parts[1]
|
||
else:
|
||
clean_name = raw_name
|
||
struct_body = struct_def_match.group(3).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=actual_type_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=actual_type_sha1, packed=is_packed)
|
||
if isinstance(st, ir.IdentifiedStructType) and st.elements is None:
|
||
try:
|
||
st.set_body(*elem_types)
|
||
except Exception as _e:
|
||
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
|
||
|
||
# bare 名(无 16 位 hex SHA1 前缀)表示 CExport,调用点需用纯名
|
||
_Prefix: str = func_name.split('.', 1)[0]
|
||
if not (len(_Prefix) == 16 and all(_c in '0123456789abcdef' for _c in _Prefix)):
|
||
Gen._export_funcs.add(func_name)
|
||
|
||
try:
|
||
func_type = self._parse_llvm_declare(stripped, Gen)
|
||
if func_type is not None:
|
||
# __new__ 方法应返回类指针类型(堆分配替换),与 _GetFunctionSignatureLlvm/_EmitFunctionLlvm 保持一致
|
||
# .stub.ll 由 .pyi 生成,.pyi 生成器对无返回类型注解的 __new__ 默认使用 t.CInt,这里修正为 ClassName*
|
||
if '.__new__' in func_name:
|
||
_np = func_name.split('.')
|
||
if len(_np[0]) == 16 and all(_c in '0123456789abcdef' for _c in _np[0]):
|
||
_new_class = _np[1]
|
||
else:
|
||
_new_class = _np[0]
|
||
_new_struct = Gen.structs.get(_new_class)
|
||
if _new_struct is None:
|
||
_full_name = func_name.split('.__new__')[0]
|
||
_new_struct = Gen.structs.get(_full_name)
|
||
if _new_struct:
|
||
_ret_type = _new_struct if isinstance(_new_struct, ir.PointerType) else ir.PointerType(_new_struct)
|
||
func_type = ir.FunctionType(_ret_type, func_type.args, var_arg=func_type.var_arg)
|
||
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):
|
||
# 类型不一致:清理 module.globals + module.scope._useset 后重建,避免 DuplicatedNameError
|
||
old_name = existing_func.name
|
||
if old_name in Gen.module.globals:
|
||
del Gen.module.globals[old_name]
|
||
module_scope = getattr(Gen.module, 'scope', None)
|
||
if getattr(module_scope, '_useset', None):
|
||
module_scope._useset.discard(old_name)
|
||
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 = Gen._get_or_declare_function(func_name, func_type)
|
||
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:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||
|
||
def _TryLoadFuncDeclsFromOutputStub(self, struct_name: str, Gen: LlvmGeneratorMixin) -> bool:
|
||
"""按需从 output 目录的 stub.ll 加载泛型特化方法声明。
|
||
|
||
当 temp stub 不包含泛型特化(如 list[str].__iter__)时,
|
||
从 output stub(由 _split_ll 从完整 IR 生成)中按需加载。
|
||
|
||
struct_name: 结构体名,如 '9163064cf3eb88f4.list[str]' 或 'list[str]'
|
||
返回 True 如果找到了并加载了新函数声明。
|
||
"""
|
||
ProjectRoot = self._find_project_root()
|
||
output_dir = os.path.join(ProjectRoot, 'output')
|
||
if not os.path.isdir(output_dir):
|
||
return False
|
||
|
||
# 提取短名称和 SHA1 前缀
|
||
sha1_prefix: str | None = None
|
||
if '.' in struct_name:
|
||
parts: list[str] = struct_name.split('.', 1)
|
||
if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]):
|
||
sha1_prefix = parts[0]
|
||
short_name: str = parts[1]
|
||
else:
|
||
short_name = struct_name
|
||
else:
|
||
short_name = struct_name
|
||
|
||
# 确定要搜索的 stub 文件列表
|
||
stub_files_to_search: list[str] = []
|
||
if sha1_prefix:
|
||
target_stub = f'{sha1_prefix}.stub.ll'
|
||
target_path = os.path.join(output_dir, target_stub)
|
||
if os.path.isfile(target_path):
|
||
stub_files_to_search.append(target_stub)
|
||
else:
|
||
# 没有SHA1前缀,搜索所有 output stub
|
||
for fname in os.listdir(output_dir):
|
||
if fname.endswith('.stub.ll'):
|
||
stub_files_to_search.append(fname)
|
||
|
||
loaded_any: bool = False
|
||
for fname in stub_files_to_search:
|
||
stub_path = os.path.join(output_dir, fname)
|
||
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
|
||
# 检查是否是目标结构体的方法
|
||
if f'.{short_name}.' not in stripped:
|
||
continue
|
||
try:
|
||
func_type = self._parse_llvm_declare(stripped, Gen)
|
||
if func_type is None:
|
||
continue
|
||
# 提取函数名:最后一个 @ 到第一个 ( 之间的内容
|
||
paren_pos: int = stripped.index('(')
|
||
at_pos: int = stripped.rindex('@', 0, paren_pos)
|
||
func_name: str = stripped[at_pos + 1:paren_pos].strip().strip('"')
|
||
if func_name not in Gen.functions:
|
||
func = ir.Function(Gen.module, func_type, name=func_name)
|
||
Gen.functions[func_name] = func
|
||
if '.' in func_name:
|
||
short_fn = func_name.split('.', 1)[1]
|
||
Gen.functions[short_fn] = func
|
||
loaded_any = True
|
||
except Exception:
|
||
pass
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"按需加载 output stub 失败: {_e}", "Exception")
|
||
return loaded_any
|
||
|
||
def _parse_llvm_declare(self, declare_line: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None:
|
||
"""解析 LLVM declare 语句"""
|
||
match = re.match(r'declare\s+(.+?)\s+@("?[^()]+"?)\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: LlvmGeneratorMixin, source_sha1: str | None = None, create_structs: bool = True) -> ir.Type | None:
|
||
"""解析 LLVM 类型字符串"""
|
||
|
||
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:
|
||
# 泛型类特化(如 list[str]):触发特化创建结构体和方法声明
|
||
# 使用 declare_only=True 避免在引用模块中生成方法体(定义由定义模块提供)
|
||
if '[' in struct_name and actual_source_sha1:
|
||
if struct_name in Gen.structs:
|
||
return Gen.structs[struct_name]
|
||
gc_base: str = struct_name.split('[')[0]
|
||
gc_args_raw: str = struct_name[len(gc_base):]
|
||
gc_type_args: list[str] = []
|
||
for gc_arg_part in gc_args_raw.split(']['):
|
||
gc_arg_part = gc_arg_part.strip('[]')
|
||
if gc_arg_part:
|
||
gc_type_args.append(gc_arg_part)
|
||
if (gc_type_args
|
||
and hasattr(self.Trans, 'ClassHandler')
|
||
and hasattr(self.Trans.ClassHandler, '_generic_class_templates')
|
||
and gc_base in self.Trans.ClassHandler._generic_class_templates):
|
||
gc_type_names: list[str] = []
|
||
for gc_ta in gc_type_args:
|
||
gc_tn: str = gc_ta
|
||
if gc_ta == 'int':
|
||
gc_tn = 'CInt'
|
||
elif gc_ta == 'double':
|
||
gc_tn = 'CDouble'
|
||
elif gc_ta == 'float':
|
||
gc_tn = 'CFloat'
|
||
elif gc_ta == 'char':
|
||
gc_tn = 'CChar'
|
||
elif gc_ta == 'bool':
|
||
gc_tn = 'CBool'
|
||
gc_type_names.append(gc_tn)
|
||
saved_module_sha1: str | None = getattr(Gen, 'module_sha1', None)
|
||
saved_trans_sha1: str | None = getattr(self.Trans, '_module_sha1', None)
|
||
Gen.module_sha1 = actual_source_sha1
|
||
self.Trans._module_sha1 = actual_source_sha1
|
||
try:
|
||
gc_spec: str | None = self.Trans.ClassHandler._specialize_generic_class(
|
||
gc_base, gc_type_args, Gen, type_names=gc_type_names,
|
||
declare_only=True)
|
||
except Exception:
|
||
gc_spec = None
|
||
Gen.module_sha1 = saved_module_sha1
|
||
self.Trans._module_sha1 = saved_trans_sha1
|
||
if gc_spec and gc_spec in Gen.structs:
|
||
return Gen.structs[gc_spec]
|
||
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
|
||
# 尝试用 source_sha1 前缀的全名查找
|
||
if actual_source_sha1:
|
||
full_key = f"{actual_source_sha1}.{struct_name}"
|
||
existing_full = Gen.structs.get(full_key)
|
||
if existing_full is not None:
|
||
return existing_full
|
||
return None
|
||
return ir.IntType(32)
|
||
|
||
return None
|
||
|
||
def _strip_param_name(self, param_str: str) -> str:
|
||
"""从 LLVM 参数字符串中剥离参数名,只保留类型部分
|
||
例如: '%"sha1.struct_name"* %param_name' -> '%"sha1.struct_name"*'
|
||
'i8* %path' -> 'i8*'
|
||
'i32' -> 'i32'
|
||
"""
|
||
param_str = param_str.strip()
|
||
stripped = re.sub(r'\s+%[\w.]+\s*$', '', param_str)
|
||
return stripped.strip()
|