from __future__ import annotations import ast import sys import os 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 = CTypeInfo() EnumNode.Name = AttrName EnumNode.BaseType = t.CEnum() EnumNode.IsEnum = True EnumNode.set('enum_class', attr) EnumNode.set('source', 'annotation_module') EnumNode.library_name = lib_name EnumNode.file = source_file self.SymbolTable[AttrName] = EnumNode if lib_name: FullName = f'{lib_name}.{AttrName}' full_EnumNode = CTypeInfo() full_EnumNode.Name = FullName full_EnumNode.IsEnum = True full_EnumNode.set('enum_class', attr) full_EnumNode.set('source', 'annotation_module') full_EnumNode.library_name = lib_name full_EnumNode.file = source_file self.SymbolTable[FullName] = full_EnumNode # 枚举成员也需要注册 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 = CTypeInfo() TypedefNode.Name = AttrName TypedefNode.IsTypedef = True TypedefNode.OriginalType = attr().CName TypedefNode.set('source', 'annotation_module') TypedefNode.library_name = lib_name TypedefNode.file = source_file self.SymbolTable[AttrName] = TypedefNode if lib_name and FullName != AttrName: FullTypedef_node = CTypeInfo() FullTypedef_node.Name = FullName FullTypedef_node.IsTypedef = True FullTypedef_node.OriginalType = attr().CName FullTypedef_node.set('source', 'annotation_module') FullTypedef_node.library_name = lib_name FullTypedef_node.file = source_file self.SymbolTable[FullName] = FullTypedef_node 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 = CTypeInfo() TypedefNode.Name = PublicName TypedefNode.IsTypedef = True TypedefNode.OriginalType = attr().CName TypedefNode.set('source', 'annotation_module') TypedefNode.library_name = lib_name TypedefNode.file = source_file self.SymbolTable[PublicName] = TypedefNode if lib_name and FullName != PublicName: FullTypedef_node = CTypeInfo() FullTypedef_node.Name = FullName FullTypedef_node.IsTypedef = True FullTypedef_node.OriginalType = attr().CName FullTypedef_node.set('source', 'annotation_module') FullTypedef_node.library_name = lib_name FullTypedef_node.file = source_file self.SymbolTable[FullName] = FullTypedef_node 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 = CTypeInfo() TypedefNode.Name = ClassName TypedefNode.IsTypedef = True TypedefNode.OriginalType = self.SymbolTable[ClassName].get('OriginalType', f'struct {ClassName}') TypedefNode.set('source', 'annotation_module') TypedefNode.set('is_ctype_subclass', True) self.SymbolTable[ClassName] = TypedefNode 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 = CTypeInfo() TypedefNode.Name = FullTypedef_name TypedefNode.IsTypedef = True 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 orig_entry.IsTypedef: orig_type_str = orig_entry.OriginalType or f'struct {original_name}' elif orig_entry.IsEnum: orig_type_str = f'enum {original_name}' TypedefNode.OriginalType = orig_type_str TypedefNode.set('source', 'annotation_module') TypedefNode.set('original_name', TargetName) self.SymbolTable[FullTypedef_name] = TypedefNode 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