From 03d0bba5340a975a6101da7ada8b30b321fa50f6 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sun, 26 Jul 2026 20:33:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BA=86=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/lib/Projectrans/Config.py | 120 +- App/lib/Projectrans/Utils.py | 21 +- App/lib/StubGen/Converter.py | 88 +- App/lib/core/BuildPipeline.py | 83 +- App/lib/core/Handles/HandlesAnnAssign.py | 77 +- App/lib/core/Handles/HandlesAssign.py | 163 ++- App/lib/core/Handles/HandlesAugAssign.py | 77 +- App/lib/core/Handles/HandlesClassDef.py | 1008 ++++++++++++-- App/lib/core/Handles/HandlesEnum.py | 12 +- App/lib/core/Handles/HandlesExpr.py | 1464 ++++++++++++++++++++- App/lib/core/Handles/HandlesExprCall.py | 942 ++++++++++++- App/lib/core/Handles/HandlesExprOps.py | 2 + App/lib/core/Handles/HandlesFor.py | 6 +- App/lib/core/Handles/HandlesFunctions.py | 81 +- App/lib/core/Handles/HandlesImports.py | 206 ++- App/lib/core/Handles/HandlesMain.py | 36 +- App/lib/core/Handles/HandlesReturn.py | 22 +- App/lib/core/Handles/HandlesStruct.py | 242 +++- App/lib/core/Handles/HandlesTranslator.py | 20 +- App/lib/core/Handles/HandlesType.py | 191 ++- App/lib/core/Handles/HandlesVar.py | 45 +- App/lib/core/IncludesScanner.py | 6 +- App/lib/core/Phase1.py | 613 ++++++++- App/lib/core/Phase2.py | 579 ++++---- App/lib/core/StubMerger.py | 610 ++++++--- App/lib/core/VLogger.py | 153 ++- App/main.py | 163 ++- Test/NegativeTest/project.vpj | 2 +- Test/Sha1Test/project.vpj | 2 +- Test/project.vpj | 2 +- _build.log | 1 - build.log | Bin 38111 -> 0 bytes project.json | 5 +- stderr.log | 0 stderr2.log | 0 stdout.log | 0 stdout2.log | 0 test_crash_dbg.log | 483 +++++++ test_stderr.log | 0 test_stdout.log | 241 ++++ 40 files changed, 6939 insertions(+), 827 deletions(-) delete mode 100644 _build.log delete mode 100644 build.log create mode 100644 stderr.log create mode 100644 stderr2.log create mode 100644 stdout.log create mode 100644 stdout2.log create mode 100644 test_crash_dbg.log create mode 100644 test_stderr.log create mode 100644 test_stdout.log diff --git a/App/lib/Projectrans/Config.py b/App/lib/Projectrans/Config.py index 95f495e..b7d9bbe 100644 --- a/App/lib/Projectrans/Config.py +++ b/App/lib/Projectrans/Config.py @@ -3,7 +3,9 @@ from stdint import * import memhub import string import stdio +import stdlib import viperlib +import lib.core.VLogger as VLogger from json.__parser import parse as json_parse from json import JsonValue, JSON_OBJECT, JSON_INT import w32.fileio as fileio @@ -50,86 +52,132 @@ def Load_project_config(path: str) -> int: global IncludesDir if path is None: + stdio.printf("[CFG] FAIL: path is None\n") + stdio.fflush(0) return 1 if _mbuddy is None: + stdio.printf("[CFG] FAIL: _mbuddy is None\n") + stdio.fflush(0) return 1 # 打开文件 + stdio.printf("[CFG] before File ctor path=%s\n", path) + stdio.fflush(0) f: fileio.File | t.CPtr = fileio.File(path, fileio.MODE.R) + f_closed_flag: int = 1 + if f is not None: + if f.closed: + f_closed_flag = 1 + else: + f_closed_flag = 0 + stdio.printf("[CFG] after File ctor f=%p closed=%d\n", f, f_closed_flag) + stdio.fflush(0) + if f is None: + stdio.printf("[CFG] FAIL: File ctor returned None\n") + stdio.fflush(0) + return 1 if f.closed: + stdio.printf("[CFG] FAIL: f.closed is True\n") + stdio.fflush(0) return 1 # 分配读取缓冲区 CFG_BUF_SIZE: t.CSizeT = 8192 buf: bytes = _mbuddy.alloc(CFG_BUF_SIZE) if buf is None: + stdio.printf("[CFG] FAIL: alloc buf failed\n") + stdio.fflush(0) f.close() return 1 + stdio.printf("[CFG] before read_all\n") + stdio.fflush(0) bytes_read: t.CInt64T = f.read_all(buf, CFG_BUF_SIZE) f.close() + stdio.printf("[CFG] after read_all bytes_read=%d\n", bytes_read) + stdio.fflush(0) if bytes_read <= 0: + stdio.printf("[CFG] FAIL: bytes_read <= 0\n") + stdio.fflush(0) return 1 # 解析 JSON + stdio.printf("[CFG] before json_parse\n") + stdio.fflush(0) root: JsonValue | t.CPtr = json_parse(_mbuddy, buf) + stdio.printf("[CFG] after json_parse root=%p\n", root) + stdio.fflush(0) if root is None: + stdio.printf("[CFG] FAIL: json_parse returned None\n") + stdio.fflush(0) return 1 if not root.is_object(): + stdio.printf("[CFG] FAIL: root is not object\n") + stdio.fflush(0) return 1 + stdio.printf("[CFG] root is object OK\n") + stdio.fflush(0) - # 读取顶层字段 - SourceDir = root["source_dir"].as_string() if root["source_dir"] is not None else None + # 读取顶层字段(使用显式 __getitem__ 调用,兼容旧编译器二进制) + sd_val: JsonValue | t.CPtr = root.__getitem__("source_dir") + SourceDir = sd_val.as_string() if sd_val is not None else None # build_dir: 统一构建目录(默认 ./.tpv_build),temp 和 output 作为其子目录 - bd_val: JsonValue | t.CPtr = root["build_dir"] + bd_val: JsonValue | t.CPtr = root.__getitem__("build_dir") if bd_val is not None and bd_val.is_string(): BuildDir = bd_val.as_string() else: BuildDir = "./.tpv_build" - ProjectName = root["name"].as_string() if root["name"] is not None else None - ProjectVersion = root["version"].as_string() if root["version"] is not None else None + name_val: JsonValue | t.CPtr = root.__getitem__("name") + ProjectName = name_val.as_string() if name_val is not None else None + ver_val: JsonValue | t.CPtr = root.__getitem__("version") + ProjectVersion = ver_val.as_string() if ver_val is not None else None # 读取 compiler 子对象 - compiler: JsonValue | t.CPtr = root["compiler"] + compiler: JsonValue | t.CPtr = root.__getitem__("compiler") if compiler is not None and compiler.is_object(): - CompilerCmd = compiler["cmd"].as_string() if compiler["cmd"] is not None else None + cc_cmd_val: JsonValue | t.CPtr = compiler.__getitem__("cmd") + CompilerCmd = cc_cmd_val.as_string() if cc_cmd_val is not None else None # 读取 compiler.flags 数组,拼接成空格分隔的字符串 - cc_flags_val: JsonValue | t.CPtr = compiler["flags"] + cc_flags_val: JsonValue | t.CPtr = compiler.__getitem__("flags") CompilerFlags = _join_flags_array(cc_flags_val) # 读取 linker 子对象 - linker: JsonValue | t.CPtr = root["linker"] + linker: JsonValue | t.CPtr = root.__getitem__("linker") if linker is not None and linker.is_object(): - LinkerCmd = linker["cmd"].as_string() if linker["cmd"] is not None else None - LinkerOutput = linker["output"].as_string() if linker["output"] is not None else None + ld_cmd_val: JsonValue | t.CPtr = linker.__getitem__("cmd") + LinkerCmd = ld_cmd_val.as_string() if ld_cmd_val is not None else None + ld_out_val: JsonValue | t.CPtr = linker.__getitem__("output") + LinkerOutput = ld_out_val.as_string() if ld_out_val is not None else None # 读取 linker.flags 数组,拼接成空格分隔的字符串 - ld_flags_val: JsonValue | t.CPtr = linker["flags"] + ld_flags_val: JsonValue | t.CPtr = linker.__getitem__("flags") LinkerFlags = _join_flags_array(ld_flags_val) # 读取 target 子对象 - target: JsonValue | t.CPtr = root["target"] + target: JsonValue | t.CPtr = root.__getitem__("target") if target is not None and target.is_object(): - TargetTriple = target["triple"].as_string() if target["triple"] is not None else None - TargetDataLayout = target["datalayout"].as_string() if target["datalayout"] is not None else None + tgt_triple_val: JsonValue | t.CPtr = target.__getitem__("triple") + TargetTriple = tgt_triple_val.as_string() if tgt_triple_val is not None else None + tgt_dl_val: JsonValue | t.CPtr = target.__getitem__("datalayout") + TargetDataLayout = tgt_dl_val.as_string() if tgt_dl_val is not None else None # 读取 options 子对象 # Sha1SliceLevel: SHA1 切片层数(默认 1,每层取 SHA1 前 2 字符作为子目录) # level=0 → 不切片,文件直接放 base_dir # level=1 → b7/{sha1}.ext # level=2 → b7/90/{sha1}.ext - options: JsonValue | t.CPtr = root["options"] + options: JsonValue | t.CPtr = root.__getitem__("options") Sha1SliceLevel = 1 StrictMode = 1 if options is not None and options.is_object(): - sl: JsonValue | t.CPtr = options["sha1_slice_level"] + sl: JsonValue | t.CPtr = options.__getitem__("sha1_slice_level") if sl is not None and sl.is_int(): Sha1SliceLevel = sl.as_int() - sm: JsonValue | t.CPtr = options["strict_mode"] + sm: JsonValue | t.CPtr = options.__getitem__("strict_mode") if sm is not None and sm.is_bool(): StrictMode = 1 if sm.as_bool() else 0 # 读取 includes 数组(取首个元素作为 includes 目录) - includes_val: JsonValue | t.CPtr = root["includes"] + includes_val: JsonValue | t.CPtr = root.__getitem__("includes") if includes_val is not None and includes_val.is_array(): arr_len: t.CSizeT = includes_val.__len__() if arr_len > 0: @@ -307,24 +355,38 @@ def get_includes_binary_dir() -> str: def print_config(): - """打印当前配置(调试用)""" + """打印当前配置""" + buf: t.CChar | t.CPtr = VLogger.fmt_buf() + if buf is None: + return if ProjectName is not None: - stdio.printf(" project: %s v%s\n", ProjectName, ProjectVersion if ProjectVersion is not None else "?") + viperlib.snprintf(buf, 1024, "project: %s v%s", ProjectName, + ProjectVersion if ProjectVersion is not None else "?") + VLogger.info(buf, "config") if SourceDir is not None: - stdio.printf(" source_dir: %s\n", SourceDir) + viperlib.snprintf(buf, 1024, "source_dir: %s", SourceDir) + VLogger.info(buf, "config") if BuildDir is not None: - stdio.printf(" build_dir: %s\n", BuildDir) + viperlib.snprintf(buf, 1024, "build_dir: %s", BuildDir) + VLogger.info(buf, "config") if TempDir is not None: - stdio.printf(" temp_dir: %s\n", TempDir) + viperlib.snprintf(buf, 1024, "temp_dir: %s", TempDir) + VLogger.info(buf, "config") if OutputDir is not None: - stdio.printf(" output_dir: %s\n", OutputDir) + viperlib.snprintf(buf, 1024, "output_dir: %s", OutputDir) + VLogger.info(buf, "config") if CompilerCmd is not None: - stdio.printf(" compiler: %s\n", CompilerCmd) + viperlib.snprintf(buf, 1024, "compiler: %s", CompilerCmd) + VLogger.info(buf, "config") if LinkerCmd is not None: - stdio.printf(" linker: %s -> %s\n", LinkerCmd, LinkerOutput if LinkerOutput is not None else "?") + viperlib.snprintf(buf, 1024, "linker: %s -> %s", LinkerCmd, + LinkerOutput if LinkerOutput is not None else "?") + VLogger.info(buf, "config") if IncludesDir is not None: - stdio.printf(" includes: %s\n", IncludesDir) - stdio.printf(" sha1_slice_level: %d\n", Sha1SliceLevel) + viperlib.snprintf(buf, 1024, "includes: %s", IncludesDir) + VLogger.info(buf, "config") + viperlib.snprintf(buf, 1024, "sha1_slice_level: %d", Sha1SliceLevel) + VLogger.info(buf, "config") # ============================================================ diff --git a/App/lib/Projectrans/Utils.py b/App/lib/Projectrans/Utils.py index e0e25a1..ca04dca 100644 --- a/App/lib/Projectrans/Utils.py +++ b/App/lib/Projectrans/Utils.py @@ -63,12 +63,14 @@ def compute_sha1(pool: memhub.MemBuddy | t.CPtr, content: str) -> str: # ============================================================ -# CleanDir - 删除目录中所有文件(非递归,保留目录本身) +# CleanDir - 递归删除目录中所有文件和子目录(保留 dir_path 本身) # # 用于 --clean 选项:清理 temp_dir 和 output_dir 中的旧文件。 +# SHA1 分片目录结构(如 temp/1c/、temp/7a/)需要递归清理, +# 否则旧的截断文件(如 256KB text.ll)会被 Phase1 跳过逻辑复用。 # ============================================================ def CleanDir(dir_path: str) -> int: - """删除目录中所有文件(非递归),返回删除的文件数,-1 表示错误""" + """递归删除目录中所有文件和子目录,保留 dir_path 本身,返回删除的文件数,-1 表示错误""" if dir_path is None: return -1 dir_len: t.CSizeT = string.strlen(dir_path) @@ -104,8 +106,19 @@ def CleanDir(dir_path: str) -> int: full_path: bytes = stdlib.malloc(dir_len + fname_len + 2) if full_path is not None: viperlib.snprintf(full_path, dir_len + fname_len + 2, "%s/%s", dir_path, fname) - if w32.win32file.DeleteFileA(full_path) != 0: - deleted += 1 + # 检查是否为目录(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + is_dir: t.CUInt32T = find_data.dwFileAttributes & w32.win32file.FILE_ATTRIBUTE_DIRECTORY + if is_dir != 0: + # 递归清理子目录中的文件 + sub_deleted: int = CleanDir(full_path) + if sub_deleted > 0: + deleted += sub_deleted + # 删除空子目录(RemoveDirectoryA 要求目录为空) + w32.win32file.RemoveDirectoryA(full_path) + else: + # 删除文件 + if w32.win32file.DeleteFileA(full_path) != 0: + deleted += 1 stdlib.free(full_path) if w32.win32file.FindNextFileA(handle, find_data) == 0: diff --git a/App/lib/StubGen/Converter.py b/App/lib/StubGen/Converter.py index 7b18c69..33db5ca 100644 --- a/App/lib/StubGen/Converter.py +++ b/App/lib/StubGen/Converter.py @@ -3,6 +3,7 @@ from stdint import * import ast import string import memhub +import viperlib # ============================================================ @@ -117,12 +118,13 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, if k == ast.ASTKind.Tuple: tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(annot) if tup.elts is not None: - tn: t.CSizeT = tup.elts.__len__() + tup_elts: list[ast.AST | t.CPtr] | t.CPtr = tup.elts + tn: t.CSizeT = tup_elts.__len__() ti: t.CSizeT = 0 while ti < tn: if ti > 0: pos = _PyiEmit(buf, buf_size, pos, ", ") - el: ast.AST | t.CPtr = tup.elts.get(ti) + el: ast.AST | t.CPtr = tup_elts.get(ti) pos = _PyiTypeStr(buf, buf_size, pos, el) ti += 1 return pos @@ -133,12 +135,13 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, pos = _PyiTypeStr(buf, buf_size, pos, cl.func) pos = _PyiEmit(buf, buf_size, pos, "(") if cl.args is not None: - can: t.CSizeT = cl.args.__len__() + cl_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args + can: t.CSizeT = cl_args.__len__() cai: t.CSizeT = 0 while cai < can: if cai > 0: pos = _PyiEmit(buf, buf_size, pos, ", ") - ca: ast.AST | t.CPtr = cl.args.get(cai) + ca: ast.AST | t.CPtr = cl_args.get(cai) pos = _PyiTypeStr(buf, buf_size, pos, ca) cai += 1 pos = _PyiEmit(buf, buf_size, pos, ")") @@ -159,10 +162,11 @@ def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, """生成函数签名到 buf,返回新的 pos""" # 装饰器 if fn.decorator_list is not None: - dn: t.CSizeT = fn.decorator_list.__len__() + fn_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = fn.decorator_list + dn: t.CSizeT = fn_decorator_list.__len__() di: t.CSizeT = 0 while di < dn: - dec: ast.AST | t.CPtr = fn.decorator_list.get(di) + dec: ast.AST | t.CPtr = fn_decorator_list.get(di) if dec is not None: pos = _PyiIndent(buf, buf_size, pos, indent_level) pos = _PyiEmit(buf, buf_size, pos, "@") @@ -184,13 +188,14 @@ def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # 位置参数 if args.args is not None: - an: t.CSizeT = args.args.__len__() + args_args: list[ast.AST | t.CPtr] | t.CPtr = args.args + an: t.CSizeT = args_args.__len__() ai: t.CSizeT = 0 while ai < an: if first == 0: pos = _PyiEmit(buf, buf_size, pos, ", ") first = 0 - ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(args.args.get(ai)) + ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(args_args.get(ai)) if ag.arg is not None: pos = _PyiEmit(buf, buf_size, pos, ag.arg) if ag.annotation is not None: @@ -242,10 +247,11 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, """生成类定义到 buf,返回新的 pos""" # 装饰器 if cls.decorator_list is not None: - dcn: t.CSizeT = cls.decorator_list.__len__() + cls_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = cls.decorator_list + dcn: t.CSizeT = cls_decorator_list.__len__() dci: t.CSizeT = 0 while dci < dcn: - ddec: ast.AST | t.CPtr = cls.decorator_list.get(dci) + ddec: ast.AST | t.CPtr = cls_decorator_list.get(dci) if ddec is not None: pos = _PyiIndent(buf, buf_size, pos, indent_level) pos = _PyiEmit(buf, buf_size, pos, "@") @@ -260,14 +266,15 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # PEP 695 类型参数 (class Foo[T]:) if cls.type_params is not None: - tpn: t.CSizeT = cls.type_params.__len__() + cls_type_params: list[ast.AST | t.CPtr] | t.CPtr = cls.type_params + tpn: t.CSizeT = cls_type_params.__len__() if tpn > 0: pos = _PyiEmit(buf, buf_size, pos, "[") tpi: t.CSizeT = 0 while tpi < tpn: if tpi > 0: pos = _PyiEmit(buf, buf_size, pos, ", ") - tpname: str = cls.type_params.get(tpi) + tpname: str = cls_type_params.get(tpi) if tpname is not None: pos = _PyiEmit(buf, buf_size, pos, tpname) tpi += 1 @@ -275,14 +282,15 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # 基类 if cls.bases is not None: - bn: t.CSizeT = cls.bases.__len__() + cls_bases: list[ast.AST | t.CPtr] | t.CPtr = cls.bases + bn: t.CSizeT = cls_bases.__len__() if bn > 0: pos = _PyiEmit(buf, buf_size, pos, "(") bi: t.CSizeT = 0 while bi < bn: if bi > 0: pos = _PyiEmit(buf, buf_size, pos, ", ") - base: ast.AST | t.CPtr = cls.bases.get(bi) + base: ast.AST | t.CPtr = cls_bases.get(bi) pos = _PyiTypeStr(buf, buf_size, pos, base) bi += 1 pos = _PyiEmit(buf, buf_size, pos, ")") @@ -293,10 +301,11 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, has_member: int = 0 if cls.children is not None: - cn: t.CSizeT = cls.children.__len__() + cls_children: list[ast.AST | t.CPtr] | t.CPtr = cls.children + cn: t.CSizeT = cls_children.__len__() ci: t.CSizeT = 0 while ci < cn: - stmt: ast.AST | t.CPtr = cls.children.get(ci) + stmt: ast.AST | t.CPtr = cls_children.get(ci) if stmt is not None: sk: int = stmt.kind() @@ -306,10 +315,11 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT, # __init__ 方法:先提取 self.x: Type 属性 if mfn.name is not None and string.strcmp(mfn.name, "__init__") == 0: if mfn.children is not None: - icn: t.CSizeT = mfn.children.__len__() + mfn_children: list[ast.AST | t.CPtr] | t.CPtr = mfn.children + icn: t.CSizeT = mfn_children.__len__() ici: t.CSizeT = 0 while ici < icn: - ibody: ast.AST | t.CPtr = mfn.children.get(ici) + ibody: ast.AST | t.CPtr = mfn_children.get(ici) if ibody is not None and ibody.kind() == ast.ASTKind.AnnAssign: iaa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(ibody) if iaa.target is not None and iaa.target.kind() == ast.ASTKind.Attribute: @@ -391,21 +401,23 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, buf[pos] = 0 return pos - n: t.CSizeT = tree.children.__len__() + tree_children: list[ast.AST | t.CPtr] | t.CPtr = tree.children + n: t.CSizeT = tree_children.__len__() # 第一遍扫描:检测 import t/c has_import_t: int = 0 has_import_c: int = 0 i: t.CSizeT = 0 while i < n: - stmt: ast.AST | t.CPtr = tree.children.get(i) + stmt: ast.AST | t.CPtr = tree_children.get(i) if stmt is not None and stmt.kind() == ast.ASTKind.Import: imp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(stmt) if imp.names is not None: - an: t.CSizeT = imp.names.__len__() + imp_names: list[ast.AST | t.CPtr] | t.CPtr = imp.names + an: t.CSizeT = imp_names.__len__() ai: t.CSizeT = 0 while ai < an: - al: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(imp.names.get(ai)) + al: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(imp_names.get(ai)) if al is not None and al.name is not None: if string.strcmp(al.name, "t") == 0: has_import_t = 1 @@ -427,7 +439,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, # 第二遍扫描:生成声明 i = 0 while i < n: - stmt = tree.children.get(i) + stmt = tree_children.get(i) if stmt is not None: k: int = stmt.kind() @@ -436,12 +448,13 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, imp2: ast.Import | t.CPtr = (ast.Import | t.CPtr)(stmt) pos = _PyiEmit(buf, buf_size, pos, "import ") if imp2.names is not None: - an2: t.CSizeT = imp2.names.__len__() + imp2_names: list[ast.AST | t.CPtr] | t.CPtr = imp2.names + an2: t.CSizeT = imp2_names.__len__() ai2: t.CSizeT = 0 while ai2 < an2: if ai2 > 0: pos = _PyiEmit(buf, buf_size, pos, ", ") - al2: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(imp2.names.get(ai2)) + al2: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(imp2_names.get(ai2)) if al2 is not None and al2.name is not None: pos = _PyiEmit(buf, buf_size, pos, al2.name) if al2.asname is not None and string.strlen(al2.asname) > 0: @@ -462,12 +475,13 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, pos = _PyiEmit(buf, buf_size, pos, ifr.module) pos = _PyiEmit(buf, buf_size, pos, " import ") if ifr.names is not None: - an3: t.CSizeT = ifr.names.__len__() + ifr_names: list[ast.AST | t.CPtr] | t.CPtr = ifr.names + an3: t.CSizeT = ifr_names.__len__() ai3: t.CSizeT = 0 while ai3 < an3: if ai3 > 0: pos = _PyiEmit(buf, buf_size, pos, ", ") - al3: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(ifr.names.get(ai3)) + al3: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(ifr_names.get(ai3)) if al3 is not None and al3.name is not None: pos = _PyiEmit(buf, buf_size, pos, al3.name) if al3.asname is not None and string.strlen(al3.asname) > 0: @@ -496,15 +510,31 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, pos = _PyiEmit(buf, buf_size, pos, nm.id) pos = _PyiEmit(buf, buf_size, pos, ": ") pos = _PyiTypeStr(buf, buf_size, pos, aa.annotation) + # 如果有值(如 CDefine 常量),输出 = value + if aa.value is not None and aa.value.kind() == ast.ASTKind.Constant: + cv: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value) + if cv.const_kind == ast.CONST_INT: + num_buf: bytes = mb.alloc(32) + if num_buf is not None: + viperlib.snprintf(num_buf, 32, " = %d", cv.int_val) + pos = _PyiEmit(buf, buf_size, pos, num_buf) + elif cv.const_kind == ast.CONST_BOOL: + if cv.int_val != 0: + pos = _PyiEmit(buf, buf_size, pos, " = True") + else: + pos = _PyiEmit(buf, buf_size, pos, " = False") + elif cv.const_kind == ast.CONST_NONE: + pos = _PyiEmit(buf, buf_size, pos, " = None") pos = _PyiEmit(buf, buf_size, pos, "\n") # 全局赋值(无注解)— 从 Constant value 推断类型 elif k == ast.ASTKind.Assign: asg: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(stmt) if asg.targets is not None and asg.value is not None: - atn: t.CSizeT = asg.targets.__len__() + asg_targets: list[ast.AST | t.CPtr] | t.CPtr = asg.targets + atn: t.CSizeT = asg_targets.__len__() if atn == 1: - atg: ast.AST | t.CPtr = asg.targets.get(0) + atg: ast.AST | t.CPtr = asg_targets.get(0) if atg is not None and atg.kind() == ast.ASTKind.Name: agn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(atg) aval: ast.AST | t.CPtr = asg.value diff --git a/App/lib/core/BuildPipeline.py b/App/lib/core/BuildPipeline.py index c8810aa..a7c6365 100644 --- a/App/lib/core/BuildPipeline.py +++ b/App/lib/core/BuildPipeline.py @@ -10,6 +10,7 @@ import w32.win32base import subprocess import viperlib import ast +import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HandlesTranslator import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesType as HandlesType @@ -68,6 +69,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str, bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE) f.close() if bytes_read <= 0: + stdlib.free(src_buf) return None if bytes_read < SRC_BUF_SIZE: src_buf[bytes_read] = 0 @@ -77,6 +79,8 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str, ast._lexer_init(lx, src_buf, mb) tokens: ast.Token | t.CPtr = ast.tokenize(lx) tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) + # parse 完成后 AST 树已独立,释放源代码缓冲区(避免 1MB/文件泄漏) + stdlib.free(src_buf) if tree is None: return None tr: HandlesTranslator.Translator | t.CPtr = mb.alloc(HandlesTranslator.Translator.__sizeof__()) @@ -268,12 +272,27 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True) if result is None: - stdio.printf("[FATAL][LLC] subprocess.run 返回 None: %s\n", module_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "subprocess.run 返回 None: %s", module_name) + VLogger.error(fb, "LLC") return 1 if result.returncode != 0: - stdio.printf("[FATAL][LLC] 编译失败 (module=%s, cmd=%s)\n", module_name, cmd) + # 先直接输出 llc 的具体错误信息(VLogger.error 会 sys.exit,必须先输出) + # 注意: subprocess 在 Windows 下将 stderr 合并到 stdout(si.hStdError = stdout_write) + # 因此 result.stderr 总是 None,错误信息在 result.stdout 中 + # stdout 通常已含换行符,不再额外加 \n if result.stdout is not None: - stdio.printf("[LLC] 输出:\n%s\n", result.stdout) + stdio.printf("%s", result.stdout) + stdio.fflush(0) + if result.stderr is not None: + stdio.printf("%s", result.stderr) + stdio.fflush(0) + # 最后输出编译失败摘要(VLogger.error 会 sys.exit) + fb = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "编译失败 (module=%s, cmd=%s)", module_name, cmd) + VLogger.error(fb, "LLC") return 1 return 0 @@ -370,9 +389,15 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f if includes_binary_dir is not None: extra_len = collect_obj_files(includes_binary_dir, extra_objs, EXTRA_BUF_SIZE) if extra_len > 0: - stdio.printf("[link] 附加 %d 字节的 includes.binary .obj 文件\n", extra_len) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "附加 %d 字节的 includes.binary .obj 文件", extra_len) + VLogger.info(fb, "link") else: - stdio.printf("[link] 警告: includes.binary 无 .obj 文件: %s\n", includes_binary_dir) + fb = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "includes.binary 无 .obj 文件: %s", includes_binary_dir) + VLogger.warning(fb, "link") # 构造命令: clang++ {obj_path} extra_objs -o {output_dir}/{linker_output} linker_flags # 注意: .obj 文件必须在 -l 库标志之前,否则链接器无法解析符号依赖 @@ -391,16 +416,25 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True) if result is None: - stdio.printf("[link] subprocess.run 返回 None\n") + VLogger.error("subprocess.run 返回 None", "link") return 1 if result.returncode != 0: - stdio.printf("[link] 链接失败,返回码: %d\n", result.returncode) - stdio.printf("[link] 命令: %s\n", cmd) - # 显示链接器错误输出(subprocess 将 stderr 合并到 stdout) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "链接失败,返回码: %d", result.returncode) + VLogger.error(fb, "link") + fb = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "命令: %s", cmd) + VLogger.error(fb, "link") + # 显示链接器错误输出 + # 注意: subprocess 在 Windows 下将 stderr 合并到 stdout,result.stderr 总是 None if result.stdout is not None: - stdio.printf("[link] 链接器输出:\n%s\n", result.stdout) + VLogger.error(result.stdout, "link") else: - stdio.printf("[link] 无输出捕获\n") + VLogger.error("无输出捕获", "link") + if result.stderr is not None: + VLogger.error(result.stderr, "link") return 1 return 0 @@ -428,7 +462,10 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT, # Step 1: 写 .ll 文件 ret: int = write_ir_to_file(ir_buf, ir_len, temp_dir, module_name) if ret != 0: - stdio.printf("[compile] 写 .ll 失败: %s\n", module_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "写 .ll 失败: %s", module_name) + VLogger.error(fb, "compile") return 1 # Step 2: 构造切片 .ll 路径并编译 → .obj @@ -438,7 +475,10 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT, ret = compile_ll_to_obj(ir_path, output_dir, module_name, cc_cmd, cc_flags) if ret != 0: - stdio.printf("[compile] llc 编译失败: %s\n", module_name) + fb = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "llc 编译失败: %s", module_name) + VLogger.error(fb, "compile") return 1 return 0 @@ -486,13 +526,22 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT, result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True) if result is None: - stdio.printf("[link] subprocess.run 返回 None\n") + VLogger.error("subprocess.run 返回 None", "link") return 1 if result.returncode != 0: - stdio.printf("[link] 链接失败,返回码: %d\n", result.returncode) - stdio.printf("[link] 命令: %s\n", cmd) + # 先直接输出 linker 的具体错误信息(VLogger.error 会 sys.exit,必须先输出) + # stdout/stderr 通常已含换行符,不再额外加 \n if result.stdout is not None: - stdio.printf("[link] 链接器输出:\n%s\n", result.stdout) + stdio.printf("%s", result.stdout) + stdio.fflush(0) + if result.stderr is not None: + stdio.printf("%s", result.stderr) + stdio.fflush(0) + # 最后输出链接失败摘要(VLogger.error 会 sys.exit) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "链接失败,返回码: %d, 命令: %s", result.returncode, cmd) + VLogger.error(fb, "link") return 1 return 0 diff --git a/App/lib/core/Handles/HandlesAnnAssign.py b/App/lib/core/Handles/HandlesAnnAssign.py index a7a13a2..31c7bfa 100644 --- a/App/lib/core/Handles/HandlesAnnAssign.py +++ b/App/lib/core/Handles/HandlesAnnAssign.py @@ -12,6 +12,7 @@ import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesNonlocal as HandlesNonlocal import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.Handles.HandlesExprCall as HandlesExprCall +import lib.core.Handles.HandlesStruct as HandlesStruct # ============================================================ @@ -62,6 +63,64 @@ def extract_cdefine_int_value(val_node: ast.AST | t.CPtr) -> int: return cn.int_val +# ============================================================ +# _annotation_contains_name - 递归检查注解中是否包含指定名称 +# +# 支持 BinOp(BitOr) 递归,同时检查 Attribute.attr 和 Name.id。 +# ============================================================ +def _annotation_contains_name(annot: ast.AST | t.CPtr, name: str) -> int: + """递归检查注解中是否包含指定名称,返回 1=包含 / 0=不包含""" + if annot is None or name is None: + return 0 + k: int = annot.kind() + if k == ast.ASTKind.Attribute: + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot) + if at.attr is not None and string.strcmp(at.attr, name) == 0: + return 1 + return 0 + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot) + if nm.id is not None and string.strcmp(nm.id, name) == 0: + return 1 + return 0 + if k == ast.ASTKind.BinOp: + bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot) + if _annotation_contains_name(bop.left, name) != 0: + return 1 + if _annotation_contains_name(bop.right, name) != 0: + return 1 + return 0 + + +# ============================================================ +# _is_ptr_element_annotation - 检查注解是否为 bytes|t.CPtr 或 str|t.CPtr +# +# 返回 1=是(下标按 8 字节步长, i8** 语义), 0=否(下标按 1 字节步长, i8* 语义) +# +# 规则: +# bytes|t.CPtr / str|t.CPtr → i8**(8 字节步长)→ 返回 1 +# t.CChar|t.CPtr / t.CInt8T|t.CPtr → i8*(1 字节步长)→ 返回 0 +# 纯 bytes / str → i8*(1 字节步长)→ 返回 0 +# +# 注: 注解中的 "|" 是"和"的意思(组合类型),非右值中的位或运算 +# ============================================================ +def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int: + """检查注解是否为 bytes|t.CPtr 或 str|t.CPtr 形式""" + if annot is None: + return 0 + k: int = annot.kind() + if k != ast.ASTKind.BinOp: + return 0 + bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot) + if bop.op != ast.OpKind.BitOr: + return 0 + if _annotation_contains_name(annot, "str") != 0: + return 1 + if _annotation_contains_name(annot, "bytes") != 0: + return 1 + return 0 + + @t.NoVTable class AnnAssignHandle(HandlesBase.Mixin): """AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针""" @@ -121,7 +180,7 @@ class AnnAssignHandle(HandlesBase.Mixin): annot: ast.AST | t.CPtr = aa.annotation if annot is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, annot, self.Trans._imported_modules, self.Trans._from_imports) + pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans) if resolved is None: # 尝试特化泛型类注解(如 list[str]) # resolve_annotation_type 返回 None 可能是因为泛型类未特化 @@ -138,11 +197,13 @@ class AnnAssignHandle(HandlesBase.Mixin): self.Trans, sub_nm.id, type_args_ps) if spec_name_ps is not None: resolved = HandlesType.resolve_annotation_type( - pool, annot, self.Trans._imported_modules, self.Trans._from_imports) + pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans) if resolved is not None: var_ty = resolved - alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, var_ty) + # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) + HandlesStruct.ensure_struct_def_in_module(pool, self.Trans.Module, var_ty) + alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, var_ty) if alloca is not None: if HandlesVar.define_var( self.Trans.SymTab, nm.id, alloca) == 0: @@ -153,6 +214,10 @@ class AnnAssignHandle(HandlesBase.Mixin): if cls_nm_ps is not None: HandlesVar.set_var_annot_class_name( self.Trans.SymTab, nm.id, cls_nm_ps) + # 检查是否为 bytes|t.CPtr 或 str|t.CPtr(i8** 语义,8 字节步长) + if _is_ptr_element_annotation(annot) != 0: + HandlesVar.set_var_ptr_element( + self.Trans.SymTab, nm.id) return 0 return 0 @@ -193,7 +258,7 @@ class AnnAssignHandle(HandlesBase.Mixin): annot: ast.AST | t.CPtr = aa.annotation if annot is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, annot, self.Trans._imported_modules, self.Trans._from_imports) + pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans) if resolved is not None: var_ty = resolved @@ -243,6 +308,10 @@ class AnnAssignHandle(HandlesBase.Mixin): if cls_nm_hd is not None: HandlesVar.set_var_annot_class_name( self.Trans.SymTab, nm.id, cls_nm_hd) + # 检查是否为 bytes|t.CPtr 或 str|t.CPtr(i8** 语义,8 字节步长) + if _is_ptr_element_annotation(annot) != 0: + HandlesVar.set_var_ptr_element( + self.Trans.SymTab, nm.id) new_vars: int = 0 existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current( diff --git a/App/lib/core/Handles/HandlesAssign.py b/App/lib/core/Handles/HandlesAssign.py index f5d825e..f79f40a 100644 --- a/App/lib/core/Handles/HandlesAssign.py +++ b/App/lib/core/Handles/HandlesAssign.py @@ -6,6 +6,7 @@ import memhub import string import stdio import viperlib +import lib.core.VLogger as VLogger import lib.core.Handles.HandlesBase as HandlesBase import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesVar as HandlesVar @@ -44,12 +45,12 @@ class AssignHandle(HandlesBase.Mixin): def Handle(self, node: ast.AST | t.CPtr) -> int: asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node) if asgn is None: - stdio.printf("[ASGN] cast failed\n") + VLogger.error("cast failed", "ASGN") return 0 targets: list[ast.AST | t.CPtr] | t.CPtr = asgn.targets if targets is None: - stdio.printf("[ASGN] targets is None\n") + VLogger.error("targets is None", "ASGN") return 0 # 从 self.Trans 取共享状态(替代 11 个参数) @@ -61,7 +62,23 @@ class AssignHandle(HandlesBase.Mixin): rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, asgn.value, None, 0, self.Trans) if rhs_val is None: - stdio.printf("[ASGN] rhs_val is None\n") + # 增强错误信息:包含 sha1 + lineno + AST 节点类型,便于定位 + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None and asgn.value is not None: + sha1: str = self.Trans.ModuleSha1 + val_kind: int = asgn.value.kind() + val_line: t.CInt = asgn.value.lineno + if sha1 is not None: + viperlib.snprintf(fb, 1024, + "rhs_val is None [sha1=%s lineno=%d kind=%d]", + sha1, val_line, val_kind) + else: + viperlib.snprintf(fb, 1024, + "rhs_val is None [lineno=%d kind=%d]", + val_line, val_kind) + VLogger.error(fb, "ASGN") + else: + VLogger.error("rhs_val is None", "ASGN") return 0 new_vars: int = 0 @@ -116,23 +133,115 @@ class AssignHandle(HandlesBase.Mixin): sub_pointee: llvmlite.LLVMType | t.CPtr = sub_alloca.Ty.Pointee if sub_pointee is not None: cls_nm_set: str = HandlesStruct.get_class_name_by_type(pool, sub_pointee) - if cls_nm_set is not None: - obj_val_set: llvmlite.Value | t.CPtr = llvmlite.build_load( - builder, sub_pointee, sub_alloca) - if obj_val_set is not None: - key_val_set: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( - builder, pool, mod, sub_asgn.slice, None, 0, self.Trans) - if key_val_set is not None: - arg_vals_set: t.CSizeT | t.CPtr = pool.alloc(16) - if arg_vals_set is not None: - arg_vals_set[0] = t.CSizeT(key_val_set) - arg_vals_set[1] = t.CSizeT(rhs_val) - HandlesExprCall._call_method_on_ptr( - pool, builder, mod, cls_nm_set, "__setitem__", - obj_val_set, arg_vals_set, 2, self.Trans) - setitem_done = 1 + obj_val_set: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, sub_pointee, sub_alloca) + # 指针类型变量 (X|t.CPtr): sub_pointee 是 Ptr(Struct), + # 需 load 获取 Ptr(Struct) 再检查 inner struct + if cls_nm_set is None and obj_val_set is not None: + if HandlesExpr.is_ptr_type(sub_pointee) != 0: + inner_ty_set: llvmlite.LLVMType | t.CPtr = sub_pointee.Pointee + if inner_ty_set is not None: + cls_nm_set = HandlesStruct.get_class_name_by_type(pool, inner_ty_set) + if cls_nm_set is not None and obj_val_set is not None: + key_val_set: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, sub_asgn.slice, None, 0, self.Trans) + if key_val_set is not None: + arg_vals_set: t.CSizeT | t.CPtr = pool.alloc(16) + if arg_vals_set is not None: + arg_vals_set[0] = t.CSizeT(key_val_set) + arg_vals_set[1] = t.CSizeT(rhs_val) + HandlesExprCall._call_method_on_ptr( + pool, builder, mod, cls_nm_set, "__setitem__", + obj_val_set, arg_vals_set, 2, self.Trans) + setitem_done = 1 + elif sub_asgn.value.kind() == ast.ASTKind.Attribute: + # self._ht[key] = val → self._ht.__setitem__(key, val) + # 通过 get_attribute_ptr 获取字段指针,再调用 __setitem__ + field_ptr_set: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr( + builder, pool, mod, sub_asgn.value, self.Trans) + if field_ptr_set is not None and field_ptr_set.Ty is not None: + if HandlesExpr.is_ptr_type(field_ptr_set.Ty) != 0: + field_pointee_set: llvmlite.LLVMType | t.CPtr = field_ptr_set.Ty.Pointee + if field_pointee_set is not None: + cls_nm_attr: str = HandlesStruct.get_class_name_by_type(pool, field_pointee_set) + obj_val_attr: llvmlite.Value | t.CPtr = field_ptr_set + # field_ptr 是 Ptr(Ptr(Struct)) (X|t.CPtr 字段): + # load 解引用获取 Ptr(Struct) + if cls_nm_attr is None and HandlesExpr.is_ptr_type(field_pointee_set) != 0: + inner_struct_attr: llvmlite.LLVMType | t.CPtr = field_pointee_set.Pointee + if inner_struct_attr is not None: + cls_nm_attr = HandlesStruct.get_class_name_by_type(pool, inner_struct_attr) + if cls_nm_attr is not None: + obj_val_attr = llvmlite.build_load( + builder, field_pointee_set, field_ptr_set) + if cls_nm_attr is not None and obj_val_attr is not None: + key_val_attr: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, sub_asgn.slice, None, 0, self.Trans) + if key_val_attr is not None: + arg_vals_attr: t.CSizeT | t.CPtr = pool.alloc(16) + if arg_vals_attr is not None: + arg_vals_attr[0] = t.CSizeT(key_val_attr) + arg_vals_attr[1] = t.CSizeT(rhs_val) + HandlesExprCall._call_method_on_ptr( + pool, builder, mod, cls_nm_attr, "__setitem__", + obj_val_attr, arg_vals_attr, 2, self.Trans) + setitem_done = 1 if setitem_done == 0: - HandlesType.fatal_error(target, "subscript ptr is None") + # 通用 fallback: 翻译 sub.value 并尝试 __setitem__ 或直接 GEP+store + # 处理 Name/Attribute 之外的节点以及 SymTab 查找失败的情况 + if sub_asgn is not None and sub_asgn.value is not None: + obj_val_gen: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, sub_asgn.value, None, 0, self.Trans) + key_val_gen: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, sub_asgn.slice, None, 0, self.Trans) + if obj_val_gen is not None and key_val_gen is not None \ + and obj_val_gen.Ty is not None: + # 内联 _get_custom_struct_cls_nm 逻辑(避免依赖新函数) + cls_nm_gen: str = HandlesStruct.get_class_name_by_type( + pool, obj_val_gen.Ty) + if cls_nm_gen is None and HandlesExpr.is_ptr_type(obj_val_gen.Ty) != 0: + inner_gen: llvmlite.LLVMType | t.CPtr = obj_val_gen.Ty.Pointee + if inner_gen is not None: + cls_nm_gen = HandlesStruct.get_class_name_by_type(pool, inner_gen) + if cls_nm_gen is not None: + # 自定义结构体: 调用 __setitem__ + arg_vals_gen: t.CSizeT | t.CPtr = pool.alloc(16) + if arg_vals_gen is not None: + arg_vals_gen[0] = t.CSizeT(key_val_gen) + arg_vals_gen[1] = t.CSizeT(rhs_val) + HandlesExprCall._call_method_on_ptr( + pool, builder, mod, cls_nm_gen, "__setitem__", + obj_val_gen, arg_vals_gen, 2, self.Trans) + setitem_done = 1 + else: + # 非自定义结构体: 直接 GEP + store + # 处理 get_subscript_ptr 因边界情况返回 None 的指针下标 + if HandlesExpr.is_ptr_type(obj_val_gen.Ty) != 0: + elem_gen: llvmlite.LLVMType | t.CPtr = obj_val_gen.Ty.Pointee + if elem_gen is not None: + store_val_gen: llvmlite.Value | t.CPtr = rhs_val + elem_ptr_gen: llvmlite.Value | t.CPtr = None + match elem_gen: + case llvmlite.LLVMType.Array(arr_elem_gen, _): + elem_ptr_gen = llvmlite.build_gep_array( + builder, elem_gen, arr_elem_gen, + obj_val_gen, key_val_gen) + case _: + elem_ptr_gen = llvmlite.build_gep( + builder, elem_gen, obj_val_gen, key_val_gen) + if elem_ptr_gen is not None and elem_ptr_gen.Ty is not None: + pt_gen: llvmlite.LLVMType | t.CPtr = elem_ptr_gen.Ty.Pointee + if pt_gen is not None: + store_val_gen = HandlesExpr.coerce_to_type( + builder, rhs_val, pt_gen) + llvmlite.build_store(builder, store_val_gen, elem_ptr_gen) + setitem_done = 1 + if setitem_done == 0: + sub_vk: int = sub_asgn.value.kind() + stdio.printf("[ASGN-SUB] fallback failed: val_kind=%d\n", sub_vk) + stdio.fflush(0) + if setitem_done == 0: + HandlesType.fatal_error(target, "subscript ptr is None") continue # Attribute 赋值: obj.field = val @@ -140,14 +249,25 @@ class AssignHandle(HandlesBase.Mixin): field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr( builder, pool, mod, target, self.Trans) if field_ptr is not None: + stdio.printf("[ASGN-ATTR] field_ptr ok ty_not_null=%d\n", + 1 if field_ptr.Ty is not None else 0) + stdio.fflush(0) # 获取字段类型,对 rhs_val 进行类型转换(如 i32 → i64) store_val: llvmlite.Value | t.CPtr = rhs_val if field_ptr.Ty is not None: field_ty: llvmlite.LLVMType | t.CPtr = field_ptr.Ty.Pointee if field_ty is not None: + stdio.printf("[ASGN-ATTR] coerce rhs_ty=%d field_ty=%d\n", + HandlesExpr.get_llvm_type_bits(rhs_val.Ty), + HandlesExpr.get_llvm_type_bits(field_ty)) + stdio.fflush(0) store_val = HandlesExpr.coerce_to_type( builder, rhs_val, field_ty) + stdio.printf("[ASGN-ATTR] pre_store\n") + stdio.fflush(0) llvmlite.build_store(builder, store_val, field_ptr) + stdio.printf("[ASGN-ATTR] post_store\n") + stdio.fflush(0) else: # 构造详细错误信息 attr_node: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(target) @@ -213,7 +333,10 @@ class AssignHandle(HandlesBase.Mixin): if existing is None: new_vars += 1 else: - stdio.printf("[ASGN] alloca failed for %s\n", nm.id) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "alloca failed for %s", nm.id) + VLogger.error(fb, "ASGN") return new_vars diff --git a/App/lib/core/Handles/HandlesAugAssign.py b/App/lib/core/Handles/HandlesAugAssign.py index 3db6e2e..31d8400 100644 --- a/App/lib/core/Handles/HandlesAugAssign.py +++ b/App/lib/core/Handles/HandlesAugAssign.py @@ -5,6 +5,8 @@ import llvmlite import memhub import stdio import string +import viperlib +import lib.core.VLogger as VLogger import lib.core.Handles.HandlesBase as HandlesBase import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesVar as HandlesVar @@ -58,7 +60,7 @@ class AugAssignHandle(HandlesBase.Mixin): field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr( builder, pool, mod, target, self.Trans) if field_ptr is None: - stdio.printf("[AUGASGN] attribute ptr is None\n") + VLogger.error("attribute ptr is None", "AUGASGN") return 0 # 2. 确定字段类型并加载当前值 @@ -68,21 +70,24 @@ class AugAssignHandle(HandlesBase.Mixin): cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load( builder, target_ty, field_ptr) if cur_val is None: - stdio.printf("[AUGASGN] cannot load attribute\n") + VLogger.error("cannot load attribute", "AUGASGN") return 0 # 3. 翻译 RHS 值 rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, aug.value, None, 0, self.Trans) if rhs_val is None: - stdio.printf("[AUGASGN] rhs is None\n") + VLogger.error("rhs is None", "AUGASGN") return 0 # 4. 应用二元运算 result: llvmlite.Value | t.CPtr = _apply_aug_op( pool, builder, aug.op, cur_val, rhs_val) if result is None: - stdio.printf("[AUGASGN] binop failed for attr op=%d\n", aug.op) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "binop failed for attr op=%d", aug.op) + VLogger.error(fb, "AUGASGN") return 0 # 5. 类型对齐并存储 @@ -92,8 +97,56 @@ class AugAssignHandle(HandlesBase.Mixin): llvmlite.build_store(builder, result, field_ptr) return 0 + # Subscript 目标: self.state[i] += x / arr[i] += x + # 流程: get_subscript_ptr → load → binop → store + if tk == ast.ASTKind.Subscript: + pool: memhub.MemBuddy | t.CPtr = self.Trans.Pool + builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder + mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module + + # 1. 获取元素指针 + elem_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_subscript_ptr( + builder, pool, mod, target, self.Trans) + if elem_ptr is None: + VLogger.error("subscript ptr is None", "AUGASGN") + return 0 + + # 2. 确定元素类型并加载当前值 + target_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None: + target_ty = elem_ptr.Ty.Pointee + cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, target_ty, elem_ptr) + if cur_val is None: + VLogger.error("cannot load subscript element", "AUGASGN") + return 0 + + # 3. 翻译 RHS 值 + rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, aug.value, None, 0, self.Trans) + if rhs_val is None: + VLogger.error("rhs is None", "AUGASGN") + return 0 + + # 4. 应用二元运算 + result: llvmlite.Value | t.CPtr = _apply_aug_op( + pool, builder, aug.op, cur_val, rhs_val) + if result is None: + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "binop failed for sub op=%d", aug.op) + VLogger.error(fb, "AUGASGN") + return 0 + + # 5. 类型对齐并存储 + result = HandlesExpr.coerce_to_type(builder, result, target_ty) + if result is None: + return 0 + llvmlite.build_store(builder, result, elem_ptr) + return 0 + if tk != ast.ASTKind.Name: - stdio.printf("[AUGASGN] only Name/Attribute target supported\n") + VLogger.error("only Name/Attribute/Subscript target supported", "AUGASGN") return 0 nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target) @@ -139,14 +192,17 @@ class AugAssignHandle(HandlesBase.Mixin): cur_val = llvmlite.build_load(builder, target_ty, target_alloca) if cur_val is None: - stdio.printf("[AUGASGN] cannot load target %s\n", nm.id) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "cannot load target %s", nm.id) + VLogger.error(fb, "AUGASGN") return 0 # 3. 翻译 RHS 值 rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, aug.value, None, 0, self.Trans) if rhs_val is None: - stdio.printf("[AUGASGN] rhs is None\n") + VLogger.error("rhs is None", "AUGASGN") return 0 # 4. 类型对齐 + 应用二元运算 @@ -154,7 +210,10 @@ class AugAssignHandle(HandlesBase.Mixin): result: llvmlite.Value | t.CPtr = _apply_aug_op( pool, builder, aug.op, cur_val, rhs_val) if result is None: - stdio.printf("[AUGASGN] binop failed for op=%d\n", aug.op) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "binop failed for op=%d", aug.op) + VLogger.error(fb, "AUGASGN") return 0 # 5. 存储结果 @@ -226,6 +285,8 @@ def _apply_aug_op(pool: memhub.MemBuddy | t.CPtr, return llvmlite.build_mul(builder, lhs, rhs) elif op == ast.OpKind.Div: return llvmlite.build_sdiv(builder, lhs, rhs) + elif op == ast.OpKind.FloorDiv: + return llvmlite.build_sdiv(builder, lhs, rhs) elif op == ast.OpKind.Mod: return llvmlite.build_srem(builder, lhs, rhs) elif op == ast.OpKind.BitAnd: diff --git a/App/lib/core/Handles/HandlesClassDef.py b/App/lib/core/Handles/HandlesClassDef.py index 62220a0..a52760d 100644 --- a/App/lib/core/Handles/HandlesClassDef.py +++ b/App/lib/core/Handles/HandlesClassDef.py @@ -6,15 +6,25 @@ import memhub import string import stdio import viperlib +import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesVar as HandlesVar import lib.core.Handles.HandlesEnum as HandlesEnum # 枚举成员临时数组最大数量(本地常量) ENUM_MEMBER_MAX_LOCAL: t.CDefine = 64 +# REnum 变体表容量上限(本地常量) +RENUM_CLASS_MAX: t.CDefine = 64 + +# 作用域类型常量(本地副本,避免旧编译器跨模块 CDefine 查找 bug) +SCOPE_FUNCTION: t.CDefine = 1 +RENUM_VARIANT_PER_CLASS: t.CDefine = 32 +RENUM_FIELD_PER_VARIANT: t.CDefine = 16 + # ============================================================ # HandlesClassDef - class 定义处理 @@ -121,6 +131,51 @@ def _is_union_class(cd: ast.ClassDef | t.CPtr) -> int: return 0 +# ============================================================ +# _is_crenum_base — 检查 base 节点是否为 t.REnum +# +# 支持 Name(id='REnum') 和 Attribute(value=Name('t'), attr='REnum') +# ============================================================ +def _is_crenum_base(base_node: ast.AST | t.CPtr) -> int: + """检查 base 节点是否为 t.REnum,返回 1=是 / 0=否""" + if base_node is None: + return 0 + k: int = base_node.kind() + # Name('REnum') 形式(from t import REnum) + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(base_node) + if nm.id is not None and string.strcmp(nm.id, "REnum") == 0: + return 1 + return 0 + # Attribute(t.REnum) 形式 + if k == ast.ASTKind.Attribute: + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(base_node) + if at.attr is not None and string.strcmp(at.attr, "REnum") == 0: + return 1 + return 0 + return 0 + + +# ============================================================ +# _is_renum_class — 检查 ClassDef 是否继承自 t.REnum +# ============================================================ +def _is_renum_class(cd: ast.ClassDef | t.CPtr) -> int: + """检查 ClassDef 是否继承自 t.REnum,返回 1=是 / 0=否""" + if cd is None or cd.bases is None: + return 0 + bases: list[ast.AST | t.CPtr] | t.CPtr = cd.bases + bn: t.CSizeT = bases.__len__() + if bn == 0: + return 0 + for bi in range(bn): + base_node: ast.AST | t.CPtr = bases.get(bi) + if base_node is None: + continue + if _is_crenum_base(base_node) == 1: + return 1 + return 0 + + # ============================================================ # VTable 装饰器检测和继承关系判断 # ============================================================ @@ -603,7 +658,10 @@ def _translate_enum_def(trans: HT.Translator | t.CPtr, children: list[ast.AST | t.CPtr] | t.CPtr = cd.children if children is None: - stdio.printf("[ENUM] %s has no body\n", class_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "%s has no body", class_name) + VLogger.warning(fb, "ENUM") return 0 cn: t.CSizeT = children.__len__() @@ -662,8 +720,49 @@ def _translate_enum_def(trans: HT.Translator | t.CPtr, member_values_buf[member_count] = cur_val member_count += 1 + elif sk == ast.ASTKind.Assign: + # 处理 Assign 形式的枚举成员: DEBUG = 0, INFO = 1 等 + # CEnum 子类中无类型注释的赋值也视为枚举成员 + ag: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(stmt) + if ag is None or ag.targets is None: + continue + tgts: list[ast.AST | t.CPtr] | t.CPtr = ag.targets + tn: t.CSizeT = tgts.__len__() + if tn == 0: + continue + # 取第一个 target 作为成员名 + t0: ast.AST | t.CPtr = tgts.get(0) + if t0 is None or t0.kind() != ast.ASTKind.Name: + continue + tgt_a: ast.Name | t.CPtr = (ast.Name | t.CPtr)(t0) + if tgt_a.id is None: + continue + + # Assign 无类型注释,默认使用 i32 + member_ty_a: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + + # 解析成员值 + cur_val_a: t.CInt64T = 0 + if ag.value is not None: + cur_val_a = _extract_int_value(ag.value) + else: + cur_val_a = next_val + + # 下一个自动值 + next_val = cur_val_a + 1 + + # 存储 + if member_count < ENUM_MEMBER_MAX_LOCAL: + member_names_buf[member_count] = t.CSizeT(tgt_a.id) + member_types_buf[member_count] = t.CSizeT(member_ty_a) + member_values_buf[member_count] = cur_val_a + member_count += 1 + if member_count == 0: - stdio.printf("[ENUM] %s has no members\n", class_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "%s has no members", class_name) + VLogger.warning(fb, "ENUM") return 0 # 确定基准类型:所有成员类型中 Bits 最大的 @@ -684,7 +783,10 @@ def _translate_enum_def(trans: HT.Translator | t.CPtr, entry: HandlesEnum.EnumEntry | t.CPtr = HandlesEnum.register_enum( pool, class_name, base_ty) if entry is None: - stdio.printf("[ENUM] failed to register %s\n", class_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "failed to register %s", class_name) + VLogger.error(fb, "ENUM") return 0 # 添加成员 @@ -702,8 +804,11 @@ def _translate_enum_def(trans: HT.Translator | t.CPtr, if mname is not None and mty2 is not None: HandlesEnum.add_enum_member(pool, entry, mname, mval, mty2) - stdio.printf("[ENUM] registered %s with %d members, base_bits=%d\n", - class_name, member_count, max_bits) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "registered %s with %d members, base_bits=%d", + class_name, member_count, max_bits) + VLogger.info(fb, "ENUM") return 0 @@ -780,18 +885,21 @@ def _translate_union_def(trans: HT.Translator | t.CPtr, children: list[ast.AST | t.CPtr] | t.CPtr = cd.children if children is None: - stdio.printf("[UNION] %s has no body\n", class_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "%s has no body", class_name) + VLogger.warning(fb, "UNION") return 0 cn: t.CSizeT = children.__len__() # 临时数组存储字段信息 - field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 32) - field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 32) + field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 48) + field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 48) if field_names_buf is None or field_types_buf is None: return 0 - string.memset(field_names_buf, 0, 8 * 32) - string.memset(field_types_buf, 0, 8 * 32) + string.memset(field_names_buf, 0, 8 * 48) + string.memset(field_types_buf, 0, 8 * 48) field_count: int = 0 max_size: int = 0 @@ -816,7 +924,7 @@ def _translate_union_def(trans: HT.Translator | t.CPtr, # 解析字段类型 field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, aa.annotation, trans._imported_modules, trans._from_imports) + pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) if field_ty is None: field_ty = llvmlite.Int32(pool) @@ -826,13 +934,16 @@ def _translate_union_def(trans: HT.Translator | t.CPtr, max_size = fsize # 存储到数组 - if field_count < 32: + if field_count < 48: field_names_buf[field_count] = t.CSizeT(tgt.id) field_types_buf[field_count] = t.CSizeT(field_ty) field_count += 1 if field_count == 0: - stdio.printf("[UNION] %s has no fields\n", class_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "%s has no fields", class_name) + VLogger.warning(fb, "UNION") return 0 if max_size < 1: @@ -860,7 +971,10 @@ def _translate_union_def(trans: HT.Translator | t.CPtr, union_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, pnode, 1, union_type_name) if union_ty is None: - stdio.printf("[UNION] failed to create type for %s\n", class_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "failed to create type for %s", class_name) + VLogger.error(fb, "UNION") return 0 # 注册命名结构体到模块 @@ -870,7 +984,10 @@ def _translate_union_def(trans: HT.Translator | t.CPtr, entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.register_struct( pool, class_name, union_ty, trans.ModuleSha1) if entry is None: - stdio.printf("[UNION] failed to register %s\n", class_name) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "failed to register %s", class_name) + VLogger.error(fb, "UNION") return 0 # 标记为当前文件可见(命名空间隔离) @@ -893,11 +1010,618 @@ def _translate_union_def(trans: HT.Translator | t.CPtr, if fname is not None and fty2 is not None: HandlesStruct.add_field(pool, entry, fname, fty2, None, None) - stdio.printf("[UNION] registered %s with %d fields, max_size=%d\n", - class_name, field_count, max_size) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "registered %s with %d fields, max_size=%d", + class_name, field_count, max_size) + VLogger.info(fb, "UNION") return 0 +# ============================================================ +# _get_annotation_field_size — 计算注解类型的字节大小 +# +# 用于 REnum 布局计算,不要求类型已注册(避免自引用死锁)。 +# 联合类型 (A | B) 按指针 8 字节处理。 +# 未知类型默认 8 字节(指针)。 +# ============================================================ +def _get_annotation_field_size(annotation: ast.AST | t.CPtr) -> int: + """计算注解类型的字节大小,用于 REnum 布局计算""" + if annotation is None: + return 8 + k: int = annotation.kind() + # 联合类型 A | B → 指针大小 8 + if k == ast.ASTKind.BinOp: + return 8 + # Name(id) 或 Attribute(attr) → 解析类型名 + type_name: str = None + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annotation) + if nm is not None: + type_name = nm.id + elif k == ast.ASTKind.Attribute: + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annotation) + if at is not None: + type_name = at.attr + if type_name is None: + return 8 + # 内置类型 + if string.strcmp(type_name, "int") == 0: + return 4 + if string.strcmp(type_name, "CInt") == 0: + return 4 + if string.strcmp(type_name, "CInt8T") == 0: + return 1 + if string.strcmp(type_name, "CInt16T") == 0: + return 2 + if string.strcmp(type_name, "CInt32T") == 0: + return 4 + if string.strcmp(type_name, "CInt64T") == 0: + return 8 + if string.strcmp(type_name, "CChar") == 0: + return 1 + if string.strcmp(type_name, "CDouble") == 0: + return 8 + if string.strcmp(type_name, "CFloat") == 0: + return 4 + if string.strcmp(type_name, "CPtr") == 0: + return 8 + if string.strcmp(type_name, "CSizeT") == 0: + return 8 + if string.strcmp(type_name, "str") == 0: + return 8 + if string.strcmp(type_name, "bytes") == 0: + return 8 + if string.strcmp(type_name, "bool") == 0: + return 4 + # 已注册的结构体类型 + struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(type_name) + if struct_ty is not None: + sz: int = _get_type_size(struct_ty) + if sz > 0: + return sz + # 未注册类型 → 默认指针大小 8(可能是自引用或前向引用) + return 8 + + +# ============================================================ +# _translate_renum_def — 翻译 REnum 类定义 +# +# REnum 语法: +# class LLVMType(t.REnum): +# class Int: +# Bits: t.CInt +# class Ptr: +# Pointee: LLVMType | t.CPtr +# ... +# +# 内存布局: {i32 __tag, , , ...} +# 每个字段位置取所有变体在该位置的最大字段大小: +# - max_size <= 4 → i32 +# - max_size > 4 → i64(可容纳指针) +# 至少包含 {i32, i32}(tag + 最小 payload) +# +# 对应 TPC 中 HandlesClassDef._RegisterREnumMembers 的逻辑 +# ============================================================ +def _translate_renum_def(trans: HT.Translator | t.CPtr, + cd: ast.ClassDef | t.CPtr) -> int: + """翻译 REnum 类定义,返回 0""" + if cd is None or cd.name is None or trans is None: + return 0 + + pool: memhub.MemBuddy | t.CPtr = trans.Pool + class_name: str = cd.name + + # 命名空间隔离:标记为当前文件可见 + HandlesStruct.add_visible_struct(pool, class_name) + + # 检查是否已注册(用 SHA1 区分跨模块同名类) + existing: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( + class_name, trans.ModuleSha1) + if existing is not None: + if existing.Ty is not None: + llvmlite.module_add_named_type(trans.Module, pool, existing.Ty) + return 0 + + children: list[ast.AST | t.CPtr] | t.CPtr = cd.children + if children is None: + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "%s has no body", class_name) + VLogger.warning(fb, "RENUM") + return 0 + + cn: t.CSizeT = children.__len__() + + # 变体最多 32 个,每个变体最多 16 个字段 + # variant_field_sizes[i][j] = 变体 i 的第 j 个字段大小 + variant_field_sizes: t.CSizeT | t.CPtr = pool.alloc(8 * 48 * 16) + if variant_field_sizes is None: + return 0 + string.memset(variant_field_sizes, 0, 8 * 48 * 16) + + variant_count: int = 0 + max_field_count: int = 0 # 所有变体中最大字段数 + + for ci in range(cn): + stmt: ast.AST | t.CPtr = children.get(ci) + if stmt is None: + continue + sk: int = stmt.kind() + + if sk == ast.ASTKind.ClassDef: + # 嵌套变体类 + variant_cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(stmt) + if variant_cd is None or variant_cd.children is None: + variant_count += 1 + continue + variant_children: list[ast.AST | t.CPtr] | t.CPtr = variant_cd.children + vcn: t.CSizeT = variant_children.__len__() + field_idx: int = 0 + for vi in range(vcn): + vstmt: ast.AST | t.CPtr = variant_children.get(vi) + if vstmt is None: + continue + vk: int = vstmt.kind() + if vk != ast.ASTKind.AnnAssign: + continue + vaa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(vstmt) + if vaa is None or vaa.target is None or vaa.annotation is None: + continue + if vaa.target.kind() != ast.ASTKind.Name: + continue + # 提取字段名 + vnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(vaa.target) + # 计算字段大小 + fsize: int = _get_annotation_field_size(vaa.annotation) + if variant_count < 32 and field_idx < 16: + # variant_field_sizes[variant_count][field_idx] = fsize + offset: t.CSizeT = variant_count * 16 + field_idx + variant_field_sizes[offset] = t.CSizeT(fsize) + # 直接注册变体字段名别名(避免 CSizeT→str 转换问题) + if vnm is not None and vnm.id is not None: + _register_renum_field_alias(pool, class_name, vnm.id, field_idx + 1) + field_idx += 1 + if field_idx > max_field_count: + max_field_count = field_idx + variant_count += 1 + + if variant_count == 0: + fb2: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb2 is not None: + viperlib.snprintf(fb2, 1024, "%s has no variants", class_name) + VLogger.warning(fb2, "RENUM") + return 0 + + # 计算每个位置的最大字段大小 + # 位置 0 = __tag (i32, 4 字节) + # 位置 1+ = 变体字段 + total_positions: int = max_field_count + 1 # +1 for tag + if total_positions < 2: + total_positions = 2 # 至少 {i32, i32} + + position_max_sizes: t.CSizeT | t.CPtr = pool.alloc(8 * total_positions) + if position_max_sizes is None: + return 0 + string.memset(position_max_sizes, 0, 8 * total_positions) + + # 位置 0 = tag = 4 字节 + position_max_sizes[0] = t.CSizeT(4) + + # 位置 1+ = 每个位置取所有变体的最大字段大小 + for pos in range(1, total_positions): + pos_max: int = 0 + for vi in range(variant_count): + field_pos: int = pos - 1 # 变体字段从位置 0 开始 + offset2: t.CSizeT = vi * 16 + field_pos + fsize_val: t.CSizeT = variant_field_sizes[offset2] + if fsize_val > pos_max: + pos_max = fsize_val + position_max_sizes[pos] = t.CSizeT(pos_max) + + # 构建 ParamNode 链表 + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + + first_node: llvmlite.ParamNode | t.CPtr = None + tail_node: llvmlite.ParamNode | t.CPtr = None + + for pos in range(total_positions): + psize: int = position_max_sizes[pos] + field_ty: llvmlite.LLVMType | t.CPtr = i32_ty + if psize > 4: + field_ty = i64_ty + pnode: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, field_ty) + if pnode is None: + continue + tail_node = llvmlite.param_list_append(first_node, tail_node, pnode) + if first_node is None: + first_node = tail_node + + if first_node is None: + fb3: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb3 is not None: + viperlib.snprintf(fb3, 1024, "failed to build field list for %s", class_name) + VLogger.error(fb3, "RENUM") + return 0 + + # 构造命名结构体类型名: "sha1.ClassName" 或 "ClassName" + renum_type_name: str = class_name + if trans.ModuleSha1 is not None: + renum_name_buf: t.CChar | t.CPtr = pool.alloc(64) + if renum_name_buf is not None: + viperlib.snprintf(renum_name_buf, 64, "%s.%s", trans.ModuleSha1, class_name) + renum_type_name = renum_name_buf + + renum_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct( + pool, first_node, total_positions, renum_type_name) + if renum_ty is None: + fb4: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb4 is not None: + viperlib.snprintf(fb4, 1024, "failed to create type for %s", class_name) + VLogger.error(fb4, "RENUM") + return 0 + + # 注册命名结构体到模块 + llvmlite.module_add_named_type(trans.Module, pool, renum_ty) + + # 注册到 HandlesStruct + entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.register_struct( + pool, class_name, renum_ty, trans.ModuleSha1) + if entry is None: + fb5: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb5 is not None: + viperlib.snprintf(fb5, 1024, "failed to register %s", class_name) + VLogger.error(fb5, "RENUM") + return 0 + + # 修复 class_name 指针不匹配: + # 别名注册时用 class_name(= cd.name,AST 原始指针), + # 但查找时用 entry.Name(register_struct 通过 pool.alloc+strcpy 复制的副本指针)。 + # 两者是不同指针,CSizeT 指针值比较会失败。 + # 调用辅助函数将已注册别名的 class_name 统一更新为 entry.Name。 + if entry.Name is not None: + _update_renum_alias_class_ptr(t.CSizeT(class_name), t.CSizeT(entry.Name)) + + # 添加 __tag 字段(位置 0,i32) + HandlesStruct.add_field(pool, entry, "__tag", i32_ty, None, None) + + # 添加 payload 字段(位置 1+,i32 或 i64) + for pos in range(1, total_positions): + psize2: int = position_max_sizes[pos] + payload_ty: llvmlite.LLVMType | t.CPtr = i32_ty + if psize2 > 4: + payload_ty = i64_ty + field_name_buf: t.CChar | t.CPtr = pool.alloc(16) + if field_name_buf is not None: + viperlib.snprintf(field_name_buf, 16, "_p%d", pos) + HandlesStruct.add_field(pool, entry, field_name_buf, payload_ty, None, None) + + # 注册变体名到全局变体表(供 translate_call 检测 REnum 变体构造) + _register_renum_variants(pool, class_name, cd, variant_count) + + # 注册变体字段名别名已在变体遍历时直接完成(避免 CSizeT→str 转换问题) + + fb6: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb6 is not None: + viperlib.snprintf(fb6, 1024, "registered %s with %d variants, %d positions", + class_name, variant_count, total_positions) + VLogger.info(fb6, "RENUM") + return 0 + + +# ============================================================ +# REnum 变体表(全局静态分配) +# +# 存储 ClassName → [VariantName, TagValue] 映射, +# 供 translate_call 检测 ClassName.VariantName(args) 形式的变体构造调用。 +# +# 布局(所有数组使用 t.CSizeT | t.CPtr 确保 8 字节步长): +# _renum_class_names[i] = 第 i 个 REnum 类名(str 指针,以 CSizeT 存储) +# _renum_class_variant_counts[i] = 第 i 个 REnum 类的变体数量 +# _renum_variant_names[i][j] = 第 i 个类的第 j 个变体名(str 指针,以 CSizeT 存储) +# _renum_variant_tags[i][j] = 第 i 个类的第 j 个变体的 tag 值 +# ============================================================ +_renum_class_names: t.CSizeT | t.CPtr = None +_renum_class_variant_counts: t.CSizeT | t.CPtr = None +_renum_variant_names: t.CSizeT | t.CPtr = None +_renum_variant_tags: t.CSizeT | t.CPtr = None +_renum_class_count: int = 0 + + +# ============================================================ +# REnum 字段别名表(全局静态分配) +# +# 存储 (class_name, field_name) → payload_index 映射, +# 供 translate_attribute 查找 REnum 变体字段。 +# +# REnum 结构体布局为 {i32 __tag, _p1, _p2, ...}, +# 但源码用变体字段名访问(如 ty.Pointee)。 +# 此表将变体字段名映射到 payload 索引(_p1=1, _p2=2, ...)。 +# +# 布局: +# _renum_alias_class[i] = 类名(str 指针,以 CSizeT 存储) +# _renum_alias_field[i] = 字段名(str 指针,以 CSizeT 存储) +# _renum_alias_index[i] = payload 索引(CSizeT) +# ============================================================ +RENUM_ALIAS_MAX: t.CDefine = 512 +_renum_alias_class: t.CSizeT | t.CPtr = None +_renum_alias_field: t.CSizeT | t.CPtr = None +_renum_alias_index: t.CSizeT | t.CPtr = None +_renum_alias_count: int = 0 + + +# ============================================================ +# _register_renum_variants — 注册 REnum 类的变体名和 tag 值 +# +# 遍历 ClassDef 的 children,提取嵌套变体类的名称, +# 按 children 顺序分配 tag 值(0, 1, 2, ...) +# +# 参数: +# pool: 内存分配器 +# class_name: REnum 类名(如 "LLVMType") +# cd: REnum 类的 ClassDef AST 节点 +# variant_count: 变体数量(由 _translate_renum_def 预计算) +# ============================================================ +def _register_renum_variants(pool: memhub.MemBuddy | t.CPtr, + class_name: str, + cd: ast.ClassDef | t.CPtr, + variant_count: int): + """注册 REnum 类的变体名和 tag 值到全局变体表""" + global _renum_class_names, _renum_class_variant_counts + global _renum_variant_names, _renum_variant_tags, _renum_class_count + + if pool is None or class_name is None or cd is None: + return + if variant_count == 0: + return + children: list[ast.AST | t.CPtr] | t.CPtr = cd.children + if children is None: + return + + # 懒初始化表(首次注册时分配) + if _renum_class_names is None: + _renum_class_names = pool.alloc(8 * RENUM_CLASS_MAX) + _renum_class_variant_counts = pool.alloc(8 * RENUM_CLASS_MAX) + _renum_variant_names = pool.alloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) + _renum_variant_tags = pool.alloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) + if (_renum_class_names is None or _renum_class_variant_counts is None + or _renum_variant_names is None or _renum_variant_tags is None): + _renum_class_names = None + return + string.memset(_renum_class_names, 0, 8 * RENUM_CLASS_MAX) + string.memset(_renum_class_variant_counts, 0, 8 * RENUM_CLASS_MAX) + string.memset(_renum_variant_names, 0, 8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) + string.memset(_renum_variant_tags, 0, 8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) + _renum_class_count = 0 + + # 检查是否已注册(避免重复注册) + for i in range(_renum_class_count): + existing: str = _renum_class_names[i] + if existing is not None and string.strcmp(existing, class_name) == 0: + return + + # 表已满 + if _renum_class_count >= RENUM_CLASS_MAX: + return + + # 注册类名 + idx: int = _renum_class_count + _renum_class_names[idx] = t.CSizeT(class_name) + _renum_class_variant_counts[idx] = variant_count + + # 遍历 children,提取嵌套变体类名并注册 + cn: t.CSizeT = children.__len__() + tag: int = 0 + for ci in range(cn): + if tag >= variant_count: + break + stmt: ast.AST | t.CPtr = children.get(ci) + if stmt is None: + continue + if stmt.kind() != ast.ASTKind.ClassDef: + continue + vcd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(stmt) + if vcd is None or vcd.name is None: + continue + name_offset: t.CSizeT = idx * RENUM_VARIANT_PER_CLASS + tag + _renum_variant_names[name_offset] = t.CSizeT(vcd.name) + _renum_variant_tags[name_offset] = tag + tag += 1 + + _renum_class_count = idx + 1 + + +# ============================================================ +# _lookup_renum_variant — 查找变体所属的 REnum 类名 +# +# 参数: +# variant_name: 变体名(如 "Int"、"Ptr") +# +# 返回: +# 成功: class_name (str) +# 失败: None +# 注: tag_value 可通过 _lookup_renum_variant_in_class(class_name, variant_name) 获取 +# ============================================================ +def _lookup_renum_variant(variant_name: str) -> str: + """查找变体名所属的 REnum 类名 + + 返回: class_name (str),或 None + """ + if variant_name is None or _renum_class_names is None: + return None + + for i in range(_renum_class_count): + vcount: int = _renum_class_variant_counts[i] + for j in range(vcount): + name_offset: t.CSizeT = i * RENUM_VARIANT_PER_CLASS + j + vname: str = _renum_variant_names[name_offset] + if vname is not None and string.strcmp(vname, variant_name) == 0: + cls_nm: str = _renum_class_names[i] + return cls_nm + return None + + +# ============================================================ +# _is_renum_class_name — 检查名称是否是已注册的 REnum 类名 +# +# 用于 translate_call 检测 ClassName.Variant(args) 形式调用时, +# 先验证 ClassName 是否是 REnum 类。 +# ============================================================ +def _is_renum_class_name(class_name: str) -> int: + """检查名称是否是已注册的 REnum 类名,返回 1=是 / 0=否""" + if class_name is None or _renum_class_names is None: + return 0 + for i in range(_renum_class_count): + existing: str = _renum_class_names[i] + if existing is not None and string.strcmp(existing, class_name) == 0: + return 1 + return 0 + + +# ============================================================ +# _lookup_renum_variant_in_class — 在指定 REnum 类中查找变体 +# +# 参数: +# class_name: REnum 类名 +# variant_name: 变体名 +# +# 返回: +# 成功: tag_value (int) +# 失败: -1 +# ============================================================ +def _lookup_renum_variant_in_class(class_name: str, variant_name: str) -> int: + """在指定 REnum 类中查找变体,返回 tag 值或 -1""" + if class_name is None or variant_name is None or _renum_class_names is None: + return -1 + for i in range(_renum_class_count): + existing: str = _renum_class_names[i] + if existing is None or string.strcmp(existing, class_name) != 0: + continue + vcount: int = _renum_class_variant_counts[i] + for j in range(vcount): + name_offset: t.CSizeT = i * RENUM_VARIANT_PER_CLASS + j + vname: str = _renum_variant_names[name_offset] + if vname is not None and string.strcmp(vname, variant_name) == 0: + return _renum_variant_tags[name_offset] + return -1 + return -1 + + +# ============================================================ +# _register_renum_field_alias — 注册 REnum 变体字段名别名 +# +# 将变体字段名(如 "Pointee")映射到 payload 索引(如 1), +# 供 translate_attribute 查找 REnum 变体字段。 +# +# 参数: +# pool: 内存分配器 +# class_name: REnum 类名(如 "LLVMType") +# field_name: 变体字段名(如 "Pointee") +# payload_idx: payload 索引(1=_p1, 2=_p2, ...) +# ============================================================ +def _register_renum_field_alias(pool: memhub.MemBuddy | t.CPtr, + class_name: str, + field_name: str, + payload_idx: int): + """注册 REnum 变体字段名别名到全局表""" + global _renum_alias_class, _renum_alias_field, _renum_alias_index + global _renum_alias_count + + if pool is None or class_name is None or field_name is None: + return + if payload_idx < 1: + return + + # 懒初始化表 + if _renum_alias_class is None: + _renum_alias_class = pool.alloc(8 * RENUM_ALIAS_MAX) + _renum_alias_field = pool.alloc(8 * RENUM_ALIAS_MAX) + _renum_alias_index = pool.alloc(8 * RENUM_ALIAS_MAX) + if (_renum_alias_class is None or _renum_alias_field is None + or _renum_alias_index is None): + _renum_alias_class = None + return + string.memset(_renum_alias_class, 0, 8 * RENUM_ALIAS_MAX) + string.memset(_renum_alias_field, 0, 8 * RENUM_ALIAS_MAX) + string.memset(_renum_alias_index, 0, 8 * RENUM_ALIAS_MAX) + _renum_alias_count = 0 + + if _renum_alias_count >= RENUM_ALIAS_MAX: + return + + # 检查是否已注册(避免重复)— 用 CSizeT 指针值比较,避免 CSizeT→str 转换问题 + cls_key: t.CSizeT = t.CSizeT(class_name) + fld_key: t.CSizeT = t.CSizeT(field_name) + for i in range(_renum_alias_count): + cls_existing: t.CSizeT = _renum_alias_class[i] + fld_existing: t.CSizeT = _renum_alias_field[i] + if cls_existing == cls_key and fld_existing == fld_key: + return + + idx: int = _renum_alias_count + _renum_alias_class[idx] = cls_key + _renum_alias_field[idx] = fld_key + _renum_alias_index[idx] = t.CSizeT(payload_idx) + _renum_alias_count = idx + 1 + + +# ============================================================ +# _update_renum_alias_class_ptr — 更新别名表中的 class_name 指针 +# +# register_struct 会通过 pool.alloc+strcpy 复制类名,导致 entry.Name +# 与原始 cd.name 是不同指针。别名注册时用 cd.name,查找时用 entry.Name, +# CSizeT 指针值比较会失败。此函数将别名表中所有匹配 old_ptr 的 class_name +# 更新为 new_ptr,确保注册和查找使用同一指针。 +# +# 参数: +# old_ptr: 旧 class_name 指针值(CSizeT(cd.name)) +# new_ptr: 新 class_name 指针值(CSizeT(entry.Name)) +# ============================================================ +def _update_renum_alias_class_ptr(old_ptr: t.CSizeT, new_ptr: t.CSizeT): + """更新别名表中的 class_name 指针,从 old_ptr 更新为 new_ptr""" + global _renum_alias_class, _renum_alias_count + + if old_ptr == new_ptr: + return + if _renum_alias_class is None: + return + for i in range(_renum_alias_count): + if _renum_alias_class[i] == old_ptr: + _renum_alias_class[i] = new_ptr + + +# ============================================================ +# _lookup_renum_field_alias — 查找 REnum 变体字段名的 payload 索引 +# +# 参数: +# class_name: REnum 类名(如 "LLVMType") +# field_name: 变体字段名(如 "Pointee") +# +# 返回: +# 成功: payload 索引(1, 2, ...) +# 失败: -1 +# ============================================================ +def _lookup_renum_field_alias(class_name: str, field_name: str) -> int: + """查找 REnum 变体字段名的 payload 索引,返回索引或 -1 + + class_name: 用 CSizeT 指针值比较(_update_renum_alias_class_ptr 已统一指针) + field_name: 用 strcmp 内容比较(注册时 vnm.id 与查找时 at.attr 是不同指针) + """ + if class_name is None or field_name is None or _renum_alias_class is None: + return -1 + cls_key: t.CSizeT = t.CSizeT(class_name) + for i in range(_renum_alias_count): + cls_existing: t.CSizeT = _renum_alias_class[i] + if cls_existing == cls_key: + # field_name 用 strcmp 内容比较(指针不同但内容相同) + fld_existing_ptr: str = (str | t.CPtr)( + t.CVoid(_renum_alias_field[i], t.CPtr)) + if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0: + return _renum_alias_index[i] + return -1 + + # ============================================================ # translate_class_def — 翻译 class 定义 # @@ -913,6 +1637,8 @@ def _translate_union_def(trans: HT.Translator | t.CPtr, _generic_class_names: list[str] | t.CPtr = None _generic_class_nodes: list[ast.ClassDef | t.CPtr] | t.CPtr = None +# 追踪每个泛型模板定义模块的 SHA1,用于特化时统一命名空间 +_generic_class_sha1s: list[str] | t.CPtr = None def _is_generic_class(cd: ast.ClassDef | t.CPtr) -> int: @@ -928,12 +1654,14 @@ def _is_generic_class(cd: ast.ClassDef | t.CPtr) -> int: def _register_generic_template(pool: memhub.MemBuddy | t.CPtr, - cd: ast.ClassDef | t.CPtr): - """注册泛型类模板(类名 + ClassDef 节点)""" - global _generic_class_names, _generic_class_nodes + cd: ast.ClassDef | t.CPtr, + module_sha1: str): + """注册泛型类模板(类名 + ClassDef 节点 + 来源模块 SHA1)""" + global _generic_class_names, _generic_class_nodes, _generic_class_sha1s if _generic_class_names is None: _generic_class_names = list[str](pool, 8) _generic_class_nodes = list[ast.ClassDef | t.CPtr](pool, 8) + _generic_class_sha1s = list[str](pool, 8) # 检查是否已注册 cn: t.CSizeT = _generic_class_names.__len__() i: t.CSizeT @@ -943,6 +1671,7 @@ def _register_generic_template(pool: memhub.MemBuddy | t.CPtr, return _generic_class_names.append(cd.name) _generic_class_nodes.append(cd) + _generic_class_sha1s.append(module_sha1) def _find_generic_template(class_name: str) -> ast.ClassDef | t.CPtr: @@ -954,7 +1683,24 @@ def _find_generic_template(class_name: str) -> ast.ClassDef | t.CPtr: for i in range(cn): nm: str = _generic_class_names.get(i) if nm is not None and string.strcmp(nm, class_name) == 0: - return _generic_class_nodes.get(i) + # 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换 + node: ast.ClassDef | t.CPtr = _generic_class_nodes.get(i) + return node + return None + + +def _find_generic_template_sha1(class_name: str) -> str: + """查找泛型类模板的来源模块 SHA1,未找到返回 None""" + if _generic_class_names is None or class_name is None: + return None + cn: t.CSizeT = _generic_class_names.__len__() + i: t.CSizeT + for i in range(cn): + nm: str = _generic_class_names.get(i) + if nm is not None and string.strcmp(nm, class_name) == 0: + # 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换 + sha1_val: str = _generic_class_sha1s.get(i) + return sha1_val return None @@ -1028,8 +1774,10 @@ def _clone_annotation(pool: memhub.MemBuddy | t.CPtr, tpn: str = tp_names.get(i) if tpn is not None and string.strcmp(nm.id, tpn) == 0: ta: str = type_args.get(i) - # 类型实参可能是复合类型(如 "AST|t.CPtr"),用 _parse_type_string 解析 - return HandlesType._parse_type_string(pool, ta, nm.ctx) + # 类型实参可能是复合类型(如 "AST|t.CPtr"),用 _parse_type_string_ptr 解析 + # 使用 _ptr 包装避免旧编译器对 ast.AST|t.CPtr 返回类型推断为 i32 的 bug + parsed_node: ast.AST | t.CPtr = HandlesType._parse_type_string_ptr(pool, ta, nm.ctx) + return parsed_node return ast.Name(pool, nm.id, nm.ctx) return None @@ -1058,10 +1806,11 @@ def _clone_annotation(pool: memhub.MemBuddy | t.CPtr, tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node) new_elts: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 8) if tup.elts is not None: - en: t.CSizeT = tup.elts.__len__() + tup_elts: list[ast.AST | t.CPtr] | t.CPtr = tup.elts + en: t.CSizeT = tup_elts.__len__() ei: t.CSizeT for ei in range(en): - el: ast.AST | t.CPtr = tup.elts.get(ei) + el: ast.AST | t.CPtr = tup_elts.get(ei) new_el: ast.AST | t.CPtr = _clone_annotation(pool, el, tp_names, type_args) if new_el is not None: new_elts.append(new_el) @@ -1090,10 +1839,11 @@ def _clone_arguments(pool: memhub.MemBuddy | t.CPtr, # 拷贝 args 列表,替换每个 Arg 的 annotation new_arg_list: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 8) if old_args.args is not None: - an: t.CSizeT = old_args.args.__len__() + old_arg_list: list[ast.AST | t.CPtr] | t.CPtr = old_args.args + an: t.CSizeT = old_arg_list.__len__() ai: t.CSizeT for ai in range(an): - arg_node: ast.AST | t.CPtr = old_args.args.get(ai) + arg_node: ast.AST | t.CPtr = old_arg_list.get(ai) if arg_node is None: continue if arg_node.kind() == ast.ASTKind.Arg: @@ -1124,10 +1874,11 @@ def _clone_classdef_for_spec(pool: memhub.MemBuddy | t.CPtr, cd.decorator_list, None) # 拷贝 children if cd.children is not None: - cn: t.CSizeT = cd.children.__len__() + cd_children: list[ast.AST | t.CPtr] | t.CPtr = cd.children + cn: t.CSizeT = cd_children.__len__() ci: t.CSizeT for ci in range(cn): - child: ast.AST | t.CPtr = cd.children.get(ci) + child: ast.AST | t.CPtr = cd_children.get(ci) if child is None: continue ck: int = child.kind() @@ -1148,10 +1899,11 @@ def _clone_classdef_for_spec(pool: memhub.MemBuddy | t.CPtr, fd.decorator_list, new_returns2, 0) # 方法体直接引用原始节点 if fd.children is not None: - bcn: t.CSizeT = fd.children.__len__() + fd_children: list[ast.AST | t.CPtr] | t.CPtr = fd.children + bcn: t.CSizeT = fd_children.__len__() bci: t.CSizeT for bci in range(bcn): - body_stmt: ast.AST | t.CPtr = fd.children.get(bci) + body_stmt: ast.AST | t.CPtr = fd_children.get(bci) if body_stmt is not None: new_fd.append(body_stmt) new_cd.append(new_fd) @@ -1179,7 +1931,9 @@ def _find_cached_spec(spec_key: str) -> str: for i in range(n): k: str = _spec_keys.get(i) if k is not None and string.strcmp(k, spec_key) == 0: - return _spec_names.get(i) + # 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换 + spec_nm: str = _spec_names.get(i) + return spec_nm return None @@ -1215,23 +1969,26 @@ def _specialize_generic_class(trans: HT.Translator | t.CPtr, # 诊断输出:追踪 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) + VLogger.debug("=== _specialize_generic_class ===", "SPEC") + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "class=%s module=%s", class_name, trans.ModuleSha1) + VLogger.debug(fb, "SPEC") 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) + if fb is not None: + viperlib.snprintf(fb, 1024, " type_arg[%d]=%s", ta_i, ta) + VLogger.debug(fb, "SPEC") # 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) + VLogger.debug("template NOT FOUND, return None", "SPEC") return None # 2. 生成特化名 @@ -1267,17 +2024,21 @@ def _specialize_generic_class(trans: HT.Translator | t.CPtr, 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) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "CACHE HIT, return cached=%s", cached) + VLogger.debug(fb, "SPEC") 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) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "EXISTING registered, return spec_name=%s field_count=%d", + spec_name, existing.FieldCount) + VLogger.debug(fb, "SPEC") _cache_spec(pool, spec_key, spec_name) return spec_name @@ -1297,7 +2058,16 @@ def _specialize_generic_class(trans: HT.Translator | t.CPtr, HandlesType.set_generic_context(tp_names, type_args) # 7. 翻译特化类 + # 切换 trans.ModuleSha1 为泛型模板定义模块的 SHA1, + # 使结构体类型名和方法定义名都用定义模块的 SHA1 前缀, + # 与调用端 get_struct_sha1(class_name) 查到的 SHA1 一致。 + saved_module_sha1: str = trans.ModuleSha1 + template_sha1: str = _find_generic_template_sha1(class_name) + if template_sha1 is not None: + trans.ModuleSha1 = template_sha1 translate_class_def(trans, spec_cd) + # 恢复原始 ModuleSha1 + trans.ModuleSha1 = saved_module_sha1 # 8. 清除 type_map 上下文 trans.GenericTypeParamNames = None @@ -1308,8 +2078,11 @@ def _specialize_generic_class(trans: HT.Translator | t.CPtr, 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) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "DONE spec_name=%s field_count=%d", + spec_name, result_entry.FieldCount) + VLogger.debug(fb, "SPEC") # 打印每个字段的类型信息 ri: int for ri in range(result_entry.FieldCount): @@ -1318,12 +2091,13 @@ def _specialize_generic_class(trans: HT.Translator | t.CPtr, 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) + if fb is not None: + viperlib.snprintf(fb, 1024, " field[%d] name=%s type=%d", + ri, rfn if rfn is not None else "(null)", + t.CSizeT(rft) if rft is not None else 0) + VLogger.debug(fb, "SPEC") else: - stdio.printf("[SPEC] DONE but result NOT FOUND in struct table!\n") - stdio.fflush(0) + VLogger.debug("DONE but result NOT FOUND in struct table!", "SPEC") return spec_name @@ -1353,20 +2127,29 @@ def translate_class_def(trans: HT.Translator | t.CPtr, if _is_union_class(cd) == 1: return _translate_union_def(trans, cd) + # REnum 类:交给 REnum 处理({i32 tag, max_payload} 布局) + if _is_renum_class(cd) == 1: + return _translate_renum_def(trans, cd) + # 泛型类:存储为模板,不发射 IR(等实例化时特化) if _is_generic_class(cd) == 1: - _register_generic_template(pool, cd) + _register_generic_template(pool, cd, trans.ModuleSha1) return 0 # 检查是否已注册(用 SHA1 区分跨模块同名类) existing: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( class_name, trans.ModuleSha1) if existing is not None: - # 结构体已注册(Phase B 重新翻译),但仍需在当前模块中翻译方法 + # 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法 if existing.Ty is not None: # Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行) llvmlite.module_add_named_type(trans.Module, pool, existing.Ty) - _translate_oop_methods(trans, cd, existing.Ty, class_name) + # Phase A-pre(declare_only=1)多遍扫描时,类已注册但不应翻译方法体 + # Phase B(declare_only=0)时全量翻译方法体 + if trans._declare_only == 1: + _translate_oop_methods(trans, cd, existing.Ty, class_name, 1) + else: + _translate_oop_methods(trans, cd, existing.Ty, class_name) # SHA1 已在 register_struct 时设置,无需再补 return 0 @@ -1387,22 +2170,43 @@ def translate_class_def(trans: HT.Translator | t.CPtr, if _has_decorator(cd.decorator_list, "NoVTable") == 1: is_novtable_deco = 1 + # ============================================================ + # PhaseA 预注册延迟检查:父类未注册时延迟注册当前类 + # + # 根因:AssignHandle(HandlesBase.Mixin) 有自己的字段 _CurrentClass, + # 当 Mixin 未注册时(字母序:HandlesAssign.py 在 HandlesBase.py 之前), + # field_count=1(不是0),field_count==0 静默返回不触发, + # AssignHandle 被注册为只有 _CurrentClass 字段(缺 Trans)。 + # 后续遍 existing is not None,不补字段 → attribute ptr is None: self.Trans + # + # 修复:declare_only==1 且父类未注册时,延迟注册到下一遍。 + # 多遍 PhaseA-pre 会在后续遍中重试(父类已注册后完整注册)。 + # Phase B(declare_only==0)不延迟,避免死锁。 + # ============================================================ + if trans._declare_only == 1 and parent_name is not None: + parent_check: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( + parent_name, trans.ModuleSha1) + if parent_check is None: + parent_check = HandlesStruct.find_struct(parent_name) + if parent_check is None: + return 0 + # ============================================================ # 1. 遍历 class body,收集字段信息 # ============================================================ # 用 CSizeT 数组存储指针值(64 位系统上 8 字节) - field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 32) - field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 32) - field_defaults_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 32) - field_annot_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 32) + field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 48) + field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 48) + field_defaults_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 48) + field_annot_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 48) if field_names_buf is None or field_types_buf is None or field_defaults_buf is None: return 0 if field_annot_buf is None: return 0 - string.memset(field_names_buf, 0, 8 * 32) - string.memset(field_types_buf, 0, 8 * 32) - string.memset(field_defaults_buf, 0, 8 * 32) - string.memset(field_annot_buf, 0, 8 * 32) + string.memset(field_names_buf, 0, 8 * 48) + string.memset(field_types_buf, 0, 8 * 48) + string.memset(field_defaults_buf, 0, 8 * 48) + string.memset(field_annot_buf, 0, 8 * 48) field_count: int = 0 @@ -1417,6 +2221,11 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # 用 SHA1 感知查找父类,规避跨模块同名找错 parent_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( parent_name, trans.ModuleSha1) + if parent_entry is None: + # 父类可能定义在另一个模块(不同 SHA1,如 HandlesBase.Mixin), + # find_struct_by_module 不回退全局查找,此处手动回退到 find_struct, + # 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败 + parent_entry = HandlesStruct.find_struct(parent_name) if parent_entry is not None: pfc: int = parent_entry.FieldCount for pfi in range(pfc): @@ -1430,7 +2239,7 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # 跳过父类的 vtable 指针字段 if string.strcmp(pfn, "__vtable__") == 0: continue - if field_count < 32: + if field_count < 48: pft: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_field_type_ptr(pfe) pfd: ast.AST | t.CPtr = HandlesStruct.get_field_default_ptr(pfe) pfa: str = HandlesStruct.get_field_annot_class_name(pfe) @@ -1484,12 +2293,12 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # 解析字段类型 field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, aa.annotation, trans._imported_modules, trans._from_imports) + pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) if field_ty is None: field_ty = llvmlite.Int32(pool) # 存储到数组(指针转 CSizeT) - if field_count < 32: + if field_count < 48: diag_name_val: t.CSizeT = t.CSizeT(tgt.id) field_names_buf[field_count] = diag_name_val field_types_buf[field_count] = t.CSizeT(field_ty) @@ -1512,7 +2321,15 @@ def translate_class_def(trans: HT.Translator | t.CPtr, field_count += 1 if field_count == 0: - stdio.printf("[CLASS] %s has no fields\n", class_name) + # PhaseA 预注册时,父类可能尚未注册(字母序单遍扫描), + # 导致子类无法继承父类字段 → field_count==0。 + # 静默返回,Phase2.py 的多遍 PhaseA-pre 会在后续遍中重试。 + if trans._declare_only == 1 and parent_name is not None: + return 0 + fb_cf: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_cf is not None: + viperlib.snprintf(fb_cf, 1024, "%s has no fields", class_name) + VLogger.info(fb_cf, "CLASS") return 0 # ============================================================ @@ -1528,7 +2345,10 @@ def translate_class_def(trans: HT.Translator | t.CPtr, i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) vtable_node: llvmlite.ParamNode | t.CPtr = pool.alloc(llvmlite.ParamNode.__sizeof__()) if vtable_node is None: - stdio.printf("[CLASS] failed to alloc vtable node for %s\n", class_name) + fb_vn: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_vn is not None: + viperlib.snprintf(fb_vn, 1024, "failed to alloc vtable node for %s", class_name) + VLogger.error(fb_vn, "CLASS") return 0 string.memset(vtable_node, 0, llvmlite.ParamNode.__sizeof__()) vtable_node.Ty = i8_ptr_ty @@ -1566,7 +2386,10 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # 创建 StructType(命名结构体) struct_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, first_node, total_field_count, type_name) if struct_ty is None: - stdio.printf("[CLASS] failed to create StructType for %s\n", class_name) + fb_st: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_st is not None: + viperlib.snprintf(fb_st, 1024, "failed to create StructType for %s", class_name) + VLogger.error(fb_st, "CLASS") return 0 # 注册命名结构体到模块(输出 %"name" = type { ... } 定义行) @@ -1587,7 +2410,10 @@ def translate_class_def(trans: HT.Translator | t.CPtr, entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.register_struct( pool, class_name, struct_ty, trans.ModuleSha1) if entry is None: - stdio.printf("[CLASS] failed to register %s\n", class_name) + fb_re: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_re is not None: + viperlib.snprintf(fb_re, 1024, "failed to register %s", class_name) + VLogger.error(fb_re, "CLASS") return 0 # 标记 VTable 状态(直接用 entry,规避跨模块同名 find_struct 找错) @@ -2154,7 +2980,7 @@ def _translate_method(trans: HT.Translator | t.CPtr, ret_ty: llvmlite.LLVMType | t.CPtr = None if fd.returns is not None: ret_ty = HandlesType.resolve_annotation_type( - pool, fd.returns, imported_modules, from_imports) + pool, fd.returns, imported_modules, from_imports, trans) if ret_ty is None and fd.returns is not None: if HandlesType.has_decorator_marker(fd.returns, "State") != 0: ret_ty = llvmlite.Void(pool) @@ -2176,9 +3002,23 @@ def _translate_method(trans: HT.Translator | t.CPtr, if func is None: return 0 + # 提取默认参数信息(方法的 args[0] 是 self,不含在 param_count 中) + md_defaults: t.CVoid | t.CPtr = None + md_default_count: int = 0 + md_param_count: int = 0 + md_args_node: ast.Arguments | t.CPtr = fd.args + if md_args_node is not None: + md_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(md_args_node) + if md_ags.args is not None: + md_param_count = md_ags.args.__len__() - 1 + if md_ags.defaults is not None: + md_defaults = md_ags.defaults + md_default_count = md_ags.defaults.__len__() + # 注册到函数表(用 ClassName.method_name 作为查找名,支持后缀匹配) max_funcs: int = 256 - if HandlesExprCall.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs) == 0: + if HandlesExprCall.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs, + md_defaults, md_default_count, md_param_count) == 0: trans._func_count = func_count + 1 # 添加 self 参数(Ptr(struct_ty)) @@ -2197,7 +3037,7 @@ def _translate_method(trans: HT.Translator | t.CPtr, param_ty: llvmlite.LLVMType | t.CPtr = i32_ty if arg.annotation is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, arg.annotation, imported_modules, from_imports) + pool, arg.annotation, imported_modules, from_imports, trans) if resolved is not None: param_ty = resolved pname: t.CChar | t.CPtr = pool.alloc(32) @@ -2217,7 +3057,7 @@ def _translate_method(trans: HT.Translator | t.CPtr, llvmlite.position_at_end(func_builder, entry_blk) # 进入函数作用域 - HandlesVar.enter_scope(trans.SymTab, HandlesVar.SCOPE_FUNCTION) + HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION) # 注册 self 为 SSA 值(不创建 alloca,不 store) # 这样 self.field 通过 lookup_var 获取 Ptr(struct_ty) 后直接 GEP @@ -2238,7 +3078,7 @@ def _translate_method(trans: HT.Translator | t.CPtr, param_ty2: llvmlite.LLVMType | t.CPtr = i32_ty if arg2.annotation is not None: resolved2: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, arg2.annotation, imported_modules, from_imports) + pool, arg2.annotation, imported_modules, from_imports, trans) if resolved2 is not None: param_ty2 = resolved2 alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, param_ty2) @@ -2306,13 +3146,23 @@ def _translate_method(trans: HT.Translator | t.CPtr, last_is_return = 1 if last_is_return == 0: if llvmlite.builder_cur_block_is_terminated(func_builder) == 0: - # __new__ 返回指针类型,隐式返回 null 指针 - if string.strcmp(method_name, "__new__") == 0: - null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null") - llvmlite.build_ret(func_builder, null_val) + # 根据返回类型生成正确的零值返回 + _m_is_void: int = 0 + _m_is_ptr: int = 0 + if ret_ty is not None: + match ret_ty: + case llvmlite.LLVMType.Void(): + _m_is_void = 1 + case llvmlite.LLVMType.Ptr(_m_pe): + _m_is_ptr = 1 + if _m_is_void != 0: + llvmlite.build_ret_void(func_builder) + elif _m_is_ptr != 0: + _m_null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null") + llvmlite.build_ret(func_builder, _m_null_val) else: - zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) - llvmlite.build_ret(func_builder, zero_val) + _m_zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) + llvmlite.build_ret(func_builder, _m_zero_val) # 恢复模块级作用域 HandlesVar.exit_scope(trans.SymTab) diff --git a/App/lib/core/Handles/HandlesEnum.py b/App/lib/core/Handles/HandlesEnum.py index cdae79f..bb4bb55 100644 --- a/App/lib/core/Handles/HandlesEnum.py +++ b/App/lib/core/Handles/HandlesEnum.py @@ -4,6 +4,8 @@ import memhub import string import llvmlite import stdio +import viperlib +import lib.core.VLogger as VLogger # ============================================================ @@ -110,7 +112,10 @@ def register_enum(pool: memhub.MemBuddy | t.CPtr, return existing if _enum_count >= ENUM_MAX: - stdio.printf("[ENUM] table full, cannot register %s\n", name) + fb_et: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_et is not None: + viperlib.snprintf(fb_et, 1024, "table full, cannot register %s", name) + VLogger.error(fb_et, "ENUM") return None entry: EnumEntry | t.CPtr = _get_enum_entry(_enum_count) @@ -151,7 +156,10 @@ def add_enum_member(pool: memhub.MemBuddy | t.CPtr, return -1 if enum_entry.MemberCount >= ENUM_MEMBER_MAX: - stdio.printf("[ENUM] member table full for %s\n", enum_entry.Name) + fb_em: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_em is not None: + viperlib.snprintf(fb_em, 1024, "member table full for %s", enum_entry.Name) + VLogger.error(fb_em, "ENUM") return -1 idx: int = enum_entry.MemberCount diff --git a/App/lib/core/Handles/HandlesExpr.py b/App/lib/core/Handles/HandlesExpr.py index 3817b94..abb75bb 100644 --- a/App/lib/core/Handles/HandlesExpr.py +++ b/App/lib/core/Handles/HandlesExpr.py @@ -4,7 +4,10 @@ import ast import memhub import string import llvmlite +import stdlib +import stdio import viperlib +import w32.fileio as fileio import lib.core.Handles.HandlesBase as HandlesBase import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesVar as HandlesVar @@ -16,6 +19,9 @@ import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesEnum as HandlesEnum import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.Handles.HandlesImports as HandlesImports +import lib.core.StubMerger as StubMerger +import lib.core.VLogger as VLogger +import lib.Projectrans.Config as Config # ============================================================ @@ -140,6 +146,47 @@ def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int: return 0 +# ============================================================ +# 检查类型是否为 Array(独立函数,避免嵌套 match 的编译器 BUG) +# ============================================================ +def is_array_type(ty: llvmlite.LLVMType | t.CPtr) -> int: + """检查 ty 是否是 Array 类型,返回 1=是, 0=否""" + if ty is None: + return 0 + match ty: + case llvmlite.LLVMType.Array(_, _): + return 1 + case _: + return 0 + + +# ============================================================ +# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体 +# +# 处理两种情况: +# - ty 直接是 Struct → 检查是否注册 +# - ty 是 Ptr(Struct) → 检查 pointee 是否注册 +# +# 返回类名字符串或 None。用于 subscript 操作中判断是否需要 +# 转发到 __getitem__/__setitem__ 而非生成 GEP。 +# ============================================================ +def _get_custom_struct_cls_nm(pool: memhub.MemBuddy | t.CPtr, + ty: llvmlite.LLVMType | t.CPtr) -> str: + """检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None""" + if ty is None: + return None + # 直接检查 ty 是否是注册的结构体 + cls_nm_direct: str = HandlesStruct.get_class_name_by_type(pool, ty) + if cls_nm_direct is not None: + return cls_nm_direct + # 如果 ty 是 Ptr,检查 pointee + if is_ptr_type(ty) != 0: + pointee_cs: llvmlite.LLVMType | t.CPtr = ty.Pointee + if pointee_cs is not None: + return HandlesStruct.get_class_name_by_type(pool, pointee_cs) + return None + + # ============================================================ # _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用 # @@ -186,6 +233,13 @@ def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr, # 整数转换 val_bits: int = get_llvm_type_bits(val.Ty) target_bits: int = get_llvm_type_bits(target_ty) + # 整数 → 指针: inttoptr(必须在纯整数转换之前检查,因为 ptr 的 bits 非零会误入 sext/trunc 路径) + # 适用于: 跨模块方法返回 i64/i32(默认推断),目标变量是指针类型 + if val_bits != 0 and is_ptr_type(target_ty) != 0: + return llvmlite.build_inttoptr(builder, val, target_ty) + # 指针 → 整数: ptrtoint(当目标明确是整数而非指针时) + if is_ptr_type(val.Ty) != 0 and target_bits != 0 and is_ptr_type(target_ty) == 0: + return llvmlite.build_ptrtoint(builder, val, target_ty) if val_bits != 0 and target_bits != 0: if val_bits == target_bits: return val @@ -207,10 +261,6 @@ def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr, # float → int if val_fbits != 0 and target_bits != 0: return llvmlite.build_fp2si(builder, val, target_ty) - # 整数 → 指针: inttoptr - # 适用于: 跨模块方法返回 i64(默认推断),目标变量是指针类型 - if val_bits != 0 and is_ptr_type(target_ty) != 0: - return llvmlite.build_inttoptr(builder, val, target_ty) # 指针 → 非指针值: build_load 解引用 # 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值 # 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load @@ -234,6 +284,8 @@ def create_global_string(builder: llvmlite.IRBuilder | t.CPtr, """创建全局字符串常量并返回 i8* bitcast""" escaped: t.CChar | t.CPtr = escape_llvm_string(pool, str_val) if escaped is None: + stdio.printf("[CGS] escape_llvm_string None\n") + stdio.fflush(0) return None slen: t.CSizeT = string.strlen(str_val) @@ -249,6 +301,8 @@ def create_global_string(builder: llvmlite.IRBuilder | t.CPtr, # 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名 gv_name: t.CChar | t.CPtr = pool.alloc(48) if gv_name is None: + stdio.printf("[CGS] gv_name alloc None\n") + stdio.fflush(0) return None if trans.ModuleSha1 is not None: viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx) @@ -257,6 +311,8 @@ def create_global_string(builder: llvmlite.IRBuilder | t.CPtr, gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name, arr_ty) if gv is None: + stdio.printf("[CGS] new_global_variable None name=%s\n", gv_name) + stdio.fflush(0) return None llvmlite.module_add_global(mod, gv) @@ -268,13 +324,19 @@ def create_global_string(builder: llvmlite.IRBuilder | t.CPtr, arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty) gv_ref_name: t.CChar | t.CPtr = pool.alloc(64) if gv_ref_name is None: + stdio.printf("[CGS] gv_ref_name alloc None\n") + stdio.fflush(0) return None viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name) gv_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name) i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) - return llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty) + bc: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty) + if bc is None: + stdio.printf("[CGS] build_bitcast None\n") + stdio.fflush(0) + return bc # ============================================================ @@ -288,7 +350,12 @@ def translate_constant(builder: llvmlite.IRBuilder | t.CPtr, """翻译常量(int/str/bool)""" cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) if cn is None: + stdio.printf("[TC] cn is None\n") + stdio.fflush(0) return None + ck: int = cn.const_kind + stdio.printf("[TC] const_kind=%d\n", ck) + stdio.fflush(0) if cn.const_kind == ast.CONST_INT: # 超出 i32 范围则用 i64(避免常量创建时被截断) iv: t.CInt64T = cn.int_val @@ -300,16 +367,50 @@ def translate_constant(builder: llvmlite.IRBuilder | t.CPtr, double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool) return llvmlite.ConstFloat(pool, double_ty, cn.float_val) elif cn.const_kind == ast.CONST_STR: - return create_global_string(builder, pool, mod, cn.str_val, trans) + sv: str = cn.str_val + if sv is None: + stdio.printf("[TC] CONST_STR but str_val is None\n") + stdio.fflush(0) + return None + stdio.printf("[TC] CONST_STR sv[0]=%d\n", sv[0]) + stdio.fflush(0) + r: llvmlite.Value | t.CPtr = create_global_string(builder, pool, mod, sv, trans) + if r is None: + stdio.printf("[TC] create_global_string returned None\n") + stdio.fflush(0) + return r elif cn.const_kind == ast.CONST_BOOL: if cn.int_val != 0: return llvmlite.const_int32(pool, 1) return llvmlite.const_int32(pool, 0) elif cn.const_kind == ast.CONST_NONE: # None → i8* null(空指针常量),用于 `p is None` / `p is not None` 比较 + stdio.printf("[TC] NONE step1 Int8\n") + stdio.fflush(0) i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + if i8_ty is None: + stdio.printf("[TC] NONE Int8 alloc None\n") + stdio.fflush(0) + return None + stdio.printf("[TC] NONE step2 Ptr\n") + stdio.fflush(0) i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) - return llvmlite.ConstNull(pool, i8_ptr_ty, "null") + if i8_ptr_ty is None: + stdio.printf("[TC] NONE Ptr alloc None\n") + stdio.fflush(0) + return None + stdio.printf("[TC] NONE step3 ConstNull\n") + stdio.fflush(0) + rv_none: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null") + if rv_none is None: + stdio.printf("[TC] NONE ConstNull None\n") + stdio.fflush(0) + else: + stdio.printf("[TC] NONE ok\n") + stdio.fflush(0) + return rv_none + stdio.printf("[TC] unknown const_kind=%d\n", ck) + stdio.fflush(0) return None @@ -447,20 +548,30 @@ def translate_list_literal(builder: llvmlite.IRBuilder | t.CPtr, return None # alloca 临时 list 空间 + # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) + HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty) if tmp is None: return None - # 调用 __new__(tmp, pool_val) → storage_ptr - new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32) - if new_args is None: - return None - string.memset(new_args, 0, 8 * 32) - new_args[0] = t.CSizeT(pool_val) - storage_ptr: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr( - pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans) - if storage_ptr is None: - storage_ptr = tmp + # 存储指针:默认用 alloca,如果有 __new__ 则用 __new__ 返回的指针 + # __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用 + # (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致, + # 避免为无 __new__ 的类生成过度声明) + storage_ptr: llvmlite.Value | t.CPtr = tmp + has_new_flag: int = 0 + if struct_entry is not None: + has_new_flag = struct_entry.HasNew + if has_new_flag != 0: + new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32) + if new_args is None: + return None + string.memset(new_args, 0, 8 * 32) + new_args[0] = t.CSizeT(pool_val) + new_ptr: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr( + pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans) + if new_ptr is not None: + storage_ptr = new_ptr # 调用 __before_init__(storage_ptr) HandlesExprCall._call_method_on_ptr( @@ -520,7 +631,14 @@ def translate_value(builder: llvmlite.IRBuilder | t.CPtr, return None k: int = node.kind() if k == ast.ASTKind.Constant: - return translate_constant(builder, pool, mod, node, trans) + rv: llvmlite.Value | t.CPtr = translate_constant(builder, pool, mod, node, trans) + if rv is None: + stdio.printf("[TV] Constant returned None\n") + stdio.fflush(0) + else: + stdio.printf("[TV] Constant ok\n") + stdio.fflush(0) + return rv elif k == ast.ASTKind.Name: return translate_name_value(builder, pool, node, trans) elif k == ast.ASTKind.BinOp: @@ -541,9 +659,145 @@ def translate_value(builder: llvmlite.IRBuilder | t.CPtr, return translate_attribute(builder, pool, mod, node, trans) elif k == ast.ASTKind.List: return translate_list_literal(builder, pool, mod, node, None, trans) + elif k == ast.ASTKind.IfExp: + return translate_ifexp(builder, pool, mod, node, trans) return None +# ============================================================ +# 翻译三元表达式 IfExp(test, body, orelse) +# +# 生成: cond ? body : orelse +# then_blk: 翻译 body → br merge +# else_blk: 翻译 orelse → br merge +# merge_blk: phi [body_val, then_blk], [orelse_val, else_blk] +# ============================================================ +def translate_ifexp(builder: llvmlite.IRBuilder | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + node: ast.AST | t.CPtr, + trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: + """翻译三元表达式 (body if test else orelse),返回 phi 结果 Value""" + if node is None or builder is None or trans is None: + return None + ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node) + if ie is None: + return None + + func: llvmlite.Function | t.CPtr = trans._cur_func + if func is None: + stdio.printf("[IFEXP] func=None\n") + stdio.fflush(0) + return None + + # 1. 求值条件 + cond_val: llvmlite.Value | t.CPtr = translate_value( + builder, pool, mod, ie.test, None, 0, trans) + if cond_val is None: + stdio.printf("[IFEXP] cond_val=None\n") + stdio.fflush(0) + return None + # 转换为 i1 + cond_bits: int = get_llvm_type_bits(cond_val.Ty) + if cond_bits == 1: + cond_i1: llvmlite.Value | t.CPtr = cond_val + else: + zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) + cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero) + + # 2. 创建三个基本块 + cnt: int = trans._label_counter + trans._label_counter = cnt + 1 + name_buf: t.CChar | t.CPtr = pool.alloc(32) + if name_buf is None: + return None + viperlib.snprintf(name_buf, 32, "ifexp.then.%d", cnt) + then_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "ifexp.else.%d", cnt) + else_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "ifexp.merge.%d", cnt) + merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + + if then_bb is None or else_bb is None or merge_bb is None: + return None + + # 3. 条件跳转 + llvmlite.build_cond_br(builder, cond_i1, then_bb, else_bb) + + # 4. then 分支 + llvmlite.position_at_end(builder, then_bb) + body_val: llvmlite.Value | t.CPtr = translate_value( + builder, pool, mod, ie.body, None, 0, trans) + then_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock + if body_val is None: + body_val = llvmlite.const_int32(pool, 0) + # 暂不 br,等类型对齐后再 br + + # 5. else 分支 + llvmlite.position_at_end(builder, else_bb) + orelse_val: llvmlite.Value | t.CPtr = translate_value( + builder, pool, mod, ie.orelse, None, 0, trans) + else_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock + if orelse_val is None: + orelse_val = llvmlite.const_int32(pool, 0) + # 暂不 br,等类型对齐后再 br + + # 类型对齐 (两分支类型可能不同) + # 必须在各自分支中执行 coerce_to_type,确保 merge 块中 PHI 是第一条指令 + # (否则 coerce_to_type 生成的 sext/zext/ptrtoint/inttoptr 指令在 PHI 之前,违反 LLVM 规则) + body_bits: int = get_llvm_type_bits(body_val.Ty) + orelse_bits: int = get_llvm_type_bits(orelse_val.Ty) + body_is_ptr: int = is_ptr_type(body_val.Ty) + orelse_is_ptr: int = is_ptr_type(orelse_val.Ty) + # 情况0: 一侧是指针,另一侧是整数(如 i64 vs i8* null) + # 必须优先处理,因为 Ptr 的 bits=0 会导致下面三个分支全部跳过 + # 选择 body_val.Ty 作为 phi_ty(then 分支优先),orelse_val 对齐到它 + # coerce_to_type 内部会调用 ptrtoint(指针→整数)或 inttoptr(整数→指针) + if body_is_ptr != orelse_is_ptr: + llvmlite.position_at_end(builder, else_blk_for_phi) + orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty) + elif body_bits > orelse_bits and orelse_bits > 0: + # 在 else 块中转换 orelse_val + llvmlite.position_at_end(builder, else_blk_for_phi) + orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty) + elif orelse_bits > body_bits and body_bits > 0: + # 回到 then 块转换 body_val + llvmlite.position_at_end(builder, then_blk_for_phi) + body_val = coerce_to_type(builder, body_val, orelse_val.Ty) + elif body_bits == orelse_bits and body_bits > 0: + # bits 相同但类型不同(如 i64 vs Ptr):null 不能用于 i64 PHI + # 此分支理论上不会触发(已被情况0覆盖),保留作为防御 + if is_ptr_type(body_val.Ty) != is_ptr_type(orelse_val.Ty): + llvmlite.position_at_end(builder, else_blk_for_phi) + orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty) + + # 现在分别在 then/else 块中 br 到 merge(在类型转换之后) + llvmlite.position_at_end(builder, then_blk_for_phi) + llvmlite.build_br(builder, merge_bb) + llvmlite.position_at_end(builder, else_blk_for_phi) + llvmlite.build_br(builder, merge_bb) + + # 6. merge 块: phi(PHI 是 merge 块的第一条指令) + llvmlite.position_at_end(builder, merge_bb) + + phi_ty: llvmlite.LLVMType | t.CPtr = body_val.Ty + if phi_ty is None: + phi_ty = llvmlite.Int32(pool) + + inc1: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming( + pool, body_val, then_blk_for_phi) + inc2: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming( + pool, orelse_val, else_blk_for_phi) + if inc1 is None or inc2 is None: + return None + # 链表顺序: inc1 (head) -> inc2 (tail) + # 之前错误写成 inc2.Next = inc1,导致 build_phi 从 inc1 遍历时 + # inc1.Next 为 None,只输出一个 phi 条目,引发 LLVM 验证错误: + # "PHINode should have one entry for each predecessor" + inc1.Next = inc2 + return llvmlite.build_phi(builder, phi_ty, inc1, 2) + + # ============================================================ # 翻译比较表达式 Compare(left, ops, comparators) # ============================================================ @@ -564,10 +818,14 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr, comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators if comparators is None or comparators.__len__() == 0: + stdio.printf("[CMP] comparators empty\n") + stdio.fflush(0) return None rhs: llvmlite.Value | t.CPtr = translate_value( builder, pool, mod, comparators.get(0), None, 0, trans) if rhs is None: + stdio.printf("[CMP] rhs=None\n") + stdio.fflush(0) return None # === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 === @@ -588,6 +846,8 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr, lhs: llvmlite.Value | t.CPtr = translate_value( builder, pool, mod, cmp.left, None, 0, trans) if lhs is None: + stdio.printf("[CMP] lhs=None\n") + stdio.fflush(0) return None # === 比较运算符重载路径 2: lhs 是 Ptr(Struct) === @@ -660,18 +920,31 @@ def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr, if uo.op == ast.OpKind.USub: # -x = 0 - x + # 根据 operand 类型选择零值常量(避免 i32 与 i64 类型不匹配) + operand_bits: int = get_llvm_type_bits(operand.Ty) zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) + if operand_bits == 64: + zero = llvmlite.const_int64(pool, 0) + elif operand_bits > 0 and operand_bits != 32: + # 其他位宽(i8/i16 等):用 i32 零值,operand 会被 coerce + zero = llvmlite.const_int32(pool, 0) return llvmlite.build_sub(builder, zero, operand) elif uo.op == ast.OpKind.UAdd: # +x = x return operand elif uo.op == ast.OpKind.Not: # not x = (x == 0),返回 i1 + operand_bits_not: int = get_llvm_type_bits(operand.Ty) zero = llvmlite.const_int32(pool, 0) + if operand_bits_not == 64: + zero = llvmlite.const_int64(pool, 0) return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero) elif uo.op == ast.OpKind.Invert: # ~x = x ^ -1 + operand_bits_inv: int = get_llvm_type_bits(operand.Ty) neg1: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, -1) + if operand_bits_inv == 64: + neg1 = llvmlite.const_int64(pool, -1) return llvmlite.build_xor(builder, operand, neg1) return operand @@ -797,6 +1070,610 @@ def translate_boolop(builder: llvmlite.IRBuilder | t.CPtr, return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count) +# ============================================================ +# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .py 文件路径 +# +# .pyi 文件可能未生成(Phase1 未输出 pyi),此时回退读取源 .py 文件。 +# 从 StubMerger 全局存储器查找 SHA1 对应的相对路径, +# 根据 rel_path 前缀选择基础目录: +# - rel_path 以 "lib/" 开头 → App 源文件,用 {Config.SourceDir}/{rel_path} +# - 其他 → includes 文件,用 {Config.IncludesDir}/{rel_path} +# +# Returns: +# stdlib.malloc 分配的路径字符串(调用者负责 free),None 失败 +# ============================================================ +def _build_source_path_from_sha1(sha1: str) -> str: + """通过 SHA1 从全局存储器构建源 .py 文件路径""" + if sha1 is None: + return None + if Config.IncludesDir is None: + return None + # 从 StubMerger 全局存储器获取相对路径数组 + rel_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() + count: int = StubMerger.GetSha1StoreCount() + if rel_arr_ptr is None or count <= 0: + return None + sha1_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr() + if sha1_arr_ptr is None: + return None + # 遍历查找匹配的 SHA1 + idx_s: int = -1 + for i in range(count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + cur_sha1: str = sha1_arr_ptr + sidx + if cur_sha1[0] == '\0': + continue + if string.strcmp(cur_sha1, sha1) == 0: + idx_s = i + break + if idx_s < 0: + return None + # 获取相对路径(MAX_REL_PATH_LEN=256,避免跨模块 CDefine 解析问题) + MAX_RPL: t.CSizeT = 256 + ridx: t.CSizeT = t.CSizeT(idx_s) * MAX_RPL + rel_path: str = rel_arr_ptr + ridx + if rel_path[0] == '\0': + return None + # 选择基础目录:rel_path 以 "lib/" 开头 → App 源文件(SourceDir),否则 includes 文件 + base_dir: str = Config.IncludesDir + is_app: int = 0 + if string.strlen(rel_path) >= 4: + if (rel_path[0] == 'l' and rel_path[1] == 'i' and + rel_path[2] == 'b' and rel_path[3] == '/'): + is_app = 1 + if is_app != 0 and Config.SourceDir is not None: + base_dir = Config.SourceDir + # 构建完整路径: {base_dir}/{rel_path} + base_len: t.CSizeT = string.strlen(base_dir) + rel_len: t.CSizeT = string.strlen(rel_path) + path_len: t.CSizeT = base_len + rel_len + 2 + buf: str = stdlib.malloc(path_len) + if buf is None: + return None + viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_path) + return buf + + +# ============================================================ +# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量 +# +# 当当前模块的 CDefine 表中找不到某个 Name 时,从 from_imports +# 解析出源模块,再从该模块的 pyi 文件中解析 CDefine 常量值。 +# 找到后缓存到当前模块的 CDefine 表中,避免重复查找。 +# +# pyi 文件格式: "NAME: t.CDefine = value" +# ============================================================ +def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr, + name: str, + from_imports: str) -> int: + """跨模块查找 CDefine 常量,返回值或 -1(未找到)""" + # [XMOD-CD] 诊断:记录跨模块 CDefine 查找入口 + _xmod_log_buf: str = pool.alloc(512) + if _xmod_log_buf is not None: + viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] enter name=%s from_imports=%s\n", name, from_imports) + _xmod_lf: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf.closed: + _xmod_lf.write_str(_xmod_log_buf) + _xmod_lf.close() + + if name is None or from_imports is None: + return -1 + + # 1. 从 from_imports 查找名称对应的模块名 + # allow_star_fallback=1: CDefine 常量(如 INVALID_HANDLE_VALUE)通过 + # from w32.win32base import * 导入,不会作为精确条目出现在 from_imports 中, + # 而是作为 *:w32.win32base 条目。必须启用 star import 回退才能找到源模块。 + mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 1) + if mod_name_raw is None: + if _xmod_log_buf is not None: + viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] FAIL step1 mod=None name=%s\n", name) + _xmod_lf2: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf2.closed: + _xmod_lf2.write_str(_xmod_log_buf) + _xmod_lf2.close() + return -1 + + # 2. 复制模块名到新缓冲区(lookup_from_import 返回的是内部指针) + # 截断于空格、null、或 ':'(别名格式的分隔符) + mod_len: t.CSizeT = 0 + while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':': + mod_len += 1 + mod_name_buf: str = pool.alloc(mod_len + 1) + if mod_name_buf is None: + return -1 + for mi in range(mod_len): + mod_name_buf[mi] = mod_name_raw[mi] + mod_name_buf[mod_len] = '\0' + + # 3. 去掉相对导入的前导点 + base_mod: str = mod_name_buf + while base_mod[0] == '.': + base_mod = base_mod + 1 + + # 4. 查找模块的 SHA1 + sha1: str = HandlesExprCall._lookup_module_sha1(base_mod) + if sha1 is None: + sha1 = HandlesExprCall._lookup_module_sha1_suffix(base_mod) + if sha1 is None: + if _xmod_log_buf is not None: + viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] FAIL step4 sha1=None base_mod=%s\n", base_mod) + _xmod_lf3: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf3.closed: + _xmod_lf3.write_str(_xmod_log_buf) + _xmod_lf3.close() + return -1 + + # [XMOD-CD] 诊断:记录 SHA1 查找成功 + if _xmod_log_buf is not None: + viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] ok step4 base_mod=%s sha1=%s\n", base_mod, sha1) + _xmod_lf4: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf4.closed: + _xmod_lf4.write_str(_xmod_log_buf) + _xmod_lf4.close() + + # 4.5 优先查全局跨模块 CDefine 表(编译期注册,无需文件 I/O) + gcdef_val: int = HandlesType.lookup_global_cdefine(sha1, name) + if HandlesType.is_cdefine_found() != 0: + return gcdef_val + + # 5. 获取 temp_dir + temp_dir: str = HandlesType.get_temp_dir() + if temp_dir is None: + return -1 + + # 6. 构建 pyi 文件路径 + td_len: t.CSizeT = string.strlen(temp_dir) + pyi_path: str = StubMerger._sliced_path(temp_dir, td_len, sha1, "pyi") + if pyi_path is None: + return -1 + + # 7. 读取 pyi 文件;若不存在或内容过短(空 stub)则回退读取源 .py 文件 + PYI_READ_BUF_SIZE: t.CSizeT = 65536 + pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE) + if pyi_buf is None: + stdlib.free(pyi_path) + return -1 + pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.R) + need_py_fallback: int = 0 + bytes_read: LONG = 0 + if pf.closed: + # .pyi 文件不存在:回退读取源 .py 文件 + need_py_fallback = 1 + else: + bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE) + pf.close() + # pyi 内容过短(< 10 字节)视为空 stub,回退 .py + if bytes_read < 10: + need_py_fallback = 1 + else: + if bytes_read < PYI_READ_BUF_SIZE: + pyi_buf[bytes_read] = 0 + else: + pyi_buf[PYI_READ_BUF_SIZE - 1] = 0 + stdlib.free(pyi_path) + + if need_py_fallback != 0: + # 从 SHA1 存储器查找相对路径,构建 {base_dir}/{rel_path} + src_path: str = _build_source_path_from_sha1(sha1) + if src_path is None: + # 存储器中找不到 SHA1 = 编译器 bug(Phase1/Phase2 未填充存储器) + # 报错终止,不静默返回 -1(避免后续生成错误的 @UNDEFINED 全局变量引用) + err_buf: str = pool.alloc(512) + if err_buf is not None: + viperlib.snprintf(err_buf, 512, + "跨模块 CDefine 查找失败:SHA1 %s 不在存储器中(base_mod=%s name=%s)。" + "Phase1/Phase2 未正确填充 PopulateSha1MapStore/AppendToSha1MapStore", + sha1, base_mod, name) + VLogger.error(err_buf, "XMOD-CD") + stdlib.free(pyi_buf) + return -1 + # [XMOD-CD] 诊断:记录 src_path + _xmod_dbg_sp2: str = pool.alloc(512) + if _xmod_dbg_sp2 is not None: + viperlib.snprintf(_xmod_dbg_sp2, 512, + "[XMOD-CD] py-fallback src_path=%s sha1=%s\n", src_path, sha1) + _xmod_lf_sp2: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf_sp2.closed: + _xmod_lf_sp2.write_str(_xmod_dbg_sp2) + _xmod_lf_sp2.close() + pf = fileio.File(src_path, fileio.MODE.R) + stdlib.free(src_path) + if pf.closed: + # [XMOD-CD] 诊断:文件打开失败 + _xmod_dbg_fc: str = pool.alloc(512) + if _xmod_dbg_fc is not None: + viperlib.snprintf(_xmod_dbg_fc, 512, + "[XMOD-CD] FAIL file-closed sha1=%s\n", sha1) + _xmod_lf_fc: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf_fc.closed: + _xmod_lf_fc.write_str(_xmod_dbg_fc) + _xmod_lf_fc.close() + stdlib.free(pyi_buf) + return -1 + bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE) + pf.close() + if bytes_read <= 0: + # [XMOD-CD] 诊断:文件读取失败 + _xmod_dbg_br: str = pool.alloc(512) + if _xmod_dbg_br is not None: + viperlib.snprintf(_xmod_dbg_br, 512, + "[XMOD-CD] FAIL bytes_read=%d sha1=%s\n", bytes_read, sha1) + _xmod_lf_br: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf_br.closed: + _xmod_lf_br.write_str(_xmod_dbg_br) + _xmod_lf_br.close() + stdlib.free(pyi_buf) + return -1 + if bytes_read < PYI_READ_BUF_SIZE: + pyi_buf[bytes_read] = 0 + else: + pyi_buf[PYI_READ_BUF_SIZE - 1] = 0 + + # 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行 + # 格式: NAME: t.CDefine = value + name_len: t.CSizeT = string.strlen(name) + total_len: t.CSizeT = string.strlen(pyi_buf) + # [XMOD-CD] 诊断:记录文件读取结果和 total_len + _xmod_dbg1: str = pool.alloc(512) + if _xmod_dbg1 is not None: + _dbg_first80: str = pool.alloc(81) + if _dbg_first80 is not None: + _dbg_n: int = 0 + while _dbg_n < 80 and _dbg_n < total_len: + _dbg_first80[_dbg_n] = pyi_buf[_dbg_n] + _dbg_n += 1 + _dbg_first80[_dbg_n] = '\0' + else: + _dbg_first80 = "" + viperlib.snprintf(_xmod_dbg1, 512, "[XMOD-CD] file-read name=%s bytes_read=%d total_len=%d first80=%.80s\n", + name, bytes_read, total_len, _dbg_first80) + _xmod_lf_d1: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf_d1.closed: + _xmod_lf_d1.write_str(_xmod_dbg1) + _xmod_lf_d1.close() + pos: t.CSizeT = 0 + result_val: int = 0 + result_found: int = 0 + while pos < total_len: + # 找到行首 + line_start: t.CSizeT = pos + # 找到行尾 + while pos < total_len and pyi_buf[pos] != '\n': + pos += 1 + line_len: t.CSizeT = pos - line_start + if pos < total_len: + pos += 1 # 跳过 '\n' + + # 检查行是否以 "name:" 开头 + if line_len < name_len + 1: + continue + match_ok: int = 1 + for ki in range(name_len): + if pyi_buf[line_start + ki] != name[ki]: + match_ok = 0 + break + if match_ok == 0: + continue + if pyi_buf[line_start + name_len] != ':': + continue + + # 检查行是否包含 "t.CDefine" + # 在行中查找 "t.CDefine" + has_cdefine: int = 0 + ci: t.CSizeT = line_start + name_len + 1 + while ci + 8 <= line_start + line_len: + if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and + pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and + pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and + pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and + pyi_buf[ci + 8] == 'e'): + has_cdefine = 1 + break + ci += 1 + if has_cdefine == 0: + continue + + # 找到 "=" 后面的值 + eq_pos: t.CSizeT = line_start + name_len + 1 + while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=': + eq_pos += 1 + if eq_pos >= line_start + line_len: + continue + eq_pos += 1 # 跳过 '=' + + # 跳过空格 + while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': + eq_pos += 1 + + # 解析整数值(支持十六进制 0x 前缀) + val_str_start: t.CSizeT = eq_pos + val_str_len: t.CSizeT = 0 + is_hex: int = 0 + if eq_pos + 1 < line_start + line_len: + if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'): + is_hex = 1 + val_str_len = 2 + eq_pos += 2 + while eq_pos < line_start + line_len: + ch: t.CChar = pyi_buf[eq_pos] + if is_hex == 1: + if ((ch >= '0' and ch <= '9') or + (ch >= 'a' and ch <= 'f') or + (ch >= 'A' and ch <= 'F')): + val_str_len += 1 + eq_pos += 1 + else: + break + else: + if ch >= '0' and ch <= '9': + val_str_len += 1 + eq_pos += 1 + else: + break + + if val_str_len == 0: + continue + + # 复制值字符串到新缓冲区并解析 + val_buf: str = pool.alloc(val_str_len + 1) + if val_buf is None: + continue + for vi in range(val_str_len): + val_buf[vi] = pyi_buf[val_str_start + vi] + val_buf[val_str_len] = '\0' + + if is_hex == 1: + # 手动解析十六进制(跳过 0x 前缀) + hex_result: int = 0 + hex_buf: str = val_buf + 2 # 跳过 "0x" + for hi in range(val_str_len - 2): + hc: t.CChar = hex_buf[hi] + hd: int = 0 + if hc >= '0' and hc <= '9': + hd = hc - '0' + elif hc >= 'a' and hc <= 'f': + hd = hc - 'a' + 10 + elif hc >= 'A' and hc <= 'F': + hd = hc - 'A' + 10 + else: + break + hex_result = hex_result * 16 + hd + result_val = hex_result + result_found = 1 + else: + result_val = string.atoi(val_buf) + result_found = 1 + break + + # 9.5 如果当前模块文件中没找到,递归查找子模块 + # 处理 from .X import NAME 和 from .X import * 的情况 + # 例如 ast.__init__.py 中 from .base import CONST_INT, ... + # 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1 + if result_found == 0: + name_len_rc: t.CSizeT = string.strlen(name) + rc_pos: t.CSizeT = 0 + while rc_pos < total_len and result_found == 0: + # 找到行首 + rc_line_start: t.CSizeT = rc_pos + # 找到行尾 + while rc_pos < total_len and pyi_buf[rc_pos] != '\n': + rc_pos += 1 + rc_line_len: t.CSizeT = rc_pos - rc_line_start + if rc_pos < total_len: + rc_pos += 1 + + # 检查行是否以 "from ." 开头(相对导入子模块) + if rc_line_len < 12: + continue + if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \ + pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \ + pyi_buf[rc_line_start+4] != ' ' or pyi_buf[rc_line_start+5] != '.': + continue + + # 提取子模块名(从 rc_line_start+6 开始,到空格/tab 为止) + rc_sub_start: t.CSizeT = rc_line_start + 6 + rc_sub_end: t.CSizeT = rc_sub_start + while rc_sub_end < rc_line_start + rc_line_len and \ + pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t': + rc_sub_end += 1 + rc_sub_len: t.CSizeT = rc_sub_end - rc_sub_start + if rc_sub_len == 0: + continue + + # [XMOD-CD] 诊断:记录找到的 from . 行和子模块名 + _xmod_dbg_rc: str = pool.alloc(512) + if _xmod_dbg_rc is not None: + _rc_sub_buf_dbg: str = pool.alloc(rc_sub_len + 1) + if _rc_sub_buf_dbg is not None: + _rc_si2: t.CSizeT + for _rc_si2 in range(rc_sub_len): + _rc_sub_buf_dbg[_rc_si2] = pyi_buf[rc_sub_start + _rc_si2] + _rc_sub_buf_dbg[rc_sub_len] = '\0' + else: + _rc_sub_buf_dbg = "" + viperlib.snprintf(_xmod_dbg_rc, 512, + "[XMOD-CD] rc-from-line name=%s sub=%s rc_line_start=%d rc_line_len=%d\n", + name, _rc_sub_buf_dbg, rc_line_start, rc_line_len) + _xmod_lf_rc: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf_rc.closed: + _xmod_lf_rc.write_str(_xmod_dbg_rc) + _xmod_lf_rc.close() + + # 检查行是否包含 "import" + rc_has_import: int = 0 + rc_ipos: t.CSizeT = rc_sub_end + while rc_ipos + 6 <= rc_line_start + rc_line_len: + if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and \ + pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and \ + pyi_buf[rc_ipos+4] == 'r' and pyi_buf[rc_ipos+5] == 't': + rc_has_import = 1 + break + rc_ipos += 1 + if rc_has_import == 0: + continue + + # 检查 NAME 是否在导入列表中,或导入是 * + # 处理多行导入:from .X import (... NAME, ...) + # 从 "import" 之后扫描,如果有 "(" 则扫描到 ")" 为止 + rc_imp_start: t.CSizeT = rc_ipos + 6 + rc_star: int = 0 + rc_name_match: int = 0 + + # 确定扫描范围:单行或跨行(括号内) + rc_scan_end: t.CSizeT = rc_line_start + rc_line_len + rc_has_paren: int = 0 + rc_pp: t.CSizeT = rc_imp_start + while rc_pp < rc_line_start + rc_line_len: + if pyi_buf[rc_pp] == '(': + rc_has_paren = 1 + break + if pyi_buf[rc_pp] == '*': + rc_star = 1 + rc_pp += 1 + + if rc_has_paren != 0: + # 多行导入:从 "(" 之后扫描到 ")" 为止 + rc_scan_end = rc_pp + 1 + rc_paren_closed: int = 0 + rc_sp: t.CSizeT = rc_pp + 1 + while rc_sp < total_len and rc_paren_closed == 0: + if pyi_buf[rc_sp] == ')': + rc_paren_closed = 1 + rc_scan_end = rc_sp + break + rc_sp += 1 + if rc_paren_closed == 0: + rc_scan_end = total_len + + # 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 * + rc_cpos: t.CSizeT = rc_imp_start + while rc_cpos < rc_scan_end: + rc_ch: t.CChar = pyi_buf[rc_cpos] + if rc_ch == '*': + rc_star = 1 + # 检查 NAME 是否匹配(作为完整单词) + if rc_cpos + name_len_rc <= rc_scan_end: + rc_match: int = 1 + rc_ki: t.CSizeT + for rc_ki in range(name_len_rc): + if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]: + rc_match = 0 + break + if rc_match == 1: + # 检查前字符是否是非标识符字符 + rc_before_ok: int = 0 + if rc_cpos == rc_imp_start: + rc_before_ok = 1 + else: + rc_before_ch: t.CChar = pyi_buf[rc_cpos - 1] + if rc_before_ch == ' ' or rc_before_ch == '\t' or \ + rc_before_ch == '(' or rc_before_ch == ',' or \ + rc_before_ch == '\n' or rc_before_ch == '\r': + rc_before_ok = 1 + # 检查后字符是否是非标识符字符 + rc_after_ok: int = 0 + rc_after_pos: t.CSizeT = rc_cpos + name_len_rc + if rc_after_pos >= rc_scan_end: + rc_after_ok = 1 + else: + rc_after_ch: t.CChar = pyi_buf[rc_after_pos] + if rc_after_ch == ' ' or rc_after_ch == '\t' or \ + rc_after_ch == ')' or rc_after_ch == ',' or \ + rc_after_ch == '\r' or rc_after_ch == '\n': + rc_after_ok = 1 + if rc_before_ok == 1 and rc_after_ok == 1: + rc_name_match = 1 + rc_cpos += 1 + + # [XMOD-CD] 诊断:记录 NAME 匹配结果 + _xmod_dbg_match: str = pool.alloc(512) + if _xmod_dbg_match is not None: + viperlib.snprintf(_xmod_dbg_match, 512, + "[XMOD-CD] rc-match name=%s star=%d name_match=%d has_paren=%d rc_scan_end=%d rc_imp_start=%d\n", + name, rc_star, rc_name_match, rc_has_paren, rc_scan_end, rc_imp_start) + _xmod_lf_m: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf_m.closed: + _xmod_lf_m.write_str(_xmod_dbg_match) + _xmod_lf_m.close() + + if rc_star == 0 and rc_name_match == 0: + continue + + # 复制子模块名到新缓冲区(pyi_buf + rc_sub_start 不是 NUL 结尾) + rc_sub_buf: str = pool.alloc(rc_sub_len + 1) + if rc_sub_buf is None: + continue + rc_si: t.CSizeT + for rc_si in range(rc_sub_len): + rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si] + rc_sub_buf[rc_sub_len] = '\0' + + # 构建完整子模块名 base_mod.X + rc_full_len: t.CSizeT = string.strlen(base_mod) + 1 + rc_sub_len + 1 + rc_full_mod: str = pool.alloc(rc_full_len) + if rc_full_mod is None: + continue + viperlib.snprintf(rc_full_mod, rc_full_len, "%s.%s", base_mod, rc_sub_buf) + + # 构建临时 from_imports "name:full_mod" + rc_fi_len: t.CSizeT = name_len_rc + 1 + string.strlen(rc_full_mod) + 1 + rc_fi: str = pool.alloc(rc_fi_len) + if rc_fi is None: + continue + viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod) + + # [XMOD-CD] 诊断:记录递归调用前的状态 + _xmod_dbg_recurse: str = pool.alloc(512) + if _xmod_dbg_recurse is not None: + viperlib.snprintf(_xmod_dbg_recurse, 512, + "[XMOD-CD] rc-recurse name=%s full_mod=%s fi=%s star=%d name_match=%d has_paren=%d scan_end=%d\n", + name, rc_full_mod, rc_fi, rc_star, rc_name_match, rc_has_paren, rc_scan_end) + _xmod_lf_rec: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf_rec.closed: + _xmod_lf_rec.write_str(_xmod_dbg_recurse) + _xmod_lf_rec.close() + + # 递归调用查找子模块 + rc_sub_val: int = _lookup_cross_module_cdefine(pool, name, rc_fi) + if HandlesType.is_cdefine_found() != 0: + result_val = rc_sub_val + result_found = 1 + + stdlib.free(pyi_buf) + + # 10. 缓存到当前模块的 CDefine 表中 + if result_found != 0: + HandlesType.register_cdefine_constant(pool, name, result_val) + HandlesType.set_cdefine_found(1) + else: + HandlesType.set_cdefine_found(0) + + # [XMOD-CD] 诊断:记录查找结果 + if _xmod_log_buf is not None: + if result_found != 0: + viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] FOUND name=%s val=%d base_mod=%s\n", name, result_val, base_mod) + else: + viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] NOTFOUND name=%s base_mod=%s sha1=%s\n", name, base_mod, sha1) + _xmod_lf5: fileio.File | t.CPtr = fileio.File( + "d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A) + if not _xmod_lf5.closed: + _xmod_lf5.write_str(_xmod_log_buf) + _xmod_lf5.close() + + return result_val + + # ============================================================ # 翻译变量引用(Name 节点)→ load # ============================================================ @@ -815,9 +1692,24 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr, # CDefine 编译期常量: 直接返回整数常量值(不生成运行时代码) # NAME: t.CDefine = value 形式定义的常量在编译期已注册到全局表 cdef_val: int = HandlesType.lookup_cdefine_constant(nm_id) - if cdef_val >= 0: + if HandlesType.is_cdefine_found() != 0: return llvmlite.const_int32(pool, cdef_val) + # 本地 CDefine 表未找到:尝试跨模块查找 + # 对于 from w32.win32base import * 导入的 INVALID_HANDLE_VALUE 等常量, + # CDefine 表在模块切换时被清空,需要从 from_imports 查找来源模块并读取 .pyi + if trans is not None: + if trans._from_imports is not None: + cdef_val = _lookup_cross_module_cdefine(pool, nm_id, trans._from_imports) + if HandlesType.is_cdefine_found() != 0: + return llvmlite.const_int32(pool, cdef_val) + + # 模块别名检查:如果 nm_id 是已导入模块名(如 win32file, fileio), + # 不应被当作普通变量或全局变量,返回 None 让上层处理 + if trans is not None and trans._imported_modules is not None: + if HandlesImports.is_module_imported(trans._imported_modules, nm_id) != 0: + return None + # global 变量:从模块作用域查找 if trans is not None: if HT.is_global_name(trans, nm_id) != 0: @@ -837,6 +1729,13 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr, alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm_id) if alloca is None: + # 跨模块 CDefine 查找(仅当 Name 不是任何变量时才尝试): + # 从 from_imports 解析源模块,再从该模块的 pyi 文件中解析 CDefine 常量值 + # (如 FLAG_IS_ASYNC 从 base.py 导入) + if trans is not None and trans._from_imports is not None: + cross_val: int = _lookup_cross_module_cdefine(pool, nm_id, trans._from_imports) + if cross_val >= 0: + return llvmlite.const_int32(pool, cross_val) return None load_ty: llvmlite.LLVMType | t.CPtr = None @@ -1090,10 +1989,22 @@ def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr, nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value) if nm.id is not None and trans is not None: alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id) + if alloca is None: + stdio.printf("[TS] alloca=None var=%s\n", nm.id) + stdio.fflush(0) + elif alloca.Ty is None: + stdio.printf("[TS] alloca.Ty=None var=%s\n", nm.id) + stdio.fflush(0) if alloca is not None and alloca.Ty is not None: if is_ptr_type(alloca.Ty) != 0: pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee + if pointee is None: + stdio.printf("[TS] pointee=None var=%s\n", nm.id) + stdio.fflush(0) if pointee is not None: + # 不调用 pointee.kind() 避免跨模块引用 LLVMType.kind 符号 + stdio.printf("[TS] var=%s pointee_not_null\n", nm.id) + stdio.fflush(0) # 单层 match(避免嵌套 match 的编译器 bug) match pointee: case llvmlite.LLVMType.Array(elem_ty, count): @@ -1104,6 +2015,8 @@ def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr, return llvmlite.build_load(builder, elem_ty, elem_ptr) return None case llvmlite.LLVMType.Ptr(inner_ty): + stdio.printf("[TS] matched Ptr var=%s\n", nm.id) + stdio.fflush(0) # 检查 inner_ty 是否是 list[T] 类型(泛型类不注册 struct) # list 的 subscript 应该走 __getitem__ 内联路径,而非指针遍历 list_struct_name: str = None @@ -1133,6 +2046,65 @@ def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr, return llvmlite.build_load( builder, elem_ty_list, casted_addr_list) return None + # inner_ty 是 Ptr 说明 alloca.Ty 是三重指针 Ptr(Ptr(Ptr(...))), + # 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。 + # 此时 [i] 应该是指针解引用 (步长 sizeof(ptr)=8), 而非调用 + # 结构体的 __getitem__ 方法 (会错误生成 AST.__getitem__ 调用)。 + # 对比 HashTable|t.CPtr: alloca.Ty=HashTable**, inner_ty=Struct(HashTable) + # (非 Ptr), 仍走下面的方法调用转发路径。 + if is_ptr_type(inner_ty) != 0: + ptr_val_pp: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, pointee, alloca) + if ptr_val_pp is not None: + elem_ptr_pp: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, inner_ty, ptr_val_pp, idx_val) + if elem_ptr_pp is not None: + return llvmlite.build_load(builder, inner_ty, elem_ptr_pp) + return None + # 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr): + # 优先转发到 __getitem__,避免 IsPtrElement 误判为指针遍历 + cls_nm_rd_ptr: str = _get_custom_struct_cls_nm(pool, inner_ty) + if cls_nm_rd_ptr is None: + stdio.printf("[TS] cls_nm=None var=%s\n", nm.id) + stdio.fflush(0) + if not (cls_nm_rd_ptr is None): + stdio.printf("[TS] cls_nm=%s var=%s\n", cls_nm_rd_ptr, nm.id) + stdio.fflush(0) + stdio.printf("[TS] pre_getitem cls=%s\n", cls_nm_rd_ptr) + stdio.fflush(0) + obj_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, pointee, alloca) + if obj_ptr_rd is None: + stdio.printf("[TS] __getitem__ build_load=None\n") + stdio.fflush(0) + if not (obj_ptr_rd is None): + arg_vals_rd_p: t.CSizeT | t.CPtr = pool.alloc(8) + if arg_vals_rd_p is None: + stdio.printf("[TS] __getitem__ alloc=None\n") + stdio.fflush(0) + if not (arg_vals_rd_p is None): + arg_vals_rd_p[0] = t.CSizeT(idx_val) + ret_rd_p: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr( + pool, builder, mod, cls_nm_rd_ptr, "__getitem__", + obj_ptr_rd, arg_vals_rd_p, 1, trans) + if ret_rd_p is None: + stdio.printf("[TS] __getitem__ call=None cls=%s\n", cls_nm_rd_ptr) + stdio.fflush(0) + if not (ret_rd_p is None): + return ret_rd_p + return None + # bytes|t.CPtr / str|t.CPtr: alloca 是 i8**, + # 直接 GEP 按 i8* 步长(8 字节),load i8* + # 仅当 inner_ty 不是自定义结构体时才走指针遍历 + if trans is not None: + ve_pe_ts: HandlesVar.VarEntry | t.CPtr = \ + HandlesVar.lookup_var_entry(trans.SymTab, nm.id) + if ve_pe_ts is not None and ve_pe_ts.IsPtrElement == 1: + elem_ptr_ts: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, pointee, alloca, idx_val) + if elem_ptr_ts is not None: + return llvmlite.build_load(builder, pointee, elem_ptr_ts) + return None # 普通指针遍历: 先 load 指针值,再 GEP ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load( builder, pointee, alloca) @@ -1146,17 +2118,19 @@ def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr, # 自定义类(结构体类型): 转发到 __getitem__ 方法调用 # 如 hashtable[key] → hashtable.__getitem__(key) cls_nm_sub: str = HandlesStruct.get_class_name_by_type(pool, pointee) - if cls_nm_sub is not None: + if cls_nm_sub is None: + pass + if not (cls_nm_sub is None): obj_val_sub: llvmlite.Value | t.CPtr = llvmlite.build_load( builder, pointee, alloca) - if obj_val_sub is not None: + if not (obj_val_sub is None): arg_vals_sub: t.CSizeT | t.CPtr = pool.alloc(8) - if arg_vals_sub is not None: + if not (arg_vals_sub is None): arg_vals_sub[0] = t.CSizeT(idx_val) ret_sub: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr( pool, builder, mod, cls_nm_sub, "__getitem__", obj_val_sub, arg_vals_sub, 1, trans) - if ret_sub is not None: + if not (ret_sub is None): return ret_sub pass @@ -1170,10 +2144,35 @@ def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr, if is_ptr_type(ptr_val.Ty) != 0: elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee if elem_ty2 is not None: - elem_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_gep( + # 数组类型: 双索引 GEP + load 元素 + match elem_ty2: + case llvmlite.LLVMType.Array(arr_elem_ty2, arr_count2): + elem_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, elem_ty2, arr_elem_ty2, ptr_val, idx_val) + if elem_ptr3 is not None: + return llvmlite.build_load(builder, arr_elem_ty2, elem_ptr3) + return None + case _: + pass + # 自定义结构体 (如 self._ht[key]): 转发到 __getitem__ 方法调用 + cls_nm_rd_gen: str = _get_custom_struct_cls_nm(pool, elem_ty2) + if cls_nm_rd_gen is None: + pass + if not (cls_nm_rd_gen is None): + arg_vals_rd_g: t.CSizeT | t.CPtr = pool.alloc(8) + if not (arg_vals_rd_g is None): + arg_vals_rd_g[0] = t.CSizeT(idx_val) + ret_rd_g: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr( + pool, builder, mod, cls_nm_rd_gen, "__getitem__", + ptr_val, arg_vals_rd_g, 1, trans) + if not (ret_rd_g is None): + return ret_rd_g + return None + # 指针类型: 单索引 GEP + load + elem_ptr4: llvmlite.Value | t.CPtr = llvmlite.build_gep( builder, elem_ty2, ptr_val, idx_val) - if elem_ptr3 is not None: - return llvmlite.build_load(builder, elem_ty2, elem_ptr3) + if elem_ptr4 is not None: + return llvmlite.build_load(builder, elem_ty2, elem_ptr4) return None @@ -1269,10 +2268,18 @@ def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr, gv: llvmlite.GlobalVariable | t.CPtr = find_global_in_module(mod, at.attr) if gv is not None and gv.Ty is not None: return make_global_ref(pool, at.attr, gv.Ty) - # 未找到: 创建前向引用 (stub 后续注入时提供声明) + # 未找到: 创建 external global 声明并添加到模块 + # 否则只生成 @_argc 引用而无声明,LLVM 报 "use of undefined value '@_argc'" use_ty: llvmlite.LLVMType | t.CPtr = ty_hint if use_ty is None: use_ty = llvmlite.Int8(pool) + # 确保 use_ty 中的跨模块结构体类型有 opaque 声明 + llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty) + ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty) + if ext_gv is not None: + ext_gv.Linkage = "external" + ext_gv.Initializer = None + llvmlite.module_add_global(mod, ext_gv) return make_global_ref(pool, at.attr, use_ty) @@ -1318,6 +2325,27 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, return llvmlite.const_int32(pool, member.Value) return None + # 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值 + # 当 value 是 Attribute(如 fileio.MODE)且 attr 部分是已注册枚举时 + if at.value.kind() == ast.ASTKind.Attribute: + inner_at_e: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value) + if inner_at_e.value is not None and inner_at_e.value.kind() == ast.ASTKind.Name: + inner_nm_e: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at_e.value) + if inner_nm_e.id is not None and inner_at_e.attr is not None: + if HandlesEnum.is_enum_class(inner_at_e.attr) == 1: + member_e: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member( + inner_at_e.attr, at.attr) + if member_e is not None: + base_ty_e: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type( + inner_at_e.attr) + if base_ty_e is not None: + name_buf_e: t.CChar | t.CPtr = pool.alloc(32) + if name_buf_e is not None: + viperlib.snprintf(name_buf_e, 32, "%lld", member_e.Value) + return llvmlite.ConstInt(pool, base_ty_e, member_e.Value, name_buf_e) + return llvmlite.const_int32(pool, member_e.Value) + return None + # 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy # 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用并加载 if at.value.kind() == ast.ASTKind.Name and trans is not None: @@ -1327,7 +2355,79 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, ma_is_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( trans.SymTab, nm_ma.id) if ma_is_var is None: - # 使用 i8* 作为类型提示 (模块级变量通常存储指针) + # 先检查是否是 CDefine 编译期常量 (如 ast.CONST_INT / win32base.INVALID_HANDLE_VALUE) + # 模块属性访问的 CDefine 常量需要跨模块查找源 .py / .pyi 文件 + cdef_ma_val: int = HandlesType.lookup_cdefine_constant(at.attr) + cd_local: int = HandlesType.is_cdefine_found() + if cd_local == 0: + # 确定真实模块名:优先从 _from_imports 解析别名 + # from_imports 中条目格式 "local_name:real_module" + # 如 win32base:w32.win32base → 别名 win32base 真实模块 w32.win32base + real_mod_name: str = nm_ma.id + if trans._from_imports is not None: + resolved_mod: str = HandlesImports.lookup_from_import( + trans._from_imports, nm_ma.id, 0) + if resolved_mod is not None: + # 复制模块名到新缓冲区(截断于空格、null、或 ':') + rm_len: t.CSizeT = 0 + while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':': + rm_len += 1 + rm_buf: str = pool.alloc(rm_len + 1) + if rm_buf is not None: + for rm_i in range(rm_len): + rm_buf[rm_i] = resolved_mod[rm_i] + rm_buf[rm_len] = '\0' + real_mod_name = rm_buf + # 即使 _from_imports 为 None(如纯 `import ast` 无 from-import), + # 也必须尝试跨模块 CDefine 查找,否则 ast.CONST_INT 会被 + # 误解析为全局变量引用 @CONST_INT(未定义符号)。 + # 构建临时 from_imports "{attr}:{real_module}" 用于跨模块查找 + if real_mod_name is not None: + ma_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(real_mod_name) + 2 + ma_fi: str = pool.alloc(ma_fi_len) + if ma_fi is not None: + viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name) + cdef_ma_val = _lookup_cross_module_cdefine(pool, at.attr, ma_fi) + if HandlesType.is_cdefine_found() != 0: + return llvmlite.const_int32(pool, cdef_ma_val) + # 检查 nm_ma.id.at.attr 是否是已导入子模块 (如 w32.win32file) + # 如果是子模块,不应创建全局变量,让嵌套属性访问路径处理 + is_submod: int = 0 + if trans._imported_modules is not None: + submod_len: t.CSizeT = string.strlen(nm_ma.id) + string.strlen(at.attr) + 2 + submod_buf: t.CChar | t.CPtr = pool.alloc(submod_len) + if submod_buf is not None: + viperlib.snprintf(submod_buf, submod_len, "%s.%s", nm_ma.id, at.attr) + if HandlesImports.is_module_imported(trans._imported_modules, submod_buf) == 1: + is_submod = 1 + # also check from_imports: at.attr 别名对应真实模块 w32.win32file + if is_submod == 0 and trans._from_imports is not None: + fi_mod_raw: str = HandlesImports.lookup_from_import( + trans._from_imports, at.attr, 0) + if fi_mod_raw is not None: + # 复制模块名到新缓冲区(截断于空格、null、或 ':') + fm_len: t.CSizeT = 0 + while fi_mod_raw[fm_len] != ' ' and fi_mod_raw[fm_len] != '\0' and fi_mod_raw[fm_len] != ':': + fm_len += 1 + fi_mod: str = pool.alloc(fm_len + 1) + if fi_mod is not None: + for fm_i in range(fm_len): + fi_mod[fm_i] = fi_mod_raw[fm_i] + fi_mod[fm_len] = '\0' + # 检查 fi_mod 是否以 "nm_ma.id." 开头 (如 w32.win32file 以 w32. 开头) + prefix_len: t.CSizeT = string.strlen(nm_ma.id) + fi_mod_len: t.CSizeT = string.strlen(fi_mod) + if fi_mod_len > prefix_len + 1: + match_prefix: int = 1 + for pi in range(prefix_len): + if fi_mod[pi] != nm_ma.id[pi]: + match_prefix = 0 + break + if match_prefix == 1 and fi_mod[prefix_len] == '.': + is_submod = 1 + if is_submod != 0: + return None # 是子模块,交给嵌套属性访问路径 + # 非 CDefine: 使用 i8* 作为类型提示 (模块级变量通常存储指针) i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) mod_gv_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global( pool, mod, trans, at, i8_ptr_hint) @@ -1336,6 +2436,51 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, if load_ty_mod is not None: return llvmlite.build_load(builder, load_ty_mod, mod_gv_ref) + # 嵌套模块属性访问 (w32.win32file.GENERIC_READ → 查找全局 @GENERIC_READ) + # 当 at.value 是 Attribute 且解析为已导入子模块时,查找 at.attr 作为全局常量 + if at.value.kind() == ast.ASTKind.Attribute and trans is not None: + inner_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value) + if inner_at.value is not None and inner_at.value.kind() == ast.ASTKind.Name: + inner_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at.value) + if inner_nm.id is not None and inner_at.attr is not None: + if HandlesVar.lookup_var(trans.SymTab, inner_nm.id) is None: + # 构建限定模块名: "w32.win32file" + qual_len: t.CSizeT = string.strlen(inner_nm.id) + string.strlen(inner_at.attr) + 2 + qual_buf: t.CChar | t.CPtr = pool.alloc(qual_len) + if qual_buf is not None: + viperlib.snprintf(qual_buf, qual_len, "%s.%s", inner_nm.id, inner_at.attr) + if HandlesImports.is_module_imported(trans._imported_modules, qual_buf) == 1: + # 查找全局变量 at.attr (stub 已注入的情况) + gv_nested: llvmlite.GlobalVariable | t.CPtr = find_global_in_module(mod, at.attr) + if gv_nested is not None and gv_nested.Ty is not None: + gv_ref_nested: llvmlite.Value | t.CPtr = make_global_ref( + pool, at.attr, gv_nested.Ty) + if gv_ref_nested is not None and gv_ref_nested.Ty is not None: + load_ty_nested: llvmlite.LLVMType | t.CPtr = gv_ref_nested.Ty.Pointee + if load_ty_nested is not None: + return llvmlite.build_load(builder, load_ty_nested, gv_ref_nested) + # 未找到全局: 尝试 CDefine 常量查找 + # 对于 w32.win32base.INVALID_HANDLE_VALUE 等嵌套模块属性访问, + # 先从限定模块的 .pyi 文件中查找 CDefine 常量值并内联 + cdef_val_nested: int = HandlesType.lookup_cdefine_constant(at.attr) + if HandlesType.is_cdefine_found() == 0: + # 构建临时 from_imports "attr:module" 用于跨模块查找 + tmp_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(qual_buf) + 2 + tmp_fi: str = pool.alloc(tmp_fi_len) + if tmp_fi is not None: + viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf) + cdef_val_nested = _lookup_cross_module_cdefine(pool, at.attr, tmp_fi) + if HandlesType.is_cdefine_found() != 0: + return llvmlite.const_int32(pool, cdef_val_nested) + # CDefine 未找到: 创建前向引用 (CDefine 通常是 i32) + i32_ty_fwd: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + gv_ref_fwd: llvmlite.Value | t.CPtr = make_global_ref( + pool, at.attr, i32_ty_fwd) + if gv_ref_fwd is not None and gv_ref_fwd.Ty is not None: + load_ty_fwd: llvmlite.LLVMType | t.CPtr = gv_ref_fwd.Ty.Pointee + if load_ty_fwd is not None: + return llvmlite.build_load(builder, load_ty_fwd, gv_ref_fwd) + # 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体) obj_ptr: llvmlite.Value | t.CPtr = None if at.value.kind() == ast.ASTKind.Name and trans is not None: @@ -1348,6 +2493,11 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, obj_ptr = translate_value(builder, pool, mod, at.value, None, 0, trans) if obj_ptr is None or obj_ptr.Ty is None: + if at.value.kind() == ast.ASTKind.Name: + nm_d: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + if nm_d.id is not None: + stdio.printf("[TA-DIAG] obj_ptr=None attr=%s name=%s\n", + at.attr, nm_d.id) return None # 如果 obj_ptr 是 Ptr(Ptr(Struct))(X|t.CPtr 变量的 alloca), @@ -1358,9 +2508,29 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, # obj_ptr 类型应该是 Ptr(Struct(...)) match obj_ptr.Ty: case llvmlite.LLVMType.Ptr(struct_ty): + # 确保跨模块结构体的完整定义在当前模块中可用(供 getelementptr 访问字段) + HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) # 查找字段索引和类型 field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field( struct_ty, at.attr) + # REnum 变体字段查找: lookup_field 失败时,尝试 REnum 别名 + # REnum 结构体字段名是 _p1/_p2/...,但源码用变体字段名访问(如 ty.Pointee) + if field_info is None: + renum_se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type( + struct_ty) + if renum_se is not None and renum_se.Name is not None: + is_rn: int = HandlesClassDef._is_renum_class_name(renum_se.Name) + if is_rn == 1: + alias_idx: int = HandlesClassDef._lookup_renum_field_alias( + renum_se.Name, at.attr) + if alias_idx >= 1: + alias_fe: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct._get_field_entry(renum_se, alias_idx) + if alias_fe is not None and alias_fe.Ty is not None: + alias_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, struct_ty, alias_fe.Ty, obj_ptr, alias_idx) + if alias_ptr is not None: + return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr) # 回退: 类型指针比较失败时,通过 AnnotClassName 按类名查找 # 传递 SHA1 以区分跨模块同名类 if field_info is None: @@ -1370,9 +2540,69 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( trans.SymTab, nm_fb.id) if var_entry is not None and var_entry.AnnotClassName is not None: + stdio.printf("[TA-DIAG] fallback annot=%s attr=%s\n", + var_entry.AnnotClassName, at.attr) cur_sha1: str = trans.ModuleSha1 field_info = HandlesStruct.lookup_field_by_class( var_entry.AnnotClassName, at.attr, cur_sha1) + if field_info is None: + stdio.printf("[TA-DIAG] lfbc_failed annot=%s attr=%s\n", + var_entry.AnnotClassName, at.attr) + stdio.fflush(0) + else: + stdio.printf("[TA-DIAG] lfbc_ok annot=%s attr=%s idx=%d\n", + var_entry.AnnotClassName, at.attr, field_info.Index) + stdio.fflush(0) + # 回退 1 成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 + # 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)), + # 需用实际结构体类型做 GEP,否则 GEP i8 失败 + if field_info is not None: + annot_se: HandlesStruct.StructEntry | t.CPtr = \ + HandlesStruct.find_struct(var_entry.AnnotClassName) + if annot_se is None: + stdio.printf("[TA-DIAG] fb1 find_struct None annot=%s\n", + var_entry.AnnotClassName) + stdio.fflush(0) + if annot_se is not None and annot_se.Ty is not None: + annot_ptr_ty: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Ptr(pool, annot_se.Ty) + casted_ptr: llvmlite.Value | t.CPtr = \ + llvmlite.build_bitcast(builder, obj_ptr, annot_ptr_ty) + if casted_ptr is not None: + obj_ptr = casted_ptr + struct_ty = annot_se.Ty + # 回退 1 更新 struct_ty 后,需重新确保 + # 注解类型结构体定义在当前模块可用 + # (原始 struct_ty 可能是 i8,ensure 无效) + HandlesStruct.ensure_struct_def_in_module( + pool, mod, annot_se.Ty) + stdio.printf("[TA-DIAG] fb1 struct_ty updated\n") + stdio.fflush(0) + else: + stdio.printf("[TA-DIAG] fb1 bitcast None\n") + stdio.fflush(0) + # 回退 2: 子类搜索 — 注解类型是基类但实际值是派生类 + # 如 node: AST | t.CPtr = If(...),访问 node.orelse + if field_info is None: + sub_entry: HandlesStruct.StructEntry | t.CPtr = \ + HandlesStruct.find_subclass_with_field( + var_entry.AnnotClassName, at.attr) + if sub_entry is not None and sub_entry.Ty is not None: + sub_field: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct.lookup_field(sub_entry.Ty, at.attr) + if sub_field is not None: + sub_ptr_ty: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Ptr(pool, sub_entry.Ty) + casted_ptr: llvmlite.Value | t.CPtr = \ + llvmlite.build_bitcast(builder, obj_ptr, sub_ptr_ty) + if casted_ptr is not None: + sub_field_ptr: llvmlite.Value | t.CPtr = \ + llvmlite.build_gep_struct( + builder, sub_entry.Ty, sub_field.Ty, + casted_ptr, sub_field.Index) + if sub_field_ptr is not None: + return llvmlite.build_load( + builder, sub_field.Ty, sub_field_ptr) if field_info is not None: field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty # 联合体:bitcast obj_ptr 到 field_ty* 后 load @@ -1385,12 +2615,49 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, return None # 普通结构体:GEP + load field_idx: int = field_info.Index + stdio.printf("[TA-DIAG] gep_try idx=%d struct_is_ptr=%d\n", + field_idx, is_ptr_type(struct_ty)) + stdio.fflush(0) field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( builder, struct_ty, field_ty, obj_ptr, field_idx) + if field_ptr is None: + stdio.printf("[TA-DIAG] gep_failed idx=%d\n", field_idx) + stdio.fflush(0) if field_ptr is not None: - return llvmlite.build_load(builder, field_ty, field_ptr) + stdio.printf("[TA-DIAG] gep_ok idx=%d is_array=%d\n", + field_idx, is_array_type(field_ty)) + stdio.fflush(0) + # 数组类型字段不能 load 为 SSA value,直接返回字段指针 + # 用于后续下标访问: self.state[0] → GEP state 字段 → GEP 数组元素 + if is_array_type(field_ty) != 0: + return field_ptr + stdio.printf("[TA-DIAG] pre_load idx=%d field_ty_not_null=%d\n", + field_idx, 1 if field_ty is not None else 0) + stdio.fflush(0) + loaded_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, field_ty, field_ptr) + stdio.printf("[TA-DIAG] post_load idx=%d loaded=%d\n", + field_idx, 1 if loaded_val is not None else 0) + stdio.fflush(0) + # 联合类型字段(如 Token | t.CPtr)被编译为 i8*, + # 若 AnnotClassName 指示了具体结构体类型,bitcast 为正确的结构体指针 + if loaded_val is not None and field_info.AnnotClassName is not None: + annot_struct: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct( + field_info.AnnotClassName) + if annot_struct is not None and annot_struct.Ty is not None: + annot_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, annot_struct.Ty) + return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty) + return loaded_val + # 诊断:所有字段查找路径失败 + sn_diag: str = HandlesStruct._extract_struct_name(struct_ty) + if sn_diag is not None: + stdio.printf("[TA-DIAG] field=None attr=%s sname=%s\n", + at.attr, sn_diag) + else: + stdio.printf("[TA-DIAG] field=None attr=%s sname=(null)\n", + at.attr) return None case _: + stdio.printf("[TA-DIAG] type_mismatch attr=%s\n", at.attr) return None # ============================================================ @@ -1419,35 +2686,117 @@ def get_subscript_ptr(builder: llvmlite.IRBuilder | t.CPtr, nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value) if nm.id is not None and trans is not None: alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id) + if alloca is None: + stdio.printf("[GSP] alloca=None var=%s\n", nm.id) + stdio.fflush(0) + elif alloca.Ty is None: + stdio.printf("[GSP] alloca.Ty=None var=%s\n", nm.id) + stdio.fflush(0) if alloca is not None and alloca.Ty is not None: if is_ptr_type(alloca.Ty) != 0: pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee + if pointee is None: + stdio.printf("[GSP] pointee=None var=%s\n", nm.id) + stdio.fflush(0) if pointee is not None: + pe_arr: int = is_array_type(pointee) + pe_ptr: int = is_ptr_type(pointee) + stdio.printf("[GSP] var=%s pe_arr=%d pe_ptr=%d\n", nm.id, pe_arr, pe_ptr) + stdio.fflush(0) # 单层 match(避免嵌套 match 的编译器 bug) match pointee: case llvmlite.LLVMType.Array(elem_ty, count): + stdio.printf("[GSP] matched Array\n") + stdio.fflush(0) # 数组遍历 return llvmlite.build_gep_array( builder, pointee, elem_ty, alloca, idx_val) case llvmlite.LLVMType.Ptr(inner_ty): - # 指针遍历 + stdio.printf("[GSP] matched Ptr\n") + stdio.fflush(0) + # inner_ty 是 Ptr 说明 alloca.Ty 是三重指针, + # 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。 + # 此时 [i]=val 应该是指针解引用赋值, 而非调用 __setitem__。 + if is_ptr_type(inner_ty) != 0: + ptr_val_gsp: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, pointee, alloca) + if ptr_val_gsp is not None: + return llvmlite.build_gep( + builder, inner_ty, ptr_val_gsp, idx_val) + return None + # 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr): + # 优先返回 None 触发 __setitem__ 重载, + # 避免 IsPtrElement 误判为指针遍历 + cls_nm_ptr_chk: str = _get_custom_struct_cls_nm(pool, inner_ty) + if cls_nm_ptr_chk is None: + pass + if not (cls_nm_ptr_chk is None): + stdio.printf("[GSP] custom struct %s, defer to __setitem__\n", + cls_nm_ptr_chk) + stdio.fflush(0) + return None + # bytes|t.CPtr / str|t.CPtr: alloca 是 i8**, + # 直接 GEP 按 i8* 步长(8 字节),不 load + # 仅当 inner_ty 不是自定义结构体时才走指针遍历 + if trans is not None: + ve_pe: HandlesVar.VarEntry | t.CPtr = \ + HandlesVar.lookup_var_entry(trans.SymTab, nm.id) + if ve_pe is not None and ve_pe.IsPtrElement == 1: + stdio.printf("[GSP] IsPtrElement=1, gep direct\n") + stdio.fflush(0) + return llvmlite.build_gep( + builder, pointee, alloca, idx_val) + # 普通指针遍历: 先 load 指针值,再 GEP ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load( builder, pointee, alloca) - if ptr_val is not None: - return llvmlite.build_gep( - builder, inner_ty, ptr_val, idx_val) - return None + if ptr_val is None: + stdio.printf("[GSP] build_load=None\n") + stdio.fflush(0) + return None + gep_r: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, inner_ty, ptr_val, idx_val) + if gep_r is None: + stdio.printf("[GSP] build_gep=None\n") + stdio.fflush(0) + return gep_r case _: - pass + # pointee 是普通标量类型 (如 i64, i32, i8): + # alloca 是 Ptr(标量), 直接 GEP 获取第 idx 个元素指针 + # 支持 arg_vals[i] = val 这类参数数组下标赋值 + stdio.printf("[GSP] matched scalar, gep direct\n") + stdio.fflush(0) + return llvmlite.build_gep(builder, pointee, alloca, idx_val) # 通用路径 + stdio.printf("[GSP] fallback to generic path\n") + stdio.fflush(0) ptr_val: llvmlite.Value | t.CPtr = translate_value( builder, pool, mod, sub.value, None, 0, trans) if ptr_val is None or ptr_val.Ty is None: + stdio.printf("[GSP] generic: ptr_val=None\n") + stdio.fflush(0) return None if is_ptr_type(ptr_val.Ty) != 0: elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee if elem_ty2 is not None: + # 数组类型: 双索引 GEP (getelementptr [N x ty], ptr, i32 0, i32 idx) + match elem_ty2: + case llvmlite.LLVMType.Array(arr_elem_ty, arr_count): + return llvmlite.build_gep_array( + builder, elem_ty2, arr_elem_ty, ptr_val, idx_val) + case _: + pass + # 自定义结构体 (如 self._ht[key]): 返回 None, + # 触发 HandlesAssign 的 __setitem__ 重载 + cls_nm_gen: str = _get_custom_struct_cls_nm(pool, elem_ty2) + if cls_nm_gen is None: + pass + if not (cls_nm_gen is None): + stdio.printf("[GSP] generic custom struct %s, defer to __setitem__\n", + cls_nm_gen) + stdio.fflush(0) + return None + # 指针类型: 单索引 GEP (getelementptr ty, ptr, idx) return llvmlite.build_gep(builder, elem_ty2, ptr_val, idx_val) return None @@ -1500,9 +2849,12 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr, match obj_ptr.Ty: case llvmlite.LLVMType.Ptr(struct_ty): + # 确保跨模块结构体的完整定义在当前模块中可用(供 getelementptr 访问字段) + # 与 translate_attribute 一致,避免 opaque 类型导致 GEP 报错 + HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field( struct_ty, at.attr) - # 回退: 类型指针比较失败时,通过 AnnotClassName 按类名查找 + # 回退 1: 类型指针比较失败时,通过 AnnotClassName 按类名查找 # 传递 SHA1 以区分跨模块同名类 if field_info is None: if at.value.kind() == ast.ASTKind.Name and trans is not None: @@ -1516,6 +2868,46 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr, cur_sha1 = trans.ModuleSha1 field_info = HandlesStruct.lookup_field_by_class( var_entry.AnnotClassName, at.attr, cur_sha1) + # 回退 1 成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 + # 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)), + # 需用实际结构体类型做 GEP,否则 GEP i8 失败 + if field_info is not None: + annot_se: HandlesStruct.StructEntry | t.CPtr = \ + HandlesStruct.find_struct(var_entry.AnnotClassName) + if annot_se is not None and annot_se.Ty is not None: + annot_ptr_ty: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Ptr(pool, annot_se.Ty) + casted_ptr: llvmlite.Value | t.CPtr = \ + llvmlite.build_bitcast(builder, obj_ptr, annot_ptr_ty) + if casted_ptr is not None: + obj_ptr = casted_ptr + struct_ty = annot_se.Ty + # 回退 1 更新 struct_ty 后,需重新确保 + # 注解类型结构体定义在当前模块可用 + HandlesStruct.ensure_struct_def_in_module( + pool, mod, annot_se.Ty) + # 回退 2: 子类搜索 — 注解类型是基类但实际值是派生类 + # 如 node: AST | t.CPtr = If(...),访问 node.orelse + # orelse 在 If 上不在 AST 上,需搜索 AST 的子类 + if field_info is None: + sub_entry: HandlesStruct.StructEntry | t.CPtr = \ + HandlesStruct.find_subclass_with_field( + var_entry.AnnotClassName, at.attr) + if sub_entry is not None and sub_entry.Ty is not None: + sub_field: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct.lookup_field(sub_entry.Ty, at.attr) + if sub_field is not None: + sub_ptr_ty: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Ptr(pool, sub_entry.Ty) + casted_ptr: llvmlite.Value | t.CPtr = \ + llvmlite.build_bitcast(builder, obj_ptr, sub_ptr_ty) + if casted_ptr is not None: + # 确保子类结构体定义在当前模块可用 + HandlesStruct.ensure_struct_def_in_module( + pool, mod, sub_entry.Ty) + return llvmlite.build_gep_struct( + builder, sub_entry.Ty, sub_field.Ty, + casted_ptr, sub_field.Index) if field_info is not None: # 联合体:bitcast obj_ptr 到 field_ty*(字段指针用于 store) if HandlesStruct.is_union_by_type(struct_ty) == 1: diff --git a/App/lib/core/Handles/HandlesExprCall.py b/App/lib/core/Handles/HandlesExprCall.py index a311ebd..21f4dbd 100644 --- a/App/lib/core/Handles/HandlesExprCall.py +++ b/App/lib/core/Handles/HandlesExprCall.py @@ -8,6 +8,7 @@ import stdio import stdlib import sys import viperlib +import lib.core.VLogger as VLogger import lib.core.Handles.HandlesBase as HandlesBase import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesExpr as HandlesExpr @@ -73,7 +74,7 @@ def _lookup_module_sha1_suffix(mod_suffix: str) -> str: idx: t.CSizeT = t.CSizeT(i) * 64 mod_name: str = _g_mod_arr + idx mod_len: t.CSizeT = string.strlen(mod_name) - if mod_len < suffix_len: + if mod_len == 0: continue # 精确匹配 if mod_len == suffix_len: @@ -87,6 +88,17 @@ def _lookup_module_sha1_suffix(mod_suffix: str) -> str: if string.strcmp(cmp_start, mod_suffix) == 0: sidx2: t.CSizeT = t.CSizeT(i) * 17 return _g_sha1_arr + sidx2 + # 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾 + # 用于 from_imports 存完整路径(如 lib.core.Handles.HandlesTranslator) + # 而 mod_arr 只存文件名(如 HandlesTranslator)的情况: + # import X as Y 的别名 Y 经 from_imports 查到完整路径, + # 需反向匹配 mod_arr 中的文件名才能找到 SHA1 + if suffix_len > mod_len: + if mod_suffix[suffix_len - mod_len - 1] == '.': + cmp_start_rev: str = mod_suffix + suffix_len - mod_len + if string.strcmp(cmp_start_rev, mod_name) == 0: + sidx_rev: t.CSizeT = t.CSizeT(i) * 17 + return _g_sha1_arr + sidx_rev return None # ============================================================ @@ -158,9 +170,9 @@ def _lookup_mod_sha1(pool: memhub.MemBuddy | t.CPtr, from_imports, class_name, allow_star_fallback) if mod_name_raw is None: return None - # 手动找到模块名结束位置(空格或 null),复制到新缓冲区 + # 手动找到模块名结束位置(空格、null、或 ':' 别名分隔符),复制到新缓冲区 mod_len: t.CSizeT = 0 - while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0': + while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':': mod_len += 1 mod_name_buf: str = pool.alloc(mod_len + 1) if mod_name_buf is None: @@ -229,6 +241,111 @@ def find_func_in_module(mod: llvmlite.LLVMModule | t.CPtr, cur = llvmlite.function_get_next(cur) return None +# ============================================================ +# _ensure_c_lib_declare - 为已知的 C 库函数创建 declare 声明 +# +# 对应 TPC 的 _C_LIB_FUNCS 机制。当裸名调用 memset/memmove/memcmp/ +# strcmp/strlen 等已知 C 库函数时,自动在当前模块创建 declare, +# 避免报 "裸名调用未定义且未导入" 错误。 +# memcpy 不在此处理(已有 _emit_llvm_memcpy_intrinsic 映射到 @llvm.memcpy)。 +# +# Args: +# pool: 内存池 +# mod: LLVM 模块 +# name: 函数名 +# +# Returns: +# Function 指针(已创建 declare),或 None 如果不是已知 C 库函数 +# ============================================================ +def _ensure_c_lib_declare(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + name: str) -> llvmlite.Function | t.CPtr: + """为已知的 C 库函数创建 declare 声明""" + if pool is None or mod is None or name is None: + return None + + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + + ret_ty: llvmlite.LLVMType | t.CPtr = None + params: int = 0 # 参数数量 + + if string.strcmp(name, "strlen") == 0 or string.strcmp(name, "strlength") == 0: + ret_ty = i64_ty + params = 1 + elif string.strcmp(name, "memset") == 0: + ret_ty = i8_ptr_ty + params = 3 + elif string.strcmp(name, "memcpy") == 0: + ret_ty = i8_ptr_ty + params = 3 + elif string.strcmp(name, "memmove") == 0: + ret_ty = i8_ptr_ty + params = 3 + elif string.strcmp(name, "memcmp") == 0: + ret_ty = i32_ty + params = 3 + elif string.strcmp(name, "strcmp") == 0: + ret_ty = i32_ty + params = 2 + elif string.strcmp(name, "strncmp") == 0: + ret_ty = i32_ty + params = 3 + elif string.strcmp(name, "strcpy") == 0: + ret_ty = i8_ptr_ty + params = 2 + elif string.strcmp(name, "strncpy") == 0: + ret_ty = i8_ptr_ty + params = 3 + elif string.strcmp(name, "strcat") == 0: + ret_ty = i8_ptr_ty + params = 2 + elif string.strcmp(name, "puts") == 0: + ret_ty = i32_ty + params = 1 + elif string.strcmp(name, "atoi") == 0: + ret_ty = i32_ty + params = 1 + elif string.strcmp(name, "atol") == 0: + ret_ty = i64_ty + params = 1 + elif string.strcmp(name, "abs") == 0: + ret_ty = i32_ty + params = 1 + elif string.strcmp(name, "labs") == 0: + ret_ty = i64_ty + params = 1 + else: + return None + + # 创建 declare + fn: llvmlite.Function | t.CPtr = llvmlite.create_declare(pool, mod, name, ret_ty) + if fn is None: + return None + # 添加参数 + if params == 1: + if string.strcmp(name, "abs") == 0: + llvmlite.add_param(pool, fn, i32_ty, "n") + elif string.strcmp(name, "labs") == 0: + llvmlite.add_param(pool, fn, i64_ty, "n") + else: + llvmlite.add_param(pool, fn, i8_ptr_ty, "s") + elif params == 2: + llvmlite.add_param(pool, fn, i8_ptr_ty, "dst") + llvmlite.add_param(pool, fn, i8_ptr_ty, "src") + elif params == 3: + if string.strcmp(name, "memset") == 0: + llvmlite.add_param(pool, fn, i8_ptr_ty, "dst") + llvmlite.add_param(pool, fn, i32_ty, "c") + llvmlite.add_param(pool, fn, i64_ty, "n") + else: + llvmlite.add_param(pool, fn, i8_ptr_ty, "dst") + llvmlite.add_param(pool, fn, i8_ptr_ty, "src") + llvmlite.add_param(pool, fn, i64_ty, "n") + return fn + # ============================================================ # _infer_external_func_ret_ty - 根据函数名推断外部函数返回类型 # @@ -268,6 +385,15 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr, return llvmlite.Ptr(pool, llvmlite.Int8(pool)) if func_name == "memmove": return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + # MemBuddy 方法(当类型信息丢失时可能走外部函数路径) + if func_name == "alloc": + return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + if func_name == "calloc": + return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + if func_name == "realloc": + return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + if func_name == "alloc_buf": + return llvmlite.Ptr(pool, llvmlite.Int8(pool)) # 返回 i64 的函数 if func_name == "strlen": @@ -327,6 +453,39 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr, if func_name == "GlobalSize": return llvmlite.Int64(pool) + # llvmlite build_* 函数返回类型推断 + # stubs 翻译时未注入,需推断返回类型避免指针被截断为 i32 + # 大多数 build_* 函数返回 Value*(指针),少数终止指令返回 void + if string.strncmp(func_name, "build_", 6) == 0: + # 返回 void 的 build_* 函数(终止指令和无返回值指令) + if func_name == "build_ret" or func_name == "build_ret_void" or \ + func_name == "build_store" or func_name == "build_br" or \ + func_name == "build_cond_br" or func_name == "build_unreachable" or \ + func_name == "build_switch": + return llvmlite.Void(pool) + # 其他 build_* 函数返回 Value*(指针) + return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + + # llvmlite 类型工厂函数返回 LLVMType*(指针) + # Int1/Int8/Int16/Int32/Int64/Ptr/Func/Array/Struct/Void/Half/Float/Double/FP128/Label + # new_param_node/new_phi_incoming 返回 ParamNode*/PhiIncoming*(指针) + # const_int32/const_int64/ConstFloat/ConstZero/ConstNull/ConstInt 返回 Value*(指针) + # function_get_name/param_get_ty 等访问器返回指针 + if func_name == "Int1" or func_name == "Int8" or func_name == "Int16" or \ + func_name == "Int32" or func_name == "Int64" or func_name == "Ptr" or \ + func_name == "Func" or func_name == "Array" or func_name == "Struct" or \ + func_name == "Void" or func_name == "Half" or func_name == "Float" or \ + func_name == "Double" or func_name == "FP128" or func_name == "Label" or \ + func_name == "new_param_node" or func_name == "new_phi_incoming" or \ + func_name == "const_int32" or func_name == "const_int64" or \ + func_name == "ConstFloat" or func_name == "ConstZero" or \ + func_name == "ConstNull" or func_name == "ConstInt" or \ + func_name == "function_get_name" or func_name == "param_get_ty" or \ + func_name == "value_get_ty" or func_name == "value_get_next" or \ + func_name == "function_get_ret_ty" or func_name == "function_get_params" or \ + func_name == "function_get_param_head" or func_name == "param_get_next": + return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + # 默认 i32 return llvmlite.Int32(pool) @@ -372,7 +531,10 @@ def _get_external_func_param_ty(pool: memhub.MemBuddy | t.CPtr, "GetTickCount", "GetProcessHeap" ] - for api_name in win32_apis: + # list 迭代不支持,用 range + get 替代 + api_count: t.CSizeT = win32_apis.__len__() + for ai in range(api_count): + api_name: str = win32_apis.get(ai) if api_name == func_name: return i64_ty @@ -1621,7 +1783,7 @@ def translate_c_llvmir(pool: memhub.MemBuddy | t.CPtr, ret_type_node: ast.AST | t.CPtr = cargs.get(1) if ret_type_node is not None: resolved_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, ret_type_node, trans._imported_modules, trans._from_imports) + pool, ret_type_node, trans._imported_modules, trans._from_imports, trans) if resolved_ty is not None: ret_ty = resolved_ty @@ -1913,7 +2075,10 @@ def translate_c_llvmir(pool: memhub.MemBuddy | t.CPtr, return binop_result # 未识别的指令,返回 0 - stdio.printf("[LLVMIR] unsupported instruction: %s\n", instr_kw) + fb_li: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_li is not None: + viperlib.snprintf(fb_li, 1024, "unsupported instruction: %s", instr_kw) + VLogger.error(fb_li, "LLVMIR") return None # ============================================================ @@ -1977,6 +2142,8 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, 普通结构体:alloca → zero + defaults → store 参数 → load """ # 创建临时 alloca + # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) + HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty) if tmp is None: return llvmlite.ConstZero(pool, struct_ty) @@ -1996,6 +2163,7 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, has_new_flag: int = 0 if ctor_entry is not None: has_new_flag = ctor_entry.HasNew + # 注意: __new__ 是可选的,不推断 HasNew(仅 __init__ 是 OOP 类标准方法) if has_new_flag != 0: new_arg_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 32) if new_arg_vals is not None: @@ -2026,6 +2194,10 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, has_init_flag: int = 0 if ctor_entry is not None: has_init_flag = ctor_entry.HasInit + # 跨模块 OOP 类: HasInit 可能未设置(_translate_oop_methods 未被调用) + # 但 IsOOP=1 说明类有方法,推断 HasInit=1 + if is_oop != 0 and has_init_flag == 0: + has_init_flag = 1 if has_init_flag != 0: # 翻译构造函数参数 arg_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 32) @@ -2108,6 +2280,115 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, # 返回指针(而非结构体值),赋值时由 coerce_to_type 按目标类型决定是否 load return tmp + +# ============================================================ +# _translate_renum_construct — REnum 变体构造函数 +# +# 语法: ClassName.VariantName(args...) +# 如 LLVMType.Int(1) → 构造 LLVMType 的 Int 变体,__tag=0,_p1=1 +# +# 生成 IR: +# %tmp = alloca {i32, i32, ...} ; REnum 类型 +# store zeroinitializer, %tmp ; 清零 +# %tag_ptr = gep %tmp, 0, 0 ; __tag 字段 +# store i32 , %tag_ptr ; 写入 tag +# %p1_ptr = gep %tmp, 0, 1 ; _p1 字段 +# store , %p1_ptr ; 写入 payload[0] +# ... (更多 payload 字段) +# return %tmp ; 返回指针 +# +# 对应 TPC 的 _HandleREnumConstructLlvm +# ============================================================ +def _translate_renum_construct(pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + class_name: str, + variant_name: str, + tag_value: int, + cargs: list[ast.AST | t.CPtr] | t.CPtr, + can: t.CSizeT, + trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: + """构造 REnum 变体实例,返回指向 alloca 的指针 + + class_name: REnum 类名(如 "LLVMType") + variant_name: 变体名(如 "Int") + tag_value: 变体的 tag 值(变体在类中的索引) + cargs: 参数 AST 列表 + can: 参数数量 + """ + if pool is None or builder is None or class_name is None: + return None + + # 查找 REnum 结构体类型(用 find_struct_by_module 规避跨模块同名) + renum_entry: HandlesStruct.StructEntry | t.CPtr = None + if trans is not None: + renum_entry = HandlesStruct.find_struct_by_module(class_name, trans.ModuleSha1) + if renum_entry is None: + renum_entry = HandlesStruct.find_struct(class_name) + if renum_entry is None: + fb_re: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_re is not None: + viperlib.snprintf(fb_re, 1024, "REnum construct: type %s not registered", class_name) + VLogger.error(fb_re, "RENUM") + return None + + renum_ty: llvmlite.LLVMType | t.CPtr = renum_entry.Ty + if renum_ty is None: + return None + + # alloca REnum 类型 + # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) + HandlesStruct.ensure_struct_def_in_module(pool, mod, renum_ty) + tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, renum_ty) + if tmp is None: + return llvmlite.ConstZero(pool, renum_ty) + + # 清零(确保未赋值的 payload 位置为零) + zero: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, renum_ty) + llvmlite.build_store(builder, zero, tmp) + + # 写入 __tag 字段(索引 0) + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + tag_const: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i32_ty, tag_value, "0") + tag_field: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry(renum_entry, 0) + tag_field_ty: llvmlite.LLVMType | t.CPtr = i32_ty + if tag_field is not None and tag_field.Ty is not None: + tag_field_ty = tag_field.Ty + tag_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, renum_ty, tag_field_ty, tmp, 0) + if tag_ptr is not None: + llvmlite.build_store(builder, tag_const, tag_ptr) + + # 写入 payload 字段(索引 1, 2, ... 对应 _p1, _p2, ...) + # 参数按位置顺序填充 payload,跳过 __tag(索引 0) + for ai in range(can): + arg: ast.AST | t.CPtr = cargs.get(ai) + if arg is None: + continue + arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, arg, None, 0, trans) + if arg_val is None: + continue + # payload 字段索引 = ai + 1(跳过 __tag) + payload_idx: int = ai + 1 + if payload_idx >= renum_entry.FieldCount: + break + pfe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry( + renum_entry, payload_idx) + if pfe is None: + continue + payload_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, renum_ty, pfe.Ty, tmp, payload_idx) + if payload_ptr is None: + continue + # 类型转换并 store(payload 位置可能是 i32 或 i64,按字段类型 coerce) + arg_val = HandlesExpr.coerce_to_type(builder, arg_val, pfe.Ty) + llvmlite.build_store(builder, arg_val, payload_ptr) + + # 返回指针(与 _translate_struct_ctor 一致,赋值时由 coerce_to_type 决定是否 load) + return tmp + + # ============================================================ # _do_virtual_call — 通过虚表间接调用虚方法 # @@ -2297,6 +2578,10 @@ def _call_method_on_ptr(pool: memhub.MemBuddy | t.CPtr, cmop_entry = HandlesStruct.find_struct_by_type(cmop_struct_ty) if cmop_entry is None: cmop_entry = HandlesStruct.find_struct(class_name) + # fallback: 若 cmop_struct_ty 为 None 但 entry 存在,从 entry 获取类型 + # 用于跨模块调用时 self_ptr 类型信息丢失的情况 + if cmop_struct_ty is None and cmop_entry is not None: + cmop_struct_ty = cmop_entry.Ty # 构造方法查找名: ClassName.method_name lookup_name: t.CChar | t.CPtr = pool.alloc(128) @@ -2345,9 +2630,14 @@ def _call_method_on_ptr(pool: memhub.MemBuddy | t.CPtr, call_ret_ty = frt else: # found_func 为 None(跨模块调用 stub 未注入):根据方法名推断返回类型 - # __new__ 返回 Ptr(struct_ty),其他方法默认 void + # __new__ 返回 Ptr(struct_ty) + # __getitem__ 返回 Ptr(struct_ty)(自定义结构体 subscript 读取) + # 其他方法默认 void if string.strcmp(method_name, "__new__") == 0 and cmop_struct_ty is not None: call_ret_ty = llvmlite.Ptr(pool, cmop_struct_ty) + elif string.strcmp(method_name, "__getitem__") == 0 and cmop_struct_ty is not None: + # __getitem__ 返回对象指针 (如 JsonValue|t.CPtr, HashTable|t.CPtr) + call_ret_ty = llvmlite.Ptr(pool, cmop_struct_ty) # 构建参数链表: self_ptr → extra_args... llvmlite.value_set_next(self_ptr, None) @@ -2367,23 +2657,79 @@ def _call_method_on_ptr(pool: memhub.MemBuddy | t.CPtr, total_count: int = 1 + extra_count - # 默认参数填入:如果调用点参数少于函数定义参数,用零值填充缺失参数 - # 这处理 list[int](bd) 中 elem_size=0 默认值缺失的情况 - if found_func is not None: + # 默认参数填充:如果调用点参数少于函数定义参数,用默认值填充缺失参数 + # 优先用函数定义的默认值,无默认值时用零值兜底 + # 查找函数的 defaults 信息 + mdf_defaults: t.CVoid | t.CPtr = None + mdf_default_count: int = 0 + mdf_param_count: int = 0 + if trans is not None and trans._funcs is not None: + mdf_entry: FuncEntry | t.CPtr = find_func_entry_in_table( + trans._funcs, trans._func_count, lookup_name) + if mdf_entry is not None: + mdf_defaults = mdf_entry.Defaults + mdf_default_count = mdf_entry.DefaultCount + mdf_param_count = mdf_entry.ParamCount + if mdf_defaults is None: + mdf_gd: FuncDefaultsEntry | t.CPtr = find_func_defaults(lookup_name) + if mdf_gd is not None: + mdf_defaults = mdf_gd.Defaults + mdf_default_count = mdf_gd.DefaultCount + mdf_param_count = mdf_gd.ParamCount + + # 计算需要填充的参数(不含 self) + # extra_count = 已提供的非 self 参数数量 + # mdf_param_count = 函数定义的非 self 参数数量 + if mdf_param_count > extra_count: + mdf_offset: int = mdf_param_count - mdf_default_count + mdf_pi: int = extra_count + while mdf_pi < mdf_param_count: + # 优先用默认值 + if mdf_defaults is not None and mdf_default_count > 0 and mdf_pi >= mdf_offset: + mdf_di: int = mdf_pi - mdf_offset + if mdf_di < mdf_default_count: + mdf_defaults_list: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)(t.CVoid(t.CUInt64T(mdf_defaults), t.CPtr)) + mdf_node: ast.AST | t.CPtr = mdf_defaults_list.get(mdf_di) + if mdf_node is not None: + mdf_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, mdf_node, None, 0, trans) + if mdf_val is not None: + llvmlite.value_set_next(mdf_val, None) + llvmlite.value_set_next(tail, mdf_val) + tail = mdf_val + total_count += 1 + mdf_pi += 1 + continue + # 无默认值时用零值兜底(需要 found_func 获取参数类型) + if found_func is not None: + param_node_z: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + skip_z: int = 0 + while skip_z <= mdf_pi and param_node_z is not None: + param_node_z = llvmlite.param_get_next(param_node_z) + skip_z += 1 + if param_node_z is not None: + zero_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(param_node_z) + zero_val: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, zero_ty) + if zero_val is not None: + llvmlite.value_set_next(zero_val, None) + llvmlite.value_set_next(tail, zero_val) + tail = zero_val + total_count += 1 + mdf_pi += 1 + elif found_func is not None: + # 无默认参数信息但有 found_func:用零值填充(保留原有逻辑) param_node: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) skip_idx: int = 0 - # 跳过已提供的参数 while skip_idx < total_count and param_node is not None: param_node = llvmlite.param_get_next(param_node) skip_idx += 1 - # 为剩余参数填入零值 while param_node is not None: missing_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(param_node) - zero_val: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, missing_ty) - if zero_val is not None: - llvmlite.value_set_next(zero_val, None) - llvmlite.value_set_next(tail, zero_val) - tail = zero_val + zero_val2: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, missing_ty) + if zero_val2 is not None: + llvmlite.value_set_next(zero_val2, None) + llvmlite.value_set_next(tail, zero_val2) + tail = zero_val2 total_count += 1 param_node = llvmlite.param_get_next(param_node) @@ -2408,8 +2754,53 @@ def _call_method_on_ptr(pool: memhub.MemBuddy | t.CPtr, return _do_virtual_call(pool, builder, mod, class_name, method_name, self_ptr, head, total_count, found_func, call_ret_ty) + # 跨模块调用(found_func 为 None):创建 declare 声明,避免 llc 报 undefined value + if found_func is None: + _ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count) + return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, 0) + +# ============================================================ +# _ensure_method_declare - 为跨模块方法调用创建 declare 声明 +# +# 当 found_func 为 None(stub 未注入或跨模块调用)时, +# 在当前模块中创建函数声明,避免 llc 报 undefined value。 +# ============================================================ +def _ensure_method_declare(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + call_name: str, + ret_ty: llvmlite.LLVMType | t.CPtr, + param_head: llvmlite.ParamNode | t.CPtr, + param_count: int): + """为跨模块方法调用创建 declare 声明""" + if pool is None or mod is None or call_name is None: + return + # 检查模块中是否已有同名函数(declare 或 define) + existing: llvmlite.Function | t.CPtr = mod.FuncHead + while existing is not None: + if existing.Name is not None: + if string.strcmp(existing.Name, call_name) == 0: + return # 已存在,无需重复声明 + existing = existing.Next + # 创建函数声明 + func: llvmlite.Function | t.CPtr = llvmlite.new_function(pool, call_name, ret_ty) + if func is None: + return + func.IsDeclared = 1 + # 添加参数(declare 中参数名无意义,设为 None 只输出类型) + pnode: llvmlite.ParamNode | t.CPtr = param_head + pi: int = 0 + while pnode is not None and pi < param_count: + if pnode.Ty is not None: + param: llvmlite.Param | t.CPtr = llvmlite.new_param(pool, pnode.Ty, None) + if param is not None: + llvmlite.function_add_param(func, param) + pnode = pnode.Next + pi += 1 + llvmlite.module_add_function(mod, func) + + # ============================================================ # _infer_method_ret_ty - 根据方法名推断返回类型 # @@ -2442,6 +2833,18 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr, if string.strcmp(method_name, "__enter__") == 0: return i8_ptr_ty + # 返回 i8* 的方法(返回字符串/类型名/描述) + if string.strcmp(method_name, "type_name") == 0: + return i8_ptr_ty + if string.strcmp(method_name, "get_name") == 0: + return i8_ptr_ty + if string.strcmp(method_name, "to_string") == 0: + return i8_ptr_ty + if string.strcmp(method_name, "__repr__") == 0: + return i8_ptr_ty + if string.strcmp(method_name, "__str__") == 0: + return i8_ptr_ty + # 返回 void 的方法 if string.strcmp(method_name, "__before_init__") == 0: return llvmlite.Void(pool) @@ -2599,6 +3002,8 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr, return _do_virtual_call(pool, builder, mod, class_name, method_name, obj_ptr, head, total_count, found_func, call_ret_ty) + # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value + _ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count) return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, 0) # ============================================================ @@ -2644,6 +3049,8 @@ def _translate_struct_ctor_kw(pool: memhub.MemBuddy | t.CPtr, trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: """结构体构造函数(关键字参数版本)""" # 创建临时 alloca + # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) + HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty) if tmp is None: return llvmlite.ConstZero(pool, struct_ty) @@ -2663,6 +3070,94 @@ def _translate_struct_ctor_kw(pool: memhub.MemBuddy | t.CPtr, llvmlite.build_store(builder, zero, tmp) _apply_struct_defaults(pool, builder, mod, struct_ty, class_name, tmp, trans) + # OOP 结构体且有 __init__:调用 __init__ 而非逐字段 store + # 关键字构造调用此前总是逐字段 store,导致 __init__ 方法体不执行 + # (如 self._args = mb.alloc(...) 不执行 → self._args 为 NULL → add_argument 崩溃) + has_init_flag: int = 0 + has_new_flag: int = 0 + if kw_entry is not None: + has_init_flag = kw_entry.HasInit + has_new_flag = kw_entry.HasNew + + # 跨模块 OOP 类: HasInit 可能未设置(_translate_oop_methods 未被调用) + # 但 IsOOP=1 说明类有方法,推断 HasInit=1(__init__ 在目标模块中存在) + # 注意: __new__ 是可选的,不推断 HasNew(仅 __init__ 是 OOP 类标准方法) + if is_oop != 0 and has_init_flag == 0: + has_init_flag = 1 + + if is_oop != 0 and has_init_flag != 0: + # 有 __init__:构建参数数组并调用 __init__ + storage_ptr: llvmlite.Value | t.CPtr = tmp + + # 如果有 __new__,调用 __new__ 获取实际存储指针 + if has_new_flag != 0: + new_arg_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 32) + if new_arg_vals is not None: + string.memset(new_arg_vals, 0, 8 * 32) + new_arg_count: int = 0 + for nai in range(can): + new_arg_node: ast.AST | t.CPtr = cargs.get(nai) + if new_arg_node is None: + continue + new_av: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, new_arg_node, None, 0, trans) + if new_av is not None: + if new_arg_count < 32: + new_arg_vals[new_arg_count] = t.CSizeT(new_av) + new_arg_count += 1 + # 关键字参数也传递给 __new__ + for nki in range(kw_count): + nkw: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(keywords.get(nki)) + if nkw is None or nkw.value is None: + continue + nkw_av: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, nkw.value, None, 0, trans) + if nkw_av is not None: + if new_arg_count < 32: + new_arg_vals[new_arg_count] = t.CSizeT(nkw_av) + new_arg_count += 1 + new_ptr: llvmlite.Value | t.CPtr = _call_method_on_ptr( + pool, builder, mod, class_name, + "__new__", tmp, new_arg_vals, new_arg_count, trans) + if new_ptr is not None: + storage_ptr = new_ptr + + # 构建 __init__ 参数数组:位置参数按顺序 + 关键字参数追加 + init_arg_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 32) + if init_arg_vals is not None: + string.memset(init_arg_vals, 0, 8 * 32) + init_arg_count: int = 0 + # 位置参数 + for ai_init in range(can): + arg_node_init: ast.AST | t.CPtr = cargs.get(ai_init) + if arg_node_init is None: + continue + av_init: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, arg_node_init, None, 0, trans) + if av_init is not None: + if init_arg_count < 32: + init_arg_vals[init_arg_count] = t.CSizeT(av_init) + init_arg_count += 1 + # 关键字参数(追加到末尾,按出现顺序) + for ki_init in range(kw_count): + kw_init: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(keywords.get(ki_init)) + if kw_init is None or kw_init.value is None: + continue + kw_av_init: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, kw_init.value, None, 0, trans) + if kw_av_init is not None: + if init_arg_count < 32: + init_arg_vals[init_arg_count] = t.CSizeT(kw_av_init) + init_arg_count += 1 + + # 调用 __init__(storage_ptr, *args) + _call_method_on_ptr(pool, builder, mod, class_name, + "__init__", storage_ptr, init_arg_vals, init_arg_count, trans) + + # 返回指针 + return storage_ptr + + # 无 __init__(或非 OOP):逐字段 store 参数值 # 先处理位置参数(按字段索引,覆盖默认值) for ai in range(can): arg: ast.AST | t.CPtr = cargs.get(ai) @@ -2798,12 +3293,15 @@ def _translate_call_with_kwargs(pool: memhub.MemBuddy | t.CPtr, cur_param = llvmlite.param_get_next(cur_param) # 使用模块中的实际函数名(可能带 SHA1 前缀,使用访问器) - # t.CExtern/t.State/t.CExport 函数(declare)使用裸名,不加 SHA1 前缀 + # declare 和 define 统一用 found_func.Name: + # - 普通函数的 declare/define 都带 SHA1 前缀(forward_declare_functions 和 stub 注入都用 mangled_name) + # - t.CExtern/t.State/t.CExport 函数的 declare/define 都用裸名 + # 旧逻辑(IsDeclared==1 时用 func_name 裸名)是错误的:stub 注入的 declare Name 已带 SHA1 前缀, + # 用裸名会导致 call @_PathToModuleName 而 define 是 @"sha1._PathToModuleName",链接报 undefined reference call_name: str = func_name - if llvmlite.function_is_declared(found_func) == 0: - found_name: t.CChar | t.CPtr = llvmlite.function_get_name(found_func) - if found_name is not None: - call_name = found_name + found_name: t.CChar | t.CPtr = llvmlite.function_get_name(found_func) + if found_name is not None: + call_name = found_name # 返回类型(使用访问器) call_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func) @@ -2891,6 +3389,93 @@ def _extract_type_args_from_slice(pool: memhub.MemBuddy | t.CPtr, result.append(tn) return result +# ============================================================ +# 泛型类构造调用辅助: list[str](pool, 8) → __new__ + __before_init__ + __init__ +# +# 复用 translate_list_literal 的构造逻辑: +# 1. alloca tmp(特化类临时空间,作为 __new__ 的 self) +# 2. 翻译 cl.args 为参数指针数组 +# 3. call __new__(tmp, *args) → storage_ptr +# 4. call __before_init__(storage_ptr) +# 5. call __init__(storage_ptr, *args) +# 6. return storage_ptr +# +# 必须独立于 translate_call 的普通函数路径:spec_name 是类名不是函数名, +# 走裸名路径会被 _lookup_mod_sha1 的 star import 回退误导,用错误的 SHA1 +# 且缺少 __new__ 方法名后缀,导致链接报 undefined reference。 +# ============================================================ +def _translate_generic_ctor_call(pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + spec_name: str, + cl: ast.Call | t.CPtr, + trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: + """泛型类构造调用: list[str](pool, 8) → __new__ + __before_init__ + __init__""" + if pool is None or builder is None or mod is None or spec_name is None or cl is None: + return None + + # 查找 struct entry 获取类型 + struct_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(spec_name) + if struct_entry is None: + HandlesType.fatal_error(cl, "泛型类构造调用: 特化类未注册") + return None + struct_ty: llvmlite.LLVMType | t.CPtr = struct_entry.Ty + if struct_ty is None: + return None + + # 确保结构体定义在当前模块中可用(供 alloca 分配空间) + HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) + + # alloca tmp(self 指针) + tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty) + if tmp is None: + return None + + # 翻译 cl.args 为参数指针数组(extra_args[i] = Value 指针) + cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args + can: t.CSizeT = 0 + if cargs is not None: + can = cargs.__len__() + + extra_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32) + if extra_args is None: + return None + string.memset(extra_args, 0, 8 * 32) + + ai: t.CSizeT = 0 + while ai < can: + arg_node: ast.AST | t.CPtr = cargs.get(ai) + if arg_node is not None: + arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, arg_node, None, 0, trans) + if arg_val is not None: + extra_args[ai] = t.CSizeT(arg_val) + ai += 1 + + # 存储指针:默认用 alloca,如果有 __new__ 则用 __new__ 返回的指针 + # __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用 + # (与 _translate_struct_ctor 保持一致,避免为无 __new__ 的类生成过度声明) + storage_ptr: llvmlite.Value | t.CPtr = tmp + has_new_flag: int = 0 + if struct_entry is not None: + has_new_flag = struct_entry.HasNew + if has_new_flag != 0: + new_ptr: llvmlite.Value | t.CPtr = _call_method_on_ptr( + pool, builder, mod, spec_name, "__new__", tmp, extra_args, can, trans) + if new_ptr is not None: + storage_ptr = new_ptr + + # call __before_init__(storage_ptr)(TransPyV 自动生成,零值填充) + _call_method_on_ptr( + pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans) + + # call __init__(storage_ptr, *args) + _call_method_on_ptr( + pool, builder, mod, spec_name, "__init__", storage_ptr, extra_args, can, trans) + + return storage_ptr + + # ============================================================ # 翻译函数调用 Call(func, args) — 模块级版本 # ============================================================ @@ -2925,7 +3510,11 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, spec_name: str = HandlesClassDef._specialize_generic_class( trans, gen_class_name, gen_type_args) if spec_name is not None: - func_name = spec_name + # 泛型类构造调用: list[str](pool, 8) → __new__ + __before_init__ + __init__ + # 不能直接用 spec_name 作为 func_name(会走裸名调用路径, + # 被 star import 回退误导用错误的 SHA1,且缺少 __new__ 方法名后缀) + return _translate_generic_ctor_call( + pool, builder, mod, spec_name, cl, trans) else: # 泛型类模板未找到:可能未 import 定义模块 gt_err: str = pool.alloc(256) @@ -2938,7 +3527,7 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, # BinOp 节点作为 Call func: 联合类型注解用作类型转换 if cl.func is not None and cl.func.kind() == ast.ASTKind.BinOp and trans is not None: binop_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, cl.func, trans._imported_modules, trans._from_imports) + pool, cl.func, trans._imported_modules, trans._from_imports, trans) if binop_ty is not None: binop_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args if binop_args is not None and binop_args.__len__() == 1: @@ -3035,6 +3624,12 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, if sz_cls_name is not None: # 泛型类型参数 T 替换: T.__sizeof__() → int.__sizeof__() sz_cls_name = HandlesType.resolve_generic_type_name(sz_cls_name) + # 去除模块前缀: Viper 解析器将 t.CInt 解析为 Name(id="t.CInt") + # 而非 Attribute(value=Name("t"), attr="CInt"),需要手动提取最后的类名分量 + # "t.CInt" → "CInt", "w32.win32file.WIN32_FIND_DATAA" → "WIN32_FIND_DATAA" + dot_pos: str = string.strrchr(sz_cls_name, '.') + if dot_pos is not None: + sz_cls_name = dot_pos + 1 # 联合类型(如 AST|t.CPtr)按指针大小处理(8 字节) # 联合类型在 TPV 中表示指针,可以指向多种类型 # 对应 TPC 中对 BinOp/BitOr 联合类型的特殊处理 @@ -3080,10 +3675,20 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, if sz_val > 0: return llvmlite.const_int64(pool, sz_val) # 类型已注册但大小为 0(opaque struct 或字段链表未填充):报错终止 - stdio.printf("[FATAL][__sizeof__] 类型 '%s' 已注册但大小为 0(字段链表未填充),立即终止编译\n", sz_cls_name) + fb_sz1: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_sz1 is not None: + viperlib.snprintf(fb_sz1, 1024, + "类型 '%s' 已注册但大小为 0(字段链表未填充),立即终止编译", + sz_cls_name) + VLogger.critical(fb_sz1, "__sizeof__") sys.exit(1) # 类型未注册:报错终止,不静默返回 0(会导致 malloc(0)/alloc(0) 堆溢出) - stdio.printf("[FATAL][__sizeof__] 类型 '%s' 未在结构体表中注册,无法计算大小,立即终止编译\n", sz_cls_name) + fb_sz2: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_sz2 is not None: + viperlib.snprintf(fb_sz2, 1024, + "类型 '%s' 未在结构体表中注册,无法计算大小,立即终止编译", + sz_cls_name) + VLogger.critical(fb_sz2, "__sizeof__") sys.exit(1) # 检测 obj.__len__() — 返回 list 对象的 __count__ 字段(偏移 8,i64) @@ -3105,6 +3710,35 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, if count_ptr is not None: return llvmlite.build_load(builder, i64_ty_len, count_ptr) + # 检测 len() 内置函数调用 — 转换为 strlen 调用 + # len(s) 对 str 类型调用 strlen,返回 i64 + if cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + if func_name is not None and string.strcmp(func_name, "len") == 0: + if can == 1: + len_arg_node: ast.AST | t.CPtr = cargs.get(0) + if len_arg_node is not None: + len_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, len_arg_node, None, 0, trans) + if len_val is not None: + # 确保 strlen 在模块中有声明 + strlen_fn: llvmlite.Function | t.CPtr = find_func_in_module(mod, "strlen") + if strlen_fn is None: + i64_ty_ln: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + i8_ptr_ty_ln: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + strlen_fn = llvmlite.create_declare(pool, mod, "strlen", i64_ty_ln) + if strlen_fn is not None: + llvmlite.add_param(pool, strlen_fn, i8_ptr_ty_ln, "s") + # 参数类型转换为 i8* + i8_ptr_ty_ln2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + len_arg_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(len_val) + len_coerced: llvmlite.Value | t.CPtr = len_val + if HandlesExpr.is_ptr_type(len_arg_ty) != 0: + len_coerced = llvmlite.build_bitcast(builder, len_val, i8_ptr_ty_ln2) + # 构建单参数链表并调用 strlen + len_coerced.Next = None + i64_ty_ln2: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + return llvmlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0) + # 检测方法调用: obj.method(args) # 当 cl.func 是 Attribute 且 obj 是已注册的结构体变量时,翻译为方法调用 if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: @@ -3203,6 +3837,7 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, tail3 = arg_val3 total3: int = 1 + can ret_ty3: llvmlite.LLVMType | t.CPtr = _infer_method_ret_ty(pool, func_name) + _ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3) return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0) # ============================================================ @@ -3387,6 +4022,7 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, llvmlite.value_set_next(ap_item_val, None) llvmlite.value_set_next(lm_obj, ap_item_val) ap_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + _ensure_method_declare(pool, mod, ap_mangled, ap_ret_ty, lm_obj, 2) return llvmlite.build_call(builder, ap_mangled, lm_obj, 2, ap_ret_ty, 0) # 获取关键字参数 @@ -3459,6 +4095,26 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, return _translate_struct_ctor( pool, builder, mod, struct_ty, func_name, cargs, can, trans) + # REnum 变体构造检测: ClassName.VariantName(args...) + # 如 LLVMType.Int(1) → 构造 LLVMType 的 Int 变体 + # 必须在跨模块调用查找之前,否则 ClassName 会被误判为模块名 + # 对应 TPC HandlesExprCall.py:585-644 的三条检测路径之一 + if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + rc_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Name: + rc_cls_node: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rc_at.value) + if rc_cls_node.id is not None: + rc_cls_name: str = rc_cls_node.id + # 检查 ClassName 是否是已注册的 REnum 类 + if HandlesClassDef._is_renum_class_name(rc_cls_name) != 0: + # 在该 REnum 类中查找变体名 func_name + rc_tag: int = HandlesClassDef._lookup_renum_variant_in_class( + rc_cls_name, func_name) + if rc_tag >= 0: + return _translate_renum_construct( + pool, builder, mod, rc_cls_name, func_name, + rc_tag, cargs, can, trans) + # 查找函数:优先从函数表按裸名精确匹配(同模块函数), # 再从模块函数链表按后缀匹配(SHA1 前缀名)。 # stubs 在翻译后才注入,翻译时跨模块函数 found_func 通常为 None, @@ -3470,6 +4126,10 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, actual_funcs = trans._funcs actual_func_count = trans._func_count found_func: llvmlite.Function | t.CPtr = find_func_in_module(mod, func_name) + # C 库函数裸名调用支持:若模块中找不到且是已知 C 库函数,自动创建 declare + # 对应 TPC 的 _C_LIB_FUNCS 机制,支持 memset/memmove/memcmp/strcmp 等裸名调用 + if found_func is None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + found_func = _ensure_c_lib_declare(pool, mod, func_name) # 使用访问器绕过 stub 类型限制 found_ret_ty: llvmlite.LLVMType | t.CPtr = None found_params: t.CPtr = None @@ -3535,15 +4195,60 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, elif ai == 2: mc_num2 = arg_val + # 默认参数填充:当实参数量 < 形参数量时,用默认值填充缺失参数 + # defaults 数组右对齐到 args:defaults[0] 对应 args[offset],offset = param_count - default_count + if can > 0 or (can == 0 and cargs is not None): + df_defaults: t.CVoid | t.CPtr = None + df_default_count: int = 0 + df_param_count: int = 0 + # 先从当前模块函数表查找 FuncEntry + if trans is not None and trans._funcs is not None: + df_entry: FuncEntry | t.CPtr = find_func_entry_in_table( + trans._funcs, trans._func_count, func_name) + if df_entry is not None: + df_defaults = df_entry.Defaults + df_default_count = df_entry.DefaultCount + df_param_count = df_entry.ParamCount + # 如果当前模块没有,从全局默认参数表查找(跨模块) + if df_defaults is None: + gd_entry: FuncDefaultsEntry | t.CPtr = find_func_defaults(func_name) + if gd_entry is not None: + df_defaults = gd_entry.Defaults + df_default_count = gd_entry.DefaultCount + df_param_count = gd_entry.ParamCount + # 填充缺失参数 + if df_defaults is not None and df_default_count > 0 and can < df_param_count: + df_offset: int = df_param_count - df_default_count + df_di: int = 0 + while df_di < df_default_count: + df_param_idx: int = df_offset + df_di + if df_param_idx >= can: + df_defaults_list: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)(t.CVoid(t.CUInt64T(df_defaults), t.CPtr)) + df_default_node: ast.AST | t.CPtr = df_defaults_list.get(df_di) + if df_default_node is not None: + df_arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, df_default_node, None, 0, trans) + if df_arg_val is not None: + df_arg_val.Next = None + if head is None: + head = df_arg_val + else: + tail.Next = df_arg_val + tail = df_arg_val + df_di += 1 + # 使用模块中的实际函数名(可能带 SHA1 前缀,如 "83210a72e829a086.atoi") # t.CExtern/t.State/t.CExport 函数不加 SHA1 前缀,使用裸名 - # declare 函数(IsDeclared=1)使用裸名(func_name),define 函数使用实际名(可能带 SHA1 前缀) + # declare 和 define 统一用 found_func.Name: + # - 普通函数的 declare/define 都带 SHA1 前缀(forward_declare_functions 和 stub 注入都用 mangled_name) + # - t.CExtern/t.State/t.CExport 函数的 declare/define 都用裸名 + # 旧逻辑(IsDeclared==1 时用 func_name 裸名)是错误的:stub 注入的 declare Name 已带 SHA1 前缀, + # 用裸名会导致 call @_PathToModuleName 而 define 是 @"sha1._PathToModuleName",链接报 undefined reference call_name: str = func_name if found_func is not None: - if llvmlite.function_is_declared(found_func) == 0: - found_name: t.CChar | t.CPtr = llvmlite.function_get_name(found_func) - if found_name is not None: - call_name = found_name + found_name: t.CChar | t.CPtr = llvmlite.function_get_name(found_func) + if found_name is not None: + call_name = found_name else: # 跨模块函数调用:found_func 为 None 时,检查是否是模块限定调用 # 如 string.strchr(...) → @{sha1_of_string}.strchr @@ -3554,6 +4259,12 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, if xmod_nm.id is not None: mod_sha1: str = _lookup_module_sha1(xmod_nm.id) if mod_sha1 is not None: + # 检查是否是 re-export 函数(如 ast._lexer_init 实际定义在 ast.lexer) + reexport_src: str = HandlesImports.lookup_reexport(mod_sha1, func_name) + if reexport_src is not None: + reexport_sha1: str = _lookup_module_sha1(reexport_src) + if reexport_sha1 is not None: + mod_sha1 = reexport_sha1 # CExport 函数定义用裸名(@strlen),跨模块调用需用裸名而非 @{sha1}.func if is_cexport_func(mod_sha1, func_name) != 0: call_name = func_name @@ -3576,12 +4287,29 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, is_imported_mod = HandlesImports.is_module_imported( trans._imported_modules, xmod_nm.id) if is_imported_mod != 0: - # 已导入模块但 SHA1 未注册:该模块未被编译,立即报错 - err_msg_imp: t.CChar | t.CPtr = pool.alloc(256) - if err_msg_imp is not None: - viperlib.snprintf(err_msg_imp, 256, "模块 '%s' 已导入但 SHA1 未注册(该模块未被编译)", xmod_nm.id) - HandlesType.fatal_error(xmod_nm, err_msg_imp) - HandlesType.fatal_error(xmod_nm, "已导入模块 SHA1 未注册") + # 别名导入(import X as Y):Y 在 _imported_modules 中, + # 但 _lookup_module_sha1("Y") 精确匹配失败(mod_arr 只存文件名)。 + # 先尝试 from_imports 查找别名→完整模块名→后缀匹配 SHA1。 + # allow_star_fallback=0 避免被 star import 回退误导。 + alias_sha1: str = None + if trans is not None and trans._from_imports is not None: + alias_sha1 = _lookup_mod_sha1(pool, trans._from_imports, xmod_nm.id, 0) + if alias_sha1 is not None: + if is_cexport_func(alias_sha1, func_name) != 0: + call_name = func_name + else: + AliasMangledLen: t.CSizeT = string.strlen(alias_sha1) + string.strlen(func_name) + 2 + alias_mangled: str = pool.alloc(AliasMangledLen) + if alias_mangled is not None: + viperlib.snprintf(alias_mangled, AliasMangledLen, "%s.%s", alias_sha1, func_name) + call_name = alias_mangled + else: + # 已导入模块但 SHA1 未注册:该模块未被编译,立即报错 + err_msg_imp: t.CChar | t.CPtr = pool.alloc(256) + if err_msg_imp is not None: + viperlib.snprintf(err_msg_imp, 256, "模块 '%s' 已导入但 SHA1 未注册(该模块未被编译)", xmod_nm.id) + HandlesType.fatal_error(xmod_nm, err_msg_imp) + HandlesType.fatal_error(xmod_nm, "已导入模块 SHA1 未注册") else: # 检查是否是通过 from import 导入的名称(如 from .__types import LLVMType) # LLVMType.Ptr(ty) 中 LLVMType 是类名不是模块名, @@ -3612,13 +4340,30 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, if trans is not None and trans._from_imports is not None: bare_sha1: str = _lookup_mod_sha1(pool, trans._from_imports, func_name, 1) if bare_sha1 is not None: - if is_cexport_func(bare_sha1, func_name) != 0: - call_name = func_name + # 别名处理: from X import Y as Z → 调用 Z 时需用 Y 作为源模块函数名 + # 例如 from json.__parser import parse as json_parse → 修饰为 SHA1.parse 而非 SHA1.json_parse + bare_orig: str = HandlesImports.lookup_from_import_original( + trans._from_imports, func_name) + bare_func_name: str = func_name + if bare_orig is not None: + # bare_orig 指向 from_imports 内部缓冲区,未 null 终止 + # 需复制到独立缓冲区(截断于空格或 null) + bo_len: t.CSizeT = 0 + while bare_orig[bo_len] != ' ' and bare_orig[bo_len] != '\0': + bo_len += 1 + bo_buf: str = pool.alloc(bo_len + 1) + if bo_buf is not None: + for bo_i in range(bo_len): + bo_buf[bo_i] = bare_orig[bo_i] + bo_buf[bo_len] = '\0' + bare_func_name = bo_buf + if is_cexport_func(bare_sha1, bare_func_name) != 0: + call_name = bare_func_name else: - bare_mangled_len: t.CSizeT = string.strlen(bare_sha1) + string.strlen(func_name) + 2 + bare_mangled_len: t.CSizeT = string.strlen(bare_sha1) + string.strlen(bare_func_name) + 2 bare_mangled: str = pool.alloc(bare_mangled_len) if bare_mangled is not None: - viperlib.snprintf(bare_mangled, bare_mangled_len, "%s.%s", bare_sha1, func_name) + viperlib.snprintf(bare_mangled, bare_mangled_len, "%s.%s", bare_sha1, bare_func_name) call_name = bare_mangled else: err_msg3: t.CChar | t.CPtr = pool.alloc(256) @@ -3637,6 +4382,8 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, # memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失) if func_name == "memcpy" and can >= 3: return _emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst2, mc_src2, mc_num2) + # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value + _ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can) return llvmlite.build_call(builder, call_name, head, can, call_ret_ty, 0) # ============================================================ @@ -3769,6 +4516,9 @@ class FuncEntry: """函数表条目""" Name: str Func: llvmlite.Function | t.CPtr + Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr]),None=无默认值 + DefaultCount: int # 默认值数量 + ParamCount: int # 形参数量(不含 self) # ============================================================ # 初始化函数表 @@ -3807,8 +4557,17 @@ def add_func_to_table(funcs: FuncEntry | t.CPtr, func_count: int, name: str, func: llvmlite.Function | t.CPtr, - max_count: int) -> int: - """添加函数到函数表,返回 0 成功""" + max_count: int, + defaults: t.CVoid | t.CPtr = None, + default_count: int = 0, + param_count: int = 0) -> int: + """添加函数到函数表,返回 0 成功 + + Args: + defaults: ast.Arguments.defaults 列表节点指针,None=无默认值 + default_count: 默认值数量(len(defaults)) + param_count: 形参数量(不含 self,len(args.args)) + """ if name is None or func is None or funcs is None: return 1 if func_count >= max_count: @@ -3818,8 +4577,14 @@ def add_func_to_table(funcs: FuncEntry | t.CPtr, entry: FuncEntry | t.CPtr = (FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) entry.Name = name entry.Func = func + entry.Defaults = defaults + entry.DefaultCount = default_count + entry.ParamCount = param_count # 同时注册到全局函数表(供跨模块 vtable 初始化查找) _register_global_func(name, func) + # 同时注册到全局默认参数表(供跨模块调用时填充默认值) + if default_count > 0: + _register_global_defaults(name, defaults, default_count, param_count) return 0 # ============================================================ @@ -3877,6 +4642,97 @@ def find_func_global(name: str) -> llvmlite.Function | t.CPtr: return None return find_func_in_table(_global_funcs, _global_func_count, name) + +# ============================================================ +# 全局默认参数注册表(跨模块默认参数填充) +# +# 独立于 _global_funcs,因为 _register_global_func 可能在 +# add_func_to_table 之前被调用(如 stub 注入),此时 defaults 还不可用。 +# ============================================================ +GLOBAL_DEFAULTS_MAX: t.CDefine = 4096 + +@t.NoVTable +class FuncDefaultsEntry: + """默认参数表条目""" + Name: str + Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr]) + DefaultCount: int + ParamCount: int # 形参数量(不含 self) + +_global_defaults: FuncDefaultsEntry | t.CPtr = None +_global_defaults_count: int = 0 + +def _init_global_defaults_table() -> int: + """初始化全局默认参数表""" + global _global_defaults + global _global_defaults_count + if _global_defaults is not None: + return 1 + size: t.CSizeT = GLOBAL_DEFAULTS_MAX * FuncDefaultsEntry.__sizeof__() + _global_defaults = stdlib.malloc(size) + if _global_defaults is None: + return 0 + string.memset(_global_defaults, 0, size) + _global_defaults_count = 0 + return 1 + +def _register_global_defaults(name: str, + defaults: t.CVoid | t.CPtr, + default_count: int, + param_count: int) -> int: + """注册函数默认参数到全局表,返回 0 成功""" + global _global_defaults_count + if name is None or defaults is None: + return 1 + if _init_global_defaults_table() == 0: + return 1 + # 已存在则跳过 + if find_func_defaults(name) is not None: + return 0 + if _global_defaults_count >= GLOBAL_DEFAULTS_MAX: + return 1 + entry_size: t.CSizeT = FuncDefaultsEntry.__sizeof__() + entry_addr: t.CUInt64T = t.CUInt64T(_global_defaults) + _global_defaults_count * entry_size + entry: FuncDefaultsEntry | t.CPtr = (FuncDefaultsEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) + entry.Name = name + entry.Defaults = defaults + entry.DefaultCount = default_count + entry.ParamCount = param_count + _global_defaults_count += 1 + return 0 + +def find_func_defaults(name: str) -> FuncDefaultsEntry | t.CPtr: + """按函数名查找默认参数信息,返回 FuncDefaultsEntry 或 None""" + if name is None or _global_defaults is None: + return None + entry_size: t.CSizeT = FuncDefaultsEntry.__sizeof__() + i: int = 0 + while i < _global_defaults_count: + entry_addr: t.CUInt64T = t.CUInt64T(_global_defaults) + i * entry_size + entry: FuncDefaultsEntry | t.CPtr = (FuncDefaultsEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) + if entry.Name is not None: + if string.strcmp(entry.Name, name) == 0: + return entry + i += 1 + return None + +def find_func_entry_in_table(funcs: FuncEntry | t.CPtr, + func_count: int, + name: str) -> FuncEntry | t.CPtr: + """在函数表中按名称查找,返回完整 FuncEntry 或 None""" + if name is None or funcs is None: + return None + entry_size: t.CSizeT = FuncEntry.__sizeof__() + i: int = 0 + while i < func_count: + entry_addr: t.CUInt64T = t.CUInt64T(funcs) + i * entry_size + entry: FuncEntry | t.CPtr = (FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) + if entry.Name is not None: + if string.strcmp(entry.Name, name) == 0: + return entry + i += 1 + return None + # ============================================================ # ExprCallHandle - 函数调用处理器(Mixin 继承模式) # diff --git a/App/lib/core/Handles/HandlesExprOps.py b/App/lib/core/Handles/HandlesExprOps.py index dac726d..e28216e 100644 --- a/App/lib/core/Handles/HandlesExprOps.py +++ b/App/lib/core/Handles/HandlesExprOps.py @@ -272,6 +272,8 @@ def translate_binop(pool: memhub.MemBuddy | t.CPtr, return llvmlite.build_mul(builder, lhs, rhs) elif op == ast.OpKind.Div: return llvmlite.build_sdiv(builder, lhs, rhs) + elif op == ast.OpKind.FloorDiv: + return llvmlite.build_sdiv(builder, lhs, rhs) elif op == ast.OpKind.Mod: return llvmlite.build_srem(builder, lhs, rhs) elif op == ast.OpKind.BitAnd: diff --git a/App/lib/core/Handles/HandlesFor.py b/App/lib/core/Handles/HandlesFor.py index 0ed8f1e..5e0d71f 100644 --- a/App/lib/core/Handles/HandlesFor.py +++ b/App/lib/core/Handles/HandlesFor.py @@ -138,7 +138,7 @@ class ForHandle(HandlesBase.Mixin): trans.SymTab, var_name) new_vars: int = 0 if var_alloca is None: - var_alloca = llvmlite.build_alloca(builder, i32_ty) + var_alloca = HandlesVar._alloca_at_entry(builder, i32_ty) if HandlesVar.define_var( trans.SymTab, var_name, var_alloca) == 0: new_vars = 1 @@ -294,13 +294,13 @@ class ForHandle(HandlesBase.Mixin): trans.SymTab, var_name) new_vars: int = 0 if var_alloca is None: - var_alloca = llvmlite.build_alloca(builder, elem_ty) + var_alloca = HandlesVar._alloca_at_entry(builder, elem_ty) if HandlesVar.define_var( trans.SymTab, var_name, var_alloca) == 0: new_vars = 1 # 创建隐式 index 变量,初始为 0 - idx_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i32_ty) + idx_alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, i32_ty) llvmlite.build_store(builder, llvmlite.const_int32(pool, 0), idx_alloca) # 创建基本块: cond / body / incr / end diff --git a/App/lib/core/Handles/HandlesFunctions.py b/App/lib/core/Handles/HandlesFunctions.py index 4f2a901..918940c 100644 --- a/App/lib/core/Handles/HandlesFunctions.py +++ b/App/lib/core/Handles/HandlesFunctions.py @@ -7,6 +7,7 @@ import string import viperlib import stdio import stdlib +import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesVar as HandlesVar import lib.core.Handles.HandlesExprCall as HandlesExprCall @@ -17,6 +18,10 @@ import lib.core.Handles.HandlesNonlocal as HandlesNonlocal import lib.core.Handles.HandlesImports as HandlesImports +# 作用域类型常量(本地副本,避免旧编译器跨模块 CDefine 查找 bug) +SCOPE_FUNCTION: t.CDefine = 1 + + # ============================================================ # extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性 # @@ -311,7 +316,7 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr, ret_ty: llvmlite.LLVMType | t.CPtr = None if fd.returns is not None: ret_ty = HandlesType.resolve_annotation_type( - pool, fd.returns, imported_modules, from_imports) + pool, fd.returns, imported_modules, from_imports, trans) if ret_ty is None and fd.returns is not None: if HandlesType.has_decorator_marker(fd.returns, "State") != 0: ret_ty = llvmlite.Void(pool) @@ -347,10 +352,24 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr, if func is None: continue + # 提取默认参数信息 + fd_args_node: ast.Arguments | t.CPtr = fd.args + fd_defaults: t.CVoid | t.CPtr = None + fd_default_count: int = 0 + fd_param_count: int = 0 + if fd_args_node is not None: + fd_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd_args_node) + if fd_ags.args is not None: + fd_param_count = fd_ags.args.__len__() + if fd_ags.defaults is not None: + fd_defaults = fd_ags.defaults + fd_default_count = fd_ags.defaults.__len__() + # 注册到函数表(用裸名 fd.name,不是 mangled_name) max_funcs: int = 256 cur_count: int = trans._func_count - if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs) == 0: + if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs, + fd_defaults, fd_default_count, fd_param_count) == 0: trans._func_count = cur_count + 1 # 注册 CExport 函数到全局表 @@ -375,7 +394,7 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr, param_ty: llvmlite.LLVMType | t.CPtr = i32_ty if arg.annotation is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, arg.annotation, imported_modules, from_imports) + pool, arg.annotation, imported_modules, from_imports, trans) if resolved is not None: param_ty = resolved pname: t.CChar | t.CPtr = pool.alloc(32) @@ -412,7 +431,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr, ret_ty: llvmlite.LLVMType | t.CPtr = None if fd.returns is not None: ret_ty = HandlesType.resolve_annotation_type( - pool, fd.returns, imported_modules, from_imports) + pool, fd.returns, imported_modules, from_imports, trans) # 如果返回类型注解纯装饰器标记(如 t.State,无实际类型),使用 void # 注意:必须在 infer_return_type 之前检测,因为 infer_return_type 至少返回 i32 @@ -490,7 +509,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr, param_ty_e: llvmlite.LLVMType | t.CPtr = i32_ty if arg_e.annotation is not None: resolved_e: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, arg_e.annotation, imported_modules, from_imports) + pool, arg_e.annotation, imported_modules, from_imports, trans) if resolved_e is not None: param_ty_e = resolved_e pname_e: t.CChar | t.CPtr = pool.alloc(32) @@ -521,7 +540,10 @@ def translate_function_def(trans: HT.Translator | t.CPtr, # 创建新的 LLVM 函数(使用 SHA1 混淆名) func = llvmlite.create_function(pool, mod, mangled_name, ret_ty) if func is None: - stdio.printf("[FUNC] create_function %s failed\n", fd.name) + fb_cf: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_cf is not None: + viperlib.snprintf(fb_cf, 1024, "create_function %s failed", fd.name) + VLogger.error(fb_cf, "FUNC") return 0 # 提取 @c.Attribute 装饰器属性并设置到函数 @@ -529,9 +551,22 @@ def translate_function_def(trans: HT.Translator | t.CPtr, if func_attrs is not None: llvmlite.function_set_attrs(func, func_attrs) + # 提取默认参数信息 + tfd_defaults: t.CVoid | t.CPtr = None + tfd_default_count: int = 0 + tfd_param_count: int = 0 + if args_node is not None: + tfd_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node) + if tfd_ags.args is not None: + tfd_param_count = tfd_ags.args.__len__() + if tfd_ags.defaults is not None: + tfd_defaults = tfd_ags.defaults + tfd_default_count = tfd_ags.defaults.__len__() + # 注册到函数表 max_funcs: int = 256 - if HandlesExprCall.add_func_to_table(funcs_ptr, func_count, fd.name, func, max_funcs) == 0: + if HandlesExprCall.add_func_to_table(funcs_ptr, func_count, fd.name, func, max_funcs, + tfd_defaults, tfd_default_count, tfd_param_count) == 0: trans._func_count = func_count + 1 # 添加参数(支持类型注解) @@ -550,7 +585,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr, param_ty: llvmlite.LLVMType | t.CPtr = i32_ty if arg.annotation is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, arg.annotation, imported_modules, from_imports) + pool, arg.annotation, imported_modules, from_imports, trans) if resolved is not None: param_ty = resolved pname: t.CChar | t.CPtr = pool.alloc(32) @@ -569,7 +604,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr, llvmlite.position_at_end(func_builder, entry_blk) # 进入函数作用域(嵌套符号表) - HandlesVar.enter_scope(trans.SymTab, HandlesVar.SCOPE_FUNCTION) + HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION) # 为参数创建 alloca if args_node is not None: @@ -587,7 +622,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr, param_ty2: llvmlite.LLVMType | t.CPtr = i32_ty if arg2.annotation is not None: resolved2: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, arg2.annotation, imported_modules, from_imports) + pool, arg2.annotation, imported_modules, from_imports, trans) if resolved2 is not None: param_ty2 = resolved2 alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, param_ty2) @@ -633,6 +668,8 @@ def translate_function_def(trans: HT.Translator | t.CPtr, for bi2 in range(bn2): stmt2: ast.AST | t.CPtr = body.get(bi2) if stmt2 is not None: + stdio.printf("[DBG] TR body stmt=%d kind=%d\n", bi2, stmt2.kind()) + stdio.fflush(0) HandlesBody.translate_stmt(trans, stmt2) # 如果函数体最后一条语句不是 Return,添加隐式 ret @@ -644,8 +681,24 @@ def translate_function_def(trans: HT.Translator | t.CPtr, if last_stmt is not None and last_stmt.kind() == ast.ASTKind.Return: last_is_return = 1 if last_is_return == 0: - zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) - llvmlite.build_ret(func_builder, zero_val) + if llvmlite.builder_cur_block_is_terminated(func_builder) == 0: + # 根据返回类型生成正确的零值返回 + _is_void: int = 0 + _is_ptr: int = 0 + if ret_ty is not None: + match ret_ty: + case llvmlite.LLVMType.Void(): + _is_void = 1 + case llvmlite.LLVMType.Ptr(_pe): + _is_ptr = 1 + if _is_void != 0: + llvmlite.build_ret_void(func_builder) + elif _is_ptr != 0: + _null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null") + llvmlite.build_ret(func_builder, _null_val) + else: + _zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) + llvmlite.build_ret(func_builder, _zero_val) # 恢复模块级作用域(退出函数作用域) HandlesVar.exit_scope(trans.SymTab) @@ -706,7 +759,7 @@ def translate_nested_function_def(trans: HT.Translator | t.CPtr, llvmlite.position_at_end(func_builder, entry_blk) # 4. 进入嵌套函数作用域(嵌套符号表) - HandlesVar.enter_scope(trans.SymTab, HandlesVar.SCOPE_FUNCTION) + HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION) # 5. 创建 _env_ptr alloca 并存储 %env 参数 env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty) @@ -927,7 +980,7 @@ def create_function(pool: memhub.MemBuddy | t.CPtr, param_ty: llvmlite.LLVMType | t.CPtr = i32_ty if arg.annotation is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, arg.annotation, None, None) + pool, arg.annotation, None, None, None) if resolved is not None: param_ty = resolved pname: t.CChar | t.CPtr = pool.alloc(32) diff --git a/App/lib/core/Handles/HandlesImports.py b/App/lib/core/Handles/HandlesImports.py index c129b08..4d9512a 100644 --- a/App/lib/core/Handles/HandlesImports.py +++ b/App/lib/core/Handles/HandlesImports.py @@ -3,6 +3,7 @@ from stdint import * import ast import memhub import string +import stdlib import viperlib import stdio import lib.core.Handles.HandlesBase as HandlesBase @@ -88,14 +89,24 @@ def is_module_imported(imported_modules: str, name: str) -> int: def add_from_import(pool: memhub.MemBuddy | t.CPtr, from_imports: str, local_name: str, - module_name: str) -> str: - """添加 from-import 映射 "name:module",返回新的 from_imports 字符串""" + module_name: str, + original_name: str = None) -> str: + """添加 from-import 映射,返回新的 from_imports 字符串 + + 格式: + 无别名: "local_name:module_name" + 有别名: "local_name:module_name:original_name" + (original_name 为源模块中的真实函数名,用于跨模块 SHA1 修饰) + """ if local_name is None or module_name is None: return from_imports - entry: t.CChar | t.CPtr = pool.alloc(128) + entry: t.CChar | t.CPtr = pool.alloc(256) if entry is None: return from_imports - viperlib.snprintf(entry, 128, "%s:%s", local_name, module_name) + if original_name is not None and original_name != local_name: + viperlib.snprintf(entry, 256, "%s:%s:%s", local_name, module_name, original_name) + else: + viperlib.snprintf(entry, 256, "%s:%s", local_name, module_name) if from_imports is None: return entry else: @@ -111,6 +122,75 @@ def add_from_import(pool: memhub.MemBuddy | t.CPtr, return from_imports +# ============================================================ +# 查找 from-import 名称 → 返回原始函数名(别名场景) +# +# 对于 from X import Y as Z,返回 Y(源模块中的真实函数名)。 +# 无别名时返回 None(local_name 即为原始名)。 +# ============================================================ +def lookup_from_import_original(from_imports: str, + local_name: str) -> str: + """查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到""" + if local_name is None or from_imports is None: + return None + name_len: t.CSizeT = string.strlen(local_name) + cur: t.CChar | t.CPtr = from_imports + ci: t.CSizeT = 0 + total_len: t.CSizeT = string.strlen(from_imports) + while ci < total_len: + # 跳过前导空格 + while ci < total_len and cur[ci] == ' ': + ci += 1 + if ci >= total_len: + break + # 找到第一个 ':' 的位置 + colon1: t.CSizeT = ci + while colon1 < total_len and cur[colon1] != ':' and cur[colon1] != ' ': + colon1 += 1 + if colon1 >= total_len or cur[colon1] != ':': + break + entry_name_len: t.CSizeT = colon1 - ci + # 跳过 star import + if entry_name_len == 1 and cur[ci] == '*': + ci = colon1 + while ci < total_len and cur[ci] != ' ': + ci += 1 + continue + # 比较名称 + if entry_name_len == name_len: + match: int = 1 + ei: t.CSizeT = 0 + while ei < name_len: + if cur[ci + ei] != local_name[ei]: + match = 0 + break + ei += 1 + if match == 1: + # 找到匹配,检查是否有第三个字段(原始名) + pos: t.CSizeT = colon1 + 1 + # 跳过模块名 + while pos < total_len and cur[pos] != ':' and cur[pos] != ' ' and cur[pos] != '\0': + pos += 1 + if pos < total_len and cur[pos] == ':': + # 有第三个字段: original_name + orig_start: t.CSizeT = pos + 1 + orig_end: t.CSizeT = orig_start + while orig_end < total_len and cur[orig_end] != ' ' and cur[orig_end] != '\0': + orig_end += 1 + orig_len: t.CSizeT = orig_end - orig_start + if orig_len > 0: + orig_buf: str = cur + orig_start + # 返回指向内部的指针(调用方需在使用期内保持 from_imports 有效) + return orig_buf + return None + return None + # 跳到下一个条目 + ci = colon1 + while ci < total_len and cur[ci] != ' ': + ci += 1 + return None + + # ============================================================ # 查找 from-import 名称 → 返回模块名或 None # @@ -150,7 +230,7 @@ def lookup_from_import(from_imports: str, name: str, if entry_name_len == 1 and cur[ci] == '*': mod_start: t.CSizeT = colon_pos + 1 mod_end: t.CSizeT = mod_start - while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0': + while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0' and cur[mod_end] != ':': mod_end += 1 star_mod = cur + mod_start # 比较名称 @@ -165,7 +245,8 @@ def lookup_from_import(from_imports: str, name: str, if match == 1: mod_start2: t.CSizeT = colon_pos + 1 mod_end2: t.CSizeT = mod_start2 - while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0': + # 模块名结束于: 空格、null、或第二个':'(别名格式的分隔符) + while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':': mod_end2 += 1 return cur + mod_start2 # 跳到下一个条目 @@ -178,6 +259,93 @@ def lookup_from_import(from_imports: str, name: str, return star_mod +# ============================================================ +# 全局 re-export 映射表(module_sha1 + func_name → source_module) +# +# 当 ast/__init__.py 中有 from .lexer import _lexer_init 时, +# _lexer_init 被 re-export 为 ast._lexer_init。 +# 跨模块调用 ast._lexer_init(...) 需使用 ast.lexer 的 SHA1 混淆, +# 而非 ast 的 SHA1。 +# +# 数据布局: +# _g_reexport_sha1s: 每条 17 字节(SHA1 16字符 + null) +# _g_reexport_funcs: 每条 64 字节(函数名 + null) +# _g_reexport_srcs: 每条 64 字节(源模块名 + null) +# ============================================================ +MAX_REEXPORT: t.CDefine = 512 + +_g_reexport_sha1s: bytes = None +_g_reexport_funcs: bytes = None +_g_reexport_srcs: bytes = None +_g_reexport_count: int = 0 + + +# ============================================================ +# register_reexport - 注册 re-export 映射 +# +# 在 HandleImportFromNames 中调用:当处理 from .X import Y 时, +# 记录当前模块(通过 ModuleSha1)re-export 了函数 Y,源模块为 X。 +# +# 幂等:重复注册相同映射不会增加条目。 +# 内存用 stdlib.malloc 分配(全局存储器,跨 Phase 持久化)。 +# ============================================================ +def register_reexport(mod_sha1: str, func_name: str, source_module: str) -> int: + """注册 re-export 映射(幂等),返回 0 成功,-1 失败""" + global _g_reexport_sha1s, _g_reexport_funcs, _g_reexport_srcs, _g_reexport_count + if mod_sha1 is None or func_name is None or source_module is None: + return -1 + # 懒初始化 + if _g_reexport_sha1s is None: + _g_reexport_sha1s = stdlib.malloc(MAX_REEXPORT * 17) + _g_reexport_funcs = stdlib.malloc(MAX_REEXPORT * 64) + _g_reexport_srcs = stdlib.malloc(MAX_REEXPORT * 64) + if _g_reexport_sha1s is None or _g_reexport_funcs is None or _g_reexport_srcs is None: + return -1 + string.memset(_g_reexport_sha1s, 0, MAX_REEXPORT * 17) + string.memset(_g_reexport_funcs, 0, MAX_REEXPORT * 64) + string.memset(_g_reexport_srcs, 0, MAX_REEXPORT * 64) + if _g_reexport_count >= MAX_REEXPORT: + return -1 + # 幂等检查:查找是否已存在相同映射 + for i in range(_g_reexport_count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + fidx: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0: + if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0: + # 已存在,更新 source_module(以防变化) + string.strcpy(_g_reexport_srcs + fidx, source_module) + return 0 + # 添加新条目 + idx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 17 + fidx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 64 + string.strcpy(_g_reexport_sha1s + idx2, mod_sha1) + string.strcpy(_g_reexport_funcs + fidx2, func_name) + string.strcpy(_g_reexport_srcs + fidx2, source_module) + _g_reexport_count += 1 + return 0 + + +# ============================================================ +# lookup_reexport - 查找 re-export 映射 +# +# 给定模块 SHA1 和函数名,返回源模块名(如 "ast.lexer")或 None。 +# 调用方通过 _lookup_module_sha1(source_module) 获取源模块的 SHA1。 +# ============================================================ +def lookup_reexport(mod_sha1: str, func_name: str) -> str: + """查找 re-export 映射,返回源模块名或 None""" + if mod_sha1 is None or func_name is None: + return None + if _g_reexport_sha1s is None or _g_reexport_count <= 0: + return None + for i in range(_g_reexport_count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + fidx: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0: + if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0: + return _g_reexport_srcs + fidx + return None + + # ============================================================ # _resolve_relative_module - 解析相对导入为完整模块名 # @@ -335,6 +503,24 @@ class ImportsHandle(HandlesBase.Mixin): if alias.asname is not None: self.Trans._imported_modules = add_imported_module( pool_val, self.Trans._imported_modules, alias.asname) + # 别名也加入 from_imports(asname:fullname), + # 使 HT.func() 跨模块调用能通过 from_imports 查找别名→完整模块名→SHA1 + # 否则 _lookup_module_sha1("HT") 找不到(mod_arr 只存文件名 HandlesTranslator) + self.Trans._from_imports = add_from_import( + pool_val, self.Trans._from_imports, alias.asname, alias.name) + else: + # 无别名时(import a.b.c),将顶层包名 "a" 也加入 _imported_modules, + # 使 a.b.c 作为属性访问的 base 被翻译时能通过 is_module_imported("a") 检查 + dot_ptr: str = string.strstr(alias.name, '.') + if dot_ptr is not None: + pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_ptr) - t.CUInt64T(alias.name)) + if pkg_len > 0 and pkg_len < 256: + pkg_buf: bytes = pool_val.alloc(pkg_len + 1) + if pkg_buf is not None: + string.strncpy(pkg_buf, alias.name, pkg_len) + pkg_buf[pkg_len] = '\0' + self.Trans._imported_modules = add_imported_module( + pool_val, self.Trans._imported_modules, pkg_buf) return 0 # ============================================================ @@ -381,13 +567,19 @@ class ImportsHandle(HandlesBase.Mixin): alias: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names.get(ni)) if alias is not None and alias.name is not None: local_name: str = alias.name + orig_name: str = None if alias.asname is not None: local_name = alias.asname + orig_name = alias.name self.Trans._from_imports = add_from_import( self.Trans.Pool, self.Trans._from_imports, - local_name, resolved) + local_name, resolved, orig_name) # 命名空间隔离:from-import 的名称标记为可见结构体 HandlesStruct.add_visible_struct(self.Trans.Pool, local_name) + # 注册 re-export 映射:当前模块 re-export 了 local_name,源模块为 resolved + # 使跨模块调用 module.func() 能解析到正确的源模块 SHA1 + if self.Trans.ModuleSha1 is not None: + register_reexport(self.Trans.ModuleSha1, local_name, resolved) return 0 diff --git a/App/lib/core/Handles/HandlesMain.py b/App/lib/core/Handles/HandlesMain.py index 435fb00..7165959 100644 --- a/App/lib/core/Handles/HandlesMain.py +++ b/App/lib/core/Handles/HandlesMain.py @@ -6,6 +6,7 @@ import memhub import string import stdio import viperlib +import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesVar as HandlesVar import lib.core.Handles.HandlesExprCall as HandlesExprCall @@ -96,6 +97,10 @@ def translate_children(trans: HT.Translator | t.CPtr, # Phase 1a 声明模式:只注册 CExport/State 函数到全局表(解决翻译顺序依赖) # Phase 1b 全量翻译:正常翻译函数体 if trans._declare_only == 0: + fd_dbg: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child) + if fd_dbg is not None and fd_dbg.name is not None: + stdio.printf("[DBG] TR func=%s\n", fd_dbg.name) + stdio.fflush(0) added: int = HandlesFunctions.translate_function_def(trans, child) added_total += added elif trans._declare_only == 1: @@ -104,6 +109,10 @@ def translate_children(trans: HT.Translator | t.CPtr, # ClassDef 在模块级直接处理(不需要 builder) # _declare_only=2(import扫描模式)时跳过,只处理 import 依赖 if trans._declare_only != 2: + cd_dbg: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child) + if cd_dbg is not None and cd_dbg.name is not None: + stdio.printf("[DBG] TR class=%s\n", cd_dbg.name) + stdio.fflush(0) HandlesClassDef.translate_class_def(trans, child) elif trans._declare_only == 0 and trans._cur_builder is not None: # 有 builder → 委托 HandlesBody 分派 @@ -171,7 +180,7 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr, # 解析类型 if aa.annotation is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, aa.annotation, trans._imported_modules, trans._from_imports) + pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) if resolved is not None: var_ty = resolved # 解析初始值 @@ -208,6 +217,11 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr, return 0 # 创建全局变量 @var_name + # 先确保 var_ty 中的跨模块结构体类型在模块中有 opaque 声明 + # 否则 @var_name = global %"sha1.ClassName"* zeroinitializer 会报 + # "use of undefined type named 'sha1.ClassName'" 错误 + # (如 _mbuddy: memhub.MemManager | t.CPtr → Ptr(Struct("aeb3...MemManager"))) + llvmlite.module_ensure_opaque_for_type(mod, pool, var_ty) gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, var_name, var_ty) if gv is None: return 0 @@ -221,7 +235,9 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr, viperlib.snprintf(init_buf, 48, "%lld", init_val) gv.Initializer = init_buf else: - gv.Initializer = "0" + # 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer, + # 整数/聚合类型也兼容 zeroinitializer,避免 "integer constant must have integer type" + gv.Initializer = "zeroinitializer" # 创建 Value 引用(@var_name, 类型为 var_ty*) var_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, var_ty) @@ -232,6 +248,16 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr, # 注册到模块作用域 if HandlesVar.define_module_var(trans.SymTab, var_name, gv_ref) == 0: + # 设置 AnnotClassName(联合类型如 Logger|t.CPtr 简化为 Ptr(i8) 时, + # 属性访问需要通过 AnnotClassName 回退到类名查找结构体) + if k == ast.ASTKind.AnnAssign: + aa_ml: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node) + if aa_ml is not None and aa_ml.annotation is not None: + cls_nm_ml: str = HandlesType.extract_class_name_from_annotation( + aa_ml.annotation, trans._imported_modules) + if cls_nm_ml is not None: + HandlesVar.set_var_annot_class_name( + trans.SymTab, var_name, cls_nm_ml) return 1 return 0 @@ -262,18 +288,18 @@ def create_wrapper_main(trans: HT.Translator | t.CPtr, main_func: llvmlite.Function | t.CPtr = llvmlite.create_function( pool, mod, "main", i32_ty) if main_func is None: - stdio.printf("[TR] CreateFunction main returned NULL\n") + VLogger.error("CreateFunction main returned NULL", "TR") return 0 entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( pool, main_func, "entry") if entry_blk is None: - stdio.printf("[TR] CreateBlock returned NULL\n") + VLogger.error("CreateBlock returned NULL", "TR") return 0 builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, main_func) if builder is None: - stdio.printf("[TR] NewBuilder returned NULL\n") + VLogger.error("NewBuilder returned NULL", "TR") return 0 llvmlite.position_at_end(builder, entry_blk) diff --git a/App/lib/core/Handles/HandlesReturn.py b/App/lib/core/Handles/HandlesReturn.py index 9b077f7..a41c3f0 100644 --- a/App/lib/core/Handles/HandlesReturn.py +++ b/App/lib/core/Handles/HandlesReturn.py @@ -55,31 +55,47 @@ class ReturnHandle(HandlesBase.Mixin): self.Trans._funcs, self.Trans._func_count, self.Trans) # val 为 None 时(裸 return):检查当前函数返回类型 - # void 函数(如 __init__/__before_init__)生成 ret void,否则 ret i32 0 + # void 函数(如 __init__/__before_init__)生成 ret void, + # ptr 函数生成 ret ptr null,否则 ret i32 0 if val is None: cur_func: llvmlite.Function | t.CPtr = self.Trans._cur_func is_void_ret: int = 0 + is_ptr_ret: int = 0 + ret_ty: llvmlite.LLVMType | t.CPtr = None if cur_func is not None: - ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cur_func) + ret_ty = llvmlite.function_get_ret_ty(cur_func) if ret_ty is not None: match ret_ty: case llvmlite.LLVMType.Void(): is_void_ret = 1 + case llvmlite.LLVMType.Ptr(_pe): + is_ptr_ret = 1 if is_void_ret != 0: llvmlite.build_ret_void(builder) return 0 + if is_ptr_ret != 0: + null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null") + llvmlite.build_ret(builder, null_val) + return 0 val = llvmlite.const_int32(pool, 0) # 类型转换:确保 val 类型与函数返回类型匹配 # 处理 i1(bool 比较结果)→ i8(t.CBool)等情况 # i1 → 更宽整数用 zext(bool 语义:1 保持 1,而非 sext 的 0xFF) + # 注意:当 val 是整数而 ret_ty 是 ptr 时(即使 bits 相同,如 i64 vs ptr), + # 也必须 inttoptr 转换,否则 llc 报 "value doesn't match function result type" cur_func_rt: llvmlite.Function | t.CPtr = self.Trans._cur_func if cur_func_rt is not None and val is not None and val.Ty is not None: ret_ty_rt: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cur_func_rt) if ret_ty_rt is not None: val_bits: int = HandlesExpr.get_llvm_type_bits(val.Ty) ret_bits: int = HandlesExpr.get_llvm_type_bits(ret_ty_rt) - if val_bits != 0 and ret_bits != 0 and val_bits != ret_bits: + val_is_ptr: int = HandlesExpr.is_ptr_type(val.Ty) + ret_is_ptr: int = HandlesExpr.is_ptr_type(ret_ty_rt) + # ptr 类型不一致性:val 是整数但 ret 是 ptr(或反之),必须转换 + if val_is_ptr != ret_is_ptr: + val = HandlesExpr.coerce_to_type(builder, val, ret_ty_rt) + elif val_bits != 0 and ret_bits != 0 and val_bits != ret_bits: if val_bits == 1 and val_bits < ret_bits: val = llvmlite.build_zext(builder, val, ret_ty_rt) else: diff --git a/App/lib/core/Handles/HandlesStruct.py b/App/lib/core/Handles/HandlesStruct.py index 460e46d..077032d 100644 --- a/App/lib/core/Handles/HandlesStruct.py +++ b/App/lib/core/Handles/HandlesStruct.py @@ -6,6 +6,8 @@ import llvmlite import stdio import ast import hashtable +import viperlib +import lib.core.VLogger as VLogger # ============================================================ @@ -19,7 +21,7 @@ import hashtable # ============================================================ STRUCT_MAX: t.CDefine = 512 -FIELD_MAX: t.CDefine = 32 +FIELD_MAX: t.CDefine = 48 FIELD_NAME_MAX: t.CDefine = 64 @@ -196,7 +198,10 @@ def register_struct(pool: memhub.MemBuddy | t.CPtr, return existing if _struct_count >= STRUCT_MAX: - stdio.printf("[STRUCT] table full, cannot register %s\n", name) + fb_st: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_st is not None: + viperlib.snprintf(fb_st, 1024, "table full, cannot register %s", name) + VLogger.error(fb_st, "STRUCT") return None entry: StructEntry | t.CPtr = _get_struct_entry(_struct_count) @@ -290,7 +295,10 @@ def add_field(pool: memhub.MemBuddy | t.CPtr, return -1 if struct_entry.FieldCount >= FIELD_MAX: - stdio.printf("[STRUCT] field table full for %s\n", struct_entry.Name) + fb_sf: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_sf is not None: + viperlib.snprintf(fb_sf, 1024, "field table full for %s", struct_entry.Name) + VLogger.error(fb_sf, "STRUCT") return -1 idx: int = struct_entry.FieldCount @@ -314,18 +322,41 @@ def add_field(pool: memhub.MemBuddy | t.CPtr, return idx +# ============================================================ +# get_struct_count — 获取已注册结构体数量(供 Phase2 多遍预注册判断收敛) +# ============================================================ +def get_struct_count() -> int: + """返回当前已注册的结构体数量""" + return _struct_count + + # ============================================================ # find_struct — 按类名查找结构体 # ============================================================ def find_struct(name: str) -> StructEntry | t.CPtr: - """按类名查找结构体,返回 StructEntry 或 None""" + """按类名查找结构体,返回 StructEntry 或 None + + 支持模块限定名(如 "HT.Translator"): + 第一遍精确匹配,第二遍按类名(最后一个 '.' 之后的部分)回退 + """ if name is None or _struct_table is None: return None + # 第一遍:精确匹配 for i in range(_struct_count): entry: StructEntry | t.CPtr = _get_struct_entry(i) if entry is not None and entry.Name is not None: if string.strcmp(entry.Name, name) == 0: return entry + # 第二遍:类名回退(处理模块限定名 "HT.Translator" → "Translator") + dot_pos: str = string.strrchr(name, '.') + if dot_pos is not None: + class_name: str = dot_pos + 1 + if class_name is not None and class_name[0] != '\0': + for i in range(_struct_count): + entry: StructEntry | t.CPtr = _get_struct_entry(i) + if entry is not None and entry.Name is not None: + if string.strcmp(entry.Name, class_name) == 0: + return entry return None @@ -455,12 +486,27 @@ def lookup_field_by_class(class_name: str, return None # 优先用 SHA1 匹配(跨模块同名类区分) + # 支持模块限定名:class_name 可能是 "HT.Translator",entry.Name 是 "Translator" + # 先精确匹配,失败后用 strrchr 提取类名部分回退匹配 if sha1 is not None: sha1_matched: int = 0 + # 计算类名回退部分("HT.Translator" → "Translator") + bare_name: str = class_name + dot_pos_lfbc: str = string.strrchr(class_name, '.') + if dot_pos_lfbc is not None: + bare_name = dot_pos_lfbc + 1 + if bare_name is None or bare_name[0] == '\0': + bare_name = class_name for i in range(_struct_count): entry: StructEntry | t.CPtr = _get_struct_entry(i) if entry is not None and entry.Name is not None: + # 精确匹配或类名回退匹配 + name_match: int = 0 if string.strcmp(entry.Name, class_name) == 0: + name_match = 1 + elif bare_name is not class_name and string.strcmp(entry.Name, bare_name) == 0: + name_match = 1 + if name_match != 0: if entry.ModuleSha1 is not None: if string.strcmp(entry.ModuleSha1, sha1) == 0: sha1_matched = 1 @@ -475,12 +521,65 @@ def lookup_field_by_class(class_name: str, # 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个 entry = find_struct(class_name) if entry is None: + # 诊断:遍历打印所有已注册结构体名,确认目标类是否在注册表中 + stdio.printf("[LFBC-DIAG] FAIL class=%s field=%s sha1=%s count=%d\n", + class_name, field_name, + sha1 if sha1 is not None else "(null)", _struct_count) + for diag_i in range(_struct_count): + diag_entry: StructEntry | t.CPtr = _get_struct_entry(diag_i) + if diag_entry is not None and diag_entry.Name is not None: + stdio.printf("[LFBC-DIAG] [%d] %s sha1=%s fc=%d\n", + diag_i, diag_entry.Name, + diag_entry.ModuleSha1 if diag_entry.ModuleSha1 is not None else "(null)", + diag_entry.FieldCount) return None for fi in range(entry.FieldCount): fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi) if fe is not None and fe.Name is not None: if string.strcmp(fe.Name, field_name) == 0: return fe + # 诊断:找到结构体但字段未找到 + stdio.printf("[LFBC-DIAG] NOFIELD class=%s field=%s sname=%s fc=%d\n", + class_name, field_name, entry.Name, entry.FieldCount) + return None + + +# ============================================================ +# find_subclass_with_field — 在基类的所有子类中搜索包含指定字段的结构体 +# +# 用于处理 "注解类型是基类但实际值是派生类" 的场景: +# node: AST | t.CPtr = If(...) +# node.orelse = orelse ← orelse 在 If 上,不在 AST 上 +# +# 遍历所有已注册结构体,通过 ParentName 链检查继承关系, +# 返回第一个包含 field_name 的子类 StructEntry。 +# ============================================================ +def find_subclass_with_field(base_class_name: str, + field_name: str) -> StructEntry | t.CPtr: + """在 base_class_name 的所有子类中搜索包含 field_name 的结构体 + + 返回第一个找到的 StructEntry(包含正确的 Ty),None=未找到 + """ + if base_class_name is None or field_name is None: + return None + sc_i: int + for sc_i in range(_struct_count): + sc_entry: StructEntry | t.CPtr = _get_struct_entry(sc_i) + if sc_entry is None or sc_entry.Name is None: + continue + # 检查 sc_entry 是否是 base_class_name 的子类(传递性) + cur_parent: str = get_parent_name(sc_entry.Name) + while cur_parent is not None: + if string.strcmp(cur_parent, base_class_name) == 0: + # 是子类,检查是否有该字段 + sc_fi: int + for sc_fi in range(sc_entry.FieldCount): + sc_fe: FieldEntry | t.CPtr = _get_field_entry(sc_entry, sc_fi) + if sc_fe is not None and sc_fe.Name is not None: + if string.strcmp(sc_fe.Name, field_name) == 0: + return sc_entry + break + cur_parent = get_parent_name(cur_parent) return None @@ -546,6 +645,28 @@ def _is_struct_type(ty: llvmlite.LLVMType | t.CPtr) -> int: return 0 +def _is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int: + """检查 ty 是否是 Ptr 类型(独立函数,规避嵌套 match 编译器 BUG)""" + if ty is None: + return 0 + match ty: + case llvmlite.LLVMType.Ptr(pointee): + return 1 + case _: + return 0 + + +def _get_ptr_pointee(ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr: + """获取 Ptr 类型的 pointee(独立函数,规避嵌套 match 编译器 BUG)""" + if ty is None: + return None + match ty: + case llvmlite.LLVMType.Ptr(pointee): + return pointee + case _: + return None + + def get_struct_type_from_value(val: llvmlite.Value | t.CPtr) -> llvmlite.LLVMType | t.CPtr: """从 Value 的类型推断结构体类型""" if val is None or val.Ty is None: @@ -935,6 +1056,119 @@ def get_field_count(name: str) -> int: return entry.FieldCount +# ============================================================ +# ensure_struct_def_in_module — 确保跨模块结构体的完整定义在当前模块中可用 +# +# 当局部变量声明为值类型(如 f: File)时,alloca 需要完整类型定义。 +# 如果结构体定义在另一个模块(名称含 SHA1 前缀,如 "0035c95a18d4f8e8.File"), +# 将其完整定义复制到当前模块。module_add_named_type 会用完整定义替换 +# 已存在的 opaque 声明,使 alloca 能正确分配空间。 +# +# Args: +# pool: 内存池 +# mod: 当前 LLVM 模块 +# ty: 变量类型(Struct/Ptr/其他) +# ============================================================ +def ensure_struct_def_in_module(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + ty: llvmlite.LLVMType | t.CPtr): + """确保跨模块结构体的完整定义在当前模块中可用(供 alloca 使用) + + 处理两种情况: + 1. Struct 值类型:需要完整定义(供 alloca 分配空间) + 2. Ptr(Struct) 指针类型:需要 pointee 至少有 opaque 声明 + """ + if ty is None or mod is None or pool is None: + return + # 指针类型:确保 pointee 至少有 opaque 声明 + # 用 _is_ptr_type 检查(独立函数,规避嵌套 match BUG) + if _is_ptr_type(ty) != 0: + llvmlite.module_ensure_opaque_for_type(mod, pool, ty) + # 如果 pointee 是 Struct,递归复制完整定义 + # (GEP 访问字段需要 sized 类型,仅 opaque 会导致 llc 报错) + pointee: llvmlite.LLVMType | t.CPtr = _get_ptr_pointee(ty) + if pointee is not None: + ensure_struct_def_in_module(pool, mod, pointee) + return + # 获取结构体名称(非 Struct 类型返回 None) + sname: t.CChar | t.CPtr = llvmlite.get_struct_name(ty) + if sname is None: + return + # 检查是否为跨模块引用(名称包含 '.') + if string.strchr(sname, 46) is None: + return # 非跨模块引用,无需处理 + # 提取类名(第一个 '.' 之后的部分) + # 必须用 strchr 而非 strrchr:泛型特化名如 "list[ast.AST|t.CPtr]" + # 内部含 '.'(ast.AST),strrchr 会错误地把类名解析为 "AST|t.CPtr]" + dot_pos: str = string.strchr(sname, '.') + if dot_pos is None: + return + class_name: str = dot_pos + 1 + if class_name is None or class_name[0] == '\0': + return + # 提取 SHA1(sname 到 dot_pos 之间,用 strlen 避免指针减法) + total_len: t.CSizeT = string.strlen(sname) + class_name_len: t.CSizeT = string.strlen(class_name) + sha1_len: t.CSizeT = total_len - class_name_len - 1 + sha1_buf: t.CChar | t.CPtr = pool.alloc(32) + if sha1_buf is None: + return + string.strncpy(sha1_buf, sname, sha1_len) + sha1_buf[sha1_len] = '\0' + # 按类名 + SHA1 查找结构体定义 + entry: StructEntry | t.CPtr = find_struct_by_module(class_name, sha1_buf) + if entry is None: + # 回退到按类名查找(可能找到不同模块的同名类,但总比没有好) + entry = find_struct(class_name) + if entry is None: + return + if entry.Ty is None: + return + # 将完整定义添加到当前模块(会替换已存在的 opaque 声明) + llvmlite.module_add_named_type(mod, pool, entry.Ty) + # 递归扫描字段类型,确保值类型子结构体(如 WIN32_FIND_DATAA 中的 FILETIME) + # 也有完整定义。指针类型字段只需 opaque 声明(已由 module_ensure_opaque_for_type 处理)。 + _ensure_field_types_in_module(pool, mod, entry.Ty) + + +# ============================================================ +# _ensure_field_types_in_module - 递归扫描结构体字段类型 +# +# 遍历 Struct 的 Fields 链表,对每个值类型子结构体调用 +# ensure_struct_def_in_module。指针/数组/基本类型递归到 pointee/element。 +# ============================================================ +def _ensure_field_types_in_module(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + ty: llvmlite.LLVMType | t.CPtr): + """递归扫描类型树,确保值类型子结构体有完整定义""" + if ty is None or mod is None or pool is None: + return + # 用 match 遍历类型(独立函数,规避嵌套 match BUG) + match ty: + case llvmlite.LLVMType.Struct(fields, fcount, sname): + # 遍历字段链表 + if fields is not None: + fld: llvmlite.ParamNode | t.CPtr = fields + while fld is not None: + if fld.Ty is not None: + # 值类型子结构体(如 FILETIME)需要完整定义 + if _is_struct_type(fld.Ty) != 0: + ensure_struct_def_in_module(pool, mod, fld.Ty) + _ensure_field_types_in_module(pool, mod, fld.Ty) + fld = fld.Next + case llvmlite.LLVMType.Ptr(pointee): + # 指针类型:pointee 只需 opaque 声明,不需要完整定义 + # 但如果 pointee 是 Struct,确保 opaque 声明存在 + if pointee is not None: + llvmlite.module_ensure_opaque_for_type(mod, pool, ty) + case llvmlite.LLVMType.Array(elem_ty, acount): + # 数组类型:元素如果是值类型结构体,需要完整定义 + if elem_ty is not None: + _ensure_field_types_in_module(pool, mod, elem_ty) + case _: + pass + + # ============================================================ # get_field_name_ptr — 获取 FieldEntry 的字段名指针(访问器) # ============================================================ diff --git a/App/lib/core/Handles/HandlesTranslator.py b/App/lib/core/Handles/HandlesTranslator.py index 87570c7..d8c67ed 100644 --- a/App/lib/core/Handles/HandlesTranslator.py +++ b/App/lib/core/Handles/HandlesTranslator.py @@ -6,6 +6,7 @@ import memhub import string import stdio import stdlib +import lib.core.VLogger as VLogger import lib.core.Handles.HandlesVar as HandlesVar import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.core.Handles.HandlesMain as HandlesMain @@ -212,7 +213,7 @@ class Translator: # 创建 LLVM 模块 mod: llvmlite.LLVMModule | t.CPtr = llvmlite.new_module(pool, "main") if mod is None: - stdio.printf("[TR] NewModule returned NULL\n") + VLogger.error("NewModule returned NULL", "TR") return 1 self.Module = mod @@ -231,7 +232,7 @@ class Translator: printf_func: llvmlite.Function | t.CPtr = llvmlite.create_declare( pool, mod, "printf", i32_ty) if printf_func is None: - stdio.printf("[TR] CreateDeclare printf returned NULL\n") + VLogger.error("CreateDeclare printf returned NULL", "TR") return 1 llvmlite.add_param(pool, printf_func, i8_ptr_ty, "fmt") printf_func.IsVarArg = 1 @@ -305,6 +306,21 @@ class Translator: elif kd == ast.ASTKind.ImportFrom: self.ImportsH.HandleImportFromModule(child) self.ImportsH.HandleImportFromNames(child) + # 预注册模块级变量(AnnAssign/Assign)到 SymTab + # 解决时序问题:class Logger 的 __init__ 引用 _mbuddy 时, + # _mbuddy 的 AnnAssign 在 AST 中位于 class Logger 之后, + # 按 AST 顺序翻译时 _mbuddy 尚未注册 → lookup_var 返回 None → rhs_val is None + # 预处理先于 translate_children 注册所有模块级变量, + # 后续 translate_children 遍历到同一 AnnAssign 时 handle_module_level_var + # 通过 lookup_module_var 检测已存在并跳过(HandlesMain.py:206-209) + if ch is not None: + for ci in range(cn_count): + child: ast.AST | t.CPtr = ch.get(ci) + if child is None: + continue + kd_mv: int = child.kind() + if kd_mv == ast.ASTKind.AnnAssign or kd_mv == ast.ASTKind.Assign: + HandlesMain.handle_module_level_var(self, child) # 创建前向声明 HandlesFunctions.forward_declare_functions(self, tree) added: int = HandlesMain.translate_children(self, tree) diff --git a/App/lib/core/Handles/HandlesType.py b/App/lib/core/Handles/HandlesType.py index fd8c9a2..26a21f7 100644 --- a/App/lib/core/Handles/HandlesType.py +++ b/App/lib/core/Handles/HandlesType.py @@ -26,6 +26,27 @@ import lib.core.Handles.HandlesStruct as HandlesStruct _g_current_file: str = None +# ============================================================ +# 全局 temp_dir(pyi 文件所在目录,由 BuildPipeline/Phase1 设置) +# +# 用于跨模块 CDefine 常量查找:当当前模块的 CDefine 表中找不到 +# 某个 Name 时,从 from_imports 解析出源模块,再从该模块的 pyi +# 文件中解析 CDefine 常量值。 +# ============================================================ +_g_temp_dir: str = None + + +def set_temp_dir(temp_dir: str) -> None: + """设置全局 temp_dir(每个项目编译开始时由 BuildPipeline/Phase1 设置)""" + global _g_temp_dir + _g_temp_dir = temp_dir + + +def get_temp_dir() -> str: + """获取全局 temp_dir""" + return _g_temp_dir + + # ============================================================ # 全局泛型特化上下文(由 _specialize_generic_class 设置/清除) # @@ -46,11 +67,55 @@ _g_generic_type_args: list[str] | t.CPtr = None _g_cdefine_names: list[str] | t.CPtr = None _g_cdefine_values: list[str] | t.CPtr = None +# ============================================================ +# 全局跨模块 CDefine 表(按模块 SHA1 索引,跨模块持久化) +# +# 本地表在模块切换时被 clear_cdefine_constants 清空, +# 导致跨模块引用(如 HandlesVar.SCOPE_FUNCTION)找不到常量。 +# 全局表在模块切换时不清空,跨模块查找直接查表,无需文件 I/O。 +# +# 数据布局: +# _g_gcdef_sha1s: 每条 17 字节(模块 SHA1 16字符 + null) +# _g_gcdef_names: 每条 64 字节(常量名 + null) +# _g_gcdef_values: 每条 32 字节(值字符串 + null) +# ============================================================ +MAX_GLOBAL_CDEFINE: t.CDefine = 1024 +_g_gcdef_sha1s: bytes = None +_g_gcdef_names: bytes = None +_g_gcdef_values: bytes = None +_g_gcdef_count: int = 0 +_g_current_module_sha1: str = None + + +def set_current_module_sha1(sha1: str) -> None: + """设置当前正在编译的模块 SHA1(Phase1 在模块开始翻译前调用)""" + global _g_current_module_sha1 + _g_current_module_sha1 = sha1 + + +def _global_cdefine_init(pool: memhub.MemBuddy | t.CPtr) -> int: + """懒初始化全局 CDefine 表""" + global _g_gcdef_sha1s, _g_gcdef_names, _g_gcdef_values + if _g_gcdef_sha1s is None: + _g_gcdef_sha1s = stdlib.malloc(MAX_GLOBAL_CDEFINE * 17) + _g_gcdef_names = stdlib.malloc(MAX_GLOBAL_CDEFINE * 64) + _g_gcdef_values = stdlib.malloc(MAX_GLOBAL_CDEFINE * 32) + if _g_gcdef_sha1s is None or _g_gcdef_names is None or _g_gcdef_values is None: + return 1 + string.memset(_g_gcdef_sha1s, 0, MAX_GLOBAL_CDEFINE * 17) + string.memset(_g_gcdef_names, 0, MAX_GLOBAL_CDEFINE * 64) + string.memset(_g_gcdef_values, 0, MAX_GLOBAL_CDEFINE * 32) + return 0 + def register_cdefine_constant(pool: memhub.MemBuddy | t.CPtr, name: str, value: int) -> None: - """注册 CDefine 编译期常量到全局表""" - global _g_cdefine_names, _g_cdefine_values + """注册 CDefine 编译期常量到本地表和全局表 + + 值以十进制字符串存储。注意:value 是 32 位有符号整数, + 0xFFFFFFFF 会存储为 "-1",lookup 时用 found 标志区分"未找到"和值为 -1。 + """ + global _g_cdefine_names, _g_cdefine_values, _g_gcdef_count if _g_cdefine_names is None: _g_cdefine_names = list[str](pool, 64) _g_cdefine_values = list[str](pool, 64) @@ -61,12 +126,54 @@ def register_cdefine_constant(pool: memhub.MemBuddy | t.CPtr, _g_cdefine_values.append(val_buf) else: _g_cdefine_values.append("0") + # 同时注册到全局跨模块表(用当前模块 SHA1 索引) + if _g_current_module_sha1 is not None and _g_gcdef_count < MAX_GLOBAL_CDEFINE: + if _global_cdefine_init(pool) == 0: + sidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 17 + nidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 64 + vidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 32 + string.strcpy(_g_gcdef_sha1s + sidx, _g_current_module_sha1) + string.strcpy(_g_gcdef_names + nidx, name) + vbuf: str = _g_gcdef_values + vidx + viperlib.snprintf(vbuf, 32, "%d", value) + _g_gcdef_count += 1 + + +def lookup_global_cdefine(module_sha1: str, name: str) -> int: + """从全局跨模块表查找 CDefine 常量 + + 用 module_sha1 + name 精确匹配。 + 找到时设置 _g_cdefine_found=1 并返回值;未找到返回 0。 + """ + global _g_cdefine_found + if module_sha1 is None or name is None: + return 0 + if _g_gcdef_sha1s is None or _g_gcdef_count <= 0: + return 0 + i: t.CSizeT = 0 + while i < _g_gcdef_count: + sidx: t.CSizeT = t.CSizeT(i) * 17 + nidx: t.CSizeT = t.CSizeT(i) * 64 + vidx: t.CSizeT = t.CSizeT(i) * 32 + if string.strcmp(_g_gcdef_sha1s + sidx, module_sha1) == 0: + if string.strcmp(_g_gcdef_names + nidx, name) == 0: + _g_cdefine_found = 1 + return string.atoi(_g_gcdef_values + vidx) + i += 1 + return 0 def lookup_cdefine_constant(name: str) -> int: - """查找 CDefine 编译期常量,返回值或 -1(未找到)""" + """查找 CDefine 编译期常量 + + 返回找到的值。由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1), + 用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。 + 调用方应在调用后检查 _g_cdefine_found。 + """ + global _g_cdefine_found + _g_cdefine_found = 0 if _g_cdefine_names is None: - return -1 + return 0 n: t.CSizeT = _g_cdefine_names.__len__() i: t.CSizeT for i in range(n): @@ -74,8 +181,23 @@ def lookup_cdefine_constant(name: str) -> int: if nm is not None and string.strcmp(nm, name) == 0: val_str: str = _g_cdefine_values.get(i) if val_str is not None: + _g_cdefine_found = 1 return string.atoi(val_str) - return -1 + return 0 + + +_g_cdefine_found: int = 0 + + +def is_cdefine_found() -> int: + """检查上次 lookup_cdefine_constant 是否找到值""" + return _g_cdefine_found + + +def set_cdefine_found(val: int) -> None: + """设置 cdefine_found 标志(供跨模块查找函数使用)""" + global _g_cdefine_found + _g_cdefine_found = val def clear_cdefine_constants() -> None: @@ -228,6 +350,20 @@ def _parse_type_string(pool: memhub.MemBuddy | t.CPtr, return ast.Name(pool, type_str, ctx) +# ============================================================ +# _parse_type_string_ptr - _parse_type_string 的 t.CPtr 返回类型包装 +# +# 旧编译器对 ast.AST | t.CPtr 跨模块返回类型推断有 bug(解析为 i32), +# 用 t.CPtr 包装绕过,外部模块应调用此函数而非 _parse_type_string +# ============================================================ +def _parse_type_string_ptr(pool: memhub.MemBuddy | t.CPtr, + type_str: str, + ctx: t.CInt) -> t.CPtr: + """_parse_type_string 的 t.CPtr 返回类型包装(绕过旧编译器类型推断 bug)""" + result: ast.AST | t.CPtr = _parse_type_string(pool, type_str, ctx) + return result + + # ============================================================ # set_current_file - 设置当前编译的文件名 # @@ -709,7 +845,8 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr, def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, node: ast.AST | t.CPtr, imported_modules: str, - from_imports: str) -> llvmlite.LLVMType | t.CPtr: + from_imports: str, + trans: HT.Translator | t.CPtr = None) -> llvmlite.LLVMType | t.CPtr: """解析类型注解节点为 LLVMType Args: @@ -717,6 +854,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, node: 类型注解 AST 节点(Name 或 Attribute) imported_modules: 已导入模块名字符串(空格分隔) from_imports: from-import 映射字符串("name:module name:module" 格式) + trans: 翻译器对象(可选,用于触发泛型特化) """ if node is None: return None @@ -727,9 +865,9 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, if k == ast.ASTKind.Constant: cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) if cn.const_kind == ast.CONST_STR and cn.str_val is not None: - sub_node: ast.AST | t.CPtr = _parse_type_string(pool, cn.str_val, ast.Ctx.Load) + sub_node: ast.AST | t.CPtr = _parse_type_string(pool, cn.str_val, ast.ASTCtx.Load) if sub_node is not None: - return resolve_annotation_type(pool, sub_node, imported_modules, from_imports) + return resolve_annotation_type(pool, sub_node, imported_modules, from_imports, trans) return None # Name 节点: str, int, bytes, from-import 名称, class 名称等 @@ -749,7 +887,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, # 用具体类型名递归解析(支持 BinOp/Attribute 类型实参) gp_sub_node: ast.AST | t.CPtr = _parse_type_string(pool, gp_ta, nm.ctx) if gp_sub_node is not None: - return resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports) + return resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports, trans) # str = bytes = t.CChar | t.CPtr = i8* if string.strcmp(nm.id, "str") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) @@ -817,7 +955,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, if other_is_cvoid != 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) other_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type( - pool, other_node, imported_modules, from_imports) + pool, other_node, imported_modules, from_imports, trans) if other_ty is None: # CPtr 单独使用或其他无效类型 → i8* return llvmlite.Ptr(pool, llvmlite.Int8(pool)) @@ -825,11 +963,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, # 无 CPtr 标记:返回第一个非 None(装饰器标记如 CExport/CExtern 被忽略) left_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type( - pool, bop.left, imported_modules, from_imports) + pool, bop.left, imported_modules, from_imports, trans) if left_ty is not None: return left_ty right_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type( - pool, bop.right, imported_modules, from_imports) + pool, bop.right, imported_modules, from_imports, trans) if right_ty is not None: return right_ty return None @@ -891,6 +1029,22 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, gen_spec_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(gen_mangled) if gen_spec_ty is not None: return gen_spec_ty + # 未特化:尝试主动触发泛型特化(与 TPC _HandleSubscript 对齐) + # 当 trans 可用时,检查是否是泛型模板并触发特化 + if trans is not None: + import lib.core.Handles.HandlesClassDef as HandlesClassDef + import lib.core.Handles.HandlesExprCall as HandlesExprCall + template_cd_chk: ast.ClassDef | t.CPtr = HandlesClassDef._find_generic_template(gen_nm.id) + if template_cd_chk is not None: + type_args_chk: list[str] | t.CPtr = HandlesExprCall._extract_type_args_from_slice( + pool, sub.slice) + if type_args_chk is not None and type_args_chk.__len__() > 0: + spec_name_chk: str = HandlesClassDef._specialize_generic_class( + trans, gen_nm.id, type_args_chk) + if spec_name_chk is not None: + gen_spec_ty = HandlesStruct.get_struct_type(gen_mangled) + if gen_spec_ty is not None: + return gen_spec_ty # 未特化:返回 None(不报错,让后续流程处理) return None # 检查 value 是否是 Attribute(Name('t'), 'CArray') @@ -912,7 +1066,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, # 相当于 t.CArray[elem_ty, None],表示静态推导长度数组 if sub.slice.kind() != ast.ASTKind.Tuple: elem_ty_single: llvmlite.LLVMType | t.CPtr = resolve_annotation_type( - pool, sub.slice, imported_modules, from_imports) + pool, sub.slice, imported_modules, from_imports, trans) if elem_ty_single is None: fatal_type_error(sub.slice, "t.CArray[?] 元素类型解析失败") return llvmlite.Ptr(pool, elem_ty_single) @@ -930,7 +1084,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, fatal_type_error(node, "t.CArray Tuple 元素为 None") # 递归解析元素类型 elem_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type( - pool, elem_node, imported_modules, from_imports) + pool, elem_node, imported_modules, from_imports, trans) if elem_ty is None: fatal_type_error(elem_node, "t.CArray 元素类型解析失败") # 解析 count(必须是整数常量、None 或 CDefine 常量名) @@ -950,7 +1104,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, if cnt_nm.id is None: fatal_type_error(count_node, "t.CArray count Name 节点 id 为 None") looked_up: int = lookup_cdefine_constant(cnt_nm.id) - if looked_up < 0: + if is_cdefine_found() == 0: fatal_type_error(count_node, "t.CArray count 不是已注册的 CDefine 常量") count_val = looked_up else: @@ -1180,6 +1334,11 @@ def _strappend(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr) remain: t.CSizeT = dst_size - dlen if remain <= 0: return - for i in range(min(slen, remain - 1)): + # 内联 min(slen, remain - 1) 避免 builtin min 未定义(TransPyV 无 builtin min) + limit: t.CSizeT = slen + cap: t.CSizeT = remain - 1 + if cap < limit: + limit = cap + for i in range(limit): dst[dlen + i] = src[i] dst[dlen + i] = '\0' \ No newline at end of file diff --git a/App/lib/core/Handles/HandlesVar.py b/App/lib/core/Handles/HandlesVar.py index 455f5fe..719b587 100644 --- a/App/lib/core/Handles/HandlesVar.py +++ b/App/lib/core/Handles/HandlesVar.py @@ -36,6 +36,7 @@ class VarEntry: Alloca: llvmlite.Value | t.CPtr Used: t.CInt AnnotClassName: t.CChar | t.CPtr # 原始类型注解的类名(str 别名在结构体字段中触发编译器 bug,改用显式联合类型) + IsPtrElement: t.CInt # 标志: 1=注解为 bytes|t.CPtr 或 str|t.CPtr, 下标按 8 字节步长 # ============================================================ @@ -154,6 +155,25 @@ def set_var_annot_class_name(symtab: SymbolTable | t.CPtr, return 0 +# ============================================================ +# set_var_ptr_element — 标记变量为"指针到 str/bytes"类型 +# +# 注解为 bytes|t.CPtr 或 str|t.CPtr 的变量,其 alloca 类型是 i8*, +# 但下标访问应按 8 字节步长(i8** 语义),而非 1 字节步长(i8* 语义)。 +# 此标志在 HandlesAnnAssign 中根据注解形式设置。 +# ============================================================ +def set_var_ptr_element(symtab: SymbolTable | t.CPtr, + name: str) -> int: + """标记变量为 ptr_element 类型,返回 0=成功 / 1=失败""" + if symtab is None or name is None: + return 1 + entry: VarEntry | t.CPtr = lookup_var_entry(symtab, name) + if entry is None: + return 1 + entry.IsPtrElement = 1 + return 0 + + # ============================================================ # add_var — 添加变量到变量表 # ============================================================ @@ -175,6 +195,27 @@ def add_var(vars_ptr: VarEntry | t.CPtr, return 0 +# ============================================================ +# _alloca_at_entry - 在函数入口块生成 alloca(确保支配性) +# +# LLVM IR 要求指令支配所有使用点。如果 alloca 在条件分支内生成, +# 但在其他分支使用,会违反支配性(Instruction does not dominate all uses)。 +# 标准做法:所有 alloca 在函数入口块生成。 +# +# 临时切换 builder.CurBlock 到入口块,生成 alloca,然后恢复。 +# ============================================================ +def _alloca_at_entry(builder: llvmlite.IRBuilder | t.CPtr, + ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr: + """在函数入口块生成 alloca,返回 alloca 值 + + 使用 llvmlite.build_alloca_at_entry 在入口块终止指令之前插入 alloca, + 确保所有 alloca 在入口块,避免支配性违规。 + """ + if builder is None or ty is None: + return None + return llvmlite.build_alloca_at_entry(builder, ty) + + # ============================================================ # get_or_create_var — 查找或创建变量 alloca(旧版兼容) # ============================================================ @@ -188,7 +229,7 @@ def get_or_create_var(pool: memhub.MemBuddy | t.CPtr, existing: llvmlite.Value | t.CPtr = find_var(vars_ptr, var_count, name) if existing is not None: return existing - alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, ty) + alloca: llvmlite.Value | t.CPtr = _alloca_at_entry(builder, ty) if alloca is None: return None if add_var(vars_ptr, var_count, name, alloca) != 0: @@ -358,7 +399,7 @@ def get_or_create_sym(symtab: SymbolTable | t.CPtr, existing: llvmlite.Value | t.CPtr = lookup_current(symtab, name) if existing is not None: return existing - alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, ty) + alloca: llvmlite.Value | t.CPtr = _alloca_at_entry(builder, ty) if alloca is None: return None define_var(symtab, name, alloca) diff --git a/App/lib/core/IncludesScanner.py b/App/lib/core/IncludesScanner.py index fa8f4c4..a33a09d 100644 --- a/App/lib/core/IncludesScanner.py +++ b/App/lib/core/IncludesScanner.py @@ -9,6 +9,7 @@ import w32.win32base as win32base import w32.fileio as fileio import hashlib import viperlib +import lib.core.VLogger as VLogger # ============================================================ @@ -312,7 +313,10 @@ 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) - stdio.printf(" [scan] %s -> %s\n", rel_path, sha1) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "扫描: %s -> %s", rel_path, sha1) + VLogger.debug(fb, "scan") # 继续搜索下一个文件 if win32file.FindNextFileA(handle, find_data) == 0: diff --git a/App/lib/core/Phase1.py b/App/lib/core/Phase1.py index e226eca..8dab910 100644 --- a/App/lib/core/Phase1.py +++ b/App/lib/core/Phase1.py @@ -16,6 +16,7 @@ import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.core.Handles.HandlesImports as HandlesImports +import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.IncludesScanner as IncludesScanner import lib.core.StubMerger as StubMerger import lib.core.BuildPipeline as BuildPipeline @@ -31,6 +32,431 @@ SRC_BUF_SIZE: t.CDefine = 1048576 # pyi 缓冲区大小(256KB) PYI_BUF_SIZE: t.CSizeT = 262144 +# 拓扑排序相关常量 +MAX_CLASSES: t.CDefine = 512 +MAX_FILE_DEPS: t.CDefine = 256 +MAX_DEP_EDGES: t.CDefine = 16 +SHA1_LEN: t.CDefine = 17 +CLASS_NAME_LEN: t.CDefine = 64 + +# 拓扑排序使用扁平字节数组存储类信息和文件依赖, +# 避免结构体中的 str 指针字段在 mb.alloc 零填充时为 NULL 导致崩溃。 +# 类名/父类名存储在 class_names_buf/parent_names_buf(每个 CLASS_NAME_LEN 字节), +# 定义 SHA1 存储在 def_sha1s_buf(每个 SHA1_LEN 字节)。 + + +# ============================================================ +# _ScanClassInheritance - 预扫描 includes 文件,收集类继承关系 +# +# 遍历所有可达 includes 文件,解析 AST 收集 ClassDef 的类名和父类名。 +# 结果存入扁平字节数组,返回收集到的类数量。 +# +# Args: +# mb: 内存池 +# result: IncludesScanner 扫描结果 +# reachable_set: 可达 SHA1 集合 +# reachable_count: 可达 SHA1 数量 +# use_reachable: 是否使用可达集合过滤 +# sha1_set: SHA1 集合(use_reachable=0 时使用) +# set_count: SHA1 集合数量 +# class_names_buf: 类名扁平数组(每个 CLASS_NAME_LEN 字节,连续内存) +# parent_names_buf: 父类名扁平数组(每个 CLASS_NAME_LEN 字节,无父类填 '\0') +# def_sha1s_buf: 定义 SHA1 扁平数组(每个 SHA1_LEN 字节) +# +# Returns: +# 收集到的类数量,-1 失败 +# ============================================================ +def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr, + result: IncludesScanner.ScanResult | t.CPtr, + reachable_set: str, reachable_count: int, + use_reachable: int, + sha1_set: str, set_count: int, + class_names_buf: bytes, + parent_names_buf: bytes, + def_sha1s_buf: bytes) -> int: + """预扫描 includes 文件,收集类继承关系""" + if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: + return -1 + + entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + class_count: int = 0 + + for i in range(result.Count): + entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size + entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) + if entry is None or entry.Sha1 is None: + continue + + sha1_e: str = entry.Sha1 + + # 按需翻译过滤 + in_set: int = 0 + if use_reachable != 0: + in_set = StubMerger._is_in_sha1_set(sha1_e, reachable_set, reachable_count) + else: + in_set = StubMerger._is_in_sha1_set(sha1_e, sha1_set, set_count) + if in_set == 0: + continue + + # 读取文件 + file_path: str = entry.Path + f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R) + if f.closed: + continue + + src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE) + if src_buf is None: + f.close() + continue + + bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE) + f.close() + if bytes_read <= 0: + stdlib.free(src_buf) + continue + if bytes_read < SRC_BUF_SIZE: + src_buf[bytes_read] = 0 + else: + src_buf[SRC_BUF_SIZE - 1] = 0 + + # 解析 AST + lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) + if lx is None: + stdlib.free(src_buf) + continue + ast._lexer_init(lx, src_buf, mb) + tokens: ast.Token | t.CPtr = ast.tokenize(lx) + tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) + if tree is None: + stdlib.free(src_buf) + continue + + # 遍历 AST 顶层节点,收集 ClassDef + ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children + if ch is not None: + cn: t.CSizeT = ch.__len__() + for ci in range(cn): + child: ast.AST | t.CPtr = ch.get(ci) + if child is None: + continue + kd: int = child.kind() + if kd != ast.ASTKind.ClassDef: + continue + + cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child) + if cd is None or cd.name is None: + continue + + if class_count >= MAX_CLASSES: + break + + # 填充扁平数组:类名(偏移 = class_count * CLASS_NAME_LEN) + name_dst: str = class_names_buf + class_count * CLASS_NAME_LEN + nm_len: t.CSizeT = string.strlen(cd.name) + if nm_len >= CLASS_NAME_LEN: + string.strncpy(name_dst, cd.name, CLASS_NAME_LEN - 1) + name_dst[CLASS_NAME_LEN - 1] = '\0' + else: + string.strcpy(name_dst, cd.name) + + # 填充扁平数组:父类名(无父类填 '\0') + parent_dst: str = parent_names_buf + class_count * CLASS_NAME_LEN + parent_dst[0] = '\0' + parent_nm: str = HandlesClassDef._get_parent_class(cd, None) + # parent_nm 是 AST 节点内部指针,不需要释放 + if parent_nm is not None: + pnm_len: t.CSizeT = string.strlen(parent_nm) + if pnm_len >= CLASS_NAME_LEN: + string.strncpy(parent_dst, parent_nm, CLASS_NAME_LEN - 1) + parent_dst[CLASS_NAME_LEN - 1] = '\0' + else: + string.strcpy(parent_dst, parent_nm) + + # 填充扁平数组:定义 SHA1(偏移 = class_count * SHA1_LEN) + sha1_dst: str = def_sha1s_buf + class_count * SHA1_LEN + string.strcpy(sha1_dst, sha1_e) + + class_count += 1 + + stdlib.free(src_buf) + + return class_count + + +# ============================================================ +# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1 +# +# Args: +# class_names_buf: 类名扁平数组 +# def_sha1s_buf: 定义 SHA1 扁平数组 +# class_count: 类数量 +# class_name: 要查找的类名 +# +# Returns: +# SHA1 字符串指针(指向 def_sha1s_buf 内部偏移),None 未找到 +# ============================================================ +def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, + class_count: int, class_name: str) -> str: + """查找类名对应的定义文件 SHA1""" + if class_names_buf is None or def_sha1s_buf is None or class_name is None: + return None + for i in range(class_count): + name_ptr: str = class_names_buf + i * CLASS_NAME_LEN + if string.strcmp(name_ptr, class_name) == 0: + return def_sha1s_buf + i * SHA1_LEN + return None + + +# ============================================================ +# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序 +# +# 算法(Kahn): +# 1. 从扁平类信息数组构建"文件 SHA1 → 依赖文件 SHA1 集合"映射 +# 2. 计算每个文件的入度(依赖数量) +# 3. 入度为 0 的文件先处理,处理后减少依赖它的文件入度 +# 4. 重复直到所有文件处理完成 +# +# 注意:依赖方向是"父类所在文件 → 子类所在文件", +# 即父类文件必须先处理。Kahn 用 DepCount 作为剩余依赖计数, +# 依赖为 0 的先处理,初始化 InDegree = DepCount。 +# +# Args: +# mb: 内存池 +# result: IncludesScanner 扫描结果 +# reachable_set: 可达 SHA1 集合 +# reachable_count: 可达 SHA1 数量 +# use_reachable: 是否使用可达集合过滤 +# sha1_set: SHA1 集合 +# set_count: SHA1 集合数量 +# class_names_buf: 类名扁平数组 +# parent_names_buf: 父类名扁平数组 +# def_sha1s_buf: 定义 SHA1 扁平数组 +# class_count: 类数量 +# out_order: 输出拓扑顺序的文件索引数组(调用者分配,int 数组) +# out_count: 输出文件数量 +# +# Returns: +# 0 成功,-1 失败 +# ============================================================ +def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr, + result: IncludesScanner.ScanResult | t.CPtr, + reachable_set: str, reachable_count: int, + use_reachable: int, + sha1_set: str, set_count: int, + class_names_buf: bytes, parent_names_buf: bytes, + def_sha1s_buf: bytes, class_count: int, + out_order: t.CPtr, out_count: t.CPtr) -> int: + """根据类继承关系对文件进行拓扑排序""" + if result is None or out_order is None or out_count is None: + return -1 + + entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + + # 1. 分配扁平数组:文件 SHA1、依赖 SHA1 列表、依赖数量、入度、处理状态 + # 用扁平数组代替 FileDepEntry 结构体,避免 str 指针字段 NULL 崩溃 + file_sha1s_buf: bytes = mb.alloc(MAX_FILE_DEPS * SHA1_LEN) + file_deps_buf: bytes = mb.alloc(MAX_FILE_DEPS * MAX_DEP_EDGES * SHA1_LEN) + file_dep_counts: t.CPtr = mb.alloc(MAX_FILE_DEPS * 4) + file_indegrees: t.CPtr = mb.alloc(MAX_FILE_DEPS * 4) + file_processed: t.CPtr = mb.alloc(MAX_FILE_DEPS * 4) + + if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None: + if file_sha1s_buf is not None: + mb.free(file_sha1s_buf) + if file_deps_buf is not None: + mb.free(file_deps_buf) + if file_dep_counts is not None: + mb.free(file_dep_counts) + if file_indegrees is not None: + mb.free(file_indegrees) + if file_processed is not None: + mb.free(file_processed) + return -1 + + string.memset(file_sha1s_buf, 0, MAX_FILE_DEPS * SHA1_LEN) + string.memset(file_deps_buf, 0, MAX_FILE_DEPS * MAX_DEP_EDGES * SHA1_LEN) + string.memset(file_dep_counts, 0, MAX_FILE_DEPS * 4) + string.memset(file_indegrees, 0, MAX_FILE_DEPS * 4) + string.memset(file_processed, 0, MAX_FILE_DEPS * 4) + + file_count: int = 0 + # sha1→file_index 映射(简单线性查找,文件数不多) + for i in range(result.Count): + entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size + entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr)) + if entry is None or entry.Sha1 is None: + continue + + sha1_e: str = entry.Sha1 + in_set: int = 0 + if use_reachable != 0: + in_set = StubMerger._is_in_sha1_set(sha1_e, reachable_set, reachable_count) + else: + in_set = StubMerger._is_in_sha1_set(sha1_e, sha1_set, set_count) + if in_set == 0: + continue + + if file_count >= MAX_FILE_DEPS: + break + + # 初始化扁平数组:文件 SHA1(偏移 = file_count * SHA1_LEN) + # DepCount/InDegree/Processed 已由 memset 清零 + sha1_dst: str = file_sha1s_buf + file_count * SHA1_LEN + string.strcpy(sha1_dst, sha1_e) + + file_count += 1 + + # 2. 构建依赖边:对每个类,找到父类定义所在文件,建立 子文件→父文件 依赖 + for ci in range(class_count): + # 读取父类名(偏移 = ci * CLASS_NAME_LEN) + parent_ptr: str = parent_names_buf + ci * CLASS_NAME_LEN + if parent_ptr[0] == '\0': + continue + + # 读取子类定义 SHA1(偏移 = ci * SHA1_LEN) + child_def_sha1: str = def_sha1s_buf + ci * SHA1_LEN + + # 查找父类定义所在文件的 SHA1 + parent_sha1: str = _FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr) + if parent_sha1 is None: + continue # 父类不在 includes 中(如 Object、CEnum 等标记基类) + + # 父类文件 SHA1 不能等于子类文件 SHA1(同文件内继承不需要依赖) + if string.strcmp(parent_sha1, child_def_sha1) == 0: + continue + + # 找到子类文件对应的索引,添加父文件 SHA1 到依赖列表 + for fi in range(file_count): + fsha1: str = file_sha1s_buf + fi * SHA1_LEN + if string.strcmp(fsha1, child_def_sha1) != 0: + continue + + # 读取当前依赖数量 + cnt_addr: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi * 4 + cnt_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr, t.CPtr)) + if cnt_ptr is None: + break + cur_cnt: int = cnt_ptr[0] + + # 检查依赖是否已添加(去重) + already: int = 0 + for di in range(cur_cnt): + dep_slot: str = file_deps_buf + fi * MAX_DEP_EDGES * SHA1_LEN + di * SHA1_LEN + if string.strcmp(dep_slot, parent_sha1) == 0: + already = 1 + break + if already != 0: + break + + if cur_cnt < MAX_DEP_EDGES: + dep_dst: str = file_deps_buf + fi * MAX_DEP_EDGES * SHA1_LEN + cur_cnt * SHA1_LEN + string.strcpy(dep_dst, parent_sha1) + cnt_ptr[0] = cur_cnt + 1 + break + + # 3. Kahn 算法:InDegree = DepCount(依赖多少个父文件) + # 依赖为 0 的先处理,处理后减少依赖它的文件入度 + for fi in range(file_count): + cnt_addr3: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi * 4 + cnt_ptr3: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr3, t.CPtr)) + ind_addr3: t.CUInt64T = t.CUInt64T(file_indegrees) + fi * 4 + ind_ptr3: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr3, t.CPtr)) + if cnt_ptr3 is not None and ind_ptr3 is not None: + ind_ptr3[0] = cnt_ptr3[0] + + # 4. 拓扑排序输出 + out_idx: int = 0 + changed: int = 1 + while changed != 0: + changed = 0 + for fi in range(file_count): + # 检查处理状态 + proc_addr4: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4 + proc_ptr4: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr4, t.CPtr)) + if proc_ptr4 is None or proc_ptr4[0] != 0: + continue + # 检查入度 + ind_addr4: t.CUInt64T = t.CUInt64T(file_indegrees) + fi * 4 + ind_ptr4: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr4, t.CPtr)) + if ind_ptr4 is None or ind_ptr4[0] > 0: + continue + + # 入度为 0,可以处理 + # 找到对应的 result.Entries 索引 + target_sha1: str = file_sha1s_buf + fi * SHA1_LEN + for ri in range(result.Count): + ea_r: t.CUInt64T = t.CUInt64T(result.Entries) + ri * entry_size + ent_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_r, t.CPtr)) + if ent_r is not None and ent_r.Sha1 is not None: + if string.strcmp(ent_r.Sha1, target_sha1) == 0: + # 写入输出索引数组 + out_idx_addr: t.CUInt64T = t.CUInt64T(out_order) + out_idx * 4 + out_int_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_idx_addr, t.CPtr)) + if out_int_ptr is not None: + out_int_ptr[0] = ri + out_idx += 1 + break + + # 标记已处理 + proc_ptr4[0] = 1 + changed = 1 + + # 减少依赖该文件的其他文件的入度 + for fj in range(file_count): + proc_addr5: t.CUInt64T = t.CUInt64T(file_processed) + fj * 4 + proc_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr5, t.CPtr)) + if proc_ptr5 is None or proc_ptr5[0] != 0: + continue + # 读取 fj 的依赖数量 + cnt_addr5: t.CUInt64T = t.CUInt64T(file_dep_counts) + fj * 4 + cnt_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr5, t.CPtr)) + if cnt_ptr5 is None: + continue + fj_dep_cnt: int = cnt_ptr5[0] + for di in range(fj_dep_cnt): + dep_slot5: str = file_deps_buf + fj * MAX_DEP_EDGES * SHA1_LEN + di * SHA1_LEN + if string.strcmp(dep_slot5, target_sha1) == 0: + ind_addr5: t.CUInt64T = t.CUInt64T(file_indegrees) + fj * 4 + ind_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr5, t.CPtr)) + if ind_ptr5 is not None: + ind_ptr5[0] = ind_ptr5[0] - 1 + break + + # 处理循环依赖(未处理的文件按原顺序追加) + if out_idx < file_count: + for fi in range(file_count): + proc_addr6: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4 + proc_ptr6: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr6, t.CPtr)) + if proc_ptr6 is None or proc_ptr6[0] != 0: + continue + target_sha1_2: str = file_sha1s_buf + fi * SHA1_LEN + for ri in range(result.Count): + ea_r2: t.CUInt64T = t.CUInt64T(result.Entries) + ri * entry_size + ent_r2: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_r2, t.CPtr)) + if ent_r2 is not None and ent_r2.Sha1 is not None: + if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0: + out_idx_addr2: t.CUInt64T = t.CUInt64T(out_order) + out_idx * 4 + out_int_ptr2: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_idx_addr2, t.CPtr)) + if out_int_ptr2 is not None: + out_int_ptr2[0] = ri + out_idx += 1 + break + proc_ptr6[0] = 1 + + # 写入输出数量 + out_count_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_count, t.CPtr)) + if out_count_ptr is not None: + out_count_ptr[0] = out_idx + + # 释放扁平数组 + mb.free(file_sha1s_buf) + mb.free(file_deps_buf) + mb.free(file_dep_counts) + mb.free(file_indegrees) + mb.free(file_processed) + + return 0 + # ============================================================ # RunPhase1 - Phase1: 扫描 includes 目录,按需翻译并生成 stub @@ -51,9 +477,12 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, log: VLogger.Logger | t.CPtr) -> int: """Phase1: 扫描 includes 目录,按需翻译并生成 stub""" if includes_dir is None or temp_dir is None: - stdio.printf("[Phase1] includes_dir 或 temp_dir 为空,跳过\n") + VLogger.warning("includes_dir 或 temp_dir 为空,跳过", "Phase1") return 1 + # 设置全局 temp_dir(供跨模块 CDefine 查找使用) + HandlesType.set_temp_dir(temp_dir) + # 确保 temp 目录存在(build_dir/temp 可能尚未创建) BuildPipeline.ensure_dir(temp_dir) @@ -63,25 +492,35 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 扫描 includes 目录 result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir) if result is None: - stdio.printf("[Phase1] 扫描失败\n") + VLogger.error("扫描失败", "Phase1") return 1 - # 读取 _sha1_map.txt 获取需要的 includes SHA1 集合 - # 优先使用 Projectrans.py 生成的 _sha1_map.txt(含依赖分析,只包含需要的 includes) - # 若不存在,则从扫描结果生成(包含所有 includes,可能导致结构体表溢出) + # 从扫描结果直接构建 SHA1 集合(不依赖 _sha1_map.txt 文件) + # Phase1 作为编译器自身,应从内存中的扫描结果获取 SHA1,避免外部文件依赖 sha1_set: str = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17) if sha1_set is None: - stdio.printf("[Phase1] sha1_set 分配失败\n") + VLogger.error("sha1_set 分配失败", "Phase1") return 1 string.memset(sha1_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17) - set_count: int = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set) + set_count: int = 0 + p1_entry_size_init: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + for se_i in range(result.Count): + if set_count >= StubMerger.MAX_INCLUDES_SHA1: + break + se_addr: t.CUInt64T = t.CUInt64T(result.Entries) + se_i * p1_entry_size_init + se_ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(se_addr, t.CPtr)) + if se_ent is None or se_ent.Sha1 is None: + continue + string.strcpy(sha1_set + set_count * 17, se_ent.Sha1) + set_count += 1 if set_count <= 0: - stdio.printf("[Phase1] _sha1_map.txt 不存在或为空,从扫描结果生成\n") - StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0) - set_count = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set) - if set_count < 0: - stdio.printf("[Phase1] 无法加载 _sha1_map.txt,跳过 Phase1\n") - return 1 + VLogger.warning("扫描结果无有效 SHA1,跳过 Phase1(项目可能无 includes 依赖)", "Phase1") + stdlib.free(sha1_set) + return 0 + # 写入 _sha1_map.txt(人类可读输出,程序内部不读取;机器分析使用 PopulateSha1MapStore 内存存储器) + StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0) + # 填充全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件) + StubMerger.PopulateSha1MapStore(result) # 构建模块 SHA1 映射(供跨模块函数调用名混淆使用) td_len_p1map: t.CSizeT = string.strlen(temp_dir) p1_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) @@ -172,6 +611,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, tr_a._declare_only = 2 tr_a.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry_a.RelPath) HandlesType.set_current_file(file_path_a) + HandlesType.set_current_module_sha1(sha1_a) HandlesType.clear_cdefine_constants() HandlesStruct.reset_visible_structs(mb, 0) ret_a: int = tr_a.translate(tree_a) @@ -235,13 +675,69 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 前面就需要用。Phase 1a 先注册所有 struct/enum/union 到全局表, # Phase 1b 全量翻译时 struct 已注册,走 existing 路径只翻译方法体。 # 只处理可达文件,避免翻译不需要的 includes(如 Test 不依赖 ast 模块)。 + # + # 拓扑排序:预扫描所有文件收集类继承关系,按依赖顺序处理文件, + # 确保父类所在文件先于子类所在文件处理(如 base.py 先于 astaux.py)。 # ============================================================ p1a_reg: int = 0 p1a_skp: int = 0 p1a_fl: int = 0 - for i in range(result.Count): - entry_addr_r: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size + # 预扫描类继承关系,构建拓扑顺序(用扁平字节数组,避免结构体 str 指针 NULL 崩溃) + class_names_buf: bytes = mb.alloc(MAX_CLASSES * CLASS_NAME_LEN) + parent_names_buf: bytes = mb.alloc(MAX_CLASSES * CLASS_NAME_LEN) + def_sha1s_buf: bytes = mb.alloc(MAX_CLASSES * SHA1_LEN) + topo_order: t.CPtr = mb.alloc(4 * MAX_FILE_DEPS) + topo_count_box: t.CPtr = mb.alloc(4) + topo_count: int = 0 + + if class_names_buf is not None and parent_names_buf is not None and def_sha1s_buf is not None and topo_order is not None and topo_count_box is not None: + string.memset(class_names_buf, 0, MAX_CLASSES * CLASS_NAME_LEN) + string.memset(parent_names_buf, 0, MAX_CLASSES * CLASS_NAME_LEN) + string.memset(def_sha1s_buf, 0, MAX_CLASSES * SHA1_LEN) + string.memset(topo_order, 0, 4 * MAX_FILE_DEPS) + string.memset(topo_count_box, 0, 4) + + class_count_scanned: int = _ScanClassInheritance( + mb, result, reachable_set, reachable_count, + use_reachable, sha1_set, set_count, + class_names_buf, parent_names_buf, def_sha1s_buf) + + if class_count_scanned > 0: + ret_topo: int = _TopoSortFiles( + mb, result, reachable_set, reachable_count, + use_reachable, sha1_set, set_count, + class_names_buf, parent_names_buf, def_sha1s_buf, class_count_scanned, + topo_order, topo_count_box) + if ret_topo == 0: + tc_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(topo_count_box, t.CPtr)) + if tc_ptr is not None: + topo_count = tc_ptr[0] + fb_topo: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_topo is not None: + viperlib.snprintf(fb_topo, 1024, + "拓扑排序: %d 个类, %d 个文件按依赖顺序处理", + class_count_scanned, topo_count) + VLogger.info(fb_topo, "Phase1") + else: + VLogger.warning("拓扑排序失败,回退到字母序", "Phase1") + topo_count = 0 + else: + VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1") + topo_count = 0 + + # 遍历文件:优先使用拓扑顺序,回退到字母序 + iter_count: int = topo_count if topo_count > 0 else result.Count + for i in range(iter_count): + # 获取文件索引:拓扑顺序或字母序 + file_idx: int = i + if topo_count > 0: + od_addr: t.CUInt64T = t.CUInt64T(topo_order) + i * 4 + od_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(od_addr, t.CPtr)) + if od_ptr is not None: + file_idx = od_ptr[0] + + entry_addr_r: t.CUInt64T = t.CUInt64T(result.Entries) + file_idx * entry_size entry_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_r, t.CPtr)) if entry_r is None: p1a_fl += 1 @@ -252,15 +748,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, p1a_fl += 1 continue - # 按需翻译过滤:只处理可达文件 - if use_reachable != 0: - if StubMerger._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0: - p1a_skp += 1 - continue - else: - if StubMerger._is_in_sha1_set(sha1_r, sha1_set, set_count) == 0: - p1a_skp += 1 - continue + # 按需翻译过滤(拓扑排序已过滤,但字母序回退需要检查) + if topo_count == 0: + if use_reachable != 0: + if StubMerger._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0: + p1a_skp += 1 + continue + else: + if StubMerger._is_in_sha1_set(sha1_r, sha1_set, set_count) == 0: + p1a_skp += 1 + continue # 读取文件内容 file_path_r: str = entry_r.Path @@ -310,6 +807,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, tr_r._declare_only = 1 tr_r.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry_r.RelPath) HandlesType.set_current_file(file_path_r) + HandlesType.set_current_module_sha1(sha1_r) HandlesType.clear_cdefine_constants() HandlesStruct.reset_visible_structs(mb, 0) ret_r: int = tr_r.translate(tree_r) @@ -325,6 +823,18 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(tr_r._nonlocal_names) stdlib.free(src_buf_r) + # 释放拓扑排序资源 + if class_names_buf is not None: + mb.free(class_names_buf) + if parent_names_buf is not None: + mb.free(parent_names_buf) + if def_sha1s_buf is not None: + mb.free(def_sha1s_buf) + if topo_order is not None: + mb.free(topo_order) + if topo_count_box is not None: + mb.free(topo_count_box) + # ============================================================ # Phase 1b: 全量翻译(struct 已注册,走 existing 路径翻译方法体) # ============================================================ @@ -380,12 +890,17 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(text_path) # text.ll 不存在,需要重新翻译 rp: str = entry.RelPath - stdio.printf("[Phase1] text.ll 不存在,重新翻译: %s (sha1=%s)\n", rp, sha1) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "text.ll 不存在,重新翻译: %s (sha1=%s)", rp, sha1) + VLogger.info(fb, "Phase1") else: # stub 不存在,需要翻译 rp: str = entry.RelPath - stdio.printf("[Phase1] 翻译: %s (sha1=%s)\n", rp, sha1) - stdio.fflush(0) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "翻译: %s (sha1=%s)", rp, sha1) + VLogger.info(fb, "Phase1") stdlib.free(stub_path) @@ -393,13 +908,17 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, file_path: str = entry.Path f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R) if f is None: - stdio.printf("[Phase1] 无法打开(None): %s\n", file_path) - stdio.fflush(0) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "无法打开(None): %s", file_path) + VLogger.error(fb, "Phase1") failed += 1 continue if f.closed: - stdio.printf("[Phase1] 无法打开: %s\n", file_path) - stdio.fflush(0) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "无法打开: %s", file_path) + VLogger.error(fb, "Phase1") failed += 1 continue @@ -412,8 +931,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE) f.close() if bytes_read <= 0: - stdio.printf("[Phase1] 读取失败: %s\n", file_path) - stdio.fflush(0) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "读取失败: %s", file_path) + VLogger.error(fb, "Phase1") stdlib.free(src_buf) failed += 1 continue @@ -423,6 +944,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, src_buf[SRC_BUF_SIZE - 1] = 0 # 解析 AST + stdio.printf("[P1B-1] parse %s\n", entry.RelPath) lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) if lx is None: stdlib.free(src_buf) @@ -431,14 +953,18 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, ast._lexer_init(lx, src_buf, mb) tokens: ast.Token | t.CPtr = ast.tokenize(lx) tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) + stdio.printf("[P1B-1a] parse-done %s\n", entry.RelPath) if tree is None: - stdio.printf("[Phase1] AST 解析失败: %s\n", file_path) - stdio.fflush(0) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "AST 解析失败: %s", file_path) + VLogger.error(fb, "Phase1") stdlib.free(src_buf) failed += 1 continue # 生成 .pyi 存根文件(直接遍历 AST,不依赖 PythonToStubConverter) + stdio.printf("[P1B-2] pyi %s\n", entry.RelPath) pyi_buf: bytes = stdlib.malloc(PYI_BUF_SIZE) if pyi_buf is not None: pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst(mb, tree, entry.RelPath, pyi_buf, PYI_BUF_SIZE) @@ -453,6 +979,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(pyi_buf) # 翻译 AST → LLVM IR + stdio.printf("[P1B-3] translate %s\n", entry.RelPath) tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() if tr is None: stdlib.free(src_buf) @@ -461,11 +988,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, tr.ModuleSha1 = sha1 tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry.RelPath) HandlesType.set_current_file(file_path) + HandlesType.set_current_module_sha1(sha1) HandlesType.clear_cdefine_constants() HandlesStruct.reset_visible_structs(mb, 0) ret: int = tr.translate(tree) + stdio.printf("[P1B-3a] translate-done %s ret=%d\n", entry.RelPath, ret) if ret != 0: - stdio.printf("[Phase1] 翻译失败: %s\n", file_path) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "翻译失败: %s", file_path) + VLogger.error(fb, "Phase1") stdlib.free(src_buf) if tr._global_names is not None: stdlib.free(tr._global_names) @@ -475,7 +1007,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, continue # dump stub IR (declarations only) - PHASE1_IR_SIZE: t.CSizeT = 262144 + stdio.printf("[P1B-4] stub %s\n", entry.RelPath) + PHASE1_IR_SIZE: t.CSizeT = 1048576 stub_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE) if stub_buf is None: stdlib.free(src_buf) @@ -500,6 +1033,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(stub_buf) # dump text IR (definitions only) + stdio.printf("[P1B-5] text %s\n", entry.RelPath) text_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE) if text_buf is None: stdlib.free(src_buf) @@ -544,7 +1078,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, translated += 1 - stdio.printf("[Phase1] 完成: 翻译=%d 跳过=%d 失败=%d\n", translated, skipped, failed) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "完成: 翻译=%d 跳过=%d 失败=%d", translated, skipped, failed) + VLogger.info(fb, "Phase1") # 释放可达 SHA1 集合(如果分配了) if reachable_set is not None: diff --git a/App/lib/core/Phase2.py b/App/lib/core/Phase2.py index ab6bb64..b72aa4f 100644 --- a/App/lib/core/Phase2.py +++ b/App/lib/core/Phase2.py @@ -17,6 +17,7 @@ import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HandlesTranslator import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.core.Handles.HandlesImports as HandlesImports +import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.BuildPipeline as BuildPipeline import lib.core.StubMerger as StubMerger import lib.Projectrans.Utils as Utils @@ -138,7 +139,10 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy | t.CPtr, ent.Path = full_path ent.Sha1 = sha1_val file_count += 1 - stdio.printf("[project] %s (sha1=%s)\n", fname, sha1_val) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "%s (sha1=%s)", fname, sha1_val) + VLogger.info(fb, "project") stdlib.free(sbuf) # full_path 不释放:ent.Path 引用它 @@ -165,10 +169,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, Returns: 0 成功,非 0 失败 """ if source_dir is None: - stdio.printf("[project] source_dir 为空\n") + VLogger.error("source_dir 为空", "project") return 1 - stdio.printf("[project] 多文件项目编译: %s\n", source_dir) + stdio.printf("[DBG] RunMultiFileProject enter src=%s\n", source_dir) + stdio.fflush(0) + + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "多文件项目编译: %s", source_dir) + VLogger.info(fb, "project") # === 1. 递归扫描 source_dir 下的 .py 文件(包括子目录)=== entry_size: t.CSizeT = SrcFileEntry.__sizeof__() @@ -178,7 +188,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, string.memset(entries, 0, MAX_SRC_FILES * entry_size) file_count: int = _ScanDirForPyFiles(mb, source_dir, entries, entry_size, 0, MAX_SRC_FILES) - stdio.printf("[project] 共 %d 个源文件\n", file_count) + stdio.printf("[DBG] scan done file_count=%d\n", file_count) + stdio.fflush(0) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "共 %d 个源文件", file_count) + VLogger.info(fb, "project") if file_count == 0: stdlib.free(entries) @@ -191,34 +206,47 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, BuildPipeline.ensure_dir(temp_dir) BuildPipeline.ensure_dir(output_dir) - # 追加 App 源文件 SHA1 到 _sha1_map.txt(供日志转存和符号查找使用) - # 格式: {sha1}:App/{filename}\n (与 includes 条目格式对应) + # 追加 App 源文件 SHA1 到 _sha1_map.txt(人类可读输出,程序内部不读取此文件) + # 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py) + # 同步追加到内存存储器(AppendToSha1MapStore,供跨模块 CDefine 查找) td_len_am: t.CSizeT = string.strlen(temp_dir) + src_dir_len_am: t.CSizeT = string.strlen(source_dir) map_path_am: bytes = stdlib.malloc(td_len_am + 32) if map_path_am is not None: viperlib.snprintf(map_path_am, td_len_am + 32, "%s/_sha1_map.txt", temp_dir) mf: fileio.File | t.CPtr = fileio.File(map_path_am, fileio.MODE.A) + line_am: bytes = stdlib.malloc(512) + # 先遍历一次填充内存存储器(不依赖文件 I/O),再写入 _sha1_map.txt + app_filled: int = 0 + for i in range(file_count): + ea_am: t.CUInt64T = t.CUInt64T(entries) + i * entry_size + ent_am: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_am, t.CPtr)) + if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None: + continue + # 计算相对路径(去除 source_dir 前缀,保留目录结构) + p_str: str = ent_am.Path + p_len: t.CSizeT = string.strlen(p_str) + rel_path_am: str = p_str + if p_len > src_dir_len_am + 1: + if string.strncmp(p_str, source_dir, src_dir_len_am) == 0: + rel_path_am = p_str + src_dir_len_am + 1 + # 同步追加到内存存储器(供跨模块 CDefine 查找) + # rel_path_am 格式如 "lib/core/StubMerger.py",与 _sha1_map.txt 一致 + if StubMerger.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) == 0: + app_filled += 1 + # 写入 _sha1_map.txt(人类可读输出) + if not mf.closed and line_am is not None: + viperlib.snprintf(line_am, 512, "%s:%s\n", ent_am.Sha1, rel_path_am) + ll_am: t.CSizeT = string.strlen(line_am) + mf.write(line_am, ll_am) + if line_am is not None: + stdlib.free(line_am) if not mf.closed: - line_am: bytes = stdlib.malloc(512) - if line_am is not None: - for i in range(file_count): - ea_am: t.CUInt64T = t.CUInt64T(entries) + i * entry_size - ent_am: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_am, t.CPtr)) - if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None: - continue - # 从路径提取文件名(最后一个 / 或 \\ 之后的部分) - p_str: str = ent_am.Path - p_len: t.CSizeT = string.strlen(p_str) - f_start: t.CSizeT = 0 - for j in range(p_len): - if p_str[j] == '/' or p_str[j] == '\\': - f_start = j + 1 - viperlib.snprintf(line_am, 512, "%s:App/%s\n", ent_am.Sha1, p_str + f_start) - ll_am: t.CSizeT = string.strlen(line_am) - mf.write(line_am, ll_am) - stdlib.free(line_am) mf.close() - stdio.printf("[project] 已追加 %d 个 App 文件到 _sha1_map.txt\n", file_count) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "已追加 %d 个 App 文件到 _sha1_map.txt 和内存存储器", app_filled) + VLogger.info(fb, "project") stdlib.free(map_path_am) # 构建模块 SHA1 映射(供跨模块函数调用名混淆使用) @@ -231,6 +259,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # 追加用户源文件的 (模块名, SHA1) 到全局映射 # 使跨模块方法调用能通过 from_imports + _lookup_module_sha1 找到正确的 SHA1 if pb_sha1_arr is not None and pb_mod_arr is not None: + src_dir_len_us: t.CSizeT = string.strlen(source_dir) for i in range(file_count): ea_us: t.CUInt64T = t.CUInt64T(entries) + i * entry_size ent_us: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_us, t.CPtr)) @@ -238,24 +267,26 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, continue if pb_inc_count >= StubMerger.MAX_INCLUDES: break - # 从路径提取模块名(文件名去掉 .py 后缀) + # 计算相对路径并转换为点分模块名(保留完整包路径) path_str: str = ent_us.Path - path_len: t.CSizeT = string.strlen(path_str) - fname_start: t.CSizeT = 0 - for j in range(path_len): - if path_str[j] == '/' or path_str[j] == '\\': - fname_start = j + 1 - mod_len: t.CSizeT = path_len - fname_start - if mod_len < 4: + path_len_us: t.CSizeT = string.strlen(path_str) + rel_path_us: str = path_str + if path_len_us > src_dir_len_us + 1: + if string.strncmp(path_str, source_dir, src_dir_len_us) == 0: + rel_path_us = path_str + src_dir_len_us + 1 + # 使用 _PathToModuleName 转换为点分模块名(处理 __init__.py → 包名) + mod_name_us: str = StubMerger._PathToModuleName(rel_path_us) + if mod_name_us is None: continue - mod_len -= 3 - if mod_len >= 64: - mod_len = 63 # 写入模块名 mod_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 64 - for k in range(mod_len): - pb_mod_arr[mod_idx + k] = path_str[fname_start + k] - pb_mod_arr[mod_idx + mod_len] = '\0' + mn_len_us: t.CSizeT = string.strlen(mod_name_us) + if mn_len_us >= 64: + string.strncpy(pb_mod_arr + mod_idx, mod_name_us, 63) + pb_mod_arr[mod_idx + 63] = '\0' + else: + string.strcpy(pb_mod_arr + mod_idx, mod_name_us) + stdlib.free(mod_name_us) # 写入 SHA1 sha1_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 17 string.strcpy(pb_sha1_arr + sha1_idx, ent_us.Sha1) @@ -265,36 +296,60 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # === 2. Phase A: 为每个文件生成 stub + text === if do_phase1 != 0: + stdio.printf("[DBG] before Phase A\n") + stdio.fflush(0) if log is not None: log.banner("Phase A: 生成 stub + text") # === Phase A-pre: 预注册所有源文件的 struct/enum/union === # 解决循环引用问题:circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义 # 仅注册 struct/enum/union(declare_only=1),不翻译方法体 - stdio.printf("[Phase A-pre] 预注册 struct/enum/union...\n") + # 多遍扫描:解决跨文件继承的字母序问题(如 HandlesAnnAssign.py 在 HandlesBase.py 之前, + # AnnAssignHandle 继承 Mixin 时 Mixin 未注册)。每遍注册新类后,下一遍子类可继承。 + VLogger.info("预注册 struct/enum/union...", "PhaseA") src_dir_len_pre: t.CSizeT = string.strlen(source_dir) - for i in range(file_count): - ea_pre: t.CUInt64T = t.CUInt64T(entries) + i * entry_size - ent_pre: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_pre, t.CPtr)) - if ent_pre is None or ent_pre.Path is None: - continue - pkg_pre: str = None - if string.strlen(ent_pre.Path) > src_dir_len_pre + 1: - rel_path_pre: str = ent_pre.Path + src_dir_len_pre + 1 - pkg_pre = HandlesImports.compute_package_from_relpath(mb, rel_path_pre) - tr_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1) - if tr_pre is None: - stdio.printf("[Phase A-pre] 警告: 预注册失败: %s\n", ent_pre.Path) - stdio.printf("[Phase A-pre] 预注册完成\n") + PHASE_A_PRE_MAX_PASSES: t.CInt = 3 + for pass_i in range(PHASE_A_PRE_MAX_PASSES): + struct_count_before: int = HandlesStruct.get_struct_count() + stdio.printf("[DBG] PhaseA-pre pass=%d struct_count_before=%d\n", pass_i, struct_count_before) + stdio.fflush(0) + for i in range(file_count): + ea_pre: t.CUInt64T = t.CUInt64T(entries) + i * entry_size + ent_pre: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_pre, t.CPtr)) + if ent_pre is None or ent_pre.Path is None: + continue + pkg_pre: str = None + if string.strlen(ent_pre.Path) > src_dir_len_pre + 1: + rel_path_pre: str = ent_pre.Path + src_dir_len_pre + 1 + pkg_pre = HandlesImports.compute_package_from_relpath(mb, rel_path_pre) + stdio.printf("[DBG] PhaseA-pre pass=%d file=%s\n", pass_i, ent_pre.Path) + stdio.fflush(0) + tr_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1) + if tr_pre is None: + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "预注册失败: %s", ent_pre.Path) + VLogger.warning(fb, "PhaseA") + struct_count_after: int = HandlesStruct.get_struct_count() + stdio.printf("[DBG] PhaseA-pre pass=%d done struct_count_after=%d\n", pass_i, struct_count_after) + stdio.fflush(0) + # 收敛检查:本遍没有新结构体注册 → 所有类已注册 + if struct_count_after == struct_count_before: + break + VLogger.info("预注册完成", "PhaseA") - PHASE_A_IR_SIZE: t.CSizeT = 262144 + PHASE_A_IR_SIZE: t.CSizeT = 1048576 td_len_pa: t.CSizeT = string.strlen(temp_dir) + stdio.printf("[DBG] PhaseA full-translate start\n") + stdio.fflush(0) for i in range(file_count): ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) if ent is None or ent.Path is None: continue + stdio.printf("[DBG] PhaseA file=%s\n", ent.Path) + stdio.fflush(0) # 计算源文件的包名(相对 source_dir 的目录部分) src_dir_len_pa: t.CSizeT = string.strlen(source_dir) @@ -352,12 +407,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, stdlib.free(deps_path_a) if do_phase2 == 0: - stdio.printf("[project] Phase A 完成(仅 stub 生成)\n") + VLogger.info("Phase A 完成(仅 stub 生成)", "project") stdlib.free(entries) return 0 # === 3. Phase B: 编译每个文件为 .obj === if do_phase2 != 0: + stdio.printf("[DBG] before Phase B\n") + stdio.fflush(0) if log is not None: log.banner("Phase B: 编译 .obj") @@ -384,28 +441,51 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) if ent is None or ent.Path is None or ent.Sha1 is None: continue + stdio.printf("[DBG] PhaseB file=%s\n", ent.Path) + stdio.fflush(0) # 组合本地 stub + 所有依赖 stub + 本地 text → 完整 IR + stdio.printf("[DBG] PhaseB step=alloc combined_ir\n") + stdio.fflush(0) combined_ir: bytes = stdlib.malloc(COMBINED_IR_SIZE) if combined_ir is None: - stdio.printf("[Phase B] combined_ir 分配失败: %s\n", ent.Path) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "combined_ir 分配失败: %s", ent.Path) + VLogger.error(fb, "PhaseB") continue + stdio.printf("[DBG] PhaseB step=BuildCombinedIR sha1=%s\n", ent.Sha1) + stdio.fflush(0) combined_len: t.CSizeT = StubMerger.BuildCombinedIR(temp_dir, ent.Sha1, combined_ir, COMBINED_IR_SIZE) + stdio.printf("[DBG] PhaseB step=BuildCombinedIR done len=%d\n", combined_len) + stdio.fflush(0) if combined_len == 0: - stdio.printf("[Phase B] BuildCombinedIR 失败: %s\n", ent.Path) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", ent.Path) + VLogger.error(fb, "PhaseB") stdlib.free(combined_ir) continue # 编译为 .obj + stdio.printf("[DBG] PhaseB step=compile_module_to_obj\n") + stdio.fflush(0) cret: int = BuildPipeline.compile_module_to_obj( combined_ir, combined_len, temp_dir, output_dir, ent.Sha1, cc_cmd, cc_flags) + stdio.printf("[DBG] PhaseB step=compile_module_to_obj done cret=%d\n", cret) + stdio.fflush(0) stdlib.free(combined_ir) if cret != 0: - stdio.printf("[FATAL][Phase B] llc 编译失败,终止编译: %s\n", ent.Path) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "llc 编译失败,终止编译: %s", ent.Path) + VLogger.error(fb, "PhaseB") sys.exit(1) compiled_count += 1 + stdio.printf("[DBG] PhaseB step=obj_path construct\n") + stdio.fflush(0) # 构造 .obj 路径,检测是否是 main 模块(test_main.py 或 main.py) od_len: t.CSizeT = string.strlen(output_dir) @@ -424,7 +504,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if op_sliced_len < 512: string.strcpy(main_obj_path, obj_path_sliced) else: - stdio.printf("[Phase B] 警告: main_obj_path 缓冲区不足\n") + VLogger.warning("main_obj_path 缓冲区不足", "PhaseB") else: # 其他模块: 追加到 obj_paths if obj_pos + op_sliced_len + 2 < OBJ_PATHS_SIZE: @@ -435,13 +515,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, obj_pos += op_sliced_len obj_paths[obj_pos] = '\0' else: - stdio.printf("[Phase B] 警告: .obj 路径缓冲区不足\n") + VLogger.warning(".obj 路径缓冲区不足", "PhaseB") stdlib.free(obj_path_sliced) + stdio.printf("[DBG] PhaseB step=obj_path done\n") + stdio.fflush(0) - stdio.printf("[Phase B] 编译完成: %d/%d\n", compiled_count, file_count) + stdio.printf("[DBG] PhaseB loop done compiled_count=%d\n", compiled_count) + stdio.fflush(0) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "编译完成: %d/%d", compiled_count, file_count) + VLogger.success(fb, "PhaseB") if compiled_count == 0: - stdio.printf("[Phase B] 无成功编译的文件\n") + VLogger.error("无成功编译的文件", "PhaseB") stdlib.free(entries) return 1 @@ -449,198 +536,191 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # includes.binary 可能缺少某些 includes .obj(如 testcheck.py), # 这些文件被用户项目导入但未被 TransPyV 自身依赖,Projectrans.py 未编译它们。 # 检测并编译缺失的 includes 文件到 output_dir,加入链接命令。 + # 数据来源:StubMerger 全局内存存储器(不读取 _sha1_map.txt 文件) if includes_dir is not None and includes_binary_dir is not None: + stdio.printf("[DBG] PhaseB+ start\n") + stdio.fflush(0) inc_compiled: int = 0 td_len_mi: t.CSizeT = string.strlen(temp_dir) - map_path_mi: bytes = stdlib.malloc(td_len_mi + 32) - if map_path_mi is not None: - viperlib.snprintf(map_path_mi, td_len_mi + 32, "%s/_sha1_map.txt", temp_dir) - mapf_mi: fileio.File | t.CPtr = fileio.File(map_path_mi, fileio.MODE.R) - if not mapf_mi.closed: - map_buf_mi: bytes = stdlib.malloc(65536) - if map_buf_mi is not None: - map_br_mi: t.CInt64T = mapf_mi.read_all(map_buf_mi, 65536) - mapf_mi.close() - if map_br_mi > 0: - if map_br_mi < 65536: - map_buf_mi[map_br_mi] = '\0' - else: - map_buf_mi[65535] = '\0' - map_len_mi: t.CSizeT = map_br_mi - mpos_mi: t.CSizeT = 0 - while mpos_mi < map_len_mi: - ml_start_mi: t.CSizeT = mpos_mi - while mpos_mi < map_len_mi: - if map_buf_mi[mpos_mi] == '\n': - break - mpos_mi += 1 - ml_len_mi: t.CSizeT = mpos_mi - ml_start_mi - mpos_mi += 1 + # 从全局存储器获取 SHA1/模块名/rel_path 数组(直接访问器,避免 box 解引用问题) + store_count_mi: int = StubMerger.GetSha1StoreCount() + stdio.printf("[DBG] PhaseB+ store_count=%d\n", store_count_mi) + stdio.fflush(0) + if store_count_mi > 0: + store_sha1_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr() + store_mod_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreModArrPtr() + store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() + stdio.printf("[DBG] PhaseB+ arrs ok sha1=%p mod=%p rel=%p\n", store_sha1_arr_mi, store_mod_arr_mi, store_rel_arr_mi) + stdio.fflush(0) + for si_mi in range(store_count_mi): + # 获取当前条目的 SHA1 和 rel_path + inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(si_mi) * 17 + inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_REL_PATH_LEN + stdio.printf("[DBG] PhaseB+ iter=%d sha1=%s rel=%s\n", si_mi, inc_sha1_mi, inc_rel_mi) + stdio.fflush(0) - # 最小长度: 16(sha1)+1(:)+9(includes/)+1+1+1 = 29 - if ml_len_mi < 26: - continue - if map_buf_mi[ml_start_mi + 16] != ':': - continue - rp_start_mi: t.CSizeT = ml_start_mi + 17 - if string.strncmp(map_buf_mi + rp_start_mi, "includes/", 9) != 0: - continue + # 检查 .obj 是否已存在于 includes.binary + ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir) + check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35) + if check_pat_mi is None: + continue + viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi) + check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__()) + obj_exists_mi: int = 0 + if check_fd_mi is not None: + string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__()) + check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi) + if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE: + w32.win32file.FindClose(check_h_mi) + obj_exists_mi = 1 + if obj_exists_mi != 0: + continue - # 提取 SHA1 - inc_sha1_mi: str = stdlib.malloc(17) - if inc_sha1_mi is None: - continue - string.strncpy(inc_sha1_mi, map_buf_mi + ml_start_mi, 16) - inc_sha1_mi[16] = '\0' + # .obj 不存在,需要编译 + # 构造源文件路径: {includes_dir}/{rel_path} + rel_path_len_mi: t.CSizeT = string.strlen(inc_rel_mi) + inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir) + src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1) + if src_fp_mi is None: + continue + viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1, + "%s/%s", includes_dir, inc_rel_mi) - # 检查 .obj 是否已存在于 includes.binary - ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir) - check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35) - if check_pat_mi is None: - continue - viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi) - check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__()) - obj_exists_mi: int = 0 - if check_fd_mi is not None: - string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__()) - check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi) - if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE: - w32.win32file.FindClose(check_h_mi) - obj_exists_mi = 1 - if obj_exists_mi != 0: - continue + # 检查是否为声明文件(只有 declare 没有实质 define) + # 判断方法: text.ll 中若有混淆函数 define(含 @")则为实现文件 + is_decl_mi: int = -1 + tpath_mi: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") + if tpath_mi is not None: + tf_mi: fileio.File | t.CPtr = fileio.File(tpath_mi, fileio.MODE.R) + if not tf_mi.closed: + is_decl_mi = 1 + tbuf_mi: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE) + if tbuf_mi is not None: + tbr_mi: t.CInt64T = tf_mi.read_all(tbuf_mi, StubMerger.STUB_READ_BUF_SIZE) + if tbr_mi > 0: + if tbr_mi < StubMerger.STUB_READ_BUF_SIZE: + tbuf_mi[tbr_mi] = '\0' + else: + tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0' + # 逐行扫描: 找 define 行中含 @" 的(混淆函数名) + tpos_mi: t.CSizeT = 0 + while tpos_mi < tbr_mi: + tls_mi: t.CSizeT = tpos_mi + while tpos_mi < tbr_mi: + if tbuf_mi[tpos_mi] == '\n': + break + tpos_mi += 1 + tll_mi: t.CSizeT = tpos_mi - tls_mi + if tpos_mi < tbr_mi: + tpos_mi += 1 + if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0: + # 临时在行尾加 \0 供 strstr 使用 + saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi] + tbuf_mi[tls_mi + tll_mi] = '\0' + if string.strstr(tbuf_mi + tls_mi, "@\"") is not None: + tbuf_mi[tls_mi + tll_mi] = saved_mi + is_decl_mi = 0 + break + tbuf_mi[tls_mi + tll_mi] = saved_mi + stdlib.free(tbuf_mi) + tf_mi.close() + stdlib.free(tpath_mi) + # is_decl_mi == -1: text.ll 不存在(Phase1 未翻译此文件,可能不被项目导入) + # is_decl_mi == 1: 声明文件(仅 declare 无 define) + # 两种情况都跳过:不编译未被 Phase1 翻译的 includes 文件 + if is_decl_mi != 0: + continue - # .obj 不存在,需要编译 - # 构造源文件路径: {includes_dir}/{rel_path_without_includes_prefix} - rel_path_len_mi: t.CSizeT = ml_len_mi - 26 - inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir) - src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1) - if src_fp_mi is None: - continue - viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1, - "%s/%s", includes_dir, map_buf_mi + rp_start_mi + 9) + # 尝试 BuildCombinedIR(stub/text 应已由 Phase1 生成) + inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE) + inc_combined_len_mi: t.CSizeT = 0 + if inc_combined_mi is not None: + inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) - # 检查是否为声明文件(只有 declare 没有实质 define) - # 判断方法: text.ll 中若有混淆函数 define(含 @\")则为实现文件 - is_decl_mi: int = -1 - tpath_mi: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") - if tpath_mi is not None: - tf_mi: fileio.File | t.CPtr = fileio.File(tpath_mi, fileio.MODE.R) - if not tf_mi.closed: - is_decl_mi = 1 - tbuf_mi: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE) - if tbuf_mi is not None: - tbr_mi: t.CInt64T = tf_mi.read_all(tbuf_mi, StubMerger.STUB_READ_BUF_SIZE) - if tbr_mi > 0: - if tbr_mi < StubMerger.STUB_READ_BUF_SIZE: - tbuf_mi[tbr_mi] = '\0' - else: - tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0' - # 逐行扫描: 找 define 行中含 @\" 的(混淆函数名) - tpos_mi: t.CSizeT = 0 - while tpos_mi < tbr_mi: - tls_mi: t.CSizeT = tpos_mi - while tpos_mi < tbr_mi: - if tbuf_mi[tpos_mi] == '\n': - break - tpos_mi += 1 - tll_mi: t.CSizeT = tpos_mi - tls_mi - if tpos_mi < tbr_mi: - tpos_mi += 1 - if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0: - # 临时在行尾加 \0 供 strstr 使用 - saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi] - tbuf_mi[tls_mi + tll_mi] = '\0' - if string.strstr(tbuf_mi + tls_mi, "@\"") is not None: - tbuf_mi[tls_mi + tll_mi] = saved_mi - is_decl_mi = 0 - break - tbuf_mi[tls_mi + tll_mi] = saved_mi - stdlib.free(tbuf_mi) - tf_mi.close() - stdlib.free(tpath_mi) - if is_decl_mi == 1: - continue + # 如果 stub/text 不存在,翻译源文件并保存 stub + text,然后重试 + if inc_combined_len_mi == 0 and inc_combined_mi is not None: + # 计算 includes 文件的包名(相对 includes_dir 的目录部分) + inc_pkg_mi: str = None + if string.strlen(src_fp_mi) > inc_dir_len_mi + 1: + inc_rel_mi2: str = src_fp_mi + inc_dir_len_mi + 1 + inc_pkg_mi = HandlesImports.compute_package_from_relpath(mb, inc_rel_mi2) + tr_mi: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, src_fp_mi, inc_sha1_mi, inc_pkg_mi) + if tr_mi is not None: + PBP_IR_SIZE: t.CSizeT = 1048576 + # 保存 stub.ll (切片路径) + inc_stub_buf: bytes = stdlib.malloc(PBP_IR_SIZE) + if inc_stub_buf is not None: + tr_mi.dump_ir(inc_stub_buf, PBP_IR_SIZE, llvmlite.OUTPUT_STUB) + inc_stub_len: t.CSizeT = string.strlen(inc_stub_buf) + inc_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll") + if inc_stub_path is not None: + isf: fileio.File | t.CPtr = fileio.File(inc_stub_path, fileio.MODE.W) + if not isf.closed: + isf.write(inc_stub_buf, inc_stub_len) + isf.close() + stdlib.free(inc_stub_path) + stdlib.free(inc_stub_buf) + # 保存 text.ll (切片路径) + inc_text_buf: bytes = stdlib.malloc(PBP_IR_SIZE) + if inc_text_buf is not None: + tr_mi.dump_ir(inc_text_buf, PBP_IR_SIZE, llvmlite.OUTPUT_TEXT) + inc_text_len: t.CSizeT = string.strlen(inc_text_buf) + inc_text_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") + if inc_text_path is not None: + itf: fileio.File | t.CPtr = fileio.File(inc_text_path, fileio.MODE.W) + if not itf.closed: + itf.write(inc_text_buf, inc_text_len) + itf.close() + stdlib.free(inc_text_path) + stdlib.free(inc_text_buf) + # 重试 BuildCombinedIR + inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) - # 尝试 BuildCombinedIR(stub/text 应已由 Phase1 生成) - inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE) - inc_combined_len_mi: t.CSizeT = 0 - if inc_combined_mi is not None: - inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) + if inc_combined_len_mi == 0: + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", src_fp_mi) + VLogger.error(fb, "PhaseB+") + if inc_combined_mi is not None: + stdlib.free(inc_combined_mi) + continue - # 如果 stub/text 不存在,翻译源文件并保存 stub + text,然后重试 - if inc_combined_len_mi == 0 and inc_combined_mi is not None: - # 计算 includes 文件的包名(相对 includes_dir 的目录部分) - inc_pkg_mi: str = None - if string.strlen(src_fp_mi) > inc_dir_len_mi + 1: - inc_rel_mi: str = src_fp_mi + inc_dir_len_mi + 1 - inc_pkg_mi = HandlesImports.compute_package_from_relpath(mb, inc_rel_mi) - tr_mi: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, src_fp_mi, inc_sha1_mi, inc_pkg_mi) - if tr_mi is not None: - PBP_IR_SIZE: t.CSizeT = 262144 - # 保存 stub.ll (切片路径) - inc_stub_buf: bytes = stdlib.malloc(PBP_IR_SIZE) - if inc_stub_buf is not None: - tr_mi.dump_ir(inc_stub_buf, PBP_IR_SIZE, llvmlite.OUTPUT_STUB) - inc_stub_len: t.CSizeT = string.strlen(inc_stub_buf) - inc_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll") - if inc_stub_path is not None: - isf: fileio.File | t.CPtr = fileio.File(inc_stub_path, fileio.MODE.W) - if not isf.closed: - isf.write(inc_stub_buf, inc_stub_len) - isf.close() - stdlib.free(inc_stub_path) - stdlib.free(inc_stub_buf) - # 保存 text.ll (切片路径) - inc_text_buf: bytes = stdlib.malloc(PBP_IR_SIZE) - if inc_text_buf is not None: - tr_mi.dump_ir(inc_text_buf, PBP_IR_SIZE, llvmlite.OUTPUT_TEXT) - inc_text_len: t.CSizeT = string.strlen(inc_text_buf) - inc_text_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") - if inc_text_path is not None: - itf: fileio.File | t.CPtr = fileio.File(inc_text_path, fileio.MODE.W) - if not itf.closed: - itf.write(inc_text_buf, inc_text_len) - itf.close() - stdlib.free(inc_text_path) - stdlib.free(inc_text_buf) - # 重试 BuildCombinedIR - inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) + # 编译为 .obj + inc_cret_mi: int = BuildPipeline.compile_module_to_obj( + inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi, + cc_cmd, cc_flags) + stdlib.free(inc_combined_mi) + if inc_cret_mi != 0: + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "llc 编译失败,终止编译: %s", src_fp_mi) + VLogger.error(fb, "PhaseB+") + sys.exit(1) - if inc_combined_len_mi == 0: - stdio.printf("[Phase B+] BuildCombinedIR 失败: %s\n", src_fp_mi) - if inc_combined_mi is not None: - stdlib.free(inc_combined_mi) - continue + inc_compiled += 1 - # 编译为 .obj - inc_cret_mi: int = BuildPipeline.compile_module_to_obj( - inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi, - cc_cmd, cc_flags) - stdlib.free(inc_combined_mi) - if inc_cret_mi != 0: - stdio.printf("[FATAL][Phase B+] llc 编译失败,终止编译: %s\n", src_fp_mi) - sys.exit(1) + # 添加到 obj_paths (切片路径) + od_len_mi: t.CSizeT = string.strlen(output_dir) + inc_obj_sliced: str = StubMerger._sliced_path(output_dir, od_len_mi, inc_sha1_mi, "obj") + if inc_obj_sliced is not None: + inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced) + if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE: + if obj_pos > 0: + obj_paths[obj_pos] = ' ' + obj_pos += 1 + string.strcpy(obj_paths + obj_pos, inc_obj_sliced) + obj_pos += inc_obj_len + obj_paths[obj_pos] = '\0' + stdlib.free(inc_obj_sliced) - inc_compiled += 1 - - # 添加到 obj_paths (切片路径) - od_len_mi: t.CSizeT = string.strlen(output_dir) - inc_obj_sliced: str = StubMerger._sliced_path(output_dir, od_len_mi, inc_sha1_mi, "obj") - if inc_obj_sliced is not None: - inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced) - if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE: - if obj_pos > 0: - obj_paths[obj_pos] = ' ' - obj_pos += 1 - string.strcpy(obj_paths + obj_pos, inc_obj_sliced) - obj_pos += inc_obj_len - obj_paths[obj_pos] = '\0' - stdlib.free(inc_obj_sliced) - - stdio.printf("[Phase B+] 编译缺失 includes: %d 个\n", inc_compiled) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "编译缺失 includes: %d 个", inc_compiled) + VLogger.info(fb, "PhaseB+") # === 4. Phase C: 链接所有 .obj → .exe === + stdio.printf("[DBG] PhaseC start\n") + stdio.fflush(0) if log is not None: log.banner("Phase C: 链接") @@ -674,20 +754,33 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, final_obj_paths[fop_pos] = '\0' obj_paths_len: t.CSizeT = fop_pos + stdio.printf("[DBG] PhaseC before link_objs_to_exe len=%d\n", obj_paths_len) + stdio.fflush(0) lret: int = BuildPipeline.link_objs_to_exe( final_obj_paths, obj_paths_len, linker_cmd, linker_flags, exe_path, includes_binary_dir) + stdio.printf("[DBG] PhaseC link_objs_to_exe done lret=%d\n", lret) + stdio.fflush(0) if lret == 0: - stdio.printf("输出: %s\n", exe_path) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "输出: %s", exe_path) + VLogger.success(fb, "PhaseC") if args.get_bool("run"): - stdio.printf("[run] 执行: %s\n", exe_path) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "执行: %s", exe_path) + VLogger.info(fb, "run") rp: subprocess.CompletedProcess | t.CPtr = subprocess.run(exe_path, False, False) if rp is not None: - stdio.printf("[run] 退出码: %d\n", rp.returncode) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "退出码: %d", rp.returncode) + VLogger.info(fb, "run") else: - stdio.printf("[Phase C] 链接失败\n") + VLogger.error("链接失败", "PhaseC") stdlib.free(entries) return 1 diff --git a/App/lib/core/StubMerger.py b/App/lib/core/StubMerger.py index bfd12c0..cfad48d 100644 --- a/App/lib/core/StubMerger.py +++ b/App/lib/core/StubMerger.py @@ -11,6 +11,7 @@ import w32.fileio as fileio import viperlib import sys import ast +import lib.core.VLogger as VLogger import lib.core.IncludesScanner as IncludesScanner import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesImports as HandlesImports @@ -39,10 +40,20 @@ _mbuddy: memhub.MemManager | t.CPtr # 最大 includes SHA1 数(用于过滤) MAX_INCLUDES_SHA1: t.CDefine = 256 # stub 读取缓冲区大小 -STUB_READ_BUF_SIZE: t.CDefine = 262144 +STUB_READ_BUF_SIZE: t.CDefine = 1048576 # 最大 includes 条目数 MAX_INCLUDES: t.CDefine = 256 +# 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件) +# Phase1 扫描完成后填充,供所有函数使用 +_sha1_store_arr: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个) +_sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个) +_sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个) +_sha1_store_count: int # 条目数 + +# rel_path 最大长度 +MAX_REL_PATH_LEN: t.CDefine = 256 + # ============================================================ # _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free) @@ -217,77 +228,31 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int, # ============================================================ -# _load_includes_sha1_set - 读取 _sha1_map.txt,收集 includes/ 开头的 SHA1 +# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1 # -# _sha1_map.txt 格式:{sha16}:{rel_path} -# 仅收集 rel_path 以 "includes/" 开头的 SHA1,用于过滤 stub 文件, -# 避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。 +# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。 +# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。 # ============================================================ def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr, temp_dir: str, sha1_set: str) -> int: - """读取 _sha1_map.txt,将 includes/ 开头的 SHA1 写入 sha1_set + """从全局内存存储器将 includes 的 SHA1 写入 sha1_set + + pool/temp_dir 参数保留以兼容现有调用者,但不再使用。 + 数据来源是 PopulateSha1MapStore 填充的全局存储器。 sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null) 返回找到的 includes SHA1 数量,-1 表示错误""" - if pool is None or temp_dir is None or sha1_set is None: + if sha1_set is None: return -1 - - # 构造路径 temp_dir/_sha1_map.txt(使用 stdlib.malloc 避免 mbuddy 池耗尽) - dir_len: t.CSizeT = string.strlen(temp_dir) - map_path: bytes = stdlib.malloc(dir_len + 32) - if map_path is None: + if _sha1_store_arr is None: return -1 - viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir) - - # 打开文件 - f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R) - if f.closed: - return -1 - - # 读取内容(使用 stdlib.malloc 避免 mbuddy 池耗尽) - MAP_BUF_SIZE: t.CSizeT = 65536 - content: str = stdlib.malloc(MAP_BUF_SIZE) - if content is None: - f.close() - return -1 - bytes_read: t.CInt64T = f.read_all(content, MAP_BUF_SIZE) - f.close() - if bytes_read <= 0: - return -1 - if bytes_read < MAP_BUF_SIZE: - content[bytes_read] = '\0' - else: - content[MAP_BUF_SIZE - 1] = '\0' - - # 解析行: {sha1}:{rel_path} count: int = 0 - pos: t.CSizeT = 0 - content_len: t.CSizeT = bytes_read - while pos < content_len: - # 找行尾 - line_start: t.CSizeT = pos - while pos < content_len: - if content[pos] == '\n': - break - pos += 1 - line_len: t.CSizeT = pos - line_start - pos += 1 # skip \n - - # 最小长度: 16(sha1) + 1(:) + 9(includes/) = 26 - if line_len < 26: - continue - # 检查第17个字符是 ':' - if content[line_start + 16] != ':': - continue - - # 检查 rel_path 是否以 "includes/" 开头 - rel_start: t.CSizeT = line_start + 17 - if string.strncmp(content + rel_start, "includes/", 9) == 0: - if count < MAX_INCLUDES_SHA1: - string.strncpy(sha1_set + count * 17, content + line_start, 16) - sha1_set[count * 17 + 16] = '\0' - count += 1 - + for i in range(_sha1_store_count): + if count >= MAX_INCLUDES_SHA1: + break + idx: t.CSizeT = t.CSizeT(i) * 17 + string.strcpy(sha1_set + t.CSizeT(count) * 17, _sha1_store_arr + idx) + count += 1 return count @@ -305,10 +270,10 @@ def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int: # ============================================================ -# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt +# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出) # -# TransPyV.exe 自己生成 _sha1_map.txt(不再依赖 Projectrans.py 预生成), -# 确保所有 includes 文件的 SHA1 都被记录,供 StubMerger 加载 stub 时过滤。 +# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。 +# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器。 # # Args: # mb: 内存池 @@ -322,7 +287,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str, scan_result: IncludesScanner.ScanResult | t.CPtr, filter_set: t.CChar | t.CPtr, filter_count: int) -> int: - """将 includes 扫描结果写入 _sha1_map.txt(供 StubMerger 使用) + """将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器) 若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。 """ @@ -367,6 +332,212 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str, return 0 +# ============================================================ +# PopulateSha1MapStore - 从扫描结果填充全局 SHA1 映射存储器 +# +# 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。 +# 模块名通过 _PathToModuleName(includes/rel_path) 计算。 +# 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。 +# +# Args: +# scan_result: IncludesScanner 扫描结果 +# +# Returns: +# 填充的条目数,-1 表示失败 +# ============================================================ +def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> int: + """从扫描结果填充全局 SHA1 映射存储器 + + 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。 + 模块名通过 _PathToModuleName(includes/rel_path) 计算。 + 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。 + """ + global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count + if scan_result is None: + return -1 + # 释放旧存储器 + if _sha1_store_arr is not None: + stdlib.free(_sha1_store_arr) + if _sha1_store_mod is not None: + stdlib.free(_sha1_store_mod) + if _sha1_store_rel is not None: + stdlib.free(_sha1_store_rel) + _sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17) + _sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64) + _sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN) + if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None: + return -1 + string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17) + string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64) + string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN) + _sha1_store_count = 0 + entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + for i in range(scan_result.Count): + if _sha1_store_count >= MAX_INCLUDES: + break + ea: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_p + ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) + if ent is None or ent.Sha1 is None or ent.RelPath is None: + continue + # 构建完整路径 includes/{RelPath}(_PathToModuleName 需要) + rp_len: t.CSizeT = string.strlen(ent.RelPath) + full_path_buf: bytes = stdlib.malloc(rp_len + 10) + if full_path_buf is None: + continue + viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath) + mod_name: str = _PathToModuleName(full_path_buf) + stdlib.free(full_path_buf) + if mod_name is not None: + idx: t.CSizeT = t.CSizeT(_sha1_store_count) * 17 + string.strcpy(_sha1_store_arr + idx, ent.Sha1) + idx2: t.CSizeT = t.CSizeT(_sha1_store_count) * 64 + mn_len: t.CSizeT = string.strlen(mod_name) + if mn_len < 64: + string.strcpy(_sha1_store_mod + idx2, mod_name) + else: + string.strncpy(_sha1_store_mod + idx2, mod_name, 63) + _sha1_store_mod[idx2 + 63] = '\0' + stdlib.free(mod_name) + # 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用) + idx3: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN + if rp_len < MAX_REL_PATH_LEN: + string.strcpy(_sha1_store_rel + idx3, ent.RelPath) + else: + string.strncpy(_sha1_store_rel + idx3, ent.RelPath, MAX_REL_PATH_LEN - 1) + _sha1_store_rel[idx3 + MAX_REL_PATH_LEN - 1] = '\0' + _sha1_store_count += 1 + return _sha1_store_count + + +# ============================================================ +# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器 +# +# Phase1 调用 PopulateSha1MapStore 填充 includes 文件, +# Phase2 扫描 App 源文件后调用此函数追加 App 模块条目, +# 确保编译 App 自身时跨模块 CDefine 查找能命中 App 模块 +# (如 StubMerger.MAX_INCLUDES 在编译 Phase2 时通过 +# _build_source_path_from_sha1 查找 StubMerger 的源路径)。 +# +# 若存储器未初始化(PopulateSha1MapStore 未调用),自动初始化。 +# 若 SHA1 已存在(去重),跳过。 +# +# Args: +# sha1: SHA1 字符串(16 字符 + null) +# rel_path: 相对路径(如 "lib/core/StubMerger.py", +# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致) +# +# Returns: +# 0 成功(含去重跳过),1 失败 +# ============================================================ +def AppendToSha1MapStore(sha1: str, rel_path: str) -> int: + """追加条目到全局 SHA1 映射存储器(Phase2 用)""" + global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count + if sha1 is None or rel_path is None: + return 1 + # 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用) + if _sha1_store_arr is None: + _sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17) + _sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64) + _sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN) + if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None: + return 1 + string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17) + string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64) + string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN) + _sha1_store_count = 0 + if _sha1_store_count >= MAX_INCLUDES: + return 1 + # 去重:若 SHA1 已存在则跳过 + for i in range(_sha1_store_count): + sidx_d: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(_sha1_store_arr + sidx_d, sha1) == 0: + return 0 + # 模块名:rel_path "lib/core/StubMerger.py" → "lib.core.StubMerger" + full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10) + if full_path_buf is None: + return 1 + viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path) + mod_name: str = _PathToModuleName(full_path_buf) + stdlib.free(full_path_buf) + if mod_name is None: + return 1 + # 写入 SHA1 + idx_a: t.CSizeT = t.CSizeT(_sha1_store_count) * 17 + string.strcpy(_sha1_store_arr + idx_a, sha1) + # 写入模块名 + idx_m: t.CSizeT = t.CSizeT(_sha1_store_count) * 64 + mn_len: t.CSizeT = string.strlen(mod_name) + if mn_len < 64: + string.strcpy(_sha1_store_mod + idx_m, mod_name) + else: + string.strncpy(_sha1_store_mod + idx_m, mod_name, 63) + _sha1_store_mod[idx_m + 63] = '\0' + stdlib.free(mod_name) + # 写入相对路径 + idx_r: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN + rp_len: t.CSizeT = string.strlen(rel_path) + if rp_len < MAX_REL_PATH_LEN: + string.strcpy(_sha1_store_rel + idx_r, rel_path) + else: + string.strncpy(_sha1_store_rel + idx_r, rel_path, MAX_REL_PATH_LEN - 1) + _sha1_store_rel[idx_r + MAX_REL_PATH_LEN - 1] = '\0' + _sha1_store_count += 1 + return 0 + + +# ============================================================ +# GetSha1StoreCount - 获取全局存储器条目数 +# ============================================================ +def GetSha1StoreCount() -> int: + """返回全局 SHA1 存储器的条目数""" + return _sha1_store_count + + +def GetSha1StoreArrPtr() -> bytes | t.CPtr: + """返回 SHA1 数组指针(每个条目 17 字节)""" + return _sha1_store_arr + + +def GetSha1StoreModArrPtr() -> bytes | t.CPtr: + """返回模块名数组指针(每个条目 64 字节)""" + return _sha1_store_mod + + +def GetSha1StoreRelArrPtr() -> bytes | t.CPtr: + """返回相对路径数组指针(每个条目 MAX_REL_PATH_LEN 字节)""" + return _sha1_store_rel + + +# ============================================================ +# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用) +# +# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。 +# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*MAX_REL_PATH_LEN] +# 内存归全局存储器所有,调用者不要 free +# ============================================================ +def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int: + """获取全局存储器数组指针 + + out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针, + 函数将存储器数组指针写入这些位置。 + 返回条目数,-1 失败 + """ + if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: + return -1 + if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None: + return -1 + sp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_sha1_arr, t.CPtr)) + if sp is not None: + sp[0] = _sha1_store_arr + mp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_mod_arr, t.CPtr)) + if mp is not None: + mp[0] = _sha1_store_mod + rp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_rel_arr, t.CPtr)) + if rp is not None: + rp[0] = _sha1_store_rel + return _sha1_store_count + + # ============================================================ # _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define # @@ -499,6 +670,39 @@ def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int: return 0 +# ============================================================ +# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块 +# +# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.py +# 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer 子模块类型。 +# 当模块名包含 '.' 时,检查其顶层包名是否在 deps_buf 中。 +# +# 例如: +# deps_buf = "ast stdio string" +# mod_name = "ast.lexer" → 顶层包 "ast" 在 deps_buf 中 → 返回 1 +# mod_name = "ast.token" → 顶层包 "ast" 在 deps_buf 中 → 返回 1 +# mod_name = "foo.bar" → 顶层包 "foo" 不在 deps_buf 中 → 返回 0 +# ============================================================ +def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int: + """检查 mod_name 是否是 deps_buf 中已导入包的子模块""" + if deps_buf is None or mod_name is None: + return 0 + # 查找第一个 '.',提取顶层包名 + dot_pos: str = string.strchr(mod_name, 46) # '.' = 46 + if dot_pos is None: + return 0 + pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(mod_name)) + if pkg_len == 0 or pkg_len >= 256: + return 0 + # 构造顶层包名(截断到 '.' 之前) + # 直接在 mod_name 上临时截断检查,避免分配 + saved_ch: t.CChar = mod_name[pkg_len] + mod_name[pkg_len] = '\0' + result: int = HandlesImports.is_module_imported(deps_buf, mod_name) + mod_name[pkg_len] = saved_ch + return result + + # ============================================================ # _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque) # @@ -806,7 +1010,70 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, in_body = 1 continue - # 跳过其他行(global 带初值定义、type 定义等,stub 已有 external 声明) + # 提取类型定义(%... = type { ... } 或 %... = type < ... >) + # stub.ll 中只有 opaque 声明,需要从 text.ll 中提取完整定义 + # 用于 alloca 和 getelementptr 等需要完整类型的指令 + if ll > 0 and dep_buf[ls] == '%': + eq_td: t.CSizeT = ls + 1 + while eq_td < ls + ll: + if dep_buf[eq_td] == '=': + break + eq_td += 1 + if eq_td < ls + ll: + # 检查是否为完整定义(= type { 或 = type <) + dep_is_full_td: int = 0 + if eq_td + 8 <= ls + ll: + if dep_buf[eq_td + 2] == 't' and dep_buf[eq_td + 3] == 'y' \ + and dep_buf[eq_td + 4] == 'p' and dep_buf[eq_td + 5] == 'e' \ + and dep_buf[eq_td + 6] == ' ': + if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<': + dep_is_full_td = 1 + if dep_is_full_td == 1: + # 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀 + saved_eq_td: t.CChar = dep_buf[eq_td] + dep_buf[eq_td] = '\0' + # 1. 注释掉 out_buf 中的 opaque 声明(如果有) + _CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls) + # 2. 检查 out_buf 中是否已有完整定义(避免重复) + has_full_td: int = _HasFullTypeDefinition(out_buf, dep_buf + ls) + dep_buf[eq_td] = saved_eq_td + if has_full_td == 0: + # 追加完整类型定义 + if out_pos + ll + 2 < out_size: + string.strncpy(out_buf + out_pos, dep_buf + ls, ll) + out_pos += ll + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + continue + + # 提取全局变量定义(@... = ...) + # 依赖 text.ll 中的全局变量带初值定义(如 @_mbuddy = global ...) + # stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义 + # 注释掉 out_buf 中的同名行(external 声明或旧定义),追加 text.ll 中的完整定义 + if ll > 0 and dep_buf[ls] == '@': + eq_gv: t.CSizeT = ls + 1 + while eq_gv < ls + ll: + if dep_buf[eq_gv] == '=': + break + eq_gv += 1 + if eq_gv < ls + ll: + # 截断到 '=' 之前(不含 '='),得到全局变量名 @name + saved_gv: t.CChar = dep_buf[eq_gv] + dep_buf[eq_gv] = '\0' + # 注释掉 out_buf 中的同名行(external 声明或旧定义) + _CommentOutGlobalInBuf(out_buf, dep_buf + ls) + dep_buf[eq_gv] = saved_gv + # 追加完整定义 + if out_pos + ll + 2 < out_size: + string.strncpy(out_buf + out_pos, dep_buf + ls, ll) + out_pos += ll + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + continue + + # 跳过其他行 return out_pos @@ -826,13 +1093,14 @@ def _PathToModuleName(path: str) -> str: if path is None: return None plen: t.CSizeT = string.strlen(path) - # 跳过 includes/ 前缀 - prefix: str = "includes/" + # 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容) prefix_len: t.CSizeT = 9 path_start: t.CSizeT = 0 - if plen > prefix_len and string.strncmp(path, prefix, prefix_len) == 0: - path_start = prefix_len - plen = plen - prefix_len + if plen > prefix_len and string.strncmp(path, "includes", 8) == 0: + psep_ch: t.CChar = path[8] + if psep_ch == '/' or psep_ch == '\\': + path_start = prefix_len + plen = plen - prefix_len # 去掉 .py 后缀 if plen > 3 and path[path_start + plen - 3] == '.' and path[path_start + plen - 2] == 'p' and path[path_start + plen - 1] == 'y': plen -= 3 @@ -869,94 +1137,33 @@ def _PathToModuleName(path: str) -> str: # ============================================================ -# _BuildIncludesSha1Map - 从 _sha1_map.txt 构建 sha1 → module_name 映射 +# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射 # -# _sha1_map.txt 格式: {sha1}:includes/{rel_path}\n +# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。 # 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。 # 所有内存由 stdlib.malloc 分配,调用者负责释放。 # ============================================================ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT, sha1_list: t.CChar | t.CPtr, mod_list: t.CChar | t.CPtr) -> int: - """从 _sha1_map.txt 构建 sha1→module_name 映射,返回条目数""" - if temp_dir is None or sha1_list is None or mod_list is None: - return 0 + """从全局内存存储器复制 sha1→module_name 映射到调用者数组 - map_path: bytes = stdlib.malloc(td_len + 32) - if map_path is None: + temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。 + 数据来源是 PopulateSha1MapStore 填充的全局存储器。 + """ + if sha1_list is None or mod_list is None: return 0 - viperlib.snprintf(map_path, td_len + 32, "%s/_sha1_map.txt", temp_dir) - mf: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R) - if mf.closed: - stdlib.free(map_path) + if _sha1_store_arr is None or _sha1_store_mod is None: return 0 - map_buf: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) - if map_buf is None: - mf.close() - stdlib.free(map_path) - return 0 - map_br: t.CInt64T = mf.read_all(map_buf, STUB_READ_BUF_SIZE) - mf.close() - stdlib.free(map_path) - if map_br <= 0: - stdlib.free(map_buf) - return 0 - if map_br < STUB_READ_BUF_SIZE: - map_buf[map_br] = '\0' - else: - map_buf[STUB_READ_BUF_SIZE - 1] = '\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: - # 提取 SHA1(16 hex) - string.strncpy(sha1, map_buf + pos, 16) - sha1[16] = '\0' - pos += 16 - # 跳过 ':' - if pos < map_br and map_buf[pos] == ':': - pos += 1 - # 提取路径(到 \r、\n 或 \0) - # 注意: Windows CRLF 换行符是 \r\n,必须同时检查 \r 避免路径包含 \r - path_start: t.CSizeT = pos - 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 - # 跳过 \r\n 或 \n - if pos < map_br and map_buf[pos] == '\r': - pos += 1 - if pos < map_br and map_buf[pos] == '\n': - pos += 1 - if path_len > 0: - # 仅收集 includes/ 前缀的条目(用户文件如 App/ 不应进入 includes 映射) - # 否则用户文件会被误判为 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) - if path_buf is not None: - string.strncpy(path_buf, map_buf + path_start, path_len) - path_buf[path_len] = '\0' - mod_name: str = _PathToModuleName(path_buf) - 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) - idx2: t.CSizeT = t.CSizeT(count) * 64 - mn_len: t.CSizeT = string.strlen(mod_name) - 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' - stdlib.free(mod_name) - count += 1 - stdlib.free(sha1) - stdlib.free(map_buf) + for i in range(_sha1_store_count): + if count >= MAX_INCLUDES: + break + idx: t.CSizeT = t.CSizeT(i) * 17 + idx2: t.CSizeT = t.CSizeT(i) * 64 + string.strcpy(sha1_list + t.CSizeT(count) * 17, _sha1_store_arr + idx) + string.strcpy(mod_list + t.CSizeT(count) * 64, _sha1_store_mod + idx2) + count += 1 return count @@ -977,16 +1184,33 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT, # ============================================================ def _FindSha1ByModName(sha1_arr: t.CChar | t.CPtr, mod_arr: t.CChar | t.CPtr, count: int, mod_name: str) -> str: - """从模块名查找 SHA1(精确匹配)""" + """从模块名查找 SHA1(精确匹配 + 包名回退) + + 精确匹配失败时,尝试 "{mod_name}.__init__" 回退, + 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。 + """ if sha1_arr is None or mod_arr is None or mod_name is None: return None if count <= 0: return None + # 第一遍:精确匹配 for i in range(count): idx: t.CSizeT = t.CSizeT(i) * 64 if string.strcmp(mod_arr + idx, mod_name) == 0: sidx: t.CSizeT = t.CSizeT(i) * 17 return sha1_arr + sidx + # 第二遍:包名回退("ast" → "ast.__init__") + nm_len: t.CSizeT = string.strlen(mod_name) + init_buf: bytes = stdlib.malloc(nm_len + 12) + if init_buf is not None: + viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name) + for i in range(count): + idx2: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(mod_arr + idx2, init_buf) == 0: + sidx2: t.CSizeT = t.CSizeT(i) * 17 + stdlib.free(init_buf) + return sha1_arr + sidx2 + stdlib.free(init_buf) return None @@ -1027,13 +1251,13 @@ def _AddSha1ToSet(set_buf: t.CChar | t.CPtr, count: int, sha1: str) -> int: # # 算法(工作列表): # 1. 扫描 source_dir 下的 .py 文件,解析 AST 获取 _imported_modules -# 2. 构建 SHA1→module_name 映射(从 _sha1_map.txt) +# 2. 构建 SHA1→module_name 映射(从全局内存存储器) # 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名 # # Args: # mb: 内存池 # source_dir: 源文件目录(Config.SourceDir) -# temp_dir: 临时目录(_sha1_map.txt 和 .deps.txt 所在位置) +# temp_dir: 临时目录(.deps.txt 所在位置;_sha1_map.txt 仅人类可读输出,不读取) # reachable_set: 输出参数,可达 SHA1 集合缓冲区(MAX_INCLUDES_SHA1 * 17 字节) # # Returns: @@ -1060,11 +1284,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, return -1 map_count: int = _BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr) if map_count <= 0: - stdio.printf("[Reachable] 无法构建 SHA1 映射\n") + VLogger.error("无法构建 SHA1 映射", "Reachable") stdlib.free(sha1_arr) stdlib.free(mod_arr) return -1 - stdio.printf("[Reachable] SHA1 映射: %d 个\n", map_count) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count) + VLogger.debug(fb, "Reachable") # 2. 扫描 source_dir 下的 .py 文件,收集直接依赖 dir_len: t.CSizeT = string.strlen(source_dir) @@ -1099,7 +1326,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) if handle == win32base.INVALID_HANDLE_VALUE: - stdio.printf("[Reachable] 未找到 .py 文件: %s\n", pattern) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "未找到 .py 文件: %s", pattern) + VLogger.warning(fb, "Reachable") stdlib.free(pattern) stdlib.free(find_data) stdlib.free(sha1_arr) @@ -1176,7 +1406,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, worklist[wl_len - 1] = '\0' wl_len -= 1 - stdio.printf("[Reachable] 源文件直接依赖: '%s'\n", worklist) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "源文件直接依赖: '%s'", worklist) + VLogger.debug(fb, "Reachable") # 2.5. 全局预加入基础容器模块(_list/_dict/json) # @@ -1223,7 +1456,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, worklist[wl_len] = ' ' wl_len += 1 worklist[wl_len] = '\0' - stdio.printf("[Reachable] 预加入容器模块后: '%s'\n", worklist) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist) + VLogger.debug(fb, "Reachable") # 3. 工作列表算法:递归收集可达 SHA1 processed: bytes = stdlib.malloc(8192) @@ -1261,13 +1497,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, if HandlesImports.is_module_imported(processed, mod_buf) != 0: already = 1 if already == 0: - # 加入 processed - if proc_len + name_len + 1 < 8192: - string.strcpy(processed + proc_len, mod_buf) - proc_len += name_len - processed[proc_len] = ' ' - proc_len += 1 - processed[proc_len] = '\0' + # 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug) + if processed is not None: + if proc_len + name_len + 1 < 8192: + string.strcpy(processed + proc_len, mod_buf) + proc_len = proc_len + name_len + processed[proc_len] = ' ' + proc_len = proc_len + 1 + processed[proc_len] = '\0' # 查找 SHA1 found_sha1: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf) @@ -1289,17 +1526,18 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, deps_buf[dbr] = '\0' else: deps_buf[2047] = '\0' - # 追加到 worklist(确保前面有空格分隔符,避免模块名合并) + # 追加到 worklist(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug) dl: t.CSizeT = string.strlen(deps_buf) - if dl > 0 and wl_len + dl + 2 < 8192: - if wl_len > 0 and worklist[wl_len - 1] != ' ': + if worklist is not None: + if dl > 0 and wl_len + dl + 2 < 8192: + if wl_len > 0 and worklist[wl_len - 1] != ' ': + worklist[wl_len] = ' ' + wl_len = wl_len + 1 + string.strcpy(worklist + wl_len, deps_buf) + wl_len = wl_len + dl worklist[wl_len] = ' ' - wl_len += 1 - string.strcpy(worklist + wl_len, deps_buf) - wl_len += dl - worklist[wl_len] = ' ' - wl_len += 1 - worklist[wl_len] = '\0' + wl_len = wl_len + 1 + worklist[wl_len] = '\0' stdlib.free(deps_buf) else: df.close() @@ -1310,15 +1548,19 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, stdlib.free(worklist) stdlib.free(processed) - stdio.printf("[Reachable] 可达 SHA1: %d 个\n", reachable_count) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "可达 SHA1: %d 个", reachable_count) + VLogger.debug(fb, "Reachable") return reachable_count # ============================================================ # BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR # -# 按需加载:读取 deps.txt 获取导入模块名,通过 _sha1_map.txt 查找 SHA1, +# 按需加载:读取 deps.txt 获取导入模块名,通过内存存储器查找 SHA1, # 仅加载实际使用的依赖 stub(而非全部扫描)。 +# _sha1_map.txt 仅作为人类可读输出,程序内部不读取。 # ============================================================ def BuildCombinedIR(temp_dir: str, local_sha1: str, out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT: @@ -1387,7 +1629,7 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, # 2. 按需加载依赖 stubs(根据 deps.txt 过滤,而非扫描全部) # 先加载依赖 stub(含 type 定义),确保 type 定义在本地 text.ll 的 define 块之前 - # 2a. 构建 includes SHA1 → module_name 映射(从 _sha1_map.txt) + # 2a. 构建 includes SHA1 → module_name 映射(从全局内存存储器) sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17) mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64) inc_count: int = 0 @@ -1436,8 +1678,17 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, is_include = 1 if deps_loaded != 0: idx_mi: t.CSizeT = t.CSizeT(ii) * 64 - if HandlesImports.is_module_imported(deps_buf, mod_arr + idx_mi) != 0: + dep_mod_name: str = mod_arr + idx_mi + if HandlesImports.is_module_imported(deps_buf, dep_mod_name) != 0: should_load = 1 + else: + # 传递性依赖:deps.txt 只记录直接导入(如 ast), + # 但包的 __init__.py 内部 from .lexer import Lexer + # 会让 IR 引用 ast.lexer 子模块类型。 + # 检查 dep_mod_name 是否是已导入包的子模块 + # (如 ast.lexer 是 ast 的子模块) + if _is_submodule_of_imported(deps_buf, dep_mod_name) != 0: + should_load = 1 break if is_include == 0: # 非 includes stub(用户文件),总是加载 @@ -1492,7 +1743,10 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, if chk_path is not None: chk_f: fileio.File | t.CPtr = fileio.File(chk_path, fileio.MODE.R) if chk_f.closed: - stdio.printf("[FATAL][BuildCombinedIR] 依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译\n", mod_nm, found_s, chk_path) + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译", mod_nm, found_s, chk_path) + VLogger.error(fb, "BuildCombinedIR") sys.exit(1) chk_f.close() stdlib.free(chk_path) diff --git a/App/lib/core/VLogger.py b/App/lib/core/VLogger.py index c1e5e99..916a141 100644 --- a/App/lib/core/VLogger.py +++ b/App/lib/core/VLogger.py @@ -1,14 +1,29 @@ import t, c from stdint import * import stdio +import stdlib import memhub +import sys import w32.win32console as w32cmd import w32.win32file as w32file -import w32.win32base as w32base +import w32.win32base as win32base # ============================================================ # VLogger - 原生日志系统(Windows 控制台彩色输出) +# +# 输出格式与 TPC (lib/core/VLogger.py) 对齐: +# ├ INFO: message +# ├ INFO[category]: message +# ├ WARN: message +# ├ ERROR: message +# ├ SUCCESS: message +# ├ DEBUG: message +# ╠ CRITICAL: message (红底白字,致命错误) +# ╠ CRITICAL[category]: message +# +# 分段着色:前缀符号(├/╠) 用级别色,LEVEL 标签用亮色,消息用默认色 +# CRITICAL 级别使用 ╠ 前缀和红底白字标签,与 TPC critical 对齐 # ============================================================ # 日志级别 @@ -18,6 +33,7 @@ class LogLevel(t.CEnum): WARNING = 2 ERROR = 3 SUCCESS = 4 + CRITICAL = 5 # Win32 控制台前景色属性 @@ -60,42 +76,72 @@ class Logger: return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE) return 0 - def _log(self, level: int, prefix: str, msg: str, color: WORD, + # ============================================================ + # _log - 核心日志输出(分段着色) + # + # 格式: ├ LEVEL: msg 或 ├ LEVEL[category]: msg + # 分三段着色: + # 1. 前缀符号 ├ (sym_color) + # 2. LEVEL 标签 (level_color) + # 3. 消息内容 (默认白色) + # ============================================================ + def _log(self, level: int, prefix_sym: str, level_str: str, msg: str, + sym_color: WORD, level_color: WORD, category: str = "") -> int: if level < self._level: return 0 - self._set_color(color) + # 前缀符号(如 ├) + self._set_color(sym_color) + stdio.printf("%s ", prefix_sym) + # LEVEL 标签(亮色) + self._set_color(level_color) if category is not None: if category[0] != 0: - stdio.printf("%s[%s] %s\n", prefix, category, msg) + stdio.printf("%s[%s]: ", level_str, category) else: - stdio.printf("%s %s\n", prefix, msg) + stdio.printf("%s: ", level_str) else: - stdio.printf("%s %s\n", prefix, msg) + stdio.printf("%s: ", level_str) + # 消息内容(默认色) self._reset_color() + stdio.printf("%s\n", msg) return 0 def debug(self, msg: str, category: str = "") -> int: - return self._log(LogLevel.DEBUG, "[DEBUG]", msg, - FOREGROUND_INTENSITY, category) + return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, + FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category) def info(self, msg: str, category: str = "") -> int: - return self._log(LogLevel.INFO, "[INFO]", msg, + return self._log(LogLevel.INFO, "├", "INFO", msg, + FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category) def warning(self, msg: str, category: str = "") -> int: - return self._log(LogLevel.WARNING, "[WARN]", msg, + return self._log(LogLevel.WARNING, "├", "WARN", msg, + FOREGROUND_YELLOW, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, category) def error(self, msg: str, category: str = "") -> int: - return self._log(LogLevel.ERROR, "[ERROR]", msg, - FOREGROUND_RED | FOREGROUND_INTENSITY, category) + ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, + FOREGROUND_RED, + FOREGROUND_RED | FOREGROUND_INTENSITY, category) + # 错误立即终止进程(避免错误后继续执行导致连锁崩溃) + sys.exit(1) + return ret def success(self, msg: str, category: str = "") -> int: - return self._log(LogLevel.SUCCESS, "[SUCCESS]", msg, + return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, + FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category) + def critical(self, msg: str, category: str = "") -> int: + # CRITICAL 使用 ╠ 前缀(红底白字标签,对齐 TPC critical 风格) + return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, + FOREGROUND_RED, + BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY, + category) + def set_level(self, level: int) -> int: self._level = level return 0 @@ -105,8 +151,8 @@ class Logger: # ============================================================ def banner(self, msg: str) -> int: - """输出分节标题(青色高亮)。""" - self._set_color(FOREGROUND_CYAN | FOREGROUND_INTENSITY) + """输出分节标题(蓝色高亮,与 TPC banner 风格对齐)。""" + self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY) stdio.printf("\n=== %s ===\n\n", msg) self._reset_color() return 0 @@ -148,9 +194,28 @@ _mbuddy: memhub.MemManager | t.CPtr # 全局 logger 指针 _g_logger: Logger | t.CPtr +# 全局格式化缓冲区(用于带变量的日志消息,1024 字节) +_fmt_buf: t.CChar | t.CPtr + + +def fmt_buf() -> t.CChar | t.CPtr: + """获取全局格式化缓冲区(1024 字节)。 + + 用法: + buf: t.CChar | t.CPtr = VLogger.fmt_buf() + if buf is not None: + viperlib.snprintf(buf, 1024, "翻译: %s (sha1=%s)", rp, sha1) + log.info(buf, "Phase1") + """ + global _fmt_buf + if _fmt_buf is None: + _fmt_buf = stdlib.malloc(1024) + return _fmt_buf + def get_logger() -> Logger | t.CPtr: """获取全局 logger 实例。若不存在则通过 _mbuddy 分配并初始化。""" + global _g_logger if _g_logger is None: if _mbuddy is None: return None @@ -167,5 +232,63 @@ def get_logger() -> Logger | t.CPtr: def set_logger(logger: Logger | t.CPtr) -> int: """设置全局 logger 实例。""" + global _g_logger _g_logger = logger return 0 + + +# ============================================================ +# 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐) +# +# 调用方式: VLogger.info("msg", "category") +# 无需先 get_logger(),方便各模块直接使用 +# ============================================================ + +def info(msg: str, category: str = "") -> int: + """输出 INFO 级别日志。""" + log: Logger | t.CPtr = get_logger() + if log is not None: + return log.info(msg, category) + return 0 + + +def warning(msg: str, category: str = "") -> int: + """输出 WARN 级别日志。""" + log: Logger | t.CPtr = get_logger() + if log is not None: + return log.warning(msg, category) + return 0 + + +def error(msg: str, category: str = "") -> int: + """输出 ERROR 级别日志并立即终止进程。""" + log: Logger | t.CPtr = get_logger() + if log is not None: + return log.error(msg, category) + # logger 不可用时也直接退出 + sys.exit(1) + return 0 + + +def success(msg: str, category: str = "") -> int: + """输出 SUCCESS 级别日志。""" + log: Logger | t.CPtr = get_logger() + if log is not None: + return log.success(msg, category) + return 0 + + +def critical(msg: str, category: str = "") -> int: + """输出 CRITICAL 级别日志(致命错误,红底白字)。""" + log: Logger | t.CPtr = get_logger() + if log is not None: + return log.critical(msg, category) + return 0 + + +def debug(msg: str, category: str = "") -> int: + """输出 DEBUG 级别日志。""" + log: Logger | t.CPtr = get_logger() + if log is not None: + return log.debug(msg, category) + return 0 diff --git a/App/main.py b/App/main.py index fbc4b27..672b47c 100644 --- a/App/main.py +++ b/App/main.py @@ -16,6 +16,7 @@ import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HandlesTranslator import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesExprCall as HandlesExprCall +import lib.core.Handles.HandlesType as HandlesType import lib.core.BuildPipeline as BuildPipeline import lib.core.IncludesScanner as IncludesScanner import lib.core.StubMerger as StubMerger @@ -31,8 +32,9 @@ import hashlib # 内存大小: fl_bytes=264 (33*8), POOL_SIZE-fl_bytes 必须是 2 的幂以避免浪费 # 1073742088 - 264 = 1073741824 (1024MB usable = 2^30) +# 注意: POOL_SIZE 必须是 2^30+264=1073742088,否则 _largest_pow2_le 会取 2^29=512MB # Phase1 翻译 87 个 includes 文件 + Phase2 翻译 30 个测试文件,512MB 不足导致 Phase2 崩溃 -POOL_SIZE: t.CDefine = 1073741824 +POOL_SIZE: t.CDefine = 1073742088 # 语言编码页 CODE_PAGE: t.CDefine = 65001 # 源代码缓冲区大小(1MB) @@ -47,18 +49,33 @@ SRC_BUF_SIZE: t.CDefine = 1048576 def main() -> int: w32cmd.SetConsoleOutputCP(CODE_PAGE) w32cmd.SetConsoleCP(CODE_PAGE) + # 用 fflush(0) 刷新所有流,确保崩溃前输出可见(0=NULL 刷新所有输出流) + stdio.printf("[DBG] main enter\n") + stdio.fflush(0) # 初始化 mbuddy 内存池 + stdio.printf("[DBG] before malloc\n") + stdio.fflush(0) arena: bytes = stdlib.malloc(POOL_SIZE) + stdio.printf("[DBG] after malloc arena=%p\n", arena) + stdio.fflush(0) if arena is None: stdio.printf("FAIL: malloc for arena failed\n") return 1 + stdio.printf("[DBG] before MemBuddy\n") + stdio.fflush(0) mb: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(arena, POOL_SIZE) + stdio.printf("[DBG] after MemBuddy mb=%p\n", mb) + stdio.fflush(0) if mb is None: stdio.printf("FAIL: MemBuddy init failed\n") return 1 # 设置全局 mbuddy 指针(sys 和 argparse 都需要) + stdio.printf("[DBG] before _mbuddy setup\n") + stdio.fflush(0) sys._mbuddy = mb + stdio.printf("[DBG] sys._mbuddy ok\n") + stdio.fflush(0) argparse._mbuddy = mb ast._mbuddy = mb lib._mbuddy = (memhub.MemManager | t.CPtr)(mb) @@ -72,44 +89,87 @@ def main() -> int: Phase2._mbuddy = (memhub.MemManager | t.CPtr)(mb) subprocess._mbuddy = mb hashlib._mbuddy = (memhub.MemManager | t.CPtr)(mb) + VLogger._mbuddy = (memhub.MemManager | t.CPtr)(mb) + stdio.printf("[DBG] before InitLib\n") + stdio.fflush(0) lib.InitLib((memhub.MemManager | t.CPtr)(mb)) + stdio.printf("[DBG] after InitLib\n") + stdio.fflush(0) # 初始化 VLogger 并打印启动日志 log: VLogger.Logger | t.CPtr = VLogger.get_logger() + stdio.printf("[DBG] after get_logger log=%p\n", log) + stdio.fflush(0) if log is not None: log.info("TransPyV 启动") + stdio.printf("[DBG] after log.info\n") + stdio.fflush(0) # 初始化命令行参数(Windows: GetCommandLineA, POSIX: /proc/self/cmdline) + stdio.printf("[DBG] before _init_argv\n") + stdio.fflush(0) sys._init_argv() + stdio.printf("[DBG] after _init_argv\n") + stdio.fflush(0) # 创建参数解析器 + stdio.printf("[DBG] before ArgumentParser\n") + stdio.fflush(0) parser: argparse.ArgumentParser | t.CPtr = argparse.ArgumentParser( "TransPyV", "TransPyV 命令行参数解析", pool=mb) + stdio.printf("[DBG] after ArgumentParser parser=%p\n", parser) + stdio.fflush(0) # 注册参数(与 Projectrans.py main() 一致) + stdio.printf("[DBG] before add_argument --project\n") + stdio.fflush(0) parser.add_argument("--project", None, argparse.STRING, 0, None, False, argparse.STORE, "project.json 路径(默认查找当前目录)") + stdio.printf("[DBG] after add_argument --project\n") + stdio.fflush(0) parser.add_argument("--src", None, argparse.STRING, 0, None, False, argparse.STORE, "源文件目录(覆盖 project.json)") + stdio.printf("[DBG] after --src\n") + stdio.fflush(0) parser.add_argument("--temp", None, argparse.STRING, 0, None, False, argparse.STORE, "声明接口临时目录(覆盖 project.json)") + stdio.printf("[DBG] after --temp\n") + stdio.fflush(0) parser.add_argument("--output", None, argparse.STRING, 0, None, False, argparse.STORE, "输出目录(覆盖 project.json)") + stdio.printf("[DBG] after --output\n") + stdio.fflush(0) parser.add_argument("--phase", None, argparse.STRING, 0, None, False, argparse.STORE, "阶段: 1=生成声明, 2=翻译+编译, all=全部") + stdio.printf("[DBG] after --phase\n") + stdio.fflush(0) parser.add_argument("--cc", None, argparse.STRING, 0, None, False, argparse.STORE, "LLVM 编译器命令(覆盖 project.json)") + stdio.printf("[DBG] after --cc\n") + stdio.fflush(0) parser.add_argument("--clean", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "清理 output 和 temp 目录") + stdio.printf("[DBG] after --clean\n") + stdio.fflush(0) parser.add_argument("--run", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "编译成功后立即执行生成的可执行文件") + stdio.printf("[DBG] after --run\n") + stdio.fflush(0) parser.add_argument("--rebuild-includes", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "删除 includes.binary 预编译缓存并重新编译所有 includes") + stdio.printf("[DBG] after --rebuild-includes\n") + stdio.fflush(0) parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "清除 .transpyc_cache 全局缓存") + stdio.printf("[DBG] after --clear-cache\n") + stdio.fflush(0) # 解析命令行参数 + stdio.printf("[DBG] before parse_args\n") + stdio.fflush(0) args: argparse.ParsedArgs | t.CPtr = parser.parse_args(sys._argc, sys._argv) + stdio.printf("[DBG] after parse_args args=%p\n", args) + stdio.fflush(0) if args is None: if log is not None: log.error("参数解析失败", "argparse") @@ -184,12 +244,21 @@ def main() -> int: proj_loaded: int = 0 proj_path: str = project if proj_path is not None: + stdio.printf("[DBG] before Load_project_config path=%s\n", proj_path) + stdio.fflush(0) if log is not None: log.banner("工程配置") if Config.Load_project_config(proj_path) == 0: + stdio.printf("[DBG] Load_project_config OK\n") + stdio.fflush(0) proj_loaded = 1 else: - stdio.printf(" 警告: 无法加载 project.vpj: %s\n", proj_path) + if log is not None: + warn_buf: str = stdlib.malloc(256) + if warn_buf is not None: + viperlib.snprintf(warn_buf, 256, "无法加载 project.vpj: %s", proj_path) + log.warning(warn_buf, "config") + stdlib.free(warn_buf) else: # 尝试默认路径 project.vpj(当前目录和上级目录) proj_path = "project.vpj" @@ -215,18 +284,33 @@ def main() -> int: if proj_dir is not None: string.memcpy(proj_dir, proj_path, slash_pos) proj_dir[slash_pos] = '\0' + stdio.printf("[DBG] before resolve_paths dir=%s\n", proj_dir) + stdio.fflush(0) Config.resolve_paths(proj_dir) + stdio.printf("[DBG] resolve_paths OK\n") + stdio.fflush(0) Config.print_config() + stdio.printf("[DBG] print_config OK\n") + stdio.fflush(0) # === --clean: 清理 temp 和 output 目录 === if _gb_clean: - stdio.printf("\n[clean] 清理临时目录...\n") + if log is not None: + log.info("清理临时目录...", "clean") if Config.TempDir is not None: n: int = Utils.CleanDir(Config.TempDir) - stdio.printf("[clean] %s: 删除 %d 个文件\n", Config.TempDir, n) + if log is not None: + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "%s: 删除 %d 个文件", Config.TempDir, n) + log.info(fb, "clean") if Config.OutputDir is not None: n2: int = Utils.CleanDir(Config.OutputDir) - stdio.printf("[clean] %s: 删除 %d 个文件\n", Config.OutputDir, n2) + if log is not None: + fb2: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb2 is not None: + viperlib.snprintf(fb2, 1024, "%s: 删除 %d 个文件", Config.OutputDir, n2) + log.info(fb2, "clean") # === --phase 参数控制 === # phase=1: 仅 stub 分离(生成 .stub.ll/.text.ll),不编译 @@ -241,7 +325,11 @@ def main() -> int: do_phase1 = 1 if phase_mode == "2" or phase_mode == "all": do_phase2 = 1 - stdio.printf("[phase] 模式: %s (phase1=%d phase2=%d)\n", phase_mode, do_phase1, do_phase2) + if log is not None: + fb3: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb3 is not None: + viperlib.snprintf(fb3, 1024, "模式: %s (phase1=%d phase2=%d)", phase_mode, do_phase1, do_phase2) + log.info(fb3, "phase") # === Phase1: 扫描 includes(按需翻译)=== # 对 includes 目录中每个 .py 文件,若 stub 不存在则翻译并分离 stub @@ -249,10 +337,15 @@ def main() -> int: ph1_temp: str = Config.TempDir if ph1_temp is None: ph1_temp = "." + stdio.printf("[DBG] before Phase1.RunPhase1 inc=%s temp=%s\n", Config.IncludesDir, ph1_temp) + stdio.fflush(0) Phase1.RunPhase1(mb, Config.IncludesDir, ph1_temp, log) + stdio.printf("[DBG] Phase1.RunPhase1 OK\n") + stdio.fflush(0) # 如果仅 Phase1(不执行 Phase2),直接退出 if do_phase2 == 0: - stdio.printf("[phase] Phase1 完成,退出\n") + if log is not None: + log.info("Phase1 完成,退出", "phase") return 0 # AST 解析:如果指定了 --src 或 --project,读取文件并解析为 AST 树 @@ -270,10 +363,14 @@ def main() -> int: mf_linker_out: str = Config.LinkerOutput if Config.LinkerOutput is not None else "app.exe" mf_includes_bin: str = Config.get_includes_binary_dir() + stdio.printf("[DBG] before Phase2.RunMultiFileProject src=%s temp=%s out=%s\n", Config.SourceDir, mf_temp, mf_output) + stdio.fflush(0) mf_ret: int = Phase2.RunMultiFileProject( mb, Config.SourceDir, mf_temp, mf_output, mf_cc, mf_cc_flags, mf_linker, mf_linker_flags, mf_linker_out, mf_includes_bin, Config.IncludesDir, do_phase1, do_phase2, log, args) + stdio.printf("[DBG] Phase2.RunMultiFileProject OK ret=%d\n", mf_ret) + stdio.fflush(0) argparse.release(args) if log is not None: log.success("TransPyV 完成") @@ -288,7 +385,11 @@ def main() -> int: if path_buf is not None: viperlib.snprintf(path_buf, sd_len + 16, "%s/main.py", Config.SourceDir) src_path = path_buf - stdio.printf("[project] 入口文件: %s\n", src_path) + if log is not None: + fb4: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb4 is not None: + viperlib.snprintf(fb4, 1024, "入口文件: %s", src_path) + log.info(fb4, "project") if src_path is not None: if log is not None: log.banner("AST 解析") @@ -296,8 +397,10 @@ def main() -> int: f: fileio.File | t.CPtr = fileio.File(src_path, fileio.MODE.R) if f.closed: if log is not None: - log.error("无法打开文件", "fileio") - stdio.printf(" 路径: %s\n", src_path) + fb5: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb5 is not None: + viperlib.snprintf(fb5, 1024, "无法打开文件: %s", src_path) + log.error(fb5, "fileio") argparse.release(args) return 1 # 分配源代码缓冲区 @@ -313,8 +416,10 @@ def main() -> int: f.close() if bytes_read < 0: if log is not None: - log.error("读取文件失败", "fileio") - stdio.printf(" 错误码: %d\n", bytes_read) + fb6: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb6 is not None: + viperlib.snprintf(fb6, 1024, "读取文件失败 (错误码: %d)", bytes_read) + log.error(fb6, "fileio") argparse.release(args) return 1 # 添加 null 终止符,确保 strlen 和 SHA1 计算正确 @@ -324,7 +429,10 @@ def main() -> int: src_buf[SRC_BUF_SIZE - 1] = 0 if log is not None: log.info("文件读取完成", "fileio") - stdio.printf("读取 %d 字节\n", bytes_read) + fb7: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb7 is not None: + viperlib.snprintf(fb7, 1024, "读取 %d 字节", bytes_read) + log.info(fb7, "fileio") # 解析 AST(拆分为词法+语法两阶段,便于调试) ast._init_tables(mb) lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) @@ -396,6 +504,8 @@ def main() -> int: # 使用 project.vpj 中的配置 temp_dir: str = Config.TempDir if Config.TempDir is not None else "." output_dir: str = Config.OutputDir if Config.OutputDir is not None else "." + # 设置全局 temp_dir(供跨模块 CDefine 查找使用) + HandlesType.set_temp_dir(temp_dir) cc_cmd: str = Config.CompilerCmd if Config.CompilerCmd is not None else "llc" cc_flags: str = "-filetype=obj -relocation-model=pic" linker_cmd: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++" @@ -468,7 +578,8 @@ def main() -> int: final_ir = combined_buf final_ir_len = combined_len else: - stdio.printf("[stub] BuildCombinedIR 失败,使用原始 IR\n") + if log is not None: + log.warning("BuildCombinedIR 失败,使用原始 IR", "stub") stdlib.free(combined_buf) br: BuildPipeline.BuildResult | t.CPtr = BuildPipeline.run_pipeline( @@ -479,21 +590,35 @@ def main() -> int: if br is not None and br.Success == 1: if log is not None: log.success("编译管线完成") - stdio.printf("输出: %s/%s\n", output_dir, linker_output) + fb8: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb8 is not None: + viperlib.snprintf(fb8, 1024, "输出: %s/%s", output_dir, linker_output) + log.info(fb8) # --run 模式:执行生成的可执行文件 if args.get_bool("run"): exe_path: bytes = stdlib.malloc(string.strlen(output_dir) + string.strlen(linker_output) + 2) if exe_path is not None: viperlib.snprintf(exe_path, 256, "%s/%s", output_dir, linker_output) - stdio.printf("[run] 执行: %s\n", exe_path) + if log is not None: + fb9: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb9 is not None: + viperlib.snprintf(fb9, 1024, "执行: %s", exe_path) + log.info(fb9, "run") r: subprocess.CompletedProcess | t.CPtr = subprocess.run(exe_path, False, False) if r is not None: - stdio.printf("[run] 退出码: %d\n", r.returncode) + if log is not None: + fb10: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb10 is not None: + viperlib.snprintf(fb10, 1024, "退出码: %d", r.returncode) + log.info(fb10, "run") else: if log is not None: log.error("编译管线失败", "pipeline") - if br is not None and br.ErrorMsg is not None: - stdio.printf("[FATAL] 编译管线失败: %s\n", br.ErrorMsg) + if br is not None and br.ErrorMsg is not None: + fb11: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb11 is not None: + viperlib.snprintf(fb11, 1024, "编译管线失败: %s", br.ErrorMsg) + log.error(fb11, "FATAL") sys.exit(1) else: if log is not None: diff --git a/Test/NegativeTest/project.vpj b/Test/NegativeTest/project.vpj index 78326f3..f701fd1 100644 --- a/Test/NegativeTest/project.vpj +++ b/Test/NegativeTest/project.vpj @@ -22,7 +22,7 @@ }, "options": { "slice_level": 3, - "sha1_slice_level": 3, + "sha1_slice_level": 1, "target": "llvm", "strict_mode": true } diff --git a/Test/Sha1Test/project.vpj b/Test/Sha1Test/project.vpj index abe83ce..db2764f 100644 --- a/Test/Sha1Test/project.vpj +++ b/Test/Sha1Test/project.vpj @@ -21,7 +21,7 @@ }, "options": { "slice_level": 3, - "sha1_slice_level": 3, + "sha1_slice_level": 1, "target": "llvm", "strict_mode": true } diff --git a/Test/project.vpj b/Test/project.vpj index 7dfbe2c..a3ba696 100644 --- a/Test/project.vpj +++ b/Test/project.vpj @@ -22,7 +22,7 @@ }, "options": { "slice_level": 3, - "sha1_slice_level": 3, + "sha1_slice_level": 1, "target": "llvm", "strict_mode": true } diff --git a/_build.log b/_build.log deleted file mode 100644 index b5b7fc3..0000000 --- a/_build.log +++ /dev/null @@ -1 +0,0 @@ -├ INFO: [剥离] 调试段已移除 (llvm-strip) diff --git a/build.log b/build.log deleted file mode 100644 index 44e75d2856031ec4f926881e98fd87a0ee8e93e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38111 zcmeHQTaO&Y8O<{i|Dhj2Fq^(q-}WIA7*H$)uwo1kyVj_>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 diff --git a/project.json b/project.json index fe2a67f..f923695 100644 --- a/project.json +++ b/project.json @@ -3,7 +3,8 @@ "name": "TransPyV", "version": "1.0.0", "source_dir": "./App", - "build_dir": "./.tpv_build", + "temp_dir": "./temp", + "output_dir": "./output", "compiler": { "cmd": "llc", "flags": ["-filetype=obj", "-relocation-model=pic"] @@ -22,7 +23,7 @@ }, "options": { "slice_level": 3, - "sha1_slice_level": 3, + "sha1_slice_level": 1, "target": "llvm", "strict_mode": true } diff --git a/stderr.log b/stderr.log new file mode 100644 index 0000000..e69de29 diff --git a/stderr2.log b/stderr2.log new file mode 100644 index 0000000..e69de29 diff --git a/stdout.log b/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/stdout2.log b/stdout2.log new file mode 100644 index 0000000..e69de29 diff --git a/test_crash_dbg.log b/test_crash_dbg.log new file mode 100644 index 0000000..2674880 --- /dev/null +++ b/test_crash_dbg.log @@ -0,0 +1,483 @@ +[DBG] main enter +├ INFO: TransPyV 启动 + +=== TransPyV 参数解析结果 === + + --project: TransPyV/Test/project.vpj + --src: (未指定) + --temp: (未指定) + --output: (未指定) + --phase: (未指定,默认 all) + --cc: (未指定) + --clean: True + --run: False + --rebuild-includes: False + --clear-cache: False + +参数解析完成。 +[DBG] before Load_project_config path=TransPyV/Test/project.vpj + +=== 工程配置 === + +[DBG] Load_project_config OK +[DBG] before resolve_paths dir=TransPyV/Test +[DBG] resolve_paths OK +├ INFO[config]: project: AstTest v1.0.0 +├ INFO[config]: source_dir: TransPyV/Test/App +├ INFO[config]: build_dir: TransPyV/Test/.tpv_build +├ INFO[config]: temp_dir: TransPyV/Test/.tpv_build/temp +├ INFO[config]: output_dir: TransPyV/Test/.tpv_build/output +├ INFO[config]: compiler: llc +├ INFO[config]: linker: clang++ -> app.exe +├ INFO[config]: includes: TransPyV/Test/../../includes +├ INFO[config]: sha1_slice_level: 1 +[DBG] print_config OK +├ INFO[clean]: 清理临时目录... +├ INFO[clean]: TransPyV/Test/.tpv_build/temp: 删除 1 个文件 +├ INFO[clean]: TransPyV/Test/.tpv_build/output: 删除 0 个文件 +├ INFO[phase]: 模式: all (phase1=1 phase2=1) +[DBG] before Phase1.RunPhase1 inc=TransPyV/Test/../../includes temp=TransPyV/Test/.tpv_build/temp + +=== Phase1: 扫描 includes(按需翻译) === + +├ INFO[Phase1]: 拓扑排序: 25 个类, 15 个文件按依赖顺序处理 +├ INFO[Phase1]: 完成: 翻译=0 跳过=78 失败=0 +[DBG] Phase1.RunPhase1 OK +[DBG] before Phase2.RunMultiFileProject src=TransPyV/Test/App temp=TransPyV/Test/.tpv_build/temp out=TransPyV/Test/.tpv_build/output +[DBG] RunMultiFileProject enter src=TransPyV/Test/App +├ INFO[project]: 多文件项目编译: TransPyV/Test/App +├ INFO[project]: asm_test.py (sha1=80241c7d6f357bd1) +├ INFO[project]: attr_test.py (sha1=17c0dfbcebfa3a0e) +├ INFO[project]: augassign_test.py (sha1=0eccc2393466d940) +├ INFO[project]: circ_a.py (sha1=ac47a9dc68e6834c) +├ INFO[project]: circ_b.py (sha1=04d33c721a603632) +├ INFO[project]: circ_test.py (sha1=5314f09055045035) +├ INFO[project]: closure_test.py (sha1=9d8e1c9631c28b1b) +├ INFO[project]: deco_test.py (sha1=b7bbe6a1c1e75ad8) +├ INFO[project]: deref_min_test.py (sha1=6f5e876273093314) +├ INFO[project]: deref_test.py (sha1=eb252050d92f8fb9) +├ INFO[project]: eq_test.py (sha1=b8cbe044a10adcca) +├ INFO[project]: float_test.py (sha1=e74870eba3e6d774) +├ INFO[project]: flow_test.py (sha1=969c589035910845) +├ INFO[project]: for_test.py (sha1=1eb1c9fc7e85d9f8) +├ INFO[project]: func_test.py (sha1=3c2fbc9e35ad3bf2) +├ INFO[project]: func_vtable_test.py (sha1=a9df037d9178888f) +├ INFO[project]: inherit_test.py (sha1=97043f0d52da437f) +├ INFO[project]: llvmir_test.py (sha1=c39f3e540adb7b4e) +├ INFO[project]: namespace_defs.py (sha1=b7eeff93aa63e70f) +├ INFO[project]: namespace_test.py (sha1=ef45adce81bf5b2d) +├ INFO[project]: new_test.py (sha1=9d798d964b9d3411) +├ INFO[project]: oop_test.py (sha1=1b9ab264a942c56b) +├ INFO[project]: opovl_test.py (sha1=a7cf0ce4066332ae) +├ INFO[project]: ptr_only_test.py (sha1=ed1c124f3cb787c9) +├ INFO[project]: ptr_test.py (sha1=07f7da85af876124) +├ INFO[project]: simple_test.py (sha1=b078e9c9c74a2a0c) +├ INFO[project]: string_min_test.py (sha1=74ca31446cac2107) +├ INFO[project]: string_test.py (sha1=e0009679269c6b63) +├ INFO[project]: struct_test.py (sha1=33a854a5c4f67d74) +├ INFO[project]: testcheck_test.py (sha1=d7662500f1c2c0ff) +├ INFO[project]: test_main.py (sha1=ff744980c190e9b0) +├ INFO[project]: type_bit_test.py (sha1=55cbbb5dec4fef2c) +├ INFO[project]: virtual_dispatch_test.py (sha1=cc3ad7f3bc880326) +├ INFO[project]: vtable_test.py (sha1=2769dee537308b00) +[DBG] scan done file_count=34 +├ INFO[project]: 共 34 个源文件 +├ INFO[project]: 已追加 34 个 App 文件到 _sha1_map.txt +[DBG] before Phase A + +=== Phase A: 生成 stub + text === + +├ INFO[PhaseA]: 预注册 struct/enum/union... +[DBG] PhaseA-pre pass=0 struct_count_before=24 +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/asm_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/attr_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/augassign_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/circ_a.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/circ_b.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/circ_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/closure_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/deco_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/deref_min_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/deref_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/eq_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/float_test.py +├ INFO[UNION]: registered DataUnion with 3 fields, max_size=8 +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/flow_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/for_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/func_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/func_vtable_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/inherit_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/llvmir_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/namespace_defs.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/namespace_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/new_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/oop_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/opovl_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/ptr_only_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/ptr_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/simple_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/string_min_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/string_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/struct_test.py +├ INFO[ENUM]: registered State with 3 members, base_bits=32 +├ INFO[ENUM]: registered Color with 4 members, base_bits=32 +├ INFO[ENUM]: registered MixedType with 3 members, base_bits=64 +├ INFO[UNION]: registered DataUnion with 3 fields, max_size=8 +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/testcheck_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/test_main.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/type_bit_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/virtual_dispatch_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/vtable_test.py +[DBG] PhaseA-pre pass=0 done struct_count_after=51 +[DBG] PhaseA-pre pass=1 struct_count_before=51 +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/asm_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/attr_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/augassign_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/circ_a.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/circ_b.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/circ_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/closure_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/deco_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/deref_min_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/deref_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/eq_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/float_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/flow_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/for_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/func_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/func_vtable_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/inherit_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/llvmir_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/namespace_defs.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/namespace_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/new_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/oop_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/opovl_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/ptr_only_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/ptr_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/simple_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/string_min_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/string_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/struct_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/testcheck_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/test_main.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/type_bit_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/virtual_dispatch_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/vtable_test.py +[DBG] PhaseA-pre pass=1 done struct_count_after=51 +├ INFO[PhaseA]: 预注册完成 +[DBG] PhaseA full-translate start +[DBG] PhaseA file=TransPyV/Test/App/asm_test.py +[DBG] PhaseA file=TransPyV/Test/App/attr_test.py +[DBG] PhaseA file=TransPyV/Test/App/augassign_test.py +[DBG] PhaseA file=TransPyV/Test/App/circ_a.py +[DBG] PhaseA file=TransPyV/Test/App/circ_b.py +[DBG] PhaseA file=TransPyV/Test/App/circ_test.py +[DBG] PhaseA file=TransPyV/Test/App/closure_test.py +[DBG] PhaseA file=TransPyV/Test/App/deco_test.py +[DBG] PhaseA file=TransPyV/Test/App/deref_min_test.py +[DBG] PhaseA file=TransPyV/Test/App/deref_test.py +[DBG] PhaseA file=TransPyV/Test/App/eq_test.py +[DBG] PhaseA file=TransPyV/Test/App/float_test.py +[DBG] PhaseA file=TransPyV/Test/App/flow_test.py +[DBG] PhaseA file=TransPyV/Test/App/for_test.py +[DBG] PhaseA file=TransPyV/Test/App/func_test.py +[DBG] PhaseA file=TransPyV/Test/App/func_vtable_test.py +[DBG] PhaseA file=TransPyV/Test/App/inherit_test.py +[DBG] PhaseA file=TransPyV/Test/App/llvmir_test.py +[DBG] PhaseA file=TransPyV/Test/App/namespace_defs.py +[DBG] PhaseA file=TransPyV/Test/App/namespace_test.py +[DBG] PhaseA file=TransPyV/Test/App/new_test.py +[DBG] PhaseA file=TransPyV/Test/App/oop_test.py +[DBG] PhaseA file=TransPyV/Test/App/opovl_test.py +[DBG] PhaseA file=TransPyV/Test/App/ptr_only_test.py +[DBG] PhaseA file=TransPyV/Test/App/ptr_test.py +[DBG] PhaseA file=TransPyV/Test/App/simple_test.py +[DBG] PhaseA file=TransPyV/Test/App/string_min_test.py +[DBG] PhaseA file=TransPyV/Test/App/string_test.py +[DBG] PhaseA file=TransPyV/Test/App/struct_test.py +[DBG] PhaseA file=TransPyV/Test/App/testcheck_test.py +[DBG] PhaseA file=TransPyV/Test/App/test_main.py +[DBG] PhaseA file=TransPyV/Test/App/type_bit_test.py +[DBG] PhaseA file=TransPyV/Test/App/virtual_dispatch_test.py +[DBG] PhaseA file=TransPyV/Test/App/vtable_test.py +[DBG] before Phase B + +=== Phase B: 编译 .obj === + +[DBG] PhaseB file=TransPyV/Test/App/asm_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=80241c7d6f357bd1 +[DBG] PhaseB step=BuildCombinedIR done len=51574 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/attr_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=17c0dfbcebfa3a0e +[DBG] PhaseB step=BuildCombinedIR done len=72070 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/augassign_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=0eccc2393466d940 +[DBG] PhaseB step=BuildCombinedIR done len=53827 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/circ_a.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=ac47a9dc68e6834c +[DBG] PhaseB step=BuildCombinedIR done len=51031 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/circ_b.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=04d33c721a603632 +[DBG] PhaseB step=BuildCombinedIR done len=51025 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/circ_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=5314f09055045035 +[DBG] PhaseB step=BuildCombinedIR done len=56407 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/closure_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=9d8e1c9631c28b1b +[DBG] PhaseB step=BuildCombinedIR done len=52838 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/deco_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=b7bbe6a1c1e75ad8 +[DBG] PhaseB step=BuildCombinedIR done len=68777 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/deref_min_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=6f5e876273093314 +[DBG] PhaseB step=BuildCombinedIR done len=50111 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/deref_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=eb252050d92f8fb9 +[DBG] PhaseB step=BuildCombinedIR done len=50661 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/eq_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=b8cbe044a10adcca +[DBG] PhaseB step=BuildCombinedIR done len=67044 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/float_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=e74870eba3e6d774 +[DBG] PhaseB step=BuildCombinedIR done len=57074 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/flow_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=969c589035910845 +[DBG] PhaseB step=BuildCombinedIR done len=51773 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/for_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=1eb1c9fc7e85d9f8 +[DBG] PhaseB step=BuildCombinedIR done len=50124 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/func_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=3c2fbc9e35ad3bf2 +[DBG] PhaseB step=BuildCombinedIR done len=54695 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/func_vtable_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=a9df037d9178888f +[DBG] PhaseB step=BuildCombinedIR done len=60040 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/inherit_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=97043f0d52da437f +[DBG] PhaseB step=BuildCombinedIR done len=63006 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/llvmir_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=c39f3e540adb7b4e +[DBG] PhaseB step=BuildCombinedIR done len=72025 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/namespace_defs.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=b7eeff93aa63e70f +[DBG] PhaseB step=BuildCombinedIR done len=52547 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/namespace_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=ef45adce81bf5b2d +[DBG] PhaseB step=BuildCombinedIR done len=63978 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/new_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=9d798d964b9d3411 +[DBG] PhaseB step=BuildCombinedIR done len=59175 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/oop_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=1b9ab264a942c56b +[DBG] PhaseB step=BuildCombinedIR done len=62087 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/opovl_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=a7cf0ce4066332ae +[DBG] PhaseB step=BuildCombinedIR done len=82295 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/ptr_only_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=ed1c124f3cb787c9 +[DBG] PhaseB step=BuildCombinedIR done len=52176 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/ptr_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=07f7da85af876124 +[DBG] PhaseB step=BuildCombinedIR done len=59942 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/simple_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=b078e9c9c74a2a0c +[DBG] PhaseB step=BuildCombinedIR done len=49754 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/string_min_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=74ca31446cac2107 +[DBG] PhaseB step=BuildCombinedIR done len=54019 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/string_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=e0009679269c6b63 +[DBG] PhaseB step=BuildCombinedIR done len=65173 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/struct_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=33a854a5c4f67d74 +[DBG] PhaseB step=BuildCombinedIR done len=71825 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/testcheck_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=d7662500f1c2c0ff +[DBG] PhaseB step=BuildCombinedIR done len=58573 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/test_main.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=ff744980c190e9b0 +[DBG] PhaseB step=BuildCombinedIR done len=57150 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/type_bit_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=55cbbb5dec4fef2c +[DBG] PhaseB step=BuildCombinedIR done len=52348 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/virtual_dispatch_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=cc3ad7f3bc880326 +[DBG] PhaseB step=BuildCombinedIR done len=59792 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB file=TransPyV/Test/App/vtable_test.py +[DBG] PhaseB step=alloc combined_ir +[DBG] PhaseB step=BuildCombinedIR sha1=2769dee537308b00 +[DBG] PhaseB step=BuildCombinedIR done len=56517 +[DBG] PhaseB step=compile_module_to_obj +[DBG] PhaseB step=compile_module_to_obj done cret=0 +[DBG] PhaseB step=obj_path construct +[DBG] PhaseB step=obj_path done +[DBG] PhaseB loop done compiled_count=34 +├ SUCCESS[PhaseB]: 编译完成: 34/34 +[DBG] PhaseB+ start +[DBG] PhaseB+ store_count=93 diff --git a/test_stderr.log b/test_stderr.log new file mode 100644 index 0000000..e69de29 diff --git a/test_stdout.log b/test_stdout.log new file mode 100644 index 0000000..91677f2 --- /dev/null +++ b/test_stdout.log @@ -0,0 +1,241 @@ +[DBG] main enter +├ INFO: TransPyV 启动 + +=== TransPyV 参数解析结果 === + + --project: TransPyV/Test/project.vpj + --src: (未指定) + --temp: (未指定) + --output: (未指定) + --phase: (未指定,默认 all) + --cc: (未指定) + --clean: True + --run: False + --rebuild-includes: False + --clear-cache: False + +参数解析完成。 +[DBG] before Load_project_config path=TransPyV/Test/project.vpj + +=== 工程配置 === + +[DBG] Load_project_config OK +[DBG] before resolve_paths dir=TransPyV/Test +[DBG] resolve_paths OK +├ INFO[config]: project: AstTest v1.0.0 +├ INFO[config]: source_dir: TransPyV/Test/App +├ INFO[config]: build_dir: TransPyV/Test/.tpv_build +├ INFO[config]: temp_dir: TransPyV/Test/.tpv_build/temp +├ INFO[config]: output_dir: TransPyV/Test/.tpv_build/output +├ INFO[config]: compiler: llc +├ INFO[config]: linker: clang++ -> app.exe +├ INFO[config]: includes: TransPyV/Test/../../includes +├ INFO[config]: sha1_slice_level: 1 +[DBG] print_config OK +├ INFO[clean]: 清理临时目录... +├ INFO[clean]: TransPyV/Test/.tpv_build/temp: 删除 1 个文件 +├ INFO[clean]: TransPyV/Test/.tpv_build/output: 删除 0 个文件 +├ INFO[phase]: 模式: all (phase1=1 phase2=1) +[DBG] before Phase1.RunPhase1 inc=TransPyV/Test/../../includes temp=TransPyV/Test/.tpv_build/temp + +=== Phase1: 扫描 includes(按需翻译) === + +├ INFO[Phase1]: 拓扑排序: 25 个类, 15 个文件按依赖顺序处理 +├ INFO[Phase1]: 完成: 翻译=0 跳过=78 失败=0 +[DBG] Phase1.RunPhase1 OK +[DBG] before Phase2.RunMultiFileProject src=TransPyV/Test/App temp=TransPyV/Test/.tpv_build/temp out=TransPyV/Test/.tpv_build/output +[DBG] RunMultiFileProject enter src=TransPyV/Test/App +├ INFO[project]: 多文件项目编译: TransPyV/Test/App +├ INFO[project]: asm_test.py (sha1=80241c7d6f357bd1) +├ INFO[project]: attr_test.py (sha1=17c0dfbcebfa3a0e) +├ INFO[project]: augassign_test.py (sha1=0eccc2393466d940) +├ INFO[project]: circ_a.py (sha1=ac47a9dc68e6834c) +├ INFO[project]: circ_b.py (sha1=04d33c721a603632) +├ INFO[project]: circ_test.py (sha1=5314f09055045035) +├ INFO[project]: closure_test.py (sha1=9d8e1c9631c28b1b) +├ INFO[project]: deco_test.py (sha1=b7bbe6a1c1e75ad8) +├ INFO[project]: deref_min_test.py (sha1=6f5e876273093314) +├ INFO[project]: deref_test.py (sha1=eb252050d92f8fb9) +├ INFO[project]: eq_test.py (sha1=b8cbe044a10adcca) +├ INFO[project]: float_test.py (sha1=e74870eba3e6d774) +├ INFO[project]: flow_test.py (sha1=969c589035910845) +├ INFO[project]: for_test.py (sha1=1eb1c9fc7e85d9f8) +├ INFO[project]: func_test.py (sha1=3c2fbc9e35ad3bf2) +├ INFO[project]: func_vtable_test.py (sha1=a9df037d9178888f) +├ INFO[project]: inherit_test.py (sha1=97043f0d52da437f) +├ INFO[project]: llvmir_test.py (sha1=c39f3e540adb7b4e) +├ INFO[project]: namespace_defs.py (sha1=b7eeff93aa63e70f) +├ INFO[project]: namespace_test.py (sha1=ef45adce81bf5b2d) +├ INFO[project]: new_test.py (sha1=9d798d964b9d3411) +├ INFO[project]: oop_test.py (sha1=1b9ab264a942c56b) +├ INFO[project]: opovl_test.py (sha1=a7cf0ce4066332ae) +├ INFO[project]: ptr_only_test.py (sha1=ed1c124f3cb787c9) +├ INFO[project]: ptr_test.py (sha1=07f7da85af876124) +├ INFO[project]: simple_test.py (sha1=b078e9c9c74a2a0c) +├ INFO[project]: string_min_test.py (sha1=74ca31446cac2107) +├ INFO[project]: string_test.py (sha1=e0009679269c6b63) +├ INFO[project]: struct_test.py (sha1=33a854a5c4f67d74) +├ INFO[project]: testcheck_test.py (sha1=d7662500f1c2c0ff) +├ INFO[project]: test_main.py (sha1=ff744980c190e9b0) +├ INFO[project]: type_bit_test.py (sha1=55cbbb5dec4fef2c) +├ INFO[project]: virtual_dispatch_test.py (sha1=cc3ad7f3bc880326) +├ INFO[project]: vtable_test.py (sha1=2769dee537308b00) +[DBG] scan done file_count=34 +├ INFO[project]: 共 34 个源文件 +├ INFO[project]: 已追加 34 个 App 文件到 _sha1_map.txt +[DBG] before Phase A + +=== Phase A: 生成 stub + text === + +├ INFO[PhaseA]: 预注册 struct/enum/union... +[DBG] PhaseA-pre pass=0 struct_count_before=24 +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/asm_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/attr_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/augassign_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/circ_a.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/circ_b.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/circ_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/closure_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/deco_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/deref_min_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/deref_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/eq_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/float_test.py +├ INFO[UNION]: registered DataUnion with 3 fields, max_size=8 +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/flow_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/for_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/func_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/func_vtable_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/inherit_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/llvmir_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/namespace_defs.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/namespace_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/new_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/oop_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/opovl_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/ptr_only_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/ptr_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/simple_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/string_min_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/string_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/struct_test.py +├ INFO[ENUM]: registered State with 3 members, base_bits=32 +├ INFO[ENUM]: registered Color with 4 members, base_bits=32 +├ INFO[ENUM]: registered MixedType with 3 members, base_bits=64 +├ INFO[UNION]: registered DataUnion with 3 fields, max_size=8 +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/testcheck_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/test_main.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/type_bit_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/virtual_dispatch_test.py +[DBG] PhaseA-pre pass=0 file=TransPyV/Test/App/vtable_test.py +[DBG] PhaseA-pre pass=0 done struct_count_after=51 +[DBG] PhaseA-pre pass=1 struct_count_before=51 +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/asm_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/attr_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/augassign_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/circ_a.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/circ_b.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/circ_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/closure_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/deco_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/deref_min_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/deref_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/eq_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/float_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/flow_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/for_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/func_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/func_vtable_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/inherit_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/llvmir_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/namespace_defs.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/namespace_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/new_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/oop_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/opovl_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/ptr_only_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/ptr_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/simple_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/string_min_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/string_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/struct_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/testcheck_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/test_main.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/type_bit_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/virtual_dispatch_test.py +[DBG] PhaseA-pre pass=1 file=TransPyV/Test/App/vtable_test.py +[DBG] PhaseA-pre pass=1 done struct_count_after=51 +├ INFO[PhaseA]: 预注册完成 +[DBG] PhaseA full-translate start +[DBG] PhaseA file=TransPyV/Test/App/asm_test.py +[DBG] PhaseA file=TransPyV/Test/App/attr_test.py +[DBG] PhaseA file=TransPyV/Test/App/augassign_test.py +[DBG] PhaseA file=TransPyV/Test/App/circ_a.py +[DBG] PhaseA file=TransPyV/Test/App/circ_b.py +[DBG] PhaseA file=TransPyV/Test/App/circ_test.py +[DBG] PhaseA file=TransPyV/Test/App/closure_test.py +[DBG] PhaseA file=TransPyV/Test/App/deco_test.py +[DBG] PhaseA file=TransPyV/Test/App/deref_min_test.py +[DBG] PhaseA file=TransPyV/Test/App/deref_test.py +[DBG] PhaseA file=TransPyV/Test/App/eq_test.py +[DBG] PhaseA file=TransPyV/Test/App/float_test.py +[DBG] PhaseA file=TransPyV/Test/App/flow_test.py +[DBG] PhaseA file=TransPyV/Test/App/for_test.py +[DBG] PhaseA file=TransPyV/Test/App/func_test.py +[DBG] PhaseA file=TransPyV/Test/App/func_vtable_test.py +[DBG] PhaseA file=TransPyV/Test/App/inherit_test.py +[DBG] PhaseA file=TransPyV/Test/App/llvmir_test.py +[DBG] PhaseA file=TransPyV/Test/App/namespace_defs.py +[DBG] PhaseA file=TransPyV/Test/App/namespace_test.py +[DBG] PhaseA file=TransPyV/Test/App/new_test.py +[DBG] PhaseA file=TransPyV/Test/App/oop_test.py +[DBG] PhaseA file=TransPyV/Test/App/opovl_test.py +[DBG] PhaseA file=TransPyV/Test/App/ptr_only_test.py +[DBG] PhaseA file=TransPyV/Test/App/ptr_test.py +[DBG] PhaseA file=TransPyV/Test/App/simple_test.py +[DBG] PhaseA file=TransPyV/Test/App/string_min_test.py +[DBG] PhaseA file=TransPyV/Test/App/string_test.py +[DBG] PhaseA file=TransPyV/Test/App/struct_test.py +[DBG] PhaseA file=TransPyV/Test/App/testcheck_test.py +[DBG] PhaseA file=TransPyV/Test/App/test_main.py +[DBG] PhaseA file=TransPyV/Test/App/type_bit_test.py +[DBG] PhaseA file=TransPyV/Test/App/virtual_dispatch_test.py +[DBG] PhaseA file=TransPyV/Test/App/vtable_test.py +[DBG] before Phase B + +=== Phase B: 编译 .obj === + +[DBG] PhaseB file=TransPyV/Test/App/asm_test.py +[DBG] PhaseB file=TransPyV/Test/App/attr_test.py +[DBG] PhaseB file=TransPyV/Test/App/augassign_test.py +[DBG] PhaseB file=TransPyV/Test/App/circ_a.py +[DBG] PhaseB file=TransPyV/Test/App/circ_b.py +[DBG] PhaseB file=TransPyV/Test/App/circ_test.py +[DBG] PhaseB file=TransPyV/Test/App/closure_test.py +[DBG] PhaseB file=TransPyV/Test/App/deco_test.py +[DBG] PhaseB file=TransPyV/Test/App/deref_min_test.py +[DBG] PhaseB file=TransPyV/Test/App/deref_test.py +[DBG] PhaseB file=TransPyV/Test/App/eq_test.py +[DBG] PhaseB file=TransPyV/Test/App/float_test.py +[DBG] PhaseB file=TransPyV/Test/App/flow_test.py +[DBG] PhaseB file=TransPyV/Test/App/for_test.py +[DBG] PhaseB file=TransPyV/Test/App/func_test.py +[DBG] PhaseB file=TransPyV/Test/App/func_vtable_test.py +[DBG] PhaseB file=TransPyV/Test/App/inherit_test.py +[DBG] PhaseB file=TransPyV/Test/App/llvmir_test.py +[DBG] PhaseB file=TransPyV/Test/App/namespace_defs.py +[DBG] PhaseB file=TransPyV/Test/App/namespace_test.py +[DBG] PhaseB file=TransPyV/Test/App/new_test.py +[DBG] PhaseB file=TransPyV/Test/App/oop_test.py +[DBG] PhaseB file=TransPyV/Test/App/opovl_test.py +[DBG] PhaseB file=TransPyV/Test/App/ptr_only_test.py +[DBG] PhaseB file=TransPyV/Test/App/ptr_test.py +[DBG] PhaseB file=TransPyV/Test/App/simple_test.py +[DBG] PhaseB file=TransPyV/Test/App/string_min_test.py +[DBG] PhaseB file=TransPyV/Test/App/string_test.py +[DBG] PhaseB file=TransPyV/Test/App/struct_test.py +[DBG] PhaseB file=TransPyV/Test/App/testcheck_test.py +[DBG] PhaseB file=TransPyV/Test/App/test_main.py +[DBG] PhaseB file=TransPyV/Test/App/type_bit_test.py +[DBG] PhaseB file=TransPyV/Test/App/virtual_dispatch_test.py +[DBG] PhaseB file=TransPyV/Test/App/vtable_test.py