From 32af3f93fa6e2b00e299461c417994ef908e11fc Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 30 Jul 2026 15:44:32 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E4=B8=BE=E5=AE=9E=E9=AA=8C=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=EF=BC=8CC1=20=E7=BC=96=E8=AF=91=20Test=20=E6=88=90?= =?UTF-8?q?=E5=8A=9F=EF=BC=8C=E6=9A=82=E6=9C=AA=E8=BE=BE=E5=88=B0=E8=87=AA?= =?UTF-8?q?=E4=B8=BE=E6=94=B6=E6=95=9B=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/lib/StubGen/Converter.py | 169 ++++++++ App/lib/core/BuildPipeline.py | 133 ++++--- App/lib/core/Handles/HandlesAnnAssign.py | 99 +++++ App/lib/core/Handles/HandlesAssign.py | 64 ++- App/lib/core/Handles/HandlesClassDef.py | 49 ++- App/lib/core/Handles/HandlesExpr.py | 457 +++++++++++++++++----- App/lib/core/Handles/HandlesExprCall.py | 351 ++++++++++++++++- App/lib/core/Handles/HandlesFor.py | 32 +- App/lib/core/Handles/HandlesIf.py | 7 +- App/lib/core/Handles/HandlesImports.py | 15 - App/lib/core/Handles/HandlesMain.py | 184 ++++++++- App/lib/core/Handles/HandlesReturn.py | 17 +- App/lib/core/Handles/HandlesStruct.py | 64 ++- App/lib/core/Handles/HandlesTranslator.py | 1 + App/lib/core/Handles/HandlesType.py | 129 +++++- App/lib/core/Handles/HandlesWhile.py | 7 +- App/lib/core/Phase1.py | 133 ++++++- App/lib/core/Phase2.py | 267 +++++++++++-- App/lib/core/StubMerger.py | 407 +++++++++++++------ App/lib/core/VLogger.py | 2 +- App/main.py | 11 +- project.json | 2 +- 22 files changed, 2158 insertions(+), 442 deletions(-) diff --git a/App/lib/StubGen/Converter.py b/App/lib/StubGen/Converter.py index 33db5ca..b354e8b 100644 --- a/App/lib/StubGen/Converter.py +++ b/App/lib/StubGen/Converter.py @@ -17,6 +17,109 @@ import viperlib # ============================================================ +# ============================================================ +# _eval_binop_int / _eval_binop_valid - 递归计算 BinOp 常量值 +# +# 用于 .pyi 存根文件生成时计算 BinOp 表达式(如 A | B, A + B)的整数值。 +# 支持 BitOr/BitAnd/Add/Sub 以及嵌套的 Name(引用已注册的 CDefine 常量)。 +# ============================================================ +def _eval_binop_valid(node: ast.AST | t.CPtr) -> int: + """检查 BinOp 节点是否可计算(所有叶子节点都是常量或已注册的 CDefine)""" + if node is None: + return 0 + k: int = node.kind() + if k == ast.ASTKind.Constant: + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) + if cn.const_kind == ast.CONST_INT: + return 1 + return 0 + if k == ast.ASTKind.UnaryOp: + uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node) + if uop.operand is not None: + return _eval_binop_valid(uop.operand) + return 0 + if k == ast.ASTKind.BinOp: + bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) + if bop.left is not None and bop.right is not None: + if _eval_binop_valid(bop.left) != 0: + if _eval_binop_valid(bop.right) != 0: + return 1 + return 0 + if k == ast.ASTKind.Name: + # Name 引用:检查是否是已注册的 CDefine 常量 + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm.id is not None: + # 从本地 CDefine 表查找 + val: int = _lookup_cdefine_for_pyi(nm.id) + if _pyi_cdefine_found() != 0: + return 1 + return 0 + return 0 + + +def _eval_binop_int(node: ast.AST | t.CPtr) -> int: + """递归计算 BinOp 节点的整数值""" + if node is None: + return 0 + k: int = node.kind() + if k == ast.ASTKind.Constant: + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) + if cn.const_kind == ast.CONST_INT: + return cn.int_val + return 0 + if k == ast.ASTKind.UnaryOp: + uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node) + if uop.op == ast.OpKind.USub and uop.operand is not None: + return -_eval_binop_int(uop.operand) + if uop.op == ast.OpKind.UAdd and uop.operand is not None: + return _eval_binop_int(uop.operand) + if uop.op == ast.OpKind.Invert and uop.operand is not None: + return ~_eval_binop_int(uop.operand) + return 0 + if k == ast.ASTKind.BinOp: + bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) + if bop.left is None or bop.right is None: + return 0 + lv: int = _eval_binop_int(bop.left) + rv: int = _eval_binop_int(bop.right) + if bop.op == ast.OpKind.BitOr: + return lv | rv + if bop.op == ast.OpKind.BitAnd: + return lv & rv + if bop.op == ast.OpKind.Add: + return lv + rv + if bop.op == ast.OpKind.Sub: + return lv - rv + return 0 + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm.id is not None: + return _lookup_cdefine_for_pyi(nm.id) + return 0 + return 0 + + +# CDefine 查找的前向声明(实际实现在 HandlesType) +# 避免循环依赖,使用弱引用 +_g_pyi_cdefine_found: int = 0 + + +def _lookup_cdefine_for_pyi(name: str) -> int: + """从本地 CDefine 表查找常量值(用于 .pyi 生成时的 BinOp 计算)""" + import lib.core.Handles.HandlesType as HandlesType + val: int = HandlesType.lookup_cdefine_constant(name) + if HandlesType.is_cdefine_found() != 0: + _g_pyi_cdefine_found = 1 + return val + _g_pyi_cdefine_found = 0 + return 0 + + +def _pyi_cdefine_found() -> int: + """返回上次 _lookup_cdefine_for_pyi 的查找结果状态""" + return _g_pyi_cdefine_found + + # ============================================================ # _PyiEmit - 追加字符串到缓冲区 # @@ -525,6 +628,72 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, pos = _PyiEmit(buf, buf_size, pos, " = False") elif cv.const_kind == ast.CONST_NONE: pos = _PyiEmit(buf, buf_size, pos, " = None") + elif aa.value is not None and aa.value.kind() == ast.ASTKind.UnaryOp: + # 处理负值常量(如 INVALID_HANDLE_VALUE = -1) + # UnaryOp(USub, Constant(1)) → -1 + uop_cv: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(aa.value) + if uop_cv.op == ast.OpKind.USub and uop_cv.operand is not None: + if uop_cv.operand.kind() == ast.ASTKind.Constant: + uop_const: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(uop_cv.operand) + if uop_const.const_kind == ast.CONST_INT: + neg_val: int = -uop_const.int_val + num_buf_u: bytes = mb.alloc(32) + if num_buf_u is not None: + viperlib.snprintf(num_buf_u, 32, " = %d", neg_val) + pos = _PyiEmit(buf, buf_size, pos, num_buf_u) + elif aa.value is not None and aa.value.kind() == ast.ASTKind.Call: + # 处理类型构造函数常量(如 t.CUnsignedLong(-11)) + # 提取第一个参数的整数值并输出 + call_cv: ast.Call | t.CPtr = (ast.Call | t.CPtr)(aa.value) + if call_cv.args is not None: + call_args_cv: list[ast.AST | t.CPtr] | t.CPtr = call_cv.args + if call_args_cv.__len__() > 0: + first_arg: ast.AST | t.CPtr = call_args_cv.get(0) + if first_arg is not None: + arg_kind: int = first_arg.kind() + if arg_kind == ast.ASTKind.Constant: + ca_cv: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(first_arg) + if ca_cv.const_kind == ast.CONST_INT: + num_buf_c: bytes = mb.alloc(32) + if num_buf_c is not None: + viperlib.snprintf(num_buf_c, 32, " = %d", ca_cv.int_val) + pos = _PyiEmit(buf, buf_size, pos, num_buf_c) + elif arg_kind == ast.ASTKind.UnaryOp: + uop_ca: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(first_arg) + if uop_ca.op == ast.OpKind.USub and uop_ca.operand is not None: + if uop_ca.operand.kind() == ast.ASTKind.Constant: + uoc: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(uop_ca.operand) + if uoc.const_kind == ast.CONST_INT: + neg_c_val: int = -uoc.int_val + num_buf_cu: bytes = mb.alloc(32) + if num_buf_cu is not None: + viperlib.snprintf(num_buf_cu, 32, " = %d", neg_c_val) + pos = _PyiEmit(buf, buf_size, pos, num_buf_cu) + elif arg_kind == ast.ASTKind.BinOp: + # Call 内部的 BinOp(如 t.CUnsignedLong(A | B)) + # 递归计算 BinOp 值 + binop_val_bo: int = _eval_binop_int(first_arg) + if binop_val_bo != 0 or _eval_binop_valid(first_arg) != 0: + num_buf_bo: bytes = mb.alloc(32) + if num_buf_bo is not None: + viperlib.snprintf(num_buf_bo, 32, " = %d", binop_val_bo) + pos = _PyiEmit(buf, buf_size, pos, num_buf_bo) + elif aa.value is not None and aa.value.kind() == ast.ASTKind.BinOp: + # 处理 BinOp 常量(如 FOREGROUND_COMBO = FOREGROUND_RED | FOREGROUND_GREEN) + # 递归计算 BinOp 值并输出 + binop_val_av: int = _eval_binop_int(aa.value) + if binop_val_av != 0 or _eval_binop_valid(aa.value) != 0: + num_buf_b: bytes = mb.alloc(32) + if num_buf_b is not None: + viperlib.snprintf(num_buf_b, 32, " = %d", binop_val_av) + pos = _PyiEmit(buf, buf_size, pos, num_buf_b) + elif aa.value is not None and aa.value.kind() == ast.ASTKind.Name: + # 处理 Name 引用常量(如 ALIAS = OTHER_CONST) + # 输出引用的名字,让消费方在本地查找 + name_val: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.value) + if name_val.id is not None: + pos = _PyiEmit(buf, buf_size, pos, " = ") + pos = _PyiEmit(buf, buf_size, pos, name_val.id) pos = _PyiEmit(buf, buf_size, pos, "\n") # 全局赋值(无注解)— 从 Constant value 推断类型 diff --git a/App/lib/core/BuildPipeline.py b/App/lib/core/BuildPipeline.py index 6b018b8..b315aea 100644 --- a/App/lib/core/BuildPipeline.py +++ b/App/lib/core/BuildPipeline.py @@ -30,7 +30,7 @@ import lib.Projectrans.Config as Config _mbuddy: memhub.MemBuddy | t.CPtr # 源代码缓冲区大小(1MB) -SRC_BUF_SIZE: t.CDefine = 1048576 +SRC_BUF_SIZE: t.CDefine = 1048576 # 2^20 = 1MB # ============================================================ @@ -282,8 +282,76 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s return 0 +def _CollectObjFilesImpl(base_dir: str, bd_len: t.CSizeT, + out_buf: bytes, out_pos: t.CSizeT, out_size: t.CSizeT) -> t.CSizeT: + """递归扫描 base_dir,收集 .obj 文件路径到 out_buf,返回新的 out_pos + + 支持切片子目录(如 dir/43/{sha1}.obj)。 + """ + find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = _mbuddy.alloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__()) + if find_data is None: + return out_pos + string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__()) + + pattern: bytes = _mbuddy.alloc(bd_len + 8) + if pattern is None: + return out_pos + viperlib.snprintf(pattern, bd_len + 8, "%s/*", base_dir) + + handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data) + _mbuddy.free(pattern) + if handle == w32.win32base.INVALID_HANDLE_VALUE: + return out_pos + + while True: + fname: str = find_data.cFileName + if fname is not None: + fname_len: t.CSizeT = string.strlen(fname) + if fname_len > 0: + # 跳过 . 和 .. + is_dot: int = 0 + if fname_len == 1 and fname[0] == '.': + is_dot = 1 + elif fname_len == 2 and fname[0] == '.' and fname[1] == '.': + is_dot = 1 + if is_dot == 0: + attrs: ULONG = find_data.dwFileAttributes + is_dir: int = 0 + if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: + is_dir = 1 + if is_dir != 0: + # 递归扫描子目录 + sub_dir: bytes = _mbuddy.alloc(bd_len + fname_len + 2) + if sub_dir is not None: + viperlib.snprintf(sub_dir, bd_len + fname_len + 2, "%s/%s", base_dir, fname) + sub_len: t.CSizeT = string.strlen(sub_dir) + out_pos = _CollectObjFilesImpl(sub_dir, sub_len, out_buf, out_pos, out_size) + _mbuddy.free(sub_dir) + else: + # 检查是否是 .obj 文件 + if fname_len > 4: + if fname[fname_len - 4] == '.' and fname[fname_len - 3] == 'o' and fname[fname_len - 2] == 'b' and fname[fname_len - 1] == 'j': + need: t.CSizeT = bd_len + 1 + fname_len + 2 + if out_pos + need < out_size: + if out_pos > 0: + out_buf[out_pos] = ' ' + out_pos += 1 + viperlib.snprintf(out_buf + out_pos, need, "%s/%s", base_dir, fname) + out_pos += bd_len + 1 + fname_len + else: + break + + if w32.win32file.FindNextFileA(handle, find_data) == 0: + break + + w32.win32file.FindClose(handle) + return out_pos + + def collect_obj_files(includes_binary_dir: str, out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT: - """扫描 includes_binary_dir 目录,收集所有 .obj 文件路径到 out_buf + """递归扫描 includes_binary_dir 目录,收集所有 .obj 文件路径到 out_buf + + 支持切片子目录(如 includes_binary_dir/43/{sha1}.obj)。 Args: includes_binary_dir: includes.binary 目录路径 @@ -297,47 +365,8 @@ def collect_obj_files(includes_binary_dir: str, out_buf: bytes, out_size: t.CSiz return 0 out_buf[0] = '\0' - out_pos: t.CSizeT = 0 - - # 构造搜索模式: dir/*.obj dir_len: t.CSizeT = string.strlen(includes_binary_dir) - pattern: bytes = _mbuddy.alloc(dir_len + 8) - if pattern is None: - return 0 - viperlib.snprintf(pattern, dir_len + 8, "%s/*.obj", includes_binary_dir) - - # FindFirstFileA - find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = _mbuddy.alloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__()) - if find_data is None: - return 0 - string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__()) - - handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data) - if handle == w32.win32base.INVALID_HANDLE_VALUE: - return 0 - - # 需要 win32base 的 INVALID_HANDLE_VALUE - while True: - fname: str = find_data.cFileName - if fname is not None: - fname_len: t.CSizeT = string.strlen(fname) - # 构造完整路径并追加到 out_buf - # 路径格式: "dir/fname " - need: t.CSizeT = dir_len + 1 + fname_len + 2 - if out_pos + need < out_size: - if out_pos > 0: - out_buf[out_pos] = ' ' - out_pos += 1 - viperlib.snprintf(out_buf + out_pos, need, "%s/%s", includes_binary_dir, fname) - out_pos += dir_len + 1 + fname_len - else: - # 缓冲区不足,停止 - break - - if w32.win32file.FindNextFileA(handle, find_data) == 0: - break - - w32.win32file.FindClose(handle) + out_pos: t.CSizeT = _CollectObjFilesImpl(includes_binary_dir, dir_len, out_buf, 0, out_size) out_buf[out_pos] = '\0' return out_pos @@ -509,13 +538,23 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT, viperlib.snprintf(cmd, cmd_len, "%s %s -o %s %s", linker_cmd, obj_paths, linker_output, linker_flags) - # 用 stdlib.system() 直接在控制台运行链接,输出直接显示 - # 避免 subprocess 管道捕获丢失输出(TransPyV 标准句柄可能无效) - sys_ret: int = stdlib.system(cmd) - if sys_ret != 0: + # 用 subprocess.run 捕获 clang++ 的 stdout/stderr 输出 + # 之前用 stdlib.system() 无法捕获具体错误(undefined reference 等), + # 导致 1.txt 中只看到"链接失败,返回码: 1"而看不到未解析符号 + result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True) + if result is None: + VLogger.error("subprocess.run 返回 None", "link") + return 1 + if result.returncode != 0: + # 先用 stdio.printf 直接输出 clang++ 的完整错误输出(含 undefined reference) + # 必须在 VLogger.error 之前输出,因为 VLogger.error 会调用 sys.exit(1) 终止进程 + if result.stdout is not None: + stdio.printf("=== clang++ 链接错误输出 ===\n%s\n", result.stdout) + if result.stderr is not None: + stdio.printf("=== clang++ stderr ===\n%s\n", result.stderr) fb: t.CChar | t.CPtr = VLogger.fmt_buf() if fb is not None: - viperlib.snprintf(fb, 1024, "链接失败,返回码: %d, 命令: %s", sys_ret, cmd) + 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 d031dc6..9096f84 100644 --- a/App/lib/core/Handles/HandlesAnnAssign.py +++ b/App/lib/core/Handles/HandlesAnnAssign.py @@ -176,6 +176,77 @@ def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int: return 0 +# ============================================================ +# _init_global_array_from_list — 从列表字面量初始化全局数组 +# +# 为每个元素生成 GEP + store 指令,将值存入全局数组的对应位置。 +# 适用于模块级 t.CArray[elem_ty, count] = [v0, v1, ...] 的初始化。 +# +# Args: +# builder: IRBuilder +# pool: 编译器内存池 +# mod: LLVMModule +# array_alloca: 全局数组变量的 Value 引用(类型为 [N x elem_ty]*) +# list_node: ast.List 节点 +# trans: Translator 对象 +# +# Returns: +# 0 成功,1 失败 +# ============================================================ +def _init_global_array_from_list(builder: llvmlite.IRBuilder | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + array_alloca: llvmlite.Value | t.CPtr, + list_node: ast.AST | t.CPtr, + trans: HT.Translator | t.CPtr) -> int: + """从列表字面量初始化全局数组,为每个元素生成 GEP + store""" + if builder is None or array_alloca is None or list_node is None: + return 1 + + # 获取数组类型(Pointee of [N x elem_ty]*) + arr_ty: llvmlite.LLVMType | t.CPtr = None + if array_alloca.Ty is not None: + arr_ty = array_alloca.Ty.Pointee + if arr_ty is None: + return 1 + + # 匹配数组类型获取元素类型 + elem_ty: llvmlite.LLVMType | t.CPtr = None + match arr_ty: + case llvmlite.LLVMType.Array(et, _): + elem_ty = et + if elem_ty is None: + return 1 + + # 获取列表元素 + lst: ast.List | t.CPtr = (ast.List | t.CPtr)(list_node) + if lst is None or lst.elts is None: + return 1 + elts: list[ast.AST | t.CPtr] | t.CPtr = lst.elts + elts_count: t.CSizeT = elts.__len__() + + # 为每个元素生成 GEP + store + i: t.CSizeT = 0 + while i < elts_count: + elem_node: ast.AST | t.CPtr = elts.get(i) + elem_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, elem_node, None, 0, trans) + if elem_val is not None: + idx_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, i) + elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, arr_ty, elem_ty, array_alloca, idx_val) + if elem_ptr is not None: + # 类型转换(如 i32 → i8 截断) + store_val: llvmlite.Value | t.CPtr = elem_val + if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None: + store_val = HandlesExpr.coerce_to_type( + builder, elem_val, elem_ptr.Ty.Pointee) + llvmlite.build_store(builder, store_val, elem_ptr) + i += 1 + + return 0 + + @t.NoVTable class AnnAssignHandle(HandlesBase.Mixin): """AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针""" @@ -221,6 +292,11 @@ class AnnAssignHandle(HandlesBase.Mixin): if HT.is_nonlocal_name(self.Trans, nm.id) != 0: return 0 + # 模块级全局变量已由 handle_module_level_var 注册到模块作用域, + # 不需要创建局部 alloca(否则会导致局部变量遮蔽全局变量) + if HandlesVar.lookup_module_var(self.Trans.SymTab, nm.id) is not None: + return 0 + # 检查是否已存在 existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current( self.Trans.SymTab, nm.id) @@ -350,6 +426,29 @@ class AnnAssignHandle(HandlesBase.Mixin): llvmlite.build_store(builder, rhs_coerced, nl_ptr) return 0 + # 模块级全局变量(由 handle_module_level_var 注册到模块作用域) + # 非函数内 global 声明,但变量已在模块作用域中(如模块级 t.CArray 初始化) + mod_glob: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var( + self.Trans.SymTab, nm.id) + if mod_glob is not None: + if aa.value is not None: + # 列表字面量 → 逐元素 GEP + store 初始化数组 + if aa.value.kind() == ast.ASTKind.List: + _init_global_array_from_list( + builder, pool, mod, mod_glob, aa.value, self.Trans) + else: + rhs_val_mg: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, aa.value, + None, 0, self.Trans) + if rhs_val_mg is not None: + target_ty_mg: llvmlite.LLVMType | t.CPtr = None + if mod_glob.Ty is not None: + target_ty_mg = mod_glob.Ty.Pointee + if target_ty_mg is not None: + rhs_val_mg = HandlesExpr.coerce_to_type(builder, rhs_val_mg, target_ty_mg) + llvmlite.build_store(builder, rhs_val_mg, mod_glob) + return 0 + # 普通局部变量 # 创建 alloca alloca: llvmlite.Value | t.CPtr = HandlesVar.get_or_create_sym( diff --git a/App/lib/core/Handles/HandlesAssign.py b/App/lib/core/Handles/HandlesAssign.py index 186190d..765ce8b 100644 --- a/App/lib/core/Handles/HandlesAssign.py +++ b/App/lib/core/Handles/HandlesAssign.py @@ -15,6 +15,7 @@ import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.core.Handles.HandlesNonlocal as HandlesNonlocal import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.StubMerger as StubMerger # ============================================================ @@ -62,23 +63,70 @@ 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: - # 增强错误信息:包含 sha1 + lineno + AST 节点类型,便于定位 + # 增强错误信息:包含文件名 + 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 + # 通过 sha1 查找文件名(人类可读) + rel_path: str = None if sha1 is not None: - viperlib.snprintf(fb, 1024, - "rhs_val is None [sha1=%s lineno=%d kind=%d]", - sha1, val_line, val_kind) + rel_path = StubMerger.LookupSha1RelPath(sha1) + # 根据 AST 节点类型提取详情 + if val_kind == ast.ASTKind.Attribute: + at_nv: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(asgn.value) + attr_nm: str = at_nv.attr + target_nm: str = "?" + if at_nv.value is not None: + if at_nv.value.kind() == ast.ASTKind.Name: + tn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_nv.value) + target_nm = tn_nm.id + if rel_path is not None: + viperlib.snprintf(fb, 1024, + "赋值右侧为 None: %s:%d '%s.%s' 属性访问失败", + rel_path, val_line, target_nm, attr_nm) + else: + viperlib.snprintf(fb, 1024, + "赋值右侧为 None: sha1=%s:%d '%s.%s' 属性访问失败", + sha1, val_line, target_nm, attr_nm) + elif val_kind == ast.ASTKind.Name: + nm_nv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(asgn.value) + if rel_path is not None: + viperlib.snprintf(fb, 1024, + "赋值右侧为 None: %s:%d 变量 '%s' 未找到", + rel_path, val_line, nm_nv.id) + else: + viperlib.snprintf(fb, 1024, + "赋值右侧为 None: sha1=%s:%d 变量 '%s' 未找到", + sha1, val_line, nm_nv.id) + elif val_kind == ast.ASTKind.Call: + cl_nv: ast.Call | t.CPtr = (ast.Call | t.CPtr)(asgn.value) + func_nm: str = "?" + if cl_nv.func is not None: + if cl_nv.func.kind() == ast.ASTKind.Name: + fn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cl_nv.func) + func_nm = fn_nm.id + if rel_path is not None: + viperlib.snprintf(fb, 1024, + "赋值右侧为 None: %s:%d 调用 '%s(...)' 返回 None", + rel_path, val_line, func_nm) + else: + viperlib.snprintf(fb, 1024, + "赋值右侧为 None: sha1=%s:%d 调用 '%s(...)' 返回 None", + sha1, val_line, func_nm) else: - viperlib.snprintf(fb, 1024, - "rhs_val is None [lineno=%d kind=%d]", - val_line, val_kind) + if rel_path is not None: + viperlib.snprintf(fb, 1024, + "赋值右侧为 None: %s:%d [kind=%d]", + rel_path, val_line, val_kind) + else: + viperlib.snprintf(fb, 1024, + "赋值右侧为 None: sha1=%s:%d [kind=%d]", + sha1, val_line, val_kind) VLogger.error(fb, "ASGN") else: - VLogger.error("rhs_val is None", "ASGN") + VLogger.error("赋值右侧为 None (asgn.value 为空)", "ASGN") return 0 new_vars: int = 0 diff --git a/App/lib/core/Handles/HandlesClassDef.py b/App/lib/core/Handles/HandlesClassDef.py index 830a1ce..8e41ab0 100644 --- a/App/lib/core/Handles/HandlesClassDef.py +++ b/App/lib/core/Handles/HandlesClassDef.py @@ -1700,15 +1700,14 @@ _generic_class_nodes: list[ast.ClassDef | t.CPtr] | t.CPtr = None _generic_class_sha1s: list[str] | t.CPtr = None -def _is_generic_class(cd: ast.ClassDef | t.CPtr) -> int: - """检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否""" - if cd is None: - return 0 - tp: list[str] | t.CPtr = cd.type_params +def _is_generic_class(tp: list[str] | t.CPtr) -> int: + """检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否 + + 接收 type_params 而非 cd,避免 cd.type_params 属性访问 + 因跨模块类型推断失败返回 None。 + """ if tp is None: return 0 - if tp.__len__() == 0: - return 0 return 1 @@ -1721,29 +1720,37 @@ def _register_generic_template(pool: memhub.MemBuddy | t.CPtr, _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__() + # 用局部变量接收 global list,触发类型推断(避免 global 变量 __len__ 失败) + names_local: list[str] | t.CPtr = _generic_class_names + cn: t.CSizeT = names_local.__len__() i: t.CSizeT for i in range(cn): - nm: str = _generic_class_names.get(i) + nm: str = names_local.get(i) if nm is not None and string.strcmp(nm, cd.name) == 0: return _generic_class_names.append(cd.name) _generic_class_nodes.append(cd) _generic_class_sha1s.append(module_sha1) + # 诊断日志:确认泛型模板注册 + fb_rg: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_rg is not None: + viperlib.snprintf(fb_rg, 1024, "注册泛型模板: %s (module=%s)", cd.name, module_sha1) + VLogger.info(fb_rg, "SPEC") def _find_generic_template(class_name: str) -> ast.ClassDef | t.CPtr: """查找泛型类模板,返回 ClassDef 节点或 None""" if _generic_class_names is None or class_name is None: return None - cn: t.CSizeT = _generic_class_names.__len__() + # 用局部变量接收 global list,触发类型推断(避免 global 变量 __len__ 失败) + names_local: list[str] | t.CPtr = _generic_class_names + nodes_local: list[ast.ClassDef | t.CPtr] | t.CPtr = _generic_class_nodes + cn: t.CSizeT = names_local.__len__() i: t.CSizeT for i in range(cn): - nm: str = _generic_class_names.get(i) + nm: str = names_local.get(i) if nm is not None and string.strcmp(nm, class_name) == 0: - # 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换 - node: ast.ClassDef | t.CPtr = _generic_class_nodes.get(i) + node: ast.ClassDef | t.CPtr = nodes_local.get(i) return node return None @@ -1752,13 +1759,15 @@ 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__() + # 用局部变量接收 global list,触发类型推断 + names_local: list[str] | t.CPtr = _generic_class_names + sha1s_local: list[str] | t.CPtr = _generic_class_sha1s + cn: t.CSizeT = names_local.__len__() i: t.CSizeT for i in range(cn): - nm: str = _generic_class_names.get(i) + nm: str = names_local.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) + sha1_val: str = sha1s_local.get(i) return sha1_val return None @@ -2191,7 +2200,9 @@ def translate_class_def(trans: HT.Translator | t.CPtr, return _translate_renum_def(trans, cd) # 泛型类:存储为模板,不发射 IR(等实例化时特化) - if _is_generic_class(cd) == 1: + # 用局部变量接收 cd.type_params,触发类型推断 + tp_val: list[str] | t.CPtr = cd.type_params + if _is_generic_class(tp_val) == 1: _register_generic_template(pool, cd, trans.ModuleSha1) return 0 diff --git a/App/lib/core/Handles/HandlesExpr.py b/App/lib/core/Handles/HandlesExpr.py index f05ad84..8bbda11 100644 --- a/App/lib/core/Handles/HandlesExpr.py +++ b/App/lib/core/Handles/HandlesExpr.py @@ -420,7 +420,14 @@ def _infer_elem_type_from_value(pool: memhub.MemBuddy | t.CPtr, # 依次尝试 "pool", "_mbuddy", "mbuddy", "mb" # ============================================================ def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: - """查找上下文中的 pool 变量,返回 alloca 或 None""" + """查找上下文中的 pool 变量,返回 alloca 或 None + + 查找顺序: pool → _mbuddy → mbuddy → mb + 兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。 + 注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理), + 此兜底仅用于函数内的动态 list 字面量(如 op=[...])。 + @_mbuddy 由入口模块(如 Phase2.py)定义并初始化,其他模块以 external 引用。 + """ if trans is None: return None alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, "pool") @@ -435,7 +442,32 @@ def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: alloca = HandlesVar.lookup_var(trans.SymTab, "mb") if alloca is not None: return alloca - return None + # 兜底: 创建外部全局 @_mbuddy 引用(i8** 类型) + # 仅用于函数内动态 list 字面量,@_mbuddy 由入口模块定义并初始化 + pool: memhub.MemBuddy | t.CPtr = trans.Pool + if pool is None or trans.Module is None or trans.SymTab is None: + return None + # 先检查是否已注册到模块作用域(避免重复创建全局变量) + existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(trans.SymTab, "_mbuddy") + if existing is not None: + return existing + # 创建 i8* 类型(t.CVoid | t.CPtr = i8*) + i8_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_fb) + # 创建外部全局 @_mbuddy(external linkage,无初始值) + gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, "_mbuddy", i8_ptr_ty_fb) + if gv_ext is not None: + gv_ext.Linkage = "external" + llvmlite.module_add_global(trans.Module, gv_ext) + # 创建 Value 引用(@_mbuddy, 类型为 i8**) + i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb) + ref_name_fb: t.CChar | t.CPtr = pool.alloc(16) + if ref_name_fb is not None: + string.strcpy(ref_name_fb, "@_mbuddy") + gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb) + # 注册到模块作用域,避免重复创建 + HandlesVar.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb) + return gv_ref_fb # ============================================================ @@ -781,15 +813,32 @@ 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: return None + rhs_node: ast.AST | t.CPtr = comparators.get(0) rhs: llvmlite.Value | t.CPtr = translate_value( - builder, pool, mod, comparators.get(0), None, 0, trans) + builder, pool, mod, rhs_node, None, 0, trans) if rhs is None: return None + # === None 比较优化: x == None / x != None 走身份比较(icmp eq/ne null)=== + # 避免对结构体指针生成无效的 __eq__/__ne__ 调用(方法未定义但类有 SHA1 时 + # try_operator_overload 会误生成 call void @...__eq__(...) 导致链接错误) + # 语义依据: Python 中 == None / != None 默认走身份比较,不调用 __eq__/__ne__ + lhs_node: ast.AST | t.CPtr = cmp.left + cmp_with_none: int = 0 + if op == ast.OpKind.Eq or op == ast.OpKind.Ne: + if rhs_node is not None and rhs_node.kind() == ast.ASTKind.Constant: + rhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rhs_node) + if rhs_cn is not None and rhs_cn.const_kind == ast.CONST_NONE: + cmp_with_none = 1 + if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Constant: + lhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(lhs_node) + if lhs_cn is not None and lhs_cn.const_kind == ast.CONST_NONE: + cmp_with_none = 1 + # === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 === # 对于值类型变量(如 cnt: Counter),用 alloca 指针尝试重载 - lhs_node: ast.AST | t.CPtr = cmp.left - if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None: + # None 比较跳过重载(走身份比较) + if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None: nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node) if nm is not None and nm.id is not None: lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( @@ -808,10 +857,12 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr, # === 比较运算符重载路径 2: lhs 是 Ptr(Struct) === # Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较 - ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload( - pool, builder, mod, lhs, rhs, op, trans, 1) - if ovl_result2 is not None: - return ovl_result2 + # None 比较跳过重载(走身份比较) + if cmp_with_none == 0: + ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload( + pool, builder, mod, lhs, rhs, op, trans, 1) + if ovl_result2 is not None: + return ovl_result2 predicate: int = llvmlite.ICMP_SLT if op == ast.OpKind.Lt: @@ -875,8 +926,12 @@ def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr, return None if uo.op == ast.OpKind.USub: - # -x = 0 - x - # 根据 operand 类型选择零值常量(避免 i32 与 i64 类型不匹配) + # -x + # 浮点类型用 fneg(直接生成 fneg 指令,避免 0.0 - x 的类型不匹配) + operand_fbits: int = get_llvm_float_bits(operand.Ty) + if operand_fbits > 0: + return llvmlite.build_fneg(builder, operand) + # 整数类型:-x = 0 - x,根据位宽选择零值常量(避免 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: @@ -1115,29 +1170,73 @@ def _build_source_path_from_sha1(sha1: str) -> str: # ============================================================ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr, name: str, - from_imports: str) -> int: - """跨模块查找 CDefine 常量,返回值或 -1(未找到)""" - if name is None or from_imports is None: - stdio.printf("[XMCD] FAIL name=%s reason=from_imports_is_none\n", name) + from_imports: str, + imported_modules: str = None) -> int: + """跨模块查找 CDefine 常量,返回值或 -1(未找到) + + 查找顺序: + 1. from_imports 精确匹配(from X import NAME) + 2. from_imports star 回退(from X import *) + 3. imported_modules 遍历(import X):当通过 import X 导入模块时, + 模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用) + """ + if name is None: return -1 + if from_imports is None: + from_imports = "" # 1. 从 from_imports 查找名称对应的模块名 # 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导 mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0) if mod_name_raw is None: # 精确匹配失败:尝试 star import 回退 - # CDefine 常量(如 INVALID_HANDLE_VALUE)通过 from w32.win32base import * 导入, - # 不会作为精确条目出现在 from_imports 中,而是作为 *:w32.win32base 条目。 mod_name_raw = HandlesImports.lookup_from_import(from_imports, name, 1) - if mod_name_raw is None: - stdio.printf("[XMCD] FAIL name=%s reason=lookup_from_import_returned_none\n", name) + + # 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6) + # 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中, + # 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules + if mod_name_raw is None: + # from_imports 完全没有匹配(精确和 star 都失败) + # 直接进入 imported_modules 遍历(步骤 9.6) + if imported_modules is None: return -1 - stdio.printf("[XMCD] star_fallback name=%s mod=%s\n", name, mod_name_raw) - # 打印 from_imports 用于诊断精确匹配失败原因 - fi_len: t.CSizeT = string.strlen(from_imports) - stdio.printf("[XMCD] from_imports (len=%d): %s\n", fi_len, from_imports) - else: - stdio.printf("[XMCD] exact_match name=%s mod=%s\n", name, mod_name_raw) + # 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历 + # 设置 result_found=0 跳过步骤 3-9,直接到 9.6 + HandlesType.set_cdefine_found(0) + # 直接遍历 imported_modules + im_len: t.CSizeT = string.strlen(imported_modules) + im_ci: t.CSizeT = 0 + while im_ci < im_len: + while im_ci < im_len and imported_modules[im_ci] == ' ': + im_ci += 1 + if im_ci >= im_len: + break + im_start: t.CSizeT = im_ci + while im_ci < im_len and imported_modules[im_ci] != ' ': + im_ci += 1 + im_end: t.CSizeT = im_ci + im_nlen: t.CSizeT = im_end - im_start + if im_nlen > 0 and im_nlen < 256: + im_buf: str = pool.alloc(im_nlen + 1) + if im_buf is not None: + mk: t.CSizeT = 0 + while mk < im_nlen: + im_buf[mk] = imported_modules[im_start + mk] + mk += 1 + im_buf[im_nlen] = '\0' + sub_fi: str = pool.alloc(im_nlen + 3) + if sub_fi is not None: + sub_fi[0] = '*' + sub_fi[1] = ':' + mk2: t.CSizeT = 0 + while mk2 < im_nlen: + sub_fi[2 + mk2] = im_buf[mk2] + mk2 += 1 + sub_fi[2 + im_nlen] = '\0' + sub_val: int = _lookup_cross_module_cdefine(pool, name, sub_fi, None) + if HandlesType.is_cdefine_found() != 0: + return sub_val + return -1 # 2. 复制模块名到新缓冲区(lookup_from_import 返回的是内部指针) # 截断于空格、null、或 ':'(别名格式的分隔符) @@ -1161,7 +1260,6 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr, if sha1 is None: sha1 = HandlesExprCall._lookup_module_sha1_suffix(base_mod) if sha1 is None: - stdio.printf("[XMCD] FAIL name=%s base_mod=%s reason=sha1_not_found\n", name, base_mod) return -1 # 5. 获取 temp_dir @@ -1529,6 +1627,45 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr, stdlib.free(pyi_buf) + # 9.6 from_imports star fallback 模块中未找到:回退到 imported_modules 遍历 + # 当 `from stdint import *` 导致 star fallback 返回 stdint,但目标常量 + # (如 SHARE_READ)实际在 `import w32.fileio` 导入的模块中时, + # 需要遍历 imported_modules 逐个查找 + if result_found == 0 and imported_modules is not None: + im2_len: t.CSizeT = string.strlen(imported_modules) + im2_ci: t.CSizeT = 0 + while im2_ci < im2_len and result_found == 0: + while im2_ci < im2_len and imported_modules[im2_ci] == ' ': + im2_ci += 1 + if im2_ci >= im2_len: + break + im2_start: t.CSizeT = im2_ci + while im2_ci < im2_len and imported_modules[im2_ci] != ' ': + im2_ci += 1 + im2_end: t.CSizeT = im2_ci + im2_nlen: t.CSizeT = im2_end - im2_start + if im2_nlen > 0 and im2_nlen < 256: + im2_buf: str = pool.alloc(im2_nlen + 1) + if im2_buf is not None: + mk3: t.CSizeT = 0 + while mk3 < im2_nlen: + im2_buf[mk3] = imported_modules[im2_start + mk3] + mk3 += 1 + im2_buf[im2_nlen] = '\0' + sub_fi2: str = pool.alloc(im2_nlen + 3) + if sub_fi2 is not None: + sub_fi2[0] = '*' + sub_fi2[1] = ':' + mk4: t.CSizeT = 0 + while mk4 < im2_nlen: + sub_fi2[2 + mk4] = im2_buf[mk4] + mk4 += 1 + sub_fi2[2 + im2_nlen] = '\0' + sub_val2: int = _lookup_cross_module_cdefine(pool, name, sub_fi2, None) + if HandlesType.is_cdefine_found() != 0: + result_val = sub_val2 + result_found = 1 + # 10. 缓存到当前模块的 CDefine 表中 if result_found != 0: HandlesType.register_cdefine_constant(pool, name, result_val) @@ -1592,8 +1729,9 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr, # 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找, # 避免对普通变量名(mb/name/ptr/pool/self 等)误触发,造成严重性能损耗 if _is_cdefine_name_pattern(nm_id) != 0: - if trans is not None and trans._from_imports is not None: - cdef_val = _lookup_cross_module_cdefine(pool, nm_id, trans._from_imports) + if trans is not None: + cdef_val = _lookup_cross_module_cdefine( + pool, nm_id, trans._from_imports, trans._imported_modules) if HandlesType.is_cdefine_found() != 0: return llvmlite.const_int32(pool, cdef_val) @@ -2121,6 +2259,92 @@ def make_global_ref(pool: memhub.MemBuddy | t.CPtr, # ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退) # 返回: Value 指针 (类型为 ty*),未识别为模块属性返回 None # ============================================================ +def _is_t_type_object_name(name: str) -> int: + """检查 name 是否是 t.py 中的类型对象名(CPtr/CInt/CDouble 等) + + 这些类型对象在运行时作为全局变量 @CPtr/@CInt 等被引用, + 但 t.py 不被编译,需要在使用处生成带 null 初值的定义。 + """ + if name is None: + return 0 + # t.py 中的类型对象名(PascalCase,首字母大写) + if string.strcmp(name, "CPtr") == 0: + return 1 + if string.strcmp(name, "CInt") == 0: + return 1 + if string.strcmp(name, "CChar") == 0: + return 1 + if string.strcmp(name, "CShort") == 0: + return 1 + if string.strcmp(name, "CLong") == 0: + return 1 + if string.strcmp(name, "CLongLong") == 0: + return 1 + if string.strcmp(name, "CDouble") == 0: + return 1 + if string.strcmp(name, "CFloat") == 0: + return 1 + if string.strcmp(name, "CBool") == 0: + return 1 + if string.strcmp(name, "CVoid") == 0: + return 1 + if string.strcmp(name, "CInt8T") == 0: + return 1 + if string.strcmp(name, "CInt16T") == 0: + return 1 + if string.strcmp(name, "CInt32T") == 0: + return 1 + if string.strcmp(name, "CInt64T") == 0: + return 1 + if string.strcmp(name, "CUInt8T") == 0: + return 1 + if string.strcmp(name, "CUInt16T") == 0: + return 1 + if string.strcmp(name, "CUInt32T") == 0: + return 1 + if string.strcmp(name, "CUInt64T") == 0: + return 1 + if string.strcmp(name, "CUnsigned") == 0: + return 1 + if string.strcmp(name, "CUnsignedInt") == 0: + return 1 + if string.strcmp(name, "CUnsignedChar") == 0: + return 1 + if string.strcmp(name, "CUnsignedShort") == 0: + return 1 + if string.strcmp(name, "CUnsignedLong") == 0: + return 1 + if string.strcmp(name, "CUnsignedLongLong") == 0: + return 1 + if string.strcmp(name, "CSizeT") == 0: + return 1 + if string.strcmp(name, "CIntPtrT") == 0: + return 1 + if string.strcmp(name, "CUIntPtrT") == 0: + return 1 + if string.strcmp(name, "CPtrDiffT") == 0: + return 1 + if string.strcmp(name, "CType") == 0: + return 1 + if string.strcmp(name, "CDefine") == 0: + return 1 + if string.strcmp(name, "CStatic") == 0: + return 1 + if string.strcmp(name, "CExport") == 0: + return 1 + if string.strcmp(name, "CArray") == 0: + return 1 + if string.strcmp(name, "CTypedef") == 0: + return 1 + if string.strcmp(name, "CNoVTable") == 0: + return 1 + if string.strcmp(name, "CNeedPtr") == 0: + return 1 + if string.strcmp(name, "CAutoPtr") == 0: + return 1 + return 0 + + def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, trans: HT.Translator | t.CPtr, @@ -2150,10 +2374,33 @@ def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr, use_ty = llvmlite.Int8(pool) # 确保 use_ty 中的跨模块结构体类型有 opaque 声明 llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty) + # 特殊处理: t.py 中的类型对象(CPtr/CInt/CDouble 等) + # t.py 不被编译,但这些类型对象在运行时被引用(如 va_arg(ap, t.CPtr))。 + # 创建带 null 初值的 linkonce_odr 全局变量定义,避免链接时 undefined reference。 + # LLVM 22+ 不允许 external linkage 全局变量带初值,使用 linkonce_odr 替代。 + is_t_type_obj: int = 0 + if string.strcmp(mod_nm.id, "t") == 0: + is_t_type_obj = _is_t_type_object_name(at.attr) 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 + if is_t_type_obj != 0: + # t.CPtr 等类型对象: 创建带 null 初值的定义(linkonce_odr) + ext_gv.Linkage = "linkonce_odr" + # i8* null 的初值文本 + null_init: bytes = pool.alloc(8) + if null_init is not None: + null_init[0] = 'n' + null_init[1] = 'u' + null_init[2] = 'l' + null_init[3] = 'l' + null_init[4] = '\0' + ext_gv.Initializer = null_init + else: + ext_gv.Initializer = None + else: + # 普通模块属性: external 声明(由其他模块提供定义) + ext_gv.Linkage = "external" + ext_gv.Initializer = None llvmlite.module_add_global(mod, ext_gv) return make_global_ref(pool, at.attr, use_ty) @@ -2302,6 +2549,24 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, is_submod = 1 if is_submod != 0: return None # 是子模块,交给嵌套属性访问路径 + # 特殊处理: t.ASM_DESCR 不是 CDefine,是内联汇编约束命名空间 + # t.ASM_DESCR.XXX 由 HandlesExprCall 的 _asm_resolve_constraint 处理 + # 此处返回 None,让上层嵌套属性访问路径处理 + if string.strcmp(nm_ma.id, "t") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0: + return None + # CDefine 未找到且属性名符合 ALL_CAPS 约定: 报 FATAL 错误 + # 不再创建未定义的外部全局变量引用(否则 llc 报 "use of undefined value") + if _is_cdefine_name_pattern(at.attr) != 0: + err_buf_sma: str = pool.alloc(256) + if err_buf_sma is not None: + cur_sha1_sma: str = "(unknown)" + if trans is not None and trans.ModuleSha1 is not None: + cur_sha1_sma = trans.ModuleSha1 + viperlib.snprintf(err_buf_sma, 256, + "[CD] FATAL: Module attr '%s.%s' NOT found (CDefine cross-module lookup failed) in module sha1=%s\n", + nm_ma.id, at.attr, cur_sha1_sma) + VLogger.error(err_buf_sma, "CD") + 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( @@ -2347,14 +2612,21 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, 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) + # CDefine 未找到: 报 FATAL 错误(不再创建未定义的全局变量引用) + # 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义, + # 导致 llc 报 "use of undefined value" 错误。 + # 正确做法是报错终止,让开发者修复跨模块 CDefine 查找失败的根本原因。 + if _is_cdefine_name_pattern(at.attr) != 0: + err_buf_ma: str = pool.alloc(256) + if err_buf_ma is not None: + cur_sha1_ma: str = "(unknown)" + if trans is not None and trans.ModuleSha1 is not None: + cur_sha1_ma = trans.ModuleSha1 + viperlib.snprintf(err_buf_ma, 256, + "[CD] FATAL: Nested attr '%s.%s' NOT found (CDefine cross-module lookup failed) in module sha1=%s\n", + qual_buf, at.attr, cur_sha1_ma) + VLogger.error(err_buf_ma, "CD") + return None # 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体) obj_ptr: llvmlite.Value | t.CPtr = None @@ -2401,7 +2673,7 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, 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 按类名查找 + # 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找 # 传递 SHA1 以区分跨模块同名类 if field_info is None: if at.value.kind() == ast.ASTKind.Name and trans is not None: @@ -2413,7 +2685,7 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, cur_sha1: str = trans.ModuleSha1 field_info = HandlesStruct.lookup_field_by_class( var_entry.AnnotClassName, at.attr, cur_sha1) - # 回退 1 成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 + # 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 # 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)), # 需用实际结构体类型做 GEP,否则 GEP i8 失败 if field_info is not None: @@ -2427,33 +2699,11 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, if casted_ptr is not None: obj_ptr = casted_ptr struct_ty = annot_se.Ty - # 回退 1 更新 struct_ty 后,需重新确保 + # 按需过滤更新 struct_ty 后,需重新确保 # 注解类型结构体定义在当前模块可用 # (原始 struct_ty 可能是 i8,ensure 无效) HandlesStruct.ensure_struct_def_in_module( pool, mod, annot_se.Ty) - # 回退 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 @@ -2483,6 +2733,28 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, 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 + # fast-fail: 字段未找到,打印诊断信息 + if trans is not None: + diag_annot: str = None + diag_var_nm: str = None + if at.value is not None and at.value.kind() == ast.ASTKind.Name: + dnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + diag_var_nm = dnm.id + if dnm.id is not None: + dve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( + trans.SymTab, dnm.id) + if dve is not None: + diag_annot = dve.AnnotClassName + fb_attr: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_attr is not None: + viperlib.snprintf(fb_attr, 1024, + "属性访问失败: .%s (var=%s annot=%s sha1=%s:%d)", + at.attr, + diag_var_nm if diag_var_nm is not None else "?", + diag_annot if diag_annot is not None else "None", + trans.ModuleSha1 if trans.ModuleSha1 is not None else "?", + at.lineno if at.lineno is not None else 0) + VLogger.error(fb_attr, "ATTR") return None case _: return None @@ -2620,6 +2892,15 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr, if obj_ptr is None and at.value.kind() == ast.ASTKind.Name and trans is not None: nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) if nm_wma.id is not None: + # CDefine 常量不可写: 报 FATAL 错误,不创建未定义全局 + if _is_cdefine_name_pattern(at.attr) != 0: + err_buf_wma: str = pool.alloc(256) + if err_buf_wma is not None: + viperlib.snprintf(err_buf_wma, 256, + "[CD] FATAL: Cannot assign to CDefine constant '%s.%s'\n", + nm_wma.id, at.attr) + VLogger.error(err_buf_wma, "CD") + return None # 使用 i8* 作为类型提示 (模块级变量通常存储指针) i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) wma_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global( @@ -2645,7 +2926,7 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr, HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field( struct_ty, at.attr) - # 回退 1: 类型指针比较失败时,通过 AnnotClassName 按类名查找 + # 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找 # 传递 SHA1 以区分跨模块同名类 if field_info is None: if at.value.kind() == ast.ASTKind.Name and trans is not None: @@ -2659,7 +2940,7 @@ 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 对应的结构体类型 + # 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 # 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)), # 需用实际结构体类型做 GEP,否则 GEP i8 失败 if field_info is not None: @@ -2673,32 +2954,10 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr, if casted_ptr is not None: obj_ptr = casted_ptr struct_ty = annot_se.Ty - # 回退 1 更新 struct_ty 后,需重新确保 + # 按需过滤更新 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: @@ -2707,6 +2966,28 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr, # 普通结构体:GEP 获取字段指针 return llvmlite.build_gep_struct( builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index) + # fast-fail: 字段未找到,打印诊断信息 + if trans is not None: + diag_annot_g: str = None + diag_var_nm_g: str = None + if at.value is not None and at.value.kind() == ast.ASTKind.Name: + dnm_g: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + diag_var_nm_g = dnm_g.id + if dnm_g.id is not None: + dve_g: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( + trans.SymTab, dnm_g.id) + if dve_g is not None: + diag_annot_g = dve_g.AnnotClassName + fb_attr_g: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_attr_g is not None: + viperlib.snprintf(fb_attr_g, 1024, + "属性写入失败: .%s (var=%s annot=%s sha1=%s:%d)", + at.attr, + diag_var_nm_g if diag_var_nm_g is not None else "?", + diag_annot_g if diag_annot_g is not None else "None", + trans.ModuleSha1 if trans.ModuleSha1 is not None else "?", + at.lineno if at.lineno is not None else 0) + VLogger.error(fb_attr_g, "ATTR") return None case _: return None diff --git a/App/lib/core/Handles/HandlesExprCall.py b/App/lib/core/Handles/HandlesExprCall.py index 6f5c6ff..7b99c5f 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 w32.fileio as fileio import lib.core.VLogger as VLogger import lib.core.Handles.HandlesBase as HandlesBase import lib.core.Handles.HandlesTranslator as HT @@ -2257,13 +2258,11 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, "__before_init__", storage_ptr, None, 0, trans) # 如果有 __init__,调用 __init__(ptr, args...) + # 注意: 仅依据 ctor_entry.HasInit 判断,不强制推断 + # (JsonValue 等类有 __new__ 但无 __init__,强制推断会导致 undefined reference) 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) @@ -2283,8 +2282,9 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, _call_method_on_ptr(pool, builder, mod, class_name, "__init__", storage_ptr, arg_vals, real_arg_count, trans) - else: - # 无 __init__:逐字段 store 参数值 + elif has_new_flag == 0: + # 无 __init__ 且无 __new__:逐字段 store 参数值 + # (有 __new__ 时参数已由 __new__ 消费,不应存入字段) for ai in range(can): arg: ast.AST | t.CPtr = cargs.get(ai) if arg is None: @@ -2974,6 +2974,262 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr, # 与 _infer_external_func_ret_ty 的默认策略保持一致 return llvmlite.Int32(pool) + +# ============================================================ +# 跨模块方法返回类型查找(从 text.ll) +# +# Phase A 翻译每个源文件后生成 text.ll,其中 define 行包含 +# 方法的实际返回类型。当 Phase A 翻译后续文件调用跨模块方法时, +# _infer_method_ret_ty 默认返回 i32,导致指针返回值被截断。 +# _LookupMethodRetTyFromTextLL 读取依赖模块的 text.ll,从 define +# 行提取实际返回类型,避免指针截断。 +# ============================================================ + +_g_temp_dir: str = None +_g_temp_dir_len: t.CSizeT = 0 + +# text.ll 读取缓冲区大小 +_TEXT_LL_READ_BUF_SIZE: t.CDefine = 262144 + + +# ============================================================ +# SetTempDir - 设置 temp 目录路径 +# +# 在 Phase2.py 的 Phase A 翻译前调用,使 HandlesExprCall +# 能够读取依赖模块的 text.ll 文件。 +# ============================================================ +def SetTempDir(temp_dir: str): + """设置 temp 目录路径(供跨模块方法返回类型查找使用)""" + global _g_temp_dir + global _g_temp_dir_len + _g_temp_dir = temp_dir + if temp_dir is not None: + _g_temp_dir_len = string.strlen(temp_dir) + else: + _g_temp_dir_len = 0 + + +# ============================================================ +# _ParseLLVMRetTyStr - 将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType +# +# 从 define 行中提取的返回类型字符串(define 与 @ 之间的内容), +# 转换为 llvmlite.LLVMType 对象。 +# +# 支持的类型: +# void → Void +# i8/i16/i32/i64 → Int8/Int16/Int32/Int64 +# double → Double, float → Float +# 任何含 * 的类型 → Ptr(Int8)(通用指针,保留 64 位地址) +# +# Args: +# pool: 内存池 +# buf: 包含类型字符串的缓冲区 +# start: 类型字符串起始位置 +# end: 类型字符串结束位置(不含) +# +# Returns: +# LLVM 类型对象,或 None(无法解析) +# ============================================================ +def _ParseLLVMRetTyStr(pool: memhub.MemBuddy | t.CPtr, + buf: bytes, + start: t.CSizeT, + end: t.CSizeT) -> llvmlite.LLVMType | t.CPtr: + """将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType""" + if pool is None or buf is None or start >= end: + return None + + # 去除前导空白 + s: t.CSizeT = start + while s < end: + c: int = buf[s] + if c != ' ' and c != '\t': + break + s += 1 + # 去除尾部空白 + e: t.CSizeT = end + while e > s: + c2: int = buf[e - 1] + if c2 != ' ' and c2 != '\t': + break + e -= 1 + + ty_len: t.CSizeT = e - s + if ty_len == 0: + return None + + # 检查是否含 * (指针类型) → 返回 i8* (通用指针) + # 包括 i8*, i32*, %"name"*, { i32, i8 }* 等 + ci: t.CSizeT = s + while ci < e: + if buf[ci] == '*': + return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + ci += 1 + + # void + if ty_len == 4 and string.strncmp(buf + s, "void", 4) == 0: + return llvmlite.Void(pool) + + # double + if ty_len == 6 and string.strncmp(buf + s, "double", 6) == 0: + return llvmlite.Double(pool) + + # float + if ty_len == 5 and string.strncmp(buf + s, "float", 5) == 0: + return llvmlite.Float(pool) + + # iN (i8, i16, i32, i64) + if ty_len >= 2 and buf[s] == 'i': + bits: int = 0 + di: t.CSizeT = s + 1 + valid: int = 1 + while di < e: + d: int = buf[di] + if d >= '0' and d <= '9': + bits = bits * 10 + (d - '0') + else: + valid = 0 + break + di += 1 + if valid != 0 and bits > 0: + if bits == 8: + return llvmlite.Int8(pool) + if bits == 16: + return llvmlite.Int16(pool) + if bits == 32: + return llvmlite.Int32(pool) + if bits == 64: + return llvmlite.Int64(pool) + + # 无法解析 + return None + + +# ============================================================ +# _LookupMethodRetTyFromTextLL - 从依赖模块的 text.ll 查找方法返回类型 +# +# 读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),搜索 +# define 行中包含 "{sha1}.{class_name}.{method_name}" 的函数, +# 提取返回类型并转换为 llvmlite.LLVMType。 +# +# 解决问题: _infer_method_ret_ty 默认返回 i32,导致指针返回值 +# 被 inttoptr 截断为 32 位(coerce_to_type line 238-239)。 +# 本函数从 text.ll 获取实际返回类型,避免截断。 +# +# Args: +# pool: 内存池 +# class_sha1: 类所属模块的 SHA1 +# class_name: 类名 +# method_name: 方法名 +# +# Returns: +# LLVM 类型对象,或 None(未找到/text.ll 不存在) +# ============================================================ +def _LookupMethodRetTyFromTextLL(pool: memhub.MemBuddy | t.CPtr, + class_sha1: str, + class_name: str, + method_name: str) -> llvmlite.LLVMType | t.CPtr: + """从依赖模块的 text.ll 中查找方法返回类型""" + if pool is None or class_sha1 is None or class_name is None or method_name is None: + return None + if _g_temp_dir is None or _g_temp_dir_len == 0: + return None + + sha1_len: t.CSizeT = string.strlen(class_sha1) + if sha1_len < 2: + return None + + # 构造 text.ll 路径 (level=1): {temp_dir}/{sha1[0:2]}/{sha1}.text.ll + # 使用 subdir 缓冲区避免 %c 格式化问题(与 StubMerger._sliced_path 一致) + path_len: t.CSizeT = _g_temp_dir_len + sha1_len + 14 + path_buf: bytes = stdlib.malloc(path_len) + if path_buf is None: + return None + subdir_buf: bytes = stdlib.malloc(3) + if subdir_buf is None: + stdlib.free(path_buf) + return None + subdir_buf[0] = class_sha1[0] + subdir_buf[1] = class_sha1[1] + subdir_buf[2] = '\0' + viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll", + _g_temp_dir, subdir_buf, class_sha1) + stdlib.free(subdir_buf) + + # 读取 text.ll + read_buf: bytes = stdlib.malloc(_TEXT_LL_READ_BUF_SIZE) + if read_buf is None: + stdlib.free(path_buf) + return None + + tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R) + if tf.closed: + stdlib.free(path_buf) + stdlib.free(read_buf) + return None + text_len: t.CInt64T = tf.read_all(read_buf, _TEXT_LL_READ_BUF_SIZE) + tf.close() + stdlib.free(path_buf) + + if text_len <= 0: + stdlib.free(read_buf) + return None + if text_len < _TEXT_LL_READ_BUF_SIZE: + read_buf[text_len] = '\0' + else: + read_buf[_TEXT_LL_READ_BUF_SIZE - 1] = '\0' + + # 构造搜索模式: "{sha1}.{class_name}.{method_name}" + cls_len: t.CSizeT = string.strlen(class_name) + meth_len: t.CSizeT = string.strlen(method_name) + search_len: t.CSizeT = sha1_len + cls_len + meth_len + 3 + search_buf: bytes = stdlib.malloc(search_len) + if search_buf is None: + stdlib.free(read_buf) + return None + viperlib.snprintf(search_buf, search_len, "%s.%s.%s", + class_sha1, class_name, method_name) + search_str_len: t.CSizeT = string.strlen(search_buf) + + # 在 text.ll 中搜索 define 行包含 search_buf + buf_len: t.CSizeT = string.strlen(read_buf) + pos: t.CSizeT = 0 + result_ty: llvmlite.LLVMType | t.CPtr = None + + while pos < buf_len and result_ty is None: + ls: t.CSizeT = pos + le: t.CSizeT = pos + while le < buf_len: + if read_buf[le] == '\n': + break + le += 1 + ll: t.CSizeT = le - ls + + # 检查是否是 define 行 + if ll >= 7 and string.strncmp(read_buf + ls, "define ", 7) == 0: + # 找 @ 符号 + at_pos: t.CSizeT = ls + 7 + while at_pos < ls + ll: + if read_buf[at_pos] == '@': + break + at_pos += 1 + if at_pos < ls + ll: + # 跳过可选引号 + name_start: t.CSizeT = at_pos + 1 + if name_start < ls + ll and read_buf[name_start] == '"': + name_start += 1 + # 检查是否匹配 search_buf + if name_start + search_str_len <= ls + ll: + if string.strncmp(read_buf + name_start, search_buf, search_str_len) == 0: + # 匹配!提取返回类型: [ls+7, at_pos) + result_ty = _ParseLLVMRetTyStr(pool, read_buf, ls + 7, at_pos) + + pos = le + 1 + + stdlib.free(search_buf) + stdlib.free(read_buf) + return result_ty + + # ============================================================ # _translate_method_call - 翻译方法调用 obj.method(args) # @@ -3069,8 +3325,14 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr, if frt is not None: call_ret_ty = frt else: - # found_func 为 None(stub 未注入):根据方法名推断返回类型 - call_ret_ty = _infer_method_ret_ty(pool, method_name) + # found_func 为 None(stub 未注入):尝试从 text.ll 查找实际返回类型 + # 避免指针返回值被 _infer_method_ret_ty 默认为 i32 导致 inttoptr 截断 + looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL( + pool, cls_sha1_mc, class_name, method_name) + if looked_up_ty is not None: + call_ret_ty = looked_up_ty + else: + call_ret_ty = _infer_method_ret_ty(pool, method_name) # 翻译调用参数 cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args @@ -3521,6 +3783,9 @@ def _translate_call_with_kwargs(pool: memhub.MemBuddy | t.CPtr, # - 整数 → 指针: inttoptr (如 t.CPtr(int_val) → i8*) # - 整数 → 整数: trunc/zext (如 t.CInt8T(i32_val) → i8) # - 指针 → 指针: bitcast (如 t.CPtr(ptr_val) → i8*) +# - 整数 → 浮点: sitofp (如 t.CDouble(i32_val) → double) +# - 浮点 → 整数: fptosi (如 t.CInt(double_val) → i32) +# - 浮点 → 浮点: fpext/fptrunc (如 t.CFloat(double_val) → float) # ============================================================ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, @@ -3532,6 +3797,8 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr, val_bits: int = HandlesExpr.get_llvm_type_bits(val.Ty) target_bits: int = HandlesExpr.get_llvm_type_bits(target_ty) + val_fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty) + target_fbits: int = HandlesExpr.get_llvm_float_bits(target_ty) # 整数 → 整数: trunc/zext if val_bits != 0 and target_bits != 0: @@ -3542,12 +3809,29 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr, else: return llvmlite.build_trunc(builder, val, target_ty) + # 整数 → 浮点: sitofp + if val_bits != 0 and target_fbits != 0: + return llvmlite.build_si2fp(builder, val, target_ty) + + # 浮点 → 整数: fptosi + if val_fbits != 0 and target_bits != 0: + return llvmlite.build_fp2si(builder, val, target_ty) + + # 浮点 → 浮点: fpext/fptrunc + if val_fbits != 0 and target_fbits != 0: + if val_fbits == target_fbits: + return val + elif val_fbits < target_fbits: + return llvmlite.build_fpext(builder, val, target_ty) + else: + return llvmlite.build_fptrunc(builder, val, target_ty) + # 指针 → 整数: ptrtoint - if val_bits == 0 and target_bits != 0: + if val_bits == 0 and val_fbits == 0 and target_bits != 0: return llvmlite.build_ptrtoint(builder, val, target_ty) - # 整数 → 指针: inttoptr - if val_bits != 0 and target_bits == 0: + # 整数 → 指针: inttoptr(必须排除浮点 target,浮点 target_bits 也是 0) + if val_bits != 0 and target_bits == 0 and target_fbits == 0: return llvmlite.build_inttoptr(builder, val, target_ty) # 指针 → 指针: bitcast @@ -3763,6 +4047,28 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, if cargs is not None: can = cargs.__len__() + # 内置强转函数: str(x)/bytes(x) → i8*, int(x) → i32 + # 等价于 (t.CChar | t.CPtr)(x) / (t.CInt32T)(x) + # 单参数:str(val) → bitcast val to i8* + # int(val) → trunc/sext val to i32 + if func_name is not None and can == 1: + if string.strcmp(func_name, "str") == 0 or string.strcmp(func_name, "bytes") == 0: + # str(x) / bytes(x) → i8* (bitcast) + cast_arg: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans) + if cast_arg is not None: + i8_ptr_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + return _translate_t_type_cast(pool, builder, cast_arg, i8_ptr_ty_builtin) + return None + elif string.strcmp(func_name, "int") == 0: + # int(x) → i32 + cast_arg_int: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans) + if cast_arg_int is not None: + i32_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + return _translate_t_type_cast(pool, builder, cast_arg_int, i32_ty_builtin) + return None + # 检测 t.XXX 类型转换: t.CUInt64T(ptr), t.CPtr(val), t.CInt(val) 等 # 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用) # 支持: @@ -3805,6 +4111,19 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, # t.XXX 类型转换已确认但不满足参数条件:返回 None,不跌落到跨模块 FATAL return None + # 裸名 typedef 调用: BYTEPTR(ptr1), HANDLE(val), CHARPTR(str) 等 + # 用户通过 `from stdint import *` 导入 typedef 后,可直接用裸名做类型转换 + # map_t_type 包含所有内置 typedef 名称(BYTEPTR/HANDLE/LPCSTR/size_t 等) + if cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + bare_ty: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type(pool, func_name) + if bare_ty is not None and can == 1: + bare_arg_node: ast.AST | t.CPtr = cargs.get(0) + if bare_arg_node is not None: + bare_arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, bare_arg_node, None, 0, trans) + if bare_arg_val is not None: + return _translate_t_type_cast(pool, builder, bare_arg_val, bare_ty) + # 检测 ClassName.__sizeof__() — 返回结构体大小常量(i64) # __sizeof__ 是内置方法,不走跨模块调用路径 # 支持: Argument.__sizeof__() 和 w32.win32file.WIN32_FIND_DATAA.__sizeof__() @@ -3907,11 +4226,6 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, # 模块属性前向引用回退为 i32),GEP base 会是 i32 而非指针, # 导致 llc 报错 "base of getelementptr must be a pointer" if HandlesExpr.is_ptr_type(obj_val.Ty) == 0: - # 诊断: 输出节点类型信息帮助定位根本原因 - len_node_kind: int = len_at.value.kind() - stdio.printf( - "[LEN-DIAG] __len__ on non-ptr (kind=%d), fallback to 0\n", - len_node_kind) return llvmlite.const_int64(pool, 0) i64_ty_len: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) # 注意: 必须用 const_int64 而非 ConstInt(...,"0") @@ -4049,7 +4363,10 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, llvmlite.value_set_next(tail3, arg_val3) tail3 = arg_val3 total3: int = 1 + can - ret_ty3: llvmlite.LLVMType | t.CPtr = _infer_method_ret_ty(pool, func_name) + ret_ty3: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL( + pool, sha1_fc, cls_nm2, func_name) + if ret_ty3 is None: + ret_ty3 = _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) diff --git a/App/lib/core/Handles/HandlesFor.py b/App/lib/core/Handles/HandlesFor.py index 5e0d71f..214b98e 100644 --- a/App/lib/core/Handles/HandlesFor.py +++ b/App/lib/core/Handles/HandlesFor.py @@ -143,12 +143,26 @@ class ForHandle(HandlesBase.Mixin): trans.SymTab, var_name, var_alloca) == 0: new_vars = 1 - # 5. 存储初始值 (类型对齐: start_val 可能是 i64,需截断到 i32) + # 获取循环变量的实际元素类型(alloca 是指针类型,需解引用 Pointee) + # 当循环变量已声明为 i64(如 t.CSizeT),使用 i64 而非硬编码 i32 + # 避免 load i32, i64* / store i32, i64* 类型不匹配 + loop_var_ty: llvmlite.LLVMType | t.CPtr = i32_ty + if var_alloca is not None and var_alloca.Ty is not None: + match var_alloca.Ty: + case llvmlite.LLVMType.Ptr(pointee_ty): + if pointee_ty is not None: + loop_var_ty = pointee_ty + loop_var_bits: int = HandlesExpr.get_llvm_type_bits(loop_var_ty) + + # 5. 存储初始值 (类型对齐: start_val 可能是 i32/i64,需对齐到 loop_var_ty) init_val: llvmlite.Value | t.CPtr = start_val if start_val is not None and start_val.Ty is not None: start_bits: int = HandlesExpr.get_llvm_type_bits(start_val.Ty) - if start_bits != 0 and start_bits != 32: - init_val = llvmlite.build_trunc(builder, start_val, i32_ty) + if start_bits != 0 and start_bits != loop_var_bits: + if start_bits < loop_var_bits: + init_val = llvmlite.build_sext(builder, start_val, loop_var_ty) + else: + init_val = llvmlite.build_trunc(builder, start_val, loop_var_ty) llvmlite.build_store(builder, init_val, var_alloca) # 6. 创建基本块: cond / body / incr / end(使用 trans._label_counter,不与 SSA 名共享) @@ -173,7 +187,7 @@ class ForHandle(HandlesBase.Mixin): # 8. cond 块: load i, icmp slt i, stop, cond_br body/end llvmlite.position_at_end(builder, cond_bb) - cur_i: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, var_alloca) + cur_i: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, loop_var_ty, var_alloca) # 类型对齐: stop_val 可能是 i64 (如 range(strlen(s))),需将 cur_i 提升到 stop_val 类型 cmp_lhs: llvmlite.Value | t.CPtr = cur_i cmp_rhs: llvmlite.Value | t.CPtr = stop_val @@ -184,7 +198,7 @@ class ForHandle(HandlesBase.Mixin): if cur_bits < stop_bits: cmp_lhs = llvmlite.build_sext(builder, cur_i, stop_val.Ty) else: - cmp_rhs = llvmlite.build_trunc(builder, stop_val, i32_ty) + cmp_rhs = llvmlite.build_trunc(builder, stop_val, loop_var_ty) cond_i1: llvmlite.Value | t.CPtr = llvmlite.build_icmp( builder, llvmlite.ICMP_SLT, cmp_lhs, cmp_rhs) llvmlite.build_cond_br(builder, cond_i1, body_bb, end_bb) @@ -215,17 +229,17 @@ class ForHandle(HandlesBase.Mixin): # 10. incr 块: i = i + step, 跳回 cond llvmlite.position_at_end(builder, incr_bb) - cur_i2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, var_alloca) - # 类型对齐: step_val 可能是 i64,需截断到 i32 与 cur_i2 类型一致 + cur_i2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, loop_var_ty, var_alloca) + # 类型对齐: step_val 可能是 i32/i64,需对齐到 loop_var_ty 与 cur_i2 类型一致 incr_step: llvmlite.Value | t.CPtr = step_val if step_val is not None and step_val.Ty is not None: step_bits: int = HandlesExpr.get_llvm_type_bits(step_val.Ty) cur2_bits: int = HandlesExpr.get_llvm_type_bits(cur_i2.Ty) if step_bits != 0 and cur2_bits != 0 and step_bits != cur2_bits: if step_bits > cur2_bits: - incr_step = llvmlite.build_trunc(builder, step_val, i32_ty) + incr_step = llvmlite.build_trunc(builder, step_val, loop_var_ty) else: - incr_step = llvmlite.build_sext(builder, step_val, i32_ty) + incr_step = llvmlite.build_sext(builder, step_val, loop_var_ty) next_i: llvmlite.Value | t.CPtr = llvmlite.build_add(builder, cur_i2, incr_step) llvmlite.build_store(builder, next_i, var_alloca) llvmlite.build_br(builder, cond_bb) diff --git a/App/lib/core/Handles/HandlesIf.py b/App/lib/core/Handles/HandlesIf.py index 64f6af2..597a85d 100644 --- a/App/lib/core/Handles/HandlesIf.py +++ b/App/lib/core/Handles/HandlesIf.py @@ -58,12 +58,17 @@ class IfHandle(HandlesBase.Mixin): trans._funcs, trans._func_count, trans) # 2. 转换为 i1 条件 - # Compare/Not 表达式已返回 i1,直接使用;其他类型与 0 比较 + # Compare/Not 表达式已返回 i1,直接使用;指针类型与 null 比较;其他类型与 0 比较 if cond_val is None: cond_val = llvmlite.const_int32(pool, 0) cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty) if cond_bits == 1: cond_i1: llvmlite.Value | t.CPtr = cond_val + elif HandlesExpr.is_ptr_type(cond_val.Ty) != 0: + # 指针类型:与 null 比较(不能用整数 0,否则 llc 报 "integer constant must have integer type") + null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null") + cond_i1 = llvmlite.build_icmp( + builder, llvmlite.ICMP_NE, cond_val, null_cond) else: zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) cond_i1 = llvmlite.build_icmp( diff --git a/App/lib/core/Handles/HandlesImports.py b/App/lib/core/Handles/HandlesImports.py index 3dce049..4d9512a 100644 --- a/App/lib/core/Handles/HandlesImports.py +++ b/App/lib/core/Handles/HandlesImports.py @@ -559,21 +559,6 @@ class ImportsHandle(HandlesBase.Mixin): resolved: str = _resolve_relative_module( self.Trans.Pool, self.Trans.CurrentPackage, impf.level, impf.module) - # [IFD] 诊断 from-import 名称处理 - mod_dbg: str = "(null)" - if impf.module is not None: - mod_dbg = impf.module - pkg_dbg: str = "(null)" - if self.Trans.CurrentPackage is not None: - pkg_dbg = self.Trans.CurrentPackage - res_dbg: str = "(null)" - if resolved is not None: - res_dbg = resolved - names_dbg: t.CSizeT = 0 - if impf.names is not None: - names_dbg = impf.names.__len__() - stdio.printf("[IFD] level=%d module=%s pkg=%s resolved=%s names=%d\n", - impf.level, mod_dbg, pkg_dbg, res_dbg, names_dbg) if resolved is not None: names: list[ast.AST | t.CPtr] | t.CPtr = impf.names if names is not None: diff --git a/App/lib/core/Handles/HandlesMain.py b/App/lib/core/Handles/HandlesMain.py index aabbd8f..426bad6 100644 --- a/App/lib/core/Handles/HandlesMain.py +++ b/App/lib/core/Handles/HandlesMain.py @@ -129,11 +129,136 @@ def translate_children(trans: HT.Translator | t.CPtr, return added_total +# ============================================================ +# _build_array_initializer_text - 从 AST List 生成 LLVM IR 数组初始化器 +# +# 生成格式: [N x elem_ty] [elem_ty val0, elem_ty val1, ...] +# 用于模块级 t.CArray 列表字面量初始化(不依赖 builder,直接生成常量初始化器) +# ============================================================ +def _build_array_initializer_text(pool: memhub.MemBuddy | t.CPtr, + var_ty: llvmlite.LLVMType | t.CPtr, + list_node: ast.List | t.CPtr) -> str: + """从 AST List 节点生成 LLVM IR 数组初始化器文本,None 失败""" + if var_ty is None or list_node is None: + return None + + # 重新进行类型转换,确保 TPV 编译器正确识别 ast.List 类型 + # (避免 init_list_node 初始值为 None 时类型推断退化为 t.CPtr) + ln: ast.List | t.CPtr = (ast.List | t.CPtr)(list_node) + if ln is None: + return None + + # 从 var_ty 提取数组元素类型和数量 + elem_ty: llvmlite.LLVMType | t.CPtr = None + arr_count: t.CInt = 0 + match var_ty: + case llvmlite.LLVMType.Array(et, cnt): + elem_ty = et + arr_count = cnt + + if elem_ty is None or arr_count <= 0: + return None + + # 确定元素类型的 IR 表示 + elem_ir_ty: str = "i8" + match elem_ty: + case llvmlite.LLVMType.Int(bits): + if bits == 8: + elem_ir_ty = "i8" + elif bits == 16: + elem_ir_ty = "i16" + elif bits == 32: + elem_ir_ty = "i32" + elif bits == 64: + elem_ir_ty = "i64" + + # 获取列表元素(通过 ln.elts 而非 list_node.elts,确保类型正确识别) + elts: list[ast.AST | t.CPtr] | t.CPtr = ln.elts + if elts is None: + return None + elts_count: t.CSizeT = elts.__len__() + + # 计算缓冲区大小: 每个元素最多 "i32 -9223372036854775808, " 约 26 字符 + buf_size: t.CSizeT = 64 + elts_count * 32 + buf: t.CChar | t.CPtr = pool.alloc(buf_size) + if buf is None: + return None + + # 写入前缀: "["(类型前缀由 _print_global 输出,初始化器只需元素列表) + written: t.CInt = viperlib.snprintf(buf, buf_size, "[") + pos: t.CSizeT = t.CSizeT(written) + + # 逐个元素写入 + i: t.CSizeT = 0 + while i < elts_count: + elem_node: ast.AST | t.CPtr = elts.get(i) + # 直接从 Constant 节点提取 int_val(避免 extract_cdefine_int_value 的 t.CInt 截断) + elem_val: t.CInt64T = 0 + if elem_node is not None: + ek: int = elem_node.kind() + if ek == ast.ASTKind.Constant: + ec: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(elem_node) + if ec is not None and ec.const_kind == ast.CONST_INT: + elem_val = ec.int_val + elif ek == ast.ASTKind.UnaryOp: + # 负数: UnaryOp(USub, Constant) + elem_val = HandlesAnnAssign.extract_cdefine_int_value(elem_node) + if i > 0: + written = viperlib.snprintf(buf + pos, buf_size - pos, ", ") + pos += t.CSizeT(written) + written = viperlib.snprintf(buf + pos, buf_size - pos, "%s %lld", elem_ir_ty, elem_val) + pos += t.CSizeT(written) + i += 1 + + # 写入后缀 "]" + viperlib.snprintf(buf + pos, buf_size - pos, "]") + return buf + + +# ============================================================ +# _build_string_ptr_initializer - 为字符串字面量初始化指针类型全局变量 +# +# 创建内部字符串常量全局 @.str.{var_name} = private constant [len+1 x i8] c"...\00" +# 返回 GEP 初始化器文本: getelementptr inbounds ([len+1 x i8], [len+1 x i8]* @.str.{var_name}, i32 0, i32 0) +# 用于 b64_tab: t.CArray[t.CChar, None] = "ABC..." 等字符串初始化指针类型 +# ============================================================ +def _build_string_ptr_initializer(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + var_name: str, + str_val: str) -> str: + """为字符串字面量创建内部常量全局并返回 GEP 初始化器文本,None 失败""" + if str_val is None or var_name is None: + return None + + # 构造内部字符串常量名称: .str.{var_name} + str_name_buf: t.CChar | t.CPtr = pool.alloc(64) + if str_name_buf is None: + return None + viperlib.snprintf(str_name_buf, 64, ".str.%s", var_name) + + # 调用 llvmlite.create_global_string 创建字符串常量全局 + str_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.create_global_string( + pool, mod, str_name_buf, str_val) + if str_gv is None: + return None + + # 生成 GEP 初始化器文本 + str_len: t.CSizeT = string.strlen(str_val) + init_buf: t.CChar | t.CPtr = pool.alloc(128) + if init_buf is None: + return None + viperlib.snprintf(init_buf, 128, + "getelementptr inbounds ([%d x i8], [%d x i8]* @%s, i32 0, i32 0)", + str_len + 1, str_len + 1, str_name_buf) + return init_buf + + # ============================================================ # handle_module_level_var - 模块级变量声明 → 创建 LLVM 全局变量 # # 当用户已定义 main(无 wrapper main builder)时,模块级 # AnnAssign/Assign 创建全局变量 @var_name 并注册到 SymTab 模块作用域 +# 支持整数、列表字面量、字符串字面量初始值 # ============================================================ def handle_module_level_var(trans: HT.Translator | t.CPtr, node: ast.AST | t.CPtr) -> int: @@ -163,7 +288,9 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr, var_name: str = None var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) init_val: t.CInt64T = 0 - has_init: int = 0 + init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str + init_str_val: str = None + init_list_node: ast.List | t.CPtr = None if k == ast.ASTKind.AnnAssign: aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node) @@ -179,12 +306,20 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr, pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) if resolved is not None: var_ty = resolved - # 解析初始值 - if aa.value is not None and aa.value.kind() == ast.ASTKind.Constant: - cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value) - if cn.const_kind == ast.CONST_INT: - init_val = cn.int_val - has_init = 1 + # 解析初始值(支持整数、列表字面量、字符串字面量) + if aa.value is not None: + val_kind: int = aa.value.kind() + if val_kind == ast.ASTKind.Constant: + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value) + if cn.const_kind == ast.CONST_INT: + init_val = cn.int_val + init_kind = 1 + elif cn.const_kind == ast.CONST_STR: + init_str_val = cn.str_val + init_kind = 3 + elif val_kind == ast.ASTKind.List: + init_list_node = (ast.List | t.CPtr)(aa.value) + init_kind = 2 elif k == ast.ASTKind.Assign: asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node) if asgn is None or asgn.targets is None: @@ -197,11 +332,19 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr, return 0 nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(t0) var_name = nm2.id - if asgn.value is not None and asgn.value.kind() == ast.ASTKind.Constant: - cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(asgn.value) - if cn2.const_kind == ast.CONST_INT: - init_val = cn2.int_val - has_init = 1 + if asgn.value is not None: + val_kind2: int = asgn.value.kind() + if val_kind2 == ast.ASTKind.Constant: + cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(asgn.value) + if cn2.const_kind == ast.CONST_INT: + init_val = cn2.int_val + init_kind = 1 + elif cn2.const_kind == ast.CONST_STR: + init_str_val = cn2.str_val + init_kind = 3 + elif val_kind2 == ast.ASTKind.List: + init_list_node = (ast.List | t.CPtr)(asgn.value) + init_kind = 2 if var_name is None: return 0 @@ -225,11 +368,26 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr, # 设置初始值(有初始值时清除 external linkage,因为 LLVM 22+ 不允许 external global 带初始值) gv.Linkage = None - if has_init != 0: + if init_kind == 1: + # 整数初始值 init_buf: t.CChar | t.CPtr = pool.alloc(48) if init_buf is not None: viperlib.snprintf(init_buf, 48, "%lld", init_val) gv.Initializer = init_buf + elif init_kind == 2: + # 列表字面量 → 数组初始化器 [N x elem_ty] [elem_ty val0, ...] + arr_init: str = _build_array_initializer_text(pool, var_ty, init_list_node) + if arr_init is not None: + gv.Initializer = arr_init + else: + gv.Initializer = "zeroinitializer" + elif init_kind == 3: + # 字符串字面量 → 创建字符串常量全局 + GEP 引用 + str_init: str = _build_string_ptr_initializer(pool, mod, var_name, init_str_val) + if str_init is not None: + gv.Initializer = str_init + else: + gv.Initializer = "zeroinitializer" else: # 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer, # 整数/聚合类型也兼容 zeroinitializer,避免 "integer constant must have integer type" diff --git a/App/lib/core/Handles/HandlesReturn.py b/App/lib/core/Handles/HandlesReturn.py index a41c3f0..8669cdf 100644 --- a/App/lib/core/Handles/HandlesReturn.py +++ b/App/lib/core/Handles/HandlesReturn.py @@ -39,8 +39,10 @@ class ReturnHandle(HandlesBase.Mixin): val: llvmlite.Value | t.CPtr = None if rt.value is not None: - # return self: 直接返回指针(self 是 SSA 参数 Ptr(struct_ty)) - # translate_value 会 load 得到结构体值,但 return self 需要指针本身 + # return self: 返回 self 指针本身(Ptr(StructTy)),而非结构体值 + # translate_value 会 load 得到结构体值,但 return self 需要指针 + # 注意: lookup_var 返回的是存储 self 的 alloca (Ptr(Ptr(StructTy))) + # 需要 load 得到实际的 self 指针 (Ptr(StructTy)) if rt.value.kind() == ast.ASTKind.Name: ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rt.value) if ret_nm is not None and ret_nm.id is not None: @@ -48,7 +50,16 @@ class ReturnHandle(HandlesBase.Mixin): self_ptr: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( self.Trans.SymTab, "self") if self_ptr is not None: - val = self_ptr + # 检查 self_ptr 是否是 Ptr(Ptr(...))(alloca 存储指针) + self_pointee_ty: llvmlite.LLVMType | t.CPtr = None + match self_ptr.Ty: + case llvmlite.LLVMType.Ptr(inner_ty): + self_pointee_ty = inner_ty + if self_pointee_ty is not None and HandlesExpr.is_ptr_type(self_pointee_ty) != 0: + # Ptr(Ptr(StructTy)) → load 得到 Ptr(StructTy) + val = llvmlite.build_load(builder, self_pointee_ty, self_ptr) + else: + val = self_ptr if val is None: val = HandlesExpr.translate_value( builder, pool, mod, rt.value, diff --git a/App/lib/core/Handles/HandlesStruct.py b/App/lib/core/Handles/HandlesStruct.py index db025f6..34ccbb8 100644 --- a/App/lib/core/Handles/HandlesStruct.py +++ b/App/lib/core/Handles/HandlesStruct.py @@ -2,6 +2,7 @@ import t, c from stdint import * import memhub import string +import stdlib import llvmlite import stdio import ast @@ -521,10 +522,28 @@ def lookup_field_by_class(class_name: str, # 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个 entry = find_struct(class_name) if entry is None: - # 诊断:遍历打印所有已注册结构体名,确认目标类是否在注册表中 - for diag_i in range(_struct_count): - diag_entry: StructEntry | t.CPtr = _get_struct_entry(diag_i) + # 诊断:目标类未注册 + fb_lfbc: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_lfbc is not None: + viperlib.snprintf(fb_lfbc, 1024, + "lookup_field_by_class: 类 '%s' 未注册 (struct_count=%d)", + class_name, _struct_count) + VLogger.error(fb_lfbc, "STRUCT") return None + # 诊断:找到结构体但字段未找到,打印字段表 + fb_fields: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_fields is not None: + viperlib.snprintf(fb_fields, 1024, + "lookup_field_by_class: 类 '%s' 已注册 (FieldCount=%d) 但字段 '%s' 未找到", + class_name, entry.FieldCount, field_name) + VLogger.error(fb_fields, "STRUCT") + 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: + fb_fn: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_fn is not None: + viperlib.snprintf(fb_fn, 1024, " 字段[%d]: %s", fi, fe.Name) + VLogger.error(fb_fn, "STRUCT") 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: @@ -534,45 +553,6 @@ def lookup_field_by_class(class_name: str, 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 - - # ============================================================ # get_struct_type — 按类名获取结构体的 LLVM 类型 # ============================================================ diff --git a/App/lib/core/Handles/HandlesTranslator.py b/App/lib/core/Handles/HandlesTranslator.py index d8c67ed..2b7d100 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 viperlib import lib.core.VLogger as VLogger import lib.core.Handles.HandlesVar as HandlesVar import lib.core.Handles.HandlesExprCall as HandlesExprCall diff --git a/App/lib/core/Handles/HandlesType.py b/App/lib/core/Handles/HandlesType.py index 5449800..ecb4f7e 100644 --- a/App/lib/core/Handles/HandlesType.py +++ b/App/lib/core/Handles/HandlesType.py @@ -9,6 +9,8 @@ import sys import stdlib import viperlib import w32.fileio as fileio +import lib.core.VLogger as VLogger +import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesImports as HandlesImports import lib.core.Handles.HandlesStruct as HandlesStruct @@ -143,6 +145,95 @@ def clear_cdefine_constants() -> None: _g_cdefine_values = None +# 常量整数表达式计算成功标志(供 _eval_const_int_expr 使用) +_g_const_eval_ok: int = 0 + + +# ============================================================ +# _eval_const_int_expr - 递归计算常量整数表达式 +# +# 支持: +# - Constant (CONST_INT): 整数字面量 +# - Name: CDefine 常量名(通过 lookup_cdefine_constant 查找) +# - BinOp (Add/Sub/Mult/FloorDiv/BitOr/BitAnd): 递归计算左右操作数 +# +# 成功: 设置 _g_const_eval_ok=1, 返回计算值 +# 失败: 设置 _g_const_eval_ok=0, 返回 0 +# +# 用于 t.CArray count 的静态计算,如 2 * ZHUFF_MAX_CODES +# ============================================================ +def _eval_const_int_expr(node: ast.AST | t.CPtr) -> int: + """递归计算常量整数表达式""" + global _g_const_eval_ok + if node is None: + _g_const_eval_ok = 0 + return 0 + k: int = node.kind() + + # Constant: 整数字面量 + if k == ast.ASTKind.Constant: + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) + if cn.const_kind == ast.CONST_INT: + _g_const_eval_ok = 1 + return cn.int_val + _g_const_eval_ok = 0 + return 0 + + # Name: CDefine 常量名 + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm.id is None: + _g_const_eval_ok = 0 + return 0 + val: int = lookup_cdefine_constant(nm.id) + if is_cdefine_found() == 0: + _g_const_eval_ok = 0 + return 0 + _g_const_eval_ok = 1 + return val + + # BinOp: 算术/位运算 + if k == ast.ASTKind.BinOp: + bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) + # 递归计算左操作数 + lv: int = _eval_const_int_expr(bop.left) + if _g_const_eval_ok == 0: + return 0 + # 递归计算右操作数 + rv: int = _eval_const_int_expr(bop.right) + if _g_const_eval_ok == 0: + return 0 + # 根据运算符执行计算 + if bop.op == ast.OpKind.Add: + _g_const_eval_ok = 1 + return lv + rv + if bop.op == ast.OpKind.Sub: + _g_const_eval_ok = 1 + return lv - rv + if bop.op == ast.OpKind.Mult: + _g_const_eval_ok = 1 + return lv * rv + if bop.op == ast.OpKind.FloorDiv: + if rv == 0: + _g_const_eval_ok = 0 + return 0 + _g_const_eval_ok = 1 + return lv // rv + if bop.op == ast.OpKind.BitOr: + _g_const_eval_ok = 1 + return lv | rv + if bop.op == ast.OpKind.BitAnd: + _g_const_eval_ok = 1 + return lv & rv + # 不支持的运算符 + _g_const_eval_ok = 0 + return 0 + + # 不支持的节点类型 + _g_const_eval_ok = 0 + return 0 + + def set_generic_context(tp_names: list[str] | t.CPtr, type_args: list[str] | t.CPtr): """设置泛型特化上下文(进入泛型类方法体翻译时调用)""" @@ -850,6 +941,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, mod_name: str = HandlesImports.lookup_from_import(from_imports, nm.id) if mod_name is not None: return map_t_type(pool, nm.id) + # 兜底: 尝试 map_t_type 中的硬编码 typedef 映射(VOIDPTR, HANDLE, ULONG 等) + # 这些是跨模块通用的 C typedef 名,即使 from-import 查找失败也能解析 + mapped_ty_fallback: llvmlite.LLVMType | t.CPtr = map_t_type(pool, nm.id) + if mapped_ty_fallback is not None: + return mapped_ty_fallback return None # Attribute 节点: t.CInt, t.CUInt64T, namespace_defs.PlainStruct 等 @@ -874,6 +970,13 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, if mod_struct_ty is not None: return mod_struct_ty return None + # 嵌套 Attribute: w32.win32base.SECURITY_ATTRIBUTES 等三级及以上限定名 + # at.value 是 Attribute 而非 Name,递归提取最终类名(at.attr)查结构体表 + # find_struct 支持模块限定名回退,此处用最外层 attr(简单类名)即可 + if at.value is not None and at.value.kind() == ast.ASTKind.Attribute: + nested_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(at.attr) + if nested_struct_ty is not None: + return nested_struct_ty return None # BinOp 节点: 联合类型注解 A | B @@ -929,7 +1032,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, # value 是 Name(泛型类名),slice 是类型实参 if sub.value.kind() == ast.ASTKind.Name: gen_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value) + # tuple[...] 类型注解:TransPyV 暂不支持 tuple 返回类型,返回 None 让上层默认处理 + # 避免走到 list[str](pool, 4) 构造路径崩溃(tuple 未定义为泛型类) if gen_nm.id is not None: + if string.strcmp(gen_nm.id, "tuple") == 0: + return None # 先收集类型实参名,再分配缓冲区拼接 mangled name gen_arg_names: list[str] | t.CPtr = list[str](pool, 4) if sub.slice.kind() == ast.ASTKind.Tuple: @@ -1031,28 +1138,20 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, pool, elem_node, imported_modules, from_imports, trans) if elem_ty is None: fatal_type_error(elem_node, "t.CArray 元素类型解析失败") - # 解析 count(必须是整数常量、None 或 CDefine 常量名) + # 解析 count(支持整数常量、None、CDefine 常量名、常量表达式如 2*MAX) # t.CArray[elem_ty, None] 等价于单参数形式 → Ptr(elem_ty) # t.CArray[elem_ty, NAME] 其中 NAME: t.CDefine = value → [value x elem_ty] + # t.CArray[elem_ty, 2 * MAX] 常量表达式静态计算 → [2*MAX x elem_ty] count_val: int = 0 if count_node.kind() == ast.ASTKind.Constant: cnt_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(count_node) if cnt_cn.const_kind == ast.CONST_NONE: return llvmlite.Ptr(pool, elem_ty) - if cnt_cn.const_kind != ast.CONST_INT: - fatal_type_error(count_node, "t.CArray count 必须是整数常量、None 或 CDefine 常量名") - count_val = cnt_cn.int_val - elif count_node.kind() == ast.ASTKind.Name: - # CDefine 常量名查找 - cnt_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(count_node) - 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 is_cdefine_found() == 0: - fatal_type_error(count_node, "t.CArray count 不是已注册的 CDefine 常量") - count_val = looked_up - else: - fatal_type_error(count_node, "t.CArray count 必须是整数常量、None 或 CDefine 常量名") + # 统一用 _eval_const_int_expr 计算常量整数表达式 + # 支持 Constant(INT)、Name(CDefine)、BinOp(2*MAX 等算术/位运算) + count_val = _eval_const_int_expr(count_node) + if _g_const_eval_ok == 0: + fatal_type_error(count_node, "t.CArray count 必须是整数常量、None、CDefine 常量名或常量表达式") if count_val <= 0: fatal_type_error(count_node, "t.CArray count 必须为正数") # 创建 ArrayType(不包装 Ptr) diff --git a/App/lib/core/Handles/HandlesWhile.py b/App/lib/core/Handles/HandlesWhile.py index 397899e..58e93a9 100644 --- a/App/lib/core/Handles/HandlesWhile.py +++ b/App/lib/core/Handles/HandlesWhile.py @@ -78,10 +78,15 @@ class WhileHandle(HandlesBase.Mixin): if cond_val is None: cond_val = llvmlite.const_int32(pool, 0) - # Compare/Not 表达式已返回 i1,直接使用;其他类型与 0 比较 + # Compare/Not 表达式已返回 i1,直接使用;指针类型与 null 比较;其他类型与 0 比较 cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty) if cond_bits == 1: cond_i1: llvmlite.Value | t.CPtr = cond_val + elif HandlesExpr.is_ptr_type(cond_val.Ty) != 0: + # 指针类型:与 null 比较(不能用整数 0,否则 llc 报 "integer constant must have integer type") + null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null") + cond_i1 = llvmlite.build_icmp( + builder, llvmlite.ICMP_NE, cond_val, null_cond) else: zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) cond_i1 = llvmlite.build_icmp( diff --git a/App/lib/core/Phase1.py b/App/lib/core/Phase1.py index 9d3d0bc..c741a8d 100644 --- a/App/lib/core/Phase1.py +++ b/App/lib/core/Phase1.py @@ -73,7 +73,9 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, - def_sha1s_buf: bytes) -> int: + def_sha1s_buf: bytes, + generic_file_indices: t.CPtr, + generic_file_count_box: t.CPtr) -> 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 @@ -88,6 +90,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr, continue sha1_e: str = entry.Sha1 + file_has_generic: int = 0 # 按需翻译过滤 in_set: int = 0 @@ -147,6 +150,22 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr, if cd is None or cd.name is None: continue + # 检测泛型类(有 type_params),记录文件索引以供 Phase 1a 预注册 + if file_has_generic == 0: + tp_scan: list[str] | t.CPtr = cd.type_params + if tp_scan is not None: + file_has_generic = 1 + if generic_file_count_box is not None: + gfc_ptr_scan: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(generic_file_count_box, t.CPtr)) + if gfc_ptr_scan is not None: + gfc_scan: int = gfc_ptr_scan[0] + if gfc_scan < MAX_FILE_DEPS and generic_file_indices is not None: + gi_addr_scan: t.CUInt64T = t.CUInt64T(generic_file_indices) + gfc_scan * 4 + gi_ptr_scan: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(gi_addr_scan, t.CPtr)) + if gi_ptr_scan is not None: + gi_ptr_scan[0] = i + gfc_ptr_scan[0] = gfc_scan + 1 + if class_count >= MAX_CLASSES: break @@ -513,14 +532,15 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, continue string.strcpy(sha1_set + set_count * 17, se_ent.Sha1) set_count += 1 + # 先填充全局 SHA1 映射存储器(即使 set_count==0 也填充,确保 Phase2 能获取 includes 条目) + # 注意:PopulateSha1MapStore 会先 free 旧存储器再重新分配 + StubMerger.PopulateSha1MapStore(result) + # 写入 _sha1_map.txt(人类可读输出,程序内部不读取;机器分析使用 PopulateSha1MapStore 内存存储器) + StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0) if set_count <= 0: 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) @@ -621,6 +641,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, p1a_registered += 1 # 生成 .deps.txt(记录依赖模块名,供依赖图按需翻译使用) + # 同时填充内存依赖存储器(PopulateIncludesDeps),供 _BuildReachableSha1Set + # 直接从内存读取,不依赖 .deps.txt 过程文件 td_len_a: t.CSizeT = string.strlen(temp_dir) deps_path_a: str = StubMerger._sliced_path(temp_dir, td_len_a, sha1_a, "deps.txt") if deps_path_a is not None: @@ -631,6 +653,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, df_a.write(tr_a._imported_modules, dl_a) df_a.close() stdlib.free(deps_path_a) + # 填充内存依赖存储器(供 _BuildReachableSha1Set 使用,不读 .deps.txt) + if tr_a._imported_modules is not None: + StubMerger.PopulateIncludesDeps(sha1_a, tr_a._imported_modules) # 释放 Translator 的 C malloc 资源 if tr_a._global_names is not None: @@ -689,6 +714,12 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, 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) + generic_file_indices: t.CPtr = mb.alloc(4 * MAX_FILE_DEPS) + generic_file_count_box: t.CPtr = mb.alloc(4) + if generic_file_indices is not None: + string.memset(generic_file_indices, 0, 4 * MAX_FILE_DEPS) + if generic_file_count_box is not None: + string.memset(generic_file_count_box, 0, 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: @@ -697,11 +728,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, 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) + if generic_file_indices is not None: + string.memset(generic_file_indices, 0, 4 * MAX_FILE_DEPS) + if generic_file_count_box is not None: + string.memset(generic_file_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) + class_names_buf, parent_names_buf, def_sha1s_buf, + generic_file_indices, generic_file_count_box) if class_count_scanned > 0: ret_topo: int = _TopoSortFiles( @@ -726,6 +762,87 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1") topo_count = 0 + # ============================================================ + # Phase 1a-pre-generic: 预注册所有泛型类模板 + # + # 在 Phase 1a(struct 注册)之前执行,确保所有泛型类模板 + #(如 GSListNode[T]、GSList[T])已注册。这样当其他文件的 + # 结构体字段使用 GSListNode[Value] 等特化类型时,模板能被找到。 + # ============================================================ + generic_file_count: int = 0 + if generic_file_count_box is not None: + gfc_box_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(generic_file_count_box, t.CPtr)) + if gfc_box_ptr is not None: + generic_file_count = gfc_box_ptr[0] + + if generic_file_count > 0 and generic_file_indices is not None: + fb_gpre: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_gpre is not None: + viperlib.snprintf(fb_gpre, 1024, "预注册 %d 个泛型类模板文件", generic_file_count) + VLogger.info(fb_gpre, "Phase1") + + for gi in range(generic_file_count): + gi_addr_p: t.CUInt64T = t.CUInt64T(generic_file_indices) + gi * 4 + gi_ptr_p: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(gi_addr_p, t.CPtr)) + if gi_ptr_p is None: + continue + file_idx_gen: int = gi_ptr_p[0] + + gen_entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + file_idx_gen * entry_size + gen_entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(gen_entry_addr, t.CPtr)) + if gen_entry is None or gen_entry.Sha1 is None: + continue + + gen_path: str = gen_entry.Path + gen_f: fileio.File | t.CPtr = fileio.File(gen_path, fileio.MODE.R) + if gen_f.closed: + continue + + gen_src: bytes = stdlib.malloc(SRC_BUF_SIZE) + if gen_src is None: + gen_f.close() + continue + + gen_br: LONG = gen_f.read_all(gen_src, SRC_BUF_SIZE) + gen_f.close() + if gen_br <= 0: + stdlib.free(gen_src) + continue + if gen_br < SRC_BUF_SIZE: + gen_src[gen_br] = 0 + else: + gen_src[SRC_BUF_SIZE - 1] = 0 + + gen_lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) + if gen_lx is None: + stdlib.free(gen_src) + continue + ast._lexer_init(gen_lx, gen_src, mb) + gen_tokens: ast.Token | t.CPtr = ast.tokenize(gen_lx) + gen_tree: ast.AST | t.CPtr = ast.parse_tokens(mb, gen_tokens) + if gen_tree is None: + stdlib.free(gen_src) + continue + + gen_tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() + if gen_tr is None: + stdlib.free(gen_src) + continue + gen_tr.ModuleSha1 = gen_entry.Sha1 + gen_tr._declare_only = 1 + gen_tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, gen_entry.RelPath) + HandlesType.set_current_file(gen_path) + HandlesType.set_current_module_sha1(gen_entry.Sha1) + HandlesType.clear_cdefine_constants() + HandlesStruct.reset_visible_structs(mb, 0) + gen_tr.translate(gen_tree) + + if gen_tr._global_names is not None: + stdlib.free(gen_tr._global_names) + if gen_tr._nonlocal_names is not None: + stdlib.free(gen_tr._nonlocal_names) + stdlib.free(gen_src) + # 遍历文件:优先使用拓扑顺序,回退到字母序 iter_count: int = topo_count if topo_count > 0 else result.Count for i in range(iter_count): @@ -834,6 +951,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, mb.free(topo_order) if topo_count_box is not None: mb.free(topo_count_box) + if generic_file_indices is not None: + mb.free(generic_file_indices) + if generic_file_count_box is not None: + mb.free(generic_file_count_box) # ============================================================ # Phase 1b: 全量翻译(struct 已注册,走 existing 路径翻译方法体) diff --git a/App/lib/core/Phase2.py b/App/lib/core/Phase2.py index 0cf358b..0b1593a 100644 --- a/App/lib/core/Phase2.py +++ b/App/lib/core/Phase2.py @@ -19,6 +19,7 @@ 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.IncludesScanner as IncludesScanner import lib.core.StubMerger as StubMerger import lib.Projectrans.Utils as Utils import lib.Projectrans.Config as Config @@ -207,6 +208,19 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, BuildPipeline.ensure_dir(temp_dir) BuildPipeline.ensure_dir(output_dir) + # === 0.5 扫描 includes 目录填充 Sha1Store(消除 Phase1 依赖)=== + # Phase2 不依赖 Phase1 的副作用:如果 Sha1Store 为空(Phase1 未运行或提前返回), + # 自行扫描 includes 目录并填充 Sha1Store,确保后续"编译缺失 includes"逻辑能正常工作 + if includes_dir is not None: + if StubMerger.GetSha1StoreCount() == 0: + inc_scan_result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir) + if inc_scan_result is not None: + inc_filled: int = StubMerger.PopulateSha1MapStore(inc_scan_result) + fb_inc: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_inc is not None: + viperlib.snprintf(fb_inc, 1024, "Phase2 自主填充 includes Sha1Store: %d 个", inc_filled) + VLogger.info(fb_inc, "project") + # 追加 App 源文件 SHA1 到 _sha1_map.txt(人类可读输出,程序内部不读取此文件) # 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py) # 同步追加到内存存储器(AppendToSha1MapStore,供跨模块 CDefine 查找) @@ -296,10 +310,62 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # === 2. Phase A: 为每个文件生成 stub + text === + # app_deps_buf: 收集所有 App 源文件的直接依赖模块名(空格分隔) + # 供 Phase B+ 的 _BuildReachableSha1Set 作为初始 worklist,实现按需编译 + # 声明在 if 块外,确保 Phase B+ 和清理代码能访问 + APP_DEPS_BUF_SIZE: t.CSizeT = 8192 + app_deps_buf: bytes = None + app_deps_len: t.CSizeT = 0 if do_phase1 != 0: if log is not None: log.banner("Phase A: 生成 stub + text") + # === Phase A-pre-inc: includes 预注册 struct + 依赖填充 === + # 必须在 App 源文件预注册之前执行:App 源文件(strict_mode=1)可能继承 + # 或引用 includes 中的 struct(如 ast.AST)。若 includes struct 未注册, + # App 源文件翻译会失败 → .obj 缺失 → undefined reference。 + # 同时填充 PopulateIncludesDeps,供 Phase B+ 的 _BuildReachableSha1Set 使用。 + # 多遍扫描:解决跨文件继承问题(如 Constant(AST) 在 AST 未注册时被跳过)。 + if includes_dir is not None and includes_binary_dir is not None: + store_count_inc_pre: int = StubMerger.GetSha1StoreCount() + if store_count_inc_pre > 0: + store_sha1_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr() + store_rel_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() + inc_dir_len_pre: t.CSizeT = string.strlen(includes_dir) + deps_filled_pre: int = 0 + PHASE_A_INC_MAX_PASSES: t.CInt = 3 + for pass_inc_i in range(PHASE_A_INC_MAX_PASSES): + struct_count_before_inc: int = HandlesStruct.get_struct_count() + for si_inc in range(store_count_inc_pre): + inc_sha1_pre: str = store_sha1_inc_pre + t.CSizeT(si_inc) * 17 + inc_rel_pre: str = store_rel_inc_pre + t.CSizeT(si_inc) * StubMerger.MAX_REL_PATH_LEN + if inc_rel_pre is None or inc_rel_pre[0] == '\0': + continue + rel_len_pre: t.CSizeT = string.strlen(inc_rel_pre) + full_path_pre: bytes = stdlib.malloc(inc_dir_len_pre + rel_len_pre + 2) + if full_path_pre is None: + continue + viperlib.snprintf(full_path_pre, inc_dir_len_pre + rel_len_pre + 2, + "%s/%s", includes_dir, inc_rel_pre) + pkg_inc_pre: str = HandlesImports.compute_package_from_relpath(mb, inc_rel_pre) + tr_inc_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans( + mb, full_path_pre, inc_sha1_pre, pkg_inc_pre, 1) + stdlib.free(full_path_pre) + if tr_inc_pre is not None: + # 依赖填充只在第一遍执行(PopulateIncludesDeps 内部有去重) + if pass_inc_i == 0: + if tr_inc_pre._imported_modules is not None: + if StubMerger.PopulateIncludesDeps(inc_sha1_pre, tr_inc_pre._imported_modules) == 0: + deps_filled_pre = deps_filled_pre + 1 + struct_count_after_inc: int = HandlesStruct.get_struct_count() + # 收敛检查:本遍没有新结构体注册 → 所有类已注册 + if struct_count_after_inc == struct_count_before_inc: + break + fb_inc_pre: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_inc_pre is not None: + viperlib.snprintf(fb_inc_pre, 1024, "includes 预注册 + 依赖填充: %d/%d", deps_filled_pre, store_count_inc_pre) + VLogger.info(fb_inc_pre, "prePhaseA") + # === Phase A-pre: 预注册所有源文件的 struct/enum/union === # 解决循环引用问题:circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义 # 仅注册 struct/enum/union(declare_only=1),不翻译方法体 @@ -334,6 +400,18 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, PHASE_A_IR_SIZE: t.CSizeT = 1048576 td_len_pa: t.CSizeT = string.strlen(temp_dir) + # 设置 temp 目录路径,使 HandlesExprCall 能读取依赖模块的 text.ll + # 用于跨模块方法返回类型查找(避免指针返回值被截断为 i32) + HandlesExprCall.SetTempDir(temp_dir) + + # app_deps_buf: 收集所有 App 源文件的直接依赖模块名(空格分隔) + # 供 Phase B+ 的 _BuildReachableSha1Set 作为初始 worklist 使用 + # 声明已在 if 块外,此处仅赋值 + app_deps_buf = stdlib.malloc(APP_DEPS_BUF_SIZE) + app_deps_len = 0 + if app_deps_buf is not None: + app_deps_buf[0] = '\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)) @@ -394,8 +472,21 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, df_a.close() stdlib.free(deps_path_a) + # 收集 _imported_modules 到 app_deps_buf(供 Phase B+ 按需编译) + if tr_a._imported_modules is not None and app_deps_buf is not None: + im_len_a: t.CSizeT = string.strlen(tr_a._imported_modules) + if im_len_a > 0 and app_deps_len + im_len_a + 1 < APP_DEPS_BUF_SIZE: + if app_deps_len > 0: + app_deps_buf[app_deps_len] = ' ' + app_deps_len = app_deps_len + 1 + string.strcpy(app_deps_buf + app_deps_len, tr_a._imported_modules) + app_deps_len = app_deps_len + im_len_a + app_deps_buf[app_deps_len] = '\0' + if do_phase2 == 0: VLogger.info("Phase A 完成(仅 stub 生成)", "project") + if app_deps_buf is not None: + stdlib.free(app_deps_buf) stdlib.free(entries) return 0 @@ -404,8 +495,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if log is not None: log.banner("Phase B: 编译 .obj") - # 收集 .obj 路径 - OBJ_PATHS_SIZE: t.CSizeT = 8192 + # 收集 .obj 路径(增大到 64KB,避免 includes .obj 路径溢出导致链接丢失符号) + # reachable_set/reachable_count: Phase B+ 填充,Phase C 用于按需链接 + reachable_set: bytes = None + reachable_count: int = 0 + OBJ_PATHS_SIZE: t.CSizeT = 65536 obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE) if obj_paths is None: return 1 @@ -457,6 +551,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, sys.exit(1) compiled_count += 1 + # 显示编译完成的文件 + fb_comp: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_comp is not None: + viperlib.snprintf(fb_comp, 1024, "[%d/%d] 编译完成: %s", i + 1, file_count, ent.Path) + VLogger.info(fb_comp, "PhaseB") # 构造 .obj 路径,检测是否是 main 模块(test_main.py 或 main.py) od_len: t.CSizeT = string.strlen(output_dir) is_main_mod: int = 0 @@ -497,39 +596,85 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, stdlib.free(entries) return 1 - # === 3.5 编译缺失的 includes 文件 === - # includes.binary 可能缺少某些 includes .obj(如 testcheck.py), - # 这些文件被用户项目导入但未被 TransPyV 自身依赖,Projectrans.py 未编译它们。 - # 检测并编译缺失的 includes 文件到 output_dir,加入链接命令。 - # 数据来源:StubMerger 全局内存存储器(不读取 _sha1_map.txt 文件) + # === 3.5 编译缺失的 includes 文件(按依赖图按需编译)=== + # 只编译被 App 源文件直接/间接引用的 includes 文件(可达集合), + # 而非遍历整个 includes 目录。通过 _BuildReachableSha1Set 构建依赖图。 if includes_dir is not None and includes_binary_dir is not None: inc_compiled: int = 0 td_len_mi: t.CSizeT = string.strlen(temp_dir) + + # 诊断日志:确认 Phase B+ 入口和 Sha1Store 状态 + fb_pbp_enter: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_pbp_enter is not None: + viperlib.snprintf(fb_pbp_enter, 1024, + "PhaseB+ 入口: store_count=%d includes_binary_dir=%s", + StubMerger.GetSha1StoreCount(), includes_binary_dir) + VLogger.info(fb_pbp_enter, "PhaseB+") + + # 创建 includes_binary_dir 目录(确保 .obj 能写入和 collect_obj_files 能扫描) + BuildPipeline.ensure_dir(includes_binary_dir) + + # declare_only 预处理已移至 Phase A 之前(Phase A-pre-inc), + # 确保 Phase A 翻译 App 源文件时 includes struct 已注册。 + # PopulateIncludesDeps 已在 Phase A-pre-inc 中填充。 + + # 构建可达 SHA1 集合(依赖图遍历) + # app_deps_buf 为 None 时回退到扫描 source_dir(非递归) + REACHABLE_SET_SIZE: t.CSizeT = t.CSizeT(StubMerger.MAX_INCLUDES_SHA1) * 17 + reachable_set: bytes = stdlib.malloc(REACHABLE_SET_SIZE) + reachable_count: int = 0 + if reachable_set is not None: + string.memset(reachable_set, 0, REACHABLE_SET_SIZE) + reachable_count = StubMerger._BuildReachableSha1Set( + mb, source_dir, temp_dir, reachable_set, app_deps_buf) + fb_reach: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_reach is not None: + viperlib.snprintf(fb_reach, 1024, "可达 includes SHA1: %d 个", reachable_count) + VLogger.info(fb_reach, "PhaseB+") + # fast-fail: 可达集合构建失败(依赖未找到),永不回退,直接终止 + if reachable_count < 0: + VLogger.error("可达集合构建失败(fast-fail),终止编译", "PhaseB+") + return 1 + else: + VLogger.error("reachable_set 分配失败,终止编译", "PhaseB+") + return 1 + + # 释放 app_deps_buf(已构建完 reachable_set,不再需要) + if app_deps_buf is not None: + stdlib.free(app_deps_buf) + app_deps_buf = None + # 从全局存储器获取 SHA1/模块名/rel_path 数组(直接访问器,避免 box 解引用问题) store_count_mi: int = StubMerger.GetSha1StoreCount() 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() + 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 - # 检查 .obj 是否已存在于 includes.binary + # 按需编译过滤:只处理 reachable_set 中的 includes + # 永不回退:reachable_count > 0 时严格按可达集合过滤 + if reachable_count > 0: + if StubMerger._is_in_sha1_set(inc_sha1_mi, reachable_set, reachable_count) == 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_pat_mi: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj") 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: + if check_pat_mi is not None and 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: + if check_pat_mi is not None: + stdlib.free(check_pat_mi) + stdlib.free(check_fd_mi) continue # .obj 不存在,需要编译 @@ -538,6 +683,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, 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: + stdlib.free(check_pat_mi) + if check_fd_mi is not None: + stdlib.free(check_fd_mi) continue viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1, "%s/%s", includes_dir, inc_rel_mi) @@ -558,7 +706,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, tbuf_mi[tbr_mi] = '\0' else: tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0' - # 逐行扫描: 找 define 行中含 @" 的(混淆函数名) + # 逐行扫描: 找 define 行即为实现文件 + # (不再要求行中含 @",因为 memhub 等模块的函数名 + # 如 @ca40915f11b8b6ad._align_up 不带引号但仍是实现文件) tpos_mi: t.CSizeT = 0 while tpos_mi < tbr_mi: tls_mi: t.CSizeT = tpos_mi @@ -570,21 +720,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, 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 + is_decl_mi = 0 + break stdlib.free(tbuf_mi) tf_mi.close() stdlib.free(tpath_mi) - # is_decl_mi == -1: text.ll 不存在(Phase1 未翻译此文件,可能不被项目导入) + # is_decl_mi == -1: text.ll 不存在(Phase1 未翻译此文件) # is_decl_mi == 1: 声明文件(仅 declare 无 define) - # 两种情况都跳过:不编译未被 Phase1 翻译的 includes 文件 - if is_decl_mi != 0: + # is_decl_mi == 0: 实现文件(有 define) + # 只跳过声明文件(is_decl_mi == 1);text.ll 不存在时继续,让后续翻译逻辑处理 + if is_decl_mi == 1: + stdlib.free(check_pat_mi) + if check_fd_mi is not None: + stdlib.free(check_fd_mi) + stdlib.free(src_fp_mi) continue # 尝试 BuildCombinedIR(stub/text 应已由 Phase1 生成) @@ -633,19 +782,43 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) if inc_combined_len_mi == 0: + # 诊断:检查 stub.ll/text.ll 是否存在 + diag_stub: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll") + diag_text: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") + diag_stub_ex: int = 0 + diag_text_ex: int = 0 + if diag_stub is not None: + df_diag_s: fileio.File | t.CPtr = fileio.File(diag_stub, fileio.MODE.R) + if not df_diag_s.closed: + diag_stub_ex = 1 + df_diag_s.close() + stdlib.free(diag_stub) + if diag_text is not None: + df_diag_t: fileio.File | t.CPtr = fileio.File(diag_text, fileio.MODE.R) + if not df_diag_t.closed: + diag_text_ex = 1 + df_diag_t.close() + stdlib.free(diag_text) fb: t.CChar | t.CPtr = VLogger.fmt_buf() if fb is not None: - viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", src_fp_mi) + viperlib.snprintf(fb, 1024, + "BuildCombinedIR 失败: %s (stub=%d text=%d is_decl=%d)", + src_fp_mi, diag_stub_ex, diag_text_ex, is_decl_mi) VLogger.error(fb, "PhaseB+") if inc_combined_mi is not None: stdlib.free(inc_combined_mi) + stdlib.free(check_pat_mi) + if check_fd_mi is not None: + stdlib.free(check_fd_mi) + stdlib.free(src_fp_mi) continue - # 编译为 .obj + # 编译为 .obj(输出到 includes_binary_dir,与存在性检查和链接收集一致) inc_cret_mi: int = BuildPipeline.compile_module_to_obj( - inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi, + inc_combined_mi, inc_combined_len_mi, temp_dir, includes_binary_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: @@ -654,10 +827,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, sys.exit(1) inc_compiled += 1 + # 显示编译完成的 includes 文件 + fb_inc_comp: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_inc_comp is not None: + viperlib.snprintf(fb_inc_comp, 1024, "[includes %d] 编译完成: %s", inc_compiled, src_fp_mi) + VLogger.info(fb_inc_comp, "PhaseB+") + + # 添加到 obj_paths (切片路径,从 includes_binary_dir 取) + ibd_len_mi2: t.CSizeT = string.strlen(includes_binary_dir) + inc_obj_sliced: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi2, inc_sha1_mi, "obj") - # 添加到 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: @@ -669,11 +848,25 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, obj_paths[obj_pos] = '\0' stdlib.free(inc_obj_sliced) + # 释放本次迭代的临时内存 + if check_pat_mi is not None: + stdlib.free(check_pat_mi) + + if check_fd_mi is not None: + stdlib.free(check_fd_mi) + + stdlib.free(src_fp_mi) + 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+") + # 释放 app_deps_buf(Phase B+ 块未执行时此处兜底释放) + if app_deps_buf is not None: + stdlib.free(app_deps_buf) + app_deps_buf = None + # === 4. Phase C: 链接所有 .obj → .exe === if log is not None: log.banner("Phase C: 链接") @@ -708,6 +901,18 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, final_obj_paths[fop_pos] = '\0' obj_paths_len: t.CSizeT = fop_pos + + # 暂时禁用按图链接:始终全量链接 includes.binary 中的 .obj + # 按图链接在 --clean 后 deps.txt 缺失时无法正确工作(跨包传递依赖丢失) + # 按图编译(Phase B+)仍保留:只编译 reachable_set 中的缺失 includes + # TODO: 待 deps.txt 生成机制完善后(如 Phase A 翻译所有 includes 的 imports),重新启用按图链接 + + # 释放 reachable_set(Phase B+ 已使用完毕) + if reachable_set is not None: + stdlib.free(reachable_set) + reachable_set = None + + # 始终传 includes_binary_dir 给 link_objs_to_exe,由其全量收集 .obj lret: int = BuildPipeline.link_objs_to_exe( final_obj_paths, obj_paths_len, linker_cmd, linker_flags, exe_path, diff --git a/App/lib/core/StubMerger.py b/App/lib/core/StubMerger.py index 33d0ffb..04e8ccc 100644 --- a/App/lib/core/StubMerger.py +++ b/App/lib/core/StubMerger.py @@ -51,6 +51,19 @@ _sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个 _sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个) _sha1_store_count: int # 条目数 +# 可达集合是否不完整(1=有 includes deps.txt 缺失,Phase C 应回退全量链接) +_g_reachable_incomplete: int + +# includes 依赖内存存储(不依赖 deps.txt 过程文件) +# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入此处 +# _BuildReachableSha1Set 从此处读取依赖,不读 deps.txt +_includes_deps_sha1: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个) +_includes_deps_str: bytes # 依赖字符串数组(每个 512 字节,MAX_INCLUDES 个) +_includes_deps_count: int # 条目数 + +# 单条依赖字符串最大长度 +MAX_DEPS_STR_LEN: t.CDefine = 512 + # rel_path 最大长度 MAX_REL_PATH_LEN: t.CDefine = 256 @@ -493,6 +506,85 @@ def GetSha1StoreCount() -> int: return _sha1_store_count +def IsReachableIncomplete() -> int: + """返回可达集合是否不完整(1=有 deps.txt 缺失,应回退全量链接)""" + return _g_reachable_incomplete + + +# ============================================================ +# PopulateIncludesDeps - 将 includes 文件的依赖存入内存 +# +# Phase B+ 前用 declare_only 翻译 includes 文件,提取 _imported_modules +# 后调用此函数存入内存。_BuildReachableSha1Set 从内存读取,不读 deps.txt。 +# +# Args: +# sha1: includes 文件的 SHA1(16字符) +# deps_str: 依赖模块名字符串(空格分隔,如 "t c stdint string") +# +# Returns: +# 0 成功,非 0 失败 +# ============================================================ +def PopulateIncludesDeps(sha1: str, deps_str: str) -> int: + """将 includes 文件的依赖存入内存""" + global _includes_deps_sha1, _includes_deps_str, _includes_deps_count + if sha1 is None: + return 1 + # 延迟初始化 + if _includes_deps_sha1 is None: + _includes_deps_sha1 = stdlib.malloc(MAX_INCLUDES * 17) + _includes_deps_str = stdlib.malloc(MAX_INCLUDES * MAX_DEPS_STR_LEN) + if _includes_deps_sha1 is None or _includes_deps_str is None: + return 1 + string.memset(_includes_deps_sha1, 0, MAX_INCLUDES * 17) + string.memset(_includes_deps_str, 0, MAX_INCLUDES * MAX_DEPS_STR_LEN) + _includes_deps_count = 0 + if _includes_deps_count >= MAX_INCLUDES: + return 1 + # 去重:若 SHA1 已存在则跳过 + for i in range(_includes_deps_count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(_includes_deps_sha1 + sidx, sha1) == 0: + return 0 + # 写入 SHA1 + idx: t.CSizeT = t.CSizeT(_includes_deps_count) * 17 + string.strcpy(_includes_deps_sha1 + idx, sha1) + # 写入依赖字符串 + idx2: t.CSizeT = t.CSizeT(_includes_deps_count) * MAX_DEPS_STR_LEN + if deps_str is not None: + dl: t.CSizeT = string.strlen(deps_str) + if dl < MAX_DEPS_STR_LEN: + string.strcpy(_includes_deps_str + idx2, deps_str) + else: + string.strncpy(_includes_deps_str + idx2, deps_str, MAX_DEPS_STR_LEN - 1) + else: + _includes_deps_str[idx2] = '\0' + _includes_deps_count = _includes_deps_count + 1 + return 0 + + +# ============================================================ +# LookupIncludesDeps - 从内存查找 includes 文件的依赖 +# +# Args: +# sha1: includes 文件的 SHA1(16字符) +# +# Returns: +# 依赖模块名字符串指针(空格分隔),None 表示未找到 +# ============================================================ +def LookupIncludesDeps(sha1: str) -> str: + """从内存查找 includes 文件的依赖""" + if sha1 is None or _includes_deps_sha1 is None: + return None + if _includes_deps_count <= 0: + return None + for i in range(_includes_deps_count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(_includes_deps_sha1 + sidx, sha1) == 0: + idx2: t.CSizeT = t.CSizeT(i) * MAX_DEPS_STR_LEN + return _includes_deps_str + idx2 + return None + + def GetSha1StoreArrPtr() -> bytes | t.CPtr: """返回 SHA1 数组指针(每个条目 17 字节)""" return _sha1_store_arr @@ -538,6 +630,32 @@ def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CP return _sha1_store_count +# ============================================================ +# LookupSha1RelPath - 通过 SHA1 查找相对路径 +# +# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free)。 +# 用于错误提示:将 sha1 转换为人类可读的文件路径。 +# +# Args: +# sha1: SHA1 字符串(16 字符) +# +# Returns: +# 相对路径字符串指针(只读),None=未找到 +# ============================================================ +def LookupSha1RelPath(sha1: str) -> str: + """通过 SHA1 查找相对路径,返回只读指针,None=未找到""" + if sha1 is None: + return None + if _sha1_store_arr is None or _sha1_store_rel is None: + return None + for i in range(_sha1_store_count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(_sha1_store_arr + sidx, sha1) == 0: + ridx: t.CSizeT = t.CSizeT(i) * MAX_REL_PATH_LEN + return _sha1_store_rel + ridx + return None + + # ============================================================ # _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define # @@ -1265,11 +1383,19 @@ def _AddSha1ToSet(set_buf: t.CChar | t.CPtr, count: int, sha1: str) -> int: # ============================================================ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, temp_dir: str, - reachable_set: t.CChar | t.CPtr) -> int: - """构建可达 SHA1 集合(依赖图按需翻译)""" + reachable_set: t.CChar | t.CPtr, + app_deps: str = None) -> int: + """构建可达 SHA1 集合(依赖图按需翻译) + + app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤 + """ + global _g_reachable_incomplete if source_dir is None or temp_dir is None or reachable_set is None: return -1 + # 重置不完整标志 + _g_reachable_incomplete = 0 + SRC_BUF_SIZE_R: t.CSizeT = 1048576 # 1. 构建 SHA1→module_name 映射 @@ -1293,29 +1419,9 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count) VLogger.debug(fb, "Reachable") - # 2. 扫描 source_dir 下的 .py 文件,收集直接依赖 - dir_len: t.CSizeT = string.strlen(source_dir) - pattern: bytes = stdlib.malloc(dir_len + 8) - if pattern is None: - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - return -1 - viperlib.snprintf(pattern, dir_len + 8, "%s/*.py", source_dir) - - find_data_size: t.CSizeT = win32file.WIN32_FIND_DATAA.__sizeof__() - find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(find_data_size + 16) - if find_data is None: - stdlib.free(pattern) - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - return -1 - string.memset(find_data, 0, find_data_size + 16) - # 工作列表(空格分隔的模块名) worklist: bytes = stdlib.malloc(8192) if worklist is None: - stdlib.free(pattern) - stdlib.free(find_data) stdlib.free(sha1_arr) stdlib.free(mod_arr) return -1 @@ -1324,70 +1430,104 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, reachable_count: int = 0 - handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) - if handle == win32base.INVALID_HANDLE_VALUE: - 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) - stdlib.free(mod_arr) - stdlib.free(worklist) - return -1 + # 2. 如果 app_deps 非空,直接使用它作为初始 worklist(跳过自己的非递归扫描) + # 否则,回退到自己的非递归扫描(source_dir/*.py,只扫描顶层) + if app_deps is not None and app_deps[0] != '\0': + ad_len: t.CSizeT = string.strlen(app_deps) + if ad_len > 0 and ad_len < 8192: + string.strcpy(worklist, app_deps) + wl_len = ad_len + else: + # 回退到自己的非递归扫描 + dir_len: t.CSizeT = string.strlen(source_dir) + pattern: bytes = stdlib.malloc(dir_len + 8) + if pattern is None: + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + stdlib.free(worklist) + return -1 + viperlib.snprintf(pattern, dir_len + 8, "%s/*.py", source_dir) - while True: - fname: str = find_data.cFileName - if fname is not None: - fname_len: t.CSizeT = string.strlen(fname) - if fname_len > 3: - is_py: int = 0 - if fname[fname_len - 3] == '.' and fname[fname_len - 2] == 'p' and fname[fname_len - 1] == 'y': - is_py = 1 - if is_py != 0: - 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", source_dir, fname) - sf: fileio.File | t.CPtr = fileio.File(full_path, fileio.MODE.R) - if not sf.closed: - sbuf: bytes = stdlib.malloc(SRC_BUF_SIZE_R) - if sbuf is not None: - br: LONG = sf.read_all(sbuf, SRC_BUF_SIZE_R) - sf.close() - if br > 0: - if br < SRC_BUF_SIZE_R: - sbuf[br] = 0 - else: - sbuf[SRC_BUF_SIZE_R - 1] = 0 - # 解析 AST 获取 _imported_modules - lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) - if lx is not None: - ast._lexer_init(lx, sbuf, mb) - tokens: ast.Token | t.CPtr = ast.tokenize(lx) - tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) - if tree is not None: - tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() - if tr is not None: - tr._declare_only = 2 - # 源文件包名:fname 是顶级文件名(无目录分隔符),包为 None - tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname) - HandlesStruct.reset_visible_structs(mb, 0) - tr.translate(tree) - # 获取 _imported_modules - if tr._imported_modules is not None: - im_len: t.CSizeT = string.strlen(tr._imported_modules) - if im_len > 0 and wl_len + im_len + 1 < 8192: - string.strcpy(worklist + wl_len, tr._imported_modules) - wl_len += im_len - worklist[wl_len] = ' ' - wl_len += 1 - worklist[wl_len] = '\0' - # 释放 Translator 资源 - if tr._global_names is not None: - stdlib.free(tr._global_names) - if tr._nonlocal_names is not None: - stdlib.free(tr._nonlocal_names) + find_data_size: t.CSizeT = win32file.WIN32_FIND_DATAA.__sizeof__() + find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(find_data_size + 16) + if find_data is None: + stdlib.free(pattern) + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + stdlib.free(worklist) + return -1 + string.memset(find_data, 0, find_data_size + 16) + + handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) + if handle == win32base.INVALID_HANDLE_VALUE: + fb_fd: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_fd is not None: + viperlib.snprintf(fb_fd, 1024, "未找到 .py 文件: %s", pattern) + VLogger.warning(fb_fd, "Reachable") + stdlib.free(pattern) + stdlib.free(find_data) + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + stdlib.free(worklist) + return -1 + + while True: + fname: str = find_data.cFileName + if fname is not None: + fname_len: t.CSizeT = string.strlen(fname) + if fname_len > 3: + is_py: int = 0 + if fname[fname_len - 3] == '.' and fname[fname_len - 2] == 'p' and fname[fname_len - 1] == 'y': + is_py = 1 + if is_py != 0: + 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", source_dir, fname) + sf: fileio.File | t.CPtr = fileio.File(full_path, fileio.MODE.R) + if not sf.closed: + sbuf: bytes = stdlib.malloc(SRC_BUF_SIZE_R) + if sbuf is not None: + br: LONG = sf.read_all(sbuf, SRC_BUF_SIZE_R) + sf.close() + if br > 0: + if br < SRC_BUF_SIZE_R: + sbuf[br] = 0 + else: + sbuf[SRC_BUF_SIZE_R - 1] = 0 + # 解析 AST 获取 _imported_modules + lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) + if lx is not None: + ast._lexer_init(lx, sbuf, mb) + tokens: ast.Token | t.CPtr = ast.tokenize(lx) + tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) + if tree is not None: + tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() + if tr is not None: + tr._declare_only = 2 + # 源文件包名:fname 是顶级文件名(无目录分隔符),包为 None + tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname) + HandlesStruct.reset_visible_structs(mb, 0) + tr.translate(tree) + # 获取 _imported_modules + if tr._imported_modules is not None: + im_len: t.CSizeT = string.strlen(tr._imported_modules) + if im_len > 0 and wl_len + im_len + 1 < 8192: + string.strcpy(worklist + wl_len, tr._imported_modules) + wl_len += im_len + worklist[wl_len] = ' ' + wl_len += 1 + worklist[wl_len] = '\0' + # 计算 App 源文件 SHA1,填充依赖到内存存储器 + # worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖 + app_sha1_r: str = IncludesScanner.compute_file_sha1(mb, full_path) + if app_sha1_r is not None: + PopulateIncludesDeps(app_sha1_r, tr._imported_modules) + stdlib.free(app_sha1_r) + # 释放 Translator 资源 + if tr._global_names is not None: + stdlib.free(tr._global_names) + if tr._nonlocal_names is not None: + stdlib.free(tr._nonlocal_names) stdlib.free(sbuf) else: stdlib.free(sbuf) @@ -1395,16 +1535,16 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, sf.close() stdlib.free(full_path) - if win32file.FindNextFileA(handle, find_data) == 0: - break - win32base.FindClose(handle) - stdlib.free(pattern) - stdlib.free(find_data) + if win32file.FindNextFileA(handle, find_data) == 0: + break + win32base.FindClose(handle) + stdlib.free(pattern) + stdlib.free(find_data) - # 去掉末尾多余空格 - if wl_len > 0 and worklist[wl_len - 1] == ' ': - worklist[wl_len - 1] = '\0' - wl_len -= 1 + # 去掉末尾多余空格 + if wl_len > 0 and worklist[wl_len - 1] == ' ': + worklist[wl_len - 1] = '\0' + wl_len -= 1 fb: t.CChar | t.CPtr = VLogger.fmt_buf() if fb is not None: @@ -1512,36 +1652,55 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, # 加入 reachable_set reachable_count = _AddSha1ToSet(reachable_set, reachable_count, found_sha1) - # 读取 .deps.txt 追加到 worklist - deps_path: str = _sliced_path(temp_dir, td_len_r, found_sha1, "deps.txt") - if deps_path is not None: - df: fileio.File | t.CPtr = fileio.File(deps_path, fileio.MODE.R) - if not df.closed: - deps_buf: bytes = stdlib.malloc(2048) - if deps_buf is not None: - dbr: t.CInt64T = df.read_all(deps_buf, 2048) - df.close() - if dbr > 0: - if dbr < 2048: - deps_buf[dbr] = '\0' - else: - deps_buf[2047] = '\0' - # 追加到 worklist(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug) - dl: t.CSizeT = string.strlen(deps_buf) - 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 = wl_len + 1 - worklist[wl_len] = '\0' - stdlib.free(deps_buf) - else: - df.close() - stdlib.free(deps_path) + # 从内存查找依赖(不读 deps.txt 过程文件) + # Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存 + deps_str: str = LookupIncludesDeps(found_sha1) + if deps_str is not None: + dl: t.CSizeT = string.strlen(deps_str) + if dl > 0: + if 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_str) + wl_len = wl_len + dl + worklist[wl_len] = ' ' + wl_len = wl_len + 1 + worklist[wl_len] = '\0' + # deps_str 为空字符串视为无依赖(合法情况,如叶子模块) + else: + # 依赖未在内存中找到 + # 按需过滤: 检查是否是 App 源文件(source_dir 下的 .py 文件) + # App 源文件不需要加入 reachable_set(只用于 includes 过滤),跳过 + sd_len_ff: t.CSizeT = string.strlen(source_dir) + mb_len_ff: t.CSizeT = string.strlen(mod_buf) + app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6) + is_app_src: int = 0 + if app_mod_path is not None: + viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.py", source_dir, mod_buf) + af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R) + if not af.closed: + is_app_src = 1 + af.close() + stdlib.free(app_mod_path) + if is_app_src != 0: + # App 源文件,跳过(不 fast-fail) + fb_skip: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_skip is not None: + viperlib.snprintf(fb_skip, 1024, "跳过 App 源文件: %s (sha1=%s)", mod_buf, found_sha1) + VLogger.debug(fb_skip, "Reachable") + else: + # fast-fail: includes 文件依赖未在内存中找到,Phase 1a-pre 未正确填充 + fb_ff: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_ff is not None: + viperlib.snprintf(fb_ff, 1024, "依赖未找到 fast-fail: 模块 %s (sha1=%s) 的依赖未在内存中注册", mod_buf, found_sha1) + VLogger.error(fb_ff, "Reachable") + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + stdlib.free(worklist) + stdlib.free(processed) + stdlib.free(mod_buf) + return -1 stdlib.free(mod_buf) stdlib.free(sha1_arr) stdlib.free(mod_arr) diff --git a/App/lib/core/VLogger.py b/App/lib/core/VLogger.py index 1afbbd2..316115c 100644 --- a/App/lib/core/VLogger.py +++ b/App/lib/core/VLogger.py @@ -101,7 +101,7 @@ class Logger: self._reset_color() # category 可选 if category is not None and category[0] != 0: - stdio.printf("[%s]", category) + stdio.printf(" [%s]", category) stdio.printf(": %s\n", msg) return 0 diff --git a/App/main.py b/App/main.py index a891c7d..10ac1db 100644 --- a/App/main.py +++ b/App/main.py @@ -227,7 +227,7 @@ def main() -> int: Config.resolve_paths(proj_dir) Config.print_config() - # === --clean: 清理 temp 和 output 目录 === + # === --clean: 清理 temp、output 和 includes.binary 目录 === if _gb_clean: if log is not None: log.info("清理临时目录...", "clean") @@ -245,6 +245,15 @@ def main() -> int: if fb2 is not None: viperlib.snprintf(fb2, 1024, "%s: 删除 %d 个文件", Config.OutputDir, n2) log.info(fb2, "clean") + # 清理 includes.binary 目录(旧 .obj 文件会导致跳过重新编译) + mf_inc_bin_clean: str = Config.get_includes_binary_dir() + if mf_inc_bin_clean is not None: + n3: int = Utils.CleanDir(mf_inc_bin_clean) + if log is not None: + fb3: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb3 is not None: + viperlib.snprintf(fb3, 1024, "%s: 删除 %d 个文件", mf_inc_bin_clean, n3) + log.info(fb3, "clean") # === --phase 参数控制 === # phase=1: 仅 stub 分离(生成 .stub.ll/.text.ll),不编译 diff --git a/project.json b/project.json index f923695..bdf4f80 100644 --- a/project.json +++ b/project.json @@ -22,7 +22,7 @@ "datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" }, "options": { - "slice_level": 3, + "slice_level": 3, "sha1_slice_level": 1, "target": "llvm", "strict_mode": true