修复了大量存在的问题,增加了假鸭子类型等等机制

This commit is contained in:
2026-06-25 14:49:46 +08:00
parent 19f2787db0
commit d88d11b646
827 changed files with 32617 additions and 18316 deletions

View File

@@ -2,12 +2,19 @@ from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.includes import t
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 IsTModule, ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName
class ImportHandle(BaseHandle):
@@ -18,13 +25,21 @@ class ImportHandle(BaseHandle):
_pyi_cache_dir = None
_project_root_cache = None
def _reset_file_cache(self):
def _reset_file_cache(self) -> None:
self._struct_Load_cache.clear()
def _find_project_root(self):
def _find_project_root(self) -> str:
if self._project_root_cache is not None:
return self._project_root_cache
import os
# 优先使用 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))
@@ -37,17 +52,29 @@ class ImportHandle(BaseHandle):
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 = os.getcwd()
return os.getcwd()
self._project_root_cache = cwd
return cwd
def _EmitImportDeclarationsLlvm(self, Node, Gen):
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:
@@ -60,7 +87,7 @@ class ImportHandle(BaseHandle):
current_module = self._get_current_module_name()
self._EmitModuleDeclarationsLlvm(name, Gen, register_module_name=current_module)
def _RegisterFromImportAliases(self, Node, Gen, 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
@@ -92,8 +119,9 @@ class ImportHandle(BaseHandle):
Gen.functions[asname] = Gen.functions[name]
if name in Gen.structs:
Gen.structs[asname] = Gen.structs[name]
if self.Trans.SymbolTable.has(name):
self.Trans.SymbolTable.insert(asname, self.Trans.SymbolTable[name])
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:
@@ -120,7 +148,7 @@ class ImportHandle(BaseHandle):
if name in struct_sha1_map:
struct_sha1_map[asname] = struct_sha1_map[name]
def _EmitImportFromDeclarationsLlvm(self, Node, Gen):
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):
@@ -128,10 +156,17 @@ class ImportHandle(BaseHandle):
self.Trans._ImportedModules.add(module)
if not hasattr(self.Trans, '_t_c_imported_names'):
self.Trans._t_c_imported_names = {}
# 注册 t/c 类型别名到 _t_type_symbolsCType 子类)
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()
@@ -207,7 +242,7 @@ class ImportHandle(BaseHandle):
self._EmitModuleDeclarationsLlvm(module, Gen, register_module_name=current_module)
self._RegisterFromImportAliases(Node, Gen, module)
def _LoadModuleDeclarationsFromFile(self, pyi_path, Gen, module_name=None, register_module_name=None, actual_module_name=None, reexport_package=None):
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
try:
@@ -323,16 +358,16 @@ class ImportHandle(BaseHandle):
else:
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
except Exception as _e:
if __import__('lib.constants.config', fromlist=['mode']).mode == "strict":
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
def _get_current_module_name(self):
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, actual_module, short_module, Gen):
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:
@@ -343,13 +378,11 @@ class ImportHandle(BaseHandle):
if short_module and short_module not in Gen.ModuleSha1Map:
Gen.ModuleSha1Map[short_module] = sub_sha1
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
def _LoadPackageInitForRelativeImport(self, mod_dir, Gen, register_module_name):
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."""
@@ -383,14 +416,13 @@ class ImportHandle(BaseHandle):
return pkg_name
def _EmitModuleDeclarationsLlvm(self, module_name, Gen, register_module_name=None):
def _EmitModuleDeclarationsLlvm(self, module_name: str, Gen: LlvmGeneratorMixin, register_module_name: str | None = None) -> None:
if module_name in ('pyzlib',):
source_sig_files = getattr(self.Trans, '_source_module_sig_files', None)
ModulePath = module_name.replace('.', os.sep) + '.pyi'
PackageInitPath = module_name.replace('.', os.sep) + os.sep + '__init__.pyi'
ProjectRoot = os.path.dirname(os.path.abspath(getattr(self.Trans, 'CurrentFile', '') or '')) or os.getcwd()
import inspect
HandlesFile = os.path.abspath(inspect.getfile(self.__class__))
TransPyCRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(HandlesFile))))
@@ -592,6 +624,7 @@ class ImportHandle(BaseHandle):
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)
@@ -601,7 +634,7 @@ class ImportHandle(BaseHandle):
if module_name:
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
def _EmitExternalGlobalDeclLlvm(self, Node, Gen, module_name=None, ModulePath=None):
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
@@ -609,66 +642,29 @@ class ImportHandle(BaseHandle):
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):
old_pointee = existing.type.pointee if isinstance(existing.type, ir.PointerType) else None
if isinstance(old_pointee, ir.IdentifiedStructType):
if old_pointee.elements is not None and len(old_pointee.elements) > 0:
return
elif isinstance(old_pointee, ir.ArrayType):
return
return
if Node.annotation:
if isinstance(Node.annotation, ast.Subscript) and isinstance(Node.annotation.value, ast.Name) and Node.annotation.value.id == 'list':
slice_node = Node.annotation.slice
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
elem_type_node = slice_node.elts[0]
count_node = slice_node.elts[1]
ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable)
if ElemTypeInfo:
elem_type = Gen._ctype_to_llvm(ElemTypeInfo)
if isinstance(elem_type, ir.VoidType):
elem_type = ir.IntType(8)
if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int):
var_type = ir.ArrayType(elem_type, count_node.value)
elif isinstance(count_node, ast.Constant) and count_node.value is None:
var_type = ir.ArrayType(elem_type, 0)
else:
var_type = Gen._ctype_to_llvm(ElemTypeInfo)
IsPtr = False
else:
TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
if TypeInfo:
var_type = Gen._ctype_to_llvm(TypeInfo)
IsPtr = TypeInfo.IsPtr
elif isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr):
list_node = None
for side in (Node.annotation.left, Node.annotation.right):
if isinstance(side, ast.Subscript) and isinstance(side.value, ast.Name) and side.value.id == 'list':
list_node = side
break
if list_node:
slice_node = list_node.slice
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
elem_type_node = slice_node.elts[0]
count_node = slice_node.elts[1]
ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable)
if ElemTypeInfo:
elem_type = Gen._ctype_to_llvm(ElemTypeInfo)
if isinstance(elem_type, ir.VoidType):
elem_type = ir.IntType(8)
if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int):
var_type = ir.ArrayType(elem_type, count_node.value)
elif isinstance(count_node, ast.Constant) and count_node.value is None:
var_type = ir.ArrayType(elem_type, 0)
else:
var_type = Gen._ctype_to_llvm(ElemTypeInfo)
IsPtr = False
else:
TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
if TypeInfo:
var_type = Gen._ctype_to_llvm(TypeInfo)
IsPtr = TypeInfo.IsPtr
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:
@@ -710,6 +706,10 @@ class ImportHandle(BaseHandle):
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):
@@ -757,16 +757,8 @@ class ImportHandle(BaseHandle):
return
gv = ir.GlobalVariable(Gen.module, var_type, name=VarName)
if Node.value:
InitVal = self._ExtractValue(Node.value, var_type, VarName)
if InitVal is not None:
gv.initializer = InitVal
gv.linkage = 'available_externally'
else:
gv.linkage = 'external'
else:
gv.linkage = 'external'
# 外部全局变量声明只声明不定义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('_'):
@@ -774,17 +766,9 @@ class ImportHandle(BaseHandle):
if VarName in Gen.module.globals:
return
gv = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName)
if Node.value and ModulePath:
InitVal = self._ExtractValue(Node.value, ir.IntType(32), VarName)
if InitVal is not None:
gv.initializer = InitVal
gv.linkage = 'available_externally'
else:
gv.linkage = 'external'
else:
gv.linkage = 'external'
gv.linkage = 'external'
def _ExtractValue(self, value_node, target_type, var_name=None):
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):
@@ -837,12 +821,10 @@ class ImportHandle(BaseHandle):
return ir.Constant(target_type, 0)
return None
def _ExtractConstValue(self, value_node):
def _ExtractConstValue(self, value_node: ast.AST) -> object:
"""Extract constant value from AST node for SymbolTable registration
支持常量、一元运算符、二元运算符和常量引用
"""
import ast
if isinstance(value_node, ast.Constant):
return value_node.value
elif isinstance(value_node, ast.BinOp):
@@ -899,9 +881,7 @@ class ImportHandle(BaseHandle):
return self._ExtractConstValue(value_node.args[0])
return None
def _ExtractTypedefOriginalType(self, value_node):
import ast
from lib.includes.t import CTypeRegistry
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)
@@ -933,9 +913,8 @@ class ImportHandle(BaseHandle):
return llvm_str
return None
def _RegisterCDefineSymbol(self, FullName, DefineValue, lineno, FilePath):
def _RegisterCDefineSymbol(self, FullName: str, DefineValue: object, lineno: int, FilePath: str) -> None:
"""Register CDefine constant to SymbolTable"""
from lib.core.Handles.HandlesBase import CTypeInfo
info = CTypeInfo()
info.IsDefine = True
info.DefineValue = DefineValue
@@ -944,20 +923,12 @@ class ImportHandle(BaseHandle):
# 直接添加到 SymbolTable 字典
self.Trans.SymbolTable.insert(FullName, info)
def _check_annotation_for_state(self, annotation) -> bool:
import ast
if isinstance(annotation, ast.Attribute):
if hasattr(annotation, 'attr') and annotation.attr in ('CExport', 'CExtern', 'State'):
return True
if hasattr(annotation, 'value') and isinstance(annotation.value, ast.Name) and annotation.value.id == 't' and annotation.attr == 'State':
return True
elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return self._check_annotation_for_state(annotation.left) or self._check_annotation_for_state(annotation.right)
elif isinstance(annotation, ast.Name):
return annotation.id in ('CExport', 'CExtern', 'State')
return False
def _check_annotation_for_state(self, annotation: ast.AST) -> bool:
return (AnnotationContainsName(annotation, 'CExport') or
AnnotationContainsName(annotation, 'CExtern') or
AnnotationContainsName(annotation, 'State'))
def _EmitExternalFuncDeclLlvm(self, Node, Gen, is_class_method=False, source_module_name=None, register_module_name=None, reexport_module_names=None):
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):
Gen._export_funcs.add(FuncName) # t.State 标记的外部 C 函数必须保持原始名称,以便链接器解析
@@ -974,12 +945,7 @@ class ImportHandle(BaseHandle):
ReturnTypeInfo = None
if Node.returns:
try:
if isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr):
ReturnTypeInfo = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.returns)
if not isinstance(ReturnTypeInfo, CTypeInfo):
ReturnTypeInfo = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable)
else:
ReturnTypeInfo = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable)
ReturnTypeInfo = self.ResolveAnnotationType(Node.returns)
if ReturnTypeInfo and ReturnTypeInfo.IsStr:
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = t.CChar()
@@ -988,7 +954,8 @@ class ImportHandle(BaseHandle):
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = t.CInt()
IsPtr = ReturnTypeInfo.IsPtr
except Exception: # 回退:返回类型解析失败时使用默认 CInt
except Exception as e: # 回退:返回类型解析失败时使用默认 CInt
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = t.CInt()
else:
@@ -1021,12 +988,7 @@ class ImportHandle(BaseHandle):
ParamType = ir.PointerType(Gen.structs.get(class_name_for_method or FuncName.split('.')[0], ir.IntType(8)))
elif Arg.annotation:
try:
if isinstance(Arg.annotation, ast.BinOp) and isinstance(Arg.annotation.op, ast.BitOr):
ParamTypeInfo = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Arg.annotation)
if ParamTypeInfo is None or not isinstance(ParamTypeInfo, CTypeInfo):
ParamTypeInfo = CTypeInfo.FromNode(Arg.annotation, self.Trans.SymbolTable)
else:
ParamTypeInfo = CTypeInfo.FromNode(Arg.annotation, self.Trans.SymbolTable)
ParamTypeInfo = self.ResolveAnnotationType(Arg.annotation)
if ParamTypeInfo is None:
ParamTypeInfo = CTypeInfo()
ParamTypeInfo.BaseType = t.CInt()
@@ -1039,8 +1001,13 @@ class ImportHandle(BaseHandle):
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: # 回退:参数类型解析失败时使用默认 i32
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
@@ -1049,11 +1016,7 @@ class ImportHandle(BaseHandle):
elif isinstance(Arg.annotation, ast.Attribute):
AnnName = Arg.annotation.attr
elif isinstance(Arg.annotation, ast.BinOp) and isinstance(Arg.annotation.op, ast.BitOr):
left = Arg.annotation.left
if isinstance(left, ast.Name):
AnnName = left.id
elif isinstance(left, ast.Attribute):
AnnName = left.attr
AnnName = ExtractTypeNameFromBinOp(Arg.annotation)
if AnnName and AnnName in Gen.structs:
ParamType = ir.PointerType(Gen.structs[AnnName])
else:
@@ -1123,8 +1086,8 @@ class ImportHandle(BaseHandle):
# property setter/deleter: 在原始 PropKey不带后缀下注册 MetaList
if FuncMeta.PROPERTY_SETTER in func_meta or FuncMeta.PROPERTY_DELETER in func_meta:
BasePropKey = FuncName.replace('$set', '').replace('$del', '')
if self.Trans.SymbolTable.has(BasePropKey):
base_existing = self.Trans.SymbolTable[BasePropKey]
base_existing = self.Trans.SymbolTable.lookup(BasePropKey)
if base_existing:
if func_meta != FuncMeta.NONE:
base_existing.MetaList = base_existing.MetaList | func_meta
else:
@@ -1143,7 +1106,7 @@ class ImportHandle(BaseHandle):
if ReexportName2 not in Gen.functions:
Gen.functions[ReexportName2] = func
def _EmitExternalClassDeclLlvm(self, Node, Gen, module_name=None, actual_module_name=None):
def _EmitExternalClassDeclLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin, module_name: str | None = None, actual_module_name: str | None = None) -> None:
ClassName = Node.name
if hasattr(Node, 'type_params') and Node.type_params:
return
@@ -1180,7 +1143,7 @@ class ImportHandle(BaseHandle):
IsCpythonObject = True
elif decorator.func.id == 'CVTable':
IsCVTable = True
if ClassName.startswith('_') and not IsCpythonObject and not IsCenum:
if ClassName.startswith('__') and not IsCpythonObject and not IsCenum:
return
IsPacked = False
if getattr(Node, 'decorator_list', None):
@@ -1260,23 +1223,46 @@ class ImportHandle(BaseHandle):
if ClassName not in Gen.class_member_bitoffsets:
Gen.class_member_bitoffsets[ClassName] = {}
has_methods = False
# 解析 __provides__/__requires__/__require_must__ 编译期元数据(跨模块加载)
for item in Node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
VarName = item.target.id
try:
TypeInfo = CTypeInfo.FromNode(item.annotation, self.Trans.SymbolTable)
if TypeInfo is None:
TypeInfo = CTypeInfo()
TypeInfo.BaseType = t.CInt()
MemberType = Gen._ctype_to_llvm(TypeInfo)
if isinstance(MemberType, ir.VoidType):
MemberType = ir.PointerType(ir.IntType(8))
if TypeInfo and TypeInfo.IsBitField:
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:
Gen.class_members[ClassName].append((VarName, MemberType))
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 and TypeInfo.ByteOrder:
if TypeInfo.ByteOrder:
Gen.class_member_byteorders[ClassName][VarName] = TypeInfo.ByteOrder
else:
Gen.class_member_byteorders[ClassName][VarName] = ""
@@ -1284,10 +1270,6 @@ class ImportHandle(BaseHandle):
const = self._BuildScalarConstant(item.value, MemberType, Gen)
if const:
Gen.class_member_defaults[ClassName][VarName] = const
except Exception: # 回退:类成员类型解析失败时使用默认 i32
Gen.class_members[ClassName].append((VarName, ir.IntType(32)))
Gen.class_member_signeds[ClassName][VarName] = None
Gen.class_member_bitfields[ClassName][VarName] = 0
elif isinstance(item, ast.FunctionDef):
has_methods = True
MethodName = item.name
@@ -1340,8 +1322,8 @@ class ImportHandle(BaseHandle):
existing.MetaList = func_meta
# setter/deleter: 同时在原始 PropKey不带后缀下注册 MetaList
if is_item_prop_setter or is_item_prop_deleter:
if self.Trans.SymbolTable.has(FuncFullName):
base_existing = self.Trans.SymbolTable[FuncFullName]
base_existing = self.Trans.SymbolTable.lookup(FuncFullName)
if base_existing:
if func_meta != FuncMeta.NONE:
base_existing.MetaList = base_existing.MetaList | func_meta
else:
@@ -1374,18 +1356,22 @@ class ImportHandle(BaseHandle):
Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset
current_bit_offset += bw
def _TryLoadStructFromStub(self, class_name: str, Gen):
cache_key = class_name
def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin) -> None:
# 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义,
# 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致
# 跨模块 struct 类型对象无法分别 set_body每个模块有独立的 self.structs
cache_key = (id(Gen), class_name)
if cache_key in self._struct_Load_cache:
return
# 先设置缓存防止递归重入,但如果加载失败则清除缓存允许后续重试
self._struct_Load_cache[cache_key] = True
import os, re
ProjectRoot = self._find_project_root()
temp_dir = os.path.join(ProjectRoot, 'temp')
if not os.path.isdir(temp_dir):
del self._struct_Load_cache[cache_key]
return
if self._stub_cache_dir != temp_dir:
self._stub_cache.clear()
self._stub_cache_dir = temp_dir
@@ -1408,15 +1394,14 @@ class ImportHandle(BaseHandle):
if '.' in full_name:
self._stub_cache.setdefault(full_name, []).append((full_name, struct_body, source_sha1))
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
if class_name not in self._stub_cache:
del self._struct_Load_cache[cache_key]
return
for full_name, struct_body, source_sha1 in self._stub_cache[class_name]:
is_packed = struct_body.startswith('<{') and struct_body.endswith('}>')
if struct_body and struct_body != 'opaque':
@@ -1438,8 +1423,6 @@ class ImportHandle(BaseHandle):
if is_packed:
Gen.class_packed.add(class_name)
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
@@ -1447,8 +1430,7 @@ class ImportHandle(BaseHandle):
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
return
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen):
import os
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:
@@ -1467,7 +1449,8 @@ class ImportHandle(BaseHandle):
try:
with open(pyi_path, 'r', encoding='utf-8') as f:
self._pyi_cache[pyi_path] = ast.parse(f.read())
except Exception:
except Exception as e:
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
self._pyi_cache[pyi_path] = None
return
@@ -1500,40 +1483,13 @@ class ImportHandle(BaseHandle):
return
return
def _BuildScalarConstant(self, value_node, target_type, Gen):
"""Build a scalar constant value from AST node"""
if isinstance(target_type, ir.BaseStructType):
return None
if isinstance(value_node, ast.Constant):
if isinstance(value_node.value, int):
return ir.Constant(target_type, value_node.value)
elif isinstance(value_node.value, str):
str_val = value_node.value + '\x00'
str_bytes = str_val.encode('utf-8')
arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes))
gv_name = f"str_const_{id(value_node)}"
gv = ir.GlobalVariable(Gen.module, arr_type, name=gv_name)
gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
gv.linkage = 'internal'
ptr_type = ir.PointerType(ir.IntType(8))
return ir.Constant(ptr_type, gv.reference)
elif isinstance(value_node.value, bool):
return ir.Constant(target_type, 1 if value_node.value else 0)
elif isinstance(value_node, ast.Name):
if value_node.id == 'True':
return ir.Constant(target_type, 1)
elif value_node.id == 'False':
if isinstance(target_type, ir.PointerType):
return ir.Constant(target_type, None)
return ir.Constant(target_type, 0)
return None
def _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, Gen):
def _LookupStubFuncType(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None:
"""从 stub.ll 文件中查找指定函数的 LLVM 类型"""
import os
import re
import llvmlite.ir as ir
if not getattr(self, '_stub_func_cache', None):
self._stub_func_cache = {}
@@ -1571,8 +1527,6 @@ class ImportHandle(BaseHandle):
stub_params = match.group(3).strip()
self._stub_func_cache[stub_func_name] = (stub_ret, stub_params, '...' in stub_params, source_sha1)
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
@@ -1613,17 +1567,15 @@ class ImportHandle(BaseHandle):
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):
def _strip_llvm_param_name(self, param_str: str) -> str:
"""剥离 LLVM IR 参数字符串中的参数名,仅保留类型部分"""
import re
param_str = param_str.strip()
result = re.sub(r'\s+%[\w.]+\s*$', '', param_str)
return result.strip()
def _parse_simple_llvm_type(self, type_str):
def _parse_simple_llvm_type(self, type_str: str) -> ir.Type | None:
"""解析简单的 LLVM 类型字符串(不创建结构体,避免副作用)"""
import llvmlite.ir as ir
type_str = type_str.strip()
type_map = {
@@ -1648,7 +1600,6 @@ class ImportHandle(BaseHandle):
return ir.PointerType(ir.IntType(8))
if type_str.startswith('[') and ']' in type_str:
import re
arr_match = re.match(r'\[(\d+)\s*x\s+(.+)\]', type_str)
if arr_match:
size = int(arr_match.group(1))
@@ -1661,20 +1612,17 @@ class ImportHandle(BaseHandle):
return None
def _LoadDeclarationsFromStubLlvm(self, module_name: str, Gen):
def _LoadDeclarationsFromStubLlvm(self, module_name: str, Gen: LlvmGeneratorMixin) -> None:
"""从 stub.ll 文件加载函数声明和结构体定义到 Gen"""
import os
import re
import llvmlite.ir as ir
ProjectRoot = self._find_project_root()
temp_dir = os.path.join(ProjectRoot, 'temp')
if not os.path.isdir(temp_dir):
return
all_stub_files = [f for f in os.listdir(temp_dir) if f.endswith('.stub.ll')]
source_sig_files = getattr(self.Trans, '_source_module_sig_files', {})
target_sha1 = None
if module_name:
@@ -1685,7 +1633,7 @@ class ImportHandle(BaseHandle):
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:
@@ -1694,7 +1642,7 @@ class ImportHandle(BaseHandle):
stub_files = []
else:
stub_files = []
needed_sha1s = set()
for filename in stub_files:
stub_path = os.path.join(temp_dir, filename)
@@ -1710,8 +1658,6 @@ class ImportHandle(BaseHandle):
for m in re.finditer(r'%\"?([a-f0-9]{16})\.', stripped):
needed_sha1s.add(m.group(1))
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
@@ -1721,9 +1667,9 @@ class ImportHandle(BaseHandle):
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', '')
@@ -1761,12 +1707,10 @@ class ImportHandle(BaseHandle):
else:
elem_types.append(ir.IntType(32))
st = Gen._get_or_create_struct(clean_name, source_sha1=source_sha1, packed=is_packed)
if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0):
if isinstance(st, ir.IdentifiedStructType) and st.elements is None:
try:
st.set_body(*elem_types)
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
@@ -1806,24 +1750,93 @@ class ImportHandle(BaseHandle):
short_name = func_name.split('.', 1)[1]
Gen.functions[short_name] = func
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
except Exception as _e:
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
if _config_mode == "strict":
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
def _parse_llvm_declare(self, declare_line: str, Gen):
"""解析 LLVM declare 语句"""
import re
import llvmlite.ir as ir
def _TryLoadFuncDeclsFromOutputStub(self, struct_name: str, Gen: LlvmGeneratorMixin) -> bool:
"""按需从 output 目录的 stub.ll 加载泛型特化方法声明。
match = re.match(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$', declare_line)
当 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
@@ -1861,10 +1874,8 @@ class ImportHandle(BaseHandle):
is_variadic = '...' in params_str
return ir.FunctionType(ret_type, param_types, var_arg=is_variadic)
def _parse_llvm_type(self, type_str: str, Gen, source_sha1=None, create_structs=True):
def _parse_llvm_type(self, type_str: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None, create_structs: bool = True) -> ir.Type | None:
"""解析 LLVM 类型字符串"""
import re
import llvmlite.ir as ir
type_str = type_str.strip()
@@ -1926,7 +1937,7 @@ class ImportHandle(BaseHandle):
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)
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
@@ -1938,6 +1949,50 @@ class ImportHandle(BaseHandle):
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:
@@ -1949,13 +2004,12 @@ class ImportHandle(BaseHandle):
return None
def _strip_param_name(self, param_str):
def _strip_param_name(self, param_str: str) -> str:
"""从 LLVM 参数字符串中剥离参数名,只保留类型部分
例如: '%"sha1.struct_name"* %param_name' -> '%"sha1.struct_name"*'
'i8* %path' -> 'i8*'
'i32' -> 'i32'
"""
import re
param_str = param_str.strip()
stripped = re.sub(r'\s+%[\w.]+\s*$', '', param_str)
return stripped.strip()