From 92a381f0031fcfa0fc9007a0ce1df81d561a53fe Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 22 Jul 2026 14:05:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=20TPV=20=E7=9A=84?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E9=94=99=E8=AF=AF=EF=BC=8C=E5=8C=85=E6=8B=AC?= =?UTF-8?q?=E9=97=AD=E5=8C=85=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/lib/Projectrans/DeclarationGenerator.py | 1188 +++++++ App/lib/Projectrans/Phase1Generator.py | 595 ++++ App/lib/Projectrans/Phase2Translator.py | 3056 +++++++++++++++++++ App/lib/Shell/Config.py | 8 + App/lib/Shell/Serializer.py | 61 + App/lib/Shell/TransPyC.py | 645 ++++ App/lib/Shell/__init__.py | 6 + App/lib/StubGen/Config.py | 43 + App/lib/StubGen/Converter.py | 983 ++++++ App/lib/StubGen/Generator.py | 313 ++ App/lib/StubGen/__init__.py | 6 + App/lib/_bootstrap.py | 37 + App/lib/constants/__init__.py | 1 + App/lib/constants/config.py | 43 + App/lib/core/CTypeInfo.py | 409 +++ App/lib/core/ConstEvaluator.py | 515 ++++ App/lib/core/DecoratorPass.py | 547 ++++ App/lib/core/DiagnosticCollector.py | 48 + App/lib/core/Exportable.py | 161 + App/lib/core/Handles/HandlesAssert.py | 60 + App/lib/core/Handles/HandlesClassDef.py | 169 +- App/lib/core/Handles/HandlesDelete.py | 102 + App/lib/core/Handles/HandlesExprAsm.py | 580 ++++ App/lib/core/Handles/HandlesExprAttr.py | 1480 +++++++++ App/lib/core/Handles/HandlesExprBuiltin.py | 991 ++++++ App/lib/core/Handles/HandlesExprCall.py | 71 +- App/lib/core/Handles/HandlesExprFormat.py | 271 ++ App/lib/core/Handles/HandlesExprLambda.py | 249 ++ App/lib/core/Handles/HandlesExprUtils.py | 285 ++ App/lib/core/Handles/HandlesFunctions.py | 51 + App/lib/core/Handles/HandlesMain.py | 38 +- App/lib/core/Handles/HandlesMatch.py | 382 +++ App/lib/core/Handles/HandlesRaise.py | 155 + App/lib/core/Handles/HandlesSpecialCall.py | 467 +++ App/lib/core/Handles/HandlesTranslator.py | 40 + App/lib/core/Handles/HandlesTry.py | 214 ++ App/lib/core/Handles/HandlesTypeMerge.py | 976 ++++++ App/lib/core/Handles/HandlesWith.py | 206 ++ App/lib/core/IncludesScanner.py | 26 +- App/lib/core/LLVMCG/BaseGen.py | 669 ++++ App/lib/core/LLVMCG/ExprGen.py | 368 +++ App/lib/core/LLVMCG/FuncGen.py | 524 ++++ App/lib/core/LLVMCG/MemoryOps.py | 286 ++ App/lib/core/LLVMCG/StructGen.py | 629 ++++ App/lib/core/LLVMCG/TypeConvert.py | 767 +++++ App/lib/core/LLVMCG/VaArg.py | 94 + App/lib/core/LLVMCG/__init__.py | 34 + App/lib/core/LLVMTypeMapper.py | 301 ++ App/lib/core/LlvmCodeGenerator.py | 45 + App/lib/core/Phase1.py | 214 +- App/lib/core/StubMerger.py | 67 +- App/lib/core/SymbolData.py | 85 + App/lib/core/SymbolExtractor.py | 307 ++ App/lib/core/SymbolInserter.py | 89 + App/lib/core/SymbolNode.py | 224 ++ App/lib/core/SymbolReexporter.py | 99 + App/lib/core/SymbolTable.py | 531 ++++ App/lib/core/SymbolTypeResolver.py | 272 ++ App/lib/core/SymbolUtils.py | 214 ++ App/lib/core/Translator/AnnotationLoader.py | 381 +++ App/lib/core/Translator/BaseTranslator.py | 157 + App/lib/core/Translator/ConstEval.py | 305 ++ App/lib/core/Translator/LlvmGenerator.py | 642 ++++ App/lib/core/Translator/PythonParser.py | 452 +++ App/lib/core/Translator/__init__.py | 11 + App/lib/core/TypeAnnotationResolver.py | 736 +++++ App/lib/core/TypeSpec.py | 332 ++ App/lib/core/stub_generator.py | 761 +++++ App/lib/core/translator.py | 34 + App/lib/includes/__init__.py | 1 + App/lib/utils/__init__.py | 1 + App/lib/utils/helpers.py | 47 + App/main.py | 4 - Test/build.log | Bin 0 -> 21434 bytes Test/crash_out.txt | 2 +- Test/dbg_out.txt | 1 + _build.log | 1 + build.log | Bin 0 -> 38111 bytes 78 files changed, 24965 insertions(+), 200 deletions(-) create mode 100644 App/lib/Projectrans/DeclarationGenerator.py create mode 100644 App/lib/Projectrans/Phase1Generator.py create mode 100644 App/lib/Projectrans/Phase2Translator.py create mode 100644 App/lib/Shell/Config.py create mode 100644 App/lib/Shell/Serializer.py create mode 100644 App/lib/Shell/TransPyC.py create mode 100644 App/lib/Shell/__init__.py create mode 100644 App/lib/StubGen/Config.py create mode 100644 App/lib/StubGen/Converter.py create mode 100644 App/lib/StubGen/Generator.py create mode 100644 App/lib/StubGen/__init__.py create mode 100644 App/lib/_bootstrap.py create mode 100644 App/lib/constants/__init__.py create mode 100644 App/lib/constants/config.py create mode 100644 App/lib/core/CTypeInfo.py create mode 100644 App/lib/core/ConstEvaluator.py create mode 100644 App/lib/core/DecoratorPass.py create mode 100644 App/lib/core/DiagnosticCollector.py create mode 100644 App/lib/core/Exportable.py create mode 100644 App/lib/core/Handles/HandlesAssert.py create mode 100644 App/lib/core/Handles/HandlesDelete.py create mode 100644 App/lib/core/Handles/HandlesExprAsm.py create mode 100644 App/lib/core/Handles/HandlesExprAttr.py create mode 100644 App/lib/core/Handles/HandlesExprBuiltin.py create mode 100644 App/lib/core/Handles/HandlesExprFormat.py create mode 100644 App/lib/core/Handles/HandlesExprLambda.py create mode 100644 App/lib/core/Handles/HandlesExprUtils.py create mode 100644 App/lib/core/Handles/HandlesMatch.py create mode 100644 App/lib/core/Handles/HandlesRaise.py create mode 100644 App/lib/core/Handles/HandlesSpecialCall.py create mode 100644 App/lib/core/Handles/HandlesTry.py create mode 100644 App/lib/core/Handles/HandlesTypeMerge.py create mode 100644 App/lib/core/Handles/HandlesWith.py create mode 100644 App/lib/core/LLVMCG/BaseGen.py create mode 100644 App/lib/core/LLVMCG/ExprGen.py create mode 100644 App/lib/core/LLVMCG/FuncGen.py create mode 100644 App/lib/core/LLVMCG/MemoryOps.py create mode 100644 App/lib/core/LLVMCG/StructGen.py create mode 100644 App/lib/core/LLVMCG/TypeConvert.py create mode 100644 App/lib/core/LLVMCG/VaArg.py create mode 100644 App/lib/core/LLVMCG/__init__.py create mode 100644 App/lib/core/LLVMTypeMapper.py create mode 100644 App/lib/core/LlvmCodeGenerator.py create mode 100644 App/lib/core/SymbolData.py create mode 100644 App/lib/core/SymbolExtractor.py create mode 100644 App/lib/core/SymbolInserter.py create mode 100644 App/lib/core/SymbolNode.py create mode 100644 App/lib/core/SymbolReexporter.py create mode 100644 App/lib/core/SymbolTable.py create mode 100644 App/lib/core/SymbolTypeResolver.py create mode 100644 App/lib/core/SymbolUtils.py create mode 100644 App/lib/core/Translator/AnnotationLoader.py create mode 100644 App/lib/core/Translator/BaseTranslator.py create mode 100644 App/lib/core/Translator/ConstEval.py create mode 100644 App/lib/core/Translator/LlvmGenerator.py create mode 100644 App/lib/core/Translator/PythonParser.py create mode 100644 App/lib/core/Translator/__init__.py create mode 100644 App/lib/core/TypeAnnotationResolver.py create mode 100644 App/lib/core/TypeSpec.py create mode 100644 App/lib/core/stub_generator.py create mode 100644 App/lib/core/translator.py create mode 100644 App/lib/includes/__init__.py create mode 100644 App/lib/utils/__init__.py create mode 100644 App/lib/utils/helpers.py create mode 100644 Test/build.log create mode 100644 Test/dbg_out.txt create mode 100644 _build.log create mode 100644 build.log diff --git a/App/lib/Projectrans/DeclarationGenerator.py b/App/lib/Projectrans/DeclarationGenerator.py new file mode 100644 index 0000000..be1917a --- /dev/null +++ b/App/lib/Projectrans/DeclarationGenerator.py @@ -0,0 +1,1188 @@ +from __future__ import annotations + +import ast +import re +from typing import List + +from lib.includes import t +from lib.includes.t import CTypeRegistry +from lib.core.SymbolUtils import IsListAnnotation, AnnotationContainsName + + +class DeclarationGenerator: + """从 .pyi AST 生成纯 LLVM IR 声明(不使用字符串操作)""" + + # 匹配 LLVM 数组类型:[N x elem_type] + _ARRAY_TYPE_RE: re.Pattern = re.compile(r'^\[(\d+)\s+x\s+(.+)\]$') + + @staticmethod + def _decay_array_to_ptr(type_str: str) -> str: + """C 语言中数组参数退化为指针:[N x elem_type] → elem_type*""" + m = DeclarationGenerator._ARRAY_TYPE_RE.match(type_str) + if m: + return f'{m.group(2)}*' + return type_str + + def __init__(self, struct_names: set[str] | None = None, enum_names: set[str] | None = None, module_sha1: str | None = None, target_triple: str | None = None, target_datalayout: str | None = None, struct_sha1_map: dict[str, str] | None = None, exception_names: set[str] | None = None, typedef_map: dict[str, ast.AST] | None = None, class_def_map: dict[str, ast.ClassDef] | None = None) -> None: + self.ir = None + self.module = None + self.builder = None + self._DefineConstants: dict[str, int | str] = {} + self.struct_names: set[str] = struct_names or set() + self.enum_names: set[str] = enum_names or set() + self.module_sha1: str | None = module_sha1 + self.struct_sha1_map: dict[str, str] = struct_sha1_map or {} + self.exception_names: set[str] = exception_names or set() + self.target_triple: str = target_triple or "x86_64-none-elf" + self.target_datalayout: str = target_datalayout or "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + self.typedef_map: dict[str, ast.AST] = typedef_map or {} + self._pyi_tree: ast.Module | None = None + self._t_alias_map: dict[str, type] = {} + # 跨模块类定义映射:{class_name: ClassDef_node},供 _get_inherited_members + # 查找其他模块定义的父类(如 exprs.py 的 Name(AST) 查找 base.py 的 AST) + self._cross_module_class_defs: dict[str, ast.ClassDef] = class_def_map or {} + t.configure_platform(self.target_triple) + + def generate(self, pyi_content: str, src_path: str) -> str: + tree: ast.Module = ast.parse(pyi_content) + + self._DefineConstants: dict[str, int | str] = {} + self._pyi_tree = tree + global_typedef_map: dict[str, ast.AST] = self.typedef_map + self.typedef_map: dict[str, ast.AST] = {} + if global_typedef_map: + self.typedef_map.update(global_typedef_map) + + # 构建 t/c 类型别名映射(from t import CInt as myint) + self._t_alias_map: dict[str, type] = {} + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.ImportFrom): + if node.module in ('t', 'c'): + for alias in node.names: + name: str = alias.name + asname: str = alias.asname or name + if node.module == 't': + t_cls: type | None = getattr(t, name, None) + if isinstance(t_cls, type) and issubclass(t_cls, t.CType): + self._t_alias_map[asname] = t_cls + + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + var_name: str = node.target.id + if node.value and isinstance(node.value, ast.Constant): + self._DefineConstants[var_name] = node.value.value + elif node.value and isinstance(node.value, ast.Name): + self._DefineConstants[var_name] = node.value.id + is_typedef: bool = False + if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef': + is_typedef = True + elif isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef': + is_typedef = True + elif isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef': + is_typedef = True + if is_typedef: + if node.value: + self.typedef_map[var_name] = node.value + elif isinstance(node.annotation, ast.BinOp) and node.annotation.right: + self.typedef_map[var_name] = node.annotation.right + + lines: list[str] = [] + lines.append('; ModuleID = "transpyc_decl"') + lines.append(f'target triple = "{self.target_triple}"') + lines.append(f'target datalayout = "{self.target_datalayout}"') + lines.append('') + + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.FunctionDef): + if hasattr(node, 'type_params') and node.type_params: + continue + decl: str | None = self._generate_func_decl(node) + if decl: + lines.append(decl) + elif isinstance(node, ast.AnnAssign): + decl: str | None = self._generate_global_decl(node) + if decl: + lines.append(decl) + elif isinstance(node, ast.Assign): + decl: str | None = self._generate_global_assign_decl(node) + if decl: + lines.append(decl) + elif isinstance(node, ast.ClassDef): + # PEP 695 泛型类不再完全跳过:子类(如 Value(GSListNode[Value]))的 + # 继承字段展平需要从泛型基类的 stub 中读取字段(如 GSListNode.Next)。 + # _generate_class_decl 内部会跳过泛型类的方法声明(T 参数 → opaque struct)。 + decls: list[str] = self._generate_class_decl(node) + lines.extend(decls) + + # 后处理:扫描引用但未定义的结构体类型,自动添加 opaque 声明 + # 这解决了泛型类(如 list[T])在 stub 中被跳过导致的 "undefined type" 错误 + opaque_decls: list[str] = self._collect_undefined_type_decls(lines) + if opaque_decls: + # 在 header 之后插入 opaque 声明(ModuleID, triple, datalayout, 空行 = 4 行) + insert_idx: int = 4 + for i, od in enumerate(opaque_decls): + lines.insert(insert_idx + i, od) + + return '\n'.join(lines) + + def _collect_undefined_type_decls(self, lines: list[str]) -> list[str]: + """扫描所有引用的 %"sha1.TypeName" 类型,返回未定义类型的 opaque 声明列表""" + # 收集已定义的类型名 + defined_types: set[str] = set() + type_def_re: re.Pattern = re.compile(r'^(%".+?")\s*=\s*type\s') + for line in lines: + m = type_def_re.match(line.strip()) + if m: + defined_types.add(m.group(1)) + + # 扫描所有引用的类型名 + referenced_types: set[str] = set() + type_ref_re: re.Pattern = re.compile(r'%"[a-f0-9]+\.[^"]+"') + for line in lines: + for m in type_ref_re.finditer(line): + ref: str = m.group(0) + if ref not in defined_types: + referenced_types.add(ref) + + # 生成 opaque 声明(排序确保确定性) + result: list[str] = [] + for type_name in sorted(referenced_types): + result.append(f'{type_name} = type opaque') + return result + + def _generate_func_decl(self, node: ast.FunctionDef) -> str | None: + """生成函数声明""" + func_name: str = node.name + is_export: bool = self._is_export_func(node) + if self.module_sha1 and not is_export: + func_name = f"{self.module_sha1}.{func_name}" + CReturnTypes: list[ast.AST] = [] + if node.decorator_list: + for decorator in node.decorator_list: + if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): + if decorator.func.attr == 'CReturn': + for arg in decorator.args: + CReturnTypes.append(arg) + if node.returns: + if isinstance(node.returns, ast.Subscript) and isinstance(node.returns.value, ast.Name) and node.returns.value.id == 'tuple': + slice_node: ast.AST = node.returns.slice + if isinstance(slice_node, ast.Tuple): + for elt in slice_node.elts: + CReturnTypes.append(elt) + else: + CReturnTypes.append(slice_node) + ret_type: str + if CReturnTypes: + elem_types: list[str] = [self._get_type_str(rt, embedded=True) for rt in CReturnTypes] + ret_type = '{ ' + ', '.join(elem_types) + ' }' + else: + ret_type = self._get_type_str(node.returns) + if not ret_type or ret_type == 'void': + ret_type = 'void' + elif ret_type == 'i8*': + if node.returns is None: + ret_type = 'void' + + params: list[str] = [] + for arg in node.args.args: + arg_type: str + if arg.annotation: + arg_type = self._get_type_str(arg.annotation) + else: + arg_type = 'i8*' + # C 语言中数组参数退化为指针:[N x elem_type] → elem_type* + arg_type = self._decay_array_to_ptr(arg_type) + if arg_type and arg_type != 'void': + params.append(arg_type) + + param_str: str = ', '.join(params) if params else '' + if node.args.vararg: + param_str = param_str + ', ...' if param_str else '...' + if func_name[0].isdigit(): + return f'declare {ret_type} @"{func_name}"({param_str})' + return f'declare {ret_type} @{func_name}({param_str})' + + def _is_export_func(self, node: ast.FunctionDef) -> bool: + """检查函数是否标记为 CExport""" + if not node.returns: + return False + return self._check_annotation_for_export(node.returns) + + def _check_annotation_for_export(self, annotation: ast.AST | None) -> bool: + """递归检查类型注解中是否包含 CExport 或 t.State""" + return bool(annotation) and (AnnotationContainsName(annotation, 'CExport') or AnnotationContainsName(annotation, 'State')) + + def _generate_global_decl(self, node: ast.AnnAssign) -> str | None: + """生成全局变量声明""" + if not isinstance(node.target, ast.Name): + return None + var_name: str = node.target.id + if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CDefine': + return None + if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef': + return None + if isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef': + return None + if isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef': + return None + if isinstance(node.annotation, ast.List): + for elt in node.annotation.elts: + if isinstance(elt, ast.Attribute) and hasattr(elt, 'attr') and elt.attr in ('CTypedef', 'Callable'): + return None + if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable': + return None + VarType: str = self._get_type_str(node.annotation) + if VarType.startswith('[0 x ') and node.value and isinstance(node.value, ast.List): + elem_type: str = VarType[5:-1] + actual_count: int = len(node.value.elts) + if actual_count > 0: + VarType = f'[{actual_count} x {elem_type}]' + return f'@{var_name} = external global {VarType}' + + def _generate_global_assign_decl(self, node: ast.Assign) -> str | None: + """从无类型标注赋值推断并生成全局变量声明""" + if not node.targets or not isinstance(node.targets[0], ast.Name): + return None + var_name: str = node.targets[0].id + VarType: str | None = self._infer_type(node.value) + if VarType: + return f'@{var_name} = external global {VarType}' + return None + + def _resolve_base_kind(self, base_name: str) -> str | None: + """解析基类名称的类型种类(支持别名) + + Returns: + 'enum' | 'union' | 'renum' | 'struct' | None + """ + # 1) 查 t 别名映射(from t import CEnum as en) + if base_name in self._t_alias_map: + t_cls: type = self._t_alias_map[base_name] + if issubclass(t_cls, (t.CEnum, t.REnum)): + return 'renum' if issubclass(t_cls, t.REnum) else 'enum' + if issubclass(t_cls, t.CUnion): + return 'union' + if issubclass(t_cls, t.CStruct): + return 'struct' + + # 2) 直接查 t 模块属性 + t_cls: type | None = getattr(t, base_name, None) + if isinstance(t_cls, type) and issubclass(t_cls, t.CType): + if issubclass(t_cls, (t.CEnum, t.REnum)): + return 'renum' if issubclass(t_cls, t.REnum) else 'enum' + if issubclass(t_cls, t.CUnion): + return 'union' + if issubclass(t_cls, t.CStruct): + return 'struct' + + return None + + def _is_marker_base(self, base_name: str) -> bool: + """检查是否为标记基类(Object/CVTable/Exception/CEnum/CUnion/CStruct/REnum 及其别名)""" + if base_name in ('Object', 'CVTable', 'Exception', 'CEnum', 'Enum', 'CStruct', 'CUnion', 'REnum'): + return True + if self._resolve_base_kind(base_name) is not None: + return True + return False + + def _generate_class_decl(self, node: ast.ClassDef) -> List[str]: + """生成结构体/类声明""" + decls: list[str] = [] + class_name: str = node.name + is_enum: bool = False + is_renum: bool = False + is_union: bool = False + is_exception: bool = False + if node.bases: + for base in node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute) and hasattr(base, 'attr'): + base_name = base.attr + elif isinstance(base, ast.Name) and hasattr(base, 'id'): + base_name = base.id + if base_name: + if base_name in self.exception_names or base_name == 'Exception': + is_exception = True + break + kind: str | None = self._resolve_base_kind(base_name) + if kind == 'enum': + is_enum = True + break + elif kind == 'union': + is_union = True + elif kind == 'renum': + is_renum = True + if is_exception: + return decls + if is_enum: + enum_values: dict[str, int] = {} + next_value: int = 0 + for item in node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + var_name: str = item.target.id + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + enum_values[var_name] = item.value.value + else: + enum_values[var_name] = next_value + next_value += 1 + elif isinstance(item, ast.Assign) and len(item.targets) == 1 and isinstance(item.targets[0], ast.Name): + var_name: str = item.targets[0].id + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + enum_values[var_name] = item.value.value + next_value = item.value.value + 1 + else: + enum_values[var_name] = next_value + next_value += 1 + for var_name, value in enum_values.items(): + decls.append(f'@__config_{class_name}_{var_name} = external global i32') + return decls + if is_renum: + # REnum 布局: { i32 __tag, } + # 对齐 _EmitREnumLlvm (HandlesClassDef.py) 的 Phase 2 逻辑 + # Bug 修复:按每个位置的 max 字段尺寸构建布局,而非选总尺寸最大的变体。 + # 不同变体在同一位置可能有不同尺寸的字段(如 Concrete 位置3是 i32, + # 但 Var 位置3是 MetaType*),用最大变体的布局会导致指针截断。 + variant_fields_list: list[list[str]] = [] + for item in node.body: + if isinstance(item, ast.ClassDef): + variant_fields: list[str] = [] + for nested_item in item.body: + if isinstance(nested_item, ast.AnnAssign) and isinstance(nested_item.target, ast.Name): + ft: str = self._get_type_str(nested_item.annotation, embedded=True) + if ft: + variant_fields.append(ft) + variant_fields_list.append(variant_fields) + elif isinstance(item, ast.Assign): + # 显式值变体(无 payload) + variant_fields_list.append([]) + # 计算每个位置的最大尺寸 + max_field_count: int = 0 + for vf in variant_fields_list: + if len(vf) > max_field_count: + max_field_count = len(vf) + renum_member_types: list[str] = ['i32'] # __tag + for pos in range(max_field_count): + pos_max_size: int = 0 + for vf in variant_fields_list: + if pos < len(vf): + sz: int = self._llvm_type_size_str(vf[pos]) + if sz > pos_max_size: + pos_max_size = sz + # <=4 用 i32,>4 用 i64(8字节,可容纳指针) + if pos_max_size <= 4: + renum_member_types.append('i32') + else: + renum_member_types.append('i64') + if len(renum_member_types) < 2: + # 无类变体或所有变体无字段时,使用 i32 作为占位(对齐 _EmitREnumLlvm 默认行为) + renum_member_types = ['i32', 'i32'] + struct_type_name: str = f'%"{self.module_sha1}.{class_name}"' if self.module_sha1 else f'%struct.{class_name}' + struct_decl: str = f'{struct_type_name} = type {{ {", ".join(renum_member_types)} }}' + decls.append(struct_decl) + return decls + member_types: list[str] = [] + has_bitfield: bool = False + total_bits: int = 0 + has_methods: bool = any(isinstance(item, ast.FunctionDef) for item in node.body) + is_cvtable: bool = False + is_cpython_object: bool = False + is_novtable: bool = False + if hasattr(node, 'decorator_list') and node.decorator_list: + for decorator in node.decorator_list: + if isinstance(decorator, ast.Attribute): + if getattr(decorator.value, 'id', None) == 't': + if decorator.attr == 'CVTable': + is_cvtable = True + elif decorator.attr == 'Object': + is_cpython_object = True + elif decorator.attr == 'NoVTable': + is_novtable = True + elif isinstance(decorator, ast.Name): + if decorator.id == 'CVTable': + is_cvtable = True + elif decorator.id == 'Object': + is_cpython_object = True + elif decorator.id == 'NoVTable': + is_novtable = True + if has_methods and not is_cpython_object: + is_cpython_object = True + has_parent_class: bool = False + parent_is_novtable: bool = False + for base in node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + if base_name and not self._is_marker_base(base_name): + has_parent_class = True + # 检查父类是否为 @t.NoVTable(非多态继承) + _p_node: ast.ClassDef | None = None + for n in ast.iter_child_nodes(self._pyi_tree): + if isinstance(n, ast.ClassDef) and n.name == base_name: + _p_node = n + break + if _p_node is None: + _p_node = self._cross_module_class_defs.get(base_name) + if _p_node is not None and hasattr(_p_node, 'decorator_list') and _p_node.decorator_list: + for dec in _p_node.decorator_list: + if isinstance(dec, ast.Attribute) and getattr(dec.value, 'id', None) == 't' and dec.attr == 'NoVTable': + parent_is_novtable = True + elif isinstance(dec, ast.Name) and dec.id == 'NoVTable': + parent_is_novtable = True + break + # 仅当非 NoVTable 且父类非 NoVTable 时才因继承启用 CVTable + # (@t.NoVTable 标记的类及其子类不使用 vtable,字段展平嵌入) + if has_parent_class and not is_cvtable and not is_novtable and not parent_is_novtable: + is_cvtable = True + base_has_vtable: bool = False + for base in node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + if base_name and not self._is_marker_base(base_name): + # 先在当前 .pyi 中查找父类,找不到则查跨模块类定义映射 + _base_node: ast.ClassDef | None = None + for n in ast.iter_child_nodes(self._pyi_tree): + if isinstance(n, ast.ClassDef) and n.name == base_name: + _base_node = n + break + if _base_node is None: + _base_node = self._cross_module_class_defs.get(base_name) + if _base_node is not None and hasattr(_base_node, 'decorator_list') and _base_node.decorator_list: + for dec in _base_node.decorator_list: + if isinstance(dec, ast.Attribute) and getattr(dec.value, 'id', None) == 't' and dec.attr == 'CVTable': + base_has_vtable = True + elif isinstance(dec, ast.Name) and dec.id == 'CVTable': + base_has_vtable = True + if base_has_vtable: + break + # NoVTable 类不添加 vtable 指针(即使有方法和父类) + if has_methods and is_cvtable and not base_has_vtable and not is_novtable: + member_types.append('i8*') + seen_member_names: set[str] = set() + for base in node.bases: + base_name: str | None = None + type_args: list[str] = [] + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + slice_node: ast.AST = base.slice + if isinstance(slice_node, ast.Name): + type_args.append(slice_node.id) + elif isinstance(slice_node, ast.Tuple): + for elt in slice_node.elts: + if isinstance(elt, ast.Name): + type_args.append(elt.id) + if base_name and not self._is_marker_base(base_name): + base_member_types: list[str] + base_seen: set[str] + base_member_types, base_seen = self._get_inherited_members(base_name) + if type_args: + base_member_types = self._specialize_member_types(base_member_types, type_args) + for mt in base_member_types: + member_types.append(mt) + seen_member_names.update(base_seen) + for item in node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + seen_member_names.add(item.target.id) + init_members: list[ast.AnnAssign] = [] + for item in node.body: + if isinstance(item, ast.FunctionDef) and item.name == '__init__': + for stmt in item.body: + if (isinstance(stmt, ast.AnnAssign) + and isinstance(stmt.target, ast.Attribute) + and isinstance(stmt.target.value, ast.Name) + and stmt.target.value.id == 'self'): + init_members.append(stmt) + untyped_self_members: list[ast.Assign] = [] + for m_name in [m.attr for m in init_members if isinstance(m.target, ast.Attribute)]: + seen_member_names.add(m_name) + for item in node.body: + if isinstance(item, ast.FunctionDef): + if hasattr(item, 'type_params') and item.type_params: + continue + for stmt in item.body: + if (isinstance(stmt, ast.Assign) + and len(stmt.targets) == 1 + and isinstance(stmt.targets[0], ast.Attribute) + and isinstance(stmt.targets[0].value, ast.Name) + and stmt.targets[0].value.id == 'self'): + attr_name: str = stmt.targets[0].attr + if attr_name not in seen_member_names: + seen_member_names.add(attr_name) + untyped_self_members.append(stmt) + for item in list(node.body) + init_members + untyped_self_members: + if isinstance(item, ast.AnnAssign): + # 跳过编译期元数据字段(__provides__/__requires__/__require_must__) + if isinstance(item.target, ast.Name) and item.target.id in ('__provides__', '__requires__', '__require_must__'): + continue + bit_width: int | None = self._get_bitfield_width(item.annotation) + if bit_width is not None: + has_bitfield = True + total_bits += bit_width + else: + VarType: str = self._get_type_str(item.annotation, embedded=True) + if IsListAnnotation(item.annotation): + slice_node: ast.AST = item.annotation.slice + is_dynamic: bool = False + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + count_node: ast.AST = slice_node.elts[1] + if isinstance(count_node, ast.Constant) and count_node.value is None: + is_dynamic = True + elif not (isinstance(count_node, ast.Constant) and isinstance(count_node.value, int) and count_node.value > 0): + if not isinstance(count_node, (ast.Name, ast.BinOp)): + is_dynamic = True + elif not isinstance(slice_node, ast.Tuple): + is_dynamic = True + if is_dynamic: + elem_type: str = self._get_type_str(slice_node if not isinstance(slice_node, ast.Tuple) else slice_node.elts[0], embedded=True) + init_len: int = 0 + if isinstance(item.value, ast.List): + init_len = len(item.value.elts) + elif isinstance(item.value, ast.Constant) and isinstance(item.value.value, str): + init_len = len(item.value.value) + 1 + VarType = f'[{init_len} x {elem_type}]' + else: + # 固定大小数组: t.CArray[elem_type, count] → [count x elem_type] + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + elem_type: str = self._get_type_str(slice_node.elts[0], embedded=True) + count_val: int = self._get_const_int(slice_node.elts[1]) + if count_val > 0: + VarType = f'[{count_val} x {elem_type}]' + elif isinstance(slice_node, (ast.Attribute, ast.Name, ast.Subscript)): + # 单参数 t.CArray[elem_type] → 指针模式 + elem_type: str = self._get_type_str(slice_node, embedded=True) + VarType = f'{elem_type}*' + member_types.append(VarType) + elif isinstance(item, ast.Assign): + if (len(item.targets) == 1 + and isinstance(item.targets[0], ast.Attribute) + and isinstance(item.targets[0].value, ast.Name) + and item.targets[0].value.id == 'self'): + attr_name: str = item.targets[0].attr + if attr_name in seen_member_names: + continue + InferredType: str = 'i32' + if item.value and isinstance(item.value, ast.Constant): + if isinstance(item.value.value, float): + InferredType = 'float' + elif isinstance(item.value.value, bool): + InferredType = 'i8' + elif isinstance(item.value.value, str): + InferredType = 'i8*' + member_types.append(InferredType) + else: + member_types.append('i32') + if has_bitfield: + if total_bits <= 8: + member_types.insert(0, 'i8') + elif total_bits <= 16: + member_types.insert(0, 'i16') + elif total_bits <= 32: + member_types.insert(0, 'i32') + else: + member_types.insert(0, 'i64') + struct_type_name: str = f'%"{self.module_sha1}.{class_name}"' if self.module_sha1 else f'%struct.{class_name}' + struct_decl: str = f'{struct_type_name} = type {{ {", ".join(member_types)} }}' + decls.append(struct_decl) + if is_cpython_object or is_cvtable: + new_func_name: str = f'{class_name}.__before_init__' + if self.module_sha1: + new_func_name = f"{self.module_sha1}.{new_func_name}" + new_func_decl: str + if new_func_name[0].isdigit(): + new_func_decl = f'declare void @"{new_func_name}"({struct_type_name}*)' + else: + new_func_decl = f'declare void @{new_func_name}({struct_type_name}*)' + decls.append(new_func_decl) + # PEP 695 泛型类跳过方法声明生成:方法参数类型 T 会被解析为 opaque struct, + # LLVM 报 "invalid type for function argument"。字段(AnnAssign)仍正常生成, + # 确保子类(如 Value(GSListNode[Value]))能从 stub 中读取继承字段(如 Next)。 + IsGenericClass: bool = hasattr(node, 'type_params') and bool(node.type_params) + if not IsGenericClass: + for item in node.body: + if isinstance(item, ast.FunctionDef): + if hasattr(item, 'type_params') and item.type_params: + continue + method_name: str = f'{class_name}.{item.name}' + if self.module_sha1: + method_name = f"{self.module_sha1}.{method_name}" + ret_type: str = self._get_type_str(item.returns) if item.returns else 'void' + if not ret_type: + ret_type = 'void' + params: list[str] = [] + for arg_idx, arg in enumerate(item.args.args): + arg_type: str + if arg.annotation: + arg_type = self._get_type_str(arg.annotation) + elif arg_idx == 0 and arg.arg == 'self': + arg_type = f'{struct_type_name}*' + else: + arg_type = 'i8*' + # C 语言中数组参数退化为指针:[N x elem_type] → elem_type* + arg_type = self._decay_array_to_ptr(arg_type) + params.append(arg_type) + param_str: str = ', '.join(params) if params else '' + if method_name[0].isdigit(): + decls.append(f'declare {ret_type} @"{method_name}"({param_str})') + else: + decls.append(f'declare {ret_type} @{method_name}({param_str})') + # 为继承但未覆写的方法生成包装声明 + # 这些声明让跨模块调用能正确解析子类包装函数的签名, + # 避免 stub 缺失导致默认 i32 返回类型 → 64 位指针截断 + decls.extend(self._generate_inherited_method_decls(node, class_name, struct_type_name)) + return decls + + def _generate_inherited_method_decls(self, node: ast.ClassDef, child_class_name: str, child_struct_type: str) -> list[str]: + """为继承但未覆写的方法生成包装声明 + + 在 Phase 1 stub 中声明子类继承方法的签名(与父类相同,但 self 参数为子类类型)。 + 这样测试模块翻译时能从 temp stub 中获取正确的返回类型, + 避免 Phase 2 includes 翻译滞后导致跨模块调用使用默认 i32 返回类型。 + """ + decls: list[str] = [] + # 收集子类自身定义的方法名(这些不需要生成包装,子类有自己的实现) + seen_method_names: set[str] = set() + for item in node.body: + if isinstance(item, ast.FunctionDef): + seen_method_names.add(item.name) + + # 沿继承链收集父类方法,近祖先优先 + for base in node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + if not base_name or self._is_marker_base(base_name): + continue + self._CollectInheritedWrapperDecls( + base_name, child_class_name, child_struct_type, + seen_method_names, decls) + return decls + + def _CollectInheritedWrapperDecls( + self, parent_name: str, child_class_name: str, child_struct_type: str, + seen_method_names: set[str], decls: list[str]) -> None: + """递归收集父类及其祖先的方法,为未覆写的方法生成包装声明""" + # 查找父类节点:先在当前 .pyi 中查找,找不到则查跨模块类定义映射 + parent_node: ast.ClassDef | None = None + for n in ast.iter_child_nodes(self._pyi_tree): + if isinstance(n, ast.ClassDef) and n.name == parent_name: + parent_node = n + break + if parent_node is None: + parent_node = self._cross_module_class_defs.get(parent_name) + if parent_node is None: + return + + # 为父类的每个方法生成包装声明 + for item in parent_node.body: + if isinstance(item, ast.FunctionDef): + if hasattr(item, 'type_params') and item.type_params: + continue + method_name: str = item.name + # __before_init__ 只在自身类生成,不生成继承包装 + if method_name == '__before_init__': + continue + # 跳过子类已覆写的方法,以及已处理的祖先方法 + if method_name in seen_method_names: + continue + seen_method_names.add(method_name) + + # 构建包装方法名:{module_sha1}.{child_class}.{method_name} + wrapper_name: str = f'{child_class_name}.{method_name}' + if self.module_sha1: + wrapper_name = f"{self.module_sha1}.{wrapper_name}" + + # 解析返回类型(与父类方法相同) + ret_type: str = self._get_type_str(item.returns) if item.returns else 'void' + if not ret_type: + ret_type = 'void' + elif ret_type == 'i8*': + if item.returns is None: + ret_type = 'void' + + # 构建参数列表:第一个参数(self)替换为子类类型,其余与父类相同 + params: list[str] = [] + for arg_idx, arg in enumerate(item.args.args): + arg_type: str + if arg_idx == 0 and arg.arg == 'self': + arg_type = f'{child_struct_type}*' + else: + if arg.annotation: + arg_type = self._get_type_str(arg.annotation) + else: + arg_type = 'i8*' + arg_type = self._decay_array_to_ptr(arg_type) + params.append(arg_type) + + param_str: str = ', '.join(params) if params else '' + if wrapper_name[0].isdigit(): + decls.append(f'declare {ret_type} @"{wrapper_name}"({param_str})') + else: + decls.append(f'declare {ret_type} @{wrapper_name}({param_str})') + + # 递归处理祖先类 + for base in parent_node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + if base_name and not self._is_marker_base(base_name): + self._CollectInheritedWrapperDecls( + base_name, child_class_name, child_struct_type, + seen_method_names, decls) + + def _get_inherited_members(self, base_name: str) -> tuple[list[str], set[str]]: + member_types: list[str] = [] + seen_names: set[str] = set() + if not hasattr(self, '_pyi_tree') or self._pyi_tree is None: + return member_types, seen_names + base_node: ast.ClassDef | None = None + for node in ast.iter_child_nodes(self._pyi_tree): + if isinstance(node, ast.ClassDef) and node.name == base_name: + base_node = node + break + if base_node is None: + # 跨模块父类查找:当前 .pyi 中没有父类定义时,从跨模块类定义映射中查找 + # (如 exprs.py 的 Name(AST),AST 定义在 base.py) + base_node = self._cross_module_class_defs.get(base_name) + if base_node is None: + return member_types, seen_names + base_decls: list[str] = self._generate_class_decl(base_node) + for decl in base_decls: + if '= type {' in decl: + type_body: str = decl.split('= type {')[1].rstrip('}').strip() + if type_body: + for t in type_body.split(','): + t = t.strip() + if t: + member_types.append(t) + break + for item in base_node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + seen_names.add(item.target.id) + elif isinstance(item, ast.Assign) and len(item.targets) == 1: + if isinstance(item.targets[0], ast.Attribute) and isinstance(item.targets[0].value, ast.Name) and item.targets[0].value.id == 'self': + seen_names.add(item.targets[0].attr) + for item in base_node.body: + if isinstance(item, ast.FunctionDef) and item.name == '__init__': + for stmt in item.body: + if isinstance(stmt, ast.AnnAssign) and isinstance(stmt.target, ast.Attribute) and isinstance(stmt.target.value, ast.Name) and stmt.target.value.id == 'self': + seen_names.add(stmt.target.attr) + elif isinstance(stmt, ast.Assign) and len(stmt.targets) == 1 and isinstance(stmt.targets[0], ast.Attribute) and isinstance(stmt.targets[0].value, ast.Name) and stmt.targets[0].value.id == 'self': + seen_names.add(stmt.targets[0].attr) + return member_types, seen_names + + def _specialize_member_types(self, member_types: list[str], type_args: list[str]) -> list[str]: + """将基类成员类型中的泛型参数 T 替换为具体类型参数 + + 处理泛型基类继承(如 GSListNode[BasicBlock])时,基类 _get_inherited_members + 返回的成员类型中会包含未特化的 T 引用(形式为 %"SHA1.T"*)。 + + 替换策略:使用 i8*(不透明指针)代替 T*,而非具体类型指针。 + 原因:跨模块场景中,具体类型指针的 SHA1 前缀可能与导入模块不一致, + 导致 TransPyC 类型转换失败。i8* 作为通用指针类型,可安全赋值给 + 任何 Class | t.CPtr 注解的变量,运行时行为与 T* 相同(都是指针)。 + + Args: + member_types: 基类成员类型字符串列表 + type_args: 类型参数名列表(如 ['BasicBlock']) + + Returns: + 特化后的成员类型字符串列表 + """ + if not type_args: + return member_types + result: list[str] = [] + for mt in member_types: + new_mt: str = mt + # 将 %"SHA1.T"* 替换为 i8*(不透明指针) + new_mt = re.sub(r'%"[a-f0-9]+\.T"\*', 'i8*', new_mt) + # 将 %"SHA1.T"(非指针,如 embedded struct)替换为 i8* + new_mt = re.sub(r'%"[a-f0-9]+\.T"', 'i8*', new_mt) + result.append(new_mt) + return result + + def _llvm_type_size_str(self, type_str: str) -> int: + """估算 LLVM 类型字符串的大小(字节),用于 REnum 变体大小比较 + + 对齐 _EmitREnumLlvm 中的大小计算逻辑: + - i1=1, i8=1, i16=2, i32=4, i64=8, float=4, double=8, ptr=8 + - [N x T] = N * size(T) + - 其他结构体类型按指针大小 8 估算(保守值) + """ + s: str = type_str.strip() + if not s: + return 0 + # 指针类型 + if s.endswith('*'): + return 8 + # 数组类型 [N x T] + m = self._ARR_TYPE_RE.match(s) if hasattr(self, '_ARR_TYPE_RE') else None + if m: + try: + n: int = int(m.group(1)) + return n * self._llvm_type_size_str(m.group(2)) + except ValueError: + return 8 + # 基本整数/浮点类型 + size_map: dict[str, int] = { + 'i1': 1, 'i8': 1, 'i16': 2, 'i32': 4, 'i64': 8, + 'float': 4, 'double': 8, 'half': 2, 'fp128': 16, + 'void': 0, + } + if s in size_map: + return size_map[s] + # %{...} 结构体类型 - 保守估算为指针大小 + if s.startswith('%') or s.startswith('{'): + return 8 + # 其他未知类型保守估算 + return 8 + + def _get_type_str(self, annotation: ast.AST | None, embedded: bool = False) -> str: + if annotation is None: + return 'i8*' + + non_struct_types: set[str] = {'list', 'dict', 'tuple', 'set', 'array', 'CArray'} + + if isinstance(annotation, ast.Name): + if annotation.id == 'None': + return 'void' + if annotation.id in ('str', 'bytes'): + return 'i8*' + llvm_type: str | None = CTypeRegistry.NameToLLVM(annotation.id) + if llvm_type is not None: + return llvm_type + resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(annotation.id) + if resolved is not None: + ctype_cls: type + ptr_level: int + ctype_cls, ptr_level = resolved + base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if ptr_level > 0: + if base == 'void': + return 'i8*' + if '*' in base: + return base + return f'{base}*' + return base + if annotation.id in self.struct_names: + sha1: str | None = self.struct_sha1_map.get(annotation.id, self.module_sha1) + sname: str = f'%"{sha1}.{annotation.id}"' if sha1 else f'%struct.{annotation.id}' + if embedded: + return sname + else: + return f'{sname}*' + if annotation.id in non_struct_types: + return 'i32' + if annotation.id in self.enum_names: + return 'i32' + if annotation.id in self.typedef_map: + resolved_str: str = self._get_type_str(self.typedef_map[annotation.id], embedded=embedded) + return resolved_str + # 检查 t 类型别名(from t import CInt as myint) + if annotation.id in self._t_alias_map: + t_cls: type = self._t_alias_map[annotation.id] + llvm: str | None = CTypeRegistry.CTypeToLLVM(t_cls) + if llvm: + return llvm + sha1: str | None = self.struct_sha1_map.get(annotation.id, self.module_sha1) + sname: str = f'%"{sha1}.{annotation.id}"' if sha1 else f'%struct.{annotation.id}' + return f'{sname}*' + elif isinstance(annotation, ast.Attribute): + attr_name: str = annotation.attr if hasattr(annotation, 'attr') else '' + module_name: str = '' + if hasattr(annotation, 'value'): + if isinstance(annotation.value, ast.Name): + module_name = annotation.value.id + elif isinstance(annotation.value, ast.Attribute) and hasattr(annotation.value, 'attr'): + module_name = annotation.value.attr + if (module_name == 't' and attr_name == 'State') or attr_name == 'State': + return '' + if (module_name == 't' and attr_name == 'Callable') or attr_name == 'Callable': + return 'i8*' + llvm_type: str | None = CTypeRegistry.NameToLLVM(attr_name) + if llvm_type is not None: + return llvm_type + resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(attr_name) + if resolved is not None: + ctype_cls: type + ptr_level: int + ctype_cls, ptr_level = resolved + base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if ptr_level > 0: + if base == 'void': + return 'i8*' + if '*' in base: + return base + return f'{base}*' + return base + if attr_name in self.typedef_map: + return self._get_type_str(self.typedef_map[attr_name], embedded=embedded) + if attr_name in self.enum_names: + return 'i32' + if attr_name in self.struct_names: + sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) + sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' + if embedded: + return sname + else: + return f'{sname}*' + if attr_name and attr_name[0].isupper() and attr_name not in non_struct_types: + sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) + sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' + return f'{sname}*' + if attr_name and attr_name not in non_struct_types: + sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) + sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' + return f'{sname}*' + return 'i32' + elif isinstance(annotation, ast.BinOp): + left_type: str = self._get_type_str(annotation.left, embedded=embedded) + right_type: str = self._get_type_str(annotation.right, embedded=embedded) + if left_type in ('', 'void'): + return right_type if right_type not in ('', 'void') else (left_type or right_type) + if right_type in ('', 'void'): + return left_type + # t.CPtr (i8*) 与 left 合并:对齐 Phase 2 _MergeAllComponentTypes 的 + # "first CPtr absorption" 语义:struct | t.CPtr → struct*(吸收,struct 变量默认是指针)。 + # 但对于本身已是指针的类型(如 bytes=str=i8*),bytes | t.CPtr → i8**(不吸收)。 + # 例外:泛型特化类型(含 '[')回退为 i8*,避免引用未定义的特化结构体。 + if right_type == 'i8*': + if '[' in left_type: + return 'i8*' + if left_type in ('', 'void'): + return 'i8*' + if left_type.startswith('%"') and left_type.endswith('*'): + return left_type + if '*' in left_type: + return f'{left_type}*' + return f'{left_type}*' + if '*' in left_type: + return left_type + if '*' in right_type: + return right_type + return left_type + elif isinstance(annotation, ast.Subscript): + base: str = self._get_type_str(annotation.value) + if base == 'i8*' and isinstance(annotation.slice, ast.Constant): + return f'[{self._get_const_int(annotation.slice)} x i8]' + # 处理 t.CPtr[Type] 语法(指向 Type 的指针) + # 例如: t.CPtr[t.CPtr] -> i8** (void**), t.CPtr[t.CInt] -> i32* (int*) + # t.CPtr[t.CPtr[t.CInt]] -> i32** (int**) + ValueIsCPtr: bool = ( + (isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CPtr') + or (isinstance(annotation.value, ast.Name) and annotation.value.id == 'CPtr') + ) + if ValueIsCPtr and isinstance(annotation.slice, (ast.Attribute, ast.Name, ast.Subscript)): + slice_type: str = self._get_type_str(annotation.slice, embedded=True) + if slice_type and slice_type != 'void': + return f'{slice_type}*' + return 'i8*' + if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int): + return f'[{annotation.slice.value} x {base}]' + # 处理 t.CArray[elem_type, count] 注解 → [count x elem_type] + # 支持 t.CArray (ast.Attribute) 和 CArray (ast.Name) 两种写法 + ValueIsCArray: bool = ( + (isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CArray') + or (isinstance(annotation.value, ast.Name) and annotation.value.id == 'CArray') + ) + if ValueIsCArray: + SliceNode: ast.AST = annotation.slice + if isinstance(SliceNode, ast.Tuple) and len(SliceNode.elts) == 2: + ElemType: str = self._get_type_str(SliceNode.elts[0], embedded=True) + CountVal: int = self._get_const_int(SliceNode.elts[1]) + if CountVal > 0: + return f'[{CountVal} x {ElemType}]' + # count 为 None 或 0 → 零长度数组(外部符号) + return f'[0 x {ElemType}]' + elif isinstance(SliceNode, (ast.Attribute, ast.Name, ast.Subscript)): + # 单参数 t.CArray[elem_type] → 指针模式 + ElemType: str = self._get_type_str(SliceNode, embedded=True) + return f'{ElemType}*' + if isinstance(annotation.value, ast.Name) and annotation.value.id == 'tuple': + slice_node: ast.AST = annotation.slice + elem_types: list[str] + if isinstance(slice_node, ast.Tuple): + elem_types = [self._get_type_str(e, embedded=True) for e in slice_node.elts] + else: + elem_types = [self._get_type_str(slice_node, embedded=True)] + return '{ ' + ', '.join(elem_types) + ' }' + # 处理泛型类类型注解(如 list[str]) + if isinstance(annotation.value, ast.Name): + gc_name: str = annotation.value.id + if gc_name in non_struct_types and gc_name != 'tuple': + gc_slice: ast.AST = annotation.slice + gc_args: list[str] = [] + if isinstance(gc_slice, ast.Name): + gc_args.append(gc_slice.id) + elif isinstance(gc_slice, ast.Tuple): + gc_valid: bool = True + for gc_elt in gc_slice.elts: + if isinstance(gc_elt, ast.Name): + gc_args.append(gc_elt.id) + else: + gc_valid = False + break + if not gc_valid: + gc_args = [] + if gc_args: + gc_mangled: list[str] = [] + for gc_ta in gc_args: + gc_llvm: str | None = CTypeRegistry.NameToLLVM(gc_ta) + if gc_llvm: + if gc_llvm in ('float', 'double', 'half', 'fp128'): + gc_cls: type | None = CTypeRegistry.GetClassByName(gc_ta) + gc_sz: int = gc_cls().Size if gc_cls else 0 + gc_mangled.append('f' + str(gc_sz)) + else: + gc_mangled.append(gc_llvm) + else: + gc_mangled.append(gc_ta) + gc_spec: str = gc_name + '[' + ']['.join(gc_mangled) + ']' + gc_sha1: str | None = self.module_sha1 + gc_sname: str = f'%"{gc_sha1}.{gc_spec}"' if gc_sha1 else f'%struct.{gc_spec}' + if embedded: + return gc_sname + return f'{gc_sname}*' + return f'{base}' + elif isinstance(annotation, ast.Constant): + if annotation.value is None: + return 'void' + if isinstance(annotation.value, int): + return 'i32' + if isinstance(annotation.value, str): + type_name: str = annotation.value + if type_name in self.enum_names: + return 'i32' + if type_name in self.struct_names: + sha1: str | None = self.struct_sha1_map.get(type_name, self.module_sha1) + sname: str = f'%"{sha1}.{type_name}"' if sha1 else f'%struct.{type_name}' + if embedded: + return sname + else: + return f'{sname}*' + llvm_type: str | None = CTypeRegistry.NameToLLVM(type_name) + if llvm_type is not None: + return llvm_type + resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(type_name) + if resolved is not None: + ctype_cls: type + ptr_level: int + ctype_cls, ptr_level = resolved + base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if ptr_level > 0: + if base == 'void': + return 'i8*' + if '*' in base: + return base + return f'{base}*' + return base + sha1: str | None = self.struct_sha1_map.get(type_name, self.module_sha1) + sname: str = f'%"{sha1}.{type_name}"' if sha1 else f'%struct.{type_name}' + return f'{sname}*' + return 'i8*' + elif isinstance(annotation, ast.Call): + if isinstance(annotation.func, ast.Name) and annotation.func.id == 'callable': + return 'i8*' + if isinstance(annotation.func, ast.Attribute) and annotation.func.attr == 'Callable': + return 'i8*' + return 'i32' + + def _infer_type(self, value: ast.AST) -> str: + """从值推断类型""" + if isinstance(value, ast.Constant): + if isinstance(value.value, int): + return 'i32' + elif isinstance(value.value, float): + return 'double' + elif isinstance(value.value, str): + return 'i8*' + elif isinstance(value.value, bool): + return 'i8' + elif isinstance(value, ast.List): + return 'i8*' + elif isinstance(value, ast.Dict): + return 'i8*' + elif isinstance(value, ast.Name): + return 'i32' + elif isinstance(value, ast.BinOp): + return self._infer_type(value.left) + elif isinstance(value, ast.Call): + return 'i8*' + return 'i32' + + def _get_bitfield_width(self, annotation: ast.AST) -> int | None: + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + right_width: int | None = self._get_bitfield_width(annotation.right) + if right_width is not None: + return right_width + return self._get_bitfield_width(annotation.left) + if isinstance(annotation, ast.Call): + if isinstance(annotation.func, ast.Attribute) and annotation.func.attr == 'Bit': + if annotation.args: + arg: ast.AST = annotation.args[0] + if isinstance(arg, ast.Constant) and isinstance(arg.value, int): + return arg.value + if isinstance(annotation, ast.Subscript): + if isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'Bit': + if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int): + return annotation.slice.value + return None + + def _get_const_int(self, node: ast.AST) -> int: + """获取常量整数值,支持符号常量和简单表达式""" + if isinstance(node, ast.Constant) and isinstance(node.value, int): + return node.value + if isinstance(node, ast.Name): + if node.id in self._DefineConstants: + val: int | str = self._DefineConstants[node.id] + if isinstance(val, int): + return val + if isinstance(node, ast.BinOp): + left_val: int = self._get_const_int(node.left) + right_val: int = self._get_const_int(node.right) + if left_val and right_val: + if isinstance(node.op, ast.Add): + return left_val + right_val + if isinstance(node.op, ast.Sub): + return left_val - right_val + if isinstance(node.op, ast.Mult): + return left_val * right_val + if isinstance(node.op, ast.Div): + return left_val // right_val + if isinstance(node.op, ast.FloorDiv): + return left_val // right_val + return 0 diff --git a/App/lib/Projectrans/Phase1Generator.py b/App/lib/Projectrans/Phase1Generator.py new file mode 100644 index 0000000..c30ed83 --- /dev/null +++ b/App/lib/Projectrans/Phase1Generator.py @@ -0,0 +1,595 @@ +from __future__ import annotations + +import os +import ast +import json +import shutil +import traceback + +from lib.Projectrans.Utils import compute_sha1, get_file_dependencies, find_reachable_files_from_entries, parse_python_file +from lib.Projectrans.DeclarationGenerator import DeclarationGenerator +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _ConfigMode +from StubGen import PythonToStubConverter + +# 全局缓存目录(不被 --clean 清除),用于缓存库文件的 .pyi / .stub.ll / .doc.json +_COMPILER_ROOT: str = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +_GLOBAL_CACHE_DIR: str = os.path.join(_COMPILER_ROOT, '.transpyc_cache') + + +def _TryGlobalCache(Sha1: str, Ext: str, TempPath: str) -> bool: + """检查全局缓存中是否存在指定文件,若存在则复制到 temp_dir 并返回 True。""" + GlobalPath: str = os.path.join(_GLOBAL_CACHE_DIR, f"{Sha1}.{Ext}") + if os.path.isfile(GlobalPath): + try: + os.makedirs(os.path.dirname(TempPath), exist_ok=True) + shutil.copy2(GlobalPath, TempPath) + return True + except Exception: + pass + return False + + +def _SaveToGlobalCache(Sha1: str, Ext: str, TempPath: str) -> None: + """将文件保存到全局缓存供后续使用。""" + GlobalPath: str = os.path.join(_GLOBAL_CACHE_DIR, f"{Sha1}.{Ext}") + try: + os.makedirs(_GLOBAL_CACHE_DIR, exist_ok=True) + shutil.copy2(TempPath, GlobalPath) + except Exception: + pass + + +class Phase1Generator: + """阶段一:从源文件生成声明接口""" + + def __init__(self, src_root: str, temp_dir: str, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None) -> None: + self.src_root: str = os.path.abspath(src_root) + self.temp_dir: str = os.path.abspath(temp_dir) + self.include_dirs: list[str] = include_dirs or [] + self.sha1_map: dict[str, str] = {} + self.include_py_map: dict[str, str] = {} + self.entry_files: list[str] | None = entry_files + self.target_triple: str | None = target_triple + self.target_datalayout: str | None = target_datalayout + os.makedirs(self.temp_dir, exist_ok=True) + # 增量编译 manifest:{abs_path: {sha1, mtime, size}} + # 未变更文件(mtime+size 匹配)跳过 open+read+sha1,仅需 stat + self._manifest: dict[str, dict] = {} + self._typedef_map_cache: dict[str, ast.AST] | None = None + self._manifest_path: str = os.path.join(self.temp_dir, '_phase1_manifest.json') + self._load_manifest() + + def _load_manifest(self) -> None: + """加载上次运行的 manifest(mtime+size → sha1 缓存)""" + try: + if os.path.isfile(self._manifest_path): + with open(self._manifest_path, 'r', encoding='utf-8') as f: + self._manifest = json.load(f) + except Exception: + self._manifest = {} + + def _save_manifest(self) -> None: + """保存 manifest 供下次增量编译使用""" + try: + with open(self._manifest_path, 'w', encoding='utf-8') as f: + json.dump(self._manifest, f) + except Exception: + pass + + def _get_sha1(self, src_path: str) -> str: + """用 mtime+size 缓存加速 sha1 计算。 + + 未变更文件(mtime+size 匹配 manifest)直接返回缓存的 sha1, + 跳过 open+read+compute_sha1,仅需一次 stat 调用。 + """ + abs_path: str = os.path.abspath(src_path) + try: + st: os.stat_result = os.stat(abs_path) + except OSError: + return '' + mtime: float = st.st_mtime + size: int = st.st_size + entry: dict = self._manifest.get(abs_path, {}) + if entry.get('mtime') == mtime and entry.get('size') == size: + sha1: str = entry.get('sha1', '') + if sha1: + return sha1 + # mtime/size 不匹配,需读取内容重新计算 + try: + with open(abs_path, 'r', encoding='utf-8') as f: + content: str = f.read() + except Exception: + return '' + sha1 = compute_sha1(content) + self._manifest[abs_path] = {'sha1': sha1, 'mtime': mtime, 'size': size} + return sha1 + + def _get_needed_include_files(self, reachable_source_files: set[str]) -> list[tuple[str, str, str]]: + """从可达源文件收集所有被引用(含传递依赖)的 include 文件""" + include_file_map: dict[str, tuple[str, str, str]] = {} + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + for root, dirs, files in os.walk(includes_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith('.py') or fname.endswith('.pyi'): + src_path: str = os.path.join(root, fname) + rel_from_inc: str = os.path.relpath(src_path, includes_dir) + ModulePath: str = rel_from_inc.replace(os.sep, '.').replace('/', '.') + module_name: str = os.path.splitext(ModulePath)[0] + info: tuple[str, str, str] = (src_path, rel_from_inc, includes_dir) + include_file_map[module_name] = info + top_pkg: str = module_name.split('.')[0] + # __init__.py 是包入口,应优先作为 top_pkg 的代表 + # (参考 find_reachable_files_from_entries 的优先级逻辑), + # 否则若 os/path.py 先被遍历,'os' 会错误指向 path.py + # 而非 os/__init__.py,导致包入口 SHA1 未注册到 sha1_map + if module_name == f"{top_pkg}.__init__": + include_file_map[top_pkg] = info + elif top_pkg not in include_file_map: + include_file_map[top_pkg] = info + + imported_from_src: set[str] = set() + for src_path in reachable_source_files: + deps: set[str] = get_file_dependencies(src_path, self.src_root) + imported_from_src.update(deps) + + # 检测 dict/list 容器使用,自动添加 _dict/_list/json 依赖 + for src_path in reachable_source_files: + try: + # 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已解析过同一文件) + content, tree = parse_python_file(src_path) + if tree is None: + continue + for node in ast.walk(tree): + if isinstance(node, ast.Subscript) and isinstance(node.value, ast.Name): + if node.value.id == 'dict': + imported_from_src.add('_dict') + imported_from_src.add('json') + break + elif node.value.id == 'list': + imported_from_src.add('_list') + break + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): + if node.func.id == 'dict': + imported_from_src.add('_dict') + imported_from_src.add('json') + break + elif node.func.id == 'list': + imported_from_src.add('_list') + break + # 检测类型注解 d: dict / l: list (非泛型形式) + if isinstance(node, ast.AnnAssign) and isinstance(node.annotation, ast.Name): + if node.annotation.id == 'dict': + imported_from_src.add('_dict') + imported_from_src.add('json') + break + elif node.annotation.id == 'list': + imported_from_src.add('_list') + break + except Exception: + pass + + needed: set[str] = set() + queue: list[str] = list(imported_from_src) + while queue: + mod_name: str = queue.pop(0) + if mod_name in needed: + continue + if mod_name in include_file_map: + needed.add(mod_name) + src_path: str = include_file_map[mod_name][0] + includes_dir: str = include_file_map[mod_name][2] + deps: set[str] = get_file_dependencies(src_path, includes_dir) + for dep in deps: + if dep in include_file_map and dep not in needed: + queue.append(dep) + + needed_infos: list[tuple[str, str, str]] = [] + for mod_name in sorted(needed): + if mod_name in include_file_map: + info: tuple[str, str, str] = include_file_map[mod_name] + if info not in needed_infos: + needed_infos.append(info) + + return needed_infos + + def run(self) -> None: + """扫描源目录,只处理入口文件可达的 .py 文件(导入遍历)""" + py_files: list[str] + if self.entry_files: + py_files = list(self.entry_files) + else: + main_py: str = os.path.join(self.src_root, 'main.py') + if os.path.exists(main_py): + py_files = [main_py] + else: + py_files = [] + for root, dirs, files in os.walk(self.src_root): + dirs[:] = [d for d in dirs if d not in ('__pycache__',)] + for file in files: + if file.endswith('.py'): + py_files.append(os.path.join(root, file)) + + if not py_files: + _vlog().warning("未找到入口文件或源文件") + return + + reachable: set[str] + if self.entry_files: + reachable = find_reachable_files_from_entries(self.src_root, py_files) + else: + reachable = find_reachable_files_from_entries(self.src_root, py_files) + + _vlog().info(f"找到 {len(reachable)} 个可达源文件(从入口遍历)") + + for i, src_path in enumerate(sorted(reachable), 1): + rel: str = os.path.relpath(src_path, self.src_root) + _vlog().info(f"[{i}/{len(reachable)}] 生成签名: {rel}") + try: + self._process_file_pyi(src_path, rel) + except Exception as e: + _vlog().error(f"生成签名失败: {e}") + traceback.print_exc() + + needed_includes: list[tuple[str, str, str]] = self._get_needed_include_files(reachable) + self._process_include_py_files_pyi(needed_includes) + + struct_names: set[str] + enum_names: set[str] + struct_sha1_map: dict[str, str] + exception_names: set[str] + struct_names, enum_names, struct_sha1_map, exception_names, class_def_map = self._build_struct_registry() + _vlog().info(f"结构体注册表: {len(struct_names)} 个类型, {len(enum_names)} 个枚举") + for name in sorted(struct_names): + _vlog().debug(f" {name}") + + # 预先收集 typedef 映射,避免在 _process_file_stub 中对每个源文件重复扫描所有 .pyi + self._typedef_map_cache: dict[str, ast.AST] = self._collect_typedef_map() + + for i, src_path in enumerate(sorted(reachable), 1): + rel: str = os.path.relpath(src_path, self.src_root) + try: + self._process_file_stub(src_path, rel, struct_names, enum_names=enum_names, struct_sha1_map=struct_sha1_map, exception_names=exception_names, class_def_map=class_def_map) + except Exception as e: + _vlog().error(f"生成声明失败: {e}") + traceback.print_exc() + + self._process_include_py_files_stub(struct_names, needed_includes, enum_names=enum_names, struct_sha1_map=struct_sha1_map, exception_names=exception_names, class_def_map=class_def_map) + + # 保存 manifest 供下次增量编译使用 + self._save_manifest() + + _vlog().success(f"声明接口生成到: {self.temp_dir}") + _vlog().info(f"SHA1 映射表 ({len(self.sha1_map)} 个文件):") + for sha1, rel in sorted(self.sha1_map.items()): + _vlog().debug(f" {sha1} -> {rel}") + + def _collect_include_py_files(self) -> list[tuple[str, str, str]]: + include_py_files: list[tuple[str, str, str]] = [] + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + for root, dirs, files in os.walk(includes_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith('.py') or fname.endswith('.pyi'): + src_path: str = os.path.join(root, fname) + rel_from_inc: str = os.path.relpath(src_path, includes_dir) + include_py_files.append((src_path, rel_from_inc, includes_dir)) + return include_py_files + + def _process_include_py_files_pyi(self, needed_includes: list[tuple[str, str, str]] | None = None) -> None: + include_py_files: list[tuple[str, str, str]] + if needed_includes is not None: + include_py_files = needed_includes + else: + include_py_files = self._collect_include_py_files() + if not include_py_files: + return + _vlog().info(f"处理 {len(include_py_files)} 个被引用的 Python 库文件") + for src_path, rel_from_inc, includes_dir in include_py_files: + ModulePath: str = rel_from_inc.replace(os.sep, '.').replace('/', '.') + module_name: str = os.path.splitext(ModulePath)[0] + try: + # 增量优化:用 mtime+size 获取 sha1,未变更文件跳过 open+read + sha1: str = self._get_sha1(src_path) + if not sha1: + continue + self.sha1_map[sha1] = f"includes/{rel_from_inc}" + top_module: str = rel_from_inc.split(os.sep)[0].split('/')[0] + if top_module not in self.include_py_map: + self.include_py_map[top_module] = sha1 + self.include_py_map[module_name] = sha1 + + sig_path: str = os.path.join(self.temp_dir, f"{sha1}.pyi") + if os.path.isfile(sig_path): + _vlog().info(f" 缓存命中: {rel_from_inc} -> {sha1}.pyi") + continue + + # 全局缓存检查(不被 --clean 清除) + if _TryGlobalCache(sha1, 'pyi', sig_path): + _vlog().info(f" 缓存命中(全局): {rel_from_inc} -> {sha1}.pyi") + continue + + # 缓存未命中,需读取 content 生成 .pyi + with open(src_path, 'r', encoding='utf-8') as f: + content: str = f.read() + sig_content: str = PythonToStubConverter.convert(content, module_name) + with open(sig_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(sig_content) + _SaveToGlobalCache(sha1, 'pyi', sig_path) + _vlog().info(f" 生成签名: {rel_from_inc} -> {sha1}.pyi") + except Exception as e: + _vlog().error(f"处理 include 文件失败 {rel_from_inc}: {e}") + + def _collect_typedef_map(self) -> dict[str, ast.AST]: + typedef_map: dict[str, ast.AST] = {} + if not os.path.isdir(self.temp_dir): + return typedef_map + for fname in os.listdir(self.temp_dir): + if not fname.endswith('.pyi'): + continue + pyi_path: str = os.path.join(self.temp_dir, fname) + try: + with open(pyi_path, 'r', encoding='utf-8') as f: + content: str = f.read() + tree: ast.Module = ast.parse(content) + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + var_name: str = node.target.id + is_typedef: bool = False + if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef': + is_typedef = True + elif isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef': + is_typedef = True + elif isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef': + is_typedef = True + if is_typedef and node.value and var_name not in typedef_map: + typedef_map[var_name] = node.value + except Exception as _e: + if _ConfigMode == "strict": + raise + _vlog().warning(f"收集 typedef 映射失败: {_e}", "Exception") + return typedef_map + + def _process_include_py_files_stub(self, struct_names: set[str], needed_includes: list[tuple[str, str, str]] | None = None, enum_names: set[str] | None = None, struct_sha1_map: dict[str, str] | None = None, exception_names: set[str] | None = None, class_def_map: dict[str, ast.ClassDef] | None = None) -> None: + include_py_files: list[tuple[str, str, str]] + if needed_includes is not None: + include_py_files = needed_includes + else: + include_py_files = self._collect_include_py_files() + if not include_py_files: + return + typedef_map: dict[str, ast.AST] = self._collect_typedef_map() + for src_path, rel_from_inc, includes_dir in include_py_files: + ModulePath: str = rel_from_inc.replace(os.sep, '.').replace('/', '.') + module_name: str = os.path.splitext(ModulePath)[0] + try: + # 增量优化:用 mtime+size 获取 sha1,未变更文件跳过 open+read + sha1: str = self._get_sha1(src_path) + if not sha1: + continue + + stub_path: str = os.path.join(self.temp_dir, f"{sha1}.stub.ll") + if os.path.isfile(stub_path): + _vlog().info(f" 缓存命中: {rel_from_inc} -> {sha1}.stub.ll") + continue + + # 全局缓存检查(不被 --clean 清除) + if _TryGlobalCache(sha1, 'stub.ll', stub_path): + _vlog().info(f" 缓存命中(全局): {rel_from_inc} -> {sha1}.stub.ll") + continue + + sig_path: str = os.path.join(self.temp_dir, f"{sha1}.pyi") + if not os.path.isfile(sig_path): + _TryGlobalCache(sha1, 'pyi', sig_path) + with open(sig_path, 'r', encoding='utf-8') as f: + sig_content: str = f.read() + + self._generate_decl_ll(sig_content, stub_path, src_path, struct_names, enum_names=enum_names, module_sha1=sha1, struct_sha1_map=struct_sha1_map, exception_names=exception_names, typedef_map=typedef_map, class_def_map=class_def_map) + _SaveToGlobalCache(sha1, 'stub.ll', stub_path) + _vlog().info(f" 生成声明: {rel_from_inc} -> {sha1}.stub.ll") + except Exception as e: + _vlog().error(f"处理 include 文件失败 {rel_from_inc}: {e}") + + def _extract_docstrings(self, content: str) -> dict[str, str]: + """从源码 AST 提取所有函数/结构体/方法的首条裸字符串字面量 docstring。 + + - 顶层 FunctionDef: key = 函数名 + - 顶层 ClassDef: key = 类名;同时遍历方法,key = "ClassName.method" + - docstring 必须是 body[0] 为 ast.Expr + ast.Constant(str) + - 空字符串不计入 + """ + result: dict[str, str] = {} + try: + tree = ast.parse(content) + except SyntaxError: + return result + for node in tree.body: + if isinstance(node, ast.FunctionDef): + doc = self._get_first_docstring(node.body) + if doc is not None and doc != '': + result[node.name] = doc + elif isinstance(node, ast.ClassDef): + doc = self._get_first_docstring(node.body) + if doc is not None and doc != '': + result[node.name] = doc + for sub in node.body: + if isinstance(sub, ast.FunctionDef): + m_doc = self._get_first_docstring(sub.body) + if m_doc is not None and m_doc != '': + result[f"{node.name}.{sub.name}"] = m_doc + return result + + def _get_first_docstring(self, body: list[ast.stmt]) -> str | None: + """返回 body 首条裸字符串字面量,否则 None""" + if body and isinstance(body[0], ast.Expr) and isinstance(body[0].value, ast.Constant) and isinstance(body[0].value.value, str): + return body[0].value.value + return None + + def _process_file_pyi(self, src_path: str, rel_path: str) -> None: + # 增量优化:先用 mtime+size 获取 sha1,未变更文件跳过 open+read + sha1: str = self._get_sha1(src_path) + if not sha1: + return + self.sha1_map[sha1] = rel_path + + sig_path: str = os.path.join(self.temp_dir, f"{sha1}.pyi") + if os.path.isfile(sig_path): + _vlog().info(f" -> {sha1}.pyi (缓存)") + # 缓存命中时补全缺失的 .doc.json + doc_path: str = os.path.join(self.temp_dir, f"{sha1}.doc.json") + if not os.path.isfile(doc_path): + if not _TryGlobalCache(sha1, 'doc.json', doc_path): + # doc.json 缺失时需读 content 提取 docstring + with open(src_path, 'r', encoding='utf-8') as f: + content: str = f.read() + docs: dict[str, str] = self._extract_docstrings(content) + try: + with open(doc_path, 'w', encoding='utf-8', newline='\n') as f: + json.dump(docs, f, ensure_ascii=False) + _SaveToGlobalCache(sha1, 'doc.json', doc_path) + except Exception as e: + _vlog().warning(f"补写 {sha1}.doc.json 失败: {e}") + return + + # 全局缓存检查 + if _TryGlobalCache(sha1, 'pyi', sig_path): + _vlog().info(f" -> {sha1}.pyi (全局缓存)") + doc_path: str = os.path.join(self.temp_dir, f"{sha1}.doc.json") + if not os.path.isfile(doc_path): + _TryGlobalCache(sha1, 'doc.json', doc_path) + return + + # 缓存未命中,需读取 content 生成 .pyi + with open(src_path, 'r', encoding='utf-8') as f: + content = f.read() + + module_name: str = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + sig_content: str = PythonToStubConverter.convert(content, module_name) + with open(sig_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(sig_content) + _SaveToGlobalCache(sha1, 'pyi', sig_path) + _vlog().info(f" -> {sha1}.pyi (签名)") + + # 提取 docstring 写入 {sha1}.doc.json,供 Phase2 跨模块 __doc__ 加载 + docs: dict[str, str] = self._extract_docstrings(content) + doc_path: str = os.path.join(self.temp_dir, f"{sha1}.doc.json") + try: + with open(doc_path, 'w', encoding='utf-8', newline='\n') as f: + json.dump(docs, f, ensure_ascii=False) + _SaveToGlobalCache(sha1, 'doc.json', doc_path) + except Exception as e: + _vlog().warning(f"写入 {sha1}.doc.json 失败: {e}") + + def _process_file_stub(self, src_path: str, rel_path: str, struct_names: set[str], enum_names: set[str] | None = None, struct_sha1_map: dict[str, str] | None = None, exception_names: set[str] | None = None, class_def_map: dict[str, ast.ClassDef] | None = None) -> None: + # 增量优化:用 mtime+size 获取 sha1,未变更文件跳过 open+read + sha1: str = self._get_sha1(src_path) + if not sha1: + return + stub_path: str = os.path.join(self.temp_dir, f"{sha1}.stub.ll") + if os.path.isfile(stub_path): + _vlog().info(f" -> {sha1}.stub.ll (缓存)") + return + + sig_path: str = os.path.join(self.temp_dir, f"{sha1}.pyi") + with open(sig_path, 'r', encoding='utf-8') as f: + sig_content: str = f.read() + + typedef_map: dict[str, ast.AST] = self._typedef_map_cache or self._collect_typedef_map() + self._generate_decl_ll(sig_content, stub_path, src_path, struct_names, enum_names=enum_names, module_sha1=sha1, struct_sha1_map=struct_sha1_map, exception_names=exception_names, typedef_map=typedef_map, class_def_map=class_def_map) + _vlog().info(f" -> {sha1}.stub.ll (声明)") + + def _build_struct_registry(self) -> tuple[set[str], set[str], dict[str, str], set[str], dict[str, ast.ClassDef]]: + struct_names: set[str] = set() + enum_names: set[str] = set() + exception_names: set[str] = set() + struct_sha1_map: dict[str, str] = {} + class_def_map: dict[str, ast.ClassDef] = {} + valid_sha1_keys: set[str] = set(self.sha1_map.keys()) + for fname in os.listdir(self.temp_dir): + if not fname.endswith('.pyi'): + continue + sha1_key: str = fname.replace('.pyi', '') + if sha1_key not in valid_sha1_keys: + continue + pyi_path: str = os.path.join(self.temp_dir, fname) + try: + with open(pyi_path, 'r', encoding='utf-8') as f: + content: str = f.read() + tree: ast.Module = ast.parse(content) + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.ClassDef): + is_enum: bool = False + is_renum: bool = False + is_exception: bool = False + if node.bases: + for base in node.bases: + if isinstance(base, ast.Attribute) and hasattr(base, 'attr'): + if base.attr == 'REnum': + is_renum = True + break + elif base.attr in ('CEnum', 'Enum'): + is_enum = True + break + elif base.attr == 'Exception' or base.attr in exception_names: + is_exception = True + break + elif isinstance(base, ast.Name) and hasattr(base, 'id'): + if base.id == 'REnum': + is_renum = True + break + elif base.id in ('CEnum', 'Enum'): + is_enum = True + break + elif base.id == 'Exception' or base.id in exception_names: + is_exception = True + break + # 也检查装饰器形式 @t.CEnum / @t.REnum + if not is_enum and not is_renum and not is_exception and hasattr(node, 'decorator_list') and node.decorator_list: + for deco in node.decorator_list: + deco_attr: str | None = None + if isinstance(deco, ast.Attribute) and hasattr(deco, 'attr'): + deco_attr = deco.attr + elif isinstance(deco, ast.Name) and hasattr(deco, 'id'): + deco_attr = deco.id + if deco_attr == 'REnum': + is_renum = True + break + elif deco_attr in ('CEnum', 'Enum'): + is_enum = True + break + # REnum 是结构体类型(带 __tag + payload),需加入 struct_names 以便 + # DeclarationGenerator._get_type_str 返回 %Name* 而非 i32 + if is_renum: + struct_names.add(node.name) + struct_sha1_map[node.name] = sha1_key + elif is_enum: + enum_names.add(node.name) + elif is_exception: + exception_names.add(node.name) + else: + struct_names.add(node.name) + struct_sha1_map[node.name] = sha1_key + # 收集所有类(含枚举/异常)的 ClassDef 节点,供 DeclarationGenerator + # 跨模块字段展平使用(如 exprs.py 的 Name(AST) 需查找 base.py 的 AST) + class_def_map[node.name] = node + except Exception as _e: + if _ConfigMode == "strict": + raise + _vlog().warning(f"构建结构体/枚举注册表失败: {_e}", "Exception") + return struct_names, enum_names, struct_sha1_map, exception_names, class_def_map + + def _generate_decl_ll(self, pyi_content: str, ll_path: str, src_path: str, struct_names: set[str] | None = None, enum_names: set[str] | None = None, module_sha1: str | None = None, struct_sha1_map: dict[str, str] | None = None, exception_names: set[str] | None = None, typedef_map: dict[str, ast.AST] | None = None, class_def_map: dict[str, ast.ClassDef] | None = None) -> None: + try: + decl_gen: DeclarationGenerator = DeclarationGenerator(struct_names=struct_names, enum_names=enum_names, module_sha1=module_sha1, target_triple=self.target_triple, target_datalayout=self.target_datalayout, struct_sha1_map=struct_sha1_map, exception_names=exception_names, typedef_map=typedef_map, class_def_map=class_def_map) + decl_ll: str = decl_gen.generate(pyi_content, src_path) + with open(ll_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(decl_ll) + except Exception as e: + _vlog().warning(f".ll 声明生成失败: {e}") + with open(ll_path, 'w', encoding='utf-8') as f: + f.write(f"; declaration for {os.path.basename(src_path)}\n; error: {e}\n") diff --git a/App/lib/Projectrans/Phase2Translator.py b/App/lib/Projectrans/Phase2Translator.py new file mode 100644 index 0000000..5c1e300 --- /dev/null +++ b/App/lib/Projectrans/Phase2Translator.py @@ -0,0 +1,3056 @@ +from __future__ import annotations + +import sys +import os +import re +import shutil +import subprocess +import ast +import traceback +import json +import pickle +import time +from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.core.CTypeInfo import CTypeInfo +from lib.includes import t +from lib.core.SymbolUtils import AnnotationContainsName +from lib.Projectrans.Utils import compute_sha1, _check_annotation_for_export, find_reachable_files_from_entries, topological_sort_files, parse_python_file +from lib.Projectrans.Phase1Generator import _TryGlobalCache, _SaveToGlobalCache, _GLOBAL_CACHE_DIR +from StubGen import PythonToStubConverter + + +def _ExtractCDefineConstantsFromPyi(pyi_path: str, all_dc: dict[str, object]) -> None: + """从 .pyi 文件中提取 CDefine 常量到 all_dc。文件不存在时静默跳过。""" + if not os.path.exists(pyi_path): + return + try: + # 复用 parse_python_file 的 AST 缓存(.pyi 文件被多处解析) + _, tree = parse_python_file(pyi_path) + if tree is None: + return + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + if AnnotationContainsName(node.annotation, 'CDefine') and node.value: + val = None + if isinstance(node.value, ast.Constant): + val = node.value.value + elif isinstance(node.value, ast.Call) and isinstance(node.value.func, ast.Name) and node.value.func.id == 'float' and node.value.args and isinstance(node.value.args[0], ast.Constant): + val = node.value.args[0].value + if val is not None: + all_dc[f"{node.target.id}"] = val + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + + +def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp_dir: str, output_dir: str, + include_dirs: list[str], triple: str | None, datalayout: str | None, + sha1_map: dict[str, str], sig_files: dict[str, str], + stub_files: dict[str, str], include_py_map: dict[str, str], slice_level: int, + shared_sym_pickle_path: str, struct_sha1_map: dict[str, str], + prebuilt_ModuleSha1Map: dict[str, str] | None = None) -> tuple[str, str, str]: + try: + trans = Phase2Translator(src_root, temp_dir, output_dir, + compile_cmd='llc', include_dirs=include_dirs, + target_triple=triple, target_datalayout=datalayout) + trans.sha1_map = sha1_map + trans.sig_files = sig_files + trans.stub_files = stub_files + trans.include_py_map = include_py_map + trans.slice_level = slice_level + trans.struct_sha1_map = struct_sha1_map + + if shared_sym_pickle_path and os.path.exists(shared_sym_pickle_path): + # 使用 pickle.load(f) 流式加载,避免 pickle.loads(f.read()) 一次性 + # 将整个 pickle 文件读入内存(磁盘副本 + 反序列化对象同时占用内存)导致 MemoryError。 + # 同时提高递归限制,防止深层嵌套对象反序列化失败。 + sys.setrecursionlimit(max(sys.getrecursionlimit(), 100000)) + with open(shared_sym_pickle_path, 'rb') as f: + shared_data = pickle.load(f) + trans._shared_symbol_table = shared_data['symbol_table'] + trans._shared_source_module_sig_files = shared_data['source_module_sig_files'] + trans._shared_all_dc = shared_data['all_dc'] + trans._shared_export_extern_funcs = shared_data['export_extern_funcs'] + trans.inline_func_symbols = shared_data['inline_func_symbols'] + trans.function_default_args = shared_data['function_default_args'] + trans.function_param_names = shared_data.get('function_param_names', {}) + trans._shared_generic_class_templates = shared_data.get('generic_class_templates', {}) + trans._shared_doc_meta = shared_data.get('shared_doc_meta', {}) + trans._shared_class_vtable = shared_data.get('class_vtable', set()) + trans._shared_cross_module_vtable = shared_data.get('cross_module_vtable', set()) + trans._shared_class_methods = shared_data.get('class_methods', {}) + trans._shared_cross_module_novtable = shared_data.get('cross_module_novtable', set()) + else: + trans._precollect_inline_symbols() + trans._build_shared_symbol_data() + + trans._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=prebuilt_ModuleSha1Map) + return ('ok', src_path, '') + except Exception as e: + return ('error', src_path, f'{e}\n{traceback.format_exc()}') + + +def _count_braces_outside_strings(text: str) -> int: + """统计花括号差值,跳过 c"..." 字符串字面量内部的括号""" + count = 0 + i = 0 + while i < len(text): + if text[i:i+2] == 'c"' and (i == 0 or text[i-1] != '\\'): + # 跳过 c"..." 字面量 + i += 2 + while i < len(text): + if text[i] == '"' and (i == 0 or text[i-1] != '\\'): + i += 1 + break + i += 1 + continue + if text[i] == '{': + count += 1 + elif text[i] == '}': + count -= 1 + i += 1 + return count + + +class Phase2Translator: + """阶段二:使用声明接口翻译源文件""" + + INCLUDE_LIB_EXTENSIONS: tuple[str, ...] = ('.dll', '.ll', '.so', '.o', '.a', '.lib') + INCLUDE_SRC_EXTENSIONS: tuple[str, ...] = ('.c', '.cpp', '.cc', '.cxx', '.m', '.mm') + INCLUDE_DIRS: list[str] = [ + os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'includes'), + ] + + def __init__(self, src_root: str, temp_dir: str, output_dir: str, compile_cmd: str = 'llc', compile_flags: list[str] | None = None, linker_cmd: str | None = None, linker_flags: list[str] | None = None, linker_output: str | None = None, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None, force_recompile_includes: bool = False) -> None: + self.src_root = os.path.abspath(src_root) + self.temp_dir = os.path.abspath(temp_dir) + self.output_dir = os.path.abspath(output_dir) + self.compile_cmd = compile_cmd + self.compile_flags = compile_flags or ['-filetype=obj'] + self.triple = target_triple + self.datalayout = target_datalayout + if not self.triple: + for i, flag in enumerate(self.compile_flags): + if flag.startswith('-mtriple='): + self.triple = flag[len('-mtriple='):] + elif flag == '-mtriple' and i + 1 < len(self.compile_flags): + self.triple = self.compile_flags[i + 1] + self.linker_cmd = linker_cmd + self.linker_flags = linker_flags or [] + self.linker_output = linker_output + self.slice_level = 3 + self.sha1_map: dict[str, str] = {} + self.sig_files: dict[str, str] = {} + self.stub_files: dict[str, str] = {} + self.include_py_map: dict[str, str] = {} + self.used_includes: set[str] = set() + self._last_ErrorStack: list | None = None + self.function_default_args: dict[str, list] = {} + self.function_param_names: dict[str, list] = {} + self.inline_func_symbols: dict[str, CTypeInfo] = {} + self.extra_link_files: list[str] = [] + self.extra_compile_files: list[str] = [] + self.extra_py_files: list[str] = [] + self._include_sha1s: set[str] = set() + # 持有 ProcessPoolExecutor 引用,避免局部变量 GC 时 finalizer 阻塞 ~99s + # (join_executor_internals 在 GC 时同步等待 worker 退出队列) + self._executor_pool: ProcessPoolExecutor | None = None + self.entry_files: list[str] | None = entry_files + self.force_recompile_includes: bool = force_recompile_includes + self._shared_symbol_table: object | None = None + self._shared_source_module_sig_files: dict[str, str] = {} + self._shared_all_dc: dict[str, dict] = {} + self._shared_export_extern_funcs: dict[str, str] = {} + self.include_dirs: list[str] = [] + self.struct_sha1_map: dict[str, str] = {} + self._shared_generic_class_templates: dict[str, dict] = {} + self._shared_doc_meta: dict[str, dict[str, str]] = {} + # 共享 vtable 状态跨模块(修复 pool.alloc(48) 虚表分派失败导致段错误) + self._shared_class_vtable: set[str] = set() + self._shared_cross_module_vtable: set[str] = set() + self._shared_class_methods: dict[str, list[str]] = {} + self._shared_cross_module_novtable: set[str] = set() + default_dirs = [d for d in self.INCLUDE_DIRS if os.path.isdir(d)] + if include_dirs: + seen = set() + self.include_dirs = [] + for d in list(include_dirs) + default_dirs: + d = os.path.abspath(d) + if d not in seen and os.path.isdir(d): + self.include_dirs.append(d) + seen.add(d) + else: + self.include_dirs = default_dirs + self._LoadSha1Map() + + def _LoadSha1Map(self) -> None: + """从 temp_dir 加载 SHA1 映射、签名文件和 stub 声明文件列表。 + temp_dir 中缺失的文件会从全局缓存补充。 + """ + if not os.path.exists(self.temp_dir): + _vlog().error(f"声明目录不存在: {self.temp_dir}") + return + + for fname in os.listdir(self.temp_dir): + fpath = os.path.join(self.temp_dir, fname) + if fname.startswith('_'): + continue + if fname.endswith('.pyi'): + sha1 = fname[:-4] + self.sig_files[sha1] = fpath + elif fname.endswith('.stub.ll'): + sha1 = fname[:-8] + self.stub_files[sha1] = fpath + + # 从全局缓存补充 temp_dir 中缺失的 .pyi / .stub.ll + if os.path.isdir(_GLOBAL_CACHE_DIR): + for fname in os.listdir(_GLOBAL_CACHE_DIR): + if fname.startswith('_'): + continue + if fname.endswith('.pyi'): + sha1 = fname[:-4] + if sha1 not in self.sig_files: + self.sig_files[sha1] = os.path.join(_GLOBAL_CACHE_DIR, fname) + elif fname.endswith('.stub.ll'): + sha1 = fname[:-8] + if sha1 not in self.stub_files: + self.stub_files[sha1] = os.path.join(_GLOBAL_CACHE_DIR, fname) + + sha1_file_path = os.path.join(self.temp_dir, '_sha1_map.txt') + if os.path.exists(sha1_file_path): + with open(sha1_file_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if ':' in line: + sha1, rel = line.split(':', 1) + self.sha1_map[sha1] = rel + if rel.startswith('includes/'): + module_name = os.path.splitext(os.path.basename(rel))[0] + self.include_py_map[module_name] = sha1 + else: + for sha1, stub_path in self.stub_files.items(): + self.sha1_map[sha1] = sha1 + + def _build_struct_sha1_map(self) -> dict[str, str]: + struct_sha1_map = {} + valid_sha1_keys = set(self.sha1_map.keys()) + # 收集 temp_dir 和全局缓存中的 .pyi 文件 + pyi_dirs: list[str] = [self.temp_dir] + if os.path.isdir(_GLOBAL_CACHE_DIR): + pyi_dirs.append(_GLOBAL_CACHE_DIR) + for scan_dir in pyi_dirs: + if not os.path.isdir(scan_dir): + continue + for fname in os.listdir(scan_dir): + if not fname.endswith('.pyi'): + continue + sha1_key = fname.replace('.pyi', '') + if sha1_key not in valid_sha1_keys: + continue + if sha1_key in struct_sha1_map: + continue + pyi_path = os.path.join(scan_dir, fname) + # 复用 parse_python_file 的 AST 缓存(.pyi 文件可能被多处解析) + _, tree = parse_python_file(pyi_path) + if tree is None: + continue + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + struct_sha1_map[node.name] = sha1_key + return struct_sha1_map + + def run(self) -> None: + """扫描源目录,翻译并生成 .ll 含代码文件(导入遍历)""" + import sys as _sys + if self.entry_files: + py_files = list(self.entry_files) + else: + main_py = os.path.join(self.src_root, 'main.py') + if os.path.exists(main_py): + py_files = [main_py] + else: + py_files = [] + for root, dirs, files in os.walk(self.src_root): + dirs[:] = [d for d in dirs if d not in ('__pycache__',)] + for file in files: + if file.endswith('.py'): + py_files.append(os.path.join(root, file)) + + if not py_files: + _vlog().info("未找到入口文件或源文件") + return + + reachable = find_reachable_files_from_entries(self.src_root, py_files) + py_files = list(reachable) + + py_files = topological_sort_files(py_files, self.src_root) + + _vlog().info(f"找到 {len(py_files)} 个可达源文件(已按依赖排序)") + _vlog().info("处理顺序:") + for i, f in enumerate(py_files[:10], 1): + rel = os.path.relpath(f, self.src_root) + _vlog().info(f" {i}. {rel}") + os.makedirs(self.output_dir, exist_ok=True) + + valid_sha1s = set(self.sha1_map.keys()) + old_stub_count = len(self.stub_files) + old_sig_count = len(self.sig_files) + self.stub_files = {k: v for k, v in self.stub_files.items() if k in valid_sha1s} + self.sig_files = {k: v for k, v in self.sig_files.items() if k in valid_sha1s} + if old_stub_count != len(self.stub_files) or old_sig_count != len(self.sig_files): + _vlog().info(f"过滤旧文件: stub {old_stub_count}->{len(self.stub_files)}, sig {old_sig_count}->{len(self.sig_files)}") + + active_sha1s = set() + self._precollect_inline_symbols() + self._build_shared_symbol_data() + self._precollect_vtable_state() + self.struct_sha1_map = self._build_struct_sha1_map() + + need_translate = [] + for i, src_path in enumerate(py_files, 1): + rel = os.path.relpath(src_path, self.src_root) + # 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已 parse 过) + content, tree = parse_python_file(src_path) + sha1 = compute_sha1(content) + active_sha1s.add(sha1) + out_path = os.path.join(self.output_dir, f"{sha1}.ll") + + current_ModuleSha1Map = self._build_current_ModuleSha1Map(sha1) + + if tree is not None: + try: + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + self.used_includes.add(alias.name.split('.')[0]) + elif isinstance(node, ast.ImportFrom): + if node.module: + self.used_includes.add(node.module.split('.')[0]) + # 检测 list/dict 容器使用,自动添加 _list/_dict/json 到 used_includes + # 以便 _scan_include_libraries 能发现并编译这些库文件 + if isinstance(node, ast.Subscript) and isinstance(node.value, ast.Name): + if node.value.id == 'dict': + self.used_includes.add('_dict') + self.used_includes.add('json') + elif node.value.id == 'list': + self.used_includes.add('_list') + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): + if node.func.id == 'dict': + self.used_includes.add('_dict') + self.used_includes.add('json') + elif node.func.id == 'list': + self.used_includes.add('_list') + # 检测类型注解 d: dict / l: list (非泛型形式) + if isinstance(node, ast.AnnAssign) and isinstance(node.annotation, ast.Name): + if node.annotation.id == 'dict': + self.used_includes.add('_dict') + self.used_includes.add('json') + elif node.annotation.id == 'list': + self.used_includes.add('_list') + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"收集源文件导入的 include 模块失败: {_e}", "Exception") + + if os.path.isfile(out_path): + deps_changed = self._check_deps_changed(sha1, current_ModuleSha1Map) + if not deps_changed: + _vlog().info(f"[{i}/{len(py_files)}] 跳过(缓存): {rel} -> {sha1}.ll") + continue + else: + if self._recombine_ll(sha1, current_ModuleSha1Map): + _vlog().info(f"[{i}/{len(py_files)}] 重组(依赖变化): {rel} -> {sha1}.ll") + continue + _vlog().info(f"[{i}/{len(py_files)}] 重译(依赖变化): {rel} -> {sha1}.ll") + + else: + stub_cache = os.path.join(self.output_dir, f"{sha1}.stub.ll") + text_cache = os.path.join(self.output_dir, f"{sha1}.text.ll") + if os.path.isfile(stub_cache) and os.path.isfile(text_cache): + if self._recombine_ll(sha1, current_ModuleSha1Map): + _vlog().info(f"[{i}/{len(py_files)}] 重组(缓存恢复): {rel} -> {sha1}.ll") + continue + + need_translate.append((i, src_path, out_path, sha1, current_ModuleSha1Map)) + + if need_translate: + # 提前从 include 文件源码中提取函数参数名,供主项目编译时 keyword 参数匹配使用 + self._extract_include_param_names_from_dirs() + # 提前从 App 源文件中提取函数默认参数,供跨模块调用参数检查使用 + # (避免并行编译时,被调用函数的文件尚未编译完,导致默认参数信息缺失) + self._extract_app_default_args(need_translate) + + _vlog().info(f"\n需要翻译 {len(need_translate)} 个文件") + t_start = time.time() + + n_workers = min(8, len(need_translate)) + + if n_workers > 1 and len(need_translate) > 1: + _vlog().info(f" 并行翻译 (workers={n_workers})") + + shared_pickle_path = os.path.join(self.temp_dir, '_shared_sym.json') + try: + shared_data = { + 'symbol_table': self._shared_symbol_table, + 'source_module_sig_files': self._shared_source_module_sig_files, + 'all_dc': self._shared_all_dc, + 'export_extern_funcs': self._shared_export_extern_funcs, + 'inline_func_symbols': self.inline_func_symbols, + 'function_default_args': self.function_default_args, + 'function_param_names': self.function_param_names, + 'generic_class_templates': self._shared_generic_class_templates, + 'shared_doc_meta': self._shared_doc_meta, + 'class_vtable': self._shared_class_vtable, + 'cross_module_vtable': self._shared_cross_module_vtable, + 'class_methods': self._shared_class_methods, + 'cross_module_novtable': self._shared_cross_module_novtable, + } + with open(shared_pickle_path, 'wb') as f: + f.write(pickle.dumps(shared_data)) + _vlog().info(f" 共享符号表已序列化 ({os.path.getsize(shared_pickle_path)//1024}KB)") + except Exception as e: + _vlog().warning(f"符号表序列化失败({e}),worker将自行构建") + shared_pickle_path = '' + + errors = [] + # 复用 self._executor_pool:避免局部变量 GC 时 finalizer 阻塞 ~99s + # (join_executor_internals 在 GC 时同步等待 worker 退出队列) + if self._executor_pool is None: + self._executor_pool = ProcessPoolExecutor(max_workers=n_workers) + executor = self._executor_pool + try: + futures = {} + for i, src_path, out_path, sha1, msm in need_translate: + rel = os.path.relpath(src_path, self.src_root) + future = executor.submit( + _parallel_translate_worker, + src_path, out_path, + self.src_root, self.temp_dir, self.output_dir, + self.include_dirs, self.triple, self.datalayout, + self.sha1_map, self.sig_files, self.stub_files, + self.include_py_map, self.slice_level, + shared_pickle_path, self.struct_sha1_map, + prebuilt_ModuleSha1Map=msm + ) + futures[future] = (i, rel) + + done_count = 0 + for future in as_completed(futures): + i, rel = futures[future] + done_count += 1 + try: + status, _, err = future.result() + if status == 'ok': + _vlog().info(f" [{done_count}/{len(need_translate)}] 完成: {rel}") + else: + _vlog().error(f" {rel}: {err}") + errors.append((rel, err)) + except Exception as e: + _vlog().error(f" {rel}: {e}") + errors.append((rel, str(e))) + except Exception: + # 异常时立即清理,正常流程不 shutdown(留给 includes 阶段复用) + if self._executor_pool is not None: + self._executor_pool.shutdown(wait=False, cancel_futures=True) + self._executor_pool = None + raise + + if errors: + _vlog().error(f"\n{len(errors)} 个文件翻译失败,继续编译 includes") + else: + for idx, (i, src_path, out_path, sha1, msm) in enumerate(need_translate): + rel = os.path.relpath(src_path, self.src_root) + t0 = time.time() + try: + self._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=msm) + t1 = time.time() + _vlog().info(f" [{idx+1}/{len(need_translate)}] {rel} ({t1-t0:.1f}s)") + except Exception as e: + _vlog().error(f" {rel}: {e}") + traceback.print_exc() + t_end = time.time() + _vlog().info(f" 翻译总耗时: {t_end-t_start:.1f}s") + + _vlog().info(f"\n[阶段二完成] .ll 文件生成到: {self.output_dir}") + self._scan_include_libraries() + self._compile_include_py_files(active_sha1s) + self._compile_ll_files(active_sha1s) + self._compile_include_sources() + if self.linker_cmd: + self._link_obj_files(active_sha1s) + # 清理 ProcessPoolExecutor:所有翻译已完成,shutdown(wait=False) 让 worker + # 后台退出。避免 GC finalizer 在程序末尾阻塞 ~99s(join_executor_internals) + if self._executor_pool is not None: + self._executor_pool.shutdown(wait=False, cancel_futures=True) + self._executor_pool = None + + def _build_current_ModuleSha1Map(self, self_sha1: str | None) -> dict[str, str]: + """构建当前模块的依赖 SHA1 映射(与 _translate_file 中逻辑一致)""" + ModuleSha1Map = {} + for sha1_key, sig_path in self.sig_files.items(): + if sha1_key == self_sha1: + continue + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + inc_mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + inc_mod_name = inc_mod_name[len('includes/'):] + inc_short_name = inc_mod_name.split('.')[-1] if '.' in inc_mod_name else inc_mod_name + actual_sha1 = sha1_key + for inc_dir in self.include_dirs: + if os.path.isdir(inc_dir): + py_path = os.path.join(inc_dir, rel_path[len('includes/'):].replace(os.sep, '/')) + py_path = os.path.splitext(py_path)[0] + '.py' + if os.path.isfile(py_path): + with open(py_path, 'r', encoding='utf-8') as f: + py_sha1 = compute_sha1(f.read()) + actual_sha1 = py_sha1 + break + ModuleSha1Map[inc_mod_name] = actual_sha1 + ModuleSha1Map[inc_short_name] = actual_sha1 + # Also add parent package name (e.g., 'hashlib' for 'hashlib.__init__') + if inc_short_name == '__init__' and '.' in inc_mod_name: + parent_pkg = inc_mod_name.rsplit('.', 1)[0] + ModuleSha1Map[parent_pkg] = actual_sha1 + continue + module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + ModuleSha1Map[module_name] = sha1_key + short_name = module_name.split('.')[-1] if '.' in module_name else module_name + ModuleSha1Map[short_name] = sha1_key + # 包入口文件:注册父包名(如 'os.__init__' -> 'os'),使 import os 能解析到包级符号 + if short_name == '__init__' and '.' in module_name: + parent_pkg = module_name.rsplit('.', 1)[0] + ModuleSha1Map[parent_pkg] = sha1_key + return ModuleSha1Map + + @staticmethod + def _extract_llvm_type_name(line: str) -> str | None: + """从 LLVM IR 类型定义行提取类型名(字符串操作,不使用正则) + + 支持 list[str]、dict[str, int] 等含特殊字符的类型名。 + 例如: + %"18e6990aa81cc5b6.list[str]" = type { ... } → "18e6990aa81cc5b6.list[str]" + %struct.foo = type { ... } → "struct.foo" + """ + stripped = line.strip() + if not stripped.startswith('%'): + return None + # 处理带引号的情况: %"name" = type ... + if stripped.startswith('%"'): + end_quote = stripped.find('"', 2) + if end_quote > 0: + return stripped[2:end_quote] + return None + # 处理不带引号的情况: %name = type ... + eq_pos = stripped.find('=') + if eq_pos > 1: + return stripped[1:eq_pos].strip() + return None + + @staticmethod + def _find_all_type_refs(text: str) -> set[str]: + """查找文本中所有 %"..."/%name 类型引用(字符串操作,不使用正则) + + 支持 list[str]、dict[str, int] 等含特殊字符的类型名。 + """ + refs: set[str] = set() + i: int = 0 + n: int = len(text) + while i < n: + if text[i] == '%': + if i + 1 < n and text[i + 1] == '"': + # 带引号: %"name" + end: int = text.find('"', i + 2) + if end > 0: + name: str = text[i + 2:end] + if name: + refs.add(name) + i = end + 1 + continue + else: + # 不带引号: %name (仅匹配字母、数字、下划线、点) + j: int = i + 1 + while j < n and (text[j].isalnum() or text[j] in '._'): + j += 1 + if j > i + 1: + refs.add(text[i + 1:j]) + i = j + continue + i += 1 + return refs + + @staticmethod + def _split_ll(ll_content: str) -> tuple[str, str]: + """将 .ll 内容拆分为 stub(声明)和 text(代码)两部分 + + stub: target, 注释, %type = type, declare, @xxx = external global + text: define ... { ... }, @xxx = global/constant (带初始化器) + """ + stub_lines = [] + text_lines = [] + in_define = False + brace_depth = 0 + module_sha1 = None + defined_names = set() + + for line in ll_content.splitlines(True): + stripped = line.strip() + + if stripped.startswith('define '): + dm = re.match(r'define\s+[^@]*@"?([a-f0-9]+)\.', stripped) + if dm and module_sha1 is None: + module_sha1 = dm.group(1) + dm2 = re.match(r'define\s+[^@]*@"?([^"\s(]+)"?', stripped) + if dm2: + defined_names.add(dm2.group(1)) + + in_global_def = False + global_var_name = None + global_type_parts = [] + global_brace_depth = 0 + + for line in ll_content.splitlines(True): + stripped = line.strip() + + if in_global_def: + text_lines.append(line) + global_type_parts.append(stripped) + global_brace_depth += _count_braces_outside_strings(stripped) + if global_brace_depth <= 0: + full_type = ' '.join(global_type_parts).strip() + depth = 0 + type_end = len(full_type) + for ci, cc in enumerate(full_type): + if cc in ('{', '['): + depth += 1 + elif cc in ('}', ']'): + depth -= 1 + elif depth == 0 and cc == ' ': + type_end = ci + break + var_type = full_type[:type_end].strip() + if var_type: + stub_lines.append(f'{global_var_name} = external global {var_type}\n') + in_global_def = False + global_var_name = None + global_type_parts = [] + global_brace_depth = 0 + continue + + if in_define: + text_lines.append(line) + brace_depth += _count_braces_outside_strings(stripped) + if brace_depth <= 0: + in_define = False + continue + + if stripped.startswith('define '): + text_lines.append(line) + in_define = True + brace_depth = _count_braces_outside_strings(stripped) + decl_line = re.sub(r'^define\s+', 'declare ', stripped) + decl_line = re.sub(r'\b(linkonce_odr|weak_odr|linkonce|weak|common|appending|internal|private)\s+', '', decl_line) + decl_line = re.sub(r'\balwaysinline\s+', '', decl_line) + paren_pos = decl_line.rfind(')') + if paren_pos >= 0: + decl_line = decl_line[:paren_pos + 1] + stub_lines.append(decl_line + '\n') + continue + + if stripped == '{' and text_lines and not in_define: + prev_stripped = text_lines[-1].strip() if text_lines else '' + if prev_stripped.startswith('define ') or prev_stripped.endswith('noredzone') or prev_stripped.endswith(')'): + text_lines.append(line) + brace_depth = 1 + in_define = True + if prev_stripped.startswith('define '): + decl_line = re.sub(r'^define\s+', 'declare ', prev_stripped) + decl_line = re.sub(r'\b(linkonce_odr|weak_odr|linkonce|weak|common|appending|internal|private)\s+', '', decl_line) + decl_line = re.sub(r'\balwaysinline\s+', '', decl_line) + paren_pos = decl_line.rfind(')') + if paren_pos >= 0: + decl_line = decl_line[:paren_pos + 1] + stub_lines.append(decl_line + '\n') + continue + + if stripped.startswith('@') and ' global ' in stripped and 'external global' not in stripped: + text_lines.append(line) + if ' internal ' not in stripped and ' private ' not in stripped: + name_match = re.match(r'(@\S+)', stripped) + if name_match: + var_name = name_match.group(1) + global_pos = stripped.find(' global ') + after_global = stripped[global_pos + 8:] + depth = 0 + type_end = len(after_global) + for ci, cc in enumerate(after_global): + if cc in ('{', '['): + depth += 1 + elif cc in ('}', ']'): + depth -= 1 + elif depth == 0 and cc == ' ': + type_end = ci + break + var_type = after_global[:type_end].strip() + brace_depth_gv = 0 + for cc in var_type: + if cc == '{': + brace_depth_gv += 1 + elif cc == '}': + brace_depth_gv -= 1 + if brace_depth_gv > 0: + in_global_def = True + global_var_name = var_name + global_type_parts = [var_type] + global_brace_depth = brace_depth_gv + elif var_type: + stub_lines.append(f'{var_name} = external global {var_type}\n') + continue + if stripped.startswith('@') and ' constant ' in stripped: + text_lines.append(line) + continue + + if stripped.startswith('declare'): + dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped) + if dm: + fname = dm.group(1) + if fname in defined_names: + stub_lines.append(line) + continue + dot_pos = fname.find('.') + if dot_pos > 0: + fname_sha1 = fname[:dot_pos] + if module_sha1 and fname_sha1 != module_sha1 and len(fname_sha1) >= 16: + continue + if re.match(r'^[a-f0-9]{16}\.', fname): + continue + stub_lines.append(line) + continue + + stub_lines.append(line) + + return ''.join(stub_lines), ''.join(text_lines) + + def _save_deps(self, sha1: str, ModuleSha1Map: dict[str, str]) -> None: + """保存依赖指纹到 .deps.json""" + deps_path = os.path.join(self.output_dir, f"{sha1}.deps.json") + deps = {} + for mod_name, dep_sha1 in ModuleSha1Map.items(): + deps[mod_name] = dep_sha1 + with open(deps_path, 'w', encoding='utf-8') as f: + json.dump(deps, f) + + def _LoadDeps(self, sha1: str) -> dict[str, str] | None: + """加载依赖指纹""" + deps_path = os.path.join(self.output_dir, f"{sha1}.deps.json") + if os.path.isfile(deps_path): + try: + with open(deps_path, 'r', encoding='utf-8') as f: + return json.load(f) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"加载依赖指纹 JSON 失败: {_e}", "Exception") + return None + + def _check_deps_changed(self, sha1: str, current_ModuleSha1Map: dict[str, str]) -> bool: + """检查依赖指纹是否变化""" + saved_deps = self._LoadDeps(sha1) + if saved_deps is None: + return True + for mod_name, dep_sha1 in current_ModuleSha1Map.items(): + if saved_deps.get(mod_name) != dep_sha1: + return True + for mod_name, dep_sha1 in saved_deps.items(): + if current_ModuleSha1Map.get(mod_name) != dep_sha1: + return True + return False + + def _recombine_ll(self, sha1: str, current_ModuleSha1Map: dict[str, str]) -> bool: + """依赖变化时重新组合 .ll:更新 .stub.ll 中的 SHA1 前缀,拼接 .stub.ll + .text.ll""" + saved_deps = self._LoadDeps(sha1) + if saved_deps is None: + return False + + stub_path = os.path.join(self.output_dir, f"{sha1}.stub.ll") + text_path = os.path.join(self.output_dir, f"{sha1}.text.ll") + ll_path = os.path.join(self.output_dir, f"{sha1}.ll") + + if not os.path.isfile(stub_path) or not os.path.isfile(text_path): + return False + + with open(stub_path, 'r', encoding='utf-8') as f: + stub_content = f.read() + with open(text_path, 'r', encoding='utf-8') as f: + text_content = f.read() + + sha1_replacements = {} + for mod_name, old_sha1 in saved_deps.items(): + new_sha1 = current_ModuleSha1Map.get(mod_name) + if new_sha1 and old_sha1 != new_sha1: + sha1_replacements[old_sha1] = new_sha1 + + if sha1_replacements: + for old_sha1, new_sha1 in sha1_replacements.items(): + stub_content = stub_content.replace(old_sha1, new_sha1) + text_content = text_content.replace(old_sha1, new_sha1) + + combined = stub_content + if not combined.endswith('\n'): + combined += '\n' + combined += text_content + + with open(ll_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(combined) + with open(stub_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(stub_content) + with open(text_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(text_content) + + self._save_deps(sha1, current_ModuleSha1Map) + + obj_path = os.path.join(self.output_dir, f"{sha1}.obj") + if os.path.isfile(obj_path): + os.remove(obj_path) + + return True + + def _inject_auto_imports(self, code: str, src_path: str) -> str: + """检测源码中对 list/dict 容器的使用,自动注入 import _list / import _dict。 + 当使用 dict 时,同时注入 import json (用于 dict 的 JSON 处理功能)。 + + 检测模式: + - list[T] / dict[V] 作为类型注解 (ast.Subscript, value=Name(id='list'/'dict')) + - list[T](...) / dict[V](...) 作为构造函数调用 (ast.Call, func=Subscript) + - list(...) / dict(...) 作为构造函数调用 (ast.Call, func=Name) + """ + try: + tree = ast.parse(code) + except SyntaxError: + return code + + # 检查是否已经导入了 _list / _dict / json + has_list_import: bool = False + has_dict_import: bool = False + has_json_import: bool = False + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.Import): + for alias in node.names: + if alias.name == '_list': + has_list_import = True + elif alias.name == '_dict': + has_dict_import = True + elif alias.name == 'json': + has_json_import = True + elif isinstance(node, ast.ImportFrom): + if node.module == '_list': + has_list_import = True + elif node.module == '_dict': + has_dict_import = True + elif node.module == 'json': + has_json_import = True + if has_list_import and has_dict_import and has_json_import: + break + + # 检查是否使用了 list / dict 容器 + uses_list: bool = False + uses_dict: bool = False + for node in ast.walk(tree): + if isinstance(node, ast.Subscript): + if isinstance(node.value, ast.Name): + if node.value.id == 'list': + uses_list = True + elif node.value.id == 'dict': + uses_dict = True + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): + if node.func.id == 'list': + uses_list = True + elif node.func.id == 'dict': + uses_dict = True + # 检测类型注解 d: dict / l: list (非泛型形式) + if isinstance(node, ast.AnnAssign) and isinstance(node.annotation, ast.Name): + if node.annotation.id == 'dict': + uses_dict = True + elif node.annotation.id == 'list': + uses_list = True + + # 注入 import 到源码开头 + prefix: str = '' + if uses_list and not has_list_import: + prefix += 'import _list\n' + if uses_dict and not has_dict_import: + prefix += 'import _dict\n' + if uses_dict and not has_json_import: + prefix += 'import json\n' + if prefix: + return prefix + code + return code + + def _translate_file(self, src_path: str, out_path: str, prebuilt_ModuleSha1Map: dict[str, str] | None = None) -> None: + """翻译单个源文件,嵌入相关 .stub.ll 声明""" + with open(src_path, 'r', encoding='utf-8') as f: + code = f.read() + + # SHA1 必须在自动导入注入之前计算,以保持与 Phase1 生成的文件名/函数名前缀一致 + sha1 = compute_sha1(code) + + # 自动导入: 检测 list 容器使用,自动注入 import _list + code = self._inject_auto_imports(code, src_path) + + if prebuilt_ModuleSha1Map is not None: + ModuleSha1Map = prebuilt_ModuleSha1Map + else: + ModuleSha1Map = self._build_current_ModuleSha1Map(sha1) + + import TransPyC as _TransPyC + trans = _TransPyC.TransPyC(code=code, triple=self.triple, datalayout=self.datalayout) + trans.translator.CurrentFile = src_path + trans.SliceLevel = self.slice_level + trans.translator.SliceLevel = self.slice_level + trans.translator.SliceCount = 0 + trans.translator.SliceInfos = [] + trans.translator.LlvmGen = None + trans.translator._module_sha1 = sha1 + trans.translator._ModuleSha1Map = ModuleSha1Map + trans.translator._struct_sha1_map = self.struct_sha1_map + trans.translator._global_function_default_args = self.function_default_args + trans.translator._global_function_param_names = self.function_param_names + trans.translator._temp_dir = self.temp_dir + trans.translator._shared_doc_meta = self._shared_doc_meta + # 传递共享 vtable 状态 + trans.translator._shared_class_vtable = self._shared_class_vtable + trans.translator._shared_cross_module_vtable = self._shared_cross_module_vtable + trans.translator._shared_class_methods = self._shared_class_methods + trans.translator._shared_cross_module_novtable = self._shared_cross_module_novtable + + if self._shared_symbol_table is not None: + trans.translator.SymbolTable = self._shared_symbol_table + # 确保共享符号表的 translator 指向当前 translator,以便 import 别名解析正确 + if hasattr(self._shared_symbol_table, 'translator'): + self._shared_symbol_table.translator = trans.translator + trans.translator._source_module_sig_files = self._shared_source_module_sig_files + trans.translator._all_define_constants = self._shared_all_dc + trans.translator._export_extern_funcs = self._shared_export_extern_funcs + if self._shared_generic_class_templates: + if not hasattr(trans.translator.ClassHandler, '_generic_class_templates'): + trans.translator.ClassHandler._generic_class_templates = {} + trans.translator.ClassHandler._generic_class_templates.update(self._shared_generic_class_templates) + else: + export_extern_funcs = {} + for sha1_key, sig_path in self.sig_files.items(): + if sha1_key == sha1: + continue + try: + with open(sig_path, 'r', encoding='utf-8') as f: + sig_content = f.read() + sig_tree = ast.parse(sig_content) + for node in ast.iter_child_nodes(sig_tree): + if isinstance(node, ast.FunctionDef): + if _check_annotation_for_export(node.returns): + export_extern_funcs[node.name] = sha1_key + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"收集导出外部函数声明失败: {_e}", "Exception") + if export_extern_funcs: + trans.translator._export_extern_funcs = export_extern_funcs + + for includes_dir in self.include_dirs: + if os.path.isdir(includes_dir): + for pyi_file in os.listdir(includes_dir): + if pyi_file.endswith('.pyi'): + pyi_path = os.path.join(includes_dir, pyi_file) + module_name = os.path.splitext(pyi_file)[0] + trans.translator.SymbolTable.LoadModuleSymbols(pyi_path, module_name, lineno=0) + for py_file in os.listdir(includes_dir): + if py_file.endswith('.py') and (not py_file.startswith('_') or py_file == '_list.py' or py_file == '_dict.py'): + module_name = os.path.splitext(py_file)[0] + sha1_key = self.include_py_map.get(module_name) + if sha1_key and sha1_key in self.sig_files: + sig_path = self.sig_files[sha1_key] + trans.translator.SymbolTable.LoadModuleSymbols(sig_path, module_name, lineno=0) + # 收集需要重导出的包,等所有模块符号加载完后再处理 + _pending_reexports = [] + for sha1_key, sig_path in self.sig_files.items(): + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + mod_name = mod_name[len('includes/'):] + trans.translator.SymbolTable.LoadModuleSymbols(sig_path, mod_name, lineno=0) + if mod_name.endswith('.__init__'): + package_name = mod_name[:-len('.__init__')] + _pending_reexports.append((sig_path, package_name)) + + # 所有 includes 模块符号已加载,现在处理包重导出 + for sig_path, package_name in _pending_reexports: + self._register_package_reexports(trans, sig_path, package_name) + + for sha1_key, sig_path in self.sig_files.items(): + if sha1_key == sha1: + continue + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + continue + module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + trans.translator.SymbolTable.LoadModuleSymbols(sig_path, module_name, lineno=0) + trans.translator._source_module_sig_files[module_name] = sig_path + short_name = module_name.split('.')[-1] if '.' in module_name else module_name + if short_name != module_name: + trans.translator._source_module_sig_files[short_name] = sig_path + + all_dc = {} + for sha1_key, stub_path in self.stub_files.items(): + if sha1_key == sha1: + continue + if os.path.exists(stub_path): + try: + with open(stub_path, 'r', encoding='utf-8') as f: + stub_content = f.read() + for line in stub_content.splitlines(): + line = line.strip() + if line.startswith('@') and '= global' in line and 'i32' in line and not line.startswith('@llvm'): + var_name = line.split('=')[0].strip().lstrip('@') + val_part = line.split('i32')[-1].strip().rstrip(']').lstrip('[').lstrip('i32') + try: + val = int(val_part) + all_dc[var_name] = val + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + for sha1_key, pyi_path in self.sig_files.items(): + if sha1_key == sha1: + continue + _ExtractCDefineConstantsFromPyi(pyi_path, all_dc) + if all_dc: + trans.translator._all_define_constants = all_dc + + for sym_name, sym_info in self.inline_func_symbols.items(): + if sym_name not in trans.translator.SymbolTable: + trans.translator.SymbolTable[sym_name] = sym_info + else: + existing = trans.translator.SymbolTable[sym_name] + if not getattr(existing, 'InlineBody', None) and getattr(sym_info, 'InlineBody', None): + existing.IsInline = sym_info.IsInline + existing.InlineBody = sym_info.InlineBody + existing.InlineParams = sym_info.InlineParams + + try: + result = trans.Convert( + OutputFilename=out_path, + SourceFilename=src_path, + target='llvm' + ) + except Exception as e: + error_stack = getattr(trans.translator, '_ErrorStack', []) + if error_stack: + self._last_ErrorStack = error_stack + chain_lines = [] + for entry in reversed(error_stack): + if len(entry) >= 3: + exc_msg, line_info = entry[1], entry[2] + chain_lines.append(" %s\n %s" % (line_info, exc_msg)) + if chain_lines: + enriched = str(e) + "\n" + "\n".join(chain_lines) + raise type(e)(enriched) from e + node_info = "" + if hasattr(trans.translator, 'LlvmGen') and trans.translator.LlvmGen: + node_info = trans.translator.LlvmGen._get_node_info() + if node_info: + enriched = "%s\n %s" % (str(e), node_info.strip()) + raise type(e)(enriched) from e + raise + + if result and isinstance(result, str): + for func_name, func_def in trans.translator.FunctionDefCache.items(): + if hasattr(func_def, 'args'): + mangled = trans.translator.LlvmGen._mangle_func_name(func_name) if trans.translator.LlvmGen else func_name + # 提取参数名 + if hasattr(func_def.args, 'args'): + param_names = [arg.arg for arg in func_def.args.args] + self.function_param_names[mangled] = param_names + self.function_param_names[func_name] = param_names + # 提取默认值 + if hasattr(func_def.args, 'defaults') and func_def.args.defaults: + defaults = [] + for d in func_def.args.defaults: + if isinstance(d, ast.Constant): + defaults.append(d.value) + else: + defaults.append(None) + self.function_default_args[mangled] = defaults + self.function_default_args[func_name] = defaults + for sym_name, sym_info in trans.translator.SymbolTable.items(): + if isinstance(sym_info, CTypeInfo) and getattr(sym_info, 'IsInline', False) and getattr(sym_info, 'InlineBody', None): + self.inline_func_symbols[sym_name] = sym_info + + out_dir = os.path.dirname(out_path) + if out_dir: + os.makedirs(out_dir, exist_ok=True) + # 修复 LLVM 22+: external global 不能有初值,改为 linkonce + result = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', result, flags=re.MULTILINE) + # 后处理:添加 llvmlite 不支持的 LLVM 属性(如 willreturn, mustprogress) + pending_str_attrs = getattr(trans.translator.LlvmGen, '_pending_str_attrs', None) + if pending_str_attrs: + for func_name, attrs in pending_str_attrs.items(): + attrs_str = ' '.join(attrs) + # 匹配 define ... @func_name(...) ... { 并在 { 之前添加属性 + pattern = rf'(define\s+[^@]*@{re.escape(func_name)}\s*\([^)]*\))([^{{]*)(\{{)' + result, n = re.subn(pattern, rf'\1\2 {attrs_str}\3', result, count=1) + if n == 0: + _vlog().warning(f"后处理添加属性失败: 未找到函数 {func_name} 的定义") + with open(out_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(result) + + stub_content, text_content = self._split_ll(result) + base = os.path.splitext(out_path)[0] + with open(base + '.stub.ll', 'w', encoding='utf-8', newline='\n') as f: + f.write(stub_content) + with open(base + '.text.ll', 'w', encoding='utf-8', newline='\n') as f: + f.write(text_content) + self._save_deps(sha1, ModuleSha1Map) + + _vlog().info(f" -> {os.path.basename(out_path)}") + else: + stub_path = self.stub_files.get(sha1) + if stub_path and os.path.exists(stub_path): + out_dir = os.path.dirname(out_path) + if out_dir: + os.makedirs(out_dir, exist_ok=True) + with open(stub_path, 'r', encoding='utf-8') as f: + stub_content = f.read() + with open(out_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(stub_content) + _vlog().info(f" -> {os.path.basename(out_path)} (仅声明)") + + def _register_package_reexports(self, trans, init_pyi_path: str, package_name: str) -> None: + """解析 __init__.pyi 中的 from .xxx import yyy 或 from __xxx import yyy,在包级别注册导出符号""" + try: + # 复用 parse_python_file 的 AST 缓存 + _, tree = parse_python_file(init_pyi_path) + if tree is None: + return + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + sub_module = node.module.lstrip('.') + for alias in node.names: + symbol_name = alias.name + exported_name = alias.asname if alias.asname else symbol_name + source_keys = [ + f"{package_name}.{sub_module}.{symbol_name}", + f"{package_name}.__{sub_module}.{symbol_name}" if not sub_module.startswith('__') else None, + symbol_name, + ] + target_key = f"{package_name}.{exported_name}" + if target_key not in trans.translator.SymbolTable: + for source_key in source_keys: + if source_key and source_key in trans.translator.SymbolTable: + trans.translator.SymbolTable[target_key] = trans.translator.SymbolTable[source_key] + break + if exported_name not in trans.translator.SymbolTable: + for source_key in source_keys: + if source_key and source_key in trans.translator.SymbolTable: + trans.translator.SymbolTable[exported_name] = trans.translator.SymbolTable[source_key] + break + except Exception as e: + _vlog().warning(f" [警告] 解析包重导出失败 {init_pyi_path}: {e}") + + def _compile_ll_files(self, active_sha1s: set[str]) -> None: + """将 output_dir 中属于 active_sha1s 的 .ll 文件编译为 .obj(跳过已编译过的 include 文件)""" + # 第一次 walk:清理 stale 文件(.obj/.deps.json/纯 .ll,排除 .stub.ll/.text.ll) + stale_files = [] + for root, dirs, files in os.walk(self.output_dir): + for file in files: + if file.endswith('.obj'): + sha1 = file[:-4] + if sha1 not in active_sha1s: + stale_files.append(os.path.join(root, file)) + elif file.endswith('.deps.json'): + sha1 = file[:-10] + if sha1 not in active_sha1s: + stale_files.append(os.path.join(root, file)) + elif file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): + sha1 = file[:-3] + if sha1 not in active_sha1s: + stale_files.append(os.path.join(root, file)) + if stale_files: + for fpath in stale_files: + try: + os.remove(fpath) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + _vlog().info(f"[清理] 删除 {len(stale_files)} 个过时文件") + + # 第二次 walk:收集需要编译的 .ll 文件(排除 .stub.ll/.text.ll) + ll_files = [] + for root, dirs, files in os.walk(self.output_dir): + for file in files: + if file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): + sha1 = file[:-3] + if sha1 in active_sha1s: + fpath = os.path.join(root, file) + obj_path = os.path.join(root, file[:-3] + '.obj') + if not os.path.isfile(obj_path): + ll_files.append(fpath) + if not ll_files: + _vlog().info("[编译] 无 .ll 文件需要重新编译") + else: + _vlog().info(f"\n[编译] 找到 {len(ll_files)} 个 .ll 文件需要编译") + + # 第三次 walk:收集 all_ll_files(用于 alwaysinline 检测,排除 include sha1s) + all_ll_files = [] + for root, dirs, files in os.walk(self.output_dir): + for file in files: + if file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): + sha1 = file[:-3] + if sha1 in active_sha1s and sha1 not in self._include_sha1s: + all_ll_files.append(os.path.join(root, file)) + + has_inline = False + for ll_path in all_ll_files: + try: + with open(ll_path, 'r', encoding='utf-8') as f: + content = f.read() + if 'alwaysinline' in content: + has_inline = True + break + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + + if has_inline: + llvm_link = shutil.which('llvm-link') or shutil.which('llvm-link.exe') + opt_cmd = shutil.which('opt') or shutil.which('opt.exe') + if llvm_link and opt_cmd: + _vlog().info(" [内联] 检测到 alwaysinline 函数,执行跨模块内联优化...") + for ll_path in all_ll_files: + try: + with open(ll_path, 'r', encoding='utf-8') as f: + content = f.read() + type_defs = [] + type_def_indices = set() + for i, line in enumerate(content.splitlines()): + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + type_defs.append(line) + type_def_indices.add(i) + if type_def_indices: + lines = content.splitlines(True) + content_no_types = ''.join(line for i, line in enumerate(lines) if i not in type_def_indices) + header_end = 0 + for i, line in enumerate(content_no_types.splitlines()): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + header_end = i + 1 + continue + break + lines2 = content_no_types.splitlines(True) + fixed = ''.join(lines2[:header_end]) + '\n'.join(type_defs) + '\n' + ''.join(lines2[header_end:]) + with open(ll_path, 'w', encoding='utf-8') as f: + f.write(fixed) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + merged_ll = os.path.join(self.output_dir, '_merged.ll') + optimized_ll = os.path.join(self.output_dir, '_merged_opt.ll') + try: + link_cmd = [llvm_link] + all_ll_files + ['-o', merged_ll] + result = subprocess.run(link_cmd, capture_output=True, text=True) + if result.returncode != 0: + _vlog().warning(f" llvm-link 失败: {result.stderr.strip()},回退到单独编译") + has_inline = False + else: + opt_result_cmd = [opt_cmd, '-passes=always-inline', merged_ll, '-S', '-o', optimized_ll] + result = subprocess.run(opt_result_cmd, capture_output=True, text=True) + if result.returncode != 0: + _vlog().warning(f" opt 失败: {result.stderr.strip()},回退到单独编译") + has_inline = False + else: + _vlog().info(" [内联] 跨模块内联优化完成") + with open(optimized_ll, 'r', encoding='utf-8') as f: + opt_content = f.read() + opt_content = re.sub(r'^(define|declare)\s+(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|external)\s+', r'\1 \2 dso_local ', opt_content, flags=re.MULTILINE) + opt_content = re.sub(r'^(define|declare)\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|external|dso_local|@)', r'\1 dso_local ', opt_content, flags=re.MULTILINE) + # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 + opt_content = re.sub(r'(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', opt_content, flags=re.MULTILINE) + opt_content = re.sub(r'(@[\w.]+)\s*=\s*(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr)\s+(global|constant)', r'\1 = \2 dso_local \3', opt_content) + opt_content = re.sub(r'(@[\w.]+)\s*=\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|dso_local)(global|constant)', r'\1 = dso_local \2', opt_content) + with open(optimized_ll, 'w', encoding='utf-8') as f: + f.write(opt_content) + merged_obj = os.path.join(self.output_dir, '_merged.obj') + try: + cmd = [self.compile_cmd] + self.compile_flags + ['-o', merged_obj, optimized_ll] + result = subprocess.run(cmd, capture_output=True, text=True, + cwd=self.output_dir) + if result.returncode == 0: + _vlog().success(f" 合并模块编译完成") + for ll_path in ll_files: + obj_path = ll_path[:-3] + '.obj' + sha1_obj = os.path.join(self.output_dir, os.path.basename(ll_path)[:-3] + '.obj') + if os.path.isfile(sha1_obj): + os.remove(sha1_obj) + return + else: + _vlog().warning(f" 合并模块编译失败: {result.stderr.strip()},回退到单独编译") + has_inline = False + except Exception as e: + _vlog().warning(f" 合并模块编译异常: {e},回退到单独编译") + has_inline = False + except FileNotFoundError: + _vlog().warning(f" 找不到 llvm-link 或 opt,回退到单独编译") + has_inline = False + else: + _vlog().warning(" 未找到 llvm-link/opt 工具,回退到单独编译(alwaysinline 可能不生效)") + + def _compile_one(ll_path: str) -> tuple[str, str, str]: + rel = os.path.relpath(ll_path, self.output_dir) + obj_path = ll_path[:-3] + '.obj' + if os.path.isfile(obj_path): + return (rel, 'cached', '') + try: + with open(ll_path, 'r', encoding='utf-8') as f: + ll_content = f.read() + sha1 = os.path.basename(ll_path)[:-3] + deps = self._LoadDeps(sha1) + stub_prefix = '' + if deps: + # 去重:同一个全局变量如果同时有 external 声明和实际定义, + # 移除 external 声明,避免 "redefinition of global" 错误 + # 注意:此去重必须在收集现有符号之前执行,否则行号偏移会导致 + # lines_to_remove 误删其他行(如全局变量定义) + ll_lines_for_dedup = ll_content.splitlines() + external_globals: dict[str, int] = {} + non_external_globals: dict[str, int] = {} + for i, line in enumerate(ll_lines_for_dedup): + stripped = line.strip() + if stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): + gm = re.match(r'(@\S+)', stripped) + if gm: + gname = gm.group(1) + is_external = re.match(r'@\S+\s*=\s*external\s+', stripped) is not None + if is_external: + external_globals[gname] = i + else: + non_external_globals[gname] = i + lines_to_remove_global_dedup: set[int] = set() + for gname in external_globals: + if gname in non_external_globals: + lines_to_remove_global_dedup.add(external_globals[gname]) + if lines_to_remove_global_dedup: + ll_content = '\n'.join( + line for i, line in enumerate(ll_lines_for_dedup) + if i not in lines_to_remove_global_dedup + ) + '\n' + existing_symbols: dict[str, str] = {} + opaque_line_indices: dict[str, int] = {} + existing_declares: set[str] = set() + existing_declare_lines: dict[str, set[int]] = {} + for i, line in enumerate(ll_content.splitlines()): + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + name = Phase2Translator._extract_llvm_type_name(stripped) + if name: + if '= type opaque' in stripped: + existing_symbols[name] = 'opaque' + opaque_line_indices[name] = i + else: + existing_symbols[name] = 'full' + elif stripped.startswith('declare'): + dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped) + if dm: + fname = dm.group(1) + existing_declares.add(fname) + existing_declare_lines.setdefault(fname, set()).add(i) + seen_stub_sha1: set[str] = set() + replaced_opaque_names: set[str] = set() + replaced_declare_names: set[str] = set() + all_stub_lines: list[str | None] = [] + stub_type_map: dict[str, tuple[bool, int]] = {} + stub_declare_names: set[str] = set() + stub_declare_lines: dict[str, str] = {} + existing_defines: set[str] = set() + existing_global_defs: set[str] = set() + stub_global_defs: set[str] = set() + for line in ll_content.splitlines(): + stripped = line.strip() + if stripped.startswith('define'): + dm = re.match(r'define\s+[^@]*@"?([^"\s(]+)"?', stripped) + if dm: + existing_defines.add(dm.group(1)) + elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): + gm = re.match(r'(@\S+)', stripped) + if gm: + existing_global_defs.add(gm.group(1)) + for dep_name, dep_sha1 in deps.items(): + if dep_sha1 in seen_stub_sha1: + continue + seen_stub_sha1.add(dep_sha1) + stub_path = os.path.join(self.output_dir, f"{dep_sha1}.stub.ll") + if not os.path.isfile(stub_path): + stub_path = os.path.join(self.temp_dir, f"{dep_sha1}.stub.ll") + if os.path.isfile(stub_path): + with open(stub_path, 'r', encoding='utf-8') as sf: + stub_content = sf.read() + for line in stub_content.splitlines(): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + continue + if stripped.startswith('declare'): + dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped) + if dm: + fname = dm.group(1) + if 'zdef_alloc' in fname: + _vlog().debug(f"[DEBUG] stub declare for {fname}: existing_declares={fname in existing_declares}, stub_declare_names={fname in stub_declare_names}, has_struct={'%' in stripped}, existing={existing_declare_lines.get(fname)}") + if fname in existing_defines: + continue + if fname in existing_declares or fname in stub_declare_names: + existing_decl_line = stub_declare_lines.get(fname) + if existing_decl_line is not None: + has_struct = '%' in stripped + existing_has_struct = '%' in existing_decl_line if existing_decl_line else False + if has_struct and not existing_has_struct: + for i, l in enumerate(all_stub_lines): + if l is not None and l.strip().startswith('declare'): + em = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', l.strip()) + if em and em.group(1) == fname: + all_stub_lines[i] = line + break + continue + elif fname in existing_declares: + has_struct = '%' in stripped + if has_struct: + replaced_declare_names.add(fname) + stub_declare_names.add(fname) + stub_declare_lines[fname] = line + all_stub_lines.append(line) + continue + continue + stub_declare_names.add(fname) + stub_declare_lines[fname] = line + all_stub_lines.append(line) + continue + if stripped.startswith('%') and '= type' in stripped: + name = Phase2Translator._extract_llvm_type_name(stripped) + if name: + is_opaque = '= type opaque' in stripped + if name in existing_symbols: + if existing_symbols[name] == 'opaque' and not is_opaque: + replaced_opaque_names.add(name) + existing_symbols[name] = 'full' + idx = len(all_stub_lines) + all_stub_lines.append(line) + stub_type_map[name] = (is_opaque, idx) + continue + if name in stub_type_map: + prev_opaque, prev_idx = stub_type_map[name] + if prev_opaque and not is_opaque: + all_stub_lines[prev_idx] = None + idx = len(all_stub_lines) + all_stub_lines.append(line) + stub_type_map[name] = (is_opaque, idx) + continue + idx = len(all_stub_lines) + all_stub_lines.append(line) + stub_type_map[name] = (is_opaque, idx) + else: + all_stub_lines.append(line) + elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): + gm = re.match(r'(@\S+)', stripped) + if gm and (gm.group(1) in existing_global_defs or gm.group(1) in stub_global_defs): + continue + if gm: + stub_global_defs.add(gm.group(1)) + all_stub_lines.append(line) + else: + all_stub_lines.append(line) + filtered = [l for l in all_stub_lines if l is not None] + if filtered: + stub_prefix = '\n'.join(filtered) + '\n' + lines_to_remove = set() + if replaced_opaque_names: + for name in replaced_opaque_names: + if name in opaque_line_indices: + lines_to_remove.add(opaque_line_indices[name]) + if replaced_declare_names: + _vlog().debug(f"[DEBUG] replaced_declare_names: {replaced_declare_names}") + for fname in replaced_declare_names: + if fname in existing_declare_lines: + lines_to_remove.update(existing_declare_lines[fname]) + _vlog().debug(f"[DEBUG] removing lines {existing_declare_lines[fname]} for {fname}") + else: + _vlog().debug(f"[DEBUG] fname {fname} not in existing_declare_lines: {list(existing_declare_lines.keys())}") + if lines_to_remove: + ll_lines = ll_content.splitlines(True) + ll_content = ''.join(line for i, line in enumerate(ll_lines) if i not in lines_to_remove) + type_defs_in_ll = [] + type_def_line_indices = set() + for i, line in enumerate(ll_content.splitlines()): + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + type_defs_in_ll.append(line) + type_def_line_indices.add(i) + if type_def_line_indices: + ll_lines = ll_content.splitlines(True) + ll_content = ''.join(line for i, line in enumerate(ll_lines) if i not in type_def_line_indices) + if type_defs_in_ll: + stub_prefix += '\n'.join(type_defs_in_ll) + '\n' + if stub_prefix: + header_end = 0 + for i, line in enumerate(ll_content.splitlines()): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + header_end = i + 1 + continue + break + ll_lines = ll_content.splitlines(True) + ll_content = ''.join(ll_lines[:header_end]) + stub_prefix + ''.join(ll_lines[header_end:]) + ll_content = re.sub(r'^(define|declare)\s+(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|external)\s+', r'\1 \2 dso_local ', ll_content, flags=re.MULTILINE) + ll_content = re.sub(r'^(define|declare)\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|external|dso_local|@)', r'\1 dso_local ', ll_content, flags=re.MULTILINE) + # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 + ll_content = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', ll_content, flags=re.MULTILINE) + ll_content = re.sub(r'^(@[\w.]+)\s*=\s*(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr)\s+(global|constant)', r'\1 = \2 dso_local \3', ll_content, flags=re.MULTILINE) + ll_content = re.sub(r'^(@[\w.]+)\s*=\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|dso_local)(global|constant)', r'\1 = dso_local \2', ll_content, flags=re.MULTILINE) + # LLVM 22+: 移除同一模块中已有 define 的 declare 语句(LLVM 22 将 declare+define 视为重复定义) + defined_funcs: set[str] = set() + for m in re.finditer(r'^define\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(', ll_content, re.MULTILINE): + defined_funcs.add(m.group(1)) + if defined_funcs: + lines: list[str] = ll_content.splitlines(True) + kept_lines: list[str] = [] + for line in lines: + m = re.match(r'^declare\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(', line) + if m and m.group(1) in defined_funcs: + continue + kept_lines.append(line) + ll_content = ''.join(kept_lines) + # 移除重复的 declare 语句(同一函数多次 declare 但没有 define) + seen_declares: set[str] = set() + dedup_lines: list[str] = ll_content.splitlines(True) + dedup_kept: list[str] = [] + for line in dedup_lines: + dm = re.match(r'^declare\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(', line) + if dm: + fname = dm.group(1) + if fname in seen_declares: + continue + seen_declares.add(fname) + dedup_kept.append(line) + ll_content = ''.join(dedup_kept) + final_type_defs = [] + final_type_def_indices = set() + seen_type_names_main = set() + for i, line in enumerate(ll_content.splitlines()): + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + type_name_main = stripped.split('=')[0].strip() + final_type_def_indices.add(i) + if type_name_main in seen_type_names_main: + continue + seen_type_names_main.add(type_name_main) + final_type_defs.append(line) + if final_type_def_indices: + final_lines = ll_content.splitlines(True) + ll_content_no_types = ''.join(line for i, line in enumerate(final_lines) if i not in final_type_def_indices) + header_end = 0 + for i, line in enumerate(ll_content_no_types.splitlines()): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + header_end = i + 1 + continue + break + final_lines2 = ll_content_no_types.splitlines(True) + ll_content = ''.join(final_lines2[:header_end]) + '\n'.join(final_type_defs) + '\n' + ''.join(final_lines2[header_end:]) + dso_ll_path = ll_path[:-3] + '_dso.ll' + with open(dso_ll_path, 'w', encoding='utf-8') as f: + f.write(ll_content) + cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, dso_ll_path] + result = subprocess.run(cmd, capture_output=True, text=True, + cwd=os.path.dirname(ll_path) or '.') + if result.returncode == 0: + return (rel, 'ok', '') + else: + return (rel, 'error', result.stderr.strip()) + except FileNotFoundError: + return (rel, 'error', f'找不到编译器: {self.compile_cmd}') + except Exception as e: + return (rel, 'error', str(e)) + + need_compile = [] + for ll_path in ll_files: + obj_path = ll_path[:-3] + '.obj' + if not os.path.isfile(obj_path): + need_compile.append(ll_path) + + if need_compile: + n_workers = min(os.cpu_count() or 4, len(need_compile), 8) + _vlog().info(f" 并行编译 {len(need_compile)} 个文件 (workers={n_workers})") + errors = [] + with ThreadPoolExecutor(max_workers=n_workers) as executor: + futures = {executor.submit(_compile_one, ll_path): ll_path for ll_path in need_compile} + for future in as_completed(futures): + rel, status, err = future.result() + if status == 'ok': + _vlog().success(f" {rel}") + elif status == 'error': + _vlog().error(f" {rel}: {err}") + errors.append((rel, err)) + elif status == 'cached': + pass + if errors: + _vlog().error(f"\n{len(errors)} 个文件编译失败") + sys.exit(1) + + def _precollect_vtable_state(self) -> None: + """预扫描 includes .py 文件,收集 @t.CVTable 类的虚表信息。 + + 并行编译(ProcessPoolExecutor)创建独立内存的 worker 进程, + vtable 状态必须预收集并通过 pickle 共享。 + 复用 parse_python_file 的 AST 缓存。 + """ + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + for root, dirs, files in os.walk(includes_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if not fname.endswith('.py'): + continue + py_path: str = os.path.join(root, fname) + code, tree = parse_python_file(py_path) + # 字符串预过滤:不含 CVTable 的文件跳过 AST 遍历 + if tree is None or 'CVTable' not in code: + continue + try: + for node in ast.iter_child_nodes(tree): + if not isinstance(node, ast.ClassDef): + continue + has_cvtable: bool = False + for dec in node.decorator_list: + if isinstance(dec, ast.Attribute): + if getattr(dec.value, 'id', None) == 't' and dec.attr == 'CVTable': + has_cvtable = True + elif isinstance(dec, ast.Name): + if dec.id == 'CVTable': + has_cvtable = True + if not has_cvtable: + continue + class_name: str = node.name + has_methods: bool = False + if class_name not in self._shared_class_methods: + self._shared_class_methods[class_name] = [] + for item in node.body: + if isinstance(item, ast.FunctionDef): + has_methods = True + method_name: str = item.name + # 构造函数不放入 vtable(避免子类与基类 vtable 大小不一致破坏多态分派) + if method_name in ('__new__', '__init__', '__before_init__'): + continue + if method_name == '__init__': + func_full_name: str = f"{class_name}.__init__" + elif method_name == '__call__': + func_full_name = f"{class_name}.__call__" + else: + func_full_name = f"{class_name}.{method_name}" + if func_full_name not in self._shared_class_methods[class_name]: + self._shared_class_methods[class_name].append(func_full_name) + if has_methods: + self._shared_class_vtable.add(class_name) + self._shared_cross_module_vtable.add(class_name) + except Exception: + pass + + def _build_include_py_index(self, includes_dir: str) -> set[str]: + """预扫描 includes 目录,构建所有 .py 文件路径集合 + + 用 set 查询替代 os.path.isfile,避免 3490 次磁盘 stat(83s→<0.1s)。 + 同时规范化路径为小写(Windows 不区分大小写)以加速查找。 + """ + py_index: set[str] = set() + for root, dirs, files in os.walk(includes_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith('.py'): + py_index.add(os.path.normcase(os.path.join(root, fname))) + return py_index + + def _scan_include_libraries(self) -> None: + """扫描 includes 目录,检测被使用的模块对应的库文件和源文件""" + if not self.used_includes: + return + + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + + # 预构建 .py 文件索引,避免递归发现时反复 os.path.isfile + py_index = self._build_include_py_index(includes_dir) + + for module_name in self.used_includes: + module_dir = os.path.join(includes_dir, module_name) + if os.path.isdir(module_dir): + self._scan_dir_for_libs(module_dir, module_name) + for root, dirs, files in os.walk(module_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith('.py'): + py_path = os.path.join(root, fname) + if py_path not in self.extra_py_files: + self.extra_py_files.append(py_path) + _vlog().info(f" [includes] 发现 Python 包文件: {os.path.relpath(py_path, includes_dir)}") + + for ext in self.INCLUDE_LIB_EXTENSIONS: + lib_path = os.path.join(includes_dir, module_name + ext) + if os.path.isfile(lib_path): + self.extra_link_files.append(lib_path) + _vlog().info(f" [includes] 发现库文件: {os.path.relpath(lib_path, includes_dir)}") + + for ext in self.INCLUDE_SRC_EXTENSIONS: + src_path = os.path.join(includes_dir, module_name + ext) + if os.path.isfile(src_path): + self.extra_compile_files.append(src_path) + _vlog().info(f" [includes] 发现源文件: {os.path.relpath(src_path, includes_dir)}") + + py_path = os.path.join(includes_dir, module_name + '.py') + if os.path.normcase(py_path) in py_index: + self.extra_py_files.append(py_path) + _vlog().info(f" [includes] 发现 Python 源文件: {os.path.relpath(py_path, includes_dir)}") + + discovered = set() + for ef in list(self.extra_py_files): + self._discover_transitive_deps(ef, includes_dir, discovered, py_index) + + if self.extra_link_files: + _vlog().info(f"\n[includes] 需要链接的库文件: {len(self.extra_link_files)} 个") + if self.extra_compile_files: + _vlog().info(f"[includes] 需要编译的源文件: {len(self.extra_compile_files)} 个") + + def _scan_dir_for_libs(self, directory: str, module_name: str) -> None: + """递归扫描目录中的库文件和源文件""" + for root, dirs, files in os.walk(directory): + for fname in files: + fpath = os.path.join(root, fname) + _, ext = os.path.splitext(fname) + ext = ext.lower() + if ext in self.INCLUDE_LIB_EXTENSIONS: + self.extra_link_files.append(fpath) + _vlog().info(f" [includes] 发现库文件: {os.path.relpath(fpath, directory)}") + elif ext in self.INCLUDE_SRC_EXTENSIONS: + self.extra_compile_files.append(fpath) + _vlog().info(f" [includes] 发现源文件: {os.path.relpath(fpath, directory)}") + + def _discover_transitive_deps(self, py_path: str, includes_dir: str, discovered: set[str], py_index: set[str] | None = None) -> None: + """递归发现 Python 文件的间接依赖 + + 复用 parse_python_file 的 AST 缓存,避免重复 open+read+ast.parse。 + py_index 为预构建的 .py 文件路径集合,避免 os.path.isfile 磁盘 stat。 + """ + if py_path in discovered: + return + discovered.add(py_path) + _, tree = parse_python_file(py_path) + if tree is None: + return + try: + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + self._try_add_include_dep(alias.name, includes_dir, discovered, py_index) + elif isinstance(node, ast.ImportFrom): + if node.module and node.level == 0: + self._try_add_include_dep(node.module, includes_dir, discovered, py_index) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"发现 include 传递依赖失败: {_e}", "Exception") + + def _try_add_include_dep(self, module_name: str, includes_dir: str, discovered: set, py_index: set[str] | None = None): + """尝试将模块添加为 include 依赖(支持子包,如 w32.win32base) + + py_index 非空时用 set 查询替代 os.path.isfile(O(1) vs 24ms/次)。 + """ + # 候选路径:子包路径(w32/win32base.py)、顶层模块(w32.py)、子包 __init__.py + candidates: list[str] = [] + # 子包路径:w32.win32base → w32/win32base.py + candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep) + '.py')) + # 子包 __init__.py:w32.win32base → w32/win32base/__init__.py + candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep), '__init__.py')) + # 顶层模块:取第一个组件 → w32.py + top_module = module_name.split('.')[0] + if top_module != module_name: + candidates.append(os.path.join(includes_dir, top_module + '.py')) + + for py_path in candidates: + exists: bool + if py_index is not None: + exists = os.path.normcase(py_path) in py_index + else: + exists = os.path.isfile(py_path) + if exists and py_path not in self.extra_py_files: + self.extra_py_files.append(py_path) + _vlog().info(f" [includes] 发现间接依赖: {os.path.relpath(py_path, includes_dir)}") + self._discover_transitive_deps(py_path, includes_dir, discovered, py_index) + return # 找到第一个匹配即可 + + def _is_decl_only_file(self, src_path: str) -> bool: + """复用 parse_python_file 的 AST 缓存""" + _, tree = parse_python_file(src_path) + if tree is None: + return False + try: + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + return False + if isinstance(node, ast.FunctionDef): + body = node.body + if len(body) == 1: + stmt = body[0] + if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant) and stmt.value.value is ...: + continue + if isinstance(stmt, ast.Pass): + continue + return False + return True + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"判断是否为声明文件失败: {_e}", "Exception") + return False + + def _sort_include_files_by_deps(self, file_list: list[str]) -> tuple[list[str], dict[str, set[str]]]: + """按依赖关系对 include 文件进行拓扑排序,确保被依赖的文件先编译。 + + 返回 (排序后的文件列表, 依赖字典)。 + 依赖字典: {fpath: set(依赖的fpath)},仅包含 file_list 中的依赖。 + """ + # 构建文件名到路径的映射 + name_to_path = {} + for fpath in file_list: + basename = os.path.splitext(os.path.basename(fpath))[0] + name_to_path[basename] = fpath + + # 也支持包内模块: vpsdk/window -> path + for fpath in file_list: + # 尝试从 includes 目录推断模块全名 + for includes_dir in self.include_dirs: + try: + rel = os.path.relpath(fpath, includes_dir) + mod_name = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') + name_to_path[mod_name] = fpath + except ValueError: + pass + + # 分析每个文件的 import 依赖(复用 parse_python_file 的 AST 缓存) + deps = {} # path -> set of paths it depends on + for fpath in file_list: + deps[fpath] = set() + try: + _, tree = parse_python_file(fpath) + if tree is None: + continue + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.Import): + for alias in node.names: + # 尝试完整路径和各级前缀,确保同包内子模块依赖能被正确解析 + # 例如 import zlib.zhuff -> 应先匹配 zlib.zhuff,再回退到 zlib + parts = alias.name.split('.') + for i in range(len(parts), 0, -1): + mod = '.'.join(parts[:i]) + if mod in name_to_path and name_to_path[mod] != fpath: + deps[fpath].add(name_to_path[mod]) + break + elif isinstance(node, ast.ImportFrom): + if node.module and node.level == 0: + # 绝对导入:同样尝试完整路径和各级前缀 + parts = node.module.split('.') + for i in range(len(parts), 0, -1): + mod = '.'.join(parts[:i]) + if mod in name_to_path and name_to_path[mod] != fpath: + deps[fpath].add(name_to_path[mod]) + break + elif node.level > 0: + # 相对导入:from .xxx import yyy 或 from ..xxx import yyy + # 根据当前文件位置推断目标模块的完整路径 + current_dir = os.path.dirname(fpath) + # node.level == 1 表示当前包,node.level == 2 表示上一级包 + package_dir = current_dir + for _ in range(node.level - 1): + package_dir = os.path.dirname(package_dir) + # 推断当前包名(从 includes 目录的相对路径) + package_name: str | None = None + for includes_dir in self.include_dirs: + try: + rel = os.path.relpath(package_dir, includes_dir) + if rel and rel != '.': + package_name = rel.replace(os.sep, '.').replace('/', '.') + break + except ValueError: + pass + # 构建目标模块名并查找 + target_mod: str | None = None + if node.module: + target_mod = f"{package_name}.{node.module}" if package_name else node.module + else: + # from . import yyy:yyy 是子模块,在 names 中 + target_mod = package_name + if target_mod: + parts = target_mod.split('.') + for i in range(len(parts), 0, -1): + mod = '.'.join(parts[:i]) + if mod in name_to_path and name_to_path[mod] != fpath: + deps[fpath].add(name_to_path[mod]) + break + # from . import yyy:yyy 可能是子模块 + if node.level > 0 and not node.module: + for alias in node.names: + sub_mod = f"{package_name}.{alias.name}" if package_name else alias.name + if sub_mod in name_to_path and name_to_path[sub_mod] != fpath: + deps[fpath].add(name_to_path[sub_mod]) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"分析 include 文件导入依赖失败: {_e}", "Exception") + + # 拓扑排序 (Kahn's algorithm) + # in_degree 只计入 file_list 中的依赖——decl-only 文件(如 stdint.py) + # 不在 file_list 中,不应计入入度,否则依赖它们的文件入度永远不为 0, + # 被误判为循环依赖通过 fallback 添加,导致编译顺序错误。 + file_set = set(file_list) + in_degree = {fpath: sum(1 for dep in deps[fpath] if dep in file_set) for fpath in file_list} + # 反向邻接表:如果 A 依赖 B,则 B -> A + reverse_adj = {fpath: [] for fpath in file_list} + for fpath in file_list: + for dep in deps[fpath]: + if dep in reverse_adj: + reverse_adj[dep].append(fpath) + + result = [] + queue = [fpath for fpath in file_list if in_degree[fpath] == 0] + # 对队列排序以保持确定性 + queue.sort(key=lambda x: x) + + while queue: + current = queue.pop(0) + result.append(current) + for neighbor in reverse_adj[current]: + in_degree[neighbor] -= 1 + if in_degree[neighbor] == 0: + queue.append(neighbor) + queue.sort(key=lambda x: x) + + # 如果有循环依赖,把剩余文件也加入 + for fpath in file_list: + if fpath not in result: + result.append(fpath) + + # 仅保留 file_list 内的依赖(用于波次并行翻译) + file_set_result = set(file_list) + deps_in_set = {fpath: (deps[fpath] & file_set_result) for fpath in file_list} + return result, deps_in_set + + def _get_precompiled_obj_path(self, src_path: str, sha1: str) -> str | None: + """计算 include 文件在 includes.binary/ 中的预编译 .obj 路径。 + + 预编译目录位于 include 目录同级,命名为 {basename}.binary/。 + 文件命名为 {sha1}.{triple_safe}.{rel_model}.obj,区分目标平台和重定位模型。 + """ + AbsSrcPath: str = os.path.abspath(src_path) + RelModel: str = 'static' + for _flag in self.compile_flags: + if _flag.startswith('-relocation-model='): + RelModel = _flag[len('-relocation-model='):] + break + for inc_dir in self.include_dirs: + abs_inc: str = os.path.abspath(inc_dir) + try: + rel: str = os.path.relpath(AbsSrcPath, abs_inc) + if rel.startswith('..'): + continue + inc_parent: str = os.path.dirname(abs_inc) + inc_base: str = os.path.basename(abs_inc) + precompiled_dir: str = os.path.join(inc_parent, inc_base + '.binary') + triple_safe: str = (self.triple or 'default').replace(os.sep, '_').replace('/', '_').replace('\\', '_').replace(':', '_') + return os.path.join(precompiled_dir, f"{sha1}.{triple_safe}.{RelModel}.obj") + except ValueError: + continue + return None + + def _compile_include_py_files(self, active_sha1s: set[str]) -> None: + """通过 TransPyC 翻译器编译 includes 目录中发现的 .py 文件 + + 优化流程:预注册符号 → 并行翻译 → 串行后处理 → 并行编译 → 注册+缓存 + """ + if not self.extra_py_files: + return + + # 按依赖关系排序 include 文件,确保被依赖的文件先编译 + sorted_files, file_deps = self._sort_include_files_by_deps(self.extra_py_files) + _vlog().info(f"\n[includes 编译] 找到 {len(sorted_files)} 个 Python 源文件") + + # 构建 include 文件的模块 SHA1 映射(预计算所有文件,构建完整映射) + include_ModuleSha1Map = self._build_current_ModuleSha1Map(None) + need_translate = [] # [(src_path, sha1, module_name, ll_path, obj_path, precompiled_obj)] + for src_path in sorted_files: + module_name = os.path.splitext(os.path.basename(src_path))[0] + # 复用 parse_python_file 缓存的 content,避免重复 open+read + py_content, _ = parse_python_file(src_path) + sha1 = compute_sha1(py_content) + self.include_py_map[module_name] = sha1 + active_sha1s.add(sha1) + self._include_sha1s.add(sha1) + + for includes_dir in self.include_dirs: + try: + rel = os.path.relpath(src_path, includes_dir) + mod_full = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') + include_ModuleSha1Map[mod_full] = sha1 + include_ModuleSha1Map[module_name] = sha1 + # 包入口文件:注册父包名(如 'os.__init__' -> 'os') + if module_name == '__init__' and '.' in mod_full: + parent_pkg = mod_full.rsplit('.', 1)[0] + include_ModuleSha1Map[parent_pkg] = sha1 + except ValueError: + pass + include_ModuleSha1Map[module_name] = sha1 + ll_path = os.path.join(self.output_dir, f"{sha1}.ll") + obj_path = os.path.join(self.output_dir, f"{sha1}.obj") + precompiled_obj = self._get_precompiled_obj_path(src_path, sha1) + + if self._is_decl_only_file(src_path): + _vlog().info(f" 跳过(声明文件): {module_name}.py") + # 声明文件(如 stdint.py)虽不编译为 .obj,但其 typedef/类型别名 + # 必须注册到共享符号表,否则依赖它的 include 文件编译时 + # 类型解析失败(如 UINT32PTR 被当作命名结构体而非 i32*) + self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) + self._collect_inline_symbols(src_path) + continue + + # 优先检查 includes.binary 预编译缓存(跨项目共享) + if not self.force_recompile_includes and precompiled_obj and os.path.isfile(precompiled_obj): + _vlog().info(f" 跳过(预编译): {module_name}.py -> {os.path.basename(precompiled_obj)}") + # 预编译跳过的文件仍需注册 .pyi/.stub.ll 声明和符号, + # 否则依赖它的 include 文件(如 vpui.py import vpsdk.window) + # 在 _EmitModuleDeclarationsLlvm 中找不到声明,导致 Undefined symbol + self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) + self._collect_inline_symbols(src_path) + self.extra_link_files.append(precompiled_obj) + continue + + if not self.force_recompile_includes and os.path.isfile(obj_path): + _vlog().info(f" 跳过(缓存): {module_name}.py -> {sha1}.obj") + self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) + self._collect_inline_symbols(src_path) + self.extra_link_files.append(obj_path) + continue + + need_translate.append((src_path, sha1, module_name, ll_path, obj_path, precompiled_obj)) + + if not need_translate: + return + + # 预注册所有待翻译文件的符号(从 Phase1 缓存加载 .pyi / .stub.ll) + # 这样并行翻译时所有跨模块符号都已可用 + for src_path, sha1, module_name, ll_path, obj_path, precompiled_obj in need_translate: + self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) + + # 预收集 stub_decls(所有文件共用,避免重复扫描 .stub.ll) + stub_decls = [] + for other_sha1, other_stub in self.stub_files.items(): + if os.path.exists(other_stub): + try: + with open(other_stub, 'r', encoding='utf-8') as sf: + for sline in sf: + sl = sline.strip() + if sl.startswith('%') and '= type' in sl and 'opaque' not in sl: + struct_name = Phase2Translator._extract_llvm_type_name(sl) + if struct_name: + stub_decls.append(sl) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + all_type_defs = {} + for sdecl in stub_decls: + tname = Phase2Translator._extract_llvm_type_name(sdecl) + if tname: + all_type_defs[tname] = sdecl + + # ========== 阶段1:并行翻译 ========== + _vlog().info(f" 翻译 {len(need_translate)} 个文件") + if len(need_translate) > 1: + # 序列化共享符号表供 worker 使用 + shared_pickle_path = os.path.join(self.output_dir, '_shared_sym_includes.pickle') + try: + shared_data = { + 'symbol_table': self._shared_symbol_table, + 'source_module_sig_files': self._shared_source_module_sig_files, + 'all_dc': self._shared_all_dc, + 'export_extern_funcs': self._shared_export_extern_funcs, + 'inline_func_symbols': self.inline_func_symbols, + 'function_default_args': self.function_default_args, + 'function_param_names': self.function_param_names, + 'generic_class_templates': self._shared_generic_class_templates, + 'shared_doc_meta': self._shared_doc_meta, + 'class_vtable': self._shared_class_vtable, + 'cross_module_vtable': self._shared_cross_module_vtable, + 'class_methods': self._shared_class_methods, + 'cross_module_novtable': self._shared_cross_module_novtable, + } + with open(shared_pickle_path, 'wb') as f: + f.write(pickle.dumps(shared_data)) + except Exception as e: + _vlog().warning(f"符号表序列化失败({e}),include将串行翻译") + shared_pickle_path = '' + + if shared_pickle_path and os.path.exists(shared_pickle_path): + n_workers = min(8, len(need_translate)) + errors = [] + # 波次并行翻译:按依赖关系分批提交,确保被依赖的文件先翻译完成。 + # 根因:_TryLoadStructFromStub 扫描 output 目录加载跨模块 struct 定义, + # 若依赖文件尚未完成翻译(output stub 未写入),只能取到 Phase1 temp + # 中的不完整 stub(缺少跨模块继承字段),导致运行时崩溃。 + item_by_path = {item[0]: item for item in need_translate} + translate_paths = set(item_by_path.keys()) + # 每个文件的依赖(仅限本轮待翻译集合内) + translate_deps = { + p: (file_deps.get(p, set()) & translate_paths) + for p in translate_paths + } + # 复用 self._executor_pool(与 App 源文件阶段共享),避免 GC 阻塞 + if self._executor_pool is None: + self._executor_pool = ProcessPoolExecutor(max_workers=n_workers) + executor = self._executor_pool + try: + completed_paths: set[str] = set() + remaining = list(need_translate) # 保持拓扑序 + in_progress: dict = {} # future -> item + done_count = 0 + + while remaining or in_progress: + # 提交所有依赖已完成的文件 + ready = [ + item for item in remaining + if translate_deps[item[0]] <= completed_paths + ] + for item in ready: + remaining.remove(item) + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + future = executor.submit( + _parallel_translate_worker, + src_path, ll_path, + self.src_root, self.temp_dir, self.output_dir, + self.include_dirs, self.triple, self.datalayout, + self.sha1_map, self.sig_files, self.stub_files, + self.include_py_map, self.slice_level, + shared_pickle_path, self.struct_sha1_map, + include_ModuleSha1Map + ) + in_progress[future] = item + + if not in_progress: + # 无可提交且无进行中:循环依赖 fallback + for item in remaining: + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + future = executor.submit( + _parallel_translate_worker, + src_path, ll_path, + self.src_root, self.temp_dir, self.output_dir, + self.include_dirs, self.triple, self.datalayout, + self.sha1_map, self.sig_files, self.stub_files, + self.include_py_map, self.slice_level, + shared_pickle_path, self.struct_sha1_map, + include_ModuleSha1Map + ) + in_progress[future] = item + remaining.clear() + + # 等待至少一个完成 + done_set, _ = wait(in_progress, return_when=FIRST_COMPLETED) + for future in done_set: + item = in_progress.pop(future) + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + completed_paths.add(src_path) + done_count += 1 + try: + status, _, err = future.result() + if status == 'ok': + _vlog().info(f" [{done_count}/{len(need_translate)}] 翻译完成: {module_name}.py") + else: + _vlog().error(f" {module_name}.py: {err}") + errors.append((module_name, err)) + except Exception as e: + _vlog().error(f" {module_name}.py: {e}") + errors.append((module_name, str(e))) + except Exception: + if self._executor_pool is not None: + self._executor_pool.shutdown(wait=False, cancel_futures=True) + self._executor_pool = None + raise + + if errors: + _vlog().error(f"\n{len(errors)} 个 include 文件翻译失败") + sys.exit(1) + else: + # 串行回退 + for item in need_translate: + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + _vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll") + self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map) + else: + item = need_translate[0] + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + _vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll") + self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map) + + # ========== 阶段2:串行后处理(dso_local + stub_decls 注入)========== + compile_tasks = [] # [(dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)] + for item in need_translate: + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + if not os.path.isfile(ll_path): + _vlog().error(f" 翻译未生成 .ll 文件: {module_name}.py") + continue + + with open(ll_path, 'r', encoding='utf-8') as f: + ll_content = f.read() + + # dso_local 正则标注(使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare) + ll_content = re.sub(r'^(define|declare)\s+(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|external)\s+', r'\1 \2 dso_local ', ll_content, flags=re.MULTILINE) + ll_content = re.sub(r'^(define|declare)\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|external|dso_local)', r'\1 dso_local ', ll_content, flags=re.MULTILINE) + # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 + ll_content = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', ll_content, flags=re.MULTILINE) + ll_content = re.sub(r'^(@[\w.]+)\s*=\s*(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr)\s+(global|constant)', r'\1 = \2 dso_local \3', ll_content, flags=re.MULTILINE) + ll_content = re.sub(r'^(@[\w.]+)\s*=\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|dso_local)(global|constant)', r'\1 = dso_local \2', ll_content, flags=re.MULTILINE) + + # stub_decls 注入(复用预收集的 stub_decls) + if stub_decls: + for sdecl in stub_decls: + sname = Phase2Translator._extract_llvm_type_name(sdecl) + if sname: + short = sname.split('.')[-1] if '.' in sname else sname + ll_content = re.sub( + r'%"?' + re.escape(sname) + r'"?\s*=\s*type\s*opaque', + sdecl, ll_content) + if short != sname: + ll_content = re.sub( + r'%"?' + re.escape(short) + r'"?\s*=\s*type\s*opaque', + sdecl, ll_content) + + max_iterations = 10 + for _ in range(max_iterations): + defined_types = set() + for line in ll_content.splitlines(): + sl = line.strip() + if sl.startswith('%') and '= type' in sl: + tname = Phase2Translator._extract_llvm_type_name(sl) + if tname: + defined_types.add(tname) + # 同时添加短名,防止 missing 循环因全名/短名不匹配而重复添加 + if '.' in tname: + parts = tname.split('.', 1) + if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): + defined_types.add(parts[1]) + + referenced_types = Phase2Translator._find_all_type_refs(ll_content) + missing = referenced_types - defined_types + new_defs = [] + for tname in sorted(missing): + if tname in all_type_defs: + new_defs.append(all_type_defs[tname]) + else: + short_key = tname.split('.')[-1] if '.' in tname else tname + for full_name, defn in all_type_defs.items(): + if full_name.split('.')[-1] == short_key: + if short_key not in defined_types: + new_defs.append(defn) + break + else: + # 未在 stub 中找到定义(如泛型类 list[T]), + # 声明为 opaque 以满足 llc 的类型前向声明要求 + new_defs.append(f'%"{tname}" = type opaque') + + if not new_defs: + break + + inject_block = '\n'.join(new_defs) + '\n' + lines = ll_content.splitlines(True) + insert_pos = 0 + for i, line in enumerate(lines): + stripped = line.strip() + if stripped and not stripped.startswith(';') and not stripped.startswith('target ') and not stripped.startswith('source_filename') and not stripped.startswith('declare ') and not stripped.startswith('@') and not stripped.startswith('define ') and not stripped.startswith('attributes ') and not stripped.startswith('!') and not stripped.startswith('module '): + insert_pos = i + break + if insert_pos == 0: + insert_pos = len(lines) + ll_content = ''.join(lines[:insert_pos]) + inject_block + ''.join(lines[insert_pos:]) + + # 将所有类型定义移到 declares 之前 + # (llc 要求 by-value struct 参数的类型在 declare 前定义,否则报 invalid type for function argument) + lines = ll_content.splitlines(True) + type_def_lines = [] + other_lines = [] + seen_type_names_inc = set() + for line in lines: + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + type_name_inc = stripped.split('=')[0].strip() + if type_name_inc in seen_type_names_inc: + continue + seen_type_names_inc.add(type_name_inc) + type_def_lines.append(line) + else: + other_lines.append(line) + if type_def_lines: + insert_pos = 0 + for i, line in enumerate(other_lines): + stripped = line.strip() + if stripped and not stripped.startswith(';') and not stripped.startswith('target ') and not stripped.startswith('source_filename') and not stripped.startswith('!'): + insert_pos = i + break + if insert_pos == 0: + insert_pos = len(other_lines) + ll_content = ''.join(other_lines[:insert_pos]) + ''.join(type_def_lines) + ''.join(other_lines[insert_pos:]) + + dso_ll_path = ll_path[:-3] + '_dso.ll' + with open(dso_ll_path, 'w', encoding='utf-8') as f: + f.write(ll_content) + compile_tasks.append((dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)) + + if not compile_tasks: + return + + # ========== 阶段3:并行编译(llc)========== + _vlog().info(f" 编译 {len(compile_tasks)} 个 .ll 文件") + compile_results = {} # task -> result + + def _compile_one_include(task): + dso_ll_path, obj_path = task[0], task[1] + cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, dso_ll_path] + result = subprocess.run(cmd, capture_output=True, text=True, cwd=self.output_dir) + return (task, result) + + if len(compile_tasks) > 1: + n_workers = min(8, len(compile_tasks)) + with ThreadPoolExecutor(max_workers=n_workers) as executor: + futures = {executor.submit(_compile_one_include, task): task for task in compile_tasks} + done_count = 0 + for future in as_completed(futures): + done_count += 1 + try: + task, result = future.result() + module_name = task[4] + if result.returncode == 0: + _vlog().success(f" [{done_count}/{len(compile_tasks)}] 编译成功: {module_name}.py") + else: + _vlog().warning(f" [{done_count}/{len(compile_tasks)}] 编译失败: {module_name}.py: {result.stderr.strip()}") + compile_results[task] = result + except Exception as e: + task = futures[future] + module_name = task[4] + _vlog().error(f" [{done_count}/{len(compile_tasks)}] 编译异常: {module_name}.py: {e}") + compile_results[task] = None + else: + task = compile_tasks[0] + task_result, result = _compile_one_include(task) + module_name = task[4] + if result.returncode == 0: + _vlog().success(f" 编译成功: {module_name}.py") + else: + _vlog().warning(f" 编译失败: {module_name}.py: {result.stderr.strip()}") + compile_results[task] = result + + # ========== 阶段4:注册 + 缓存 ========== + has_error = False + for task in compile_tasks: + dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content = task + result = compile_results.get(task) + if result and result.returncode == 0: + self.extra_link_files.append(obj_path) + # 复制到 includes.binary 预编译目录 + if precompiled_obj: + precompiled_dir = os.path.dirname(precompiled_obj) + try: + if not os.path.exists(precompiled_dir): + os.makedirs(precompiled_dir, exist_ok=True) + shutil.copy2(obj_path, precompiled_obj) + except Exception as _e: + if _config_mode == "strict": + _vlog().warning(f"预编译缓存写入失败: {_e}") + # 注册符号 + self._register_compiled_include(src_path, sha1, ll_content, include_ModuleSha1Map) + # 清理临时文件(仅成功时清理,失败时保留以便调试) + # DEBUG: 保留 .ll 文件以便静态分析 + # for ext in ['.ll', '_dso.ll']: + # p = ll_path[:-3] + ext + # if os.path.isfile(p): + # os.remove(p) + else: + has_error = True + _vlog().warning(f" 保留失败文件以便调试: {ll_path}") + + if has_error: + _vlog().error(f"\nincludes 文件编译失败") + sys.exit(1) + + # 从 include 文件源码中提取函数参数名,供主项目编译时 keyword 参数匹配使用 + self._extract_include_param_names(sorted_files) + + def _extract_include_param_names_from_dirs(self) -> None: + """从 include 目录的 Python 源码中提取函数参数名,存入 function_param_names + + 优化:单次递归遍历 AST,维护当前类上下文,O(N) 复杂度。 + 复用 parse_python_file 的 AST 缓存。 + """ + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + for filename in os.listdir(includes_dir): + if not filename.endswith('.py'): + continue + src_path = os.path.join(includes_dir, filename) + try: + source, tree = parse_python_file(src_path) + if tree is None: + continue + sha1 = compute_sha1(source) + self._collect_function_params(tree, sha1) + except Exception as e: + _vlog().warning(f" 提取参数名失败 {src_path}: {e}") + + def _collect_function_params(self, tree: ast.AST, sha1: str | None) -> None: + """单次递归遍历 AST 收集函数参数名(含类方法),O(N) 复杂度 + + 遍历时维护 current_class 上下文:遇到 ClassDef 进入类作用域, + 遇到 FunctionDef 记录参数名并标注所属类,函数内部的嵌套定义 + 不再属于外层类。 + """ + def visit(node: ast.AST, current_class: str | None) -> None: + for child in ast.iter_child_nodes(node): + if isinstance(child, ast.ClassDef): + # 进入类作用域,递归处理类体 + visit(child, child.name) + elif isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)): + func_name = child.name + param_names = [arg.arg for arg in child.args.args] + self.function_param_names[func_name] = param_names + if sha1: + self.function_param_names[f'{sha1}.{func_name}'] = param_names + if current_class: + self.function_param_names[f'{current_class}.{func_name}'] = param_names + if sha1: + self.function_param_names[f'{sha1}.{current_class}.{func_name}'] = param_names + # 函数内部的嵌套定义不属于外层类 + visit(child, None) + else: + visit(child, current_class) + visit(tree, None) + + def _extract_app_default_args(self, need_translate: list) -> None: + """从 App 源文件中提取函数默认参数,存入 function_default_args + + 在 Phase 2 并行编译开始前调用,确保跨模块调用参数检查能正确识别默认参数。 + need_translate 中每个元素是 (i, src_path, out_path, sha1, current_ModuleSha1Map) + 复用 parse_python_file 的 AST 缓存(run() 循环已 parse 过)。 + """ + for entry in need_translate: + src_path = entry[1] + sha1 = entry[3] + _, tree = parse_python_file(src_path) + if tree is None: + continue + try: + for node in ast.walk(tree): + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + func_name = node.name + defaults = getattr(node.args, 'defaults', []) + if defaults: + default_vals = [] + for d in defaults: + if isinstance(d, ast.Constant): + default_vals.append(d.value) + else: + default_vals.append(None) + # 存储多种键格式,确保跨模块调用检查能找到 + self.function_default_args[func_name] = default_vals + self.function_default_args[f'{sha1}.{func_name}'] = default_vals + # 也更新参数名(预扫描时补充) + param_names = [arg.arg for arg in node.args.args] + if func_name not in self.function_param_names: + self.function_param_names[func_name] = param_names + if f'{sha1}.{func_name}' not in self.function_param_names: + self.function_param_names[f'{sha1}.{func_name}'] = param_names + except Exception as e: + _vlog().warning(f" 提取默认参数失败 {src_path}: {e}") + + def _extract_include_param_names(self, sorted_files: list[str]) -> None: + """从 include 文件的 Python 源码中提取函数参数名,存入 function_param_names + + 优化:复用 _collect_function_params 的 O(N) 单次遍历 + parse_python_file 缓存。 + """ + for src_path in sorted_files: + if self._is_decl_only_file(src_path): + continue + try: + _, tree = parse_python_file(src_path) + if tree is None: + continue + module_name = os.path.splitext(os.path.basename(src_path))[0] + sha1 = self.include_py_map.get(module_name) + self._collect_function_params(tree, sha1) + except Exception as e: + _vlog().warning(f" 提取参数名失败 {src_path}: {e}") + + def _register_compiled_include(self, src_path: str, sha1: str, ll_content: str, include_ModuleSha1Map: dict[str, str]) -> None: + """编译 include 文件成功后,生成签名和 stub 文件并注册到符号表中""" + module_name = os.path.splitext(os.path.basename(src_path))[0] + + # 确定完整模块名(如 vpsdk.window) + mod_full = module_name + for includes_dir in self.include_dirs: + try: + rel = os.path.relpath(src_path, includes_dir) + mod_full = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') + break + except ValueError: + pass + + # 1. 生成 .pyi 签名文件(如果还没有的话) + sig_path = os.path.join(self.temp_dir, f"{sha1}.pyi") + if not os.path.isfile(sig_path): + # 全局缓存检查(不被 --clean 清除) + _TryGlobalCache(sha1, 'pyi', sig_path) + if not os.path.isfile(sig_path): + try: + with open(src_path, 'r', encoding='utf-8') as f: + py_content = f.read() + sig_content = PythonToStubConverter.convert(py_content, mod_full) + os.makedirs(self.temp_dir, exist_ok=True) + with open(sig_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(sig_content) + _SaveToGlobalCache(sha1, 'pyi', sig_path) + except Exception as e: + if _config_mode == "strict": + raise + _vlog().warning(f"签名生成失败不阻塞编译: {e}", "Exception") + + # 注册到 sig_files 和 _source_module_sig_files + if os.path.isfile(sig_path): + self.sig_files[sha1] = sig_path + self.sha1_map[sha1] = f"includes/{mod_full.replace('.', os.sep)}.py" + if self._shared_source_module_sig_files is not None: + self._shared_source_module_sig_files[mod_full] = sig_path + self._shared_source_module_sig_files[module_name] = sig_path + + # 将签名信息加载到共享符号表中 + if self._shared_symbol_table is not None: + try: + self._shared_symbol_table.LoadModuleSymbols(sig_path, mod_full, lineno=0) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"加载模块签名到共享符号表失败: {_e}", "Exception") + + # 2. 生成 .stub.ll 文件 + stub_path = os.path.join(self.output_dir, f"{sha1}.stub.ll") + if not os.path.isfile(stub_path): + # 全局缓存检查(不被 --clean 清除) + _TryGlobalCache(sha1, 'stub.ll', stub_path) + if not os.path.isfile(stub_path) and ll_content: + try: + stub_content, _ = self._split_ll(ll_content) + with open(stub_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(stub_content) + _SaveToGlobalCache(sha1, 'stub.ll', stub_path) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"生成 .stub.ll 文件失败: {_e}", "Exception") + + # 注册到 stub_files + if os.path.isfile(stub_path): + self.stub_files[sha1] = stub_path + elif os.path.isfile(sig_path): + # 如果 stub 文件不存在,也尝试从 temp 目录查找 + temp_stub = os.path.join(self.temp_dir, f"{sha1}.stub.ll") + if os.path.isfile(temp_stub): + self.stub_files[sha1] = temp_stub + else: + # 最后尝试从全局缓存直接读取 + _TryGlobalCache(sha1, 'stub.ll', temp_stub) + if os.path.isfile(temp_stub): + self.stub_files[sha1] = temp_stub + + def _precollect_inline_symbols(self) -> None: + """在主翻译循环之前,扫描includes目录中used_includes相关的模块收集内联函数AST""" + if not self.used_includes: + return + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + for module_name in self.used_includes: + module_dir = os.path.join(includes_dir, module_name) + if os.path.isdir(module_dir): + for root, dirs, files in os.walk(module_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith('.py'): + src_path = os.path.join(root, fname) + self._collect_inline_symbols(src_path) + py_path = os.path.join(includes_dir, module_name + '.py') + if os.path.isfile(py_path): + self._collect_inline_symbols(py_path) + for ef in self.extra_py_files: + self._collect_inline_symbols(ef) + + def _collect_inline_symbols(self, src_path: str): + """解析源文件,收集内联函数的AST信息到inline_func_symbols + + 复用 parse_python_file 的 AST 缓存,避免重复 open+read+ast.parse。 + """ + try: + _, tree = parse_python_file(src_path) + if tree is None: + return + module_name = os.path.splitext(os.path.basename(src_path))[0] + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.FunctionDef) and node.returns: + is_inline = False + if isinstance(node.returns, ast.BinOp) and isinstance(node.returns.op, ast.BitOr): + for part in [node.returns.left, node.returns.right]: + if isinstance(part, ast.Attribute) and part.attr == 'CInline': + is_inline = True + break + if isinstance(part, ast.Name) and part.id == 'CInline': + is_inline = True + break + elif isinstance(node.returns, ast.Attribute) and node.returns.attr == 'CInline': + is_inline = True + elif isinstance(node.returns, ast.Name) and node.returns.id == 'CInline': + is_inline = True + if is_inline: + func_name = node.name + sym_key = f"{module_name}.{func_name}" + info = CTypeInfo() + info.Name = func_name + info.IsFunction = True + info.IsInline = True + info.InlineBody = node.body + info.InlineParams = [arg.arg for arg in node.args.args] + info.Storage = t.CInline() + self.inline_func_symbols[func_name] = info + self.inline_func_symbols[sym_key] = info + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"预收集内联函数符号失败: {_e}", "Exception") + + def _build_shared_symbol_data(self) -> None: + """一次性构建所有文件共享的符号表数据,避免每个文件重复加载""" + t0 = time.time() + + _vlog().info("[缓存] 构建共享符号表数据...") + + import TransPyC as _TransPyC + base_trans = _TransPyC.TransPyC(code="pass", triple=self.triple, datalayout=self.datalayout) + base_trans.translator.LlvmGen = None + base_trans.translator._ModuleSha1Map = {} + base_trans.translator._global_function_default_args = self.function_default_args + base_trans.translator._global_function_param_names = self.function_param_names + + for includes_dir in self.include_dirs: + if os.path.isdir(includes_dir): + for pyi_file in os.listdir(includes_dir): + if pyi_file.endswith('.pyi'): + pyi_path = os.path.join(includes_dir, pyi_file) + module_name = os.path.splitext(pyi_file)[0] + base_trans.translator.SymbolTable.LoadModuleSymbols(pyi_path, module_name, lineno=0) + for py_file in os.listdir(includes_dir): + if py_file.endswith('.py') and (not py_file.startswith('_') or py_file == '_list.py' or py_file == '_dict.py'): + module_name = os.path.splitext(py_file)[0] + sha1_key = self.include_py_map.get(module_name) + if sha1_key and sha1_key in self.sig_files: + sig_path = self.sig_files[sha1_key] + base_trans.translator.SymbolTable.LoadModuleSymbols(sig_path, module_name, lineno=0) + # 收集需要重导出的包,等所有模块符号加载完后再处理 + _pending_reexports = [] + for sha1_key, sig_path in self.sig_files.items(): + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + mod_name = mod_name[len('includes/'):] + base_trans.translator.SymbolTable.LoadModuleSymbols(sig_path, mod_name, lineno=0) + if mod_name.endswith('.__init__'): + package_name = mod_name[:-len('.__init__')] + _pending_reexports.append((sig_path, package_name)) + + # 所有 includes 模块符号已加载,现在处理包重导出 + for sig_path, package_name in _pending_reexports: + self._register_package_reexports(base_trans, sig_path, package_name) + + for sha1_key, sig_path in self.sig_files.items(): + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + continue + module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + base_trans.translator.SymbolTable.LoadModuleSymbols(sig_path, module_name, lineno=0) + base_trans.translator._source_module_sig_files[module_name] = sig_path + short_name = module_name.split('.')[-1] if '.' in module_name else module_name + if short_name != module_name: + base_trans.translator._source_module_sig_files[short_name] = sig_path + + all_dc = {} + for sha1_key, stub_path in self.stub_files.items(): + if os.path.exists(stub_path): + try: + with open(stub_path, 'r', encoding='utf-8') as f: + stub_content = f.read() + for line in stub_content.splitlines(): + line = line.strip() + if line.startswith('@') and '= global' in line and 'i32' in line and not line.startswith('@llvm'): + var_name = line.split('=')[0].strip().lstrip('@') + val_part = line.split('i32')[-1].strip().rstrip(']').lstrip('[').lstrip('i32') + try: + val = int(val_part) + all_dc[var_name] = val + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + + for sha1_key, pyi_path in self.sig_files.items(): + _ExtractCDefineConstantsFromPyi(pyi_path, all_dc) + + export_extern_funcs = {} + for sha1_key, sig_path in self.sig_files.items(): + try: + # 复用 parse_python_file 的 AST 缓存(sig 文件被多处解析) + _, sig_tree = parse_python_file(sig_path) + if sig_tree is None: + continue + for node in ast.iter_child_nodes(sig_tree): + if isinstance(node, ast.FunctionDef): + if _check_annotation_for_export(node.returns): + export_extern_funcs[node.name] = sha1_key + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"收集共享导出外部函数失败: {_e}", "Exception") + + for sym_name, sym_info in self.inline_func_symbols.items(): + if sym_name not in base_trans.translator.SymbolTable: + base_trans.translator.SymbolTable[sym_name] = sym_info + else: + existing = base_trans.translator.SymbolTable[sym_name] + if not getattr(existing, 'InlineBody', None) and getattr(sym_info, 'InlineBody', None): + existing.IsInline = sym_info.IsInline + existing.InlineBody = sym_info.InlineBody + existing.InlineParams = sym_info.InlineParams + + self._shared_symbol_table = base_trans.translator.SymbolTable + self._shared_source_module_sig_files = dict(base_trans.translator._source_module_sig_files) + self._shared_all_dc = all_dc + self._shared_export_extern_funcs = export_extern_funcs + + generic_class_templates = {} + for includes_dir in self.include_dirs: + if os.path.isdir(includes_dir): + abs_includes = os.path.join(self.src_root, includes_dir) if not os.path.isabs(includes_dir) else includes_dir + if not os.path.isdir(abs_includes): + abs_includes = includes_dir + for py_file in os.listdir(abs_includes): + if py_file.endswith('.py') and (not py_file.startswith('_') or py_file == '_list.py' or py_file == '_dict.py'): + py_path = os.path.join(abs_includes, py_file) + try: + # 复用 parse_python_file 的 AST 缓存 + _, py_tree = parse_python_file(py_path) + if py_tree is None: + continue + for node in py_tree.body: + if isinstance(node, ast.ClassDef) and hasattr(node, 'type_params') and node.type_params: + type_params = [tp.name for tp in node.type_params] + generic_class_templates[node.name] = { + 'node': node, + 'type_params': type_params, + } + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"收集泛型类模板失败: {_e}", "Exception") + self._shared_generic_class_templates = generic_class_templates + + # 加载所有 {sha1}.doc.json 到 _shared_doc_meta,供跨模块 __doc__ 查询 + shared_doc_meta: dict[str, dict[str, str]] = {} + if os.path.isdir(self.temp_dir): + for fname in os.listdir(self.temp_dir): + if fname.endswith('.doc.json'): + sha1_key = fname[:-len('.doc.json')] + try: + with open(os.path.join(self.temp_dir, fname), 'r', encoding='utf-8') as f: + data = json.load(f) + if isinstance(data, dict): + shared_doc_meta[sha1_key] = {str(k): str(v) for k, v in data.items()} + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"加载 {fname} 失败: {_e}", "Exception") + # 也从全局缓存加载 .doc.json(temp_dir 被 --clean 清空时补充) + if os.path.isdir(_GLOBAL_CACHE_DIR): + for fname in os.listdir(_GLOBAL_CACHE_DIR): + if fname.endswith('.doc.json'): + sha1_key = fname[:-len('.doc.json')] + if sha1_key in shared_doc_meta: + continue + try: + with open(os.path.join(_GLOBAL_CACHE_DIR, fname), 'r', encoding='utf-8') as f: + data = json.load(f) + if isinstance(data, dict): + shared_doc_meta[sha1_key] = {str(k): str(v) for k, v in data.items()} + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"加载全局缓存 {fname} 失败: {_e}", "Exception") + self._shared_doc_meta = shared_doc_meta + + t1 = time.time() + _vlog().info(f"[缓存] 共享符号表构建完成 ({t1-t0:.2f}s, {len(self._shared_symbol_table)} 符号, {len(generic_class_templates)} 泛型模板, {len(shared_doc_meta)} doc 元数据)") + + def _compile_include_sources(self): + """扫描项目目录中的汇编存根,编译 includes 目录中发现的 C/C++ 源文件""" + extra_sources = list(self.extra_compile_files) + scan_dirs = [] + if self.src_root and os.path.isdir(self.src_root): + scan_dirs.append(self.src_root) + project_dir = os.path.dirname(self.output_dir) + if project_dir and os.path.isdir(project_dir) and project_dir not in scan_dirs: + scan_dirs.append(project_dir) + for scan_dir in scan_dirs: + for root, dirs, files in os.walk(scan_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith(('.S', '.s', '.c', '.cpp')): + if fname.startswith('test_'): + continue + fpath = os.path.join(root, fname) + if fpath not in extra_sources: + extra_sources.append(fpath) + if not extra_sources: + return + + cc_map = { + '.c': 'gcc', + '.cpp': 'g++', '.cc': 'g++', '.cxx': 'g++', + '.m': 'clang', '.mm': 'clang++', + '.s': 'gcc', '.S': 'gcc', + } + + _vlog().info(f"\n[includes 编译] 找到 {len(extra_sources)} 个源文件") + + for src_path in extra_sources: + _, ext = os.path.splitext(src_path) + ext = ext.lower() + obj_path = os.path.join(self.output_dir, os.path.splitext(os.path.basename(src_path))[0] + '.obj') + if ext in ('.s', '.S') and any('oformat' in f for f in self.linker_flags): + cc = 'clang' + compile_src = src_path + extra_flags = ['-c', '-target', 'x86_64-none-elf', '-o', obj_path, compile_src] + else: + cc = cc_map.get(ext, 'gcc') + extra_flags = ['-c', '-o', obj_path, src_path] + + _vlog().info(f" 编译: {os.path.basename(src_path)} -> {os.path.basename(obj_path)}") + + try: + cmd = [cc] + extra_flags + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode == 0: + _vlog().success(" 编译成功") + self.extra_link_files.append(obj_path) + else: + _vlog().error(f" 编译失败: {result.stderr.strip()}") + _vlog().error(f"\nC/C++ 源文件编译失败,立即终止编译。") + sys.exit(1) + except FileNotFoundError: + _vlog().error(f" 找不到编译器: {cc}") + _vlog().error(f"\n找不到编译器,立即终止编译。") + sys.exit(1) + except Exception as e: + _vlog().error(f" {e}") + _vlog().error(f"\n编译异常,立即终止编译。") + sys.exit(1) + + def _strip_debug_sections(self, exe_path: str) -> None: + """剥离 .exe 中的调试段(跳过裸机二进制)""" + if not os.path.exists(exe_path): + return + ext = os.path.splitext(exe_path)[1].lower() + if ext not in ('.exe', '.dll', '.obj', '.o', '.elf'): + _vlog().info(f" [剥离] 跳过({ext} 格式无需剥离)") + return + for tool in ['llvm-strip', 'strip']: + strip_cmd = shutil.which(tool) + if strip_cmd: + try: + result = subprocess.run( + [strip_cmd, '--strip-debug', exe_path], + capture_output=True, text=True + ) + if result.returncode == 0: + _vlog().info(f" [剥离] 调试段已移除 ({tool})") + else: + _vlog().warning(f" [剥离] 警告: {result.stderr.strip()}") + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"剥离可执行文件调试段失败: {_e}", "Exception") + break + + def _link_obj_files(self, active_sha1s: set[str]) -> None: + """将 active_sha1s 对应的 .obj 文件和 includes 库文件链接为可执行文件""" + obj_files = [] + merged_obj = os.path.join(self.output_dir, '_merged.obj') + if os.path.isfile(merged_obj): + obj_files.append(merged_obj) + else: + for root, dirs, files in os.walk(self.output_dir): + for file in files: + if file.endswith('.obj'): + sha1 = file[:-4] + if sha1 in active_sha1s and sha1 not in self._include_sha1s: + obj_files.append(os.path.join(root, file)) + + if not obj_files and not self.extra_link_files: + _vlog().info("[链接] 无文件可链接") + return + + _vlog().info(f"\n[链接] 找到 {len(obj_files)} 个 .obj 文件") + + all_link_files = obj_files + self.extra_link_files + seen = set() + all_link_files = [f for f in all_link_files if not (f in seen or seen.add(f))] + if self.extra_link_files: + _vlog().info(f"[链接] 额外库文件: {len(self.extra_link_files)} 个") + for lib in self.extra_link_files: + _vlog().info(f" - {os.path.basename(lib)}") + + if not self.linker_cmd: + _vlog().info("[链接] 未配置链接器") + return + + exe_path = os.path.join(self.output_dir, self.linker_output) if self.linker_output else os.path.join(self.output_dir, 'a.exe') + is_binary = self.linker_output and self.linker_output.endswith('.bin') + + # 检查是否直接输出二进制(使用 --oformat binary / -oformat binary 标志) + is_direct_binary = is_binary and any('oformat' in f for f in self.linker_flags) + + if is_binary and not is_direct_binary: + link_output = exe_path[:-4] + '_pe.exe' + else: + link_output = exe_path + + # 自动复制或生成 linker.ld 到输出目录 + linker_ld_src = os.path.join(os.path.dirname(self.output_dir), 'linker.ld') + linker_ld_dst = os.path.join(self.output_dir, 'linker.ld') + if os.path.isfile(linker_ld_src) and not os.path.isfile(linker_ld_dst): + shutil.copy2(linker_ld_src, linker_ld_dst) + _vlog().info(f" [链接] 使用链接脚本: linker.ld") + elif not os.path.isfile(linker_ld_dst) and any('-T' in f or '-T' in f.replace(',', ' ') for f in self.linker_flags): + default_ld = """ENTRY(_start) + +SECTIONS { + . = 0x100000; + + .text : { + *(.text) + *(.text.*) + } + + .rodata : { + *(.rodata) + *(.rodata.*) + } + + .data : { + *(.data) + *(.data.*) + } + + .bss : { + *(.bss) + *(.bss.*) + } + + /DISCARD/ : { + *(.comment) + *(.note) + *(.eh_frame) + *(.eh_frame_hdr) + *(.reloc) + *(.rela) + *(.debug*) + } +} +""" + with open(linker_ld_dst, 'w', encoding='utf-8', newline='\n') as f: + f.write(default_ld) + _vlog().info(f" [链接] 自动创建默认链接脚本: linker.ld") + + try: + # 将 linker_flags 拆分为非库标志和库标志(-l/-L),库标志放在目标文件之后 + # 这样 Unix 风格链接器才能正确解析库中的符号 + # -Wl, 选项是链接器全局选项,放在目标文件之前 + non_lib_flags = [] + lib_flags = [] + i = 0 + while i < len(self.linker_flags): + flag = self.linker_flags[i] + if flag in ('-l', '-L') and i + 1 < len(self.linker_flags): + # -l xxx / -L xxx 分开的形式 + lib_flags.extend([flag, self.linker_flags[i + 1]]) + i += 2 + elif flag.startswith('-l') or flag.startswith('-L'): + # -lxxx / -Lxxx 合并的形式 + lib_flags.append(flag) + i += 1 + else: + # 其他所有标志(包括 -Wl, 选项)放在目标文件之前 + non_lib_flags.append(flag) + i += 1 + cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags + _vlog().info(f" 执行: {' '.join(cmd)}") + result = subprocess.run( + cmd, + capture_output=True, + text=True, + cwd=self.output_dir or '.' + ) + if result.returncode == 0: + if is_binary and not is_direct_binary: + objcopy = shutil.which('llvm-objcopy') or shutil.which('objcopy') + if objcopy: + conv = subprocess.run( + [objcopy, '-O', 'binary', link_output, exe_path], + capture_output=True, text=True + ) + if conv.returncode == 0: + os.remove(link_output) + _vlog().success(f" 生成裸机二进制: {exe_path}") + file_size = os.path.getsize(exe_path) + _vlog().info(f" [大小] {file_size} 字节") + else: + _vlog().error(f" 二进制转换失败: {conv.stderr.strip()}") + sys.exit(1) + else: + _vlog().error(" 找不到 llvm-objcopy,无法生成裸机二进制") + sys.exit(1) + else: + _vlog().success(f" 生成: {exe_path}") + file_size = os.path.getsize(exe_path) + _vlog().info(f" [大小] {file_size} 字节") + if not is_binary: + self._strip_debug_sections(exe_path) + else: + _vlog().error(f" 链接失败: {result.stderr.strip()}") + _vlog().error(f"\n链接失败,立即终止编译。") + sys.exit(1) + except FileNotFoundError: + _vlog().error(f" 找不到链接器: {self.linker_cmd}") + _vlog().error(f"\n找不到链接器,立即终止编译。") + sys.exit(1) + except Exception as e: + _vlog().error(f" {e}") + _vlog().error(f"\n链接异常,立即终止编译。") + sys.exit(1) diff --git a/App/lib/Shell/Config.py b/App/lib/Shell/Config.py new file mode 100644 index 0000000..5bfcfc7 --- /dev/null +++ b/App/lib/Shell/Config.py @@ -0,0 +1,8 @@ +from __future__ import annotations + + +class Config: + """配置类""" + def __init__(self) -> None: + self.debug: bool = False + self.mode: str = 'relaxed' # 'relaxed' 或 'strict' diff --git a/App/lib/Shell/Serializer.py b/App/lib/Shell/Serializer.py new file mode 100644 index 0000000..f87409e --- /dev/null +++ b/App/lib/Shell/Serializer.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +import json +import struct +from typing import Any + + +def SerializeSymbolTable(SymbolTable: dict[str, Any]) -> bytes: + """将符号表序列化为二进制格式 + + 格式: + - 4字节: JSON数据长度(小端序) + - N字节: JSON数据(UTF-8编码) + + Args: + SymbolTable: 符号表字典 + + Returns: + 二进制字节数据 + """ + JsonData: bytes = json.dumps(SymbolTable, ensure_ascii=False).encode('utf-8') + length: int = len(JsonData) + # 使用4字节小端序整数表示长度 + header: bytes = struct.pack(' dict[str, Any]: + """从二进制数据反序列化符号表 + + Args: + data: 二进制字节数据 + + Returns: + 符号表字典 + """ + if len(data) < 4: + raise ValueError("Invalid symbin file: data too short") + # 解析4字节长度头(小端序) + length: int = struct.unpack(' None: + """初始化SymbolFile对象 + + Args: + file: 文件路径 + string: 代码字符串 + type: 文件类型,支持"c"或"py" + encoding: 文件编码 + """ + self.FilePath: str | None = file + self.CodeString: str | None = string + self.FileType: str | None = type + self.encoding: str = encoding + self.symbols: dict[str, Any] = {} diff --git a/App/lib/Shell/TransPyC.py b/App/lib/Shell/TransPyC.py new file mode 100644 index 0000000..2aa92e7 --- /dev/null +++ b/App/lib/Shell/TransPyC.py @@ -0,0 +1,645 @@ +from __future__ import annotations + +import sys +import os +import ast +import shlex +import datetime +import tempfile +import subprocess +from typing import Any +from lib.includes import c +from lib.includes import t +from lib.constants.config import ( + DEFAULT_INPUT_FILE, DEFAULT_OUTPUT_FILE, + DEFAULT_COMPILE_COMMAND, + DEFAULT_COMPILE_FLAGS, + HELP_MESSAGE, DEFAULT_METADATA +) +from lib.utils.helpers import ( + DetectFileType, ExecuteCommand, + AppendFileContent +) +from lib.core.VLogger import info, warning, error, success, debug +from lib.core.translator import Translator +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.Shell.Serializer import SerializeSymbolTable, SymbolFile +from lib.Shell.Config import Config + + +class TransPyC: + """TransPyC 主类""" + + def __init__(self, code: str | None = None, debug: bool = False, triple: str | None = None, datalayout: str | None = None) -> None: + """初始化TransPyC对象 + + Args: + code: 代码字符串 + debug: 调试模式 + triple: LLVM target triple (e.g. "x86_64-pc-linux-gnu") + datalayout: LLVM data layout string + """ + self.Args: dict[str, Any] = {} + self.HeaderFiles: list[str] = [] + self.HelperFiles: list[str] = [] + self.AnnotationFiles: list[str] = [] + self.translator: Translator = Translator() + self.code: str | None = code + self.translator.Content = code + self.config: Config = Config() + self.translator.config = self.config + self.config.debug = debug + self.SymbolFiles: list[SymbolFile] = [] + self.SliceLevel: int | str = 3 + self.SliceCount: int = 0 + self.SliceInfos: list[Any] = [] + self.metadata: dict[str, Any] = DEFAULT_METADATA.copy() + self.triple: str | None = triple + self.datalayout: str | None = datalayout + self._source_module_sig_files: dict[str, Any] = {} + + @staticmethod + def PreProcessSymbol(SymbolFile: SymbolFile, debug: bool = False) -> bytes | tuple[bytes, str]: + """预处理符号文件,提取符号表并返回二进制数据 + + Args: + SymbolFile: SymbolFile对象,包含文件路径和类型信息 + debug: 是否启用调试模式,如果为True,返回 (bytes, DebugInfo) + 对于Python文件,DebugInfo包含p2c日志 + 对于C文件,DebugInfo包含基本处理信息 + + Returns: + 如果debug为False: bytes - 序列化后的符号表二进制数据 + 如果debug为True: (bytes, str) - (二进制数据, 调试日志) + """ + translator: Translator = Translator() + DebugLogs: list[str] | str = [] + + try: + # 读取文件内容 + if SymbolFile.FilePath: + with open(SymbolFile.FilePath, 'r', encoding=SymbolFile.encoding) as f: + content: str = f.read() + elif SymbolFile.CodeString: + content = SymbolFile.CodeString + else: + raise ValueError("SymbolFile must have either FilePath or CodeString") + + # 根据文件类型处理 + FileType: str | None = SymbolFile.FileType + if not FileType and SymbolFile.FilePath: + # 从文件路径推断类型 + if SymbolFile.FilePath.endswith('.py'): + FileType = 'py' + elif SymbolFile.FilePath.endswith('.c') or SymbolFile.FilePath.endswith('.h'): + FileType = 'c' + + if FileType == 'py': + # Python文件处理 + translator.OriginalLines = content.split('\n') + translator.Content = content + + # 设置调试文件(如果需要) + if debug: + DebugFile: str = SymbolFile.FilePath.replace('.py', '.p2c') if SymbolFile.FilePath else 'debug.p2c' + translator.SetDebugFile(DebugFile) + # 清空调试文件 + with open(DebugFile, 'w', encoding=SymbolFile.encoding) as f: + f.write('') + + # 解析AST并提取符号 + tree: ast.Module = ast.parse(content) + + if debug: + with open(DebugFile, 'a', encoding=SymbolFile.encoding) as f: + f.write('=== AST Tree (Compact) ===\n') + f.write(ast.dump(tree)) + f.write('\n\n') + + # 遍历AST提取符号 + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.ClassDef): + translator.SymbolTable[node.name] = {'type': 'struct', 'members': {}} + for item in node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + VarName: str = item.target.id + TypeInfo: CTypeInfo | None = translator.TypeMergeHandler.GetCTypeInfo(item.annotation) if item.annotation else None + VarType: str = TypeInfo.ToString() if TypeInfo else 'unknown' + IsPtr: bool = TypeInfo.IsPtr if TypeInfo else False + translator.SymbolTable[node.name]['members'][VarName] = { + 'type': VarType, + 'IsPtr': IsPtr + } + elif isinstance(node, ast.FunctionDef): + translator.SymbolTable[node.name] = {'type': 'function'} + elif isinstance(node, ast.AnnAssign): + if isinstance(node.target, ast.Name): + VarName: str = node.target.id + VarType: str = 'unknown' + IsPtr: bool = False + if node.annotation: + try: + TypeInfo: CTypeInfo | None = translator.TypeMergeHandler.GetCTypeInfo(node.annotation) + VarType = TypeInfo.ToString() if TypeInfo else 'unknown' + IsPtr = TypeInfo.IsPtr if TypeInfo else False + except: + pass + translator.SymbolTable[VarName] = { + 'type': 'variable', + 'declared_type': VarType, + 'IsPtr': IsPtr + } + + if debug: + with open(DebugFile, 'a', encoding=SymbolFile.encoding) as f: + f.write('=== Symbol Table ===\n') + f.write(str(translator.SymbolTable)) + f.write('\n\n') + # 读取调试日志 + with open(DebugFile, 'r', encoding=SymbolFile.encoding) as f: + DebugLogs = f.read() + + elif FileType == 'c': + # C文件处理 - 使用现有的ParseHelperFiles逻辑 + translator.ParseHelperFiles([SymbolFile.FilePath], SymbolFile.encoding) + + if debug: + DebugLogs = f"=== C File Symbol Extraction ===\n" + DebugLogs += f"File: {SymbolFile.FilePath}\n" + DebugLogs += f"=== Symbol Table ===\n" + DebugLogs += str(translator.SymbolTable) + DebugLogs += "\n\n" + else: + raise ValueError(f"Unsupported file type: {FileType}") + + # 序列化符号表 + BinaryData: bytes = SerializeSymbolTable(translator.SymbolTable) + + if debug: + return BinaryData, DebugLogs + return BinaryData + + except Exception as e: + error_msg: str = f"Error processing symbol file: {e}" + if debug: + return b'', error_msg + raise RuntimeError(error_msg) + + def ParseArgs(self) -> None: + """解析命令行参数""" + I: int = 1 + while I < len(sys.argv): + if sys.argv[I] == '-f': + if I + 1 < len(sys.argv): + self.Args['Input'] = sys.argv[I + 1] + I += 2 + else: + error('-f requires an argument') + sys.exit(1) + elif sys.argv[I] == '-o': + if I + 1 < len(sys.argv): + self.Args['Output'] = sys.argv[I + 1] + I += 2 + else: + error('-o requires an argument') + sys.exit(1) + elif sys.argv[I] == '-wh': + if I + 1 < len(sys.argv): + self.HeaderFiles = sys.argv[I + 1:] + break + else: + error('-wh requires arguments') + sys.exit(1) + elif sys.argv[I] == '-debug': + if I + 1 < len(sys.argv): + self.Args['Debug'] = sys.argv[I + 1] + I += 2 + else: + error('-debug requires an argument') + sys.exit(1) + elif sys.argv[I] == '-cc': + # 编译命令 + if I + 1 < len(sys.argv): + self.Args['CompileCommand'] = sys.argv[I + 1] + I += 2 + else: + error('-cc requires an argument') + sys.exit(1) + elif sys.argv[I] == '-cflags': + # 编译标志 + if I + 1 < len(sys.argv): + self.Args['CompileFlags'] = sys.argv[I + 1] + I += 2 + else: + error('-cflags requires an argument') + sys.exit(1) + elif sys.argv[I] == '-run': + # 是否运行生成的程序 + self.Args['Run'] = True + I += 1 + elif sys.argv[I] == '-args': + # 运行时参数 + if I + 1 < len(sys.argv): + self.Args['RunArgs'] = sys.argv[I + 1] + I += 2 + else: + error('-args requires an argument') + sys.exit(1) + elif sys.argv[I] == '-level' or sys.argv[I] == '-l': + if I + 1 < len(sys.argv): + LevelArg: str = sys.argv[I + 1] + if LevelArg.isdigit(): + self.Args['SliceLevel'] = int(LevelArg) + elif LevelArg == 's': + self.Args['SliceLevel'] = 's' + else: + self.Args['SliceLevel'] = 3 + I += 1 + else: + self.Args['SliceLevel'] = 3 + elif sys.argv[I] == '-h': + # 辅助文件,用于解析符号信息 + if I + 1 < len(sys.argv): + # 收集所有后续参数作为辅助文件,直到遇到下一个以-开头的参数 + while I + 1 < len(sys.argv) and not sys.argv[I + 1].startswith('-'): + self.HelperFiles.append(sys.argv[I + 1]) + I += 1 + I += 1 + else: + error('-h requires arguments') + sys.exit(1) + elif sys.argv[I] == '-presym': + # 预处理符号文件,生成.symbin文件 + # 格式: -presym -o [-debug ] + if I + 1 < len(sys.argv): + InputFile: str = sys.argv[I + 1] + I += 2 + + # 解析可选参数 + OutputFile: str | None = None + DebugFile: str | None = None + while I < len(sys.argv) and sys.argv[I].startswith('-'): + if sys.argv[I] == '-o': + if I + 1 < len(sys.argv): + OutputFile = sys.argv[I + 1] + I += 2 + else: + error('-o requires an argument') + sys.exit(1) + elif sys.argv[I] == '-debug': + if I + 1 < len(sys.argv): + DebugFile = sys.argv[I + 1] + I += 2 + else: + error('-debug requires an argument') + sys.exit(1) + else: + break + + # 如果没有指定输出文件,使用默认名称 + if not OutputFile: + OutputFile = InputFile.replace('.py', '.symbin').replace('.c', '.symbin') + + # 执行预处理 + self.Args['PreSym'] = { + 'input': InputFile, + 'output': OutputFile, + 'debug': DebugFile + } + else: + error('-presym requires an argument') + sys.exit(1) + elif sys.argv[I] == '-e' or sys.argv[I] == '--encoding': + if I + 1 < len(sys.argv): + self.Args['Encoding'] = sys.argv[I + 1] + I += 2 + else: + error('-e/--encoding requires an argument') + sys.exit(1) + elif sys.argv[I] == '--triple': + if I + 1 < len(sys.argv): + self.triple = sys.argv[I + 1] + I += 2 + else: + error('--triple requires an argument') + sys.exit(1) + elif sys.argv[I] == '--datalayout': + if I + 1 < len(sys.argv): + self.datalayout = sys.argv[I + 1] + I += 2 + else: + error('--datalayout requires an argument') + sys.exit(1) + else: + error(f'Unknown argument {sys.argv[I]}') + sys.exit(1) + + # 检查是否有预处理符号文件的请求,如果有则跳过输入输出检查 + if 'PreSym' not in self.Args and ('Input' not in self.Args or 'Output' not in self.Args): + error('缺少参数') + info(HELP_MESSAGE) + sys.exit(1) + + def CompileAndRun(self, OutputFile: str) -> None: + """编译并运行生成的C代码""" + # 获取编译命令 + CompileCmd: str = self.Args.get('CompileCommand', DEFAULT_COMPILE_COMMAND) + CompileFlags: str = self.Args.get('CompileFlags', DEFAULT_COMPILE_FLAGS) + + # 构建编译命令(使用列表形式,避免 shell 注入) + FullCompileCmd: list[str] = shlex.split(CompileCmd) + shlex.split(CompileFlags) + [OutputFile, '-o', f'{OutputFile}.exe'] + info(f'编译命令: {" ".join(FullCompileCmd)}') + + # 执行编译命令 + try: + CompileResult: subprocess.CompletedProcess[str] | None = ExecuteCommand(FullCompileCmd) + if CompileResult: + success('编译成功!') + + # 检查是否需要运行 + if self.Args.get('Run', False): + RunArgs: str = self.Args.get('RunArgs', '') + RunCmd: list[str] = [f'{OutputFile}.exe'] + (shlex.split(RunArgs) if RunArgs else []) + info(f'运行: {" ".join(RunCmd)}') + + # 执行运行命令 + RunResult: subprocess.CompletedProcess[str] | None = ExecuteCommand(RunCmd) + if RunResult: + info('执行输出:') + info(RunResult.stdout) + if RunResult.stderr: + warning('执行错误:') + warning(RunResult.stderr) + except Exception as e: + error(f'编译失败: {e}') + + def WriteDebugInfo(self, content: str) -> None: + """写入调试信息到指定文件""" + # 获取调试文件路径 + if 'Debug' in self.Args: + DebugFile: str = self.Args['Debug'] + else: + DebugFile: str = self.Args.get('Output', '').replace('.c', '.p2c') + + if DebugFile: + AppendFileContent(DebugFile, content) + + def PythonToC(self, InputFile: str, OutputFile: str) -> None: + """Python到C的转换""" + # 获取编码参数 + encoding: str = self.Args.get('Encoding', 'utf-8') + + # 设置调试文件路径(使用 .p2c 扩展名) + if 'Debug' in self.Args: + DebugFile: str = self.Args['Debug'] + else: + # 默认使用 .p2c 文件 + DebugFile: str = OutputFile.replace('.c', '.p2c') + + # 设置翻译器的调试文件 + self.translator.SetDebugFile(DebugFile) + + # 先清空调试文件 + with open(DebugFile, 'w', encoding=encoding) as f: + f.write('') + + # 解析辅助文件,提取符号信息 + if self.HelperFiles: + self.translator.ParseHelperFiles(self.HelperFiles, encoding) + + # 加载注解文件,提取类型定义 + if self.AnnotationFiles: + self.translator.LoadAnnotationFiles(self.AnnotationFiles) + + # 获取编码参数 + encoding = self.Args.get('Encoding', 'utf-8') + with open(InputFile, 'r', encoding=encoding) as F: + Content: str = F.read() + + # 保存原始代码行 + self.translator.OriginalLines = Content.split('\n') + self.translator.Content = Content + + # 解析Python代码为AST + Tree: ast.Module = ast.parse(Content) + + # 解析文件以提取类型信息(用于 EmbeddedAssignments) + self.translator.ParsePythonFile(InputFile, encoding) + + # 写入AST树信息(压缩格式) + with open(DebugFile, 'a', encoding=encoding) as f: + f.write('=== AST Tree (Compact) ===\n') + f.write(ast.dump(Tree)) + f.write('\n\n') + + Target: str = 'llvm' + CCode: str = self.translator.GenerateCCode(Tree, target=Target) + + timestamp: str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + filename: str = os.path.basename(OutputFile) + + encoding = self.Args.get('Encoding', 'utf-8') + with open(OutputFile, 'w', encoding=encoding) as F: + F.write(CCode) + F.write('\n') + + def CToPython(self, InputFile: str, OutputFile: str, HeaderFiles: list[str]) -> None: + """C到Python的转换(暂时禁用)""" + warning("C到Python的转换功能暂时禁用,请使用Python到C的转换功能。") + + def AddSymbol(self, SymbolFiles: SymbolFile | list[SymbolFile]) -> None: + """添加符号文件 + + Args: + SymbolFiles: 符号文件或符号文件列表 + """ + if isinstance(SymbolFiles, list): + self.SymbolFiles.extend(SymbolFiles) + else: + self.SymbolFiles.append(SymbolFiles) + + # 收集文件路径和编码 + HelperFiles: list[str] = [] + encodings: list[str] = [] + for SymbolFile in self.SymbolFiles: + FilePath: str | None = SymbolFile.FilePath + encoding: str = SymbolFile.encoding + if FilePath: + HelperFiles.append(FilePath) + encodings.append(encoding) + + # 解析辅助文件 + if HelperFiles: + # 暂时使用第一个文件的编码 + encoding = encodings[0] if encodings else 'utf-8' + self.translator.ParseHelperFiles(HelperFiles, encoding) + + def Convert(self, OutputFilename: str | None = None, SourceFilename: str | None = None, target: str = 'llvm') -> str | tuple[str, dict[str, Any]] | None: + """转换代码 + + Args: + OutputFilename: 输出文件名(用于模板渲染) + SourceFilename: 源文件路径(用于设置CurrentFile,影响导入模块的查找) + target: 目标代码类型,仅支持 'llvm' + + Returns: + 如果debug是字符串,则返回生成的代码 + 如果debug是True,则返回生成的代码和调试信息 + """ + if not self.code: + error('未提供代码') + return None + + from lib.core.Handles.HandlesImports import ImportHandle + ImportHandle._struct_Load_cache.clear() + + # 如果提供了 SourceFilename,先设置 CurrentFile + # 这样导入模块时会相对于源文件所在目录查找 + if SourceFilename: + self.translator.CurrentFile = SourceFilename + + # 加载注解文件,提取类型定义 + if self.AnnotationFiles: + self.translator.LoadAnnotationFiles(self.AnnotationFiles) + + # 保存原始代码行 + self.translator.OriginalLines = self.code.split('\n') + + # 解析Python代码为AST + Tree: ast.Module = ast.parse(self.code) + + # 从源代码直接预扫描 import 语句,注册别名 + # 注意:不能依赖 Tree 中的 import 节点,因为 ParsePythonFile 可能修改了 AST + if not getattr(self.translator, '_ImportedModules', None): + self.translator._ImportedModules = set() + _prescan_tree: ast.Module = ast.parse(self.code) + for _node in ast.iter_child_nodes(_prescan_tree): + if isinstance(_node, ast.Import): + for _alias in _node.names: + _name: str = _alias.name + if _name in ('c', 't'): + continue + self.translator._ImportedModules.add(_name) + if _alias.asname: + self.translator.SymbolTable.import_aliases[_alias.asname] = _name + elif isinstance(_node, ast.ImportFrom): + _module_name: str | None = _node.module + if _module_name and _module_name not in ('c', 't'): + self.translator._ImportedModules.add(_module_name) + for _alias in _node.names: + if _alias.asname: + self.translator.SymbolTable.import_aliases[_alias.asname] = f"{_module_name}.{_alias.name}" + + # 解析代码以提取类型信息(用于 EmbeddedAssignments 和 TypedefAssignments) + # 创建临时文件路径用于解析 + with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as f: + f.write(self.code) + TempFile: str = f.name + try: + # UpdateCurrentFile=False 表示不修改 CurrentFile + if SourceFilename: + self.translator.ParsePythonFile(TempFile, UpdateCurrentFile=False) + else: + self.translator.ParsePythonFile(TempFile) + finally: + os.unlink(TempFile) + + timestamp: str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + if self.triple: + self.translator.triple = self.triple + if self.datalayout: + self.translator.datalayout = self.datalayout + + # 生成代码 + code: str = self.translator.GenerateCCode(Tree, target='llvm') + filename: str = os.path.basename(OutputFilename) if OutputFilename else 'generated.ll' + + # 处理调试信息 + if isinstance(self.config.debug, str): + # 如果debug是字符串,写入调试信息到文件 + with open(self.config.debug, 'w', encoding='utf-8') as f: + f.write('=== Symbol Table ===\n') + f.write(str(self.translator.SymbolTable) + '\n\n') + f.write('=== AST Tree ===\n') + f.write(ast.dump(Tree, indent=2) + '\n\n') + f.write('=== Export Table ===\n') + f.write(self.translator.Exportable.to_json() + '\n\n') + f.write('=== Generated Code ===\n') + f.write(code + '\n') + return code + elif self.config.debug: + # 如果debug是True,返回生成的代码和调试信息 + DebugInfo: dict[str, Any] = { + 'SymbolTable': self.translator.SymbolTable, + 'ast_tree': ast.dump(Tree, indent=2), + 'Exportable': self.translator.Exportable.to_dict(), + 'generated_code': code + } + return code, DebugInfo + else: + # 如果debug是False,只返回生成的代码 + return code + + def Run(self) -> None: + """主运行函数""" + # 检查是否有命令行参数 + if len(sys.argv) > 1: + # 有命令行参数,使用 ParseArgs 方法解析 + self.ParseArgs() + + # 检查是否有预处理符号文件的请求 + if 'PreSym' in self.Args: + PresymConfig: dict[str, Any] = self.Args['PreSym'] + InputFile: str = PresymConfig['input'] + OutputFile: str = PresymConfig['output'] + DebugFile: str | None = PresymConfig['debug'] + + # 获取编码参数 + encoding: str = self.Args.get('Encoding', 'utf-8') + + # 推断文件类型 + FileType: str | None = 'py' if InputFile.endswith('.py') else 'c' if InputFile.endswith('.c') else None + + # 创建 SymbolFile 对象,传入编码参数 + SymbolFile: SymbolFile = SymbolFile(file=InputFile, type=FileType, encoding=encoding) + + # 调用 PreProcessSymbol + if DebugFile: + BinaryData: bytes + DebugInfo: str + BinaryData, DebugInfo = TransPyC.PreProcessSymbol(SymbolFile, debug=True) + # 写入调试文件 + with open(DebugFile, 'w', encoding=encoding) as f: + f.write(DebugInfo) + info(f"调试信息写入: {DebugFile}") + else: + BinaryData: bytes = TransPyC.PreProcessSymbol(SymbolFile, debug=False) + + # 写入二进制符号文件 + with open(OutputFile, 'wb') as f: + f.write(BinaryData) + + success(f"符号文件生成: {OutputFile}") + return + + InputFile: str = self.Args['Input'] + OutputFile: str = self.Args['Output'] + else: + # 没有命令行参数,使用默认的测试文件名称 + InputFile: str = DEFAULT_INPUT_FILE + OutputFile: str = DEFAULT_OUTPUT_FILE + info('使用默认测试文件: test.py -> test.c') + + FileType: str = DetectFileType(InputFile) + + if FileType == '.py': + self.PythonToC(InputFile, OutputFile) + # 检查是否需要编译和运行 + if 'CompileCommand' in self.Args or self.Args.get('Run', False): + self.CompileAndRun(OutputFile) + elif FileType == '.c': + self.CToPython(InputFile, OutputFile, self.HeaderFiles) + else: + error(f'不支持的文件类型: {FileType}') + sys.exit(1) diff --git a/App/lib/Shell/__init__.py b/App/lib/Shell/__init__.py new file mode 100644 index 0000000..ffec9e5 --- /dev/null +++ b/App/lib/Shell/__init__.py @@ -0,0 +1,6 @@ +"""Shell 包 - TransPyC 命令行入口""" +from lib.Shell.Serializer import SerializeSymbolTable, deSerializeSymbolTable, SymbolFile +from lib.Shell.Config import Config +from lib.Shell.TransPyC import TransPyC + +__all__ = ['SerializeSymbolTable', 'deSerializeSymbolTable', 'SymbolFile', 'Config', 'TransPyC'] diff --git a/App/lib/StubGen/Config.py b/App/lib/StubGen/Config.py new file mode 100644 index 0000000..4e3b135 --- /dev/null +++ b/App/lib/StubGen/Config.py @@ -0,0 +1,43 @@ +"""StubGen 配置模块""" +from __future__ import annotations +import json +from dataclasses import dataclass, field +from typing import List, Dict, Optional + + +@dataclass +class StubGenConfig: + """存根生成器配置""" + InputDir: Optional[str] = None + OutputDir: str = "./stubs" + InputFiles: List[str] = field(default_factory=list) + IncludePatterns: List[str] = field(default_factory=lambda: ["*.h", "*.c", "*.py"]) + ExcludePatterns: List[str] = field(default_factory=list) + TypeMappings: Dict[str, str] = field(default_factory=dict) + PreserveStructure: bool = True # 保持目录结构 + GenerateGuards: bool = True # 生成宏守卫 + verbose: bool = False + DryRun: bool = False + + @classmethod + def from_dict(cls, data: dict) -> StubGenConfig: + """从字典创建配置""" + return cls( + InputDir=data.get('InputDir'), + OutputDir=data.get('OutputDir', './stubs'), + InputFiles=data.get('InputFiles', []), + IncludePatterns=data.get('IncludePatterns', ['*.h', '*.c', '*.py']), + ExcludePatterns=data.get('ExcludePatterns', []), + TypeMappings=data.get('TypeMappings', {}), + PreserveStructure=data.get('PreserveStructure', True), + GenerateGuards=data.get('GenerateGuards', True), + verbose=data.get('verbose', False), + DryRun=data.get('DryRun', False), + ) + + @classmethod + def from_file(cls, FilePath: str) -> StubGenConfig: + """从 JSON 文件加载配置""" + with open(FilePath, 'r', encoding='utf-8') as f: + data: dict = json.load(f) + return cls.from_dict(data) \ No newline at end of file diff --git a/App/lib/StubGen/Converter.py b/App/lib/StubGen/Converter.py new file mode 100644 index 0000000..d7ee5f7 --- /dev/null +++ b/App/lib/StubGen/Converter.py @@ -0,0 +1,983 @@ +"""StubGen 转换器模块 - 将 Python 文件转换为存根格式""" +from __future__ import annotations +import ast +import re +import fnmatch +from typing import List, Dict +from lib.core.SymbolUtils import IsListAnnotation + + +class PythonToStubConverter: + """将 Python 文件转换为存根格式""" + + # __include 别名映射表 {alias: ModuleName} + IncludeAliasMap: Dict[str, str] = {} + + # 变量排除列表 - 匹配这些模式的变量不会被添加到存根文件 + VarExcludeList: List[str] = [] + + # 宏排除列表 - 匹配这些模式的宏不会被添加到存根文件 + MacroExcludeList: List[str] = [] + + @classmethod + def SetIncludeAliasMap(cls, alias_map: Dict[str, str]) -> None: + """设置 __include 别名映射表""" + cls.IncludeAliasMap = alias_map + + @classmethod + def SetVarExcludeList(cls, exclude_list: List[str]) -> None: + """设置变量排除列表""" + cls.VarExcludeList = exclude_list + + @classmethod + def SetMacroExcludeList(cls, exclude_list: List[str]) -> None: + """设置宏排除列表""" + cls.MacroExcludeList = exclude_list + + @classmethod + def _ShouldExcludeVar(cls, VarName: str) -> bool: + """检查变量是否应该被排除""" + for pattern in cls.VarExcludeList: + if fnmatch.fnmatch(VarName, pattern): + return True + return False + + @classmethod + def _ShouldExcludeMacro(cls, MacroName: str) -> bool: + """检查宏是否应该被排除""" + for pattern in cls.MacroExcludeList: + if fnmatch.fnmatch(MacroName, pattern): + return True + return False + + @staticmethod + def convert(PyContent: str, ModuleName: str) -> str: + """将 Python 代码转换为存根格式""" + lines: list[str] = PyContent.split('\n') + StubLines: list[str] = [] + + # 添加文件头 + StubLines.append('"""') + StubLines.append(f'Auto-generated Python stub file from {ModuleName}.py') + StubLines.append(f'Module: {ModuleName}') + StubLines.append('"""') + StubLines.append('') + + # 解析 Python 代码,检查是否已经导入了 t 和 c + HasImportT: bool = False + HasImportC: bool = False + try: + tree: ast.Module = ast.parse(PyContent) + for node in tree.body: + if isinstance(node, ast.Import): + for alias in node.names: + if alias.name == 't': + HasImportT = True + elif alias.name == 'c': + HasImportC = True + except: + pass + + # 添加默认导入(如果源代码中没有) + if not HasImportT: + StubLines.append('import t') + if not HasImportC: + StubLines.append('import c') + if not HasImportT or not HasImportC: + StubLines.append('') + + # 添加 c.CPragma("once") + # StubLines.append('c.CPragma("once")') + StubLines.append('') + + # 生成文件级别宏守卫名称 + #FileGuardName = f'__{ModuleName.upper()}_DEFINE__' + + # 添加文件开头宏守卫 + #StubLines.append(f'c.CIfndef({FileGuardName})') + #StubLines.append(f'{FileGuardName}: t.CDefine') + #StubLines.append('') + + # 解析 Python 代码,提取类型定义 + try: + tree = ast.parse(PyContent) + + # 第一遍扫描:收集 Postdefinition 变量 + PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]} + for node in tree.body: + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation) + # 检查是否是 Postdefinition 类型 + if 't.Postdefinition' in TypeStr: + # 提取引用的类名 + ClassName: str | None = PythonToStubConverter._ExtractPostdefClass(node.annotation) + if ClassName: + if ClassName not in PostdefVars: + PostdefVars[ClassName] = [] + PostdefVars[ClassName].append((node.target.id, TypeStr, node)) + + # 按原始顺序处理节点,保持代码的原始顺序 + PrevType: str | None = None # 记录前一个节点的类型 + + for node in tree.body: + CurrentType: str | None = None + + if isinstance(node, ast.ClassDef): + CurrentType = 'class' + # 如果前一个不是类,添加空行分隔 + if PrevType is not None and PrevType != 'class': + StubLines.append('') + # 检查是否有对应的 Postdefinition 变量 + ClassPostdefs: list[tuple[str, str, ast.AnnAssign]] = PostdefVars.get(node.name, []) + ClassLines: list[str] = PythonToStubConverter._ConvertClass( + node, SourceLines=lines, PostdefVars=ClassPostdefs + ) + StubLines.extend(ClassLines) + elif isinstance(node, ast.FunctionDef): + CurrentType = 'function' + # 如果前一个是类或者是第一个函数,添加空行分隔 + if PrevType == 'class' or (PrevType is not None and PrevType != 'function'): + StubLines.append('') + FuncLines: list[str] = PythonToStubConverter._ConvertFunction(node, SourceLines=lines) + StubLines.extend(FuncLines) + StubLines.append('') # 函数后空行 + elif isinstance(node, ast.AnnAssign): + CurrentType = 'variable' + # 跳过 Postdefinition 变量(已经在类中处理) + TypeStr = PythonToStubConverter._GetTypeString(node.annotation) + if 't.Postdefinition' in TypeStr: + continue + # 检查是否带有 t.CStatic 标志,如果有则不排除 + HasStatic: bool = 't.CStatic' in TypeStr + # 检查变量是否应该被排除 + ShouldExclude: bool = False + if isinstance(node.target, ast.Name) and not HasStatic: + VarName: str = node.target.id + if PythonToStubConverter._ShouldExcludeVar(VarName): + ShouldExclude = True + if ShouldExclude: + continue + # 如果前一个不是变量,添加空行分隔 + if PrevType is not None and PrevType != 'variable': + StubLines.append('') + VarLines: list[str] = PythonToStubConverter._ConvertVariable(node, SourceLines=lines) + # 移除多余的空行 + VarLines = [line for line in VarLines if line.strip()] + StubLines.extend(VarLines) + elif isinstance(node, ast.Import): + # 处理导入语句 + CurrentType = 'import' + ImportStr: str = PythonToStubConverter._GetImportString(node, SourceLines=lines) + if ImportStr: + if PrevType is not None and PrevType != 'import': + StubLines.append('') + StubLines.append(ImportStr) + elif isinstance(node, ast.Assign): + CurrentType = 'variable' + # 处理模块级别的全局变量(无类型标注) + if PrevType is not None and PrevType != 'variable': + StubLines.append('') + AssignLines: list[str] = PythonToStubConverter._ConvertGlobalAssign(node) + StubLines.extend(AssignLines) + elif isinstance(node, ast.ImportFrom): + # 处理 from ... import ... 语句 + CurrentType = 'import' + ImportStr = PythonToStubConverter._GetImportFromString(node, SourceLines=lines) + if ImportStr: + if PrevType is not None and PrevType != 'import': + StubLines.append('') + StubLines.append(ImportStr) + elif isinstance(node, ast.Expr): + # 处理模块级别的宏调用,如 c.CIf(...), c.CEndif() + ExprStr: str = PythonToStubConverter._GetExprString(node.value) + if ExprStr: + CurrentType = 'macro' + StubLines.append(ExprStr) + StubLines.append('') # 宏后空行 + elif isinstance(node, ast.If): + # 处理模块级别的 if 宏条件,如 if c.CIf(FF_MULTI_PARTITION): + IfStr: str = PythonToStubConverter._GetModuleIfMacroString(node) + if IfStr: + CurrentType = 'macro' + StubLines.append(IfStr) + StubLines.append('') # 宏后空行 + + if CurrentType: + PrevType = CurrentType + + # 添加文件结尾宏守卫 + #StubLines.append('') + #StubLines.append('c.CEndif()') + + except SyntaxError as e: + # 如果解析失败,添加注释说明 + StubLines.append(f'# Warning: Failed to parse Python code: {e}') + StubLines.append('# Original content:') + StubLines.append('"""') + StubLines.append(PyContent[:1000]) # 只显示前1000字符 + StubLines.append('"""') + + return '\n'.join(StubLines) + + @staticmethod + def _ConvertClass(node: ast.ClassDef, SourceLines: list[str] | None = None, PostdefVars: list[tuple[str, str, ast.AnnAssign]] | None = None, indent: int = 0) -> list[str]: + """转换类定义 + + Args: + node: 类定义节点 + SourceLines: 源代码行列表 + PostdefVars: Postdefinition 变量列表 [(VarName, TypeStr, node), ...] + indent: 缩进级别(用于嵌套类) + """ + lines: list[str] = [] + PostdefVars = PostdefVars or [] + IndentStr: str = ' ' * indent + + # 处理类装饰器 + for decorator in node.decorator_list: + DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator) + if DecoratorStr: + lines.append(f'{IndentStr}@{DecoratorStr}') + + # 检查是否是结构体、联合体或枚举 + BaseNames: list[str] = [base.attr if isinstance(base, ast.Attribute) else base.id + for base in node.bases + if isinstance(base, (ast.Name, ast.Attribute))] + + # 保留原始基类 + if node.bases: + BaseStrs: list[str] = [] + for base in node.bases: + if isinstance(base, ast.Name): + BaseStrs.append(base.id) + elif isinstance(base, ast.Attribute): + BaseStrs.append(f'{base.value.id}.{base.attr}') + elif isinstance(base, ast.Subscript): + # 处理泛型类型,如 memory_block_t[MAX_ORDER + 1] + BaseStrs.append(PythonToStubConverter._GetTypeString(base)) + ClassTypeParamStr: str = '' + if hasattr(node, 'type_params') and node.type_params: + param_names: list[str] = [tp.name for tp in node.type_params] + ClassTypeParamStr = f'[{", ".join(param_names)}]' + if BaseStrs: + lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}({", ".join(BaseStrs)}):') + else: + lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}:') + else: + ClassTypeParamStr = '' + if hasattr(node, 'type_params') and node.type_params: + param_names = [tp.name for tp in node.type_params] + ClassTypeParamStr = f'[{", ".join(param_names)}]' + lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}:') + + # 处理类成员 + HasMembers: bool = False + seen_member_names: set[str] = set() + for item in node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + seen_member_names.add(item.target.id) + init_members: list[ast.AnnAssign] = [] + for item in node.body: + if isinstance(item, ast.FunctionDef) and item.name == '__init__': + for stmt in item.body: + if (isinstance(stmt, ast.AnnAssign) + and isinstance(stmt.target, ast.Attribute) + and isinstance(stmt.target.value, ast.Name) + and stmt.target.value.id == 'self'): + init_members.append(stmt) + untyped_self_members: list[ast.Assign] = [] + for m_name in [m.target.attr for m in init_members if isinstance(m.target, ast.Attribute)]: + seen_member_names.add(m_name) + for item in node.body: + if isinstance(item, ast.FunctionDef): + for stmt in item.body: + if (isinstance(stmt, ast.Assign) + and len(stmt.targets) == 1 + and isinstance(stmt.targets[0], ast.Attribute) + and isinstance(stmt.targets[0].value, ast.Name) + and stmt.targets[0].value.id == 'self'): + attr_name: str = stmt.targets[0].attr + if attr_name not in seen_member_names: + seen_member_names.add(attr_name) + untyped_self_members.append(stmt) + for item in list(node.body) + init_members + untyped_self_members: + if isinstance(item, ast.Assign): + if (len(item.targets) == 1 + and isinstance(item.targets[0], ast.Attribute) + and isinstance(item.targets[0].value, ast.Name) + and item.targets[0].value.id == 'self'): + HasMembers = True + MemberIndent: str = IndentStr + ' ' + attr_name = item.targets[0].attr + if attr_name in seen_member_names: + continue + InferredType: str = 'int' + if item.value and isinstance(item.value, ast.Constant): + if isinstance(item.value.value, float): + InferredType = 'float' + elif isinstance(item.value.value, bool): + InferredType = 'bool' + elif isinstance(item.value.value, str): + InferredType = 'str' + lines.append(f'{MemberIndent}{attr_name}: {InferredType}') + else: + HasMembers = True + MemberIndent = IndentStr + ' ' + if SourceLines and hasattr(item, 'lineno'): + StartLine: int = item.lineno - 1 + EndLine: int = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1 + for i in range(StartLine, min(EndLine, len(SourceLines))): + lines.append(MemberIndent + SourceLines[i].lstrip()) + elif isinstance(item, ast.AnnAssign): + HasMembers = True + TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) + MemberIndent = IndentStr + ' ' + # __provides__/__requires__/__require_must__ 元数据:保留值以便跨模块加载 + if isinstance(item.target, ast.Name) and item.target.id in ('__provides__', '__requires__', '__require_must__'): + if item.value is not None: + try: + value_str: str = ast.unparse(item.value) + except Exception: + value_str = '' + lines.append(f'{MemberIndent}{item.target.id}: {TypeStr} = {value_str}') + else: + lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}') + continue + # 检查是否是宏变量(类型包含 t.CDefine) + if 't.CDefine' in TypeStr: + # 检查宏变量是否应该被排除 + if isinstance(item.target, ast.Name): + MacroName: str = item.target.id + if PythonToStubConverter._ShouldExcludeMacro(MacroName): + continue # 跳过该宏 + # 宏变量保持原样(包括赋值) + if SourceLines and hasattr(item, 'lineno'): + StartLine = item.lineno - 1 + EndLine = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1 + for i in range(StartLine, min(EndLine, len(SourceLines))): + lines.append(MemberIndent + SourceLines[i].lstrip()) + else: + lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}') + else: + if isinstance(item.target, ast.Attribute): + MemberName: str = item.target.attr + else: + MemberName = item.target.id + FinalTypeStr: str = TypeStr + if IsListAnnotation(item.annotation): + slice_node: ast.AST = item.annotation.slice + has_size: bool = False + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + count_node: ast.AST = slice_node.elts[1] + if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int) and count_node.value > 0: + has_size = True + else: + try: + count_expr: str = ast.unparse(count_node) + except Exception: + count_expr = '' + if count_expr and count_expr != 'None': + has_size = True + elem_type_str: str = PythonToStubConverter._GetTypeString(slice_node.elts[0]) + FinalTypeStr = f't.CArray[{elem_type_str}, {count_expr}]' + if not has_size: + elem_type_str = PythonToStubConverter._GetTypeString(slice_node if not isinstance(slice_node, ast.Tuple) else slice_node.elts[0]) + init_len: int = 0 + if item.value is not None: + if isinstance(item.value, ast.List): + init_len = len(item.value.elts) + elif isinstance(item.value, ast.Constant) and isinstance(item.value.value, str): + init_len = len(item.value.value) + 1 + if init_len > 0: + FinalTypeStr = f't.CArray[{elem_type_str}, {init_len}]' + else: + FinalTypeStr = f't.CArray[{elem_type_str}, None]' + if item.value is not None and isinstance(item.value, ast.Constant): + try: + value_repr: str = ast.unparse(item.value) + lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr} = {value_repr}') + except Exception: + lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}') + else: + lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}') + elif isinstance(item, ast.FunctionDef): + HasMembers = True + FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent+1, SourceLines=SourceLines, class_name=node.name) + lines.extend(FuncStub) + elif isinstance(item, ast.Expr): + # 处理宏调用,如 c.CIf(...), c.CEndif(), c.CElif(), c.CElse() + ExprStr: str = PythonToStubConverter._GetExprString(item.value) + if ExprStr: + lines.append(f'{IndentStr} {ExprStr}') + elif isinstance(item, ast.If): + # 处理 if c.CIf(...): 形式的宏条件 + IfStr: str = PythonToStubConverter._get_if_macro_string(item, indent=indent+1) + if IfStr: + lines.append(IfStr) + elif isinstance(item, ast.ClassDef): + # 处理嵌套类 + HasMembers = True + NestedClassLines: list[str] = PythonToStubConverter._ConvertClass(item, SourceLines=SourceLines, PostdefVars=[], indent=indent+1) + # 移除嵌套类的宏守卫(因为已经在父类的宏守卫内) + FilteredLines: list[str] = [] + for line in NestedClassLines: + # 跳过宏守卫相关行 + if line.strip().startswith('c.CIfndef(') or line.strip().startswith('c.CEndif()'): + continue + if ': t.CDefine' in line and '__' in line: + continue + FilteredLines.append(line) + lines.extend(FilteredLines) + + if not HasMembers: + lines.append(f'{IndentStr} pass') + + # 添加 Postdefinition 变量 + if PostdefVars: + lines.append('') + for VarName, TypeStr, _ in PostdefVars: + lines.append(f'{VarName}: {TypeStr}') + + return lines + + @staticmethod + def _ConvertFunction(node: ast.FunctionDef, indent: int = 0, SourceLines: list[str] | None = None, class_name: str | None = None) -> list[str]: + """转换函数定义""" + lines: list[str] = [] + IndentStr: str = ' ' * indent + + # 检查是否是宏函数(返回类型包含 t.CDefine) + is_macro_func: bool = False + if node.returns: + ReturnType: str = PythonToStubConverter._GetTypeString(node.returns) + if 't.CDefine' in ReturnType: + is_macro_func = True + + # 如果是宏函数,检查是否应该被排除 + if is_macro_func: + if PythonToStubConverter._ShouldExcludeMacro(node.name): + return lines # 返回空列表,排除该宏 + + # 保持原样(包括代码块) + if SourceLines: + StartLine: int = node.lineno - 1 + EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1 + for i in range(StartLine, min(EndLine, len(SourceLines))): + lines.append(SourceLines[i]) + if lines and lines[-1].strip() and not lines[-1].rstrip().endswith(':'): + pass + else: + lines.append(f'{IndentStr} pass') + return lines + + # 处理函数装饰器 + for decorator in node.decorator_list: + DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator) + if DecoratorStr: + lines.append(f'{IndentStr}@{DecoratorStr}') + + # 构建参数列表 + params: list[str] = [] + for arg_idx, arg in enumerate(node.args.args): + ArgName: str = arg.arg + if arg.annotation: + TypeStr: str = PythonToStubConverter._GetTypeString(arg.annotation) + params.append(f'{ArgName}: {TypeStr}') + elif class_name and arg_idx == 0 and ArgName == 'self': + params.append(f'self: {class_name}') + else: + params.append(ArgName) + + # 处理 *args + if node.args.vararg: + ArgName = node.args.vararg.arg + if node.args.vararg.annotation: + TypeStr = PythonToStubConverter._GetTypeString(node.args.vararg.annotation) + params.append(f'*{ArgName}: {TypeStr}') + else: + params.append(f'*{ArgName}') + + # 处理 **kwargs + if node.args.kwarg: + ArgName = node.args.kwarg.arg + if node.args.kwarg.annotation: + TypeStr = PythonToStubConverter._GetTypeString(node.args.kwarg.annotation) + params.append(f'**{ArgName}: {TypeStr}') + else: + params.append(f'**{ArgName}') + + ParamStr: str = ', '.join(params) + + # 返回类型 - 保持原始类型(不添加t.State) + if node.returns: + ReturnType = PythonToStubConverter._GetTypeString(node.returns) + else: + ReturnType = 't.CInt' + + TypeParamStr: str = '' + if hasattr(node, 'type_params') and node.type_params: + param_names: list[str] = [tp.name for tp in node.type_params] + TypeParamStr = f'[{", ".join(param_names)}]' + + lines.append(f'{IndentStr}def {node.name}{TypeParamStr}({ParamStr}) -> {ReturnType}: pass') + return lines + + @staticmethod + def _ConvertVariable(node: ast.AnnAssign, SourceLines: list[str] | None = None) -> list[str]: + """转换变量定义""" + lines: list[str] = [] + if isinstance(node.target, ast.Name): + VarName: str = node.target.id + TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation) + # 检查是否是宏变量(类型包含 t.CDefine) + if 't.CDefine' in TypeStr: + # 检查宏变量是否应该被排除 + if PythonToStubConverter._ShouldExcludeMacro(VarName): + return lines # 返回空列表,排除该宏 + # 宏变量保持原样(包括赋值) + if SourceLines and hasattr(node, 'lineno'): + StartLine: int = node.lineno - 1 + EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1 + for i in range(StartLine, min(EndLine, len(SourceLines))): + lines.append(SourceLines[i]) + else: + lines.append(f'{VarName}: {TypeStr}') + return lines + # 非宏变量:不加 t.State + # 含有 typedef 的变量不加 extern,普通变量需要加 extern + has_typedef: bool = 't.CTypedef' in TypeStr or 'CTypedef' in TypeStr + if has_typedef and node.value is not None: + ValueTypeStr: str = PythonToStubConverter._GetTypeString(node.value) + lines.append(f'{VarName}: {TypeStr} = {ValueTypeStr}') + else: + if not has_typedef and 't.CExtern' not in TypeStr and 't.CStatic' not in TypeStr: + TypeStr = f't.CExtern | {TypeStr}' + lines.append(f'{VarName}: {TypeStr}') + return lines + + @staticmethod + def _ConvertGlobalAssign(node: ast.Assign) -> list[str]: + """转换模块级别的全局变量(无类型标注)""" + lines: list[str] = [] + if not node.targets or not isinstance(node.targets[0], ast.Name): + return lines + + VarName: str = node.targets[0].id + + # 跳过私有变量(以下划线开头) + if VarName.startswith('_'): + return lines + + # 从值推断类型 + inferred_type: str | None = PythonToStubConverter._InferTypeFromValue(node.value) + if inferred_type: + lines.append(f'{VarName}: {inferred_type}') + else: + lines.append(f'{VarName}: t.CInt') + + return lines + + @staticmethod + def _InferTypeFromValue(value: ast.AST) -> str | None: + """从值推断 C 类型""" + if isinstance(value, ast.Constant): + if isinstance(value.value, int): + return 't.CInt' + elif isinstance(value.value, float): + return 't.CDouble' + elif isinstance(value.value, str): + return 't.CCharPtr' + elif isinstance(value.value, bool): + return 't.CInt' + elif isinstance(value, ast.List): + return 't.CVoidPtr' + elif isinstance(value, ast.Dict): + return 't.CVoidPtr' + elif isinstance(value, ast.Name): + return 't.CInt' + elif isinstance(value, ast.BinOp): + left_type: str | None = PythonToStubConverter._InferTypeFromValue(value.left) + if left_type: + return left_type + return 't.CInt' + elif isinstance(value, ast.Call): + # 函数调用返回值,假设为指针类型 + if isinstance(value.func, ast.Name): + func_name: str = value.func.id + if func_name == 'malloc': + return 't.CVoidPtr' + elif func_name in ('ctypes.cast', 'cast'): + return 't.CVoidPtr' + return 't.CVoidPtr' + return None + + @staticmethod + def _GetTypeString(annotation: ast.AST) -> str: + """获取类型字符串""" + if isinstance(annotation, ast.Name): + return annotation.id + elif isinstance(annotation, ast.Attribute): + if isinstance(annotation.value, ast.Name): + return f'{annotation.value.id}.{annotation.attr}' + else: + # 处理嵌套属性访问,如 t.attr.packed + ParentStr: str = PythonToStubConverter._GetTypeString(annotation.value) + return f'{ParentStr}.{annotation.attr}' + elif isinstance(annotation, ast.Subscript): + base: str = PythonToStubConverter._GetTypeString(annotation.value) + if isinstance(annotation.slice, ast.Tuple): + slice_strs: list[str] = [PythonToStubConverter._GetTypeString(e) for e in annotation.slice.elts] + SliceStr: str = ', '.join(slice_strs) + else: + SliceStr = PythonToStubConverter._GetTypeString(annotation.slice) + return f'{base}[{SliceStr}]' + elif isinstance(annotation, ast.BinOp): + # 处理 BinOp,如 t.CExtern | fd_table.__set_default__(...) + left: ast.AST = annotation.left + right: ast.AST = annotation.right + + # 检查右侧是否是 __set_default__ 调用 + if isinstance(right, ast.Call) and isinstance(right.func, ast.Attribute) and right.func.attr in ('__set_default__', '__set_default'): + # 去除 __set_default__,保留左侧,右侧替换为函数名部分 + LeftStr: str = PythonToStubConverter._GetTypeString(left) + RightStr: str = PythonToStubConverter._GetTypeString(right.func.value) + if isinstance(annotation.op, ast.BitOr): + return f'{LeftStr} | {RightStr}' + + LeftStr = PythonToStubConverter._GetTypeString(left) + RightStr = PythonToStubConverter._GetTypeString(right) + if isinstance(annotation.op, ast.BitOr): + return f'{LeftStr} | {RightStr}' + elif isinstance(annotation.op, ast.Add): + return f'{LeftStr} + {RightStr}' + elif isinstance(annotation.op, ast.Sub): + return f'{LeftStr} - {RightStr}' + elif isinstance(annotation.op, ast.Mult): + return f'{LeftStr} * {RightStr}' + elif isinstance(annotation.op, ast.Div): + return f'{LeftStr} / {RightStr}' + elif isinstance(annotation.op, ast.Mod): + return f'{LeftStr} % {RightStr}' + elif isinstance(annotation, ast.UnaryOp): + if isinstance(annotation.op, ast.Not): + operand: str = PythonToStubConverter._GetTypeString(annotation.operand) + return f'not {operand}' + elif isinstance(annotation.op, ast.Invert): + operand = PythonToStubConverter._GetTypeString(annotation.operand) + return f'~{operand}' + elif isinstance(annotation.op, ast.USub): + operand = PythonToStubConverter._GetTypeString(annotation.operand) + return f'-{operand}' + elif isinstance(annotation, ast.Compare): + # 处理比较操作,如 FF_MAX_SS != FF_MIN_SS + left: str = PythonToStubConverter._GetTypeString(annotation.left) + comparators: list[str] = [PythonToStubConverter._GetTypeString(c) for c in annotation.comparators] + ops: list[str] = [] + for op in annotation.ops: + if isinstance(op, ast.Eq): + ops.append('==') + elif isinstance(op, ast.NotEq): + ops.append('!=') + elif isinstance(op, ast.Lt): + ops.append('<') + elif isinstance(op, ast.LtE): + ops.append('<=') + elif isinstance(op, ast.Gt): + ops.append('>') + elif isinstance(op, ast.GtE): + ops.append('>=') + else: + ops.append('?') + result: str = left + for i, op in enumerate(ops): + result += f' {op} {comparators[i]}' + return result + elif isinstance(annotation, ast.Constant): + return repr(annotation.value) + elif isinstance(annotation, ast.Index): + return PythonToStubConverter._GetTypeString(annotation.value) + elif isinstance(annotation, ast.List): + # 处理列表,如 [MAX_FILE_DESCRIPTORS] + elements: list[str] = [PythonToStubConverter._GetTypeString(elem) for elem in annotation.elts] + return f'[{", ".join(elements)}]' + elif isinstance(annotation, ast.Call): + # 处理函数调用形式的类型注解,如 t.Postdefinition(xxa) 或 callable(t.CVoid, app_id = t.CInt) + # 如果是 __set_default__ 调用,只返回函数名部分 + if isinstance(annotation.func, ast.Attribute) and annotation.func.attr in ('__set_default__', '__set_default'): + return PythonToStubConverter._GetTypeString(annotation.func.value) + + FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func) + ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in annotation.args]) + # 处理关键字参数 + keywords_str: str = ', '.join([f'{kw.arg} = {PythonToStubConverter._GetTypeString(kw.value)}' for kw in annotation.keywords]) + # 合并位置参数和关键字参数 + if ArgsStr and keywords_str: + return f'{FuncStr}({ArgsStr}, {keywords_str})' + elif keywords_str: + return f'{FuncStr}({keywords_str})' + else: + return f'{FuncStr}({ArgsStr})' + return 't.CVoid' + + @staticmethod + def _ExtractPostdefClass(annotation: ast.AST) -> str | None: + """从 Postdefinition 类型注解中提取类名 + + 例如:t.Postdefinition(__buddy_system) -> '__buddy_system' + t.Postdefinition(xxx) | t.CTypedef -> 'xxx' + """ + # 处理 BinOp (如: t.Postdefinition(xxx) | t.CTypedef) + if isinstance(annotation, ast.BinOp): + # 递归检查左操作数 + left_result: str | None = PythonToStubConverter._ExtractPostdefClass(annotation.left) + if left_result: + return left_result + # 递归检查右操作数 + right_result: str | None = PythonToStubConverter._ExtractPostdefClass(annotation.right) + if right_result: + return right_result + return None + + # 处理函数调用 (如: t.Postdefinition(__buddy_system)) + if isinstance(annotation, ast.Call): + FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func) + if FuncStr == 't.Postdefinition' and annotation.args: + # 提取第一个参数 + first_arg: ast.AST = annotation.args[0] + if isinstance(first_arg, ast.Name): + return first_arg.id + elif isinstance(first_arg, ast.Attribute): + return f'{first_arg.value.id}.{first_arg.attr}' + + return None + + @staticmethod + def _GetImportString(node: ast.Import, SourceLines: list[str] | None = None) -> str: + """获取导入语句字符串""" + parts: list[str] = [] + IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap + + for alias in node.names: + ModuleName: str = alias.name + # 检查是否是 __include.xxx 格式的导入 + if ModuleName.startswith('__include.'): + # 提取别名(xxx 部分) + SubModule: str = ModuleName[len('__include.'):] + if SubModule in IncludeAliasMap: + # 替换为实际模块路径 + RealModule: str = IncludeAliasMap[SubModule] + if alias.asname: + parts.append(f'{RealModule} as {alias.asname}') + else: + parts.append(f'{RealModule} as {SubModule}') + else: + # 不在映射表中,保持原样 + if alias.asname: + parts.append(f'{ModuleName} as {alias.asname}') + else: + parts.append(ModuleName) + else: + if alias.asname: + parts.append(f'{alias.name} as {alias.asname}') + else: + parts.append(alias.name) + + result: str = f'import {", ".join(parts)}' + + # 提取并保留注释 + if SourceLines and hasattr(node, 'lineno'): + LineIdx: int = node.lineno - 1 + if 0 <= LineIdx < len(SourceLines): + line: str = SourceLines[LineIdx] + CommentMatch: re.Match | None = re.search(r'#.*$', line) + if CommentMatch: + result += ' ' + CommentMatch.group(0) + + return result + + @staticmethod + def _GetImportFromString(node: ast.ImportFrom, SourceLines: list[str] | None = None) -> str: + """获取 from ... import ... 语句字符串""" + module: str = node.module or '' + parts: list[str] = [] + IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap + + # 处理相对导入 (from . import xxx 或 from ..package import xxx) + if node.level > 0: + if module: + if node.level == 1: + full_module: str = f'.{module}' + else: + full_module = '.' * node.level + module + else: + full_module = '.' * node.level + for alias in node.names: + if alias.asname: + parts.append(f'{alias.name} as {alias.asname}') + else: + parts.append(alias.name) + return f'from {full_module} import {", ".join(parts)}' + + # 提取注释(在生成结果前获取) + comment: str = '' + if SourceLines and hasattr(node, 'lineno'): + LineIdx = node.lineno - 1 + if 0 <= LineIdx < len(SourceLines): + line = SourceLines[LineIdx] + CommentMatch: re.Match | None = re.search(r'#.*$', line) + if CommentMatch: + comment = ' ' + CommentMatch.group(0) + + # 检查是否是 from __include import xxx 格式 + if module == '__include': + for alias in node.names: + name: str = alias.name + if name in IncludeAliasMap: + # 替换为实际模块路径 + RealModule: str = IncludeAliasMap[name] + if alias.asname: + parts.append(f'import {RealModule} as {alias.asname}{comment}') + else: + parts.append(f'import {RealModule} as {name}{comment}') + else: + # 不在映射表中,保持原样 + if alias.asname: + parts.append(f'from {module} import {name} as {alias.asname}{comment}') + else: + parts.append(f'from {module} import {name}{comment}') + return '\n'.join(parts) + + # 普通 from ... import ... 语句 + for alias in node.names: + if alias.asname: + parts.append(f'{alias.name} as {alias.asname}') + else: + parts.append(alias.name) + return f'from {module} import {", ".join(parts)}{comment}' + + @staticmethod + def _GetDecoratorString(decorator: ast.AST) -> str: + """获取装饰器字符串""" + if isinstance(decorator, ast.Name): + return decorator.id + elif isinstance(decorator, ast.Attribute): + if isinstance(decorator.value, ast.Name): + return f'{decorator.value.id}.{decorator.attr}' + return decorator.attr + elif isinstance(decorator, ast.Call): + # 处理带参数的装饰器,如 @c.Attribute(t.attr.packed) + FuncStr: str = PythonToStubConverter._GetDecoratorString(decorator.func) + ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in decorator.args]) + return f'{FuncStr}({ArgsStr})' + return '' + + @staticmethod + def _GetExprString(expr: ast.AST) -> str: + """获取表达式字符串(用于宏调用)""" + if isinstance(expr, ast.Call): + # 处理函数调用,如 c.CIf(...), c.CEndif(), c.CUndef(...), c.CPragma(...) + FuncStr: str = PythonToStubConverter._GetDecoratorString(expr.func) + if FuncStr and ('CIf' in FuncStr or 'CEndif' in FuncStr or 'CElif' in FuncStr or 'CElse' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr): + ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in expr.args]) + return f'{FuncStr}({ArgsStr})' if ArgsStr else f'{FuncStr}()' + return '' + + @staticmethod + def _get_if_macro_string(node: ast.If, indent: int = 1) -> str: + """获取类内部的 if 宏字符串(如 if c.CIf(...):)""" + IndentStr: str = ' ' * indent + InnerIndent: str = ' ' * (indent + 1) + # 检查条件是否是宏调用 + if isinstance(node.test, ast.Call): + FuncStr: str = PythonToStubConverter._GetDecoratorString(node.test.func) + if FuncStr and 'CIf' in FuncStr: + ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in node.test.args]) + result: list[str] = [f'{IndentStr}if {FuncStr}({ArgsStr}):' if ArgsStr else f'{IndentStr}if {FuncStr}():'] + # 处理 if 体内的语句 + for item in node.body: + if isinstance(item, ast.AnnAssign): + TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) + result.append(f'{InnerIndent}{item.target.id}: {TypeStr}') + elif isinstance(item, ast.Expr): + ExprStr: str = PythonToStubConverter._GetExprString(item.value) + if ExprStr: + result.append(f'{InnerIndent}{ExprStr}') + return '\n'.join(result) + return '' + + @staticmethod + def _GetModuleIfMacroString(node: ast.If) -> str: + """获取模块级别的 if 宏字符串(如 if c.CIf(FF_MULTI_PARTITION):)""" + return PythonToStubConverter._ProcessIfMacro(node, indent=0) + + @staticmethod + def _ProcessIfMacro(node: ast.If, indent: int = 0) -> str: + """处理 if 宏节点,支持 elif 和 else""" + IndentStr: str = ' ' * indent + + # 检查条件是否是宏调用 + if isinstance(node.test, ast.Call): + FuncStr: str = PythonToStubConverter._GetDecoratorString(node.test.func) + if FuncStr and 'CIf' in FuncStr: + ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in node.test.args]) + result: list[str] = [f'{IndentStr}if {FuncStr}({ArgsStr}):' if ArgsStr else f'{IndentStr}if {FuncStr}():'] + # 处理 if 体内的语句 + for item in node.body: + result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) + + # 处理 elif 和 else + current: ast.If = node + while current.orelse: + if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If): + # elif 情况 + elif_node: ast.If = current.orelse[0] + if isinstance(elif_node.test, ast.Call): + ElifFuncStr: str = PythonToStubConverter._GetDecoratorString(elif_node.test.func) + if ElifFuncStr and 'CIf' in ElifFuncStr: + ElifArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in elif_node.test.args]) + result.append(f'{IndentStr}elif {ElifFuncStr}({ElifArgsStr}):' if ElifArgsStr else f'{IndentStr}elif {ElifFuncStr}():') + for item in elif_node.body: + result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) + current = elif_node + continue + break + else: + # else 情况 + result.append(f'{IndentStr}else:') + for item in current.orelse: + result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) + break + + return '\n'.join(result) + return '' + + @staticmethod + def _ProcessMacroBodyItem(item: ast.AST, indent: int = 0) -> list[str]: + """处理宏体内的单个语句""" + IndentStr: str = ' ' * indent + result: list[str] = [] + + if isinstance(item, ast.ClassDef): + class_stub: list[str] = PythonToStubConverter._ConvertClass(item) + for line in class_stub: + if line.strip(): + result.append(f'{IndentStr}{line}') + else: + result.append(line) + elif isinstance(item, ast.FunctionDef): + FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent) + result.extend(FuncStub) + elif isinstance(item, ast.AnnAssign): + TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) + result.append(f'{IndentStr}{item.target.id}: {TypeStr}') + elif isinstance(item, ast.Expr): + ExprStr: str = PythonToStubConverter._GetExprString(item.value) + if ExprStr: + result.append(f'{IndentStr}{ExprStr}') + elif isinstance(item, ast.If): + # 处理嵌套的 if 宏 + NestedIf: str = PythonToStubConverter._ProcessIfMacro(item, indent=indent) + if NestedIf: + result.append(NestedIf) + + return result \ No newline at end of file diff --git a/App/lib/StubGen/Generator.py b/App/lib/StubGen/Generator.py new file mode 100644 index 0000000..526071f --- /dev/null +++ b/App/lib/StubGen/Generator.py @@ -0,0 +1,313 @@ +"""StubGen 生成器模块 - 存根生成器主类和命令行入口""" +from __future__ import annotations +import sys +import os +import re +import argparse +import logging +import traceback +from pathlib import Path +from typing import List + +from lib.core.VLogger import get_logger as _vlog +from lib.core.stub_generator import CHeaderParser, PythonStubGenerator, CTypeMapper +from lib.StubGen.Config import StubGenConfig +from lib.StubGen.Converter import PythonToStubConverter + + +class StubGen: + """存根生成器主类""" + + def __init__(self, config: StubGenConfig) -> None: + self.config: StubGenConfig = config + self.logger: logging.Logger = self._SetupLogger() + self.GeneratedFiles: List[str] = [] + self.FailedFiles: List[str] = [] + + # 应用自定义类型映射 + if config.TypeMappings: + CTypeMapper.BASIC_TYPE_MAP.update(config.TypeMappings) + + def _SetupLogger(self) -> logging.Logger: + """设置日志""" + logger: logging.Logger = logging.getLogger('StubGen') + logger.setLevel(logging.DEBUG if self.config.verbose else logging.INFO) + + if not logger.handlers: + handler: logging.StreamHandler = logging.StreamHandler(sys.stdout) + formatter: logging.Formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s', + datefmt='%H:%M:%S' + ) + handler.setFormatter(formatter) + logger.addHandler(handler) + + return logger + + def FindInputFiles(self) -> list[tuple[str, str]]: + """查找输入文件,返回 (文件路径, 相对路径) 列表""" + files: list[tuple[str, str]] = [] + + # 添加显式指定的文件 + for FilePath in self.config.InputFiles: + if os.path.exists(FilePath): + RelPath: str = os.path.relpath(FilePath, self.config.InputDir or '.') + files.append((FilePath, RelPath)) + else: + self.logger.warning(f"Input file not found: {FilePath}") + + # 从输入目录查找 + if self.config.InputDir and os.path.exists(self.config.InputDir): + InputPath: Path = Path(self.config.InputDir) + for pattern in self.config.IncludePatterns: + for FilePath in InputPath.rglob(pattern): + # 检查是否在排除列表中 + excluded: bool = False + RelPath: str = os.path.relpath(str(FilePath), self.config.InputDir) + + for exclude_pattern in self.config.ExcludePatterns: + if FilePath.match(exclude_pattern) or RelPath == exclude_pattern: + excluded = True + break + + if not excluded: + files.append((str(FilePath), RelPath)) + + # 去重并保持顺序 + seen: set[str] = set() + UniqueFiles: list[tuple[str, str]] = [] + for FilePath, RelPath in files: + key: str = FilePath + if key not in seen: + seen.add(key) + UniqueFiles.append((FilePath, RelPath)) + + return UniqueFiles + + def _GenerateGuardName(self, FilePath: str) -> str: + """生成宏守卫名称""" + # 获取文件名(不含扩展名) + BaseName: str = os.path.splitext(os.path.basename(FilePath))[0] + + # 转换为大写,替换特殊字符为下划线 + guard: str = re.sub(r'[^a-zA-Z0-9_]', '_', BaseName).upper() + guard = re.sub(r'_+', '_', guard) # 合并多个下划线 + guard = guard.strip('_') + + return f'{guard}_DEFINE_H' + + def _GetOutputPath(self, RelPath: str) -> str: + """获取输出文件路径""" + # 更改扩展名为 .pyi (Python stub file) + BaseName: str = os.path.splitext(RelPath)[0] + OutputRelPath: str = BaseName + '.pyi' + + # 构建完整输出路径 + if self.config.OutputDir: + OutputPath: str = os.path.join(self.config.OutputDir, OutputRelPath) + else: + OutputPath = OutputRelPath + + return OutputPath + + def GenerateStub(self, InputFile: str, RelPath: str) -> bool: + """生成单个存根文件""" + try: + self.logger.info(f"Processing: {InputFile}") + + # 确定输出文件路径 + OutputFile: str = self._GetOutputPath(RelPath) + + # 确保输出目录存在 + OutputDir: str = os.path.dirname(OutputFile) + if OutputDir and not os.path.exists(OutputDir): + if not self.config.DryRun: + os.makedirs(OutputDir, exist_ok=True) + self.logger.debug(f"Created directory: {OutputDir}") + + if self.config.DryRun: + self.logger.info(f"[DRY RUN] Would generate: {OutputFile}") + return True + + # 根据文件类型选择处理方式 + ext: str = os.path.splitext(InputFile)[1].lower() + + if ext in ['.h', '.c']: + # C 文件:使用 CHeaderParser + content: str = self._GenerateFromC(InputFile, RelPath) + elif ext == '.py': + # Python 文件:使用 PythonToStubConverter + content = self._GenerateFromPy(InputFile, RelPath) + else: + self.logger.warning(f"Unsupported file type: {ext}") + return False + + # 写入文件 + with open(OutputFile, 'w', encoding='utf-8') as f: + f.write(content) + + self.GeneratedFiles.append(OutputFile) + self.logger.info(f"Generated: {OutputFile}") + return True + + except Exception as e: + self.logger.error(f"Failed to process {InputFile}: {e}") + traceback.print_exc() + self.FailedFiles.append(InputFile) + return False + + def _GenerateFromC(self, InputFile: str, RelPath: str) -> str: + """从 C 文件生成存根""" + parser: CHeaderParser = CHeaderParser() + parser.parse_file(InputFile) + + generator: PythonStubGenerator = PythonStubGenerator(parser) + ModuleName: str = os.path.splitext(os.path.basename(InputFile))[0] + content: str = generator.generate(ModuleName) + + # 添加宏守卫(如果需要) + if self.config.GenerateGuards: + guard_name: str = self._GenerateGuardName(InputFile) + guard_comment: str = f"\n# Guard: {guard_name}\n" + content = content + guard_comment + + return content + + def _GenerateFromPy(self, InputFile: str, RelPath: str) -> str: + """从 Python 文件生成存根""" + with open(InputFile, 'r', encoding='utf-8') as f: + PyContent: str = f.read() + + ModuleName: str = os.path.splitext(os.path.basename(InputFile))[0] + content: str = PythonToStubConverter.convert(PyContent, ModuleName) + + # 添加宏守卫(如果需要) + if self.config.GenerateGuards: + guard_name: str = self._GenerateGuardName(InputFile) + guard_comment: str = f"\n# Guard: {guard_name}\n" + content = content + guard_comment + + return content + + def run(self) -> bool: + """运行生成器""" + self.logger.info("=" * 60) + self.logger.info("StubGen - C/H/Py to Python Stub Generator") + self.logger.info("=" * 60) + + # 查找输入文件 + InputFiles: list[tuple[str, str]] = self.FindInputFiles() + + if not InputFiles: + self.logger.warning("No input files found!") + return False + + self.logger.info(f"Found {len(InputFiles)} input file(s)") + for FilePath, RelPath in InputFiles: + self.logger.debug(f" - {RelPath}") + + # 处理每个文件 + SuccessCount: int = 0 + for FilePath, RelPath in InputFiles: + if self.GenerateStub(FilePath, RelPath): + SuccessCount += 1 + + # 输出统计信息 + self.logger.info("=" * 60) + self.logger.info(f"Summary: {SuccessCount}/{len(InputFiles)} files generated successfully") + + if self.FailedFiles: + self.logger.warning(f"Failed files ({len(self.FailedFiles)}):") + for f in self.FailedFiles: + self.logger.warning(f" - {f}") + + return SuccessCount == len(InputFiles) + + +def main() -> None: + parser: argparse.ArgumentParser = argparse.ArgumentParser( + description='StubGen - Generate Python stub files from C/H/Py sources', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=''' +Examples: + # 使用配置文件 + %(prog)s -c config.json + + # 处理单个文件 + %(prog)s -i input.h -o output.py + + # 批量处理目录(保持目录结构) + %(prog)s -d ./kernel -o ./kernel/include + + # 干运行(不实际生成文件) + %(prog)s -d ./kernel --dry-run + + # 不生成宏守卫 + %(prog)s -d ./kernel --no-guards + ''' + ) + + # 输入选项 + InputGroup: argparse._MutuallyExclusiveGroup = parser.add_mutually_exclusive_group(required=True) + InputGroup.add_argument('-c', '--config', help='Configuration file (JSON)') + InputGroup.add_argument('-i', '--input', help='Input file') + InputGroup.add_argument('-d', '--directory', help='Input directory') + + # 输出选项 + parser.add_argument('-o', '--output', help='Output directory') + + # 其他选项 + parser.add_argument('--include', action='append', + help='Include patterns (default: *.h, *.c, *.py)') + parser.add_argument('--exclude', action='append', + help='Exclude patterns') + parser.add_argument('--no-guards', action='store_true', + help='Do not generate guard macros') + parser.add_argument('--no-structure', action='store_true', + help='Do not preserve directory structure') + parser.add_argument('-v', '--verbose', action='store_true', + help='Verbose output') + parser.add_argument('--dry-run', action='store_true', + help='Dry run (do not create files)') + + args: argparse.Namespace = parser.parse_args() + + # 加载配置 + if args.config: + if not os.path.exists(args.config): + _vlog().error(f"配置文件未找到: {args.config}") + sys.exit(1) + config: StubGenConfig = StubGenConfig.from_file(args.config) + else: + config = StubGenConfig() + config.IncludePatterns = args.include or ['*.h', '*.c', '*.py'] + config.ExcludePatterns = args.exclude or [] + config.GenerateGuards = not args.no_guards + config.PreserveStructure = not args.no_structure + config.verbose = args.verbose + config.DryRun = args.dry_run + + if args.input: + config.InputFiles = [args.input] + if args.output: + config.OutputDir = os.path.dirname(args.output) or '.' + elif args.directory: + config.InputDir = args.directory + if args.output: + config.OutputDir = args.output + + # 创建生成器并运行 + generator: StubGen = StubGen(config) + + # 如果指定了单个输出文件,直接处理 + if args.input and args.output and not os.path.isdir(args.output): + RelPath: str = os.path.relpath(args.input, config.InputDir or '.') + success: bool = generator.GenerateStub(args.input, RelPath) + else: + success = generator.run() + + sys.exit(0 if success else 1) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/App/lib/StubGen/__init__.py b/App/lib/StubGen/__init__.py new file mode 100644 index 0000000..00a45bd --- /dev/null +++ b/App/lib/StubGen/__init__.py @@ -0,0 +1,6 @@ +"""StubGen 包 - 存根文件生成器""" +from lib.StubGen.Config import StubGenConfig +from lib.StubGen.Converter import PythonToStubConverter +from lib.StubGen.Generator import StubGen, main + +__all__ = ['StubGenConfig', 'PythonToStubConverter', 'StubGen', 'main'] diff --git a/App/lib/_bootstrap.py b/App/lib/_bootstrap.py new file mode 100644 index 0000000..8089950 --- /dev/null +++ b/App/lib/_bootstrap.py @@ -0,0 +1,37 @@ +"""集中化 sys.path 引导模块 + +在首次导入时一次性设置好编译器运行所需的 sys.path: +- 项目根目录:让 ``import TransPyC``、``from StubGen import``、动态 importlib 能解析 +- lib/includes:让用户注解模块中的 ``import t`` / ``import c`` 能解析 + +入口点(TransPyC.py / Projectrans.py)应在最开头 ``import lib._bootstrap``。 +lib 内部模块也可 ``import lib._bootstrap`` 作为安全网,重复导入是无副作用的 no-op。 +""" +from __future__ import annotations + +import os +import sys + +_BOOTSTRAPPED: bool = False + + +def _bootstrap() -> None: + global _BOOTSTRAPPED + if _BOOTSTRAPPED: + return + _BOOTSTRAPPED = True + + # lib/_bootstrap.py 所在目录 + _LibDir: str = os.path.dirname(os.path.abspath(__file__)) + # 项目根目录 = lib 的父目录 + _ProjectRoot: str = os.path.dirname(_LibDir) + # lib/includes 目录(编译器内部类型系统 t.py / c.py) + _IncludesDir: str = os.path.join(_LibDir, 'includes') + + if _ProjectRoot not in sys.path: + sys.path.insert(0, _ProjectRoot) + if _IncludesDir not in sys.path: + sys.path.append(_IncludesDir) + + +_bootstrap() diff --git a/App/lib/constants/__init__.py b/App/lib/constants/__init__.py new file mode 100644 index 0000000..013292a --- /dev/null +++ b/App/lib/constants/__init__.py @@ -0,0 +1 @@ +"""全局常量定义""" diff --git a/App/lib/constants/config.py b/App/lib/constants/config.py new file mode 100644 index 0000000..250a029 --- /dev/null +++ b/App/lib/constants/config.py @@ -0,0 +1,43 @@ +# 常量定义 +from __future__ import annotations + +DEFAULT_INPUT_FILE: str = 'test.py' +DEFAULT_OUTPUT_FILE: str = 'test.ll' + +SLICE_THRESHOLD: int = 50 +SLICE_LEVEL_DEFAULT: int = 3 +SLICE_LEVELS: dict[int | str, str] = { + 0: 'no_optimize', + 1: 'conservative', + 2: 'moderate', + 3: 'aggressive', + 's': 'size', +} + +DEFAULT_COMPILE_COMMAND: str = 'gcc' +DEFAULT_COMPILE_FLAGS: str = '' + +DEFAULT_METADATA: dict[str, str] = { + 'PROJECT_NAME': 'TransPyC Project', + 'AUTHOR': 'TransPyC Team', + 'VERSION': '1.0.0', + 'DESCRIPTION': 'Auto-generated LLVM IR file', + 'COPYRIGHT_YEAR': '2026', + 'COPYRIGHT_HOLDER': 'Galactic Vorpal Software Development Studio (e.g. GVSDS Inc.)' +} + +ERROR_MESSAGES: dict[str, str] = { + 'MISSING_ARGS': 'Missing required arguments -f and/or -o', + 'UNKNOWN_ARG': 'Unknown argument', + 'UNSUPPORTED_TYPE': 'Unsupported file type', + 'FAILED_PARSE': 'Failed to parse file', + 'COMPILE_FAILED': 'Compilation or execution failed' +} + +HELP_MESSAGE: str = ''' +Usage: python TransPyC.py -f InputFile -o OutputFile [-wh header_files] [-debug DebugFile] + [-cc compile_command] [-cflags CompileFlags] [-run] [-args RunArgs] [-h HelperFiles] + -h: Specify helper files (C or Python) to help identify structs, functions, variables, and ptrs +''' + +mode: str = "strict" diff --git a/App/lib/core/CTypeInfo.py b/App/lib/core/CTypeInfo.py new file mode 100644 index 0000000..2e64c17 --- /dev/null +++ b/App/lib/core/CTypeInfo.py @@ -0,0 +1,409 @@ +"""CTypeInfo — C 类型信息对象,从 HandlesBase 中提取以避免 llvmlite 导入链。 + +本模块不导入 llvmlite,因此可以被 Projectrans 构建脚本安全导入。 +""" +from __future__ import annotations +from typing import TYPE_CHECKING, List, Any +if TYPE_CHECKING: + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator + +import ast +from enum import IntFlag, auto + +from lib.includes import t +from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES + + +class FuncMeta(IntFlag): + NONE = 0 + STATIC_METHOD = auto() + PROPERTY_GETTER = auto() + PROPERTY_SETTER = auto() + PROPERTY_DELETER = auto() + CLASS_METHOD = auto() + ABSTRACT = auto() + + +class _Delegated: + """CTypeInfo 旧字段名委托描述符 — 静态委托到 _ts/_sm 的字段 + + 替代旧的 FIELD_ROUTES + __setattr__/__getattr__ 动态路由: + 描述符在类创建时绑定,访问时直接走 __get__/__set__, + 无 dict 查找和字符串比较的运行时开销。 + """ + __slots__ = ('_target_attr', '_field_name') + + def __init__(self, target_attr: str, field_name: str) -> None: + self._target_attr: str = target_attr # '_ts' or '_sm' + self._field_name: str = field_name + + def __get__(self, obj: Any, objtype: type | None = None) -> Any: + if obj is None: + return self + target: Any = object.__getattribute__(obj, self._target_attr) + return getattr(target, self._field_name) + + def __set__(self, obj: Any, value: Any) -> None: + target: Any = object.__getattribute__(obj, self._target_attr) + setattr(target, self._field_name, value) + + +class CTypeInfo: + """ + C 类型信息对象 - 统一符号表条目和类型计算 + + 设计原则: + - BaseType 是 CType 实例(来自 t 模块),永远不使用硬编码字符串 + - 描述符分为两种: + - VarConst/VarVolatile: 变量本身(指针)的限定符,如 int * const + - DataConst/DataVolatile: 指向数据的限定符,如 const int * + + 属性(类型计算): + - BaseType: t.CType - 基础类型实例 (如 t.CInt(), t.CVoid()) + - PtrCount: int - 指针层数 + - VarConst: bool - 变量本身(指针)是否 const,如 int * const + - VarVolatile: bool - 变量本身(指针)是否 volatile + - DataConst: bool - 指向的数据是否 const,如 const int * + - DataVolatile: bool - 指向的数据是否 volatile + - ArrayDims: List[str] - 数组维度 + - Storage: t.CType - 存储类 (static, extern 等) + - IsFuncPtr: bool - 是否函数指针 + - FuncPtrParams: List[Tuple[str, CTypeInfo]] - 函数指针参数列表,每个元素是 (参数名, 参数类型) + - FuncPtrReturn: CTypeInfo - 函数指针返回类型 + - IsBitField: bool - 是否位域 + - BitWidth: int - 位域宽度 + - IsTypedef/IsStruct/IsEnum/IsUnion: bool - 类型种类 + + 属性(符号表元数据): + - Name: str - 名称(struct/enum/union/typedef 名) + - Members: Dict[str, CTypeInfo] - 成员字典(用于 struct/union) + - OriginalType: str - typedef 的原始类型 + - DeclaredType: str - 声明类型 + - CreturnTypes: list - C 返回类型列表 + - Lineno: int - 定义行号 + - IsAnonymous: bool - 是否匿名类型 + - IsCpythonObject: bool - 是否 CPython 对象 + - IsEnumMember: bool - 是否枚举成员 + """ + + def __init__(self) -> None: + object.__setattr__(self, '_ts', TypeSpec()) + sm: SymbolMeta = SymbolMeta() + sm.meta_list = FuncMeta.NONE + object.__setattr__(self, '_sm', sm) + + # --- 新公共 API:供新代码直接访问 TypeSpec / SymbolMeta / SymbolKind --- + @property + def ts(self) -> TypeSpec: + """TypeSpec — 纯类型描述(只读)""" + return self._ts + + @property + def sm(self) -> SymbolMeta: + """SymbolMeta — 符号表元数据(只读)""" + return self._sm + + @property + def kind(self) -> 'SymbolKind': + """SymbolKind — 符号类型种类枚举""" + return self._sm.kind + + # 注:旧字段名(PtrCount/IsStruct/Name 等)的委托通过 _Delegated 描述符实现, + # 在模块末尾从 FIELD_ROUTES 静态绑定,无需 __setattr__/__getattr__ 动态路由 + + + @property + def BaseType(self) -> t.CType | tuple | None: + return self._ts._base_type + + @BaseType.setter + def BaseType(self, value: t.CType | type | tuple | List | None) -> None: + """设置 BaseType,接受以下类型: + + - None: 清空 + - t.CType 实例: 单个类型,自动设置 IsStruct/IsEnum/IsUnion 标志 + - type (CType 子类): 如 t.CStruct,自动实例化 + - tuple/list of t.CType: 多个组合类型 + """ + ts: TypeSpec = self._ts + sm: SymbolMeta = self._sm + + if value is None: + ts._base_type = None + sm.is_struct = False + sm.is_enum = False + sm.is_union = False + sm.is_renum = False + return + + if isinstance(value, (tuple, list)): + ts._base_type = tuple(value) + for v in value: + if isinstance(v, t.CStruct): + sm.is_struct = True + elif isinstance(v, t.CEnum): + sm.is_enum = True + elif isinstance(v, t.CUnion): + sm.is_union = True + elif isinstance(v, t.REnum): + sm.is_renum = True + sm.is_enum = True + return + + if isinstance(value, type) and issubclass(value, t.CType): + ts._base_type = value() + return + + if isinstance(value, t.CType): + ts._base_type = value + if isinstance(value, t.CStruct): + sm.is_struct = True + sm.is_enum = False + sm.is_union = False + sm.is_renum = False + elif isinstance(value, t.REnum): + sm.is_renum = True + sm.is_enum = True + sm.is_struct = False + sm.is_union = False + elif isinstance(value, t.CEnum): + sm.is_enum = True + sm.is_struct = False + sm.is_union = False + sm.is_renum = False + elif isinstance(value, t.CUnion): + sm.is_union = True + sm.is_struct = False + sm.is_enum = False + sm.is_renum = False + return + + raise TypeError(f"BaseType must be t.CType instance, type subclass, or tuple of t.CType, got {type(value)}") + + @property + def TypeCls(self) -> type | None: + """获取 CType 类(从 BaseType 推断)""" + bt: t.CType | tuple | None = self._ts._base_type + if bt: + return type(bt) + sm: SymbolMeta = self._sm + if sm.is_struct: + return t.CStruct + if sm.is_enum: + return t.CEnum + if sm.is_union: + return t.CUnion + if sm.is_typedef: + return t._CTypedef + return None + + @property + def IsPtr(self) -> bool: + return self._ts.ptr_count > 0 + + @property + def IsVoid(self) -> bool: + return isinstance(self._ts._base_type, t.CVoid) + + @property + def IsStr(self) -> bool: + return isinstance(self._ts._base_type, t.CChar) and self._ts.ptr_count > 0 + + @property + def IsInt(self) -> bool: + bt: t.CType | tuple | None = self._ts._base_type + return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is not None and bt.IsSigned is True and not isinstance(bt, t.CVoid) + + @property + def IsUInt(self) -> bool: + bt: t.CType | tuple | None = self._ts._base_type + return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is False + + @property + def IsFloat(self) -> bool: + bt: t.CType | tuple | None = self._ts._base_type + return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is None and getattr(bt, 'Size', 0) in (32, 64, 128) and not isinstance(bt, t.CVoid) + + def ToString(self) -> str: + ts: TypeSpec = self._ts + sm: SymbolMeta = self._sm + bt: t.CType | tuple | None = ts._base_type + + if ts.is_func_ptr: + return 'Callable' + parts: list[str] = [] + + if ts.storage and not isinstance(ts.storage, t.CExport): + parts.append(type(ts.storage).__name__) + + if ts.data_const or ts.data_volatile: + qualifiers: list[str] = [] + if ts.data_const: + qualifiers.append("const") + if ts.data_volatile: + qualifiers.append("volatile") + parts.append("_".join(qualifiers)) + + if sm.is_typedef and sm.name: + return sm.name + elif sm.is_renum and sm.name: + base_name: str = sm.name + elif bt: + if isinstance(bt, str): + base_name = bt + elif isinstance(bt, type) and issubclass(bt, t.CType): + base_name = bt.__name__ + elif isinstance(bt, t.CType): + # 对命名的 CType(CStruct/CEnum/CUnion/REnum),优先使用其 name 属性 + # 否则回退到类名(如 'CInt'、'CChar') + _named: str | None = getattr(bt, 'name', None) + base_name = _named if _named else type(bt).__name__ + else: + base_name = str(bt) + else: + base_name = "void" + + if ts.is_array_ptr: + ptr_str: str = "" + if ts.ptr_count > 0: + ptr_str = "*" * ts.ptr_count + if ts.array_ptr: + inner: str = ts.array_ptr.ToString() + if ptr_str: + parts.append(f"{base_name} {ptr_str}({inner})") + else: + parts.append(f"{base_name} ({inner})") + else: + if ptr_str: + parts.append(f"{base_name} {ptr_str}()") + else: + parts.append(f"{base_name} ()") + elif ts.array_ptr: + inner = ts.array_ptr.ToString() + if ts.ptr_count > 0: + parts.append(f"{base_name} {'*' * ts.ptr_count}({inner})") + else: + parts.append(f"{base_name} ({inner})") + else: + parts.append(base_name) + if ts.ptr_count > 0: + parts.append("*" * ts.ptr_count) + if ts.var_const: + parts.append("const") + if ts.var_volatile: + parts.append("volatile") + + for dim in ts.array_dims: + if dim: + parts.append(f"[{dim}]") + else: + parts.append("[]") + + return " ".join(parts) if parts else "void" + + @staticmethod + def CreateFromTypeName(TypeName: str) -> "CTypeInfo": + """从类型名字符串创建 CTypeInfo(兼容旧 API)""" + info: CTypeInfo = CTypeInfo() + if TypeName.startswith('struct '): + info.BaseType = t.CStruct(name=TypeName[7:].strip()) if TypeName[7:].strip() else t.CStruct() + elif TypeName.startswith('enum '): + info.BaseType = t.CEnum(name=TypeName[5:].strip()) if TypeName[5:].strip() else t.CEnum() + elif TypeName.startswith('union '): + info.BaseType = t.CUnion(name=TypeName[6:].strip()) if TypeName[6:].strip() else t.CUnion() + else: + info.BaseType = t.CStruct(name=TypeName) + return info + + # LLVM primitive type name → (CTypeClass, PtrCount) mapping + # Used when generic type inference produces LLVM type names like 'i64', 'double', etc. + _LLVM_PRIMITIVE_MAP: dict[str, tuple[type, int]] | None = None + + @classmethod + def _get_llvm_primitive_map(cls) -> dict[str, tuple[type, int]]: + if cls._LLVM_PRIMITIVE_MAP is not None: + return cls._LLVM_PRIMITIVE_MAP + cls._LLVM_PRIMITIVE_MAP = { + 'void': (t.CVoid, 0), + 'i1': (t.CInt, 0), + 'i8': (t.CChar, 0), + 'i16': (t.CShort, 0), + 'i32': (t.CInt, 0), + 'i64': (t.CLong, 0), + 'i128': (t.CInt, 0), + 'float': (t.CFloat, 0), + 'double': (t.CDouble, 0), + 'fp128': (t.CFloat128T, 0), + } + return cls._LLVM_PRIMITIVE_MAP + + @staticmethod + def FromTypeName(TypeName: str) -> "CTypeInfo": + """从简单类型名构造 CTypeInfo(不经过 FromStr 字符串解析)""" + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver + return TypeAnnotationResolver.from_type_name(TypeName) + + @staticmethod + def TryEvalConstExpr(node: ast.AST, SymbolTable: Any) -> Any: + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver + return TypeAnnotationResolver.try_eval_const_expr(node, SymbolTable) + + @classmethod + def FromNode(cls, Node: ast.AST, SymbolTable: Any) -> "CTypeInfo": + """从 AST 节点解析 CTypeInfo(类方法) + + Args: + Node: AST 节点(如 ast.Name, ast.Attribute 等) + SymbolTable: 符号表字典 + """ + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver + return TypeAnnotationResolver.from_node(Node, SymbolTable) + + + def Copy(self) -> "CTypeInfo": + NewInfo: CTypeInfo = CTypeInfo() + NewInfo._ts = self._ts.copy() + NewInfo._sm = self._sm.copy() + # CTypeInfo 引用字段需要深拷贝 + if isinstance(NewInfo._ts.func_ptr_return, CTypeInfo): + NewInfo._ts.func_ptr_return = NewInfo._ts.func_ptr_return.Copy() + if isinstance(NewInfo._ts.original_type, CTypeInfo): + NewInfo._ts.original_type = NewInfo._ts.original_type.Copy() + if NewInfo._ts.array_ptr: + NewInfo._ts.array_ptr = NewInfo._ts.array_ptr.Copy() + return NewInfo + + def get(self, key: str, default: Any = None) -> Any: + """获取额外属性""" + return self._sm._extra.get(key, default) + + def set(self, key: str, value: Any) -> None: + """设置额外属性""" + self._sm._extra[key] = value + + @staticmethod + def VoidTypeInfo() -> "CTypeInfo": + info: CTypeInfo = CTypeInfo() + info.BaseType = t.CVoid() + return info + + def ToLLVM(self, Gen: LlvmCodeGenerator) -> Any: + from llvmlite import ir as _ir + if Gen is None: + return _ir.IntType(32) + return Gen._ctype_to_llvm(self) + + def __str__(self) -> str: + return self.ToString() + + def __repr__(self) -> str: + ts: TypeSpec = self._ts + sm: SymbolMeta = self._sm + return f"CTypeInfo(BaseType={ts._base_type}, PtrCount={ts.ptr_count}, IsTypedef={sm.is_typedef}, OriginalType={ts.original_type!r}, VarConst={ts.var_const}, DataConst={ts.data_const})" + + +# 从 FIELD_ROUTES 静态绑定 _Delegated 描述符到 CTypeInfo 类 +# 这是一次性配置(类创建期执行),替代运行时 __setattr__/__getattr__ 动态路由 +for _old_name, (_target, _field) in FIELD_ROUTES.items(): + setattr(CTypeInfo, _old_name, _Delegated('_' + _target, _field)) +del _old_name, _target, _field \ No newline at end of file diff --git a/App/lib/core/ConstEvaluator.py b/App/lib/core/ConstEvaluator.py new file mode 100644 index 0000000..9897e2b --- /dev/null +++ b/App/lib/core/ConstEvaluator.py @@ -0,0 +1,515 @@ +"""统一常量表达式求值器 + +整合了原先分散在 5 处的常量求值逻辑: + - SymbolTable._eval_const_expr + - CTypeInfo.TryEvalConstExpr + - ConstEvalMixin._try_eval_const_expr + - HandlesAssign._eval_const_expr + - HandlesIf._eval_const_expr + +ConstEvaluator 提供三个级别的求值: + 1. eval_basic(node) — 纯 AST 常量 + 算术(无外部依赖) + 2. eval_with_symtab(node) — + SymbolTable 中的 define 查找 + 3. eval_full(node, ctx) — + Gen._define_constants / _all_define_constants / 平台宏 +""" +from __future__ import annotations + +import ast +from typing import Any, Callable, Optional, TYPE_CHECKING + +import llvmlite.ir as ir + +from lib.core.Handles.HandlesBase import CTypeInfo + +if TYPE_CHECKING: + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator + + +class ConstEvaluator: + """统一常量表达式求值器""" + + # ================================================================== + # Level 2: + SymbolTable define 查找 + # ================================================================== + + @staticmethod + def eval_with_symtab(node: ast.AST, symtab: Any) -> Optional[Any]: + """求值常量表达式,支持从 SymbolTable 查找 define 值。 + + 支持: Constant, BinOp, UnaryOp, Name(define), Attribute(define) + """ + if isinstance(node, ast.Constant): + return node.value + if isinstance(node, ast.Name): + if hasattr(symtab, '__contains__') and node.id in symtab: + info: CTypeInfo = symtab[node.id] + if info.IsDefine and isinstance(info.DefineValue, int): + return info.DefineValue + return None + if isinstance(node, ast.Attribute): + # 先尝试 t.CSizeT().Size 模式(类型构造调用 + .Size 属性) + size_val = ConstEvaluator._eval_ctype_size_attr(node) + if size_val is not None: + return size_val + return ConstEvaluator._eval_attribute_define(node, symtab) + return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_with_symtab(n, symtab)) + + # ================================================================== + # Level 3: + Gen._define_constants / _all_define_constants / 平台宏 + # ================================================================== + + @staticmethod + def eval_full(node: ast.AST, ctx: 'EvalContext') -> Optional[Any]: + """完整求值,支持 define_constants、平台宏、BoolOp、Compare。 + + ctx: EvalContext 实例,封装了 Gen、SymbolTable 等上下文。 + """ + # BoolOp (HandlesIf 逻辑) + if isinstance(node, ast.BoolOp): + if isinstance(node.op, ast.And): + for val in node.values: + result: Any = ConstEvaluator.eval_full(val, ctx) + if result is None: + return None + if not result: + return 0 + return 1 + elif isinstance(node.op, ast.Or): + for val in node.values: + result: Any = ConstEvaluator.eval_full(val, ctx) + if result is None: + return None + if result: + return 1 + return 0 + return None + + # Compare (HandlesIf 逻辑) + if isinstance(node, ast.Compare): + left: Any = ConstEvaluator.eval_full(node.left, ctx) + if left is None: + return None + op: ast.cmpop + comparator: ast.AST + for op, comparator in zip(node.ops, node.comparators): + right: Any = ConstEvaluator.eval_full(comparator, ctx) + if right is None: + return None + result: bool | None = ConstEvaluator._eval_compare_op(op, left, right) + if result is None: + return None + if not result: + return 0 + left = right + return 1 + + # Constant — 直接返回原始值,bool→0/1 转换由 HandlesIf 自行处理 + if isinstance(node, ast.Constant): + return node.value + + # Name — 查 define_constants / SymbolTable / 平台宏 + if isinstance(node, ast.Name): + return ctx.lookup_name(node.id) + + # Attribute — 查 define_constants / SymbolTable / 平台宏 + if isinstance(node, ast.Attribute): + # 先尝试 t.CSizeT().Size 模式(类型构造调用 + .Size 属性) + size_val = ConstEvaluator._eval_ctype_size_attr(node) + if size_val is not None: + return size_val + return ctx.lookup_attribute(node) + + # UnaryOp + if isinstance(node, ast.UnaryOp): + operand: Any = ConstEvaluator.eval_full(node.operand, ctx) + if operand is None: + return None + if isinstance(node.op, ast.Not): + return 1 if not operand else 0 + if isinstance(node.op, ast.USub): + return -operand + if isinstance(node.op, ast.UAdd): + return +operand + if isinstance(node.op, ast.Invert): + return ~operand + return None + + # Call — 编译时函数调用(如 ctraits.isptr(x)) + if isinstance(node, ast.Call): + return ConstEvaluator._eval_compile_time_call(node, ctx) + + # BinOp + return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_full(n, ctx)) + + # ================================================================== + # 内部辅助 + # ================================================================== + + @staticmethod + def _eval_compile_time_call(node: ast.Call, ctx: 'EvalContext') -> Optional[Any]: + """处理编译时函数调用,如 ctraits.isptr(x)""" + func: ast.expr = node.func + func_name: str | None = None + module_name: str | None = None + + if isinstance(func, ast.Attribute): + if isinstance(func.value, ast.Name): + module_name = func.value.id + func_name = func.attr + elif isinstance(func, ast.Name): + func_name = func.id + + # ctraits.isptr(x) — 带模块前缀 + # isptr(x) — 通过 from ctraits import isptr 导入 + is_isptr: bool = False + if module_name == 'ctraits' and func_name == 'isptr': + is_isptr = True + elif func_name == 'isptr' and not module_name: + # 检查是否通过 from ctraits import isptr 导入 + t_c_imported = getattr(ctx.translator, '_t_c_imported_names', {}) if ctx.translator else {} + if func_name in t_c_imported: + src_module, _ = t_c_imported[func_name] + if src_module == 'ctraits': + is_isptr = True + # 也检查符号表中是否有 ctraits.isptr + if not is_isptr and ctx.symtab: + sym = ctx.symtab.lookup('ctraits.isptr') if hasattr(ctx.symtab, 'lookup') else None + if sym: + is_isptr = True + + if is_isptr and node.args and ctx.translator: + try: + arg_node: ast.expr = node.args[0] + # 局部变量:优先通过 LLVM 类型判断(GetCTypeInfo 无法解析局部变量名) + if ctx.Gen and isinstance(arg_node, ast.Name): + var_name: str = arg_node.id + variables: dict = getattr(ctx.Gen, 'variables', {}) + if var_name in variables: + var_val: Any = variables[var_name] + if hasattr(var_val, 'type') and isinstance(var_val.type, ir.PointerType): + pointee: ir.Type = var_val.type.pointee + if isinstance(pointee, ir.PointerType): + return 1 + return 0 + # 类型名 / typedef:通过 TypeMergeHandler 获取 CTypeInfo + type_info = ctx.translator.TypeMergeHandler.GetCTypeInfo(arg_node) + if type_info and (type_info.IsPtr or type_info.PtrCount > 0): + return 1 + if type_info and not (type_info.IsPtr or type_info.PtrCount > 0): + return 0 + return None + except Exception: + return None + return None + + @staticmethod + def _eval_arith(node: ast.AST, recurse: Callable[[ast.AST], Any]) -> Optional[Any]: + """通用算术求值:BinOp + UnaryOp""" + if isinstance(node, ast.BinOp): + left: Any = recurse(node.left) + right: Any = recurse(node.right) + if left is None or right is None: + return None + return ConstEvaluator._apply_binop(node.op, left, right) + if isinstance(node, ast.UnaryOp): + operand: Any = recurse(node.operand) + if operand is None: + return None + if isinstance(node.op, ast.USub): + return -operand + if isinstance(node.op, ast.UAdd): + return +operand + if isinstance(node.op, ast.Invert): + return ~operand + return None + return None + + @staticmethod + def _apply_binop(op: ast.operator, left: Any, right: Any) -> Optional[Any]: + """应用二元运算符""" + try: + if isinstance(op, ast.Add): + return left + right + elif isinstance(op, ast.Sub): + return left - right + elif isinstance(op, ast.Mult): + return left * right + elif isinstance(op, ast.Div): + return left // right if isinstance(left, int) and isinstance(right, int) else left / right + elif isinstance(op, ast.FloorDiv): + if isinstance(right, (int, float)) and right == 0: + return None + return left // right + elif isinstance(op, ast.Mod): + if isinstance(right, (int, float)) and right == 0: + return None + return left % right + elif isinstance(op, ast.Pow): + return left ** right + elif isinstance(op, ast.LShift): + return left << right + elif isinstance(op, ast.RShift): + return left >> right + elif isinstance(op, ast.BitOr): + return left | right + elif isinstance(op, ast.BitXor): + return left ^ right + elif isinstance(op, ast.BitAnd): + return left & right + except Exception: + return None + return None + + @staticmethod + def _eval_compare_op(op: ast.cmpop, left: Any, right: Any) -> Optional[bool]: + """应用比较运算符""" + if isinstance(op, ast.Eq): + return left == right + elif isinstance(op, ast.NotEq): + return left != right + elif isinstance(op, ast.Lt): + return left < right + elif isinstance(op, ast.LtE): + return left <= right + elif isinstance(op, ast.Gt): + return left > right + elif isinstance(op, ast.GtE): + return left >= right + return None + + @staticmethod + def _eval_ctype_size_attr(node: ast.Attribute) -> Optional[int]: + """求值 t.CSizeT().Size 等类型构造调用 + .Size 属性模式。 + + 匹配 AST 形态: Attribute(attr='Size', value=Call(func=Attribute(value=Name(id='t'), attr='CSizeT'))) + 或直接 Call(func=Name(id='CSizeT'))(from t import CSizeT 后的使用方式)。 + 返回类型大小(位数),如 CSizeT().Size 返回 64(64 位平台)。 + """ + if node.attr != 'Size': + return None + if not isinstance(node.value, ast.Call): + return None + call_node: ast.Call = node.value + if call_node.args or call_node.keywords: + return None + func: ast.expr = call_node.func + type_name: str | None = None + if isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name) and func.value.id == 't': + type_name = func.attr + elif isinstance(func, ast.Name): + type_name = func.id + if not type_name: + return None + # 延迟导入避免循环依赖 + try: + from lib.includes.t import CTypeRegistry + except Exception: + return None + cls = CTypeRegistry.GetClassByName(type_name) + if cls is None: + return None + try: + instance = cls() + size_val = getattr(instance, 'Size', None) + if isinstance(size_val, int): + return size_val + except Exception: + pass + return None + + @staticmethod + def _eval_attribute_define(node: ast.Attribute, symtab: Any) -> Optional[Any]: + """从 SymbolTable 查找 Attribute 形式的 define 值""" + parts: list[str] = [] + current: ast.AST = node + while isinstance(current, ast.Attribute): + parts.append(current.attr) + current = current.value + if isinstance(current, ast.Name): + parts.append(current.id) + parts.reverse() + attr_name: str = parts[-1] + possible_keys: list[str] = [attr_name, '.'.join(parts)] + for key in possible_keys: + if hasattr(symtab, '__contains__') and key in symtab: + info: CTypeInfo = symtab[key] + if info.IsDefine and isinstance(info.DefineValue, int): + return info.DefineValue + # 模糊匹配 + if len(parts) >= 2: + mod_key: str + mod_info: CTypeInfo + for mod_key, mod_info in (symtab.items() if hasattr(symtab, 'items') else []): + if mod_info.IsDefine and isinstance(mod_info.DefineValue, int): + if mod_key.endswith('.' + attr_name) or mod_key == attr_name: + return mod_info.DefineValue + return None + + @staticmethod + def _intify(val: Any) -> Any: + """float → int 转换(仅当 float 值恰好是整数时)""" + if isinstance(val, float) and val == int(val): + return int(val) + return val + + +class EvalContext: + """常量求值上下文,封装 Gen / SymbolTable / 平台宏查找逻辑。 + + 由调用方构造并传入 ConstEvaluator.eval_full()。 + """ + + def __init__(self, Gen: LlvmCodeGenerator | None = None, symtab: Any = None, platform_macros: dict | None = None, translator: Any = None): + self.Gen: LlvmCodeGenerator | None = Gen + self.symtab: Any = symtab + self._platform_macros: dict | None = platform_macros + self.translator: Any = translator + + def lookup_name(self, name: str) -> Optional[Any]: + """查找 Name 节点的值:define_constants → SymbolTable → 平台宏""" + # 1. Gen._define_constants + if self.Gen and name in self.Gen._define_constants: + val: Any = self.Gen._define_constants[name] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + + # 2. Gen._DefineConstants (大写版本) + if self.Gen and hasattr(self.Gen, '_DefineConstants') and name in self.Gen._DefineConstants: + val: Any = self.Gen._DefineConstants[name] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + + # 3. SymbolTable + if self.symtab and name in self.symtab: + info: CTypeInfo = self.symtab[name] + val: Any = info.value + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + if info.IsDefine and isinstance(info.DefineValue, (int, float)): + return ConstEvaluator._intify(info.DefineValue) + + # 4. 平台宏 + macros: dict = self._get_platform_macros() + if name in macros: + return macros[name] + + return None + + def lookup_attribute(self, node: ast.Attribute) -> Optional[Any]: + """查找 Attribute 节点的值""" + module_name: str | None = node.value.id if isinstance(node.value, ast.Name) else None + attr_name: str = node.attr + + if module_name and attr_name: + combined_key: str = f"{module_name}.{attr_name}" + + # 1. Gen._define_constants + if self.Gen and combined_key in self.Gen._define_constants: + val: Any = self.Gen._define_constants[combined_key] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + + # 2. SymbolTable (精确匹配 + 后缀匹配) + if self.symtab: + key: str + info: CTypeInfo + for key, info in self.symtab.items(): + if key.endswith(f".{combined_key}") or key == combined_key: + if info.IsDefine and isinstance(info.DefineValue, (int, float)): + return ConstEvaluator._intify(info.DefineValue) + + # 3. _all_define_constants + all_dc: dict = self._get_all_define_constants() + if combined_key in all_dc: + val: Any = all_dc[combined_key] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + if attr_name in all_dc: + val: Any = all_dc[attr_name] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + dc_key: str + dc_val: Any + for dc_key, dc_val in all_dc.items(): + if dc_key == attr_name or dc_key.endswith(f".{attr_name}"): + if isinstance(dc_val, (int, float)): + return ConstEvaluator._intify(dc_val) + + # 4. SymbolTable 短名查找 + if self.symtab and attr_name in self.symtab: + info: CTypeInfo = self.symtab[attr_name] + if info.IsDefine and isinstance(info.DefineValue, (int, float)): + return ConstEvaluator._intify(info.DefineValue) + + # 5. Gen._define_constants 短名 + if self.Gen and attr_name in self.Gen._define_constants: + val: Any = self.Gen._define_constants[attr_name] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + + # 6. 平台宏 + macros: dict = self._get_platform_macros() + full_key: str | None = self._get_attr_full_name(node) + if full_key: + if full_key in macros: + return macros[full_key] + short_name: str = full_key.split('.')[-1] if '.' in full_key else full_key + if short_name in macros: + return macros[short_name] + + return None + + def _get_all_define_constants(self) -> dict: + """获取 _all_define_constants""" + if self.Gen and hasattr(self.Gen, '_all_define_constants'): + return self.Gen._all_define_constants + return {} + + def _get_platform_macros(self) -> dict: + """获取平台宏""" + if self._platform_macros is not None: + return self._platform_macros + if not self.Gen: + return {} + macros: dict[str, int] = {} + pi: dict = self.Gen._platform_info + ptr_size: int = self.Gen.ptr_size + if pi.get('is_windows'): + macros['_WIN32'] = 1 + if not pi.get('is_32bit'): + macros['_WIN64'] = 1 + macros['WIN64'] = 1 + macros['WIN32'] = 1 + if pi.get('is_linux'): + macros['__linux__'] = 1 + if pi.get('is_macos'): + macros['__APPLE__'] = 1 + macros['__MACH__'] = 1 + if pi.get('is_x86_64'): + macros['__x86_64__'] = 1 + macros['__x86_64'] = 1 + macros['_M_X64'] = 1 + elif pi.get('is_arm64'): + macros['__aarch64__'] = 1 + macros['_M_ARM64'] = 1 + if not pi.get('is_32bit') and pi.get('is_linux'): + macros['__LP64__'] = 1 + elif pi.get('is_32bit'): + macros['_ILP32'] = 1 + macros['SIZEOF_VOID_P'] = ptr_size + macros['__SIZEOF_POINTER__'] = ptr_size + macros['NDEBUG'] = 0 + return macros + + @staticmethod + def _get_attr_full_name(node: ast.Attribute) -> Optional[str]: + """从 Attribute 节点提取完整名称 (e.g. 'module.name')""" + parts: list[str] = [] + current: ast.AST = node + while isinstance(current, ast.Attribute): + parts.append(current.attr) + current = current.value + if isinstance(current, ast.Name): + parts.append(current.id) + parts.reverse() + return '.'.join(parts) if parts else None diff --git a/App/lib/core/DecoratorPass.py b/App/lib/core/DecoratorPass.py new file mode 100644 index 0000000..475f3f3 --- /dev/null +++ b/App/lib/core/DecoratorPass.py @@ -0,0 +1,547 @@ +""" +DecoratorPass - Viper 语言自定义装饰器的 IR 层包装转换 (v4) + +v4 增强版特性: +1. 栈帧局部上下文 (ctx):替代全局变量,解决并发/递归状态错乱 +2. 流程劫持:handler 返回 i32 控制原函数调用次数 + - 返回 0:跳过原函数调用(@cache, @mock) + - 返回 1:正常调用一次(默认) + - 返回 N > 1:循环调用 N 次(@repeat) +3. alwaysinline 属性:消除 wrapper 调用开销 +4. 参数打包与修改:pre-phase 后从 args_struct 读回参数,装饰器可修改入参 +5. 返回值修改:ret_ptr 可写,装饰器可在后置阶段修改返回值 +6. 递归装饰保护:最外层 wrapper 检查全局标志位,递归调用时跳过所有装饰逻辑 + +装饰器调用约定 (v4): + i32 decor_name(i8* ctx, i8* func_name, i32 phase, + i8* args_ptr, i8* ret_ptr, i8* decor_args_ptr) + + 参数说明: + ctx : i8* - 栈帧局部上下文缓冲区(32字节),每次调用独立分配 + 前置阶段可写入状态,后置阶段可读取,生命周期绑定本次调用 + 多线程/递归完全隔离,替代全局变量 + func_name : i8* - 原始函数名字符串 + phase : i32 - 执行阶段(0=前置, 1=后置) + args_ptr : i8* - 指向函数参数打包结构体(可读写,无参数时为 null) + 前置阶段可写入修改参数值,wrapper 会从结构体读回修改后的参数 + ret_ptr : i8* - 指向返回值(前置阶段为 null,void 函数始终为 null) + 后置阶段可读写,装饰器可修改返回值 + decor_args_ptr : i8* - 指向装饰器参数打包结构体(无参数装饰器为 null) + + 返回值(仅前置阶段有效,后置阶段忽略): + 0 : 跳过原函数调用(@cache, @mock, @skip) + 1 : 调用原函数一次(默认行为,@log, @timing, @trace) + N>1: 循环调用原函数 N 次(@repeat, @benchmark) + + 编译器生成的 wrapper(以 add(i32, i32) -> i32, @log 为例): + define i32 @__decor_wrap_add(i32 %a, i32 %b) alwaysinline { + entry: + ; 递归保护:检查标志位 + %rec_val = Load i8, i8* @__decor_rec___decor_wrap_add + %is_rec = icmp ne i8 %rec_val, 0 + br i1 %is_rec, label %recursive_call, label %decorated_entry + + recursive_call: + ; 递归调用:跳过所有装饰逻辑,直接调用原函数 + %rec_result = call i32 @add(i32 %a, i32 %b) + ret i32 %rec_result + + decorated_entry: + ; 设置递归保护标志 + store i8 1, i8* @__decor_rec___decor_wrap_add + + ; 分配栈帧局部上下文 + %ctx = alloca [32 x i8] + %ctx_ptr = bitcast [32 x i8]* %ctx to i8* + + ; 打包函数参数到结构体(装饰器可通过 args_ptr 修改) + %args = alloca {i32, i32} + store i32 %a, i32* getelementptr({i32, i32}* %args, i32 0, i32 0) + store i32 %b, i32* getelementptr({i32, i32}* %args, i32 0, i32 1) + %args_ptr = bitcast {i32, i32}* %args to i8* + + ; 分配返回值存储(零初始化) + %ret = alloca i32 + store i32 0, i32* %ret + + ; 前置阶段:handler 返回调用次数 + %n = call i32 @log(i8* %ctx_ptr, i8* "add", i32 0, + i8* %args_ptr, i8* null, i8* null) + br label %loop.header + + loop.header: + %i = phi i32 [0, %decorated_entry], [%i.next, %loop.body] + %cond = icmp slt i32 %i, %n + br i1 %cond, label %loop.body, label %post + + loop.body: + ; 从结构体读回参数(装饰器可能已修改) + %a.Loaded = Load i32, i32* getelementptr({i32, i32}* %args, i32 0, i32 0) + %b.Loaded = Load i32, i32* getelementptr({i32, i32}* %args, i32 0, i32 1) + %result = call i32 @add(i32 %a.Loaded, i32 %b.Loaded) + store i32 %result, i32* %ret + %i.next = add i32 %i, 1 + br label %loop.header + + post: + %ret_ptr = bitcast i32* %ret to i8* + call i32 @log(i8* %ctx_ptr, i8* "add", i32 1, + i8* %args_ptr, i8* %ret_ptr, i8* null) + + ; 清除递归保护标志 + store i8 0, i8* @__decor_rec___decor_wrap_add + + %final = Load i32, i32* %ret + ret i32 %final + } + +链式装饰器(从下到上嵌套): + @log + @timing + def f(x) -> int: + 等价于 log(timing(f)) + 执行顺序:log_pre → timing_pre → f → timing_post → log_post + 生成:__decor_wrap_f (log) → __decor_wrap_f_timing (timing) → f + 递归保护仅在最外层 wrapper 生效,递归调用直接跳到原始 f +""" +from __future__ import annotations +import llvmlite.ir as ir +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator + + +# 栈帧局部上下文缓冲区大小(字节) +_CTX_SIZE: int = 32 + +# 装饰器处理函数的 LLVM 签名:i32 (i8*, i8*, i32, i8*, i8*, i8*) +_DECOR_HANDLER_PARAM_TYPES: ir.FunctionType | None = None # 延迟初始化 + + +def _get_decor_handler_func_type() -> ir.FunctionType: + """获取装饰器处理函数的 LLVM FunctionType: i32 (i8*, i8*, i32, i8*, i8*, i8*)""" + global _DECOR_HANDLER_PARAM_TYPES + if _DECOR_HANDLER_PARAM_TYPES is None: + i8_ptr: ir.PointerType = ir.IntType(8).as_pointer() + _DECOR_HANDLER_PARAM_TYPES = ir.FunctionType( + ir.IntType(32), + [i8_ptr, i8_ptr, ir.IntType(32), i8_ptr, i8_ptr, i8_ptr] + ) + return _DECOR_HANDLER_PARAM_TYPES + + +def _find_or_declare_handler(module: ir.Module, handler_name: str) -> ir.Function: + """在 module 中查找或声明装饰器处理函数""" + f: ir.Function + for f in module.functions: + if f.name == handler_name: + return f + func_type: ir.FunctionType = _get_decor_handler_func_type() + handler: ir.Function = ir.Function(module, func_type, name=handler_name) + return handler + + +def _make_string_constant(module: ir.Module, text: str, name_prefix: str = "decor.str") -> ir.Constant: + """在 module 中创建一个全局字符串常量,返回 i8* 指针""" + counter: int = getattr(_make_string_constant, '_counter', 0) + 1 + _make_string_constant._counter = counter + const_name: str = f"{name_prefix}.{counter}" + + encoded: bytes = text.encode('utf-8') + b'\x00' + const_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(encoded)) + const_val: ir.Constant = ir.Constant(const_type, bytearray(encoded)) + + global_var: ir.GlobalVariable = ir.GlobalVariable(module, const_type, name=const_name) + global_var.global_constant = True + global_var.linkage = 'private' + global_var.initializer = const_val + + return global_var.bitcast(ir.IntType(8).as_pointer()) + + +def _make_decor_args_constant(module: ir.Module, decorator_info: dict) -> ir.Constant: + """ + 为带参数的装饰器生成全局常量结构体,返回 i8* 指针。 + 无参数装饰器返回 null。 + + 支持的参数类型: + - int → i32 / i64 + - float → f64 + - bool → i1 + - str → i8*(全局字符串常量) + """ + deco_args: list = decorator_info.get('args', []) + deco_kwargs: dict = decorator_info.get('kwargs', {}) + + if not deco_args and not deco_kwargs: + return ir.Constant(ir.IntType(8).as_pointer(), None) + + field_types: list = [] + field_values: list = [] + + arg: int | str | float | bool + for arg in deco_args: + llvm_type: ir.Type | None + llvm_val: ir.Constant | None + llvm_type, llvm_val = _python_value_to_llvm(module, arg) + if llvm_type is None: + return ir.Constant(ir.IntType(8).as_pointer(), None) + field_types.append(llvm_type) + field_values.append(llvm_val) + + kw: str + for kw in sorted(deco_kwargs.keys()): + llvm_type: ir.Type | None + llvm_val: ir.Constant | None + llvm_type, llvm_val = _python_value_to_llvm(module, deco_kwargs[kw]) + if llvm_type is None: + return ir.Constant(ir.IntType(8).as_pointer(), None) + field_types.append(llvm_type) + field_values.append(llvm_val) + + struct_type: ir.LiteralStructType = ir.LiteralStructType(field_types) + struct_const: ir.Constant = ir.Constant(struct_type, field_values) + + counter: int = getattr(_make_decor_args_constant, '_counter', 0) + 1 + _make_decor_args_constant._counter = counter + global_name: str = f"__decor_args.{counter}" + + global_var: ir.GlobalVariable = ir.GlobalVariable(module, struct_type, name=global_name) + global_var.global_constant = True + global_var.linkage = 'private' + global_var.initializer = struct_const + + return global_var.bitcast(ir.IntType(8).as_pointer()) + + +def _python_value_to_llvm(module: ir.Module, value: int | str | float | bool) -> tuple[ir.Type | None, ir.Constant | None]: + """将 Python 值转换为 (LLVM类型, LLVM常量) 元组。""" + if isinstance(value, bool): + return ir.IntType(1), ir.Constant(ir.IntType(1), int(value)) + elif isinstance(value, int): + if -2**31 <= value < 2**31: + return ir.IntType(32), ir.Constant(ir.IntType(32), value) + else: + return ir.IntType(64), ir.Constant(ir.IntType(64), value) + elif isinstance(value, float): + return ir.DoubleType(), ir.Constant(ir.DoubleType(), value) + elif isinstance(value, str): + str_const = _make_string_constant(module, value, name_prefix="decor.arg") + return ir.IntType(8).as_pointer(), str_const + else: + return None, None + + +def _ZeroConstant(llvm_type: ir.Type) -> ir.Constant | None: + """为给定 LLVM 类型创建零值常量""" + if isinstance(llvm_type, ir.IntType): + return ir.Constant(llvm_type, 0) + elif isinstance(llvm_type, (ir.FloatType, ir.DoubleType)): + return ir.Constant(llvm_type, 0.0) + elif isinstance(llvm_type, ir.PointerType): + return ir.Constant(llvm_type, None) + elif isinstance(llvm_type, ir.ArrayType): + elem_zero: ir.Constant | None = _ZeroConstant(llvm_type.element) + if elem_zero is None: + return None + return ir.Constant(llvm_type, [elem_zero] * llvm_type.count) + else: + return None + + +def _generate_single_wrapper(module: ir.Module, original_func: ir.Function, wrapper_name: str, func_name_str: str, + decorator_info: dict, is_export: bool = False, + is_outermost: bool = False, true_original_func: ir.Function | None = None) -> ir.Function: + """ + 为单个装饰器生成一层 wrapper 函数。 + + v4 Wrapper 结构(最外层含递归保护): + entry → 递归保护检查(仅最外层) + recursive_call → 递归时直接调用原函数,跳过所有装饰(仅最外层) + decorated_entry → 设置递归标志,分配 ctx/args/ret,调用 handler 前置 + loop.header → phi 计数器,比较 i < n_calls + loop.body → 从 args_struct 读回参数(支持修改),调用原函数 + post → 调用 handler 后置,清除递归标志,返回结果 + """ + decor_name: str = decorator_info['name'] + handler: ir.Function = _find_or_declare_handler(module, decor_name) + func_name_const: ir.Constant = _make_string_constant(module, func_name_str, name_prefix="decor.fn") + + func_type: ir.FunctionType = original_func.ftype + return_type: ir.Type = func_type.return_type + param_types: list[ir.Type] = [p.type for p in original_func.args] + is_void: bool = isinstance(return_type, ir.VoidType) + + wrapper_type: ir.FunctionType = ir.FunctionType(return_type, param_types) + wrapper: ir.Function = ir.Function(module, wrapper_type, name=wrapper_name) + + # 添加 alwaysinline 属性,优化器完全内联消除调用开销 + wrapper.attributes.add('alwaysinline') + + i32: ir.IntType = ir.IntType(32) + i8: ir.IntType = ir.IntType(8) + i8_ptr: ir.PointerType = ir.IntType(8).as_pointer() + phase_pre: ir.Constant = ir.Constant(i32, 0) + phase_post: ir.Constant = ir.Constant(i32, 1) + null_ptr: ir.Constant = ir.Constant(i8_ptr, None) + zero_i32: ir.Constant = ir.Constant(i32, 0) + one_i32: ir.Constant = ir.Constant(i32, 1) + one_i8: ir.Constant = ir.Constant(i8, 1) + zero_i8: ir.Constant = ir.Constant(i8, 0) + + # 递归保护:仅最外层 wrapper 生成 + rec_flag: ir.GlobalVariable | None = None + if is_outermost and true_original_func is not None: + rec_flag_name: str = f"__decor_rec_{wrapper_name}" + # 检查是否已存在 + rec_flag = None + g: ir.GlobalValue + for g in module.global_values: + if g.name == rec_flag_name: + rec_flag = g + break + if rec_flag is None: + rec_flag = ir.GlobalVariable(module, i8, name=rec_flag_name) + rec_flag.global_constant = False + rec_flag.linkage = 'internal' + rec_flag.initializer = zero_i8 + + # 创建基本块 + entry_block: ir.Block = wrapper.append_basic_block("entry") + + recursive_call_block: ir.Block | None + decorated_entry_block: ir.Block | None + if rec_flag is not None: + recursive_call_block = wrapper.append_basic_block("recursive_call") + decorated_entry_block = wrapper.append_basic_block("decorated_entry") + else: + recursive_call_block = None + decorated_entry_block = None + + loop_header_block: ir.Block = wrapper.append_basic_block("loop.header") + loop_body_block: ir.Block = wrapper.append_basic_block("loop.body") + post_block: ir.Block = wrapper.append_basic_block("post") + + # ==== Entry block ==== + builder: ir.IRBuilder = ir.IRBuilder(entry_block) + + loop_predecessor: ir.Block + if rec_flag is not None: + # 递归保护:检查标志位 + rec_val: ir.LoadInstr = builder.load(rec_flag, name="rec_val") + is_recursive: ir.ICMPInstr = builder.icmp_signed('!=', rec_val, zero_i8) + builder.cbranch(is_recursive, recursive_call_block, decorated_entry_block) + + # ==== Recursive call block ==== + builder = ir.IRBuilder(recursive_call_block) + # 递归调用:直接调用真正的原函数,跳过所有装饰逻辑 + rec_call_args: list[ir.Argument] = [arg for arg in wrapper.args] + rec_ret_val: ir.CallInstr = builder.call(true_original_func, rec_call_args) + if is_void: + builder.ret_void() + else: + builder.ret(rec_ret_val) + + # ==== Decorated entry block ==== + builder = ir.IRBuilder(decorated_entry_block) + # 设置递归保护标志 + builder.store(one_i8, rec_flag) + + # 记录 loop.header 的前置块为 decorated_entry_block + loop_predecessor = decorated_entry_block + else: + loop_predecessor = entry_block + + # 1. 分配栈帧局部上下文(每次调用独立,线程安全,递归安全) + ctx_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), _CTX_SIZE) + ctx_alloca: ir.AllocaInstr = builder.alloca(ctx_type, name="ctx") + ctx_ptr: ir.BitCastInstr = builder.bitcast(ctx_alloca, i8_ptr) + + # 2. 打包函数参数到结构体(装饰器可通过 args_ptr 读写修改) + args_alloca: ir.AllocaInstr | None = None + args_struct_type: ir.LiteralStructType | None = None + if param_types: + args_struct_type = ir.LiteralStructType(param_types) + args_alloca = builder.alloca(args_struct_type, name="args") + zero: ir.Constant = ir.Constant(i32, 0) + i: int + arg: ir.Argument + for i, arg in enumerate(wrapper.args): + field_idx: ir.Constant = ir.Constant(i32, i) + gep: ir.GEPInstr = builder.gep(args_alloca, [zero, field_idx], inbounds=True) + builder.store(arg, gep) + args_ptr: ir.BitCastInstr | ir.Constant = builder.bitcast(args_alloca, i8_ptr) + else: + args_ptr = null_ptr + + # 3. 分配返回值存储(零初始化,确保跳过原函数时返回安全默认值) + ret_alloca: ir.AllocaInstr | None + if not is_void: + ret_alloca = builder.alloca(return_type, name="ret") + zero_val: ir.Constant | None = _ZeroConstant(return_type) + if zero_val is not None: + builder.store(zero_val, ret_alloca) + else: + ret_alloca = None + + # 4. 获取装饰器参数指针 + decor_args_ptr: ir.Constant = _make_decor_args_constant(module, decorator_info) + + # 5. 前置阶段:handler 返回调用次数 + n_calls: ir.CallInstr = builder.call(handler, [ctx_ptr, func_name_const, phase_pre, + args_ptr, null_ptr, decor_args_ptr]) + + # 6. 跳转到循环头 + builder.branch(loop_header_block) + + # ==== Loop header block ==== + builder = ir.IRBuilder(loop_header_block) + i_phi: ir.PhiInstr = builder.phi(i32, "i") + i_phi.add_incoming(zero_i32, loop_predecessor) + cond: ir.ICMPInstr = builder.icmp_signed('<', i_phi, n_calls) + builder.cbranch(cond, loop_body_block, post_block) + + # ==== Loop body block ==== + builder = ir.IRBuilder(loop_body_block) + + # v4: 从 args_struct 读回参数(装饰器可能在 pre-phase 中修改了参数) + call_args: list[ir.LoadInstr | ir.Argument] = [] + if args_alloca is not None and args_struct_type is not None: + zero = ir.Constant(i32, 0) + for i in range(len(param_types)): + field_idx: ir.Constant = ir.Constant(i32, i) + gep: ir.GEPInstr = builder.gep(args_alloca, [zero, field_idx], inbounds=True) + Loaded_arg: ir.LoadInstr = builder.load(gep, name=f"arg.{i}") + call_args.append(Loaded_arg) + + ret_val: ir.CallInstr = builder.call(original_func, call_args) + if ret_alloca is not None: + builder.store(ret_val, ret_alloca) + i_next: ir.AddInstr = builder.add(i_phi, one_i32) + i_phi.add_incoming(i_next, loop_body_block) + builder.branch(loop_header_block) + + # ==== Post block ==== + builder = ir.IRBuilder(post_block) + + # 后置阶段 + ret_ptr: ir.BitCastInstr | ir.Constant + if ret_alloca is not None: + ret_ptr = builder.bitcast(ret_alloca, i8_ptr) + else: + ret_ptr = null_ptr + builder.call(handler, [ctx_ptr, func_name_const, phase_post, + args_ptr, ret_ptr, decor_args_ptr]) + + # 递归保护:清除标志(必须在 post-phase 之后、return 之前) + if rec_flag is not None: + builder.store(zero_i8, rec_flag) + + # 返回 + if is_void: + builder.ret_void() + else: + final_ret: ir.LoadInstr = builder.load(ret_alloca, name="final_ret") + builder.ret(final_ret) + + return wrapper + + +def _redirect_calls(module: ir.Module, old_func: ir.Function, new_func: ir.Function, skip_func_names: set[str] | None = None) -> None: + """ + 替换 module 中所有对 old_func 的调用指令为对 new_func 的调用。 + 跳过 skip_func_names 中的函数(wrapper 函数内部不应被重定向)。 + """ + if skip_func_names is None: + skip_func_names = set() + + func: ir.Function + for func in module.functions: + if func.name in skip_func_names: + continue + if func.name == new_func.name: + continue + block: ir.Block + for block in func.blocks: + instr: ir.Instruction + for instr in list(block.instructions): + if not isinstance(instr, ir.CallInstr): + continue + callee: ir.Function = instr.callee + if isinstance(callee, ir.Function) and callee is old_func: + instr.callee = new_func + + +def run(Gen: LlvmCodeGenerator) -> None: + """ + 执行 DecoratorPass。 + + 处理流程: + 1. 遍历 _decorated_funcs 中所有带装饰标记的函数 + 2. 对每个函数,按装饰器列表从底到顶生成嵌套 wrapper + 3. 将原函数改为 internal 链接 + 4. 替换所有调用点 + """ + if not Gen._decorated_funcs: + return + + module: ir.Module = Gen.module + decorated: dict = Gen._decorated_funcs.copy() + + mangled_name: str + info: dict + for mangled_name, info in decorated.items(): + decorators: list = info['decorators'] + func_name: str = info['func_name'] + is_export: bool = info['is_export'] + + # 查找原始函数 + original_func: ir.Function | None = None + f: ir.Function + for f in module.functions: + if f.name == mangled_name: + original_func = f + break + if original_func is None: + continue + + # 原函数改为 internal 链接(仅 wrapper 调用) + original_func.linkage = 'internal' + + # 按装饰器列表从底到顶生成嵌套 wrapper + # Python 装饰器顺序:@a @b def f() => a(b(f)) + # 执行顺序:先应用最靠近函数的 @b,再应用 @a + + current_func: ir.Function = original_func + all_wrapper_names: set[str] = set() + + i: int + for i in range(len(decorators) - 1, -1, -1): + deco: dict = decorators[i] + is_outermost: bool = (i == 0) + deco_name: str = deco['name'] + + wrapper_name: str + if is_outermost: + wrapper_name = f"__decor_wrap_{mangled_name}" + else: + wrapper_name = f"__decor_wrap_{mangled_name}_{deco_name}" + + all_wrapper_names.add(wrapper_name) + + wrapper: ir.Function = _generate_single_wrapper( + module, current_func, wrapper_name, + func_name, deco, is_export=is_export, + is_outermost=is_outermost, + true_original_func=original_func if is_outermost else None + ) + current_func = wrapper + + # 更新 functions 映射,使原函数名指向最外层 wrapper + Gen.functions[mangled_name] = current_func + if func_name in Gen.functions: + Gen.functions[func_name] = current_func + + # 替换所有非 wrapper 函数中对原函数的调用 + _redirect_calls(module, original_func, current_func, all_wrapper_names) diff --git a/App/lib/core/DiagnosticCollector.py b/App/lib/core/DiagnosticCollector.py new file mode 100644 index 0000000..33b7357 --- /dev/null +++ b/App/lib/core/DiagnosticCollector.py @@ -0,0 +1,48 @@ +"""诊断系统 — 收集编译过程中的警告和错误 + +替代原先静默回退的模式: + - return 'i32' → 记录 Warning + 回退到 'i32' + - return [] → 记录 Error + 回退到 [] + +使用方式: + diag = DiagnosticCollector() + diag.warn(file, lineno, "无法解析类型,回退到 i32") + diag.error(file, lineno, f"加载模块失败: {path}") + diag.report() # 打印所有诊断信息 +""" +from __future__ import annotations + +import os +from dataclasses import dataclass, field +from enum import Enum +from typing import List + + +class DiagnosticLevel(Enum): + WARNING = "WARNING" + ERROR = "ERROR" + + +@dataclass +class Diagnostic: + level: DiagnosticLevel + file: str + lineno: int + message: str + + def __str__(self) -> str: + short_file: str = os.path.basename(self.file) if self.file else "" + return f"[{self.level.value}] {short_file}:{self.lineno} — {self.message}" + + +class DiagnosticCollector: + """诊断信息收集器""" + + def __init__(self) -> None: + self._diagnostics: list[Diagnostic] = [] + + def warn(self, file: str, lineno: int, message: str) -> None: + self._diagnostics.append(Diagnostic(DiagnosticLevel.WARNING, file, lineno, message)) + + def error(self, file: str, lineno: int, message: str) -> None: + self._diagnostics.append(Diagnostic(DiagnosticLevel.ERROR, file, lineno, message)) diff --git a/App/lib/core/Exportable.py b/App/lib/core/Exportable.py new file mode 100644 index 0000000..ca6f9d7 --- /dev/null +++ b/App/lib/core/Exportable.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +""" +导出表模块 - 用于收集和存储文件的公开符号信息 +""" +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Dict, List, Optional, Any + + +@dataclass +class EnumMember: + """枚举成员信息""" + name: str + value: int + lineno: int + + +@dataclass +class EnumInfo: + """枚举类型信息""" + name: str + members: List[EnumMember] = field(default_factory=list) + lineno: int = 0 + is_public: bool = True + + +@dataclass +class FunctionParam: + """函数参数信息""" + name: str + type_name: str + is_pointer: bool = False + + +@dataclass +class FunctionInfo: + """函数信息""" + name: str + return_type: str + params: List[FunctionParam] = field(default_factory=list) + lineno: int = 0 + is_public: bool = True + + +@dataclass +class StructMember: + """结构体成员信息""" + name: str + type_name: str + is_pointer: bool = False + array_size: Optional[int] = None + + +@dataclass +class StructInfo: + """结构体类型信息""" + name: str + members: List[StructMember] = field(default_factory=list) + lineno: int = 0 + is_public: bool = True + + +@dataclass +class TypedefInfo: + """类型别名信息""" + name: str + original_type: str + lineno: int = 0 + is_public: bool = True + + +@dataclass +class Exportable: + """导出表 - 存储文件的所有公开符号""" + filename: str + enums: List[EnumInfo] = field(default_factory=list) + functions: List[FunctionInfo] = field(default_factory=list) + structs: List[StructInfo] = field(default_factory=list) + typedefs: List[TypedefInfo] = field(default_factory=list) + + def add_enum(self, name: str, lineno: int, is_public: bool = True) -> EnumInfo: + """添加枚举类型""" + enum_info: EnumInfo = EnumInfo(name=name, lineno=lineno, is_public=is_public) + self.enums.append(enum_info) + return enum_info + + def add_function(self, name: str, return_type: str, lineno: int, is_public: bool = True) -> FunctionInfo: + """添加函数""" + func_info: FunctionInfo = FunctionInfo(name=name, return_type=return_type, lineno=lineno, is_public=is_public) + self.functions.append(func_info) + return func_info + + def add_struct(self, name: str, lineno: int, is_public: bool = True) -> StructInfo: + """添加结构体""" + struct_info: StructInfo = StructInfo(name=name, lineno=lineno, is_public=is_public) + self.structs.append(struct_info) + return struct_info + + def add_typedef(self, name: str, original_type: str, lineno: int, is_public: bool = True) -> TypedefInfo: + """添加类型别名""" + typedef_info: TypedefInfo = TypedefInfo(name=name, original_type=original_type, lineno=lineno, is_public=is_public) + self.typedefs.append(typedef_info) + return typedef_info + + def to_dict(self) -> Dict[str, Any]: + """转换为字典格式""" + return { + 'filename': self.filename, + 'enums': [ + { + 'name': enum.name, + 'lineno': enum.lineno, + 'is_public': enum.is_public, + 'members': [ + {'name': m.name, 'value': m.value, 'lineno': m.lineno} + for m in enum.members + ] + } + for enum in self.enums + ], + 'functions': [ + { + 'name': func.name, + 'return_type': func.return_type, + 'lineno': func.lineno, + 'is_public': func.is_public, + 'params': [ + {'name': p.name, 'type_name': p.type_name, 'is_pointer': p.is_pointer} + for p in func.params + ] + } + for func in self.functions + ], + 'structs': [ + { + 'name': struct.name, + 'lineno': struct.lineno, + 'is_public': struct.is_public, + 'members': [ + { + 'name': m.name, + 'type_name': m.type_name, + 'is_pointer': m.is_pointer, + 'array_size': m.array_size + } + for m in struct.members + ] + } + for struct in self.structs + ], + 'typedefs': [ + { + 'name': typedef.name, + 'original_type': typedef.original_type, + 'lineno': typedef.lineno, + 'is_public': typedef.is_public + } + for typedef in self.typedefs + ] + } + diff --git a/App/lib/core/Handles/HandlesAssert.py b/App/lib/core/Handles/HandlesAssert.py new file mode 100644 index 0000000..93b0392 --- /dev/null +++ b/App/lib/core/Handles/HandlesAssert.py @@ -0,0 +1,60 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator +from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP +import ast +import llvmlite.ir as ir + +class AssertHandle(BaseHandle): + def _HandleAssertLlvm(self, Node: ast.Assert) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + cond_val: ir.Value | None = self.HandleExprLlvm(Node.test) + if not cond_val: + return + if not isinstance(cond_val.type, ir.IntType) or cond_val.type.width != 1: + zero: ir.Constant = ir.Constant(cond_val.type, 0) + cond_val = Gen.builder.icmp_signed('!=', cond_val, zero, name="assert_cond") + AssertFailBB: ir.Block = Gen.func.append_basic_block(name="assert.fail") + AssertOkBB: ir.Block = Gen.func.append_basic_block(name="assert.ok") + Gen.builder.cbranch(cond_val, AssertOkBB, AssertFailBB) + Gen.builder.position_at_start(AssertFailBB) + exc_code: int = 9 + exc_msg: ir.Value | None = None + if Node.msg: + if isinstance(Node.msg, ast.Constant) and isinstance(Node.msg.value, str): + exc_msg = self.HandleExprLlvm(Node.msg) + elif isinstance(Node.msg, ast.Call) and isinstance(Node.msg.func, ast.Name): + exc_code = EXCEPTION_CODE_MAP.get(Node.msg.func.id, 99) + if Node.msg.args: + first_arg: ast.expr = Node.msg.args[0] + if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str): + exc_msg = self.HandleExprLlvm(first_arg) + elif isinstance(Node.msg, ast.Name): + exc_code = EXCEPTION_CODE_MAP.get(Node.msg.id, 99) + else: + exc_msg = self.HandleExprLlvm(Node.msg) + if Gen.eh_except_block_stack: + ExceptBB: ir.Block = Gen.eh_except_block_stack[-1][0] + EndBB: ir.Block | None = Gen.eh_except_block_stack[-1][1] + exception_code: ir.AllocaInstr = Gen.eh_except_block_stack[-1][2] + eh_message: ir.AllocaInstr = Gen.eh_except_block_stack[-1][3] + Gen._store(ir.Constant(ir.IntType(32), exc_code), exception_code) + if exc_msg: + Gen._store(exc_msg, eh_message) + else: + null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + Gen._store(null_ptr, eh_message) + Gen.builder.branch(ExceptBB) + else: + printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + if exc_msg: + fmt: ir.Value = Gen.emit_constant("AssertionError: %s\n", 'string') + Gen.builder.call(printf_func, [fmt, exc_msg], name="assert_fail_print") + else: + fmt: ir.Value = Gen.emit_constant("AssertionError\n", 'string') + Gen.builder.call(printf_func, [fmt], name="assert_fail_print") + exit_func: ir.Function = Gen.get_or_declare_c_func('exit', ir.FunctionType(ir.VoidType(), [ir.IntType(32)])) + Gen.builder.call(exit_func, [ir.Constant(ir.IntType(32), 1)], name="call_exit") + Gen.builder.unreachable() + Gen.builder.position_at_start(AssertOkBB) \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesClassDef.py b/App/lib/core/Handles/HandlesClassDef.py index d7c665e..1ffc538 100644 --- a/App/lib/core/Handles/HandlesClassDef.py +++ b/App/lib/core/Handles/HandlesClassDef.py @@ -199,10 +199,16 @@ def _is_marker_base(base_name: str) -> int: # ============================================================ # _get_base_name — 从 base AST 节点提取基类名 # -# 支持 Name(id), Attribute(attr), Subscript +# 支持 Name(id), Attribute(attr), Subscript(value, slice) +# 对于 Subscript(如 GSListNode[Value]),需要 pool 拼接特化名 "GSListNode[Value]" # ============================================================ -def _get_base_name(base_node: ast.AST | t.CPtr) -> str: - """从 base AST 节点提取基类名,返回 None=失败""" +def _get_base_name(base_node: ast.AST | t.CPtr, + pool: memhub.MemBuddy | t.CPtr = None) -> str: + """从 base AST 节点提取基类名,返回 None=失败 + + 对于 Subscript 节点,需要 pool 分配内存拼接特化名; + pool=None 时降级返回基类名(不含类型实参) + """ if base_node is None: return None k: int = base_node.kind() @@ -214,6 +220,40 @@ def _get_base_name(base_node: ast.AST | t.CPtr) -> str: if k == ast.ASTKind.Attribute: at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(base_node) return at.attr + # Subscript(value, slice) 形式 — 如 GSListNode[Value] + if k == ast.ASTKind.Subscript: + sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(base_node) + if sub is None or sub.value is None or sub.slice is None: + return None + # 提取基类名(如 "GSListNode") + base_nm: str = _get_base_name(sub.value, pool) + if base_nm is None: + return None + # 提取类型实参名(如 "Value") + slice_k: int = sub.slice.kind() + arg_nm: str = None + if slice_k == ast.ASTKind.Name: + sl_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.slice) + arg_nm = sl_nm.id + # 如果无法提取类型实参,返回基类名(降级) + if arg_nm is None: + return base_nm + # 无 pool 时返回基类名(降级,无法拼接特化名) + if pool is None: + return base_nm + # 拼接特化名 "GSListNode[Value]" + base_len: t.CSizeT = string.strlen(base_nm) + arg_len: t.CSizeT = string.strlen(arg_nm) + total_len: t.CSizeT = base_len + arg_len + 3 # "[" + arg + "]" + NUL + mangled: str = pool.alloc(total_len) + if mangled is None: + return base_nm # 降级 + mangled[0] = '\0' + string.strcat(mangled, base_nm) + string.strcat(mangled, "[") + string.strcat(mangled, arg_nm) + string.strcat(mangled, "]") + return mangled return None @@ -221,11 +261,17 @@ def _get_base_name(base_node: ast.AST | t.CPtr) -> str: # _get_parent_class — 获取 ClassDef 的真实父类名(非标记基类) # # 遍历 bases,返回第一个非标记基类的名字,None=无真实父类 +# 支持 Name, Attribute, Subscript 三种 base 节点 +# 对于 Subscript(如 GSListNode[Value]),需要 trans.Pool 拼接特化名 # ============================================================ -def _get_parent_class(cd: ast.ClassDef | t.CPtr) -> str: +def _get_parent_class(cd: ast.ClassDef | t.CPtr, + trans: HT.Translator | t.CPtr = None) -> str: """获取 ClassDef 的真实父类名,返回 None=无真实父类""" if cd is None or cd.bases is None: return None + pool: memhub.MemBuddy | t.CPtr = None + if trans is not None: + pool = trans.Pool bases: list[ast.AST | t.CPtr] | t.CPtr = cd.bases bn: t.CSizeT = bases.__len__() if bn == 0: @@ -234,12 +280,66 @@ def _get_parent_class(cd: ast.ClassDef | t.CPtr) -> str: base_node: ast.AST | t.CPtr = bases.get(bi) if base_node is None: continue - bname: str = _get_base_name(base_node) + bname: str = _get_base_name(base_node, pool) if bname is not None and _is_marker_base(bname) == 0: return bname return None +# ============================================================ +# _trigger_base_specialization — 触发 base 中 Subscript 的泛型特化 +# +# 对于 class Value(GSListNode[Value]),GSListNode[Value] 是 Subscript 节点。 +# 在 translate_class_def 处理 Value 之前,需要先特化 GSListNode[Value], +# 否则 find_struct_by_module("GSListNode[Value]") 返回 None,父类字段不被继承。 +# +# 遍历 cd.bases,如果 base 是 Subscript,提取 class_name 和 type_args, +# 调用 _specialize_generic_class 触发特化。 +# ============================================================ +def _trigger_base_specialization(trans: HT.Translator | t.CPtr, + cd: ast.ClassDef | t.CPtr): + """触发 base 中 Subscript 的泛型特化(如 GSListNode[Value])""" + if trans is None or cd is None or cd.bases is None: + return + pool: memhub.MemBuddy | t.CPtr = trans.Pool + bases: list[ast.AST | t.CPtr] | t.CPtr = cd.bases + bn: t.CSizeT = bases.__len__() + if bn == 0: + return + for bi in range(bn): + base_node: ast.AST | t.CPtr = bases.get(bi) + if base_node is None: + continue + bk: int = base_node.kind() + if bk != ast.ASTKind.Subscript: + continue + sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(base_node) + if sub is None or sub.value is None or sub.slice is None: + continue + # 提取泛型类名(如 "GSListNode") + gen_class_name: str = _get_base_name(sub.value, pool) + if gen_class_name is None: + continue + # 跳过标记基类 + if _is_marker_base(gen_class_name) == 1: + continue + # 提取类型实参名(如 "Value") + slice_k: int = sub.slice.kind() + arg_nm: str = None + if slice_k == ast.ASTKind.Name: + sl_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.slice) + arg_nm = sl_nm.id + if arg_nm is None: + continue + # 构建 type_args 列表 + type_args: list[str] | t.CPtr = list[str](pool, 1) + if type_args is None: + continue + type_args.append(arg_nm) + # 触发特化(_specialize_generic_class 有缓存,重复调用安全) + _specialize_generic_class(trans, gen_class_name, type_args) + + # ============================================================ # _has_any_func_cvtable — 检查类中是否有任何函数标记了 @t.CVTable # @@ -304,7 +404,7 @@ def _should_method_be_virtual(cd: ast.ClassDef | t.CPtr, return 1 # 4. 检查继承关系 - parent_name: str = _get_parent_class(cd) + parent_name: str = _get_parent_class(cd, trans) if parent_name is not None: # 用 SHA1 感知查找父类,规避跨模块同名找错 is_nvt: int = 0 @@ -349,7 +449,7 @@ def _detect_vtable_status(cd: ast.ClassDef | t.CPtr, return _has_any_func_cvtable(cd) # 检查是否有真实父类(继承自动启用 CVTable) - parent_name: str = _get_parent_class(cd) + parent_name: str = _get_parent_class(cd, trans) if parent_name is not None: # 用 SHA1 感知查找父类,规避跨模块同名找错 is_nvt: int = 0 @@ -1111,9 +1211,27 @@ def _specialize_generic_class(trans: HT.Translator | t.CPtr, pool: memhub.MemBuddy | t.CPtr = trans.Pool + # ============================================================ + # 诊断输出:追踪 GSListNode 特化调用路径 + # ============================================================ + if string.strcmp(class_name, "GSListNode") == 0: + stdio.printf("[SPEC] === _specialize_generic_class ===\n") + stdio.printf("[SPEC] class=%s module=%s\n", class_name, trans.ModuleSha1) + if type_args is not None: + ta_n: t.CSizeT = type_args.__len__() + ta_i: t.CSizeT + for ta_i in range(ta_n): + ta: str = type_args.get(ta_i) + if ta is not None: + stdio.printf("[SPEC] type_arg[%d]=%s\n", ta_i, ta) + stdio.fflush(0) + # 1. 查找泛型模板 template_cd: ast.ClassDef | t.CPtr = _find_generic_template(class_name) if template_cd is None: + if string.strcmp(class_name, "GSListNode") == 0: + stdio.printf("[SPEC] template NOT FOUND, return None\n") + stdio.fflush(0) return None # 2. 生成特化名 @@ -1148,11 +1266,18 @@ def _specialize_generic_class(trans: HT.Translator | t.CPtr, string.strcat(spec_key, ">") cached: str = _find_cached_spec(spec_key) if cached is not None: + if string.strcmp(class_name, "GSListNode") == 0: + stdio.printf("[SPEC] CACHE HIT, return cached=%s\n", cached) + stdio.fflush(0) return cached # 4. 检查是否已注册(Phase B 重复特化) existing: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(spec_name) if existing is not None: + if string.strcmp(class_name, "GSListNode") == 0: + stdio.printf("[SPEC] EXISTING registered, return spec_name=%s field_count=%d\n", + spec_name, existing.FieldCount) + stdio.fflush(0) _cache_spec(pool, spec_key, spec_name) return spec_name @@ -1179,6 +1304,27 @@ def _specialize_generic_class(trans: HT.Translator | t.CPtr, trans.GenericTypeArgs = None HandlesType.clear_generic_context() + # 诊断输出:检查特化结果 + if string.strcmp(class_name, "GSListNode") == 0: + result_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(spec_name) + if result_entry is not None: + stdio.printf("[SPEC] DONE spec_name=%s field_count=%d\n", + spec_name, result_entry.FieldCount) + # 打印每个字段的类型信息 + ri: int + for ri in range(result_entry.FieldCount): + rfe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry( + result_entry, ri) + if rfe is not None: + rfn: str = HandlesStruct.get_field_name_ptr(rfe) + rft: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_field_type_ptr(rfe) + stdio.printf("[SPEC] field[%d] name=%s type=%d\n", + ri, rfn if rfn is not None else "(null)", + t.CSizeT(rft) if rft is not None else 0) + else: + stdio.printf("[SPEC] DONE but result NOT FOUND in struct table!\n") + stdio.fflush(0) + return spec_name @@ -1224,11 +1370,18 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # SHA1 已在 register_struct 时设置,无需再补 return 0 + # ============================================================ + # 触发 base 中 Subscript 的泛型特化(如 GSListNode[Value]) + # 必须在 _detect_vtable_status 和父类字段继承之前调用, + # 否则 find_struct_by_module("GSListNode[Value]") 返回 None + # ============================================================ + _trigger_base_specialization(trans, cd) + # ============================================================ # VTable 检测:判断是否启用虚表 # ============================================================ has_vtable: int = _detect_vtable_status(cd, trans) - parent_name: str = _get_parent_class(cd) + parent_name: str = _get_parent_class(cd, trans) is_novtable_deco: int = 0 if cd.decorator_list is not None: if _has_decorator(cd.decorator_list, "NoVTable") == 1: diff --git a/App/lib/core/Handles/HandlesDelete.py b/App/lib/core/Handles/HandlesDelete.py new file mode 100644 index 0000000..f4871d9 --- /dev/null +++ b/App/lib/core/Handles/HandlesDelete.py @@ -0,0 +1,102 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator +from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta +import ast +import llvmlite.ir as ir + + +class DeleteHandle(BaseHandle): + def _HandleDeleteLlvm(self, Node: ast.Delete) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + for target in Node.targets: + if isinstance(target, ast.Subscript): + ClassName: str | None = self.Trans.ExprHandler._get_var_class(target.value, Gen) + if ClassName and Gen._has_function(f'{ClassName}.__delitem__'): + obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.value) + idx_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.slice) + if obj_val and idx_val: + # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 + if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: + obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + Gen.builder.call(Gen._get_function(f'{ClassName}.__delitem__'), [obj_val, idx_val], name=f"call_{ClassName}.__delitem__") + elif isinstance(target, ast.Name): + ClassName: str | None = Gen.var_struct_class.get(target.id) + if ClassName and Gen._has_function(f'{ClassName}.__delete__'): + obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target) + if obj_val: + # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 + if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: + obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + Gen.builder.call(Gen._get_function(f'{ClassName}.__delete__'), [obj_val], name=f"call_{ClassName}.__delete__") + elif isinstance(target, ast.Attribute): + # 检查 property deleter + AttrName: str = target.attr + ClassName: str | None = None + obj_val_for_prop: ir.Value | None = None + + if isinstance(target.value, ast.Name) and target.value.id == 'self': + # del self.attr — 检查当前类的 property deleter + ClassName = self.Trans._CurrentCpythonObjectClass + if ClassName: + PropKey: str = f'{ClassName}.{AttrName}' + PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey) + if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_DELETER in PropInfo.MetaList: + SelfVar: ir.Value | None = Gen._get_var_ptr('self') + if SelfVar: + SelfPtr: ir.Value = Gen._load(SelfVar, name="self") + DeleterFunc: ir.Function | None = Gen._get_function(PropKey + '$del') + if DeleterFunc and SelfPtr: + Gen.builder.call(DeleterFunc, [SelfPtr], name=f"prop_del_{PropKey}") + continue + else: + # del obj.attr — 检查 obj 类的 property deleter + ClassName = self.Trans.ExprHandler._get_var_class(target.value, Gen) + if ClassName: + PropKey: str = f'{ClassName}.{AttrName}' + PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey) + if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_DELETER in PropInfo.MetaList: + obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.value) + if obj_val: + DeleterFunc: ir.Function | None = Gen._get_function(PropKey + '$del') + if DeleterFunc: + Gen.builder.call(DeleterFunc, [obj_val], name=f"prop_del_{PropKey}") + continue + + # 原有的 __del__ 处理逻辑 + obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target) + if obj_val: + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.PointerType): + obj_val = Gen._load(obj_val, name="Load_del_ptr") + ClassName = None + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + for CN, ST in Gen.structs.items(): + if obj_val.type.pointee == ST: + ClassName = CN + break + elif isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + if isinstance(target.value, ast.Name) and target.value.id == 'self': + AttrName = target.attr + SelfClassName: str | None = self.Trans._CurrentCpythonObjectClass + if SelfClassName and SelfClassName in Gen.class_members: + for member_name, member_type in Gen.class_members[SelfClassName]: + if member_name == AttrName: + for CN in Gen.structs: + if CN == AttrName or Gen._has_function(f'{CN}.__del__'): + ClassName = CN + break + break + if not ClassName: + for CN, ST in Gen.structs.items(): + if Gen._has_function(f'{CN}.__del__'): + ClassName = CN + break + if ClassName and Gen._has_function(f'{ClassName}.__del__'): + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + Gen.builder.call(Gen._get_function(f'{ClassName}.__del__'), [obj_val], name=f"call_{ClassName}.__del__") \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesExprAsm.py b/App/lib/core/Handles/HandlesExprAsm.py new file mode 100644 index 0000000..22cff72 --- /dev/null +++ b/App/lib/core/Handles/HandlesExprAsm.py @@ -0,0 +1,580 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Any +import ast +import re +import llvmlite.ir as ir +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import BaseHandle +from lib.core.SymbolUtils import IsTModule +from lib.includes import t + +# 猴子补丁 llvmlite.ir.InlineAsm 以支持 asm_dialect 参数 +# 添加保护避免重复补丁 +_InlineAsmPatched = getattr(ir.InlineAsm, '_transpyc_patched', False) + +if not _InlineAsmPatched: + _OrigInlineAsmInit = ir.InlineAsm.__init__ + _OrigInlineAsmDescr = ir.InlineAsm.descr + + def _patched_inline_asm_init(self, ftype, asm, constraint, side_effect=False, asm_dialect=None): + _OrigInlineAsmInit(self, ftype, asm, constraint, side_effect) + self.asm_dialect = asm_dialect + + def _patched_inline_asm_descr(self, buf): + sideeffect = 'sideeffect' if self.side_effect else '' + dialect_str = getattr(self, 'asm_dialect', None) + if dialect_str == 'intel': + dialect = 'inteldialect' + else: + dialect = '' + fmt = 'asm {sideeffect} {dialect} "{asm}", "{constraint}"' + buf.append(fmt.format(sideeffect=sideeffect, dialect=dialect, asm=self.asm, + constraint=self.constraint)) + + ir.InlineAsm.__init__ = _patched_inline_asm_init + ir.InlineAsm.descr = _patched_inline_asm_descr + ir.InlineAsm._transpyc_patched = True + + +class ExprAsmHandle(BaseHandle): + @staticmethod + def _format_to_dialect(asm_format: str | None) -> str | None: + """将用户指定的 format 参数转换为 LLVM asm_dialect 值""" + fmt: str = asm_format.lower() if asm_format else 'intel' + if fmt == 'att': + return 'att' + elif fmt == 'arm': + return None # ARM 没有专门的 dialect 标记 + else: + return 'intel' # 默认 Intel + + def _HandleAsmLlvm(self, Node: ast.Call) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + + op_arg: Any = None + input_arg: Any = None + output_arg: Any = None + asm_format: str = 'Intel' # 汇编格式: Intel / AT&T / ARM + for kw in Node.keywords: + if kw.arg == 'op': + op_arg = kw.value + elif kw.arg in ('input', 'inp', 'inputs'): + input_arg = kw.value + elif kw.arg in ('output', 'out', 'outputs'): + output_arg = kw.value + elif kw.arg == 'format': + if isinstance(kw.value, ast.Constant) and isinstance(kw.value.value, str): + asm_format = kw.value.value + + if len(Node.args) >= 1: + pos_op: Any = None + if len(Node.args) >= 2 and op_arg is None: + pos_op = Node.args[1] + return self._HandleAsmWithOpLlvm(Node.args[0], op_arg or pos_op, input_arg, output_arg, asm_format) + + if len(Node.args) < 2: + return ir.Constant(ir.IntType(32), 1) + + args: list[ast.AST] = Node.args + + if len(args) == 4: + output_type_arg: ast.AST = args[0] + asm_template_arg: ast.AST = args[1] + constraint_arg: ast.AST = args[2] + clobber_arg: ast.AST = args[3] + + output_type: ir.Type | None = self._infer_expr_llvm_type(output_type_arg) + if not output_type: + output_type = ir.IntType(32) + + asm_template: str = '' + if isinstance(asm_template_arg, ast.Constant) and isinstance(asm_template_arg.value, str): + asm_template = self._prepare_intel_asm(self._mangle_asm_calls(asm_template_arg.value)) + + constraint: str = '' + if isinstance(constraint_arg, ast.Constant) and isinstance(constraint_arg.value, str): + constraint = constraint_arg.value + + clobber: str = '' + if isinstance(clobber_arg, ast.Constant) and isinstance(clobber_arg.value, str): + clobber = clobber_arg.value + + full_constraint: str = constraint + func_type: ir.FunctionType = ir.FunctionType(output_type, []) + asm_dialect: str | None = self._format_to_dialect(asm_format) + inline_asm: ir.InlineAsm = ir.InlineAsm(func_type, asm_template, constraint, side_effect=True, asm_dialect=asm_dialect) + result: Any = Gen.builder.call(inline_asm, [], name="asm_result") + return result + + elif len(args) >= 2: + output_type_arg: ast.AST = args[0] + asm_template_arg: ast.AST = args[1] + + output_type: ir.Type | None = self._infer_expr_llvm_type(output_type_arg) + if not output_type: + output_type = ir.IntType(32) + + asm_template: str = '' + if isinstance(asm_template_arg, ast.Constant) and isinstance(asm_template_arg.value, str): + asm_template = self._prepare_intel_asm(self._mangle_asm_calls(asm_template_arg.value)) + + output_constraints: list[str] = [] + output_values: list[Any] = [] + if len(args) > 2 and isinstance(args[2], (ast.List, ast.Tuple)): + for item in args[2].elts: + if isinstance(item, (ast.List, ast.Tuple)) and len(item.elts) >= 2: + constraint_node: ast.AST = item.elts[0] + value_node: ast.AST = item.elts[1] + if isinstance(constraint_node, ast.Constant) and isinstance(constraint_node.value, str): + output_constraints.append(constraint_node.value) + value: Any = self.HandleExprLlvm(value_node) + if value: + output_values.append(value) + + input_constraints: list[str] = [] + input_values: list[Any] = [] + if len(args) > 3 and isinstance(args[3], (ast.List, ast.Tuple)): + for item in args[3].elts: + if isinstance(item, (ast.List, ast.Tuple)) and len(item.elts) >= 2: + constraint_node: ast.AST = item.elts[0] + value_node: ast.AST = item.elts[1] + if isinstance(constraint_node, ast.Constant) and isinstance(constraint_node.value, str): + input_constraints.append(constraint_node.value) + value: Any = self.HandleExprLlvm(value_node) + if value: + input_values.append(value) + + clobbers: list[str] = [] + if len(args) > 4 and isinstance(args[4], (ast.List, ast.Tuple)): + for item in args[4].elts: + if isinstance(item, ast.Constant) and isinstance(item.value, str): + clobbers.append(item.value) + + constraint_parts: list[str] = [] + for oc in output_constraints: + constraint_parts.append(oc) + + input_start_idx: int = len(output_constraints) + for ic in input_constraints: + constraint_parts.append(ic) + + full_constraint: str = ",".join(constraint_parts) + + if clobbers: + if full_constraint: + full_constraint += "," + for c in clobbers: + full_constraint += f"~{{{c}}}" + + operands: list[Any] = output_values + input_values + param_types: list[ir.Type] = [v.type for v in operands] + func_type: ir.FunctionType = ir.FunctionType(output_type, param_types) + asm_dialect: str | None = self._format_to_dialect(asm_format) + inline_asm: ir.InlineAsm = ir.InlineAsm(func_type, asm_template, full_constraint, side_effect=False, asm_dialect=asm_dialect) + result: Any = Gen.builder.call(inline_asm, operands, name="asm_result") + return result + + return None + + def _prepare_intel_asm(self, template: str) -> str: + template = re.sub(r'%(\d+)', r'$\1', template) + template = re.sub(r'%([a-zA-Z][a-zA-Z0-9]*)', r'\1', template) + return template + + def _normalize_asm_template(self, template: str) -> str: + lines = template.split('\n') + if len(lines) <= 1: + return template + first_line = lines[0] + rest_lines = lines[1:] + stripped = [] + for line in rest_lines: + s = line.lstrip() + if s: + stripped.append(s) + else: + stripped.append('') + return '\n'.join([first_line] + stripped) + + def _process_joined_str_template(self, node: ast.AST, out_input_ops: list[Any], out_input_constraints: list[str], + out_output_ops: list[Any], out_output_constraints: list[str]) -> str: + if not isinstance(node, ast.JoinedStr): + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + return '' + + raw_parts: list[tuple[str, str | int]] = [] + for value in node.values: + if isinstance(value, ast.Constant) and isinstance(value.value, str): + raw_parts.append(('text', value.value)) + elif isinstance(value, ast.FormattedValue): + expr: ast.AST = value.value + if isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute): + if isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c': + if expr.func.attr == 'AsmInp': + if expr.args: + v: Any = self.HandleExprLlvm(expr.args[0]) + if v: + c: str = 'r' + if len(expr.args) >= 2: + c = self._parse_asm_descr(expr.args[1]) + if not c: + c = 'r' + out_input_ops.append(v) + out_input_constraints.append(c) + raw_parts.append(('input', len(out_input_ops) - 1)) + elif expr.func.attr == 'AsmOut': + if expr.args: + target_ptr: Any = self._get_var_ptr_for_asm(expr.args[0]) + if target_ptr: + v = target_ptr + else: + v = self.HandleExprLlvm(expr.args[0]) + if v: + c: str = '=r' + if len(expr.args) >= 2: + c = self._parse_asm_descr(expr.args[1]) + if not c: + c = '=r' + if not c.startswith('='): + c = '=' + c + out_output_ops.append(v) + out_output_constraints.append(c) + raw_parts.append(('output', len(out_output_ops) - 1)) + else: + fallback: Any = self.HandleExprLlvm(expr) + if fallback and isinstance(fallback.type, ir.IntType): + out_input_ops.append(fallback) + out_input_constraints.append('r') + raw_parts.append(('input', len(out_input_ops) - 1)) + else: + raw_parts.append(('text', '')) + + num_outputs: int = len(out_output_ops) + parts: list[str] = [] + for kind, data in raw_parts: + if kind == 'text': + parts.append(data) + elif kind == 'output': + parts.append(f'%{data}') + elif kind == 'input': + parts.append(f'%{num_outputs + data}') + return ''.join(parts) + + _X86_REGISTERS = frozenset({ + 'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi', 'rbp', 'rsp', + 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', + 'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'ebp', 'esp', + 'ax', 'bx', 'cx', 'dx', 'si', 'di', 'bp', 'sp', + 'al', 'bl', 'cl', 'dl', 'ah', 'bh', 'ch', 'dh', + 'sil', 'dil', 'bpl', 'spl', + 'r8d', 'r9d', 'r10d', 'r11d', 'r12d', 'r13d', 'r14d', 'r15d', + 'r8w', 'r9w', 'r10w', 'r11w', 'r12w', 'r13w', 'r14w', 'r15w', + 'r8b', 'r9b', 'r10b', 'r11b', 'r12b', 'r13b', 'r14b', 'r15b', + }) + + def _mangle_asm_calls(self, template: str) -> str: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + sha1: str = getattr(Gen, 'module_sha1', '') + if not sha1: + return template + + def replace_call(m: re.Match[str]) -> str: + prefix: str = m.group(1) + func_name: str = m.group(2) + if func_name.lower() in self._X86_REGISTERS: + return m.group(0) + mangled: str = Gen._mangle_func_name(func_name) + if '.' in mangled: + return f'{prefix}\\22{mangled}\\22' + return f"{prefix}{mangled}" + + return re.sub(r'(call\s+)(\w+)', replace_call, template) + + def _HandleAsmWithOpLlvm(self, asm_template_arg: ast.AST, op_arg: ast.AST | None = None, input_arg: ast.AST | None = None, output_arg: ast.AST | None = None, asm_format: str = 'Intel') -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + + input_ops: list[Any] = [] + input_constraints: list[str] = [] + output_ops: list[Any] = [] + output_constraints: list[str] = [] + + if output_arg and isinstance(output_arg, (ast.List, ast.Tuple)): + for item in output_arg.elts: + if isinstance(item, ast.Call) and isinstance(item.func, ast.Attribute): + if item.func.value.id == 'c' and item.func.attr == 'AsmOut': + if len(item.args) >= 1: + target_ptr: Any = self._get_var_ptr_for_asm(item.args[0]) + if target_ptr: + output_ops.append(target_ptr) + else: + value: Any = self.HandleExprLlvm(item.args[0]) + if value: + output_ops.append(value) + constraint: str = '=r' + if len(item.args) >= 2: + base_constraint: str = self._parse_asm_descr(item.args[1]) + if base_constraint: + if base_constraint.startswith('='): + constraint = base_constraint + else: + constraint = '=' + base_constraint + output_constraints.append(constraint) + + raw_template: str + if isinstance(asm_template_arg, ast.JoinedStr): + raw_template = self._normalize_asm_template( + self._process_joined_str_template( + asm_template_arg, input_ops, input_constraints, output_ops, output_constraints + ) + ) + elif isinstance(asm_template_arg, ast.Constant) and isinstance(asm_template_arg.value, str): + raw_template = self._normalize_asm_template(asm_template_arg.value) + else: + raw_template = '' + + raw_template = self._mangle_asm_calls(raw_template) + asm_dialect: str | None = self._format_to_dialect(asm_format) + asm_template: str + if asm_format == 'AT&T': + asm_template = raw_template # AT&T 不需要转换 + elif asm_format == 'ARM': + asm_template = raw_template # ARM 不需要转换 + else: + asm_template = self._prepare_intel_asm(raw_template) # Intel → LLVM IR 格式 + + if input_arg and isinstance(input_arg, (ast.List, ast.Tuple)): + for item in input_arg.elts: + if isinstance(item, ast.Call) and isinstance(item.func, ast.Attribute): + if item.func.value.id == 'c' and item.func.attr == 'AsmInp': + if len(item.args) >= 1: + value: Any = self.HandleExprLlvm(item.args[0]) + if value: + input_ops.append(value) + constraint: str = 'r' + if len(item.args) >= 2: + constraint = self._parse_asm_descr(item.args[1]) + if not constraint: + constraint = 'r' + input_constraints.append(constraint) + elif isinstance(item, (ast.List, ast.Tuple)) and len(item.elts) >= 2: + value_node: ast.AST = item.elts[0] + descr_node: ast.AST = item.elts[1] + value: Any = self.HandleExprLlvm(value_node) + if value: + input_ops.append(value) + constraint: str = self._parse_asm_descr(descr_node) + if not constraint: + constraint = 'r' + input_constraints.append(constraint) + + clobbers: list[str] = [] + if op_arg and isinstance(op_arg, (ast.List, ast.Tuple)): + for item in op_arg.elts: + clobber: str = self._parse_asm_descr(item) + if clobber: + clobbers.append(clobber) + + constraint_parts: list[str] = [] + for oc in output_constraints: + c: str = oc if oc.startswith('=') else ('=' + oc) + constraint_parts.append(c) + for ic in input_constraints: + if ic in ('d', 'edx', 'rdx'): + constraint_parts.append('{dx}') + else: + constraint_parts.append(ic) + + full_constraint: str = ",".join(constraint_parts) + + constraint_to_reg: dict[str, str] = { + 'a': 'rax', 'b': 'rbx', 'c': 'rcx', 'd': 'rdx', + 'S': 'rsi', 'D': 'rdi', + 'A': 'rax', 'U': 'r8', + } + used_regs: set[str] = set() + for ic in input_constraints: + if ic in constraint_to_reg: + used_regs.add(constraint_to_reg[ic]) + for oc in output_constraints: + base_oc: str = oc.lstrip('=').lstrip('+') + if base_oc in constraint_to_reg: + used_regs.add(constraint_to_reg[base_oc]) + + if clobbers: + has_memory: bool = 'memory' in clobbers + if has_memory and output_ops: + clobbers = [c for c in clobbers if c != 'memory'] + clobber_final: list[str] + if asm_format == 'ARM': + # ARM 不需要 x86 的 e→r 寄存器名转换 + clobber_final = [c for c in clobbers if c not in used_regs] + else: + clobber_64bit: list[str] = [] + for c in clobbers: + if c in ['eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'ebp', 'esp']: + clobber_64bit.append(c.replace('e', 'r')) + else: + clobber_64bit.append(c) + clobber_final = [c for c in clobber_64bit if c not in used_regs] + if clobber_final: + if full_constraint: + full_constraint += "," + full_constraint += ",".join([f"~{{{c}}}" for c in clobber_final]) + + if output_ops: + single_out: bool = len(output_ops) == 1 + if single_out: + op: Any = output_ops[0] + ret_type: ir.Type + if isinstance(op, ir.AllocaInstr): + ret_type = op.type.pointee + elif isinstance(op, ir.GlobalVariable): + ret_type = op.type.pointee + elif isinstance(op, (ir.GEPInstr, ir.CastInstr)): + ret_type = op.type.pointee if isinstance(op.type, ir.PointerType) else op.type + else: + ret_type = op.type + else: + def _get_ret_type(op: Any) -> ir.Type: + if isinstance(op, (ir.AllocaInstr, ir.GlobalVariable)): + return op.type.pointee + return op.type + ret_type = ir.LiteralStructType([_get_ret_type(op) for op in output_ops]) + if isinstance(ret_type, ir.PointerType) and isinstance(ret_type.pointee, ir.IdentifiedStructType): + ret_type = ir.IntType(64) + operands: list[Any] = input_ops + param_types: list[ir.Type] = [op.type for op in operands] + func_type: ir.FunctionType = ir.FunctionType(ret_type, param_types) + inline_asm: ir.InlineAsm = ir.InlineAsm(func_type, asm_template, full_constraint, side_effect=len(output_ops) == 0, asm_dialect=asm_dialect) + result: Any = Gen.builder.call(inline_asm, operands, name="asm_result") + if single_out: + target: Any = output_ops[0] + if isinstance(target, ir.AllocaInstr): + if target.type.pointee != result.type: + i64t: ir.IntType = ir.IntType(64) + iv: Any = Gen.builder.ptrtoint(result, i64t) + ptr: Any = Gen.builder.bitcast(target, ir.PointerType(i64t)) + Gen.builder.store(iv, ptr) + else: + Gen.builder.store(result, target) + elif isinstance(target, (ir.GlobalVariable, ir.GEPInstr, ir.CastInstr)): + target_pointee: ir.Type = target.type.pointee + if target_pointee != result.type: + if isinstance(target_pointee, ir.PointerType) and isinstance(result, ir.Instruction) and result.type == ir.IntType(64): + ptr_val: Any = Gen.builder.inttoptr(result, target_pointee) + Gen.builder.store(ptr_val, target) + else: + i64t: ir.IntType = ir.IntType(64) + iv: Any = Gen.builder.ptrtoint(result, i64t) + ptr: Any = Gen.builder.bitcast(target, ir.PointerType(i64t)) + Gen.builder.store(iv, ptr) + else: + Gen.builder.store(result, target) + else: + dst: Any = Gen._allocaEntry(result.type) + Gen.builder.store(result, dst) + else: + for i, out_op in enumerate(output_ops): + val: Any = Gen.builder.extract_value(result, i, name=f"asm_out_{i}") + target: Any = out_op if isinstance(out_op, ir.AllocaInstr) else Gen._allocaEntry(val.type) + if isinstance(target, ir.AllocaInstr) and target.type.pointee != val.type: + i64t: ir.IntType = ir.IntType(64) + iv: Any = Gen.builder.ptrtoint(val, i64t) + ptr: Any = Gen.builder.bitcast(target, ir.PointerType(i64t)) + Gen.builder.store(iv, ptr) + else: + Gen.builder.store(val, target) + else: + operands: list[Any] = input_ops + param_types: list[ir.Type] = [op.type for op in operands] + func_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), param_types) + inline_asm: ir.InlineAsm = ir.InlineAsm(func_type, asm_template, full_constraint, side_effect=True, asm_dialect=asm_dialect) + Gen.builder.call(inline_asm, operands, name="asm_call") + return ir.Constant(ir.IntType(32), 1) + + def _get_var_ptr_for_asm(self, node: ast.AST) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(node, ast.Name): + var_name: str = node.id + if var_name in Gen.variables and Gen.variables[var_name] is not None: + ptr: Any = Gen.variables[var_name] + if isinstance(ptr, ir.AllocaInstr): + return ptr + if isinstance(ptr, ir.GlobalVariable): + return ptr + elif isinstance(node, ast.Attribute): + obj_ptr: Any = self._get_var_ptr_for_asm(node.value) + if obj_ptr is not None: + obj_type: ir.Type = obj_ptr.type.pointee + if isinstance(obj_type, ir.PointerType) and isinstance(obj_type.pointee, ir.IdentifiedStructType): + Loaded: Any = Gen.builder.load(obj_ptr, name="asm_attr_Load") + struct_type: ir.IdentifiedStructType = obj_type.pointee + class_name: str | None = Gen._resolve_class_for_var(node.value.id) if isinstance(node.value, ast.Name) else None + if class_name: + offset: int = Gen._get_member_offset(node.attr, class_name) + gep: Any = Gen.builder.gep(Loaded, [ir.Constant(ir.IntType(32), 0), + ir.Constant(ir.IntType(32), offset)], + inbounds=True) + return gep + for idx, elem_type in enumerate(struct_type.elements): + gep: Any = Gen.builder.gep(Loaded, [ir.Constant(ir.IntType(32), 0), + ir.Constant(ir.IntType(32), idx)], + inbounds=True) + return gep + elif isinstance(obj_type, ir.IdentifiedStructType): + class_name: str | None = Gen._resolve_class_for_var(node.value.id) if isinstance(node.value, ast.Name) else None + if class_name: + offset: int = Gen._get_member_offset(node.attr, class_name) + gep: Any = Gen.builder.gep(obj_ptr, [ir.Constant(ir.IntType(32), 0), + ir.Constant(ir.IntType(32), offset)], + inbounds=True) + return gep + for idx, elem_type in enumerate(obj_type.elements): + gep: Any = Gen.builder.gep(obj_ptr, [ir.Constant(ir.IntType(32), 0), + ir.Constant(ir.IntType(32), idx)], + inbounds=True) + return gep + return None + + def _parse_asm_descr(self, expr: ast.AST) -> str: + if isinstance(expr, ast.BinOp): + left_val: str = self._parse_asm_descr(expr.left) + right_val: str = self._parse_asm_descr(expr.right) + return left_val + right_val + + elif isinstance(expr, ast.Attribute): + if isinstance(expr.value, ast.Attribute): + if (isinstance(expr.value.value, ast.Name) and + IsTModule(expr.value.value.id, self.Trans.SymbolTable) and + expr.value.attr == 'ASM_DESCR'): + AttrName: str = expr.attr + return getattr(t.ASM_DESCR, AttrName, "") + elif isinstance(expr.value, ast.Name) and IsTModule(expr.value.id, self.Trans.SymbolTable): + AttrName: str = expr.attr + return getattr(t.ASM_DESCR, AttrName, "") + + elif isinstance(expr, ast.Constant): + return expr.value + + return "" + + def _infer_expr_llvm_type(self, expr: ast.AST) -> ir.Type | None: + if isinstance(expr, ast.Name): + type_name: str = expr.id + if hasattr(t, type_name): + ctype: Any = getattr(t, type_name) + if isinstance(ctype, type) and issubclass(ctype, t.CType): + size: Any = getattr(ctype, 'Size', None) + if size is not None: + return ir.IntType(size) + if type_name == 'CFloat': + return ir.FloatType() + elif type_name == 'CDouble': + return ir.DoubleType() + elif isinstance(expr, ast.Attribute): + if isinstance(expr.value, ast.Name) and IsTModule(expr.value.id, self.Trans.SymbolTable): + return self._infer_expr_llvm_type(ast.Name(id=expr.attr, ctx=ast.Load())) + return None diff --git a/App/lib/core/Handles/HandlesExprAttr.py b/App/lib/core/Handles/HandlesExprAttr.py new file mode 100644 index 0000000..dbfc085 --- /dev/null +++ b/App/lib/core/Handles/HandlesExprAttr.py @@ -0,0 +1,1480 @@ +from __future__ import annotations +from typing import Any, TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta +import ast +import llvmlite.ir as ir + + +class ExprAttrHandle(BaseHandle): + + # ========== 辅助方法 ========== + + def _apply_bswap_on_Load(self, val: ir.Value, ClassName: str, AttrName: str) -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + byte_order: str = getattr(Gen, 'class_member_byteorders', {}).get(ClassName, {}).get(AttrName, "") + return Gen._apply_bswap_if_big(val, byte_order, f"bswap_load_{AttrName}") + + def _load_arr_elem_with_bswap(self, Gen: LlvmCodeGenerator, ElemPtr: ir.Value, arr_byte_order: str) -> ir.Value: + """加载数组元素并按字节序应用 bswap""" + val: ir.Value = Gen._load(ElemPtr, name="arr_elem_load") + return Gen._apply_bswap_if_big(val, arr_byte_order, "bswap_arr_load") + + def _gep_and_Load_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, offset: int | None, AttrName: str, ClassName: str, ST: ir.Type | None = None) -> ir.Value | None: + """从结构体指针通过 GEP 取成员,处理 array/struct pointer/class_members 特殊情况""" + if offset is None: + return None + if isinstance(ObjVal.type, ir.PointerType): + pointee: Any = ObjVal.type.pointee + if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset >= len(pointee.elements): + # sha1 冲突修复:pointee 字段不足时,尝试 bitcast 到 ST(更完整的版本) + if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements): + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_gep_{ClassName}") + else: + return None + MemberPtr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.ArrayType): + return MemberPtr + if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.IdentifiedStructType): + return MemberPtr + # 检查 pointer-to-pointer 且实际元素是 ArrayType + if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.PointerType): + actual_elem_type: Any = None + if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements): + actual_elem_type = ST.elements[offset] + if isinstance(actual_elem_type, ir.ArrayType): + return Gen.builder.bitcast(MemberPtr, ir.PointerType(actual_elem_type), name=f"cast_arr_{AttrName}") + # 检查 class_members 中的 ArrayType + if ClassName in Gen.class_members: + for member_name, member_type in Gen.class_members[ClassName]: + if member_name == AttrName and isinstance(member_type, ir.ArrayType): + return Gen.builder.bitcast(MemberPtr, ir.PointerType(member_type), name=f"cast_arr_{AttrName}") + val: Any = Gen._load(MemberPtr, name=AttrName) + return self._apply_bswap_on_Load(val, ClassName, AttrName) + + def _try_Load_struct_from_stub(self, Gen: LlvmCodeGenerator, ClassName: str) -> tuple[ir.Type | None, bool]: + """尝试从 stub 加载结构体定义,返回 (struct_type, ok)""" + if ClassName not in Gen.structs: + return None, False + st: Any = Gen.structs[ClassName] + if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + st = Gen.structs.get(ClassName, st) + if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): + return st, False + return st, True + + def _find_specialized_struct_with_body(self, Gen: LlvmCodeGenerator, opaque_struct: Any) -> tuple[ir.IdentifiedStructType, str] | None: + """查找 opaque 非特化泛型结构体的特化版本(如 GSList -> GSList[Param])。 + 所有特化版本布局相同,可用于 GEP 偏移计算。""" + if not isinstance(opaque_struct, ir.IdentifiedStructType): + return None + base_name: str = opaque_struct.name or "" + short_name: str = base_name.split('.')[-1] if '.' in base_name else base_name + if not short_name or '[' in short_name: + return None + for CN2, ST2 in Gen.structs.items(): + if not isinstance(ST2, ir.IdentifiedStructType): + continue + if ST2 is opaque_struct: + continue + if ST2.elements is None: + continue + st2_short: str = (ST2.name or "").split('.')[-1] if '.' in (ST2.name or "") else (ST2.name or "") + if st2_short.startswith(short_name + '['): + return (ST2, CN2) + return None + + def _try_access_opaque_generic_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, opaque_struct: Any, Node: ast.Attribute, orig_CN: str, parent_hint: str | None = None) -> ir.Value | None: + """通过特化版本访问 opaque 非特化泛型结构体成员。 + + 遍历所有候选特化版本(如 GSList[Param]/GSList[NameEntry]), + 优先选择 class_members 中包含 Node.attr 字段的版本, + 避免选到错误特化版本(如 GSList[NameEntry] 而非 GSList[Param]) + 导致 _get_member_offset 返回 None 而生成 ret null。 + + parent_hint: 父上下文提供的特化提示(如 'GSList[BasicBlock]'), + 当所有候选都有目标字段(如 Head/Tail/Count)时, + 用此提示选择正确特化版本。""" + # 收集所有候选特化版本(有 body 的) + candidates: list[tuple[ir.IdentifiedStructType, str]] = [] + if isinstance(opaque_struct, ir.IdentifiedStructType): + base_name: str = opaque_struct.name or "" + short_name: str = base_name.split('.')[-1] if '.' in base_name else base_name + if short_name and '[' not in short_name: + for CN2, ST2 in Gen.structs.items(): + if not isinstance(ST2, ir.IdentifiedStructType): + continue + if ST2 is opaque_struct: + continue + if ST2.elements is None: + continue + st2_short: str = (ST2.name or "").split('.')[-1] if '.' in (ST2.name or "") else (ST2.name or "") + if st2_short.startswith(short_name + '['): + candidates.append((ST2, CN2)) + if not candidates: + return None + # 优先级 1: parent_hint 精确匹配(如 'GSList[BasicBlock]') + # 当父对象字段声明记录了特化名时,直接使用,避免 Head/Tail/Count 等公共字段歧义 + if parent_hint: + hint_short: str = parent_hint.split('.')[-1] if '.' in parent_hint else parent_hint + for spec_st, spec_CN in candidates: + spec_short: str = spec_CN.split('.')[-1] if '.' in spec_CN else spec_CN + if spec_short == hint_short: + CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(spec_st), name=f"cast_spec_{orig_CN}") + for lookup_CN in (spec_CN, orig_CN): + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(lookup_CN, {}) + if Node.attr in bitfield_offsets: + return self._load_bitfield_member(CastedObj, lookup_CN, Node.attr) + offset: int | None = Gen._get_member_offset(Node.attr, lookup_CN) + if offset is not None: + return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, lookup_CN, spec_st) + break + # 优先级 2: class_members 中包含 Node.attr 字段的特化版本 + best_candidate: tuple[ir.IdentifiedStructType, str] | None = None + for spec_st, spec_CN in candidates: + for lookup_CN in (spec_CN, orig_CN): + lookup_short: str = lookup_CN.split('.')[-1] if '.' in lookup_CN else lookup_CN + for chk_CN in (lookup_CN, lookup_short): + if chk_CN in Gen.class_members: + if any(name == Node.attr for name, _ in Gen.class_members[chk_CN]): + best_candidate = (spec_st, spec_CN) + break + if best_candidate: + break + if best_candidate: + break + if best_candidate is None: + best_candidate = candidates[0] + spec_st: ir.IdentifiedStructType + spec_CN: str + spec_st, spec_CN = best_candidate + CastedObj = Gen.builder.bitcast(ObjVal, ir.PointerType(spec_st), name=f"cast_spec_{orig_CN}") + for lookup_CN in (spec_CN, orig_CN): + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(lookup_CN, {}) + if Node.attr in bitfield_offsets: + return self._load_bitfield_member(CastedObj, lookup_CN, Node.attr) + offset: int | None = Gen._get_member_offset(Node.attr, lookup_CN) + if offset is not None: + return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, lookup_CN, spec_st) + return None + + def _make_define_constant(self, Gen: LlvmCodeGenerator, val: int | float | str) -> ir.Value | None: + """将 CDefine 值转为 LLVM 常量""" + if isinstance(val, int): + if abs(val) > 2147483647: + return ir.Constant(ir.IntType(64), val) + return ir.Constant(ir.IntType(32), val) + elif isinstance(val, float): + return ir.Constant(ir.DoubleType(), val) + elif isinstance(val, str): + return Gen._create_string_global(val) + return None + + def _lookup_cdefine(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: + """在符号表和模块中查找 CDefine 常量值""" + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if not imported_modules: + return None + PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + key: str = f"{mod_name}.{AttrName}" + if key not in PossibleKeys: + PossibleKeys.append(key) + for lookup_key in PossibleKeys: + SymInfo: Any = self.Trans.SymbolTable.lookup(lookup_key) + if SymInfo and SymInfo.IsDefine and SymInfo.DefineValue is not None: + return self._make_define_constant(Gen, SymInfo.DefineValue) + # 也检查 _define_constants + define_constants: Any = getattr(Gen, '_define_constants', {}) + for key in (f"{VarName}.{AttrName}", AttrName): + if key in define_constants: + return self._make_define_constant(Gen, define_constants[key]) + return None + + def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: + """在模块全局变量中查找 import 的属性""" + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if not imported_modules: + return None + resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) + if VarName not in imported_modules and resolved_mod not in imported_modules: + return None + PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + key: str = f"{mod_name}.{AttrName}" + if key not in PossibleKeys: + PossibleKeys.append(key) + for key in PossibleKeys: + if key in Gen.module.globals: + GVar: Any = Gen.module.globals[key] + if isinstance(GVar, ir.Function): + return GVar + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return GVar + return Gen._load(GVar, name=AttrName) + # SHA1 前缀查找 + ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) + sha1_candidates: list[str] = [VarName, resolved_mod] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + if mod_name not in sha1_candidates: + sha1_candidates.append(mod_name) + for cand in sha1_candidates: + sha1: str | None = ModuleSha1Map.get(cand) + if sha1: + prefixed: str = f"{sha1}.{AttrName}" + if prefixed in Gen.module.globals: + GVar2: Any = Gen.module.globals[prefixed] + if isinstance(GVar2, ir.Function): + return GVar2 + if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return GVar2 + return Gen._load(GVar2, name=AttrName) + if AttrName in Gen.module.globals: + gv: Any = Gen.module.globals[AttrName] + if AttrName in Gen.variables and Gen.variables[AttrName] is None: + str_gv_name: str = AttrName + '_str' + if str_gv_name in Gen.module.globals: + str_gv: Any = Gen.module.globals[str_gv_name] + return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") + return Gen.builder.load(gv, name=AttrName) + return None + + def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None: + """根据变量名解析其结构体类名""" + ClassName: str | None = Gen.var_struct_class.get(VarName) + if not ClassName and getattr(Gen, 'global_struct_class', None): + ClassName = Gen.global_struct_class.get(VarName) + if ClassName: + Gen.var_struct_class[VarName] = ClassName + if not ClassName and VarName in Gen.module.globals: + GVar: Any = Gen.module.globals[VarName] + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found: Any = Gen.find_struct_by_pointee(GVar.type.pointee) + if found: + ClassName = found[0] + Gen.var_struct_class[VarName] = ClassName + if getattr(Gen, 'global_struct_class', None): + Gen.global_struct_class[VarName] = ClassName + if not ClassName and VarName in Gen.variables: + VarPtr: Any = Gen.variables[VarName] + if VarPtr is not None and isinstance(VarPtr.type, ir.PointerType): + pointee: Any = VarPtr.type.pointee + if isinstance(pointee, ir.PointerType): + pointee = pointee.pointee + found = Gen.find_struct_by_pointee(pointee) + if found: + ClassName = found[0] + return ClassName + + def _check_struct_match(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, ClassName: str) -> bool: + """检查 ObjVal 是否指向 ClassName 对应的结构体""" + if ClassName not in Gen.structs: + return False + if isinstance(ObjVal.type, ir.PointerType): + ST: Any = Gen.structs[ClassName] + if ObjVal.type.pointee == ST: + return True + if isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType): + # 精确名匹配(含 sha1 前缀)或短名匹配(去掉 sha1 前缀) + if ObjVal.type.pointee.name == ST.name: + return True + if Gen._extract_short_name(ObjVal.type.pointee.name) == Gen._extract_short_name(ST.name): + return True + return False + + # ========== _HandleAttributeLlvm 子方法 ========== + + def _handle_attr_self(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: + """处理 self.xxx 属性访问""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + ClassName: str | None = self.Trans._CurrentCpythonObjectClass + if not ClassName: + return None + # 检查 property getter + PropKey: str = f'{ClassName}.{Node.attr}' + PropInfo: Any = self.Trans.SymbolTable.lookup(PropKey) + if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_GETTER in PropInfo.MetaList: + SelfVar: Any = Gen._get_var_ptr('self') + if SelfVar: + SelfPtr: Any = Gen._load(SelfVar, name="self") + GetterFunc: Any = Gen._get_function(PropKey) + if GetterFunc and SelfPtr: + return Gen.builder.call(GetterFunc, [SelfPtr], name=f"prop_{PropKey}") + offset: int | None = Gen._get_member_offset(Node.attr, ClassName) + SelfVar = Gen._get_var_ptr('self') + if not SelfVar: + return None + SelfPtr = Gen._load(SelfVar, name="self") + if not isinstance(SelfPtr.type, ir.PointerType): + return None + pointee: Any = SelfPtr.type.pointee + if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: + # 修复 Bug 4/5:NoVTable fix 清除 struct elements 后,_handle_attr_self + # 必须主动从 stub 文件重新加载 struct 定义,否则 self.Trans 等属性访问 + # 会因 pointee.elements 为 None 返回 None,导致 AnnAssign/Store 不生成 IR。 + # 与 _handle_attr_nested 的处理保持一致。 + # 策略:先用 ClassName 直接尝试加载;若失败,遍历 Gen.structs 按名字匹配 + # 找到对应的 dict key(可能是 sha1 前缀形式),再用该 key 加载。 + loaded: bool = False + for TryCN in (ClassName,): + st, ok = self._try_Load_struct_from_stub(Gen, TryCN) + if ok and isinstance(st, ir.IdentifiedStructType) and st.elements is not None: + pointee = st + loaded = True + break + if not loaded: + for CN2, ST2 in Gen.structs.items(): + if isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name: + st2, ok2 = self._try_Load_struct_from_stub(Gen, CN2) + if ok2 and isinstance(st2, ir.IdentifiedStructType) and st2.elements is not None: + pointee = st2 + loaded = True + break + if not loaded: + # 最后回退:在 Gen.structs 中查找同名非 opaque 版本 + for CN2, ST2 in Gen.structs.items(): + if isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name and ST2.elements is not None: + pointee = ST2 + break + # 当 pointee 元素数不足以容纳 offset 时(常见于跨模块继承:子类 struct + # 在父类 class_members 加载前生成,只有部分继承字段),从 Gen.structs + # 中查找同名且元素数更多的完整定义。 + if (isinstance(pointee, ir.IdentifiedStructType) + and offset is not None + and pointee.elements is not None + and offset >= len(pointee.elements)): + _cur_count: int = len(pointee.elements) + for CN2, ST2 in Gen.structs.items(): + if (isinstance(ST2, ir.IdentifiedStructType) + and ST2.name == pointee.name + and ST2.elements is not None + and len(ST2.elements) > _cur_count + and offset < len(ST2.elements)): + pointee = ST2 + break + if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset is not None and offset < len(pointee.elements): + if isinstance(pointee.elements[offset], ir.VoidType): + return None + return self._gep_and_Load_member(Gen, SelfPtr, offset, Node.attr, ClassName, pointee) + DynamicVarName: str = f"self.{Node.attr}" + if DynamicVarName in Gen.variables: + return Gen._load(Gen.variables[DynamicVarName], name=Node.attr) + return None + + def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: + """处理枚举成员访问 (VarName 是枚举类型名)""" + SymInfo: Any = self.Trans.SymbolTable.lookup(VarName) + if not SymInfo or not SymInfo.IsEnum: + return None + # 优先用带前缀的 key 查找(避免短名被同名函数/变量覆盖) + FullKey: str = f"{VarName}.{Node.attr}" + MemberInfo: Any = self.Trans.SymbolTable.lookup(FullKey) + if not MemberInfo or not MemberInfo.IsEnumMember or MemberInfo.EnumName != VarName: + # 回退到短名查找 + MemberInfo = self.Trans.SymbolTable.lookup(Node.attr) + if not MemberInfo or not MemberInfo.IsEnumMember or MemberInfo.EnumName != VarName: + # 最后遍历符号表 + for key, info in self.Trans.SymbolTable.items(): + if info.IsEnumMember and info.EnumName == VarName and key == Node.attr: + if isinstance(info.value, int): + return ir.Constant(ir.IntType(32), info.value) + return None + if isinstance(MemberInfo.value, int): + return ir.Constant(ir.IntType(32), MemberInfo.value) + return None + + def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: + """处理 x.attr 形式 (x 是普通 Name)""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + AttrName: str = Node.attr + + # __doc__ 魔法字符串:函数/结构体的 docstring + if AttrName == '__doc__': + doc_ptr: ir.Value | None = Gen._get_doc_ptr(VarName) + if doc_ptr is not None: + return doc_ptr + + # 先尝试枚举成员 + result: Any = self._handle_attr_enum(Node, VarName) + if result is not None: + return result + + # 尝试 CDefine 常量 + result = self._lookup_cdefine(Gen, VarName, AttrName) + if result is not None: + return result + + # 尝试 import 模块全局变量 + result = self._lookup_module_global(Gen, VarName, AttrName) + if result is not None: + return result + + # FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储) + # 当 a 是 str 变量时, a 本身是 char*, 但 a__meta__ 存储了 __data__/__mbuddy__ 等字段 + # 直接通过 a.__field__ 访问影子结构体字段 (语法糖) + _fd_meta_name: str = f"{VarName}__meta__" + if _fd_meta_name in Gen.variables and '_str' in Gen.structs: + _fd_offset = Gen._get_member_offset(AttrName, '_str') + if _fd_offset is not None: + _fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name] + _fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr") + return Gen._load(_fd_field_ptr, name=f"{VarName}_{AttrName}_val") + + # 解析结构体类名:若 VarName 是结构体变量,优先走结构体字段访问路径 + # 避免结构体字段名(如 stdout)与全局变量名冲突时被误解析为全局变量 + ClassName: str | None = self._resolve_var_classname(Gen, VarName) + + # 尝试 attr 直接作为全局变量(仅当 VarName 不是结构体变量时) + if not ClassName and AttrName in Gen.module.globals: + gv: Any = Gen.module.globals[AttrName] + if AttrName in Gen.variables and Gen.variables[AttrName] is None: + str_gv_name: str = AttrName + '_str' + if str_gv_name in Gen.module.globals: + str_gv: Any = Gen.module.globals[str_gv_name] + return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") + return Gen.builder.load(gv, name=AttrName) + + if not ClassName: + return None + + # 检查 property getter + PropKey: str = f'{ClassName}.{AttrName}' + PropInfo: Any = self.Trans.SymbolTable.lookup(PropKey) + if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_GETTER in PropInfo.MetaList: + ObjVal: Any = self.HandleExprLlvm(Node.value) + GetterFunc: Any = Gen._get_function(PropKey) + if GetterFunc and ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + return Gen.builder.call(GetterFunc, [ObjVal], name=f"prop_{PropKey}") + + # 获取类型信息 + TypeInfo: Any = self.Trans.SymbolTable.lookup(ClassName) + IsUnion: bool = TypeInfo.IsUnion if TypeInfo else False + IsCenum: bool = TypeInfo.IsEnum if TypeInfo else False + IsRenum: bool = TypeInfo.IsRenum if TypeInfo else False + + # REnum 处理 + if IsRenum: + if AttrName == 'tag': + ObjVal = self.HandleExprLlvm(Node.value) + if ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + tag_ptr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="tag_ptr") + return Gen._load(tag_ptr, name="tag_val") + NestedStructName: str = f"{ClassName}_{AttrName}" + if NestedStructName in Gen.structs: + NestedStructType: Any = Gen.structs[NestedStructName] + ObjVal = self.HandleExprLlvm(Node.value) + if ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + return Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") + + # CEnum 处理 + if IsCenum: + for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"): + info: Any = self.Trans.SymbolTable.lookup(qname) + if info and info.IsEnumMember and isinstance(info.value, int): + return ir.Constant(ir.IntType(32), info.value) + for key, info in self.Trans.SymbolTable.items(): + if key == AttrName: + if info.IsEnumMember and info.EnumName == ClassName: + if isinstance(info.value, int): + return ir.Constant(ir.IntType(32), info.value) + break + + # CUnion 处理 + if IsUnion: + NestedStructName = f"{ClassName}_{AttrName}" + if NestedStructName in Gen.structs: + NestedStructType = Gen.structs[NestedStructName] + ObjVal = self.HandleExprLlvm(Node.value) + if ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + return Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") + + # 普通结构体成员访问 + ObjVal = self.HandleExprLlvm(Node.value) + if not ObjVal: + return None + # char* → struct* cast + if self._is_char_pointer(ObjVal): + if ClassName in Gen.structs: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + # 二级指针解引用 + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + + # 检查结构体匹配 + struct_match: bool = self._check_struct_match(Gen, ObjVal, ClassName) + if not struct_match and ClassName and VarName: + if VarName in Gen.var_struct_class: + CN: str = Gen.var_struct_class[VarName] + if CN != ClassName and CN in Gen.structs: + ClassName = CN + struct_match = self._check_struct_match(Gen, ObjVal, ClassName) + # 最终回退:var_struct_class 可能被前一个函数污染(如 cur 在不同函数中 + # 分别是 Line/Param/BasicBlock),通过 ObjVal 的实际 pointee 类型恢复正确类名。 + # 但若 var_struct_class 已记录特化名(如 ndarray[double]),且 ObjVal 的 + # pointee 是对应的 opaque 基础类型(如 ndarray),则 bitcast 到特化版本, + # 不覆盖为裸模板名(否则后续方法调用会查找 ndarray.xxx 而非 ndarray[double].xxx) + if not struct_match and isinstance(ObjVal.type, ir.PointerType): + found: Any = Gen.find_struct_by_pointee(ObjVal.type.pointee) + if found: + found_name: str = found[0] + # 检查 var_struct_class 是否已记录 found_name 的特化版本 + preserved: bool = False + if VarName and VarName in Gen.var_struct_class: + cur_spec: str = Gen.var_struct_class[VarName] + if ('[' in cur_spec + and cur_spec.split('[')[0] == found_name + and cur_spec in Gen.structs): + # bitcast ObjVal (opaque base*) 到特化类型指针,保留特化名 + ObjVal = Gen.builder.bitcast( + ObjVal, ir.PointerType(Gen.structs[cur_spec]), + name=f"cast_spec_{cur_spec}") + ClassName = cur_spec + struct_match = True + preserved = True + if not preserved: + ClassName = found_name + struct_match = True + if VarName: + Gen.var_struct_class[VarName] = ClassName + elif isinstance(ObjVal.type.pointee, ir.IdentifiedStructType): + # sha1 冲突 fallback:find_struct_by_pointee 因 sha1 前缀不匹配失败时, + # 用短名匹配,优先选字段最多的完整版本 + pointee_short: str = Gen._extract_short_name(ObjVal.type.pointee.name) + best_elem_count: int = 0 + for CN, ST in Gen.structs.items(): + if isinstance(ST, ir.IdentifiedStructType): + st_short: str = Gen._extract_short_name(ST.name) + if st_short == pointee_short: + elem_count: int = len(ST.elements) if ST.elements else 0 + if elem_count > best_elem_count: + ClassName = CN + best_elem_count = elem_count + struct_match = True + if struct_match and VarName: + # 同样保护特化名:若已记录的特化名的短名基础部分与 ClassName 匹配,保留 + cur_spec2: str = Gen.var_struct_class.get(VarName, '') + if ('[' in cur_spec2 + and cur_spec2.split('[')[0] == ClassName + and cur_spec2 in Gen.structs): + ObjVal = Gen.builder.bitcast( + ObjVal, ir.PointerType(Gen.structs[cur_spec2]), + name=f"cast_spec_{cur_spec2}") + ClassName = cur_spec2 + else: + Gen.var_struct_class[VarName] = ClassName + if not struct_match: + return None + + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, ClassName) + if not ok: + return None + + # bitfield 处理 + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(ClassName, {}) + bitfields: Any = Gen.class_member_bitfields.get(ClassName, {}) + if AttrName in bitfield_offsets and bitfields.get(AttrName, 0) > 0: + return self._load_bitfield_member(ObjVal, ClassName, AttrName) + if AttrName in bitfields and bitfields[AttrName] > 0: + if ClassName not in Gen.class_member_bitoffsets: + Gen.class_member_bitoffsets[ClassName] = {} + if AttrName not in Gen.class_member_bitoffsets[ClassName]: + bo: int = 0 + for bf_name, bf_width in bitfields.items(): + if bf_name == AttrName: + break + bo += bf_width + Gen.class_member_bitoffsets[ClassName][AttrName] = bo + return self._load_bitfield_member(ObjVal, ClassName, AttrName) + + offset = Gen._get_member_offset(AttrName, ClassName) + # 检查 offset 是否超出实际结构体 + if isinstance(ObjVal.type, ir.PointerType): + actual_pointee: Any = ObjVal.type.pointee + if isinstance(actual_pointee, ir.IdentifiedStructType): + actual_elems: Any = actual_pointee.elements + if actual_elems is not None and offset is not None and offset >= len(actual_elems): + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + actual_elems = actual_pointee.elements + if actual_elems is not None and offset >= len(actual_elems): + # sha1 冲突修复:ObjVal 的 pointee 可能是字段不全的版本(如 2 字段 Function), + # 而 st(来自 _try_Load_struct_from_stub)可能是字段完整的版本。 + # bitcast ObjVal 到 st 的类型,使 GEP 能正确访问完整字段。 + if st is not None and isinstance(st, ir.IdentifiedStructType) and st.elements is not None and offset < len(st.elements): + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_load_{ClassName}") + else: + return None + result = self._gep_and_Load_member(Gen, ObjVal, offset, AttrName, ClassName, st) + return result + + def _handle_attr_nested(self, Node: ast.Attribute) -> ir.Value | None: + """处理 a.b.c 嵌套属性访问""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(Node.value.value, ast.Name): + ParentVarName: str = Node.value.value.id + ParentAttrName: str = Node.value.attr + # __doc__ 魔法字符串:ClassName.method.__doc__ 或 module.ClassName.__doc__ + if Node.attr == '__doc__': + full_symbol: str = f"{ParentVarName}.{ParentAttrName}" + doc_ptr: ir.Value | None = Gen._get_doc_ptr(full_symbol) + if doc_ptr is not None: + return doc_ptr + # 回退:module.ClassName.__doc__ → 仅用 ClassName 查找 + doc_ptr = Gen._get_doc_ptr(ParentAttrName) + if doc_ptr is not None: + return doc_ptr + enum_result: Any = self._try_resolve_cross_module_enum(ParentVarName, ParentAttrName, Node.attr) + if enum_result is not None: + return enum_result + # 尝试跨模块子模块全局变量解析: a.b.c 其中 a.b 是已知子模块, c 是该模块的全局变量 + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if imported_modules: + full_ModulePath: str = f"{ParentVarName}.{ParentAttrName}" + if full_ModulePath in imported_modules: + result: Any = self._lookup_module_global(Gen, full_ModulePath, Node.attr) + if result is not None: + return result + elif isinstance(Node.value.value, ast.Attribute) and isinstance(Node.value.value.value, ast.Name): + # 三层嵌套:module.ClassName.method.__doc__ + if Node.attr == '__doc__': + ModuleName: str = Node.value.value.value.id + ClassName2: str = Node.value.value.attr + MethodName: str = Node.value.attr + FullSymbol3: str = f"{ModuleName}.{ClassName2}.{MethodName}" + doc_ptr3: ir.Value | None = Gen._get_doc_ptr(FullSymbol3) + if doc_ptr3 is not None: + return doc_ptr3 + # 回退:ClassName.method.__doc__ + doc_ptr3 = Gen._get_doc_ptr(f"{ClassName2}.{MethodName}") + if doc_ptr3 is not None: + return doc_ptr3 + # 三层嵌套:module.submodule.EnumClass.MEMBER + ModuleAlias3: str = f"{Node.value.value.value.id}.{Node.value.value.attr}" + EnumClassName3: str = Node.value.attr + enum_result3: Any = self._try_resolve_cross_module_enum(ModuleAlias3, EnumClassName3, Node.attr) + if enum_result3 is not None: + return enum_result3 + cdefine_result: Any = self._try_resolve_nested_cdefine(Node) + if cdefine_result is not None: + return cdefine_result + ObjVal: Any = self.HandleExprLlvm(Node.value) + if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): + return None + pointee: Any = ObjVal.type.pointee + + # char* → struct* cast + 成员访问 + if isinstance(pointee, ir.IntType) and pointee.width == 8: + AttrClassName: str | None = self._get_attr_class(Node.value, Gen) + if AttrClassName and AttrClassName in Gen.structs: + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, AttrClassName) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + return None + CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_{AttrClassName}") + offset: int | None = Gen._get_member_offset(Node.attr, AttrClassName) + if offset is None: + return None + if offset < len(st.elements) and isinstance(st.elements[offset], ir.VoidType): + return None + return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, AttrClassName, st) + + # 已知结构体类型 → 直接成员访问 + if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + # 提取父上下文特化提示:当 Node.value 是 Attribute(Name, attr) 时, + # 从 var_struct_class 和 class_member_element_class 获取字段声明的特化名。 + # 例如 func.Blocks.Head 中,func 是 Function,Blocks 字段声明为 GSList[BasicBlock], + # 提示为 'GSList[BasicBlock]',避免在所有 GSList[T] 都有 Head 字段时选错特化版本。 + parent_hint: str | None = None + if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name): + p_var: str = Node.value.value.id + p_attr: str = Node.value.attr + p_CN: str | None = Gen.var_struct_class.get(p_var) + if p_CN: + p_elem_map: Any = Gen.class_member_element_class.get(p_CN, {}) + parent_hint = p_elem_map.get(p_attr) + for CN, ST in Gen.structs.items(): + if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name): + st, ok = self._try_Load_struct_from_stub(Gen, CN) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN, parent_hint) + if spec_val is not None: + return spec_val + break + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) + if Node.attr in bitfield_offsets: + return self._load_bitfield_member(ObjVal, CN, Node.attr) + offset = Gen._get_member_offset(Node.attr, CN) + if offset is None: + break + return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + return None + + def _handle_attr_call_result(self, Node: ast.Attribute) -> ir.Value | None: + """处理 func().attr 属性访问""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + # Check if this is c.Deref(...) — we need the pointer, not the Loaded value + ObjVal: Any = None + is_cderef: bool = (isinstance(Node.value, ast.Call) and + isinstance(Node.value.func, ast.Attribute) and + isinstance(Node.value.func.value, ast.Name) and + Node.value.func.value.id == 'c' and + Node.value.func.attr == 'Deref' and + Node.value.args) + if is_cderef: + deref_arg: ast.expr = Node.value.args[0] + inner_val: Any = self.HandleExprLlvm(deref_arg) + if inner_val and isinstance(inner_val.type, ir.PointerType): + if isinstance(inner_val.type.pointee, ir.PointerType): + ObjVal = Gen._load(inner_val, name="deref_load_ptr") + else: + ObjVal = inner_val + if ObjVal is None: + ObjVal = self.HandleExprLlvm(Node.value) + if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): + return None + pointee: Any = ObjVal.type.pointee + if isinstance(pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr") + pointee = ObjVal.type.pointee + found: Any = Gen.find_struct_by_pointee(pointee) + if not found: + return None + CN: str + ST: Any + CN, ST = found + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, CN) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) + if spec_val is not None: + return spec_val + return None + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) + if Node.attr in bitfield_offsets: + return self._load_bitfield_member(ObjVal, CN, Node.attr) + offset: int | None = Gen._get_member_offset(Node.attr, CN) + if offset is None: + return None + return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + + def _handle_attr_subscript_result(self, Node: ast.Attribute) -> ir.Value | None: + """处理 arr[i].attr 属性访问""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + ObjVal: Any = self.HandleExprLlvm(Node.value) + if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): + return None + pointee: Any = ObjVal.type.pointee + if not isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + return None + found: Any = Gen.find_struct_by_pointee(pointee) + if not found: + return None + CN: str + ST: Any + CN, ST = found + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, CN) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) + if spec_val is not None: + return spec_val + return None + offset: int | None = Gen._get_member_offset(Node.attr, CN) + if offset is None: + return None + return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + + def _handle_attr_fallback(self, Node: ast.Attribute) -> ir.Value | None: + """兜底:对任意值尝试指针解引用后查找结构体成员""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + ObjVal: Any = self.HandleExprLlvm(Node.value) + if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): + return None + pointee: Any = ObjVal.type.pointee + if isinstance(pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr_fallback") + pointee = ObjVal.type.pointee + if not isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + return None + found: Any = Gen.find_struct_by_pointee(pointee) + if not found: + return None + CN: str + ST: Any + CN, ST = found + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, CN) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) + if spec_val is not None: + return spec_val + return None + offset: int | None = Gen._get_member_offset(Node.attr, CN) + if offset is None: + return None + return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + + # ========== 主入口 ========== + + def _HandleAttributeLlvm(self, Node: ast.Attribute) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + # 处理 t/c import 的别名重定向 + t_c_imported: Any = getattr(self.Trans, '_t_c_imported_names', {}) + if VarName != 'self' and VarName in t_c_imported: + src_module: str + src_name: str + src_module, src_name = t_c_imported[VarName] + virtual_attr: ast.Attribute = ast.Attribute( + value=ast.Name(id=src_module, ctx=ast.Load()), + attr=src_name, ctx=ast.Load() + ) + ast.copy_location(virtual_attr, Node) + return self._HandleAttributeLlvm(ast.Attribute( + value=virtual_attr, attr=Node.attr, ctx=ast.Load() + )) + if VarName == 'self': + return self._handle_attr_self(Node, VarName) + return self._handle_attr_name(Node, VarName) + + elif isinstance(Node.value, ast.Attribute): + return self._handle_attr_nested(Node) + + elif isinstance(Node.value, ast.Call): + return self._handle_attr_call_result(Node) + + elif isinstance(Node.value, ast.Subscript): + return self._handle_attr_subscript_result(Node) + + # 兜底 + return self._handle_attr_fallback(Node) + + # ========== HandleSubscript 及其辅助 ========== + + def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + # 查询数组元素字节序 + arr_byte_order: str = '' + if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders: + arr_byte_order = Gen.local_var_byteorders[Node.value.id] + ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen) + if ClassName and Gen._has_function(f'{ClassName}.__getitem__'): + obj_val: Any = self.HandleExprLlvm(Node.value) + idx_val: Any = self.HandleExprLlvm(Node.slice) + if obj_val and idx_val: + # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 + # 需要先 inttoptr 转换为 i8*,再 bitcast 到目标 struct 指针 + if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: + obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + if isinstance(idx_val.type, ir.IntType) and idx_val.type.width < 64: + idx_val = Gen.builder.zext(idx_val, ir.IntType(64), name="zext_idx") + _getitem_func: Any = Gen._get_function(f'{ClassName}.__getitem__') + # 处理泛型特化类型不匹配:obj_val 可能是通用类型,而函数期望特化类型 + if _getitem_func.ftype.args and _getitem_func.ftype.args[0] != obj_val.type: + _expected_type = _getitem_func.ftype.args[0] + if isinstance(_expected_type, ir.PointerType) and isinstance(obj_val.type, ir.PointerType): + obj_val = Gen.builder.bitcast(obj_val, _expected_type, name=f"cast_{ClassName}_getitem") + result: Any = Gen.builder.call(_getitem_func, [obj_val, idx_val], name=f"call_{ClassName}.__getitem__") + return result + ValueVal: Any = self.HandleExprLlvm(Node.value) + if not ValueVal: + return None + IndexVal: Any = self.HandleExprLlvm(Node.slice) + if not IndexVal: + return None + if isinstance(IndexVal.type, ir.PointerType) and isinstance(IndexVal.type.pointee, ir.IntType) and IndexVal.type.pointee.width == 8: + Loaded_byte: Any = Gen._load(IndexVal, name="Load_char_idx") + IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx") + if isinstance(ValueVal.type, ir.IntType): + var_ptr: Any = self._get_int_ptr(Node.value) + if var_ptr: + i8_ptr: Any = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") + ptr: Any = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") + return Gen._load(ptr, name="int_subscript_val") + return None + # 当变量本身是二级指针(如 elem_ptr: T | t.CPtr = AST**), + # 但 HandleExprLlvm 已自动 load 返回 AST*(一级指针), + # 需要检查 Gen.variables 中的原始 alloca 类型, + # 若是二级指针且存储的是结构体指针,应 bitcast ValueVal 为 AST** 后 GEP 再 load, + # 避免 GEP AST 结构体数组返回错误的 AST*(指向存储位置而非实际对象)。 + # 仅当 var_ptr_element 标记为 True 时触发(由 AnnAssign 检测 (S|t.CPtr)|t.CPtr 注解设置), + # 防止 pts: Point | t.CPtr(简单结构体指针)错误走二级指针路径。 + if (isinstance(Node.value, ast.Name) + and isinstance(ValueVal.type, ir.PointerType) + and isinstance(ValueVal.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))): + VarName: str = Node.value.id + _vpe_load: bool = (getattr(Gen, 'var_ptr_element', {}).get(VarName, False) + or getattr(Gen, 'global_var_ptr_element', {}).get(VarName, False)) + if _vpe_load and VarName in Gen.variables and Gen.variables[VarName] is not None: + VarAlloca: ir.Value = Gen.variables[VarName] + if (isinstance(VarAlloca.type, ir.PointerType) + and isinstance(VarAlloca.type.pointee, ir.PointerType)): + # 原始 alloca 是二级指针,ValueVal 是已 load 的一级结构体指针 + # 该指针指向数据缓冲区,缓冲区存储的是 AST* 指针数组, + # 需 bitcast ValueVal 为 AST** 后 GEP 指针数组再 load 出 AST* + PtrToStruct: ir.Type = ValueVal.type.pointee + BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal, + ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp_load") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_load") + ElemPtrLoad: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name="subscript_pp_load") + return Gen._load(ElemPtrLoad, name="ptr_elem_load") + if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") + ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name="subscript") + if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8: + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + if isinstance(ValueVal.type, ir.PointerType): + pointee: Any = ValueVal.type.pointee + if isinstance(pointee, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript") + elem_type: Any = pointee.element + if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + else: + if isinstance(pointee, ir.IntType) and pointee.width == 8: + elem_class: str | None = self._infer_element_struct_class(Node.value, Gen) + if elem_class and elem_class in Gen.structs: + CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index") + ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name="subscript") + if isinstance(pointee, ir.IntType): + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + return ElemPtr + # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr) + # elem_ptr 是 i8* 但实际指向 i8* (字符串指针), elem_ptr[0] 应加载 8 字节 + if isinstance(Node.value, ast.Name): + _ptr_elem_flag: bool = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) + or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) + if _ptr_elem_flag: + CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") + ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript") + return Gen._load(ElemPtr, name="str_ptr_load") + arr_type: Any = self._infer_array_member_type(Node.value, Gen) + if arr_type and isinstance(arr_type, ir.ArrayType): + arr_ptr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(arr_type), name=f"cast_arr_subscript") + zero = ir.Constant(ir.IntType(32), 0) + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name="subscript") + arr_elem_type: Any = arr_type.element + if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript") + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + elif isinstance(ValueVal.type, ir.ArrayType): + zero = ir.Constant(ir.IntType(32), 0) + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: Any = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript") + var_arr_elem_type: Any = VarPtr.type.pointee.element + if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + if isinstance(Node.value, ast.Attribute): + AttrPtr: Any = self._get_attr_ptr(Node.value) + if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript") + attr_arr_elem_type: Any = AttrPtr.type.pointee.element + if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + if isinstance(Node.value, ast.Subscript): + InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) + if InnerPtr and isinstance(InnerPtr.type, ir.PointerType): + inner_pointee: Any = InnerPtr.type.pointee + if isinstance(inner_pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript") + inner_elem_type: Any = inner_pointee.element + if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") + Gen._store(ValueVal, arr_alloc) + ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript") + alloc_arr_elem_type: Any = ValueVal.type.element + if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + return None + + def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + ValueVal: Any = self.HandleExprLlvm(Node.value) + if not ValueVal: + return None + IndexVal: Any = self.HandleExprLlvm(Node.slice) + if not IndexVal: + return None + if isinstance(IndexVal.type, ir.PointerType) and isinstance(IndexVal.type.pointee, ir.IntType) and IndexVal.type.pointee.width == 8: + Loaded_byte: Any = Gen._load(IndexVal, name="Load_char_idx_ptr") + IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx_ptr") + if isinstance(ValueVal.type, ir.PointerType): + pointee: Any = ValueVal.type.pointee + if isinstance(pointee, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript_ptr") + return ElemPtr + elif isinstance(pointee, ir.PointerType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_ptr") + ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript_ptr") + return ElemPtr + else: + # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr, bytes | t.CPtr) + # elem_ptr 是 i8* 但实际指向 i8* (指针数组), 应按 8 字节步长 GEP + if isinstance(Node.value, ast.Name): + _ptr_elem_flag: bool = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) + or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) + if _ptr_elem_flag: + CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr") + ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript_ptr") + return ElemPtr + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") + ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript_ptr") + return ElemPtr + elif isinstance(ValueVal.type, ir.IntType): + var_ptr: Any = self._get_int_ptr(Node.value) + if var_ptr: + i8_ptr: Any = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") + ElemPtr = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") + return ElemPtr + elif isinstance(ValueVal.type, ir.ArrayType): + zero = ir.Constant(ir.IntType(32), 0) + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: Any = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript_ptr") + return ElemPtr + if isinstance(Node.value, ast.Subscript): + InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) + if InnerPtr: + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript_ptr") + return ElemPtr + if isinstance(Node.value, ast.Attribute): + AttrPtr: Any = self._get_attr_ptr(Node.value) + if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript_ptr") + return ElemPtr + arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") + Gen._store(ValueVal, arr_alloc) + ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript_ptr") + return ElemPtr + return None + + # ========== _get_attr_ptr / _get_int_ptr / _get_attr_class ========== + + def _get_attr_ptr(self, Node: ast.Attribute) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if not isinstance(Node, ast.Attribute): + return None + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + if VarName == 'self': + ClassName: str | None = self.Trans._CurrentCpythonObjectClass + if ClassName: + offset: int | None = Gen._get_member_offset(Node.attr, ClassName) + SelfVar: Any = Gen._get_var_ptr('self') + if SelfVar: + SelfPtr: Any = Gen._load(SelfVar, name="self") + if isinstance(SelfPtr.type, ir.PointerType): + MemberPtr: Any = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + ClassName = Gen.var_struct_class.get(VarName) + if not ClassName and getattr(Gen, 'global_struct_class', None): + ClassName = Gen.global_struct_class.get(VarName) + if not ClassName: + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) + if imported_modules and (VarName in imported_modules or resolved_mod in imported_modules): + AttrName: str = Node.attr + ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) + candidates: list[str] = [VarName, resolved_mod] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + if mod_name not in candidates: + candidates.append(mod_name) + for cand in candidates: + sha1: str | None = ModuleSha1Map.get(cand) + if sha1: + prefixed: str = f"{sha1}.{AttrName}" + if prefixed in Gen.module.globals: + return Gen.module.globals[prefixed] + if AttrName in Gen.module.globals: + gv: Any = Gen.module.globals[AttrName] + if isinstance(gv, ir.GlobalVariable): + return gv + if ClassName and ClassName in Gen.structs: + VarPtr: Any = Gen.variables.get(VarName) + if VarPtr: + ObjVal: Any = VarPtr + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]: + ST: Any = Gen.structs[ClassName] + if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + ST = Gen.structs.get(ClassName, ST) + if ST.elements is not None and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{ClassName}") + offset = Gen._get_member_offset(Node.attr, ClassName) + if offset is not None and ST.elements is not None: + MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + elif isinstance(Node.value, ast.Attribute): + InnerPtr: Any = self._get_attr_ptr(Node.value) + if InnerPtr and isinstance(InnerPtr.type, ir.PointerType): + pointee: Any = InnerPtr.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found: Any = Gen.find_struct_by_pointee(pointee) + if found: + CN: str + ST: Any + CN, ST = found + if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen) + ST = Gen.structs.get(CN, ST) + if ST.elements is not None and isinstance(InnerPtr.type, ir.PointerType) and isinstance(InnerPtr.type.pointee, ir.IdentifiedStructType) and InnerPtr.type.pointee.elements is None: + InnerPtr = Gen.builder.bitcast(InnerPtr, ir.PointerType(ST), name=f"cast_{CN}") + offset = Gen._get_member_offset(Node.attr, CN) + if offset is not None and ST.elements is not None: + MemberPtr = Gen.builder.gep(InnerPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + elif isinstance(pointee, ir.IntType) and pointee.width == 8: + AttrClassName: str | None = self._get_attr_class(Node.value, Gen) + if AttrClassName and AttrClassName in Gen.structs: + CastedObj: Any = Gen.builder.bitcast(InnerPtr, ir.PointerType(Gen.structs[AttrClassName]), name=f"cast_{AttrClassName}") + offset = Gen._get_member_offset(Node.attr, AttrClassName) + if offset is not None: + MemberPtr = Gen.builder.gep(CastedObj, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + elif isinstance(Node.value, ast.Subscript): + SubPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) + if SubPtr and isinstance(SubPtr.type, ir.PointerType): + pointee = SubPtr.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found = Gen.find_struct_by_pointee(pointee) + if found: + CN, ST = found + offset = Gen._get_member_offset(Node.attr, CN) + if offset is not None: + MemberPtr = Gen.builder.gep(SubPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + return None + + def _HandleSliceLlvm(self, base_val: ir.Value, slice_node: ast.Slice | ast.Index, base_ast: ast.AST) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + lower_val: Any = None + upper_val: Any = None + step_val: Any = None + if isinstance(slice_node, ast.Slice): + if slice_node.lower: + lower_val = self.HandleExprLlvm(slice_node.lower) + if slice_node.upper: + upper_val = self.HandleExprLlvm(slice_node.upper) + if slice_node.step: + step_val = self.HandleExprLlvm(slice_node.step) + if isinstance(slice_node, ast.Index): + return self.HandleExprLlvm(slice_node.value) + return None + + def _infer_element_struct_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + if isinstance(node, ast.Attribute): + attr_name: str = node.attr + if isinstance(node.value, ast.Name) and node.value.id == 'self': + current_class: str | None = self.Trans._CurrentCpythonObjectClass + if current_class: + if current_class in Gen.class_member_element_class: + elem_class: str | None = Gen.class_member_element_class[current_class].get(attr_name) + if elem_class and elem_class in Gen.structs: + return elem_class + if current_class in Gen.class_members: + for member_name, member_type in Gen.class_members[current_class]: + if member_name == attr_name: + if isinstance(member_type, ir.IdentifiedStructType): + found: Any = Gen.find_struct_by_pointee(member_type) + if found: + return found[0] + break + class_name: str | None = Gen.var_struct_class.get(attr_name) + if class_name: + return class_name + elif isinstance(node, ast.Name): + var_name: str = node.id + class_name = Gen.var_struct_class.get(var_name) + if class_name: + return class_name + return None + + def _get_int_ptr(self, target: ast.AST) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(target, ast.Name): + VarName: str = target.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: Any = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType): + pointee: Any = VarPtr.type.pointee + if isinstance(pointee, ir.ArrayType): + return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}") + return VarPtr + elif isinstance(target, ast.Attribute): + if isinstance(target.value, ast.Name): + obj_name: str = target.value.id + if obj_name in Gen.variables and Gen.variables[obj_name] is not None: + obj_ptr: Any = Gen.variables[obj_name] + if isinstance(obj_ptr.type, ir.PointerType): + pointee = obj_ptr.type.pointee + if isinstance(pointee, ir.PointerType): + obj_ptr = Gen._load(obj_ptr, name=f"Load_{obj_name}") + pointee = obj_ptr.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + StructName: str | None = None + if isinstance(pointee, ir.IdentifiedStructType): + StructName = pointee.name + if not StructName: + found: Any = Gen.find_struct_by_pointee(pointee) + if found: + StructName = found[0] + if StructName and StructName in Gen.structs: + offset: int | None = Gen._get_member_offset(target.attr, StructName) + if offset is not None: + return Gen.builder.gep(obj_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") + obj_val: Any = self.HandleExprLlvm(target.value) + if obj_val and isinstance(obj_val.type, ir.PointerType): + if isinstance(obj_val.type.pointee, ir.PointerType): + obj_val = Gen._load(obj_val, name="Load_attr_ptr") + pointee = obj_val.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + StructName = None + if isinstance(pointee, ir.IdentifiedStructType): + StructName = pointee.name + if not StructName: + found = Gen.find_struct_by_pointee(pointee) + if found: + StructName = found[0] + if StructName and StructName in Gen.structs: + offset = Gen._get_member_offset(target.attr, StructName) + if offset is not None: + return Gen.builder.gep(obj_val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") + return None + + def _resolve_struct_class_for_attr(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + if isinstance(Node, ast.Name): + VarName: str = Node.id + if VarName in Gen.var_struct_class: + return Gen.var_struct_class[VarName] + elif isinstance(Node, ast.Attribute): + ClassName: str | None = self._resolve_struct_class_for_attr(Node.value, Gen) + if ClassName and ClassName in Gen.class_members: + for member_name, _ in Gen.class_members[ClassName]: + if member_name == Node.attr: + return ClassName + return None + + def _infer_array_member_type(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> ir.ArrayType | None: + if isinstance(Node, ast.Attribute): + parent_class: str | None = self._get_attr_class(Node, Gen) + if parent_class and parent_class in Gen.class_members: + for m_name, m_type in Gen.class_members[parent_class]: + if m_name == Node.attr and isinstance(m_type, ir.ArrayType): + return m_type + if parent_class and parent_class in Gen.structs: + st: Any = Gen.structs[parent_class] + if isinstance(st, ir.IdentifiedStructType) and st.elements is not None: + offset: int | None = Gen._get_member_offset(Node.attr, parent_class) + if offset is not None and offset < len(st.elements): + elem: Any = st.elements[offset] + if isinstance(elem, ir.ArrayType): + return elem + return None + + def _build_attr_path(self, node: ast.AST) -> list[str]: + parts: list[str] = [] + current: ast.AST = node + while isinstance(current, ast.Attribute): + parts.append(current.attr) + current = current.value + if isinstance(current, ast.Name): + parts.append(current.id) + parts.reverse() + return parts + + def _try_resolve_nested_cdefine(self, Node: ast.Attribute) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if not imported_modules: + return None + parts: list[str] = self._build_attr_path(Node) + if len(parts) < 2: + return None + attr_name: str = parts[-1] + module_parts: list[str] = parts[:-1] + possible_keys: list[str] = [attr_name] + full_path: str = '.'.join(parts) + possible_keys.append(full_path) + if module_parts: + ModulePath: str = '.'.join(module_parts) + resolved_first: str = self.Trans.SymbolTable.resolve_alias(module_parts[0]) + if resolved_first != module_parts[0]: + resolved_parts: list[str] = [resolved_first] + module_parts[1:] + resolved_path: str = '.'.join(resolved_parts) + possible_keys.append(f"{resolved_path}.{attr_name}") + for mod_name in imported_modules: + if mod_name.endswith('.' + ModulePath) or mod_name == ModulePath: + key: str = f"{mod_name}.{attr_name}" + if key not in possible_keys: + possible_keys.append(key) + if module_parts and (mod_name.endswith('.' + module_parts[-1]) or mod_name == module_parts[-1]): + key = f"{mod_name}.{attr_name}" + if key not in possible_keys: + possible_keys.append(key) + for lookup_key in possible_keys: + SymInfo: Any = self.Trans.SymbolTable.lookup(lookup_key) + if SymInfo and SymInfo.IsDefine and SymInfo.DefineValue is not None: + val: Any = SymInfo.DefineValue + if isinstance(val, int): + if val > 0x7FFFFFFF or val < -0x80000000: + return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF) + return ir.Constant(ir.IntType(32), val) + elif isinstance(val, float): + return ir.Constant(ir.FloatType(), val) + return None + + def _try_resolve_cross_module_enum(self, module_alias: str, enum_class_name: str, member_name: str) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if not imported_modules: + return None + resolved_module: str = self.Trans.SymbolTable.resolve_alias(module_alias) + if resolved_module not in imported_modules and module_alias not in imported_modules: + return None + enum_keys: list[str] = [enum_class_name, f"{resolved_module}.{enum_class_name}"] + for enum_key in enum_keys: + SymInfo: Any = self.Trans.SymbolTable.lookup(enum_key) + if SymInfo: + if SymInfo.IsEnum: + for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"): + info: Any = self.Trans.SymbolTable.lookup(qname) + if info and info.IsEnumMember and isinstance(info.value, int): + return ir.Constant(ir.IntType(32), info.value) + for key, info in self.Trans.SymbolTable.items(): + if info.IsEnumMember and info.EnumName == enum_key and key == member_name: + if isinstance(info.value, int): + return ir.Constant(ir.IntType(32), info.value) + for key, info in self.Trans.SymbolTable.items(): + if info.IsEnumMember and key == member_name: + if isinstance(info.value, int): + for enum_key in enum_keys: + if info.EnumName == enum_key: + return ir.Constant(ir.IntType(32), info.value) + return None + + def _get_attr_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + if isinstance(Node, ast.Attribute): + # 注: 不再硬编码 ZLIB_VERSION / C_OK,它们应从符号表查找 + if isinstance(Node.value, ast.Name) and Node.value.id == 'self': + ClassName: str | None = self.Trans._CurrentCpythonObjectClass + if ClassName and ClassName in Gen.class_member_element_class: + return Gen.class_member_element_class[ClassName].get(Node.attr) + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + if VarName in Gen.var_struct_class: + ParentClass: str = Gen.var_struct_class[VarName] + if ParentClass in Gen.class_member_element_class: + return Gen.class_member_element_class[ParentClass].get(Node.attr) + if isinstance(Node.value, ast.Attribute): + ParentClass = self._get_attr_class(Node.value, Gen) + if ParentClass and ParentClass in Gen.class_member_element_class: + return Gen.class_member_element_class[ParentClass].get(Node.attr) + return None + + def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None: + return Gen._get_member_offset(field_name, ClassName) \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesExprBuiltin.py b/App/lib/core/Handles/HandlesExprBuiltin.py new file mode 100644 index 0000000..ae09c61 --- /dev/null +++ b/App/lib/core/Handles/HandlesExprBuiltin.py @@ -0,0 +1,991 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin +import ast +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.includes.t import CTypeRegistry +from lib.core.SymbolUtils import IsTModule, ExtractTypeNameFromBinOp + + +class ExprBuiltinHandle(BaseHandle): + _C_LIB_FUNCS: dict[str, object] = { + 'strlen': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'strlength': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'memset': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]), + 'memcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'memmove': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'memcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'strncmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'strncpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcat': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'puts': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), + 'atoi': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), + 'atol': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'abs': lambda: (ir.IntType(32), [ir.IntType(32)]), + 'labs': lambda: (ir.IntType(64), [ir.IntType(64)]), + } + + def _HandleBuiltinCallLlvm(self, Node: ast.Call) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not isinstance(Node.func, ast.Name): + return None + FuncName: str = Node.func.id + + if FuncName == 'print': + return self._HandlePrintLlvm(Node) + elif FuncName == 'len': + if Node.args: + return self._HandleLenLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'sizeof': + if Node.args: + return self._HandleSizeofLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'abs': + if Node.args: + return self._HandleAbsLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'arg': + return self._HandleArgLlvm(Node.args) + elif FuncName == 'bool': + if Node.args: + Val = self.HandleExprLlvm(Node.args[0]) + if Val: + if isinstance(Val.type, ir.IntType): + if Val.type.width == 1: + return Val + return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result") + elif isinstance(Val.type, ir.PointerType): + return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result") + return Val + return ir.Constant(ir.IntType(1), 0) + elif FuncName == 'int': + if Node.args: + Val = self.HandleExprLlvm(Node.args[0]) + if Val: + if isinstance(Val.type, ir.IntType): + if Val.type.width < 32: + return Gen.builder.zext(Val, ir.IntType(32), name="cast_int") + elif Val.type.width > 32: + return Gen.builder.trunc(Val, ir.IntType(32), name="trunc_int") + return Val + elif isinstance(Val.type, ir.PointerType): + if isinstance(Val.type.pointee, ir.IntType) and Val.type.pointee.width == 8: + result = self._HandleAtoiLlvm(Val) + if result: + return result + elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)): + return Gen.builder.fptosi(Val, ir.IntType(32), name="float_to_int") + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'float' or FuncName == 'double': + if Node.args: + Val = self.HandleExprLlvm(Node.args[0]) + if Val: + target_type = ir.DoubleType() if FuncName == 'double' else ir.FloatType() + if isinstance(Val.type, (ir.FloatType, ir.DoubleType)): + if Val.type != target_type: + if isinstance(target_type, ir.DoubleType): + return Gen.builder.fpext(Val, target_type, name="fpext_double") + return Gen.builder.fptrunc(Val, target_type, name="fptrunc_float") + return Val + elif isinstance(Val.type, ir.IntType): + if Val.type.width == 1: + return Gen.builder.uitofp(Val, target_type, name="uitofp") + return Gen.builder.sitofp(Val, target_type, name="sitofp") + return ir.Constant(ir.DoubleType() if FuncName == 'double' else ir.FloatType(), 0.0) + elif FuncName == 'min': + if len(Node.args) >= 2: + a = self.HandleExprLlvm(Node.args[0]) + b = self.HandleExprLlvm(Node.args[1]) + if a and b: + is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType)) + if is_float: + fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="min_int2float") + fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="min_int2float") + cmp = Gen.builder.fcmp_ordered('olt', fa, fb, name="min_cmp") + return Gen.builder.select(cmp, fa, fb, name="min_val") + else: + cmp = Gen.builder.icmp_signed('<', a, b, name="min_cmp") + return Gen.builder.select(cmp, a, b, name="min_val") + elif len(Node.args) == 1: + return self.HandleExprLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'max': + if len(Node.args) >= 2: + a = self.HandleExprLlvm(Node.args[0]) + b = self.HandleExprLlvm(Node.args[1]) + if a and b: + is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType)) + if is_float: + fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="max_int2float") + fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="max_int2float") + cmp = Gen.builder.fcmp_ordered('ogt', fa, fb, name="max_cmp") + return Gen.builder.select(cmp, fa, fb, name="max_val") + else: + cmp = Gen.builder.icmp_signed('>', a, b, name="max_cmp") + return Gen.builder.select(cmp, a, b, name="max_val") + elif len(Node.args) == 1: + return self.HandleExprLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'str': + if Node.args: + arg_node = Node.args[0] + arg_val = self.HandleExprLlvm(arg_node) + if arg_val: + if isinstance(arg_val.type, ir.PointerType): + ClassName = self.Trans.ExprHandler._get_var_class(arg_node, Gen) + if ClassName and Gen._has_function(f'{ClassName}.__str__'): + if isinstance(arg_val.type, ir.PointerType) and isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8: + if ClassName in Gen.structs: + arg_val = Gen.builder.bitcast(arg_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}_str") + str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [arg_val], name=f"call_{ClassName}.__str__") + return str_val + return arg_val + return self._EmitStringFromValue(arg_val) + return ir.Constant(ir.IntType(8).as_pointer(), None) + elif FuncName == 'bytes': + # bytes() — 类型转换为 i8* 指针 (等同于 t.CPtr / str) + # bytes(int_addr) → inttoptr; bytes(ptr) → bitcast + if Node.args: + arg_val = self.HandleExprLlvm(Node.args[0]) + if arg_val: + if isinstance(arg_val.type, ir.PointerType): + if isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8: + return arg_val + return Gen.builder.bitcast(arg_val, ir.IntType(8).as_pointer(), name="bytes_cast") + if isinstance(arg_val.type, ir.IntType): + return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="bytes_int2ptr") + return ir.Constant(ir.IntType(8).as_pointer(), None) + elif FuncName == 'input': + return self._HandleInputLlvm(Node) + elif FuncName == 'ord': + if Node.args: + return self._HandleOrdLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'chr': + if Node.args: + return self._HandleChrLlvm(Node.args[0]) + return ir.Constant(ir.IntType(8), 0) + elif FuncName == 'malloc': + return self._HandleMallocLlvm(Node.args) + elif FuncName == 'realloc': + return self._HandleReallocLlvm(Node.args) + elif FuncName == 'free': + return self._HandleFreeLlvm(Node.args) + elif FuncName == 'memcpy': + return self._HandleMemcpyLlvm(Node.args) + elif FuncName == 'memset': + return self._HandleMemsetLlvm(Node.args) + elif FuncName == 'va_start': + return self._HandleVaStartLlvm(Node.args) + elif FuncName == 'va_end': + return self._HandleVaEndLlvm(Node.args) + elif FuncName == 'va_arg': + return self._HandleVaArgLlvm(Node.args) + elif FuncName == 'dir': + return self._HandleDirLlvm(Node) + elif FuncName == 'type': + return self._HandleTypeLlvm(Node) + return None + + def _HandlePrintLlvm(self, Node: ast.Call) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + args: list[ast.expr] = Node.args + end_arg: ast.expr | None = None + sep_arg: ast.expr | None = None + if Node.keywords: + for kw in Node.keywords: + if kw.arg == 'end': + end_arg = kw.value + elif kw.arg == 'sep': + sep_arg = kw.value + sep_str: str = ' ' + if sep_arg and isinstance(sep_arg, ast.Constant) and isinstance(sep_arg.value, str): + sep_str = sep_arg.value + end_str: str = '\n' + if end_arg: + if isinstance(end_arg, ast.Constant) and isinstance(end_arg.value, str): + end_str = end_arg.value + elif isinstance(end_arg, ast.Constant) and end_arg.value is None: + end_str = '' + if not args: + if end_str: + nl_fmt = Gen.emit_constant(end_str, 'string') + Gen.builder.call(printf_func, [nl_fmt], name="print_nl") + return ir.Constant(ir.IntType(32), 0) + has_fstring = any(isinstance(arg, ast.JoinedStr) for arg in args) + if has_fstring: + return self._HandlePrintWithFString(Node, printf_func, args, sep_str, end_str) + fmt_parts = [] + fmt_args = [] + for i, arg in enumerate(args): + if i > 0: + fmt_parts.append(sep_str) + val = self.HandleExprLlvm(arg) + if not val: + fmt_parts.append('(null)') + continue + if isinstance(val, ir.Constant) and isinstance(val.type, ir.PointerType) and val.constant is None: + fmt_parts.append('nil') + continue + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if isinstance(pointee, ir.IntType) and pointee.width == 8: + fmt_parts.append('%s') + fmt_args.append(val) + elif isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8: + elem_ptr = Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="print_arr_to_ptr") + fmt_parts.append('%s') + fmt_args.append(elem_ptr) + elif isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen) + if not ClassName and isinstance(arg, ast.Name): + ClassName = Gen.var_struct_class.get(arg.id) + if ClassName and Gen._has_function(f'{ClassName}.__str__'): + if isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8: + if ClassName in Gen.structs: + val = Gen.builder.bitcast(val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [val], name=f"call_{ClassName}.__str__") + fmt_parts.append('%s') + fmt_args.append(str_val) + else: + int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int") + fmt_parts.append('0x%llx') + fmt_args.append(int_val) + else: + int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int") + fmt_parts.append('%lld') + fmt_args.append(int_val) + elif isinstance(val.type, ir.IntType): + is_unsigned = self._is_val_unsigned(arg, val) + if val.type.width == 1: + fmt_parts.append('%d') + val = Gen.builder.zext(val, ir.IntType(32), name="zext_bool_print") + elif val.type.width == 8: + if self._is_char_type(arg): + char_var = Gen._allocaEntry(ir.IntType(8), name="print_char_buf") + Gen.builder.store(val, char_var) + fmt_parts.append('%c') + fmt_args.append(char_var) + continue + fmt_parts.append('%d') + if is_unsigned: + val = Gen.builder.zext(val, ir.IntType(32), name="zext_i8_print") + else: + val = Gen.builder.sext(val, ir.IntType(32), name="sext_i8_print") + elif val.type.width == 16: + fmt_parts.append('%u' if is_unsigned else '%d') + if is_unsigned: + val = Gen.builder.zext(val, ir.IntType(32), name="zext_i16_print") + else: + val = Gen.builder.sext(val, ir.IntType(32), name="sext_i16_print") + elif val.type.width == 32: + fmt_parts.append('%u' if is_unsigned else '%d') + elif val.type.width == 64: + fmt_parts.append('%llu' if is_unsigned else '%lld') + else: + fmt_parts.append('%d') + val = Gen.builder.zext(val, ir.IntType(32), name="zext_int_print") + fmt_args.append(val) + elif isinstance(val.type, ir.FloatType): + fmt_parts.append('%f') + fmt_args.append(Gen.builder.fpext(val, ir.DoubleType(), name="fpext_printf_float")) + elif isinstance(val.type, ir.DoubleType): + fmt_parts.append('%f') + fmt_args.append(val) + else: + fmt_parts.append('(unknown)') + fmt_parts.append(end_str) + fmt_str = ''.join(fmt_parts) + fmt_ptr = Gen._create_string_global(fmt_str) + call_args = [fmt_ptr] + fmt_args + return Gen.builder.call(printf_func, call_args, name="print_call") + + def _HandlePrintWithFString(self, Node: ast.Call, printf_func: ir.Function, args: list[ast.expr], sep_str: str, end_str: str) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + for i, arg in enumerate(args): + if i > 0: + sep_const = Gen.emit_constant(sep_str, 'string') + Gen.builder.call(printf_func, [sep_const], name="print_sep") + if isinstance(arg, ast.JoinedStr): + str_val = self.HandleExprLlvm(arg) + if str_val: + Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None) + else: + ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen) + if ClassName and Gen._has_function(f'{ClassName}.__str__'): + obj_val = self.HandleExprLlvm(arg) + if obj_val: + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + if ClassName in Gen.structs: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [obj_val], name=f"call_{ClassName}.__str__") + Gen._RegisterTempPtr(str_val) + Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None) + else: + val = self.HandleExprLlvm(arg) + if val: + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if not (isinstance(pointee, ir.IntType) and pointee.width == 8): + try: + val = Gen._load(val, name="print_Load") + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + is_u = Gen._check_node_unsigned(arg) + if not is_u and id(val) in Gen._unsigned_results: + is_u = True + Gen._emit_printf([val], unsigned_flags=[is_u], sep=None, end=None) + if end_str: + end_const = Gen.emit_constant(end_str, 'string') + Gen.builder.call(printf_func, [end_const], name="print_end") + return ir.Constant(ir.IntType(32), 0) + + def _is_char_type(self, arg_node: ast.AST) -> bool: + if isinstance(arg_node, ast.Call): + if isinstance(arg_node.func, ast.Name): + return arg_node.func.id in ('CChar', 'CUInt8T', 'CInt8T') + if isinstance(arg_node.func, ast.Attribute): + return arg_node.func.attr in ('CChar', 'CUInt8T', 'CInt8T') + return False + + def _is_val_unsigned(self, arg_node: ast.AST, val: ir.Value) -> bool: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if isinstance(arg_node, ast.Name): + var_name: str = arg_node.id + signedness: object = Gen.var_signedness.get(var_name) + if signedness: + if isinstance(signedness, bool): + return signedness + return 'unsigned' in signedness + if isinstance(val.type, ir.IntType): + return False + return False + + def _try_declare_c_lib_func(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.Function | None: + if func_name not in self._C_LIB_FUNCS: + return None + ret_type: ir.Type + param_types: list[ir.Type] + ret_type, param_types = self._C_LIB_FUNCS[func_name]() + func_type: ir.FunctionType = ir.FunctionType(ret_type, param_types) + func: ir.Function = ir.Function(Gen.module, func_type, name=func_name) + Gen.functions[func_name] = func + return func + + def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name) and arg_node.value.id == 'self': + attr_name = arg_node.attr + len_member = f'{attr_name}__len' + current_class = self.Trans._CurrentCpythonObjectClass + if current_class and current_class in Gen.class_members: + for m_name, m_type in Gen.class_members[current_class]: + if m_name == len_member: + self_val = Gen._get_var_ptr('self') + if self_val: + self_ptr = Gen._load(self_val, name="self_for_len") + offset = Gen._get_member_offset(len_member, current_class) + len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"self_{len_member}_ptr") + return Gen._load(len_ptr, name=f"self_{len_member}") + val = self.HandleExprLlvm(arg_node) + if not val: + return None + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if isinstance(pointee, ir.PointerType): + val = Gen._load(val, name="len_deref") + pointee = val.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__len__', val, Gen) + if result is not None: + return result + break + if isinstance(pointee, ir.ArrayType): + return ir.Constant(ir.IntType(64), pointee.count) + if isinstance(pointee, (ir.FloatType, ir.DoubleType, ir.IntType)) and pointee.width != 8: + return ir.Constant(ir.IntType(64), 0) + if val.type != ir.PointerType(ir.IntType(8)): + val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast") + correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))]) + strlen_func = None + if 'strlen' in Gen.functions: + existing = Gen.functions['strlen'] + if existing.ftype == correct_strlen_type: + strlen_func = existing + if not strlen_func: + try: + strlen_func = ir.Function(Gen.module, correct_strlen_type, name='strlen') + Gen.functions['strlen'] = strlen_func + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + if strlen_func: + result = Gen.builder.call(strlen_func, [val], name="strlen_call") + return result + elif isinstance(val.type, ir.ArrayType): + return ir.Constant(ir.IntType(64), val.type.count) + return ir.Constant(ir.IntType(64), 0) + + def _resolve_nested_opaque_structs(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, visited: set | None = None) -> None: + """递归解析嵌套的 opaque struct,从 stub 文件中加载其定义""" + if visited is None: + visited = set() + if isinstance(struct_type, ir.PointerType): + self._resolve_nested_opaque_structs(struct_type.pointee, Gen, visited) + return + if not isinstance(struct_type, ir.IdentifiedStructType): + return + if struct_type in visited: + return + visited.add(struct_type) + if struct_type.elements is None or len(struct_type.elements) == 0: + # 尝试从 stub 加载 opaque struct + try: + st_name = struct_type.name + except Exception: + st_name = None + if st_name: + # 提取短名称(去掉模块前缀) + if '.' in st_name: + short_name = st_name.split('.', 1)[1] + else: + short_name = st_name + self.Trans.ImportHandler._TryLoadStructFromStub(short_name, Gen) + return + # 递归解析嵌套的 struct 成员 + for elem in struct_type.elements: + self._resolve_nested_opaque_structs(elem, Gen, visited) + + def _HandleSizeofLlvm(self, Node: ast.AST) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + type_name: str | None + if isinstance(Node, ast.Name): + type_name = Node.id + elif isinstance(Node, ast.Attribute): + type_name = Node.attr + else: + return ir.Constant(ir.IntType(64), 0) + + if type_name not in Gen.structs: + # str/bytes 是指针类型 (char*/void*),sizeof = 指针大小 + if type_name in ('str', 'bytes'): + return ir.Constant(ir.IntType(64), 8) + ctype_cls = CTypeRegistry.GetClassByName(type_name) + if ctype_cls is not None: + try: + ctype_inst = ctype_cls() + size_bits = getattr(ctype_inst, 'Size', 0) or 0 + size_bytes = size_bits // 8 + return ir.Constant(ir.IntType(64), size_bytes) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型失败: {_e}", "Exception") + resolved = CTypeRegistry.ResolveName(type_name) + if resolved: + ctype_cls, ptr_level = resolved + try: + ctype_inst = ctype_cls() + size_bits = getattr(ctype_inst, 'Size', 0) or 0 + size_bytes = size_bits // 8 + if ptr_level > 0: + size_bytes = 8 + return ir.Constant(ir.IntType(64), size_bytes) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型失败: {_e}", "Exception") + return ir.Constant(ir.IntType(64), 0) + + struct_type = Gen.structs[type_name] + if isinstance(struct_type, ir.PointerType): + struct_type = struct_type.pointee + if isinstance(struct_type, ir.IdentifiedStructType) and (struct_type.elements is None or len(struct_type.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(type_name, Gen) + struct_type = Gen.structs.get(type_name, struct_type) + # 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确 + self._resolve_nested_opaque_structs(struct_type, Gen) + size = Gen._get_struct_size(struct_type) + if size > 0: + return ir.Constant(ir.IntType(64), size) + return ir.Constant(ir.IntType(64), 0) + + def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + val: ir.Value | None = self.HandleExprLlvm(arg_node) + if not val: + return None + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__abs__', val, Gen) + if result is not None: + return result + break + if isinstance(val.type, ir.IntType): + neg_val = Gen.builder.neg(val, name="abs_neg") + is_neg = Gen.builder.icmp_signed('<', val, ir.Constant(val.type, 0), name="is_neg") + return Gen.builder.select(is_neg, neg_val, val, name="abs_result") + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + zero = ir.Constant(val.type, 0.0) + neg_val = Gen.builder.fneg(val, name="fabs_neg") + is_neg = Gen.builder.fcmp_ordered('<', val, zero, name="f_is_neg") + return Gen.builder.select(is_neg, neg_val, val, name="fabs_result") + return None + + def _HandleDirLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + return ir.Constant(ir.IntType(8).as_pointer(), None) + + def _HandleTypeLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + type_str: str + if not Node.args: + type_str = "" + else: + arg = Node.args[0] + enum_type_name = None + + def _get_attr_path(node: ast.AST) -> str | None: + if isinstance(node, ast.Name): + return node.id + elif isinstance(node, ast.Attribute): + return f"{_get_attr_path(node.value)}.{node.attr}" + return None + + if isinstance(arg, ast.Name): + arg_name = arg.id + Gen = self.Trans.LlvmGen + if arg_name in Gen.var_type_info: + type_info = Gen.var_type_info[arg_name] + if isinstance(type_info, dict) and type_info.get('type') == 'enum': + enum_type_name = type_info.get('name') + if not enum_type_name and self.Trans.SymbolTable.has(arg_name): + TypeInfo = self.Trans.SymbolTable[arg_name] + if TypeInfo.IsEnum: + enum_type_name = arg_name + elif isinstance(arg, ast.Attribute): + LastPart = None + enum_class_name = None + if isinstance(arg.value, ast.Name): + LastPart = arg.attr + if self.Trans.SymbolTable.has(LastPart): + AttrInfo = self.Trans.SymbolTable[LastPart] + if AttrInfo.IsEnumMember and AttrInfo.EnumName: + enum_type_name = AttrInfo.EnumName + elif isinstance(arg.value, ast.Attribute): + attr_path = _get_attr_path(arg) + if attr_path: + parts = attr_path.split('.') + if len(parts) >= 2: + enum_class_name = parts[-2] + LastPart = parts[-1] + qualified_name_dot = f"{enum_class_name}.{LastPart}" + qualified_name_under = f"{enum_class_name}_{LastPart}" + enum_member_found = None + for qname in (qualified_name_dot, qualified_name_under): + if self.Trans.SymbolTable.has(qname): + info = self.Trans.SymbolTable[qname] + if info.IsEnumMember and info.EnumName == enum_class_name: + enum_member_found = info + break + if not enum_member_found: + for key in self.Trans.SymbolTable: + if key == LastPart: + info = self.Trans.SymbolTable[key] + if info.IsEnumMember and info.EnumName == enum_class_name: + enum_member_found = info + break + if enum_member_found: + enum_type_name = enum_class_name + if enum_type_name: + type_str = f"" + else: + llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg) + if isinstance(llvm_type, ir.PointerType): + pointee = llvm_type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + llvm_type = pointee + type_info = self.Trans.ExprUtils._llvm_type_to_detailed_string(llvm_type) + type_str = f"" + return Gen.emit_constant(type_str, 'string') + + def _HandleInputLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if Node.args and isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, str): + prompt = Node.args[0].value + prompt_ptr = Gen.emit_constant(prompt, 'string') + printf_func = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + Gen.builder.call(printf_func, [prompt_ptr]) + scanf_func = Gen.get_or_declare_c_func('scanf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + input_buffer = Gen._alloca(ir.ArrayType(ir.IntType(8), 256), name="input_buffer") + result = Gen.builder.call(scanf_func, [Gen.emit_constant("%255s", 'string'), input_buffer]) + return Gen.builder.gep(input_buffer, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="input_result") + + def _HandleAtoiLlvm(self, str_ptr: ir.Value) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + atoi_func: ir.Function = Gen.get_or_declare_c_func('atoi', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))])) + return Gen.builder.call(atoi_func, [str_ptr], name="atoi_result") + + def _HandleOrdLlvm(self, expr_node: ast.AST) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + val: ir.Value | None = self.HandleExprLlvm(expr_node) + if val and isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8: + return Gen._load(val, name="ord_result") + return ir.Constant(ir.IntType(32), 0) + + def _HandleChrLlvm(self, expr_node: ast.AST) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + val: ir.Value | None = self.HandleExprLlvm(expr_node) + if val: + char_ptr = Gen._alloca(ir.IntType(8), name="chr_char") + Gen.builder.store(Gen.builder.trunc(val, ir.IntType(8), name="trunc_chr"), char_ptr) + return Gen.builder.gep(char_ptr, [ir.Constant(ir.IntType(32), 0)], name="chr_result") + return ir.Constant(ir.IntType(8).as_pointer(), None) + + def _EmitStringFromValue(self, val: ir.Value) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if isinstance(val.type, ir.IntType): + if val.type.width == 8: + buf = Gen._alloca(ir.IntType(8), name="str_buf", size=ir.Constant(ir.IntType(32), 2)) + null_ptr = Gen.builder.gep(buf, [ir.Constant(ir.IntType(32), 1)], name="null_byte") + Gen.builder.store(ir.Constant(ir.IntType(8), 0), null_ptr) + Gen.builder.store(val, buf) + return Gen.builder.gep(buf, [ir.Constant(ir.IntType(32), 0)], name="str_result") + buf = Gen._alloca(ir.IntType(8), name="str_buf", size=ir.Constant(ir.IntType(32), 32)) + snprintf_func = Gen.get_or_declare_c_func('snprintf', ir.FunctionType(ir.IntType(32), [ + ir.PointerType(ir.IntType(8)), + ir.IntType(64), + ir.PointerType(ir.IntType(8)) + ], var_arg=True)) + fmt_ptr = Gen.emit_constant("%d", 'string') + Gen.builder.call(snprintf_func, [buf, ir.Constant(ir.IntType(64), 32), fmt_ptr, val]) + return Gen.builder.gep(buf, [ir.Constant(ir.IntType(32), 0)], name="str_result") + return ir.Constant(ir.IntType(8).as_pointer(), None) + + def _HandleMallocLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) + malloc_func: ir.Function = Gen._get_or_declare_func('malloc', malloc_type) + param_type: ir.Type = malloc_func.type.pointee.args[0] + size_val: ir.Value | None = None + if args: + size_val = self.HandleExprLlvm(args[0]) + if size_val and isinstance(size_val.type, ir.IntType): + if isinstance(param_type, ir.IntType) and size_val.type.width != param_type.width: + if size_val.type.width < param_type.width: + size_val = Gen.builder.zext(size_val, param_type, name="zext_malloc_size") + else: + size_val = Gen.builder.trunc(size_val, param_type, name="trunc_malloc_size") + if not size_val: + size_val = ir.Constant(param_type, 1) + result: ir.Value = Gen.builder.call(malloc_func, [size_val], name="malloc_result") + return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="malloc_cast") + + def _HandleReallocLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + realloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)]) + realloc_func: ir.Function = Gen._get_or_declare_func('realloc', realloc_type) + size_param_type: ir.Type = realloc_func.type.pointee.args[1] + ptr_val: ir.Value | None = None + size_val: ir.Value | None = None + if len(args) >= 2: + ptr_val = self.HandleExprLlvm(args[0]) + size_val = self.HandleExprLlvm(args[1]) + elif len(args) == 1: + size_val = self.HandleExprLlvm(args[0]) + if not ptr_val: + ptr_val = ir.Constant(ir.PointerType(ir.IntType(8)), None) + if isinstance(ptr_val.type, ir.PointerType) and not (isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8): + ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="realloc_cast_ptr") + if size_val and isinstance(size_val.type, ir.IntType): + if isinstance(size_param_type, ir.IntType) and size_val.type.width != size_param_type.width: + if size_val.type.width < size_param_type.width: + size_val = Gen.builder.zext(size_val, size_param_type, name="zext_realloc_size") + else: + size_val = Gen.builder.trunc(size_val, size_param_type, name="trunc_realloc_size") + if not size_val: + size_val = ir.Constant(size_param_type, 0) + result: ir.Value = Gen.builder.call(realloc_func, [ptr_val, size_val], name="realloc_result") + return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="realloc_cast") + + def _HandleFreeLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]) + free_func: ir.Function = Gen._get_or_declare_func('free', free_type) + if args: + var_name: str | None = None + if isinstance(args[0], ast.Name): + var_name = args[0].id + ptr_val: ir.Value | None = self.HandleExprLlvm(args[0]) + if ptr_val is not None: + if var_name and hasattr(Gen, '_var_to_heap_ptr') and var_name in Gen._var_to_heap_ptr: + registered_val = Gen._var_to_heap_ptr[var_name] + Gen._unregister_local_heap_ptr(registered_val) + del Gen._var_to_heap_ptr[var_name] + if isinstance(ptr_val.type, ir.PointerType): + if isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8: + pass + else: + ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="free_cast_ptr") + elif isinstance(ptr_val.type, ir.IntType): + ptr_val = Gen.builder.inttoptr(ptr_val, ir.PointerType(ir.IntType(8)), name="free_inttoptr") + Gen.builder.call(free_func, [ptr_val], name="free_call") + return ir.Constant(ir.IntType(32), 0) + + def _HandleMemcpyLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if 'llvm.memcpy' not in Gen.functions: + memcpy_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) + Gen.functions['llvm.memcpy'] = ir.Function(Gen.module, memcpy_type, name='llvm.memcpy') + if len(args) >= 3: + dst: ir.Value | None = self.HandleExprLlvm(args[0]) + src: ir.Value | None = self.HandleExprLlvm(args[1]) + size: ir.Value | None = self.HandleExprLlvm(args[2]) + if dst and src and size: + i8ptr: ir.PointerType = ir.PointerType(ir.IntType(8)) + if isinstance(dst.type, ir.PointerType) and dst.type != i8ptr: + dst = Gen.builder.bitcast(dst, i8ptr, name="memcpy_dst_cast") + elif isinstance(dst.type, ir.IntType): + if dst.type.width < 64: + dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_dst_ptr") + dst = Gen.builder.inttoptr(dst, i8ptr, name="dst_int2ptr") + if isinstance(src.type, ir.PointerType) and src.type != i8ptr: + src = Gen.builder.bitcast(src, i8ptr, name="memcpy_src_cast") + elif isinstance(src.type, ir.IntType): + if src.type.width < 64: + src = Gen.builder.zext(src, ir.IntType(64), name="zext_src_ptr") + src = Gen.builder.inttoptr(src, i8ptr, name="src_int2ptr") + if isinstance(size.type, ir.IntType) and size.type.width < 64: + size = Gen.builder.zext(size, ir.IntType(64), name="zext_memcpy_size") + isvolatile: ir.Constant = ir.Constant(ir.IntType(1), 0) + Gen.builder.call(Gen.functions['llvm.memcpy'], [dst, src, size, isvolatile], name="memcpy_call") + return ir.Constant(ir.IntType(32), 0) + + def _HandleMemsetLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + memset_func: ir.Function = Gen.get_or_declare_c_func('memset', ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)])) + if len(args) >= 3: + dst: ir.Value | None = self.HandleExprLlvm(args[0]) + val: ir.Value | None = self.HandleExprLlvm(args[1]) + size: ir.Value | None = self.HandleExprLlvm(args[2]) + if dst and val is not None and size: + if isinstance(dst.type, ir.PointerType) and not (isinstance(dst.type.pointee, ir.IntType) and dst.type.pointee.width == 8): + dst = Gen.builder.bitcast(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_cast") + elif isinstance(dst.type, ir.IntType): + if dst.type.width < 64: + dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_memset_dst") + elif dst.type.width > 64: + dst = Gen.builder.trunc(dst, ir.IntType(64), name="trunc_memset_dst") + dst = Gen.builder.inttoptr(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_int2ptr") + if isinstance(val.type, ir.PointerType): + # 0 字面量被推断为 null 指针(i8*),memset 第二参数需要 i32 + val = ir.Constant(ir.IntType(32), 0) + elif isinstance(val.type, ir.IntType) and val.type.width != 32: + if val.type.width < 32: + val = Gen.builder.zext(val, ir.IntType(32), name="zext_memset_val") + else: + val = Gen.builder.trunc(val, ir.IntType(32), name="trunc_memset_val") + if isinstance(size.type, ir.IntType) and size.type.width < 64: + size = Gen.builder.zext(size, ir.IntType(64), name="zext_memset_size") + elif isinstance(size.type, ir.IntType) and size.type.width > 64: + size = Gen.builder.trunc(size, ir.IntType(64), name="trunc_memset_size") + Gen.builder.call(memset_func, [dst, val, size], name="memset_call") + return ir.Constant(ir.IntType(32), 0) + + def _HandleVaStartLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + return ir.Constant(ir.IntType(32), 0) + + def _HandleVaEndLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + return ir.Constant(ir.IntType(32), 0) + + def _HandleArgLlvm(self, args: list[ast.expr], CallNode: ast.Call | None = None) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + va_list_ptr: ir.Value | None = None + variadic_info: dict | None = getattr(Gen, '_va_arg_info', None) + if not variadic_info: + variadic_info = getattr(Gen, '_variadic_info', None) + if variadic_info: + va_list_ptr = variadic_info.get('va_list_ptr') + if not va_list_ptr: + vararg_name: str = variadic_info.get('vararg_name', 'args') if variadic_info else 'args' + if vararg_name in Gen._direct_values: + va_list_ptr = Gen._direct_values[vararg_name] + elif vararg_name in Gen.variables: + va_list_ptr = Gen.variables[vararg_name] + if not va_list_ptr: + return ir.Constant(ir.IntType(32), 0) + target_type: ir.Type = ir.IntType(32) + if args: + type_arg: ast.expr = args[0] + if isinstance(type_arg, ast.Name): + type_name: str = type_arg.id + if type_name in ('str', 'bytes'): + target_type = ir.PointerType(ir.IntType(8)) + elif type_name == 'CPtr': + target_type = ir.PointerType(ir.IntType(8)) + elif type_name in Gen.structs: + target_type = ir.PointerType(Gen.structs[type_name]) + else: + ctype_cls: type | None = CTypeRegistry.GetClassByName(type_name) + if ctype_cls is not None: + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + target_type = resolved + else: + resolved: tuple | None = CTypeRegistry.ResolveName(type_name) + if resolved is not None: + ctype_cls, ptr_level = resolved + llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str: + base: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if base is not None: + for _ in range(ptr_level): + if isinstance(base, ir.VoidType): + base = ir.IntType(8).as_pointer() + else: + base = ir.PointerType(base) + target_type = base + elif isinstance(type_arg, ast.Attribute): + if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable): + attr_name: str = type_arg.attr + ctype_cls: type | None = CTypeRegistry.GetClassByName(attr_name) + if ctype_cls is not None: + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + target_type = resolved + else: + assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None) + if assign_node: + ann: ast.AST | None = getattr(assign_node, 'annotation', None) + if ann: + ann_name: str | None = None + if isinstance(ann, ast.Name): + ann_name = ann.id + elif isinstance(ann, ast.Attribute): + ann_name = ann.attr + elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr): + ann_name = ExtractTypeNameFromBinOp(ann) + if ann_name: + ctype_cls: type | None = CTypeRegistry.GetClassByName(ann_name) + if ctype_cls is not None: + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + target_type = resolved + elif ann_name in Gen.structs: + target_type = ir.PointerType(Gen.structs[ann_name]) + if not getattr(Gen, '_va_arg_counter', None): + Gen._va_arg_counter = 0 + Gen._va_arg_counter += 1 + result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type) + if result is not None and getattr(result, 'name', None): + result.name = f"va_arg_result_{Gen._va_arg_counter}" + return result + + def _resolve_vararg_type(self, type_arg: ast.expr, Gen: LlvmGeneratorMixin) -> ir.Type: + """将类型表达式解析为 LLVM 类型(供 arg/va_arg 共用)。""" + target_type: ir.Type = ir.IntType(32) + if isinstance(type_arg, ast.Name): + type_name: str = type_arg.id + if type_name in ('str', 'bytes'): + target_type = ir.PointerType(ir.IntType(8)) + elif type_name == 'CPtr': + target_type = ir.PointerType(ir.IntType(8)) + elif type_name in Gen.structs: + target_type = ir.PointerType(Gen.structs[type_name]) + else: + ctype_cls: type | None = CTypeRegistry.GetClassByName(type_name) + if ctype_cls is not None: + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + target_type = resolved + else: + resolved_tuple: tuple | None = CTypeRegistry.ResolveName(type_name) + if resolved_tuple is not None: + ctype_cls, ptr_level = resolved_tuple + llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str: + base: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if base is not None: + for _ in range(ptr_level): + if isinstance(base, ir.VoidType): + base = ir.IntType(8).as_pointer() + else: + base = ir.PointerType(base) + target_type = base + elif isinstance(type_arg, ast.Attribute): + if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable): + attr_name: str = type_arg.attr + ctype_cls: type | None = CTypeRegistry.GetClassByName(attr_name) + if ctype_cls is not None: + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + target_type = resolved + return target_type + + def _infer_vararg_type_from_context(self, Gen: LlvmGeneratorMixin) -> ir.Type: + """从当前赋值上下文推断 va_arg/arg 的目标类型。""" + target_type: ir.Type = ir.IntType(32) + assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None) + if assign_node: + ann: ast.AST | None = getattr(assign_node, 'annotation', None) + if ann: + ann_name: str | None = None + if isinstance(ann, ast.Name): + ann_name = ann.id + elif isinstance(ann, ast.Attribute): + ann_name = ann.attr + elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr): + ann_name = ExtractTypeNameFromBinOp(ann) + if ann_name: + ctype_cls: type | None = CTypeRegistry.GetClassByName(ann_name) + if ctype_cls is not None: + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + target_type = resolved + elif ann_name in Gen.structs: + target_type = ir.PointerType(Gen.structs[ann_name]) + return target_type + + def _HandleVaArgLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not args: + return ir.Constant(ir.IntType(32), 0) + # 求值 va_list 指针(args[0]) + va_list_ptr: ir.Value = self.HandleExprLlvm(args[0]) + if va_list_ptr is None: + return ir.Constant(ir.IntType(32), 0) + # 解析目标类型 + if len(args) >= 2: + target_type: ir.Type = self._resolve_vararg_type(args[1], Gen) + else: + target_type: ir.Type = self._infer_vararg_type_from_context(Gen) + if not getattr(Gen, '_va_arg_counter', None): + Gen._va_arg_counter = 0 + Gen._va_arg_counter += 1 + result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type) + if result is not None and getattr(result, 'name', None): + result.name = f"va_arg_explicit_{Gen._va_arg_counter}" + return result if result is not None else ir.Constant(ir.IntType(32), 0) diff --git a/App/lib/core/Handles/HandlesExprCall.py b/App/lib/core/Handles/HandlesExprCall.py index 3e2ec2e..afb630a 100644 --- a/App/lib/core/Handles/HandlesExprCall.py +++ b/App/lib/core/Handles/HandlesExprCall.py @@ -17,7 +17,6 @@ import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesImports as HandlesImports import lib.core.Handles.HandlesClassDef as HandlesClassDef - # ============================================================ # 全局模块 SHA1 映射(module_name → SHA1) # @@ -30,7 +29,6 @@ _g_sha1_arr: bytes = None _g_mod_arr: bytes = None _g_sha1_count: int = 0 - def set_module_sha1_map(sha1_arr: bytes, mod_arr: bytes, count: int) -> int: """设置全局模块 SHA1 映射""" global _g_sha1_arr @@ -41,7 +39,6 @@ def set_module_sha1_map(sha1_arr: bytes, mod_arr: bytes, count: int) -> int: _g_sha1_count = count return 0 - def _lookup_module_sha1(mod_name: str) -> str: """查找模块名对应的 SHA1,找不到返回 None""" if mod_name is None: @@ -57,7 +54,6 @@ def _lookup_module_sha1(mod_name: str) -> str: return _g_sha1_arr + sidx return None - def _lookup_module_sha1_suffix(mod_suffix: str) -> str: """后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "llvmlite.__types" @@ -93,7 +89,6 @@ def _lookup_module_sha1_suffix(mod_suffix: str) -> str: return _g_sha1_arr + sidx2 return None - # ============================================================ # 全局 CExport 函数表(module_sha1 + func_name) # @@ -108,7 +103,6 @@ _g_cexport_func_arr: bytes = None _g_cexport_count: int = 0 _CEXPORT_MAX: t.CDefine = 256 - def register_cexport_func(sha1: str, func_name: str) -> int: """注册 CExport 函数到全局表(sha1+func_name 二元组)""" global _g_cexport_sha1_arr @@ -132,7 +126,6 @@ def register_cexport_func(sha1: str, func_name: str) -> int: return 1 return 0 - def is_cexport_func(sha1: str, func_name: str) -> int: """检查 (sha1, func_name) 是否是 CExport 函数""" if sha1 is None or func_name is None: @@ -147,7 +140,6 @@ def is_cexport_func(sha1: str, func_name: str) -> int: return 1 return 0 - # ============================================================ # _lookup_mod_sha1 - 从 from_imports 查找类所属模块的 SHA1 # @@ -185,14 +177,12 @@ def _lookup_mod_sha1(pool: memhub.MemBuddy | t.CPtr, sha1 = _lookup_module_sha1_suffix(base_mod) return sha1 - # ============================================================ # HandlesExprCall - 函数调用表达式处理(Mixin 继承模式) # # 工具函数和 FuncEntry 保留为模块级,ExprCallHandle 提供 trans 接口 # ============================================================ - # ============================================================ # 在模块函数链表中按名称查找函数 # ============================================================ @@ -239,7 +229,6 @@ def find_func_in_module(mod: llvmlite.LLVMModule | t.CPtr, cur = llvmlite.function_get_next(cur) return None - # ============================================================ # _infer_external_func_ret_ty - 根据函数名推断外部函数返回类型 # @@ -341,7 +330,6 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr, # 默认 i32 return llvmlite.Int32(pool) - # ============================================================ # _get_external_func_param_ty - 获取外部函数第 idx 个参数的期望类型 # @@ -390,7 +378,6 @@ def _get_external_func_param_ty(pool: memhub.MemBuddy | t.CPtr, return None - # ============================================================ # _emit_llvm_memcpy_intrinsic — 将 memcpy 调用映射到 @llvm.memcpy 内联函数 # @@ -498,7 +485,6 @@ def _emit_llvm_memcpy_intrinsic(pool: memhub.MemBuddy | t.CPtr, # 返回 dst(匹配 C memcpy 语义: 返回 dest 指针) return dst_coerced - # ============================================================ # c.Deref(ptr) — 解引用指针,load i8(字符遍历用例) # @@ -525,7 +511,6 @@ def translate_c_deref(pool: memhub.MemBuddy | t.CPtr, i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) return llvmlite.build_load(builder, i8_ty, ptr_val) - # ============================================================ # c.DerefAs(ptr, val) — 解引用写入,*ptr = val # @@ -559,7 +544,6 @@ def translate_c_derefas(pool: memhub.MemBuddy | t.CPtr, llvmlite.build_store(builder, val, casted_ptr) return val - # ============================================================ # c.Addr(var) — 取变量地址,返回 alloca 指针(不 load) # @@ -587,7 +571,6 @@ def translate_c_addr(pool: memhub.MemBuddy | t.CPtr, return HandlesVar.lookup_var(trans.SymTab, nm.id) - # ============================================================ # c.Load(a, b) — 指针间复制: *a = *b # @@ -641,7 +624,6 @@ def translate_c_load(pool: memhub.MemBuddy | t.CPtr, llvmlite.build_store(builder, loaded, dst_cast) return loaded - # ============================================================ # c.Asm — 内联汇编支持 # @@ -657,7 +639,6 @@ ASM_CONST_BUF_SIZE: t.CDefine = 512 ASM_ARGS_BUF_SIZE: t.CDefine = 512 ASM_LINE_BUF_SIZE: t.CDefine = 4096 - # ============================================================ # AsmOperand - 内联汇编操作数条目 # ============================================================ @@ -668,7 +649,6 @@ class AsmOperand: Constraint: str # 约束字符串 (如 "r", "=r") IsOutput: int # 1=输出, 0=输入 - # ============================================================ # _asm_append_cstr — 将 C 字符串 src 追加到 dst 末尾 # ============================================================ @@ -688,7 +668,6 @@ def _asm_append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, i += 1 dst[dlen + i] = '\0' - # ============================================================ # _asm_escape_for_ir — 将汇编文本转义为 LLVM IR 字符串内容 # @@ -732,7 +711,6 @@ def _asm_escape_for_ir(pool: memhub.MemBuddy | t.CPtr, buf[pos] = '\0' return buf - # ============================================================ # _resolve_register — 将 ASM_DESCR 属性名映射到约束字符串 # ============================================================ @@ -820,7 +798,6 @@ def _resolve_register(attr_name: str) -> str: return "" - # ============================================================ # _resolve_asm_descr — 递归解析约束表达式 # @@ -882,7 +859,6 @@ def _resolve_asm_descr(pool: memhub.MemBuddy | t.CPtr, return "" - # ============================================================ # _asm_get_operand — 获取操作数数组中第 idx 个元素的指针 # ============================================================ @@ -893,7 +869,6 @@ def _asm_get_operand(base: AsmOperand | t.CPtr, entry_addr: t.CUInt64T = t.CUInt64T(base) + idx * entry_size return (AsmOperand | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) - # ============================================================ # _asm_is_c_call — 检查 Call 节点是否是 c.XXX 调用 # @@ -913,7 +888,6 @@ def _asm_is_c_call(node: ast.Call | t.CPtr) -> str: return None return at.attr - # ============================================================ # _asm_add_operand — 添加操作数到数组 # @@ -931,9 +905,14 @@ def _asm_add_operand(operands: AsmOperand | t.CPtr, op.Value = val op.Constraint = constraint op.IsOutput = is_output + if val is not None: + stdio.printf("[A] count=%d op=%lld val=%lld val.Ty=%lld val.Name=%lld is_out=%d\n", + count, t.CInt64T(op), t.CInt64T(val), t.CInt64T(val.Ty), t.CInt64T(val.Name), is_output) + else: + stdio.printf("[A] count=%d op=%lld val=None is_out=%d\n", count, t.CInt64T(op), is_output) + stdio.fflush(0) return count + 1 - # ============================================================ # translate_c_asm — 翻译 c.Asm 内联汇编 # @@ -1198,6 +1177,9 @@ def translate_c_asm(pool: memhub.MemBuddy | t.CPtr, ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool) if output_count > 0 and first_output_idx >= 0: out_op: AsmOperand | t.CPtr = _asm_get_operand(operands, first_output_idx) + stdio.printf("[R] out_idx=%d out_op=%lld Val=%lld\n", + first_output_idx, t.CInt64T(out_op), t.CInt64T(out_op.Value)) + stdio.fflush(0) if out_op.Value is not None and out_op.Value.Ty is not None: # alloca 的类型是指针,Pointee 是目标类型 if out_op.Value.Ty.Pointee is not None: @@ -1397,7 +1379,6 @@ def translate_c_asm(pool: memhub.MemBuddy | t.CPtr, return None - # ============================================================ # c.LLVMIR — 内联 LLVM IR 支持 # @@ -1419,7 +1400,6 @@ def translate_c_asm(pool: memhub.MemBuddy | t.CPtr, LLVMIR_MAX_OPS: t.CDefine = 16 LLVMIR_TEMPLATE_BUF: t.CDefine = 512 - # ============================================================ # _llvmir_get_op — 获取第 i 个 AsmOperand 槽位 # ============================================================ @@ -1429,7 +1409,6 @@ def _llvmir_get_op(buf: AsmOperand | t.CPtr, i: int) -> AsmOperand | t.CPtr: entry_addr: t.CUInt64T = t.CUInt64T(buf) + i * entry_size return (AsmOperand | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) - # ============================================================ # _llvmir_find_op_marker — 在字符串中查找 %__OP 并返回编号 # @@ -1441,7 +1420,6 @@ def _llvmir_find_op_marker(s: t.CChar | t.CPtr) -> t.CPtr: return None return string.strstr(s, "%__OP") - # ============================================================ # _llvmir_parse_op_num — 从 %__OPN__ 中解析编号 N # @@ -1466,7 +1444,6 @@ def _llvmir_parse_op_num(marker: t.CChar | t.CPtr) -> int: ch = num_start[i] return num - # ============================================================ # _llvmir_match_icmp_pred — 将预测字符串转为 ICMP 常量 # ============================================================ @@ -1496,7 +1473,6 @@ def _llvmir_match_icmp_pred(pred: str) -> int: return 9 return 0 - # ============================================================ # _llvmir_match_fcmp_pred — 将预测字符串转为 FCMP 常量 # ============================================================ @@ -1534,7 +1510,6 @@ def _llvmir_match_fcmp_pred(pred: str) -> int: return 13 return 0 - # ============================================================ # _llvmir_extract_two_ops — 从模板中提取两个操作数编号 # @@ -1567,7 +1542,6 @@ def _llvmir_extract_two_ops(template: t.CChar | t.CPtr, out_nums[1] = _llvmir_parse_op_num(m2) return 1 - # ============================================================ # _llvmir_resolve_op — 根据编号解析操作数值 # ============================================================ @@ -1590,7 +1564,6 @@ def _llvmir_resolve_op(op_num: int, op2: AsmOperand | t.CPtr = _llvmir_get_op(output_targets, idx) return op2.Value - # ============================================================ # _llvmir_store_outputs — 将结果存储到输出变量 # ============================================================ @@ -1608,7 +1581,6 @@ def _llvmir_store_outputs(pool: memhub.MemBuddy | t.CPtr, llvmlite.build_store(builder, result, op.Value) return result - # ============================================================ # translate_c_llvmir — 翻译 c.LLVMIR 内联 LLVM IR # ============================================================ @@ -1953,7 +1925,6 @@ def translate_c_llvmir(pool: memhub.MemBuddy | t.CPtr, stdio.printf("[LLVMIR] unsupported instruction: %s\n", instr_kw) return None - # ============================================================ # _apply_struct_defaults — 应用结构体字段的默认值 # @@ -1989,7 +1960,6 @@ def _apply_struct_defaults(pool: memhub.MemBuddy | t.CPtr, default_val = HandlesExpr.coerce_to_type(builder, default_val, fe.Ty) llvmlite.build_store(builder, default_val, field_ptr) - # ============================================================ # _translate_struct_ctor — 结构体构造函数 Point(10, 20) # @@ -2147,7 +2117,6 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, # 返回指针(而非结构体值),赋值时由 coerce_to_type 按目标类型决定是否 load return tmp - # ============================================================ # _do_virtual_call — 通过虚表间接调用虚方法 # @@ -2308,7 +2277,6 @@ def _do_virtual_call(pool: memhub.MemBuddy | t.CPtr, # 12. 间接调用 return llvmlite.build_call_indirect(builder, func_ptr, args_head, args_count, ret_ty) - # ============================================================ # _call_method_on_ptr — 在结构体指针上调用方法 # @@ -2446,7 +2414,6 @@ def _call_method_on_ptr(pool: memhub.MemBuddy | t.CPtr, return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, 0) - # ============================================================ # _infer_method_ret_ty - 根据方法名推断返回类型 # @@ -2486,7 +2453,6 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr, # 默认 i32(free, reset, __init__, __exit__, _fl_push 等) return llvmlite.Int32(pool) - # ============================================================ # _translate_method_call - 翻译方法调用 obj.method(args) # @@ -2633,7 +2599,6 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr, return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, 0) - # ============================================================ # _kwarg_name_matches — 检查关键字参数名是否匹配函数参数名 # @@ -2656,7 +2621,6 @@ def _kwarg_name_matches(kw_arg: str, return 0 return 1 - # ============================================================ # _translate_struct_ctor_kw — 结构体构造函数(关键字参数) # @@ -2747,7 +2711,6 @@ def _translate_struct_ctor_kw(pool: memhub.MemBuddy | t.CPtr, # 返回指针(而非结构体值),赋值时由 coerce_to_type 按目标类型决定是否 load return tmp - # ============================================================ # _translate_call_with_kwargs — 带关键字参数的函数调用 # @@ -2850,7 +2813,6 @@ def _translate_call_with_kwargs(pool: memhub.MemBuddy | t.CPtr, return _emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst1, mc_src1, mc_num1) return llvmlite.build_call(builder, call_name, head, actual_count, call_ret_ty, 0) - # ============================================================ # _translate_t_type_cast - 翻译 t.XXX(value) 类型转换 # @@ -2892,14 +2854,12 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr, # 指针 → 指针: bitcast return llvmlite.build_bitcast(builder, val, target_ty) - # ============================================================ # 泛型实例化辅助:从 Subscript slice 提取类型实参名 # ============================================================ # _get_type_name_from_annotation 已移至 HandlesType.py(支持 BinOp/Attribute) # HandlesExprCall 通过 HandlesType._get_type_name_from_annotation 调用 - def _extract_type_args_from_slice(pool: memhub.MemBuddy | t.CPtr, slice_node: ast.AST | t.CPtr) -> list[str] | t.CPtr: """从 Subscript.slice 提取类型实参名列表 @@ -2929,7 +2889,6 @@ def _extract_type_args_from_slice(pool: memhub.MemBuddy | t.CPtr, result.append(tn) return result - # ============================================================ # 翻译函数调用 Call(func, args) — 模块级版本 # ============================================================ @@ -3678,7 +3637,6 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, return _emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst2, mc_src2, mc_num2) return llvmlite.build_call(builder, call_name, head, can, call_ret_ty, 0) - # ============================================================ # 翻译闭包调用 — 通过闭包指针间接调用 # @@ -3738,7 +3696,6 @@ def translate_closure_call(pool: memhub.MemBuddy | t.CPtr, env_ptr.Next = None return llvmlite.build_call_indirect(builder, fn_typed, env_ptr, 1, i32_ty) - # ============================================================ # 翻译 printf 调用 — 模块级版本 # ============================================================ @@ -3802,7 +3759,6 @@ def translate_printf_call(pool: memhub.MemBuddy | t.CPtr, # 让 llc 识别为 variadic 调用,生成正确的 Win64 ABI 代码(浮点参数同时传 XMM 和整数寄存器) llvmlite.build_call(builder, "printf", fmt_ptr, can, i32_ty, 1) - # ============================================================ # FuncEntry 结构体(用于函数表) # ============================================================ @@ -3812,7 +3768,6 @@ class FuncEntry: Name: str Func: llvmlite.Function | t.CPtr - # ============================================================ # 初始化函数表 # ============================================================ @@ -3825,7 +3780,6 @@ def init_func_table(pool: memhub.MemBuddy | t.CPtr, string.memset(funcs_ptr, 0, size) return funcs_ptr - # ============================================================ # 查找函数 # ============================================================ @@ -3844,7 +3798,6 @@ def find_func_in_table(funcs: FuncEntry | t.CPtr, return entry.Func return None - # ============================================================ # 添加函数到函数表 # ============================================================ @@ -3867,7 +3820,6 @@ def add_func_to_table(funcs: FuncEntry | t.CPtr, _register_global_func(name, func) return 0 - # ============================================================ # 全局函数注册表(跨模块函数查找) # @@ -3879,7 +3831,6 @@ GLOBAL_FUNC_MAX: t.CDefine = 8192 _global_funcs: FuncEntry | t.CPtr = None _global_func_count: int = 0 - def _init_global_func_table() -> int: """初始化全局函数表(用 stdlib.malloc 分配,生命周期=整个程序)""" global _global_funcs @@ -3894,7 +3845,6 @@ def _init_global_func_table() -> int: _global_func_count = 0 return 1 - def _register_global_func(name: str, func: llvmlite.Function | t.CPtr) -> int: """注册函数到全局表(已存在则跳过),返回 0 成功 @@ -3919,14 +3869,12 @@ def _register_global_func(name: str, func: llvmlite.Function | t.CPtr) -> int: _global_func_count += 1 return 0 - def find_func_global(name: str) -> llvmlite.Function | t.CPtr: """在全局函数表中按名称查找,返回 Function 指针或 None""" if name is None or _global_funcs is None: return None return find_func_in_table(_global_funcs, _global_func_count, name) - # ============================================================ # ExprCallHandle - 函数调用处理器(Mixin 继承模式) # @@ -3958,7 +3906,6 @@ class ExprCallHandle(HandlesBase.Mixin): self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module, cl, self.Trans) - # ============================================================ # NewExprCallHandle - 工厂函数 # ============================================================ diff --git a/App/lib/core/Handles/HandlesExprFormat.py b/App/lib/core/Handles/HandlesExprFormat.py new file mode 100644 index 0000000..02693cf --- /dev/null +++ b/App/lib/core/Handles/HandlesExprFormat.py @@ -0,0 +1,271 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import BaseHandle +import ast +import re +import llvmlite.ir as ir + + +class ExprFormatHandle(BaseHandle): + def _HandleJoinedStrLlvm(self, Node: ast.JoinedStr) -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if not Gen or not Gen.builder: + return ir.Constant(ir.IntType(8).as_pointer(), None) + format_str: str = "" + cast_args: list[Any] = [] + for part in Node.values: + if isinstance(part, ast.Constant) and isinstance(part.value, str): + format_str += part.value.replace('\\', '\\\\').replace('%', '%%').replace('\n', '\\n').replace('\t', '\\t').replace('\0', '\\0') + elif isinstance(part, ast.FormattedValue): + fmt_class: str | None = self.Trans.ExprHandler._get_var_class(part.value, Gen) + if fmt_class and Gen._has_function(f'{fmt_class}.__str__'): + obj_val: Any = self.HandleExprLlvm(part.value) + if obj_val: + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + if fmt_class in Gen.structs: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[fmt_class]), name=f"cast_{fmt_class}") + val: Any = Gen.builder.call(Gen._get_function(f'{fmt_class}.__str__'), [obj_val], name=f"call_{fmt_class}.__str__") + Gen._RegisterTempPtr(val) + format_str += "%s" + cast_args.append(val) + else: + format_str += "%s" + cast_args.append(ir.Constant(ir.PointerType(ir.IntType(8)), None)) + continue + val = self.HandleExprLlvm(part.value) + if not val: + format_str += "%d" + cast_args.append(ir.Constant(ir.IntType(32), 0)) + continue + # 检查 format_spec(Python f-string 格式说明符) + if part.format_spec is not None: + c_fmt: str | None = self._parse_format_spec(part.format_spec, val) + if c_fmt is not None: + cast_val: Any = self._cast_value_for_format(val, c_fmt, Gen, part.value) + format_str += c_fmt + cast_args.append(cast_val) + continue + if isinstance(val.type, ir.PointerType): + pointee: ir.Type = val.type.pointee + if isinstance(pointee, ir.IntType) and pointee.width == 8: + format_str += "%s" + cast_args.append(val) + elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + int_val: Any = Gen.builder.ptrtoint(val, ir.IntType(64), name="fstr_ptr2int") + trunc_val: Any = Gen.builder.trunc(int_val, ir.IntType(32), name="fstr_trunc") + format_str += "%d" + cast_args.append(trunc_val) + else: + Loaded: Any = Gen._load(val, name="fstr_deref") + if isinstance(Loaded.type, ir.IntType): + if Loaded.type.width == 8: + format_str += "%c" + ext: Any = Gen.builder.zext(Loaded, ir.IntType(32), name="fstr_char2int") + cast_args.append(ext) + else: + format_str += "%d" + cast_args.append(Loaded) + elif isinstance(Loaded.type, (ir.FloatType, ir.DoubleType)): + format_str += "%f" + cast_args.append(Loaded) + elif isinstance(Loaded.type, ir.PointerType): + format_str += "%s" + cast_args.append(Loaded) + else: + format_str += "%d" + cast_args.append(Loaded) + elif isinstance(val.type, ir.IntType): + if val.type.width == 8: + format_str += "%c" + ext = Gen.builder.zext(val, ir.IntType(32), name="fstr_char2int") + cast_args.append(ext) + elif val.type.width == 1: + zext: Any = Gen.builder.zext(val, ir.IntType(32), name="fstr_bool2int") + format_str += "%d" + cast_args.append(zext) + else: + is_u: bool = Gen._check_node_unsigned(part.value) + format_str += "%u" if is_u else "%d" + cast_args.append(val) + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + format_str += "%f" + cast_args.append(val) + else: + format_str += "%d" + cast_args.append(val) + if not format_str: + return Gen.emit_constant("", 'string') + format_ptr: Any = Gen._create_string_global(format_str) + # 统一使用 snprintf 生成字符串,返回 i8* + # __str__ 方法内使用堆分配(返回值需持久化),其他场景使用栈分配(自动释放) + in_str_method: bool = bool(getattr(Gen, 'func', None) and getattr(Gen.func, 'name', '').endswith('.__str__')) + snprintf_type: ir.FunctionType = ir.FunctionType(ir.IntType(32), [ + ir.PointerType(ir.IntType(8)), + ir.IntType(64), + ir.PointerType(ir.IntType(8)) + ], var_arg=True) + snprintf_func: Any = Gen.get_or_declare_c_func('snprintf', snprintf_type) + null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + # 第一遍:获取所需长度(NULL 缓冲,size=0) + len_val: ir.Value = Gen.builder.call(snprintf_func, [null_ptr, ir.Constant(ir.IntType(64), 0), format_ptr] + cast_args, name="fstr_len") + # 加 1 用于 null 终止符 + size_val: ir.Value = Gen.builder.add(len_val, ir.Constant(ir.IntType(32), 1), name="fstr_size") + size_ext: ir.Value = Gen.builder.zext(size_val, ir.IntType(64), name="fstr_size_ext") + if in_str_method: + # 堆分配(__str__ 返回值需跨函数生命周期) + malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) + malloc_func: Any = Gen._get_or_declare_func('malloc', malloc_type) + buf_ptr: ir.Value = Gen.builder.call(malloc_func, [size_ext], name="fstr_heap_buf") + Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf") + Gen._RegisterTempPtr(buf_ptr) + return buf_ptr + # 栈分配(局部使用,自动释放) + buf_ptr: ir.Value = Gen._alloca(ir.IntType(8), name="fstr_stack_buf", size=size_val) + Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf") + return buf_ptr + + def _parse_format_spec(self, format_spec_node: ast.AST, val: ir.Value) -> str | None: + """将 Python f-string format_spec 转换为 C printf 格式说明符。 + + 支持: d/x/X/o/c/f/s 及 [[fill]align][sign][#][0][width][.precision][type] 语法。 + 返回 None 表示无法解析,调用方应回退到类型推断。 + """ + if isinstance(format_spec_node, ast.JoinedStr): + parts: list[str] = [] + for v in format_spec_node.values: + if isinstance(v, ast.Constant) and isinstance(v.value, str): + parts.append(v.value) + else: + return None + spec_str: str = ''.join(parts) + elif isinstance(format_spec_node, ast.Constant) and isinstance(format_spec_node.value, str): + spec_str = format_spec_node.value + else: + return None + + if not spec_str: + return None + + m: Any = re.match(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$', spec_str) + if not m: + return None + + fill_align, sign, alt, zero, width, comma, precision, fmt_type = m.groups() + + c_flags: str = '' + if sign == '+': + c_flags += '+' + elif sign == ' ': + c_flags += ' ' + if alt: + c_flags += '#' + + if fill_align: + align_char: str = fill_align[-1] if len(fill_align) > 1 else fill_align[0] + if align_char in ('<', '^'): + c_flags += '-' + if zero: + c_flags += '0' + + c_width: str = width or '' + c_precision: str = precision or '' + + length_mod: str = '' + if isinstance(val.type, ir.IntType): + if val.type.width == 64: + length_mod = 'll' + elif val.type.width == 32: + length_mod = 'l' + elif val.type.width == 16: + length_mod = 'h' + elif val.type.width == 8: + length_mod = 'hh' + + c_specifier: str = '' + if fmt_type in ('d', 'n'): + c_specifier = f'{length_mod}d' + elif fmt_type == 'x': + c_specifier = f'{length_mod}x' + elif fmt_type == 'X': + c_specifier = f'{length_mod}X' + elif fmt_type == 'o': + c_specifier = f'{length_mod}o' + elif fmt_type == 'b': + return None + elif fmt_type == 'c': + c_specifier = 'c' + elif fmt_type in ('f', 'F'): + c_specifier = 'f' + elif fmt_type in ('e', 'E', 'g', 'G'): + c_specifier = fmt_type + elif fmt_type == 's': + c_specifier = 's' + elif fmt_type == '%': + c_specifier = 'f%%' + else: + if isinstance(val.type, ir.IntType): + if val.type.width == 8: + c_specifier = 'c' + elif val.type.width == 1: + c_specifier = 'd' + else: + c_specifier = f'{length_mod}d' + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + c_specifier = 'f' + elif isinstance(val.type, ir.PointerType): + c_specifier = 's' + else: + c_specifier = 'd' + + return f'%{c_flags}{c_width}{c_precision}{c_specifier}' + + def _cast_value_for_format(self, val: ir.Value, c_fmt: str, Gen: Any, arg_node: ast.AST) -> ir.Value: + """根据 C printf 格式说明符转换值类型。""" + fmt_type_char: str = '' + for ch in reversed(c_fmt): + if ch.isalpha() and ch not in ('l', 'h'): + fmt_type_char = ch + break + elif ch == '%': + break + + if fmt_type_char == 's': + if isinstance(val.type, ir.PointerType): + pointee: ir.Type = val.type.pointee + if isinstance(pointee, ir.IntType) and pointee.width == 8: + return val + if isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8: + return Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="fstr_arr2ptr") + return val + elif fmt_type_char == 'c': + if isinstance(val.type, ir.IntType) and val.type.width < 32: + return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_c") + return val + elif fmt_type_char in ('d', 'x', 'X', 'o', 'i'): + if isinstance(val.type, ir.IntType): + if val.type.width == 8: + is_u: bool = Gen._check_node_unsigned(arg_node) + if is_u: + return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i8") + return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i8") + elif val.type.width == 16: + is_u = Gen._check_node_unsigned(arg_node) + if is_u: + return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i16") + return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i16") + elif val.type.width == 1: + return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_bool") + return val + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + return Gen.builder.fptosi(val, ir.IntType(32), name="fstr_f2i") + return val + elif fmt_type_char in ('f', 'e', 'E', 'g', 'G'): + if isinstance(val.type, ir.FloatType): + return Gen.builder.fpext(val, ir.DoubleType(), name="fstr_fpext") + elif isinstance(val.type, ir.IntType): + return Gen.builder.sitofp(val, ir.DoubleType(), name="fstr_i2f") + return val + return val \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesExprLambda.py b/App/lib/core/Handles/HandlesExprLambda.py new file mode 100644 index 0000000..7003785 --- /dev/null +++ b/App/lib/core/Handles/HandlesExprLambda.py @@ -0,0 +1,249 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import BaseHandle +import ast +import llvmlite.ir as ir + + +class ExprLambdaHandle(BaseHandle): + def _HandleLambdaLlvm(self, Node: ast.Lambda) -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if not getattr(Gen, '_lambda_counter', None): + Gen._lambda_counter = 0 + lambda_id: int = Gen._lambda_counter + Gen._lambda_counter += 1 + fn_name: str = f"lambda_fn_{lambda_id}" + captured_vars: list[tuple[str, Any]] = self._get_lambda_captured_vars(Node) + env_types: list[ir.Type] = [] + env_var_names: list[str] = [] + for var_name, var_val in captured_vars: + env_types.append(var_val.type) + env_var_names.append(var_name) + env_struct_type: ir.LiteralStructType = ir.LiteralStructType(env_types) if env_types else ir.LiteralStructType([]) + ret_type: ir.Type = self.Trans.ExprUtils._infer_expr_llvm_type_full(Node.body) + param_types: list[ir.Type] = [ir.PointerType(env_struct_type)] + if Node.args: + for arg in Node.args.args: + param_types.append(ir.IntType(32)) + fn_type: ir.FunctionType = ir.FunctionType(ret_type, param_types) + fn: ir.Function = ir.Function(Gen.module, fn_type, name=fn_name) + Gen.functions[fn_name] = fn + entry_block: ir.Block = fn.append_basic_block(name=f"{fn_name}_entry") + prev_builder: Any = Gen.builder + prev_func: Any = Gen.func + prev_vars: dict[str, Any] = dict(Gen.variables) + Gen.builder = ir.IRBuilder(entry_block) + Gen.func = fn + Gen.variables = {} + for i, arg in enumerate(fn.args): + arg.name = f"arg_{i}" if i > 0 else "env" + if env_types: + for idx, var_name in enumerate(env_var_names): + env_ptr: Any = fn.args[0] + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + member_ptr: Any = Gen.builder.gep(env_ptr, [zero, ir.Constant(ir.IntType(32), idx)], name=f"env_{var_name}") + Loaded: Any = Gen._load(member_ptr, name=f"Load_env_{var_name}") + temp_alloca: Any = Gen._alloca(Loaded.type, name=f"temp_{var_name}") + Gen._store(Loaded, temp_alloca) + Gen.variables[var_name] = temp_alloca + if Node.args: + for i, arg in enumerate(Node.args.args): + Gen.variables[arg.arg] = Gen._alloca(ir.IntType(32), name=arg.arg) + Gen._store(fn.args[i + 1], Gen.variables[arg.arg]) + body_val: Any = self.HandleExprLlvm(Node.body) + if body_val: + if isinstance(body_val.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType): + body_val = Gen._load(body_val, name="Load_lambda_ret") + if body_val.type != ret_type: + if isinstance(body_val.type, ir.IntType) and isinstance(ret_type, ir.IntType): + if body_val.type.width < ret_type.width: + body_val = Gen.builder.zext(body_val, ret_type, name="zext_lambda_ret") + elif body_val.type.width > ret_type.width: + body_val = Gen.builder.trunc(body_val, ret_type, name="trunc_lambda_ret") + elif isinstance(body_val.type, ir.PointerType) and isinstance(ret_type, ir.PointerType): + body_val = Gen.builder.bitcast(body_val, ret_type, name="bitcast_lambda_ret") + Gen.builder.ret(body_val) + else: + if isinstance(ret_type, ir.PointerType): + Gen.builder.ret(ir.Constant(ret_type, None)) + elif isinstance(ret_type, ir.IntType): + Gen.builder.ret(ir.Constant(ret_type, 0)) + elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)): + Gen.builder.ret(ir.Constant(ret_type, 0.0)) + elif isinstance(ret_type, ir.BaseStructType): + if ret_type.elements: + zero_val: ir.Constant = ir.Constant(ret_type, [ + ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) + else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) + else ir.Constant(et, ir.Undefined) for et in ret_type.elements]) + else: + zero_val = ir.Constant(ret_type, None) + Gen.builder.ret(zero_val) + else: + Gen.builder.ret(ir.Constant(ret_type, 0)) + Gen.builder = prev_builder + Gen.func = prev_func + Gen.variables = prev_vars + closure_struct_type: ir.LiteralStructType = ir.LiteralStructType([ + ir.PointerType(ir.IntType(8)), + ir.PointerType(fn_type) + ]) + closure_ptr: Any = Gen._alloca(closure_struct_type, name=f"closure_{lambda_id}") + env_ptr: Any = Gen._alloca(env_struct_type, name=f"env_{lambda_id}") + if env_types: + for idx, (var_name, var_val) in enumerate(captured_vars): + zero = ir.Constant(ir.IntType(32), 0) + member_ptr = Gen.builder.gep(env_ptr, [zero, ir.Constant(ir.IntType(32), idx)], name=f"store_env_{var_name}") + Gen._store(var_val, member_ptr) + zero = ir.Constant(ir.IntType(32), 0) + env_field_ptr: Any = Gen.builder.gep(closure_ptr, [zero, zero], name="closure_env_ptr") + env_i8_ptr: Any = Gen.builder.bitcast(env_ptr, ir.PointerType(ir.IntType(8)), name="env_i8_ptr") + Gen._store(env_i8_ptr, env_field_ptr) + fn_field_ptr: Any = Gen.builder.gep(closure_ptr, [zero, ir.Constant(ir.IntType(32), 1)], name="closure_fn_ptr") + Gen._store(fn, fn_field_ptr) + return closure_ptr + + def _get_lambda_captured_vars(self, Node: ast.Lambda) -> list[tuple[str, ir.Value]]: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + captured: list[tuple[str, Any]] = [] + free_vars: list[str] = self._get_lambda_free_vars(Node) + for var_name in free_vars: + if var_name in Gen.variables: + var_ptr: Any = Gen.variables[var_name] + if isinstance(var_ptr, ir.AllocaInstr) or isinstance(var_ptr, ir.GlobalVariable): + Loaded: Any = Gen._load(var_ptr, name=f"capture_{var_name}") + captured.append((var_name, Loaded)) + return captured + + def _get_lambda_free_vars(self, Node: ast.Lambda) -> list[str]: + bound_vars: set[str] = set() + free_vars: set[str] = set() + # 收集 lambda body 中的 bound 和 free 变量 + self.Trans.ExprUtils._collect_names(Node.body, bound_vars, free_vars) + # lambda 参数也是 bound 的 + if Node.args: + for arg in Node.args.args: + bound_vars.add(arg.arg) + # free 变量 = 在 body 中引用(Load)但不是参数也不是 body 内赋值的变量 + result: list[str] = list(free_vars - bound_vars) + return result + + def _HandleIfExpLlvm(self, Node: ast.IfExp) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + TestVal: Any = self.HandleExprLlvm(Node.test) + if not TestVal: + return None + if not isinstance(TestVal.type, ir.IntType) or TestVal.type.width != 1: + TestVal = Gen.builder.icmp_signed('!=', TestVal, Gen._ZeroConst(TestVal.type), name="ifexp_cond") + ThenBB: ir.Block = Gen.func.append_basic_block(name="ifexp.then") + ElseBB: ir.Block = Gen.func.append_basic_block(name="ifexp.else") + MergeBB: ir.Block = Gen.func.append_basic_block(name="ifexp.end") + Gen.builder.cbranch(TestVal, ThenBB, ElseBB) + # 先在 ThenBB 中生成 BodyVal + Gen.builder.position_at_start(ThenBB) + BodyVal: Any = self.HandleExprLlvm(Node.body) + if not BodyVal: + BodyVal = ir.Constant(ir.IntType(32), 0) + ThenEndBB: ir.Block = Gen.builder.block + # 在 ElseBB 中生成 OrelseVal + Gen.builder.position_at_start(ElseBB) + OrelseVal: Any = self.HandleExprLlvm(Node.orelse) + if not OrelseVal: + OrelseVal = ir.Constant(ir.IntType(32), 0) + ElseEndBB: ir.Block = Gen.builder.block + # 确定 phi 节点的目标类型 + result_type: ir.Type = BodyVal.type + if BodyVal.type != OrelseVal.type: + if isinstance(BodyVal.type, ir.IntType) and isinstance(OrelseVal.type, ir.IntType): + result_type = BodyVal.type if BodyVal.type.width >= OrelseVal.type.width else OrelseVal.type + elif isinstance(BodyVal.type, ir.PointerType) and isinstance(OrelseVal.type, ir.PointerType): + # 指针类型,使用 BodyVal 的类型 + result_type = BodyVal.type + elif isinstance(BodyVal.type, ir.IntType) and isinstance(OrelseVal.type, ir.PointerType): + # BodyVal 是整数,OrelseVal 是指针(字符串字面量) + # 如果指针指向 i8,将指针转换为 i8(加载字符) + if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8: + result_type = ir.IntType(8) + else: + result_type = OrelseVal.type + elif isinstance(BodyVal.type, ir.PointerType) and isinstance(OrelseVal.type, ir.IntType): + # BodyVal 是指针(字符串字面量),OrelseVal 是整数 + # 如果指针指向 i8,将指针转换为 i8(加载字符) + if isinstance(BodyVal.type.pointee, ir.IntType) and BodyVal.type.pointee.width == 8: + result_type = ir.IntType(8) + else: + result_type = BodyVal.type + else: + result_type = OrelseVal.type + # 在 ThenBB 末尾添加类型转换(如果需要)和分支 + if not ThenEndBB.is_terminated: + Gen.builder.position_at_end(ThenEndBB) + if BodyVal.type != result_type: + if isinstance(BodyVal.type, ir.IntType) and isinstance(result_type, ir.IntType): + if BodyVal.type.width < result_type.width: + BodyVal = Gen.builder.zext(BodyVal, result_type, name="ifexp_body_zext") + else: + BodyVal = Gen.builder.trunc(BodyVal, result_type, name="ifexp_body_trunc") + elif isinstance(BodyVal.type, ir.IntType) and isinstance(result_type, ir.PointerType): + # 如果 result_type 是 i8*,说明 Else 分支是字符串字面量 + # 不应该将整数转换为指针,而应该保持整数类型 + # 这里将 result_type 改为 i8 + result_type = ir.IntType(8) + # 重新处理 Else 分支的类型转换 + elif isinstance(BodyVal.type, ir.PointerType) and isinstance(result_type, ir.IntType): + # 指针转整数 + BodyVal = Gen.builder.ptrtoint(BodyVal, result_type, name="ifexp_body_ptr2int") + Gen.builder.branch(MergeBB) + # 在 ElseBB 末尾添加类型转换(如果需要)和分支 + if not ElseEndBB.is_terminated: + Gen.builder.position_at_end(ElseEndBB) + if OrelseVal.type != result_type: + if isinstance(OrelseVal.type, ir.IntType) and isinstance(result_type, ir.IntType): + if OrelseVal.type.width < result_type.width: + OrelseVal = Gen.builder.zext(OrelseVal, result_type, name="ifexp_orelse_zext") + else: + OrelseVal = Gen.builder.trunc(OrelseVal, result_type, name="ifexp_orelse_trunc") + elif isinstance(OrelseVal.type, ir.IntType) and isinstance(result_type, ir.PointerType): + # 如果 result_type 是 i8*,说明 BodyVal 分支是字符串字面量 + # 不应该将整数转换为指针,而应该保持整数类型 + # 这里将 result_type 改为 i8 + result_type = ir.IntType(8) + # 重新处理 BodyVal 分支的类型转换(已经在上面处理过了) + elif isinstance(OrelseVal.type, ir.PointerType) and isinstance(result_type, ir.IntType): + # 如果指针指向 i8,加载字符值 + if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8: + OrelseVal = Gen._load(OrelseVal, name="ifexp_orelse_Load_char") + else: + OrelseVal = Gen.builder.ptrtoint(OrelseVal, result_type, name="ifexp_orelse_ptr2int") + Gen.builder.branch(MergeBB) + Gen.builder.position_at_start(MergeBB) + result_phi: Any = Gen.builder.phi(result_type, name="ifexp.result") + result_phi.add_incoming(BodyVal, ThenEndBB) + result_phi.add_incoming(OrelseVal, ElseEndBB) + return result_phi + + def _HandleNamedExprLlvm(self, Node: ast.NamedExpr) -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + ValueVal: Any = self.HandleExprLlvm(Node.value) + if not ValueVal: + return None + TargetName: str = Node.target.id + if TargetName in Gen._reg_values: + del Gen._reg_values[TargetName] + if TargetName in Gen.variables and Gen.variables[TargetName] is not None: + VarPtr: Any = Gen.variables[TargetName] + if VarPtr.type.pointee == ValueVal.type: + Gen._store(ValueVal, VarPtr) + else: + NewVar: Any = Gen._alloca(ValueVal.type, name=TargetName) + Gen._store(ValueVal, NewVar) + Gen.variables[TargetName] = NewVar + else: + NewVar = Gen._alloca(ValueVal.type, name=TargetName) + Gen._store(ValueVal, NewVar) + Gen.variables[TargetName] = NewVar + Gen._reg_values[TargetName] = ValueVal + return ValueVal \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesExprUtils.py b/App/lib/core/Handles/HandlesExprUtils.py new file mode 100644 index 0000000..de3deb8 --- /dev/null +++ b/App/lib/core/Handles/HandlesExprUtils.py @@ -0,0 +1,285 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +import ast +import llvmlite.ir as ir +from lib.core.SymbolUtils import IsTModule + + +class ExprUtils: + def __init__(self, translator: "Translator") -> None: + self.Trans: Translator = translator + + def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None: + OpMap: dict[type, str] = { + ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/', + ast.FloorDiv: '//', ast.Mod: '%', ast.Pow: '**', ast.BitAnd: '&', + ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>', + ast.MatMult: '@', + } + return OpMap.get(type(Op), None) + + def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None: + OpMap: dict[type, str] = {ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-'} + return OpMap.get(type(Op), None) + + def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None: + OpMap: dict[type, str] = { + ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=', + ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not', + ast.In: 'in', ast.NotIn: 'not in', + } + return OpMap.get(type(Op), None) + + def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + if isinstance(node, ast.Name): + VarName: str = node.id + return Gen.var_struct_class.get(VarName) + if isinstance(node, ast.Attribute): + AttrName: str = node.attr + # 从父对象的类成员类型推导(如 self._ht → ParsedArgs._ht → HashTable) + # 必须先查 BaseClass,避免字段名和变量名冲突导致错误分派 + # (如 old_args.args 匹配到函数参数 args 的 var_struct_class='AST') + BaseClass: str | None = self._get_var_class(node.value, Gen) + if not BaseClass and isinstance(node.value, ast.Name) and node.value.id == 'self': + BaseClass = getattr(self.Trans, '_CurrentCpythonObjectClass', None) + if BaseClass and BaseClass in Gen.class_members: + for m_name, m_type in Gen.class_members[BaseClass]: + if m_name == AttrName: + if isinstance(m_type, ir.PointerType) and isinstance(m_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found = Gen.find_struct_by_pointee(m_type.pointee) + if found: + return found[0] + # 联合类型(如 GSList[Param] | t.CPtr)降级为 i8* 时, + # 从 class_member_element_class 查找泛型特化类名 + element_class_map = getattr(Gen, 'class_member_element_class', {}) + element_class = element_class_map.get(BaseClass, {}).get(AttrName) + if element_class: + return element_class + break + # Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后, + # 避免字段名和变量名冲突导致错误分派) + cls: str | None = Gen.var_struct_class.get(AttrName) + if cls: + return cls + return None + return None + + def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None: + FullMethodName: str = f'{ClassName}.{op_name}' + if not Gen._has_function(FullMethodName): + FullMethodName = f'{ClassName}.{op_name}__' + if not Gen._has_function(FullMethodName): + # 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且 + # 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本 + # (如 'ndarray[double]')。注解 'numpy.ndarray | t.CPtr' 未指定类型参数时, + # var_struct_class 仅记录裸模板名,但方法只在特化版本中定义。 + # 选择策略:优先选 obj_val.type.pointee 实际匹配的特化;否则选第一个有方法的特化。 + if '[' not in ClassName: + SpecClass: str | None = None + for CN in Gen.structs.keys(): + if CN.startswith(f'{ClassName}[') and Gen._has_function(f'{CN}.{op_name}'): + SpecClass = CN + break + if CN.startswith(f'{ClassName}[') and Gen._has_function(f'{CN}.{op_name}__'): + SpecClass = CN + break + if SpecClass: + FullMethodName = f'{SpecClass}.{op_name}' + if not Gen._has_function(FullMethodName): + FullMethodName = f'{SpecClass}.{op_name}__' + if Gen._has_function(FullMethodName): + func: Any = Gen._get_function(FullMethodName) + call_args: list[Any] = [obj_val] + if other_val is not None: + call_args.append(other_val) + if len(func.ftype.args) == len(call_args): + for i, (expected_type, actual_val) in enumerate(zip(func.ftype.args, call_args)): + if isinstance(expected_type, ir.PointerType) and isinstance(actual_val.type, ir.PointerType): + if expected_type != actual_val.type: + if isinstance(expected_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + call_args[i] = Gen.builder.bitcast(actual_val, expected_type, name=f"bitcast_arg_{i}") + result: Any = Gen.builder.call(func, call_args, name=f"call_{FullMethodName}") + return result + return None + + def _llvm_type_to_detailed_string(self, llvm_type: ir.Type, var_name: str = "") -> dict[str, Any]: + info: dict[str, Any] = { + 'name': "unknown", + 'size': 0, + 'signed': False, + 'ptr': False + } + if isinstance(llvm_type, ir.IntType): + info['size'] = llvm_type.width // 8 + info['signed'] = True + if llvm_type.width == 8: + info['name'] = "char" + elif llvm_type.width == 16: + info['name'] = "short" + elif llvm_type.width == 32: + info['name'] = "int" + elif llvm_type.width == 64: + info['name'] = "long long" + else: + info['name'] = f"int{llvm_type.width}" + elif isinstance(llvm_type, ir.DoubleType): + info['size'] = 8 + info['signed'] = True + info['name'] = "double" + elif isinstance(llvm_type, ir.FloatType): + info['size'] = 4 + info['signed'] = True + info['name'] = "float" + elif isinstance(llvm_type, ir.PointerType): + info['size'] = 8 + info['signed'] = False + info['ptr'] = True + pointee_info: dict[str, Any] = self._llvm_type_to_detailed_string(llvm_type.pointee) + info['name'] = f"{pointee_info['name']}*" + elif isinstance(llvm_type, (ir.LiteralStructType, ir.IdentifiedStructType)): + size: int = 0 + for elem in llvm_type.elements: + elem_info: dict[str, Any] = self._llvm_type_to_detailed_string(elem) + size += elem_info['size'] + info['size'] = size + info['signed'] = False + if isinstance(llvm_type, ir.IdentifiedStructType): + info['name'] = llvm_type.name + else: + info['name'] = "struct" + elif isinstance(llvm_type, ir.ArrayType): + elem_info = self._llvm_type_to_detailed_string(llvm_type.element) + info['size'] = elem_info['size'] * llvm_type.count + info['signed'] = elem_info['signed'] + info['name'] = f"{elem_info['name']}[]" + elif isinstance(llvm_type, ir.VoidType): + info['size'] = 0 + info['signed'] = False + info['name'] = "void" + else: + info['size'] = 0 + info['signed'] = False + info['name'] = str(llvm_type) + return info + + def _infer_expr_llvm_type_full(self, node: ast.AST) -> ir.Type: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(node, ast.Constant): + if isinstance(node.value, bool): + return ir.IntType(32) + elif isinstance(node.value, int): + return ir.IntType(32) + elif isinstance(node.value, float): + return ir.DoubleType() + elif isinstance(node.value, str): + if getattr(node, 'kind', None) == 'u': + return ir.PointerType(ir.IntType(16)) + return ir.PointerType(ir.IntType(8)) + return ir.IntType(32) + elif isinstance(node, ast.Name): + if node.id in Gen.variables: + var_ptr: Any = Gen.variables[node.id] + if isinstance(var_ptr.type, ir.PointerType): + return var_ptr.type.pointee + return var_ptr.type + if node.id in Gen._reg_values: + return Gen._reg_values[node.id].type + return ir.IntType(32) + elif isinstance(node, ast.Attribute): + attr_name: str = node.attr + AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name) + if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None): + return ir.IntType(32) + return ir.IntType(32) + elif isinstance(node, ast.BinOp): + left_type: ir.Type = self._infer_expr_llvm_type_full(node.left) + right_type: ir.Type = self._infer_expr_llvm_type_full(node.right) + if isinstance(left_type, (ir.FloatType, ir.DoubleType)) or isinstance(right_type, (ir.FloatType, ir.DoubleType)): + return ir.DoubleType() + if isinstance(left_type, ir.PointerType) or isinstance(right_type, ir.PointerType): + return ir.PointerType(ir.IntType(8)) + return ir.IntType(32) + elif isinstance(node, ast.UnaryOp): + return self._infer_expr_llvm_type_full(node.operand) + elif isinstance(node, ast.Subscript): + val_type: ir.Type = self._infer_expr_llvm_type_full(node.value) + if isinstance(val_type, ir.PointerType): + if isinstance(val_type.pointee, ir.IntType) and val_type.pointee.width == 8: + return ir.IntType(8) + if isinstance(val_type.pointee, ir.ArrayType): + return ir.PointerType(val_type.pointee.element) + return val_type.pointee + if isinstance(val_type, ir.ArrayType): + return val_type.element + if isinstance(val_type, ir.IntType): + return ir.IntType(8) + return ir.IntType(32) + elif isinstance(node, ast.Call): + if isinstance(node.func, ast.Name): + func_name: str = node.func.id + if Gen._has_function(func_name): + fn: Any = Gen._get_function(func_name) + return fn.function_type.return_type + if func_name == 'len': + return ir.IntType(32) + if func_name == 'sizeof': + return ir.IntType(32) + if func_name == 'ord': + return ir.IntType(32) + if func_name == 'chr': + return ir.IntType(8) + if func_name == 'int': + return ir.IntType(32) + if func_name == 'float' or func_name == 'double': + return ir.DoubleType() + elif isinstance(node.func, ast.Attribute): + if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c': + if node.func.attr == 'Deref': + return ir.IntType(64) + if isinstance(node.func.value, ast.Name) and IsTModule(node.func.value.id, self.Trans.SymbolTable): + if node.func.attr == 'CChar': + return ir.PointerType(ir.IntType(8)) + return ir.IntType(32) + elif isinstance(node, ast.Compare): + return ir.IntType(1) + elif isinstance(node, ast.BoolOp): + return ir.IntType(1) + elif isinstance(node, ast.IfExp): + return self._infer_expr_llvm_type_full(node.body) + elif isinstance(node, ast.Attribute): + return ir.PointerType(ir.IntType(8)) + return ir.IntType(32) + + def _collect_names(self, node: ast.AST, bound: set[str], free: set[str] | None = None) -> None: + """收集 AST 节点中的绑定变量和自由变量 + + Args: + node: AST 节点 + bound: 集合,收集绑定变量(ast.Store 上下文) + free: 集合,收集自由变量(ast.Load 上下文),可为 None + """ + if free is None: + free = set() + if isinstance(node, ast.Name): + if isinstance(node.ctx, ast.Store): + bound.add(node.id) + elif isinstance(node.ctx, ast.Load): + free.add(node.id) + elif isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef)): + # 这些节点有自己的作用域,不递归进入 + # 但需要收集参数名作为 bound + if hasattr(node, 'args') and node.args: + for arg in node.args.args: + bound.add(arg.arg) + elif getattr(node, '_fields', None): + for field in node._fields: + value: Any = getattr(node, field, None) + if isinstance(value, list): + for item in value: + if isinstance(item, (ast.stmt, ast.expr)): + self._collect_names(item, bound, free) + elif isinstance(value, (ast.stmt, ast.expr)): + self._collect_names(value, bound, free) \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesFunctions.py b/App/lib/core/Handles/HandlesFunctions.py index e785cfb..0a9449c 100644 --- a/App/lib/core/Handles/HandlesFunctions.py +++ b/App/lib/core/Handles/HandlesFunctions.py @@ -399,6 +399,9 @@ def translate_function_def(trans: HT.Translator | t.CPtr, if fd is None or fd.name is None: return 0 + stdio.printf("[FD] enter name=%s\n", fd.name) + stdio.fflush(0) + pool: memhub.MemBuddy | t.CPtr = trans.Pool mod: llvmlite.LLVMModule | t.CPtr = trans.Module imported_modules: str = trans._imported_modules @@ -409,6 +412,8 @@ def translate_function_def(trans: HT.Translator | t.CPtr, i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) # 推断返回类型:优先使用返回类型注解 + stdio.printf("[FD] %s 推断返回类型前\n", fd.name) + stdio.fflush(0) ret_ty: llvmlite.LLVMType | t.CPtr = None if fd.returns is not None: ret_ty = HandlesType.resolve_annotation_type( @@ -425,6 +430,8 @@ def translate_function_def(trans: HT.Translator | t.CPtr, param_types_str: str = HandlesType.build_param_types_str(pool, fd.args) ret_ty = HandlesType.infer_return_type( pool, fd.children, param_types_str) + stdio.printf("[FD] %s 推断返回类型后=%d\n", fd.name, ret_ty) + stdio.fflush(0) # 检测是否为外部声明函数(t.CExtern 或 t.State) # 语义:t.CExtern 忽略 body 体,仅生成 declare(由链接器解析符号) @@ -442,6 +449,9 @@ def translate_function_def(trans: HT.Translator | t.CPtr, # t.CExtern/t.State 忽略 body 体,仅声明(无论 body 是否为 pass) if has_extern != 0 or has_state != 0: is_extern_decl = 1 + stdio.printf("[FD] %s extern=%d state=%d export=%d is_extern_decl=%d\n", + fd.name, has_extern, has_state, has_export, is_extern_decl) + stdio.fflush(0) # SHA1 命名空间:t.CExtern/t.State/t.CExport 不加前缀,直接用裸名 # (裸名映射到 C 标准库符号,带 sha1 前缀会导致 undefined reference) @@ -450,6 +460,8 @@ def translate_function_def(trans: HT.Translator | t.CPtr, mangled_name: str = fd.name else: mangled_name: str = _mangle_func_name(trans, fd.name, 0) + stdio.printf("[FD] %s mangled_name=%s\n", fd.name, mangled_name) + stdio.fflush(0) # 注册 CExport 函数到全局表(供跨模块调用查表) # t.CExport 函数定义用裸名(@strlen),跨模块调用需查表确认用裸名而非 @{sha1}.func @@ -505,7 +517,11 @@ def translate_function_def(trans: HT.Translator | t.CPtr, args_node: ast.Arguments | t.CPtr = fd.args # 检查是否已有前向声明(由 forward_declare_functions 创建) + stdio.printf("[FD] %s find_func_in_module 前\n", fd.name) + stdio.fflush(0) func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, mangled_name) + stdio.printf("[FD] %s find_func_in_module 后=%d\n", fd.name, func) + stdio.fflush(0) if func is not None and llvmlite.function_is_declared(func) != 0: # 复用前向声明:清除 IsDeclared 标记,转为 define func.IsDeclared = 0 @@ -513,9 +529,15 @@ def translate_function_def(trans: HT.Translator | t.CPtr, func_attrs_reuse: t.CChar | t.CPtr = extract_func_attrs(pool, fd.decorator_list, imported_modules) if func_attrs_reuse is not None: llvmlite.function_set_attrs(func, func_attrs_reuse) + stdio.printf("[FD] %s 复用前向声明\n", fd.name) + stdio.fflush(0) else: # 创建新的 LLVM 函数(使用 SHA1 混淆名) + stdio.printf("[FD] %s create_function 前\n", fd.name) + stdio.fflush(0) func = llvmlite.create_function(pool, mod, mangled_name, ret_ty) + stdio.printf("[FD] %s create_function 后=%d\n", fd.name, func) + stdio.fflush(0) if func is None: stdio.printf("[FUNC] create_function %s failed\n", fd.name) return 0 @@ -553,22 +575,38 @@ def translate_function_def(trans: HT.Translator | t.CPtr, if pname is not None: viperlib.snprintf(pname, 32, "%%%s", arg.arg) llvmlite.add_param(pool, func, param_ty, pname) + stdio.printf("[FD] %s 新函数创建完成\n", fd.name) + stdio.fflush(0) # 创建 entry 块 + stdio.printf("[FD] %s create_block 前\n", fd.name) + stdio.fflush(0) entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, "entry") + stdio.printf("[FD] %s create_block 后=%d\n", fd.name, entry_blk) + stdio.fflush(0) if entry_blk is None: return 0 # 创建函数专属 builder + stdio.printf("[FD] %s new_builder 前\n", fd.name) + stdio.fflush(0) func_builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, func) + stdio.printf("[FD] %s new_builder 后=%d\n", fd.name, func_builder) + stdio.fflush(0) if func_builder is None: return 0 llvmlite.position_at_end(func_builder, entry_blk) # 进入函数作用域(嵌套符号表) + stdio.printf("[FD] %s enter_scope 前\n", fd.name) + stdio.fflush(0) HandlesVar.enter_scope(trans.SymTab, HandlesVar.SCOPE_FUNCTION) + stdio.printf("[FD] %s enter_scope 后\n", fd.name) + stdio.fflush(0) # 为参数创建 alloca + stdio.printf("[FD] %s 参数 alloca 前\n", fd.name) + stdio.fflush(0) if args_node is not None: ags2: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node) if ags2.args is not None: @@ -603,6 +641,8 @@ def translate_function_def(trans: HT.Translator | t.CPtr, param_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue( pool, param_ty2, pname2) llvmlite.build_store(func_builder, param_val, alloca) + stdio.printf("[FD] %s 参数 alloca 后\n", fd.name) + stdio.fflush(0) # 保存模块级作用域状态(仅非变量表相关) old_func: llvmlite.Function | t.CPtr = trans._cur_func @@ -617,6 +657,8 @@ def translate_function_def(trans: HT.Translator | t.CPtr, HT.clear_scope_names(trans) # 预扫描函数体:为局部变量提前创建 alloca + stdio.printf("[FD] %s pre_scan_allocas 前\n", fd.name) + stdio.fflush(0) body: list[ast.AST | t.CPtr] | t.CPtr = fd.children if body is not None: bn: t.CSizeT = body.__len__() @@ -624,14 +666,23 @@ def translate_function_def(trans: HT.Translator | t.CPtr, stmt: ast.AST | t.CPtr = body.get(bi) if stmt is not None: HandlesBody.pre_scan_allocas(trans, stmt) + stdio.printf("[FD] %s pre_scan_allocas 后\n", fd.name) + stdio.fflush(0) # 翻译函数体 + stdio.printf("[FD] %s translate_stmt 前 body_len=%d\n", fd.name, + body.__len__() if body is not None else 0) + stdio.fflush(0) if body is not None: bn2: t.CSizeT = body.__len__() for bi2 in range(bn2): stmt2: ast.AST | t.CPtr = body.get(bi2) if stmt2 is not None: + stdio.printf("[FD] %s translate_stmt bi2=%d kd=%d 前\n", fd.name, bi2, stmt2.kind()) + stdio.fflush(0) HandlesBody.translate_stmt(trans, stmt2) + stdio.printf("[FD] %s translate_stmt bi2=%d 后\n", fd.name, bi2) + stdio.fflush(0) # 如果函数体最后一条语句不是 Return,添加隐式 ret last_is_return: int = 0 diff --git a/App/lib/core/Handles/HandlesMain.py b/App/lib/core/Handles/HandlesMain.py index 3b29f02..abcf863 100644 --- a/App/lib/core/Handles/HandlesMain.py +++ b/App/lib/core/Handles/HandlesMain.py @@ -81,47 +81,81 @@ def translate_children(trans: HT.Translator | t.CPtr, cn_count: t.CSizeT = ch.__len__() added_total: int = 0 + stdio.printf("[TC] cn_count=%d\n", cn_count) + stdio.fflush(0) for ci in range(cn_count): child: ast.AST | t.CPtr = ch.get(ci) if child is None: continue kd: int = child.kind() + stdio.printf("[TC] ci=%d kd=%d\n", ci, kd) + stdio.fflush(0) if kd == ast.ASTKind.Import: + stdio.printf("[TC] ci=%d 处理 Import\n", ci) + stdio.fflush(0) trans.ImportsH.HandleImport(child) + stdio.printf("[TC] ci=%d Import 后\n", ci) + stdio.fflush(0) elif kd == ast.ASTKind.ImportFrom: + stdio.printf("[TC] ci=%d 处理 ImportFrom\n", ci) + stdio.fflush(0) trans.ImportsH.HandleImportFromModule(child) trans.ImportsH.HandleImportFromNames(child) + stdio.printf("[TC] ci=%d ImportFrom 后\n", ci) + stdio.fflush(0) elif kd == ast.ASTKind.FunctionDef: # Phase 1a 声明模式:只注册 CExport/State 函数到全局表(解决翻译顺序依赖) # Phase 1b 全量翻译:正常翻译函数体 + fd_dbg: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child) + fd_name_dbg: str = "?" if fd_dbg is None or fd_dbg.name is None else fd_dbg.name + stdio.printf("[TC] ci=%d FunctionDef name=%s _declare_only=%d 前\n", ci, fd_name_dbg, trans._declare_only) + stdio.fflush(0) if trans._declare_only == 0: added: int = HandlesFunctions.translate_function_def(trans, child) added_total += added elif trans._declare_only == 1: _register_cexport_from_funcdef(trans, child) + stdio.printf("[TC] ci=%d FunctionDef 后\n", ci) + stdio.fflush(0) elif kd == ast.ASTKind.ClassDef: # ClassDef 在模块级直接处理(不需要 builder) # _declare_only=2(import扫描模式)时跳过,只处理 import 依赖 + cd_node: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child) + cd_name: str = "?" if cd_node is None or cd_node.name is None else cd_node.name + stdio.printf("[TC] ci=%d ClassDef name=%s 前\n", ci, cd_name) + stdio.fflush(0) if trans._declare_only != 2: - cd_node: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child) - cd_name: str = "?" if cd_node is None or cd_node.name is None else cd_node.name HandlesClassDef.translate_class_def(trans, child) + stdio.printf("[TC] ci=%d ClassDef 后\n", ci) + stdio.fflush(0) elif trans._declare_only == 0 and trans._cur_builder is not None: # 有 builder → 委托 HandlesBody 分派 + stdio.printf("[TC] ci=%d translate_stmt 前\n", ci) + stdio.fflush(0) added = HandlesBody.translate_stmt(trans, child) added_total += added + stdio.printf("[TC] ci=%d translate_stmt 后\n", ci) + stdio.fflush(0) elif kd == ast.ASTKind.AnnAssign and trans._declare_only != 2: # 模块级 AnnAssign # _declare_only=2(import扫描模式)时跳过 # _declare_only=1(struct注册模式)时只处理 CDefine(在 handle_module_level_var 内部判断) # _declare_only=0(全量翻译)时处理所有模块级 AnnAssign + stdio.printf("[TC] ci=%d AnnAssign 前\n", ci) + stdio.fflush(0) added = handle_module_level_var(trans, child) added_total += added + stdio.printf("[TC] ci=%d AnnAssign 后\n", ci) + stdio.fflush(0) elif trans._declare_only == 0 and kd == ast.ASTKind.Assign: # 无 builder 的模块级 Assign → 创建全局变量(仅全量翻译模式) + stdio.printf("[TC] ci=%d Assign 前\n", ci) + stdio.fflush(0) added = handle_module_level_var(trans, child) added_total += added + stdio.printf("[TC] ci=%d Assign 后\n", ci) + stdio.fflush(0) return added_total diff --git a/App/lib/core/Handles/HandlesMatch.py b/App/lib/core/Handles/HandlesMatch.py new file mode 100644 index 0000000..4b8e78a --- /dev/null +++ b/App/lib/core/Handles/HandlesMatch.py @@ -0,0 +1,382 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator +from lib.core.Handles.HandlesBase import BaseHandle +import ast +import llvmlite.ir as ir + + +class MatchHandle(BaseHandle): + def _HandleMatchLlvm(self, Node: ast.Match) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + SubjectVal: ir.Value | None = self.HandleExprLlvm(Node.subject) + if not SubjectVal: + return + + IsRenumMatch: bool = False + RenumName: str | None = None + SubjectPtr: ir.Value | None = None + if isinstance(Node.subject, ast.Name): + VarName: str = Node.subject.id + TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName) + if TypeInfo and TypeInfo.IsRenum: + IsRenumMatch = True + RenumName = TypeInfo.Name + SubjectPtr = Gen._Load_var(VarName) + + if not IsRenumMatch: + for case in Node.cases: + if isinstance(case.pattern, ast.MatchClass): + cls_node: ast.expr = case.pattern.cls + VariantName: str | None = None + QualifiedName: str | None = None + if isinstance(cls_node, ast.Name): + VariantName = cls_node.id + elif isinstance(cls_node, ast.Attribute): + VariantName = cls_node.attr + # Extract enum name for qualified lookup to avoid collision + # with same-name factory functions (e.g. def Ptr vs LLVMType.Ptr) + if isinstance(cls_node.value, ast.Attribute): + QualifiedName = f"{cls_node.value.attr}.{VariantName}" + elif isinstance(cls_node.value, ast.Name): + QualifiedName = f"{cls_node.value.id}.{VariantName}" + if VariantName: + # Try qualified name first (e.g., "LLVMType.Ptr") to avoid + # collision with same-name functions (e.g., def Ptr(...)) + SymInfo: "SymbolTable.SymbolInfo" = None + if QualifiedName: + SymInfo = self.Trans.SymbolTable.lookup(QualifiedName) + if not (SymInfo and SymInfo.IsEnumMember): + SymInfo = self.Trans.SymbolTable.lookup(VariantName) + if SymInfo and SymInfo.IsEnumMember and SymInfo.EnumName: + EnumName: str = SymInfo.EnumName + EnumInfo: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(EnumName) + if EnumInfo and EnumInfo.IsRenum: + IsRenumMatch = True + RenumName = EnumName + if SubjectPtr is None: + SubjectPtr = self.HandleExprLlvm(Node.subject) + break + + if IsRenumMatch and SubjectPtr: + self._HandleRenumMatchLlvm(Node, RenumName, SubjectPtr) + return + + if not isinstance(SubjectVal.type, ir.IntType): + try: + SubjectVal = Gen.builder.ptrtoint(SubjectVal, ir.IntType(64), name="match_subj") + SubjectVal = Gen.builder.trunc(SubjectVal, ir.IntType(32), name="match_subj_i32") + except Exception: # 回退:ptrtoint 失败时直接返回 + return + SwitchIntType: ir.IntType = SubjectVal.type + DefaultBB: ir.Block = Gen.func.append_basic_block(name="match.default") + AfterBB: ir.Block = Gen.func.append_basic_block(name="match.end") + CaseBBs: list[ir.Block] = [] + CaseValues: list[ir.Value] = [] + HasDefault: bool = False + HasNoBreak: list[bool] = [] + for i, case in enumerate(Node.cases): + pattern: ast.pattern = case.pattern + if isinstance(pattern, ast.MatchValue): + Val: ir.Value | None = self.HandleExprLlvm(pattern.value) + if Val: + CaseVal: ir.Value + if isinstance(Val.type, ir.IntType): + if Val.type != SwitchIntType: + if Val.type.width > SwitchIntType.width: + CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1)) + else: + CaseVal = ir.Constant(SwitchIntType, Val.constant) + else: + CaseVal = Val + else: + try: + CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}") + except Exception: # 回退:ptrtoint 失败时设默认值 0 + CaseVal = ir.Constant(SwitchIntType, 0) + CaseValues.append(CaseVal) + CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}")) + elif isinstance(pattern, ast.MatchOr): + for j, SubPattern in enumerate(pattern.patterns): + if isinstance(SubPattern, ast.MatchValue): + Val: ir.Value | None = self.HandleExprLlvm(SubPattern.value) + if Val: + CaseVal: ir.Value + if isinstance(Val.type, ir.IntType): + if Val.type != SwitchIntType: + if Val.type.width > SwitchIntType.width: + CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1)) + else: + CaseVal = ir.Constant(SwitchIntType, Val.constant) + else: + CaseVal = Val + else: + try: + CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}_{j}") + except Exception: # 回退:ptrtoint 失败时设默认值 0 + CaseVal = ir.Constant(SwitchIntType, 0) + CaseValues.append(CaseVal) + CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}_{j}")) + elif isinstance(pattern, ast.MatchSingleton): + if pattern.value is None: + CaseValues.append(ir.Constant(SwitchIntType, 0)) + CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}")) + elif isinstance(pattern, ast.MatchAs): + if pattern.pattern is None: + HasDefault = True + CaseBBs.append(DefaultBB) + elif isinstance(pattern, ast.MatchSequence): + HasDefault = True + CaseBBs.append(DefaultBB) + def _HasNoBreak(stmts: list[ast.stmt]) -> bool: + for stmt in stmts: + if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call): + if isinstance(stmt.value.func, ast.Attribute): + if (isinstance(stmt.value.func.value, ast.Name) and + stmt.value.func.value.id == 'c' and + stmt.value.func.attr == 'NoBreak'): + return True + if getattr(stmt, 'body', None) and isinstance(stmt.body, list): + if _HasNoBreak(stmt.body): + return True + if getattr(stmt, 'orelse', None): + if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse): + return True + return False + HasNoBreak.append(_HasNoBreak(case.body) if case.body else False) + if not HasDefault: + CaseBBs.append(DefaultBB) + SwitchCases: list[tuple[ir.Value, ir.Block]] = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB] + switch_instr: ir.SwitchInstr = Gen.builder.switch(SubjectVal, DefaultBB) + for val, bb in SwitchCases: + switch_instr.add_case(val, bb) + CaseIdx: int = 0 + for i, case in enumerate(Node.cases): + pattern: ast.pattern = case.pattern + if isinstance(pattern, ast.MatchOr): + NumSubCases: int = len(pattern.patterns) + for j in range(NumSubCases): + if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB: + Gen.builder.position_at_start(CaseBBs[CaseIdx]) + self.HandleBodyLlvm(case.body) + if not Gen.builder.block.is_terminated: + if not HasNoBreak[i]: + Gen.builder.branch(AfterBB) + CaseIdx += 1 + elif isinstance(pattern, (ast.MatchValue, ast.MatchSingleton)): + if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB: + Gen.builder.position_at_start(CaseBBs[CaseIdx]) + self.HandleBodyLlvm(case.body) + if not Gen.builder.block.is_terminated: + if not HasNoBreak[i]: + Gen.builder.branch(AfterBB) + CaseIdx += 1 + elif isinstance(pattern, ast.MatchAs): + if pattern.pattern is None: + Gen.builder.position_at_start(DefaultBB) + self.HandleBodyLlvm(case.body) + if not Gen.builder.block.is_terminated: + if not HasNoBreak[i]: + Gen.builder.branch(AfterBB) + CaseIdx += 1 + elif isinstance(pattern, ast.MatchSequence): + Gen.builder.position_at_start(DefaultBB) + self.HandleBodyLlvm(case.body) + if not Gen.builder.block.is_terminated: + if not HasNoBreak[i]: + Gen.builder.branch(AfterBB) + CaseIdx += 1 + if not HasDefault: + Gen.builder.position_at_start(DefaultBB) + Gen.builder.branch(AfterBB) + elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases): + Gen.builder.position_at_start(DefaultBB) + Gen.builder.branch(AfterBB) + Gen.builder.position_at_start(AfterBB) + + def _HandleRenumMatchLlvm(self, Node: ast.Match, RenumName: str, SubjectPtr: ir.Value) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + if isinstance(SubjectPtr.type, ir.PointerType) and isinstance(SubjectPtr.type.pointee, ir.PointerType): + SubjectPtr = Gen._load(SubjectPtr, name="Load_match_subj") + # Bug fix: HandleExprLlvm 可能对 REnum 字段执行了 load,返回值而非指针。 + # 情况1: SubjectPtr 不是指针类型(如 IntType)→ alloca REnum 结构体 + store + # 情况2: SubjectPtr 是指针但 pointee 不是结构体(如 i32* 指向 __tag)→ bitcast + NeedAlloca: bool = not isinstance(SubjectPtr.type, ir.PointerType) + NeedBitcast: bool = (isinstance(SubjectPtr.type, ir.PointerType) and + not isinstance(SubjectPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))) + if NeedAlloca or NeedBitcast: + RenumStructType: Any = Gen.structs.get(RenumName) + if RenumStructType is None: + # 跨模块编译时 REnum 结构体可能尚未在 Gen.structs 中注册 + # (如 import llvmlite 后 LLVMType 仅在符号表,Gen.structs 无条目)。 + # 使用 _get_or_create_struct 按需创建(可能为 opaque)。 + RenumStructType = Gen._get_or_create_struct(RenumName) + if NeedAlloca: + AllocaPtr: ir.Value = Gen._allocaEntry(RenumStructType, name="match_subj_alloca") + CastedPtr: ir.Value = Gen.builder.bitcast(AllocaPtr, ir.PointerType(SubjectPtr.type), name="match_subj_cast") + Gen._store(SubjectPtr, CastedPtr) + SubjectPtr = AllocaPtr + else: + SubjectPtr = Gen.builder.bitcast(SubjectPtr, ir.PointerType(RenumStructType), name="match_subj_recast") + # REnum 布局为 { i32 __tag, },tag 在偏移 0。 + # 使用 bitcast 到 i32* 替代 gep [0,0],兼容 opaque 结构体 + # (跨模块编译时 REnum 结构体可能未 set_body,gep 会失败)。 + tag_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, ir.PointerType(ir.IntType(32)), name="match_tag_ptr") + TagVal: ir.Value = Gen._load(tag_ptr, name="match_tag_val") + DefaultBB: ir.Block = Gen.func.append_basic_block(name="match.default") + AfterBB: ir.Block = Gen.func.append_basic_block(name="match.end") + CaseBBs: list[ir.Block] = [] + CaseValues: list[ir.Value] = [] + CaseBindings: list[list[tuple[str, str, ir.Type, int]]] = [] + HasDefault: bool = False + HasNoBreak: list[bool] = [] + for i, case in enumerate(Node.cases): + pattern: ast.pattern = case.pattern + bindings: list[tuple[str, str, ir.Type, int]] = [] + if isinstance(pattern, ast.MatchClass): + cls_node: ast.expr = pattern.cls + VariantName: str | None = None + QualifiedName: str | None = None + if isinstance(cls_node, ast.Name): + VariantName = cls_node.id + elif isinstance(cls_node, ast.Attribute): + VariantName = cls_node.attr + if isinstance(cls_node.value, ast.Attribute): + QualifiedName = f"{cls_node.value.attr}.{VariantName}" + elif isinstance(cls_node.value, ast.Name): + QualifiedName = f"{cls_node.value.id}.{VariantName}" + if VariantName: + TagValue: int | None = None + SymInfo: "SymbolTable.SymbolInfo" = None + if QualifiedName: + SymInfo = self.Trans.SymbolTable.lookup(QualifiedName) + if not (SymInfo and SymInfo.IsEnumMember): + SymInfo = self.Trans.SymbolTable.lookup(VariantName) + if SymInfo and SymInfo.IsEnumMember: + TagValue = SymInfo.value + if TagValue is not None: + CaseValues.append(ir.Constant(ir.IntType(32), TagValue)) + CaseBB: ir.Block = Gen.func.append_basic_block(name=f"match.case_{VariantName}") + CaseBBs.append(CaseBB) + NestedStructName: str = f"{RenumName}_{VariantName}" + if NestedStructName in Gen.structs: + members: list[tuple[str, ir.Type]] = Gen.class_members.get(NestedStructName, []) + payLoad_members: list[tuple[str, ir.Type]] = [(n, t) for n, t in members if n != '__tag'] + for j, sub_pat in enumerate(pattern.patterns): + if isinstance(sub_pat, ast.MatchAs) and sub_pat.name and j < len(payLoad_members): + bindings.append((sub_pat.name, payLoad_members[j][0], payLoad_members[j][1], j)) + CaseBindings.append(bindings) + else: + CaseValues.append(ir.Constant(ir.IntType(32), 0)) + CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}")) + CaseBindings.append([]) + elif isinstance(pattern, ast.MatchValue): + Val: ir.Value | None = self.HandleExprLlvm(pattern.value) + if Val: + CaseVal: ir.Value + if isinstance(Val.type, ir.IntType): + CaseVal = Val + else: + try: + CaseVal = Gen.builder.ptrtoint(Val, ir.IntType(32), name=f"case_val_{i}") + except Exception: # 回退:ptrtoint 失败时设默认值 0 + CaseVal = ir.Constant(ir.IntType(32), 0) + CaseValues.append(CaseVal) + CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}")) + CaseBindings.append([]) + elif isinstance(pattern, ast.MatchAs): + if pattern.pattern is None: + HasDefault = True + CaseBBs.append(DefaultBB) + CaseBindings.append([]) + elif isinstance(pattern, ast.MatchSequence): + HasDefault = True + CaseBBs.append(DefaultBB) + CaseBindings.append([]) + def _HasNoBreak(stmts: list[ast.stmt]) -> bool: + for stmt in stmts: + if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call): + if isinstance(stmt.value.func, ast.Attribute): + if (isinstance(stmt.value.func.value, ast.Name) and + stmt.value.func.value.id == 'c' and + stmt.value.func.attr == 'NoBreak'): + return True + if getattr(stmt, 'body', None) and isinstance(stmt.body, list): + if _HasNoBreak(stmt.body): + return True + if getattr(stmt, 'orelse', None): + if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse): + return True + return False + HasNoBreak.append(_HasNoBreak(case.body) if case.body else False) + if not HasDefault: + CaseBBs.append(DefaultBB) + SwitchCases: list[tuple[ir.Value, ir.Block]] = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB] + switch_instr: ir.SwitchInstr = Gen.builder.switch(TagVal, DefaultBB) + for val, bb in SwitchCases: + switch_instr.add_case(val, bb) + CaseIdx: int = 0 + for i, case in enumerate(Node.cases): + pattern: ast.pattern = case.pattern + if isinstance(pattern, ast.MatchClass): + if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB: + Gen.builder.position_at_start(CaseBBs[CaseIdx]) + bindings: list[tuple[str, str, ir.Type, int]] = CaseBindings[CaseIdx] if CaseIdx < len(CaseBindings) else [] + cls_node: ast.expr = pattern.cls + VariantName: str | None = None + if isinstance(cls_node, ast.Name): + VariantName = cls_node.id + elif isinstance(cls_node, ast.Attribute): + VariantName = cls_node.attr + if VariantName: + NestedStructName: str = f"{RenumName}_{VariantName}" + if NestedStructName in Gen.structs: + NestedStructType: ir.Type = Gen.structs[NestedStructName] + NestedStructPtrType: ir.PointerType = ir.PointerType(NestedStructType) + variant_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, NestedStructPtrType, name=f"match_cast_{VariantName}") + for bind_name, member_name, member_type, member_idx in bindings: + elem_ptr: ir.Value = Gen.builder.gep(variant_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), member_idx + 1)], name=f"match_{bind_name}") + # REnum 嵌套结构体共享 max_variant_struct 布局,成员类型可能与 + # 结构体字段类型不一致。bitcast 到成员类型以确保后续 load 得到正确类型。 + if isinstance(elem_ptr.type, ir.PointerType) and elem_ptr.type.pointee != member_type: + elem_ptr = Gen.builder.bitcast(elem_ptr, ir.PointerType(member_type), name=f"match_{bind_name}_cast") + Gen.variables[bind_name] = elem_ptr + self.HandleBodyLlvm(case.body) + for bind_name, _, _, _ in bindings: + if bind_name in Gen.variables: + del Gen.variables[bind_name] + if not Gen.builder.block.is_terminated: + if not HasNoBreak[i]: + Gen.builder.branch(AfterBB) + CaseIdx += 1 + elif isinstance(pattern, ast.MatchValue): + if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB: + Gen.builder.position_at_start(CaseBBs[CaseIdx]) + self.HandleBodyLlvm(case.body) + if not Gen.builder.block.is_terminated: + if not HasNoBreak[i]: + Gen.builder.branch(AfterBB) + CaseIdx += 1 + elif isinstance(pattern, ast.MatchAs): + if pattern.pattern is None: + Gen.builder.position_at_start(DefaultBB) + self.HandleBodyLlvm(case.body) + if not Gen.builder.block.is_terminated: + if not HasNoBreak[i]: + Gen.builder.branch(AfterBB) + CaseIdx += 1 + elif isinstance(pattern, ast.MatchSequence): + Gen.builder.position_at_start(DefaultBB) + self.HandleBodyLlvm(case.body) + if not Gen.builder.block.is_terminated: + if not HasNoBreak[i]: + Gen.builder.branch(AfterBB) + CaseIdx += 1 + if not HasDefault: + Gen.builder.position_at_start(DefaultBB) + Gen.builder.branch(AfterBB) + elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases): + Gen.builder.position_at_start(DefaultBB) + Gen.builder.branch(AfterBB) + Gen.builder.position_at_start(AfterBB) \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesRaise.py b/App/lib/core/Handles/HandlesRaise.py new file mode 100644 index 0000000..9802e92 --- /dev/null +++ b/App/lib/core/Handles/HandlesRaise.py @@ -0,0 +1,155 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator +import ast +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP +from lib.constants.config import mode as _config_mode + + +class RaiseHandle(BaseHandle): + def _get_exception_code(self, ExcName: str) -> int: + if ExcName in EXCEPTION_CODE_MAP: + return EXCEPTION_CODE_MAP[ExcName] + if ExcName in self.Trans.exception_registry: + return self.Trans.exception_registry[ExcName] + return 1 + + def _HandleRaiseLlvm(self, Node: ast.Raise) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + exc_val: ir.Constant = ir.Constant(ir.IntType(32), 1) + exc_msg: ir.Value | None = None + IsStopIteration: bool = False + if Node.exc: + if isinstance(Node.exc, ast.Constant) and isinstance(Node.exc.value, int): + exc_val = ir.Constant(ir.IntType(32), Node.exc.value) + elif isinstance(Node.exc, ast.Name): + ExcName: str = Node.exc.id + if ExcName == 'StopIteration': + IsStopIteration = True + exc_val = ir.Constant(ir.IntType(32), self._get_exception_code(ExcName)) + elif isinstance(Node.exc, ast.Call) and isinstance(Node.exc.func, ast.Name): + ExcName: str = Node.exc.func.id + if ExcName == 'StopIteration': + IsStopIteration = True + exc_val = ir.Constant(ir.IntType(32), self._get_exception_code(ExcName)) + if Node.exc.args: + first_arg: ast.expr = Node.exc.args[0] + if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str): + exc_msg = self.HandleExprLlvm(first_arg) + elif isinstance(first_arg, (ast.Name, ast.Call, ast.Attribute)): + arg_val: ir.Value | None = self.HandleExprLlvm(first_arg) + if arg_val: + if isinstance(arg_val.type, ir.PointerType): + if self._is_char_pointer(arg_val): + exc_msg = arg_val + else: + try: + exc_msg = Gen.builder.bitcast(arg_val, ir.PointerType(ir.IntType(8)), name="exc_msg_cast") + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + else: + try: + exc_msg = Gen.builder.inttoptr(arg_val, ir.PointerType(ir.IntType(8)), name="exc_msg_ptr") + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + else: + val: ir.Value | None = self.HandleExprLlvm(Node.exc) + if val: + if isinstance(val.type, ir.IntType): + if val.type.width != 32: + try: + val = Gen.builder.trunc(val, ir.IntType(32), name="exc_trunc") + except Exception: # 回退:trunc 失败时设默认值 1 + val = ir.Constant(ir.IntType(32), 1) + exc_val = val + else: + try: + val = Gen.builder.ptrtoint(val, ir.IntType(32), name="exc_ptr2int") + exc_val = val + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + if IsStopIteration: + if Gen._stop_iter_flag_param is not None: + Gen.builder.store(ir.Constant(ir.IntType(1), 1), Gen._stop_iter_flag_param) + if Gen.func and Gen.func.type.pointee.return_type != ir.VoidType(): + ret_type: ir.Type = Gen.func.type.pointee.return_type + if isinstance(ret_type, ir.PointerType): + Gen.builder.ret(ir.Constant(ret_type, None)) + elif isinstance(ret_type, ir.IntType): + Gen.builder.ret(ir.Constant(ret_type, 0)) + elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)): + Gen.builder.ret(ir.Constant(ret_type, 0.0)) + elif isinstance(ret_type, ir.BaseStructType): + if ret_type.elements: + zero_val: ir.Constant = ir.Constant(ret_type, [ + ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) + else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) + else ir.Constant(et, ir.Undefined) for et in ret_type.elements]) + else: + zero_val = ir.Constant(ret_type, None) + Gen.builder.ret(zero_val) + else: + Gen.builder.ret(ir.Constant(ret_type, 0)) + else: + Gen.builder.ret_void() + return + if Gen.eh_except_block_stack: + ExceptBB: ir.Block = Gen.eh_except_block_stack[-1][0] + EndBB: ir.Block | None = Gen.eh_except_block_stack[-1][1] + exception_code: ir.AllocaInstr = Gen.eh_except_block_stack[-1][2] + eh_message: ir.AllocaInstr = Gen.eh_except_block_stack[-1][3] + Gen._store(exc_val, exception_code) + if exc_msg: + Gen._store(exc_msg, eh_message) + else: + null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + Gen._store(null_ptr, eh_message) + if Gen.func and ExceptBB and ExceptBB.function is Gen.func: + Gen.builder.branch(ExceptBB) + else: + eh_msg_arg: ir.Argument | None = None + eh_code_arg: ir.Argument | None = None + if Gen.func: + for arg in Gen.func.args: + if arg.name == '__eh_msg_out__': + eh_msg_arg = arg + elif arg.name == '__eh_code_out__': + eh_code_arg = arg + if eh_msg_arg is not None: + if exc_msg: + Gen.builder.store(exc_msg, eh_msg_arg) + else: + null_ptr = ir.Constant(ir.PointerType(ir.IntType(8)), None) + Gen.builder.store(null_ptr, eh_msg_arg) + if eh_code_arg is not None: + Gen.builder.store(exc_val, eh_code_arg) + if Gen.func and Gen.func.type.pointee.return_type == ir.IntType(32): + Gen.builder.ret(ir.Constant(ir.IntType(32), 1)) + else: + Gen.builder.ret_void() + else: + eh_msg_arg: ir.Argument | None = None + eh_code_arg: ir.Argument | None = None + if Gen.func: + for arg in Gen.func.args: + if arg.name == '__eh_msg_out__': + eh_msg_arg = arg + elif arg.name == '__eh_code_out__': + eh_code_arg = arg + if eh_msg_arg is not None: + if exc_msg: + Gen.builder.store(exc_msg, eh_msg_arg) + else: + null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + Gen.builder.store(null_ptr, eh_msg_arg) + if eh_code_arg is not None: + Gen.builder.store(exc_val, eh_code_arg) + if Gen.func and Gen.func.type.pointee.return_type == ir.IntType(32): + Gen.builder.ret(ir.Constant(ir.IntType(32), 1)) + else: + Gen.builder.ret_void() \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesSpecialCall.py b/App/lib/core/Handles/HandlesSpecialCall.py new file mode 100644 index 0000000..61ea1de --- /dev/null +++ b/App/lib/core/Handles/HandlesSpecialCall.py @@ -0,0 +1,467 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +import ast +import llvmlite.ir as ir +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin +from lib.core.Handles.HandlesBase import BaseHandle, BuiltinTypeMap +from lib.includes.t import CTypeRegistry +from lib.core.SymbolUtils import IsTModule +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode + + +class CSpecialCallHandle(BaseHandle): + def _HandleCIfLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if Node.args: + ArgVal: ir.Value | None = self.HandleExprLlvm(Node.args[0]) + if ArgVal: + if isinstance(ArgVal, ir.Constant) and isinstance(ArgVal.type, ir.IntType): + return ir.Constant(ir.IntType(1), 1 if ArgVal.constant != 0 else 0) + if isinstance(ArgVal.type, ir.IntType): + return Gen.builder.icmp_signed('!=', ArgVal, Gen._ZeroConst(ArgVal.type), name="cif_cond") + if isinstance(ArgVal.type, ir.PointerType): + return Gen.builder.icmp_signed('!=', ArgVal, Gen._ZeroConst(ArgVal.type), name="cif_cond") + return ir.Constant(ir.IntType(1), 0) + def _HandleCAddrLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not Node.args: + return ir.Constant(ir.IntType(8).as_pointer(), None) + Arg = Node.args[0] + if isinstance(Arg, ast.Call): + ClassName = None + if isinstance(Arg.func, ast.Name): + ClassName = Arg.func.id + elif isinstance(Arg.func, ast.Attribute): + ClassName = Arg.func.attr + if ClassName and ClassName in Gen.structs: + StructType = Gen.structs[ClassName] + if isinstance(StructType, ir.PointerType): + StructType = StructType.pointee + ObjPtr = Gen._allocaEntry(StructType, name=f"{ClassName}_obj") + zero_val = Gen._zero_value_for_type(StructType) + if zero_val is not None: + Gen.builder.store(zero_val, ObjPtr) + ConstructorName = f"{ClassName}.__init__" + if ConstructorName in Gen.functions: + func = Gen.functions[ConstructorName] + InitArgs = [ObjPtr] + for carg in Arg.args: + ArgVal = self.HandleExprLlvm(carg) + if ArgVal: + InitArgs.append(ArgVal) + for kw in Arg.keywords: + ArgVal = self.HandleExprLlvm(kw.value) + if ArgVal: + InitArgs.append(ArgVal) + if hasattr(self.Trans.ExprCallHandle, '_append_eh_msg_out_arg'): + self.Trans.ExprCallHandle._append_eh_msg_out_arg(InitArgs, func, Gen) + adjusted = Gen._adjust_args(InitArgs, func) + Gen.builder.call(func, adjusted, name=f"{ClassName}_construct") + return Gen.builder.bitcast(ObjPtr, ir.IntType(8).as_pointer(), name=f"{ClassName}_as_i8ptr") + if isinstance(Arg, ast.Subscript): + SubPtr = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Arg) + if SubPtr and isinstance(SubPtr.type, ir.PointerType): + return Gen.builder.bitcast(SubPtr, ir.IntType(8).as_pointer(), name="addr_subscript_as_i8ptr") + if isinstance(Arg, ast.Attribute): + AttrPtr = self.Trans.ExprAttrHandle._get_attr_ptr(Arg) + if AttrPtr and isinstance(AttrPtr.type, ir.PointerType): + return Gen.builder.bitcast(AttrPtr, ir.IntType(8).as_pointer(), name="addr_attr_as_i8ptr") + if isinstance(Arg, ast.Name): + VarName = Arg.id + if VarName in Gen.functions: + func = Gen.functions[VarName] + return Gen.builder.bitcast(func, ir.IntType(8).as_pointer(), name="addr_func_as_i8ptr") + if VarName in Gen.variables and Gen.variables[VarName] is not None: + var_ptr = Gen.variables[VarName] + if isinstance(var_ptr.type, ir.PointerType): + # For pointer-to-pointer allocas (var_ptr is T**): + # - If T is a named struct (@t.Object class), the variable semantically + # IS the struct, so c.Addr should return the struct pointer value. + # - If T is a primitive pointer type (e.g., i8*), the variable semantically + # holds a pointer, so c.Addr should return &var (alloca address). + if isinstance(var_ptr.type.pointee, ir.PointerType): + pointee = var_ptr.type.pointee.pointee + if isinstance(pointee, ir.IdentifiedStructType): + # @t.Object class variable — return Loaded struct pointer + ArgVal = self.HandleExprLlvm(Arg) + if ArgVal and isinstance(ArgVal.type, ir.PointerType): + return Gen.builder.bitcast(ArgVal, ir.IntType(8).as_pointer(), name="addr_as_i8ptr") + # Pointer-type variable — return alloca address (&var) + return Gen.builder.bitcast(var_ptr, ir.IntType(8).as_pointer(), name="addr_as_i8ptr") + return Gen.builder.bitcast(var_ptr, ir.IntType(8).as_pointer(), name="addr_as_i8ptr") + ArgVal = self.HandleExprLlvm(Arg) + if ArgVal: + if isinstance(ArgVal.type, ir.IntType): + alloca = Gen._alloca(ArgVal.type, name="addr_tmp") + Gen._store(ArgVal, alloca) + return Gen.builder.bitcast(alloca, ir.IntType(8).as_pointer(), name="addr_as_i8ptr") + elif isinstance(ArgVal.type, ir.PointerType): + return Gen.builder.bitcast(ArgVal, ir.IntType(8).as_pointer(), name="addr_bitcast") + return ir.Constant(ir.IntType(8).as_pointer(), None) + + def _HandleCSetLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if len(Node.args) >= 2: + target_arg: ast.expr = Node.args[0] + value_arg: ast.expr = Node.args[1] + target_ptr: ir.Value | None = None + if isinstance(target_arg, ast.Call) and isinstance(target_arg.func, ast.Attribute): + if isinstance(target_arg.func.value, ast.Name) and target_arg.func.value.id == 'c': + if target_arg.func.attr == 'Deref' and target_arg.args: + deref_arg = target_arg.args[0] + if isinstance(deref_arg, ast.Name) and deref_arg.id in Gen.variables and Gen.variables[deref_arg.id] is not None: + target_ptr = Gen._load(Gen.variables[deref_arg.id], name="deref_load_ptr") + else: + inner_value = self.HandleExprLlvm(deref_arg) + if inner_value: + if isinstance(inner_value.type, ir.PointerType): + if isinstance(inner_value.type.pointee, ir.PointerType): + inner_value = Gen._load(inner_value, name="cast_deref") + target_ptr = inner_value + elif target_arg.func.attr == 'Addr' and target_arg.args: + target_ptr = self.HandleExprLlvm(target_arg) + elif isinstance(target_arg.func.value, ast.Name) and IsTModule(target_arg.func.value.id, self.Trans.SymbolTable): + type_attr = target_arg.func.attr + resolved_cname = self.Trans.TSpecialCallHandle._ResolveTAttrToCName(type_attr) + if target_arg.args and resolved_cname is not None: + inner_val = self.HandleExprLlvm(target_arg.args[0]) + if inner_val and isinstance(inner_val.type, ir.PointerType): + target_llvm_type = Gen._basic_type_to_llvm(resolved_cname) + if target_llvm_type: + target_ptr = Gen.builder.bitcast(inner_val, ir.PointerType(target_llvm_type), name="tcast_ptr") + if not target_ptr: + if isinstance(target_arg, ast.Subscript): + target_ptr = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(target_arg) + else: + target_ptr = self.HandleExprLlvm(target_arg) + Val = self.HandleExprLlvm(value_arg) + if target_ptr and Val: + if isinstance(target_ptr.type, ir.PointerType): + ValToStore = Val + pointee = target_ptr.type.pointee + if isinstance(pointee, ir.PointerType) and isinstance(pointee.pointee, ir.IntType) and pointee.pointee.width == 8: + if isinstance(Val.type, ir.PointerType) and isinstance(Val.type.pointee, ir.IntType) and Val.type.pointee.width == 8: + ValToStore = Val + elif isinstance(Val.type, ir.IntType): + ValToStore = Gen.builder.inttoptr(Val, pointee, name="int_to_ptr_set") + elif isinstance(pointee, ir.IntType): + if isinstance(Val.type, ir.ArrayType) and isinstance(Val.type.element, ir.IntType) and Val.type.element.width == 8: + zero = ir.Constant(ir.IntType(32), 0) + value_ptr = Gen.builder.gep(Val, [zero, zero], name="value_ptr") + ValToStore = Gen._load(value_ptr, name="Load_value") + elif isinstance(Val.type, ir.PointerType) and isinstance(Val.type.pointee, ir.IntType) and Val.type.pointee.width == 8: + ValToStore = Gen._load(Val, name="Load_char_value") + if isinstance(ValToStore.type, ir.IntType) and isinstance(pointee, ir.IntType): + if ValToStore.type.width < pointee.width: + ValToStore = Gen.builder.zext(ValToStore, pointee, name="zext_set") + elif ValToStore.type.width > pointee.width: + ValToStore = Gen.builder.trunc(ValToStore, pointee, name="trunc_set") + elif Val.type != pointee: + if isinstance(pointee, ir.PointerType) and isinstance(Val.type, ir.PointerType): + ValToStore = Gen.builder.bitcast(Val, pointee, name="bitcast_set") + Gen._store(ValToStore, target_ptr) + return ir.Constant(ir.IntType(32), 1) + + def _HandleCLoadLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if len(Node.args) < 2: + return ir.Constant(ir.IntType(32), 0) + # c.Load(a, b) 语义: *a = *b(a 是目标,b 是源) + # wiki 文档: c.Load(ptr, value) -> *ptr = *value; + dst_val: ir.Value | None = self.HandleExprLlvm(Node.args[0]) + src_val: ir.Value | None = self.HandleExprLlvm(Node.args[1]) + if not src_val or not dst_val: + return ir.Constant(ir.IntType(32), 0) + if not isinstance(src_val.type, ir.PointerType) or not isinstance(dst_val.type, ir.PointerType): + return ir.Constant(ir.IntType(32), 0) + pointee = dst_val.type.pointee + # c.Addr 统一返回 i8* 通用指针,丢失原始类型信息。 + # 若 dst 是 c.Addr(var),从 var 的 alloca 类型推断真实 pointee(如 i64), + # 否则 c.Load 只会按 i8 加载 1 字节(IEEE 754 double→u64 位重解释 bug)。 + if (isinstance(pointee, ir.IntType) and pointee.width == 8 + and isinstance(Node.args[0], ast.Call) + and isinstance(Node.args[0].func, ast.Attribute) + and Node.args[0].func.attr == 'Addr' + and Node.args[0].args + and isinstance(Node.args[0].args[0], ast.Name)): + var_name = Node.args[0].args[0].id + if var_name in Gen.variables and Gen.variables[var_name] is not None: + var_ptr = Gen.variables[var_name] + if isinstance(var_ptr.type, ir.PointerType): + pointee = var_ptr.type.pointee + # 按 pointee 类型 bitcast src 加载,bitcast dst 存储 + if src_val.type.pointee != pointee: + src_cast = Gen.builder.bitcast(src_val, ir.PointerType(pointee), name="cLoad_src_cast") + else: + src_cast = src_val + Loaded = Gen._load(src_cast, name="cLoad_loaded") + if Loaded is None: + return ir.Constant(ir.IntType(32), 0) + if dst_val.type.pointee != pointee: + dst_cast = Gen.builder.bitcast(dst_val, ir.PointerType(pointee), name="cLoad_dst_cast") + else: + dst_cast = dst_val + Gen._store(Loaded, dst_cast) + return ir.Constant(ir.IntType(32), 1) + + def _HandleCDerefLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not Node.args: + return ir.Constant(ir.IntType(64), 0) + # 检测嵌套 c.Deref(c.Deref(...)):设置标志让内层 Deref 加载为指针 + IsNestedDeref: bool = ( + isinstance(Node.args[0], ast.Call) + and isinstance(Node.args[0].func, ast.Attribute) + and Node.args[0].func.attr == 'Deref' + ) + if IsNestedDeref: + Gen._nested_deref_load_ptr = True + try: + arg_val: ir.Value | None = self.HandleExprLlvm(Node.args[0]) + finally: + if IsNestedDeref: + Gen._nested_deref_load_ptr = False + if arg_val is None: + return ir.Constant(ir.IntType(32), 0) + if isinstance(arg_val.type, ir.PointerType): + pointee: ir.Type = arg_val.type.pointee + # 嵌套 Deref 的内层:加载为指针 (i8*),不推断目标类型 + # 因为结果会被外层 Deref 再次解引用 + NestedLoadPtr: bool = getattr(Gen, '_nested_deref_load_ptr', False) + if NestedLoadPtr: + PtrType: ir.Type = ir.PointerType(ir.IntType(8)) + casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(PtrType), name="deref_cast") + Loaded = Gen._load(casted, name="deref") + if Loaded is not None: + return Loaded + # void* (i8*) 需要根据赋值目标类型推断加载类型 + elif isinstance(pointee, ir.IntType) and pointee.width == 8: + target_type: ir.Type | None = self._infer_deref_target_type(Gen, Node) + if target_type is not None and not isinstance(target_type, ir.IntType): + # bitcast i8* -> target_type* then load + casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(target_type), name="deref_cast") + Loaded = Gen._load(casted, name="deref") + if Loaded is not None: + return Loaded + elif target_type is not None and isinstance(target_type, ir.IntType): + # 目标是整数类型 (如 i32/i64): bitcast 并加载 + if target_type.width != 8: + casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(target_type), name="deref_cast") + Loaded = Gen._load(casted, name="deref") + if Loaded is not None: + return Loaded + Loaded = Gen._load(arg_val, name="deref") + if Loaded is not None: + return Loaded + return arg_val + + def _infer_deref_target_type(self, Gen: LlvmGeneratorMixin, DerefNode: ast.Call | None = None) -> ir.Type | None: + """从当前赋值上下文推断 c.Deref 的目标加载类型。 + 当用户写 v: int = c.Deref(ptr) 时,从赋值注解推断目标类型。 + 注意:仅当 c.Deref 是赋值的直接 RHS 时才推断;若 c.Deref 嵌套在 + ifexp/compare/binop 中(如 `a = 1 if c.Deref(p) == 65 else 0`), + 不应推断,否则会把 i8* 错误 bitcast 为 i32* 加载 4 字节。""" + assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None) + if not assign_node: + return None + # 仅当 c.Deref 是赋值的直接 RHS 时才推断类型 + if DerefNode is not None and getattr(assign_node, 'value', None) is not DerefNode: + return None + ann: ast.AST | None = getattr(assign_node, 'annotation', None) + if not ann: + return None + ann_name: str | None = None + if isinstance(ann, ast.Name): + ann_name = ann.id + elif isinstance(ann, ast.Attribute): + ann_name = ann.attr + elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr): + # 处理 int | t.CPtr 形式的联合类型注解 + for side in (ann.left, ann.right): + if isinstance(side, ast.Name): + ann_name = side.id + break + elif isinstance(side, ast.Attribute): + ann_name = side.attr + break + if not ann_name: + return None + # 使用 BuiltinTypeMap 统一查询(同时支持 Python 类名和字符串速记) + entry: tuple[type, int] | None = BuiltinTypeMap.Get(ann_name) + if entry is not None: + ctype_cls: type = entry[0] + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str: + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + return resolved + # 检查是否是已注册的结构体类型 + if ann_name in Gen.structs: + return Gen.structs[ann_name] + return None + + def _HandleCPtrToIntLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not Node.args: + return ir.Constant(ir.IntType(64), 0) + arg_val: ir.Value | None = self.HandleExprLlvm(Node.args[0]) + if arg_val is None: + return ir.Constant(ir.IntType(64), 0) + if isinstance(arg_val.type, ir.PointerType): + return Gen.builder.ptrtoint(arg_val, ir.IntType(64), name="ptrtoint_result") + if isinstance(arg_val.type, ir.IntType): + if arg_val.type.width < 64: + return Gen.builder.zext(arg_val, ir.IntType(64), name="zext_ptrtoint") + elif arg_val.type.width > 64: + return Gen.builder.trunc(arg_val, ir.IntType(64), name="trunc_ptrtoint") + return arg_val + return ir.Constant(ir.IntType(64), 0) + + +class TSpecialCallHandle(BaseHandle): + + @staticmethod + def _ResolveTAttrToCName(attr: str) -> str | None: + ctype_cls: type | None = CTypeRegistry.GetClassByName(attr) + if ctype_cls is not None: + try: + inst = ctype_cls() + cname = type(inst).__name__ + if cname: + return cname + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"处理函数调用失败: {_e}", "Exception") + return None + + def _HandleTSpecialCallLlvm(self, Node: ast.Call) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not Node.args: + return None + attr: str = Node.func.attr + + if attr == 'CType': + return self._HandleCTypeLlvm(Node) + + target_type: str | None = self._ResolveTAttrToCName(attr) + if target_type is not None: + IsPtr_cast = False + if len(Node.args) >= 2: + second_arg = Node.args[1] + if isinstance(second_arg, ast.Attribute) and isinstance(second_arg.value, ast.Name) and IsTModule(second_arg.value.id, self.Trans.SymbolTable) and second_arg.attr == 'CPtr': + IsPtr_cast = True + elif isinstance(second_arg, ast.Name) and second_arg.id == 'CPtr': + IsPtr_cast = True + if IsPtr_cast: + return self._HandleTPtrCastLlvm(Node, attr) + if target_type == 'void': + target_type = 'void *' + return self.Trans.ExprCallHandle._HandleTypeCastLlvm(Node.args[0], target_type) + return None + + def _HandleCTypeLlvm(self, Node: ast.Call) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + arg_val: ir.Value | None = self.HandleExprLlvm(Node.args[0]) + if not arg_val: + return None + target_type_str: str = 'unsigned long long' + if len(Node.args) >= 2: + type_parts: list[str] = [] + ptr_count: int = 0 + for i in range(1, len(Node.args)): + type_arg = Node.args[i] + if isinstance(type_arg, ast.Attribute) and isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable): + type_attr = type_arg.attr + _PREFIX_MAP: dict[str, str] = { + 'CUnsigned': 'unsigned', 'CSigned': 'signed', + 'CConst': 'const', 'CVolatile': 'volatile', + } + if type_attr in _PREFIX_MAP: + type_parts.append(_PREFIX_MAP[type_attr]) + elif type_attr in ('CPtr', 'CArrayPtr'): + ptr_count += 1 + else: + resolved = self._ResolveTAttrToCName(type_attr) + if resolved is not None: + type_parts.append(resolved) + if type_parts: + target_type_str = ' '.join(type_parts) + if ptr_count > 0: + target_type_str += ' ' + '*' * ptr_count + if isinstance(arg_val.type, ir.PointerType): + int_val = Gen.builder.ptrtoint(arg_val, ir.IntType(64), name="ptr_to_int") + return int_val + return self.Trans.ExprCallHandle._HandleTypeCastLlvm(Node.args[0], target_type_str) + + def _HandleTPtrCastLlvm(self, Node: ast.Call, attr: str) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if attr in ('CChar', 'CUnsignedChar'): + first_arg = Node.args[0] + if isinstance(first_arg, ast.Call) and isinstance(first_arg.func, ast.Attribute) and isinstance(first_arg.func.value, ast.Name) and IsTModule(first_arg.func.value.id, self.Trans.SymbolTable) and first_arg.func.attr == 'CInt' and first_arg.args: + inner_val = self.HandleExprLlvm(first_arg.args[0]) + if isinstance(inner_val.type, ir.PointerType): + return Gen.builder.bitcast(inner_val, ir.PointerType(ir.IntType(8)), name=f"c{attr.lower()}_ptr_from_cint") + expr_val = self.HandleExprLlvm(Node.args[0]) + if isinstance(expr_val.type, ir.IntType) and expr_val.type.width == 8: + var = Gen._alloca(ir.ArrayType(ir.IntType(8), 2), name=f"c{attr.lower()}_ptr") + zero = ir.Constant(ir.IntType(32), 0) + char_ptr = Gen.builder.gep(var, [zero, zero], name="char_ptr") + Gen._store(expr_val, char_ptr) + null_char = ir.Constant(ir.IntType(8), 0) + null_ptr = Gen.builder.gep(var, [zero, ir.Constant(ir.IntType(32), 1)], name="null_ptr") + Gen._store(null_char, null_ptr) + return char_ptr + elif isinstance(expr_val.type, ir.PointerType): + if isinstance(expr_val.type.pointee, ir.PointerType): + Loaded_ptr = Gen._load(expr_val, name="load_ptr") + if isinstance(Loaded_ptr.type, ir.PointerType) and isinstance(Loaded_ptr.type.pointee, ir.IntType) and Loaded_ptr.type.pointee.width == 8: + return Loaded_ptr + if isinstance(expr_val.type.pointee, ir.IntType) and expr_val.type.pointee.width == 8: + return expr_val + return Gen.builder.bitcast(expr_val, ir.PointerType(ir.IntType(8)), name="cast_to_char_ptr") + elif isinstance(expr_val.type, ir.IntType): + if expr_val.type.width == 32: + expr_val = Gen.builder.zext(expr_val, ir.IntType(64), name="zext_to_64") + return Gen.builder.inttoptr(expr_val, ir.PointerType(ir.IntType(8)), name=f"inttoptr_c{attr.lower()}") + return Gen.emit_constant(0, 'int') + elif attr == 'CVoid': + return self.Trans.ExprCallHandle._HandleTypeCastLlvm(Node.args[0], 'void *') + elif attr == 'CInt': + expr_val = self.HandleExprLlvm(Node.args[0]) + if expr_val: + if isinstance(expr_val.type, ir.IntType): + if expr_val.type.width < 64: + expr_val = Gen.builder.zext(expr_val, ir.IntType(64), name="zext_to_64") + return Gen.builder.inttoptr(expr_val, ir.PointerType(ir.IntType(32)), name="cint_ptr") + return Gen.builder.bitcast(expr_val, ir.PointerType(ir.IntType(32)), name="cint_ptr") + else: + ctype_cls = CTypeRegistry.GetClassByName(attr) + if ctype_cls is not None: + llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) + target_type = Gen._type_str_to_llvm(llvm_str) + if target_type: + # 基础类型 (int/float) → 加一层指针 → T* + if isinstance(target_type, (ir.IntType, ir.FloatType, ir.DoubleType)): + target_ptr_type = ir.PointerType(target_type) + expr_val = self.HandleExprLlvm(Node.args[0]) + if expr_val: + if isinstance(expr_val.type, ir.IntType): + if expr_val.type.width < 64: + expr_val = Gen.builder.zext(expr_val, ir.IntType(64), name="zext_to_64") + return Gen.builder.inttoptr(expr_val, target_ptr_type, name=f"{attr.lower()}_ptr") + return Gen.builder.bitcast(expr_val, target_ptr_type, name=f"{attr.lower()}_ptr") + # 指针类型 (如 CPtr → i8*) → 再加一层指针 → i8** + elif isinstance(target_type, ir.PointerType): + target_ptr_type = ir.PointerType(target_type) + expr_val = self.HandleExprLlvm(Node.args[0]) + if expr_val: + if isinstance(expr_val.type, ir.IntType): + if expr_val.type.width < 64: + expr_val = Gen.builder.zext(expr_val, ir.IntType(64), name="zext_to_64") + return Gen.builder.inttoptr(expr_val, target_ptr_type, name=f"{attr.lower()}_ptr") + return Gen.builder.bitcast(expr_val, target_ptr_type, name=f"{attr.lower()}_ptr") + return None diff --git a/App/lib/core/Handles/HandlesTranslator.py b/App/lib/core/Handles/HandlesTranslator.py index 87570c7..8f57c89 100644 --- a/App/lib/core/Handles/HandlesTranslator.py +++ b/App/lib/core/Handles/HandlesTranslator.py @@ -206,11 +206,19 @@ class Translator: pool: memhub.MemBuddy | t.CPtr = _mbuddy + stdio.printf("[TR] step1 _init_state 前\n") + stdio.fflush(0) # 初始化状态 self._init_state(pool) + stdio.printf("[TR] step1 _init_state 后\n") + stdio.fflush(0) # 创建 LLVM 模块 + stdio.printf("[TR] step2 new_module 前\n") + stdio.fflush(0) mod: llvmlite.LLVMModule | t.CPtr = llvmlite.new_module(pool, "main") + stdio.printf("[TR] step2 new_module 后=%d\n", mod) + stdio.fflush(0) if mod is None: stdio.printf("[TR] NewModule returned NULL\n") return 1 @@ -220,7 +228,11 @@ class Translator: triple: str = Config.TargetTriple if triple is None: triple = "x86_64-pc-windows-msvc" + stdio.printf("[TR] step3 module_set_target 前\n") + stdio.fflush(0) llvmlite.module_set_target(mod, triple) + stdio.printf("[TR] step3 module_set_target 后\n") + stdio.fflush(0) # 类型 i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) @@ -228,8 +240,12 @@ class Translator: i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) # 声明 printf + stdio.printf("[TR] step4 create_declare printf 前\n") + stdio.fflush(0) printf_func: llvmlite.Function | t.CPtr = llvmlite.create_declare( pool, mod, "printf", i32_ty) + stdio.printf("[TR] step4 create_declare printf 后=%d\n", printf_func) + stdio.fflush(0) if printf_func is None: stdio.printf("[TR] CreateDeclare printf returned NULL\n") return 1 @@ -237,13 +253,19 @@ class Translator: printf_func.IsVarArg = 1 # 声明 malloc(闭包分配用) + stdio.printf("[TR] step5 create_declare malloc 前\n") + stdio.fflush(0) malloc_func: llvmlite.Function | t.CPtr = llvmlite.create_declare( pool, mod, "malloc", i8_ptr_ty) + stdio.printf("[TR] step5 create_declare malloc 后\n") + stdio.fflush(0) if malloc_func is not None: i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) llvmlite.add_param(pool, malloc_func, i64_ty, "size") # 检查用户是否定义了 main 函数 + stdio.printf("[TR] step6 检查 user main 前\n") + stdio.fflush(0) has_user_main: int = 0 ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children if ch is not None: @@ -256,7 +278,11 @@ class Translator: if string.strcmp(fd.name, "main") == 0: has_user_main = 1 break + stdio.printf("[TR] step6 检查 user main 后=%d\n", has_user_main) + stdio.fflush(0) + stdio.printf("[TR] step7 _translate_module_level 前\n") + stdio.fflush(0) if self._declare_only != 0: # Phase 1a-pre(2=import扫描) / Phase 1a(1=struct注册): 只处理模块级,不创建 main 函数和 builder self._translate_module_level(pool, mod, tree) @@ -268,6 +294,8 @@ class Translator: else: # 用户已定义 main → 委托 HandlesMain 翻译模块级 self._translate_module_level(pool, mod, tree) + stdio.printf("[TR] step7 _translate_module_level 后\n") + stdio.fflush(0) return 0 @@ -290,6 +318,8 @@ class Translator: """委托 HandlesMain.translate_children() 翻译模块级语句(trans 单参)""" # 全量翻译模式下,先处理导入语句,再创建前向声明,解决前向引用问题 if self._declare_only == 0: + stdio.printf("[TR._translate_module_level] _declare_only==0, 预处理 imports\n") + stdio.fflush(0) # 预处理导入语句,确保 _imported_modules 和 _from_imports 已填充 # (前向声明需要解析类型注解,如 t.CArray[str] 依赖 t 模块已导入) ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children @@ -305,9 +335,19 @@ class Translator: elif kd == ast.ASTKind.ImportFrom: self.ImportsH.HandleImportFromModule(child) self.ImportsH.HandleImportFromNames(child) + stdio.printf("[TR._translate_module_level] 预处理 imports 完成\n") + stdio.fflush(0) # 创建前向声明 + stdio.printf("[TR._translate_module_level] forward_declare_functions 前\n") + stdio.fflush(0) HandlesFunctions.forward_declare_functions(self, tree) + stdio.printf("[TR._translate_module_level] forward_declare_functions 后\n") + stdio.fflush(0) + stdio.printf("[TR._translate_module_level] translate_children 前\n") + stdio.fflush(0) added: int = HandlesMain.translate_children(self, tree) + stdio.printf("[TR._translate_module_level] translate_children 后=%d\n", added) + stdio.fflush(0) # ============================================================ # IR 输出 diff --git a/App/lib/core/Handles/HandlesTry.py b/App/lib/core/Handles/HandlesTry.py new file mode 100644 index 0000000..1fd04e7 --- /dev/null +++ b/App/lib/core/Handles/HandlesTry.py @@ -0,0 +1,214 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator +from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP +import ast +import llvmlite.ir as ir + + +class TryHandle(BaseHandle): + def _get_exception_type_code(self, type_node: ast.expr) -> int: + if isinstance(type_node, ast.Name): + name: str = type_node.id + if name in EXCEPTION_CODE_MAP: + return EXCEPTION_CODE_MAP[name] + if name in self.Trans.exception_registry: + return self.Trans.exception_registry[name] + return 1 + if isinstance(type_node, ast.Attribute): + name: str = type_node.attr + if name in EXCEPTION_CODE_MAP: + return EXCEPTION_CODE_MAP[name] + if name in self.Trans.exception_registry: + return self.Trans.exception_registry[name] + return 1 + if isinstance(type_node, ast.Tuple): + for elt in type_node.elts: + code: int = self._get_exception_type_code(elt) + if code != 1: + return code + return 1 + return 1 + + def _get_exception_name(self, type_node: ast.expr) -> str | None: + if isinstance(type_node, ast.Name): + return type_node.id + if isinstance(type_node, ast.Attribute): + return type_node.attr + return None + + def _get_all_match_codes(self, type_node: ast.expr) -> list[int]: + name: str | None = self._get_exception_name(type_node) + if name is None: + return [self._get_exception_type_code(type_node)] + if name not in self.Trans.exception_registry: + return [self._get_exception_type_code(type_node)] + codes: list[int] = [self.Trans.exception_registry[name]] + self._collect_descendant_codes(name, codes) + return codes + + def _collect_descendant_codes(self, parent_name: str, codes: list[int]) -> None: + for child_name, parent in self.Trans.exception_parents.items(): + if parent == parent_name: + child_code: int | None = self.Trans.exception_registry.get(child_name) + if child_code is not None and child_code not in codes: + codes.append(child_code) + self._collect_descendant_codes(child_name, codes) + + def _HandleTryLlvm(self, Node: ast.Try) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + if not Gen.func: + return + exception_code: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name="eh_code") + eh_message: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name="eh_message") + Gen._store(ir.Constant(ir.IntType(32), 0), exception_code) + Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), eh_message) + HasFinally: bool = bool(Node.finalbody) + if HasFinally: + Gen.eh_finally_stack.append((None, None, None)) + else: + Gen.eh_finally_stack.append(None) + DispatchBB: ir.Block = Gen.func.append_basic_block(name="try.dispatch") + AfterBB: ir.Block = Gen.func.append_basic_block(name="try.after") + except_blocks: list[tuple[ir.Block, list[int], ast.ExceptHandler]] = [] + for i, handler in enumerate(Node.handlers): + ExcTypeCodes: list[int] = [1] + if handler.type: + ExcTypeCodes = self._get_all_match_codes(handler.type) + ExceptBB: ir.Block = Gen.func.append_basic_block(name=f"try.except_{i}") + Gen.eh_except_block_stack.append((DispatchBB, None, exception_code, eh_message)) + except_blocks.append((ExceptBB, ExcTypeCodes, handler)) + TryBB: ir.Block = Gen.func.append_basic_block(name="try.body") + ElseBB: ir.Block | None = None + if Node.orelse: + ElseBB = Gen.func.append_basic_block(name="try.else") + Gen.builder.branch(TryBB) + Gen.builder.position_at_start(TryBB) + self.HandleBodyLlvm(Node.body) + if not Gen.builder.block.is_terminated: + Gen._store(ir.Constant(ir.IntType(32), 0), exception_code) + if ElseBB: + Gen.builder.branch(ElseBB) + else: + Gen.builder.branch(AfterBB) + Gen.builder.position_at_start(DispatchBB) + eh_code_val: ir.Value = Gen._load(exception_code, name="Load_eh_code") + next_check_bb: ir.Block | None = None + handler_var_map: dict[int, tuple[str, ir.AllocaInstr, ir.Value | None]] = {} + UnhandledBB: ir.Block = Gen.func.append_basic_block(name="try.unhandled") + for i, (ExceptBB, ExcTypeCodes, handler) in enumerate(except_blocks): + MatchBB: ir.Block = Gen.func.append_basic_block(name=f"try.match_{i}") + if i > 0 and next_check_bb is not None: + Gen.builder.position_at_start(next_check_bb) + eh_code_val = Gen._load(exception_code, name=f"Load_eh_code_{i}") + if handler.type is None: + Gen.builder.branch(MatchBB) + else: + match_cond: ir.Value | None = None + for code in ExcTypeCodes: + is_code_match: ir.Value = Gen.builder.icmp_signed('==', eh_code_val, ir.Constant(ir.IntType(32), code), name=f"eh_match_{i}_c{code}") + if match_cond is None: + match_cond = is_code_match + else: + match_cond = Gen.builder.or_(match_cond, is_code_match, name=f"eh_match_any_{i}") + if i < len(except_blocks) - 1: + next_check_bb = Gen.func.append_basic_block(name=f"try.check_{i}") + else: + next_check_bb = UnhandledBB + Gen.builder.cbranch(match_cond, MatchBB, next_check_bb) + Gen.builder.position_at_start(MatchBB) + if handler.name: + var_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(8).as_pointer(), name=handler.name) + eh_msg_val: ir.Value = Gen._load(eh_message, name=f"Load_eh_msg_{handler.name}") + Gen._store(eh_msg_val, var_alloca) + handler_var_map[i] = (handler.name, var_alloca, Gen.variables.get(handler.name)) + Gen.builder.branch(ExceptBB) + Gen.builder.position_at_start(UnhandledBB) + eh_msg_out_arg: ir.Argument | None = None + eh_code_out_arg: ir.Argument | None = None + if Gen.func and len(Gen.func.args) > 0: + for arg in Gen.func.args: + if arg.name == '__eh_msg_out__': + eh_msg_out_arg = arg + elif arg.name == '__eh_code_out__': + eh_code_out_arg = arg + if eh_msg_out_arg is not None and eh_code_out_arg is not None: + eh_msg_val: ir.Value = Gen._load(eh_message, name="Load_eh_msg_propagate") + eh_code_val_prop: ir.Value = Gen._load(exception_code, name="Load_eh_code_propagate") + Gen._store(eh_msg_val, eh_msg_out_arg) + Gen._store(eh_code_val_prop, eh_code_out_arg) + ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None + if isinstance(ret_type, ir.IdentifiedStructType): + if ret_type.elements: + zero_val: ir.Constant = ir.Constant(ret_type, [ + ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) + else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) + else ir.Constant(et, ir.Undefined) for et in ret_type.elements]) + else: + zero_val = ir.Constant(ret_type, None) + Gen.builder.ret(zero_val) + elif isinstance(ret_type, ir.PointerType): + Gen.builder.ret(ir.Constant(ret_type, None)) + elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)): + Gen.builder.ret(ir.Constant(ret_type, 0.0)) + elif isinstance(ret_type, ir.IntType): + Gen.builder.ret(ir.Constant(ret_type, 1)) + elif isinstance(ret_type, ir.VoidType): + Gen.builder.ret_void() + else: + Gen.builder.ret(ir.Constant(ir.IntType(32), 1)) + else: + Gen._store(ir.Constant(ir.IntType(32), 0), exception_code) + Gen.builder.branch(AfterBB) + for idx, (ExceptBB, ExcTypeCodes, handler) in enumerate(except_blocks): + Gen.builder.position_at_start(ExceptBB) + old_var: ir.Value | None = None + if idx in handler_var_map: + hname: str = handler_var_map[idx][0] + var_alloca: ir.AllocaInstr = handler_var_map[idx][1] + old_var = handler_var_map[idx][2] + Gen.variables[hname] = var_alloca + self.HandleBodyLlvm(handler.body) + if idx in handler_var_map: + hname = handler_var_map[idx][0] + old_var = handler_var_map[idx][2] + if old_var is not None: + Gen.variables[hname] = old_var + elif hname in Gen.variables: + del Gen.variables[hname] + if not Gen.builder.block.is_terminated: + Gen.builder.branch(AfterBB) + Gen.eh_except_block_stack.pop() + if ElseBB: + Gen.builder.position_at_start(ElseBB) + self.HandleBodyLlvm(Node.orelse) + if not Gen.builder.block.is_terminated: + Gen.builder.branch(AfterBB) + Gen.builder.position_at_start(AfterBB) + if HasFinally: + Gen.eh_finally_stack.pop() + if Node.finalbody: + FinallyBB: ir.Block = Gen.func.append_basic_block(name="try.finally") + pending_ret_flag: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name="pending_ret_flag") + pending_ret_val: ir.AllocaInstr | None = Gen._allocaEntry(ir.IntType(32), name="pending_ret_val") if Gen.func.type.pointee.return_type == ir.IntType(32) else None + Gen.eh_finally_stack.append((FinallyBB, pending_ret_flag, pending_ret_val)) + Gen.builder.branch(FinallyBB) + Gen.builder.position_at_start(FinallyBB) + Gen._store(ir.Constant(ir.IntType(32), 0), pending_ret_flag) + self.HandleBodyLlvm(Node.finalbody) + if Gen.eh_finally_stack: + _, pending_ret_flag, pending_ret_val = Gen.eh_finally_stack[-1] + Gen.eh_finally_stack.pop() + ret_flag_val: ir.Value = Gen._load(pending_ret_flag, name="Load_ret_flag") + ret_flag_is_set: ir.Value = Gen.builder.icmp_signed('!=', ret_flag_val, ir.Constant(ir.IntType(32), 0), name="check_ret_flag") + RetBB: ir.Block = Gen.func.append_basic_block(name="finally.ret") + ContinueBB: ir.Block = Gen.func.append_basic_block(name="try.continue") + Gen.builder.cbranch(ret_flag_is_set, RetBB, ContinueBB) + Gen.builder.position_at_start(RetBB) + if pending_ret_val: + ret_val: ir.Value = Gen._load(pending_ret_val, name="Load_ret_val") + Gen.builder.ret(ret_val) + else: + Gen.builder.ret_void() + Gen.builder.position_at_start(ContinueBB) \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesTypeMerge.py b/App/lib/core/Handles/HandlesTypeMerge.py new file mode 100644 index 0000000..4af492f --- /dev/null +++ b/App/lib/core/Handles/HandlesTypeMerge.py @@ -0,0 +1,976 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator +from typing import List +from lib.core.Handles.HandlesBase import BaseHandle, CTypeHelper, CTypeInfo +from lib.core.SymbolUtils import IsTModule, IsListAnnotation +from lib.includes import t +import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes) +import ast + + +class HandlesTypeMerge(BaseHandle): + """处理类型合并逻辑 - 所有类型分析的核心模块""" + + def _MakeVoidCTypeInfo(self) -> CTypeInfo: + Info = CTypeInfo() + Info.BaseType = t.CVoid + return Info + + @staticmethod + def _MakeCTypeInfoFromName(TypeName: str) -> CTypeInfo: + """直接从类型名构造 CTypeInfo,不经过 FromStr/字符串解析""" + return CTypeInfo.FromTypeName(TypeName) + + def GetCTypeInfo(self, Node: ast.AST) -> CTypeInfo: + """获取 CTypeInfo 对象""" + Result = self._GetCTypeInfoInternal(Node) + if isinstance(Result, CTypeInfo): + return Result + elif isinstance(Result, str): + return self._MakeCTypeInfoFromName(Result) + else: + return self._MakeVoidCTypeInfo() + + def _GetCTypeInfoInternal(self, Node: ast.AST) -> CTypeInfo | str | None: + """鍐呴儴鏂规硶锛岃繑鍥?CTypeInfo""" + LineNum = getattr(Node, 'lineno', None) + + self.Trans.DebugPrint(f"[GetCTypeInfo] Node type: {type(Node).__name__}, dump: {ast.dump(Node)[:60]}...") + + if isinstance(Node, ast.Constant): + TypeName = Node.value + return self._MakeCTypeInfoFromName(TypeName) + + if isinstance(Node, ast.Name): + TypeName = Node.id + t_c_imported = getattr(self.Trans, '_t_c_imported_names', {}) + if TypeName in t_c_imported: + src_module, src_name = t_c_imported[TypeName] + virtual_attr = ast.Attribute( + value=ast.Name(id=src_module, ctx=ast.Load()), + attr=src_name, + ctx=ast.Load() + ) + ast.copy_location(virtual_attr, Node) + return self.GetCTypeInfo(virtual_attr) + Entry = self.Trans.SymbolTable.lookup(TypeName) + if Entry and Entry.IsTypedef: + if isinstance(Entry, CTypeInfo) and Entry.BaseType: + if not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0: + Result = Entry.Copy() + Result.IsTypedef = True + Result.Name = TypeName + return Result + if Entry.OriginalType: + if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + elif isinstance(Entry.OriginalType, CTypeInfo): + Resolved = Entry.OriginalType.Copy() + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + elif isinstance(Entry.OriginalType, str): + # 字符串 OriginalType(如 'void *')需解析为 CTypeInfo, + # 否则返回的 Entry BaseType=None 会被下游误判为 i32 + Resolved = CTypeInfo.FromTypeName(Entry.OriginalType) + if Resolved and Resolved.BaseType: + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + return Entry + # REnum 优先于 IsEnum 处理(REnum 同时有 IsEnum=True 和 IsStruct=True) + # 必须设置 Name 和 PtrCount=1,否则 BinOp `LLVMType | t.CPtr` 合并后丢失 REnum 语义 + if isinstance(Entry, CTypeInfo) and Entry.IsRenum: + Result = Entry.Copy() + Result.Name = TypeName + Result.PtrCount = 1 # REnum 变量默认为指针(和 struct 一致) + return Result + # 类名与枚举成员名冲突时(如 Constant 既是 ASTKind.Constant 枚举成员又是结构体类名), + # 优先检查 Gen.structs:类型注解应解析为结构体而非枚举值 + _Gen = getattr(self.Trans, 'LlvmGen', None) + if _Gen and TypeName in _Gen.structs: + Result = CTypeInfo() + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + Result.Name = TypeName + Result.PtrCount = 1 + return Result + if isinstance(Entry, CTypeInfo) and Entry.IsEnum: + return Entry.Copy() + if isinstance(Entry, dict) and Entry.get('IsEnum'): + Result = CTypeInfo() + Result.BaseType = t.CEnum(TypeName) + Result.IsEnum = True + return Result + # 检测结构体类型 (CTypeInfo with IsStruct=True, 来自 _InsertClassSymbol) + if isinstance(Entry, CTypeInfo) and Entry.IsStruct: + Result = Entry.Copy() + if Result.BaseType is None: + Result.BaseType = t.CStruct(name=TypeName) + Result.Name = TypeName + Result.PtrCount = 1 # struct 变量默认为指针 + return Result + return self._MakeCTypeInfoFromName(TypeName) + + elif isinstance(Node, ast.Attribute): + ModuleParts = [] + Current = Node + while isinstance(Current, ast.Attribute): + ModuleParts.insert(0, Current.attr) + Current = Current.value + + if isinstance(Current, ast.Name): + ModuleParts.insert(0, Current.id) + + if len(ModuleParts) >= 2: + TypeName = ModuleParts[-1] + ModulePath = '.'.join(ModuleParts[:-1]) + elif len(ModuleParts) == 1: + TypeName = ModuleParts[0] + ModulePath = None + else: + return self._MakeVoidCTypeInfo() + + # 解析 import 别名: 如 import fat32_types as types -> types -> fat32_types + if ModulePath: + import_aliases = self.Trans.SymbolTable.import_aliases + if ModulePath in import_aliases: + ModulePath = import_aliases[ModulePath] + else: + # 也尝试在符号表中查找模块别名 + entry = self.Trans.SymbolTable.lookup(ModulePath) + if isinstance(entry, CTypeInfo) and entry.IsModuleAlias: + resolved = entry.ResolvedModule + if resolved: + ModulePath = resolved + + self.Trans.DebugPrint(f"[GetCTypeInfo-Attribute] 查询: '{TypeName}' ModulePath: {ModulePath}") + + if ModulePath == 't' and TypeName == 'State': + Info = CTypeInfo() + Info.BaseType = t.CVoid + Info.IsState = True + return Info + + if ModulePath == 't' or (ModulePath and ModulePath.startswith('t.')): + if self._IsTModuleType(TypeName): + TypeClass = CTypeHelper.GetTModuleCType(TypeName) or getattr(t, TypeName, None) + if TypeClass == t.CArrayPtr: + Info = CTypeInfo() + Info.IsArrayPtr = True + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CPtr: + Info = CTypeInfo() + Info.PtrCount = 1 + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CConst: + Info = CTypeInfo() + Info.DataConst = True + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CVolatile: + Info = CTypeInfo() + Info.DataVolatile = True + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CInline: + Info = CTypeInfo() + Info.Storage = t.CInline() + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CStatic: + Info = CTypeInfo() + Info.Storage = t.CStatic() + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CExtern: + Info = CTypeInfo() + Info.Storage = t.CExtern() + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CExport: + Info = CTypeInfo() + Info.Storage = t.CExport() + Info.BaseType = t.CVoid + return Info + elif TypeClass in (t.BigEndian, t.LittleEndian): + Info = CTypeInfo() + Info.BaseType = t.CInt() + Info.ByteOrder = 'big' if TypeClass == t.BigEndian else 'little' + return Info + elif TypeClass: + Info = CTypeInfo() + Info.BaseType = TypeClass + return Info + elif CTypeHelper.GetCName(TypeName): + return self._MakeCTypeInfoFromName(CTypeHelper.GetCName(TypeName)) + + FullName = f"{ModulePath}.{TypeName}" if ModulePath else TypeName + + # 优先检查 Gen.structs:类名与枚举成员名冲突时(如 Import 既是 ASTKind.Import + # 枚举成员又是 ast.Import 结构体类名),类型注解应解析为结构体而非枚举值 + # (与 ast.Name 路径 line 100-107 的逻辑一致) + _Gen = getattr(self.Trans, 'LlvmGen', None) + if _Gen and TypeName in _Gen.structs: + Result = CTypeInfo() + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + Result.Name = TypeName + Result.PtrCount = 1 + return Result + + Entry = self.Trans.SymbolTable.lookup(TypeName) + if Entry and Entry.IsTypedef: + if isinstance(Entry, CTypeInfo) and Entry.BaseType: + if not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0: + Result = Entry.Copy() + Result.IsTypedef = True + Result.Name = TypeName + return Result + if Entry.OriginalType: + if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + elif isinstance(Entry.OriginalType, CTypeInfo): + Resolved = Entry.OriginalType.Copy() + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + elif isinstance(Entry.OriginalType, str): + # 字符串 OriginalType(如 'void *')需解析为 CTypeInfo, + # 否则返回的 Result BaseType=_CTypedef 会被下游误判为 i32 + Resolved = CTypeInfo.FromTypeName(Entry.OriginalType) + if Resolved and Resolved.BaseType: + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + Result = CTypeInfo() + Result.BaseType = t._CTypedef(TypeName) + Result.IsTypedef = True + Result.Name = TypeName + return Result + if Entry: + # If BaseType is None but this is a class/struct, set BaseType to CStruct + Result = Entry.Copy() + if Result.BaseType is None: + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + # struct 变量默认为指针(与 ast.Name 路径一致,见 line 121) + # 同时确保 Name 字段被设置,供 ToString() 使用 + if Result.IsStruct: + if Result.PtrCount == 0: + Result.PtrCount = 1 + if not Result.Name: + Result.Name = TypeName + return Result + return CTypeInfo() + + elif isinstance(Node, ast.Call): + if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': + ReturnTypeInfo = self._GetCTypeInfoInternal(Node.args[0]) if Node.args else None + if isinstance(ReturnTypeInfo, CTypeInfo): + ReturnType = ReturnTypeInfo + else: + ReturnType = CTypeInfo() + ReturnType.BaseType = t.CVoid() + + ParamTypes = [] + ParamNames = [] + for kw in Node.keywords: + ParamTypeInfo = self._GetCTypeInfoInternal(kw.value) + if not isinstance(ParamTypeInfo, CTypeInfo): + ParamTypeInfo = self._MakeCTypeInfoFromName('int') + ParamTypes.append(ParamTypeInfo) + ParamNames.append(kw.arg) + + for arg in Node.args[1:]: + if isinstance(arg, ast.Tuple): + for elt in arg.elts: + ParamTypeInfo = self._GetCTypeInfoInternal(elt) + if not isinstance(ParamTypeInfo, CTypeInfo): + ParamTypeInfo = self._MakeCTypeInfoFromName('int') + ParamTypes.append(ParamTypeInfo) + ParamNames.append('') + else: + ParamTypeInfo = self._GetCTypeInfoInternal(arg) + if not isinstance(ParamTypeInfo, CTypeInfo): + ParamTypeInfo = self._MakeCTypeInfoFromName('int') + ParamTypes.append(ParamTypeInfo) + ParamNames.append('') + + if not ParamTypes: + ParamTypes.append(self._MakeCTypeInfoFromName('void')) + ParamNames.append('') + + Info = CTypeInfo() + Info.IsFuncPtr = True + Info.FuncPtrReturn = ReturnType + Info.FuncPtrParams = list(zip(ParamNames, ParamTypes)) + return Info + + if isinstance(Node.func, ast.Attribute) and isinstance(Node.func.value, ast.Name): + ModuleName = Node.func.value.id + TypeName = Node.func.attr + + if ModuleName == 't' and CTypeHelper.GetTModuleCType(TypeName) == t._CTypedef and Node.args: + TypeArg = Node.args[0] + if isinstance(TypeArg, ast.Name): + return TypeArg.id + elif isinstance(TypeArg, ast.Attribute): + AttrModule = TypeArg.value.id if isinstance(TypeArg.value, ast.Name) else None + AttrName = TypeArg.attr + if AttrModule == 't' and getattr(t, AttrName, None): + TypeObj = getattr(t, AttrName) + if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType): + return TypeObj.__name__ + return AttrName + elif isinstance(TypeArg, ast.Constant): + return str(TypeArg.value) + + if ModuleName in self.Trans._UserTypeModules: + module = self.Trans._UserTypeModules[ModuleName] + if getattr(module, TypeName, None): + TypeObj = getattr(module, TypeName) + if isinstance(TypeObj, type): + if issubclass(TypeObj, t.CType): + return TypeObj.__name__ + + if ModuleName == 't': + TypeObj = CTypeHelper.GetTModuleCType(TypeName) + if TypeObj is None: + TypeObj = getattr(t, TypeName, None) + if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType): + Result = CTypeInfo() + Result.BaseType = TypeObj + return Result + + if ModuleName == 't' and TypeName == 'State': + Result = CTypeInfo() + Result.IsState = True + return Result + + if ModuleName == 't' and TypeName == 'Bit': + if Node.args and isinstance(Node.args[0], ast.Constant): + BitWidth = Node.args[0].value + Result = CTypeInfo() + Result.IsBitField = True + Result.BitWidth = BitWidth + Result.BaseType = t.CInt + return Result + + Result = CTypeInfo() + Result.BaseType = t.CVoid + return Result + + elif isinstance(Node, ast.Subscript): + if (isinstance(Node.value, ast.Attribute) + and isinstance(Node.value.value, ast.Name) + and IsTModule(Node.value.value.id, self.Trans.SymbolTable) + and Node.value.attr == 'Bit'): + if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): + Result = CTypeInfo() + Result.IsBitField = True + Result.BitWidth = Node.slice.value + Result.BaseType = t.CInt + return Result + return self._HandleSubscript(Node) + + elif isinstance(Node, ast.BinOp): + return self.MergeTypes(Node) + + elif isinstance(Node, ast.List): + if Node.elts: + BaseTypes = [] + for elt in Node.elts: + EltType = self._GetCTypeInfoInternal(elt) + if isinstance(EltType, CTypeInfo) and EltType.BaseType: + BaseTypes.append(EltType.BaseType) + if BaseTypes: + Result = CTypeInfo() + Result.BaseType = tuple(BaseTypes) + return Result + return self._MakeVoidCTypeInfo() + + return self._MakeVoidCTypeInfo() + + def _HandleSubscript(self, Node: ast.Subscript) -> CTypeInfo: + """澶勭悊 Subscript 鑺傜偣锛屼娇鐢?CTypeInfo 鏍戝舰缁撴瀯鍒嗘瀽 AST + + 例如? + t.CVoid[5] -> void *[5] + t.CPtr[t.CPtr[t.CPtr]][5][6] -> void ***[5][6] + t.CArrayPtr[t.CArrayPtr] -> void *(*)[] + t.CPtr[t.CArrayPtr[t.CArrayPtr]] -> void *(*(*))[] + """ + def CollectAll(node: ast.AST, ParentIsArrayPtr: bool = False) -> CTypeInfo: + """鏀堕泦绫诲瀷淇℃伅锛岃繑鍥?CTypeInfo""" + if isinstance(node, ast.Subscript): + value_node = node.value + slice_node = node.slice + + if isinstance(value_node, ast.Attribute): + VtInfo = self.GetCTypeInfo(value_node) + + if isinstance(value_node, ast.Attribute) and value_node.attr == 'Callable' and isinstance(value_node.value, ast.Name) and IsTModule(value_node.value.id, self.Trans.SymbolTable): + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + params_list = slice_node.elts[0] + return_node = slice_node.elts[1] + ParamTypes = [] + ParamNames = [] + if isinstance(params_list, ast.List): + for elt in params_list.elts: + ParamTypeInfo = self._GetCTypeInfoInternal(elt) + if isinstance(ParamTypeInfo, CTypeInfo): + ParamTypes.append(ParamTypeInfo) + ParamNames.append('') + else: + ParamTypes.append(self._MakeCTypeInfoFromName('int')) + ParamNames.append('') + if not ParamTypes: + ParamTypes.append(self._MakeCTypeInfoFromName('void')) + ParamNames.append('') + ReturnTypeInfo = self._GetCTypeInfoInternal(return_node) + if isinstance(ReturnTypeInfo, CTypeInfo): + ReturnType = ReturnTypeInfo + else: + ReturnType = CTypeInfo() + ReturnType.BaseType = t.CVoid() + Info = CTypeInfo() + Info.IsFuncPtr = True + Info.FuncPtrReturn = ReturnType + Info.FuncPtrParams = list(zip(ParamNames, ParamTypes)) + return Info + + if VtInfo and VtInfo.IsArrayPtr: + ResultInfo = CTypeInfo() + ResultInfo.PtrCount = VtInfo.PtrCount + ResultInfo.IsArrayPtr = True + if isinstance(slice_node, ast.Attribute): + SliceVtInfo = self.GetCTypeInfo(slice_node) + if SliceVtInfo and SliceVtInfo.IsArrayPtr: + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.IsArrayPtr = True + ResultInfo.ArrayPtr.PtrCount = SliceVtInfo.PtrCount + if SliceVtInfo.ArrayPtr: + ResultInfo.ArrayPtr.ArrayPtr = SliceVtInfo.ArrayPtr + elif SliceVtInfo and SliceVtInfo.PtrCount > 0: + ResultInfo.ArrayPtr = SliceVtInfo + else: + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.IsArrayPtr = True + elif isinstance(slice_node, ast.Subscript): + ResultInfo.ArrayPtr = CollectAll(slice_node) + elif isinstance(slice_node, ast.Name): + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.BaseType = CTypeInfo.CreateFromTypeName(slice_node.id).BaseType + elif isinstance(slice_node, ast.BinOp): + ResultInfo.ArrayPtr = CTypeInfo() + try: + ResultInfo.ArrayPtr.ArrayDims.append(ast.unparse(slice_node)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayPtr.ArrayDims.append('N') + elif isinstance(slice_node, ast.Constant): + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.ArrayDims.append(str(slice_node.value)) + else: + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.IsArrayPtr = True + elif VtInfo and VtInfo.PtrCount == 1 and not VtInfo.ArrayPtr: + ResultInfo = CTypeInfo() + ResultInfo.BaseType = t.CVoid + ResultInfo.PtrCount = 1 + if isinstance(slice_node, ast.Subscript): + inner_info = CollectAll(slice_node) + if inner_info and inner_info.PtrCount > 0: + ResultInfo.PtrCount += inner_info.PtrCount + if inner_info.BaseType and not isinstance(inner_info.BaseType, t.CVoid): + ResultInfo.BaseType = inner_info.BaseType + elif inner_info and inner_info.ArrayPtr: + ResultInfo.ArrayPtr = inner_info + elif inner_info and inner_info.BaseType and not isinstance(inner_info.BaseType, t.CVoid): + ResultInfo.BaseType = inner_info.BaseType + elif isinstance(slice_node, ast.Attribute): + SliceVtInfo = self.GetCTypeInfo(slice_node) + if SliceVtInfo and SliceVtInfo.PtrCount == 1 and not SliceVtInfo.ArrayPtr: + ResultInfo.PtrCount = 2 + if SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): + ResultInfo.BaseType = SliceVtInfo.BaseType + elif SliceVtInfo and SliceVtInfo.IsArrayPtr: + inner = CTypeInfo() + inner.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr = inner + elif SliceVtInfo and SliceVtInfo.ArrayPtr: + ResultInfo.ArrayPtr = SliceVtInfo + elif SliceVtInfo and SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): + ResultInfo.BaseType = SliceVtInfo.BaseType + elif isinstance(slice_node, ast.Name): + SliceType = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + ResultInfo.PtrCount += 1 + elif SliceType == t.CArrayPtr: + inner = CTypeInfo() + inner.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr = inner + else: + ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(slice_node.id).BaseType + elif isinstance(slice_node, ast.BinOp): + merged = self.MergeTypes(slice_node) + if merged: + ResultInfo.PtrCount += merged.PtrCount + if merged.BaseType and not isinstance(merged.BaseType, t.CVoid): + ResultInfo.BaseType = merged.BaseType + if merged.ArrayPtr: + ResultInfo.ArrayPtr = merged.ArrayPtr + elif isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif VtInfo: + ResultInfo = CTypeInfo() + ResultInfo.BaseType = VtInfo.BaseType + if isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif isinstance(slice_node, ast.Name): + ResultInfo.ArrayDims.append(slice_node.id) + elif isinstance(slice_node, ast.BinOp): + try: + ResultInfo.ArrayDims.append(ast.unparse(slice_node)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayDims.append('N') + elif isinstance(slice_node, ast.Subscript): + ResultInfo.ArrayPtr = CollectAll(slice_node) + else: + ResultInfo = CTypeInfo() + if isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif isinstance(slice_node, ast.Name): + SliceType = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + ResultInfo.PtrCount += 1 + elif SliceType == t.CArrayPtr: + ResultInfo.ArrayPtr = CTypeInfo() + else: + ResultInfo.ArrayDims.append(slice_node.id) + elif isinstance(value_node, ast.Subscript): + ResultInfo = CollectAll(value_node) + if isinstance(slice_node, ast.Attribute): + SliceVtInfo = self.GetCTypeInfo(slice_node) + if SliceVtInfo and SliceVtInfo.PtrCount >= 1 and not SliceVtInfo.ArrayPtr: + ResultInfo.PtrCount += SliceVtInfo.PtrCount + if SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): + ResultInfo.BaseType = SliceVtInfo.BaseType + elif SliceVtInfo and SliceVtInfo.IsArrayPtr: + NewInfo = CTypeInfo() + NewInfo.ArrayPtr = ResultInfo + ResultInfo = NewInfo + elif SliceVtInfo and SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): + ResultInfo.BaseType = SliceVtInfo.BaseType + elif isinstance(slice_node, ast.Subscript): + InnerNode = CollectAll(slice_node) + if ResultInfo.ArrayPtr is None: + ResultInfo.ArrayPtr = InnerNode + else: + current = ResultInfo + while current.ArrayPtr is not None: + current = current.ArrayPtr + current.ArrayPtr = InnerNode + elif isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif isinstance(slice_node, ast.Name): + SliceType = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + ResultInfo.PtrCount += 1 + elif SliceType == t.CArrayPtr: + ResultInfo.ArrayPtr = CTypeInfo() + else: + ResultInfo.ArrayDims.append(slice_node.id) + elif isinstance(slice_node, ast.BinOp): + try: + ResultInfo.ArrayDims.append(ast.unparse(slice_node)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayDims.append('N') + elif isinstance(value_node, ast.Name): + ResultInfo = CTypeInfo() + if IsListAnnotation(node): + if not isinstance(slice_node, ast.Tuple): + ElementType = self._GetCTypeInfoInternal(slice_node) + if ElementType and isinstance(ElementType, CTypeInfo): + ResultInfo.BaseType = ElementType.BaseType if ElementType.BaseType else t.CVoid() + ResultInfo.PtrCount = ElementType.PtrCount + 1 + ResultInfo.IsFuncPtr = ElementType.IsFuncPtr + ResultInfo.FuncPtrReturn = ElementType.FuncPtrReturn + ResultInfo.FuncPtrParams = ElementType.FuncPtrParams + else: + ResultInfo.BaseType = t.CVoid() + ResultInfo.PtrCount = 1 + elif isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1: + ElementType = self._GetCTypeInfoInternal(slice_node.elts[0]) + if ElementType and isinstance(ElementType, CTypeInfo): + ResultInfo.BaseType = ElementType.BaseType if ElementType.BaseType else t.CVoid() + ResultInfo.PtrCount = ElementType.PtrCount + ResultInfo.IsFuncPtr = ElementType.IsFuncPtr + ResultInfo.FuncPtrReturn = ElementType.FuncPtrReturn + ResultInfo.FuncPtrParams = ElementType.FuncPtrParams + for elt in slice_node.elts[1:]: + if isinstance(elt, ast.Constant): + ResultInfo.ArrayDims.append(str(elt.value)) + elif isinstance(elt, ast.Name): + ResultInfo.ArrayDims.append(elt.id) + else: + try: + ResultInfo.ArrayDims.append(ast.unparse(elt)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayDims.append('N') + else: + ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(value_node.id).BaseType + else: + # 检查是否为泛型类特化(如 list[str]) + _is_generic_template: bool = ( + hasattr(self.Trans, 'ClassHandler') + and hasattr(self.Trans.ClassHandler, '_generic_class_templates') + and value_node.id in self.Trans.ClassHandler._generic_class_templates + ) + if _is_generic_template: + # 走和 _ResolveGenericSlice 同样的规范化路径(_resolve_generic_slice_type), + # 确保字段类型名与特化名完全一致: + # - ast.Name: 用原始名(如 'Param' → 'Param',不加 SHA1 前缀), + # 与 _resolve_generic_slice_type 的 ast.Name 分支一致 + # - BinOp/Attribute: 走 _resolve_type_arg_str(加 SHA1 前缀 / 'ptr' 标记) + # 避免字段类型用 'GSList[sha1.Param]' 而特化名用 'GSList[Param]' 导致 + # find_struct_by_pointee 匹配到 opaque 副本,方法分派失败 + type_arg_str: str + if isinstance(slice_node, ast.Tuple): + arg_parts: list[str] = [] + for elt in slice_node.elts: + slice_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(elt) + arg_parts.append(slice_res[0] if slice_res else self.Trans.ExprCallHandle._resolve_type_arg_str(elt)) + # 必须用 '][' 分隔以匹配 _mangle_generic_class_name 的格式 + # (class_name + '[' + ']['.join(mangled_args) + ']') + type_arg_str = ']['.join(arg_parts) + else: + slice_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node) + type_arg_str = slice_res[0] if slice_res else self.Trans.ExprCallHandle._resolve_type_arg_str(slice_node) + full_spec_name: str = f'{value_node.id}[{type_arg_str}]' + ResultInfo.BaseType = t.CStruct(name=full_spec_name) + ResultInfo.Name = full_spec_name + ResultInfo.IsStruct = True + ResultInfo.PtrCount = 1 + # 触发泛型特化发射:确保特化的方法被发射 + struct sha1 注册 + # 与局部变量 AnnAssign 路径一致;否则函数参数注解路径 + # 只构建 mangled 名,方法调用分派时 class_sha1=None 导致 + # "Undefined method" 错误(如 lst.__len__() 在 list[T] | t.CPtr 参数上) + # _specialize_generic_class 内部有 spec_key 去重,重复调用安全 + if hasattr(self.Trans, 'ExprCallHandle') and self.Trans.ExprCallHandle is not None: + self.Trans.ExprCallHandle._ResolveGenericSlice(node) + else: + ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(value_node.id).BaseType + if isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif isinstance(slice_node, ast.Name): + SliceType = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + ResultInfo.PtrCount += 1 + elif SliceType == t.CArrayPtr: + ResultInfo.ArrayPtr = CTypeInfo() + else: + ResultInfo.ArrayDims.append(slice_node.id) + elif isinstance(slice_node, ast.BinOp): + try: + ResultInfo.ArrayDims.append(ast.unparse(slice_node)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayDims.append('N') + elif isinstance(slice_node, ast.Subscript): + InnerNode = CollectAll(slice_node) + ResultInfo.ArrayDims.extend(InnerNode.ArrayDims) + else: + ResultInfo = CTypeInfo() + + return ResultInfo + elif isinstance(node, ast.Attribute): + VtInfo = self.GetCTypeInfo(node) + result = CTypeInfo() + if VtInfo and VtInfo.IsArrayPtr: + result.ArrayPtr = CTypeInfo() + return result + elif VtInfo and VtInfo.PtrCount == 1 and not VtInfo.ArrayPtr: + result.BaseType = t.CVoid + result.PtrCount = 1 + return result + elif VtInfo and VtInfo.BaseType: + result.BaseType = VtInfo.BaseType + return result + else: + return CTypeInfo() + elif isinstance(node, ast.Name): + result = CTypeInfo() + result.BaseType = CTypeInfo.CreateFromTypeName(node.id).BaseType + return result + else: + return CTypeInfo() + + return CollectAll(Node) + + def _CollectAllOperandTypes(self, Node: ast.AST) -> List: + """递归收集所有操作数的类型(用于一次性合并)""" + Types = [] + + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + Types.extend(self._CollectAllOperandTypes(Node.left)) + Types.extend(self._CollectAllOperandTypes(Node.right)) + else: + Types.append(Node) + + return Types + + def _GetTypeFromNode(self, Node: ast.AST) -> CTypeInfo: + """浠?AST 鑺傜偣鑾峰彇绫诲瀷""" + if isinstance(Node, ast.Call): + if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': + return self.GetCTypeInfo(Node) + elif isinstance(Node.func, ast.Attribute) and CTypeHelper.GetTModuleCType(Node.func.attr) == t._CTypedef: + return self.GetCTypeInfo(Node) + elif isinstance(Node.func, ast.Attribute) and Node.func.attr == 'Bit': + if Node.args and isinstance(Node.args[0], ast.Constant): + Result = CTypeInfo() + Result.BaseType = t.CInt + Result.IsBitField = True + Result.BitWidth = Node.args[0].value + return Result + return self._MakeVoidCTypeInfo() + elif isinstance(Node, ast.Subscript): + if (isinstance(Node.value, ast.Attribute) + and isinstance(Node.value.value, ast.Name) + and IsTModule(Node.value.value.id, self.Trans.SymbolTable) + and Node.value.attr == 'Bit'): + if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): + Result = CTypeInfo() + Result.BaseType = t.CInt + Result.IsBitField = True + Result.BitWidth = Node.slice.value + return Result + return self.GetCTypeInfo(Node) + else: + return self.GetCTypeInfo(Node) + + def MergeTypes(self, Node: ast.BinOp): + """处理类型组合,如 t.CInt | t.CPtr ?t.CStatic | t.CInt + + 这是 | 运算符的类型合并处理函数 + 收集所有操作数,一次性合? + """ + if not isinstance(Node, ast.BinOp) or not isinstance(Node.op, ast.BitOr): + return None + + AllOperands = self._CollectAllOperandTypes(Node) + + AllTypes = [] + for Operand in AllOperands: + TypeItem = self._GetTypeFromNode(Operand) + AllTypes.append(TypeItem) + + if isinstance(Node.left, ast.Attribute): + LeftParts = [] + current = Node.left + while isinstance(current, ast.Attribute): + attr_type = getattr(t, current.attr, None) + if attr_type and isinstance(attr_type, type) and issubclass(attr_type, t.CType): + LeftParts.insert(0, attr_type) + current = current.value + if isinstance(current, ast.Name): + name_type = getattr(t, current.id, None) + if name_type and isinstance(name_type, type) and issubclass(name_type, t.CType): + LeftParts.insert(0, name_type) + if LeftParts and LeftParts[-1] == t._CTypedef: + for type_item in AllTypes: + if isinstance(type_item, CTypeInfo) and type_item.IsFuncPtr: + type_item.IsTypedef = True + type_item.OriginalType = CTypeInfo() + type_item.OriginalType.IsFuncPtr = True + type_item.OriginalType.FuncPtrReturn = type_item.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + type_item.OriginalType.FuncPtrParams = list(type_item.FuncPtrParams) if type_item.FuncPtrParams else [] + return type_item + Result = CTypeInfo() + if isinstance(AllTypes[-1], CTypeInfo): + Result.BaseType = AllTypes[-1].BaseType + else: + Result.BaseType = t.CInt + Result.IsTypedef = True + return Result + + return self._MergeAllComponentTypes(*AllTypes) + + def _MergeAllComponentTypes(self, *Types: CTypeInfo) -> CTypeInfo: + """合并多个类型 - 直接使用 CTypeInfo 类""" + if not Types: + return CTypeInfo() + + Result = CTypeInfo() + # 统计 t.CPtr(CVoid + PtrCount=1)的个数,用于正确累加指针层数 + # 因为 (AST | t.CPtr) | t.CPtr 展开后会包含多个 t.CPtr,每个都需要累加 + cptr_count: int = 0 + + for TypeItem in Types: + if TypeItem is None: + continue + if TypeItem.IsState: + Result.IsState = True + if not Result.Storage: + Result.Storage = t.CExport() + continue + if TypeItem.IsBitField: + Result.BaseType = t.CInt + Result.IsBitField = True + Result.BitWidth = TypeItem.BitWidth + return Result + if TypeItem.IsFuncPtr: + Result.BaseType = t.CVoid + Result.IsFuncPtr = True + Result.FuncPtrParams = TypeItem.FuncPtrParams + Result.FuncPtrReturn = TypeItem.FuncPtrReturn + return Result + if TypeItem.IsTypedef: + if TypeItem.BaseType and (not isinstance(TypeItem.BaseType, (t._CTypedef,)) or TypeItem.PtrCount > 0): + if Result.BaseType is None or isinstance(Result.BaseType, (t.CVoid, t._CTypedef)): + Result.BaseType = TypeItem.BaseType + Result.Name = TypeItem.Name + Result.IsTypedef = True + Result.PtrCount = max(Result.PtrCount, TypeItem.PtrCount) + if TypeItem.OriginalType: + Result.OriginalType = TypeItem.OriginalType + else: + Result.IsTypedef = True + Result.Name = TypeItem.Name + if TypeItem.OriginalType: + Result.OriginalType = TypeItem.OriginalType + continue + # 统计 t.CPtr(CVoid + PtrCount>0) + # 注意: t.CPtr[t.CPtr] 的 PtrCount=2(双重指针),需要完整累加 + if TypeItem.PtrCount > 0 and TypeItem.BaseType and (TypeItem.BaseType is t.CVoid or isinstance(TypeItem.BaseType, t.CVoid)): + cptr_count += TypeItem.PtrCount + # BaseType 保持不变,只计数,后续统一累加 + if TypeItem.IsRenum and TypeItem.Name: + Result.IsRenum = True + Result.IsEnum = True + Result.IsStruct = True + if not Result.Name: + Result.Name = TypeItem.Name + continue + self._MergeCTypeInfoInto(Result, TypeItem) + + # 对所有 t.CPtr 统一累加指针层数 + # 仅对 struct 类型吸收第一个 CPtr(struct 变量默认是指针,AST | t.CPtr = AST*)。 + # 对于本身已是指针的类型(如 bytes=CChar*),bytes | t.CPtr = i8**(不吸收)。 + base_ptr_count: int = Result.PtrCount + Result.PtrCount += cptr_count + if Result.IsStruct and base_ptr_count > 0 and cptr_count > 0: + Result.PtrCount -= 1 + + if Result.BaseType is None: + Result.BaseType = t.CVoid + + return Result + + def _MergeCTypeInfoInto(self, Target: CTypeInfo, Source: CTypeInfo): + if Source.PtrCount > 0 and Source.BaseType and (Source.BaseType is t.CVoid or isinstance(Source.BaseType, t.CVoid)): + # t.CPtr 的处理已提升到 _MergeAllComponentTypes 中统一计数累加, + # 此处保留仅作为兜底(从其他路径直接调用 _MergeCTypeInfoInto 时) + if Target.PtrCount < Source.PtrCount: + Target.PtrCount = Source.PtrCount + elif Source.PtrCount > 0 and Source.BaseType and Source.BaseType is not t.CVoid and not isinstance(Source.BaseType, t.CVoid): + if Target.BaseType is None or Target.BaseType is t.CVoid or isinstance(Target.BaseType, t.CVoid): + Target.BaseType = Source.BaseType + # 当 Target 已有 PtrCount(来自之前的 t.CPtr),累加而非替换 + Target.PtrCount = Source.PtrCount + Target.PtrCount + elif Target.PtrCount == 0: + Target.PtrCount = Source.PtrCount + elif Source.PtrCount > Target.PtrCount: + Target.PtrCount = Source.PtrCount + elif Source.PtrCount > Target.PtrCount: + Target.PtrCount = Source.PtrCount + if Source.BaseType and Source.BaseType is not t.CVoid and not isinstance(Source.BaseType, (t.CVoid, t._CTypedef)): + if Target.BaseType is None: + Target.BaseType = Source.BaseType + elif (Target.BaseType is t.CVoid or isinstance(Target.BaseType, t.CVoid)) and Target.PtrCount == 0: + Target.BaseType = Source.BaseType + elif (Source.BaseType is t.CVoid or isinstance(Source.BaseType, t.CVoid)) and Target.BaseType is None: + Target.BaseType = Source.BaseType + # Struct 名称保留:BinOp `list[AST | t.CPtr] | t.CPtr` 合并时, + # _HandleSubscript 已在 Source.Name 设置了特化名(如 'list[5dab8cb390496d22.ASTptr]'), + # 合并后 Target.Name 必须保留此名称,否则 class_member_element_class + # 无法获取正确的特化类名 → 方法分派 fallback 到错误的类 + if Source.IsStruct and Source.Name and not Target.Name: + Target.Name = Source.Name + # REnum 标志和名称保留(BinOp `LLVMType | t.CPtr` 合并时不能丢失 REnum 语义) + # 同时传播 IsStruct=True,让 CPtr 吸收逻辑生效(REnum 在内存布局上是 struct) + if Source.IsRenum and Source.Name: + Target.IsRenum = True + Target.IsEnum = True + Target.IsStruct = True + if not Target.Name: + Target.Name = Source.Name + if Source.IsTypedef and Source.Name: + if Source.BaseType and (not isinstance(Source.BaseType, (t._CTypedef,)) or Source.PtrCount > 0): + if Target.BaseType is None or isinstance(Target.BaseType, (t.CVoid, t._CTypedef)): + Target.BaseType = Source.BaseType + Target.IsTypedef = True + Target.Name = Source.Name + if Source.OriginalType: + Target.OriginalType = Source.OriginalType + elif Target.BaseType is None or isinstance(Target.BaseType, (t.CVoid, t._CTypedef)): + if Source.OriginalType: + if isinstance(Source.OriginalType, CTypeInfo) and Source.OriginalType.BaseType: + Target.BaseType = Source.OriginalType.BaseType + Target.PtrCount = max(Target.PtrCount, Source.OriginalType.PtrCount) + Target.IsTypedef = True + Target.Name = Source.Name + Target.OriginalType = Source.OriginalType + elif isinstance(Source.OriginalType, CTypeInfo): + Target.IsTypedef = True + Target.Name = Source.Name + Target.OriginalType = Source.OriginalType + else: + Target.BaseType = t.CStruct(name=Source.Name) + Target.IsStruct = True + else: + Target.BaseType = t.CStruct(name=Source.Name) + Target.IsStruct = True + if Source.ArrayPtr: + if Target.ArrayPtr is None: + Target.ArrayPtr = Source.ArrayPtr + else: + if Source.PtrCount > Target.PtrCount: + Target.PtrCount = Source.PtrCount + Target.ArrayPtr = Source.ArrayPtr + if Source.PtrCount > 0: + Target.ArrayDims = [] + elif Source.ArrayDims: + Target.ArrayDims.extend(Source.ArrayDims) + if Source.VarConst: + Target.VarConst = True + if Source.VarVolatile: + Target.VarVolatile = True + if Source.DataConst: + Target.DataConst = True + if Source.DataVolatile: + Target.DataVolatile = True + if Source.IsFuncPtr: + Target.IsFuncPtr = True + Target.FuncPtrParams = Source.FuncPtrParams + Target.FuncPtrReturn = Source.FuncPtrReturn + if Source.Storage and not Target.Storage: + Target.Storage = Source.Storage + if Source.ByteOrder and not Target.ByteOrder: + Target.ByteOrder = Source.ByteOrder diff --git a/App/lib/core/Handles/HandlesWith.py b/App/lib/core/Handles/HandlesWith.py new file mode 100644 index 0000000..6f5d06d --- /dev/null +++ b/App/lib/core/Handles/HandlesWith.py @@ -0,0 +1,206 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator +from lib.core.Handles.HandlesBase import BaseHandle +import ast +import llvmlite.ir as ir + + +class WithHandle(BaseHandle): + def _ResolveInheritedMethod(self, ClassName: str, MethodName: str, Gen: "Translator.LlvmGen") -> tuple[str | None, "ir.Function | None"]: + """沿继承链查找方法,返回 (定义方法的类名, 函数对象) + + 当子类未覆写 __enter__/__exit__ 等方法时,沿 Gen.class_parent 链查找父类实现。 + _generate_inherited_method_wrappers 只为 class_methods[ClassName] 中的方法生成包装, + 不包括父类独有方法,因此 with 语句需要单独处理继承查找。 + """ + # 先检查子类自身 + FullName: str = f'{ClassName}.{MethodName}' + func = Gen._find_function(FullName) if hasattr(Gen, '_find_function') else Gen.functions.get(FullName) + if func: + return (ClassName, func) + # 沿继承链查找 + p: str | None = Gen.class_parent.get(ClassName) if hasattr(Gen, 'class_parent') else None + while p: + parent_full: str = f'{p}.{MethodName}' + parent_func = Gen._find_function(parent_full) if hasattr(Gen, '_find_function') else Gen.functions.get(parent_full) + if parent_func: + return (p, parent_func) + p = Gen.class_parent.get(p) + return (None, None) + + def _HandleWithLlvm(self, Node: ast.With) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + for item in Node.items: + context_expr: ast.expr = item.context_expr + asname: str | None = None + target: ast.expr | None = getattr(item, 'optional_vars', getattr(item, 'target', getattr(item, 'asname', None))) + if target and isinstance(target, ast.Name): + asname = target.id + ClassName: str | None = None + if isinstance(context_expr, ast.Call): + if isinstance(context_expr.func, ast.Name): + ClassName = context_expr.func.id + elif isinstance(context_expr.func, ast.Attribute): + ClassName = context_expr.func.attr + if ClassName and ClassName not in Gen.structs: + SymInfo: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(ClassName) + if SymInfo: + if SymInfo.IsStruct or SymInfo.IsRenum: + pass + else: + ClassName = None + else: + ClassName = None + elif isinstance(context_expr, ast.Name): + VarName: str = context_expr.id + if VarName in Gen.var_struct_class: + ClassName = Gen.var_struct_class[VarName] + ctx_val: ir.Value | None = self.HandleExprLlvm(context_expr) + if not ctx_val: + continue + original_ctx_val: ir.Value = ctx_val + if ClassName and isinstance(ctx_val.type, ir.PointerType): + pointee: ir.Type = ctx_val.type.pointee + if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee) + if found: + CN: str = found[0] + ST: ir.Type = found[1] + ClassName = CN + StructPtrType: ir.PointerType | None = ir.PointerType(Gen.structs[ClassName]) if ClassName and ClassName in Gen.structs else None + if StructPtrType and isinstance(ctx_val.type, ir.PointerType) and ctx_val.type.pointee != Gen.structs.get(ClassName): + ctx_val = Gen.builder.bitcast(ctx_val, StructPtrType, name="cast_with_ctx") + ctx_alloca: ir.AllocaInstr = Gen._allocaEntry(ctx_val.type, name="__with_ctx") + Gen._store(ctx_val, ctx_alloca) + Gen._UnregisterTempPtr(ctx_val) + if ClassName: + Gen.var_struct_class['__with_ctx'] = ClassName + Gen._var_to_heap_ptr['__with_ctx'] = ctx_val + original_asname_heap_ptr: ir.Value | None = Gen._var_to_heap_ptr.get(asname) if asname else None + ctx_is_var_ref: bool = isinstance(context_expr, ast.Name) + enter_result: ir.Value | None = None + if ClassName: + # 沿继承链查找 __enter__(子类可能未覆写,使用父类实现) + enter_class, enter_func = self._ResolveInheritedMethod(ClassName, '__enter__', Gen) + if enter_func is not None: + ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx") + # 父类方法需要 self 为父类指针类型,bitcast 子类指针 + if enter_class != ClassName and enter_class in Gen.structs: + parent_ptr_type = ir.PointerType(Gen.structs[enter_class]) + if ctx_Loaded.type != parent_ptr_type: + ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_enter_self") + enter_call: ir.CallInstr = Gen.builder.call(enter_func, [ctx_Loaded], name="call_enter") + enter_result = enter_call + if isinstance(enter_call.type, ir.PointerType): + pointee: ir.Type = enter_call.type.pointee + if isinstance(pointee, ir.PointerType) and isinstance(pointee.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + enter_result = Gen._load(enter_call, name="enter_deref") + elif self._is_char_pointer(enter_call) and StructPtrType: + enter_result = Gen.builder.bitcast(enter_call, StructPtrType, name="cast_enter") + if asname and enter_result: + if isinstance(enter_result.type, ir.PointerType) and isinstance(enter_result.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(enter_result.type.pointee) + if found: + CN: str = found[0] + ST: ir.Type = found[1] + Gen.var_struct_class[asname] = CN + Gen._var_to_heap_ptr[asname] = enter_result + Gen._register_local_heap_ptr(enter_result, VarName=asname) + if asname in Gen._reg_values: + OldVal: ir.Value = Gen._reg_values[asname] + var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=asname) + Gen._store(OldVal, var) + Gen.variables[asname] = var + del Gen._reg_values[asname] + if asname in Gen.variables and Gen.variables[asname] is not None: + VarPtr: ir.Value = Gen.variables[asname] + if VarPtr.type.pointee == enter_result.type: + Gen._store(enter_result, VarPtr) + elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(enter_result.type, ir.PointerType): + CastedVal: ir.Value = Gen.builder.bitcast(enter_result, VarPtr.type.pointee, name=f"cast_{asname}") + Gen._store(CastedVal, VarPtr) + else: + NewVar: ir.AllocaInstr = Gen._alloca(enter_result.type, name=asname) + Gen._store(enter_result, NewVar) + Gen.variables[asname] = NewVar + else: + Gen._reg_values[asname] = enter_result + Gen.variables[asname] = None + # 如果类有 __provides__,自动生成 push 代码 + if ClassName and ClassName in Gen.class_provides: + # 编译期检查: with provider 栈深度是否超过 _MAX_PROVIDER_DEPTH (32) + # _with_provides_stack 只反映当前嵌套路径,顺序执行的 with 会在退出时弹栈 + current_depth: int = sum(len(pl) for pl in Gen._with_provides_stack) + new_fields: int = len(Gen.class_provides[ClassName]) + if current_depth + new_fields > 32: + raise SyntaxError( + f"[TransPyC] with provider stack overflow at line {Node.lineno}: " + f"current depth {current_depth} + new {new_fields} fields > max 32. " + f"Class '{ClassName}' provides too many fields or with nesting too deep." + ) + # 编译期压栈:记录当前 with 上下文提供的字段名,供 __require_must__ 静态检查 + Gen._with_provides_stack.append(Gen.class_provides[ClassName]) + ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx") + ctx_i8: ir.Value = ctx_Loaded + if isinstance(ctx_Loaded.type, ir.PointerType) and ctx_Loaded.type != ir.PointerType(ir.IntType(8)): + ctx_i8 = Gen.builder.bitcast(ctx_Loaded, ir.PointerType(ir.IntType(8)), name="ctx_push_i8") + for field_name in Gen.class_provides[ClassName]: + field_str: ir.Value = Gen._create_string_global(field_name) + push_func = Gen.functions.get('_push_provider') + if push_func: + Gen.builder.call(push_func, [field_str, ctx_i8], name="call_push_provider") + self.Trans.VarScopes.append({}) + if asname: + self.Trans.VarScopes[-1][asname] = True + self.HandleBodyLlvm(Node.body) + if self.Trans.VarScopes: + self.Trans.VarScopes.pop() + # 如果类有 __provides__,自动生成 pop 代码 + if ClassName and ClassName in Gen.class_provides: + for _ in Gen.class_provides[ClassName]: + pop_func = Gen.functions.get('_pop_provider') + if pop_func: + Gen.builder.call(pop_func, [], name="call_pop_provider") + # 编译期弹栈 + if Gen._with_provides_stack: + Gen._with_provides_stack.pop() + if ClassName: + # 沿继承链查找 __exit__(子类可能未覆写,使用父类实现) + exit_class, exit_func = self._ResolveInheritedMethod(ClassName, '__exit__', Gen) + if exit_func is not None: + ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx") + # 父类方法需要 self 为父类指针类型,bitcast 子类指针 + if exit_class != ClassName and exit_class in Gen.structs: + parent_ptr_type = ir.PointerType(Gen.structs[exit_class]) + if ctx_Loaded.type != parent_ptr_type: + ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_exit_self") + Gen.builder.call(exit_func, [ctx_Loaded], name="call_exit") + Gen._unregister_local_heap_ptr(original_ctx_val) + if ctx_val is not original_ctx_val: + Gen._unregister_local_heap_ptr(ctx_val) + if ctx_is_var_ref and original_asname_heap_ptr and original_asname_heap_ptr is not original_ctx_val and original_asname_heap_ptr is not ctx_val: + Gen._unregister_local_heap_ptr(original_asname_heap_ptr) + if '__with_ctx' in Gen._var_to_heap_ptr: + del Gen._var_to_heap_ptr['__with_ctx'] + if asname and enter_result is not None and isinstance(ctx_val.type, ir.PointerType) and isinstance(enter_result.type, ir.PointerType): + ctx_int: ir.Value = Gen.builder.ptrtoint(ctx_val, ir.IntType(64), name="ctx_int") + enter_int: ir.Value = Gen.builder.ptrtoint(enter_result, ir.IntType(64), name="enter_int") + same_ptr: ir.Value = Gen.builder.icmp_unsigned('==', ctx_int, enter_int, name="same_ptr_check") + with_free_bb: ir.Block = Gen.func.append_basic_block("with_free_ctx") + with_skip_bb: ir.Block = Gen.func.append_basic_block("with_skip_free") + Gen.builder.cbranch(same_ptr, with_skip_bb, with_free_bb) + Gen.builder.position_at_start(with_free_bb) + raw: ir.Value = Gen.builder.bitcast(ctx_val, ir.PointerType(ir.IntType(8)), name="with_free_cast") + free_func: ir.Function = Gen.get_or_declare_c_func('free', ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])) + Gen.builder.call(free_func, [raw]) + Gen.builder.branch(with_skip_bb) + Gen.builder.position_at_start(with_skip_bb) + Gen._unregister_local_heap_ptr(enter_result) + Gen._unregister_local_heap_ptr(original_ctx_val) + elif isinstance(ctx_val.type, ir.PointerType): + raw: ir.Value = Gen.builder.bitcast(ctx_val, ir.PointerType(ir.IntType(8)), name="with_free_cast") + free_func: ir.Function = Gen.get_or_declare_c_func('free', ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])) + Gen.builder.call(free_func, [raw]) + Gen._unregister_local_heap_ptr(original_ctx_val) \ No newline at end of file diff --git a/App/lib/core/IncludesScanner.py b/App/lib/core/IncludesScanner.py index 85c7978..3c0f5b4 100644 --- a/App/lib/core/IncludesScanner.py +++ b/App/lib/core/IncludesScanner.py @@ -171,7 +171,6 @@ def compute_file_sha1(pool: memhub.MemBuddy | t.CPtr, bytes_read: t.CInt64T = f.read_all(buf, BUF_SIZE) f.close() if bytes_read <= 0: - stdlib.free(buf) return None # 原地去除 \r(CRLF → LF),与 Projectrans.py 文本模式读取一致 @@ -190,21 +189,17 @@ def compute_file_sha1(pool: memhub.MemBuddy | t.CPtr, # 计算 SHA1 ctx: hashlib.sha1 | t.CPtr = hashlib.sha1() if ctx is None: - stdlib.free(buf) return None ctx.update(buf) digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN) if digest is None: - stdlib.free(buf) return None ctx.final(digest) # 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符) hex_buf: str = stdlib.malloc(17) if hex_buf is None: - stdlib.free(buf) - stdlib.free(digest) return None for i in range(8): hi: int = (digest[i] >> 4) & 0xF @@ -218,9 +213,6 @@ def compute_file_sha1(pool: memhub.MemBuddy | t.CPtr, else: hex_buf[i * 2 + 1] = 'a' + (lo - 10) hex_buf[16] = '\0' - # 释放临时缓冲区(hex_buf 由调用者负责释放) - stdlib.free(buf) - stdlib.free(digest) return hex_buf @@ -253,8 +245,6 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) if handle == win32base.INVALID_HANDLE_VALUE: - stdlib.free(pattern) - stdlib.free(find_data) return 1 # 遍历所有文件和子目录 @@ -294,7 +284,6 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, prefix_len = string.strlen(rel_prefix) rel_path: bytes = stdlib.malloc(prefix_len + fname_len + 2) if rel_path is None: - stdlib.free(full_path) break if rel_prefix is not None and prefix_len > 0: viperlib.snprintf(rel_path, prefix_len + fname_len + 2, "%s/%s", rel_prefix, fname) @@ -323,21 +312,13 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, sha1: str = compute_file_sha1(pool, full_path) if sha1 is not None: add_file_entry(result, pool, full_path, rel_path, sha1) - # 释放 compute_file_sha1 返回的临时 hex_buf - stdlib.free(sha1) - - # 释放本次迭代分配的路径缓冲区 - stdlib.free(full_path) - stdlib.free(rel_path) + stdio.printf(" [scan] %s -> %s\n", rel_path, sha1) # 继续搜索下一个文件 - nxt: t.CInt = win32file.FindNextFileA(handle, find_data) - if nxt == 0: + if win32file.FindNextFileA(handle, find_data) == 0: break win32file.FindClose(handle) - stdlib.free(pattern) - stdlib.free(find_data) return 0 @@ -360,8 +341,7 @@ def scan_includes(pool: memhub.MemBuddy | t.CPtr, scan_directory_recursive(pool, includes_dir, None, result) - count: t.CInt = result.Count - stdio.printf("[Phase1] 扫描完成: %d 个 .py 文件\n", count) + stdio.printf("[Phase1] 扫描完成: %d 个 .py 文件\n", result.Count) return result diff --git a/App/lib/core/LLVMCG/BaseGen.py b/App/lib/core/LLVMCG/BaseGen.py new file mode 100644 index 0000000..cd0b890 --- /dev/null +++ b/App/lib/core/LLVMCG/BaseGen.py @@ -0,0 +1,669 @@ +from __future__ import annotations +from typing import Any +import ast +import os +import llvmlite.ir as ir +from lib.includes import t as _t +from lib.includes.t import CTypeRegistry +from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo, BuiltinTypeMap + + +class VaArgInstruction(ir.Instruction): + def descr(self, buf: list[str]) -> None: + ptr: ir.Value = self.operands[0] + buf.append("va_arg {0} {1}, {2}\n".format( + ptr.type, ptr.get_reference(), self.type)) + + +class BaseGenMixin: + + @staticmethod + def _parse_triple(triple: str | None) -> dict[str, bool | int]: + """解析目标三元组,返回平台信息字典""" + info: dict[str, bool | int] = { + 'is_windows': False, 'is_linux': False, 'is_macos': False, + 'is_x86_64': False, 'is_arm64': False, 'is_32bit': False, + 'ptr_size': 8, 'long_size': 8, 'wchar_t_size': 32, + } + if not triple: + return info + triple_lower: str = triple.lower() + # 检测操作系统 + if 'windows' in triple_lower or 'win32' in triple_lower or 'mingw' in triple_lower or 'msvc' in triple_lower or 'cygwin' in triple_lower: + info['is_windows'] = True + # Windows LLP64: long = 32 位, wchar_t = 16 位 + info['long_size'] = 32 + info['wchar_t_size'] = 16 + elif 'linux' in triple_lower: + info['is_linux'] = True + elif 'darwin' in triple_lower or 'macos' in triple_lower or 'apple' in triple_lower: + info['is_macos'] = True + # 检测架构 + if 'x86_64' in triple_lower or 'amd64' in triple_lower or 'x64' in triple_lower: + info['is_x86_64'] = True + elif 'aarch64' in triple_lower or 'arm64' in triple_lower: + info['is_arm64'] = True + elif 'i386' in triple_lower or 'i686' in triple_lower or 'arm' in triple_lower: + info['is_32bit'] = True + info['ptr_size'] = 4 + info['long_size'] = 32 + if info['is_windows']: + info['wchar_t_size'] = 16 + else: + info['wchar_t_size'] = 32 + return info + + def __init__(self, triple: str | None = None, datalayout: str | None = None) -> None: + self.module: ir.Module = ir.Module(name="transpyc_module") + if triple: + self.module.triple = triple + else: + self.module.triple = "x86_64-none-elf" + if datalayout: + self.module.data_layout = datalayout + else: + self.module.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + + # 解析目标平台信息 + self._platform_info: dict[str, bool | int] = self._parse_triple(self.module.triple) + self.ptr_size: int = self._platform_info['ptr_size'] + # 根据目标平台配置 t 模块中的平台相关类型大小 + _t.configure_platform(self.module.triple) + self.builder: ir.IRBuilder | None = None + self.func: ir.Function | None = None + self.functions: dict[str, ir.Function] = {} + self.variables: dict[str, ir.Value | None] = {} + self._direct_values: dict[str, ir.Value] = {} + self._reg_values: dict[str, ir.Value] = {} + self.var_signedness: dict[str, bool] = {} + self._unsigned_results: set[int] = {} + self.string_const_counter: int = 0 + self.Vtables: dict[str, ir.GlobalVariable] = {} + self.class_methods: dict[str, list[str]] = {} + self.class_members: dict[str, list[tuple[str, ir.Type]]] = {} + self.class_member_defaults: dict[str, dict[str, Any]] = {} + self.class_member_signeds: dict[str, dict[str, bool]] = {} + self.class_member_bitfields: dict[str, dict[str, int]] = {} + self.class_member_byteorders: dict[str, dict[str, str]] = {} + self.local_var_byteorders: dict[str, str] = {} + self.class_member_element_class: dict[str, dict[str, str]] = {} + self.class_member_bitoffsets: dict[str, dict[str, int]] = {} + self.structs: dict[str, ir.IdentifiedStructType] = {} + self.typedef_int_widths: dict[str, int] = {} # typedef名称 -> 底层整数位宽 (如 'UINT' -> 32, 'BYTE' -> 8) + self._cross_module_vtable_classes: set[str] = set() + self._cross_module_novtable: set[str] = set() + self.class_vtable: set[str] = set() + self._class_graph: dict[str, Any] = {} + self.class_parent: dict[str, str] = {} + # Store member annotations for deferred type resolution (cross-module class references) + self.class_member_annotations: dict[str, dict[str, Any]] = {} + self.loop_break_targets: list[ir.Block] = [] + self.loop_continue_targets: list[ir.Block] = [] + self.eh_jmp_buf_stack: list[ir.Value] = [] + self.eh_except_block_stack: list[ir.Block] = [] + self.eh_finally_stack: list[Any] = [] + self.global_vars: set[str] = set() + self.nonlocal_params: dict[str, Any] = {} + self._variable_scope_stack: list[dict[str, Any]] = [] # Stack of outer scope variables for nested function nonlocal lookup + self.var_struct_class: dict[str, str] = {} + self.var_ptr_element: dict[str, bool] = {} + # 全局变量的指针元素标志(持久,不受函数重置影响) + # 用于全局变量 _argv: bytes | t.CPtr 等,使 _argv[index] 使用 8 字节指针步长 + self.global_var_ptr_element: dict[str, bool] = {} + self.class_provides: dict[str, list[str]] = {} + self.class_requires: dict[str, list[str]] = {} + self.class_require_must: dict[str, list[str]] = {} + # 编译期 with 上下文栈:记录当前嵌套 with 链中所有可用的 provides 字段名 + # 用于 __require_must__ 的纯编译期静态可达性检查 + self._with_provides_stack: list[list[str]] = [] + # 函数参数自动取地址模式: func_name -> [None, None, 'need', 'always', ...] + # 'need': t.CNeedPtr — 仅对非指针值取地址 + # 'always': t.CAutoPtr — 无条件取地址 + self._auto_addr_params: dict[str, list[str | None]] = {} + self.global_struct_class: dict[str, str] = {} + self.var_type_info: dict[str, Any] = {} + self.var_const_flags: dict[str, bool] = {} + self._current_source_file: str | None = None + self._current_source_lines: list[str] = [] + self._ns_cache: dict[str, str] = {} + self._type_cache: dict[tuple[str, bool], ir.Type] = {} + self._typedef_cache: dict[str, Any] = {} + self._func_sig_cache: dict[str, Any] = {} + self.var_type_assignments: dict[str, Any] = {} + self._temp_struct_ptrs: list[ir.Value] = [] + self._stop_iter_flag_param: ir.Value | None = None + self._local_heap_ptrs: list[tuple[ir.Value, str, str | None]] = [] + self._var_to_heap_ptr: dict[str, ir.Value] = {} + self.known_return_types: dict[str, ir.Type] = {} + self._current_lineno: int = 0 + self._current_node_info: str = "" + self.module_sha1: str | None = None + self.ModuleSha1Map: dict[str, str] = {} + self._temp_dir: str | None = None + self._export_funcs: set[str] = set() + self._export_extern_funcs: dict[str, str] = {} + self._current_module_func_names: set[str] = set() + # __doc__ 魔法字符串注册表:symbol_name -> docstring 文本(None 表示空/优化剥离) + self._doc_strings: dict[str, str | None] = {} + self.class_packed: set[str] = set() + self._define_constants: dict[str, int] = {} + # 以下属性原先在外部懒注入,此处显式初始化以避免 hasattr/getattr 动态反射 + self._struct_sha1_map: dict[str, str] = {} + self.SymbolTable: Any = None + self._Trans: Any = None + self.class_sha1_map: dict[str, str] = {} + # 以下属性原先懒注入,此处显式初始化以消除 hasattr/getattr 动态反射 + self._import_handler_ref: Any = None + self._classes_in_progress: set[str] = set() + # 字段收集期间添加当前类名,阻止 CollectAnnAssignMember 触发的嵌套 + # _generate_structs 用不完整 class_members 调用 set_body(set_body 只能调用一次) + # 用集合而非单值,因为嵌套特化类 _EmitClassLlvm 会清除单值,导致外层类被错误设置 body + self._collecting_members_set: set[str] = set() + self._last_var_name: str | None = None + self._variadic_info: dict | None = None + self._vtable_copy_counter: int = 0 + self._decorated_funcs: dict = {} + self._current_tree: Any = None + self._DefineConstants: dict = {} + self._all_define_constants: dict = {} + pi: dict[str, bool | int] = self._platform_info + # 根据目标平台设置宏(声明式,从三元组推导) + if pi['is_windows']: + self._define_constants['_WIN32'] = 1 + if not pi['is_32bit']: + self._define_constants['_WIN64'] = 1 + self._define_constants['WIN64'] = 1 + self._define_constants['WIN32'] = 1 + if pi['is_linux']: + self._define_constants['__linux__'] = 1 + if pi['is_macos']: + self._define_constants['__APPLE__'] = 1 + self._define_constants['__MACH__'] = 1 + if pi['is_x86_64']: + self._define_constants['__x86_64__'] = 1 + self._define_constants['__x86_64'] = 1 + self._define_constants['_M_X64'] = 1 + elif pi['is_arm64']: + self._define_constants['__aarch64__'] = 1 + self._define_constants['_M_ARM64'] = 1 + if not pi['is_32bit'] and pi['is_linux']: + self._define_constants['__LP64__'] = 1 + elif pi['is_32bit']: + self._define_constants['_ILP32'] = 1 + self._define_constants['SIZEOF_VOID_P'] = pi['ptr_size'] + self._define_constants['__SIZEOF_POINTER__'] = pi['ptr_size'] + + def find_struct_by_pointee(self, pointee: ir.Type) -> tuple[str, ir.IdentifiedStructType] | None: + """根据 pointee 类型查找匹配的结构体,返回 (class_name, struct_type) 或 None""" + for class_name, struct_type in self.structs.items(): + if pointee == struct_type: + return (class_name, struct_type) + return None + + @staticmethod + def _extract_short_name(name: str) -> str: + """从结构体类型名中提取短名(去掉 sha1 前缀)。 + 如 "6bc91356c652f8ca.Function" → "Function" + 如 "Function" → "Function"(无 sha1 前缀时原样返回) + """ + if '.' in name: + prefix: str = name.split('.', 1)[0] + if len(prefix) == 16 and all(c in '0123456789abcdef' for c in prefix): + return name.split('.', 1)[1] + return name + + @staticmethod + def _extract_struct_sha1(st: ir.IdentifiedStructType) -> str: + """从 IdentifiedStructType 的 name 中提取 sha1 前缀(16 位十六进制)。 + 返回空字符串表示无 sha1 前缀。用于同名类冲突检测。 + """ + if not isinstance(st, ir.IdentifiedStructType): + return '' + try: + name: str = st.name + except (AssertionError, AttributeError): + return '' + if '.' in name: + prefix: str = name.split('.', 1)[0] + if len(prefix) == 16 and all(c in '0123456789abcdef' for c in prefix): + return prefix + return '' + + def _get_or_create_struct(self, name: str, source_sha1: str | None = None, packed: bool = False) -> ir.IdentifiedStructType: + clean_name: str = name.replace(' *', '').replace('*', '').replace(' ', '_').replace('const', 'const_').replace('volatile', 'volatile_') + # 如果名称包含模块前缀(如 "a9fa0f6200c09e65.zbit_writer"), + # 先尝试用短名称(如 "zbit_writer")查找已注册的 struct, + # 避免为同一 struct 创建多个不同的类型对象导致跨模块引用类型不一致 + if '.' in clean_name and clean_name not in self.structs: + parts: list[str] = clean_name.split('.', 1) + if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): + short_name: str = parts[1] + if short_name in self.structs: + existing_short: ir.IdentifiedStructType = self.structs[short_name] + # 治本修复:同名类 sha1 冲突检测。 + # 当请求的 sha1 前缀与现有 struct 的 sha1 前缀不一致时 + # (如 stmts.py:Module 与 __module.py:Module 同名冲突), + # 不能复用已有 struct(set_body 已锁定为错误布局), + # 需创建独立 struct 并以 sha1 前缀全名存储。 + _existing_sha1: str = self._extract_struct_sha1(existing_short) + if _existing_sha1 and _existing_sha1 != parts[0]: + if clean_name in self.structs: + return self.structs[clean_name] + st_new: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=clean_name, packed=packed) + self.structs[clean_name] = st_new + return st_new + return existing_short + # 优先解析 typedef 整数类型(如 INT/UINT/BYTE),避免将其注册为 opaque struct。 + # 必须在 `if clean_name in self.structs` 缓存检查之前执行, + # 否则首次调用(SymbolTable 未就绪时)会将 typedef 名缓存为 opaque struct, + # 后续调用即使 SymbolTable 就绪也只会返回缓存的错误类型。 + # 此修复同时解决两个问题: + # 1. alloca unsized type:变量声明 `argc: INT = 6` 不再生成 `alloca %"...INT"` + # 2. 下标访问:FindStructNameInAnnotation 不再匹配 typedef 名(因不在 Gen.structs) + td_width: int | None = self._resolve_typedef_int_width(clean_name) + if td_width is not None: + if td_width > 0: + self.typedef_int_widths[clean_name] = td_width + return ir.IntType(td_width) + # td_width == 0: 特殊 typedef 名称(str/bytes/list 等),返回 i8 + self.typedef_int_widths[clean_name] = 0 + return ir.IntType(8) + if clean_name in self.structs: + existing: ir.IdentifiedStructType = self.structs[clean_name] + # 治本修复:同名类 sha1 冲突检测(短名调用路径)。 + # 当 source_sha1 与现有 struct 的 sha1 前缀不一致时(跨模块同名类冲突), + # 不能复用已有 struct(set_body 已锁定为错误布局), + # 需创建独立 struct 并以 sha1 前缀全名存储,不覆盖短名条目。 + if source_sha1: + _existing_sha1: str = self._extract_struct_sha1(existing) + if _existing_sha1 and _existing_sha1 != source_sha1: + full_key: str = f"{source_sha1}.{clean_name}" + if full_key in self.structs: + st_full: ir.IdentifiedStructType = self.structs[full_key] + if packed and isinstance(st_full, ir.IdentifiedStructType) and not st_full.packed: + st_full.packed = True + return st_full + ns_name: str = f"{source_sha1}.{clean_name}" + st_new: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=ns_name, packed=packed) + self.structs[full_key] = st_new + return st_new + if packed and isinstance(existing, ir.IdentifiedStructType) and not existing.packed: + existing.packed = True + return existing + if clean_name in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum', 'Void', 'void'}: + return ir.IntType(8) + if self.SymbolTable: + Entry: _CTypeInfo | None = self.SymbolTable.lookup(clean_name) + if Entry and Entry.IsTypedef: + resolved_str: str = self._resolve_typedef(clean_name) + if resolved_str != clean_name: + return self._type_str_to_llvm(resolved_str) + if Entry.BaseType: + if not isinstance(Entry.BaseType, (_t._CTypedef,)) or Entry.PtrCount > 0: + resolved: str = self._resolve_ctype_to_str(Entry) + if resolved: + return self._type_str_to_llvm(resolved) + if Entry and Entry.IsRenum: + if clean_name in self.structs: + return self.structs[clean_name] + # REnum 结构体尚未创建时不能 fall through 到 IsEnum(会返回 i32), + # 需要继续到下方正常结构体创建路径,创建 opaque 结构体。 + # 这对自引用 REnum(如 LLVMType 包含 LLVMType*)至关重要: + # _parse_llvm_type 先通过此路径创建 opaque 结构体,_TryLoadStructFromStub 再 set_body。 + elif Entry and Entry.IsEnum and clean_name not in self.class_methods: + return ir.IntType(32) + if Entry and Entry.IsExceptionClass: + return ir.IntType(32) + if Entry and Entry.IsFunction: + return ir.PointerType(ir.IntType(8)) + if Entry and Entry.IsVariable: + return ir.PointerType(ir.IntType(8)) + if Entry and Entry.BaseType: + if isinstance(Entry.BaseType, type) and issubclass(Entry.BaseType, _t.CEnum): + return ir.IntType(32) + if Entry.BaseType is _t.CEnum: + return ir.IntType(32) + ns_name: str + if source_sha1: + ns_name = f"{source_sha1}.{clean_name}" + elif clean_name in self._struct_sha1_map: + ns_name = f"{self._struct_sha1_map[clean_name]}.{clean_name}" + else: + ns_name = self._mangle_name(clean_name) + st: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=ns_name, packed=packed) + self.structs[clean_name] = st + # 尝试解析 typedef 的底层整数位宽 + width: int | None = self._resolve_typedef_int_width(clean_name) + if width is not None: + self.typedef_int_widths[clean_name] = width + return st + + def _resolve_typedef_int_width(self, name: str) -> int | None: + """解析 typedef 名称的底层整数位宽 + + 通过 CTypeRegistry 和 BuiltinTypeMap 动态解析, + 替代硬编码的 TYPEDEF_NAMES 映射表。 + 返回 int 位宽或 None(无法解析)。 + """ + # 1. 先查 BuiltinTypeMap(覆盖 BYTEPTR 等指针别名) + bm: Any = BuiltinTypeMap.Get(name) + if bm: + ctype_cls: Any + ptr_count: int + ctype_cls, ptr_count = bm + if ptr_count > 0: + return None # 指针类型,不是整数 typedef + llvm_str: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + + # 2. 再查 SymbolTable(用户定义的 typedef) + if self.SymbolTable: + Entry: _CTypeInfo | None = self.SymbolTable.lookup(name) + if Entry and Entry.IsTypedef: + if Entry.BaseType: + if not isinstance(Entry.BaseType, (_t._CTypedef,)) and Entry.PtrCount == 0: + llvm_str: str = CTypeRegistry.CTypeToLLVM(type(Entry.BaseType) if isinstance(Entry.BaseType, _t.CType) else Entry.BaseType) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + if Entry.OriginalType: + if isinstance(Entry.OriginalType, _CTypeInfo) and Entry.OriginalType.BaseType: + if not isinstance(Entry.OriginalType.BaseType, (_t._CTypedef,)) and Entry.OriginalType.PtrCount == 0: + llvm_str: str = CTypeRegistry.CTypeToLLVM(type(Entry.OriginalType.BaseType) if isinstance(Entry.OriginalType.BaseType, _t.CType) else Entry.OriginalType.BaseType) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + elif isinstance(Entry.OriginalType, str): + # OriginalType 是类型名字符串(如 'CInt', 'CUnsignedInt'), + # 通过 CTypeRegistry 解析为整数位宽 + cls: Any = CTypeRegistry.GetClassByName(Entry.OriginalType) + if cls: + llvm_str: str = CTypeRegistry.CTypeToLLVM(cls) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + + # 3. 尝试 CTypeRegistry 直接按名称查找 + ctype_cls: Any = CTypeRegistry.GetClassByName(name) + if ctype_cls: + llvm_str: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + + # 4. 特殊名称(非整数类型,但需要标记为 typedef 以跳过 opaque 定义) + # 注意:如果 name 已注册为 struct(用户定义了同名 class,如 includes/_dict.py 中的 dict), + # 则不应视为 typedef,应返回 None 让调用方走 struct 创建路径。 + # 解决时序问题:_parse_llvm_declare 从 stub.ll 解析时 dict 可能尚未注册到 Gen.structs, + # 但 SymbolTable 在 .pyi 解析阶段已就绪,可通过 is_struct 检查区分 class 和 typedef。 + _SPECIAL_TYPEDEFS: set[str] = { + 'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', + 'CStruct', 'enum', 'REnum', 'renum', + 'list', 'dict', 'tuple', 'set', 'str', 'bytes', 'array', 'type', 'CArray', + } + if name in _SPECIAL_TYPEDEFS: + if name in self.structs: + return None # 已注册为 struct,不视为 typedef + if self.SymbolTable is not None: + try: + if self.SymbolTable.is_struct(name): + return None # SymbolTable 中标记为 struct,不视为 typedef + except Exception: + pass + return 0 # 标记为 typedef,但位宽为 0(非整数) + + return None + + def _set_node_info(self, node: ast.AST, extra_info: str = "") -> None: + if hasattr(node, 'lineno'): + self._current_lineno = node.lineno + else: + self._current_lineno = 0 + if hasattr(node, 'col_offset'): + self._current_node_info = f"line {self._current_lineno}, col {node.col_offset}" + else: + self._current_node_info = f"line {self._current_lineno}" + if extra_info: + self._current_node_info += f" ({extra_info})" + + def _set_source_info(self, filepath: str) -> None: + self._current_source_file = filepath + try: + with open(filepath, 'r', encoding='utf-8') as f: + self._current_source_lines = f.readlines() + except Exception: # 文件读取失败时回退为空行列表 + self._current_source_lines = [] + + def _skip_mangle(self, name: str) -> bool: + skip_prefixes: tuple[str, ...] = ('llvm.', 'str_const_', 'printf', 'memset_pattern') + if any(name.startswith(p) for p in skip_prefixes): + return True + if '.' in name: + parts: list[str] = name.split('.', 1) + if len(parts[0]) >= 8 and all(c in '0123456789abcdef' for c in parts[0]): + return True + return False + + def _mangle_name(self, name: str) -> str: + if self._skip_mangle(name): + return name + if name in self._export_funcs: + return name + if self.module_sha1: + return f"{self.module_sha1}.{name}" + return name + + def _find_function(self, name: str) -> ir.Function | None: + if name in self.functions: + return self.functions[name] + g: Any = self.module.globals.get(name) + if g and isinstance(g, ir.Function): + self.functions[name] = g + return g + mangled: str = self._mangle_func_name(name) + if mangled != name: + if mangled in self.functions: + self.functions[name] = self.functions[mangled] + return self.functions[mangled] + g = self.module.globals.get(mangled) + if g and isinstance(g, ir.Function): + self.functions[mangled] = g + self.functions[name] = g + return g + for sha1 in self.ModuleSha1Map.values(): + prefixed: str = f"{sha1}.{name}" + if prefixed in self.functions: + self.functions[name] = self.functions[prefixed] + return self.functions[prefixed] + g = self.module.globals.get(prefixed) + if g and isinstance(g, ir.Function): + self.functions[prefixed] = g + self.functions[name] = g + return g + return None + + def _has_function(self, name: str) -> bool: + return self._find_function(name) is not None + + def _get_or_declare_function(self, mangled_name: str, func_type: ir.FunctionType) -> ir.Function: + g = self.module.globals.get(mangled_name) + if g and isinstance(g, ir.Function): + return g + return ir.Function(self.module, func_type, name=mangled_name) + + def _get_function(self, name: str) -> ir.Function | None: + func: ir.Function | None = self._find_function(name) + return func + + def _mangle_func_name(self, name: str, module_name: str | None = None) -> str: + if name in self._export_funcs and not module_name: + return name + if name in self._export_extern_funcs: + target_sha1: str = self._export_extern_funcs[name] + if module_name: + # 显式带 module_name 调用(如 stdlib.system):即使当前模块定义了同名函数, + # 也应按模块匹配 extern,返回裸名。snprintf 之所以工作而 system 不工作, + # 就是因为 os._win32 定义了自己的 system 包装,导致旧逻辑跳过了 extern 匹配。 + if module_name == target_sha1: + return name + if module_name in self.ModuleSha1Map and self.ModuleSha1Map[module_name] == target_sha1: + return name + init_name: str = f"{module_name}.__init__" + if init_name in self.ModuleSha1Map and self.ModuleSha1Map[init_name] == target_sha1: + return name + # 兜底:module_name 不在 ModuleSha1Map 中或 sha1 不匹配, + # 但函数在 _export_extern_funcs 中(C extern 不应混淆), + # 仅当当前模块未定义同名函数时才返回裸名。 + if name not in self._current_module_func_names: + return name + elif name not in self._current_module_func_names: + # 无 module_name 调用(如 from stdlib import system; system()): + # 仅当当前模块未定义同名函数时才返回 extern 裸名, + # 否则让本地定义走正常 mangle 路径(本地 def 遮蔽 import 语义)。 + return name + # main 函数作为程序入口点,永远不混淆 + if name == 'main': + return name + # Try class_sha1_map first for class methods (e.g., 'md5.__init__' -> class 'md5') + class_sha1_map: dict[str, str] = self.class_sha1_map + if class_sha1_map: + if '.' in name: + base_class: str = name.split('.')[0] + if base_class in class_sha1_map: + sha1: str = class_sha1_map[base_class] + return f"{sha1}.{name}" + elif name in class_sha1_map: + sha1: str = class_sha1_map[name] + return f"{sha1}.{name}" + # If module_name is already a SHA1 hash (16 hex chars), use it directly + if module_name and len(module_name) == 16 and all(c in '0123456789abcdef' for c in module_name): + return f"{module_name}.{name}" + if module_name and module_name in self.ModuleSha1Map: + sha1: str = self.ModuleSha1Map[module_name] + return f"{sha1}.{name}" + # Try parent package name (e.g., 'hashlib' -> 'hashlib.__init__') + if module_name: + init_name: str = f"{module_name}.__init__" + if init_name in self.ModuleSha1Map: + sha1: str = self.ModuleSha1Map[init_name] + return f"{sha1}.{name}" + if self._skip_mangle(name): + return name + if self.module_sha1: + return f"{self.module_sha1}.{name}" + return name + + def _get_source_line(self, lineno: int) -> str | None: + if 0 < lineno <= len(self._current_source_lines): + return self._current_source_lines[lineno - 1].rstrip('\r\n') + return None + + def _get_node_info(self) -> str: + info: str = "" + if self._current_node_info: + info = f" [{self._current_node_info}]" + if self._current_source_file: + info += f" in {self._current_source_file}" + lineno: int = self._current_lineno + if lineno > 0: + info += f", line {lineno}:" + source_line: str | None = self._get_source_line(lineno) + if source_line: + info += f"\n → {source_line}" + return info + + # ========== 统一工具方法 ========== + + def _create_string_global(self, s: str, name: str | None = None) -> ir.Constant: + """创建字符串全局变量并返回 i8* 指针。 + + 统一字符串全局变量创建模式:编码为 utf-8 + '\\x00' 终止符, + 创建 internal linkage 的 GlobalVariable,返回 i8* 指针常量。 + """ + str_val: str = s + '\x00' + str_bytes: bytes = str_val.encode('utf-8') + arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes)) + gv_name: str = name or f"str_const_{self.string_const_counter}" + self.string_const_counter += 1 + gv: ir.GlobalVariable = ir.GlobalVariable(self.module, arr_type, name=gv_name) + gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) + gv.linkage = 'internal' + return ir.Constant(ir.PointerType(ir.IntType(8)), gv.get_reference()) + + def _create_doc_global(self, symbol_name: str, docstring: str | None) -> None: + """为符号创建 __doc__ 全局常量。 + + - docstring 为 None 或空字符串时,不创建常量(访问时内联空指针) + - slice_level != 0(优化开启)时,内容视为空 + - 非空时创建 internal linkage、global_constant=True 的只读字符串常量 + - 常量名格式:{module_sha1}.{symbol_name}.__doc__ + """ + slice_level: int = getattr(self._Trans, 'SliceLevel', 3) + if slice_level != 0: + docstring = None + if docstring is None or docstring == '': + self._doc_strings[symbol_name] = None + return + self._doc_strings[symbol_name] = docstring + mangled: str = self._mangle_name(f"{symbol_name}.__doc__") + self._create_string_global(docstring, name=mangled) + gv: ir.GlobalVariable | None = self.module.globals.get(mangled) + if gv is not None: + gv.global_constant = True + + def _get_doc_ptr(self, symbol_name: str) -> ir.Value | None: + """获取符号 __doc__ 的 i8* 指针。 + + - 若符号未注册 __doc__,返回 None(调用方回退到其他解析) + - 若 __doc__ 为空(无 docstring 或优化剥离),返回 i8* null 内联空指针 + - 若 __doc__ 非空,返回指向字符串全局常量的 i8* 指针 + - 跨模块 docstring(在 _doc_strings 中有文本但 module.globals 无对应常量)首次访问时创建本地 internal linkage 只读副本 + """ + if symbol_name not in self._doc_strings: + return None + docstring: str | None = self._doc_strings[symbol_name] + if docstring is None or docstring == '': + return ir.Constant(ir.PointerType(ir.IntType(8)), None) + mangled: str = self._mangle_name(f"{symbol_name}.__doc__") + gv: ir.GlobalVariable | None = self.module.globals.get(mangled) + if gv is None: + # 跨模块 docstring:创建本地 internal linkage 只读字符串常量副本 + self._create_string_global(docstring, name=mangled) + gv = self.module.globals.get(mangled) + if gv is None: + return ir.Constant(ir.PointerType(ir.IntType(8)), None) + gv.global_constant = True + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + return self.builder.gep(gv, [zero, zero], name=f"{symbol_name}_doc_ptr") + + def _apply_bswap_if_big(self, val: ir.Value, byte_order: str, name: str) -> ir.Value: + """大端字节序时对整数应用 bswap,否则原值返回。 + + 仅对 16/32/64 位整数生效,统一 bswap 逻辑。 + """ + if byte_order == 'big' and isinstance(val.type, ir.IntType) and val.type.width in (16, 32, 64): + return self.builder.bswap(val, name=name) + return val + + def _zero_value_for_type(self, llvm_type: ir.Type) -> ir.Constant | None: + """为指定 LLVM 类型生成零值常量。 + + 合并原 HandlesAssign._zero_value 与 StructGen._zero_value_for_type 的逻辑。 + 返回 ir.Constant 或 None(无法生成零值的类型,如 opaque struct)。 + """ + if isinstance(llvm_type, ir.IntType): + return ir.Constant(llvm_type, 0) + elif isinstance(llvm_type, (ir.FloatType, ir.DoubleType)): + return ir.Constant(llvm_type, 0.0) + elif isinstance(llvm_type, ir.PointerType): + return ir.Constant(llvm_type, None) + elif isinstance(llvm_type, ir.ArrayType): + elem_zv: ir.Constant | None = self._zero_value_for_type(llvm_type.element) + if elem_zv is None: + return None + return ir.Constant(llvm_type, [elem_zv] * llvm_type.count) + elif isinstance(llvm_type, ir.BaseStructType): + if llvm_type.elements is not None and len(llvm_type.elements) > 0: + elem_zvs: list[ir.Constant | None] = [self._zero_value_for_type(m) for m in llvm_type.elements] + if any(zv is None for zv in elem_zvs): + return None + return ir.Constant(llvm_type, elem_zvs) + return None + return None \ No newline at end of file diff --git a/App/lib/core/LLVMCG/ExprGen.py b/App/lib/core/LLVMCG/ExprGen.py new file mode 100644 index 0000000..e8eb6a1 --- /dev/null +++ b/App/lib/core/LLVMCG/ExprGen.py @@ -0,0 +1,368 @@ +from __future__ import annotations +from typing import Any +import logging +import llvmlite.ir as ir +from lib.constants.config import mode as _config_mode + + +class ExprGenMixin: + def emit_constant(self, value: Any, type_name: str = 'int') -> ir.Value: + if type_name == 'int': + return ir.Constant(ir.IntType(32), int(value)) + elif type_name == 'uint64_t' or type_name == 'unsigned long long': + return ir.Constant(ir.IntType(64), int(value)) + elif type_name == 'uint32_t' or type_name == 'unsigned int': + return ir.Constant(ir.IntType(32), int(value)) + elif type_name == 'uint16_t' or type_name == 'unsigned short': + return ir.Constant(ir.IntType(16), int(value)) + elif type_name == 'uint8_t' or type_name == 'unsigned char': + return ir.Constant(ir.IntType(8), int(value)) + elif type_name == 'int64_t' or type_name == 'long long': + return ir.Constant(ir.IntType(64), int(value)) + elif type_name == 'int32_t' or type_name == 'int': + return ir.Constant(ir.IntType(32), int(value)) + elif type_name == 'int16_t' or type_name == 'short': + return ir.Constant(ir.IntType(16), int(value)) + elif type_name == 'int8_t' or type_name == 'char': + return ir.Constant(ir.IntType(8), int(value)) + elif type_name == 'float' or type_name == 'float32_t' or type_name == 'FLOAT32': + return ir.Constant(ir.FloatType(), float(value)) + elif type_name == 'double' or type_name == 'float64_t' or type_name == 'FLOAT64': + return ir.Constant(ir.DoubleType(), float(value)) + elif type_name == 'float16_t' or type_name == 'FLOAT16': + return ir.Constant(ir.IntType(16), int(value)) + elif type_name == 'float8_t' or type_name == 'FLOAT8': + return ir.Constant(ir.IntType(8), int(value)) + elif type_name == 'float128_t' or type_name == 'FLOAT128': + return ir.Constant(ir.IntType(128), int(value)) + elif type_name == 'string': + encoded: bytes = value.encode('utf-8') + b'\x00' + str_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(encoded)) + str_const: ir.Constant = ir.Constant(str_type, bytearray(encoded)) + global_var: ir.GlobalVariable = ir.GlobalVariable(self.module, str_type, name=f"str_const_{self.string_const_counter}") + self.string_const_counter += 1 + global_var.initializer = str_const + global_var.linkage = 'internal' + return self.builder.bitcast(global_var, ir.PointerType(ir.IntType(8)), name="str") + return ir.Constant(ir.IntType(32), int(value)) + + def emit_binary_op(self, op: str, left: ir.Value, right: ir.Value, is_unsigned: bool = False) -> ir.Value: + if isinstance(left.type, ir.PointerType) and isinstance(right.type, ir.IntType): + # 数组指针需要先 decay 为元素指针,否则 GEP 会以整个数组为步长 + # 例如 [64 x i8]* + 1 会前进 64 字节而非 1 字节 + if isinstance(left.type.pointee, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + left = self.builder.gep(left, [zero, zero], name="array_decay") + right = self.builder.zext(right, ir.IntType(64), name="int2ptrint") + if op == '+' or op == 'Add': + return self.builder.gep(left, [right], name="ptr_add") + elif op == '-' or op == 'Sub': + return self.builder.gep(left, [self.builder.neg(right, name="neg")], name="ptr_sub") + if isinstance(left.type, ir.IntType) and isinstance(right.type, ir.PointerType): + if isinstance(right.type.pointee, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + right = self.builder.gep(right, [zero, zero], name="array_decay_rev") + left = self.builder.zext(left, ir.IntType(64), name="int2ptrint2") + if op == '+' or op == 'Add': + return self.builder.gep(right, [left], name="ptr_add_rev") + is_float: bool = isinstance(left.type, (ir.FloatType, ir.DoubleType)) or \ + isinstance(right.type, (ir.FloatType, ir.DoubleType)) + if is_float: + ftype: ir.Type + if isinstance(left.type, (ir.FloatType, ir.DoubleType)): + ftype = left.type + elif isinstance(right.type, (ir.FloatType, ir.DoubleType)): + ftype = right.type + else: + ftype = ir.DoubleType() + if not isinstance(left.type, (ir.FloatType, ir.DoubleType)): + left = self._to_float(left, ftype) + elif left.type != ftype: + if isinstance(left.type, ir.FloatType) and isinstance(ftype, ir.DoubleType): + left = self.builder.fpext(left, ftype, name="fpext_l") + else: + left = self.builder.fptrunc(left, ftype, name="fptrunc_l") + if not isinstance(right.type, (ir.FloatType, ir.DoubleType)): + right = self._to_float(right, ftype) + elif right.type != ftype: + if isinstance(right.type, ir.FloatType) and isinstance(ftype, ir.DoubleType): + right = self.builder.fpext(right, ftype, name="fpext_r") + else: + right = self.builder.fptrunc(right, ftype, name="fptrunc_r") + float_ops: dict[str, Any] = { + '+': self.builder.fadd, 'Add': self.builder.fadd, + '-': self.builder.fsub, 'Sub': self.builder.fsub, + '*': self.builder.fmul, 'Mult': self.builder.fmul, + '/': self.builder.fdiv, 'Div': self.builder.fdiv, + '%': self.builder.frem, 'Mod': self.builder.frem, + } + float_cmp_ops: dict[str, str] = { + '==': '==', 'Eq': '==', '!=': '!=', 'NotEq': '!=', + '<': '<', 'Lt': '<', '<=': '<=', 'LtE': '<=', + '>': '>', 'Gt': '>', '>=': '>=', 'GtE': '>=', + } + if op in float_ops: + return float_ops[op](left, right, name=op) + if op in float_cmp_ops: + return self.builder.fcmp_ordered(float_cmp_ops[op], left, right, name=op) + ops: dict[str, Any] + if is_unsigned: + ops = { + '+': self.builder.add, 'Add': self.builder.add, + '-': self.builder.sub, 'Sub': self.builder.sub, + '*': self.builder.mul, 'Mult': self.builder.mul, + '/': self.builder.udiv, 'Div': self.builder.udiv, + '%': self.builder.urem, 'Mod': self.builder.urem, + '//': self.builder.udiv, 'FloorDiv': self.builder.udiv, + } + else: + ops = { + '+': self.builder.add, 'Add': self.builder.add, + '-': self.builder.sub, 'Sub': self.builder.sub, + '*': self.builder.mul, 'Mult': self.builder.mul, + '/': self.builder.sdiv, 'Div': self.builder.sdiv, + '%': self.builder.srem, 'Mod': self.builder.srem, + '//': self.builder.sdiv, 'FloorDiv': self.builder.sdiv, + } + cmp_ops: dict[str, str] = { + '==': '==', 'Eq': '==', '!=': '!=', 'NotEq': '!=', + '<': '<', 'Lt': '<', '<=': '<=', 'LtE': '<=', + '>': '>', 'Gt': '>', '>=': '>=', 'GtE': '>=', + } + logic_ops: dict[str, Any] = { + '&&': self.builder.and_, 'And': self.builder.and_, + '||': self.builder.or_, 'Or': self.builder.or_, + } + if op in ('>>', 'RShift') and not is_unsigned: + if isinstance(left.type, ir.IntType): + if left.type.width == 8: + is_unsigned = True + elif left.type.width == 16: + is_unsigned = True + elif left.type.width == 32: + if self._last_var_name: + is_unsigned = self._is_var_unsigned(self._last_var_name) + elif left.type.width == 64: + if self._last_var_name: + is_unsigned = self._is_var_unsigned(self._last_var_name) + shift_ops: dict[str, Any] = { + '>>': self.builder.lshr if is_unsigned else self.builder.ashr, + 'RShift': self.builder.lshr if is_unsigned else self.builder.ashr, + '<<': self.builder.shl, 'LShift': self.builder.shl, + } + bit_ops: dict[str, Any] = { + '&': self.builder.and_, + '|': self.builder.or_, + '^': self.builder.xor, + } + if op == '**' or op == 'Pow': + if isinstance(left, ir.Constant) and isinstance(right, ir.Constant): + try: + lv: Any = left.constant + rv: Any = right.constant + if isinstance(lv, int) and isinstance(rv, int) and rv >= 0: + result: int = lv ** rv + return ir.Constant(left.type, result) + except Exception as _e: + if _config_mode == "strict": + logging.warning(f"异常被忽略: {_e}") + pass + ftype: ir.Type + if isinstance(left.type, (ir.FloatType, ir.DoubleType)): + ftype = left.type + elif isinstance(right.type, (ir.FloatType, ir.DoubleType)): + ftype = right.type + else: + ftype = ir.DoubleType() + lf: ir.Value = self._to_float(left, ftype) + rf: ir.Value = self._to_float(right, ftype) + powf: ir.Function | None = self.module.globals.get('llvm.pow.f64') + if not powf: + fnty: ir.FunctionType = ir.FunctionType(ir.DoubleType(), [ir.DoubleType(), ir.DoubleType()]) + powf = ir.Function(self.module, fnty, name='llvm.pow.f64') + if lf.type != ir.DoubleType(): + lf = self.builder.fpext(lf, ir.DoubleType(), name="ext_f64") + if rf.type != ir.DoubleType(): + rf = self.builder.fpext(rf, ir.DoubleType(), name="ext_f64") + result: ir.Value = self.builder.call(powf, [lf, rf], name="pow") + both_int: bool = isinstance(left.type, ir.IntType) and isinstance(right.type, ir.IntType) + right_is_pos_int_const: bool = isinstance(right, ir.Constant) and isinstance(right.constant, int) and right.constant >= 0 + if both_int and right_is_pos_int_const: + return self.builder.fptosi(result, left.type, name="pow2int") + if isinstance(left.type, ir.FloatType): + return self.builder.fptrunc(result, ir.FloatType(), name="pow2f32") + return result + if op in ops: + try: + return ops[op](left, right, name=op) + except Exception as e: + raise ValueError(f"{e}{self._get_node_info()}") + if op in shift_ops: + try: + return shift_ops[op](left, right, name=op) + except Exception as e: + raise ValueError(f"{e}{self._get_node_info()}") + if op in bit_ops: + try: + return bit_ops[op](left, right, name=op) + except Exception as e: + raise ValueError(f"{e}{self._get_node_info()}") + if op in cmp_ops: + try: + if is_unsigned: + return self.builder.icmp_unsigned(cmp_ops[op], left, right, name=op) + return self.builder.icmp_signed(cmp_ops[op], left, right, name=op) + except Exception as e: + raise ValueError(f"{e}{self._get_node_info()}") + if op in logic_ops: + try: + return logic_ops[op](left, right, name=op) + except Exception as e: + raise ValueError(f"{e}{self._get_node_info()}") + return left + + def _to_float(self, val: ir.Value, ftype: ir.Type | None = None) -> ir.Value: + if ftype is None: + ftype = ir.DoubleType() + if isinstance(val.type, (ir.FloatType, ir.DoubleType)): + if val.type == ftype: + return val + if isinstance(val.type, ir.FloatType) and isinstance(ftype, ir.DoubleType): + return self.builder.fpext(val, ftype, name="fpext") + return self.builder.fptrunc(val, ftype, name="fptrunc") + if isinstance(val.type, ir.IntType): + if val.type.width == 1: + val = self.builder.zext(val, ir.IntType(32), name="bool2i32") + return self.builder.sitofp(val, ftype, name="int2float") + return val + + def emit_return(self, value: ir.Value | None = None) -> None: + if not self.builder or self.builder.block.is_terminated: + return + if value is not None: + self._unregister_local_heap_ptr(value) + self._emit_local_heap_frees() + if self._variadic_info and self._variadic_info.get('va_start_called'): + va_list_ptr: ir.Value = self._variadic_info['va_list_ptr'] + self.emit_va_end(va_list_ptr) + if value is None: + if self.func and hasattr(self.func, 'ftype'): + ret_type: ir.Type = self.func.ftype.return_type + if isinstance(ret_type, ir.IntType): + self.builder.ret(ir.Constant(ret_type, 0)) + elif isinstance(ret_type, ir.PointerType): + self.builder.ret(ir.Constant(ret_type, None)) + elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)): + self.builder.ret(ir.Constant(ret_type, 0.0)) + elif isinstance(ret_type, ir.BaseStructType): + if ret_type.elements: + zero_val: ir.Constant = ir.Constant(ret_type, [ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) else ir.Constant(et, ir.Undefined) for et in ret_type.elements]) + else: + zero_val = ir.Constant(ret_type, None) + self.builder.ret(zero_val) + elif isinstance(ret_type, ir.ArrayType): + self.builder.ret(ir.Constant(ret_type, None)) + else: + self.builder.ret_void() + else: + self.builder.ret_void() + else: + if self.func and hasattr(self.func, 'ftype'): + ret_type: ir.Type = self.func.ftype.return_type + if ret_type != value.type: + if isinstance(ret_type, ir.PointerType) and isinstance(value.type, ir.PointerType): + value = self.builder.bitcast(value, ret_type, name="ret_cast") + elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType): + if isinstance(value.type.pointee, ir.IntType) and isinstance(ret_type, ir.IntType) and value.type.pointee.width == ret_type.width: + value = self._load(value, name="ret_Load") + elif isinstance(ret_type, ir.IntType): + value = self.builder.ptrtoint(value, ret_type, name="ret_ptrtoint") + elif isinstance(value.type.pointee, ret_type.__class__) and not isinstance(value.type.pointee, ir.IntType): + value = self._load(value, name="ret_Load") + elif isinstance(value.type.pointee, ir.PointerType): + Loaded: ir.Value = self._load(value, name="ret_deref") + if Loaded.type == ret_type: + value = Loaded + elif isinstance(ret_type, ir.IntType): + value = self.builder.ptrtoint(Loaded, ret_type, name="ret_ptrtoint") + elif isinstance(ret_type, ir.IntType) and isinstance(value.type, ir.IntType): + if value.type.width < ret_type.width: + if value.type.width == 1: + # i1 是布尔值(来自 icmp/fcmp/__contains__),无符号语义,必须 zext + value = self.builder.zext(value, ret_type, name="ret_zext_bool") + else: + is_unsigned: bool = id(value) in self._unsigned_results + if is_unsigned: + value = self.builder.zext(value, ret_type, name="ret_zext") + else: + value = self.builder.sext(value, ret_type, name="ret_sext") + elif value.type.width > ret_type.width: + value = self.builder.trunc(value, ret_type, name="ret_trunc") + elif isinstance(ret_type, ir.PointerType) and isinstance(value.type, ir.IntType): + value = self.builder.inttoptr(value, ret_type, name="ret_inttoptr") + elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)) and isinstance(value.type, ir.IntType): + value = self.builder.sitofp(value, ret_type, name="ret_int2float") + elif isinstance(ret_type, ir.IntType) and isinstance(value.type, (ir.FloatType, ir.DoubleType)): + value = self.builder.fptosi(value, ret_type, name="ret_float2int") + elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)) and isinstance(value.type, (ir.FloatType, ir.DoubleType)): + if ret_type != value.type: + if isinstance(value.type, ir.FloatType) and isinstance(ret_type, ir.DoubleType): + value = self.builder.fpext(value, ret_type, name="ret_fpext") + elif isinstance(value.type, ir.DoubleType) and isinstance(ret_type, ir.FloatType): + value = self.builder.fptrunc(value, ret_type, name="ret_fptrunc") + self.builder.ret(value) + + def _emit_printf(self, args: list[ir.Value], unsigned_flags: list[bool] | None = None, sep: str = " ", end: str = "\n") -> ir.Value | None: + if not args: + return None + if unsigned_flags is None: + unsigned_flags = [] + format_str: str = "" + cast_args: list[ir.Value] = [] + for i, arg in enumerate(args): + is_u: bool = i < len(unsigned_flags) and unsigned_flags[i] + if isinstance(arg.type, ir.IntType): + if arg.type.width == 1: + # i1 是布尔值(来自 icmp/fcmp/__contains__),zext 到 i32 后用 %d + format_str += "%d" + ext: ir.Value = self.builder.zext(arg, ir.IntType(32), name="bool2int") + cast_args.append(ext) + elif arg.type.width == 8: + format_str += "%c" + ext: ir.Value = self.builder.zext(arg, ir.IntType(32), name="char2int") + cast_args.append(ext) + elif arg.type.width == 64: + format_str += "%llu" if is_u else "%lld" + cast_args.append(arg) + else: + format_str += "%u" if is_u else "%d" + cast_args.append(arg) + elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)): + format_str += "%f" + cast_args.append(arg) + elif isinstance(arg.type, ir.PointerType): + if isinstance(arg.type.pointee, ir.IntType) and arg.type.pointee.width == 8: + format_str += "%s" + cast_args.append(arg) + else: + format_str += "%d" + int_val: ir.Value = self.builder.ptrtoint(arg, ir.IntType(64), name="ptr2int") + trunc_val: ir.Value = self.builder.trunc(int_val, ir.IntType(32), name="trunc") + cast_args.append(trunc_val) + else: + format_str += "%d" + cast_args.append(arg) + if sep is not None and i < len(args) - 1: + format_str += sep + if end is not None: + format_str += end + string_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(format_str.encode('utf-8')) + 1) + string_const: ir.Constant = ir.Constant(string_type, bytearray(format_str + '\x00', 'utf-8')) + global_var: ir.GlobalVariable = ir.GlobalVariable(self.module, string_type, name=f"str_const_{self.string_const_counter}") + self.string_const_counter += 1 + global_var.initializer = string_const + global_var.linkage = 'internal' + format_ptr: ir.Value = self.builder.bitcast(global_var, ir.PointerType(ir.IntType(8))) + call_args: list[ir.Value] = [format_ptr] + cast_args + printf_func: ir.Function | None = self.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + return self.builder.call(printf_func, call_args, name="call_printf") \ No newline at end of file diff --git a/App/lib/core/LLVMCG/FuncGen.py b/App/lib/core/LLVMCG/FuncGen.py new file mode 100644 index 0000000..dc743c6 --- /dev/null +++ b/App/lib/core/LLVMCG/FuncGen.py @@ -0,0 +1,524 @@ +from __future__ import annotations +from typing import Any +import ast +import os +import re +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode + + +class FuncGenMixin: + + def setup_from_symbol_table(self, SymbolTable: Any) -> None: + self.SymbolTable: Any = SymbolTable + for name, info in SymbolTable.items(): + if isinstance(info, CTypeInfo): + # CTypeInfo 对象:struct/union 由 StructGen 处理,此处只处理 dict 格式 + continue + if not isinstance(info, dict): + if hasattr(info, 'get'): + info = dict(info) if hasattr(info, '__iter__') else {} + else: + continue + TypeKind: str = info.get('type', '') + if TypeKind == 'struct': + ClassName: str = name + self.class_methods[ClassName] = [] + self.class_members[ClassName] = [] + members: dict[str, Any] = info.get('members', {}) + if members: + for MemberName, MemberInfo in members.items(): + if isinstance(MemberInfo, dict): + MemberType: ir.Type = self._type_str_to_llvm(MemberInfo.get('type', 'int'), MemberInfo.get('IsPtr', False)) + self.class_members[ClassName].append((MemberName, MemberType)) + elif isinstance(MemberInfo, CTypeInfo): + MemberType: ir.Type = self._ctype_to_llvm(MemberInfo) + if MemberType is None: + MemberType = self._type_str_to_llvm(MemberInfo.ToString(), MemberInfo.IsPtr) + self.class_members[ClassName].append((MemberName, MemberType)) + self._generate_structs() + self._create_Vtable_globals() + + def register_method(self, ClassName: str, MethodName: str) -> None: + if ClassName not in self.class_methods: + self.class_methods[ClassName] = [] + if ClassName not in self.class_members: + self.class_members[ClassName] = [] + self._generate_structs() + self._create_Vtable_globals() + # 构造函数(__new__/__init__/__before_init__)不放入 vtable, + # 避免子类与基类 vtable 大小不一致导致虚分派索引偏移 + _short_name: str = MethodName.split('.')[-1] if '.' in MethodName else MethodName + if _short_name in ('__new__', '__init__', '__before_init__'): + return + if MethodName not in self.class_methods[ClassName]: + self.class_methods[ClassName].append(MethodName) + + def _apply_auto_addr(self, func_name: str, args: list[ir.Value]) -> list[ir.Value]: + """对函数参数应用自动取地址逻辑(t.CNeedPtr / t.CAutoPtr)。 + + 在调用前对标记为 auto-addr 的参数自动取地址: + - 'need' (t.CNeedPtr): 仅对非指针值取地址(alloca + store + get pointer) + - 'always' (t.CAutoPtr): 无条件取地址 + """ + modes: list[str | None] | None = self._auto_addr_params.get(func_name) + if not modes: + return args + new_args: list[ir.Value] = list(args) + for i, mode in enumerate(modes): + if i >= len(new_args) or mode is None: + continue + arg: ir.Value = new_args[i] + is_ptr: bool = isinstance(arg.type, ir.PointerType) + if mode == 'always' or (mode == 'need' and not is_ptr): + # alloca + store + get pointer + slot: ir.Value = self.builder.alloca(arg.type) + self.builder.store(arg, slot) + new_args[i] = slot + return new_args + + def _adjust_args(self, args: list[ir.Value], func: ir.Function) -> list[ir.Value]: + adjusted: list[ir.Value] = [] + for i, (arg, param) in enumerate(zip(args, func.args)): + if arg.type != param.type: + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): + # C 语言中数组参数退化为指针,不做 load 而是退化为元素指针 + if isinstance(param.type, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") + adjusted.append(elem_ptr) + else: + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + continue + try: + if isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.PointerType): + if isinstance(param.type.pointee, ir.IntType) and param.type.pointee.width == 8 and arg.type.width == 8: + # 当参数是 i8(字符值)而函数期望 i8*(字符串指针)时 + # 为字符值分配内存,创建 null-terminated 字符串 + tmp: ir.AllocaInstr = self._alloca(ir.ArrayType(ir.IntType(8), 2), name=f"char2str_arg_{i}") + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + char_ptr: ir.Value = self.builder.gep(tmp, [zero, zero], name=f"char2str_ptr_{i}") + self._store(arg, char_ptr) + null_ptr: ir.Value = self.builder.gep(tmp, [zero, ir.Constant(ir.IntType(32), 1)], name=f"char2str_null_{i}") + self._store(ir.Constant(ir.IntType(8), 0), null_ptr) + adjusted.append(char_ptr) + else: + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): + # 当参数是指针而函数期望整数时,使用 ptrtoint + adjusted.append(self.builder.ptrtoint(arg, param.type, name=f"ptrtoint_arg_{i}")) + else: + if arg.type.width < 32: + arg = self.builder.zext(arg, ir.IntType(32), name=f"zext_arg_{i}") + adjusted.append(self.builder.inttoptr(arg, param.type, name=f"inttoptr_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): + # 当参数是指针而函数期望整数时,使用 ptrtoint + adjusted.append(self.builder.ptrtoint(arg, param.type, name=f"ptrtoint_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType): + if isinstance(arg.type.pointee, ir.PointerType) and isinstance(param.type.pointee, ir.IntType): + Loaded: ir.Value = self._load(arg, name=f"Load_arg_{i}") + adjusted.append(Loaded) + elif isinstance(arg.type.pointee, ir.ArrayType) and isinstance(param.type.pointee, (ir.IntType, ir.FloatType, ir.DoubleType)): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") + if elem_ptr.type != param.type: + adjusted.append(self.builder.bitcast(elem_ptr, param.type, name=f"cast_arg_{i}")) + else: + adjusted.append(elem_ptr) + elif isinstance(arg.type.pointee, ir.ArrayType) and isinstance(param.type.pointee, ir.PointerType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") + if elem_ptr.type != param.type: + adjusted.append(self.builder.bitcast(elem_ptr, param.type, name=f"cast_arg_{i}")) + else: + adjusted.append(elem_ptr) + else: + adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) + else: + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + elif isinstance(arg.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + tmp: ir.AllocaInstr = self._alloca(param.type, name=f"temp_arg_{i}") + Loaded: ir.Value = self.builder.bitcast(arg, ir.PointerType(param.type), name=f"cast_arg_{i}") + Loaded_val: ir.Value = self._load(Loaded, name=f"Load_arg_{i}") + self._store(Loaded_val, tmp) + adjusted.append(self._load(tmp, name=f"reLoad_arg_{i}")) + else: + tmp: ir.AllocaInstr = self._alloca(param.type, name=f"temp_arg_{i}") + cast_ptr: ir.Value = self.builder.bitcast(arg, ir.PointerType(param.type), name=f"cast_arg_{i}") + Loaded_val: ir.Value = self._load(cast_ptr, name=f"Load_arg_{i}") + self._store(Loaded_val, tmp) + adjusted.append(self._load(tmp, name=f"reLoad_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and arg.type.pointee == param.type: + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + elif isinstance(param.type, ir.PointerType) and param.type.pointee == arg.type: + tmp: ir.AllocaInstr = self._alloca(arg.type, name=f"temp_arg_{i}") + self._store(arg, tmp) + adjusted.append(tmp) + elif isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.IntType): + if arg.type.width < param.type.width: + # 整数扩展:使用 sext(符号扩展)以正确处理负数 + adjusted.append(self.builder.sext(arg, param.type, name=f"sext_arg_{i}")) + else: + adjusted.append(self.builder.trunc(arg, param.type, name=f"trunc_arg_{i}")) + elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)) and isinstance(param.type, (ir.FloatType, ir.DoubleType)): + if isinstance(arg.type, ir.DoubleType) and isinstance(param.type, ir.FloatType): + adjusted.append(self.builder.fptrunc(arg, param.type, name=f"fptrunc_arg_{i}")) + elif isinstance(arg.type, ir.FloatType) and isinstance(param.type, ir.DoubleType): + adjusted.append(self.builder.fpext(arg, param.type, name=f"fpext_arg_{i}")) + else: + adjusted.append(arg) + elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)) and isinstance(param.type, ir.IntType): + adjusted.append(self.builder.fptosi(arg, param.type, name=f"fptosi_arg_{i}")) + elif isinstance(arg.type, ir.IntType) and isinstance(param.type, (ir.FloatType, ir.DoubleType)): + adjusted.append(self.builder.sitofp(arg, param.type, name=f"sitofp_arg_{i}")) + elif isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.PointerType): + # 整数转指针:inttoptr + if arg.type.width < 64: + i64_val: ir.Value = self.builder.sext(arg, ir.IntType(64), name=f"sext_arg_{i}") + adjusted.append(self.builder.inttoptr(i64_val, param.type, name=f"inttoptr_arg_{i}")) + else: + adjusted.append(self.builder.inttoptr(arg, param.type, name=f"inttoptr_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): + # 指针转整数:ptrtoint + i64_val: ir.Value = self.builder.ptrtoint(arg, ir.IntType(64), name=f"ptrtoint_arg_{i}") + if i64_val.type.width > param.type.width: + adjusted.append(self.builder.trunc(i64_val, param.type, name=f"trunc_arg_{i}")) + elif i64_val.type.width < param.type.width: + adjusted.append(self.builder.sext(i64_val, param.type, name=f"sext_arg_{i}")) + else: + adjusted.append(i64_val) + else: + adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) + except Exception: # 参数类型调整失败时使用简化回退逻辑 + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + else: + adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and arg.type.pointee == param.type: + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + elif isinstance(param.type, ir.PointerType) and param.type.pointee == arg.type: + tmp: ir.AllocaInstr = self._alloca(arg.type, name=f"temp_arg_{i}") + self._store(arg, tmp) + adjusted.append(tmp) + elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType): + # 任意指针到指针的类型转换(如 struct* -> i8*) + adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) + else: + adjusted.append(arg) + else: + adjusted.append(arg) + while len(adjusted) < len(func.args): + missing_idx: int = len(adjusted) + param: ir.Argument = func.args[missing_idx] + param_type: ir.Type = param.type if hasattr(param, 'type') else param + if isinstance(param_type, ir.PointerType): + adjusted.append(ir.Constant(param_type, None)) + elif isinstance(param_type, ir.IntType): + adjusted.append(ir.Constant(param_type, 0)) + elif isinstance(param_type, (ir.FloatType, ir.DoubleType)): + adjusted.append(ir.Constant(param_type, 0.0)) + else: + adjusted.append(ir.Constant(ir.IntType(32), 0)) + if func.type.pointee.var_arg: + for i in range(len(func.args), len(args)): + arg: ir.Value = args[i] + adjusted.append(arg) + return adjusted + + def _get_member_offset(self, field_name: str, ClassName: str | None = None) -> int | None: + # 治本修复:同名类 sha1 冲突检测。 + # 当 class_sha1_map[ClassName] 的 sha1 与 Gen.structs[ClassName] 的 sha1 不一致时, + # class_members[ClassName] 可能存储了错误模块的同名类成员。 + # 此处检测冲突,加载正确模块的 class_members 并以 sha1 前缀全名缓存。 + _ResolvedClass: str = ClassName if ClassName else '' + if ClassName: + _ClassSha1Map: dict[str, str] = getattr(self, 'class_sha1_map', {}) + _ExpectedSha1: str | None = _ClassSha1Map.get(ClassName) + if _ExpectedSha1: + _ExistingStruct = self.structs.get(ClassName) + if isinstance(_ExistingStruct, ir.IdentifiedStructType): + _ExistingSha1: str = self._extract_struct_sha1(_ExistingStruct) + if _ExistingSha1 and _ExistingSha1 != _ExpectedSha1: + # sha1 冲突:使用全名查找 class_members + _FullKey: str = f"{_ExpectedSha1}.{ClassName}" + if _FullKey in self.class_members and self.class_members[_FullKey]: + _ResolvedClass = _FullKey + else: + # 从正确 stub 加载 class_members + if self._Trans and hasattr(self._Trans, 'ImportHandler'): + _StubName: str = f"{_ExpectedSha1}.stub.ll" + # 临时保存旧值,清除短名条目以绕过 guard,加载后恢复 + _OldMembers = self.class_members.get(ClassName) + self.class_members[ClassName] = [] + try: + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, _StubName, self) + except Exception: + pass + _NewMembers = self.class_members.get(ClassName) + if _NewMembers: + self.class_members[_FullKey] = _NewMembers + _ResolvedClass = _FullKey + # 恢复旧值(避免影响其他代码对短名的依赖) + if _OldMembers: + self.class_members[ClassName] = _OldMembers + else: + self.class_members.pop(ClassName, None) + has_vtable: bool = ClassName and (ClassName in self.class_vtable or ClassName in self._cross_module_vtable_classes) + base: int = 1 if has_vtable else 0 + if ClassName and ClassName in self.structs: + struct_type: ir.IdentifiedStructType = self.structs[ClassName] + if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None and len(struct_type.elements) > 0: + if ClassName in self.class_members: + expected_fields: int = len(self.class_members[ClassName]) + actual_elements: int = len(struct_type.elements) + if base > 0 and actual_elements == expected_fields: + base = 0 + elif base == 0 and actual_elements == expected_fields + 1: + # 跨模块类可能未被检测为 CVTable(如 Name 继承 AST@t.CVTable), + # 但实际 struct 首元素是 vtable ptr → 需 base=1。 + # 但若类有继承(class_parent 有值),多出的 1 可能是继承字段而非 vtable, + # 需检查 struct 首元素类型:i8*/i8** 为 vtable,其他为继承字段。 + _first_elem = struct_type.elements[0] if actual_elements > 0 else None + _has_parent = self.class_parent.get(ClassName) is not None + _is_vtable_ptr = (isinstance(_first_elem, ir.PointerType) and + isinstance(_first_elem.pointee, ir.IntType) and _first_elem.pointee.width == 8) + if not _has_parent or _is_vtable_ptr: + base = 1 + if _ResolvedClass and _ResolvedClass in self.class_members: + for i, (name, _) in enumerate(self.class_members[_ResolvedClass]): + if name == field_name: + return base + i + # 继承链回退:当字段不在当前类的 class_members 中时(常见于跨模块导入 + # 父类未加载,字段被填充为 __inherited_N 占位名),沿 class_parent 链向上查找。 + # 子类 struct 布局为 [vtable?] + [基类字段...] + [子类字段...], + # 基类字段在子类 struct 中的起始偏移即为 base(子类), + # 因此祖先 class_members 中的索引 i 对应子类 struct 中的偏移 base + i。 + if ClassName and field_name: + _parent: str | None = self.class_parent.get(ClassName) + _visited: set = {ClassName} if ClassName else set() + while _parent and _parent not in _visited: + _visited.add(_parent) + # 跳过 __inherited_N 占位条目,只在真实字段名中查找 + if _parent in self.class_members: + _members: list = self.class_members[_parent] + _has_placeholder: bool = any(n.startswith('__inherited_') for n, _ in _members) + if not _has_placeholder: + for i, (name, _) in enumerate(_members): + if name == field_name: + return base + i + # 父类 class_members 未加载或含占位条目,尝试从 stub 加载真实字段 + if self._Trans and hasattr(self._Trans, 'ImportHandler'): + sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {}) + for mod_name, mod_sha1 in sha1_map.items(): + stub_name: str = f"{mod_sha1}.stub.ll" + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(_parent, stub_name, self) + if _parent in self.class_members and not any(n.startswith('__inherited_') for n, _ in self.class_members[_parent]): + for i, (name, _) in enumerate(self.class_members[_parent]): + if name == field_name: + return base + i + break + _parent = self.class_parent.get(_parent) + short_name: str | None = ClassName.split('.')[-1] if ClassName and '.' in ClassName else None + if short_name and short_name in self.class_members: + for i, (name, _) in enumerate(self.class_members[short_name]): + if name == field_name: + return base + i + if ClassName and field_name: + if ClassName not in self.class_members or field_name not in [n for n, _ in self.class_members.get(ClassName, [])]: + if self._Trans and hasattr(self._Trans, 'ImportHandler'): + sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {}) + for mod_name, mod_sha1 in sha1_map.items(): + stub_name: str = f"{mod_sha1}.stub.ll" + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, stub_name, self) + if ClassName in self.class_members and len(self.class_members[ClassName]) > 0: + for i, (name, _) in enumerate(self.class_members[ClassName]): + if name == field_name: + return base + i + break + if short_name: + for mod_name, mod_sha1 in sha1_map.items(): + stub_name: str = f"{mod_sha1}.stub.ll" + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_name, stub_name, self) + if short_name in self.class_members and len(self.class_members[short_name]) > 0: + for i, (name, _) in enumerate(self.class_members[short_name]): + if name == field_name: + return base + i + break + if ClassName and field_name: + for cn_key in self.class_members: + if cn_key == ClassName or cn_key.endswith(f'.{ClassName}') or (short_name and (cn_key == short_name or cn_key.endswith(f'.{short_name}'))): + for i, (name, _) in enumerate(self.class_members[cn_key]): + if name == field_name: + return base + i + # REnum 变体搜索: ClassName 是 REnum(如 MetaType),成员注册在变体结构 + # (MetaType_Var, MetaType_Concrete, MetaType_App)中。变体与 REnum 主结构 + # 共享 max_variant_struct 布局,偏移一致(__tag 在偏移 0,字段从偏移 1 开始) + if ClassName and field_name: + variant_prefix: str = f"{ClassName}_" + for cn_key in self.class_members: + if cn_key.startswith(variant_prefix): + for i, (name, _) in enumerate(self.class_members[cn_key]): + if name == field_name: + return i + if short_name: + variant_prefix = f"{short_name}_" + for cn_key in self.class_members: + if cn_key.startswith(variant_prefix): + for i, (name, _) in enumerate(self.class_members[cn_key]): + if name == field_name: + return i + if ClassName and ClassName in self.structs: + struct_type: ir.IdentifiedStructType = self.structs[ClassName] + if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None and len(struct_type.elements) > 0: + for cn_key, members in self.class_members.items(): + if cn_key == short_name or (short_name and cn_key.endswith(f'.{short_name}')): + for i, (name, _) in enumerate(members): + if name == field_name and i < len(struct_type.elements): + return i + break + else: + if short_name: + for cn_key, members in self.class_members.items(): + sn: str = cn_key.split('.')[-1] if '.' in cn_key else cn_key + if sn == short_name: + for i, (name, _) in enumerate(members): + if name == field_name and i < len(struct_type.elements): + return i + break + if ClassName and field_name and ClassName not in self.class_members: + if self._Trans and hasattr(self._Trans, 'ImportHandler'): + for temp_dir_candidate in [getattr(self, '_temp_dir', None)]: + if temp_dir_candidate and os.path.isdir(temp_dir_candidate): + for pyi_file in os.listdir(temp_dir_candidate): + if pyi_file.endswith('.pyi'): + stub_name: str = pyi_file.replace('.pyi', '.stub.ll') + short_cn: str = short_name if short_name else ClassName + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_cn, stub_name, self) + if short_cn in self.class_members and len(self.class_members[short_cn]) > 0: + for i, (name, _) in enumerate(self.class_members[short_cn]): + if name == field_name: + return base + i + break + if ClassName and ClassName in self.structs and (ClassName not in self.class_members or len(self.class_members.get(ClassName, [])) == 0): + struct_type: ir.IdentifiedStructType = self.structs[ClassName] + if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None: + if self._Trans and hasattr(self._Trans, 'ClassHandler'): + class_handler: Any = self._Trans.ClassHandler + if self._current_tree: + for node in ast.iter_child_nodes(self._current_tree): + if isinstance(node, ast.ClassDef) and (node.name == ClassName or node.name == short_name): + if node.name not in self.class_members: + self.class_members[node.name] = [] + if len(self.class_members[node.name]) == 0: + class_handler._PreRegisterClassMembers(node, self) + if ClassName in self.class_members: + for i, (name, _) in enumerate(self.class_members[ClassName]): + if name == field_name: + return base + i + if short_name and short_name in self.class_members: + for i, (name, _) in enumerate(self.class_members[short_name]): + if name == field_name: + return base + i + break + # 最后回退:当继承链回退失败但 struct body 已加载时, + # 从 struct elements 数和 class_members 数推断继承字段偏移。 + # 继承字段在 struct 开头(base 之后),此 fallback 仅适用于单继承字段的情况 + # (如 GSListNode[T] 的 Next 字段)。子类 struct 布局: + # [vtable?] + [基类字段...] + [子类字段...] + # 当 class_members 只含子类自身字段、基类 class_members 未加载时, + # 继承字段数 = struct_elements - class_members_count, + # 若该值 == 1,则所查字段(如 Next)的偏移 = base + 0。 + if ClassName and field_name: + _st_final = self.structs.get(ClassName) + if isinstance(_st_final, ir.IdentifiedStructType) and _st_final.elements is not None: + _num_elements_final: int = len(_st_final.elements) + _members_final: list = self.class_members.get(ClassName, []) + _num_members_final: int = len(_members_final) + if _num_elements_final > _num_members_final: + _num_inherited_final: int = _num_elements_final - _num_members_final + if _num_inherited_final == 1: + # 单继承字段,偏移 = base(vtable 之后第一个位置) + return base + 0 + return None + + def _resolve_class_for_var(self, var_name: str) -> str | None: + for ClassName in self.class_methods: + if var_name == ClassName or var_name.startswith(f"__with_{ClassName}_"): + return ClassName + if var_name in self.variables: + var: ir.Value = self.variables[var_name] + if isinstance(var.type, ir.PointerType): + for ClassName, struct_type in self.structs.items(): + if var.type.pointee == struct_type: + return ClassName + if isinstance(var.type.pointee, ir.PointerType) and var.type.pointee.pointee == struct_type: + return ClassName + return None + + def _infer_return_type(self, FuncName: str) -> ir.Type: + if FuncName in self.functions: + return self.functions[FuncName].type.pointee.return_type + # 也尝试混淆后的名称 + mangled: str = self._mangle_func_name(FuncName) + if mangled != FuncName and mangled in self.functions: + return self.functions[mangled].type.pointee.return_type + if FuncName in self.known_return_types: + return self.known_return_types[FuncName] + if mangled != FuncName and mangled in self.known_return_types: + return self.known_return_types[mangled] + for ClassName, methods in self.class_methods.items(): + for method in methods: + if FuncName == method: + return ir.VoidType() + if self._import_handler_ref: + try: + # 先用原始名称查找,再用混淆名称查找 + stub_func_type: Any = self._import_handler_ref._LookupStubFuncType(FuncName, self) + if stub_func_type is None and mangled != FuncName: + stub_func_type = self._import_handler_ref._LookupStubFuncType(mangled, self) + if stub_func_type and hasattr(stub_func_type, 'return_type'): + return stub_func_type.return_type + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"查找桩函数返回类型失败: {_e}", "Exception") + return ir.IntType(32) + + def _get_or_declare_func(self, name: str, func_type: ir.FunctionType) -> ir.Function: + for fname, fobj in self.functions.items(): + if fname == name: + return fobj + if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname): + return fobj + func_decl: ir.Function = ir.Function(self.module, func_type, name=name) + self.functions[name] = func_decl + return func_decl + + def get_or_declare_c_func(self, name: str, fallback_type: ir.FunctionType | None = None) -> ir.Function | None: + if name in self.functions: + return self.functions[name] + for fname, fobj in self.functions.items(): + if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname): + return fobj + stub_func_type: ir.FunctionType | None = None + if self._import_handler_ref: + try: + stub_func_type = self._import_handler_ref._LookupStubFuncType(name, self) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"查找桩函数类型失败: {_e}", "Exception") + if stub_func_type: + func_decl: ir.Function = ir.Function(self.module, stub_func_type, name=name) + self.functions[name] = func_decl + return func_decl + if fallback_type is not None: + func_decl: ir.Function = ir.Function(self.module, fallback_type, name=name) + self.functions[name] = func_decl + return func_decl + return None \ No newline at end of file diff --git a/App/lib/core/LLVMCG/MemoryOps.py b/App/lib/core/LLVMCG/MemoryOps.py new file mode 100644 index 0000000..07003e6 --- /dev/null +++ b/App/lib/core/LLVMCG/MemoryOps.py @@ -0,0 +1,286 @@ +from __future__ import annotations +import llvmlite.ir as ir + + +class MemoryOpsMixin: + + def _register_local_heap_ptr(self, val: ir.Value, ClassName: str | None = None, VarName: str | None = None) -> None: + if isinstance(val.type, ir.PointerType): + pointee: ir.Type = val.type.pointee + if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + self._local_heap_ptrs.append((val, 'struct', ClassName)) + elif isinstance(pointee, ir.IntType) and pointee.width == 8: + self._local_heap_ptrs.append((val, 'i8', ClassName)) + if VarName: + self._var_to_heap_ptr[VarName] = val + + def _unregister_local_heap_ptr(self, val: ir.Value) -> None: + self._local_heap_ptrs = [(v, t, cn) for v, t, cn in self._local_heap_ptrs if v is not val] + + def _emit_local_heap_frees(self) -> None: + if not self._local_heap_ptrs or not self.builder: + return + deleted_objects: set[ir.Value] = set() + for ptr, ptr_type, ClassName in self._local_heap_ptrs: + if ptr_type == 'i8': + Loaded_ptr: ir.Value = self.builder.load(ptr, name="load_ptr") + null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + is_null: ir.Value = self.builder.icmp_unsigned('==', Loaded_ptr, null_ptr, name="is_null") + not_null_block: ir.Block = self.builder.append_basic_block(name="not_null") + next_block: ir.Block = self.builder.append_basic_block(name="next") + self.builder.cbranch(is_null, next_block, not_null_block) + self.builder.position_at_end(not_null_block) + if ClassName and self._has_function(f'{ClassName}.__del__'): + cast_ptr: ir.Value + if isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, ir.IntType) and ptr.type.pointee.width == 8: + cast_ptr = self.builder.bitcast(ptr, ir.PointerType(self.structs[ClassName]), name=f"cast_{ClassName}") + else: + cast_ptr = ptr + if cast_ptr not in deleted_objects: + self.builder.call(self._get_function(f'{ClassName}.__del__'), [cast_ptr], name=f"call_{ClassName}.__del__") + deleted_objects.add(cast_ptr) + raw: ir.Value + if ptr_type == 'struct': + raw = self.builder.bitcast(ptr, ir.PointerType(ir.IntType(8)), name="local_free_cast") + elif ptr_type == 'i8': + raw = ptr + else: + continue + free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]) + free_func: ir.Function = self._get_or_declare_func('free', free_type) + self.builder.call(free_func, [raw]) + if ptr_type == 'i8': + self.builder.branch(next_block) + self.builder.position_at_end(next_block) + self._local_heap_ptrs = [] + + def _RegisterTempPtr(self, val: ir.Value) -> None: + if isinstance(val.type, ir.PointerType): + pointee: ir.Type = val.type.pointee + if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + for CN, ST in self.structs.items(): + if pointee == ST: + self._temp_struct_ptrs.append(val) + return + elif isinstance(pointee, ir.IntType) and pointee.width == 8: + self._temp_struct_ptrs.append(val) + + def _UnregisterTempPtr(self, val: ir.Value) -> None: + self._temp_struct_ptrs = [t for t in self._temp_struct_ptrs if t is not val] + + def _EmitTempFrees(self) -> None: + if not self._temp_struct_ptrs or not self.builder: + return + for ptr in self._temp_struct_ptrs: + raw: ir.Value + if isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + raw = self.builder.bitcast(ptr, ir.PointerType(ir.IntType(8)), name="free_cast") + elif isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, ir.IntType) and ptr.type.pointee.width == 8: + raw = ptr + else: + continue + free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]) + free_func: ir.Function = self._get_or_declare_func('free', free_type) + self.builder.call(free_func, [raw]) + self._temp_struct_ptrs = [] + + def _ZeroConst(self, typ: ir.Type) -> ir.Constant: + if isinstance(typ, ir.PointerType): + return ir.Constant(typ, None) + if isinstance(typ, (ir.LiteralStructType, ir.IdentifiedStructType)): + return ir.Constant(typ, None) + if isinstance(typ, ir.ArrayType): + return ir.Constant(typ, None) + return ir.Constant(typ, 0) + + def _alloca(self, typ: ir.Type, name: str = '', align: int | None = None, size: ir.Value | None = None) -> ir.AllocaInstr: + if align is None: + align = self._get_align(typ) + instr: ir.AllocaInstr + if size is not None: + instr = self.builder.alloca(typ, size=size, name=name) + else: + instr = self.builder.alloca(typ, name=name) + if align: + instr.align = align + return instr + + def _allocaEntry(self, typ: ir.Type, name: str = '', align: int | None = None) -> ir.AllocaInstr: + if align is None: + align = self._get_align(typ) + saved_block: ir.Block = self.builder.block + entry_block: ir.Block = self.func.entry_basic_block + instr: ir.AllocaInstr + if entry_block.instructions: + first_instr: ir.Instruction = entry_block.instructions[0] + self.builder.position_before(first_instr) + instr = self.builder.alloca(typ, name=name) + if align: + instr.align = align + self.builder.position_at_end(saved_block) + else: + self.builder.position_at_start(entry_block) + instr = self.builder.alloca(typ, name=name) + if align: + instr.align = align + self.builder.position_at_end(saved_block) + return instr + + def _load(self, ptr: ir.Value, name: str = '', align: int | None = None) -> ir.LoadInstr: + if align is None: + if isinstance(ptr.type, ir.PointerType): + align = self._get_align(ptr.type.pointee) + else: + align = 0 + if isinstance(ptr.type, ir.PointerType): + pointee_type: ir.Type = ptr.type.pointee + if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths: + width: int = self.typedef_int_widths[pointee_type.name] + if width > 0: + actual_type: ir.IntType = ir.IntType(width) + bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load") + return self.builder.load(bitcast_ptr, name=name, align=align) + return self.builder.load(ptr, name=name, align=align) + + def _Load_var(self, name: str) -> ir.Value | None: + if name in self._direct_values: + return self._direct_values[name] + if name in self.variables and self.variables[name] is not None: + VarPtr: ir.Value = self.variables[name] + if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return VarPtr + if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return VarPtr + return self._load(VarPtr, name=name) + if name in self.global_vars and name in self.module.globals: + GVar: ir.GlobalVariable = self.module.globals[name] + self.variables[name] = GVar + if isinstance(GVar, ir.Function): + return GVar + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return GVar + return self._load(GVar, name=name) + if name in self.module.globals: + GVar = self.module.globals[name] + self.variables[name] = GVar + if isinstance(GVar, ir.Function): + return GVar + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return GVar + return self._load(GVar, name=name) + return None + + def _coerce_value(self, val: ir.Value, target_type: ir.Type) -> ir.Value | None: + if val.type == target_type: + return val + if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths: + if isinstance(val.type, ir.IntType): + return val + if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.IntType): + if target_type.width < val.type.width: + return self.builder.trunc(val, target_type, name="trunc_store") + elif val.type.width == 1: + # i1 是布尔值(来自 icmp/fcmp/__contains__ 归一化),无符号语义, + # 必须用 zext 而非 sext,否则 i1 的 1 会被符号扩展为 -1 + return self.builder.zext(val, target_type, name="zext_bool_store") + else: + return self.builder.sext(val, target_type, name="sext_store") + if isinstance(target_type, ir.PointerType) and isinstance(val.type, ir.PointerType): + if isinstance(val, ir.Constant) and val.constant is None: + return ir.Constant(target_type, None) + return self.builder.bitcast(val, target_type, name="ptr_bitcast_store") + if isinstance(target_type, ir.PointerType) and isinstance(val.type, ir.IntType): + if val.type.width < 64: + val = self.builder.zext(val, ir.IntType(64), name="zext_ptr_store") + return self.builder.inttoptr(val, target_type, name="int_to_ptr_store") + if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.PointerType): + if isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == target_type.width: + return self.builder.load(val, name="load_ptr_as_int_store") + if target_type.width <= 8 and isinstance(val.type.pointee, ir.IntType): + Loaded: ir.Value = self.builder.load(val, name="load_ptr_byte_store") + if Loaded.type != target_type: + if isinstance(Loaded.type, ir.IntType) and isinstance(target_type, ir.IntType): + if target_type.width < Loaded.type.width: + return self.builder.trunc(Loaded, target_type, name="trunc_ptr_byte_store") + return self.builder.zext(Loaded, target_type, name="zext_ptr_byte_store") + return Loaded + return self.builder.ptrtoint(val, target_type, name="ptr_to_int_store") + if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType): + return self.builder.fptrunc(val, target_type, name="fptrunc_store") + if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType): + return self.builder.fpext(val, target_type, name="fpext_store") + if isinstance(target_type, ir.IntType) and isinstance(val.type, (ir.FloatType, ir.DoubleType)): + return self.builder.fptosi(val, target_type, name="fptosi_store") + if isinstance(target_type, (ir.FloatType, ir.DoubleType)) and isinstance(val.type, ir.IntType): + return self.builder.sitofp(val, target_type, name="sitofp_store") + if isinstance(target_type, ir.ArrayType) and isinstance(val.type, ir.ArrayType): + if target_type.count == val.type.count and target_type.element == val.type.element: + return val + if (isinstance(target_type.element, ir.IntType) and target_type.element.width == 8 + and isinstance(val.type.element, ir.IntType) and val.type.element.width == 8): + if target_type.count > val.type.count: + padded: list[ir.Constant] = list(val.constant) if hasattr(val, 'constant') else [] + if padded and len(padded) == val.type.count: + padded.extend([ir.Constant(ir.IntType(8), 0)] * (target_type.count - val.type.count)) + return ir.Constant(target_type, padded) + elif target_type.count < val.type.count: + trimmed: list[ir.Constant] = list(val.constant) if hasattr(val, 'constant') else [] + if trimmed and len(trimmed) == val.type.count: + return ir.Constant(target_type, trimmed[:target_type.count]) + if isinstance(val.type, ir.PointerType) and not isinstance(target_type, ir.PointerType): + if val.type.pointee == target_type: + return self.builder.load(val, name="Load_struct_for_store") + if isinstance(val.type.pointee, ir.IdentifiedStructType) and isinstance(target_type, ir.IdentifiedStructType): + if val.type.pointee.name == target_type.name: + Loaded: ir.Value = self.builder.load(val, name="Load_struct_for_store") + if Loaded.type == target_type: + return Loaded + return None + + def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None) -> ir.StoreInstr: + if align is None: + if isinstance(ptr.type, ir.PointerType): + align = self._get_align(ptr.type.pointee) + else: + align = 0 + if isinstance(ptr.type, ir.PointerType): + target_type: ir.Type = ptr.type.pointee + if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths: + if isinstance(val.type, ir.IntType): + width: int = self.typedef_int_widths[target_type.name] + if width > 0: + actual_type: ir.IntType = ir.IntType(width) + bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast") + if val.type.width != actual_type.width: + if val.type.width > actual_type.width: + val = self.builder.trunc(val, actual_type, name="trunc_typedef") + else: + val = self.builder.zext(val, actual_type, name="zext_typedef") + return self.builder.store(val, bitcast_ptr, align=align) + if val.type != target_type: + if isinstance(val, ir.Constant) and val.constant is None: + if isinstance(target_type, ir.PointerType): + val = ir.Constant(target_type, None) + elif isinstance(target_type, ir.IdentifiedStructType): + val = ir.Constant(ir.PointerType(target_type), None) + ptr = self.builder.bitcast(ptr, ir.PointerType(ir.PointerType(target_type)), name="null_struct_ptr_cast") + else: + coerced: ir.Value | None = self._coerce_value(val, target_type) + if coerced is not None: + val = coerced + else: + src_info: str = self._get_node_info() + raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}") + return self.builder.store(val, ptr, align=align) + + def _get_var_ptr(self, name: str) -> ir.Value | None: + if name in self._reg_values: + val: ir.Value = self._reg_values[name] + var: ir.AllocaInstr = self._alloca(val.type, name=name) + self._store(val, var) + self.variables[name] = var + del self._reg_values[name] + return var + if name in self.variables and self.variables[name] is not None: + return self.variables[name] + return None \ No newline at end of file diff --git a/App/lib/core/LLVMCG/StructGen.py b/App/lib/core/LLVMCG/StructGen.py new file mode 100644 index 0000000..13098bb --- /dev/null +++ b/App/lib/core/LLVMCG/StructGen.py @@ -0,0 +1,629 @@ +from __future__ import annotations +from typing import Any +import re +import ast +import llvmlite.ir as ir +from llvmlite.ir.values import _Undefined +from lib.includes import t as _t + + +class StructGenMixin: + + def _get_align(self, llvm_type: ir.Type) -> int: + if isinstance(llvm_type, ir.IntType): + return max(1, llvm_type.width // 8) + if isinstance(llvm_type, ir.FloatType): + return 4 + if isinstance(llvm_type, ir.DoubleType): + return 8 + if isinstance(llvm_type, ir.PointerType): + return self.ptr_size + if isinstance(llvm_type, ir.ArrayType): + return self._get_align(llvm_type.element) + if isinstance(llvm_type, ir.LiteralStructType): + return max((self._get_align(f) for f in llvm_type.elements), default=1) + if isinstance(llvm_type, ir.IdentifiedStructType): + if llvm_type.elements is None or len(llvm_type.elements) == 0: + return self.ptr_size # opaque struct, assume pointer alignment + return max((self._get_align(f) for f in llvm_type.elements), default=1) + if isinstance(llvm_type, ir.VoidType): + return 0 + return 4 + + def _resolve_opaque_struct(self, llvm_type: ir.Type) -> ir.Type: + """尝试从 Gen.structs 中查找已解析的同名 struct,解决跨模块 struct 类型对象不一致的问题""" + if not isinstance(llvm_type, ir.IdentifiedStructType): + return llvm_type + if llvm_type.elements is not None and len(llvm_type.elements) > 0: + return llvm_type + # opaque struct,尝试通过名称在 Gen.structs 中查找已解析的版本 + st_name: str + try: + st_name = llvm_type.name + except Exception: + return llvm_type + if not st_name: + return llvm_type + # 提取短名称 + short_name: str = st_name + if '.' in st_name: + short_name = st_name.split('.', 1)[1] + # 尝试短名称查找 + if short_name in self.structs: + resolved: ir.IdentifiedStructType = self.structs[short_name] + if isinstance(resolved, ir.IdentifiedStructType) and resolved.elements is not None and len(resolved.elements) > 0: + return resolved + # 尝试全名查找 + if st_name in self.structs: + resolved: ir.IdentifiedStructType = self.structs[st_name] + if isinstance(resolved, ir.IdentifiedStructType) and resolved.elements is not None and len(resolved.elements) > 0: + return resolved + # 尝试通过导入处理器从 stub 文件加载 struct 定义 + import_handler: Any = self._import_handler_ref + if import_handler is not None: + try: + import_handler._TryLoadStructFromStub(short_name, self) + except Exception: + pass + # 加载后再次查找 + if short_name in self.structs: + resolved = self.structs[short_name] + if isinstance(resolved, ir.IdentifiedStructType) and resolved.elements is not None and len(resolved.elements) > 0: + return resolved + if st_name in self.structs: + resolved = self.structs[st_name] + if isinstance(resolved, ir.IdentifiedStructType) and resolved.elements is not None and len(resolved.elements) > 0: + return resolved + return llvm_type + + def _get_struct_size(self, llvm_type: ir.Type) -> int: + if isinstance(llvm_type, ir.IntType): + return max(1, llvm_type.width // 8) + if isinstance(llvm_type, ir.FloatType): + return 4 + if isinstance(llvm_type, ir.DoubleType): + return 8 + if isinstance(llvm_type, ir.PointerType): + return self.ptr_size + if isinstance(llvm_type, ir.ArrayType): + return self._get_struct_size(llvm_type.element) * llvm_type.count + if isinstance(llvm_type, ir.LiteralStructType): + total: int = 0 + max_align: int = 1 + for f in llvm_type.fields: + fa: int = self._get_align(f) + fs: int = self._get_struct_size(f) + max_align = max(max_align, fa) + if fa > 1: + total = (total + fa - 1) // fa * fa + total += fs + if max_align > 1: + total = (total + max_align - 1) // max_align * max_align + return total + if isinstance(llvm_type, ir.IdentifiedStructType): + # 尝试解析 opaque struct 为已注册的版本 + llvm_type = self._resolve_opaque_struct(llvm_type) + if llvm_type.elements is None or len(llvm_type.elements) == 0: + return self.ptr_size # opaque struct, assume pointer size + total: int = 0 + max_align: int = 1 + for f in llvm_type.elements: + fa: int = self._get_align(f) + fs: int = self._get_struct_size(f) + max_align = max(max_align, fa) + if fa > 1: + total = (total + fa - 1) // fa * fa + total += fs + if max_align > 1: + total = (total + max_align - 1) // max_align * max_align + return total + return 4 + + @staticmethod + def _strip_unnecessary_quotes(ir_str: str) -> str: + def _unquote(m: re.Match) -> str: + prefix: str = m.group(1) + name: str = m.group(2) + if '.' in name: + return m.group(0) + if re.match(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$', name): + return prefix + name + return m.group(0) + return re.sub(r'([@%])"([^"]+)"', _unquote, ir_str) + + def finalize(self) -> str: + self._set_Vtable_initializers() + self._fix_global_var_init() + ir_text: str + try: + ir_text = str(self.module) + except TypeError as e: + for func in self.module.functions: + for block in func.blocks: + for instr in block.instructions: + try: + str(instr) + except TypeError as te: + pass + raise + ir_text = self._strip_unnecessary_quotes(ir_text) + struct_defs: list[str] = [] + struct_names: set[str] = set() + for ClassName, struct_type in self.structs.items(): + if isinstance(struct_type, ir.IdentifiedStructType): + sname: str = struct_type.name + sname_ir: str + if sname[0].isdigit() or '.' in sname or not sname.replace('_', '').replace('.', '').isalnum(): + sname_ir = f'%"{sname}"' + else: + sname_ir = f'%{sname}' + if struct_type.elements is None or len(struct_type.elements) == 0: + if ClassName not in self.typedef_int_widths: + struct_names.add(sname) + struct_defs.append(f'{sname_ir} = type opaque') + continue + elems: str = ', '.join(str(e) for e in struct_type.elements) + if struct_type.packed: + struct_defs.append(f'{sname_ir} = type <{{ {elems} }}>') + else: + struct_defs.append(f'{sname_ir} = type {{ {elems} }}') + struct_names.add(sname) + if struct_defs: + lines: list[str] = ir_text.split('\n') + filtered: list[str] = [] + for line in lines: + stripped: str = line.strip() + skip: bool = False + for name in struct_names: + patterns: list[str] + if name[0].isdigit() or '.' in name or not name.replace('_', '').replace('.', '').isalnum(): + patterns = [f'%"{name}" = type', f'%"{name}"=type', f'%{name} = type', f'%{name}=type'] + else: + patterns = [f'%{name} = type', f'%{name}= type', f'%{name}=type'] + for p in patterns: + if stripped.startswith(p): + skip = True + break + if skip: + break + if not skip: + filtered.append(line) + ir_text = '\n'.join(filtered) + header_end: int = ir_text.find('\ndefine') + if header_end == -1: + header_end = ir_text.find('\ndeclare') + if header_end == -1: + header_end = ir_text.find('\n@') + if header_end == -1: + header_end = len(ir_text) + insert_pos: int = ir_text.rfind('\n', 0, header_end) + if insert_pos == -1: + insert_pos = header_end + struct_block: str = '\n'.join(struct_defs) + '\n' + ir_text = ir_text[:insert_pos + 1] + struct_block + ir_text[insert_pos + 1:] + for name in struct_names: + if name[0].isdigit() or '.' in name: + ir_text = re.sub( + r'%' + re.escape(name) + r'(?=[^a-zA-Z0-9_$\.]|$)', + r'%"' + name + r'"', + ir_text + ) + + return ir_text + + def _generate_structs(self) -> None: + all_classes: set[str] = set(self.class_methods.keys()) | set(self.class_members.keys()) + + preserved_structs: dict[str, ir.IdentifiedStructType] = {} + for name, st in self.structs.items(): + if name not in all_classes: + preserved_structs[name] = st + elif isinstance(st, ir.IdentifiedStructType): + preserved_structs[name] = st + + self.structs.clear() + + for name, st in preserved_structs.items(): + self.structs[name] = st + + # Step 1: 先为所有类创建空的 struct,确保后面可以正确引用 + all_classes = set(self.class_methods.keys()) | set(self.class_members.keys()) + for ClassName in all_classes: + is_packed: bool = ClassName in self.class_packed + if self.SymbolTable: + Entry = self.SymbolTable.lookup(ClassName) + if Entry and Entry.IsExceptionClass: + continue + # 注意:类名可能与 CEnum 成员名冲突(如 ASTKind.Module 枚举成员与 Module 类同名) + # 如果 ClassName 在 class_methods 中,表明是 _EmitClassLlvm 显式处理的常规类, + # 不应被 IsEnum/IsTypedef 检查跳过 + if Entry and Entry.IsEnum and ClassName not in self.class_methods: + if not Entry.IsRenum: + continue + if Entry and Entry.IsTypedef and ClassName not in self.class_methods: + if Entry.BaseType and not isinstance(Entry.BaseType, (type(None),)): + if not isinstance(Entry.BaseType, (_t._CTypedef,)): + continue + if ClassName in self.structs: + if is_packed: + self.structs[ClassName].packed = True + continue + # 修复跨模块类型定义冲突:如果类是从其他模块导入的(已在 class_sha1_map 注册), + # 必须使用导入的 source_sha1 作为前缀,而非本地模块的 sha1。 + # 否则会创建缺少基类字段(如 GSListNode.Next)的本地类型定义, + # 导致字段索引错位 → 访问违规崩溃。 + _imported_sha1: str = self.class_sha1_map.get(ClassName, '') + if _imported_sha1: + mangled_name: str = f"{_imported_sha1}.{ClassName}" + else: + mangled_name: str = self._mangle_name(ClassName) + struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, mangled_name, packed=is_packed) + self.structs[ClassName] = struct_type + + # Step 2: 解析跨模块的成员类型 + for ClassName, annotations in self.class_member_annotations.items(): + if ClassName not in self.class_members: + continue + for i, (member_name, member_type) in enumerate(self.class_members[ClassName]): + if member_name in annotations: + annotation: Any = annotations[member_name] + ResolvedType: ir.Type | None = None + + if isinstance(annotation, ast.Attribute): + ClassTypeName: str = annotation.attr + if ClassTypeName in self.structs: + ResolvedType = self.structs[ClassTypeName] + elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + left: Any = annotation.left + if isinstance(left, ast.Attribute): + ClassTypeName: str = left.attr + if ClassTypeName in self.structs: + ResolvedType = self.structs[ClassTypeName] + + if ResolvedType: + self.class_members[ClassName][i] = (member_name, ResolvedType) + + struct_name_map: dict[str, ir.IdentifiedStructType] = {} + for struct_name, struct in self.structs.items(): + try: + struct_name_map[struct.name] = struct + except AssertionError: + struct_name_map[struct_name] = struct + orig: Any = getattr(struct, '_original_name', None) + if orig: + struct_name_map[orig] = struct + + # Step 3: 更新成员类型,确保使用正确的 mangled struct 引用 + for ClassName in all_classes: + if ClassName not in self.class_members: + continue + for i, (member_name, member_type) in enumerate(self.class_members[ClassName]): + if isinstance(member_type, ir.PointerType): + pointee: ir.Type = member_type.pointee + if isinstance(pointee, ir.IdentifiedStructType): + pname: str | None + try: + pname = pointee.name + except AssertionError: + pname = None + struct: ir.IdentifiedStructType | None = struct_name_map.get(pname) if pname else None + if struct is None: + orig: Any = getattr(pointee, '_original_name', None) + struct = struct_name_map.get(orig) if orig else None + if struct is not None: + self.class_members[ClassName][i] = (member_name, ir.PointerType(struct)) + else: + raw_name: str = pname or '' + if raw_name.startswith('struct.'): + raw_name = raw_name[7:] + new_struct: ir.IdentifiedStructType = self._get_or_create_struct(raw_name) + self.class_members[ClassName][i] = (member_name, ir.PointerType(new_struct)) + elif isinstance(member_type, ir.IdentifiedStructType): + mname: str | None + try: + mname = member_type.name + except AssertionError: + mname = None + struct: ir.IdentifiedStructType | None = struct_name_map.get(mname) if mname else None + if struct is None: + orig: Any = getattr(member_type, '_original_name', None) + struct = struct_name_map.get(orig) if orig else None + if struct is not None: + self.class_members[ClassName][i] = (member_name, struct) + else: + raw_name: str = mname or '' + if raw_name.startswith('struct.'): + raw_name = raw_name[7:] + new_struct: ir.IdentifiedStructType = self._get_or_create_struct(raw_name) + self.class_members[ClassName][i] = (member_name, new_struct) + + # Step 4: 创建最终的 structs 并设置正确的成员类型 + for ClassName in all_classes: + if self.SymbolTable: + Entry = self.SymbolTable.lookup(ClassName) + if Entry and Entry.IsExceptionClass: + continue + # 注意:类名可能与 CEnum 成员名冲突(如 ASTKind.Constant 枚举成员与 Constant 类同名) + # 如果 ClassName 在 class_methods 中,表明是 _EmitClassLlvm 显式处理的常规类, + # 不应被 IsEnum 检查跳过(与 Step 1 的守卫保持一致) + if Entry and Entry.IsEnum and ClassName not in self.class_methods: + if not Entry.IsRenum: + continue + existing_st: ir.IdentifiedStructType | None = self.structs.get(ClassName) + if isinstance(existing_st, ir.IdentifiedStructType) and existing_st.elements is not None and len(existing_st.elements) > 0: + # 治本修复:检查 struct body 是否不完整(元素数少于 class_members + vtable 预期)。 + # 常见于跨模块继承:子类 struct 在父类 class_members 加载前从 stub 生成, + # stub 中的定义可能不完整或类型不正确(如 GSList* 代替 AST*、i8* 代替 i32)。 + # 此处检测到不完整后清除 _elements 和 stub 缓存,让后续代码重新从 stub 加载。 + _has_vtable_precheck: bool = ClassName in self.class_vtable or ClassName in self._cross_module_vtable_classes + if not _has_vtable_precheck: + _p_pre: str | None = self.class_parent.get(ClassName) + while _p_pre: + if _p_pre in self.class_vtable or _p_pre in self._cross_module_vtable_classes: + _has_vtable_precheck = True + break + _p_pre = self.class_parent.get(_p_pre) + _expected_count_pre: int = len(self.class_members.get(ClassName, [])) + (1 if _has_vtable_precheck else 0) + # has_vtable 时,首字段必须是 i8*(vtable 指针)。 + # 若首字段非指针(如 i8 占位符),struct body 不完整,需清除重新加载。 + _vtable_first_ok: bool = True + if _has_vtable_precheck and len(existing_st.elements) > 0: + _vtable_first_ok = isinstance(existing_st.elements[0], ir.PointerType) + # 检测 T* 占位类型(未特化的泛型类型参数)。 + # 当 struct 包含 T* 字段时(如 BasicBlock 继承 GSListNode[BasicBlock] 但 stub 未特化), + # 说明 stub 使用了未特化的基类,需清除重新生成。 + _has_t_placeholder_pre: bool = False + for _elem_pre in existing_st.elements: + if isinstance(_elem_pre, ir.PointerType) and isinstance(_elem_pre.pointee, ir.IdentifiedStructType): + _pname_pre: str = _elem_pre.pointee.name + _short_pre: str = _pname_pre.rsplit('.', 1)[-1] if '.' in _pname_pre else _pname_pre + if _short_pre == 'T': + _has_t_placeholder_pre = True + break + if len(existing_st.elements) >= _expected_count_pre and _vtable_first_ok and not _has_t_placeholder_pre: + if ClassName in self.class_packed and not existing_st.packed: + existing_st.packed = True + continue + # struct body 不完整,清除 _elements 和 stub 缓存以便重新加载 + existing_st._elements = None + _import_handler_pre: Any = self._import_handler_ref + if _import_handler_pre is not None: + _cache_key_pre: tuple = (id(self), ClassName) + if _cache_key_pre in _import_handler_pre._struct_Load_cache: + del _import_handler_pre._struct_Load_cache[_cache_key_pre] + has_vtable: bool = ClassName in self.class_vtable or ClassName in self._cross_module_vtable_classes + if not has_vtable: + p: str | None = self.class_parent.get(ClassName) + while p: + if p in self.class_vtable or p in self._cross_module_vtable_classes: + has_vtable = True + self.class_vtable.add(ClassName) + break + p = self.class_parent.get(p) + has_bitfield: bool = ClassName in self.class_member_bitfields and any( + self.class_member_bitfields[ClassName].get(name, 0) > 0 + for name, _ in self.class_members.get(ClassName, []) + ) + all_members: list[tuple[str, ir.Type]] = self.class_members.get(ClassName, []) + # 跳过正在发射中且成员尚未填充的类(递归特化时避免过早设置空 body) + if not all_members and not has_vtable and ClassName in self._classes_in_progress: + continue + # 字段收集期间触发的嵌套 _generate_structs:class_members 可能不完整, + # set_body 只能调用一次,跳过等字段收集完成后再设置 body + if ClassName in self._collecting_members_set: + continue + all_bitfield: bool = all( + self.class_member_bitfields.get(ClassName, {}).get(name, 0) > 0 + for name, _ in all_members + ) if all_members else False + + mangled_name: str = self._mangle_name(ClassName) + + member_types: list[ir.Type] + if has_bitfield and all_bitfield: + total_bits: int = sum(self.class_member_bitfields.get(ClassName, {}).get(name, 0) + for name, _ in all_members) + storage_type: ir.IntType + if total_bits <= 8: + storage_type = ir.IntType(8) + elif total_bits <= 16: + storage_type = ir.IntType(16) + elif total_bits <= 32: + storage_type = ir.IntType(32) + else: + storage_type = ir.IntType(64) + member_types = [storage_type] + bitfield_offsets: dict[str, int] = {} + current_bit_offset: int = 0 + for name, _ in all_members: + bit_width: int = self.class_member_bitfields.get(ClassName, {}).get(name, 0) + bitfield_offsets[name] = current_bit_offset + current_bit_offset += bit_width + self.class_member_bitoffsets[ClassName] = bitfield_offsets + else: + member_types = [] + if has_vtable: + member_types.append(ir.PointerType(ir.IntType(8))) + if ClassName in self.class_members: + for _, member_type in self.class_members[ClassName]: + if member_type is None: + member_type = ir.IntType(32) + member_types.append(member_type) + else: + if has_vtable: + # has_vtable 但 class_members 为空(跨模块导入类): + # 保持 member_types 为空([i8*] 占位会导致 stub 加载跳过, + # 因为 set_body 后 is_opaque=False)。让 struct 保持 opaque, + # 由 _TryLoadStructFromStub 设置完整 body。 + member_types = [] + else: + # 无 vtable 且 class_members 为空:常见于 register_method 早期 + # 调用 _generate_structs(此时 class_members[ClassName]=[] 但尚未 + # 填充实际字段)。保持 opaque,避免 set_body(*[i8]) 永久损坏 struct + # (llvmlite 的 set_body 只能调用一次)。后续 _generate_structs 调用 + # 会从 class_members 或 stub 设置正确 body。 + member_types = [] + + # 获取之前创建的 struct 并设置成员 + struct_type: ir.IdentifiedStructType = self.structs[ClassName] + if ClassName in self.class_packed: + struct_type.packed = True + # 只在未设置 body 时设置,避免重复定义错误 + if struct_type.elements is None: + # 跨模块 struct 的 class_members 可能不完整(当前模块只解析了部分字段), + # 优先从 stub.ll 加载完整定义。set_body 只能对 opaque struct 调用一次, + # 若用不完整 class_members 设置 body,后续无法修正,会导致 GEP 越界。 + # 但本地定义的类(_classes_in_progress)不从 stub 加载,因为 stub 可能 + # 来自同名导入类,其 vtable 状态与本地定义不同(@t.NoVTable 冲突) + import_handler: Any = self._import_handler_ref + if import_handler is not None and ClassName not in self._classes_in_progress: + try: + # 传入 source_sha1 确保从正确的 stub 文件加载完整定义 + _src_sha1: str = self.class_sha1_map.get(ClassName, '') + import_handler._TryLoadStructFromStub(ClassName, self, source_sha1=_src_sha1 or None) + except Exception: + pass + struct_type = self.structs.get(ClassName, struct_type) + # 检测 stub body 中的 T* 占位类型(未特化的泛型类型参数)。 + # 若 stub 使用了未特化的基类(如 GSListNode 而非 GSListNode[BasicBlock]), + # 清除 _elements 让 set_body 用正确的 class_members 重新设置。 + if struct_type.elements is not None and member_types: + _has_t_placeholder_post: bool = False + for _elem_post in struct_type.elements: + if isinstance(_elem_post, ir.PointerType) and isinstance(_elem_post.pointee, ir.IdentifiedStructType): + _pname_post: str = _elem_post.pointee.name + _short_post: str = _pname_post.rsplit('.', 1)[-1] if '.' in _pname_post else _pname_post + if _short_post == 'T': + _has_t_placeholder_post = True + break + if _has_t_placeholder_post: + struct_type._elements = None + if struct_type.elements is None: + # 防御性检查:member_types 为空时不调用 set_body。 + # set_body(*[]) 会设置 elements=() 使 is_opaque=False, + # 后续无法再次 set_body(llvmlite 限制),导致跨模块类型永久损坏。 + # 根因:register_method 在 class_members[ClassName]=[] 时立即调用 + # _generate_structs,此时 member_types 为空。保持 opaque 状态, + # 让后续 _TryLoadStructFromStub 能正确设置 body。 + if member_types: + struct_type.set_body(*member_types) + + def _create_Vtable_globals(self) -> None: + for ClassName, methods in self.class_methods.items(): + if ClassName in self.Vtables: + continue + if not methods: + continue + if ClassName not in self.class_vtable and ClassName not in self._cross_module_vtable_classes: + continue + # 跳过正在发射中的类:它们的方法列表可能还不完整(如继承方法已添加但自有方法未添加), + # 此时创建 vtable 会导致大小不匹配。这些类的 vtable 会在其 _EmitClassLlvm 完成后 + # 由 _create_vtable_for_class 显式创建,或在模块级 _create_Vtable_globals 调用中创建。 + if ClassName in self._classes_in_progress: + continue + # 跨模块虚表过滤:_precollect_vtable_state 会将所有 includes 的有方法类同时加入 + # _shared_class_vtable 和 _shared_cross_module_vtable,导致 class_vtable 与 + # _cross_module_vtable_classes 内容相同,无法用以区分本模块类与跨模块类。 + # _generate_structs 会为所有 class_methods 的类创建空 struct type,导致 self.structs + # 也包含所有类。过滤条件是 self.functions:只考虑本模块定义的函数(define,有函数体), + # 跳过跨模块的 declare(无函数体)。跨模块类的 declare 通过 HandlesImports 加入 + # self.functions(短名如 "MemManager.alloc"),但其 is_declaration=True。 + # 若只存在 declare 而无 define,说明本模块未定义该类,虚表应由定义模块创建。 + # vtable linkage='internal',跨模块调用通过对象的 vtable 指针(首字段)完成分派, + # 不依赖本模块的 vtable 全局变量。 + has_local_method: bool = False + prefix: str = f"{ClassName}." + for func_name, func_obj in self.functions.items(): + if func_name.startswith(prefix): + # 跳过跨模块的 declare(无函数体),只考虑本模块定义的函数 + if not getattr(func_obj, 'is_declaration', False): + has_local_method = True + break + if not has_local_method: + continue + VtableType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) + Vtable: ir.GlobalVariable = ir.GlobalVariable(self.module, VtableType, name=self._mangle_name(f"{ClassName}_Vtable")) + Vtable.initializer = ir.Constant(VtableType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) + Vtable.linkage = 'internal' + self.Vtables[ClassName] = Vtable + + def _create_vtable_for_class(self, ClassName: str) -> None: + """为单个类创建 vtable(用于 _EmitClassLlvm 中当前类的方法已全部注册后)。""" + methods: list = self.class_methods.get(ClassName, []) + if not methods: + return + if ClassName not in self.class_vtable and ClassName not in self._cross_module_vtable_classes: + return + _vtable_name: str = self._mangle_name(f"{ClassName}_Vtable") + # 如果虚表已在导入阶段创建(方法列表不完整,缺少继承方法),删除旧虚表 + if ClassName in self.Vtables: + _old_vtable = self.Vtables[ClassName] + _old_name = _old_vtable.name + if _old_name in self.module.globals: + del self.module.globals[_old_name] + # llvmlite 的 NameScope._useset 追踪所有已使用的名字, + # del module.globals[name] 不会从 _useset 移除, + # 导致再次创建同名 GlobalVariable 时 scope.register() 报 DuplicatedNameError + self.module.scope._useset.discard(_old_name) + del self.Vtables[ClassName] + # 防御性:如果全局变量已存在但不在 Vtables 字典中,也删除 + if _vtable_name in self.module.globals: + del self.module.globals[_vtable_name] + self.module.scope._useset.discard(_vtable_name) + VtableType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) + Vtable: ir.GlobalVariable = ir.GlobalVariable(self.module, VtableType, name=_vtable_name) + Vtable.initializer = ir.Constant(VtableType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods)) + Vtable.linkage = 'internal' + self.Vtables[ClassName] = Vtable + + def _fix_global_var_init(self) -> None: + for gv_name, gv in list(self.module.globals.items()): + if not isinstance(gv, ir.GlobalVariable): + continue + if gv.linkage in ('external', 'extern_weak'): + continue + needs_fix: bool = False + if gv.initializer is None: + needs_fix = True + elif hasattr(gv.initializer, 'constant') and isinstance(gv.initializer.constant, _Undefined): + needs_fix = True + elif isinstance(gv.initializer, ir.Constant): + if isinstance(gv.initializer.constant, list): + if any(hasattr(c, 'constant') and isinstance(c.constant, _Undefined) for c in gv.initializer.constant): + needs_fix = True + if not needs_fix: + continue + gv.initializer = ir.Constant(gv.value_type, None) + if gv_name.startswith('str_const_') or gv_name.endswith('_Vtable') or gv_name.endswith('_str'): + gv.linkage = 'internal' + + def _set_Vtable_initializers(self) -> None: + for ClassName, methods in self.class_methods.items(): + if ClassName not in self.Vtables: + continue + if not methods: + continue + struct_type: ir.IdentifiedStructType | None = self.structs.get(ClassName) + if struct_type is None: + continue + Vtable: ir.GlobalVariable = self.Vtables[ClassName] + Vtable_entries: list[ir.Constant] = [] + for MethodName in methods: + short_name: str = MethodName.split('.')[-1] if '.' in MethodName else MethodName + full_MethodName: str = f"{ClassName}.{short_name}" + func: ir.Function | None = None + if full_MethodName in self.functions: + func = self.functions[full_MethodName] + if func is None: + parent_cls: str | None = self.class_parent.get(ClassName) + while parent_cls: + parent_full: str = f"{parent_cls}.{short_name}" + if parent_full in self.functions: + func = self.functions[parent_full] + break + parent_cls = self.class_parent.get(parent_cls) + if func is None: + Vtable_entries.append(ir.Constant(ir.PointerType(ir.IntType(8)), None)) + continue + Vtable_entries.append(func.bitcast(ir.PointerType(ir.IntType(8)))) + Vtable.initializer = ir.Constant(Vtable.type.pointee, Vtable_entries) \ No newline at end of file diff --git a/App/lib/core/LLVMCG/TypeConvert.py b/App/lib/core/LLVMCG/TypeConvert.py new file mode 100644 index 0000000..f32d5b9 --- /dev/null +++ b/App/lib/core/LLVMCG/TypeConvert.py @@ -0,0 +1,767 @@ +from __future__ import annotations +from typing import Any +import ast +import re +import logging +import llvmlite.ir as ir +from lib.includes import t as _t +from lib.includes.t import CTypeRegistry +from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo, BuiltinTypeMap +from lib.constants.config import mode as _config_mode +from lib.core.VLogger import get_logger as _vlog +from lib.core.SymbolUtils import IsTModule + + +class TypeConvertMixin: + + # 限定符前缀映射表:(前缀字符串, 去除长度) + # 这些限定符(const/volatile/static/extern/register/inline/export/CAuto 等) + # 仅影响 C 语义,对 LLVM IR 类型本身无影响,统一去除后递归解析内部类型。 + _QUALIFIER_PREFIXES: tuple[tuple[str, int], ...] = ( + ('const ', 6), ('volatile ', 9), ('const_', 6), ('volatile_', 9), + ('static ', 7), ('extern ', 7), ('register ', 9), ('inline ', 7), + ('export ', 7), + ('CStatic ', 8), ('CExtern ', 8), ('CRegister ', 10), + ('CInline ', 8), ('CAuto ', 6), + ) + + def _ctype_to_llvm(self, type_info: _CTypeInfo | str | None) -> ir.Type: + if type_info is None: + return ir.IntType(32) + + if isinstance(type_info, str): + return self._type_str_to_llvm(type_info) + + if not isinstance(type_info, _CTypeInfo): + return ir.IntType(32) + + if type_info.IsFuncPtr: + ret_type: ir.Type = self._ctype_to_llvm(type_info.FuncPtrReturn) if type_info.FuncPtrReturn else ir.VoidType() + param_types: list[ir.Type] = [] + if type_info.FuncPtrParams: + for _name, param_info in type_info.FuncPtrParams: + if isinstance(param_info, _CTypeInfo): + param_types.append(self._ctype_to_llvm(param_info)) + else: + param_types.append(ir.IntType(32)) + fn_type: ir.FunctionType = ir.FunctionType(ret_type, param_types) + return ir.PointerType(fn_type) + + if type_info.IsState and type_info.PtrCount == 0 and not type_info.IsTypedef: + if type_info.BaseType is None or (isinstance(type_info.BaseType, type) and issubclass(type_info.BaseType, _t.CVoid)) or isinstance(type_info.BaseType, _t.CVoid): + return ir.VoidType() + + if type_info.IsTypedef and type_info.Name: + resolved: ir.Type | None = self._resolve_typedef_ctype(type_info) + if resolved is not None: + return resolved + + # REnum 类型:通过 Name 查找或创建结构体(BaseType 可能为 None) + # 这对 BinOp `LLVMType | t.CPtr` 合并后的 CTypeInfo 至关重要 + if type_info.IsRenum and type_info.Name: + if type_info.Name in self.structs: + base: ir.Type = self.structs[type_info.Name] + else: + base = self._get_or_create_struct(type_info.Name) + result: ir.Type = base + for _ in range(type_info.PtrCount): + result = ir.PointerType(result) + return result + + BaseType: ir.Type = self._base_ctype_to_llvm(type_info.BaseType, type_info) + + result: ir.Type = BaseType + for _ in range(type_info.PtrCount): + if isinstance(result, ir.VoidType): + result = ir.IntType(8).as_pointer() + else: + result = ir.PointerType(result) + + for dim in reversed(type_info.ArrayDims): + try: + dim_val: int = int(dim) + if dim_val > 0: + result = ir.ArrayType(result, dim_val) + except (ValueError, TypeError): + pass + + return result + + def _resolve_typedef_ctype(self, type_info: _CTypeInfo) -> ir.Type | None: + name: str = type_info.Name + if not name or not self.SymbolTable: + return None + + entry: Any = self.SymbolTable.lookup(name) + if not entry or not isinstance(entry, _CTypeInfo): + return None + + if entry.BaseType and (not isinstance(entry.BaseType, _t._CTypedef) or entry.PtrCount > 0): + resolved: Any = entry.Copy() + resolved.IsTypedef = False + if type_info.PtrCount > resolved.PtrCount: + resolved.PtrCount = type_info.PtrCount + return self._ctype_to_llvm(resolved) + + if entry.OriginalType: + if isinstance(entry.OriginalType, _CTypeInfo): + resolved: Any = entry.OriginalType.Copy() + resolved.IsTypedef = False + if type_info.PtrCount > resolved.PtrCount: + resolved.PtrCount = type_info.PtrCount + return self._ctype_to_llvm(resolved) + elif isinstance(entry.OriginalType, str): + resolved: Any = _CTypeInfo.FromTypeName(entry.OriginalType) + if resolved and resolved.BaseType: + resolved.IsTypedef = False + if type_info.PtrCount > resolved.PtrCount: + resolved.PtrCount = type_info.PtrCount + return self._ctype_to_llvm(resolved) + + return None + + def _base_ctype_to_llvm(self, BaseType: Any, type_info: _CTypeInfo | None = None) -> ir.Type: + if BaseType is None: + return ir.VoidType() + + if isinstance(BaseType, (tuple, list)): + elem_types: list[ir.Type] = [self._base_ctype_to_llvm(bt, type_info) for bt in BaseType] + return ir.LiteralStructType(elem_types) if elem_types else ir.IntType(32) + + if isinstance(BaseType, type) and issubclass(BaseType, _t.CType): + BaseType = BaseType() + + if isinstance(BaseType, _t.CVoid): + return ir.VoidType() + + if isinstance(BaseType, _t.CStruct): + struct_name: str = getattr(BaseType, 'name', '') or (type_info.Name if type_info else '') + if struct_name and struct_name in self.structs: + return self.structs[struct_name] + if struct_name: + return self._get_or_create_struct(struct_name) + return ir.LiteralStructType([]) + + if isinstance(BaseType, (_t.CEnum, _t.REnum)): + enum_name: str = type_info.Name if type_info else '' + if enum_name and enum_name in self.structs: + return self.structs[enum_name] + return ir.IntType(32) + + if isinstance(BaseType, _t.CUnion): + union_name: str = type_info.Name if type_info else '' + if union_name and union_name in self.structs: + return self.structs[union_name] + if union_name: + return self._get_or_create_struct(union_name) + return ir.IntType(32) + + if isinstance(BaseType, _t._CTypedef): + typedef_name: str = getattr(BaseType, 'value', '') or (type_info.Name if type_info else '') + if typedef_name and self.SymbolTable: + entry: Any = self.SymbolTable.lookup(typedef_name) + if entry and isinstance(entry, _CTypeInfo) and entry.BaseType: + if not isinstance(entry.BaseType, _t._CTypedef) or entry.PtrCount > 0: + return self._base_ctype_to_llvm(entry.BaseType, entry) + return ir.IntType(32) + + if isinstance(BaseType, _t.CDefine): + # Use 64-bit type if the define value exceeds 32-bit range + if type_info and isinstance(type_info.DefineValue, int): + if type_info.DefineValue < 0 or type_info.DefineValue > 0xFFFFFFFF: + return ir.IntType(64) + return ir.IntType(32) + + if isinstance(BaseType, _t.CPass): + return ir.VoidType() + + llvm_str: str = CTypeRegistry.CTypeToLLVM(type(BaseType)) + return self._llvm_str_to_ir(llvm_str) + + def _get_source_sha1_from_type_str(self, type_str: str) -> str | None: + """从类型字符串中提取模块名,返回对应的源模块 SHA1。 + 用于跨模块类型引用(如 memhub.MemBuddy),确保用源模块的 SHA1 创建 struct。 + """ + if not type_str or '.' not in type_str: + return None + # 去除指针标记和空格 + clean: str = type_str.replace('*', '').strip() + if '.' not in clean: + return None + # 提取模块名(最后一个 '.' 之前的部分) + parts: list[str] = clean.rsplit('.', 1) + if len(parts) < 2: + return None + module_name: str = parts[0] + # 跳过 t/c 等内建模块 + if module_name in {'t', 'c'}: + return None + # 从 ModuleSha1Map 查找 SHA1(支持完整路径名和短名) + sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {}) + source_sha1: str | None = sha1_map.get(module_name) + if not source_sha1 and '.' in module_name: + source_sha1 = sha1_map.get(module_name.split('.')[-1]) + return source_sha1 + + def _resolve_typedef(self, type_name: str) -> str: + if type_name in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum', 'CPtr'}: + return type_name + if type_name == 'Callable': + return 'Callable' + visited: set[str] = set() + current: str = type_name + while current not in visited: + visited.add(current) + if self.SymbolTable: + Entry: _CTypeInfo | None = self.SymbolTable.lookup(current) + if isinstance(Entry, dict) and Entry.get('type') == 'typedef': + OriginalType: str = Entry.get('OriginalType', '') + if OriginalType: + current = OriginalType + continue + elif Entry and Entry.IsTypedef: + if Entry.OriginalType: + if isinstance(Entry.OriginalType, _CTypeInfo): + if Entry.OriginalType.IsFuncPtr: + return 'Callable' + return Entry.OriginalType.ToString() if Entry.OriginalType.BaseType else current + elif isinstance(Entry.OriginalType, str): + current = Entry.OriginalType + continue + if Entry.BaseType: + if not isinstance(Entry.BaseType, (_t._CTypedef,)) or Entry.PtrCount > 0: + resolved: str = self._resolve_ctype_to_str(Entry) + if resolved and resolved != current: + current = resolved + continue + bm: Any = BuiltinTypeMap.Get(current) + if bm: + base_cls: Any = bm[0] + try: + instance: Any = base_cls() + cname: str = type(instance).__name__ + if cname: + current = cname + ' *' * bm[1] + continue + except Exception as _e: + if _config_mode == "strict": + logging.warning(f"异常被忽略: {_e}") + pass + bm: Any = BuiltinTypeMap.Get(current) + if bm and bm[1] > 0: + base_cls: Any = bm[0] + try: + instance: Any = base_cls() + cname: str = type(instance).__name__ + if cname: + current = cname + ' *' * bm[1] + continue + except Exception as _e: + if _config_mode == "strict": + logging.warning(f"异常被忽略: {_e}") + pass + break + return current + + def _resolve_ctype_to_str(self, type_info: _CTypeInfo) -> str: + parts: list[str] = [] + base: Any = type_info.BaseType + if base is None or base is _t.CVoid or isinstance(base, _t.CVoid): + parts.append('void') + elif isinstance(base, _t._CTypedef) or base is _t._CTypedef: + return '' + elif isinstance(base, _t.CStruct) or base is _t.CStruct: + parts.append(f'struct {getattr(base, "name", "")}') + elif isinstance(base, _t.CEnum) or base is _t.CEnum: + parts.append(f'enum {getattr(base, "name", "")}') + else: + cname: str = '' + if isinstance(base, type) and issubclass(base, _t.CType): + cname = base.__name__ + elif isinstance(base, _t.CType): + cname = type(base).__name__ + if cname: + parts.append(cname) + else: + return '' + if type_info.PtrCount > 0: + parts.append('*' * type_info.PtrCount) + return ' '.join(parts) + + def _type_str_to_llvm(self, type_str: str, IsPtr: bool = False) -> ir.Type: + type_str = type_str.strip() + cache_key: tuple[str, bool] = (type_str, IsPtr) + cached: ir.Type | None = self._type_cache.get(cache_key) + if cached is not None: + return cached + result: ir.Type = self._type_str_to_llvm_impl(type_str, IsPtr) + if result is not None: + if isinstance(result, (ir.IntType, ir.FloatType, ir.DoubleType, ir.VoidType)): + self._type_cache[cache_key] = result + elif isinstance(result, ir.PointerType) and isinstance(result.pointee, (ir.IntType, ir.FloatType, ir.DoubleType, ir.VoidType)): + self._type_cache[cache_key] = result + elif isinstance(result, ir.ArrayType) and not isinstance(result.element, (ir.IdentifiedStructType, ir.LiteralStructType)): + self._type_cache[cache_key] = result + return result + + _CType2LLVM = _type_str_to_llvm + + def _type_str_to_llvm_impl(self, type_str: str, IsPtr: bool = False) -> ir.Type: + array_dims: list[int] = [] + dim_match: list[str] = re.findall(r'\[(\d+)\]', type_str) + if dim_match: + array_dims = [int(d) for d in dim_match] + type_str = re.sub(r'\s*\[\d+\]', '', type_str).strip() + result: ir.Type = self._type_str_to_llvm_base(type_str, IsPtr) + for dim in reversed(array_dims): + result = ir.ArrayType(result, dim) + return result + + def _type_str_to_llvm_base(self, type_str: str, IsPtr: bool = False) -> ir.Type: + ptr_depth: int = 0 + while type_str.endswith('*'): + ptr_depth += 1 + type_str = type_str[:-1].strip() + if ptr_depth > 0: + IsPtr = True + if type_str in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'}: + result: ir.Type + if IsPtr: + result = ir.IntType(8).as_pointer() + for _ in range(ptr_depth - 1): + result = ir.PointerType(result) + return result + return ir.IntType(32) + if type_str in {'const', 'volatile'}: + return ir.IntType(8) + if type_str == 'Callable': + return ir.IntType(8).as_pointer() + for _q_prefix, _q_offset in self._QUALIFIER_PREFIXES: + if type_str.startswith(_q_prefix): + return self._type_str_to_llvm(type_str[_q_offset:].strip(), IsPtr) + resolved: str = self._resolve_typedef(type_str) + if resolved != type_str: + if '*' in resolved: + IsPtr = False + return self._type_str_to_llvm(resolved, IsPtr) + if type_str.endswith('*'): + BaseType: str = type_str[:-1].strip() + base_llvm: ir.Type | None = self._basic_type_to_llvm(BaseType) + if base_llvm is not None and isinstance(base_llvm, ir.VoidType): + result: ir.Type = ir.IntType(8).as_pointer() + for _ in range(ptr_depth): + result = ir.PointerType(result) + return result + if base_llvm is not None and not isinstance(base_llvm, ir.VoidType): + if isinstance(base_llvm, ir.PointerType): + return base_llvm + return ir.PointerType(base_llvm) + if BaseType in self.structs: + return ir.PointerType(self.structs[BaseType]) + if BaseType.startswith('struct '): + stripped: str = BaseType[7:].strip() + if stripped in self.structs: + return ir.PointerType(self.structs[stripped]) + if '.' in BaseType: + LastPart: str = BaseType.split('.')[-1] + if LastPart in self.structs: + return ir.PointerType(self.structs[LastPart]) + _src_sha1: str | None = self._get_source_sha1_from_type_str(BaseType) + placeholder: ir.IdentifiedStructType = self._get_or_create_struct(LastPart, source_sha1=_src_sha1) + return ir.PointerType(placeholder) + if base_llvm is not None and isinstance(base_llvm, ir.VoidType): + return ir.PointerType(ir.IntType(8)) + if type_str.startswith('{') and type_str.endswith('}'): + inner: str = type_str[1:-1].strip() + elem_strs: list[str] = [] + depth: int = 0 + current: str = '' + for ch in inner: + if ch == ',' and depth == 0: + elem_strs.append(current.strip()) + current = '' + else: + if ch == '{': + depth += 1 + elif ch == '}': + depth -= 1 + current += ch + if current.strip(): + elem_strs.append(current.strip()) + elem_types: list[ir.Type] = [] + for es in elem_strs: + et: ir.Type = self._type_str_to_llvm(es, '*' in es) + if isinstance(et, ir.VoidType): + et = ir.IntType(8).as_pointer() + elem_types.append(et) + if elem_types: + return ir.LiteralStructType(elem_types) + return ir.IntType(32) + if type_str in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'}: + return ir.IntType(32) + if type_str.startswith('%'): + stripped: str = type_str[1:] + if stripped in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'}: + return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) + basic_stripped: ir.Type | None = self._basic_type_to_llvm(stripped) + if basic_stripped is not None: + if IsPtr and not isinstance(basic_stripped, ir.VoidType): + return ir.PointerType(basic_stripped) + return basic_stripped + if self.SymbolTable: + _Entry: _CTypeInfo | None = self.SymbolTable.lookup(type_str) + if _Entry and _Entry.IsTypedef: + resolved_str: str = self._resolve_typedef(type_str) + if resolved_str != type_str: + return self._type_str_to_llvm(resolved_str, IsPtr) + if _Entry.BaseType: + if not isinstance(_Entry.BaseType, (_t._CTypedef,)) or _Entry.PtrCount > 0: + resolved: str = self._resolve_ctype_to_str(_Entry) + if resolved: + return self._type_str_to_llvm(resolved, IsPtr) + if _Entry and _Entry.IsRenum: + if type_str in self.structs: + if IsPtr: + return ir.PointerType(self.structs[type_str]) + return self.structs[type_str] + # REnum 结构体尚未创建时不能 fall through 到 IsEnum(会返回 i32), + # 需要通过 _get_or_create_struct 创建 opaque 结构体(自引用安全)。 + _renum_sha1: str | None = self._get_source_sha1_from_type_str(type_str) + opaque: ir.Type = self._get_or_create_struct(type_str, source_sha1=_renum_sha1) + if IsPtr: + return ir.PointerType(opaque) + return opaque + if _Entry and _Entry.IsStruct: + # 结构体类型(来自 _InsertClassSymbol):必须返回结构体类型而非 i32, + # 否则 (ast.Import | t.CPtr) 联合转换会得到 i32* 导致字段访问 GEP 偏移错误 + if type_str in self.structs: + if IsPtr: + return ir.PointerType(self.structs[type_str]) + return self.structs[type_str] + # 结构体尚未创建时,通过 _get_or_create_struct 创建 opaque 结构体 + # (_TryLoadStructFromStub 会在后续阶段填充字段) + _struct_sha1: str | None = self._get_source_sha1_from_type_str(type_str) + opaque_struct: ir.IdentifiedStructType = self._get_or_create_struct(type_str, source_sha1=_struct_sha1) + if IsPtr: + return ir.PointerType(opaque_struct) + return opaque_struct + if _Entry and _Entry.IsEnum: + return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) + if _Entry and _Entry.IsExceptionClass: + return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) + if _Entry and _Entry.IsFunction: + return ir.PointerType(ir.IntType(8)) + if _Entry and _Entry.BaseType: + if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum: + return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) + for ClassName in self.structs: + if type_str == ClassName: + if IsPtr: + return ir.PointerType(self.structs[ClassName]) + return self.structs[ClassName] + if '.' in type_str and not type_str.startswith('%'): + LastPart: str = type_str.split('.')[-1] + basic_last: ir.Type | None = self._basic_type_to_llvm(LastPart) + if basic_last is not None: + if IsPtr and not isinstance(basic_last, ir.VoidType): + return ir.PointerType(basic_last) + return basic_last + if self.SymbolTable: + _Entry: _CTypeInfo | None = self.SymbolTable.lookup(LastPart) + if _Entry and _Entry.IsTypedef: + resolved_str: str = self._resolve_typedef(LastPart) + if resolved_str != LastPart: + return self._type_str_to_llvm(resolved_str, IsPtr) + if _Entry.BaseType: + if not isinstance(_Entry.BaseType, (_t._CTypedef,)) or _Entry.PtrCount > 0: + resolved: str = self._resolve_ctype_to_str(_Entry) + if resolved: + return self._type_str_to_llvm(resolved, IsPtr) + if _Entry and _Entry.IsRenum: + if LastPart in self.structs: + if IsPtr: + return ir.PointerType(self.structs[LastPart]) + return self.structs[LastPart] + # REnum 结构体尚未创建时不能 fall through 到 IsEnum(会返回 i32)。 + _lp_sha1: str | None = self._get_source_sha1_from_type_str(type_str) + opaque_lp: ir.Type = self._get_or_create_struct(LastPart, source_sha1=_lp_sha1) + if IsPtr: + return ir.PointerType(opaque_lp) + return opaque_lp + if _Entry and _Entry.IsEnum: + return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) + if _Entry and _Entry.IsExceptionClass: + return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) + if _Entry and _Entry.IsFunction: + return ir.PointerType(ir.IntType(8)) + if _Entry and _Entry.BaseType: + if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum: + return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) + if LastPart in self.structs: + if IsPtr: + return ir.PointerType(self.structs[LastPart]) + return self.structs[LastPart] + _lp_sha1_2: str | None = self._get_source_sha1_from_type_str(type_str) + placeholder: ir.IdentifiedStructType = self._get_or_create_struct(LastPart, source_sha1=_lp_sha1_2) + if IsPtr: + return ir.PointerType(placeholder) + return placeholder + if type_str.startswith('struct '): + stripped: str = type_str[7:].strip().replace(' *', '').replace('*', '') + if stripped in self.structs: + if IsPtr: + return ir.PointerType(self.structs[stripped]) + return self.structs[stripped] + basic_match: ir.Type | None = self._basic_type_to_llvm(stripped) + if basic_match is not None: + if IsPtr and not isinstance(basic_match, ir.VoidType): + return ir.PointerType(basic_match) + return basic_match + _stripped_sha1: str | None = self._get_source_sha1_from_type_str(stripped) + placeholder: ir.IdentifiedStructType = self._get_or_create_struct(stripped, source_sha1=_stripped_sha1) + if IsPtr: + return ir.PointerType(placeholder) + return placeholder + if type_str.startswith('%struct.'): + stripped: str = type_str[8:].strip().replace(' *', '').replace('*', '') + if stripped in self.structs: + if IsPtr: + return ir.PointerType(self.structs[stripped]) + return self.structs[stripped] + basic_match: ir.Type | None = self._basic_type_to_llvm(stripped) + if basic_match is not None: + if IsPtr and not isinstance(basic_match, ir.VoidType): + return ir.PointerType(basic_match) + return basic_match + _stripped_sha1_2: str | None = self._get_source_sha1_from_type_str(stripped) + placeholder: ir.IdentifiedStructType = self._get_or_create_struct(stripped, source_sha1=_stripped_sha1_2) + if IsPtr: + return ir.PointerType(placeholder) + return placeholder + if type_str.startswith('union '): + stripped: str = type_str[6:].strip() + basic_match: ir.Type | None = self._basic_type_to_llvm(stripped) + if basic_match is not None: + if IsPtr and not isinstance(basic_match, ir.VoidType): + return ir.PointerType(basic_match) + return basic_match + if type_str.endswith('*'): + pointee: ir.Type = self._type_str_to_llvm(type_str[:-1].strip()) + if isinstance(pointee, ir.VoidType): + return ir.PointerType(ir.IntType(8)) + return ir.PointerType(pointee) + result: ir.Type | None = self._basic_type_to_llvm(type_str) + if result is None: + # 未知类型:可能是跨模块结构体(如 'Import' 来自 ast 模块)。 + # 尝试通过 _get_or_create_struct 创建/查找结构体, + # 该方法内部会检查 typedef/enum 等并返回正确类型。 + # 必须在 fallback 到 i32 之前执行,否则 (ast.Import | t.CPtr) 联合转换 + # 会得到 i32* 导致字段访问 GEP 偏移错误(AccessViolation 崩溃)。 + _cand_sha1: str | None = self._get_source_sha1_from_type_str(type_str) + candidate: ir.Type = self._get_or_create_struct(type_str, source_sha1=_cand_sha1) + if isinstance(candidate, ir.IdentifiedStructType): + result = candidate + else: + result = ir.IntType(32) + if IsPtr: + if isinstance(result, ir.VoidType): + result = ir.PointerType(ir.IntType(8)) + elif isinstance(result, ir.PointerType): + pass + else: + result = ir.PointerType(result) + for _ in range(ptr_depth - 1): + result = ir.PointerType(result) + return result + + def _llvm_str_to_ir(self, llvm_str: str) -> ir.Type | None: + _LLVM_IR_TYPES: dict[str, ir.Type] = { + 'void': ir.VoidType(), 'i1': ir.IntType(1), 'i8': ir.IntType(8), + 'i16': ir.IntType(16), 'i32': ir.IntType(32), 'i64': ir.IntType(64), + 'i128': ir.IntType(128), 'float': ir.FloatType(), 'double': ir.DoubleType(), + 'half': ir.IntType(16), 'fp128': ir.IntType(128), + } + return _LLVM_IR_TYPES.get(llvm_str) + + def _basic_type_to_llvm(self, type_str: str) -> ir.Type | None: + _LLVM_IR_TYPES: dict[str, ir.Type] = { + 'void': ir.VoidType(), 'i1': ir.IntType(1), 'i8': ir.IntType(8), + 'i16': ir.IntType(16), 'i32': ir.IntType(32), 'i64': ir.IntType(64), + 'i128': ir.IntType(128), 'f32': ir.FloatType(), 'f64': ir.DoubleType(), + 'half': ir.IntType(16), 'fp128': ir.IntType(128), + } + if type_str in _LLVM_IR_TYPES: + return _LLVM_IR_TYPES[type_str] + resolved: Any = CTypeRegistry.ResolveName(type_str) + if resolved is not None: + ctype_cls: Any + ptr_level: int + ctype_cls, ptr_level = resolved + llvm_str: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str: + # CTypeToLLVM 对 CPtr 等指针类型已返回含 * 的串(如 'i8*'), + # 此时 ptr_level 已内含在 llvm_str 中,直接解析即可 + if '*' in llvm_str: + return self._type_str_to_llvm(llvm_str) + base: ir.Type | None = self._llvm_str_to_ir(llvm_str) + if base is not None: + for _ in range(ptr_level): + if isinstance(base, ir.VoidType): + base = ir.IntType(8).as_pointer() + else: + base = ir.PointerType(base) + return base + if type_str == '*': + return ir.IntType(8) + if type_str in ('const_', 'const'): + return ir.IntType(8) + return None + + def _is_type_unsigned(self, type_str: str) -> bool: + s: str = type_str.strip() + if s.endswith('*'): + return True + if s.startswith('unsigned'): + return True + resolved: Any = CTypeRegistry.ResolveName(s) + if resolved is not None: + ctype_cls: Any + _: int + ctype_cls, _ = resolved + try: + inst: Any = ctype_cls() + if getattr(inst, 'IsSigned', None) is False: + return True + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"判断类型符号性失败: {_e}", "Exception") + return False + + def _record_var_signedness(self, name: str, type_str_or_bool: str | bool) -> None: + if isinstance(type_str_or_bool, bool): + self.var_signedness[name] = type_str_or_bool + else: + self.var_signedness[name] = self._is_type_unsigned(type_str_or_bool) + + def _is_var_unsigned(self, name: str) -> bool: + return self.var_signedness.get(name, False) + + def _check_node_unsigned(self, node: ast.AST) -> bool: + if isinstance(node, ast.Name): + return self._is_var_unsigned(node.id) + if isinstance(node, ast.BinOp): + return self._check_node_unsigned(node.left) or self._check_node_unsigned(node.right) + if isinstance(node, ast.UnaryOp): + return self._check_node_unsigned(node.operand) + if isinstance(node, ast.Call): + return self._check_call_unsigned(node) + if isinstance(node, ast.Subscript): + return self._check_node_unsigned(node.value) + if isinstance(node, ast.Attribute): + # 检查是否为结构体字段,并查询其符号性 + field_signed: bool | None = self._check_attribute_signedness(node) + if field_signed is not None: + return not field_signed # IsSigned=False 表示无符号 → 返回 True + return self._check_node_unsigned(node.value) + return False + + def _check_call_unsigned(self, node: ast.Call) -> bool: + """推断函数调用返回值的无符号性。 + + 修复:原代码对所有 Call 节点返回 False,导致函数调用返回值 + 直接参与 >>/% 等运算时丢失无符号性,退化成 ashr/srem。 + + 规则(用户确认): + - a = b 赋值时,若 a 无显式类型注解,则自动推导为 b 的类型(含符号性) + - 若 a 有显式有符号类型,b 无符号,则相当于将 b 强转为 a 的有符号类型 + - 此处仅推断 b(调用本身)的符号性,赋值时的强转由赋值逻辑处理 + """ + func_node: ast.AST = node.func + func_name: str = '' + + if isinstance(func_node, ast.Name): + # 直接调用: rdrand64() + func_name = func_node.id + elif isinstance(func_node, ast.Attribute): + # t.CUInt64T(x) 等类型转换调用 + if hasattr(func_node.value, 'id') and IsTModule(func_node.value.id, self.SymbolTable): + if self._is_type_unsigned(func_node.attr): + return True + # module.func() 形式的调用 + try: + func_name = f"{func_node.value.id}.{func_node.attr}" + except AttributeError: + return False + else: + return False + + if not func_name or not self.SymbolTable: + return False + + try: + sym_info: Any = self.SymbolTable.lookup(func_name) + except Exception: + return False + + if sym_info and isinstance(sym_info, _CTypeInfo): + if sym_info.IsFunction or sym_info.IsFuncPtr: + ret_info: Any = sym_info.FuncPtrReturn + if ret_info and isinstance(ret_info, _CTypeInfo): + # 指针类型视为无符号 + if ret_info.PtrCount > 0: + return True + # 检查返回类型的 IsUInt 属性(IsSigned is False) + if ret_info.IsUInt: + return True + elif sym_info.IsTypedef: + # 修复:typedef 强转调用(如 ULONGLONG(0x11))需要解析底层类型的无符号性 + # IsUInt 对组合类型(CType | CType 产生的 list)会失败,需多策略回退 + if sym_info.IsUInt: + return True + # 策略1:直接检查 BaseType(可能是组合类型,如 ULONGLONG = CUnsignedLong | CLong) + bt: Any = sym_info.BaseType + if isinstance(bt, (tuple, list)): + for elem in bt: + if isinstance(elem, _t.CType) and getattr(elem, 'IsSigned', None) is False: + return True + elif isinstance(bt, _t.CType) and getattr(bt, 'IsSigned', None) is False: + return True + # 策略2:通过 OriginalType 解析 + if sym_info.OriginalType: + ot: Any = sym_info.OriginalType + if isinstance(ot, str): + if self._is_type_unsigned(ot): + return True + elif isinstance(ot, _CTypeInfo): + if ot.IsUInt: + return True + elif isinstance(ot, (tuple, list)): + for elem in ot: + if isinstance(elem, _t.CType) and getattr(elem, 'IsSigned', None) is False: + return True + # 策略3:通过 _resolve_typedef 解析为字符串后判断 + resolved_str: str = self._resolve_typedef(func_name) + if resolved_str and self._is_type_unsigned(resolved_str): + return True + return False + + def _check_attribute_signedness(self, node: ast.Attribute) -> bool | None: + """检查 Attribute 节点是否引用具有已知符号性的结构体字段。 + + Returns: + True: 字段是有符号类型 + False: 字段是无符号类型 + None: 无法确定(非结构体字段或符号性未知) + """ + if not isinstance(node.value, ast.Name): + return None + var_name: str = node.value.id + field_name: str = node.attr + class_name: str | None = self._resolve_class_for_var(var_name) + if not class_name: + return None + signed_dict: dict[str, bool] = self.class_member_signeds.get(class_name, {}) + if field_name in signed_dict: + return signed_dict[field_name] + return None \ No newline at end of file diff --git a/App/lib/core/LLVMCG/VaArg.py b/App/lib/core/LLVMCG/VaArg.py new file mode 100644 index 0000000..b349639 --- /dev/null +++ b/App/lib/core/LLVMCG/VaArg.py @@ -0,0 +1,94 @@ +from __future__ import annotations +import llvmlite.ir as ir +from lib.core.LLVMCG.BaseGen import VaArgInstruction + + +class VaArgMixin: + + def _get_va_start_intrinsic(self) -> ir.Function: + if 'llvm.va_start' in self.functions: + return self.functions['llvm.va_start'] + ftype: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.IntType(8).as_pointer()]) + func: ir.Function = ir.Function(self.module, ftype, name='llvm.va_start') + self.functions['llvm.va_start'] = func + return func + + def _get_va_end_intrinsic(self) -> ir.Function: + if 'llvm.va_end' in self.functions: + return self.functions['llvm.va_end'] + ftype: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.IntType(8).as_pointer()]) + func: ir.Function = ir.Function(self.module, ftype, name='llvm.va_end') + self.functions['llvm.va_end'] = func + return func + + def emit_va_start(self, va_list_ptr: ir.Value) -> None: + if not self.builder: + return None + func: ir.Function = self._get_va_start_intrinsic() + i8ptr_type: ir.PointerType = ir.IntType(8).as_pointer() + if va_list_ptr.type == i8ptr_type: + self.builder.call(func, [va_list_ptr]) + else: + ptr: ir.Value = self.builder.bitcast(va_list_ptr, i8ptr_type, name="va_list_i8ptr") + self.builder.call(func, [ptr]) + return None + + def _emit_stack_align(self, alignment: int = 16) -> None: + if not self.builder: + return None + void: ir.VoidType = ir.VoidType() + asm_type: ir.FunctionType = ir.FunctionType(void, []) + mask: int = -alignment + asm_str: str = f"andq $${mask}, %rsp" + asm_func: ir.InlineAsm = ir.InlineAsm(asm_type, asm_str, "~{dirflag},~{fpsr},~{flags},~{rsp}", side_effect=True) + self.builder.call(asm_func, [], name="align_stack") + return None + + def emit_va_end(self, va_list_ptr: ir.Value) -> None: + if not self.builder: + return None + func: ir.Function = self._get_va_end_intrinsic() + i8ptr_type: ir.PointerType = ir.IntType(8).as_pointer() + if va_list_ptr.type == i8ptr_type: + self.builder.call(func, [va_list_ptr]) + else: + ptr: ir.Value = self.builder.bitcast(va_list_ptr, i8ptr_type, name="va_list_i8ptr") + self.builder.call(func, [ptr]) + return None + + def emit_va_arg(self, va_list_ptr: ir.Value, arg_type: ir.Type) -> ir.Value: + if not self.builder: + return self._ZeroConst(arg_type) + if not hasattr(self, '_va_arg_counter'): + self._va_arg_counter: int = 0 + self._va_arg_counter += 1 + is_x86_64: bool = self._platform_info['is_x86_64'] + if is_x86_64: + va_arg_type: ir.Type + if isinstance(arg_type, ir.PointerType): + va_arg_type = ir.IntType(64) + elif isinstance(arg_type, (ir.FloatType, ir.DoubleType)): + va_arg_type = ir.DoubleType() + elif isinstance(arg_type, ir.IntType) and arg_type.width < 64: + va_arg_type = ir.IntType(64) + else: + va_arg_type = arg_type + instr: ir.Instruction = VaArgInstruction(self.builder.block, va_arg_type, 'va_arg', (va_list_ptr,), name=f'va_arg_raw_{self._va_arg_counter}') + self.builder._insert(instr) + result: ir.Value + if va_arg_type != arg_type: + if isinstance(arg_type, ir.PointerType): + result = self.builder.inttoptr(instr, arg_type, name=f'va_arg_ptr_{self._va_arg_counter}') + elif isinstance(arg_type, ir.FloatType): + result = self.builder.fptrunc(instr, arg_type, name=f'va_arg_fptrunc_{self._va_arg_counter}') + else: + result = self.builder.trunc(instr, arg_type, name=f'va_arg_trunc_{self._va_arg_counter}') + else: + result = instr + return result + else: + if isinstance(arg_type, ir.IntType) and arg_type.width < 64: + arg_type = ir.IntType(64) + instr: ir.Instruction = VaArgInstruction(self.builder.block, arg_type, 'va_arg', (va_list_ptr,), name=f'va_arg_raw_{self._va_arg_counter}') + self.builder._insert(instr) + return instr \ No newline at end of file diff --git a/App/lib/core/LLVMCG/__init__.py b/App/lib/core/LLVMCG/__init__.py new file mode 100644 index 0000000..6b231e9 --- /dev/null +++ b/App/lib/core/LLVMCG/__init__.py @@ -0,0 +1,34 @@ +""" +LLVMCG 包 - LLVM 代码生成器模块 + +将 LlvmCodeGenerator.py 拆分为多个 Mixin 模块: +- BaseGen: 基础设施(__init__、平台解析、名称修饰、函数查找) +- TypeConvert: 类型转换(CType -> LLVM IR 类型) +- StructGen: 结构体生成(struct/vtable 定义与初始化) +- MemoryOps: 内存操作(alloca/Load/store/coerce) +- ExprGen: 表达式生成(常量、二元运算、返回、printf) +- FuncGen: 函数处理(参数调整、成员偏移、函数声明) +- VaArg: 变长参数处理(va_start/va_end/va_arg) +""" + +from lib.core.LLVMCG.BaseGen import ( + BaseGenMixin, + VaArgInstruction, +) +from lib.core.LLVMCG.TypeConvert import TypeConvertMixin +from lib.core.LLVMCG.StructGen import StructGenMixin +from lib.core.LLVMCG.MemoryOps import MemoryOpsMixin +from lib.core.LLVMCG.ExprGen import ExprGenMixin +from lib.core.LLVMCG.FuncGen import FuncGenMixin +from lib.core.LLVMCG.VaArg import VaArgMixin + +__all__ = [ + 'BaseGenMixin', + 'TypeConvertMixin', + 'StructGenMixin', + 'MemoryOpsMixin', + 'ExprGenMixin', + 'FuncGenMixin', + 'VaArgMixin', + 'VaArgInstruction', +] diff --git a/App/lib/core/LLVMTypeMapper.py b/App/lib/core/LLVMTypeMapper.py new file mode 100644 index 0000000..0e8cbe9 --- /dev/null +++ b/App/lib/core/LLVMTypeMapper.py @@ -0,0 +1,301 @@ +"""LLVMTypeMapper — AST 注解节点到 LLVM IR 类型字符串的映射器 + +从 SymbolTable 中提取的类型解析逻辑,职责单一: +将 Python AST 注解节点翻译为 LLVM IR 类型字符串(如 'i32', 'i8*', '{ i32, i8* }')。 + +原 SymbolTable 上的 4 个方法: + - _GetLLVMTypeStr → LLVMTypeMapper.get_llvm_type_str + - _GetFuncRetTypeStr → LLVMTypeMapper.get_func_ret_type_str + - _GetFuncParamTypeStr → LLVMTypeMapper.get_func_param_type_str + - _ResolveTypedefValueType → LLVMTypeMapper.resolve_typedef_value_type +""" +from __future__ import annotations + +import ast +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + +from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap +from lib.core.SymbolUtils import IsTModule, IsListAnnotation +from lib.includes import t as _t +from lib.includes.t import CTypeRegistry + + +class LLVMTypeMapper: + """AST 注解节点 → LLVM IR 类型字符串 映射器""" + + def __init__(self, symtab: SymbolTable): + self._symtab = symtab + + # ================================================================== + # 公共 API + # ================================================================== + + def get_llvm_type_str(self, node: ast.AST | None) -> str: + """将 AST 注解节点转换为 LLVM IR 类型字符串""" + if node is None: + self._symtab.diagnostics.warn('', 0, "类型注解为 None,回退到 i32") + return 'i32' + if isinstance(node, ast.Name): + return self._resolve_name(node) + elif isinstance(node, ast.Attribute): + return self._resolve_attribute(node) + elif isinstance(node, ast.Subscript): + return self._resolve_subscript(node) + elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + return self._resolve_bitor(node) + elif isinstance(node, ast.Constant): + if isinstance(node.value, bool): + return 'i8' + return 'i32' + elif isinstance(node, ast.Call): + return self._resolve_call(node) + self._symtab.diagnostics.warn('', 0, f"无法解析类型节点 {type(node).__name__},回退到 i32") + return 'i32' + + def get_func_ret_type_str(self, returns_node: ast.AST | None) -> str: + """提取函数返回类型的 LLVM 字符串""" + if returns_node is None: + return 'i32' + if isinstance(returns_node, ast.BinOp) and isinstance(returns_node.op, ast.BitOr): + left_str: str = self.get_llvm_type_str(returns_node.left) + right_str: str = self.get_llvm_type_str(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: str = self.get_llvm_type_str(returns_node) + return result if result else 'i32' + + def get_func_param_type_str(self, annotation_node: ast.AST | None) -> str: + """提取函数参数类型的 LLVM 字符串""" + if annotation_node is None: + return 'i8*' + if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr): + left_str: str = self.get_llvm_type_str(annotation_node.left) + right_str: str = self.get_llvm_type_str(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 + return right_str if right_str else left_str + result: str = self.get_llvm_type_str(annotation_node) + return result if result else 'i8*' + + def resolve_typedef_value_type(self, node: ast.AST | None) -> str: + """解析 typedef 值表达式的 C 类型字符串""" + if node is None: + return '' + if isinstance(node, ast.Name): + if node.id in self._symtab: + Entry: CTypeInfo = self._symtab[node.id] + if Entry.IsTypedef and Entry.OriginalType: + return Entry.OriginalType + return '' + if isinstance(node, ast.Attribute): + if isinstance(node.value, ast.Name) and IsTModule(node.value.id): + # 直接判断属性名是否为 CPtr(消除对 CName=='*' 的依赖) + if node.attr == 'CPtr': + return 'ptr' + CName: str | None = CTypeHelper.GetCName(node.attr) + if CName == 'CPtr': + return 'ptr' + if CName: + return CName + return '' + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + left_str: str = self.resolve_typedef_value_type(node.left) + right_str: str = self.resolve_typedef_value_type(node.right) + if right_str == 'ptr' or (right_str and right_str.endswith('*')): + base: str = 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: str = 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 _ctype_to_llvm_str(self, info: CTypeInfo) -> str: + """从 CTypeInfo 的 BaseType+PtrCount 生成归一化的 LLVM 类型字符串。 + + void* 在 LLVM IR 中归一化为 i8*;struct/enum/union 等非基础类型返回空字符串。 + """ + if not info or not info.BaseType: + return '' + base_type = info.BaseType + base_llvm: str = '' + if isinstance(base_type, type) and issubclass(base_type, _t.CType): + base_llvm = CTypeRegistry.CTypeToLLVM(base_type) + elif isinstance(base_type, _t.CType): + base_llvm = CTypeRegistry.CTypeToLLVM(type(base_type)) + if not base_llvm: + return '' + if info.PtrCount > 0: + if base_llvm == 'void': + return 'i8*' + return base_llvm + '*' * info.PtrCount + return base_llvm + + def _ctype_str_to_llvm(self, type_str: str) -> str: + """将 C 类型名字符串(如 'CUnsignedInt *')转换为 LLVM 类型字符串(如 'i32*') + + 支持: + - C 类名:'CUnsignedInt' → 'i32','CChar' → 'i8' + - 指针:'CUnsignedInt *' → 'i32*','void *' → 'i8*' + - 已是 LLVM 类型:'i32' → 'i32','i8*' → 'i8*' + 无法识别返回空字符串。 + """ + s: str = type_str.strip() + ptr_count: int = 0 + while s.endswith('*'): + ptr_count += 1 + s = s[:-1].strip() + base_llvm: str = '' + if s in ('void', 'CVoid'): + base_llvm = 'void' + else: + base_llvm = CTypeRegistry.NameToLLVM(s) + if not base_llvm and s in ('i8', 'i16', 'i32', 'i64', 'float', 'double', 'half', 'fp128', 'void'): + base_llvm = s + if not base_llvm: + return '' + if ptr_count > 0: + if base_llvm == 'void': + return 'i8*' + return base_llvm + '*' * ptr_count + return base_llvm + + def _resolve_name(self, node: ast.Name) -> str: + """解析 ast.Name 节点""" + id_map: dict[str, str] = { + 'str': 'i8*', 'int': 'i32', 'bool': 'i8', + 'float': 'double', 'None': 'void', + 'UINT8PTR': 'i8*', 'INT8PTR': 'i8*', 'BYTEPTR': 'i8*', + 'UINT16PTR': 'i16*', 'INT16PTR': 'i16*', + 'UINT32PTR': 'i32*', 'INT32PTR': 'i32*', + 'UINT64PTR': 'i64*', 'INT64PTR': 'i64*', + } + if node.id in id_map: + return id_map[node.id] + # 通过 BuiltinTypeMap 解析 VOIDPTR、INTPTR 等指针别名 + builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(node.id) + if builtin_entry: + TypeClass: type + base_ptr: int + TypeClass, base_ptr = builtin_entry + base_llvm: str = CTypeRegistry.CTypeToLLVM(TypeClass) + if base_ptr > 0: + if base_llvm == 'void': + return 'i8*' + return base_llvm + '*' * base_ptr + return base_llvm if base_llvm else 'i32' + # 查找 SymbolTable 中的 typedef + if node.id in self._symtab: + entry: CTypeInfo = self._symtab[node.id] + if entry and entry.IsTypedef: + # 优先用 OriginalType 解析 + if entry.OriginalType: + if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.IsFuncPtr: + return 'i8*' + elif isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.BaseType: + # 用 CTypeRegistry 生成归一化 LLVM 字符串(void*→i8*),struct/enum 回退 ToString() + resolved: str = self._ctype_to_llvm_str(entry.OriginalType) + if resolved: + return resolved + return entry.OriginalType.ToString() + elif isinstance(entry.OriginalType, str): + # 字符串形式的 OriginalType(如 'CUnsignedInt *')需转换为 LLVM 类型字符串 + llvm_str: str = self._ctype_str_to_llvm(entry.OriginalType) + if llvm_str: + return llvm_str + # OriginalType 为空或字符串无法转换时,回退到 BaseType + if entry.BaseType and (not isinstance(entry.BaseType, (_t._CTypedef,)) or entry.PtrCount > 0): + resolved = self._ctype_to_llvm_str(entry) + if resolved: + return resolved + self._symtab.diagnostics.warn('', 0, f"无法解析 Name 类型 '{node.id}',回退到 i32") + return 'i32' + + def _resolve_attribute(self, node: ast.Attribute) -> str: + """解析 ast.Attribute 节点""" + attr_name: str = node.attr if hasattr(node, 'attr') else '' + llvm_str: str | None = 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}*' + self._symtab.diagnostics.warn('', 0, f"无法解析 Attribute 类型 '{attr_name}',回退到 i32") + return 'i32' + + def _resolve_subscript(self, node: ast.Subscript) -> str: + """解析 ast.Subscript 节点""" + if isinstance(node.value, ast.Name) and node.value.id == 'tuple': + slice_node: ast.AST = node.slice + elem_types: list[str] = [] + if isinstance(slice_node, ast.Tuple): + elt: ast.AST + for elt in slice_node.elts: + elem_types.append(self.get_llvm_type_str(elt)) + else: + elem_types.append(self.get_llvm_type_str(slice_node)) + if elem_types: + return '{ ' + ', '.join(elem_types) + ' }' + if IsListAnnotation(node): + slice_node: ast.AST = node.slice + elem_type: str + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1: + elem_type = self.get_llvm_type_str(slice_node.elts[0]) + else: + elem_type = self.get_llvm_type_str(slice_node) + return elem_type + '*' + self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32") + return 'i32' + + def _resolve_bitor(self, node: ast.BinOp) -> str: + """解析 ast.BinOp(BitOr) 节点""" + left: str = self.get_llvm_type_str(node.left) + right: str = self.get_llvm_type_str(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 + + def _resolve_call(self, node: ast.Call) -> str: + """解析 ast.Call 节点""" + if isinstance(node.func, ast.Attribute): + if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id): + return self.get_llvm_type_str(ast.Attribute(value=ast.Name(id='t'), attr=node.func.attr)) + self._symtab.diagnostics.warn('', 0, "无法解析 Call 类型,回退到 i32") + return 'i32' diff --git a/App/lib/core/LlvmCodeGenerator.py b/App/lib/core/LlvmCodeGenerator.py new file mode 100644 index 0000000..b09db65 --- /dev/null +++ b/App/lib/core/LlvmCodeGenerator.py @@ -0,0 +1,45 @@ +# LLVM 代码生成器主类 +# 通过 Mixin 模式组合各功能模块,保持向后兼容的导入路径 +# +# 拆分后的模块位于 lib/core/LLVMCG/ 目录下: +# - BaseGen.py: __init__、平台解析、名称修饰、函数查找 +# - TypeConvert.py: 类型转换(CType -> LLVM IR) +# - StructGen.py: 结构体生成(struct/vtable) +# - MemoryOps.py: 内存操作(alloca/Load/store) +# - ExprGen.py: 表达式生成(常量、二元运算、返回) +# - FuncGen.py: 函数处理(参数调整、成员偏移) +# - VaArg.py: 变长参数处理 +from __future__ import annotations + +from lib.core.LLVMCG.BaseGen import BaseGenMixin +from lib.core.LLVMCG.TypeConvert import TypeConvertMixin +from lib.core.LLVMCG.StructGen import StructGenMixin +from lib.core.LLVMCG.MemoryOps import MemoryOpsMixin +from lib.core.LLVMCG.ExprGen import ExprGenMixin +from lib.core.LLVMCG.FuncGen import FuncGenMixin +from lib.core.LLVMCG.VaArg import VaArgMixin + +# from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin + + +class LlvmCodeGenerator( + BaseGenMixin, + TypeConvertMixin, + StructGenMixin, + MemoryOpsMixin, + ExprGenMixin, + FuncGenMixin, + VaArgMixin, +): + """LLVM IR 代码生成器 + + 通过 Mixin 模式组合各功能模块。 + 实际方法实现分布在 lib/core/LLVMCG/ 下的各模块中。 + """ + #builder: ir.IRBuilder = None + #_Trans: LlvmGeneratorMixin = None + pass + + +# 向后兼容:重新导出常量和辅助类 +__all__: list[str] = ['LlvmCodeGenerator', 'VaArgInstruction'] diff --git a/App/lib/core/Phase1.py b/App/lib/core/Phase1.py index e3d1f62..2ed7fab 100644 --- a/App/lib/core/Phase1.py +++ b/App/lib/core/Phase1.py @@ -44,56 +44,32 @@ SRC_BUF_SIZE: t.CDefine = 1048576 def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, log: VLogger.Logger | t.CPtr) -> int: """Phase1: 扫描 includes 目录,按需翻译并生成 stub""" - stdio.printf("[P1 ENTER] mb=%p inc=%p temp=%p log=%p\n", mb, includes_dir, temp_dir, log) - stdio.fflush(0) if includes_dir is None or temp_dir is None: stdio.printf("[Phase1] includes_dir 或 temp_dir 为空,跳过\n") return 1 - stdio.printf("[P1] before log.banner\n") - stdio.fflush(0) if log is not None: log.banner("Phase1: 扫描 includes(按需翻译)") - stdio.printf("[P1] after log.banner\n") - stdio.fflush(0) # 扫描 includes 目录 - stdio.printf("[P1] before scan_includes: mb=%p inc=%s\n", mb, includes_dir) - stdio.fflush(0) result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir) - stdio.printf("[P1] after scan_includes: result=%p\n", result) - stdio.fflush(0) if result is None: stdio.printf("[Phase1] 扫描失败\n") return 1 - stdio.printf("[P1] result.Count=%d\n", result.Count) - stdio.fflush(0) - # 读取 _sha1_map.txt 获取需要的 includes SHA1 集合 + # 优先使用 Projectrans.py 生成的 _sha1_map.txt(含依赖分析,只包含需要的 includes) + # 若不存在,则从扫描结果生成(包含所有 includes,可能导致结构体表溢出) sha1_set: str = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17) - stdio.printf("[P1] sha1_set=%p\n", sha1_set) - stdio.fflush(0) if sha1_set is None: stdio.printf("[Phase1] sha1_set 分配失败\n") return 1 string.memset(sha1_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17) - stdio.printf("[P1] before _load_includes_sha1_set\n") - stdio.fflush(0) set_count: int = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set) - stdio.printf("[P1] set_count=%d\n", set_count) - stdio.fflush(0) if set_count <= 0: stdio.printf("[Phase1] _sha1_map.txt 不存在或为空,从扫描结果生成\n") - stdio.fflush(0) - stdio.printf("[P1] before WriteIncludesSha1Map\n") - stdio.fflush(0) StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0) - stdio.printf("[P1] after WriteIncludesSha1Map, before _load_includes_sha1_set\n") - stdio.fflush(0) set_count = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set) - stdio.printf("[P1] set_count2=%d\n", set_count) - stdio.fflush(0) if set_count < 0: stdio.printf("[Phase1] 无法加载 _sha1_map.txt,跳过 Phase1\n") return 1 @@ -105,17 +81,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, p1_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) p1_mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) p1_inc_count: int = 0 - stdio.printf("[P1] before _BuildIncludesSha1Map\n") - stdio.fflush(0) if p1_sha1_arr is not None and p1_mod_arr is not None: p1_inc_count = StubMerger._BuildIncludesSha1Map(temp_dir, td_len_p1map, p1_sha1_arr, p1_mod_arr) - stdio.printf("[P1] after _BuildIncludesSha1Map: inc=%d\n", p1_inc_count) - stdio.fflush(0) - stdio.printf("[P1] before set_module_sha1_map\n") - stdio.fflush(0) HandlesExprCall.set_module_sha1_map(p1_sha1_arr, p1_mod_arr, p1_inc_count) - stdio.printf("[P1] after set_module_sha1_map\n") - stdio.fflush(0) # 初始化 AST 表(只需一次) ast._init_tables(mb) @@ -276,13 +244,21 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, p1a_fl: int = 0 for i in range(result.Count): + stdio.printf("[Phase1a] iter=%d/%d\n", i, result.Count) + stdio.fflush(0) entry_addr_r: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size + stdio.printf("[Phase1a] iter=%d entry_addr=%d\n", i, entry_addr_r) + stdio.fflush(0) entry_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_r, t.CPtr)) + stdio.printf("[Phase1a] iter=%d entry_r=%d\n", i, entry_r) + stdio.fflush(0) if entry_r is None: p1a_fl += 1 continue sha1_r: str = entry_r.Sha1 + stdio.printf("[Phase1a] iter=%d sha1=%s\n", i, sha1_r) + stdio.fflush(0) if sha1_r is None: p1a_fl += 1 continue @@ -299,6 +275,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 读取文件内容 file_path_r: str = entry_r.Path + stdio.printf("[Phase1a] 处理: sha1=%s path=%s\n", sha1_r, file_path_r) + stdio.fflush(0) f_r: fileio.File | t.CPtr = fileio.File(file_path_r, fileio.MODE.R) if f_r.closed: p1a_fl += 1 @@ -347,21 +325,67 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, HandlesType.set_current_file(file_path_r) HandlesType.clear_cdefine_constants() HandlesStruct.reset_visible_structs(mb, 0) + stdio.printf("[Phase1a] 翻译开始: sha1=%s\n", sha1_r) + stdio.fflush(0) ret_r: int = tr_r.translate(tree_r) + stdio.printf("[Phase1a] 翻译完成: sha1=%s ret=%d\n", sha1_r, ret_r) + stdio.fflush(0) if ret_r != 0: p1a_fl += 1 else: p1a_reg += 1 # 释放 Translator 的 C malloc 资源 + stdio.printf("[Phase1a] 释放开始: sha1=%s\n", sha1_r) + stdio.fflush(0) if tr_r._global_names is not None: + stdio.printf("[Phase1a] free(_global_names) 前: sha1=%s\n", sha1_r) + stdio.fflush(0) stdlib.free(tr_r._global_names) + stdio.printf("[Phase1a] free(_global_names) 后: sha1=%s\n", sha1_r) + stdio.fflush(0) if tr_r._nonlocal_names is not None: + stdio.printf("[Phase1a] free(_nonlocal_names) 前: sha1=%s\n", sha1_r) + stdio.fflush(0) stdlib.free(tr_r._nonlocal_names) - + stdio.printf("[Phase1a] free(_nonlocal_names) 后: sha1=%s\n", sha1_r) + stdio.fflush(0) + stdio.printf("[Phase1a] free(src_buf_r) 前: sha1=%s\n", sha1_r) + stdio.fflush(0) stdlib.free(src_buf_r) + stdio.printf("[Phase1a] free(src_buf_r) 后: sha1=%s\n", sha1_r) + stdio.fflush(0) + stdio.printf("[Phase1a] 循环已退出\n") + stdio.fflush(0) + test_val: int = 42 + stdio.printf("[Phase1a] test_val=%d p1a_reg=%d p1a_skp=%d p1a_fl=%d\n", test_val, p1a_reg, p1a_skp, p1a_fl) + stdio.fflush(0) + stdio.printf("[Phase1a] done simple\n") + stdio.fflush(0) + stdio.printf("[Phase1a] done ascii reg=%d skp=%d fl=%d\n", p1a_reg, p1a_skp, p1a_fl) + stdio.fflush(0) + test_ptr: bytes = stdlib.malloc(64) + if test_ptr is not None: + stdio.printf("[Phase1a] malloc test ok ptr=%d\n", test_ptr) + stdio.fflush(0) + stdlib.free(test_ptr) + stdio.printf("[Phase1a] free test ok\n") + stdio.fflush(0) + stdio.printf("[Phase1a] 测试中文=%d\n", 42) + stdio.fflush(0) + stdio.printf("[Phase1a] 完成: 注册=%d 跳过=%d 失败=%d\n", 15, 78, 0) + stdio.fflush(0) + stdio.printf("[Phase1a] before_var_check ok\n") + stdio.fflush(0) + stdio.printf("[Phase1a] 完成: 注册=%d\n", p1a_reg) + stdio.fflush(0) + stdio.printf("[Phase1a] 完成: 注册=%d 跳过=%d\n", p1a_reg, p1a_skp) + stdio.fflush(0) stdio.printf("[Phase1a] 完成: 注册=%d 跳过=%d 失败=%d\n", p1a_reg, p1a_skp, p1a_fl) + stdio.fflush(0) + stdio.printf("[Phase1a] ALL_DIAG_OK\n") + stdio.fflush(0) # ============================================================ # Phase 1b: 全量翻译(struct 已注册,走 existing 路径翻译方法体) @@ -371,40 +395,70 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, skipped: int = 0 failed: int = 0 + stdio.printf("[Phase1b] 开始: Count=%d entry_size=%d\n", result.Count, entry_size) + stdio.fflush(0) for i in range(result.Count): + stdio.printf("[Phase1b] iter=%d/%d\n", i, result.Count) + stdio.fflush(0) # 获取 entry entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size + stdio.printf("[Phase1b] iter=%d entry_addr=%d\n", i, entry_addr) + stdio.fflush(0) entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) + stdio.printf("[Phase1b] iter=%d entry=%d\n", i, entry) + stdio.fflush(0) if entry is None: failed += 1 continue sha1: str = entry.Sha1 + stdio.printf("[Phase1b] iter=%d sha1=%s\n", i, sha1) + stdio.fflush(0) if sha1 is None: failed += 1 continue # 检查 SHA1 是否在翻译集合中(按需翻译:只翻译可达的文件) + in_set: int = 0 if use_reachable != 0: - if StubMerger._is_in_sha1_set(sha1, reachable_set, reachable_count) == 0: - skipped += 1 - continue + in_set = StubMerger._is_in_sha1_set(sha1, reachable_set, reachable_count) else: - if StubMerger._is_in_sha1_set(sha1, sha1_set, set_count) == 0: - skipped += 1 - continue + in_set = StubMerger._is_in_sha1_set(sha1, sha1_set, set_count) + stdio.printf("[Phase1b] iter=%d in_set=%d use_reachable=%d\n", i, in_set, use_reachable) + stdio.fflush(0) + if in_set == 0: + skipped += 1 + continue # 构造 stub 路径: {temp_dir}/{sha1}.stub.ll + stdio.printf("[Phase1b] iter=%d strlen 前\n", i) + stdio.fflush(0) dir_len: t.CSizeT = string.strlen(temp_dir) sha1_len: t.CSizeT = string.strlen(sha1) + stdio.printf("[Phase1b] iter=%d strlen 后 dir_len=%d sha1_len=%d\n", i, dir_len, sha1_len) + stdio.fflush(0) stub_path: bytes = stdlib.malloc(dir_len + sha1_len + 16) if stub_path is None: failed += 1 continue + stdio.printf("[Phase1b] iter=%d snprintf 前 stub_path=%d\n", i, stub_path) + stdio.fflush(0) viperlib.snprintf(stub_path, dir_len + sha1_len + 16, "%s/%s.stub.ll", temp_dir, sha1) + stdio.printf("[Phase1b] iter=%d snprintf 后\n", i) + stdio.fflush(0) # 检查 stub 是否已存在(按需翻译:跳过已存在的) + stdio.printf("[Phase1b] iter=%d File() 前 stub_path=%d\n", i, stub_path) + stdio.fflush(0) sf: fileio.File | t.CPtr = fileio.File(stub_path, fileio.MODE.R) + stdio.printf("[Phase1b] iter=%d File() 后 sf=%d\n", i, sf) + stdio.fflush(0) + if sf is None: + stdio.printf("[Phase1b] iter=%d sf is None, 翻译\n", i) + stdio.fflush(0) + else: + stdio.printf("[Phase1b] iter=%d sf.closed=%d\n", i, sf.closed) + stdio.fflush(0) if not sf.closed: sf.close() # 检查 text.ll 是否也存在(虚表扫描需要 text.ll) @@ -419,31 +473,71 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, continue stdlib.free(text_path) # text.ll 不存在,需要重新翻译 - stdio.printf("[Phase1] text.ll 不存在,重新翻译: %s (sha1=%s)\n", entry.RelPath, sha1) + stdio.printf("[Phase1b] iter=%d RelPath 前\n", i) + stdio.fflush(0) + rp: str = entry.RelPath + stdio.printf("[Phase1b] iter=%d RelPath=%s\n", i, rp) + stdio.fflush(0) + stdio.printf("[Phase1] text.ll 不存在,重新翻译: %s (sha1=%s)\n", rp, sha1) else: # stub 不存在,需要翻译 - stdio.printf("[Phase1] 翻译: %s (sha1=%s)\n", entry.RelPath, sha1) + stdio.printf("[Phase1b] iter=%d else 分支 RelPath 前\n", i) + stdio.fflush(0) + rp: str = entry.RelPath + stdio.printf("[Phase1b] iter=%d else RelPath=%s\n", i, rp) + stdio.fflush(0) + stdio.printf("[Phase1] 翻译: %s (sha1=%s)\n", rp, sha1) + stdio.fflush(0) + stdio.printf("[Phase1b] iter=%d free(stub_path) 前\n", i) + stdio.fflush(0) stdlib.free(stub_path) + stdio.printf("[Phase1b] iter=%d free(stub_path) 后\n", i) + stdio.fflush(0) # 读取文件内容 + stdio.printf("[Phase1b] iter=%d entry.Path 前\n", i) + stdio.fflush(0) file_path: str = entry.Path + stdio.printf("[Phase1b] iter=%d file_path=%s\n", i, file_path) + stdio.fflush(0) + stdio.printf("[Phase1b] iter=%d File(file_path) 前\n", i) + stdio.fflush(0) f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R) + stdio.printf("[Phase1b] iter=%d File(file_path) 后 f=%d\n", i, f) + stdio.fflush(0) + if f is None: + stdio.printf("[Phase1] 无法打开(None): %s\n", file_path) + stdio.fflush(0) + failed += 1 + continue + stdio.printf("[Phase1b] iter=%d f.closed=%d\n", i, f.closed) + stdio.fflush(0) if f.closed: stdio.printf("[Phase1] 无法打开: %s\n", file_path) + stdio.fflush(0) failed += 1 continue + stdio.printf("[Phase1b] iter=%d malloc(src_buf) 前\n", i) + stdio.fflush(0) src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE) + stdio.printf("[Phase1b] iter=%d malloc(src_buf) 后=%d\n", i, src_buf) + stdio.fflush(0) if src_buf is None: f.close() failed += 1 continue + stdio.printf("[Phase1b] iter=%d read_all 前\n", i) + stdio.fflush(0) bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE) + stdio.printf("[Phase1b] iter=%d read_all 后=%d\n", i, bytes_read) + stdio.fflush(0) f.close() if bytes_read <= 0: stdio.printf("[Phase1] 读取失败: %s\n", file_path) + stdio.fflush(0) stdlib.free(src_buf) failed += 1 continue @@ -452,33 +546,67 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, else: src_buf[SRC_BUF_SIZE - 1] = 0 + stdio.printf("[Phase1b] iter=%d AST 前\n", i) + stdio.fflush(0) + # 解析 AST + stdio.printf("[Phase1b] iter=%d new_lexer 前\n", i) + stdio.fflush(0) lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) + stdio.printf("[Phase1b] iter=%d new_lexer 后=%d\n", i, lx) + stdio.fflush(0) if lx is None: stdlib.free(src_buf) failed += 1 continue + stdio.printf("[Phase1b] iter=%d _lexer_init 前\n", i) + stdio.fflush(0) ast._lexer_init(lx, src_buf, mb) + stdio.printf("[Phase1b] iter=%d _lexer_init 后\n", i) + stdio.fflush(0) + stdio.printf("[Phase1b] iter=%d tokenize 前\n", i) + stdio.fflush(0) tokens: ast.Token | t.CPtr = ast.tokenize(lx) + stdio.printf("[Phase1b] iter=%d tokenize 后=%d\n", i, tokens) + stdio.fflush(0) + stdio.printf("[Phase1b] iter=%d parse_tokens 前\n", i) + stdio.fflush(0) tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) + stdio.printf("[Phase1b] iter=%d parse_tokens 后=%d\n", i, tree) + stdio.fflush(0) if tree is None: stdio.printf("[Phase1] AST 解析失败: %s\n", file_path) + stdio.fflush(0) stdlib.free(src_buf) failed += 1 continue # 翻译 AST → LLVM IR + stdio.printf("[Phase1b] iter=%d Translator() 前\n", i) + stdio.fflush(0) tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() + stdio.printf("[Phase1b] iter=%d Translator() 后=%d\n", i, tr) + stdio.fflush(0) if tr is None: stdlib.free(src_buf) failed += 1 continue + stdio.printf("[Phase1b] iter=%d ModuleSha1 前\n", i) + stdio.fflush(0) tr.ModuleSha1 = sha1 + stdio.printf("[Phase1b] iter=%d CurrentPackage 前\n", i) + stdio.fflush(0) tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry.RelPath) + stdio.printf("[Phase1b] iter=%d set_current_file 前\n", i) + stdio.fflush(0) HandlesType.set_current_file(file_path) HandlesType.clear_cdefine_constants() HandlesStruct.reset_visible_structs(mb, 0) + stdio.printf("[Phase1b] iter=%d translate 前\n", i) + stdio.fflush(0) ret: int = tr.translate(tree) + stdio.printf("[Phase1b] iter=%d translate 后=%d\n", i, ret) + stdio.fflush(0) if ret != 0: stdio.printf("[Phase1] 翻译失败: %s\n", file_path) stdlib.free(src_buf) diff --git a/App/lib/core/StubMerger.py b/App/lib/core/StubMerger.py index 1183cc4..96d4c66 100644 --- a/App/lib/core/StubMerger.py +++ b/App/lib/core/StubMerger.py @@ -8,7 +8,6 @@ import llvmlite import w32.win32file as win32file import w32.win32base as win32base import w32.fileio as fileio -import w32.win32memory as win32memory import viperlib import sys import ast @@ -70,7 +69,6 @@ def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr, f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R) if f.closed: stdio.printf("[StubMerger] _sha1_map.txt 不存在: %s\n", map_path) - stdlib.free(map_path) return -1 # 读取内容(使用 stdlib.malloc 避免 mbuddy 池耗尽) @@ -78,15 +76,15 @@ def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr, content: str = stdlib.malloc(MAP_BUF_SIZE) if content is None: f.close() - stdlib.free(map_path) return -1 bytes_read: t.CInt64T = f.read_all(content, MAP_BUF_SIZE) f.close() - stdlib.free(map_path) if bytes_read <= 0: - stdlib.free(content) return -1 - content[bytes_read] = '\0' + if bytes_read < MAP_BUF_SIZE: + content[bytes_read] = '\0' + else: + content[MAP_BUF_SIZE - 1] = '\0' # 解析行: {sha1}:{rel_path} count: int = 0 @@ -117,7 +115,6 @@ def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr, sha1_set[count * 17 + 16] = '\0' count += 1 - stdlib.free(content) return count @@ -195,8 +192,6 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str, written_count += 1 f.close() - stdlib.free(map_path) - stdlib.free(line_buf) stdio.printf("[Phase1] 已写入 _sha1_map.txt (%d 个 includes)\n", written_count) return 0 @@ -743,32 +738,16 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT, map_buf[map_br] = '\0' else: map_buf[STUB_READ_BUF_SIZE - 1] = '\0' - stdio.printf("[BISM] map_br=%lld\n", map_br) - stdio.fflush(0) - - # 堆探针:测试堆是否已被之前的操作损坏 - probe: bytes = stdlib.malloc(17) - stdio.printf("[BISM] probe malloc(17)=%p\n", probe) - stdio.fflush(0) - if probe is not None: - stdlib.free(probe) - stdio.printf("[BISM] probe free OK\n") - stdio.fflush(0) count: int = 0 pos: t.CSizeT = 0 + # 在循环外分配 sha1 缓冲区,避免反复 malloc/free 导致堆碎片化 + sha1: t.CChar | t.CPtr = stdlib.malloc(17) + if sha1 is None: + stdlib.free(map_buf) + return 0 while pos < map_br and count < MAX_INCLUDES: - stdio.printf("[BISM] iter=%d pos=%d\n", count, pos) - stdio.fflush(0) # 提取 SHA1(16 hex) - sha1: t.CChar | t.CPtr = stdlib.malloc(17) - stdio.printf("[BISM] malloc(17)=%p\n", sha1) - stdio.fflush(0) - if sha1 is None: - break - hv: int = win32memory.HeapValidate(win32memory.GetProcessHeap(), 0, None) - stdio.printf("[BISM] HV after malloc(sha1)=%d\n", hv) - stdio.fflush(0) string.strncpy(sha1, map_buf + pos, 16) sha1[16] = '\0' pos += 16 @@ -781,8 +760,6 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT, while pos < map_br and map_buf[pos] != '\n' and map_buf[pos] != '\r' and map_buf[pos] != '\0': pos += 1 path_len: t.CSizeT = pos - path_start - stdio.printf("[BISM] path_len=%d\n", path_len) - stdio.fflush(0) # 跳过 \r\n 或 \n if pos < map_br and map_buf[pos] == '\r': pos += 1 @@ -793,49 +770,25 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT, # 否则用户文件会被误判为 includes,导致 is_module_imported 模块名不匹配而跳过加载 if path_len >= 9 and string.strncmp(map_buf + path_start, "includes/", 9) == 0: path_buf: t.CChar | t.CPtr = stdlib.malloc(path_len + 1) - stdio.printf("[BISM] malloc(%d)=%p\n", path_len + 1, path_buf) - stdio.fflush(0) if path_buf is not None: string.strncpy(path_buf, map_buf + path_start, path_len) path_buf[path_len] = '\0' - hv2: int = win32memory.HeapValidate(win32memory.GetProcessHeap(), 0, None) - stdio.printf("[BISM] HV after fill(path_buf)=%d\n", hv2) - stdio.fflush(0) mod_name: str = _PathToModuleName(path_buf) - stdio.printf("[BISM] _PathToModuleName=%p\n", mod_name) - stdio.fflush(0) - hv3: int = win32memory.HeapValidate(win32memory.GetProcessHeap(), 0, None) - stdio.printf("[BISM] HV after _PathToModuleName=%d\n", hv3) - stdio.fflush(0) stdlib.free(path_buf) if mod_name is not None: # 存储 sha1 和 mod_name 到数组中 idx: t.CSizeT = t.CSizeT(count) * 17 string.strcpy(sha1_list + idx, sha1) - hv4: int = win32memory.HeapValidate(win32memory.GetProcessHeap(), 0, None) - stdio.printf("[BISM] HV after strcpy(sha1_list)=%d\n", hv4) - stdio.fflush(0) idx2: t.CSizeT = t.CSizeT(count) * 64 mn_len: t.CSizeT = string.strlen(mod_name) - stdio.printf("[BISM] mn_len=%d count=%d\n", mn_len, count) - stdio.fflush(0) if mn_len < 64: string.strcpy(mod_list + idx2, mod_name) else: string.strncpy(mod_list + idx2, mod_name, 63) mod_list[idx2 + 63] = '\0' - hv5: int = win32memory.HeapValidate(win32memory.GetProcessHeap(), 0, None) - stdio.printf("[BISM] HV after strcpy(mod_list)=%d\n", hv5) - stdio.fflush(0) stdlib.free(mod_name) - stdio.printf("[BISM] free(mod_name) OK\n") - stdio.fflush(0) count += 1 - stdio.printf("[BISM] before free(sha1=%p)\n", sha1) - stdio.fflush(0) - stdlib.free(sha1) - stdio.printf("[BISM] free(sha1) OK\n") - stdio.fflush(0) + stdlib.free(sha1) stdlib.free(map_buf) return count diff --git a/App/lib/core/SymbolData.py b/App/lib/core/SymbolData.py new file mode 100644 index 0000000..c422de0 --- /dev/null +++ b/App/lib/core/SymbolData.py @@ -0,0 +1,85 @@ +"""符号表处理的数据结构:四阶段管线中传递的中间数据""" +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Dict, List, Optional, Tuple, TYPE_CHECKING +import ast + +if TYPE_CHECKING: + from lib.core.Handles.HandlesBase import CTypeInfo + + +@dataclass +class ClassSymbolData: + """从 AST 提取的类符号数据""" + name: str + type_kind: str # 'struct', 'union', 'enum', 'exception' + lineno: int + members: Dict[str, CTypeInfo] + is_cpython_object: bool = False + is_packed: bool = False + enum_members: List[Tuple[str, int, int]] = field(default_factory=list) # (name, value, lineno) + + +@dataclass +class TypedefSymbolData: + """Typedef 符号数据(含原始注解和解析结果)""" + name: str + lineno: int + # 原始注解数据(由 Extractor 设置,Resolver 使用) + annotation: ast.expr | None = None + value: ast.expr | int | str | float | None = None + has_cdefine: bool = False + has_postdef: bool = False + has_ctypedef: bool = False + # 解析结果(由 Resolver 设置,Inserter 使用) + original_type_kind: Optional[str] = None + original_class: type | None = None + members: Optional[Dict] = None + + +@dataclass +class FuncSymbolData: + """函数符号数据(含原始注解和解析结果)""" + name: str + lineno: int + # 原始数据(由 Extractor 设置) + returns_node: ast.expr | None = None + params: List = field(default_factory=list) # [(name, annotation_node), ...] + is_variadic: bool = False + is_inline: bool = False + # 解析结果(由 Resolver 设置) + ret_type: str | None = None + param_types: List = field(default_factory=list) # [str, ...] + # 解析结果(CTypeInfo,保留符号性,由 Resolver 设置) + ret_ctype: Optional[CTypeInfo] = None + + +@dataclass +class DefineSymbolData: + """Define 符号数据(含原始值和解析结果)""" + name: str + lineno: int + # 原始数据(由 Extractor 设置) + value_node: ast.expr | None = None + # 解析结果(由 Resolver 设置) + value: int | str | float | None = None + + +@dataclass +class AnonymousTypeData: + """匿名类型数据""" + name: str + is_union: bool + members: Dict[str, CTypeInfo] + lineno: int + + +@dataclass +class ModuleSymbols: + """从模块提取的完整符号数据,在四阶段管线中传递""" + file_path: str + classes: List[ClassSymbolData] = field(default_factory=list) + typedefs: List[TypedefSymbolData] = field(default_factory=list) + functions: List[FuncSymbolData] = field(default_factory=list) + defines: List[DefineSymbolData] = field(default_factory=list) + anonymous_types: Dict[str, AnonymousTypeData] = field(default_factory=dict) diff --git a/App/lib/core/SymbolExtractor.py b/App/lib/core/SymbolExtractor.py new file mode 100644 index 0000000..f3fba6a --- /dev/null +++ b/App/lib/core/SymbolExtractor.py @@ -0,0 +1,307 @@ +"""Pass 1: ASTSymbolExtractor — 从 AST 提取原始符号信息""" +from __future__ import annotations +import ast +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + +from lib.core.SymbolData import ( + ModuleSymbols, ClassSymbolData, TypedefSymbolData, + FuncSymbolData, DefineSymbolData, AnonymousTypeData +) +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName +from lib.includes import t + + +class ASTSymbolExtractor: + """从 Python 源文件的 AST 中提取所有符号信息""" + + def __init__(self, symbol_table: SymbolTable) -> None: + self._symtab: SymbolTable = symbol_table + + def extract(self, file_path: str) -> ModuleSymbols: + """提取文件中的所有符号信息""" + with open(file_path, 'r', encoding='utf-8') as f: + code: str = f.read() + tree: ast.Module = ast.parse(code) + + result: ModuleSymbols = ModuleSymbols(file_path=file_path) + + # 第一步:收集所有类的成员和匿名类型 + class_members: dict[str, dict[str, CTypeInfo]] = {} + for node in tree.body: + if isinstance(node, ast.ClassDef): + members: dict[str, CTypeInfo] = {} + self._collect_members(node, members, result.anonymous_types) + class_members[node.name] = members + + # 第二步:从每个顶层节点提取符号信息 + for node in tree.body: + if isinstance(node, ast.ClassDef): + members_info: dict[str, CTypeInfo] = class_members.get(node.name, {}) + result.classes.append(self._extract_class(node, members_info)) + elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + self._extract_ann_assign(node, result) + elif isinstance(node, ast.FunctionDef): + result.functions.append(self._extract_function(node)) + + return result + + # ------------------------------------------------------------------ + # 成员收集(递归处理嵌套匿名类) + # ------------------------------------------------------------------ + + def _collect_members(self, class_node: ast.ClassDef, members: dict[str, CTypeInfo], anonymous_types: dict[str, AnonymousTypeData], prefix: str = '') -> None: + """递归收集类成员,包括嵌套匿名类型""" + for item in class_node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + member_name: str = item.target.id + full_name: str = f'{prefix}{member_name}' if prefix else member_name + member_type: CTypeInfo | None = CTypeInfo.FromNode(item.annotation, self._symtab) + if member_type is None: + member_type = CTypeInfo() + member_type.BaseType = t.CInt() + is_ptr: bool = member_type.IsPtr + if isinstance(item.annotation, ast.BinOp): + if AnnotationContainsName(item.annotation, 'CPtr'): + is_ptr = True + members[full_name] = member_type.Copy() + + elif isinstance(item, ast.ClassDef): + is_anonymous: bool = any( + any(AnnotationContainsTType(base, 'Anonymous') for base in item.bases) + ) if item.bases else False + + if is_anonymous: + is_union: bool = any( + any(AnnotationContainsTType(base, 'CUnion') for base in item.bases) + ) + # 也检查装饰器形式 @t.CUnion + if not is_union and hasattr(item, 'decorator_list') and item.decorator_list: + for deco in item.decorator_list: + if isinstance(deco, ast.Attribute) and hasattr(deco.value, 'id') and IsTModule(deco.value.id, self._symtab) and deco.attr == 'CUnion': + is_union = True + break + elif isinstance(deco, ast.Name): + cls2: type | None = getattr(t, deco.id, None) + if cls2 is None and hasattr(self._symtab, '_t_type_symbols'): + cls2 = self._symtab._t_type_symbols.get(deco.id) + if cls2 is not None and isinstance(cls2, type) and issubclass(cls2, t.CUnion): + is_union = True + break + + nested_name: str = f'{prefix}{item.name}' if prefix else item.name + members[nested_name] = CTypeInfo() + members[nested_name].BaseType = f'union {item.name}' if is_union else f'struct {item.name}' + members[nested_name].PtrCount = 0 + members[nested_name].ArrayDims = [] + + new_prefix: str = f'{prefix}{item.name}.' if prefix else f'{item.name}.' + self._collect_members(item, members, anonymous_types, new_prefix) + + anonymous_members: dict[str, CTypeInfo] = {} + for full_name, member_info in members.items(): + if full_name.startswith(f"{nested_name}."): + member_name: str = full_name[len(nested_name) + 1:] + anonymous_members[member_name] = member_info + + if item.name not in anonymous_types: + anonymous_types[item.name] = AnonymousTypeData( + name=item.name, + is_union=is_union, + members=anonymous_members, + lineno=item.lineno + ) + + # ------------------------------------------------------------------ + # 类符号提取 + # ------------------------------------------------------------------ + + def _extract_class(self, node: ast.ClassDef, members_info: dict[str, CTypeInfo]) -> ClassSymbolData: + """从 ClassDef 节点提取类符号数据""" + type_kind: str = 'struct' + is_cpython_object: bool = False + is_packed: bool = 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 + IsTModule(decorator.value.id, self._symtab)): + if decorator.attr == 'Object': + is_cpython_object = True + elif decorator.attr == 'CUnion': + type_kind = 'union' + elif decorator.attr == 'CEnum': + type_kind = 'enum' + elif decorator.attr == 'REnum': + type_kind = 'renum' + elif decorator.attr == 'CStruct': + type_kind = 'struct' + elif isinstance(decorator, ast.Name): + # from t import CStruct / CUnion / CEnum / REnum + cls: type | None = getattr(t, decorator.id, None) + if cls is None and hasattr(self._symtab, '_t_type_symbols'): + cls = self._symtab._t_type_symbols.get(decorator.id) + if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): + if issubclass(cls, t.CUnion): + type_kind = 'union' + elif issubclass(cls, t.REnum): + type_kind = 'renum' + elif issubclass(cls, t.CEnum): + type_kind = 'enum' + elif issubclass(cls, t.CStruct): + type_kind = 'struct' + elif decorator.id == 'Object': + is_cpython_object = True + 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 IsTModule(arg.value.value.id, self._symtab) and arg.value.attr == 'attr' and arg.attr == 'packed': + is_packed = True + + # 检查基类 + for base in node.bases: + if AnnotationContainsTType(base, 'CUnion'): + type_kind = 'union' + break + elif AnnotationContainsTType(base, 'CEnum'): + type_kind = 'enum' + break + elif AnnotationContainsTType(base, 'REnum'): + type_kind = 'renum' + break + elif AnnotationContainsTType(base, 'CStruct'): + type_kind = 'struct' + break + elif AnnotationContainsTType(base, 'Object'): + is_cpython_object = True + type_kind = 'struct' + break + elif isinstance(base, ast.Name) and base.id == 'Exception': + type_kind = 'exception' + break + elif isinstance(base, ast.Name): + base_entry: CTypeInfo | None = self._symtab.get(base.id) + if base_entry and base_entry.IsExceptionClass: + type_kind = 'exception' + break + + # 提取枚举成员 + enum_members: list[tuple[str, int, int]] = [] + if type_kind == 'enum': + next_enum_value: int = 0 + for item in node.body: + if isinstance(item, ast.Assign): + if len(item.targets) == 1 and isinstance(item.targets[0], ast.Name): + member_name: str = 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 + enum_members.append((member_name, next_enum_value - 1, item.lineno)) + elif isinstance(item, ast.AnnAssign): + if isinstance(item.target, ast.Name): + member_name: str = 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 + enum_members.append((member_name, next_enum_value - 1, item.lineno)) + + # REnum 变体提取(嵌套 ClassDef + Assign 显式值) + # 与 HandlesClassDef._EmitREnumLlvm 的 tag 分配逻辑对齐: + # - 先收集 Assign 显式值 + # - ClassDef 变体从 max(explicit)+1 开始顺序编号 + # - Assign 无显式值时用独立计数器 variant_index + if type_kind == 'renum': + explicit_values: dict[str, int] = {} + for item in node.body: + if isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name): + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + explicit_values[target.id] = item.value.value + next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0 + variant_index: int = 0 + for item in node.body: + if isinstance(item, ast.ClassDef): + variant_name_r: str = item.name + tag: int = next_class_tag + next_class_tag += 1 + enum_members.append((variant_name_r, tag, item.lineno)) + elif isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name): + var_name: str = target.id + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + value: int = item.value.value + else: + value = variant_index + variant_index += 1 + enum_members.append((var_name, value, item.lineno)) + + return ClassSymbolData( + name=node.name, + type_kind=type_kind, + lineno=node.lineno, + members=members_info, + is_cpython_object=is_cpython_object, + is_packed=is_packed, + enum_members=enum_members, + ) + + # ------------------------------------------------------------------ + # AnnAssign 提取(typedef / define) + # ------------------------------------------------------------------ + + def _extract_ann_assign(self, node: ast.AnnAssign, result: ModuleSymbols) -> None: + """从 AnnAssign 节点提取 typedef 或 define 数据""" + var_name: str = node.target.id + has_cdefine: bool = AnnotationContainsTType(node.annotation, 'CDefine') if node.annotation else False + has_postdef: bool = AnnotationContainsTType(node.annotation, 'Postdefinition') if node.annotation else False + has_ctypedef: bool = AnnotationContainsTType(node.annotation, 'CTypedef') if node.annotation else False + + if has_cdefine: + result.defines.append(DefineSymbolData( + name=var_name, + lineno=node.lineno, + value_node=node.value, + )) + else: + result.typedefs.append(TypedefSymbolData( + name=var_name, + lineno=node.lineno, + annotation=node.annotation, + value=node.value, + has_cdefine=has_cdefine, + has_postdef=has_postdef, + has_ctypedef=has_ctypedef, + )) + + # ------------------------------------------------------------------ + # 函数提取 + # ------------------------------------------------------------------ + + def _extract_function(self, node: ast.FunctionDef) -> FuncSymbolData: + """从 FunctionDef 节点提取函数符号数据""" + params: list[tuple[str, ast.AST | None]] = [(arg.arg, arg.annotation) for arg in node.args.args] + is_variadic: bool = node.args.vararg is not None + is_inline: bool = CheckAnnotationHasCInline(node.returns) + + return FuncSymbolData( + name=node.name, + lineno=node.lineno, + returns_node=node.returns, + params=params, + is_variadic=is_variadic, + is_inline=is_inline, + ) diff --git a/App/lib/core/SymbolInserter.py b/App/lib/core/SymbolInserter.py new file mode 100644 index 0000000..01dc3a1 --- /dev/null +++ b/App/lib/core/SymbolInserter.py @@ -0,0 +1,89 @@ +"""Pass 3: SymbolInserter — 将解析后的符号插入 SymbolTable(主命名空间)""" +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + +from lib.core.SymbolData import ModuleSymbols + + +class SymbolInserter: + """将解析后的符号插入 SymbolTable 的主命名空间(无前缀)""" + + def __init__(self, symbol_table: SymbolTable) -> None: + self._symtab: SymbolTable = symbol_table + self._namespace: str | None = None + + def set_namespace(self, namespace: str) -> None: + """设置当前模块的命名空间""" + self._namespace = namespace + self._symtab.register_namespace(namespace) + + def insert(self, module_symbols: ModuleSymbols) -> list[str]: + """插入所有符号到主命名空间,返回已加载的符号名列表""" + loaded: list[str] = [] + file_path: str = module_symbols.file_path + ns: dict[str, object] = self._symtab._namespaces.get(self._namespace, {}) if self._namespace else {} + + # 插入类符号 + for cls in module_symbols.classes: + self._symtab._InsertClassSymbol( + cls.name, cls.type_kind, cls.lineno, file_path, + cls.members, cls.is_cpython_object, cls.is_packed + ) + loaded.append(cls.name) + if self._namespace: + ns[cls.name] = self._symtab._symbols[cls.name] + + # 插入枚举成员(短名 + 类名.成员名)—— CEnum 和 REnum 共用 + if cls.type_kind in ('enum', 'renum'): + for member_name, member_value, member_lineno in cls.enum_members: + self._symtab._InsertEnumMemberSymbol(member_name, cls.name, member_lineno, file_path, value=member_value) + loaded.append(member_name) + class_member_name: str = f"{cls.name}.{member_name}" + self._symtab._InsertEnumMemberSymbol(class_member_name, cls.name, member_lineno, file_path, value=member_value) + loaded.append(class_member_name) + # REnum: 记录变体名列表到类符号(供 HandlesExprCall 跨模块变体构造识别) + if cls.type_kind == 'renum': + renum_info = self._symtab._symbols[cls.name] + renum_info.RenumVariants = [m[0] for m in cls.enum_members] + + # 插入 typedef 符号 + for td in module_symbols.typedefs: + self._symtab._InsertTypedefSymbol( + td.name, td.original_type_kind, td.original_class, + td.lineno, file_path, td.members + ) + loaded.append(td.name) + if self._namespace: + ns[td.name] = self._symtab._symbols[td.name] + + # 插入函数符号 + for func in module_symbols.functions: + # 优先使用 CTypeInfo(保留符号性),回退到 LLVM 类型字符串 + ret_type = func.ret_ctype if func.ret_ctype else func.ret_type + self._symtab._InsertFuncSymbol( + func.name, ret_type, func.param_types, + func.lineno, file_path, func.is_variadic, func.is_inline + ) + loaded.append(func.name) + if self._namespace: + ns[func.name] = self._symtab._symbols[func.name] + + # 插入 define 符号 + for define in module_symbols.defines: + self._symtab._InsertDefineSymbol( + define.name, define.value, define.lineno, file_path + ) + loaded.append(define.name) + if self._namespace: + ns[define.name] = self._symtab._symbols[define.name] + + # 插入匿名类型符号(短名) + for anon_name, anon_data in module_symbols.anonymous_types.items(): + self._symtab._InsertAnonymousSymbol( + anon_name, anon_data.is_union, anon_data.members, + anon_data.lineno, file_path + ) + + return loaded diff --git a/App/lib/core/SymbolNode.py b/App/lib/core/SymbolNode.py new file mode 100644 index 0000000..f1aa0ef --- /dev/null +++ b/App/lib/core/SymbolNode.py @@ -0,0 +1,224 @@ +from __future__ import annotations +from typing import Any, Dict, Optional + +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.includes import t + + +class SymbolNode: + """符号表节点,类似 AST 节点的树形结构 + + attributes 存储 CTypeInfo 对象,不再使用 TypeInfo + """ + + def __init__(self, name: str, NodeType: str, lineno: int = 0, file: str = '', parent: SymbolNode | None = None): + self.name: str = name + self.type: str = NodeType + self.lineno: int = lineno + self.file: str = file + self.parent: SymbolNode | None = parent + self.children: Dict[str, SymbolNode] = {} + self.attributes: CTypeInfo = CTypeInfo() + self.attributes.Name = name + self.attributes.Lineno = lineno + + def get(self, key: str, default: Any = None) -> Any: + """获取属性值""" + if hasattr(self.attributes, key): + return getattr(self.attributes, key) + return self.attributes._extra.get(key, default) + + def set(self, key: str, value: Any) -> None: + """设置属性值""" + if hasattr(self.attributes, key): + attr: Any = getattr(type(self.attributes), key, None) + if isinstance(attr, property) and attr.fset is None: + return + setattr(self.attributes, key, value) + else: + self.attributes._extra[key] = value + + def HasChild(self, name: str) -> bool: + """检查是否有子节点""" + return name in self.children + + def GetChild(self, name: str) -> Optional[SymbolNode]: + """获取子节点""" + return self.children.get(name) + + def AddChild(self, node: SymbolNode): + """添加子节点""" + node.parent = self + self.children[node.name] = node + + def RemoveChild(self, name: str) -> bool: + """删除子节点""" + if name in self.children: + del self.children[name] + return True + return False + + def find(self, path: str, separator: str = '.') -> Optional[SymbolNode]: + """根据路径查找节点,如 'test.ClassA'""" + parts: list[str] = path.split(separator) + current: SymbolNode = self + + for part in parts: + if current.HasChild(part): + current = current.GetChild(part) + else: + return None + + return current + + def ToDict(self) -> Dict[str, Any]: + """转换为字典格式""" + result: dict[str, Any] = { + 'type': self.type, + 'lineno': self.lineno, + 'file': self.file, + } + # 序列化 CTypeInfo 属性 + attrs: CTypeInfo = self.attributes + if attrs.BaseType: + result['BaseType'] = str(attrs.BaseType) + if attrs.PtrCount: + result['PtrCount'] = attrs.PtrCount + if attrs.IsTypedef: + result['IsTypedef'] = True + if attrs.IsStruct: + result['IsStruct'] = True + if attrs.IsEnum: + result['IsEnum'] = True + if attrs.IsUnion: + result['IsUnion'] = True + if attrs.IsFunction: + result['IsFunction'] = True + if attrs.IsVariable: + result['IsVariable'] = True + if attrs.IsDefine: + result['IsDefine'] = True + if attrs.OriginalType: + result['OriginalType'] = str(attrs.OriginalType) + if attrs._sm._extra: + result.update(attrs._sm._extra) + + if self.children: + result['children'] = {name: child.ToDict() for name, child in self.children.items()} + + return result + + @classmethod + def FromDict(cls, name: str, data: Dict[str, Any]) -> SymbolNode: + """从字典创建节点(兼容性)""" + node = cls(name=name, NodeType=data.get('type', 'unknown')) + node.lineno = data.get('lineno', 0) + node.file = data.get('file', '') + + for key, value in data.items(): + if key not in ('type', 'lineno', 'file', 'children'): + node.set(key, value) + + if 'children' in data: + for ChildName, ChildData in data['children'].items(): + node.AddChild(cls.FromDict(ChildName, ChildData)) + + return node + + @classmethod + def CreateMember(cls, name: str, TypeName: str | None = None, TypeInfo: 'CTypeInfo | None' = None, IsPtr: bool = False, dims: list[int] | None = None, lineno: int = 0, file: str = '') -> SymbolNode: + """创建成员节点(工厂函数)""" + node = cls(name=name, NodeType='member', lineno=lineno, file=file) + node.attributes = CTypeInfo() + node.attributes.Name = name + node.attributes.Lineno = lineno + if TypeInfo is not None: + node.attributes = TypeInfo + node.attributes.Name = name + node.attributes.Lineno = lineno + else: + if TypeName is not None: + node.attributes.BaseType = CTypeInfo.CreateFromTypeName(TypeName) + if IsPtr: + node.attributes.PtrCount = 1 + if dims: + node.attributes.ArrayDims = list(dims) + return node + + @classmethod + def CreateClass(cls, name: str, TypeKind: type, members: dict | None = None, lineno: int = 0, file: str = '', IsCpythonObject: bool = False, IsPacked: bool = False) -> SymbolNode: + """创建类节点(工厂函数)""" + node: SymbolNode = cls(name=name, NodeType=TypeKind, lineno=lineno, file=file) + node.attributes = CTypeInfo() + node.attributes.Name = name + node.attributes.Lineno = lineno + node.attributes.IsCpythonObject = IsCpythonObject + node.attributes.IsPacked = IsPacked + if members: + for MemberName, MemberInfo in members.items(): + if isinstance(MemberInfo, CTypeInfo): + node.attributes.Members[MemberName] = MemberInfo + elif isinstance(MemberInfo, dict): + CTypeMember: CTypeInfo = CTypeInfo() + type_str: str = MemberInfo.get('type', 'int') + IsPtr: bool = MemberInfo.get('IsPtr', False) + if isinstance(type_str, str): + try: + resolved: CTypeInfo | None = CTypeInfo.FromTypeName(type_str) + if resolved and resolved.BaseType: + CTypeMember = resolved + else: + CTypeMember.BaseType = t.CInt() + except Exception: + CTypeMember.BaseType = t.CInt() + else: + CTypeMember.BaseType = t.CInt() + if IsPtr and not CTypeMember.IsPtr: + CTypeMember.PtrCount = max(CTypeMember.PtrCount, 1) + node.attributes.Members[MemberName] = CTypeMember + if TypeKind == 'struct': + node.attributes.IsStruct = True + elif TypeKind == 'enum': + node.attributes.IsEnum = True + elif TypeKind == 'union': + node.attributes.IsUnion = True + elif TypeKind == 'typedef': + node.attributes.IsTypedef = True + elif TypeKind == 'exception': + node.attributes.IsExceptionClass = True + return node + + @classmethod + def CreateTypedef(cls, name: str, OriginalType: CTypeInfo | str | None, members: dict | None = None, lineno: int = 0, file: str = '') -> SymbolNode: + node: SymbolNode = cls(name=name, NodeType='typedef', lineno=lineno, file=file) + node.attributes = CTypeInfo() + node.attributes.Name = name + node.attributes.Lineno = lineno + node.attributes.IsTypedef = True + if isinstance(OriginalType, CTypeInfo): + node.attributes.OriginalType = OriginalType + elif isinstance(OriginalType, str): + node.attributes.OriginalType = CTypeInfo.FromTypeName(OriginalType) if OriginalType else None + else: + node.attributes.OriginalType = OriginalType + return node + + @classmethod + def CreateAnonymous(cls, name: str, IsUnion: bool, members: dict, lineno: int = 0, file: str = '') -> SymbolNode: + """创建匿名类型节点(工厂函数)""" + NodeType: type = t.CUnion if IsUnion else t.CStruct + node: SymbolNode = cls(name=name, NodeType=NodeType, lineno=lineno, file=file) + node.attributes = CTypeInfo() + node.attributes.Name = name + node.attributes.Lineno = lineno + node.attributes.IsAnonymous = True + if IsUnion: + node.attributes.IsUnion = True + else: + node.attributes.IsStruct = True + return node + + @classmethod + def CreateModule(cls, name: str, lineno: int = 0, file: str = '') -> SymbolNode: + """创建模块节点(工厂函数)""" + return cls(name=name, NodeType='module', lineno=lineno, file=file) diff --git a/App/lib/core/SymbolReexporter.py b/App/lib/core/SymbolReexporter.py new file mode 100644 index 0000000..ae86b80 --- /dev/null +++ b/App/lib/core/SymbolReexporter.py @@ -0,0 +1,99 @@ +"""Pass 4: PackageReexporter — 将符号重新导出到命名空间前缀下""" +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + +from lib.core.SymbolData import ModuleSymbols + + +class PackageReexporter: + """将已解析的符号重新导出到命名空间前缀下(如 memhub.MemPool)""" + + def __init__(self, symbol_table: SymbolTable) -> None: + self._symtab: SymbolTable = symbol_table + self._namespace: str | None = None + + def set_namespace(self, namespace: str) -> None: + """设置当前模块的命名空间""" + self._namespace = namespace + + def reexport(self, module_symbols: ModuleSymbols, prefixes: list[str], lineno: int = 0) -> list[str]: + """将符号重新导出到所有命名空间前缀下,返回新增的符号名列表""" + loaded: list[str] = [] + file_path: str = module_symbols.file_path + ns: dict[str, object] = self._symtab._namespaces.get(self._namespace, {}) if self._namespace else {} + + for prefix in prefixes: + # 插入类符号(前缀.类名) + for cls in module_symbols.classes: + full_name: str = f"{prefix}.{cls.name}" + self._symtab._InsertClassSymbol( + full_name, cls.type_kind, cls.lineno, file_path, + cls.members, cls.is_cpython_object, cls.is_packed + ) + loaded.append(full_name) + if self._namespace: + ns[full_name] = self._symtab._symbols[full_name] + + # 插入枚举成员(前缀.成员名 + 前缀.类名.成员名)—— CEnum 和 REnum 共用 + if cls.type_kind in ('enum', 'renum'): + for member_name, member_value, member_lineno in cls.enum_members: + full_member_name: str = f"{prefix}.{member_name}" + self._symtab._InsertEnumMemberSymbol(full_member_name, cls.name, member_lineno, file_path, value=member_value) + loaded.append(full_member_name) + # 同时注册 prefix.ClassName.member_name,以支持 module.Class.MEMBER 三层访问 + qualified_member_name: str = f"{prefix}.{cls.name}.{member_name}" + self._symtab._InsertEnumMemberSymbol(qualified_member_name, cls.name, member_lineno, file_path, value=member_value) + loaded.append(qualified_member_name) + # REnum: 记录变体名列表到前缀类符号 + if cls.type_kind == 'renum': + renum_info = self._symtab._symbols[full_name] + renum_info.RenumVariants = [m[0] for m in cls.enum_members] + + # 插入 typedef 符号(前缀.名称) + for td in module_symbols.typedefs: + full_name: str = f"{prefix}.{td.name}" + self._symtab._InsertTypedefSymbol( + full_name, td.original_type_kind, td.original_class, + td.lineno, file_path, td.members + ) + loaded.append(full_name) + if self._namespace: + ns[full_name] = self._symtab._symbols[full_name] + + # 插入函数符号(前缀.名称) + for func in module_symbols.functions: + full_name: str = f"{prefix}.{func.name}" + # 优先使用 CTypeInfo(保留符号性),回退到 LLVM 类型字符串 + ret_type = func.ret_ctype if func.ret_ctype else func.ret_type + self._symtab._InsertFuncSymbol( + full_name, ret_type, func.param_types, + func.lineno, file_path, func.is_variadic, func.is_inline + ) + loaded.append(full_name) + if self._namespace: + ns[full_name] = self._symtab._symbols[full_name] + + # 插入 define 符号(前缀.名称) + for define in module_symbols.defines: + full_name: str = f"{prefix}.{define.name}" + self._symtab._InsertDefineSymbol( + full_name, define.value, define.lineno, file_path + ) + loaded.append(full_name) + if self._namespace: + ns[full_name] = self._symtab._symbols[full_name] + + # 插入模块别名 + self._symtab._InsertModuleSymbol(prefix, lineno, file_path) + + # 插入匿名类型符号(前缀.名称) + for anon_name, anon_data in module_symbols.anonymous_types.items(): + full_type_name: str = f"{prefix}.{anon_name}" + self._symtab._InsertAnonymousSymbol( + full_type_name, anon_data.is_union, anon_data.members, + anon_data.lineno, file_path + ) + + return loaded diff --git a/App/lib/core/SymbolTable.py b/App/lib/core/SymbolTable.py new file mode 100644 index 0000000..9646b54 --- /dev/null +++ b/App/lib/core/SymbolTable.py @@ -0,0 +1,531 @@ +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 diff --git a/App/lib/core/SymbolTypeResolver.py b/App/lib/core/SymbolTypeResolver.py new file mode 100644 index 0000000..deb567c --- /dev/null +++ b/App/lib/core/SymbolTypeResolver.py @@ -0,0 +1,272 @@ +"""Pass 2: TypeResolver — 解析 typedef 类型、函数签名、define 值""" +from __future__ import annotations +import ast +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + +from lib.core.SymbolData import ( + ModuleSymbols, TypedefSymbolData, FuncSymbolData, DefineSymbolData +) +from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper +from lib.core.LLVMTypeMapper import LLVMTypeMapper +from lib.core.SymbolUtils import IsTModule +from lib.includes import t + + +class TypeResolver: + """解析从 AST 提取的原始符号中的类型信息""" + + def __init__(self, symbol_table: SymbolTable): + self._symtab = symbol_table + self._type_mapper = LLVMTypeMapper(symbol_table) + # 本模块内已解析的 typedef: name → original_class(供同模块 typedef 间引用解析) + self._resolved_typedefs: dict[str, str | None] = {} + + def resolve(self, module_symbols: ModuleSymbols) -> ModuleSymbols: + """解析所有类型信息,就地更新 ModuleSymbols""" + # 构建 class_members 查找表(typedef Postdefinition 需要) + class_members: dict[str, dict[str, CTypeInfo]] = {cls.name: cls.members for cls in module_symbols.classes} + + # 解析 typedef(按顺序记录,供后续 typedef 引用同模块已解析的 typedef) + td: TypedefSymbolData + for td in module_symbols.typedefs: + self._resolve_typedef(td, class_members) + self._resolved_typedefs[td.name] = td.original_class + + # 解析函数签名 + func: FuncSymbolData + for func in module_symbols.functions: + self._resolve_function(func) + + # 解析 define 值 + define: DefineSymbolData + for define in module_symbols.defines: + self._resolve_define(define) + + return module_symbols + + # ------------------------------------------------------------------ + # Typedef 解析 + # ------------------------------------------------------------------ + + def _resolve_typedef(self, td: TypedefSymbolData, class_members: dict[str, dict[str, CTypeInfo]]): + """解析单个 typedef 的类型信息""" + is_postdef_with_typedef: bool = td.has_postdef and td.has_ctypedef + + if is_postdef_with_typedef: + self._resolve_postdef_typedef(td, class_members) + elif td.has_ctypedef: + self._resolve_ctypedef_typedef(td) + else: + # 无特殊注解的 typedef + td.original_type_kind = None + td.original_class = None + + def _resolve_postdef_typedef(self, td: TypedefSymbolData, class_members: dict[str, dict[str, CTypeInfo]]): + """解析 Postdefinition + CTypedef 组合注解""" + original_class: str | None = self._find_postdef_arg(td.annotation) + if original_class and original_class in class_members: + members_info: dict[str, CTypeInfo] = class_members[original_class] + original_type_kind: str = 'struct' + if original_class in self._symtab: + orig_type_info: CTypeInfo = self._symtab[original_class] + if orig_type_info.type_cls in (t.CStruct, t.CEnum, t.CUnion): + if orig_type_info.type_cls is t.CStruct: + original_type_kind = 'struct' + elif orig_type_info.type_cls is t.CEnum: + original_type_kind = 'enum' + elif orig_type_info.type_cls is t.CUnion: + original_type_kind = 'union' + td.original_type_kind = original_type_kind + td.original_class = original_class + td.members = members_info + else: + td.original_type_kind = None + td.original_class = None + + def _resolve_ctypedef_typedef(self, td: TypedefSymbolData): + """解析 CTypedef 注解""" + base_type_name: str | None = self._find_base_type(td.annotation) + _TYPE_QUALIFIERS: frozenset[str] = frozenset({'CTypedef', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern', 'CDefine', 'CPostdefinition'}) + + if base_type_name == 'Callable': + original_type: CTypeInfo = CTypeInfo() + original_type.IsFuncPtr = True + original_type.FuncPtrReturn = CTypeInfo.VoidTypeInfo() + original_type.FuncPtrParams = [] + td.original_type_kind = 'typedef' + td.original_class = original_type + elif base_type_name and base_type_name not in _TYPE_QUALIFIERS: + cname: str | None = CTypeHelper.GetCName(base_type_name) + if cname: + td.original_class = cname + else: + td.original_class = base_type_name + td.original_type_kind = 'typedef' + else: + # 尝试从值表达式解析 + self._resolve_typedef_from_value(td) + + def _resolve_typedef_from_value(self, td: TypedefSymbolData): + """从值表达式解析 typedef 类型 + + BinOp(如 UINT | t.CPtr)优先于 _find_base_type 处理, + 因为 _find_base_type 是静态方法无法解析 Name 引用(如 UINT)。 + """ + if not td.value: + td.original_type_kind = None + td.original_class = None + return + + # 优先处理 BinOp(_find_base_type 无法解析 Name 引用) + if isinstance(td.value, ast.BinOp) and isinstance(td.value.op, ast.BitOr): + left_type: str = self._resolve_type_expr(td.value.left) + right_type: str = self._resolve_type_expr(td.value.right) + is_ptr_type: bool = right_type == 'ptr' or left_type == 'ptr' + if is_ptr_type: + base_name: str = left_type if left_type != 'ptr' else (right_type if right_type != 'ptr' else '') + original_type: str = (base_name + ' *') if base_name else 'void *' + td.original_class = original_type + td.original_type_kind = 'typedef' + elif left_type: + td.original_class = left_type + td.original_type_kind = 'typedef' + elif right_type: + td.original_class = right_type + td.original_type_kind = 'typedef' + else: + td.original_type_kind = None + td.original_class = None + return + + # 非 BinOp:使用 _find_base_type + value_base_type: str | None = self._find_base_type(td.value) + _PTR_MODIFIERS: frozenset[str] = frozenset({'CPtr', 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern'}) + + if value_base_type and value_base_type not in _PTR_MODIFIERS: + cname: str | None = CTypeHelper.GetCName(value_base_type) + if cname: + td.original_class = cname + else: + td.original_class = value_base_type + td.original_type_kind = 'typedef' + return + + if isinstance(td.value, ast.Name): + ref_name: str = td.value.id + # 先查本模块已解析的 typedef + if ref_name in self._resolved_typedefs: + ref_class: str | None = self._resolved_typedefs[ref_name] + if ref_class: + td.original_class = ref_class + td.original_type_kind = 'typedef' + return + # 再查 symtab(跨模块) + if ref_name in self._symtab: + ref_info: CTypeInfo = self._symtab[ref_name] + if ref_info and ref_info.IsTypedef and ref_info.OriginalType: + td.original_class = ref_info.OriginalType + td.original_type_kind = 'typedef' + return + + td.original_type_kind = None + td.original_class = None + + def _resolve_type_expr(self, node: ast.AST) -> str: + """解析类型表达式为 C 类型名字符串,优先查本模块已解析的 typedef + + 解决同模块 typedef 间引用(如 UINTPTR = UINT | t.CPtr 中的 UINT) + 在 TypeResolver 阶段尚未插入 symtab 的问题。 + """ + if isinstance(node, ast.Name): + if node.id in self._resolved_typedefs: + return self._resolved_typedefs[node.id] or '' + return self._type_mapper.resolve_typedef_value_type(node) + + # ------------------------------------------------------------------ + # 函数签名解析 + # ------------------------------------------------------------------ + + def _resolve_function(self, func: FuncSymbolData): + """解析函数的返回类型和参数类型""" + func.ret_type = self._type_mapper.get_func_ret_type_str(func.returns_node) + # 同时解析为 CTypeInfo,保留符号性(LLVM 类型字符串 'i64' 不区分有无符号) + if func.returns_node is not None: + try: + func.ret_ctype = CTypeInfo.FromNode(func.returns_node, self._symtab) + except Exception: + func.ret_ctype = None + func.param_types = [] + _arg_name: str + annotation: ast.expr | None + for _arg_name, annotation in func.params: + if annotation: + func.param_types.append(self._type_mapper.get_func_param_type_str(annotation)) + else: + func.param_types.append('i8*') + + # ------------------------------------------------------------------ + # Define 值解析 + # ------------------------------------------------------------------ + + def _resolve_define(self, define: DefineSymbolData): + """解析 define 常量值""" + if define.value_node: + define.value = self._symtab._eval_const_expr(define.value_node) + + # ------------------------------------------------------------------ + # 静态工具方法(原 _LoadSymbolsFromFile 中的闭包) + # ------------------------------------------------------------------ + + @staticmethod + def _find_base_type(node: ast.AST | None) -> str | None: + """从注解 AST 节点中查找基础类型名称""" + if isinstance(node, ast.Attribute): + if hasattr(node.value, 'id') and IsTModule(node.value.id): + if node.attr == 'CPtr': + return 'ptr' + return node.attr + elif isinstance(node, ast.Subscript): + if isinstance(node.value, ast.Attribute) and isinstance(node.value.value, ast.Name) and IsTModule(node.value.value.id) and node.value.attr == 'Callable': + return 'Callable' + elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + left_result: str | None = TypeResolver._find_base_type(node.left) + if left_result == 'ptr': + right_result: str | None = TypeResolver._find_base_type(node.right) + return (right_result if right_result and right_result != 'ptr' else 'void') + ' *' + if left_result == 'Callable': + return 'Callable' + right_result: str | None = TypeResolver._find_base_type(node.right) + if right_result == 'ptr': + return (left_result if left_result and left_result != 'ptr' else 'void') + ' *' + if right_result == 'Callable': + return 'Callable' + # 类型拼接语义:CLong | CLong → CLongLong, CUnsignedLong | CLong → CUnsignedLongLong + long_types: frozenset[str] = frozenset({'CLong', 'CUnsignedLong'}) + if left_result in long_types and right_result in long_types: + if left_result == 'CUnsignedLong' or right_result == 'CUnsignedLong': + return 'CUnsignedLongLong' + return 'CLongLong' + if left_result: + return left_result + if right_result: + return right_result + return None + + @staticmethod + def _find_postdef_arg(node: ast.AST | None) -> str | None: + """从注解 AST 节点中查找 Postdefinition 参数""" + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Attribute): + if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id) and node.func.attr == 'Postdefinition': + if node.args and isinstance(node.args[0], ast.Name): + return node.args[0].id + elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + left_result: str | None = TypeResolver._find_postdef_arg(node.left) + if left_result: + return left_result + right_result: str | None = TypeResolver._find_postdef_arg(node.right) + if right_result: + return right_result + return None diff --git a/App/lib/core/SymbolUtils.py b/App/lib/core/SymbolUtils.py new file mode 100644 index 0000000..8aadd76 --- /dev/null +++ b/App/lib/core/SymbolUtils.py @@ -0,0 +1,214 @@ +"""共享工具函数:符号表处理中使用的 AST 分析工具""" +from __future__ import annotations +import ast +from lib.includes import t + +_T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__}) + + +def IsTModule(module_id: str, symtable: object | None = None) -> bool: + """检查模块标识符是否指向 t 类型模块(支持别名如 import t as tt)""" + if module_id in _T_MODULE_NAMES: + return True + if symtable is not None and hasattr(symtable, 'import_aliases'): + resolved: str = symtable.import_aliases.get(module_id, module_id) + if resolved in _T_MODULE_NAMES: + return True + return False + + +def IsTModulePath(module_path: str) -> bool: + """检查模块路径是否指向 t 类型模块""" + if module_path in _T_MODULE_NAMES: + return True + if module_path: + t_name: str + for t_name in _T_MODULE_NAMES: + if module_path == t_name or module_path.startswith(t_name + '.'): + return True + return False + + +def AnnotationContainsTType(node: ast.AST, TypeName: str) -> bool: + """检测 AST 节点中是否包含指定名称的 t 模块类型""" + if isinstance(node, ast.Attribute): + if (hasattr(node.value, 'id') and IsTModule(node.value.id) 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 + + +def IsListAnnotation(annotation: ast.expr) -> bool: + """检查注解是否为栈上固定数组类型(t.CArray[...] 或 list[type, count])""" + if not isinstance(annotation, ast.Subscript): + return False + value = annotation.value + # t.CArray[...] + if (isinstance(value, ast.Attribute) + and isinstance(value.value, ast.Name) + and value.value.id == 't' + and value.attr == 'CArray'): + return True + # list[type, count] — 仅匹配双参数 Tuple 切片(固定数组), + # 单参数 list[type] 不匹配(走泛型类 list[T] 路径) + if (isinstance(value, ast.Name) + and value.id == 'list' + and isinstance(annotation.slice, ast.Tuple) + and len(annotation.slice.elts) == 2): + return True + return False + + +def ExtractListFromBinOp(annotation: ast.expr) -> ast.Subscript | None: + """从 BinOp(BitOr) 注解中提取 list[...] 部分,如 list[i32] | t.CPtr""" + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + for side in (annotation.left, annotation.right): + if IsListAnnotation(side): + return side + return None + + +class ListAnnotationParseResult: + """list[...] 注解解析结果。 + + is_pointer=True 表示单参数 list[type] → 指针模式; + is_pointer=False 表示 Tuple(2) 模式 list[type, count]。 + """ + __slots__ = ('elem_type_node', 'count_node', 'is_pointer') + + def __init__(self, elem_type_node: ast.expr, count_node: ast.expr | None, is_pointer: bool) -> None: + self.elem_type_node: ast.expr = elem_type_node + self.count_node: ast.expr | None = count_node + self.is_pointer: bool = is_pointer + + +def ParseListAnnotation(annotation: ast.expr) -> ListAnnotationParseResult | None: + """解析 list[...] 注解,统一处理 list[type, count] / list[type] / BinOp(BitOr) 形式。 + + 自动处理 BinOp(BitOr) 包裹(如 list[i32, N] | t.CPtr)。 + 返回 None 表示不是 list 注解。 + """ + list_node: ast.expr = annotation + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + extracted: ast.Subscript | None = ExtractListFromBinOp(annotation) + if extracted is None: + return None + list_node = extracted + if not IsListAnnotation(list_node): + return None + slice_node: ast.AST = list_node.slice + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + return ListAnnotationParseResult(slice_node.elts[0], slice_node.elts[1], is_pointer=False) + # 单参数 list[type] → 指针模式 + return ListAnnotationParseResult(slice_node, None, is_pointer=True) + + +def CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool: + """检测注解节点中是否包含 CInline""" + if annotation_node is None: + return False + if isinstance(annotation_node, ast.Attribute): + if hasattr(annotation_node.value, 'id') and IsTModule(annotation_node.value.id) 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 CheckAnnotationHasCInline(annotation_node.left) or CheckAnnotationHasCInline(annotation_node.right) + return False + + +def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None: + """从 BinOp(BitOr) 注解左侧提取类型名(如 TypeA | TypeB → TypeA)。 + + 返回 Name.id 或 Attribute.attr,非 BinOp 返回 None。 + """ + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + left: ast.expr = annotation.left + if isinstance(left, ast.Name): + return left.id + if isinstance(left, ast.Attribute): + return left.attr + return None + + +def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set) -> str | None: + """递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。 + + 检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。 + 支持泛型特化类型(如 GSList[Param]),构造特化名后检查是否在 struct_names 中。 + """ + if isinstance(node, ast.Name) and node.id in struct_names: + return node.id + if isinstance(node, ast.Constant) and isinstance(node.value, str) and node.value in struct_names: + return node.value + if isinstance(node, ast.Attribute) and node.attr in struct_names: + return node.attr + if isinstance(node, ast.Subscript): + base_name: str | None = None + if isinstance(node.value, ast.Name): + base_name = node.value.id + elif isinstance(node.value, ast.Attribute): + base_name = node.value.attr + if base_name: + slice_node: ast.AST = node.slice + if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index): + slice_node = slice_node.value + type_args: list[str] = [] + if isinstance(slice_node, ast.Name): + type_args.append(slice_node.id) + elif isinstance(slice_node, ast.Attribute): + type_args.append(slice_node.attr) + elif isinstance(slice_node, ast.Tuple): + for elt in slice_node.elts: + if isinstance(elt, ast.Name): + type_args.append(elt.id) + elif isinstance(elt, ast.Attribute): + type_args.append(elt.attr) + if type_args: + spec_name: str = base_name + ''.join(f'[{arg}]' for arg in type_args) + if spec_name in struct_names: + return spec_name + if base_name in struct_names: + return base_name + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + result: str | None = FindStructNameInAnnotation(node.left, struct_names) + if result: + return result + return FindStructNameInAnnotation(node.right, struct_names) + return None + + +def AnnotationContainsName(annotation: ast.AST, name: str) -> bool: + """检查注解中是否包含指定名称(支持 BinOp(BitOr) 递归)。 + + 同时检查 Attribute.attr 和 Name.id,比 ast.dump 字符串匹配更精确。 + """ + if isinstance(annotation, ast.Attribute): + return annotation.attr == name + if isinstance(annotation, ast.Name): + return annotation.id == name + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + return AnnotationContainsName(annotation.left, name) or AnnotationContainsName(annotation.right, name) + return False + + +def IsCPtrNode(node: ast.AST) -> bool: + """检查节点是否为 t.CPtr 属性引用。""" + return (isinstance(node, ast.Attribute) + and isinstance(node.value, ast.Name) + and node.value.id == 't' + and node.attr == 'CPtr') diff --git a/App/lib/core/Translator/AnnotationLoader.py b/App/lib/core/Translator/AnnotationLoader.py new file mode 100644 index 0000000..3e64d43 --- /dev/null +++ b/App/lib/core/Translator/AnnotationLoader.py @@ -0,0 +1,381 @@ +from __future__ import annotations + +import ast +import os +import importlib +import types +from typing import Any + +import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes) + +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.SymbolUtils import IsTModule, AnnotationContainsName +from lib.includes import t +from lib.includes.t import CType, CEnum +from lib.constants.config import mode as _config_mode +from lib.core.VLogger import get_logger as _vlog + + +class AnnotationLoaderMixin: + """注解模块加载 Mixin + + 提供注解模块/文件的加载与符号表注册能力。 + """ + + def _LoadAnnotationModule(self, ModuleName: str, library_name: str | None = None, parse_method: str = 'auto') -> int: + """加载注解模块并将 CType 子类注册为 typedef + + 支持两种模式: + 1. AST 模式:如果 ModuleName 是文件路径,解析文件提取 CType + 2. importlib 模式:如果 ModuleName 是模块名,导入模块提取 CType + + Args: + ModuleName: 模块名或文件路径 + library_name: 手动指定的库名称,用于注册到符号表。如果为 None,则从 ModuleName 推断 + parse_method: 解析方法,可选值: + - 'auto': 自动检测(默认) + - 'file': 强制使用 AST 模式(文件路径) + - 'module': 强制使用 importlib 模式(模块名) + """ + def register_to_symboltable(module: types.ModuleType, lib_name: str | None = None, source_file: str | None = None) -> int: + """将模块中的 CType 子类和 CEnum 类注册到符号表""" + count: int = 0 + for AttrName in dir(module): + attr: type = getattr(module, AttrName) + # 优先检查是否是 CEnum 子类(枚举) + if isinstance(attr, type) and issubclass(attr, CEnum) and attr is not CEnum: + # 注册枚举类型 + EnumNode: CTypeInfo = 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.insert(AttrName, EnumNode) + if lib_name: + FullName: str = f'{lib_name}.{AttrName}' + full_EnumNode: CTypeInfo = 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.insert(FullName, full_EnumNode) + # 枚举成员也需要注册 + for MemberName in dir(attr): + if not MemberName.startswith('_'): + member: int = getattr(attr, MemberName, None) + if isinstance(member, int): + MemberNode: CTypeInfo = 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.insert(MemberName, MemberNode) + self.SymbolTable.insert(f"{AttrName}.{MemberName}", MemberNode) + self.SymbolTable.insert(f"{AttrName}_{MemberName}", MemberNode) + if lib_name: + self.SymbolTable.insert(f"{lib_name}.{AttrName}.{MemberName}", MemberNode) + self.SymbolTable.insert(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: str = f'{lib_name}.{AttrName}' + else: + FullName = AttrName + # 同时注册带前缀和不带前缀的名称(都是 typedef 别名) + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = AttrName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = attr.__name__ + TypedefNode.set('source', 'annotation_module') + TypedefNode.library_name = lib_name + TypedefNode.file = source_file + self.SymbolTable.insert(AttrName, TypedefNode) + if lib_name and FullName != AttrName: + FullTypedef_node: CTypeInfo = CTypeInfo() + FullTypedef_node.Name = FullName + FullTypedef_node.IsTypedef = True + FullTypedef_node.OriginalType = attr.__name__ + FullTypedef_node.set('source', 'annotation_module') + FullTypedef_node.library_name = lib_name + FullTypedef_node.file = source_file + self.SymbolTable.insert(FullName, FullTypedef_node) + count += 1 + # 检查是否是下划线前缀的 CType 子类(如 _CTypedef -> CTypedef) + elif AttrName.startswith('_') and len(AttrName) > 1: + PublicName: str = AttrName[1:] + PublicAttr: type | None = 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 not self.SymbolTable.has(PublicName): + if lib_name: + FullName: str = f'{lib_name}.{PublicName}' + else: + FullName = PublicName + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = PublicName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = attr.__name__ + TypedefNode.set('source', 'annotation_module') + TypedefNode.library_name = lib_name + TypedefNode.file = source_file + self.SymbolTable.insert(PublicName, TypedefNode) + if lib_name and FullName != PublicName: + FullTypedef_node: CTypeInfo = CTypeInfo() + FullTypedef_node.Name = FullName + FullTypedef_node.IsTypedef = True + FullTypedef_node.OriginalType = attr.__name__ + FullTypedef_node.set('source', 'annotation_module') + FullTypedef_node.library_name = lib_name + FullTypedef_node.file = source_file + self.SymbolTable.insert(FullName, FullTypedef_node) + count += 1 + return count + + # 确定解析方法 + use_file_mode: bool = 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: str | None = 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: dict = self.EmbeddedAssignments.copy() + saved_typedef: dict = 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: str = lib_name + count: int = 0 + + # 检查符号表中新增的类型(注解文件解析后的所有 struct 类型) + # 需要检查这些类型是否继承自 CType,如果是则是 typedef 别名 + new_types: list[str] = [] + 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: str = f.read() + ann_tree: ast.Module = ast.parse(ann_content) + for node in ann_tree.body: + if isinstance(node, ast.ClassDef): + ClassName: str = node.name + # 检查是否有基类 + for base in node.bases: + if isinstance(base, ast.Name) and base.id == 'CType': + # 这个类继承自 CType,是 typedef 别名 + # 更新符号表中的类型为 typedef + if self.SymbolTable.has(ClassName): + TypedefNode: CTypeInfo = 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.insert(ClassName, TypedefNode) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析注解文件类型定义失败: {_e}", "Exception") + + # 为所有新增的类型添加库前缀和 source 标记 + for TypeName in new_types: + # 添加带前缀的类型名 + FullName: str = f'{TypePrefix}.{TypeName}' + TypeInfo: CTypeInfo | dict[str, Any] = self.SymbolTable[TypeName].copy() + TypeInfo['source'] = 'annotation_module' + TypeInfo['original_name'] = TypeName + self.SymbolTable.insert(FullName, TypeInfo) + count += 1 + + # 再次遍历 AST,查找注解文件中的 typedef 语句(AnnAssign with t.CTypedef) + # 注意:必须在类定义处理完之后再处理 typedef + typedef_annots: list[tuple[str, str]] = [] + # 查找 CEnum 成员变量(AnnAssign with t.CEnum | t.State 或 t.CEnum) + cEnumMembers: list[tuple[str, int | None]] = [] + try: + with open(ModuleName, 'r', encoding='utf-8') as f: + ann_content: str = f.read() + ann_tree: ast.Module = 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: str = node.target.id + # 检查注解是否是 BinOp (Union) 或直接是 Attribute + IsCenum: bool = False + if isinstance(node.annotation, ast.BinOp): + # 检查是否包含 CEnum + if AnnotationContainsName(node.annotation, 'CEnum'): + 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: int | str | float | None = node.value.value + else: + value = None + cEnumMembers.append((TargetName, value)) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析 CEnum 成员变量失败: {_e}", "Exception") + + # 处理 CEnum 成员变量 + for TargetName, value in cEnumMembers: + # 注册枚举成员 + MemberNode: CTypeInfo = CTypeInfo() + MemberNode.Name = TargetName + MemberNode.BaseType = t.CEnum(TypePrefix) + MemberNode.value = value + MemberNode.EnumName = TypePrefix + MemberNode.library_name = TypePrefix + MemberNode.IsEnumMember = True + self.SymbolTable.insert(TargetName, MemberNode) + self.SymbolTable.insert(f"{TypePrefix}.{TargetName}", MemberNode) + self.SymbolTable.insert(f"{TypePrefix}_{TargetName}", MemberNode) + + try: + with open(ModuleName, 'r', encoding='utf-8') as f: + ann_content: str = f.read() + ann_tree: ast.Module = 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: str = node.target.id + # 检查注解是否是 t.CTypedef + if isinstance(node.annotation, ast.Attribute): + if isinstance(node.annotation.value, ast.Name): + if IsTModule(node.annotation.value.id) and node.annotation.attr == 'CTypedef': + # 检查值是否是 Name + if isinstance(node.value, ast.Name): + original_name: str = node.value.id + typedef_annots.append((TargetName, original_name)) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析 typedef 语句失败: {_e}", "Exception") + + # 处理 typedef 注解 + for TargetName, original_name in typedef_annots: + # 检查原始类型是否在符号表中(带前缀或不带前缀) + orig_with_prefix: str = f'{TypePrefix}.{original_name}' + if self.SymbolTable.has(orig_with_prefix): + # 添加带前缀的 typedef 别名 + FullTypedef_name: str = f'{TypePrefix}.{TargetName}' + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = FullTypedef_name + TypedefNode.IsTypedef = True + orig_entry: CTypeInfo | dict[str, Any] = self.SymbolTable.lookup(orig_with_prefix) + orig_type_str: 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.insert(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: types.ModuleType = importlib.import_module(ModuleName) + count: int = 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[str | tuple]) -> None: + """加载多个注解文件并注册到符号表 + + Args: + FilePaths: 文件路径列表,每个元素可以是: + - str: 文件路径或模块名 + - tuple: (path, options) 或 (path, library_name, parse_method) + """ + for item in FilePaths: + if isinstance(item, tuple): + if len(item) >= 3: + path: str + library_name: str + parse_method: str + path, library_name, parse_method = item[:3] + count: int = self._LoadAnnotationModule(path, library_name, parse_method) + elif len(item) == 2: + path: str + library_name: str + path, library_name = item + count: int = self._LoadAnnotationModule(path, library_name, 'auto') + else: + count: int = self._LoadAnnotationModule(item) + else: + count: int = self._LoadAnnotationModule(item) + + # 别名,保持向后兼容 + LoadAnnotationFiles = _LoadAnnotationFiles diff --git a/App/lib/core/Translator/BaseTranslator.py b/App/lib/core/Translator/BaseTranslator.py new file mode 100644 index 0000000..03f8537 --- /dev/null +++ b/App/lib/core/Translator/BaseTranslator.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +import ast +from typing import Any, Callable, Dict, TYPE_CHECKING + +from lib.core.Exportable import Exportable + +if TYPE_CHECKING: + import llvmlite.ir as ir + +from lib.core.Handles import ( + IfHandle, ForHandle, WhileHandle, + AnnAssignHandle, + HandlesTypeMerge, ExprHandle, BodyHandle, + ReturnHandle, AssignHandle, AugAssignHandle, DeleteHandle, + WithHandle, TryHandle, AssertHandle, RaiseHandle, MatchHandle, + ExprUtils, ExprOpsHandle, ExprAttrHandle, ExprBuiltinHandle, + ExprAsmHandle, ExprFormatHandle, ExprLambdaHandle, ExprCallHandle, + CSpecialCallHandle, TSpecialCallHandle, + ClassHandle, + FunctionHandle, + ImportHandle +) +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.SymbolTable import SymbolTable +from lib.core.SymbolNode import SymbolNode +from lib.constants.config import mode as _ConfigMode +from lib.core.VLogger import get_logger as _vlog + + +def _StrictLog(logger: Callable[[str], None], msg: str) -> None: + """在 strict 模式下记录异常日志""" + if _ConfigMode == "strict": + logger(msg) + + +class BaseTranslatorMixin: + """代码转换器基础 Mixin + + 提供 __init__、日志、调试输出与表达式处理等基础能力。 + """ + + def __init__(self) -> None: + self.VarScopes: list[Dict[str, SymbolNode]] = [{}] # 跟踪变量作用域,第一个是全局作用域 + self.FunctionReturnTypes: Dict[str, CTypeInfo] = {} # 记录函数名和其返回类型 + self.CurrentCReturnTypes: list[CTypeInfo] | None = None # 当前函数的 CReturn 类型列表 + self._CurrentCpythonObjectClass: str | None = None # 当前正在处理的 CPython 对象类名 + self.FunctionDefCache: dict = {} # 函数定义缓存 + self.OriginalLines: list[str] = [] # 原始代码行 + self.Content: str = '' + self.DebugFile: str | None = None # 调试输出文件路径 + self.IsHeader: bool = False # 是否生成头文件(仅处理定义、宏、导入等,忽略函数体) + self.AnnotationModules: set[str] = set() # 注解模块集合,用于识别类型定义模块 + self.Warnings: list[str] = [] # 警告日志 + self._ErrorStack: list[tuple[str, str, str]] = [] # 错误栈,按顺序收集错误位置 + self.Errors: list[dict[str, str | None]] = [] # 错误日志 + self.SliceCount: int = 0 # 切片计数 + self.SliceInfos: list = [] # 切片信息列表 + self.SliceLevel: int = 3 # 切片优化级别 + self.GeneratedTypes: set[str] = set() # 在当前代码中生成的类型(struct/typedef) + self.EmbeddedAssignments: dict[str, list] = {} # {ClassName: [var1, var2, ...]} 嵌入的实例化变量 + self.TypedefAssignments: dict[str, list] = {} # {ClassName: [(var1, IsPtr), ...]} 嵌入的 typedef/指针变量 + self.ChainAssignmentArrays: list = [] # 链式赋值数组形式 [(ClassName, VarName, ArraySize_node), ...] + self.tree: ast.AST | None = None # 保存解析后的 AST 树 + self.LibraryPaths: list[str] = ['.'] # 库搜索路径列表,默认包含当前目录 + self.InClass: bool = False # 是否正在处理 class 定义 + self._UserTypeModules: dict = {} + self._source_module_sig_files: dict = {} + self._global_function_default_args: dict = {} + self._global_function_param_names: dict = {} + self.exception_registry: dict = {} + self.exception_parents: dict = {} + self._next_exception_code: int = 100 + # 以下属性原先在外部懒注入,此处显式初始化以避免 hasattr/getattr 动态反射 + self.config: Any = None + self._ImportedModules: set[str] | None = None + self._t_c_imported_names: dict = {} + + # 导出表 + self.Exportable = Exportable(filename='') + + self.SymbolTable = SymbolTable(self) + self.IfHandler = IfHandle(self) + self.ForHandler = ForHandle(self) + self.WhileHandler = WhileHandle(self) + self.AnnAssignHandler = AnnAssignHandle(self) + self.TypeMergeHandler = HandlesTypeMerge(self) + self.ExprHandler = ExprHandle(self) + self.ExprUtils = ExprUtils(self) + self.ExprOpsHandle = ExprOpsHandle(self) + self.ExprAttrHandle = ExprAttrHandle(self) + self.ExprBuiltinHandle = ExprBuiltinHandle(self) + self.ExprAsmHandle = ExprAsmHandle(self) + self.ExprFormatHandle = ExprFormatHandle(self) + self.ExprLambdaHandle = ExprLambdaHandle(self) + self.ExprCallHandle = ExprCallHandle(self) + self.CSpecialCallHandle = CSpecialCallHandle(self) + self.TSpecialCallHandle = TSpecialCallHandle(self) + self.ClassHandler = ClassHandle(self) + self.FunctionHandler = FunctionHandle(self) + self.ImportHandler = ImportHandle(self) + self.BodyHandler = BodyHandle(self) + self.ReturnHandler = ReturnHandle(self) + self.AssignHandler = AssignHandle(self) + self.AugAssignHandler = AugAssignHandle(self) + self.DeleteHandler = DeleteHandle(self) + self.WithHandler = WithHandle(self) + self.TryHandler = TryHandle(self) + self.AssertHandler = AssertHandle(self) + self.RaiseHandler = RaiseHandle(self) + self.MatchHandler = MatchHandle(self) + + def LogWarning(self, message: str, LineNum: int | None = None) -> None: + """记录警告 + + Args: + message: 警告消息 + LineNum: 行号(可选) + """ + warning = f"Warning: {message}" + if LineNum is not None: + warning += f" (line {LineNum})" + self.Warnings.append(warning) + + def LogError(self, message: str, LineNum: int | None = None) -> None: + """记录错误 + + Args: + message: 错误消息 + LineNum: 行号 + """ + _vlog().error(message) + entry: dict[str, str | int | None] = {'message': message, 'line': LineNum} + self.Errors.append(entry) + + # 检查 config 中是否有 debug 文件 + DebugFile: str | None = None + if hasattr(self, 'config') and self.config: + DebugFile = getattr(self.config, 'debug', None) + + if DebugFile: + with open(DebugFile, 'a', encoding='utf-8') as f: + LineInfo: str = f' line {LineNum}' if LineNum else '' + f.write(f'[ERROR]{LineInfo}: {message}\n') + + def SetDebugFile(self, FilePath: str) -> None: + """设置调试输出文件""" + self.DebugFile = FilePath + + def DebugPrint(self, *args: Any, **kwargs: Any) -> None: + """输出调试信息到文件""" + if self.DebugFile: + with open(self.DebugFile, 'a', encoding='utf-8') as f: + print(*args, file=f, **kwargs) + + def HandleExpr(self, Node: ast.AST, UseSingleQuote: bool = False, VarType: ir.Type | None = None) -> Any: + return self.ExprHandler.HandleExpr(Node, UseSingleQuote, VarType) diff --git a/App/lib/core/Translator/ConstEval.py b/App/lib/core/Translator/ConstEval.py new file mode 100644 index 0000000..8785365 --- /dev/null +++ b/App/lib/core/Translator/ConstEval.py @@ -0,0 +1,305 @@ +from __future__ import annotations + +import ast +from typing import TYPE_CHECKING +import llvmlite.ir as ir + +from lib.core.ConstEvaluator import ConstEvaluator + +if TYPE_CHECKING: + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator + + +class ConstEvalMixin: + """常量表达式求值 Mixin + + 提供数组初始化常量构建与常量表达式求值能力。 + """ + + @staticmethod + def _bswap_constant_value(val: int, width: int) -> int: + """编译期字节序交换""" + if width == 16: + return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF) + elif width == 32: + return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF) + elif width == 64: + return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF) + return val + + def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None: + _zv: ir.Constant | None = Gen._zero_value_for_type(ElemType) + _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined) + if not isinstance(value_node, (ast.List, ast.Set)): + return None + if isinstance(ElemType, ir.ArrayType): + constants: list[ir.Constant] = [] + for elt in value_node.elts: + if isinstance(elt, (ast.List, ast.Set)): + inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen) + if inner_consts: + try: + constants.append(ir.Constant(ElemType, inner_consts)) + except Exception: # fallback to _fallback on constant construction failure + constants.append(_fallback) + else: + constants.append(_fallback) + else: + constants.append(_fallback) + while len(constants) < ArrayCount: + constants.append(_fallback) + return constants[:ArrayCount] + StructName: str | None = None + if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: + StructName = elem_type_node.id + constants = [] + for elt in value_node.elts: + if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName: + const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + elif isinstance(elt, ast.Constant): + # 字符串常量:根据引号类型决定 i8 或 i8* + if isinstance(elt.value, str): + const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + else: + const = self._BuildScalarConstantUnified(elt, ElemType, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + elif isinstance(elt, ast.UnaryOp): + const = self._BuildScalarConstantUnified(elt, ElemType, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr': + if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int): + try: + constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF)) + except Exception: # fallback to _fallback on constant construction failure + constants.append(_fallback) + else: + constants.append(_fallback) + elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord': + if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1: + try: + constants.append(ir.Constant(ElemType, ord(elt.args[0].value))) + except Exception: # fallback to _fallback on constant construction failure + constants.append(_fallback) + else: + constants.append(_fallback) + else: + const_val: int | float | str | None = self._try_eval_const_call(elt, Gen) + if const_val is not None: + try: + constants.append(ir.Constant(ElemType, const_val)) + except Exception: # fallback to _fallback on constant construction failure + constants.append(_fallback) + else: + constants.append(_fallback) + while len(constants) < ArrayCount: + constants.append(_fallback) + if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64): + swapped: list[ir.Constant] = [] + for c in constants: + if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int): + swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width) + swapped.append(ir.Constant(ElemType, swapped_val)) + else: + swapped.append(c) + constants = swapped + return constants[:ArrayCount] + + def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None: + """根据引号类型决定字符串常量的类型: + + - 单引号单字符 → i8 (char 值) + - 双引号/单引号多字符/三重引号 → i8* (C 字符串指针) + - 空字符串 → 0 (None),传入 ptr 目标自动隐式转换 + """ + if not isinstance(node.value, str): + return None + s: str = node.value + # 空字符串 → 0 (None) + if len(s) == 0: + try: + return ir.Constant(target_type, 0) + except Exception: + return None + # 获取源代码以判断引号类型 + quote_char: str | None = None + is_triple: bool = False + col_offset: int = getattr(node, 'col_offset', 0) + node_lineno: int = getattr(node, 'lineno', 0) + # AST 的 lineno 可能不准确(常量节点可能指向下一行), + # 因此在 lineno 和 lineno-1 两行中查找引号 + for try_lineno in (node_lineno, node_lineno - 1): + source_line: str | None = Gen._get_source_line(try_lineno) + if source_line and col_offset < len(source_line): + ch: str = source_line[col_offset] + if ch in ("'", '"'): + quote_char = ch + if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'): + is_triple = True + break + # 判断是否为 char (i8):单引号、非三重、单字符 + is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1) + if is_char: + # 单引号单字符 → i8 + if isinstance(target_type, ir.IntType): + try: + return ir.Constant(target_type, ord(s)) + except Exception: + return None + # 目标类型不是整数 → 类型不匹配 + return None + else: + # 双引号/多字符/三重 → i8* (C 字符串) + if isinstance(target_type, ir.PointerType): + str_val: str = s + '\x00' + str_bytes: bytes = str_val.encode('utf-8') + arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes)) + gv_name: str = f"str_const_{id(node)}" + gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name) + gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) + gv.linkage = 'internal' + return ir.Constant(target_type, gv.get_reference()) + # 目标类型不是指针 → 类型不匹配 + return None + + def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None: + """统一的标量常量构建方法。 + + 合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性: + - 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False) + - 字符串处理委托给 _BuildStringLiteralConstant + - 显式拒绝 BaseStructType + - 全部 try/except 保护 + """ + # 显式拒绝结构体类型 + if isinstance(target_type, ir.BaseStructType): + return None + # 处理常量节点 + if isinstance(node, ast.Constant): + # bool 必须在 int 之前判断(bool 是 int 的子类) + if isinstance(node.value, bool): + try: + return ir.Constant(target_type, 1 if node.value else 0) + except Exception: + return None + elif isinstance(node.value, int): + try: + return ir.Constant(target_type, node.value) + except Exception: + return None + elif isinstance(node.value, float): + try: + return ir.Constant(target_type, node.value) + except Exception: + return None + elif isinstance(node.value, str): + # 字符串处理委托给 _BuildStringLiteralConstant + const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen) + if const is not None: + return const + # 回退:直接创建字符串全局变量(i8*) + try: + return Gen._create_string_global(node.value) + except Exception: + return None + # 处理一元负号 + elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub): + if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int): + try: + return ir.Constant(target_type, -node.operand.value) + except Exception: + return None + # 处理 True/False 名称节点 + elif isinstance(node, ast.Name): + if node.id == 'True': + try: + return ir.Constant(target_type, 1) + except Exception: + return None + elif node.id == 'False': + try: + if isinstance(target_type, ir.PointerType): + return ir.Constant(target_type, None) + return ir.Constant(target_type, 0) + except Exception: + return None + return None + + def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None: + if isinstance(node, ast.Call): + func_name: str | None = None + if isinstance(node.func, ast.Name): + func_name = node.func.id + elif isinstance(node.func, ast.Attribute): + func_name = node.func.attr + if func_name: + arg_vals: list[int | float | str | None] = [] + for arg in node.args: + v: int | float | str | None = self._try_eval_const_expr(arg, Gen) + if v is None: + return None + arg_vals.append(v) + if func_name == 'COLOR_RGB' and len(arg_vals) == 3: + r: int | float | str | None + g: int | float | str | None + b: int | float | str | None + r, g, b = arg_vals + return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r) + if func_name in Gen._DefineConstants: + return Gen._DefineConstants[func_name] + for key in Gen.functions: + if key.endswith(f'__{func_name}'): + return None + return None + return self._try_eval_const_expr(node, Gen) + + def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None: + return ConstEvaluator.eval_with_symtab(node, self.SymbolTable) + + def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None: + inner_type: ir.Type = ArrayType.element + count: int = ArrayType.count + elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else [] + _zv: ir.Constant | None = Gen._zero_value_for_type(inner_type) + _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined) + constants: list[ir.Constant] = [] + for elt in elts: + if isinstance(inner_type, ir.ArrayType): + if isinstance(elt, (ast.List, ast.Set)): + inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen) + if inner_consts: + constants.append(ir.Constant(inner_type, inner_consts)) + else: + constants.append(_fallback) + else: + constants.append(_fallback) + elif isinstance(elt, ast.Constant): + const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + elif isinstance(elt, ast.UnaryOp): + const = self._BuildScalarConstantUnified(elt, inner_type, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + else: + constants.append(_fallback) + while len(constants) < count: + constants.append(_fallback) + return constants[:count] diff --git a/App/lib/core/Translator/LlvmGenerator.py b/App/lib/core/Translator/LlvmGenerator.py new file mode 100644 index 0000000..4ae3d90 --- /dev/null +++ b/App/lib/core/Translator/LlvmGenerator.py @@ -0,0 +1,642 @@ +from __future__ import annotations + +import ast +import re +import llvmlite.ir as ir + +from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo, CTypeHelper +from lib.includes import t +from lib.constants.config import mode as _config_mode +from lib.core.VLogger import get_logger as _vlog +from lib.core.DecoratorPass import run as _run_decorator_pass +from lib.core.SymbolUtils import IsTModule, AnnotationContainsName + + +class LlvmGeneratorMixin: + """LLVM IR 生成 Mixin + + 提供 LLVM IR 直接生成、模块级 LLVM IR 处理与 C 代码生成入口能力。 + """ + + def GenerateLlvmDirect(self, Tree: ast.Module) -> str: + Gen: LlvmCodeGenerator = self.LlvmGen + + # === 新增:轻量 AST 遍历,收集类图信息,解决前向引用 === + Gen._class_graph: dict[str, dict[str, bool | list[str]]] = {} + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.ClassDef): + bases: list[str] = [] + for base in Node.bases: + if hasattr(base, 'id'): + bases.append(base.id) + elif hasattr(base, 'attr'): + bases.append(base.attr) + has_methods: bool = any(isinstance(item, ast.FunctionDef) for item in Node.body) + # 检测 @t.NoVTable 装饰器(非多态继承基类标记) + has_novtable: bool = False + if hasattr(Node, 'decorator_list') and Node.decorator_list: + for decorator in Node.decorator_list: + if isinstance(decorator, ast.Attribute): + if getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'NoVTable': + has_novtable = True + elif isinstance(decorator, ast.Name): + if decorator.id == 'NoVTable': + has_novtable = True + Gen._class_graph[Node.name] = { + 'bases': bases, + 'has_methods': has_methods, + 'has_novtable': has_novtable, + } + # === 结束新增 === + + # 预扫描:为所有 -> str 注解的函数注册正确的返回类型 i8* + # 这样在类方法中调用这些函数时,前向声明会使用正确的类型 + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.FunctionDef): + if Node.returns and isinstance(Node.returns, ast.Name) and Node.returns.id == 'str': + Gen.known_return_types[Node.name] = ir.PointerType(ir.IntType(8)) + + # 首先收集所有 CDefine 常量,确保类定义中可以引用 + def _extract_call_const_val(node: ast.AST) -> int | str | float | None: + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Attribute): + if getattr(node.func.value, 'id', None) == 't': + if node.args and isinstance(node.args[0], ast.Constant): + return node.args[0].value + elif isinstance(node.func, ast.Name): + if node.args and isinstance(node.args[0], ast.Constant): + return node.args[0].value + return None + + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.AnnAssign) and isinstance(Node.target, ast.Name) and Node.value: + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Node.annotation) + if TypeInfo and TypeInfo.BaseType and ((isinstance(TypeInfo.BaseType, type) and issubclass(TypeInfo.BaseType, t.CDefine)) or isinstance(TypeInfo.BaseType, t.CDefine)): + val: int | str | float | None = None + if isinstance(Node.value, ast.Constant): + val = Node.value.value + else: + val = _extract_call_const_val(Node.value) + if val is not None: + if not hasattr(Gen, '_define_constants'): + Gen._define_constants: dict[str, int | str | float | bool] = {} + Gen._define_constants[Node.target.id] = val + existing = self.SymbolTable.lookup(Node.target.id) + if not (existing and existing.IsTypedef and existing.OriginalType): + sym_info: _CTypeInfo = _CTypeInfo() + sym_info.IsDefine = True + sym_info.DefineValue = val + self.SymbolTable.insert(Node.target.id, sym_info) + elif isinstance(Node, ast.Assign): + for target in Node.targets: + if isinstance(target, ast.Name): + TypeInfo: CTypeInfo | None = None + if Node.type_comment: + try: + tc_ast: ast.Expression = ast.parse(Node.type_comment, mode='eval') + TypeInfo = self.TypeMergeHandler.GetCTypeInfo(tc_ast.body) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型注释失败: {_e}", "Exception") + if TypeInfo and TypeInfo.BaseType and ((isinstance(TypeInfo.BaseType, type) and issubclass(TypeInfo.BaseType, t.CDefine)) or isinstance(TypeInfo.BaseType, t.CDefine)): + val: int | str | float | None = None + if isinstance(Node.value, ast.Constant): + val = Node.value.value + else: + val = _extract_call_const_val(Node.value) + if val is not None: + if not hasattr(Gen, '_define_constants'): + Gen._define_constants: dict[str, int | str | float | bool] = {} + Gen._define_constants[target.id] = val + sym_info: _CTypeInfo = _CTypeInfo() + sym_info.IsDefine = True + sym_info.DefineValue = val + self.SymbolTable.insert(target.id, sym_info) + + # 预扫描所有顶层函数定义,填充 FunctionDefCache(不创建 LLVM 声明) + # 这样类方法编译时遇到未声明的函数可以按需前向声明 + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.FunctionDef): + FuncName: str = Node.name + if FuncName not in self.FunctionDefCache: + self.FunctionDefCache[FuncName] = Node + + # 首先处理所有导入语句,确保模块在类型解析前被加载 + for Node in ast.iter_child_nodes(Tree): + Gen._set_node_info(Node) + try: + if isinstance(Node, ast.Import): + self.ImportHandler._EmitImportDeclarationsLlvm(Node, Gen) + elif isinstance(Node, ast.ImportFrom): + self.ImportHandler._EmitImportFromDeclarationsLlvm(Node, Gen) + except Exception as e: + lineno: int = getattr(Node, 'lineno', 0) + source_line: str | None = Gen._get_source_line(lineno) + src_file: str = getattr(Gen, '_current_source_file', '') + src_path: str = src_file if src_file else 'unknown' + line_info: str = "源代码 (%s, line %d)" % (src_path, lineno) + if source_line: + line_info += ":\n %d | %s" % (lineno, source_line) + self._ErrorStack.append((type(e).__name__, str(e), line_info)) + + # 前向声明所有顶层函数,确保全局变量初始化时能引用函数指针 + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.FunctionDef): + Gen._set_node_info(Node) + self.FunctionHandler._EmitFunctionForwardDeclLlvm(Node, Gen) + + # 预创建全局变量(带初始化器),确保类方法编译时能引用全局数组等 + Gen._current_tree: ast.Module = Tree + for Node in ast.iter_child_nodes(Tree): + Gen._set_node_info(Node) + if isinstance(Node, ast.AnnAssign): + self.AssignHandler._EmitGlobalAnnAssignLlvm(Node, Gen) + elif isinstance(Node, ast.Assign): + self.AssignHandler._EmitGlobalAssignLlvm(Node, Gen) + + # 首先处理所有类定义(包括 CUnion),确保类型定义在使用前完成 + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.ClassDef): + Gen._set_node_info(Node) + self.ClassHandler._EmitClassLlvm(Node, Gen) + + Gen._generate_structs() + Gen._create_Vtable_globals() + + for Node in ast.iter_child_nodes(Tree): + Gen._set_node_info(Node) + try: + if isinstance(Node, ast.Import): + continue + elif isinstance(Node, ast.ImportFrom): + continue + elif isinstance(Node, ast.ClassDef): + continue + elif isinstance(Node, ast.FunctionDef): + self.FunctionHandler._EmitFunctionLlvm(Node, Gen) + elif isinstance(Node, ast.AnnAssign): + self.AssignHandler._EmitGlobalAnnAssignLlvm(Node, Gen) + elif isinstance(Node, ast.Assign): + self.AssignHandler._EmitGlobalAssignLlvm(Node, Gen) + elif isinstance(Node, ast.If): + pass + elif isinstance(Node, ast.Expr): + self._HandleModuleLevelExprLlvm(Node, Gen) + except Exception as e: + lineno: int = getattr(Node, 'lineno', 0) + source_line: str | None = Gen._get_source_line(lineno) + src_file: str = getattr(Gen, '_current_source_file', '') + src_path: str = src_file if src_file else 'unknown' + line_info: str = "源代码 (%s, line %d)" % (src_path, lineno) + if source_line: + line_info += ":\n %d | %s" % (lineno, source_line) + self._ErrorStack.append((type(e).__name__, str(e), line_info)) + raise + Gen._set_Vtable_initializers() + # 执行 DecoratorPass:为带自定义装饰器的函数生成 wrapper + _run_decorator_pass(Gen) + ir_code: str = Gen.finalize() + return ir_code + + def _HandleModuleLevelExprLlvm(self, Node: ast.Expr, Gen: LlvmCodeGenerator) -> None: + if not isinstance(Node.value, ast.Call): + return + call: ast.Call = Node.value + if not isinstance(call.func, ast.Attribute): + return + if not isinstance(call.func.value, ast.Name) or call.func.value.id != 'c': + return + func_name: str = call.func.attr + if func_name == 'LLVMIR': + self._HandleModuleLevelLLVMIR(call, Gen) + + def _HandleModuleLevelLLVMIR(self, Node: ast.Call, Gen: LlvmCodeGenerator) -> None: + if not Node.args: + return + first_arg: ast.expr = Node.args[0] + if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str): + ir_text: str = first_arg.value + elif isinstance(first_arg, ast.JoinedStr): + parts: list[str] = [] + for value in first_arg.values: + if isinstance(value, ast.Constant) and isinstance(value.value, str): + parts.append(value.value) + elif isinstance(value, ast.FormattedValue): + parts.append('') + ir_text = ''.join(parts) + else: + return + self._InsertModuleLevelLLVMIR(Gen, ir_text) + + def _InsertModuleLevelLLVMIR(self, Gen: LlvmCodeGenerator, ir_text: str) -> None: + for line in ir_text.strip().split('\n'): + line = line.strip() + if not line: + continue + module_flags_match: re.Match[str] | None = re.match(r'!llvm\.module\.flags\s*=\s*!\{(!\d+)\}', line) + if module_flags_match: + ref: str = module_flags_match.group(1) + if not hasattr(Gen, '_pending_module_flags'): + Gen._pending_module_flags: list = [] + Gen._pending_module_flags_refs: str = ref + continue + metadata_match: re.Match[str] | None = re.match(r'(!\d+)\s*=\s*!\{(.+)\}', line) + if metadata_match: + md_id: str = metadata_match.group(1) + md_body: str = metadata_match.group(2) + fields: list[ir.Constant | ir.MetaDataString | str] = [] + for field_str in re.split(r',\s*', md_body): + field_str = field_str.strip() + if not field_str: + continue + int_m: re.Match[str] | None = re.match(r'^i32\s+(\d+)$', field_str) + if int_m: + fields.append(ir.Constant(ir.IntType(32), int(int_m.group(1)))) + continue + str_m: re.Match[str] | None = re.match(r'^"([^"]*)"$', field_str) + if str_m: + fields.append(ir.MetaDataString(Gen.module, str_m.group(1))) + continue + mdstr_m: re.Match[str] | None = re.match(r'^!"([^"]*)"$', field_str) + if mdstr_m: + fields.append(ir.MetaDataString(Gen.module, mdstr_m.group(1))) + continue + int_m2: re.Match[str] | None = re.match(r'^(\d+)$', field_str) + if int_m2: + fields.append(ir.Constant(ir.IntType(32), int(int_m2.group(1)))) + continue + ref_m: re.Match[str] | None = re.match(r'^(!\d+)$', field_str) + if ref_m: + fields.append(ref_m.group(1)) + continue + if fields: + if not hasattr(Gen, '_pending_metadata'): + Gen._pending_metadata: list[tuple[str, list]] = [] + Gen._pending_metadata.append((md_id, fields)) + continue + self._FlushModuleMetadata(Gen) + + def _FlushModuleMetadata(self, Gen: LlvmCodeGenerator) -> None: + if not hasattr(Gen, '_pending_metadata') or not Gen._pending_metadata: + return + md_map: dict[str, ir.MetaDataString] = {} + for md_id, fields in Gen._pending_metadata: + resolved: list[ir.Constant | ir.MetaDataString] = [] + for f in fields: + if isinstance(f, str) and f.startswith('!'): + ref: ir.MetaDataString | None = md_map.get(f) + if ref: + resolved.append(ref) + else: + resolved.append(f) + md_value: ir.MetaDataString = Gen.module.add_metadata(resolved) + md_map[md_id] = md_value + if hasattr(Gen, '_pending_module_flags_refs'): + ref: str = Gen._pending_module_flags_refs + md_node: ir.MetaDataString | None = md_map.get(ref) + if md_node: + Gen.module.add_named_metadata('llvm.module.flags', md_node) + Gen._pending_metadata = [] + Gen._pending_module_flags_refs = None + + def GenerateCCode(self, Tree: ast.Module, target: str = 'llvm') -> str: + """生成代码 + + Args: + Tree: Python AST 树 + target: 目标代码类型,仅支持 'llvm' + + Returns: + 生成的代码字符串 + """ + self._generated_vars: set[str] = set() + self.VarScopes = [{}] # 全局作用域 + + # 预扫描 import 语句,注册别名,确保类型解析时能正确解析模块别名 + if not getattr(self, '_ImportedModules', None): + self._ImportedModules: set[str] = set() + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.Import): + for alias in Node.names: + name: str = alias.name + if name in ('c', 't'): + continue + self._ImportedModules.add(name) + if alias.asname: + self.SymbolTable.import_aliases[alias.asname] = name + elif isinstance(Node, ast.ImportFrom): + module_name: str | None = Node.module + if module_name and module_name not in ('c', 't'): + self._ImportedModules.add(module_name) + for alias in Node.names: + if alias.asname: + self.SymbolTable.import_aliases[alias.asname] = f"{module_name}.{alias.name}" + + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.ClassDef): + ClassName: str = Node.name + + # 检查是否是特殊类型(枚举或联合体) + IsSpecialType: bool = False + if Node.bases: + for base in Node.bases: + if hasattr(base, 'attr'): + if base.attr in ['CEnum', 'Enum', 'CUnion', 'REnum']: + IsSpecialType = True + break + elif hasattr(base, 'id'): + if base.id in ['CEnum', 'Enum', 'CUnion', 'REnum']: + IsSpecialType = True + break + + # 如果是特殊类型,跳过这里的处理(由 HandleClassDef 处理) + if IsSpecialType: + continue + + members: dict[str, CTypeInfo] = {} + IsTypedef: bool = False + TypedefName: str | None = None + + if hasattr(Node, 'annotation') and Node.annotation: + try: + if AnnotationContainsName(Node.annotation, 'CTypedef'): + IsTypedef = True + if isinstance(Node.annotation, ast.Call): + if Node.annotation.args: + if isinstance(Node.annotation.args[0], ast.Constant): + TypedefName = Node.annotation.args[0].value + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析 typedef 注解失败: {_e}", "Exception") + + if not IsTypedef: + for item in Node.body: + if isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name) and target.id == '__annotations__': + if isinstance(item.value, ast.Dict): + for key, value in zip(item.value.keys, item.value.values): + if isinstance(key, ast.Constant) and key.value == '__type__': + if AnnotationContainsName(value, 'CTypedef'): + IsTypedef = True + + if IsTypedef: + TypedefKey: str = TypedefName if TypedefName else ClassName + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = TypedefKey + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'struct {ClassName}' + TypedefNode.set('IsComplete', True) + TypedefNode.file = '' + self.SymbolTable.insert(TypedefKey, TypedefNode) + + for item in Node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + VarName: str = item.target.id + try: + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(item.annotation) + if TypeInfo is None: + TypeInfo = CTypeInfo() + TypeInfo.BaseType = t.CInt() + IsPtr: bool = TypeInfo.IsPtr + if not IsPtr: + if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): + if AnnotationContainsName(item.annotation, 'CPtr'): + IsPtr = True + dims: list[int] = [] + if hasattr(item.annotation, 'slice') and isinstance(item.annotation.slice, ast.Constant): + if isinstance(item.annotation.value, ast.Name): + if IsTModule(item.annotation.value.id, self.SymbolTable): + if hasattr(item.annotation, 'slice'): + try: + dims.append(int(item.annotation.slice.value)) + except Exception as _e: + if _config_mode == "strict": + self.LogWarning(f"异常被忽略: {_e}") + MemberInfo: CTypeInfo = TypeInfo.Copy() + if dims: + MemberInfo.ArrayDims = [str(d) for d in dims] + members[VarName] = MemberInfo + except Exception as _e: + if _config_mode == "strict": + self.LogWarning(f"异常被忽略: {_e}") + + if not IsTypedef: + StructNode: CTypeInfo = CTypeInfo() + StructNode.Name = ClassName + StructNode.IsStruct = True + StructNode.Members = members or {} + StructNode.file = '' + existing: CTypeInfo | None = self.SymbolTable.lookup(ClassName) + if existing: + if hasattr(existing, 'IsCpythonObject') and existing.IsCpythonObject: + StructNode.IsCpythonObject = True + self.SymbolTable.insert(ClassName, StructNode) + elif isinstance(Node, ast.FunctionDef): + FuncName: str = Node.name + if not self.SymbolTable.has(FuncName): + FuncNode: CTypeInfo = CTypeInfo() + FuncNode.Name = FuncName + FuncNode.IsFunction = True + FuncNode.file = '' + self.SymbolTable.insert(FuncName, FuncNode) + elif isinstance(Node, ast.AnnAssign): + if isinstance(Node.target, ast.Name): + VarName: str = Node.target.id + + VarType: str = 'unknown' + IsPtr: bool = False + IsTypedef_assignment: bool = False + + if Node.annotation: + try: + if AnnotationContainsName(Node.annotation, 'CTypedef'): + IsTypedef_assignment = True + except Exception as _e: + if _config_mode == "strict": + self.LogWarning(f"异常被忽略: {_e}") + + if IsTypedef_assignment and Node.value: + if isinstance(Node.value, ast.Name): + OriginalType: str = Node.value.id + if OriginalType in self.GeneratedTypes: + OriginalInfo: CTypeInfo | None = self.SymbolTable.lookup(OriginalType) + if OriginalInfo: + if isinstance(OriginalInfo, dict): + actual_type: str = OriginalInfo.get('type', 'struct') + elif isinstance(OriginalInfo, CTypeInfo): + if OriginalInfo.IsEnum: + actual_type = 'enum' + elif OriginalInfo.IsTypedef: + actual_type = 'typedef' + elif OriginalInfo.IsStruct: + actual_type = 'struct' + else: + actual_type = 'struct' + else: + actual_type = 'struct' + if actual_type == 'enum': + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'enum {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + elif actual_type == 'typedef': + TypedefNode = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + if isinstance(OriginalInfo, dict): + TypedefNode.OriginalType = OriginalInfo.get('OriginalType', f'struct {OriginalType}') + elif isinstance(OriginalInfo, CTypeInfo) and OriginalInfo.OriginalType: + TypedefNode.OriginalType = OriginalInfo.OriginalType + else: + TypedefNode.OriginalType = f'struct {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + else: + TypedefNode = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'struct {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + continue + else: + OriginalInfo: CTypeInfo | None = self.SymbolTable.lookup(OriginalType) + if OriginalInfo: + if isinstance(OriginalInfo, dict): + actual_type: str = OriginalInfo.get('type', 'struct') + elif isinstance(OriginalInfo, CTypeInfo): + if OriginalInfo.IsEnum: + actual_type = 'enum' + elif OriginalInfo.IsTypedef: + actual_type = 'typedef' + elif OriginalInfo.IsStruct: + actual_type = 'struct' + else: + actual_type = 'struct' + else: + actual_type = 'struct' + if actual_type == 'typedef': + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + if isinstance(OriginalInfo, dict): + TypedefNode.OriginalType = OriginalInfo.get('OriginalType', f'struct {OriginalType}') + elif isinstance(OriginalInfo, CTypeInfo) and OriginalInfo.OriginalType: + TypedefNode.OriginalType = OriginalInfo.OriginalType + else: + TypedefNode.OriginalType = f'struct {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + if isinstance(OriginalInfo, dict): + OriginalInfo['skip_generation'] = True + continue + elif actual_type == 'struct': + TypedefNode = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'struct {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + if isinstance(OriginalInfo, dict): + OriginalInfo['skip_generation'] = True + continue + elif actual_type == 'enum': + TypedefNode = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'enum {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + if isinstance(OriginalInfo, dict): + OriginalInfo['skip_generation'] = True + continue + elif isinstance(Node.value, ast.Attribute): + CName: str | None = CTypeHelper.GetCName(Node.value.attr) + if CName: + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = CName + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + continue + + if Node.annotation: + try: + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Node.annotation) + IsPtr = TypeInfo.IsPtr if TypeInfo else False + except Exception as _e: + if _config_mode == "strict": + self.LogWarning(f"异常被忽略: {_e}") + # 如果符号已经存在且是 typedef,不要覆盖它 + existing = self.SymbolTable.lookup(VarName) + if existing and existing.IsTypedef and existing.OriginalType: + # typedef 已正确插入,保持现有条目不覆盖 + pass + else: + var_node: CTypeInfo = CTypeInfo() + var_node.Name = VarName + var_node.IsVariable = True + var_node.PtrCount = 1 if IsPtr else 0 + var_node.file = '' + self.SymbolTable.insert(VarName, var_node) + + self.LlvmGen: LlvmCodeGenerator = LlvmCodeGenerator( + triple=getattr(self, 'triple', None), + datalayout=getattr(self, 'datalayout', None), + ) + self.LlvmGen._Trans = self + self.LlvmGen._import_handler_ref = self.ImportHandler + if hasattr(self, '_module_sha1'): + self.LlvmGen.module_sha1 = self._module_sha1 + if hasattr(self, '_ModuleSha1Map'): + self.LlvmGen.ModuleSha1Map = self._ModuleSha1Map + if hasattr(self, '_struct_sha1_map'): + self.LlvmGen._struct_sha1_map = self._struct_sha1_map + if hasattr(self, '_temp_dir'): + self.LlvmGen._temp_dir = self._temp_dir + if hasattr(self, '_export_extern_funcs'): + self.LlvmGen._export_extern_funcs = self._export_extern_funcs + self.LlvmGen.setup_from_symbol_table(self.SymbolTable) + if hasattr(self, 'CurrentFile') and self.CurrentFile: + self.LlvmGen._set_source_info(self.CurrentFile) + + # --- 修复:共享 vtable 状态跨模块 --- + # 根因:LlvmGen 每模块重新创建,class_vtable/class_methods 等是实例级属性, + # 不跨模块共享。导致模块 B 编译时 _specialize_generic_class 触发的 + # pool.alloc(48) 找不到 MemManager 的虚表状态,直接调用 stub 返回 NULL → 段错误。 + # 修复:将 vtable 状态存储在 Translator 对象上并赋值给每个新 Gen。 + if not hasattr(self, '_shared_class_vtable'): + self._shared_class_vtable: set[str] = set() + self._shared_cross_module_vtable: set[str] = set() + self._shared_class_methods: dict[str, list[str]] = {} + self._shared_cross_module_novtable: set[str] = set() + # 合并 setup_from_symbol_table 创建的实例级 class_methods 到共享 dict + # (仅添加新类,不覆盖已有方法列表) + for _cls_name, _methods in self.LlvmGen.class_methods.items(): + if _cls_name not in self._shared_class_methods: + self._shared_class_methods[_cls_name] = _methods + else: + for _m in _methods: + if _m not in self._shared_class_methods[_cls_name]: + self._shared_class_methods[_cls_name].append(_m) + # 替换为共享状态 + self.LlvmGen.class_vtable = self._shared_class_vtable + self.LlvmGen._cross_module_vtable_classes = self._shared_cross_module_vtable + self.LlvmGen.class_methods = self._shared_class_methods + self.LlvmGen._cross_module_novtable = self._shared_cross_module_novtable + # --- 修复结束 --- + + return self.GenerateLlvmDirect(Tree) diff --git a/App/lib/core/Translator/PythonParser.py b/App/lib/core/Translator/PythonParser.py new file mode 100644 index 0000000..9bcabcc --- /dev/null +++ b/App/lib/core/Translator/PythonParser.py @@ -0,0 +1,452 @@ +from __future__ import annotations + +import ast +import json +import struct + +from lib.utils.helpers import DetectFileType +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.SymbolUtils import IsTModule, AnnotationContainsName +from lib.includes import t +from lib.constants.config import mode as _ConfigMode +from lib.core.VLogger import get_logger as _vlog + + +class PythonParserMixin: + """Python 文件解析 Mixin + + 提供辅助文件解析、symbin 文件加载与 Python 源文件解析能力。 + """ + + def ParseHelperFiles(self, HelperFiles: list[str], encoding: str = 'utf-8') -> None: + """解析辅助文件,提取符号信息 + + 支持 .py, .c, .h 和 .symbin 文件 + """ + for FilePath in HelperFiles: + ext: str = DetectFileType(FilePath) + if ext == '.py': + self.ParsePythonFile(FilePath, encoding, UpdateCurrentFile=False) + elif ext == '.c': + pass # .c 文件解析已移除,不再使用旧的正则解析路径 + elif FilePath.endswith('.symbin'): + self.LoadSymbinFile(FilePath) + + def LoadSymbinFile(self, FilePath: str) -> None: + """从.symbin文件加载符号表 + + Args: + FilePath: .symbin文件路径 + """ + try: + with open(FilePath, 'rb') as f: + data: bytes = f.read() + + # 导入序列化函数(避免循环导入) + # 解析二进制格式 + if len(data) < 4: + _vlog().warning(f"无效的 symbin 文件 (太短): {FilePath}") + return + + # 解析4字节长度头(小端序) + length: int = struct.unpack(' None: + """解析Python文件,提取类、函数、变量信息 + + Args: + FilePath: 要解析的文件路径 + encoding: 文件编码 + UpdateCurrentFile: 是否更新 CurrentFile(默认为 True,处理导入时设为 False) + """ + try: + with open(FilePath, 'r', encoding=encoding) as f: + content: str = f.read() + + # 保存当前处理的文件路径(仅在主文件处理时更新) + if UpdateCurrentFile: + self.CurrentFile = FilePath + + # 解析Python代码为AST + tree: ast.Module = ast.parse(content) + + # 保存 AST 树供后续使用 + self.tree = tree + + # 第一遍:检测特殊赋值语句,收集跟在 class 后面的 + # 遍历 tree.body,找到所有 ClassDef,然后收集紧随其后的赋值语句 + i: int = 0 + while i < len(tree.body): + node: ast.stmt = tree.body[i] + + # 检测 ClassDef + if isinstance(node, ast.ClassDef): + ClassName: str = node.name + # 使用类名_行号作为唯一 key + ClassKey: str = f"{ClassName}_{node.lineno}" + + # 检查后面的语句,收集特殊赋值 + j: int = i + 1 + while j < len(tree.body): + NextNode: ast.stmt = tree.body[j] + + if isinstance(NextNode, ast.Assign): + # 检测 xxx = xxx 或 xxx = yyy + if len(NextNode.targets) == 1 and isinstance(NextNode.targets[0], ast.Name): + TargetName: str = NextNode.targets[0].id + if isinstance(NextNode.value, ast.Name): + ValueName: str = NextNode.value.id + if ValueName == ClassName: + # 这是 class 的实例化 + if ClassKey not in self.EmbeddedAssignments: + self.EmbeddedAssignments[ClassKey] = [] + self.EmbeddedAssignments[ClassKey].append(TargetName) + j += 1 + continue + elif isinstance(NextNode, ast.AnnAssign): + # 检测 xxx: t.Postdefinition(yyy) 或 xxx: t.CTypedef = xxx 或 xxx: t.CPtr = xxx + if isinstance(NextNode.target, ast.Name): + TargetName: str = NextNode.target.id + # 先处理新格式:t.Postdefinition(...) + annotation: ast.expr | None = NextNode.annotation + PostdefNode: ast.Call | None = None + + # 递归搜索整个注解树,找到 t.Postdefinition(...) 调用 + def find_PostdefNode(node: ast.AST) -> ast.Call | None: + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Attribute): + if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id, self.SymbolTable) and node.func.attr == 'Postdefinition': + return node + elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + LeftResult: ast.Call | None = find_PostdefNode(node.left) + if LeftResult: + return LeftResult + RightResult: ast.Call | None = find_PostdefNode(node.right) + if RightResult: + return RightResult + return None + + PostdefNode = find_PostdefNode(annotation) + # 检查是否找到 t.Postdefinition(...) 调用 + if PostdefNode: + if (isinstance(PostdefNode.func, ast.Attribute) and + hasattr(PostdefNode.func, 'value') and + hasattr(PostdefNode.func.value, 'id') and + IsTModule(PostdefNode.func.value.id, self.SymbolTable) and + PostdefNode.func.attr == 'Postdefinition' and + PostdefNode.args): + # 检查第一个参数是否是当前类名 + arg: ast.expr = PostdefNode.args[0] + if isinstance(arg, ast.Name) and arg.id == ClassName: + # 检查是否包含 CTypedef 或 CPtr + HasCtypedef: bool = AnnotationContainsName(NextNode.annotation, 'CTypedef') + IsPtr: bool = AnnotationContainsName(NextNode.annotation, 'CPtr') + + # 解析维度列表(第二个参数) + dimensions: list[ast.expr] = [] + if len(PostdefNode.args) > 1: + # 第二个参数应该是一个列表 + dim_arg: ast.expr = PostdefNode.args[1] + if isinstance(dim_arg, ast.List): + for DimExpr in dim_arg.elts: + dimensions.append(DimExpr) + + # 如果没有从 Postdefinition 参数中获取维度,尝试从类型注解中的 Subscript 获取 + # 例如:memory_block_t[MAX_ORDER + 1] | t.CPtr + if not dimensions: + def extract_subscript_dims(node: ast.AST) -> list[ast.expr]: + """递归提取 Subscript 节点的维度""" + dims: list[ast.expr] = [] + if isinstance(node, ast.Subscript): + # 提取当前维度 + if isinstance(node.slice, ast.Constant): + dims.append(ast.Constant(value=node.slice.value)) + elif isinstance(node.slice, ast.Name): + dims.append(ast.Name(id=node.slice.id, ctx=ast.Load())) + elif isinstance(node.slice, ast.BinOp): + dims.append(node.slice) + # 递归提取更外层的维度 + dims.extend(extract_subscript_dims(node.value)) + elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + # 处理 Type | t.CPtr 的情况,检查左侧 + dims.extend(extract_subscript_dims(node.left)) + return dims + + # 从注解中提取维度 + subscript_dims: list[ast.expr] = extract_subscript_dims(annotation) + if subscript_dims: + # 反转维度顺序(从内到外) + dimensions = list(reversed(subscript_dims)) + + # 获取赋值值 + value: ast.expr | None = None + if hasattr(NextNode, 'value') and NextNode.value is not None: + value = NextNode.value + + # 添加到对应的 assignments + # 对于 t.Postdefinition(...),只有包含 CTypedef 或 CPtr 时才视为 typedef 类型 + if HasCtypedef or IsPtr: + if ClassKey not in self.TypedefAssignments: + self.TypedefAssignments[ClassKey] = [] + # 如果有维度信息,使用字典存储 + if dimensions: + self.TypedefAssignments[ClassKey].append({ + 'name': TargetName, + 'IsPtr': IsPtr, + 'dimensions': dimensions, + 'value': value + }) + else: + self.TypedefAssignments[ClassKey].append((TargetName, IsPtr)) + else: + if ClassKey not in self.EmbeddedAssignments: + self.EmbeddedAssignments[ClassKey] = [] + # 如果有维度信息,使用字典存储 + if dimensions: + self.EmbeddedAssignments[ClassKey].append({ + 'name': TargetName, + 'dimensions': dimensions, + 'value': value + }) + else: + self.EmbeddedAssignments[ClassKey].append(TargetName) + + # 检查是否有 __set_default__ 初始化 + if AnnotationContainsName(NextNode.annotation, '__set_default__'): + self._handle_set_default_in_parse(ClassKey, TargetName, ClassName, NextNode) + j += 1 + continue + # 再处理旧格式:xxx: t.CTypedef = xxx 或 xxx: t.CPtr = xxx + # 注意:这种格式的 typedef 不应该被合并到结构体定义中, + # 而应该生成独立的 typedef 语句,所以不添加到 TypedefAssignments + elif NextNode.annotation and isinstance(NextNode.annotation, ast.Attribute): + if isinstance(NextNode.annotation.value, ast.Name): + annot_id: str = NextNode.annotation.value.id + annot_attr: str = NextNode.annotation.attr + if annot_id == 't' and NextNode.value: + if isinstance(NextNode.value, ast.Name): + ValueName: str = NextNode.value.id + if ValueName == ClassName: + # 检查是 CTypedef 还是 CPtr + if annot_attr == t.CPtr.__name__: + # t.CPtr 表示指针变量,加入 TypedefAssignments,设置 IsPtr=True + if ClassKey not in self.TypedefAssignments: + self.TypedefAssignments[ClassKey] = [] + self.TypedefAssignments[ClassKey].append((TargetName, True)) + j += 1 + continue + # 注意:t.CTypedef 不在这里处理,由 HandlesAnnAssign 生成独立的 typedef 语句 + # 如果不是特殊赋值,停止收集 + break + + i = j + continue + + i += 1 + + # 提取类定义(作为结构体) + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + ClassName: str = node.name + # 先把类名加入 GeneratedTypes,这样 GetTypeName 可以正确识别 + self.GeneratedTypes.add(ClassName) + + # 检测是否是 CEnum + IsCenum: bool = False + for base in node.bases: + if isinstance(base, ast.Attribute): + if base.attr in ('CEnum', 'REnum'): + IsCenum = True + break + elif isinstance(base, ast.Name): + if base.id in ('CEnum', 'REnum'): + IsCenum = True + break + + # 检测是否有 @t.Object 装饰器或继承自 t.Object + IsCpythonObject: bool = 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 + IsTModule(decorator.value.id, self.SymbolTable) 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 + IsTModule(decorator.func.value.id, self.SymbolTable) and + decorator.func.attr == 'Object'): + IsCpythonObject = True + break + elif isinstance(decorator.func, ast.Name): + if decorator.func.id == 'Object': + IsCpythonObject = True + break + elif isinstance(decorator, ast.Name): + if decorator.id == 'Object': + IsCpythonObject = True + break + if not IsCpythonObject and hasattr(node, 'bases') and node.bases: + for base in node.bases: + if isinstance(base, ast.Attribute): + if (hasattr(base.value, 'id') and + IsTModule(base.value.id, self.SymbolTable) and + base.attr == 'Object'): + IsCpythonObject = True + break + elif isinstance(base, ast.Name): + if base.id == 'Object': + IsCpythonObject = True + break + + if IsCenum: + # 注册为 enum 类型 + EnumNode: CTypeInfo = CTypeInfo() + EnumNode.Name = ClassName + EnumNode.IsEnum = True + EnumNode.set('file', '') + self.SymbolTable.insert(ClassName, EnumNode) + + # 注册 enum 成员(提取枚举值,与 SymbolExtractor 逻辑一致) + next_enum_value: int = 0 + for item in node.body: + MemberName: str | None = None + ItemValue: object | None = None + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + MemberName = item.target.id + ItemValue = item.value + elif isinstance(item, ast.Assign) and len(item.targets) == 1 and isinstance(item.targets[0], ast.Name): + MemberName = item.targets[0].id + ItemValue = item.value + if MemberName is None: + continue + MemberNode: CTypeInfo = CTypeInfo() + MemberNode.Name = MemberName + MemberNode.BaseType = t.CEnum(ClassName) + # 提取枚举值:优先用显式赋值,否则自动编号 + if ItemValue is not None and isinstance(ItemValue, ast.Constant) and isinstance(ItemValue.value, int): + MemberNode.value = ItemValue.value + next_enum_value = ItemValue.value + 1 + else: + MemberNode.value = next_enum_value + next_enum_value += 1 + MemberNode.EnumName = ClassName + MemberNode.library_name = None + MemberNode.IsEnumMember = True + self.SymbolTable.insert(MemberName, MemberNode) + self.SymbolTable.insert(f"{ClassName}.{MemberName}", MemberNode) + self.SymbolTable.insert(f"{ClassName}_{MemberName}", MemberNode) + + continue + + # 检测是否是 Anonymous + IsAnonymous: bool = False + + # 提取结构体成员信息 + members: dict[str, CTypeInfo] = {} + for item in node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + VarName: str = item.target.id + try: + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(item.annotation) + if TypeInfo is None: + TypeInfo = CTypeInfo() + TypeInfo.BaseType = t.CInt() + IsPtr: bool = TypeInfo.IsPtr + if not IsPtr: + if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): + if AnnotationContainsName(item.annotation, 'CPtr'): + IsPtr = True + dims: list[int] = [] + if hasattr(item.annotation, 'slice') and isinstance(item.annotation.slice, ast.Constant): + if isinstance(item.annotation.value, ast.Name): + if IsTModule(item.annotation.value.id, self.SymbolTable): + if hasattr(item.annotation, 'slice'): + try: + dims.append(int(item.annotation.slice.value)) + except Exception as _e: + if _ConfigMode == "strict": + raise + _vlog().warning(f"解析数组维度失败: {_e}", "Exception") + MemberInfo: CTypeInfo = TypeInfo.Copy() + if dims: + MemberInfo.ArrayDims = [str(d) for d in dims] + members[VarName] = MemberInfo + except Exception as _e: + if _ConfigMode == "strict": + raise + _vlog().warning(f"解析结构体成员失败: {_e}", "Exception") + StructNode: CTypeInfo = CTypeInfo() + StructNode.Name = ClassName + StructNode.IsStruct = True + StructNode.Members = members or {} + StructNode.set('file', '') + StructNode.set('IsAnonymous', IsAnonymous) + StructNode.set('IsCpythonObject', IsCpythonObject) + self.SymbolTable.insert(ClassName, StructNode) + elif isinstance(node, ast.FunctionDef): + FuncName: str = node.name + if not self.SymbolTable.has(FuncName): + FuncNode: CTypeInfo = CTypeInfo() + FuncNode.Name = FuncName + FuncNode.IsFunction = True + FuncNode.set('file', '') + self.SymbolTable.insert(FuncName, FuncNode) + elif isinstance(node, ast.AnnAssign): + if isinstance(node.target, ast.Name): + VarName: str = node.target.id + # 检查是否是 t.CTypedef 格式,如果是则跳过(由 HandleClassDef 处理) + is_ctypedef: bool = False + if node.annotation: + if isinstance(node.annotation, ast.Attribute): + if isinstance(node.annotation.value, ast.Name): + if IsTModule(node.annotation.value.id, self.SymbolTable) and node.annotation.attr == 'CTypedef': + is_ctypedef = True + elif isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.op, ast.BitOr): + def _has_ctype(ann: ast.AST) -> bool: + if isinstance(ann, ast.Attribute): + return hasattr(ann.value, 'id') and IsTModule(ann.value.id) and ann.attr == 'CTypedef' + if isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr): + return _has_ctype(ann.left) or _has_ctype(ann.right) + if isinstance(ann, ast.Subscript): + return _has_ctype(ann.value) + return False + if _has_ctype(node.annotation): + is_ctypedef = True + if not is_ctypedef: + # 如果符号已经存在且是 typedef,不要覆盖它 + existing = self.SymbolTable.lookup(VarName) + if existing and existing.IsTypedef and existing.OriginalType: + pass + else: + IsPtr: bool = False + if node.annotation: + try: + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(node.annotation) + IsPtr = TypeInfo.IsPtr if TypeInfo else False + except Exception as _e: + if _ConfigMode == "strict": + raise + _vlog().warning(f"获取类型信息失败(ParsePythonFile): {_e}", "Exception") + var_node: CTypeInfo = CTypeInfo() + var_node.Name = VarName + var_node.IsVariable = True + var_node.set('IsPtr', IsPtr) + var_node.set('file', '') + self.SymbolTable.insert(VarName, var_node) + except Exception as e: + _vlog().warning(f'解析 Python 文件失败 {FilePath}: {e}') + + # ParseCFile 已移除 - .h/.c 文件的旧正则解析路径不再使用 + # 类型解析现在直接通过 CTypeInfo 对象进行,不经过中间字符串格式 diff --git a/App/lib/core/Translator/__init__.py b/App/lib/core/Translator/__init__.py new file mode 100644 index 0000000..84114dc --- /dev/null +++ b/App/lib/core/Translator/__init__.py @@ -0,0 +1,11 @@ +"""Translator 包 - 代码转换器模块""" +from lib.core.Translator.BaseTranslator import BaseTranslatorMixin, _StrictLog +from lib.core.Translator.AnnotationLoader import AnnotationLoaderMixin +from lib.core.Translator.PythonParser import PythonParserMixin +from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin +from lib.core.Translator.ConstEval import ConstEvalMixin + +__all__ = [ + 'BaseTranslatorMixin', 'AnnotationLoaderMixin', 'PythonParserMixin', + 'LlvmGeneratorMixin', 'ConstEvalMixin', '_StrictLog' +] diff --git a/App/lib/core/TypeAnnotationResolver.py b/App/lib/core/TypeAnnotationResolver.py new file mode 100644 index 0000000..9ebcad1 --- /dev/null +++ b/App/lib/core/TypeAnnotationResolver.py @@ -0,0 +1,736 @@ +from __future__ import annotations +import ast +import warnings +from typing import TYPE_CHECKING + +from lib.core.Handles.HandlesBase import CTypeInfo, BuiltinTypeMap, CTypeHelper +from lib.core.ConstEvaluator import ConstEvaluator +from lib.core.SymbolUtils import IsTModule, IsTModulePath, IsListAnnotation +from lib.includes import t +from lib.constants.config import mode as _config_mode + +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + + +class TypeAnnotationResolver: + """从 AST 节点解析类型注解,独立于 CTypeInfo 数据模型。""" + + @staticmethod + def from_type_name(TypeName: str) -> CTypeInfo: + """从简单类型名构造 CTypeInfo(不经过 FromStr 字符串解析)""" + # Handle pointer types like 'CUInt32T *', 'void *', etc. + ptr_count: int = 0 + base_name: str = TypeName + while base_name.endswith(' *') or base_name.endswith('*'): + ptr_count += 1 + base_name = base_name.rstrip(' *').rstrip() + + entry: tuple[type, int] | None = BuiltinTypeMap.Get(base_name) + if entry: + TypeClass: type + base_ptr: int + TypeClass, base_ptr = entry + info: CTypeInfo = CTypeInfo() + info.BaseType = TypeClass + info.PtrCount = base_ptr + ptr_count + return info + # Handle LLVM primitive type names (e.g., 'i64', 'double' from generic type inference) + llvm_map: dict = CTypeInfo._get_llvm_primitive_map() + llvm_entry: tuple[type, int] | None = llvm_map.get(base_name) + if llvm_entry: + TypeClass: type + base_ptr: int + TypeClass, base_ptr = llvm_entry + info: CTypeInfo = CTypeInfo() + info.BaseType = TypeClass + info.PtrCount = base_ptr + ptr_count + return info + # Fallback with pointer count + info: CTypeInfo = CTypeInfo.CreateFromTypeName(TypeName) + if ptr_count > 0: + info.PtrCount = ptr_count + return info + + @staticmethod + def try_eval_const_expr(node: ast.AST, SymbolTable: SymbolTable) -> int | float | str | None: + return ConstEvaluator.eval_with_symtab(node, SymbolTable) + + @staticmethod + def from_node(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """从 AST 节点解析 CTypeInfo + + Args: + Node: AST 节点(如 ast.Name, ast.Attribute 等) + SymbolTable: 符号表字典 + """ + if isinstance(Node, ast.Constant): + return TypeAnnotationResolver._from_node_constant(Node, SymbolTable) + if isinstance(Node, ast.Name): + return TypeAnnotationResolver._from_node_name(Node, SymbolTable) + if isinstance(Node, ast.Call): + return TypeAnnotationResolver._from_node_call(Node, SymbolTable) + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + return TypeAnnotationResolver._from_node_binop(Node, SymbolTable) + if isinstance(Node, ast.Subscript): + return TypeAnnotationResolver._from_node_subscript(Node, SymbolTable) + if isinstance(Node, ast.Attribute): + return TypeAnnotationResolver._from_node_attribute(Node, SymbolTable) + return CTypeInfo() + + @staticmethod + def _from_node_constant(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Constant 节点""" + TypeName: str = Node.value + return TypeAnnotationResolver.from_type_name(TypeName) + + @staticmethod + def _from_node_name(Node: ast.Name, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Name 节点""" + TypeName: str = Node.id + TypeObj: type | None = CTypeHelper.GetTModuleCType(TypeName) + if TypeObj is None: + TypeObj = getattr(t, TypeName, None) + if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType) and TypeObj is not t.CType: + if TypeObj == t.CPtr: + Result: CTypeInfo = CTypeInfo() + Result.PtrCount = 1 + Result.BaseType = t.CVoid() + return Result + if TypeObj == t.State: + Result: CTypeInfo = CTypeInfo() + Result.IsState = True + Result.BaseType = t.CVoid() + return Result + Inst: t.CType = TypeObj() + Result: CTypeInfo = CTypeInfo() + Result.Name = type(Inst).__name__ + # 存储类修饰符(CExtern, CExport, CInline, CThreadLocal 等): + # position 含 STORAGE_CLASS 且不含 BASE → 纯修饰符,无实际类型 + if t.CType.STORAGE_CLASS in TypeObj.position and t.CType.BASE not in TypeObj.position: + Result.BaseType = t.CVoid() + Result.Storage = Inst + return Result + # 类型限定符(CConst, CVolatile 等) + if t.CType.TYPE_QUALIFIER in TypeObj.position and t.CType.BASE not in TypeObj.position: + Result.BaseType = t.CVoid() + if TypeObj == t.CConst: + Result.DataConst = True + elif TypeObj == t.CVolatile: + Result.DataVolatile = True + return Result + Result.BaseType = TypeObj + if hasattr(Inst, 'IsSigned'): + Result.IsSigned = Inst.IsSigned + return Result + # 检查 BuiltinTypeMap(覆盖 VOIDPTR, BYTEPTR 等指针别名) + builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(TypeName) + if builtin_entry: + TypeClass: type + base_ptr: int + TypeClass, base_ptr = builtin_entry + Result: CTypeInfo = CTypeInfo() + Result.BaseType = TypeClass + Result.PtrCount = base_ptr + return Result + if TypeName in SymbolTable: + Entry: CTypeInfo | dict = SymbolTable[TypeName] + if Entry: + if isinstance(Entry, CTypeInfo): + if Entry.IsTypedef: + if Entry.BaseType and (not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0): + Result = Entry.Copy() + Result.IsTypedef = True + Result.Name = TypeName + return Result + if Entry.OriginalType: + if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + elif isinstance(Entry.OriginalType, CTypeInfo): + Resolved = Entry.OriginalType.Copy() + elif isinstance(Entry.OriginalType, str) and Entry.OriginalType == 'Callable': + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + elif isinstance(Entry.OriginalType, str): + Resolved = TypeAnnotationResolver.from_type_name(Entry.OriginalType) + else: + Resolved = Entry.OriginalType + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + TypeEntry = BuiltinTypeMap.Get(TypeName) + if TypeEntry: + Result = CTypeInfo() + Result.BaseType = TypeEntry[0]() + Result.PtrCount = TypeEntry[1] + Result.IsTypedef = True + Result.Name = TypeName + return Result + return Entry + if Entry.IsEnum: + Result = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsEnum = True + Result.Name = TypeName + return Result + if Entry.IsExceptionClass: + Result = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsExceptionClass = True + Result.Name = TypeName + return Result + if Entry.BaseType is None and (Entry.IsStruct or Entry.Name): + Entry.BaseType = t.CStruct(name=TypeName) + Entry.IsStruct = True + return Entry + elif isinstance(Entry, dict): + if Entry.get('type') == 'typedef': + OriginalType = Entry.get('OriginalType', '') + if OriginalType: + if OriginalType == 'Callable': + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + Resolved = TypeAnnotationResolver.from_type_name(OriginalType) + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + TypeEntry = BuiltinTypeMap.Get(TypeName) + if TypeEntry: + Result = CTypeInfo() + Result.BaseType = TypeEntry[0]() + Result.PtrCount = TypeEntry[1] + Result.IsTypedef = True + Result.Name = TypeName + return Result + Result = CTypeInfo() + Result.BaseType = t._CTypedef(TypeName) + Result.IsTypedef = True + Result.Name = TypeName + return Result + # 检查 t 类型别名(from t import CInt as myint) + if hasattr(SymbolTable, '_t_type_symbols'): + t_cls = SymbolTable._t_type_symbols.get(TypeName) + if t_cls is not None: + Result = CTypeInfo() + Result.BaseType = t_cls + return Result + # 特殊处理 str 类型,它应该是 char*(指针) + if TypeName in ('str', 'bytes'): + Result = CTypeInfo() + Result.BaseType = t.CChar() + Result.PtrCount = 1 + return Result + return TypeAnnotationResolver.from_type_name(TypeName) + + @staticmethod + def _from_node_call(Node: ast.Call, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Call 节点""" + if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': + Result: CTypeInfo = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = [] + try: + if len(Node.args) > 0: + RetInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.args[0], SymbolTable) + if RetInfo: + Result.FuncPtrReturn = RetInfo + kw: ast.keyword + for kw in Node.keywords: + ParamInfo: CTypeInfo = TypeAnnotationResolver.from_node(kw.value, SymbolTable) + if ParamInfo: + Result.FuncPtrParams.append((kw.arg or '', ParamInfo)) + except Exception as _e: + if _config_mode == "strict": + warnings.warn(f"异常被忽略: {_e}") + return Result + if isinstance(Node.func, ast.Attribute): + if isinstance(Node.func.value, ast.Name) and IsTModule(Node.func.value.id, SymbolTable): + if Node.func.attr == 'Bit' and len(Node.args) > 0: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsBitField = True + if isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, int): + Result.BitWidth = Node.args[0].value + else: + Result.BitWidth = 1 + return Result + return CTypeInfo() + + @staticmethod + def _from_node_binop(Node: ast.BinOp, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.BinOp (BitOr) 节点""" + LeftInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.left, SymbolTable) + RightInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.right, SymbolTable) + + if LeftInfo.IsState or RightInfo.IsState: + Result: CTypeInfo = CTypeInfo() + Result.IsState = True + NonStateInfo: CTypeInfo = LeftInfo if not LeftInfo.IsState else RightInfo + StateInfo: CTypeInfo = LeftInfo if LeftInfo.IsState else RightInfo + if NonStateInfo.BaseType and (not isinstance(NonStateInfo.BaseType, (t._CTypedef,)) or NonStateInfo.PtrCount > 0): + Result.BaseType = NonStateInfo.BaseType + elif NonStateInfo.IsTypedef and NonStateInfo.Name: + if NonStateInfo.BaseType and (not isinstance(NonStateInfo.BaseType, (t._CTypedef,)) or NonStateInfo.PtrCount > 0): + Result.BaseType = NonStateInfo.BaseType + Result.PtrCount = NonStateInfo.PtrCount + Result.IsTypedef = True + Result.Name = NonStateInfo.Name + if NonStateInfo.OriginalType: + Result.OriginalType = NonStateInfo.OriginalType + else: + Result.BaseType = t.CStruct(name=NonStateInfo.Name) + Result.IsStruct = True + Result.IsTypedef = True + Result.Name = NonStateInfo.Name + if NonStateInfo.OriginalType: + Result.OriginalType = NonStateInfo.OriginalType + else: + Result.BaseType = t.CVoid() + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if not Result.Storage: + Result.Storage = t.CExport() + if LeftInfo.Storage and not isinstance(LeftInfo.Storage, t.CExport): + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage and not isinstance(RightInfo.Storage, t.CExport): + Result.Storage = RightInfo.Storage + if LeftInfo.DataConst or RightInfo.DataConst: + Result.DataConst = True + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + return Result + + if LeftInfo.IsPtr or RightInfo.IsPtr: + Result = CTypeInfo() + # 当双方都是指针时(如 (AST | t.CPtr) | t.CPtr),PtrCount 应相加 + # 而非取 max,否则 AST** 会被折叠为 AST*,导致对象切片问题 + if LeftInfo.IsPtr and RightInfo.IsPtr: + Result.PtrCount = LeftInfo.PtrCount + RightInfo.PtrCount + # BaseType 优先取非 void 侧(如 AST),而非 t.CPtr 的 void + if LeftInfo.BaseType and not isinstance(LeftInfo.BaseType, t.CVoid): + Result.BaseType = LeftInfo.BaseType + elif RightInfo.BaseType and not isinstance(RightInfo.BaseType, t.CVoid): + Result.BaseType = RightInfo.BaseType + else: + Result.BaseType = t.CUnsignedChar() + else: + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + NonPtrInfo = LeftInfo if not LeftInfo.IsPtr else RightInfo + PtrInfo = RightInfo if RightInfo.IsPtr else LeftInfo + if NonPtrInfo.BaseType and (not isinstance(NonPtrInfo.BaseType, (t._CTypedef,)) or NonPtrInfo.PtrCount > 0): + Result.BaseType = NonPtrInfo.BaseType + elif NonPtrInfo.IsTypedef and NonPtrInfo.Name: + if NonPtrInfo.BaseType and (not isinstance(NonPtrInfo.BaseType, (t._CTypedef,)) or NonPtrInfo.PtrCount > 0): + Result.BaseType = NonPtrInfo.BaseType + Result.IsTypedef = True + Result.Name = NonPtrInfo.Name + if NonPtrInfo.OriginalType: + Result.OriginalType = NonPtrInfo.OriginalType + else: + Result.BaseType = t.CStruct(name=NonPtrInfo.Name) + Result.IsStruct = True + elif PtrInfo.BaseType and (not isinstance(PtrInfo.BaseType, (t._CTypedef,)) or PtrInfo.PtrCount > 0): + Result.BaseType = PtrInfo.BaseType + else: + Result.BaseType = t.CUnsignedChar() + if LeftInfo.DataConst or RightInfo.DataConst: + Result.DataConst = True + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + if LeftInfo.DataVolatile or RightInfo.DataVolatile: + Result.DataVolatile = True + if LeftInfo.VarVolatile or RightInfo.VarVolatile: + Result.VarVolatile = True + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + + if LeftInfo.DataConst or RightInfo.DataConst: + Result = CTypeInfo() + BaseTypeSide = LeftInfo if not LeftInfo.DataConst else RightInfo + QualSide = LeftInfo if LeftInfo.DataConst else RightInfo + Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else QualSide.BaseType + if not Result.BaseType: + Result.BaseType = t.CInt() + Result.DataConst = True + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + if LeftInfo.DataVolatile or RightInfo.DataVolatile: + Result.DataVolatile = True + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + + if LeftInfo.DataVolatile or RightInfo.DataVolatile: + Result = CTypeInfo() + BaseTypeSide = LeftInfo if not LeftInfo.DataVolatile else RightInfo + QualSide = LeftInfo if LeftInfo.DataVolatile else RightInfo + Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else QualSide.BaseType + if not Result.BaseType: + Result.BaseType = t.CInt() + Result.DataVolatile = True + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + + if LeftInfo.Storage or RightInfo.Storage: + Result = CTypeInfo() + BaseTypeSide = LeftInfo if not LeftInfo.Storage else RightInfo + StorageSide = LeftInfo if LeftInfo.Storage else RightInfo + # CExtern | CExport 等价于 t.State:两边都有 Storage 时设置 IsState + if BaseTypeSide.IsState or StorageSide.IsState or (LeftInfo.Storage and RightInfo.Storage): + Result.IsState = True + Result.BaseType = t.CVoid() + Result.Storage = LeftInfo.Storage if LeftInfo.Storage else RightInfo.Storage + return Result + Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else StorageSide.BaseType + if not Result.BaseType: + Result.BaseType = t.CInt() + Result.Storage = LeftInfo.Storage if LeftInfo.Storage else RightInfo.Storage + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if LeftInfo.DataConst or RightInfo.DataConst: + Result.DataConst = True + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + return Result + + # 检查是否有位域类型 (t.Bit) + if LeftInfo.IsBitField: + Result = CTypeInfo() + Result.BaseType = LeftInfo.BaseType if LeftInfo.BaseType else t.CInt() + Result.IsBitField = True + Result.BitWidth = LeftInfo.BitWidth + return Result + if RightInfo.IsBitField: + Result = CTypeInfo() + Result.BaseType = RightInfo.BaseType if RightInfo.BaseType else t.CInt() + Result.IsBitField = True + Result.BitWidth = RightInfo.BitWidth + return Result + + # 处理字节序类型 (t.BigEndian, t.LittleEndian) + # 合并两侧:保留非字节序侧的实际类型(如 CUInt64T),取字节序侧的 ByteOrder + if LeftInfo.ByteOrder or RightInfo.ByteOrder: + Result = CTypeInfo() + ByteOrderSide: CTypeInfo = LeftInfo if LeftInfo.ByteOrder else RightInfo + OtherSide: CTypeInfo = RightInfo if LeftInfo.ByteOrder else LeftInfo + if OtherSide.BaseType and (not isinstance(OtherSide.BaseType, (t._CTypedef,)) or OtherSide.PtrCount > 0): + Result.BaseType = OtherSide.BaseType + elif ByteOrderSide.BaseType and (not isinstance(ByteOrderSide.BaseType, (t._CTypedef,)) or ByteOrderSide.PtrCount > 0): + Result.BaseType = ByteOrderSide.BaseType + else: + Result.BaseType = t.CInt() + Result.ByteOrder = LeftInfo.ByteOrder or RightInfo.ByteOrder + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + return Result + + if LeftInfo.IsFuncPtr or RightInfo.IsFuncPtr: + Result = CTypeInfo() + FuncPtrSide = LeftInfo if LeftInfo.IsFuncPtr else RightInfo + Result.IsFuncPtr = True + Result.FuncPtrParams = list(FuncPtrSide.FuncPtrParams) + Result.FuncPtrReturn = FuncPtrSide.FuncPtrReturn + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + + if LeftInfo.IsTypedef and RightInfo.BaseType: + if LeftInfo.BaseType and (not isinstance(LeftInfo.BaseType, (t._CTypedef,)) or LeftInfo.PtrCount > 0): + Result = LeftInfo.Copy() + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + return RightInfo + if RightInfo.IsTypedef and LeftInfo.BaseType: + if RightInfo.BaseType and (not isinstance(RightInfo.BaseType, (t._CTypedef,)) or RightInfo.PtrCount > 0): + Result = RightInfo.Copy() + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + return Result + return LeftInfo + + if LeftInfo.BaseType: + return LeftInfo + elif RightInfo.BaseType: + return RightInfo + return CTypeInfo() + + @staticmethod + def _from_node_subscript(Node: ast.Subscript, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Subscript 节点""" + base: ast.AST = Node.value + if isinstance(base, ast.Attribute): + if isinstance(base.value, ast.Name) and IsTModule(base.value.id, SymbolTable) and base.attr == 'Bit': + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsBitField = True + if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): + Result.BitWidth = Node.slice.value + else: + Result.BitWidth = 1 + return Result + if IsListAnnotation(Node): + slice_node: ast.AST = Node.slice + elts: list[ast.AST] = [] + if isinstance(slice_node, ast.Tuple): + elts = slice_node.elts + elif isinstance(slice_node, (ast.Attribute, ast.Name, ast.Subscript)): + elts = [slice_node] + if elts: + ElemInfo: CTypeInfo = TypeAnnotationResolver.from_node(elts[0], SymbolTable) + if ElemInfo and ElemInfo.BaseType: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = ElemInfo.BaseType + Result.PtrCount = ElemInfo.PtrCount + Result.ArrayDims = list(ElemInfo.ArrayDims) + if len(elts) >= 2: + count_val: int | None = TypeAnnotationResolver.try_eval_const_expr(elts[1], SymbolTable) + if count_val is not None and isinstance(count_val, int) and count_val > 0: + Result.ArrayDims.insert(0, str(count_val)) + else: + Result.PtrCount += 1 + if ElemInfo.IsPtr and not ElemInfo.ArrayDims: + Result.PtrCount = max(Result.PtrCount, 1) + return Result + if isinstance(base, ast.Attribute): + parts: list[str] = [] + current: ast.AST = base + while isinstance(current, ast.Attribute): + parts.insert(0, current.attr) + current = current.value + if isinstance(current, ast.Name): + parts.insert(0, current.id) + if parts and IsTModule(parts[0], SymbolTable) and parts[-1] == t.CPtr.__name__: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CVoid() + Result.PtrCount = 1 + slice_node: ast.AST = Node.slice + if isinstance(slice_node, ast.Subscript): + InnerInfo: CTypeInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) + if InnerInfo: + if InnerInfo.PtrCount > 0: + Result.PtrCount += InnerInfo.PtrCount + if InnerInfo.BaseType and not isinstance(InnerInfo.BaseType, t.CVoid): + Result.BaseType = InnerInfo.BaseType + elif isinstance(slice_node, ast.Attribute): + SliceInfo: CTypeInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) + if SliceInfo: + if SliceInfo.IsPtr: + Result.PtrCount += SliceInfo.PtrCount + elif SliceInfo.BaseType and not isinstance(SliceInfo.BaseType, t.CVoid): + Result.BaseType = SliceInfo.BaseType + elif isinstance(slice_node, ast.Name): + SliceType: type | None = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + Result.PtrCount += 1 + else: + SliceInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) + if SliceInfo and SliceInfo.BaseType and not isinstance(SliceInfo.BaseType, t.CVoid): + Result.BaseType = SliceInfo.BaseType + return Result + if parts and IsTModule(parts[0], SymbolTable) and parts[-1] == 'Callable': + Result = CTypeInfo() + Result.IsFuncPtr = True + slice_node = Node.slice + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + params_list = slice_node.elts[0] + return_node = slice_node.elts[1] + ParamTypes = [] + if isinstance(params_list, ast.List): + for elt in params_list.elts: + ParamTypeInfo = TypeAnnotationResolver.from_node(elt, SymbolTable) + if ParamTypeInfo: + ParamTypes.append(('', ParamTypeInfo)) + if not ParamTypes: + ParamTypes.append(('', TypeAnnotationResolver.from_type_name('void'))) + Result.FuncPtrParams = ParamTypes + ReturnTypeInfo = TypeAnnotationResolver.from_node(return_node, SymbolTable) + Result.FuncPtrReturn = ReturnTypeInfo if ReturnTypeInfo else CTypeInfo.VoidTypeInfo() + return Result + return CTypeInfo() + + @staticmethod + def _from_node_attribute(Node: ast.Attribute, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Attribute 节点""" + ModuleParts: list[str] = [] + Current: ast.AST = Node + while isinstance(Current, ast.Attribute): + ModuleParts.insert(0, Current.attr) + Current = Current.value + + if isinstance(Current, ast.Name): + ModuleParts.insert(0, Current.id) + + TypeName: str + ModulePath: str | None + if len(ModuleParts) >= 2: + TypeName = ModuleParts[-1] + ModulePath = '.'.join(ModuleParts[:-1]) + elif len(ModuleParts) == 1: + TypeName = ModuleParts[0] + ModulePath = None + else: + return CTypeInfo() + + # 解析 import 别名: 如 import vpsdk.window as window -> window -> vpsdk.window + if ModulePath: + resolved: bool = False + import_aliases: dict = SymbolTable.import_aliases + if ModulePath in import_aliases: + ModulePath = import_aliases[ModulePath] + resolved = True + if not resolved and ModulePath in SymbolTable: + entry: CTypeInfo = SymbolTable[ModulePath] + if isinstance(entry, CTypeInfo) and entry.IsModuleAlias: + resolved_name = entry.ResolvedModule + if resolved_name: + ModulePath = resolved_name + + if ModulePath and IsTModulePath(ModulePath): + TypeClass: type | None = CTypeHelper.GetTModuleCType(TypeName) + if TypeClass is None: + TypeClass = getattr(t, TypeName, None) + if TypeClass is not None and TypeClass.HasPosition(t.CType.POINTER): + Info: CTypeInfo = CTypeInfo() + Info.PtrCount = 1 + if TypeClass == t.CArrayPtr: + Info.IsArrayPtr = True + Info.BaseType = t.CVoid + return Info + if TypeClass is not None and TypeClass.IsStorageClass(): + Info: CTypeInfo = CTypeInfo() + Info.Storage = TypeClass() + if TypeClass == t.State: + Info.IsState = True + Info.BaseType = t.CVoid() + return Info + if TypeClass is not None and TypeClass.IsTypeQualifier(): + Info: CTypeInfo = CTypeInfo() + if TypeClass == t.CConst: + Info.DataConst = True + elif TypeClass == t.CVolatile: + Info.DataVolatile = True + return Info + if TypeClass is not None and TypeClass in (t.BigEndian, t.LittleEndian): + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.ByteOrder = 'big' if TypeClass == t.BigEndian else 'little' + return Result + if (TypeClass is not None + and isinstance(TypeClass, type) + and issubclass(TypeClass, t.CType) + and TypeClass is not t.CType + and TypeClass.HasPosition(t.CType.BASE)): + if TypeClass == t.State: + Result: CTypeInfo = CTypeInfo() + Result.IsState = True + Result.BaseType = t.CVoid() + return Result + Inst: t.CType = TypeClass() + Result: CTypeInfo = CTypeInfo() + Result.BaseType = TypeClass + Result.Name = type(Inst).__name__ + if hasattr(Inst, 'IsSigned'): + Result.IsSigned = Inst.IsSigned + return Result + CNAME: str | None = CTypeHelper.GetCName(TypeName) + if CNAME: + return TypeAnnotationResolver.from_type_name(CNAME) + + # 处理字节序类型 + if TypeName == 'BigEndian': + Result = CTypeInfo() + Result.BaseType = t.CInt() + Result.ByteOrder = 'big' + return Result + if TypeName == 'LittleEndian': + Result = CTypeInfo() + Result.BaseType = t.CInt() + Result.ByteOrder = 'little' + return Result + + FullName: str = f"{ModulePath}.{TypeName}" if ModulePath else TypeName + + if TypeName in SymbolTable: + Entry: CTypeInfo = SymbolTable[TypeName] + if Entry and Entry.IsTypedef: + Resolved: CTypeInfo = Entry.Copy() + if Resolved.BaseType is None: + Resolved.BaseType = t._CTypedef(TypeName) + return Resolved + if Entry and Entry.IsEnum: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsEnum = True + Result.Name = TypeName + return Result + if Entry and Entry.IsExceptionClass: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsExceptionClass = True + Result.Name = TypeName + return Result + if Entry: + Result: CTypeInfo = Entry.Copy() + if Result.BaseType is None and (Result.IsStruct or Result.IsCpythonObject or Result.Name): + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + return Result + return CTypeInfo() + + if FullName in SymbolTable: + Info: CTypeInfo = SymbolTable[FullName] + if Info and Info.IsTypedef: + OriginalType: str = Info.get('OriginalType', '') + if OriginalType and 'typedef' in OriginalType: + parts: list[str] = OriginalType.split() + if len(parts) >= 2: + BaseType_name: str = parts[1] + if not BaseType_name.startswith('C'): + BaseType_name = 'C' + BaseType_name + CNAME: str | None = CTypeHelper.GetCName(BaseType_name) + if CNAME: + return TypeAnnotationResolver.from_type_name(CNAME) + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t._CTypedef(TypeName) + Result.IsTypedef = True + return Result + if Info: + Result: CTypeInfo = Info.Copy() + if Result.BaseType is None and (Result.IsStruct or Result.IsCpythonObject or Result.Name): + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + return Result + return CTypeInfo() + + return CTypeInfo() diff --git a/App/lib/core/TypeSpec.py b/App/lib/core/TypeSpec.py new file mode 100644 index 0000000..1b28bf5 --- /dev/null +++ b/App/lib/core/TypeSpec.py @@ -0,0 +1,332 @@ +"""TypeSpec, SymbolMeta, SymbolKind — CTypeInfo 的拆分类型 + +Phase 1 重构:将 CTypeInfo 的 40+ 平铺字段拆分为三个聚焦的类型: +- TypeSpec: 纯类型描述(BaseType + 指针 + 限定符 + 数组维度等) +- SymbolMeta: 符号表元数据(名称、行号、文件、类型种类、成员等) +- SymbolKind: 枚举类型种类(从 is_xxx bool 字段派生,供新代码使用) + +设计原则: +- CTypeInfo 内部组合 TypeSpec + SymbolMeta +- 旧 property 委托到新字段,外部代码零修改 +- 渐进式迁移:先加新字段,后删旧字段 +- 类型种类 bool 保持独立(因为 IsTypedef 和 IsStruct 可以同时为 True) +- SymbolKind 从 is_xxx bool 字段派生,供新代码使用 +""" +from __future__ import annotations + +import enum +from typing import TYPE_CHECKING, List, Dict, Tuple, Any, Optional + +if TYPE_CHECKING: + from lib.core.Handles.HandlesBase import CTypeInfo + +from lib.includes import t + + +class SymbolKind(enum.Enum): + """符号类型种类 — 从 is_xxx bool 字段派生的主类型 + + 注意:IsTypedef 与 IsStruct/IsEnum/IsUnion 正交(可以同时为 True), + 因此 SymbolKind 只反映"主类型",IsTypedef 需要单独查询。 + """ + UNKNOWN = 0 + VARIABLE = 1 + FUNCTION = 2 + STRUCT = 3 + UNION = 4 + ENUM = 5 + DEFINE = 6 + ENUM_MEMBER = 7 + MODULE = 8 + EXCEPTION = 9 + RENUM = 10 + FUNC_PTR = 11 + STATE = 12 + CPYTHON_OBJECT = 13 + + +# CTypeInfo 旧字段 → (目标, 新字段名) 的路由表 +# 目标: 'ts' = TypeSpec, 'sm' = SymbolMeta +FIELD_ROUTES = { + # --- TypeSpec 字段 --- + 'PtrCount': ('ts', 'ptr_count'), + 'VarConst': ('ts', 'var_const'), + 'VarVolatile': ('ts', 'var_volatile'), + 'DataConst': ('ts', 'data_const'), + 'DataVolatile': ('ts', 'data_volatile'), + 'ArrayDims': ('ts', 'array_dims'), + 'Storage': ('ts', 'storage'), + 'IsFuncPtr': ('ts', 'is_func_ptr'), + 'FuncPtrParams': ('ts', 'func_ptr_params'), + 'FuncParamNames': ('ts', 'func_param_names'), + 'FuncPtrReturn': ('ts', 'func_ptr_return'), + 'IsVariadic': ('ts', 'is_variadic'), + 'IsBitField': ('ts', 'is_bitfield'), + 'BitWidth': ('ts', 'bit_width'), + 'ByteOrder': ('ts', 'byte_order'), + 'IsArrayPtr': ('ts', 'is_array_ptr'), + 'ArrayPtr': ('ts', 'array_ptr'), + 'IsState': ('ts', 'is_state'), + 'OriginalType': ('ts', 'original_type'), + 'IsSigned': ('ts', 'is_signed'), + + # --- SymbolMeta 字段 --- + 'Name': ('sm', 'name'), + 'Lineno': ('sm', 'lineno'), + 'file': ('sm', 'file'), + 'ModuleName': ('sm', 'module_name'), + 'Members': ('sm', 'members'), + 'IsCpythonObject':('sm', 'is_cpython_object'), + 'IsPacked': ('sm', 'is_packed'), + 'IsAnonymous': ('sm', 'is_anonymous'), + 'IsInline': ('sm', 'is_inline'), + 'InlineBody': ('sm', 'inline_body'), + 'InlineParams': ('sm', 'inline_params'), + 'RenumVariants': ('sm', 'renum_variants'), + 'DeclaredType': ('sm', 'declared_type'), + 'CreturnTypes': ('sm', 'creturn_types'), + 'MetaList': ('sm', 'meta_list'), + 'IsModuleAlias': ('sm', 'is_module_alias'), + 'ResolvedModule': ('sm', 'resolved_module'), + 'DefineValue': ('sm', 'define_value'), + 'EnumName': ('sm', 'enum_name'), + 'value': ('sm', 'enum_value'), + + # --- 类型种类 bool(直接存储在 SymbolMeta 的 __slots__ 字段)--- + 'IsStruct': ('sm', 'is_struct'), + 'IsEnum': ('sm', 'is_enum'), + 'IsUnion': ('sm', 'is_union'), + 'IsTypedef': ('sm', 'is_typedef'), + 'IsVariable': ('sm', 'is_variable'), + 'IsFunction': ('sm', 'is_function'), + 'IsDefine': ('sm', 'is_define'), + 'IsEnumMember': ('sm', 'is_enum_member'), + 'IsRenum': ('sm', 'is_renum'), + 'IsExceptionClass': ('sm', 'is_exception_class'), + + # --- _extra(CTypeInfo 旧 _extra 字典,迁移到 SymbolMeta)--- + '_extra': ('sm', '_extra'), +} + + +class TypeSpec: + """纯类型描述 — 不包含符号表元数据 + + 职责:描述一个 C 类型的结构(基础类型、指针层数、限定符、数组维度等) + 不包含:名称、行号、文件路径、符号种类等元数据(这些归 SymbolMeta) + + 注意:当前阶段保持与 CTypeInfo 旧字段的兼容性, + PtrCount/VarConst/DataConst 等仍然使用扁平字段。 + 未来 Phase 2 会将指针改为嵌套 PointerLayer 表示。 + """ + + __slots__ = ( + '_base_type', 'ptr_count', + 'var_const', 'var_volatile', 'data_const', 'data_volatile', + 'array_dims', 'storage', + 'is_func_ptr', 'func_ptr_params', 'func_param_names', 'func_ptr_return', + 'is_variadic', + 'is_bitfield', 'bit_width', + 'byte_order', + 'is_array_ptr', 'array_ptr', + 'is_state', + 'original_type', + 'is_signed', + ) + + def __init__(self): + self._base_type: t.CType | tuple | None = None # t.CType 实例或 tuple + self.ptr_count: int = 0 + self.var_const: bool = False + self.var_volatile: bool = False + self.data_const: bool = False + self.data_volatile: bool = False + self.array_dims: List[str] = [] + self.storage: t.CType | None = None # t.CType (CStatic/CExtern/CExport 等) + self.is_func_ptr: bool = False + self.func_ptr_params: List[Tuple[str, CTypeInfo]] = [] # (参数名, CTypeInfo) + self.func_param_names: List[str] = [] + self.func_ptr_return: CTypeInfo | None = None # CTypeInfo + self.is_variadic: bool = False + self.is_bitfield: bool = False + self.bit_width: int = 0 + self.byte_order: str = "" # "big", "little", or "" + self.is_array_ptr: bool = False + self.array_ptr: CTypeInfo | None = None # CTypeInfo + self.is_state: bool = False + self.original_type: CTypeInfo | str | None = None # CTypeInfo | str | None (typedef 原始类型) + self.is_signed: Optional[bool] = None # None = 未设置, True/False = 有符号/无符号 + + @property + def base_type(self) -> t.CType | tuple | None: + return self._base_type + + @base_type.setter + def base_type(self, value: t.CType | tuple | None): + """设置 BaseType,与 CTypeInfo.BaseType setter 逻辑一致(不含 kind 副作用)""" + if value is None: + self._base_type = None + return + + if isinstance(value, (tuple, list)): + self._base_type = tuple(value) + return + + if isinstance(value, type) and issubclass(value, t.CType): + self._base_type = value() + return + + if isinstance(value, t.CType): + self._base_type = value + return + + raise TypeError( + f"base_type must be t.CType instance, type subclass, or tuple of t.CType, got {type(value)}" + ) + + def copy(self) -> TypeSpec: + """深拷贝""" + new: TypeSpec = TypeSpec() + new._base_type = self._base_type + new.ptr_count = self.ptr_count + new.var_const = self.var_const + new.var_volatile = self.var_volatile + new.data_const = self.data_const + new.data_volatile = self.data_volatile + new.array_dims = list(self.array_dims) + new.storage = self.storage + new.is_func_ptr = self.is_func_ptr + new.func_ptr_params = list(self.func_ptr_params) + new.func_param_names = list(self.func_param_names) + new.func_ptr_return = self.func_ptr_return + new.is_variadic = self.is_variadic + new.is_bitfield = self.is_bitfield + new.bit_width = self.bit_width + new.byte_order = self.byte_order + new.is_array_ptr = self.is_array_ptr + new.array_ptr = self.array_ptr + new.is_state = self.is_state + new.original_type = self.original_type + new.is_signed = self.is_signed + return new + + +class SymbolMeta: + """符号表条目的元数据 — 与类型描述无关 + + 职责:记录符号的名称、位置、种类、成员等元数据 + 不包含:类型结构(BaseType、指针、限定符等,这些归 TypeSpec) + + is_struct/is_enum/is_union/is_typedef/is_variable/is_function/is_define/ + is_enum_member/is_renum/is_exception_class: 独立 bool 字段,存储类型种类。 + 这些 flag 不是互斥的(IsTypedef 和 IsStruct 可以同时为 True)。 + SymbolKind 从这些 bool 字段派生,供新代码使用。 + """ + + __slots__ = ( + 'name', 'lineno', 'file', 'module_name', + 'members', + 'is_cpython_object', 'is_packed', + 'define_value', + 'enum_name', 'enum_value', + 'is_inline', 'inline_body', 'inline_params', + 'is_anonymous', + 'renum_variants', + 'declared_type', 'creturn_types', + 'meta_list', + 'is_module_alias', 'resolved_module', + 'is_struct', 'is_enum', 'is_union', 'is_typedef', + 'is_variable', 'is_function', 'is_define', + 'is_enum_member', 'is_renum', 'is_exception_class', + '_extra', + ) + + def __init__(self): + self.name: str = "" + self.lineno: int = 0 + self.file: str = "" + self.module_name: str = "" + self.members: Dict[str, CTypeInfo] = {} # 成员名 -> CTypeInfo + self.is_cpython_object: bool = False + self.is_packed: bool = False + self.define_value: int | str | float | bool | None = None + self.enum_name: str = "" # 枚举成员所属的枚举类名 + self.enum_value: int | None = None # 枚举成员的值 + self.is_inline: bool = False + self.inline_body: list | None = None + self.inline_params: list | None = None + self.is_anonymous: bool = False + self.renum_variants: list = [] + self.declared_type: str = "" + self.creturn_types: list = [] + self.meta_list: Any = None # FuncMeta + self.is_module_alias: bool = False + self.resolved_module: str | None = None + self.is_struct: bool = False + self.is_enum: bool = False + self.is_union: bool = False + self.is_typedef: bool = False + self.is_variable: bool = False + self.is_function: bool = False + self.is_define: bool = False + self.is_enum_member: bool = False + self.is_renum: bool = False + self.is_exception_class: bool = False + self._extra: dict = {} + + @property + def kind(self) -> SymbolKind: + """从 is_xxx bool 字段派生主类型种类""" + if self.is_struct or self.is_cpython_object: + return SymbolKind.STRUCT if not self.is_cpython_object else SymbolKind.CPYTHON_OBJECT + if self.is_enum: + return SymbolKind.RENUM if self.is_renum else SymbolKind.ENUM + if self.is_union: + return SymbolKind.UNION + if self.is_function: + return SymbolKind.FUNCTION + if self.is_variable: + return SymbolKind.VARIABLE + if self.is_define: + return SymbolKind.DEFINE + if self.is_enum_member: + return SymbolKind.ENUM_MEMBER + if self.is_exception_class: + return SymbolKind.EXCEPTION + return SymbolKind.UNKNOWN + + def copy(self) -> SymbolMeta: + """深拷贝""" + new: SymbolMeta = SymbolMeta() + new.name = self.name + new.lineno = self.lineno + new.file = self.file + new.module_name = self.module_name + new.members = dict(self.members) + new.is_cpython_object = self.is_cpython_object + new.is_packed = self.is_packed + new.define_value = self.define_value + new.enum_name = self.enum_name + new.enum_value = self.enum_value + new.is_inline = self.is_inline + new.inline_body = self.inline_body + new.inline_params = list(self.inline_params) if self.inline_params else None + new.is_anonymous = self.is_anonymous + new.renum_variants = list(self.renum_variants) + new.declared_type = self.declared_type + new.creturn_types = list(self.creturn_types) + new.meta_list = self.meta_list + new.is_module_alias = self.is_module_alias + new.resolved_module = self.resolved_module + new.is_struct = self.is_struct + new.is_enum = self.is_enum + new.is_union = self.is_union + new.is_typedef = self.is_typedef + new.is_variable = self.is_variable + new.is_function = self.is_function + new.is_define = self.is_define + new.is_enum_member = self.is_enum_member + new.is_renum = self.is_renum + new.is_exception_class = self.is_exception_class + new._extra = dict(self._extra) + return new diff --git a/App/lib/core/stub_generator.py b/App/lib/core/stub_generator.py new file mode 100644 index 0000000..5b60618 --- /dev/null +++ b/App/lib/core/stub_generator.py @@ -0,0 +1,761 @@ +#!/usr/bin/env python3 +""" +C/H 文件到 Python 存根文件生成器 + +将 C 头文件 (.h) 或 C 源文件 (.c) 转换为 Python 存根文件 (.pyi) +生成的 Python 代码使用 TransPyC 语法,包含类型注解 +""" +from __future__ import annotations + +import os +import re +import sys +from typing import Any +from lib.core.VLogger import get_logger as _vlog + + +class CTypeMapper: + + # 硬编码 C 类型名 → Python 类型映射表 + # 这是 stub 生成器(C 头文件 → Python stub)专用的固定映射, + # 不再依赖运行时 CTypeRegistry._cname_to_class(该映射已删除)。 + _CNAME_TO_PY: dict[str, str] = { + # 基本类型 + 'char': 't.CChar', 'int': 't.CInt', 'short': 't.CShort', + 'long': 't.CLong', 'long long': 't.CLongLong', + 'float': 't.CFloat', 'double': 't.CDouble', 'void': 't.CVoid', + # unsigned / signed 变体 + 'unsigned': 't.CUnsigned', 'unsigned char': 't.CUnsignedChar', + 'unsigned int': 't.CUnsignedInt', 'unsigned short': 't.CUnsignedShort', + 'unsigned long': 't.CUnsignedLong', + 'unsigned long long': 't.CUnsignedLongLong', + 'signed char': 't.CSignedChar', + # bool / size_t + 'bool': 't.CBool', '_Bool': 't.CBool', 'size_t': 't.CSizeT', + 'ssize_t': 't.CLong', + # stdint 固定宽度类型 + 'int8_t': 't.CInt8T', 'int16_t': 't.CInt16T', + 'int32_t': 't.CInt32T', 'int64_t': 't.CInt64T', + 'uint8_t': 't.CUInt8T', 'uint16_t': 't.CUInt16T', + 'uint32_t': 't.CUInt32T', 'uint64_t': 't.CUInt64T', + 'intptr_t': 't.CIntPtrT', 'uintptr_t': 't.CUIntPtrT', + 'ptrdiff_t': 't.CPtrDiffT', + # 宽字符 / 字符类型 + 'wchar_t': 't.CWCharT', 'char8_t': 't.CChar8T', + 'char16_t': 't.CChar16T', 'char32_t': 't.CChar32T', + # 浮点特化类型 + 'float8_t': 't.CFloat8T', 'float16_t': 't.CFloat16T', + 'float32_t': 't.CFloat32T', 'float64_t': 't.CFloat64T', + 'float128_t': 't.CFloat128T', + # signed 速记 + 'signed': 't.CInt', 'signed int': 't.CInt', + 'signed short': 't.CShort', 'signed long': 't.CLong', + } + + @classmethod + def _ResolveCNameToPyType(cls, c_type: str) -> str: + """将 C 类型名解析为 Python 类型字符串(如 'int' → 't.CInt') + + 使用硬编码映射表,不再依赖 CTypeRegistry._cname_to_class。 + 未识别的类型原样返回(用于 struct/union/enum/typedef 用户自定义名)。 + """ + py: str | None = cls._CNAME_TO_PY.get(c_type) + if py is not None: + return py + return c_type + + @classmethod + def MapType(cls, c_type: str, IsPtr: bool = False, IsArray: bool = False) -> str: + c_type = c_type.strip() + + IsConst: bool = 'const ' in c_type + c_type = c_type.replace('const ', '').replace('const', '').strip() + IsVolatile: bool = 'volatile ' in c_type + c_type = c_type.replace('volatile ', '').replace('volatile', '').strip() + IsStatic: bool = 'static ' in c_type + c_type = c_type.replace('static ', '').replace('static', '').strip() + IsExtern: bool = 'extern ' in c_type + c_type = c_type.replace('extern ', '').replace('extern', '').strip() + IsRegister: bool = 'register ' in c_type + c_type = c_type.replace('register ', '').replace('register', '').strip() + IsInline: bool = 'inline ' in c_type + c_type = c_type.replace('inline ', '').replace('inline', '').strip() + + PtrSuffix: str = '' + if IsPtr or c_type.endswith('*'): + PtrSuffix = ' | t.CPtr' + c_type = c_type.rstrip('*').strip() + + if c_type.startswith('struct '): + c_type = c_type[7:].strip() + elif c_type.startswith('union '): + c_type = c_type[6:].strip() + elif c_type.startswith('enum '): + c_type = c_type[5:].strip() + + PyType: str = cls._ResolveCNameToPyType(c_type) + + result: str = PyType + PtrSuffix + + if IsArray: + result += ' | t.CArrayPtr' + + return result + + +def _find_matching_brace(text: str, start: int) -> int: + depth: int = 0 + i: int = start + while i < len(text): + if text[i] == '{': + depth += 1 + elif text[i] == '}': + depth -= 1 + if depth == 0: + return i + elif text[i] == '"' or text[i] == "'": + quote: str = text[i] + i += 1 + while i < len(text) and text[i] != quote: + if text[i] == '\\': + i += 1 + i += 1 + elif text[i] == '/' and i + 1 < len(text) and text[i + 1] == '/': + while i < len(text) and text[i] != '\n': + i += 1 + continue + elif text[i] == '/' and i + 1 < len(text) and text[i + 1] == '*': + i += 2 + while i + 1 < len(text) and not (text[i] == '*' and text[i + 1] == '/'): + i += 1 + i += 1 + i += 1 + return -1 + + +def _strip_comments(content: str) -> str: + result: list[str] = [] + i: int = 0 + while i < len(content): + if content[i] == '/' and i + 1 < len(content) and content[i + 1] == '/': + while i < len(content) and content[i] != '\n': + i += 1 + elif content[i] == '/' and i + 1 < len(content) and content[i + 1] == '*': + i += 2 + while i + 1 < len(content) and not (content[i] == '*' and content[i + 1] == '/'): + i += 1 + i += 2 + elif content[i] == '"' or content[i] == "'": + quote: str = content[i] + result.append(content[i]) + i += 1 + while i < len(content) and content[i] != quote: + if content[i] == '\\' and i + 1 < len(content): + result.append(content[i]) + i += 1 + result.append(content[i]) + i += 1 + if i < len(content): + result.append(content[i]) + i += 1 + else: + result.append(content[i]) + i += 1 + return ''.join(result) + + +def _strip_preprocessor_guards(content: str) -> str: + lines: list[str] = content.split('\n') + filtered: list[str] = [] + for line in lines: + stripped: str = line.strip() + if stripped.startswith('#ifndef') and '_H' in stripped.upper(): + continue + if stripped.startswith('#define') and '_H' in stripped.upper(): + continue + if stripped == '#endif': + continue + filtered.append(line) + return '\n'.join(filtered) + + +class CHeaderParser: + """C 头文件解析器""" + + def __init__(self) -> None: + self.defines: list[dict[str, str | None]] = [] + self.structs: dict[str, dict[str, Any]] = {} + self.enums: dict[str, dict[str, Any]] = {} + self.typedefs: list[dict[str, str]] = [] + self.functions: dict[str, dict[str, Any]] = {} + self.variables: dict[str, dict[str, Any]] = {} + self.unions: dict[str, dict[str, Any]] = {} + self.func_ptr_typedefs: dict[str, dict[str, Any]] = {} + self._typedef_struct_map: dict[str, str] = {} + self._typedef_enum_map: dict[str, str] = {} + self._typedef_union_map: dict[str, str] = {} + + def ParseFile(self, FilePath: str) -> None: + with open(FilePath, 'r', encoding='utf-8') as f: + content: str = f.read() + self.ParseContent(content) + + def ParseContent(self, content: str) -> None: + content = _strip_comments(content) + content = _strip_preprocessor_guards(content) + self._ParseDefines(content) + self._ParseTypedefStructs(content) + self._ParseTypedefEnums(content) + self._ParseTypedefUnions(content) + self._ParseFuncPtrTypedefs(content) + self._ParseSimpleTypedefs(content) + self._ParseStandaloneStructs(content) + self._ParseStandaloneEnums(content) + self._ParseStandaloneUnions(content) + stripped: str = self._StripBodies(content) + self._ParseFunctions(stripped) + self._ParseVariables(stripped) + + def _StripBodies(self, content: str) -> str: + result: str = content + while True: + pos: int = result.find('{') + if pos < 0: + break + end: int = _find_matching_brace(result, pos) + if end < 0: + break + result = result[:pos] + result[end + 1:] + return result + + def _ParseDefines(self, content: str) -> None: + for match in re.finditer(r'#define\s+(\w+)(?:\s+(.+?))?\s*$', content, re.MULTILINE): + Name: str = match.group(1) + Value: str | None = match.group(2) + if Name.startswith('_') and Name.endswith('_'): + continue + if Name.startswith('__'): + continue + self.defines.append({ + 'name': Name, + 'value': Value.strip() if Value else None, + }) + + def _ParseTypedefStructs(self, content: str) -> None: + pattern: str = r'typedef\s+struct\s*(\w*)\s*\{' + for match in re.finditer(pattern, content): + brace_start: int = content.index('{', match.start()) + brace_end: int = _find_matching_brace(content, brace_start) + if brace_end < 0: + continue + body: str = content[brace_start + 1:brace_end] + rest: str = content[brace_end + 1:].lstrip() + AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest) + if not AliasMatch: + continue + AliasName: str = AliasMatch.group(1) + TagName: str | None = match.group(1) if match.group(1) else None + members: list[dict[str, Any]] = self._ParseStructMembers(body) + self.structs[AliasName] = { + 'name': AliasName, + 'tag': TagName, + 'members': members, + } + if TagName: + self._typedef_struct_map[TagName] = AliasName + + def _ParseTypedefEnums(self, content: str) -> None: + pattern: str = r'typedef\s+enum\s*(\w*)\s*\{' + for match in re.finditer(pattern, content): + brace_start: int = content.index('{', match.start()) + brace_end: int = _find_matching_brace(content, brace_start) + if brace_end < 0: + continue + body: str = content[brace_start + 1:brace_end] + rest: str = content[brace_end + 1:].lstrip() + AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest) + if not AliasMatch: + continue + AliasName: str = AliasMatch.group(1) + TagName: str | None = match.group(1) if match.group(1) else None + values: list[dict[str, str | None]] = self._ParseEnumValues(body) + self.enums[AliasName] = { + 'name': AliasName, + 'tag': TagName, + 'values': values, + } + if TagName: + self._typedef_enum_map[TagName] = AliasName + + def _ParseTypedefUnions(self, content: str) -> None: + pattern: str = r'typedef\s+union\s*(\w*)\s*\{' + for match in re.finditer(pattern, content): + brace_start: int = content.index('{', match.start()) + brace_end: int = _find_matching_brace(content, brace_start) + if brace_end < 0: + continue + body: str = content[brace_start + 1:brace_end] + rest: str = content[brace_end + 1:].lstrip() + AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest) + if not AliasMatch: + continue + AliasName: str = AliasMatch.group(1) + TagName: str | None = match.group(1) if match.group(1) else None + members: list[dict[str, Any]] = self._ParseStructMembers(body) + self.unions[AliasName] = { + 'name': AliasName, + 'tag': TagName, + 'members': members, + } + if TagName: + self._typedef_union_map[TagName] = AliasName + + def _ParseFuncPtrTypedefs(self, content: str) -> None: + pattern: str = r'typedef\s+([\w\s\*]+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;' + for match in re.finditer(pattern, content): + ReturnType: str = match.group(1).strip() + Name: str = match.group(2) + ParamsStr: str = match.group(3).strip() + params: list[dict[str, Any]] = self._ParseFuncParams(ParamsStr) + self.func_ptr_typedefs[Name] = { + 'name': Name, + 'return_type': ReturnType, + 'params': params, + } + + def _ParseSimpleTypedefs(self, content: str) -> None: + stripped: str = self._StripBodies(content) + pattern: str = r'typedef\s+([\w\s\*]+?)\s+(\w+)\s*;' + for match in re.finditer(pattern, stripped): + BaseType: str = match.group(1).strip() + AliasName: str = match.group(2) + if AliasName in self.structs: + continue + if AliasName in self.enums: + continue + if AliasName in self.unions: + continue + if AliasName in self.func_ptr_typedefs: + continue + if BaseType.startswith('typedef'): + continue + self.typedefs.append({'name': AliasName, 'base': BaseType}) + + def _ParseStandaloneStructs(self, content: str) -> None: + pattern: str = r'(? None: + pattern: str = r'(? None: + pattern: str = r'(? None: + pattern: str = r'(?:(?:static|extern|inline)\s+)*([\w][\w\s\*]*?)\b(\w+)\s*\(([^)]*)\)\s*;' + for match in re.finditer(pattern, content): + ReturnType: str = match.group(1).strip() + FuncName: str = match.group(2) + ParamsStr: str = match.group(3).strip() + if FuncName in self.structs or FuncName in self.enums or FuncName in self.unions: + continue + if FuncName in self.func_ptr_typedefs: + continue + if FuncName in self.defines: + continue + if ReturnType in ('struct', 'union', 'enum', 'typedef'): + continue + if re.match(r'^\d', ReturnType): + continue + if not re.match(r'^[A-Za-z_]', FuncName): + continue + params: list[dict[str, Any]] = self._ParseFuncParams(ParamsStr) + self.functions[FuncName] = { + 'name': FuncName, + 'return_type': ReturnType, + 'params': params, + } + + def _ParseVariables(self, content: str) -> None: + pattern: str = r'(?:(?:static|extern|volatile|const)\s+)*([\w][\w\s\*]*?)\s+(\w+)\s*(?:\[(\d*)\])?\s*;' + for match in re.finditer(pattern, content): + TypeStr: str = match.group(1).strip() + VarName: str = match.group(2) + ArraySize: str | None = match.group(3) + if VarName in self.functions or VarName in self.structs or VarName in self.enums: + continue + if VarName in self.unions or VarName in self.func_ptr_typedefs: + continue + if VarName in self.defines: + continue + if TypeStr.startswith('typedef'): + continue + if TypeStr in ('struct', 'union', 'enum', 'typedef'): + continue + if re.match(r'^(if|else|while|for|return|switch|case|break|continue|do|goto)$', VarName): + continue + if not re.match(r'^[A-Za-z_]', VarName): + continue + IsPtr: bool = '*' in TypeStr + IsArray: bool = ArraySize is not None + self.variables[VarName] = { + 'name': VarName, + 'type': TypeStr, + 'IsPtr': IsPtr, + 'is_array': IsArray, + 'array_size': int(ArraySize) if ArraySize and ArraySize.isdigit() else None, + } + + def _ParseStructMembers(self, body: str) -> list[dict[str, Any]]: + members: list[dict[str, Any]] = [] + for line in body.split(';'): + line = line.strip() + if not line: + continue + arr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s+(\w+)\s*\[(\d*)\]', line) + if arr_match: + MemberType: str = arr_match.group(1).strip() + MemberName: str = arr_match.group(2) + ArraySize: str = arr_match.group(3) + members.append({ + 'name': MemberName, + 'type': MemberType, + 'is_array': True, + 'array_size': int(ArraySize) if ArraySize and ArraySize.isdigit() else None, + }) + continue + ptr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s*\*\s*(\w+)', line) + if ptr_match: + MemberType: str = ptr_match.group(1).strip() + ' *' + MemberName: str = ptr_match.group(2) + members.append({ + 'name': MemberName, + 'type': MemberType, + 'IsPtr': True, + }) + continue + simple_match: re.Match[str] | None = re.match(r'([\w\s]+?)\s+(\w+)', line) + if simple_match: + MemberType: str = simple_match.group(1).strip() + MemberName: str = simple_match.group(2) + members.append({ + 'name': MemberName, + 'type': MemberType, + }) + return members + + def _ParseEnumValues(self, body: str) -> list[dict[str, str | None]]: + values: list[dict[str, str | None]] = [] + for item in body.split(','): + item = item.strip() + if not item: + continue + eq_pos: int = item.find('=') + if eq_pos >= 0: + Name: str = item[:eq_pos].strip() + Value: str | None = item[eq_pos + 1:].strip() + else: + Name = item.strip() + Value = None + if Name and re.match(r'^[A-Za-z_]\w*$', Name): + values.append({'name': Name, 'value': Value}) + return values + + def _ParseFuncParams(self, params_str: str) -> list[dict[str, Any]]: + params: list[dict[str, Any]] = [] + if not params_str or params_str.strip() == 'void' or params_str.strip() == '': + return params + for param in params_str.split(','): + param = param.strip() + if not param: + continue + func_ptr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s*\(\s*\*\s*(\w*)\s*\)\s*\([^)]*\)', param) + if func_ptr_match: + RetType: str = func_ptr_match.group(1).strip() + ParamName: str = func_ptr_match.group(2) or 'callback' + params.append({ + 'name': ParamName, + 'type': RetType + ' (*)', + 'is_func_ptr': True, + }) + continue + arr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s+(\w+)\s*\[\d*\]', param) + if arr_match: + ParamType: str = arr_match.group(1).strip() + ParamName: str = arr_match.group(2) + params.append({ + 'name': ParamName, + 'type': ParamType, + 'is_array': True, + }) + continue + tokens: list[str] = param.split() + if len(tokens) >= 2: + LastToken: str = tokens[-1] + PtrCount: int = 0 + while LastToken.startswith('*'): + PtrCount += 1 + LastToken = LastToken[1:] + TypeTokens: list[str] = tokens[:-1] + for _ in range(PtrCount): + TypeTokens.append('*') + ParamName: str = LastToken + ParamType: str = ' '.join(TypeTokens) + if not ParamName: + ParamName = 'arg' + ParamType = param + params.append({ + 'name': ParamName, + 'type': ParamType, + }) + elif len(tokens) == 1: + params.append({ + 'name': 'arg', + 'type': tokens[0], + }) + return params + + +class PythonStubGenerator: + """Python 存根文件生成器""" + + def __init__(self, parser: CHeaderParser) -> None: + self.parser: CHeaderParser = parser + self.OutputLines: list[str] = [] + + def generate(self, ModuleName: str | None = None) -> str: + self.OutputLines = [] + self._AddHeader(ModuleName) + self._GenerateImports() + self._GenerateDefines() + self._GenerateFuncPtrTypedefs() + self._GenerateSimpleTypedefs() + self._GenerateStructs() + self._GenerateUnions() + self._GenerateEnums() + self._GenerateVariables() + self._GenerateFunctions() + return '\n'.join(self.OutputLines) + + def _AddHeader(self, ModuleName: str | None = None) -> None: + self.OutputLines.append('"""') + self.OutputLines.append('Auto-generated Python stub file from C header') + if ModuleName: + self.OutputLines.append(f'Module: {ModuleName}') + self.OutputLines.append('"""') + self.OutputLines.append('') + + def _GenerateImports(self) -> None: + self.OutputLines.append('import t') + self.OutputLines.append('import c') + self.OutputLines.append('') + + def _GenerateDefines(self) -> None: + if not self.parser.defines: + return + for define in self.parser.defines: + Name: str = define['name'] + Value: str | None = define['value'] + if Value is not None: + self.OutputLines.append(f'{Name}: t.CDefine = {Value}') + else: + self.OutputLines.append(f'{Name}: t.CDefine') + self.OutputLines.append('') + + def _GenerateFuncPtrTypedefs(self) -> None: + if not self.parser.func_ptr_typedefs: + return + for Name, Info in self.parser.func_ptr_typedefs.items(): + RetType: str = CTypeMapper.MapType(Info['return_type']) + ParamTypes: list[str] = [] + for p in Info['params']: + ParamTypes.append(CTypeMapper.MapType(p['type'])) + ParamStr: str = ', '.join(ParamTypes) if ParamTypes else '' + self.OutputLines.append(f'{Name}: t.CTypedef = t.Callable[[{ParamStr}], {RetType}]') + self.OutputLines.append('') + + def _GenerateSimpleTypedefs(self) -> None: + if not self.parser.typedefs: + return + for td in self.parser.typedefs: + AliasName: str = td['name'] + BaseType: str = td['base'] + PyType: str = CTypeMapper.MapType(BaseType) + self.OutputLines.append(f'{AliasName}: t.CTypedef | {PyType}') + self.OutputLines.append('') + + def _GenerateStructs(self) -> None: + if not self.parser.structs: + return + for StructName, StructInfo in self.parser.structs.items(): + self.OutputLines.append(f'@c.Attribute(t.attr.packed)') + self.OutputLines.append(f'class {StructName}(t.CStruct):') + if StructInfo['members']: + for member in StructInfo['members']: + MemberName: str = member['name'] + IsPtr: bool = member.get('IsPtr', False) + IsArray: bool = member.get('is_array', False) + ArraySize: int | None = member.get('array_size') + PyType: str = CTypeMapper.MapType(member['type'], IsPtr=IsPtr) + if IsArray and ArraySize is not None: + self.OutputLines.append(f' {MemberName}: list[{PyType}, {ArraySize}]') + elif IsArray: + self.OutputLines.append(f' {MemberName}: {PyType} | t.CArrayPtr') + else: + self.OutputLines.append(f' {MemberName}: {PyType}') + else: + self.OutputLines.append(' pass') + self.OutputLines.append('') + + def _GenerateUnions(self) -> None: + if not self.parser.unions: + return + for UnionName, UnionInfo in self.parser.unions.items(): + self.OutputLines.append(f'@c.Attribute(t.attr.packed)') + self.OutputLines.append(f'class {UnionName}(t.CUnion):') + if UnionInfo['members']: + for member in UnionInfo['members']: + PyType: str = CTypeMapper.MapType(member['type'], IsPtr=member.get('IsPtr', False)) + self.OutputLines.append(f' {member["name"]}: {PyType}') + else: + self.OutputLines.append(' pass') + self.OutputLines.append('') + + def _GenerateEnums(self) -> None: + if not self.parser.enums: + return + for EnumName, EnumInfo in self.parser.enums.items(): + self.OutputLines.append(f'class __{EnumName.lower()}(t.CEnum):') + if EnumInfo['values']: + for value in EnumInfo['values']: + if value['value'] is not None: + self.OutputLines.append(f' {value["name"]}: t.CDefine = {value["value"]}') + else: + self.OutputLines.append(f' {value["name"]}: t.CDefine') + else: + self.OutputLines.append(' pass') + self.OutputLines.append(f'{EnumName}: t.CTypedef = __{EnumName.lower()}') + self.OutputLines.append('') + + def _GenerateVariables(self) -> None: + if not self.parser.variables: + return + for VarName, VarInfo in self.parser.variables.items(): + PyType: str = CTypeMapper.MapType(VarInfo['type'], IsPtr=VarInfo['IsPtr'], IsArray=VarInfo['is_array']) + self.OutputLines.append(f'{VarName}: {PyType}') + self.OutputLines.append('') + + def _GenerateFunctions(self) -> None: + if not self.parser.functions: + return + for FuncName, FuncInfo in self.parser.functions.items(): + params: list[str] = [] + for param in FuncInfo['params']: + if param.get('is_func_ptr'): + PyType: str = CTypeMapper.MapType(param['type']) + elif param.get('is_array'): + PyType: str = CTypeMapper.MapType(param['type'], IsPtr=True) + else: + PyType: str = CTypeMapper.MapType(param['type']) + params.append(f'{param["name"]}: {PyType}') + ParamStr: str = ', '.join(params) if params else '' + ReturnType: str = CTypeMapper.MapType(FuncInfo['return_type']) + self.OutputLines.append(f'def {FuncName}({ParamStr}) -> {ReturnType}: pass') + self.OutputLines.append('') + + +def GenerateStubFromC(InputFile: str, OutputFile: str | None = None) -> str: + parser: CHeaderParser = CHeaderParser() + parser.ParseFile(InputFile) + generator: PythonStubGenerator = PythonStubGenerator(parser) + ModuleName: str = os.path.splitext(os.path.basename(InputFile))[0] + content: str = generator.generate(ModuleName) + if OutputFile: + with open(OutputFile, 'w', encoding='utf-8') as f: + f.write(content) + _vlog().success(f"生成: {OutputFile}") + return content + + +if __name__ == '__main__': + if len(sys.argv) >= 3: + GenerateStubFromC(sys.argv[1], sys.argv[2]) + elif len(sys.argv) == 2: + _vlog().info(GenerateStubFromC(sys.argv[1])) + else: + test_code: str = ''' +struct memory_block { + uint64_t size; + uint8_t state; + uint8_t order; + struct memory_block *next; + struct memory_block *prev; +}; + +typedef struct memory_block memory_block_t; + +enum color_format { + COLOR_FORMAT_BGRA, + COLOR_FORMAT_RGBA, + COLOR_FORMAT_BGR, + COLOR_FORMAT_RGB +}; + +void *malloc(size_t size); +void free(void *ptr); +void *memcpy(void *dest, const void *src, size_t n); +''' + parser: CHeaderParser = CHeaderParser() + parser.ParseContent(test_code) + generator: PythonStubGenerator = PythonStubGenerator(parser) + _vlog().info(generator.generate("test_header")) diff --git a/App/lib/core/translator.py b/App/lib/core/translator.py new file mode 100644 index 0000000..219684d --- /dev/null +++ b/App/lib/core/translator.py @@ -0,0 +1,34 @@ +# 核心转换逻辑 +# 通过 Mixin 模式组合各功能模块 +# +# 拆分后的模块位于 lib/core/Translator/ 目录下: +# - BaseTranslator.py: __init__、日志、Handle 实例化 +# - AnnotationLoader.py: 注解模块加载 +# - PythonParser.py: Python 文件解析 +# - LlvmGenerator.py: LLVM IR 生成 +# - ConstEval.py: 常量表达式求值 +from __future__ import annotations + +from lib.core.Translator.BaseTranslator import BaseTranslatorMixin, _StrictLog +from lib.core.Translator.AnnotationLoader import AnnotationLoaderMixin +from lib.core.Translator.PythonParser import PythonParserMixin +from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin +from lib.core.Translator.ConstEval import ConstEvalMixin + + +class Translator( + BaseTranslatorMixin, + AnnotationLoaderMixin, + PythonParserMixin, + LlvmGeneratorMixin, + ConstEvalMixin, +): + """代码转换器 + + 通过 Mixin 模式组合各功能模块。 + 实际方法实现分布在 lib/core/Translator/ 下的各模块中。 + """ + pass + + +__all__: list[str] = ['Translator', '_StrictLog'] diff --git a/App/lib/includes/__init__.py b/App/lib/includes/__init__.py new file mode 100644 index 0000000..6129c5d --- /dev/null +++ b/App/lib/includes/__init__.py @@ -0,0 +1 @@ +"""类型系统与 C 语法定义 (t.py / c.py)""" diff --git a/App/lib/utils/__init__.py b/App/lib/utils/__init__.py new file mode 100644 index 0000000..f68d323 --- /dev/null +++ b/App/lib/utils/__init__.py @@ -0,0 +1 @@ +"""通用工具函数""" diff --git a/App/lib/utils/helpers.py b/App/lib/utils/helpers.py new file mode 100644 index 0000000..225a19a --- /dev/null +++ b/App/lib/utils/helpers.py @@ -0,0 +1,47 @@ +# 工具函数 +from __future__ import annotations + +import os +import subprocess + +from lib.core.VLogger import get_logger as _vlog + + +def DetectFileType(FilePath: str) -> str: + """检测文件类型""" + _: str + ext: str + _, ext = os.path.splitext(FilePath) + return ext.lower() + + +def ExecuteCommand(command: list[str], shell: bool = False, CaptureOutput: bool = True, text: bool = True) -> subprocess.CompletedProcess[str] | None: + """执行命令 + command 应该是列表形式(如 ['gcc', 'file.c', '-o', 'file.exe']) + 避免字符串拼接以防止 shell 注入""" + try: + result: subprocess.CompletedProcess[str] = subprocess.run( + command, + shell=shell, + check=True, + capture_output=CaptureOutput, + text=text + ) + return result + except subprocess.CalledProcessError as e: + _vlog().error(f'编译失败: {e}') + if e.stderr: + _vlog().error(f'错误输出: {e.stderr}') + return None + + +def AppendFileContent(FilePath: str, content: str) -> bool: + """追加文件内容""" + try: + with open(FilePath, 'a', encoding='utf-8') as f: + f.write(content) + f.write('\n') + return True + except Exception as e: + _vlog().error(f'写入文件失败 {FilePath}: {e}') + return False diff --git a/App/main.py b/App/main.py index ee17071..a3c08ed 100644 --- a/App/main.py +++ b/App/main.py @@ -249,11 +249,7 @@ def main() -> int: ph1_temp: str = Config.TempDir if ph1_temp is None: ph1_temp = "." - stdio.printf("[DBG MAIN] before Phase1: Inc=%s temp=%s\n", Config.IncludesDir, ph1_temp) - stdio.fflush(0) Phase1.RunPhase1(mb, Config.IncludesDir, ph1_temp, log) - stdio.printf("[DBG MAIN] after Phase1\n") - stdio.fflush(0) # 如果仅 Phase1(不执行 Phase2),直接退出 if do_phase2 == 0: stdio.printf("[phase] Phase1 完成,退出\n") diff --git a/Test/build.log b/Test/build.log new file mode 100644 index 0000000000000000000000000000000000000000..6aa0c89a7721168c9f2a2dacd57f46d38ee4dee0 GIT binary patch literal 21434 zcmeHP-EQ2*74CI`zQgn?4pMH(8InUT&;o_i76A%3sFUVG5`_7q#F|TPmt4uZQ~Kw) zQJn;_B^QlbB(dY5XcM)K?bdRfcPX^1)m2}i!(GYpkPNVH-lkb8O-;J8&__wvLQVu?PY?fG%YYGlQ|r zQCls{Ode1sXh0A#g$^F^d0-;UeG7Z?XM%SQKLoSDWWqLG7a>Tr^g!(TTnGnYWO~#z z$F_Z~`Dqwv7-$%17-$%17-$%17-$%17-$%17-$%17-$%17-$%17-$%17-$%17-$%1 z7-$%17-$%17-$%17-$%17-$%17-$%17-$%182GQ!mj>1tCARmaLT>x~_#de%uFX^N}vwIX8KN4mZVRR>GD$y%gqw(44G z)(hj6DP}3C`qq)WW}8*l$~L-UnQA*GRGlkV$yNosqT6uQ#iZeVraLB}>R@YOFYA+N znTK*zN+UEaK&q=vH6tL^+w#UQcSsm3lfh|a2n$Gcv?S@4%{@)<;H$mS=kEoa(}N0QH5@5(qG#1JSDu?;$()e#$zd)~ z%TZW}-DV}LBRZ@78M^Rr=ZoQud;3rCj2>*a!8(GA>tT!_OX4(%#Pp3k1CNrlHW#iWS1VJ)dq@HDR3R_n_ciFX#E;D3b@MJwTR^ooE8?shHf2O5r@bT{O z_KnH#mdfP#))|yXW}YH^9$^5SDXTNFoWjp!1(n^MGvqnMrO}%~|8~t5L4bvN=L7 z_5&Oc9#}ORC`WW*l0k4h$()1`cV=pWGg7ZMRx2MadiA`^lwO zF8y#^FPcB+HIxA4DUcyCuYkebzm2}Q*9M~3OL}eaBl$nMK!TMhp(IK{o&#|3!pbsB z`f>JxoQ^g+G>Jf`A7x=T+5j?rlcw=+N~;Ec_~YT`rnGwSyITiu|3#<8gMU4g4Vios zOqiQYeBi=*(z_xfs_%F&D($)a`o)XizkGSzo}=eUlT5+nkVihUU6=dzr1J83H%6|^ zX9}KLvZ*787?f40zCaG)hz3xpL#@(SSwveYD5xc;IEIDia6j<$HCC0(R%xuP-K`Qd zae%q3`z3*gT??v3zg2>#R{rXAhvP6xsUz27Th3XKX_Mmyj*vz|S+XnVaZBG+Y%?Oi zZ@Y{zXqi}Bqh~_7Mq%y-$ib9b+Je68Gsp8yyA_Z%Oa zHVRDcK)HM)tQJmJ^`pjTl@|n5naUU<9Lv?2d7g=& ztL<_O`2iA+EiWXHR@-X%e3eFyT^Bm08OVN=rT_XaC$Q~dQ&570%W>hNfhvu{G)-T| z15gexm%2#1jeKMuL>@^5ZVD^6nmIWAsE=Q-`V5`2JIwB|b}nj^h)VjvVb{DC=uM{+aaQM|TEyw+D~kKi+rn@-U}LRNhN5*YwDk zYbs&PHCHg^nhh9p4fu_@#`DHpV|8PWp*WCfejXOL0gFhy^3qGdcq96jVUS230vSgE zt1!j^YnX*{7tKg!-?&>O-EK=<6*Uk_AyPghXWmI%ECs;z;7Yfw)PyTu98(jn7zx*d zE9C(7;7U<*O}JvBQ4g*ZVb_Bz#ohJbO3`>dxKfN>53Urs*MuvE<@Ml7=|N4nV(ecN zt{B+Yg)1Z&b>RxxLp`|CRx>Oij4rWoGr@N{L8KxMD(753ZE$ z)PpOfGBx3fDOF9lV!l)pu9!vDge&G&+Hl7wPAwY7q_?p+-La5f&{xzk-NDLiI;J~M znN7!Z=P0x3nC<{&HXYNQn#`tSx^t4*bWC?R0*t7WuCZPwANrF&-E6fd2+u!}M+xiV zs(g}L`LT%P-h&aXFm|$S;8twRPu{Pn@p8p+j;5$9Y;feKAq#`M_XoT0ECC382q75! zb!YV3UsOvi46ffh_+)PhjCOxH`uvuBM`ZBm?+5qy4u5|aTo@m(s(QC^PxolL$GiJxk`QoW zaS>n>2^4Whkpfl{OoAQrUn0+V_bGoNRXvyOo~^OFzHRbUmh2sAUFTGt`l|Z-PS#)#6X0(x}k3OzAFY5^Kr5gX&mKr5gX&mKr8TnQ(*A~7ui!%wyd9vr@>6Vxi)?C zolPT6qV0Y^$&#|)-xwYoKmA48);`geS+itP(! zk`@mKK_@_}PMpn1I;N~RPjHsC>5O{%W4<&&y6!uCVAG>%atOw3;kcSay-f!9hAehi~i?U z#PXzFYZv}cjdz|Zie#tRJ1L^QW(BOw=+ikpXbe>jwq_liFUQgIY_z&9%O2{rWw(od z$@t`O&EOy4q$!HLQM)jID$69@2xJpyuZi*%nP14WG$%N1NF7Q6+lBqv!7v|{ji5*J zt7*HSe||;3HDGH(zFN~(eQ;~qw+r)^4u*~GR1W5}i|}XpcG~Ef>``!A&YB2cna49J zE{bPZV@H)fk8aCp7w)2WXK>mG3@PCKA8#Li`|kA0?ZYcKrZ;YF8kW0Z7Ryvh(uV); z=)+snZ@#RO&XEwJl6l_#%zr;p^kfeAL*? zPv6VoC?~9FZZoHzq2eGTT@Jm-$)!d@0UkF`O?KB6D;$%c=#33Zx%tcG-%7bl}GyOo_ntlD<{ zYLREsB3@1YlZZn+;ujzIESyB#8zp5&h-rFxka9FW@<^19-NVN7`lS;AYTZ5Qwty$9 zhC0aUxTEV<4YlO?UN_g68t7%5j(-AFHcH}CTx4r2y(mXX)-Zq6W~dHza0>ZqI6Q^e z9iL)HtgS^{l-v9-9`AP&*#fS!%uawYo_L=^w3U&jY`>$eLG*2aOS0RUY#nZ}qH{$# zSgAXqIfpz)Ekl>FL`OcSM+wW@y0MA8+r7q zGE=|L3zJzDA=pSrU4)V42C*NzIQS{pX61!o^Wxa{+|YDdLD|*?&q%cWm_@>#}_csR}#i3V#p%cBE*imh_iY~FB4?J zMa>{&wu>#7c(KR2(523`FggU4=LLRfGLM8TrV;KsvX)tx;EG)SW)p&9Com&QBd_gr z%kJVy=4+8E3o4V?mK_Ebu>zC%anvzYJxQuY77))PK^!B*qQGw*94$vy%NGS3jxcig z>uZ9*#x$f|>)7uv6MMmCBNK<>8taE{h{L#JDf|7Ut5UIaCp7ussNFgX zYpEiyuV&@9;6jeW7ntpt2nA8ZZ@{oY5`Bbx^yHanlJ#$sm7wTT(nPUr>5LEUd1OLtI=%t0;+ z{D5y!$cvHFb!hba3x7|LS#E4mMol|*Fm|KX*?G%2?DuPis^H@<5aTMkmhTbD_ej6v zVB|U3!V(p1wuSwe#QZYj#=gt%6L%Z>{r=L`6l`(G9FN(AdNJk|Pxoxcp0}V25l?ag zpW@KM7*pK+;1=epVB_mLBFJ-M%C{_+zh@H)$G~KrY<3oPZ%Y!)p2N6gvVxKHAK%R ze!#-mvMoxO(=lUF-9HdSk&F3m>6m;|Vbt-u&d95w&F}xu#ud8`zix*pLYP|-U5~47 zx(Ys}s%#vQm7Q_!(1Q}Fme&(TL$ z4!?YD_P>q|J6VixO@9CS;cM@6fAO;`)9?N$EJmE};e#T{#Iaao^2WcXAKd)!oomyp zUmbpO?eP1rr(b_E`S`}H*}Xd-POe?w*w{EGksXiZXKWTUQh?csq+<+zUhn*cGiRPX zf4+Ku==`&Ig{Wan-g$HKuUoTD%myfFz4Gc&j(qgqU2b$QZB%O}822;hN1z(yE6taU zk3Ri)^7-}25AQ9`A0S8IJg6;y>nYe^l^t8`felnS8q^?_quD?cQI6&TeL^`J)R2>- zK|MD)8q`&jqd|QyIU3a2lA}S*D>)j}o|2mCAIxTC^fM$vu4eFQ3(V&iq91ZGt$kCvdh8zuQSjf?! zhJ+jqYB8OB3FA;JEnysL1t*L{tqg^6s1>I$4z-dM#-Uco!Z_5*TNsC0(F@~H zD}`YkY6UWkL#=Fvai|s3Fb=g68_1zlc*8i<%5fNnT9FRpP%GVG9BKtUkVC1=2XZJC z|1b_UO8~~9W)#3U)a(NohnlGX<5062U>s@&1dKz?mVj}nnH4Y&H46jAp=NBrIMnP8 z7>Ak(0^?A#MqnIjh6#*A%|?N7sF^D;4mHaK#-V1!z&Oawym$xq(hc|zF`0WS$F)5Sp|91bY+ed%;+;~iU PjkqVi+A)0m(MSFV#PLn6 literal 0 HcmV?d00001