935 lines
50 KiB
Python
935 lines
50 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper
|
||
from lib.includes import t
|
||
from typing import Any, Dict, Iterator
|
||
import ast
|
||
import re
|
||
|
||
def _IsTModuleType(TypeName: str) -> bool:
|
||
"""检测类型名称是否是 t 模块中的类型(CType 或其他特殊类型)"""
|
||
if TypeName in ('t', 'State', 'Bit', 'Anonymous', 'Postdefinition'):
|
||
return True
|
||
TypeClass = getattr(t, TypeName, None)
|
||
if TypeClass and isinstance(TypeClass, type):
|
||
if issubclass(TypeClass, t.CType):
|
||
return True
|
||
FallbackClass = getattr(t, f'_{TypeName}', None)
|
||
if FallbackClass and isinstance(FallbackClass, type):
|
||
if issubclass(FallbackClass, t.CType):
|
||
return True
|
||
return False
|
||
|
||
def _AnnotationContainsTType(node, TypeName: str) -> bool:
|
||
"""检测 AST 节点中是否包含指定名称的 t 模块类型"""
|
||
if isinstance(node, ast.Attribute):
|
||
if (hasattr(node.value, 'id') and node.value.id == 't' and
|
||
node.attr == TypeName):
|
||
return True
|
||
elif isinstance(node, ast.BinOp):
|
||
return _AnnotationContainsTType(node.left, TypeName) or _AnnotationContainsTType(node.right, TypeName)
|
||
elif isinstance(node, ast.Call):
|
||
if _AnnotationContainsTType(node.func, TypeName):
|
||
return True
|
||
for arg in node.args:
|
||
if _AnnotationContainsTType(arg, TypeName):
|
||
return True
|
||
elif isinstance(node, ast.Subscript):
|
||
if _AnnotationContainsTType(node.value, TypeName):
|
||
return True
|
||
if _AnnotationContainsTType(node.slice, TypeName):
|
||
return True
|
||
return False
|
||
|
||
|
||
class SymbolTable:
|
||
def __init__(self, translator: "Translator"):
|
||
self.translator = translator
|
||
self._symbols: Dict[str, CTypeInfo] = {}
|
||
|
||
def clear(self):
|
||
self._symbols.clear()
|
||
|
||
def get(self, name: str, default=None) -> CTypeInfo:
|
||
return self._symbols.get(name, default)
|
||
|
||
def set(self, name: str, value: Any):
|
||
if isinstance(value, CTypeInfo):
|
||
self._symbols[name] = value
|
||
else:
|
||
# 兼容旧代码传入 dict 的情况
|
||
info = self._CTypeInfoFromDict(name, value)
|
||
self._symbols[name] = info
|
||
|
||
def update(self, symbols: Dict[str, Any]):
|
||
for name, value in symbols.items():
|
||
self.set(name, value)
|
||
|
||
def keys(self) -> Iterator[str]:
|
||
return iter(self._symbols.keys())
|
||
|
||
def values(self) -> Iterator[CTypeInfo]:
|
||
return iter(self._symbols.values())
|
||
|
||
def items(self) -> Iterator[tuple[str, CTypeInfo]]:
|
||
return iter(self._symbols.items())
|
||
|
||
def pop(self, name: str, *args) -> CTypeInfo:
|
||
return self._symbols.pop(name, *args)
|
||
|
||
def __getitem__(self, name: str) -> CTypeInfo:
|
||
if name in self._symbols:
|
||
return self._symbols[name]
|
||
raise KeyError(name)
|
||
|
||
def __setitem__(self, name: str, value: Any):
|
||
self.set(name, value)
|
||
|
||
def __delitem__(self, name: str):
|
||
del self._symbols[name]
|
||
|
||
def __contains__(self, name: str) -> bool:
|
||
return name in self._symbols
|
||
|
||
def __len__(self) -> int:
|
||
return len(self._symbols)
|
||
|
||
def __iter__(self) -> Iterator[str]:
|
||
return iter(self._symbols)
|
||
|
||
def ToDict(self) -> Dict[str, Any]:
|
||
"""序列化为字典格式"""
|
||
result = {}
|
||
for name, info in self._symbols.items():
|
||
entry = {'name': name}
|
||
if info.BaseType:
|
||
entry['BaseType'] = str(info.BaseType)
|
||
if info.PtrCount:
|
||
entry['PtrCount'] = info.PtrCount
|
||
if info.IsTypedef:
|
||
entry['IsTypedef'] = True
|
||
if info.IsStruct:
|
||
entry['IsStruct'] = True
|
||
if info.IsEnum:
|
||
entry['IsEnum'] = True
|
||
if info.IsUnion:
|
||
entry['IsUnion'] = True
|
||
if info.IsFunction:
|
||
entry['IsFunction'] = True
|
||
if info.IsVariable:
|
||
entry['IsVariable'] = True
|
||
if info.IsDefine:
|
||
entry['IsDefine'] = True
|
||
if info.OriginalType:
|
||
entry['OriginalType'] = str(info.OriginalType)
|
||
if info.Lineno:
|
||
entry['lineno'] = info.Lineno
|
||
if info.file:
|
||
entry['file'] = info.file
|
||
if info.Members:
|
||
entry['members'] = info.Members
|
||
extra = info._sm._extra
|
||
if extra:
|
||
entry.update(extra)
|
||
result[name] = entry
|
||
return result
|
||
|
||
def FromDict(self, symbols: Dict[str, Any]):
|
||
"""从字典格式反序列化"""
|
||
self._symbols.clear()
|
||
for name, attrs in symbols.items():
|
||
if isinstance(attrs, dict):
|
||
info = self._CTypeInfoFromDict(name, attrs)
|
||
self._symbols[name] = info
|
||
elif isinstance(attrs, CTypeInfo):
|
||
self._symbols[name] = attrs
|
||
|
||
def _CTypeInfoFromDict(self, name: str, attrs: dict) -> CTypeInfo:
|
||
"""从旧 dict 格式创建 CTypeInfo(兼容旧代码)"""
|
||
info = CTypeInfo()
|
||
info.Name = name
|
||
node_type = attrs.get('type', '')
|
||
|
||
if node_type == 'struct' or attrs.get('IsStruct'):
|
||
info.IsStruct = True
|
||
elif node_type == 'enum' or attrs.get('IsEnum'):
|
||
info.IsEnum = True
|
||
elif node_type == 'union' or attrs.get('IsUnion'):
|
||
info.IsUnion = True
|
||
elif node_type == 'typedef' or attrs.get('IsTypedef'):
|
||
info.IsTypedef = True
|
||
elif node_type == 'function' or attrs.get('IsFunction'):
|
||
info.IsFunction = True
|
||
elif node_type == 'variable' or attrs.get('IsVariable'):
|
||
info.IsVariable = True
|
||
elif node_type == 'define' or attrs.get('IsDefine'):
|
||
info.IsDefine = True
|
||
elif node_type == 'enum_member' or attrs.get('IsEnumMember'):
|
||
info.IsEnumMember = True
|
||
|
||
if 'PtrCount' in attrs:
|
||
info.PtrCount = attrs['PtrCount']
|
||
if 'OriginalType' in attrs:
|
||
info.OriginalType = attrs['OriginalType']
|
||
if 'lineno' in attrs:
|
||
info.Lineno = attrs['lineno']
|
||
if 'file' in attrs:
|
||
info.file = attrs['file']
|
||
if 'members' in attrs:
|
||
info.Members = attrs['members']
|
||
if 'IsPtr' in attrs:
|
||
info.IsPtr = attrs['IsPtr']
|
||
if 'dims' in attrs:
|
||
info.ArrayDims = attrs['dims']
|
||
if 'IsCpythonObject' in attrs:
|
||
info.IsCpythonObject = attrs['IsCpythonObject']
|
||
if 'IsAnonymous' in attrs:
|
||
info.IsAnonymous = attrs['IsAnonymous']
|
||
if 'IsPacked' in attrs:
|
||
info.IsPacked = attrs['IsPacked']
|
||
if 'EnumName' in attrs:
|
||
info.EnumName = attrs['EnumName']
|
||
if 'DefineValue' in attrs:
|
||
info.DefineValue = attrs['DefineValue']
|
||
|
||
# 其他属性存入 _extra
|
||
skip_keys = {'type', 'PtrCount', 'OriginalType', 'lineno', 'file',
|
||
'members', 'IsPtr', 'dims', 'IsCpythonObject', 'IsAnonymous',
|
||
'IsPacked', 'EnumName', 'DefineValue',
|
||
'IsStruct', 'IsEnum', 'IsUnion', 'IsTypedef',
|
||
'IsFunction', 'IsVariable', 'IsDefine', 'IsEnumMember'}
|
||
for key, value in attrs.items():
|
||
if key not in skip_keys:
|
||
info.set(key, value)
|
||
|
||
return info
|
||
|
||
def LoadModuleSymbols(self, FullModulePath: str, asname: str, lineno: int = 0) -> list[str]:
|
||
return self._LoadSymbolsFromFile(FullModulePath, asname, lineno)
|
||
|
||
def _LoadSymbolsFromFile(self, FilePath: str, namespace_prefix: str | list[str], lineno: int = 0) -> list[str]:
|
||
from lib.core.Handles.HandlesTypeMerge import HandlesTypeMerge
|
||
import ast
|
||
|
||
LoadedSymbols = []
|
||
|
||
try:
|
||
with open(FilePath, 'r', encoding='utf-8') as f:
|
||
ModuleCode = f.read()
|
||
ModuleTree = ast.parse(ModuleCode)
|
||
|
||
ClassMembers = {}
|
||
AnonymousTypes = {}
|
||
|
||
for node in ModuleTree.body:
|
||
if isinstance(node, ast.ClassDef):
|
||
ClassName = node.name
|
||
MembersInfo = {}
|
||
|
||
def CollectMembers(ClassNode, prefix=''):
|
||
for item in ClassNode.body:
|
||
if isinstance(item, ast.AnnAssign):
|
||
if isinstance(item.target, ast.Name):
|
||
MemberName = item.target.id
|
||
FullName = f'{prefix}{MemberName}' if prefix else MemberName
|
||
MemberTypeInfo = CTypeInfo.FromNode(item.annotation, self.translator.SymbolTable)
|
||
if MemberTypeInfo is None:
|
||
MemberTypeInfo = CTypeInfo()
|
||
MemberTypeInfo.BaseType = t.CInt()
|
||
IsPtr = MemberTypeInfo.IsPtr
|
||
if isinstance(item.annotation, ast.BinOp):
|
||
AnnotationStr = ast.dump(item.annotation)
|
||
if 'CPtr' in AnnotationStr:
|
||
IsPtr = True
|
||
MembersInfo[FullName] = MemberTypeInfo.Copy()
|
||
elif isinstance(item, ast.ClassDef):
|
||
IsAnonymous = any(
|
||
any(_AnnotationContainsTType(base, 'Anonymous') for base in item.bases)
|
||
) if item.bases else False
|
||
|
||
if IsAnonymous:
|
||
IsUnion = any(
|
||
any(_AnnotationContainsTType(base, 'CUnion') for base in item.bases)
|
||
)
|
||
|
||
NestedMemberName = f'{prefix}{item.name}' if prefix else item.name
|
||
MembersInfo[NestedMemberName] = CTypeInfo()
|
||
MembersInfo[NestedMemberName].BaseType = f'union {item.name}' if IsUnion else f'struct {item.name}'
|
||
MembersInfo[NestedMemberName].PtrCount = 0
|
||
MembersInfo[NestedMemberName].ArrayDims = []
|
||
|
||
NewPrefix = f'{prefix}{item.name}.' if prefix else f'{item.name}.'
|
||
CollectMembers(item, NewPrefix)
|
||
|
||
AnonymousMembers = {}
|
||
for FullName, MemberInfo in MembersInfo.items():
|
||
if FullName.startswith(f"{NestedMemberName}."):
|
||
MemberName = FullName[len(NestedMemberName) + 1:]
|
||
AnonymousMembers[MemberName] = MemberInfo
|
||
|
||
if item.name not in AnonymousTypes:
|
||
AnonymousTypes[item.name] = {
|
||
'IsUnion': IsUnion,
|
||
'members': AnonymousMembers,
|
||
'lineno': item.lineno
|
||
}
|
||
|
||
CollectMembers(node)
|
||
ClassMembers[ClassName] = MembersInfo
|
||
|
||
LoadedSymbols = []
|
||
|
||
prefixes = [namespace_prefix] if isinstance(namespace_prefix, str) else namespace_prefix
|
||
|
||
for prefix in prefixes:
|
||
for node in ModuleTree.body:
|
||
if isinstance(node, ast.ClassDef):
|
||
ClassName = node.name
|
||
MembersInfo = ClassMembers.get(ClassName, {})
|
||
|
||
TypeKind = 'struct'
|
||
IsCpythonObject = False
|
||
IsPacked = False
|
||
|
||
if hasattr(node, 'decorator_list') and node.decorator_list:
|
||
for decorator in node.decorator_list:
|
||
if isinstance(decorator, ast.Attribute):
|
||
if (hasattr(decorator.value, 'id') and
|
||
decorator.value.id == 't' and
|
||
decorator.attr == 'Object'):
|
||
IsCpythonObject = True
|
||
break
|
||
elif isinstance(decorator, ast.Call):
|
||
if isinstance(decorator.func, ast.Attribute):
|
||
if hasattr(decorator.func.value, 'id') and decorator.func.value.id == 'c' and decorator.func.attr == 'Attribute':
|
||
for arg in decorator.args:
|
||
if isinstance(arg, ast.Attribute):
|
||
if isinstance(arg.value, ast.Attribute):
|
||
if hasattr(arg.value.value, 'id') and arg.value.value.id == 't' and arg.value.attr == 'attr' and arg.attr == 'packed':
|
||
IsPacked = True
|
||
|
||
for base in node.bases:
|
||
if _AnnotationContainsTType(base, 'CUnion'):
|
||
TypeKind = 'union'
|
||
break
|
||
elif _AnnotationContainsTType(base, 'CEnum'):
|
||
TypeKind = 'enum'
|
||
break
|
||
elif _AnnotationContainsTType(base, 'CStruct'):
|
||
TypeKind = 'struct'
|
||
break
|
||
elif _AnnotationContainsTType(base, 'Object'):
|
||
IsCpythonObject = True
|
||
TypeKind = 'struct'
|
||
break
|
||
elif isinstance(base, ast.Name) and base.id == 'Exception':
|
||
TypeKind = 'exception'
|
||
break
|
||
elif isinstance(base, ast.Name):
|
||
base_entry = self.get(base.id)
|
||
if base_entry and hasattr(base_entry, 'IsExceptionClass') and base_entry.IsExceptionClass:
|
||
TypeKind = 'exception'
|
||
break
|
||
|
||
if prefix == prefixes[0]:
|
||
self._InsertClassSymbol(ClassName, TypeKind, node.lineno, FilePath, MembersInfo, IsCpythonObject, IsPacked)
|
||
LoadedSymbols.append(ClassName)
|
||
|
||
FullName = f"{prefix}.{ClassName}"
|
||
self._InsertClassSymbol(FullName, TypeKind, node.lineno, FilePath, MembersInfo, IsCpythonObject, IsPacked)
|
||
LoadedSymbols.append(FullName)
|
||
|
||
if TypeKind == 'enum':
|
||
next_enum_value = 0
|
||
for item in node.body:
|
||
if isinstance(item, ast.Assign):
|
||
if len(item.targets) == 1 and isinstance(item.targets[0], ast.Name):
|
||
MemberName = item.targets[0].id
|
||
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||
next_enum_value = item.value.value + 1
|
||
else:
|
||
next_enum_value += 1
|
||
if prefix == prefixes[0]:
|
||
self._InsertEnumMemberSymbol(MemberName, ClassName, item.lineno, FilePath)
|
||
LoadedSymbols.append(MemberName)
|
||
ClassMemberName = f"{ClassName}.{MemberName}"
|
||
self._InsertEnumMemberSymbol(ClassMemberName, ClassName, item.lineno, FilePath)
|
||
LoadedSymbols.append(ClassMemberName)
|
||
FullMemberName = f"{prefix}.{MemberName}"
|
||
self._InsertEnumMemberSymbol(FullMemberName, ClassName, item.lineno, FilePath)
|
||
LoadedSymbols.append(FullMemberName)
|
||
elif isinstance(item, ast.AnnAssign):
|
||
if isinstance(item.target, ast.Name):
|
||
MemberName = item.target.id
|
||
if item.value:
|
||
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||
next_enum_value = item.value.value + 1
|
||
else:
|
||
next_enum_value += 1
|
||
else:
|
||
next_enum_value += 1
|
||
if prefix == prefixes[0]:
|
||
self._InsertEnumMemberSymbol(MemberName, ClassName, item.lineno, FilePath)
|
||
LoadedSymbols.append(MemberName)
|
||
ClassMemberName = f"{ClassName}.{MemberName}"
|
||
self._InsertEnumMemberSymbol(ClassMemberName, ClassName, item.lineno, FilePath)
|
||
LoadedSymbols.append(ClassMemberName)
|
||
FullMemberName = f"{prefix}.{MemberName}"
|
||
self._InsertEnumMemberSymbol(FullMemberName, ClassName, item.lineno, FilePath)
|
||
LoadedSymbols.append(FullMemberName)
|
||
|
||
elif isinstance(node, ast.AnnAssign):
|
||
if isinstance(node.target, ast.Name):
|
||
VarName = node.target.id
|
||
HasCDefine = _AnnotationContainsTType(node.annotation, 'CDefine') if node.annotation else False
|
||
HasPostdef = _AnnotationContainsTType(node.annotation, 'Postdefinition') if node.annotation else False
|
||
HasCtypedef = _AnnotationContainsTType(node.annotation, 'CTypedef') if node.annotation else False
|
||
IsPostdefWithTypedef = HasPostdef and HasCtypedef
|
||
|
||
if HasCDefine:
|
||
DefineValue = None
|
||
if node.value:
|
||
DefineValue = self._eval_const_expr(node.value)
|
||
if prefix == prefixes[0]:
|
||
self._InsertDefineSymbol(VarName, DefineValue, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertDefineSymbol(FullName, DefineValue, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
continue
|
||
|
||
def FindBaseType(n):
|
||
if isinstance(n, ast.Attribute):
|
||
if hasattr(n.value, 'id') and n.value.id == 't':
|
||
if n.attr == 'CPtr':
|
||
return 'ptr'
|
||
return n.attr
|
||
elif isinstance(n, ast.Subscript):
|
||
if isinstance(n.value, ast.Attribute) and isinstance(n.value.value, ast.Name) and n.value.value.id == 't' and n.value.attr == 'Callable':
|
||
return 'Callable'
|
||
elif isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr):
|
||
LeftResult = FindBaseType(n.left)
|
||
if LeftResult == 'ptr':
|
||
RightResult = FindBaseType(n.right)
|
||
return (RightResult if RightResult and RightResult != 'ptr' else 'void') + ' *'
|
||
if LeftResult == 'Callable':
|
||
return 'Callable'
|
||
RightResult = FindBaseType(n.right)
|
||
if RightResult == 'ptr':
|
||
return (LeftResult if LeftResult and LeftResult != 'ptr' else 'void') + ' *'
|
||
if RightResult == 'Callable':
|
||
return 'Callable'
|
||
if LeftResult:
|
||
return LeftResult
|
||
if RightResult:
|
||
return RightResult
|
||
return None
|
||
|
||
if IsPostdefWithTypedef:
|
||
def FindPostdefArg(n):
|
||
if isinstance(n, ast.Call):
|
||
if isinstance(n.func, ast.Attribute):
|
||
if hasattr(n.func.value, 'id') and n.func.value.id == 't' and n.func.attr == 'Postdefinition':
|
||
if n.args and isinstance(n.args[0], ast.Name):
|
||
return n.args[0].id
|
||
elif isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr):
|
||
LeftResult = FindPostdefArg(n.left)
|
||
if LeftResult:
|
||
return LeftResult
|
||
RightResult = FindPostdefArg(n.right)
|
||
if RightResult:
|
||
return RightResult
|
||
return None
|
||
|
||
OriginalClass = FindPostdefArg(node.annotation)
|
||
if OriginalClass and OriginalClass in ClassMembers:
|
||
MembersInfo = ClassMembers[OriginalClass]
|
||
OriginalType_kind = 'struct'
|
||
if OriginalClass in self:
|
||
OrigTypeInfo = self[OriginalClass]
|
||
if OrigTypeInfo.type_cls in (t.CStruct, t.CEnum, t.CUnion):
|
||
if OrigTypeInfo.type_cls is t.CStruct:
|
||
OriginalType_kind = 'struct'
|
||
elif OrigTypeInfo.type_cls is t.CEnum:
|
||
OriginalType_kind = 'enum'
|
||
elif OrigTypeInfo.type_cls is t.CUnion:
|
||
OriginalType_kind = 'union'
|
||
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, OriginalType_kind, OriginalClass, node.lineno, FilePath, MembersInfo)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, OriginalType_kind, OriginalClass, node.lineno, FilePath, MembersInfo)
|
||
LoadedSymbols.append(FullName)
|
||
else:
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, None, None, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, None, None, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
elif HasCtypedef:
|
||
BaseTypeName = FindBaseType(node.annotation)
|
||
_TYPE_QUALIFIERS = {'CTypedef', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern', 'CDefine', 'CPostdefinition'}
|
||
if BaseTypeName == 'Callable':
|
||
OriginalType = CTypeInfo()
|
||
OriginalType.IsFuncPtr = True
|
||
OriginalType.FuncPtrReturn = CTypeInfo.VoidTypeInfo()
|
||
OriginalType.FuncPtrParams = []
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, 'typedef', OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, 'typedef', OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
continue
|
||
elif BaseTypeName and BaseTypeName not in _TYPE_QUALIFIERS:
|
||
CName = CTypeHelper.GetCName(BaseTypeName)
|
||
if CName and CName != BaseTypeName:
|
||
OriginalType = CName
|
||
else:
|
||
OriginalType = BaseTypeName[1:] if BaseTypeName.startswith('C') else BaseTypeName
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, 'typedef', OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, 'typedef', OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
else:
|
||
if node.value:
|
||
ValueBaseType = FindBaseType(node.value)
|
||
_PTR_MODIFIERS = {'CPtr', 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern'}
|
||
if ValueBaseType and ValueBaseType not in _PTR_MODIFIERS:
|
||
CName = CTypeHelper.GetCName(ValueBaseType)
|
||
if CName and CName != ValueBaseType:
|
||
OriginalType = CName
|
||
else:
|
||
OriginalType = ValueBaseType[1:] if ValueBaseType.startswith('C') else ValueBaseType
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, 'typedef', OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, 'typedef', OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
continue
|
||
if isinstance(node.value, ast.BinOp) and isinstance(node.value.op, ast.BitOr):
|
||
LeftType = self._ResolveTypedefValueType(node.value.left)
|
||
RightType = self._ResolveTypedefValueType(node.value.right)
|
||
IsPtrType = RightType == 'ptr' or LeftType == 'ptr'
|
||
if IsPtrType:
|
||
BaseName = LeftType if LeftType != 'ptr' else (RightType if RightType != 'ptr' else '')
|
||
if BaseName:
|
||
OriginalType = BaseName + ' *'
|
||
else:
|
||
OriginalType = 'void *'
|
||
elif LeftType:
|
||
OriginalType = LeftType
|
||
elif RightType:
|
||
OriginalType = RightType
|
||
if OriginalType:
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, 'typedef', OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, 'typedef', OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
continue
|
||
if isinstance(node.value, ast.Name):
|
||
RefName = node.value.id
|
||
if RefName in self:
|
||
RefInfo = self[RefName]
|
||
if RefInfo and RefInfo.IsTypedef and RefInfo.OriginalType:
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, 'typedef', RefInfo.OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, 'typedef', RefInfo.OriginalType, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
continue
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, None, None, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, None, None, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
else:
|
||
if prefix == prefixes[0]:
|
||
self._InsertTypedefSymbol(VarName, None, None, node.lineno, FilePath)
|
||
LoadedSymbols.append(VarName)
|
||
FullName = f"{prefix}.{VarName}"
|
||
self._InsertTypedefSymbol(FullName, None, None, node.lineno, FilePath)
|
||
LoadedSymbols.append(FullName)
|
||
|
||
elif isinstance(node, ast.FunctionDef):
|
||
FuncName = node.name
|
||
RetType = self._GetFuncRetTypeStr(node.returns)
|
||
ParamTypes = []
|
||
for arg in node.args.args:
|
||
if arg.annotation:
|
||
ParamTypes.append(self._GetFuncParamTypeStr(arg.annotation))
|
||
else:
|
||
ParamTypes.append('i8*')
|
||
FuncIsVariadic = node.args.vararg is not None
|
||
FuncIsInline = self._CheckAnnotationHasCInline(node.returns)
|
||
if prefix == prefixes[0]:
|
||
self._InsertFuncSymbol(FuncName, RetType, ParamTypes, node.lineno, FilePath, FuncIsVariadic, FuncIsInline)
|
||
LoadedSymbols.append(FuncName)
|
||
FullName = f"{prefix}.{FuncName}"
|
||
self._InsertFuncSymbol(FullName, RetType, ParamTypes, node.lineno, FilePath, FuncIsVariadic, FuncIsInline)
|
||
LoadedSymbols.append(FullName)
|
||
|
||
self._InsertModuleSymbol(prefix, lineno, FilePath)
|
||
|
||
for AnonymousTypeName, AnonymousTypeData in AnonymousTypes.items():
|
||
if prefix == prefixes[0]:
|
||
self._InsertAnonymousSymbol(AnonymousTypeName, AnonymousTypeData['IsUnion'], AnonymousTypeData['members'], AnonymousTypeData['lineno'], FilePath)
|
||
FullTypeName = f"{prefix}.{AnonymousTypeName}"
|
||
self._InsertAnonymousSymbol(FullTypeName, AnonymousTypeData['IsUnion'], AnonymousTypeData['members'], AnonymousTypeData['lineno'], FilePath)
|
||
|
||
if LoadedSymbols:
|
||
pass
|
||
return LoadedSymbols
|
||
|
||
except Exception as e:
|
||
import traceback
|
||
print(traceback.format_exc())
|
||
print(f"[SymbolTable Error] Failed to Load module: {FilePath} - {e}")
|
||
return []
|
||
|
||
def _GetLLVMTypeStr(self, node):
|
||
if node is None:
|
||
return 'i32'
|
||
if isinstance(node, ast.Name):
|
||
if node.id == 'str':
|
||
return 'i8*'
|
||
elif node.id == 'int':
|
||
return 'i32'
|
||
elif node.id == 'bool':
|
||
return 'i8'
|
||
elif node.id == 'float':
|
||
return 'double'
|
||
elif node.id == 'None':
|
||
return 'void'
|
||
elif node.id in ('UINT8PTR', 'INT8PTR', 'BYTEPTR'):
|
||
return 'i8*'
|
||
elif node.id in ('UINT16PTR', 'INT16PTR'):
|
||
return 'i16*'
|
||
elif node.id in ('UINT32PTR', 'INT32PTR'):
|
||
return 'i32*'
|
||
elif node.id in ('UINT64PTR', 'INT64PTR'):
|
||
return 'i64*'
|
||
else:
|
||
if node.id in self:
|
||
entry = self[node.id]
|
||
if entry and hasattr(entry, 'IsTypedef') and entry.IsTypedef and hasattr(entry, 'OriginalType') and entry.OriginalType:
|
||
if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.IsFuncPtr:
|
||
return 'i8*'
|
||
elif isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.BaseType:
|
||
return entry.OriginalType.ToString()
|
||
elif isinstance(entry.OriginalType, str):
|
||
return entry.OriginalType
|
||
return 'i32'
|
||
elif isinstance(node, ast.Attribute):
|
||
attr_name = node.attr if hasattr(node, 'attr') else ''
|
||
from lib.includes.t import CTypeRegistry
|
||
llvm_str = CTypeRegistry.NameToLLVM(attr_name)
|
||
if llvm_str is not None:
|
||
return llvm_str
|
||
if attr_name in ('CState', 'CDefine', 'CTypedef', 'CExtern', 'CStatic', 'CConst', 'State'):
|
||
return ''
|
||
if attr_name in ('CCharPtr', 'CIntPtr', 'CVoidPtr', 'CArrayPtr'):
|
||
return 'i8*'
|
||
if attr_name == 'CVoidPtr':
|
||
return 'i8*'
|
||
if attr_name and attr_name[0].isupper() and attr_name not in CTypeRegistry._name_to_class:
|
||
return f'%struct.{attr_name}*'
|
||
if attr_name and attr_name not in CTypeRegistry._name_to_class:
|
||
return f'%struct.{attr_name}*'
|
||
return 'i32'
|
||
elif isinstance(node, ast.Subscript):
|
||
if isinstance(node.value, ast.Name) and node.value.id == 'tuple':
|
||
slice_node = node.slice
|
||
elem_types = []
|
||
if isinstance(slice_node, ast.Tuple):
|
||
for elt in slice_node.elts:
|
||
elem_types.append(self._GetLLVMTypeStr(elt))
|
||
else:
|
||
elem_types.append(self._GetLLVMTypeStr(slice_node))
|
||
if elem_types:
|
||
return '{ ' + ', '.join(elem_types) + ' }'
|
||
if isinstance(node.value, ast.Name) and node.value.id == 'list':
|
||
slice_node = node.slice
|
||
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1:
|
||
elem_type = self._GetLLVMTypeStr(slice_node.elts[0])
|
||
else:
|
||
elem_type = self._GetLLVMTypeStr(slice_node)
|
||
return elem_type + '*'
|
||
return 'i32'
|
||
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
|
||
left = self._GetLLVMTypeStr(node.left)
|
||
right = self._GetLLVMTypeStr(node.right)
|
||
if right and right.endswith('*'):
|
||
return right
|
||
if left and left.endswith('*'):
|
||
return left
|
||
if left and left not in ('i32', 'void', 'i64'):
|
||
return left
|
||
if right and right not in ('i32', 'void', 'i64'):
|
||
return right
|
||
return left if left else right
|
||
elif isinstance(node, ast.Constant):
|
||
if isinstance(node.value, bool):
|
||
return 'i8'
|
||
return 'i32'
|
||
elif isinstance(node, ast.Call):
|
||
if isinstance(node.func, ast.Attribute):
|
||
if hasattr(node.func.value, 'id') and node.func.value.id == 't':
|
||
return self._GetLLVMTypeStr(ast.Attribute(value=ast.Name(id='t'), attr=node.func.attr))
|
||
return 'i32'
|
||
return 'i32'
|
||
|
||
def _GetFuncRetTypeStr(self, returns_node):
|
||
if returns_node is None:
|
||
return 'i32'
|
||
actual = returns_node
|
||
if isinstance(returns_node, ast.BinOp) and isinstance(returns_node.op, ast.BitOr):
|
||
left_str = self._GetLLVMTypeStr(returns_node.left)
|
||
right_str = self._GetLLVMTypeStr(returns_node.right)
|
||
if right_str and right_str.endswith('*'):
|
||
return right_str
|
||
if left_str and left_str.endswith('*'):
|
||
return left_str
|
||
if left_str and left_str not in ('i32', 'void', 'i64'):
|
||
return left_str
|
||
if right_str and right_str not in ('i32', 'void', 'i64'):
|
||
return right_str
|
||
return left_str
|
||
result = self._GetLLVMTypeStr(actual)
|
||
return result if result else 'i32'
|
||
|
||
@staticmethod
|
||
def _CheckAnnotationHasCInline(annotation_node) -> bool:
|
||
if annotation_node is None:
|
||
return False
|
||
if isinstance(annotation_node, ast.Attribute):
|
||
if hasattr(annotation_node.value, 'id') and annotation_node.value.id == 't' and annotation_node.attr == 'CInline':
|
||
return True
|
||
if isinstance(annotation_node, ast.Name):
|
||
if annotation_node.id == 'CInline':
|
||
return True
|
||
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
|
||
return SymbolTable._CheckAnnotationHasCInline(annotation_node.left) or SymbolTable._CheckAnnotationHasCInline(annotation_node.right)
|
||
return False
|
||
|
||
def _GetFuncParamTypeStr(self, annotation_node):
|
||
if annotation_node is None:
|
||
return 'i8*'
|
||
actual = annotation_node
|
||
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
|
||
left_str = self._GetLLVMTypeStr(annotation_node.left)
|
||
right_str = self._GetLLVMTypeStr(annotation_node.right)
|
||
if right_str and right_str.endswith('*'):
|
||
return right_str
|
||
if left_str and left_str.endswith('*'):
|
||
return left_str
|
||
if right_str and right_str not in ('i32', 'void', 'i64'):
|
||
return right_str
|
||
if left_str and left_str not in ('i32', 'void', 'i64'):
|
||
return left_str
|
||
if left_str and left_str.endswith('*'):
|
||
return left_str
|
||
return right_str if right_str else left_str
|
||
result = self._GetLLVMTypeStr(actual)
|
||
return result if result else 'i8*'
|
||
|
||
def _ResolveTypedefValueType(self, node):
|
||
if node is None:
|
||
return ''
|
||
if isinstance(node, ast.Name):
|
||
if node.id in self:
|
||
Entry = self[node.id]
|
||
if hasattr(Entry, 'IsTypedef') and Entry.IsTypedef and Entry.OriginalType:
|
||
return Entry.OriginalType
|
||
return ''
|
||
if isinstance(node, ast.Attribute):
|
||
if isinstance(node.value, ast.Name) and node.value.id == 't':
|
||
CName = CTypeHelper.GetCName(node.attr)
|
||
if CName and CName == '*':
|
||
return 'ptr'
|
||
if CName:
|
||
return CName
|
||
return ''
|
||
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
|
||
left_str = self._ResolveTypedefValueType(node.left)
|
||
right_str = self._ResolveTypedefValueType(node.right)
|
||
if right_str == 'ptr' or (right_str and right_str.endswith('*')):
|
||
base = left_str if left_str and left_str != 'ptr' else ''
|
||
return base + ' *' if base else 'void *'
|
||
if left_str == 'ptr' or (left_str and left_str.endswith('*')):
|
||
base = right_str if right_str and right_str != 'ptr' else ''
|
||
return base + ' *' if base else 'void *'
|
||
if right_str and right_str not in ('ptr', 'void', 'i32', 'i64'):
|
||
return right_str
|
||
if left_str and left_str not in ('ptr', 'void', 'i32', 'i64'):
|
||
return left_str
|
||
return left_str if left_str else right_str
|
||
return ''
|
||
|
||
def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict, IsCpythonObject: bool, IsPacked: bool = False):
|
||
info = CTypeInfo()
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
info.Members = members if members else {}
|
||
|
||
if TypeKind == 'struct':
|
||
info.IsStruct = True
|
||
elif TypeKind == 'union':
|
||
info.IsUnion = True
|
||
elif TypeKind == 'enum':
|
||
info.IsEnum = True
|
||
elif TypeKind == 'exception':
|
||
info.IsStruct = True
|
||
info.IsExceptionClass = True
|
||
|
||
if IsCpythonObject:
|
||
info.IsCpythonObject = True
|
||
if IsPacked:
|
||
info.IsPacked = True
|
||
|
||
self._symbols[FullName] = info
|
||
|
||
def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str):
|
||
info = 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()
|
||
info.IsTypedef = True
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
if members:
|
||
info.Members = members
|
||
|
||
if isinstance(OriginalClass, CTypeInfo):
|
||
info.OriginalType = OriginalClass
|
||
elif OriginalType_kind == 'typedef' and OriginalClass and isinstance(OriginalClass, str) and ('*' in OriginalClass):
|
||
OriginalType = OriginalClass
|
||
if OriginalType == 'CVoid *':
|
||
OriginalType = 'void *'
|
||
info.OriginalType = OriginalType
|
||
elif OriginalType_kind == 'typedef' and OriginalClass == 'void':
|
||
info.OriginalType = 'void *'
|
||
elif OriginalType_kind == 'typedef' and OriginalClass:
|
||
info.OriginalType = OriginalClass
|
||
elif OriginalType_kind and OriginalClass and isinstance(OriginalClass, str):
|
||
info.OriginalType = f'{OriginalType_kind} {OriginalClass}'
|
||
else:
|
||
info.OriginalType = OriginalClass
|
||
|
||
self._symbols[FullName] = info
|
||
|
||
def _InsertModuleSymbol(self, name: str, lineno: int, FilePath: str):
|
||
info = 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()
|
||
info.IsFunction = True
|
||
if isinstance(RetType, str):
|
||
info.FuncPtrReturn = CTypeInfo.FromTypeName(RetType) if RetType else CTypeInfo.VoidTypeInfo()
|
||
elif isinstance(RetType, CTypeInfo):
|
||
info.FuncPtrReturn = RetType
|
||
else:
|
||
info.FuncPtrReturn = CTypeInfo.VoidTypeInfo()
|
||
info.FuncPtrParams = [(f'arg{i}', pt) for i, pt in enumerate(ParamTypes)]
|
||
info.IsVariadic = IsVariadic
|
||
info.IsInline = IsInline
|
||
if IsInline:
|
||
info.Storage = t.CInline()
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
self._symbols[FullName] = info
|
||
|
||
def _InsertDefineSymbol(self, FullName: str, DefineValue, lineno: int, FilePath: str):
|
||
info = CTypeInfo()
|
||
info.IsDefine = True
|
||
info.DefineValue = DefineValue
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
self._symbols[FullName] = info
|
||
|
||
def _eval_const_expr(self, node):
|
||
"""计算常量表达式的值"""
|
||
import ast
|
||
if isinstance(node, ast.Constant):
|
||
return node.value
|
||
elif isinstance(node, ast.BinOp):
|
||
left = self._eval_const_expr(node.left)
|
||
right = self._eval_const_expr(node.right)
|
||
if left is None or right is None:
|
||
return None
|
||
if isinstance(node.op, ast.Add):
|
||
return left + right
|
||
elif isinstance(node.op, ast.Sub):
|
||
return left - right
|
||
elif isinstance(node.op, ast.Mult):
|
||
return left * right
|
||
elif isinstance(node.op, ast.Div):
|
||
return left // right if isinstance(left, int) and isinstance(right, int) else left / right
|
||
elif isinstance(node.op, ast.FloorDiv):
|
||
return left // right
|
||
elif isinstance(node.op, ast.Mod):
|
||
return left % right
|
||
elif isinstance(node.op, ast.Pow):
|
||
return left ** right
|
||
elif isinstance(node.op, ast.LShift):
|
||
return left << right
|
||
elif isinstance(node.op, ast.RShift):
|
||
return left >> right
|
||
elif isinstance(node.op, ast.BitOr):
|
||
return left | right
|
||
elif isinstance(node.op, ast.BitXor):
|
||
return left ^ right
|
||
elif isinstance(node.op, ast.BitAnd):
|
||
return left & right
|
||
elif isinstance(node, ast.UnaryOp):
|
||
operand = self._eval_const_expr(node.operand)
|
||
if operand is None:
|
||
return None
|
||
if isinstance(node.op, ast.USub):
|
||
return -operand
|
||
elif isinstance(node.op, ast.UAdd):
|
||
return +operand
|
||
elif isinstance(node.op, ast.Invert):
|
||
return ~operand
|
||
elif isinstance(node, ast.Name):
|
||
# 引用其他常量
|
||
if node.id in self:
|
||
info = self[node.id]
|
||
if hasattr(info, 'IsDefine') and info.IsDefine and hasattr(info, 'DefineValue') and info.DefineValue is not None:
|
||
return info.DefineValue
|
||
return None
|
||
|
||
def _InsertAnonymousSymbol(self, FullName: str, IsUnion: bool, members: dict, lineno: int, FilePath: str):
|
||
info = CTypeInfo()
|
||
if IsUnion:
|
||
info.IsUnion = True
|
||
else:
|
||
info.IsStruct = True
|
||
info.IsAnonymous = True
|
||
info.Members = members if members else {}
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
self._symbols[FullName] = info
|
||
|
||
|