532 lines
22 KiB
Python
532 lines
22 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING, Any, Iterator
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
|
||
import ast
|
||
import traceback
|
||
|
||
from lib.core.Handles.HandlesBase import CTypeInfo
|
||
from lib.includes import t
|
||
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 CheckAnnotationHasCInline as _CheckAnnotationHasCInline
|
||
|
||
|
||
class SymbolTable:
|
||
def __init__(self, translator: Translator) -> None:
|
||
self.translator: Translator = translator
|
||
self._symbols: dict[str, CTypeInfo] = {}
|
||
self._namespaces: dict[str, dict[str, CTypeInfo]] = {} # 模块名 -> 符号字典
|
||
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 __getstate__(self) -> dict:
|
||
"""pickle 序列化:排除不可序列化的字段(translator/_type_mapper/diagnostics)"""
|
||
state: dict = self.__dict__.copy()
|
||
state.pop('translator', None)
|
||
state.pop('_type_mapper', None)
|
||
state.pop('diagnostics', None)
|
||
return state
|
||
|
||
def __setstate__(self, state: dict) -> None:
|
||
"""pickle 反序列化:重建可序列化的字段,translator 由调用方后续设置"""
|
||
self.__dict__.update(state)
|
||
self.translator = None # 由调用方赋值
|
||
self.diagnostics = DiagnosticCollector()
|
||
self._type_mapper = LLVMTypeMapper(self)
|
||
|
||
def clear(self) -> None:
|
||
self._symbols.clear()
|
||
self._namespaces.clear()
|
||
self._t_type_symbols.clear()
|
||
self.import_aliases.clear()
|
||
|
||
def get(self, name: str, default: CTypeInfo | None = None) -> CTypeInfo | None:
|
||
return self._symbols.get(name, default)
|
||
|
||
def set(self, name: str, value: CTypeInfo | dict[str, Any]) -> None:
|
||
if isinstance(value, CTypeInfo):
|
||
self._symbols[name] = value
|
||
else:
|
||
# 兼容旧代码传入 dict 的情况
|
||
info: CTypeInfo = self._CTypeInfoFromDict(name, value)
|
||
self._symbols[name] = info
|
||
|
||
def update(self, symbols: dict[str, CTypeInfo | dict[str, Any]]) -> None:
|
||
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: Any) -> 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: CTypeInfo | dict[str, Any]) -> None:
|
||
self.set(name, value)
|
||
|
||
def __delitem__(self, name: str) -> None:
|
||
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 lookup(self, name: str) -> CTypeInfo | None:
|
||
"""查找符号,支持别名解析和 dotted name
|
||
|
||
解析顺序:
|
||
1. 直接匹配 _symbols
|
||
2. 如果包含 '.',解析第一个段为别名后查找剩余路径
|
||
3. 查 _t_type_symbols(t/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 register_namespace(self, module_name: str) -> None:
|
||
"""注册一个模块命名空间"""
|
||
if module_name not in self._namespaces:
|
||
self._namespaces[module_name] = {}
|
||
|
||
def has(self, name: str) -> bool:
|
||
"""检查符号是否存在(替代 `name in SymbolTable`)"""
|
||
return name in self._symbols
|
||
|
||
def is_struct(self, name: str) -> bool:
|
||
"""检查名称是否为结构体"""
|
||
entry: CTypeInfo | None = self._symbols.get(name)
|
||
return entry is not None and (entry.IsStruct or entry.IsCpythonObject)
|
||
|
||
def is_define(self, name: str) -> bool:
|
||
"""检查名称是否为常量定义"""
|
||
entry: CTypeInfo | None = self._symbols.get(name)
|
||
return entry is not None and entry.IsDefine
|
||
|
||
def insert(self, name: str, info: CTypeInfo) -> None:
|
||
"""插入符号(替代 `SymbolTable[name] = info`)"""
|
||
self._symbols[name] = info
|
||
|
||
def resolve_alias(self, module_path: str) -> str:
|
||
"""解析 import 别名,无别名返回原路径"""
|
||
return self.import_aliases.get(module_path, module_path)
|
||
|
||
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: dict[str, Any] = {'name': name}
|
||
if info.BaseType:
|
||
entry['BaseType'] = self._SerializeBaseType(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: 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]) -> 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: 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[str, Any]) -> CTypeInfo:
|
||
"""从旧 dict 格式创建 CTypeInfo(兼容旧代码)"""
|
||
info: CTypeInfo = CTypeInfo()
|
||
info.Name = name
|
||
node_type: str = 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']
|
||
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: frozenset[str] = frozenset({'type', 'name', 'PtrCount', 'OriginalType', 'lineno', 'file',
|
||
'members', 'IsPtr', 'dims', 'IsCpythonObject', 'IsAnonymous',
|
||
'IsPacked', 'EnumName', 'DefineValue', 'BaseType',
|
||
'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]:
|
||
try:
|
||
# 提取模块名作为命名空间
|
||
module_name: str = namespace_prefix[0] if isinstance(namespace_prefix, list) else namespace_prefix
|
||
|
||
# Pass 1: 从 AST 提取原始符号信息
|
||
extractor: ASTSymbolExtractor = ASTSymbolExtractor(self)
|
||
module_symbols: ModuleSymbols = extractor.extract(FilePath)
|
||
|
||
# Pass 2: 解析类型信息(typedef、函数签名、define 值)
|
||
resolver: TypeResolver = TypeResolver(self)
|
||
resolver.resolve(module_symbols)
|
||
|
||
# Pass 3: 插入符号到主命名空间(无前缀)
|
||
inserter: SymbolInserter = SymbolInserter(self)
|
||
inserter.set_namespace(module_name)
|
||
loaded: list[str] = inserter.insert(module_symbols)
|
||
|
||
# Pass 4: 重新导出到命名空间前缀下
|
||
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:
|
||
_vlog().error(traceback.format_exc())
|
||
self.diagnostics.error(FilePath, lineno, f"加载模块失败: {e}")
|
||
return []
|
||
|
||
# ==================================================================
|
||
# 类型解析辅助方法(委托到 LLVMTypeMapper)
|
||
# ==================================================================
|
||
|
||
@staticmethod
|
||
def _CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool:
|
||
return _CheckAnnotationHasCInline(annotation_node)
|
||
|
||
# ==================================================================
|
||
# 符号插入方法(供 SymbolInserter / PackageReexporter 调用)
|
||
# ==================================================================
|
||
|
||
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 {}
|
||
|
||
if TypeKind == 'struct':
|
||
info.IsStruct = True
|
||
elif TypeKind == 'union':
|
||
info.IsUnion = True
|
||
elif TypeKind == 'enum':
|
||
info.IsEnum = True
|
||
elif TypeKind == 'renum':
|
||
info.IsStruct = True
|
||
info.IsRenum = True
|
||
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, value: int | None = None) -> None:
|
||
info: CTypeInfo = CTypeInfo()
|
||
info.IsEnumMember = True
|
||
info.EnumName = EnumName
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
if value is not None:
|
||
info.value = value
|
||
self._symbols[FullName] = info
|
||
|
||
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:
|
||
info.Members = members
|
||
|
||
if isinstance(OriginalClass, CTypeInfo):
|
||
info.OriginalType = OriginalClass
|
||
elif OriginalType_kind == 'typedef' and OriginalClass and isinstance(OriginalClass, str) and ('*' in OriginalClass):
|
||
OriginalType: str = 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) -> None:
|
||
info: CTypeInfo = CTypeInfo()
|
||
info.IsModuleAlias = True
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
self._symbols[name] = info
|
||
|
||
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()
|
||
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: 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: ast.AST) -> int | float | str | None:
|
||
"""计算常量表达式的值(委托到 ConstEvaluator)"""
|
||
return ConstEvaluator.eval_with_symtab(node, self)
|
||
|
||
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:
|
||
info.IsStruct = True
|
||
info.IsAnonymous = True
|
||
info.Members = members if members else {}
|
||
info.Lineno = lineno
|
||
info.file = FilePath
|
||
self._symbols[FullName] = info
|