可用的回归测试通过的标准版本
This commit is contained in:
401
lib/core/Translator/AnnotationLoader.py
Normal file
401
lib/core/Translator/AnnotationLoader.py
Normal file
@@ -0,0 +1,401 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import sys
|
||||
import os
|
||||
|
||||
from lib.core.SymbolNode import SymbolNode
|
||||
from lib.core.Handles.HandlesBase import CTypeInfo
|
||||
from lib.includes import t
|
||||
|
||||
|
||||
class AnnotationLoaderMixin:
|
||||
"""注解模块加载 Mixin
|
||||
|
||||
提供注解模块/文件的加载与符号表注册能力。
|
||||
"""
|
||||
|
||||
def _LoadAnnotationModule(self, ModuleName: str, library_name: str = None, parse_method: str = 'auto'):
|
||||
"""加载注解模块并将 CType 子类注册为 typedef
|
||||
|
||||
支持两种模式:
|
||||
1. AST 模式:如果 ModuleName 是文件路径,解析文件提取 CType
|
||||
2. importlib 模式:如果 ModuleName 是模块名,导入模块提取 CType
|
||||
|
||||
Args:
|
||||
ModuleName: 模块名或文件路径
|
||||
library_name: 手动指定的库名称,用于注册到符号表。如果为 None,则从 ModuleName 推断
|
||||
parse_method: 解析方法,可选值:
|
||||
- 'auto': 自动检测(默认)
|
||||
- 'file': 强制使用 AST 模式(文件路径)
|
||||
- 'module': 强制使用 importlib 模式(模块名)
|
||||
"""
|
||||
import importlib
|
||||
import sys
|
||||
import os
|
||||
|
||||
CurrentDir = os.path.dirname(os.path.abspath(__file__))
|
||||
ProjectRoot = os.path.dirname(os.path.dirname(os.path.dirname(CurrentDir)))
|
||||
if ProjectRoot not in sys.path:
|
||||
sys.path.insert(0, ProjectRoot)
|
||||
|
||||
def register_to_symboltable(module, lib_name: str = None, source_file: str = None):
|
||||
"""将模块中的 CType 子类和 CEnum 类注册到符号表"""
|
||||
from lib.includes.t import CType, CEnum
|
||||
count = 0
|
||||
for AttrName in dir(module):
|
||||
attr = getattr(module, AttrName)
|
||||
# 优先检查是否是 CEnum 子类(枚举)
|
||||
if isinstance(attr, type) and issubclass(attr, CEnum) and attr is not CEnum:
|
||||
# 注册枚举类型
|
||||
EnumNode = SymbolNode.CreateClass(
|
||||
name=AttrName,
|
||||
TypeKind='enum',
|
||||
lineno=0
|
||||
)
|
||||
EnumNode.set('enum_class', attr)
|
||||
EnumNode.set('source', 'annotation_module')
|
||||
EnumNode.set('library_name', lib_name)
|
||||
EnumNode.set('file', source_file)
|
||||
self.SymbolTable[AttrName] = EnumNode.attributes
|
||||
if lib_name:
|
||||
FullName = f'{lib_name}.{AttrName}'
|
||||
full_EnumNode = SymbolNode.CreateClass(
|
||||
name=FullName,
|
||||
TypeKind='enum',
|
||||
lineno=0
|
||||
)
|
||||
full_EnumNode.set('enum_class', attr)
|
||||
full_EnumNode.set('source', 'annotation_module')
|
||||
full_EnumNode.set('library_name', lib_name)
|
||||
full_EnumNode.set('file', source_file)
|
||||
self.SymbolTable[FullName] = full_EnumNode.attributes
|
||||
# 枚举成员也需要注册
|
||||
for MemberName in dir(attr):
|
||||
if not MemberName.startswith('_'):
|
||||
member = getattr(attr, MemberName, None)
|
||||
if isinstance(member, int):
|
||||
MemberNode = CTypeInfo()
|
||||
MemberNode.Name = MemberName
|
||||
MemberNode.BaseType = t.CEnum(AttrName)
|
||||
MemberNode.value = member
|
||||
MemberNode.EnumName = AttrName
|
||||
MemberNode.library_name = lib_name
|
||||
MemberNode.IsEnumMember = True
|
||||
self.SymbolTable[MemberName] = MemberNode
|
||||
self.SymbolTable[f"{AttrName}.{MemberName}"] = MemberNode
|
||||
self.SymbolTable[f"{AttrName}_{MemberName}"] = MemberNode
|
||||
if lib_name:
|
||||
self.SymbolTable[f"{lib_name}.{AttrName}.{MemberName}"] = MemberNode
|
||||
self.SymbolTable[f"{lib_name}_{AttrName}_{MemberName}"] = MemberNode
|
||||
count += 1
|
||||
# 检查是否是 CType 子类(typedef 别名)
|
||||
elif isinstance(attr, type) and issubclass(attr, CType) and attr is not CType:
|
||||
# 继承 CType 的类是类型别名(typedef),不是结构体
|
||||
# 使用手动指定的库名称作为前缀
|
||||
if lib_name:
|
||||
FullName = f'{lib_name}.{AttrName}'
|
||||
else:
|
||||
FullName = AttrName
|
||||
# 同时注册带前缀和不带前缀的名称(都是 typedef 别名)
|
||||
TypedefNode = SymbolNode.CreateClass(
|
||||
name=AttrName,
|
||||
TypeKind='typedef',
|
||||
lineno=0
|
||||
)
|
||||
TypedefNode.set('OriginalType', attr().CName)
|
||||
TypedefNode.set('source', 'annotation_module')
|
||||
TypedefNode.set('library_name', lib_name)
|
||||
TypedefNode.set('file', source_file)
|
||||
self.SymbolTable[AttrName] = TypedefNode.attributes
|
||||
if lib_name and FullName != AttrName:
|
||||
FullTypedef_node = SymbolNode.CreateClass(
|
||||
name=FullName,
|
||||
TypeKind='typedef',
|
||||
lineno=0
|
||||
)
|
||||
FullTypedef_node.set('OriginalType', attr().CName)
|
||||
FullTypedef_node.set('source', 'annotation_module')
|
||||
FullTypedef_node.set('library_name', lib_name)
|
||||
FullTypedef_node.set('file', source_file)
|
||||
self.SymbolTable[FullName] = FullTypedef_node.attributes
|
||||
count += 1
|
||||
# 检查是否是下划线前缀的 CType 子类(如 _CTypedef -> CTypedef)
|
||||
elif AttrName.startswith('_') and len(AttrName) > 1:
|
||||
PublicName = AttrName[1:]
|
||||
PublicAttr = getattr(module, PublicName, None)
|
||||
if PublicAttr is not None and not isinstance(attr, type):
|
||||
continue
|
||||
if isinstance(attr, type) and issubclass(attr, CType) and attr is not CType:
|
||||
if PublicName not in self.SymbolTable:
|
||||
if lib_name:
|
||||
FullName = f'{lib_name}.{PublicName}'
|
||||
else:
|
||||
FullName = PublicName
|
||||
TypedefNode = SymbolNode.CreateClass(
|
||||
name=PublicName,
|
||||
TypeKind='typedef',
|
||||
lineno=0
|
||||
)
|
||||
TypedefNode.set('OriginalType', attr().CName)
|
||||
TypedefNode.set('source', 'annotation_module')
|
||||
TypedefNode.set('library_name', lib_name)
|
||||
TypedefNode.set('file', source_file)
|
||||
self.SymbolTable[PublicName] = TypedefNode.attributes
|
||||
if lib_name and FullName != PublicName:
|
||||
FullTypedef_node = SymbolNode.CreateClass(
|
||||
name=FullName,
|
||||
TypeKind='typedef',
|
||||
lineno=0
|
||||
)
|
||||
FullTypedef_node.set('OriginalType', attr().CName)
|
||||
FullTypedef_node.set('source', 'annotation_module')
|
||||
FullTypedef_node.set('library_name', lib_name)
|
||||
FullTypedef_node.set('file', source_file)
|
||||
self.SymbolTable[FullName] = FullTypedef_node.attributes
|
||||
count += 1
|
||||
return count
|
||||
|
||||
# 确定解析方法
|
||||
use_file_mode = False
|
||||
if parse_method == 'file':
|
||||
use_file_mode = True
|
||||
elif parse_method == 'module':
|
||||
use_file_mode = False
|
||||
else: # auto
|
||||
use_file_mode = os.path.isfile(ModuleName)
|
||||
|
||||
# 确定库名称
|
||||
lib_name = library_name
|
||||
if lib_name is None:
|
||||
if use_file_mode:
|
||||
# 从文件路径推断库名称
|
||||
lib_name = os.path.splitext(os.path.basename(ModuleName))[0]
|
||||
else:
|
||||
# 模块名就是库名称
|
||||
lib_name = ModuleName
|
||||
|
||||
# 根据解析方法加载模块
|
||||
if use_file_mode:
|
||||
# AST 模式:使用 AST 解析文件,不执行代码
|
||||
try:
|
||||
# 临时保存当前的 EmbeddedAssignments 和 TypedefAssignments
|
||||
saved_embedded = self.EmbeddedAssignments.copy()
|
||||
saved_typedef = self.TypedefAssignments.copy()
|
||||
|
||||
# 清空 PD 收集(注解文件不应该收集 PD 语句)
|
||||
self.EmbeddedAssignments = {}
|
||||
self.TypedefAssignments = {}
|
||||
|
||||
# 直接调用 ParsePythonFile 解析文件提取类型信息
|
||||
# 注意:这不会执行任何 Python 代码,只是解析 AST
|
||||
# UpdateCurrentFile=False 表示不修改 CurrentFile
|
||||
self.ParsePythonFile(ModuleName, UpdateCurrentFile=False)
|
||||
|
||||
# 恢复主代码文件的 PD 收集
|
||||
self.EmbeddedAssignments = saved_embedded
|
||||
self.TypedefAssignments = saved_typedef
|
||||
|
||||
# 从文件名推断类型前缀(如 test_t -> test_t.xxx)
|
||||
TypePrefix = lib_name
|
||||
count = 0
|
||||
|
||||
# 检查符号表中新增的类型(注解文件解析后的所有 struct 类型)
|
||||
# 需要检查这些类型是否继承自 CType,如果是则是 typedef 别名
|
||||
new_types = []
|
||||
for TypeName, TypeInfo in self.SymbolTable.items():
|
||||
if TypeInfo.IsStruct:
|
||||
new_types.append(TypeName)
|
||||
|
||||
# 再次遍历 AST,检测继承自 CType 的类
|
||||
try:
|
||||
with open(ModuleName, 'r', encoding='utf-8') as f:
|
||||
ann_content = f.read()
|
||||
ann_tree = ast.parse(ann_content)
|
||||
for node in ann_tree.body:
|
||||
if isinstance(node, ast.ClassDef):
|
||||
ClassName = node.name
|
||||
# 检查是否有基类
|
||||
for base in node.bases:
|
||||
if isinstance(base, ast.Name) and base.id == 'CType':
|
||||
# 这个类继承自 CType,是 typedef 别名
|
||||
# 更新符号表中的类型为 typedef
|
||||
if ClassName in self.SymbolTable:
|
||||
TypedefNode = SymbolNode.CreateClass(
|
||||
name=ClassName,
|
||||
TypeKind='typedef',
|
||||
lineno=0
|
||||
)
|
||||
TypedefNode.set('OriginalType', self.SymbolTable[ClassName].get('OriginalType', f'struct {ClassName}'))
|
||||
TypedefNode.set('source', 'annotation_module')
|
||||
TypedefNode.set('is_ctype_subclass', True)
|
||||
self.SymbolTable[ClassName] = TypedefNode.attributes
|
||||
except Exception as _e:
|
||||
from lib.core.VLogger import get_logger as _vlog
|
||||
from lib.constants.config import mode as _ConfigMode
|
||||
if _ConfigMode == "strict":
|
||||
raise
|
||||
_vlog().warning(f"解析注解文件类型定义失败: {_e}", "Exception")
|
||||
|
||||
# 为所有新增的类型添加库前缀和 source 标记
|
||||
for TypeName in new_types:
|
||||
# 添加带前缀的类型名
|
||||
FullName = f'{TypePrefix}.{TypeName}'
|
||||
TypeInfo = self.SymbolTable[TypeName].copy()
|
||||
TypeInfo['source'] = 'annotation_module'
|
||||
TypeInfo['original_name'] = TypeName
|
||||
self.SymbolTable[FullName] = TypeInfo
|
||||
count += 1
|
||||
|
||||
# 再次遍历 AST,查找注解文件中的 typedef 语句(AnnAssign with t.CTypedef)
|
||||
# 注意:必须在类定义处理完之后再处理 typedef
|
||||
typedef_annots = []
|
||||
# 查找 CEnum 成员变量(AnnAssign with t.CEnum | t.State 或 t.CEnum)
|
||||
cEnumMembers = []
|
||||
try:
|
||||
with open(ModuleName, 'r', encoding='utf-8') as f:
|
||||
ann_content = f.read()
|
||||
ann_tree = ast.parse(ann_content)
|
||||
for node in ann_tree.body:
|
||||
# 检测 xxx: t.CEnum 形式的枚举成员变量
|
||||
if isinstance(node, ast.AnnAssign):
|
||||
if isinstance(node.target, ast.Name) and node.annotation:
|
||||
TargetName = node.target.id
|
||||
# 检查注解是否是 BinOp (Union) 或直接是 Attribute
|
||||
IsCenum = False
|
||||
if isinstance(node.annotation, ast.BinOp):
|
||||
# 检查是否包含 CEnum
|
||||
annot_str = ast.dump(node.annotation)
|
||||
if 'CEnum' in annot_str:
|
||||
IsCenum = True
|
||||
elif isinstance(node.annotation, ast.Attribute):
|
||||
# 直接是 t.CEnum
|
||||
if node.annotation.attr == t.CEnum.__name__:
|
||||
IsCenum = True
|
||||
|
||||
if IsCenum:
|
||||
# 检查值是否是 Constant (枚举成员值)
|
||||
if node.value and isinstance(node.value, ast.Constant):
|
||||
value = node.value.value
|
||||
else:
|
||||
value = None
|
||||
cEnumMembers.append((TargetName, value))
|
||||
except Exception as _e:
|
||||
from lib.core.VLogger import get_logger as _vlog
|
||||
from lib.constants.config import mode as _ConfigMode
|
||||
if _ConfigMode == "strict":
|
||||
raise
|
||||
_vlog().warning(f"解析 CEnum 成员变量失败: {_e}", "Exception")
|
||||
|
||||
# 处理 CEnum 成员变量
|
||||
for TargetName, value in cEnumMembers:
|
||||
# 注册枚举成员
|
||||
MemberNode = CTypeInfo()
|
||||
MemberNode.Name = TargetName
|
||||
MemberNode.BaseType = t.CEnum(TypePrefix)
|
||||
MemberNode.value = value
|
||||
MemberNode.EnumName = TypePrefix
|
||||
MemberNode.library_name = TypePrefix
|
||||
MemberNode.IsEnumMember = True
|
||||
self.SymbolTable[TargetName] = MemberNode
|
||||
self.SymbolTable[f"{TypePrefix}.{TargetName}"] = MemberNode
|
||||
self.SymbolTable[f"{TypePrefix}_{TargetName}"] = MemberNode
|
||||
|
||||
try:
|
||||
with open(ModuleName, 'r', encoding='utf-8') as f:
|
||||
ann_content = f.read()
|
||||
ann_tree = ast.parse(ann_content)
|
||||
for node in ann_tree.body:
|
||||
# 检测 xxx: t.CTypedef = xxx 形式的 typedef 语句
|
||||
if isinstance(node, ast.AnnAssign):
|
||||
if isinstance(node.target, ast.Name) and node.annotation and node.value:
|
||||
TargetName = node.target.id
|
||||
# 检查注解是否是 t.CTypedef
|
||||
if isinstance(node.annotation, ast.Attribute):
|
||||
if isinstance(node.annotation.value, ast.Name):
|
||||
if node.annotation.value.id == 't' and node.annotation.attr == 'CTypedef':
|
||||
# 检查值是否是 Name
|
||||
if isinstance(node.value, ast.Name):
|
||||
original_name = node.value.id
|
||||
typedef_annots.append((TargetName, original_name))
|
||||
except Exception as _e:
|
||||
from lib.core.VLogger import get_logger as _vlog
|
||||
from lib.constants.config import mode as _ConfigMode
|
||||
if _ConfigMode == "strict":
|
||||
raise
|
||||
_vlog().warning(f"解析 typedef 语句失败: {_e}", "Exception")
|
||||
|
||||
# 处理 typedef 注解
|
||||
for TargetName, original_name in typedef_annots:
|
||||
# 检查原始类型是否在符号表中(带前缀或不带前缀)
|
||||
orig_with_prefix = f'{TypePrefix}.{original_name}'
|
||||
if orig_with_prefix in self.SymbolTable:
|
||||
# 添加带前缀的 typedef 别名
|
||||
FullTypedef_name = f'{TypePrefix}.{TargetName}'
|
||||
TypedefNode = SymbolNode.CreateClass(
|
||||
name=FullTypedef_name,
|
||||
TypeKind='typedef',
|
||||
lineno=0
|
||||
)
|
||||
orig_entry = self.SymbolTable.get(orig_with_prefix, {})
|
||||
orig_type_str = f'struct {original_name}'
|
||||
if isinstance(orig_entry, dict):
|
||||
if orig_entry.get('type') == 'typedef':
|
||||
orig_type_str = orig_entry.get('OriginalType', f'struct {original_name}')
|
||||
elif orig_entry.get('type') == 'enum':
|
||||
orig_type_str = f'enum {original_name}'
|
||||
elif hasattr(orig_entry, 'IsTypedef') and orig_entry.IsTypedef:
|
||||
orig_type_str = getattr(orig_entry, 'OriginalType', f'struct {original_name}')
|
||||
elif hasattr(orig_entry, 'IsEnum') and orig_entry.IsEnum:
|
||||
orig_type_str = f'enum {original_name}'
|
||||
TypedefNode.set('OriginalType', orig_type_str)
|
||||
TypedefNode.set('source', 'annotation_module')
|
||||
TypedefNode.set('original_name', TargetName)
|
||||
self.SymbolTable[FullTypedef_name] = TypedefNode.attributes
|
||||
count += 1
|
||||
|
||||
self.AnnotationModules.add(lib_name)
|
||||
return count
|
||||
except Exception as e:
|
||||
# 恢复 PD 收集
|
||||
self.EmbeddedAssignments = saved_embedded
|
||||
self.TypedefAssignments = saved_typedef
|
||||
self.LogWarning(f"AST模式加载注解模块失败: {ModuleName}, 错误: {e}")
|
||||
return 0
|
||||
else:
|
||||
# importlib 模式:导入模块
|
||||
try:
|
||||
module = importlib.import_module(ModuleName)
|
||||
count = register_to_symboltable(module, lib_name, None)
|
||||
self.AnnotationModules.add(lib_name)
|
||||
return count
|
||||
except ImportError:
|
||||
self.LogWarning(f"importlib模式加载注解模块失败: {ModuleName}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
self.LogWarning(f"加载注解模块失败: {ModuleName}, 错误: {e}")
|
||||
return 0
|
||||
|
||||
def _LoadAnnotationFiles(self, FilePaths: list):
|
||||
"""加载多个注解文件并注册到符号表
|
||||
|
||||
Args:
|
||||
FilePaths: 文件路径列表,每个元素可以是:
|
||||
- str: 文件路径或模块名
|
||||
- tuple: (path, options) 或 (path, library_name, parse_method)
|
||||
"""
|
||||
for item in FilePaths:
|
||||
if isinstance(item, tuple):
|
||||
if len(item) >= 3:
|
||||
path, library_name, parse_method = item[:3]
|
||||
count = self._loadAnnotationModule(path, library_name, parse_method)
|
||||
elif len(item) == 2:
|
||||
path, library_name = item
|
||||
count = self._loadAnnotationModule(path, library_name, 'auto')
|
||||
else:
|
||||
count = self._loadAnnotationModule(item)
|
||||
else:
|
||||
count = self._loadAnnotationModule(item)
|
||||
|
||||
# 别名,保持向后兼容
|
||||
LoadAnnotationFiles = _LoadAnnotationFiles
|
||||
Reference in New Issue
Block a user