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

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

@@ -1,11 +1,22 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any, Dict, Iterator
if TYPE_CHECKING:
from lib.core.translator import Translator
import ast
import traceback
from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper
from lib.includes import t
from typing import Any, Dict, Iterator
import ast
from lib.core.VLogger import get_logger as _vlog
from lib.core.DiagnosticCollector import DiagnosticCollector
from lib.core.LLVMTypeMapper import LLVMTypeMapper
from lib.core.SymbolExtractor import ASTSymbolExtractor
from lib.core.SymbolTypeResolver import TypeResolver
from lib.core.SymbolInserter import SymbolInserter
from lib.core.SymbolReexporter import PackageReexporter
from lib.core.ConstEvaluator import ConstEvaluator
from lib.core.SymbolData import ModuleSymbols
# 从 SymbolUtils 重导出,保持向后兼容
from lib.core.SymbolUtils import IsTModuleType as _IsTModuleType
@@ -14,30 +25,35 @@ from lib.core.SymbolUtils import CheckAnnotationHasCInline as _CheckAnnotationHa
class SymbolTable:
def __init__(self, translator: "Translator"):
self.translator = translator
self._symbols: Dict[str, CTypeInfo] = {}
from lib.core.DiagnosticCollector import DiagnosticCollector
self.diagnostics = DiagnosticCollector()
from lib.core.LLVMTypeMapper import LLVMTypeMapper
self._type_mapper = LLVMTypeMapper(self)
self.import_aliases: Dict[str, str] = {}
def __init__(self, translator: Translator) -> None:
self.translator: Translator = translator
self._symbols: dict[str, CTypeInfo] = {}
self._namespaces: dict[str, dict[str, CTypeInfo]] = {} # 模块名 -> 符号字典
self._namespace_imports: dict[str, set[str]] = {} # 模块名 -> 已导入的模块名集合
self._t_type_symbols: dict[str, type] = {} # 别名 -> t.CType 子类 (from t import CEnum as en)
self.diagnostics: DiagnosticCollector = DiagnosticCollector()
self._type_mapper: LLVMTypeMapper = LLVMTypeMapper(self)
self.import_aliases: dict[str, str] = {} # 别名 -> 模块路径
def clear(self):
def clear(self) -> None:
self._symbols.clear()
self._namespaces.clear()
self._namespace_imports.clear()
self._t_type_symbols.clear()
self.import_aliases.clear()
def get(self, name: str, default=None) -> CTypeInfo:
def get(self, name: str, default: CTypeInfo | None = None) -> CTypeInfo | None:
return self._symbols.get(name, default)
def set(self, name: str, value: Any):
def set(self, name: str, value: CTypeInfo | dict[str, Any]) -> None:
if isinstance(value, CTypeInfo):
self._symbols[name] = value
else:
# 兼容旧代码传入 dict 的情况
info = self._CTypeInfoFromDict(name, value)
info: CTypeInfo = self._CTypeInfoFromDict(name, value)
self._symbols[name] = info
def update(self, symbols: Dict[str, Any]):
def update(self, symbols: dict[str, CTypeInfo | dict[str, Any]]) -> None:
for name, value in symbols.items():
self.set(name, value)
@@ -50,7 +66,7 @@ class SymbolTable:
def items(self) -> Iterator[tuple[str, CTypeInfo]]:
return iter(self._symbols.items())
def pop(self, name: str, *args) -> CTypeInfo:
def pop(self, name: str, *args: Any) -> CTypeInfo:
return self._symbols.pop(name, *args)
def __getitem__(self, name: str) -> CTypeInfo:
@@ -58,10 +74,10 @@ class SymbolTable:
return self._symbols[name]
raise KeyError(name)
def __setitem__(self, name: str, value: Any):
def __setitem__(self, name: str, value: CTypeInfo | dict[str, Any]) -> None:
self.set(name, value)
def __delitem__(self, name: str):
def __delitem__(self, name: str) -> None:
del self._symbols[name]
def __contains__(self, name: str) -> bool:
@@ -78,8 +94,73 @@ class SymbolTable:
# ==================================================================
def lookup(self, name: str) -> CTypeInfo | None:
"""查找符号,不存在返回 None替代 `SymbolTable.get(name)`"""
return self._symbols.get(name)
"""查找符号,支持别名解析和 dotted name
解析顺序:
1. 直接匹配 _symbols
2. 如果包含 '.',解析第一个段为别名后查找剩余路径
3. 查 _t_type_symbolst/c 类型别名,返回模拟 CTypeInfo
"""
if name in self._symbols:
return self._symbols[name]
if '.' not in name:
return self._lookup_t_type(name)
# dotted name: 尝试解析第一个段为别名
parts: list[str] = name.split('.', 1)
first: str = parts[0]
rest: str = parts[1]
# 1) 别名解析import X as Y → Y.Symbol → X.Symbol
resolved: str | None = self.import_aliases.get(first)
if resolved:
alias_name: str = f"{resolved}.{rest}"
if alias_name in self._symbols:
return self._symbols[alias_name]
# 2) 纯路径查找module.ClassName
# (已在 line 100 检查过,此处为兜底,保留注释说明路径查找语义)
return None
def _lookup_t_type(self, name: str) -> CTypeInfo | None:
"""在 _t_type_symbols 中查找 t/c 类型别名,返回模拟 CTypeInfo"""
cls: type | None = self._t_type_symbols.get(name)
if cls is None:
return None
info: CTypeInfo = CTypeInfo()
info.Name = name
info.BaseType = cls()
if issubclass(cls, (t.CEnum, t.REnum)):
info.IsEnum = True
elif issubclass(cls, t.CUnion):
info.IsUnion = True
elif issubclass(cls, t.CStruct):
info.IsStruct = True
return info
def lookup_in_namespace(self, name: str, namespace: str) -> CTypeInfo | None:
"""在指定模块命名空间中查找符号"""
ns: dict[str, CTypeInfo] | None = self._namespaces.get(namespace)
if ns:
return ns.get(name)
return None
def register_namespace(self, module_name: str) -> None:
"""注册一个模块命名空间"""
if module_name not in self._namespaces:
self._namespaces[module_name] = {}
def register_namespace_import(self, importer: str, imported: str) -> None:
"""记录模块导入关系"""
if importer not in self._namespace_imports:
self._namespace_imports[importer] = set()
self._namespace_imports[importer].add(imported)
def get_namespace_symbols(self, module_name: str) -> dict[str, CTypeInfo]:
"""获取模块的命名空间符号表"""
return self._namespaces.get(module_name, {})
def has(self, name: str) -> bool:
"""检查符号是否存在(替代 `name in SymbolTable`"""
@@ -87,70 +168,70 @@ class SymbolTable:
def lookup_struct(self, name: str) -> CTypeInfo | None:
"""查找结构体符号,非结构体返回 None"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
if entry and (entry.IsStruct or entry.IsCpythonObject):
return entry
return None
def lookup_enum(self, name: str) -> CTypeInfo | None:
"""查找枚举符号,非枚举返回 None"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
if entry and entry.IsEnum:
return entry
return None
def lookup_typedef(self, name: str) -> CTypeInfo | None:
"""查找 typedef 符号,非 typedef 返回 None"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
if entry and entry.IsTypedef:
return entry
return None
def lookup_function(self, name: str) -> CTypeInfo | None:
"""查找函数符号,非函数返回 None"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
if entry and (entry.IsFunction or entry.IsFuncPtr):
return entry
return None
def lookup_define(self, name: str) -> CTypeInfo | None:
"""查找常量定义符号,非 define 返回 None"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
if entry and entry.IsDefine:
return entry
return None
def is_struct(self, name: str) -> bool:
"""检查名称是否为结构体"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
return entry is not None and (entry.IsStruct or entry.IsCpythonObject)
def is_enum(self, name: str) -> bool:
"""检查名称是否为枚举"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
return entry is not None and entry.IsEnum
def is_typedef(self, name: str) -> bool:
"""检查名称是否为 typedef"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
return entry is not None and entry.IsTypedef
def is_function(self, name: str) -> bool:
"""检查名称是否为函数"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
return entry is not None and (entry.IsFunction or entry.IsFuncPtr)
def is_variable(self, name: str) -> bool:
"""检查名称是否为变量"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
return entry is not None and entry.IsVariable
def is_define(self, name: str) -> bool:
"""检查名称是否为常量定义"""
entry = self._symbols.get(name)
entry: CTypeInfo | None = self._symbols.get(name)
return entry is not None and entry.IsDefine
def insert(self, name: str, info: CTypeInfo):
def insert(self, name: str, info: CTypeInfo) -> None:
"""插入符号(替代 `SymbolTable[name] = info`"""
self._symbols[name] = info
@@ -160,19 +241,25 @@ class SymbolTable:
def find_by_suffix(self, suffix: str) -> list[tuple[str, CTypeInfo]]:
"""按名称后缀模糊查找符号(替代遍历 + endswith 匹配)"""
results = []
results: list[tuple[str, CTypeInfo]] = []
for name, info in self._symbols.items():
if name.endswith(suffix):
results.append((name, info))
return results
def ToDict(self) -> Dict[str, Any]:
"""序列化为字典格式"""
result = {}
def ToDict(self) -> dict[str, Any]:
"""序列化为字典格式BaseType 使用可重建的 dict 格式
返回的 dict 包含:
- 符号条目:{name: {attrs}}(与旧格式兼容)
- '__namespaces__': {namespace: [symbol_names]} 命名空间索引
- '__import_aliases__': {alias: module_path} 导入别名
"""
result: dict[str, Any] = {}
for name, info in self._symbols.items():
entry = {'name': name}
entry: dict[str, Any] = {'name': name}
if info.BaseType:
entry['BaseType'] = str(info.BaseType)
entry['BaseType'] = self._SerializeBaseType(info.BaseType)
if info.PtrCount:
entry['PtrCount'] = info.PtrCount
if info.IsTypedef:
@@ -197,27 +284,99 @@ class SymbolTable:
entry['file'] = info.file
if info.Members:
entry['members'] = info.Members
extra = info._sm._extra
extra: dict[str, Any] = info._sm._extra
if extra:
entry.update(extra)
result[name] = entry
# 序列化命名空间信息
if self._namespaces:
result['__namespaces__'] = {
ns: list(symbols.keys()) for ns, symbols in self._namespaces.items()
}
if self.import_aliases:
result['__import_aliases__'] = dict(self.import_aliases)
if self._t_type_symbols:
result['__t_type_symbols__'] = {
name: cls.__name__ for name, cls in self._t_type_symbols.items()
}
return result
@staticmethod
def _SerializeBaseType(base_type: t.CType | tuple | list | str | None) -> dict | str:
"""将 BaseType 序列化为可重建的格式"""
if isinstance(base_type, t.CType):
return {'class': type(base_type).__name__}
if isinstance(base_type, (tuple, list)):
return {'classes': [type(b).__name__ for b in base_type if isinstance(b, t.CType)]}
return str(base_type)
@staticmethod
def _DeserializeBaseType(data: Any) -> Any:
"""从序列化格式恢复 BaseType"""
if isinstance(data, dict):
if 'class' in data:
cls: type | None = getattr(t, data['class'], None)
if cls and isinstance(cls, type) and issubclass(cls, t.CType):
return cls()
elif 'classes' in data:
types: list[t.CType] = []
for cname in data['classes']:
cls: type | None = getattr(t, cname, None)
if cls and isinstance(cls, type) and issubclass(cls, t.CType):
types.append(cls())
if types:
return tuple(types)
elif isinstance(data, str):
cls: type | None = getattr(t, data, None)
if cls and isinstance(cls, type) and issubclass(cls, t.CType):
return cls()
return None
def FromDict(self, symbols: Dict[str, Any]):
"""从字典格式反序列化"""
def FromDict(self, symbols: dict[str, Any]) -> None:
"""从字典格式反序列化(兼容带/不带命名空间信息的两种格式)"""
self._symbols.clear()
self._namespaces.clear()
self.import_aliases.clear()
self._t_type_symbols.clear()
_SPECIAL_KEYS: frozenset[str] = frozenset({'__namespaces__', '__import_aliases__', '__t_type_symbols__'})
for name, attrs in symbols.items():
if name in _SPECIAL_KEYS:
continue
if isinstance(attrs, dict):
info = self._CTypeInfoFromDict(name, attrs)
info: CTypeInfo = self._CTypeInfoFromDict(name, attrs)
self._symbols[name] = info
elif isinstance(attrs, CTypeInfo):
self._symbols[name] = attrs
# 恢复命名空间信息
ns_data: dict[str, list[str]] | None = symbols.get('__namespaces__')
if ns_data:
for ns, sym_names in ns_data.items():
self._namespaces[ns] = {}
for sym_name in sym_names:
if sym_name in self._symbols:
self._namespaces[ns][sym_name] = self._symbols[sym_name]
aliases_data: dict[str, str] | None = symbols.get('__import_aliases__')
if aliases_data:
self.import_aliases.update(aliases_data)
# 恢复 t 类型别名
t_type_data: dict[str, str] | None = symbols.get('__t_type_symbols__')
if t_type_data:
for name, cls_name in t_type_data.items():
cls: type | None = getattr(t, cls_name, None)
if cls and isinstance(cls, type) and issubclass(cls, t.CType):
self._t_type_symbols[name] = cls
def _CTypeInfoFromDict(self, name: str, attrs: dict) -> CTypeInfo:
def _CTypeInfoFromDict(self, name: str, attrs: dict[str, Any]) -> CTypeInfo:
"""从旧 dict 格式创建 CTypeInfo兼容旧代码"""
info = CTypeInfo()
info: CTypeInfo = CTypeInfo()
info.Name = name
node_type = attrs.get('type', '')
node_type: str = attrs.get('type', '')
if node_type == 'struct' or attrs.get('IsStruct'):
info.IsStruct = True
@@ -260,13 +419,17 @@ class SymbolTable:
info.EnumName = attrs['EnumName']
if 'DefineValue' in attrs:
info.DefineValue = attrs['DefineValue']
if 'BaseType' in attrs:
info.BaseType = self._DeserializeBaseType(attrs['BaseType'])
if 'name' in attrs and attrs['name'] != name:
info.Name = attrs['name']
# 其他属性存入 _extra
skip_keys = {'type', 'PtrCount', 'OriginalType', 'lineno', 'file',
skip_keys: frozenset[str] = frozenset({'type', 'name', 'PtrCount', 'OriginalType', 'lineno', 'file',
'members', 'IsPtr', 'dims', 'IsCpythonObject', 'IsAnonymous',
'IsPacked', 'EnumName', 'DefineValue',
'IsPacked', 'EnumName', 'DefineValue', 'BaseType',
'IsStruct', 'IsEnum', 'IsUnion', 'IsTypedef',
'IsFunction', 'IsVariable', 'IsDefine', 'IsEnumMember'}
'IsFunction', 'IsVariable', 'IsDefine', 'IsEnumMember'})
for key, value in attrs.items():
if key not in skip_keys:
info.set(key, value)
@@ -281,34 +444,33 @@ class SymbolTable:
return self._LoadSymbolsFromFile(FullModulePath, asname, lineno)
def _LoadSymbolsFromFile(self, FilePath: str, namespace_prefix: str | list[str], lineno: int = 0) -> list[str]:
from lib.core.SymbolExtractor import ASTSymbolExtractor
from lib.core.SymbolTypeResolver import TypeResolver
from lib.core.SymbolInserter import SymbolInserter
from lib.core.SymbolReexporter import PackageReexporter
try:
# 提取模块名作为命名空间
module_name: str = namespace_prefix[0] if isinstance(namespace_prefix, list) else namespace_prefix
# Pass 1: 从 AST 提取原始符号信息
extractor = ASTSymbolExtractor(self)
module_symbols = extractor.extract(FilePath)
extractor: ASTSymbolExtractor = ASTSymbolExtractor(self)
module_symbols: ModuleSymbols = extractor.extract(FilePath)
# Pass 2: 解析类型信息typedef、函数签名、define 值)
resolver = TypeResolver(self)
resolver: TypeResolver = TypeResolver(self)
resolver.resolve(module_symbols)
# Pass 3: 插入符号到主命名空间(无前缀)
inserter = SymbolInserter(self)
loaded = inserter.insert(module_symbols)
inserter: SymbolInserter = SymbolInserter(self)
inserter.set_namespace(module_name)
loaded: list[str] = inserter.insert(module_symbols)
# Pass 4: 重新导出到命名空间前缀下
prefixes = [namespace_prefix] if isinstance(namespace_prefix, str) else namespace_prefix
reexporter = PackageReexporter(self)
prefixes: list[str] = [namespace_prefix] if isinstance(namespace_prefix, str) else namespace_prefix
reexporter: PackageReexporter = PackageReexporter(self)
reexporter.set_namespace(module_name)
loaded.extend(reexporter.reexport(module_symbols, prefixes, lineno))
return loaded
except Exception as e:
import traceback
print(traceback.format_exc())
_vlog().error(traceback.format_exc())
self.diagnostics.error(FilePath, lineno, f"加载模块失败: {e}")
return []
@@ -316,28 +478,28 @@ class SymbolTable:
# 类型解析辅助方法(委托到 LLVMTypeMapper
# ==================================================================
def _GetLLVMTypeStr(self, node):
def _GetLLVMTypeStr(self, node: ast.AST) -> str:
return self._type_mapper.get_llvm_type_str(node)
def _GetFuncRetTypeStr(self, returns_node):
def _GetFuncRetTypeStr(self, returns_node: ast.AST | None) -> str:
return self._type_mapper.get_func_ret_type_str(returns_node)
@staticmethod
def _CheckAnnotationHasCInline(annotation_node) -> bool:
def _CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool:
return _CheckAnnotationHasCInline(annotation_node)
def _GetFuncParamTypeStr(self, annotation_node):
def _GetFuncParamTypeStr(self, annotation_node: ast.AST) -> str:
return self._type_mapper.get_func_param_type_str(annotation_node)
def _ResolveTypedefValueType(self, node):
def _ResolveTypedefValueType(self, node: ast.AST) -> str:
return self._type_mapper.resolve_typedef_value_type(node)
# ==================================================================
# 符号插入方法(供 SymbolInserter / PackageReexporter 调用)
# ==================================================================
def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict, IsCpythonObject: bool, IsPacked: bool = False):
info = CTypeInfo()
def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict[str, CTypeInfo], IsCpythonObject: bool, IsPacked: bool = False) -> None:
info: CTypeInfo = CTypeInfo()
info.Lineno = lineno
info.file = FilePath
info.Members = members if members else {}
@@ -359,17 +521,18 @@ class SymbolTable:
self._symbols[FullName] = info
def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str):
info = CTypeInfo()
def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str) -> None:
info: CTypeInfo = CTypeInfo()
info.IsEnumMember = True
info.EnumName = EnumName
info.Lineno = lineno
info.file = FilePath
self._symbols[FullName] = info
def _InsertTypedefSymbol(self, FullName: str, OriginalType_kind: str | None, OriginalClass, lineno: int, FilePath: str, members: dict | None = None):
info = CTypeInfo()
def _InsertTypedefSymbol(self, FullName: str, OriginalType_kind: str | None, OriginalClass: CTypeInfo | str | None, lineno: int, FilePath: str, members: dict[str, CTypeInfo] | None = None) -> None:
info: CTypeInfo = CTypeInfo()
info.IsTypedef = True
info.Name = FullName
info.Lineno = lineno
info.file = FilePath
if members:
@@ -378,7 +541,7 @@ class SymbolTable:
if isinstance(OriginalClass, CTypeInfo):
info.OriginalType = OriginalClass
elif OriginalType_kind == 'typedef' and OriginalClass and isinstance(OriginalClass, str) and ('*' in OriginalClass):
OriginalType = OriginalClass
OriginalType: str = OriginalClass
if OriginalType == 'CVoid *':
OriginalType = 'void *'
info.OriginalType = OriginalType
@@ -393,15 +556,15 @@ class SymbolTable:
self._symbols[FullName] = info
def _InsertModuleSymbol(self, name: str, lineno: int, FilePath: str):
info = CTypeInfo()
def _InsertModuleSymbol(self, name: str, lineno: int, FilePath: str) -> None:
info: CTypeInfo = CTypeInfo()
info.IsModuleAlias = True
info.Lineno = lineno
info.file = FilePath
self._symbols[name] = info
def _InsertFuncSymbol(self, FullName: str, RetType, ParamTypes: list, lineno: int, FilePath: str, IsVariadic: bool = False, IsInline: bool = False):
info = CTypeInfo()
def _InsertFuncSymbol(self, FullName: str, RetType: str | CTypeInfo | None, ParamTypes: list[str], lineno: int, FilePath: str, IsVariadic: bool = False, IsInline: bool = False) -> None:
info: CTypeInfo = CTypeInfo()
info.IsFunction = True
if isinstance(RetType, str):
info.FuncPtrReturn = CTypeInfo.FromTypeName(RetType) if RetType else CTypeInfo.VoidTypeInfo()
@@ -418,21 +581,20 @@ class SymbolTable:
info.file = FilePath
self._symbols[FullName] = info
def _InsertDefineSymbol(self, FullName: str, DefineValue, lineno: int, FilePath: str):
info = CTypeInfo()
def _InsertDefineSymbol(self, FullName: str, DefineValue: int | str | float | bool | None, lineno: int, FilePath: str) -> None:
info: CTypeInfo = CTypeInfo()
info.IsDefine = True
info.DefineValue = DefineValue
info.Lineno = lineno
info.file = FilePath
self._symbols[FullName] = info
def _eval_const_expr(self, node):
def _eval_const_expr(self, node: ast.AST) -> int | float | str | None:
"""计算常量表达式的值(委托到 ConstEvaluator"""
from lib.core.ConstEvaluator import ConstEvaluator
return ConstEvaluator.eval_with_symtab(node, self)
def _InsertAnonymousSymbol(self, FullName: str, IsUnion: bool, members: dict, lineno: int, FilePath: str):
info = CTypeInfo()
def _InsertAnonymousSymbol(self, FullName: str, IsUnion: bool, members: dict[str, CTypeInfo], lineno: int, FilePath: str) -> None:
info: CTypeInfo = CTypeInfo()
if IsUnion:
info.IsUnion = True
else: