修正了一些错误
This commit is contained in:
@@ -10,6 +10,7 @@ import w32.win32base
|
||||
import subprocess
|
||||
import viperlib
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
import lib.core.Handles.HandlesType as HandlesType
|
||||
@@ -68,6 +69,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
|
||||
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
|
||||
f.close()
|
||||
if bytes_read <= 0:
|
||||
stdlib.free(src_buf)
|
||||
return None
|
||||
if bytes_read < SRC_BUF_SIZE:
|
||||
src_buf[bytes_read] = 0
|
||||
@@ -77,6 +79,8 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
|
||||
ast._lexer_init(lx, src_buf, mb)
|
||||
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
|
||||
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
|
||||
# parse 完成后 AST 树已独立,释放源代码缓冲区(避免 1MB/文件泄漏)
|
||||
stdlib.free(src_buf)
|
||||
if tree is None:
|
||||
return None
|
||||
tr: HandlesTranslator.Translator | t.CPtr = mb.alloc(HandlesTranslator.Translator.__sizeof__())
|
||||
@@ -268,12 +272,27 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
|
||||
|
||||
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
|
||||
if result is None:
|
||||
stdio.printf("[FATAL][LLC] subprocess.run 返回 None: %s\n", module_name)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "subprocess.run 返回 None: %s", module_name)
|
||||
VLogger.error(fb, "LLC")
|
||||
return 1
|
||||
if result.returncode != 0:
|
||||
stdio.printf("[FATAL][LLC] 编译失败 (module=%s, cmd=%s)\n", module_name, cmd)
|
||||
# 先直接输出 llc 的具体错误信息(VLogger.error 会 sys.exit,必须先输出)
|
||||
# 注意: subprocess 在 Windows 下将 stderr 合并到 stdout(si.hStdError = stdout_write)
|
||||
# 因此 result.stderr 总是 None,错误信息在 result.stdout 中
|
||||
# stdout 通常已含换行符,不再额外加 \n
|
||||
if result.stdout is not None:
|
||||
stdio.printf("[LLC] 输出:\n%s\n", result.stdout)
|
||||
stdio.printf("%s", result.stdout)
|
||||
stdio.fflush(0)
|
||||
if result.stderr is not None:
|
||||
stdio.printf("%s", result.stderr)
|
||||
stdio.fflush(0)
|
||||
# 最后输出编译失败摘要(VLogger.error 会 sys.exit)
|
||||
fb = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "编译失败 (module=%s, cmd=%s)", module_name, cmd)
|
||||
VLogger.error(fb, "LLC")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@@ -370,9 +389,15 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
|
||||
if includes_binary_dir is not None:
|
||||
extra_len = collect_obj_files(includes_binary_dir, extra_objs, EXTRA_BUF_SIZE)
|
||||
if extra_len > 0:
|
||||
stdio.printf("[link] 附加 %d 字节的 includes.binary .obj 文件\n", extra_len)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "附加 %d 字节的 includes.binary .obj 文件", extra_len)
|
||||
VLogger.info(fb, "link")
|
||||
else:
|
||||
stdio.printf("[link] 警告: includes.binary 无 .obj 文件: %s\n", includes_binary_dir)
|
||||
fb = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "includes.binary 无 .obj 文件: %s", includes_binary_dir)
|
||||
VLogger.warning(fb, "link")
|
||||
|
||||
# 构造命令: clang++ {obj_path} extra_objs -o {output_dir}/{linker_output} linker_flags
|
||||
# 注意: .obj 文件必须在 -l 库标志之前,否则链接器无法解析符号依赖
|
||||
@@ -391,16 +416,25 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
|
||||
|
||||
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
|
||||
if result is None:
|
||||
stdio.printf("[link] subprocess.run 返回 None\n")
|
||||
VLogger.error("subprocess.run 返回 None", "link")
|
||||
return 1
|
||||
if result.returncode != 0:
|
||||
stdio.printf("[link] 链接失败,返回码: %d\n", result.returncode)
|
||||
stdio.printf("[link] 命令: %s\n", cmd)
|
||||
# 显示链接器错误输出(subprocess 将 stderr 合并到 stdout)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "链接失败,返回码: %d", result.returncode)
|
||||
VLogger.error(fb, "link")
|
||||
fb = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "命令: %s", cmd)
|
||||
VLogger.error(fb, "link")
|
||||
# 显示链接器错误输出
|
||||
# 注意: subprocess 在 Windows 下将 stderr 合并到 stdout,result.stderr 总是 None
|
||||
if result.stdout is not None:
|
||||
stdio.printf("[link] 链接器输出:\n%s\n", result.stdout)
|
||||
VLogger.error(result.stdout, "link")
|
||||
else:
|
||||
stdio.printf("[link] 无输出捕获\n")
|
||||
VLogger.error("无输出捕获", "link")
|
||||
if result.stderr is not None:
|
||||
VLogger.error(result.stderr, "link")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@@ -428,7 +462,10 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT,
|
||||
# Step 1: 写 .ll 文件
|
||||
ret: int = write_ir_to_file(ir_buf, ir_len, temp_dir, module_name)
|
||||
if ret != 0:
|
||||
stdio.printf("[compile] 写 .ll 失败: %s\n", module_name)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "写 .ll 失败: %s", module_name)
|
||||
VLogger.error(fb, "compile")
|
||||
return 1
|
||||
|
||||
# Step 2: 构造切片 .ll 路径并编译 → .obj
|
||||
@@ -438,7 +475,10 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT,
|
||||
|
||||
ret = compile_ll_to_obj(ir_path, output_dir, module_name, cc_cmd, cc_flags)
|
||||
if ret != 0:
|
||||
stdio.printf("[compile] llc 编译失败: %s\n", module_name)
|
||||
fb = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "llc 编译失败: %s", module_name)
|
||||
VLogger.error(fb, "compile")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@@ -486,13 +526,22 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT,
|
||||
|
||||
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
|
||||
if result is None:
|
||||
stdio.printf("[link] subprocess.run 返回 None\n")
|
||||
VLogger.error("subprocess.run 返回 None", "link")
|
||||
return 1
|
||||
if result.returncode != 0:
|
||||
stdio.printf("[link] 链接失败,返回码: %d\n", result.returncode)
|
||||
stdio.printf("[link] 命令: %s\n", cmd)
|
||||
# 先直接输出 linker 的具体错误信息(VLogger.error 会 sys.exit,必须先输出)
|
||||
# stdout/stderr 通常已含换行符,不再额外加 \n
|
||||
if result.stdout is not None:
|
||||
stdio.printf("[link] 链接器输出:\n%s\n", result.stdout)
|
||||
stdio.printf("%s", result.stdout)
|
||||
stdio.fflush(0)
|
||||
if result.stderr is not None:
|
||||
stdio.printf("%s", result.stderr)
|
||||
stdio.fflush(0)
|
||||
# 最后输出链接失败摘要(VLogger.error 会 sys.exit)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "链接失败,返回码: %d, 命令: %s", result.returncode, cmd)
|
||||
VLogger.error(fb, "link")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import lib.core.Handles.HandlesType as HandlesType
|
||||
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
|
||||
import lib.core.Handles.HandlesClassDef as HandlesClassDef
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -62,6 +63,64 @@ def extract_cdefine_int_value(val_node: ast.AST | t.CPtr) -> int:
|
||||
return cn.int_val
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _annotation_contains_name - 递归检查注解中是否包含指定名称
|
||||
#
|
||||
# 支持 BinOp(BitOr) 递归,同时检查 Attribute.attr 和 Name.id。
|
||||
# ============================================================
|
||||
def _annotation_contains_name(annot: ast.AST | t.CPtr, name: str) -> int:
|
||||
"""递归检查注解中是否包含指定名称,返回 1=包含 / 0=不包含"""
|
||||
if annot is None or name is None:
|
||||
return 0
|
||||
k: int = annot.kind()
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot)
|
||||
if at.attr is not None and string.strcmp(at.attr, name) == 0:
|
||||
return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot)
|
||||
if nm.id is not None and string.strcmp(nm.id, name) == 0:
|
||||
return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
|
||||
if _annotation_contains_name(bop.left, name) != 0:
|
||||
return 1
|
||||
if _annotation_contains_name(bop.right, name) != 0:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _is_ptr_element_annotation - 检查注解是否为 bytes|t.CPtr 或 str|t.CPtr
|
||||
#
|
||||
# 返回 1=是(下标按 8 字节步长, i8** 语义), 0=否(下标按 1 字节步长, i8* 语义)
|
||||
#
|
||||
# 规则:
|
||||
# bytes|t.CPtr / str|t.CPtr → i8**(8 字节步长)→ 返回 1
|
||||
# t.CChar|t.CPtr / t.CInt8T|t.CPtr → i8*(1 字节步长)→ 返回 0
|
||||
# 纯 bytes / str → i8*(1 字节步长)→ 返回 0
|
||||
#
|
||||
# 注: 注解中的 "|" 是"和"的意思(组合类型),非右值中的位或运算
|
||||
# ============================================================
|
||||
def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int:
|
||||
"""检查注解是否为 bytes|t.CPtr 或 str|t.CPtr 形式"""
|
||||
if annot is None:
|
||||
return 0
|
||||
k: int = annot.kind()
|
||||
if k != ast.ASTKind.BinOp:
|
||||
return 0
|
||||
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
|
||||
if bop.op != ast.OpKind.BitOr:
|
||||
return 0
|
||||
if _annotation_contains_name(annot, "str") != 0:
|
||||
return 1
|
||||
if _annotation_contains_name(annot, "bytes") != 0:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
@t.NoVTable
|
||||
class AnnAssignHandle(HandlesBase.Mixin):
|
||||
"""AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针"""
|
||||
@@ -121,7 +180,7 @@ class AnnAssignHandle(HandlesBase.Mixin):
|
||||
annot: ast.AST | t.CPtr = aa.annotation
|
||||
if annot is not None:
|
||||
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
|
||||
pool, annot, self.Trans._imported_modules, self.Trans._from_imports)
|
||||
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
|
||||
if resolved is None:
|
||||
# 尝试特化泛型类注解(如 list[str])
|
||||
# resolve_annotation_type 返回 None 可能是因为泛型类未特化
|
||||
@@ -138,11 +197,13 @@ class AnnAssignHandle(HandlesBase.Mixin):
|
||||
self.Trans, sub_nm.id, type_args_ps)
|
||||
if spec_name_ps is not None:
|
||||
resolved = HandlesType.resolve_annotation_type(
|
||||
pool, annot, self.Trans._imported_modules, self.Trans._from_imports)
|
||||
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
|
||||
if resolved is not None:
|
||||
var_ty = resolved
|
||||
|
||||
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, var_ty)
|
||||
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
|
||||
HandlesStruct.ensure_struct_def_in_module(pool, self.Trans.Module, var_ty)
|
||||
alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, var_ty)
|
||||
if alloca is not None:
|
||||
if HandlesVar.define_var(
|
||||
self.Trans.SymTab, nm.id, alloca) == 0:
|
||||
@@ -153,6 +214,10 @@ class AnnAssignHandle(HandlesBase.Mixin):
|
||||
if cls_nm_ps is not None:
|
||||
HandlesVar.set_var_annot_class_name(
|
||||
self.Trans.SymTab, nm.id, cls_nm_ps)
|
||||
# 检查是否为 bytes|t.CPtr 或 str|t.CPtr(i8** 语义,8 字节步长)
|
||||
if _is_ptr_element_annotation(annot) != 0:
|
||||
HandlesVar.set_var_ptr_element(
|
||||
self.Trans.SymTab, nm.id)
|
||||
return 0
|
||||
return 0
|
||||
|
||||
@@ -193,7 +258,7 @@ class AnnAssignHandle(HandlesBase.Mixin):
|
||||
annot: ast.AST | t.CPtr = aa.annotation
|
||||
if annot is not None:
|
||||
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
|
||||
pool, annot, self.Trans._imported_modules, self.Trans._from_imports)
|
||||
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
|
||||
if resolved is not None:
|
||||
var_ty = resolved
|
||||
|
||||
@@ -243,6 +308,10 @@ class AnnAssignHandle(HandlesBase.Mixin):
|
||||
if cls_nm_hd is not None:
|
||||
HandlesVar.set_var_annot_class_name(
|
||||
self.Trans.SymTab, nm.id, cls_nm_hd)
|
||||
# 检查是否为 bytes|t.CPtr 或 str|t.CPtr(i8** 语义,8 字节步长)
|
||||
if _is_ptr_element_annotation(annot) != 0:
|
||||
HandlesVar.set_var_ptr_element(
|
||||
self.Trans.SymTab, nm.id)
|
||||
|
||||
new_vars: int = 0
|
||||
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
|
||||
|
||||
@@ -6,6 +6,7 @@ import memhub
|
||||
import string
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesVar as HandlesVar
|
||||
@@ -44,12 +45,12 @@ class AssignHandle(HandlesBase.Mixin):
|
||||
def Handle(self, node: ast.AST | t.CPtr) -> int:
|
||||
asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
|
||||
if asgn is None:
|
||||
stdio.printf("[ASGN] cast failed\n")
|
||||
VLogger.error("cast failed", "ASGN")
|
||||
return 0
|
||||
|
||||
targets: list[ast.AST | t.CPtr] | t.CPtr = asgn.targets
|
||||
if targets is None:
|
||||
stdio.printf("[ASGN] targets is None\n")
|
||||
VLogger.error("targets is None", "ASGN")
|
||||
return 0
|
||||
|
||||
# 从 self.Trans 取共享状态(替代 11 个参数)
|
||||
@@ -61,7 +62,23 @@ class AssignHandle(HandlesBase.Mixin):
|
||||
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, asgn.value, None, 0, self.Trans)
|
||||
if rhs_val is None:
|
||||
stdio.printf("[ASGN] rhs_val is None\n")
|
||||
# 增强错误信息:包含 sha1 + lineno + AST 节点类型,便于定位
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None and asgn.value is not None:
|
||||
sha1: str = self.Trans.ModuleSha1
|
||||
val_kind: int = asgn.value.kind()
|
||||
val_line: t.CInt = asgn.value.lineno
|
||||
if sha1 is not None:
|
||||
viperlib.snprintf(fb, 1024,
|
||||
"rhs_val is None [sha1=%s lineno=%d kind=%d]",
|
||||
sha1, val_line, val_kind)
|
||||
else:
|
||||
viperlib.snprintf(fb, 1024,
|
||||
"rhs_val is None [lineno=%d kind=%d]",
|
||||
val_line, val_kind)
|
||||
VLogger.error(fb, "ASGN")
|
||||
else:
|
||||
VLogger.error("rhs_val is None", "ASGN")
|
||||
return 0
|
||||
|
||||
new_vars: int = 0
|
||||
@@ -116,23 +133,115 @@ class AssignHandle(HandlesBase.Mixin):
|
||||
sub_pointee: llvmlite.LLVMType | t.CPtr = sub_alloca.Ty.Pointee
|
||||
if sub_pointee is not None:
|
||||
cls_nm_set: str = HandlesStruct.get_class_name_by_type(pool, sub_pointee)
|
||||
if cls_nm_set is not None:
|
||||
obj_val_set: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, sub_pointee, sub_alloca)
|
||||
if obj_val_set is not None:
|
||||
key_val_set: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
|
||||
if key_val_set is not None:
|
||||
arg_vals_set: t.CSizeT | t.CPtr = pool.alloc(16)
|
||||
if arg_vals_set is not None:
|
||||
arg_vals_set[0] = t.CSizeT(key_val_set)
|
||||
arg_vals_set[1] = t.CSizeT(rhs_val)
|
||||
HandlesExprCall._call_method_on_ptr(
|
||||
pool, builder, mod, cls_nm_set, "__setitem__",
|
||||
obj_val_set, arg_vals_set, 2, self.Trans)
|
||||
setitem_done = 1
|
||||
obj_val_set: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, sub_pointee, sub_alloca)
|
||||
# 指针类型变量 (X|t.CPtr): sub_pointee 是 Ptr(Struct),
|
||||
# 需 load 获取 Ptr(Struct) 再检查 inner struct
|
||||
if cls_nm_set is None and obj_val_set is not None:
|
||||
if HandlesExpr.is_ptr_type(sub_pointee) != 0:
|
||||
inner_ty_set: llvmlite.LLVMType | t.CPtr = sub_pointee.Pointee
|
||||
if inner_ty_set is not None:
|
||||
cls_nm_set = HandlesStruct.get_class_name_by_type(pool, inner_ty_set)
|
||||
if cls_nm_set is not None and obj_val_set is not None:
|
||||
key_val_set: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
|
||||
if key_val_set is not None:
|
||||
arg_vals_set: t.CSizeT | t.CPtr = pool.alloc(16)
|
||||
if arg_vals_set is not None:
|
||||
arg_vals_set[0] = t.CSizeT(key_val_set)
|
||||
arg_vals_set[1] = t.CSizeT(rhs_val)
|
||||
HandlesExprCall._call_method_on_ptr(
|
||||
pool, builder, mod, cls_nm_set, "__setitem__",
|
||||
obj_val_set, arg_vals_set, 2, self.Trans)
|
||||
setitem_done = 1
|
||||
elif sub_asgn.value.kind() == ast.ASTKind.Attribute:
|
||||
# self._ht[key] = val → self._ht.__setitem__(key, val)
|
||||
# 通过 get_attribute_ptr 获取字段指针,再调用 __setitem__
|
||||
field_ptr_set: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
|
||||
builder, pool, mod, sub_asgn.value, self.Trans)
|
||||
if field_ptr_set is not None and field_ptr_set.Ty is not None:
|
||||
if HandlesExpr.is_ptr_type(field_ptr_set.Ty) != 0:
|
||||
field_pointee_set: llvmlite.LLVMType | t.CPtr = field_ptr_set.Ty.Pointee
|
||||
if field_pointee_set is not None:
|
||||
cls_nm_attr: str = HandlesStruct.get_class_name_by_type(pool, field_pointee_set)
|
||||
obj_val_attr: llvmlite.Value | t.CPtr = field_ptr_set
|
||||
# field_ptr 是 Ptr(Ptr(Struct)) (X|t.CPtr 字段):
|
||||
# load 解引用获取 Ptr(Struct)
|
||||
if cls_nm_attr is None and HandlesExpr.is_ptr_type(field_pointee_set) != 0:
|
||||
inner_struct_attr: llvmlite.LLVMType | t.CPtr = field_pointee_set.Pointee
|
||||
if inner_struct_attr is not None:
|
||||
cls_nm_attr = HandlesStruct.get_class_name_by_type(pool, inner_struct_attr)
|
||||
if cls_nm_attr is not None:
|
||||
obj_val_attr = llvmlite.build_load(
|
||||
builder, field_pointee_set, field_ptr_set)
|
||||
if cls_nm_attr is not None and obj_val_attr is not None:
|
||||
key_val_attr: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
|
||||
if key_val_attr is not None:
|
||||
arg_vals_attr: t.CSizeT | t.CPtr = pool.alloc(16)
|
||||
if arg_vals_attr is not None:
|
||||
arg_vals_attr[0] = t.CSizeT(key_val_attr)
|
||||
arg_vals_attr[1] = t.CSizeT(rhs_val)
|
||||
HandlesExprCall._call_method_on_ptr(
|
||||
pool, builder, mod, cls_nm_attr, "__setitem__",
|
||||
obj_val_attr, arg_vals_attr, 2, self.Trans)
|
||||
setitem_done = 1
|
||||
if setitem_done == 0:
|
||||
HandlesType.fatal_error(target, "subscript ptr is None")
|
||||
# 通用 fallback: 翻译 sub.value 并尝试 __setitem__ 或直接 GEP+store
|
||||
# 处理 Name/Attribute 之外的节点以及 SymTab 查找失败的情况
|
||||
if sub_asgn is not None and sub_asgn.value is not None:
|
||||
obj_val_gen: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, sub_asgn.value, None, 0, self.Trans)
|
||||
key_val_gen: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
|
||||
if obj_val_gen is not None and key_val_gen is not None \
|
||||
and obj_val_gen.Ty is not None:
|
||||
# 内联 _get_custom_struct_cls_nm 逻辑(避免依赖新函数)
|
||||
cls_nm_gen: str = HandlesStruct.get_class_name_by_type(
|
||||
pool, obj_val_gen.Ty)
|
||||
if cls_nm_gen is None and HandlesExpr.is_ptr_type(obj_val_gen.Ty) != 0:
|
||||
inner_gen: llvmlite.LLVMType | t.CPtr = obj_val_gen.Ty.Pointee
|
||||
if inner_gen is not None:
|
||||
cls_nm_gen = HandlesStruct.get_class_name_by_type(pool, inner_gen)
|
||||
if cls_nm_gen is not None:
|
||||
# 自定义结构体: 调用 __setitem__
|
||||
arg_vals_gen: t.CSizeT | t.CPtr = pool.alloc(16)
|
||||
if arg_vals_gen is not None:
|
||||
arg_vals_gen[0] = t.CSizeT(key_val_gen)
|
||||
arg_vals_gen[1] = t.CSizeT(rhs_val)
|
||||
HandlesExprCall._call_method_on_ptr(
|
||||
pool, builder, mod, cls_nm_gen, "__setitem__",
|
||||
obj_val_gen, arg_vals_gen, 2, self.Trans)
|
||||
setitem_done = 1
|
||||
else:
|
||||
# 非自定义结构体: 直接 GEP + store
|
||||
# 处理 get_subscript_ptr 因边界情况返回 None 的指针下标
|
||||
if HandlesExpr.is_ptr_type(obj_val_gen.Ty) != 0:
|
||||
elem_gen: llvmlite.LLVMType | t.CPtr = obj_val_gen.Ty.Pointee
|
||||
if elem_gen is not None:
|
||||
store_val_gen: llvmlite.Value | t.CPtr = rhs_val
|
||||
elem_ptr_gen: llvmlite.Value | t.CPtr = None
|
||||
match elem_gen:
|
||||
case llvmlite.LLVMType.Array(arr_elem_gen, _):
|
||||
elem_ptr_gen = llvmlite.build_gep_array(
|
||||
builder, elem_gen, arr_elem_gen,
|
||||
obj_val_gen, key_val_gen)
|
||||
case _:
|
||||
elem_ptr_gen = llvmlite.build_gep(
|
||||
builder, elem_gen, obj_val_gen, key_val_gen)
|
||||
if elem_ptr_gen is not None and elem_ptr_gen.Ty is not None:
|
||||
pt_gen: llvmlite.LLVMType | t.CPtr = elem_ptr_gen.Ty.Pointee
|
||||
if pt_gen is not None:
|
||||
store_val_gen = HandlesExpr.coerce_to_type(
|
||||
builder, rhs_val, pt_gen)
|
||||
llvmlite.build_store(builder, store_val_gen, elem_ptr_gen)
|
||||
setitem_done = 1
|
||||
if setitem_done == 0:
|
||||
sub_vk: int = sub_asgn.value.kind()
|
||||
stdio.printf("[ASGN-SUB] fallback failed: val_kind=%d\n", sub_vk)
|
||||
stdio.fflush(0)
|
||||
if setitem_done == 0:
|
||||
HandlesType.fatal_error(target, "subscript ptr is None")
|
||||
continue
|
||||
|
||||
# Attribute 赋值: obj.field = val
|
||||
@@ -140,14 +249,25 @@ class AssignHandle(HandlesBase.Mixin):
|
||||
field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
|
||||
builder, pool, mod, target, self.Trans)
|
||||
if field_ptr is not None:
|
||||
stdio.printf("[ASGN-ATTR] field_ptr ok ty_not_null=%d\n",
|
||||
1 if field_ptr.Ty is not None else 0)
|
||||
stdio.fflush(0)
|
||||
# 获取字段类型,对 rhs_val 进行类型转换(如 i32 → i64)
|
||||
store_val: llvmlite.Value | t.CPtr = rhs_val
|
||||
if field_ptr.Ty is not None:
|
||||
field_ty: llvmlite.LLVMType | t.CPtr = field_ptr.Ty.Pointee
|
||||
if field_ty is not None:
|
||||
stdio.printf("[ASGN-ATTR] coerce rhs_ty=%d field_ty=%d\n",
|
||||
HandlesExpr.get_llvm_type_bits(rhs_val.Ty),
|
||||
HandlesExpr.get_llvm_type_bits(field_ty))
|
||||
stdio.fflush(0)
|
||||
store_val = HandlesExpr.coerce_to_type(
|
||||
builder, rhs_val, field_ty)
|
||||
stdio.printf("[ASGN-ATTR] pre_store\n")
|
||||
stdio.fflush(0)
|
||||
llvmlite.build_store(builder, store_val, field_ptr)
|
||||
stdio.printf("[ASGN-ATTR] post_store\n")
|
||||
stdio.fflush(0)
|
||||
else:
|
||||
# 构造详细错误信息
|
||||
attr_node: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(target)
|
||||
@@ -213,7 +333,10 @@ class AssignHandle(HandlesBase.Mixin):
|
||||
if existing is None:
|
||||
new_vars += 1
|
||||
else:
|
||||
stdio.printf("[ASGN] alloca failed for %s\n", nm.id)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "alloca failed for %s", nm.id)
|
||||
VLogger.error(fb, "ASGN")
|
||||
|
||||
return new_vars
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import llvmlite
|
||||
import memhub
|
||||
import stdio
|
||||
import string
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesVar as HandlesVar
|
||||
@@ -58,7 +60,7 @@ class AugAssignHandle(HandlesBase.Mixin):
|
||||
field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
|
||||
builder, pool, mod, target, self.Trans)
|
||||
if field_ptr is None:
|
||||
stdio.printf("[AUGASGN] attribute ptr is None\n")
|
||||
VLogger.error("attribute ptr is None", "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 2. 确定字段类型并加载当前值
|
||||
@@ -68,21 +70,24 @@ class AugAssignHandle(HandlesBase.Mixin):
|
||||
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, target_ty, field_ptr)
|
||||
if cur_val is None:
|
||||
stdio.printf("[AUGASGN] cannot load attribute\n")
|
||||
VLogger.error("cannot load attribute", "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 3. 翻译 RHS 值
|
||||
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, aug.value, None, 0, self.Trans)
|
||||
if rhs_val is None:
|
||||
stdio.printf("[AUGASGN] rhs is None\n")
|
||||
VLogger.error("rhs is None", "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 4. 应用二元运算
|
||||
result: llvmlite.Value | t.CPtr = _apply_aug_op(
|
||||
pool, builder, aug.op, cur_val, rhs_val)
|
||||
if result is None:
|
||||
stdio.printf("[AUGASGN] binop failed for attr op=%d\n", aug.op)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "binop failed for attr op=%d", aug.op)
|
||||
VLogger.error(fb, "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 5. 类型对齐并存储
|
||||
@@ -92,8 +97,56 @@ class AugAssignHandle(HandlesBase.Mixin):
|
||||
llvmlite.build_store(builder, result, field_ptr)
|
||||
return 0
|
||||
|
||||
# Subscript 目标: self.state[i] += x / arr[i] += x
|
||||
# 流程: get_subscript_ptr → load → binop → store
|
||||
if tk == ast.ASTKind.Subscript:
|
||||
pool: memhub.MemBuddy | t.CPtr = self.Trans.Pool
|
||||
builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder
|
||||
mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module
|
||||
|
||||
# 1. 获取元素指针
|
||||
elem_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_subscript_ptr(
|
||||
builder, pool, mod, target, self.Trans)
|
||||
if elem_ptr is None:
|
||||
VLogger.error("subscript ptr is None", "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 2. 确定元素类型并加载当前值
|
||||
target_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
|
||||
target_ty = elem_ptr.Ty.Pointee
|
||||
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, target_ty, elem_ptr)
|
||||
if cur_val is None:
|
||||
VLogger.error("cannot load subscript element", "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 3. 翻译 RHS 值
|
||||
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, aug.value, None, 0, self.Trans)
|
||||
if rhs_val is None:
|
||||
VLogger.error("rhs is None", "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 4. 应用二元运算
|
||||
result: llvmlite.Value | t.CPtr = _apply_aug_op(
|
||||
pool, builder, aug.op, cur_val, rhs_val)
|
||||
if result is None:
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "binop failed for sub op=%d", aug.op)
|
||||
VLogger.error(fb, "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 5. 类型对齐并存储
|
||||
result = HandlesExpr.coerce_to_type(builder, result, target_ty)
|
||||
if result is None:
|
||||
return 0
|
||||
llvmlite.build_store(builder, result, elem_ptr)
|
||||
return 0
|
||||
|
||||
if tk != ast.ASTKind.Name:
|
||||
stdio.printf("[AUGASGN] only Name/Attribute target supported\n")
|
||||
VLogger.error("only Name/Attribute/Subscript target supported", "AUGASGN")
|
||||
return 0
|
||||
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
|
||||
@@ -139,14 +192,17 @@ class AugAssignHandle(HandlesBase.Mixin):
|
||||
cur_val = llvmlite.build_load(builder, target_ty, target_alloca)
|
||||
|
||||
if cur_val is None:
|
||||
stdio.printf("[AUGASGN] cannot load target %s\n", nm.id)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "cannot load target %s", nm.id)
|
||||
VLogger.error(fb, "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 3. 翻译 RHS 值
|
||||
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, aug.value, None, 0, self.Trans)
|
||||
if rhs_val is None:
|
||||
stdio.printf("[AUGASGN] rhs is None\n")
|
||||
VLogger.error("rhs is None", "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 4. 类型对齐 + 应用二元运算
|
||||
@@ -154,7 +210,10 @@ class AugAssignHandle(HandlesBase.Mixin):
|
||||
result: llvmlite.Value | t.CPtr = _apply_aug_op(
|
||||
pool, builder, aug.op, cur_val, rhs_val)
|
||||
if result is None:
|
||||
stdio.printf("[AUGASGN] binop failed for op=%d\n", aug.op)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "binop failed for op=%d", aug.op)
|
||||
VLogger.error(fb, "AUGASGN")
|
||||
return 0
|
||||
|
||||
# 5. 存储结果
|
||||
@@ -226,6 +285,8 @@ def _apply_aug_op(pool: memhub.MemBuddy | t.CPtr,
|
||||
return llvmlite.build_mul(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.Div:
|
||||
return llvmlite.build_sdiv(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.FloorDiv:
|
||||
return llvmlite.build_sdiv(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.Mod:
|
||||
return llvmlite.build_srem(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.BitAnd:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,8 @@ import memhub
|
||||
import string
|
||||
import llvmlite
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -110,7 +112,10 @@ def register_enum(pool: memhub.MemBuddy | t.CPtr,
|
||||
return existing
|
||||
|
||||
if _enum_count >= ENUM_MAX:
|
||||
stdio.printf("[ENUM] table full, cannot register %s\n", name)
|
||||
fb_et: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_et is not None:
|
||||
viperlib.snprintf(fb_et, 1024, "table full, cannot register %s", name)
|
||||
VLogger.error(fb_et, "ENUM")
|
||||
return None
|
||||
|
||||
entry: EnumEntry | t.CPtr = _get_enum_entry(_enum_count)
|
||||
@@ -151,7 +156,10 @@ def add_enum_member(pool: memhub.MemBuddy | t.CPtr,
|
||||
return -1
|
||||
|
||||
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
|
||||
stdio.printf("[ENUM] member table full for %s\n", enum_entry.Name)
|
||||
fb_em: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_em is not None:
|
||||
viperlib.snprintf(fb_em, 1024, "member table full for %s", enum_entry.Name)
|
||||
VLogger.error(fb_em, "ENUM")
|
||||
return -1
|
||||
|
||||
idx: int = enum_entry.MemberCount
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -272,6 +272,8 @@ def translate_binop(pool: memhub.MemBuddy | t.CPtr,
|
||||
return llvmlite.build_mul(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.Div:
|
||||
return llvmlite.build_sdiv(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.FloorDiv:
|
||||
return llvmlite.build_sdiv(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.Mod:
|
||||
return llvmlite.build_srem(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.BitAnd:
|
||||
|
||||
@@ -138,7 +138,7 @@ class ForHandle(HandlesBase.Mixin):
|
||||
trans.SymTab, var_name)
|
||||
new_vars: int = 0
|
||||
if var_alloca is None:
|
||||
var_alloca = llvmlite.build_alloca(builder, i32_ty)
|
||||
var_alloca = HandlesVar._alloca_at_entry(builder, i32_ty)
|
||||
if HandlesVar.define_var(
|
||||
trans.SymTab, var_name, var_alloca) == 0:
|
||||
new_vars = 1
|
||||
@@ -294,13 +294,13 @@ class ForHandle(HandlesBase.Mixin):
|
||||
trans.SymTab, var_name)
|
||||
new_vars: int = 0
|
||||
if var_alloca is None:
|
||||
var_alloca = llvmlite.build_alloca(builder, elem_ty)
|
||||
var_alloca = HandlesVar._alloca_at_entry(builder, elem_ty)
|
||||
if HandlesVar.define_var(
|
||||
trans.SymTab, var_name, var_alloca) == 0:
|
||||
new_vars = 1
|
||||
|
||||
# 创建隐式 index 变量,初始为 0
|
||||
idx_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i32_ty)
|
||||
idx_alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, i32_ty)
|
||||
llvmlite.build_store(builder, llvmlite.const_int32(pool, 0), idx_alloca)
|
||||
|
||||
# 创建基本块: cond / body / incr / end
|
||||
|
||||
@@ -7,6 +7,7 @@ import string
|
||||
import viperlib
|
||||
import stdio
|
||||
import stdlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesVar as HandlesVar
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
@@ -17,6 +18,10 @@ import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
|
||||
import lib.core.Handles.HandlesImports as HandlesImports
|
||||
|
||||
|
||||
# 作用域类型常量(本地副本,避免旧编译器跨模块 CDefine 查找 bug)
|
||||
SCOPE_FUNCTION: t.CDefine = 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性
|
||||
#
|
||||
@@ -311,7 +316,7 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr,
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if fd.returns is not None:
|
||||
ret_ty = HandlesType.resolve_annotation_type(
|
||||
pool, fd.returns, imported_modules, from_imports)
|
||||
pool, fd.returns, imported_modules, from_imports, trans)
|
||||
if ret_ty is None and fd.returns is not None:
|
||||
if HandlesType.has_decorator_marker(fd.returns, "State") != 0:
|
||||
ret_ty = llvmlite.Void(pool)
|
||||
@@ -347,10 +352,24 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr,
|
||||
if func is None:
|
||||
continue
|
||||
|
||||
# 提取默认参数信息
|
||||
fd_args_node: ast.Arguments | t.CPtr = fd.args
|
||||
fd_defaults: list[ast.AST | t.CPtr] | t.CPtr = None
|
||||
fd_default_count: int = 0
|
||||
fd_param_count: int = 0
|
||||
if fd_args_node is not None:
|
||||
fd_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd_args_node)
|
||||
if fd_ags.args is not None:
|
||||
fd_param_count = fd_ags.args.__len__()
|
||||
if fd_ags.defaults is not None:
|
||||
fd_defaults = fd_ags.defaults
|
||||
fd_default_count = fd_ags.defaults.__len__()
|
||||
|
||||
# 注册到函数表(用裸名 fd.name,不是 mangled_name)
|
||||
max_funcs: int = 256
|
||||
cur_count: int = trans._func_count
|
||||
if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs) == 0:
|
||||
if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs,
|
||||
fd_defaults, fd_default_count, fd_param_count) == 0:
|
||||
trans._func_count = cur_count + 1
|
||||
|
||||
# 注册 CExport 函数到全局表
|
||||
@@ -375,7 +394,7 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr,
|
||||
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
|
||||
pool, arg.annotation, imported_modules, from_imports)
|
||||
pool, arg.annotation, imported_modules, from_imports, trans)
|
||||
if resolved is not None:
|
||||
param_ty = resolved
|
||||
pname: t.CChar | t.CPtr = pool.alloc(32)
|
||||
@@ -412,7 +431,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if fd.returns is not None:
|
||||
ret_ty = HandlesType.resolve_annotation_type(
|
||||
pool, fd.returns, imported_modules, from_imports)
|
||||
pool, fd.returns, imported_modules, from_imports, trans)
|
||||
|
||||
# 如果返回类型注解纯装饰器标记(如 t.State,无实际类型),使用 void
|
||||
# 注意:必须在 infer_return_type 之前检测,因为 infer_return_type 至少返回 i32
|
||||
@@ -490,7 +509,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
param_ty_e: llvmlite.LLVMType | t.CPtr = i32_ty
|
||||
if arg_e.annotation is not None:
|
||||
resolved_e: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
|
||||
pool, arg_e.annotation, imported_modules, from_imports)
|
||||
pool, arg_e.annotation, imported_modules, from_imports, trans)
|
||||
if resolved_e is not None:
|
||||
param_ty_e = resolved_e
|
||||
pname_e: t.CChar | t.CPtr = pool.alloc(32)
|
||||
@@ -521,7 +540,10 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
# 创建新的 LLVM 函数(使用 SHA1 混淆名)
|
||||
func = llvmlite.create_function(pool, mod, mangled_name, ret_ty)
|
||||
if func is None:
|
||||
stdio.printf("[FUNC] create_function %s failed\n", fd.name)
|
||||
fb_cf: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_cf is not None:
|
||||
viperlib.snprintf(fb_cf, 1024, "create_function %s failed", fd.name)
|
||||
VLogger.error(fb_cf, "FUNC")
|
||||
return 0
|
||||
|
||||
# 提取 @c.Attribute 装饰器属性并设置到函数
|
||||
@@ -529,9 +551,22 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
if func_attrs is not None:
|
||||
llvmlite.function_set_attrs(func, func_attrs)
|
||||
|
||||
# 提取默认参数信息
|
||||
tfd_defaults: list[ast.AST | t.CPtr] | t.CPtr = None
|
||||
tfd_default_count: int = 0
|
||||
tfd_param_count: int = 0
|
||||
if args_node is not None:
|
||||
tfd_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
|
||||
if tfd_ags.args is not None:
|
||||
tfd_param_count = tfd_ags.args.__len__()
|
||||
if tfd_ags.defaults is not None:
|
||||
tfd_defaults = tfd_ags.defaults
|
||||
tfd_default_count = tfd_ags.defaults.__len__()
|
||||
|
||||
# 注册到函数表
|
||||
max_funcs: int = 256
|
||||
if HandlesExprCall.add_func_to_table(funcs_ptr, func_count, fd.name, func, max_funcs) == 0:
|
||||
if HandlesExprCall.add_func_to_table(funcs_ptr, func_count, fd.name, func, max_funcs,
|
||||
tfd_defaults, tfd_default_count, tfd_param_count) == 0:
|
||||
trans._func_count = func_count + 1
|
||||
|
||||
# 添加参数(支持类型注解)
|
||||
@@ -550,7 +585,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
|
||||
pool, arg.annotation, imported_modules, from_imports)
|
||||
pool, arg.annotation, imported_modules, from_imports, trans)
|
||||
if resolved is not None:
|
||||
param_ty = resolved
|
||||
pname: t.CChar | t.CPtr = pool.alloc(32)
|
||||
@@ -569,7 +604,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
llvmlite.position_at_end(func_builder, entry_blk)
|
||||
|
||||
# 进入函数作用域(嵌套符号表)
|
||||
HandlesVar.enter_scope(trans.SymTab, HandlesVar.SCOPE_FUNCTION)
|
||||
HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION)
|
||||
|
||||
# 为参数创建 alloca
|
||||
if args_node is not None:
|
||||
@@ -587,7 +622,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
param_ty2: llvmlite.LLVMType | t.CPtr = i32_ty
|
||||
if arg2.annotation is not None:
|
||||
resolved2: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
|
||||
pool, arg2.annotation, imported_modules, from_imports)
|
||||
pool, arg2.annotation, imported_modules, from_imports, trans)
|
||||
if resolved2 is not None:
|
||||
param_ty2 = resolved2
|
||||
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, param_ty2)
|
||||
@@ -633,6 +668,8 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
for bi2 in range(bn2):
|
||||
stmt2: ast.AST | t.CPtr = body.get(bi2)
|
||||
if stmt2 is not None:
|
||||
stdio.printf("[DBG] TR body stmt=%d kind=%d\n", bi2, stmt2.kind())
|
||||
stdio.fflush(0)
|
||||
HandlesBody.translate_stmt(trans, stmt2)
|
||||
|
||||
# 如果函数体最后一条语句不是 Return,添加隐式 ret
|
||||
@@ -644,8 +681,24 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
|
||||
if last_stmt is not None and last_stmt.kind() == ast.ASTKind.Return:
|
||||
last_is_return = 1
|
||||
if last_is_return == 0:
|
||||
zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||||
llvmlite.build_ret(func_builder, zero_val)
|
||||
if llvmlite.builder_cur_block_is_terminated(func_builder) == 0:
|
||||
# 根据返回类型生成正确的零值返回
|
||||
_is_void: int = 0
|
||||
_is_ptr: int = 0
|
||||
if ret_ty is not None:
|
||||
match ret_ty:
|
||||
case llvmlite.LLVMType.Void():
|
||||
_is_void = 1
|
||||
case llvmlite.LLVMType.Ptr(_pe):
|
||||
_is_ptr = 1
|
||||
if _is_void != 0:
|
||||
llvmlite.build_ret_void(func_builder)
|
||||
elif _is_ptr != 0:
|
||||
_null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null")
|
||||
llvmlite.build_ret(func_builder, _null_val)
|
||||
else:
|
||||
_zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||||
llvmlite.build_ret(func_builder, _zero_val)
|
||||
|
||||
# 恢复模块级作用域(退出函数作用域)
|
||||
HandlesVar.exit_scope(trans.SymTab)
|
||||
@@ -706,7 +759,7 @@ def translate_nested_function_def(trans: HT.Translator | t.CPtr,
|
||||
llvmlite.position_at_end(func_builder, entry_blk)
|
||||
|
||||
# 4. 进入嵌套函数作用域(嵌套符号表)
|
||||
HandlesVar.enter_scope(trans.SymTab, HandlesVar.SCOPE_FUNCTION)
|
||||
HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION)
|
||||
|
||||
# 5. 创建 _env_ptr alloca 并存储 %env 参数
|
||||
env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty)
|
||||
@@ -927,7 +980,7 @@ def create_function(pool: memhub.MemBuddy | t.CPtr,
|
||||
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
|
||||
pool, arg.annotation, None, None)
|
||||
pool, arg.annotation, None, None, None)
|
||||
if resolved is not None:
|
||||
param_ty = resolved
|
||||
pname: t.CChar | t.CPtr = pool.alloc(32)
|
||||
|
||||
@@ -3,6 +3,7 @@ from stdint import *
|
||||
import ast
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
import stdio
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
@@ -88,14 +89,24 @@ def is_module_imported(imported_modules: str, name: str) -> int:
|
||||
def add_from_import(pool: memhub.MemBuddy | t.CPtr,
|
||||
from_imports: str,
|
||||
local_name: str,
|
||||
module_name: str) -> str:
|
||||
"""添加 from-import 映射 "name:module",返回新的 from_imports 字符串"""
|
||||
module_name: str,
|
||||
original_name: str = None) -> str:
|
||||
"""添加 from-import 映射,返回新的 from_imports 字符串
|
||||
|
||||
格式:
|
||||
无别名: "local_name:module_name"
|
||||
有别名: "local_name:module_name:original_name"
|
||||
(original_name 为源模块中的真实函数名,用于跨模块 SHA1 修饰)
|
||||
"""
|
||||
if local_name is None or module_name is None:
|
||||
return from_imports
|
||||
entry: t.CChar | t.CPtr = pool.alloc(128)
|
||||
entry: t.CChar | t.CPtr = pool.alloc(256)
|
||||
if entry is None:
|
||||
return from_imports
|
||||
viperlib.snprintf(entry, 128, "%s:%s", local_name, module_name)
|
||||
if original_name is not None and original_name != local_name:
|
||||
viperlib.snprintf(entry, 256, "%s:%s:%s", local_name, module_name, original_name)
|
||||
else:
|
||||
viperlib.snprintf(entry, 256, "%s:%s", local_name, module_name)
|
||||
if from_imports is None:
|
||||
return entry
|
||||
else:
|
||||
@@ -111,6 +122,75 @@ def add_from_import(pool: memhub.MemBuddy | t.CPtr,
|
||||
return from_imports
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 查找 from-import 名称 → 返回原始函数名(别名场景)
|
||||
#
|
||||
# 对于 from X import Y as Z,返回 Y(源模块中的真实函数名)。
|
||||
# 无别名时返回 None(local_name 即为原始名)。
|
||||
# ============================================================
|
||||
def lookup_from_import_original(from_imports: str,
|
||||
local_name: str) -> str:
|
||||
"""查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到"""
|
||||
if local_name is None or from_imports is None:
|
||||
return None
|
||||
name_len: t.CSizeT = string.strlen(local_name)
|
||||
cur: t.CChar | t.CPtr = from_imports
|
||||
ci: t.CSizeT = 0
|
||||
total_len: t.CSizeT = string.strlen(from_imports)
|
||||
while ci < total_len:
|
||||
# 跳过前导空格
|
||||
while ci < total_len and cur[ci] == ' ':
|
||||
ci += 1
|
||||
if ci >= total_len:
|
||||
break
|
||||
# 找到第一个 ':' 的位置
|
||||
colon1: t.CSizeT = ci
|
||||
while colon1 < total_len and cur[colon1] != ':' and cur[colon1] != ' ':
|
||||
colon1 += 1
|
||||
if colon1 >= total_len or cur[colon1] != ':':
|
||||
break
|
||||
entry_name_len: t.CSizeT = colon1 - ci
|
||||
# 跳过 star import
|
||||
if entry_name_len == 1 and cur[ci] == '*':
|
||||
ci = colon1
|
||||
while ci < total_len and cur[ci] != ' ':
|
||||
ci += 1
|
||||
continue
|
||||
# 比较名称
|
||||
if entry_name_len == name_len:
|
||||
match: int = 1
|
||||
ei: t.CSizeT = 0
|
||||
while ei < name_len:
|
||||
if cur[ci + ei] != local_name[ei]:
|
||||
match = 0
|
||||
break
|
||||
ei += 1
|
||||
if match == 1:
|
||||
# 找到匹配,检查是否有第三个字段(原始名)
|
||||
pos: t.CSizeT = colon1 + 1
|
||||
# 跳过模块名
|
||||
while pos < total_len and cur[pos] != ':' and cur[pos] != ' ' and cur[pos] != '\0':
|
||||
pos += 1
|
||||
if pos < total_len and cur[pos] == ':':
|
||||
# 有第三个字段: original_name
|
||||
orig_start: t.CSizeT = pos + 1
|
||||
orig_end: t.CSizeT = orig_start
|
||||
while orig_end < total_len and cur[orig_end] != ' ' and cur[orig_end] != '\0':
|
||||
orig_end += 1
|
||||
orig_len: t.CSizeT = orig_end - orig_start
|
||||
if orig_len > 0:
|
||||
orig_buf: str = cur + orig_start
|
||||
# 返回指向内部的指针(调用方需在使用期内保持 from_imports 有效)
|
||||
return orig_buf
|
||||
return None
|
||||
return None
|
||||
# 跳到下一个条目
|
||||
ci = colon1
|
||||
while ci < total_len and cur[ci] != ' ':
|
||||
ci += 1
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 查找 from-import 名称 → 返回模块名或 None
|
||||
#
|
||||
@@ -150,7 +230,7 @@ def lookup_from_import(from_imports: str, name: str,
|
||||
if entry_name_len == 1 and cur[ci] == '*':
|
||||
mod_start: t.CSizeT = colon_pos + 1
|
||||
mod_end: t.CSizeT = mod_start
|
||||
while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0':
|
||||
while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0' and cur[mod_end] != ':':
|
||||
mod_end += 1
|
||||
star_mod = cur + mod_start
|
||||
# 比较名称
|
||||
@@ -165,7 +245,8 @@ def lookup_from_import(from_imports: str, name: str,
|
||||
if match == 1:
|
||||
mod_start2: t.CSizeT = colon_pos + 1
|
||||
mod_end2: t.CSizeT = mod_start2
|
||||
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0':
|
||||
# 模块名结束于: 空格、null、或第二个':'(别名格式的分隔符)
|
||||
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':':
|
||||
mod_end2 += 1
|
||||
return cur + mod_start2
|
||||
# 跳到下一个条目
|
||||
@@ -178,6 +259,93 @@ def lookup_from_import(from_imports: str, name: str,
|
||||
return star_mod
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 全局 re-export 映射表(module_sha1 + func_name → source_module)
|
||||
#
|
||||
# 当 ast/__init__.py 中有 from .lexer import _lexer_init 时,
|
||||
# _lexer_init 被 re-export 为 ast._lexer_init。
|
||||
# 跨模块调用 ast._lexer_init(...) 需使用 ast.lexer 的 SHA1 混淆,
|
||||
# 而非 ast 的 SHA1。
|
||||
#
|
||||
# 数据布局:
|
||||
# _g_reexport_sha1s: 每条 17 字节(SHA1 16字符 + null)
|
||||
# _g_reexport_funcs: 每条 64 字节(函数名 + null)
|
||||
# _g_reexport_srcs: 每条 64 字节(源模块名 + null)
|
||||
# ============================================================
|
||||
MAX_REEXPORT: t.CDefine = 512
|
||||
|
||||
_g_reexport_sha1s: bytes = None
|
||||
_g_reexport_funcs: bytes = None
|
||||
_g_reexport_srcs: bytes = None
|
||||
_g_reexport_count: int = 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# register_reexport - 注册 re-export 映射
|
||||
#
|
||||
# 在 HandleImportFromNames 中调用:当处理 from .X import Y 时,
|
||||
# 记录当前模块(通过 ModuleSha1)re-export 了函数 Y,源模块为 X。
|
||||
#
|
||||
# 幂等:重复注册相同映射不会增加条目。
|
||||
# 内存用 stdlib.malloc 分配(全局存储器,跨 Phase 持久化)。
|
||||
# ============================================================
|
||||
def register_reexport(mod_sha1: str, func_name: str, source_module: str) -> int:
|
||||
"""注册 re-export 映射(幂等),返回 0 成功,-1 失败"""
|
||||
global _g_reexport_sha1s, _g_reexport_funcs, _g_reexport_srcs, _g_reexport_count
|
||||
if mod_sha1 is None or func_name is None or source_module is None:
|
||||
return -1
|
||||
# 懒初始化
|
||||
if _g_reexport_sha1s is None:
|
||||
_g_reexport_sha1s = stdlib.malloc(MAX_REEXPORT * 17)
|
||||
_g_reexport_funcs = stdlib.malloc(MAX_REEXPORT * 64)
|
||||
_g_reexport_srcs = stdlib.malloc(MAX_REEXPORT * 64)
|
||||
if _g_reexport_sha1s is None or _g_reexport_funcs is None or _g_reexport_srcs is None:
|
||||
return -1
|
||||
string.memset(_g_reexport_sha1s, 0, MAX_REEXPORT * 17)
|
||||
string.memset(_g_reexport_funcs, 0, MAX_REEXPORT * 64)
|
||||
string.memset(_g_reexport_srcs, 0, MAX_REEXPORT * 64)
|
||||
if _g_reexport_count >= MAX_REEXPORT:
|
||||
return -1
|
||||
# 幂等检查:查找是否已存在相同映射
|
||||
for i in range(_g_reexport_count):
|
||||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||||
fidx: t.CSizeT = t.CSizeT(i) * 64
|
||||
if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0:
|
||||
if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0:
|
||||
# 已存在,更新 source_module(以防变化)
|
||||
string.strcpy(_g_reexport_srcs + fidx, source_module)
|
||||
return 0
|
||||
# 添加新条目
|
||||
idx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 17
|
||||
fidx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 64
|
||||
string.strcpy(_g_reexport_sha1s + idx2, mod_sha1)
|
||||
string.strcpy(_g_reexport_funcs + fidx2, func_name)
|
||||
string.strcpy(_g_reexport_srcs + fidx2, source_module)
|
||||
_g_reexport_count += 1
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# lookup_reexport - 查找 re-export 映射
|
||||
#
|
||||
# 给定模块 SHA1 和函数名,返回源模块名(如 "ast.lexer")或 None。
|
||||
# 调用方通过 _lookup_module_sha1(source_module) 获取源模块的 SHA1。
|
||||
# ============================================================
|
||||
def lookup_reexport(mod_sha1: str, func_name: str) -> str:
|
||||
"""查找 re-export 映射,返回源模块名或 None"""
|
||||
if mod_sha1 is None or func_name is None:
|
||||
return None
|
||||
if _g_reexport_sha1s is None or _g_reexport_count <= 0:
|
||||
return None
|
||||
for i in range(_g_reexport_count):
|
||||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||||
fidx: t.CSizeT = t.CSizeT(i) * 64
|
||||
if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0:
|
||||
if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0:
|
||||
return _g_reexport_srcs + fidx
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _resolve_relative_module - 解析相对导入为完整模块名
|
||||
#
|
||||
@@ -335,6 +503,24 @@ class ImportsHandle(HandlesBase.Mixin):
|
||||
if alias.asname is not None:
|
||||
self.Trans._imported_modules = add_imported_module(
|
||||
pool_val, self.Trans._imported_modules, alias.asname)
|
||||
# 别名也加入 from_imports(asname:fullname),
|
||||
# 使 HT.func() 跨模块调用能通过 from_imports 查找别名→完整模块名→SHA1
|
||||
# 否则 _lookup_module_sha1("HT") 找不到(mod_arr 只存文件名 HandlesTranslator)
|
||||
self.Trans._from_imports = add_from_import(
|
||||
pool_val, self.Trans._from_imports, alias.asname, alias.name)
|
||||
else:
|
||||
# 无别名时(import a.b.c),将顶层包名 "a" 也加入 _imported_modules,
|
||||
# 使 a.b.c 作为属性访问的 base 被翻译时能通过 is_module_imported("a") 检查
|
||||
dot_ptr: str = string.strstr(alias.name, '.')
|
||||
if dot_ptr is not None:
|
||||
pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_ptr) - t.CUInt64T(alias.name))
|
||||
if pkg_len > 0 and pkg_len < 256:
|
||||
pkg_buf: bytes = pool_val.alloc(pkg_len + 1)
|
||||
if pkg_buf is not None:
|
||||
string.strncpy(pkg_buf, alias.name, pkg_len)
|
||||
pkg_buf[pkg_len] = '\0'
|
||||
self.Trans._imported_modules = add_imported_module(
|
||||
pool_val, self.Trans._imported_modules, pkg_buf)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
@@ -381,13 +567,19 @@ class ImportsHandle(HandlesBase.Mixin):
|
||||
alias: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names.get(ni))
|
||||
if alias is not None and alias.name is not None:
|
||||
local_name: str = alias.name
|
||||
orig_name: str = None
|
||||
if alias.asname is not None:
|
||||
local_name = alias.asname
|
||||
orig_name = alias.name
|
||||
self.Trans._from_imports = add_from_import(
|
||||
self.Trans.Pool, self.Trans._from_imports,
|
||||
local_name, resolved)
|
||||
local_name, resolved, orig_name)
|
||||
# 命名空间隔离:from-import 的名称标记为可见结构体
|
||||
HandlesStruct.add_visible_struct(self.Trans.Pool, local_name)
|
||||
# 注册 re-export 映射:当前模块 re-export 了 local_name,源模块为 resolved
|
||||
# 使跨模块调用 module.func() 能解析到正确的源模块 SHA1
|
||||
if self.Trans.ModuleSha1 is not None:
|
||||
register_reexport(self.Trans.ModuleSha1, local_name, resolved)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import memhub
|
||||
import string
|
||||
import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesVar as HandlesVar
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
@@ -96,6 +97,10 @@ def translate_children(trans: HT.Translator | t.CPtr,
|
||||
# Phase 1a 声明模式:只注册 CExport/State 函数到全局表(解决翻译顺序依赖)
|
||||
# Phase 1b 全量翻译:正常翻译函数体
|
||||
if trans._declare_only == 0:
|
||||
fd_dbg: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
|
||||
if fd_dbg is not None and fd_dbg.name is not None:
|
||||
stdio.printf("[DBG] TR func=%s\n", fd_dbg.name)
|
||||
stdio.fflush(0)
|
||||
added: int = HandlesFunctions.translate_function_def(trans, child)
|
||||
added_total += added
|
||||
elif trans._declare_only == 1:
|
||||
@@ -104,6 +109,10 @@ def translate_children(trans: HT.Translator | t.CPtr,
|
||||
# ClassDef 在模块级直接处理(不需要 builder)
|
||||
# _declare_only=2(import扫描模式)时跳过,只处理 import 依赖
|
||||
if trans._declare_only != 2:
|
||||
cd_dbg: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
|
||||
if cd_dbg is not None and cd_dbg.name is not None:
|
||||
stdio.printf("[DBG] TR class=%s\n", cd_dbg.name)
|
||||
stdio.fflush(0)
|
||||
HandlesClassDef.translate_class_def(trans, child)
|
||||
elif trans._declare_only == 0 and trans._cur_builder is not None:
|
||||
# 有 builder → 委托 HandlesBody 分派
|
||||
@@ -171,7 +180,7 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
# 解析类型
|
||||
if aa.annotation is not None:
|
||||
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
|
||||
pool, aa.annotation, trans._imported_modules, trans._from_imports)
|
||||
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
|
||||
if resolved is not None:
|
||||
var_ty = resolved
|
||||
# 解析初始值
|
||||
@@ -208,6 +217,11 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
return 0
|
||||
|
||||
# 创建全局变量 @var_name
|
||||
# 先确保 var_ty 中的跨模块结构体类型在模块中有 opaque 声明
|
||||
# 否则 @var_name = global %"sha1.ClassName"* zeroinitializer 会报
|
||||
# "use of undefined type named 'sha1.ClassName'" 错误
|
||||
# (如 _mbuddy: memhub.MemManager | t.CPtr → Ptr(Struct("aeb3...MemManager")))
|
||||
llvmlite.module_ensure_opaque_for_type(mod, pool, var_ty)
|
||||
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, var_name, var_ty)
|
||||
if gv is None:
|
||||
return 0
|
||||
@@ -221,7 +235,9 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
viperlib.snprintf(init_buf, 48, "%lld", init_val)
|
||||
gv.Initializer = init_buf
|
||||
else:
|
||||
gv.Initializer = "0"
|
||||
# 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer,
|
||||
# 整数/聚合类型也兼容 zeroinitializer,避免 "integer constant must have integer type"
|
||||
gv.Initializer = "zeroinitializer"
|
||||
|
||||
# 创建 Value 引用(@var_name, 类型为 var_ty*)
|
||||
var_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, var_ty)
|
||||
@@ -232,6 +248,16 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
|
||||
# 注册到模块作用域
|
||||
if HandlesVar.define_module_var(trans.SymTab, var_name, gv_ref) == 0:
|
||||
# 设置 AnnotClassName(联合类型如 Logger|t.CPtr 简化为 Ptr(i8) 时,
|
||||
# 属性访问需要通过 AnnotClassName 回退到类名查找结构体)
|
||||
if k == ast.ASTKind.AnnAssign:
|
||||
aa_ml: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
|
||||
if aa_ml is not None and aa_ml.annotation is not None:
|
||||
cls_nm_ml: str = HandlesType.extract_class_name_from_annotation(
|
||||
aa_ml.annotation, trans._imported_modules)
|
||||
if cls_nm_ml is not None:
|
||||
HandlesVar.set_var_annot_class_name(
|
||||
trans.SymTab, var_name, cls_nm_ml)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@@ -262,18 +288,18 @@ def create_wrapper_main(trans: HT.Translator | t.CPtr,
|
||||
main_func: llvmlite.Function | t.CPtr = llvmlite.create_function(
|
||||
pool, mod, "main", i32_ty)
|
||||
if main_func is None:
|
||||
stdio.printf("[TR] CreateFunction main returned NULL\n")
|
||||
VLogger.error("CreateFunction main returned NULL", "TR")
|
||||
return 0
|
||||
|
||||
entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
|
||||
pool, main_func, "entry")
|
||||
if entry_blk is None:
|
||||
stdio.printf("[TR] CreateBlock returned NULL\n")
|
||||
VLogger.error("CreateBlock returned NULL", "TR")
|
||||
return 0
|
||||
|
||||
builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, main_func)
|
||||
if builder is None:
|
||||
stdio.printf("[TR] NewBuilder returned NULL\n")
|
||||
VLogger.error("NewBuilder returned NULL", "TR")
|
||||
return 0
|
||||
llvmlite.position_at_end(builder, entry_blk)
|
||||
|
||||
|
||||
@@ -55,31 +55,47 @@ class ReturnHandle(HandlesBase.Mixin):
|
||||
self.Trans._funcs, self.Trans._func_count, self.Trans)
|
||||
|
||||
# val 为 None 时(裸 return):检查当前函数返回类型
|
||||
# void 函数(如 __init__/__before_init__)生成 ret void,否则 ret i32 0
|
||||
# void 函数(如 __init__/__before_init__)生成 ret void,
|
||||
# ptr 函数生成 ret ptr null,否则 ret i32 0
|
||||
if val is None:
|
||||
cur_func: llvmlite.Function | t.CPtr = self.Trans._cur_func
|
||||
is_void_ret: int = 0
|
||||
is_ptr_ret: int = 0
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if cur_func is not None:
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cur_func)
|
||||
ret_ty = llvmlite.function_get_ret_ty(cur_func)
|
||||
if ret_ty is not None:
|
||||
match ret_ty:
|
||||
case llvmlite.LLVMType.Void():
|
||||
is_void_ret = 1
|
||||
case llvmlite.LLVMType.Ptr(_pe):
|
||||
is_ptr_ret = 1
|
||||
if is_void_ret != 0:
|
||||
llvmlite.build_ret_void(builder)
|
||||
return 0
|
||||
if is_ptr_ret != 0:
|
||||
null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null")
|
||||
llvmlite.build_ret(builder, null_val)
|
||||
return 0
|
||||
val = llvmlite.const_int32(pool, 0)
|
||||
|
||||
# 类型转换:确保 val 类型与函数返回类型匹配
|
||||
# 处理 i1(bool 比较结果)→ i8(t.CBool)等情况
|
||||
# i1 → 更宽整数用 zext(bool 语义:1 保持 1,而非 sext 的 0xFF)
|
||||
# 注意:当 val 是整数而 ret_ty 是 ptr 时(即使 bits 相同,如 i64 vs ptr),
|
||||
# 也必须 inttoptr 转换,否则 llc 报 "value doesn't match function result type"
|
||||
cur_func_rt: llvmlite.Function | t.CPtr = self.Trans._cur_func
|
||||
if cur_func_rt is not None and val is not None and val.Ty is not None:
|
||||
ret_ty_rt: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cur_func_rt)
|
||||
if ret_ty_rt is not None:
|
||||
val_bits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
|
||||
ret_bits: int = HandlesExpr.get_llvm_type_bits(ret_ty_rt)
|
||||
if val_bits != 0 and ret_bits != 0 and val_bits != ret_bits:
|
||||
val_is_ptr: int = HandlesExpr.is_ptr_type(val.Ty)
|
||||
ret_is_ptr: int = HandlesExpr.is_ptr_type(ret_ty_rt)
|
||||
# ptr 类型不一致性:val 是整数但 ret 是 ptr(或反之),必须转换
|
||||
if val_is_ptr != ret_is_ptr:
|
||||
val = HandlesExpr.coerce_to_type(builder, val, ret_ty_rt)
|
||||
elif val_bits != 0 and ret_bits != 0 and val_bits != ret_bits:
|
||||
if val_bits == 1 and val_bits < ret_bits:
|
||||
val = llvmlite.build_zext(builder, val, ret_ty_rt)
|
||||
else:
|
||||
|
||||
@@ -6,6 +6,8 @@ import llvmlite
|
||||
import stdio
|
||||
import ast
|
||||
import hashtable
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -19,7 +21,7 @@ import hashtable
|
||||
# ============================================================
|
||||
|
||||
STRUCT_MAX: t.CDefine = 512
|
||||
FIELD_MAX: t.CDefine = 32
|
||||
FIELD_MAX: t.CDefine = 48
|
||||
FIELD_NAME_MAX: t.CDefine = 64
|
||||
|
||||
|
||||
@@ -196,7 +198,10 @@ def register_struct(pool: memhub.MemBuddy | t.CPtr,
|
||||
return existing
|
||||
|
||||
if _struct_count >= STRUCT_MAX:
|
||||
stdio.printf("[STRUCT] table full, cannot register %s\n", name)
|
||||
fb_st: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_st is not None:
|
||||
viperlib.snprintf(fb_st, 1024, "table full, cannot register %s", name)
|
||||
VLogger.error(fb_st, "STRUCT")
|
||||
return None
|
||||
|
||||
entry: StructEntry | t.CPtr = _get_struct_entry(_struct_count)
|
||||
@@ -290,7 +295,10 @@ def add_field(pool: memhub.MemBuddy | t.CPtr,
|
||||
return -1
|
||||
|
||||
if struct_entry.FieldCount >= FIELD_MAX:
|
||||
stdio.printf("[STRUCT] field table full for %s\n", struct_entry.Name)
|
||||
fb_sf: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_sf is not None:
|
||||
viperlib.snprintf(fb_sf, 1024, "field table full for %s", struct_entry.Name)
|
||||
VLogger.error(fb_sf, "STRUCT")
|
||||
return -1
|
||||
|
||||
idx: int = struct_entry.FieldCount
|
||||
@@ -314,18 +322,41 @@ def add_field(pool: memhub.MemBuddy | t.CPtr,
|
||||
return idx
|
||||
|
||||
|
||||
# ============================================================
|
||||
# get_struct_count — 获取已注册结构体数量(供 Phase2 多遍预注册判断收敛)
|
||||
# ============================================================
|
||||
def get_struct_count() -> int:
|
||||
"""返回当前已注册的结构体数量"""
|
||||
return _struct_count
|
||||
|
||||
|
||||
# ============================================================
|
||||
# find_struct — 按类名查找结构体
|
||||
# ============================================================
|
||||
def find_struct(name: str) -> StructEntry | t.CPtr:
|
||||
"""按类名查找结构体,返回 StructEntry 或 None"""
|
||||
"""按类名查找结构体,返回 StructEntry 或 None
|
||||
|
||||
支持模块限定名(如 "HT.Translator"):
|
||||
第一遍精确匹配,第二遍按类名(最后一个 '.' 之后的部分)回退
|
||||
"""
|
||||
if name is None or _struct_table is None:
|
||||
return None
|
||||
# 第一遍:精确匹配
|
||||
for i in range(_struct_count):
|
||||
entry: StructEntry | t.CPtr = _get_struct_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, name) == 0:
|
||||
return entry
|
||||
# 第二遍:类名回退(处理模块限定名 "HT.Translator" → "Translator")
|
||||
dot_pos: str = string.strrchr(name, '.')
|
||||
if dot_pos is not None:
|
||||
class_name: str = dot_pos + 1
|
||||
if class_name is not None and class_name[0] != '\0':
|
||||
for i in range(_struct_count):
|
||||
entry: StructEntry | t.CPtr = _get_struct_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, class_name) == 0:
|
||||
return entry
|
||||
return None
|
||||
|
||||
|
||||
@@ -455,12 +486,27 @@ def lookup_field_by_class(class_name: str,
|
||||
return None
|
||||
|
||||
# 优先用 SHA1 匹配(跨模块同名类区分)
|
||||
# 支持模块限定名:class_name 可能是 "HT.Translator",entry.Name 是 "Translator"
|
||||
# 先精确匹配,失败后用 strrchr 提取类名部分回退匹配
|
||||
if sha1 is not None:
|
||||
sha1_matched: int = 0
|
||||
# 计算类名回退部分("HT.Translator" → "Translator")
|
||||
bare_name: str = class_name
|
||||
dot_pos_lfbc: str = string.strrchr(class_name, '.')
|
||||
if dot_pos_lfbc is not None:
|
||||
bare_name = dot_pos_lfbc + 1
|
||||
if bare_name is None or bare_name[0] == '\0':
|
||||
bare_name = class_name
|
||||
for i in range(_struct_count):
|
||||
entry: StructEntry | t.CPtr = _get_struct_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
# 精确匹配或类名回退匹配
|
||||
name_match: int = 0
|
||||
if string.strcmp(entry.Name, class_name) == 0:
|
||||
name_match = 1
|
||||
elif bare_name is not class_name and string.strcmp(entry.Name, bare_name) == 0:
|
||||
name_match = 1
|
||||
if name_match != 0:
|
||||
if entry.ModuleSha1 is not None:
|
||||
if string.strcmp(entry.ModuleSha1, sha1) == 0:
|
||||
sha1_matched = 1
|
||||
@@ -475,12 +521,65 @@ def lookup_field_by_class(class_name: str,
|
||||
# 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个
|
||||
entry = find_struct(class_name)
|
||||
if entry is None:
|
||||
# 诊断:遍历打印所有已注册结构体名,确认目标类是否在注册表中
|
||||
stdio.printf("[LFBC-DIAG] FAIL class=%s field=%s sha1=%s count=%d\n",
|
||||
class_name, field_name,
|
||||
sha1 if sha1 is not None else "(null)", _struct_count)
|
||||
for diag_i in range(_struct_count):
|
||||
diag_entry: StructEntry | t.CPtr = _get_struct_entry(diag_i)
|
||||
if diag_entry is not None and diag_entry.Name is not None:
|
||||
stdio.printf("[LFBC-DIAG] [%d] %s sha1=%s fc=%d\n",
|
||||
diag_i, diag_entry.Name,
|
||||
diag_entry.ModuleSha1 if diag_entry.ModuleSha1 is not None else "(null)",
|
||||
diag_entry.FieldCount)
|
||||
return None
|
||||
for fi in range(entry.FieldCount):
|
||||
fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
if string.strcmp(fe.Name, field_name) == 0:
|
||||
return fe
|
||||
# 诊断:找到结构体但字段未找到
|
||||
stdio.printf("[LFBC-DIAG] NOFIELD class=%s field=%s sname=%s fc=%d\n",
|
||||
class_name, field_name, entry.Name, entry.FieldCount)
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# find_subclass_with_field — 在基类的所有子类中搜索包含指定字段的结构体
|
||||
#
|
||||
# 用于处理 "注解类型是基类但实际值是派生类" 的场景:
|
||||
# node: AST | t.CPtr = If(...)
|
||||
# node.orelse = orelse ← orelse 在 If 上,不在 AST 上
|
||||
#
|
||||
# 遍历所有已注册结构体,通过 ParentName 链检查继承关系,
|
||||
# 返回第一个包含 field_name 的子类 StructEntry。
|
||||
# ============================================================
|
||||
def find_subclass_with_field(base_class_name: str,
|
||||
field_name: str) -> StructEntry | t.CPtr:
|
||||
"""在 base_class_name 的所有子类中搜索包含 field_name 的结构体
|
||||
|
||||
返回第一个找到的 StructEntry(包含正确的 Ty),None=未找到
|
||||
"""
|
||||
if base_class_name is None or field_name is None:
|
||||
return None
|
||||
sc_i: int
|
||||
for sc_i in range(_struct_count):
|
||||
sc_entry: StructEntry | t.CPtr = _get_struct_entry(sc_i)
|
||||
if sc_entry is None or sc_entry.Name is None:
|
||||
continue
|
||||
# 检查 sc_entry 是否是 base_class_name 的子类(传递性)
|
||||
cur_parent: str = get_parent_name(sc_entry.Name)
|
||||
while cur_parent is not None:
|
||||
if string.strcmp(cur_parent, base_class_name) == 0:
|
||||
# 是子类,检查是否有该字段
|
||||
sc_fi: int
|
||||
for sc_fi in range(sc_entry.FieldCount):
|
||||
sc_fe: FieldEntry | t.CPtr = _get_field_entry(sc_entry, sc_fi)
|
||||
if sc_fe is not None and sc_fe.Name is not None:
|
||||
if string.strcmp(sc_fe.Name, field_name) == 0:
|
||||
return sc_entry
|
||||
break
|
||||
cur_parent = get_parent_name(cur_parent)
|
||||
return None
|
||||
|
||||
|
||||
@@ -546,6 +645,28 @@ def _is_struct_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""检查 ty 是否是 Ptr 类型(独立函数,规避嵌套 match 编译器 BUG)"""
|
||||
if ty is None:
|
||||
return 0
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Ptr(pointee):
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
|
||||
|
||||
def _get_ptr_pointee(ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""获取 Ptr 类型的 pointee(独立函数,规避嵌套 match 编译器 BUG)"""
|
||||
if ty is None:
|
||||
return None
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Ptr(pointee):
|
||||
return pointee
|
||||
case _:
|
||||
return None
|
||||
|
||||
|
||||
def get_struct_type_from_value(val: llvmlite.Value | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""从 Value 的类型推断结构体类型"""
|
||||
if val is None or val.Ty is None:
|
||||
@@ -935,6 +1056,119 @@ def get_field_count(name: str) -> int:
|
||||
return entry.FieldCount
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ensure_struct_def_in_module — 确保跨模块结构体的完整定义在当前模块中可用
|
||||
#
|
||||
# 当局部变量声明为值类型(如 f: File)时,alloca 需要完整类型定义。
|
||||
# 如果结构体定义在另一个模块(名称含 SHA1 前缀,如 "0035c95a18d4f8e8.File"),
|
||||
# 将其完整定义复制到当前模块。module_add_named_type 会用完整定义替换
|
||||
# 已存在的 opaque 声明,使 alloca 能正确分配空间。
|
||||
#
|
||||
# Args:
|
||||
# pool: 内存池
|
||||
# mod: 当前 LLVM 模块
|
||||
# ty: 变量类型(Struct/Ptr/其他)
|
||||
# ============================================================
|
||||
def ensure_struct_def_in_module(pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
ty: llvmlite.LLVMType | t.CPtr):
|
||||
"""确保跨模块结构体的完整定义在当前模块中可用(供 alloca 使用)
|
||||
|
||||
处理两种情况:
|
||||
1. Struct 值类型:需要完整定义(供 alloca 分配空间)
|
||||
2. Ptr(Struct) 指针类型:需要 pointee 至少有 opaque 声明
|
||||
"""
|
||||
if ty is None or mod is None or pool is None:
|
||||
return
|
||||
# 指针类型:确保 pointee 至少有 opaque 声明
|
||||
# 用 _is_ptr_type 检查(独立函数,规避嵌套 match BUG)
|
||||
if _is_ptr_type(ty) != 0:
|
||||
llvmlite.module_ensure_opaque_for_type(mod, pool, ty)
|
||||
# 如果 pointee 是 Struct,递归复制完整定义
|
||||
# (GEP 访问字段需要 sized 类型,仅 opaque 会导致 llc 报错)
|
||||
pointee: llvmlite.LLVMType | t.CPtr = _get_ptr_pointee(ty)
|
||||
if pointee is not None:
|
||||
ensure_struct_def_in_module(pool, mod, pointee)
|
||||
return
|
||||
# 获取结构体名称(非 Struct 类型返回 None)
|
||||
sname: t.CChar | t.CPtr = llvmlite.get_struct_name(ty)
|
||||
if sname is None:
|
||||
return
|
||||
# 检查是否为跨模块引用(名称包含 '.')
|
||||
if string.strchr(sname, 46) is None:
|
||||
return # 非跨模块引用,无需处理
|
||||
# 提取类名(第一个 '.' 之后的部分)
|
||||
# 必须用 strchr 而非 strrchr:泛型特化名如 "list[ast.AST|t.CPtr]"
|
||||
# 内部含 '.'(ast.AST),strrchr 会错误地把类名解析为 "AST|t.CPtr]"
|
||||
dot_pos: str = string.strchr(sname, '.')
|
||||
if dot_pos is None:
|
||||
return
|
||||
class_name: str = dot_pos + 1
|
||||
if class_name is None or class_name[0] == '\0':
|
||||
return
|
||||
# 提取 SHA1(sname 到 dot_pos 之间,用 strlen 避免指针减法)
|
||||
total_len: t.CSizeT = string.strlen(sname)
|
||||
class_name_len: t.CSizeT = string.strlen(class_name)
|
||||
sha1_len: t.CSizeT = total_len - class_name_len - 1
|
||||
sha1_buf: t.CChar | t.CPtr = pool.alloc(32)
|
||||
if sha1_buf is None:
|
||||
return
|
||||
string.strncpy(sha1_buf, sname, sha1_len)
|
||||
sha1_buf[sha1_len] = '\0'
|
||||
# 按类名 + SHA1 查找结构体定义
|
||||
entry: StructEntry | t.CPtr = find_struct_by_module(class_name, sha1_buf)
|
||||
if entry is None:
|
||||
# 回退到按类名查找(可能找到不同模块的同名类,但总比没有好)
|
||||
entry = find_struct(class_name)
|
||||
if entry is None:
|
||||
return
|
||||
if entry.Ty is None:
|
||||
return
|
||||
# 将完整定义添加到当前模块(会替换已存在的 opaque 声明)
|
||||
llvmlite.module_add_named_type(mod, pool, entry.Ty)
|
||||
# 递归扫描字段类型,确保值类型子结构体(如 WIN32_FIND_DATAA 中的 FILETIME)
|
||||
# 也有完整定义。指针类型字段只需 opaque 声明(已由 module_ensure_opaque_for_type 处理)。
|
||||
_ensure_field_types_in_module(pool, mod, entry.Ty)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _ensure_field_types_in_module - 递归扫描结构体字段类型
|
||||
#
|
||||
# 遍历 Struct 的 Fields 链表,对每个值类型子结构体调用
|
||||
# ensure_struct_def_in_module。指针/数组/基本类型递归到 pointee/element。
|
||||
# ============================================================
|
||||
def _ensure_field_types_in_module(pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
ty: llvmlite.LLVMType | t.CPtr):
|
||||
"""递归扫描类型树,确保值类型子结构体有完整定义"""
|
||||
if ty is None or mod is None or pool is None:
|
||||
return
|
||||
# 用 match 遍历类型(独立函数,规避嵌套 match BUG)
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Struct(fields, fcount, sname):
|
||||
# 遍历字段链表
|
||||
if fields is not None:
|
||||
fld: llvmlite.ParamNode | t.CPtr = fields
|
||||
while fld is not None:
|
||||
if fld.Ty is not None:
|
||||
# 值类型子结构体(如 FILETIME)需要完整定义
|
||||
if _is_struct_type(fld.Ty) != 0:
|
||||
ensure_struct_def_in_module(pool, mod, fld.Ty)
|
||||
_ensure_field_types_in_module(pool, mod, fld.Ty)
|
||||
fld = fld.Next
|
||||
case llvmlite.LLVMType.Ptr(pointee):
|
||||
# 指针类型:pointee 只需 opaque 声明,不需要完整定义
|
||||
# 但如果 pointee 是 Struct,确保 opaque 声明存在
|
||||
if pointee is not None:
|
||||
llvmlite.module_ensure_opaque_for_type(mod, pool, ty)
|
||||
case llvmlite.LLVMType.Array(elem_ty, acount):
|
||||
# 数组类型:元素如果是值类型结构体,需要完整定义
|
||||
if elem_ty is not None:
|
||||
_ensure_field_types_in_module(pool, mod, elem_ty)
|
||||
case _:
|
||||
pass
|
||||
|
||||
|
||||
# ============================================================
|
||||
# get_field_name_ptr — 获取 FieldEntry 的字段名指针(访问器)
|
||||
# ============================================================
|
||||
|
||||
@@ -6,6 +6,7 @@ import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesVar as HandlesVar
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.Handles.HandlesMain as HandlesMain
|
||||
@@ -212,7 +213,7 @@ class Translator:
|
||||
# 创建 LLVM 模块
|
||||
mod: llvmlite.LLVMModule | t.CPtr = llvmlite.new_module(pool, "main")
|
||||
if mod is None:
|
||||
stdio.printf("[TR] NewModule returned NULL\n")
|
||||
VLogger.error("NewModule returned NULL", "TR")
|
||||
return 1
|
||||
self.Module = mod
|
||||
|
||||
@@ -231,7 +232,7 @@ class Translator:
|
||||
printf_func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
|
||||
pool, mod, "printf", i32_ty)
|
||||
if printf_func is None:
|
||||
stdio.printf("[TR] CreateDeclare printf returned NULL\n")
|
||||
VLogger.error("CreateDeclare printf returned NULL", "TR")
|
||||
return 1
|
||||
llvmlite.add_param(pool, printf_func, i8_ptr_ty, "fmt")
|
||||
printf_func.IsVarArg = 1
|
||||
@@ -305,6 +306,21 @@ class Translator:
|
||||
elif kd == ast.ASTKind.ImportFrom:
|
||||
self.ImportsH.HandleImportFromModule(child)
|
||||
self.ImportsH.HandleImportFromNames(child)
|
||||
# 预注册模块级变量(AnnAssign/Assign)到 SymTab
|
||||
# 解决时序问题:class Logger 的 __init__ 引用 _mbuddy 时,
|
||||
# _mbuddy 的 AnnAssign 在 AST 中位于 class Logger 之后,
|
||||
# 按 AST 顺序翻译时 _mbuddy 尚未注册 → lookup_var 返回 None → rhs_val is None
|
||||
# 预处理先于 translate_children 注册所有模块级变量,
|
||||
# 后续 translate_children 遍历到同一 AnnAssign 时 handle_module_level_var
|
||||
# 通过 lookup_module_var 检测已存在并跳过(HandlesMain.py:206-209)
|
||||
if ch is not None:
|
||||
for ci in range(cn_count):
|
||||
child: ast.AST | t.CPtr = ch.get(ci)
|
||||
if child is None:
|
||||
continue
|
||||
kd_mv: int = child.kind()
|
||||
if kd_mv == ast.ASTKind.AnnAssign or kd_mv == ast.ASTKind.Assign:
|
||||
HandlesMain.handle_module_level_var(self, child)
|
||||
# 创建前向声明
|
||||
HandlesFunctions.forward_declare_functions(self, tree)
|
||||
added: int = HandlesMain.translate_children(self, tree)
|
||||
|
||||
@@ -26,6 +26,27 @@ import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
_g_current_file: str = None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 全局 temp_dir(pyi 文件所在目录,由 BuildPipeline/Phase1 设置)
|
||||
#
|
||||
# 用于跨模块 CDefine 常量查找:当当前模块的 CDefine 表中找不到
|
||||
# 某个 Name 时,从 from_imports 解析出源模块,再从该模块的 pyi
|
||||
# 文件中解析 CDefine 常量值。
|
||||
# ============================================================
|
||||
_g_temp_dir: str = None
|
||||
|
||||
|
||||
def set_temp_dir(temp_dir: str) -> None:
|
||||
"""设置全局 temp_dir(每个项目编译开始时由 BuildPipeline/Phase1 设置)"""
|
||||
global _g_temp_dir
|
||||
_g_temp_dir = temp_dir
|
||||
|
||||
|
||||
def get_temp_dir() -> str:
|
||||
"""获取全局 temp_dir"""
|
||||
return _g_temp_dir
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 全局泛型特化上下文(由 _specialize_generic_class 设置/清除)
|
||||
#
|
||||
@@ -46,11 +67,55 @@ _g_generic_type_args: list[str] | t.CPtr = None
|
||||
_g_cdefine_names: list[str] | t.CPtr = None
|
||||
_g_cdefine_values: list[str] | t.CPtr = None
|
||||
|
||||
# ============================================================
|
||||
# 全局跨模块 CDefine 表(按模块 SHA1 索引,跨模块持久化)
|
||||
#
|
||||
# 本地表在模块切换时被 clear_cdefine_constants 清空,
|
||||
# 导致跨模块引用(如 HandlesVar.SCOPE_FUNCTION)找不到常量。
|
||||
# 全局表在模块切换时不清空,跨模块查找直接查表,无需文件 I/O。
|
||||
#
|
||||
# 数据布局:
|
||||
# _g_gcdef_sha1s: 每条 17 字节(模块 SHA1 16字符 + null)
|
||||
# _g_gcdef_names: 每条 64 字节(常量名 + null)
|
||||
# _g_gcdef_values: 每条 32 字节(值字符串 + null)
|
||||
# ============================================================
|
||||
MAX_GLOBAL_CDEFINE: t.CDefine = 1024
|
||||
_g_gcdef_sha1s: bytes = None
|
||||
_g_gcdef_names: bytes = None
|
||||
_g_gcdef_values: bytes = None
|
||||
_g_gcdef_count: int = 0
|
||||
_g_current_module_sha1: str = None
|
||||
|
||||
|
||||
def set_current_module_sha1(sha1: str) -> None:
|
||||
"""设置当前正在编译的模块 SHA1(Phase1 在模块开始翻译前调用)"""
|
||||
global _g_current_module_sha1
|
||||
_g_current_module_sha1 = sha1
|
||||
|
||||
|
||||
def _global_cdefine_init(pool: memhub.MemBuddy | t.CPtr) -> int:
|
||||
"""懒初始化全局 CDefine 表"""
|
||||
global _g_gcdef_sha1s, _g_gcdef_names, _g_gcdef_values
|
||||
if _g_gcdef_sha1s is None:
|
||||
_g_gcdef_sha1s = stdlib.malloc(MAX_GLOBAL_CDEFINE * 17)
|
||||
_g_gcdef_names = stdlib.malloc(MAX_GLOBAL_CDEFINE * 64)
|
||||
_g_gcdef_values = stdlib.malloc(MAX_GLOBAL_CDEFINE * 32)
|
||||
if _g_gcdef_sha1s is None or _g_gcdef_names is None or _g_gcdef_values is None:
|
||||
return 1
|
||||
string.memset(_g_gcdef_sha1s, 0, MAX_GLOBAL_CDEFINE * 17)
|
||||
string.memset(_g_gcdef_names, 0, MAX_GLOBAL_CDEFINE * 64)
|
||||
string.memset(_g_gcdef_values, 0, MAX_GLOBAL_CDEFINE * 32)
|
||||
return 0
|
||||
|
||||
|
||||
def register_cdefine_constant(pool: memhub.MemBuddy | t.CPtr,
|
||||
name: str, value: int) -> None:
|
||||
"""注册 CDefine 编译期常量到全局表"""
|
||||
global _g_cdefine_names, _g_cdefine_values
|
||||
"""注册 CDefine 编译期常量到本地表和全局表
|
||||
|
||||
值以十进制字符串存储。注意:value 是 32 位有符号整数,
|
||||
0xFFFFFFFF 会存储为 "-1",lookup 时用 found 标志区分"未找到"和值为 -1。
|
||||
"""
|
||||
global _g_cdefine_names, _g_cdefine_values, _g_gcdef_count
|
||||
if _g_cdefine_names is None:
|
||||
_g_cdefine_names = list[str](pool, 64)
|
||||
_g_cdefine_values = list[str](pool, 64)
|
||||
@@ -61,12 +126,54 @@ def register_cdefine_constant(pool: memhub.MemBuddy | t.CPtr,
|
||||
_g_cdefine_values.append(val_buf)
|
||||
else:
|
||||
_g_cdefine_values.append("0")
|
||||
# 同时注册到全局跨模块表(用当前模块 SHA1 索引)
|
||||
if _g_current_module_sha1 is not None and _g_gcdef_count < MAX_GLOBAL_CDEFINE:
|
||||
if _global_cdefine_init(pool) == 0:
|
||||
sidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 17
|
||||
nidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 64
|
||||
vidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 32
|
||||
string.strcpy(_g_gcdef_sha1s + sidx, _g_current_module_sha1)
|
||||
string.strcpy(_g_gcdef_names + nidx, name)
|
||||
vbuf: str = _g_gcdef_values + vidx
|
||||
viperlib.snprintf(vbuf, 32, "%d", value)
|
||||
_g_gcdef_count += 1
|
||||
|
||||
|
||||
def lookup_global_cdefine(module_sha1: str, name: str) -> int:
|
||||
"""从全局跨模块表查找 CDefine 常量
|
||||
|
||||
用 module_sha1 + name 精确匹配。
|
||||
找到时设置 _g_cdefine_found=1 并返回值;未找到返回 0。
|
||||
"""
|
||||
global _g_cdefine_found
|
||||
if module_sha1 is None or name is None:
|
||||
return 0
|
||||
if _g_gcdef_sha1s is None or _g_gcdef_count <= 0:
|
||||
return 0
|
||||
i: t.CSizeT = 0
|
||||
while i < _g_gcdef_count:
|
||||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||||
nidx: t.CSizeT = t.CSizeT(i) * 64
|
||||
vidx: t.CSizeT = t.CSizeT(i) * 32
|
||||
if string.strcmp(_g_gcdef_sha1s + sidx, module_sha1) == 0:
|
||||
if string.strcmp(_g_gcdef_names + nidx, name) == 0:
|
||||
_g_cdefine_found = 1
|
||||
return string.atoi(_g_gcdef_values + vidx)
|
||||
i += 1
|
||||
return 0
|
||||
|
||||
|
||||
def lookup_cdefine_constant(name: str) -> int:
|
||||
"""查找 CDefine 编译期常量,返回值或 -1(未找到)"""
|
||||
"""查找 CDefine 编译期常量
|
||||
|
||||
返回找到的值。由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1),
|
||||
用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。
|
||||
调用方应在调用后检查 _g_cdefine_found。
|
||||
"""
|
||||
global _g_cdefine_found
|
||||
_g_cdefine_found = 0
|
||||
if _g_cdefine_names is None:
|
||||
return -1
|
||||
return 0
|
||||
n: t.CSizeT = _g_cdefine_names.__len__()
|
||||
i: t.CSizeT
|
||||
for i in range(n):
|
||||
@@ -74,8 +181,23 @@ def lookup_cdefine_constant(name: str) -> int:
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
val_str: str = _g_cdefine_values.get(i)
|
||||
if val_str is not None:
|
||||
_g_cdefine_found = 1
|
||||
return string.atoi(val_str)
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
_g_cdefine_found: int = 0
|
||||
|
||||
|
||||
def is_cdefine_found() -> int:
|
||||
"""检查上次 lookup_cdefine_constant 是否找到值"""
|
||||
return _g_cdefine_found
|
||||
|
||||
|
||||
def set_cdefine_found(val: int) -> None:
|
||||
"""设置 cdefine_found 标志(供跨模块查找函数使用)"""
|
||||
global _g_cdefine_found
|
||||
_g_cdefine_found = val
|
||||
|
||||
|
||||
def clear_cdefine_constants() -> None:
|
||||
@@ -228,6 +350,20 @@ def _parse_type_string(pool: memhub.MemBuddy | t.CPtr,
|
||||
return ast.Name(pool, type_str, ctx)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _parse_type_string_ptr - _parse_type_string 的 t.CPtr 返回类型包装
|
||||
#
|
||||
# 旧编译器对 ast.AST | t.CPtr 跨模块返回类型推断有 bug(解析为 i32),
|
||||
# 用 t.CPtr 包装绕过,外部模块应调用此函数而非 _parse_type_string
|
||||
# ============================================================
|
||||
def _parse_type_string_ptr(pool: memhub.MemBuddy | t.CPtr,
|
||||
type_str: str,
|
||||
ctx: t.CInt) -> t.CPtr:
|
||||
"""_parse_type_string 的 t.CPtr 返回类型包装(绕过旧编译器类型推断 bug)"""
|
||||
result: ast.AST | t.CPtr = _parse_type_string(pool, type_str, ctx)
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================
|
||||
# set_current_file - 设置当前编译的文件名
|
||||
#
|
||||
@@ -709,7 +845,8 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr,
|
||||
def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
imported_modules: str,
|
||||
from_imports: str) -> llvmlite.LLVMType | t.CPtr:
|
||||
from_imports: str,
|
||||
trans: HT.Translator | t.CPtr = None) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""解析类型注解节点为 LLVMType
|
||||
|
||||
Args:
|
||||
@@ -717,6 +854,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
node: 类型注解 AST 节点(Name 或 Attribute)
|
||||
imported_modules: 已导入模块名字符串(空格分隔)
|
||||
from_imports: from-import 映射字符串("name:module name:module" 格式)
|
||||
trans: 翻译器对象(可选,用于触发泛型特化)
|
||||
"""
|
||||
if node is None:
|
||||
return None
|
||||
@@ -727,9 +865,9 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
|
||||
if cn.const_kind == ast.CONST_STR and cn.str_val is not None:
|
||||
sub_node: ast.AST | t.CPtr = _parse_type_string(pool, cn.str_val, ast.Ctx.Load)
|
||||
sub_node: ast.AST | t.CPtr = _parse_type_string(pool, cn.str_val, ast.ASTCtx.Load)
|
||||
if sub_node is not None:
|
||||
return resolve_annotation_type(pool, sub_node, imported_modules, from_imports)
|
||||
return resolve_annotation_type(pool, sub_node, imported_modules, from_imports, trans)
|
||||
return None
|
||||
|
||||
# Name 节点: str, int, bytes, from-import 名称, class 名称等
|
||||
@@ -749,7 +887,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
# 用具体类型名递归解析(支持 BinOp/Attribute 类型实参)
|
||||
gp_sub_node: ast.AST | t.CPtr = _parse_type_string(pool, gp_ta, nm.ctx)
|
||||
if gp_sub_node is not None:
|
||||
return resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports)
|
||||
return resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports, trans)
|
||||
# str = bytes = t.CChar | t.CPtr = i8*
|
||||
if string.strcmp(nm.id, "str") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
@@ -817,7 +955,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
if other_is_cvoid != 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
other_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
|
||||
pool, other_node, imported_modules, from_imports)
|
||||
pool, other_node, imported_modules, from_imports, trans)
|
||||
if other_ty is None:
|
||||
# CPtr 单独使用或其他无效类型 → i8*
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
@@ -825,11 +963,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
# 无 CPtr 标记:返回第一个非 None(装饰器标记如 CExport/CExtern 被忽略)
|
||||
left_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
|
||||
pool, bop.left, imported_modules, from_imports)
|
||||
pool, bop.left, imported_modules, from_imports, trans)
|
||||
if left_ty is not None:
|
||||
return left_ty
|
||||
right_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
|
||||
pool, bop.right, imported_modules, from_imports)
|
||||
pool, bop.right, imported_modules, from_imports, trans)
|
||||
if right_ty is not None:
|
||||
return right_ty
|
||||
return None
|
||||
@@ -891,6 +1029,22 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
gen_spec_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(gen_mangled)
|
||||
if gen_spec_ty is not None:
|
||||
return gen_spec_ty
|
||||
# 未特化:尝试主动触发泛型特化(与 TPC _HandleSubscript 对齐)
|
||||
# 当 trans 可用时,检查是否是泛型模板并触发特化
|
||||
if trans is not None:
|
||||
import lib.core.Handles.HandlesClassDef as HandlesClassDef
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
template_cd_chk: ast.ClassDef | t.CPtr = HandlesClassDef._find_generic_template(gen_nm.id)
|
||||
if template_cd_chk is not None:
|
||||
type_args_chk: list[str] | t.CPtr = HandlesExprCall._extract_type_args_from_slice(
|
||||
pool, sub.slice)
|
||||
if type_args_chk is not None and type_args_chk.__len__() > 0:
|
||||
spec_name_chk: str = HandlesClassDef._specialize_generic_class(
|
||||
trans, gen_nm.id, type_args_chk)
|
||||
if spec_name_chk is not None:
|
||||
gen_spec_ty = HandlesStruct.get_struct_type(gen_mangled)
|
||||
if gen_spec_ty is not None:
|
||||
return gen_spec_ty
|
||||
# 未特化:返回 None(不报错,让后续流程处理)
|
||||
return None
|
||||
# 检查 value 是否是 Attribute(Name('t'), 'CArray')
|
||||
@@ -912,7 +1066,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
# 相当于 t.CArray[elem_ty, None],表示静态推导长度数组
|
||||
if sub.slice.kind() != ast.ASTKind.Tuple:
|
||||
elem_ty_single: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
|
||||
pool, sub.slice, imported_modules, from_imports)
|
||||
pool, sub.slice, imported_modules, from_imports, trans)
|
||||
if elem_ty_single is None:
|
||||
fatal_type_error(sub.slice, "t.CArray[?] 元素类型解析失败")
|
||||
return llvmlite.Ptr(pool, elem_ty_single)
|
||||
@@ -930,7 +1084,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
fatal_type_error(node, "t.CArray Tuple 元素为 None")
|
||||
# 递归解析元素类型
|
||||
elem_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
|
||||
pool, elem_node, imported_modules, from_imports)
|
||||
pool, elem_node, imported_modules, from_imports, trans)
|
||||
if elem_ty is None:
|
||||
fatal_type_error(elem_node, "t.CArray 元素类型解析失败")
|
||||
# 解析 count(必须是整数常量、None 或 CDefine 常量名)
|
||||
@@ -950,7 +1104,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
if cnt_nm.id is None:
|
||||
fatal_type_error(count_node, "t.CArray count Name 节点 id 为 None")
|
||||
looked_up: int = lookup_cdefine_constant(cnt_nm.id)
|
||||
if looked_up < 0:
|
||||
if is_cdefine_found() == 0:
|
||||
fatal_type_error(count_node, "t.CArray count 不是已注册的 CDefine 常量")
|
||||
count_val = looked_up
|
||||
else:
|
||||
@@ -1180,6 +1334,11 @@ def _strappend(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr)
|
||||
remain: t.CSizeT = dst_size - dlen
|
||||
if remain <= 0:
|
||||
return
|
||||
for i in range(min(slen, remain - 1)):
|
||||
# 内联 min(slen, remain - 1) 避免 builtin min 未定义(TransPyV 无 builtin min)
|
||||
limit: t.CSizeT = slen
|
||||
cap: t.CSizeT = remain - 1
|
||||
if cap < limit:
|
||||
limit = cap
|
||||
for i in range(limit):
|
||||
dst[dlen + i] = src[i]
|
||||
dst[dlen + i] = '\0'
|
||||
@@ -36,6 +36,7 @@ class VarEntry:
|
||||
Alloca: llvmlite.Value | t.CPtr
|
||||
Used: t.CInt
|
||||
AnnotClassName: t.CChar | t.CPtr # 原始类型注解的类名(str 别名在结构体字段中触发编译器 bug,改用显式联合类型)
|
||||
IsPtrElement: t.CInt # 标志: 1=注解为 bytes|t.CPtr 或 str|t.CPtr, 下标按 8 字节步长
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -154,6 +155,25 @@ def set_var_annot_class_name(symtab: SymbolTable | t.CPtr,
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# set_var_ptr_element — 标记变量为"指针到 str/bytes"类型
|
||||
#
|
||||
# 注解为 bytes|t.CPtr 或 str|t.CPtr 的变量,其 alloca 类型是 i8*,
|
||||
# 但下标访问应按 8 字节步长(i8** 语义),而非 1 字节步长(i8* 语义)。
|
||||
# 此标志在 HandlesAnnAssign 中根据注解形式设置。
|
||||
# ============================================================
|
||||
def set_var_ptr_element(symtab: SymbolTable | t.CPtr,
|
||||
name: str) -> int:
|
||||
"""标记变量为 ptr_element 类型,返回 0=成功 / 1=失败"""
|
||||
if symtab is None or name is None:
|
||||
return 1
|
||||
entry: VarEntry | t.CPtr = lookup_var_entry(symtab, name)
|
||||
if entry is None:
|
||||
return 1
|
||||
entry.IsPtrElement = 1
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# add_var — 添加变量到变量表
|
||||
# ============================================================
|
||||
@@ -175,6 +195,27 @@ def add_var(vars_ptr: VarEntry | t.CPtr,
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _alloca_at_entry - 在函数入口块生成 alloca(确保支配性)
|
||||
#
|
||||
# LLVM IR 要求指令支配所有使用点。如果 alloca 在条件分支内生成,
|
||||
# 但在其他分支使用,会违反支配性(Instruction does not dominate all uses)。
|
||||
# 标准做法:所有 alloca 在函数入口块生成。
|
||||
#
|
||||
# 临时切换 builder.CurBlock 到入口块,生成 alloca,然后恢复。
|
||||
# ============================================================
|
||||
def _alloca_at_entry(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""在函数入口块生成 alloca,返回 alloca 值
|
||||
|
||||
使用 llvmlite.build_alloca_at_entry 在入口块终止指令之前插入 alloca,
|
||||
确保所有 alloca 在入口块,避免支配性违规。
|
||||
"""
|
||||
if builder is None or ty is None:
|
||||
return None
|
||||
return llvmlite.build_alloca_at_entry(builder, ty)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# get_or_create_var — 查找或创建变量 alloca(旧版兼容)
|
||||
# ============================================================
|
||||
@@ -188,7 +229,7 @@ def get_or_create_var(pool: memhub.MemBuddy | t.CPtr,
|
||||
existing: llvmlite.Value | t.CPtr = find_var(vars_ptr, var_count, name)
|
||||
if existing is not None:
|
||||
return existing
|
||||
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, ty)
|
||||
alloca: llvmlite.Value | t.CPtr = _alloca_at_entry(builder, ty)
|
||||
if alloca is None:
|
||||
return None
|
||||
if add_var(vars_ptr, var_count, name, alloca) != 0:
|
||||
@@ -358,7 +399,7 @@ def get_or_create_sym(symtab: SymbolTable | t.CPtr,
|
||||
existing: llvmlite.Value | t.CPtr = lookup_current(symtab, name)
|
||||
if existing is not None:
|
||||
return existing
|
||||
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, ty)
|
||||
alloca: llvmlite.Value | t.CPtr = _alloca_at_entry(builder, ty)
|
||||
if alloca is None:
|
||||
return None
|
||||
define_var(symtab, name, alloca)
|
||||
|
||||
@@ -9,6 +9,7 @@ import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import hashlib
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -312,7 +313,10 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr,
|
||||
sha1: str = compute_file_sha1(pool, full_path)
|
||||
if sha1 is not None:
|
||||
add_file_entry(result, pool, full_path, rel_path, sha1)
|
||||
stdio.printf(" [scan] %s -> %s\n", rel_path, sha1)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "扫描: %s -> %s", rel_path, sha1)
|
||||
VLogger.debug(fb, "scan")
|
||||
|
||||
# 继续搜索下一个文件
|
||||
if win32file.FindNextFileA(handle, find_data) == 0:
|
||||
|
||||
@@ -16,6 +16,7 @@ import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
import lib.core.Handles.HandlesType as HandlesType
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.Handles.HandlesImports as HandlesImports
|
||||
import lib.core.Handles.HandlesClassDef as HandlesClassDef
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.StubMerger as StubMerger
|
||||
import lib.core.BuildPipeline as BuildPipeline
|
||||
@@ -31,6 +32,431 @@ SRC_BUF_SIZE: t.CDefine = 1048576
|
||||
# pyi 缓冲区大小(256KB)
|
||||
PYI_BUF_SIZE: t.CSizeT = 262144
|
||||
|
||||
# 拓扑排序相关常量
|
||||
MAX_CLASSES: t.CDefine = 512
|
||||
MAX_FILE_DEPS: t.CDefine = 256
|
||||
MAX_DEP_EDGES: t.CDefine = 16
|
||||
SHA1_LEN: t.CDefine = 17
|
||||
CLASS_NAME_LEN: t.CDefine = 64
|
||||
|
||||
# 拓扑排序使用扁平字节数组存储类信息和文件依赖,
|
||||
# 避免结构体中的 str 指针字段在 mb.alloc 零填充时为 NULL 导致崩溃。
|
||||
# 类名/父类名存储在 class_names_buf/parent_names_buf(每个 CLASS_NAME_LEN 字节),
|
||||
# 定义 SHA1 存储在 def_sha1s_buf(每个 SHA1_LEN 字节)。
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _ScanClassInheritance - 预扫描 includes 文件,收集类继承关系
|
||||
#
|
||||
# 遍历所有可达 includes 文件,解析 AST 收集 ClassDef 的类名和父类名。
|
||||
# 结果存入扁平字节数组,返回收集到的类数量。
|
||||
#
|
||||
# Args:
|
||||
# mb: 内存池
|
||||
# result: IncludesScanner 扫描结果
|
||||
# reachable_set: 可达 SHA1 集合
|
||||
# reachable_count: 可达 SHA1 数量
|
||||
# use_reachable: 是否使用可达集合过滤
|
||||
# sha1_set: SHA1 集合(use_reachable=0 时使用)
|
||||
# set_count: SHA1 集合数量
|
||||
# class_names_buf: 类名扁平数组(每个 CLASS_NAME_LEN 字节,连续内存)
|
||||
# parent_names_buf: 父类名扁平数组(每个 CLASS_NAME_LEN 字节,无父类填 '\0')
|
||||
# def_sha1s_buf: 定义 SHA1 扁平数组(每个 SHA1_LEN 字节)
|
||||
#
|
||||
# Returns:
|
||||
# 收集到的类数量,-1 失败
|
||||
# ============================================================
|
||||
def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
|
||||
result: IncludesScanner.ScanResult | t.CPtr,
|
||||
reachable_set: str, reachable_count: int,
|
||||
use_reachable: int,
|
||||
sha1_set: str, set_count: int,
|
||||
class_names_buf: bytes,
|
||||
parent_names_buf: bytes,
|
||||
def_sha1s_buf: bytes) -> int:
|
||||
"""预扫描 includes 文件,收集类继承关系"""
|
||||
if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
|
||||
return -1
|
||||
|
||||
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
class_count: int = 0
|
||||
|
||||
for i in range(result.Count):
|
||||
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
|
||||
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
|
||||
if entry is None or entry.Sha1 is None:
|
||||
continue
|
||||
|
||||
sha1_e: str = entry.Sha1
|
||||
|
||||
# 按需翻译过滤
|
||||
in_set: int = 0
|
||||
if use_reachable != 0:
|
||||
in_set = StubMerger._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||
else:
|
||||
in_set = StubMerger._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||
if in_set == 0:
|
||||
continue
|
||||
|
||||
# 读取文件
|
||||
file_path: str = entry.Path
|
||||
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
|
||||
if f.closed:
|
||||
continue
|
||||
|
||||
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
|
||||
if src_buf is None:
|
||||
f.close()
|
||||
continue
|
||||
|
||||
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
|
||||
f.close()
|
||||
if bytes_read <= 0:
|
||||
stdlib.free(src_buf)
|
||||
continue
|
||||
if bytes_read < SRC_BUF_SIZE:
|
||||
src_buf[bytes_read] = 0
|
||||
else:
|
||||
src_buf[SRC_BUF_SIZE - 1] = 0
|
||||
|
||||
# 解析 AST
|
||||
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
|
||||
if lx is None:
|
||||
stdlib.free(src_buf)
|
||||
continue
|
||||
ast._lexer_init(lx, src_buf, mb)
|
||||
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
|
||||
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
|
||||
if tree is None:
|
||||
stdlib.free(src_buf)
|
||||
continue
|
||||
|
||||
# 遍历 AST 顶层节点,收集 ClassDef
|
||||
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
|
||||
if ch is not None:
|
||||
cn: t.CSizeT = ch.__len__()
|
||||
for ci in range(cn):
|
||||
child: ast.AST | t.CPtr = ch.get(ci)
|
||||
if child is None:
|
||||
continue
|
||||
kd: int = child.kind()
|
||||
if kd != ast.ASTKind.ClassDef:
|
||||
continue
|
||||
|
||||
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
|
||||
if cd is None or cd.name is None:
|
||||
continue
|
||||
|
||||
if class_count >= MAX_CLASSES:
|
||||
break
|
||||
|
||||
# 填充扁平数组:类名(偏移 = class_count * CLASS_NAME_LEN)
|
||||
name_dst: str = class_names_buf + class_count * CLASS_NAME_LEN
|
||||
nm_len: t.CSizeT = string.strlen(cd.name)
|
||||
if nm_len >= CLASS_NAME_LEN:
|
||||
string.strncpy(name_dst, cd.name, CLASS_NAME_LEN - 1)
|
||||
name_dst[CLASS_NAME_LEN - 1] = '\0'
|
||||
else:
|
||||
string.strcpy(name_dst, cd.name)
|
||||
|
||||
# 填充扁平数组:父类名(无父类填 '\0')
|
||||
parent_dst: str = parent_names_buf + class_count * CLASS_NAME_LEN
|
||||
parent_dst[0] = '\0'
|
||||
parent_nm: str = HandlesClassDef._get_parent_class(cd, None)
|
||||
# parent_nm 是 AST 节点内部指针,不需要释放
|
||||
if parent_nm is not None:
|
||||
pnm_len: t.CSizeT = string.strlen(parent_nm)
|
||||
if pnm_len >= CLASS_NAME_LEN:
|
||||
string.strncpy(parent_dst, parent_nm, CLASS_NAME_LEN - 1)
|
||||
parent_dst[CLASS_NAME_LEN - 1] = '\0'
|
||||
else:
|
||||
string.strcpy(parent_dst, parent_nm)
|
||||
|
||||
# 填充扁平数组:定义 SHA1(偏移 = class_count * SHA1_LEN)
|
||||
sha1_dst: str = def_sha1s_buf + class_count * SHA1_LEN
|
||||
string.strcpy(sha1_dst, sha1_e)
|
||||
|
||||
class_count += 1
|
||||
|
||||
stdlib.free(src_buf)
|
||||
|
||||
return class_count
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
|
||||
#
|
||||
# Args:
|
||||
# class_names_buf: 类名扁平数组
|
||||
# def_sha1s_buf: 定义 SHA1 扁平数组
|
||||
# class_count: 类数量
|
||||
# class_name: 要查找的类名
|
||||
#
|
||||
# Returns:
|
||||
# SHA1 字符串指针(指向 def_sha1s_buf 内部偏移),None 未找到
|
||||
# ============================================================
|
||||
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes,
|
||||
class_count: int, class_name: str) -> str:
|
||||
"""查找类名对应的定义文件 SHA1"""
|
||||
if class_names_buf is None or def_sha1s_buf is None or class_name is None:
|
||||
return None
|
||||
for i in range(class_count):
|
||||
name_ptr: str = class_names_buf + i * CLASS_NAME_LEN
|
||||
if string.strcmp(name_ptr, class_name) == 0:
|
||||
return def_sha1s_buf + i * SHA1_LEN
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序
|
||||
#
|
||||
# 算法(Kahn):
|
||||
# 1. 从扁平类信息数组构建"文件 SHA1 → 依赖文件 SHA1 集合"映射
|
||||
# 2. 计算每个文件的入度(依赖数量)
|
||||
# 3. 入度为 0 的文件先处理,处理后减少依赖它的文件入度
|
||||
# 4. 重复直到所有文件处理完成
|
||||
#
|
||||
# 注意:依赖方向是"父类所在文件 → 子类所在文件",
|
||||
# 即父类文件必须先处理。Kahn 用 DepCount 作为剩余依赖计数,
|
||||
# 依赖为 0 的先处理,初始化 InDegree = DepCount。
|
||||
#
|
||||
# Args:
|
||||
# mb: 内存池
|
||||
# result: IncludesScanner 扫描结果
|
||||
# reachable_set: 可达 SHA1 集合
|
||||
# reachable_count: 可达 SHA1 数量
|
||||
# use_reachable: 是否使用可达集合过滤
|
||||
# sha1_set: SHA1 集合
|
||||
# set_count: SHA1 集合数量
|
||||
# class_names_buf: 类名扁平数组
|
||||
# parent_names_buf: 父类名扁平数组
|
||||
# def_sha1s_buf: 定义 SHA1 扁平数组
|
||||
# class_count: 类数量
|
||||
# out_order: 输出拓扑顺序的文件索引数组(调用者分配,int 数组)
|
||||
# out_count: 输出文件数量
|
||||
#
|
||||
# Returns:
|
||||
# 0 成功,-1 失败
|
||||
# ============================================================
|
||||
def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
|
||||
result: IncludesScanner.ScanResult | t.CPtr,
|
||||
reachable_set: str, reachable_count: int,
|
||||
use_reachable: int,
|
||||
sha1_set: str, set_count: int,
|
||||
class_names_buf: bytes, parent_names_buf: bytes,
|
||||
def_sha1s_buf: bytes, class_count: int,
|
||||
out_order: t.CPtr, out_count: t.CPtr) -> int:
|
||||
"""根据类继承关系对文件进行拓扑排序"""
|
||||
if result is None or out_order is None or out_count is None:
|
||||
return -1
|
||||
|
||||
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
|
||||
# 1. 分配扁平数组:文件 SHA1、依赖 SHA1 列表、依赖数量、入度、处理状态
|
||||
# 用扁平数组代替 FileDepEntry 结构体,避免 str 指针字段 NULL 崩溃
|
||||
file_sha1s_buf: bytes = mb.alloc(MAX_FILE_DEPS * SHA1_LEN)
|
||||
file_deps_buf: bytes = mb.alloc(MAX_FILE_DEPS * MAX_DEP_EDGES * SHA1_LEN)
|
||||
file_dep_counts: t.CPtr = mb.alloc(MAX_FILE_DEPS * 4)
|
||||
file_indegrees: t.CPtr = mb.alloc(MAX_FILE_DEPS * 4)
|
||||
file_processed: t.CPtr = mb.alloc(MAX_FILE_DEPS * 4)
|
||||
|
||||
if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None:
|
||||
if file_sha1s_buf is not None:
|
||||
mb.free(file_sha1s_buf)
|
||||
if file_deps_buf is not None:
|
||||
mb.free(file_deps_buf)
|
||||
if file_dep_counts is not None:
|
||||
mb.free(file_dep_counts)
|
||||
if file_indegrees is not None:
|
||||
mb.free(file_indegrees)
|
||||
if file_processed is not None:
|
||||
mb.free(file_processed)
|
||||
return -1
|
||||
|
||||
string.memset(file_sha1s_buf, 0, MAX_FILE_DEPS * SHA1_LEN)
|
||||
string.memset(file_deps_buf, 0, MAX_FILE_DEPS * MAX_DEP_EDGES * SHA1_LEN)
|
||||
string.memset(file_dep_counts, 0, MAX_FILE_DEPS * 4)
|
||||
string.memset(file_indegrees, 0, MAX_FILE_DEPS * 4)
|
||||
string.memset(file_processed, 0, MAX_FILE_DEPS * 4)
|
||||
|
||||
file_count: int = 0
|
||||
# sha1→file_index 映射(简单线性查找,文件数不多)
|
||||
for i in range(result.Count):
|
||||
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
|
||||
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
|
||||
if entry is None or entry.Sha1 is None:
|
||||
continue
|
||||
|
||||
sha1_e: str = entry.Sha1
|
||||
in_set: int = 0
|
||||
if use_reachable != 0:
|
||||
in_set = StubMerger._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||
else:
|
||||
in_set = StubMerger._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||
if in_set == 0:
|
||||
continue
|
||||
|
||||
if file_count >= MAX_FILE_DEPS:
|
||||
break
|
||||
|
||||
# 初始化扁平数组:文件 SHA1(偏移 = file_count * SHA1_LEN)
|
||||
# DepCount/InDegree/Processed 已由 memset 清零
|
||||
sha1_dst: str = file_sha1s_buf + file_count * SHA1_LEN
|
||||
string.strcpy(sha1_dst, sha1_e)
|
||||
|
||||
file_count += 1
|
||||
|
||||
# 2. 构建依赖边:对每个类,找到父类定义所在文件,建立 子文件→父文件 依赖
|
||||
for ci in range(class_count):
|
||||
# 读取父类名(偏移 = ci * CLASS_NAME_LEN)
|
||||
parent_ptr: str = parent_names_buf + ci * CLASS_NAME_LEN
|
||||
if parent_ptr[0] == '\0':
|
||||
continue
|
||||
|
||||
# 读取子类定义 SHA1(偏移 = ci * SHA1_LEN)
|
||||
child_def_sha1: str = def_sha1s_buf + ci * SHA1_LEN
|
||||
|
||||
# 查找父类定义所在文件的 SHA1
|
||||
parent_sha1: str = _FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr)
|
||||
if parent_sha1 is None:
|
||||
continue # 父类不在 includes 中(如 Object、CEnum 等标记基类)
|
||||
|
||||
# 父类文件 SHA1 不能等于子类文件 SHA1(同文件内继承不需要依赖)
|
||||
if string.strcmp(parent_sha1, child_def_sha1) == 0:
|
||||
continue
|
||||
|
||||
# 找到子类文件对应的索引,添加父文件 SHA1 到依赖列表
|
||||
for fi in range(file_count):
|
||||
fsha1: str = file_sha1s_buf + fi * SHA1_LEN
|
||||
if string.strcmp(fsha1, child_def_sha1) != 0:
|
||||
continue
|
||||
|
||||
# 读取当前依赖数量
|
||||
cnt_addr: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi * 4
|
||||
cnt_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr, t.CPtr))
|
||||
if cnt_ptr is None:
|
||||
break
|
||||
cur_cnt: int = cnt_ptr[0]
|
||||
|
||||
# 检查依赖是否已添加(去重)
|
||||
already: int = 0
|
||||
for di in range(cur_cnt):
|
||||
dep_slot: str = file_deps_buf + fi * MAX_DEP_EDGES * SHA1_LEN + di * SHA1_LEN
|
||||
if string.strcmp(dep_slot, parent_sha1) == 0:
|
||||
already = 1
|
||||
break
|
||||
if already != 0:
|
||||
break
|
||||
|
||||
if cur_cnt < MAX_DEP_EDGES:
|
||||
dep_dst: str = file_deps_buf + fi * MAX_DEP_EDGES * SHA1_LEN + cur_cnt * SHA1_LEN
|
||||
string.strcpy(dep_dst, parent_sha1)
|
||||
cnt_ptr[0] = cur_cnt + 1
|
||||
break
|
||||
|
||||
# 3. Kahn 算法:InDegree = DepCount(依赖多少个父文件)
|
||||
# 依赖为 0 的先处理,处理后减少依赖它的文件入度
|
||||
for fi in range(file_count):
|
||||
cnt_addr3: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi * 4
|
||||
cnt_ptr3: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr3, t.CPtr))
|
||||
ind_addr3: t.CUInt64T = t.CUInt64T(file_indegrees) + fi * 4
|
||||
ind_ptr3: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr3, t.CPtr))
|
||||
if cnt_ptr3 is not None and ind_ptr3 is not None:
|
||||
ind_ptr3[0] = cnt_ptr3[0]
|
||||
|
||||
# 4. 拓扑排序输出
|
||||
out_idx: int = 0
|
||||
changed: int = 1
|
||||
while changed != 0:
|
||||
changed = 0
|
||||
for fi in range(file_count):
|
||||
# 检查处理状态
|
||||
proc_addr4: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4
|
||||
proc_ptr4: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr4, t.CPtr))
|
||||
if proc_ptr4 is None or proc_ptr4[0] != 0:
|
||||
continue
|
||||
# 检查入度
|
||||
ind_addr4: t.CUInt64T = t.CUInt64T(file_indegrees) + fi * 4
|
||||
ind_ptr4: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr4, t.CPtr))
|
||||
if ind_ptr4 is None or ind_ptr4[0] > 0:
|
||||
continue
|
||||
|
||||
# 入度为 0,可以处理
|
||||
# 找到对应的 result.Entries 索引
|
||||
target_sha1: str = file_sha1s_buf + fi * SHA1_LEN
|
||||
for ri in range(result.Count):
|
||||
ea_r: t.CUInt64T = t.CUInt64T(result.Entries) + ri * entry_size
|
||||
ent_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_r, t.CPtr))
|
||||
if ent_r is not None and ent_r.Sha1 is not None:
|
||||
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
|
||||
# 写入输出索引数组
|
||||
out_idx_addr: t.CUInt64T = t.CUInt64T(out_order) + out_idx * 4
|
||||
out_int_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_idx_addr, t.CPtr))
|
||||
if out_int_ptr is not None:
|
||||
out_int_ptr[0] = ri
|
||||
out_idx += 1
|
||||
break
|
||||
|
||||
# 标记已处理
|
||||
proc_ptr4[0] = 1
|
||||
changed = 1
|
||||
|
||||
# 减少依赖该文件的其他文件的入度
|
||||
for fj in range(file_count):
|
||||
proc_addr5: t.CUInt64T = t.CUInt64T(file_processed) + fj * 4
|
||||
proc_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr5, t.CPtr))
|
||||
if proc_ptr5 is None or proc_ptr5[0] != 0:
|
||||
continue
|
||||
# 读取 fj 的依赖数量
|
||||
cnt_addr5: t.CUInt64T = t.CUInt64T(file_dep_counts) + fj * 4
|
||||
cnt_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr5, t.CPtr))
|
||||
if cnt_ptr5 is None:
|
||||
continue
|
||||
fj_dep_cnt: int = cnt_ptr5[0]
|
||||
for di in range(fj_dep_cnt):
|
||||
dep_slot5: str = file_deps_buf + fj * MAX_DEP_EDGES * SHA1_LEN + di * SHA1_LEN
|
||||
if string.strcmp(dep_slot5, target_sha1) == 0:
|
||||
ind_addr5: t.CUInt64T = t.CUInt64T(file_indegrees) + fj * 4
|
||||
ind_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr5, t.CPtr))
|
||||
if ind_ptr5 is not None:
|
||||
ind_ptr5[0] = ind_ptr5[0] - 1
|
||||
break
|
||||
|
||||
# 处理循环依赖(未处理的文件按原顺序追加)
|
||||
if out_idx < file_count:
|
||||
for fi in range(file_count):
|
||||
proc_addr6: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4
|
||||
proc_ptr6: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr6, t.CPtr))
|
||||
if proc_ptr6 is None or proc_ptr6[0] != 0:
|
||||
continue
|
||||
target_sha1_2: str = file_sha1s_buf + fi * SHA1_LEN
|
||||
for ri in range(result.Count):
|
||||
ea_r2: t.CUInt64T = t.CUInt64T(result.Entries) + ri * entry_size
|
||||
ent_r2: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_r2, t.CPtr))
|
||||
if ent_r2 is not None and ent_r2.Sha1 is not None:
|
||||
if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0:
|
||||
out_idx_addr2: t.CUInt64T = t.CUInt64T(out_order) + out_idx * 4
|
||||
out_int_ptr2: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_idx_addr2, t.CPtr))
|
||||
if out_int_ptr2 is not None:
|
||||
out_int_ptr2[0] = ri
|
||||
out_idx += 1
|
||||
break
|
||||
proc_ptr6[0] = 1
|
||||
|
||||
# 写入输出数量
|
||||
out_count_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_count, t.CPtr))
|
||||
if out_count_ptr is not None:
|
||||
out_count_ptr[0] = out_idx
|
||||
|
||||
# 释放扁平数组
|
||||
mb.free(file_sha1s_buf)
|
||||
mb.free(file_deps_buf)
|
||||
mb.free(file_dep_counts)
|
||||
mb.free(file_indegrees)
|
||||
mb.free(file_processed)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# RunPhase1 - Phase1: 扫描 includes 目录,按需翻译并生成 stub
|
||||
@@ -51,9 +477,12 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
log: VLogger.Logger | t.CPtr) -> int:
|
||||
"""Phase1: 扫描 includes 目录,按需翻译并生成 stub"""
|
||||
if includes_dir is None or temp_dir is None:
|
||||
stdio.printf("[Phase1] includes_dir 或 temp_dir 为空,跳过\n")
|
||||
VLogger.warning("includes_dir 或 temp_dir 为空,跳过", "Phase1")
|
||||
return 1
|
||||
|
||||
# 设置全局 temp_dir(供跨模块 CDefine 查找使用)
|
||||
HandlesType.set_temp_dir(temp_dir)
|
||||
|
||||
# 确保 temp 目录存在(build_dir/temp 可能尚未创建)
|
||||
BuildPipeline.ensure_dir(temp_dir)
|
||||
|
||||
@@ -63,25 +492,35 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
# 扫描 includes 目录
|
||||
result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir)
|
||||
if result is None:
|
||||
stdio.printf("[Phase1] 扫描失败\n")
|
||||
VLogger.error("扫描失败", "Phase1")
|
||||
return 1
|
||||
|
||||
# 读取 _sha1_map.txt 获取需要的 includes SHA1 集合
|
||||
# 优先使用 Projectrans.py 生成的 _sha1_map.txt(含依赖分析,只包含需要的 includes)
|
||||
# 若不存在,则从扫描结果生成(包含所有 includes,可能导致结构体表溢出)
|
||||
# 从扫描结果直接构建 SHA1 集合(不依赖 _sha1_map.txt 文件)
|
||||
# Phase1 作为编译器自身,应从内存中的扫描结果获取 SHA1,避免外部文件依赖
|
||||
sha1_set: str = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17)
|
||||
if sha1_set is None:
|
||||
stdio.printf("[Phase1] sha1_set 分配失败\n")
|
||||
VLogger.error("sha1_set 分配失败", "Phase1")
|
||||
return 1
|
||||
string.memset(sha1_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
|
||||
set_count: int = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set)
|
||||
set_count: int = 0
|
||||
p1_entry_size_init: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
for se_i in range(result.Count):
|
||||
if set_count >= StubMerger.MAX_INCLUDES_SHA1:
|
||||
break
|
||||
se_addr: t.CUInt64T = t.CUInt64T(result.Entries) + se_i * p1_entry_size_init
|
||||
se_ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(se_addr, t.CPtr))
|
||||
if se_ent is None or se_ent.Sha1 is None:
|
||||
continue
|
||||
string.strcpy(sha1_set + set_count * 17, se_ent.Sha1)
|
||||
set_count += 1
|
||||
if set_count <= 0:
|
||||
stdio.printf("[Phase1] _sha1_map.txt 不存在或为空,从扫描结果生成\n")
|
||||
StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0)
|
||||
set_count = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set)
|
||||
if set_count < 0:
|
||||
stdio.printf("[Phase1] 无法加载 _sha1_map.txt,跳过 Phase1\n")
|
||||
return 1
|
||||
VLogger.warning("扫描结果无有效 SHA1,跳过 Phase1(项目可能无 includes 依赖)", "Phase1")
|
||||
stdlib.free(sha1_set)
|
||||
return 0
|
||||
# 写入 _sha1_map.txt(人类可读输出,程序内部不读取;机器分析使用 PopulateSha1MapStore 内存存储器)
|
||||
StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0)
|
||||
# 填充全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
|
||||
StubMerger.PopulateSha1MapStore(result)
|
||||
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用)
|
||||
td_len_p1map: t.CSizeT = string.strlen(temp_dir)
|
||||
p1_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
@@ -172,6 +611,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
tr_a._declare_only = 2
|
||||
tr_a.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry_a.RelPath)
|
||||
HandlesType.set_current_file(file_path_a)
|
||||
HandlesType.set_current_module_sha1(sha1_a)
|
||||
HandlesType.clear_cdefine_constants()
|
||||
HandlesStruct.reset_visible_structs(mb, 0)
|
||||
ret_a: int = tr_a.translate(tree_a)
|
||||
@@ -235,13 +675,69 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
# 前面就需要用。Phase 1a 先注册所有 struct/enum/union 到全局表,
|
||||
# Phase 1b 全量翻译时 struct 已注册,走 existing 路径只翻译方法体。
|
||||
# 只处理可达文件,避免翻译不需要的 includes(如 Test 不依赖 ast 模块)。
|
||||
#
|
||||
# 拓扑排序:预扫描所有文件收集类继承关系,按依赖顺序处理文件,
|
||||
# 确保父类所在文件先于子类所在文件处理(如 base.py 先于 astaux.py)。
|
||||
# ============================================================
|
||||
p1a_reg: int = 0
|
||||
p1a_skp: int = 0
|
||||
p1a_fl: int = 0
|
||||
|
||||
for i in range(result.Count):
|
||||
entry_addr_r: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
|
||||
# 预扫描类继承关系,构建拓扑顺序(用扁平字节数组,避免结构体 str 指针 NULL 崩溃)
|
||||
class_names_buf: bytes = mb.alloc(MAX_CLASSES * CLASS_NAME_LEN)
|
||||
parent_names_buf: bytes = mb.alloc(MAX_CLASSES * CLASS_NAME_LEN)
|
||||
def_sha1s_buf: bytes = mb.alloc(MAX_CLASSES * SHA1_LEN)
|
||||
topo_order: t.CPtr = mb.alloc(4 * MAX_FILE_DEPS)
|
||||
topo_count_box: t.CPtr = mb.alloc(4)
|
||||
topo_count: int = 0
|
||||
|
||||
if class_names_buf is not None and parent_names_buf is not None and def_sha1s_buf is not None and topo_order is not None and topo_count_box is not None:
|
||||
string.memset(class_names_buf, 0, MAX_CLASSES * CLASS_NAME_LEN)
|
||||
string.memset(parent_names_buf, 0, MAX_CLASSES * CLASS_NAME_LEN)
|
||||
string.memset(def_sha1s_buf, 0, MAX_CLASSES * SHA1_LEN)
|
||||
string.memset(topo_order, 0, 4 * MAX_FILE_DEPS)
|
||||
string.memset(topo_count_box, 0, 4)
|
||||
|
||||
class_count_scanned: int = _ScanClassInheritance(
|
||||
mb, result, reachable_set, reachable_count,
|
||||
use_reachable, sha1_set, set_count,
|
||||
class_names_buf, parent_names_buf, def_sha1s_buf)
|
||||
|
||||
if class_count_scanned > 0:
|
||||
ret_topo: int = _TopoSortFiles(
|
||||
mb, result, reachable_set, reachable_count,
|
||||
use_reachable, sha1_set, set_count,
|
||||
class_names_buf, parent_names_buf, def_sha1s_buf, class_count_scanned,
|
||||
topo_order, topo_count_box)
|
||||
if ret_topo == 0:
|
||||
tc_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(topo_count_box, t.CPtr))
|
||||
if tc_ptr is not None:
|
||||
topo_count = tc_ptr[0]
|
||||
fb_topo: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_topo is not None:
|
||||
viperlib.snprintf(fb_topo, 1024,
|
||||
"拓扑排序: %d 个类, %d 个文件按依赖顺序处理",
|
||||
class_count_scanned, topo_count)
|
||||
VLogger.info(fb_topo, "Phase1")
|
||||
else:
|
||||
VLogger.warning("拓扑排序失败,回退到字母序", "Phase1")
|
||||
topo_count = 0
|
||||
else:
|
||||
VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1")
|
||||
topo_count = 0
|
||||
|
||||
# 遍历文件:优先使用拓扑顺序,回退到字母序
|
||||
iter_count: int = topo_count if topo_count > 0 else result.Count
|
||||
for i in range(iter_count):
|
||||
# 获取文件索引:拓扑顺序或字母序
|
||||
file_idx: int = i
|
||||
if topo_count > 0:
|
||||
od_addr: t.CUInt64T = t.CUInt64T(topo_order) + i * 4
|
||||
od_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(od_addr, t.CPtr))
|
||||
if od_ptr is not None:
|
||||
file_idx = od_ptr[0]
|
||||
|
||||
entry_addr_r: t.CUInt64T = t.CUInt64T(result.Entries) + file_idx * entry_size
|
||||
entry_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_r, t.CPtr))
|
||||
if entry_r is None:
|
||||
p1a_fl += 1
|
||||
@@ -252,15 +748,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
p1a_fl += 1
|
||||
continue
|
||||
|
||||
# 按需翻译过滤:只处理可达文件
|
||||
if use_reachable != 0:
|
||||
if StubMerger._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0:
|
||||
p1a_skp += 1
|
||||
continue
|
||||
else:
|
||||
if StubMerger._is_in_sha1_set(sha1_r, sha1_set, set_count) == 0:
|
||||
p1a_skp += 1
|
||||
continue
|
||||
# 按需翻译过滤(拓扑排序已过滤,但字母序回退需要检查)
|
||||
if topo_count == 0:
|
||||
if use_reachable != 0:
|
||||
if StubMerger._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0:
|
||||
p1a_skp += 1
|
||||
continue
|
||||
else:
|
||||
if StubMerger._is_in_sha1_set(sha1_r, sha1_set, set_count) == 0:
|
||||
p1a_skp += 1
|
||||
continue
|
||||
|
||||
# 读取文件内容
|
||||
file_path_r: str = entry_r.Path
|
||||
@@ -310,6 +807,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
tr_r._declare_only = 1
|
||||
tr_r.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry_r.RelPath)
|
||||
HandlesType.set_current_file(file_path_r)
|
||||
HandlesType.set_current_module_sha1(sha1_r)
|
||||
HandlesType.clear_cdefine_constants()
|
||||
HandlesStruct.reset_visible_structs(mb, 0)
|
||||
ret_r: int = tr_r.translate(tree_r)
|
||||
@@ -325,6 +823,18 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
stdlib.free(tr_r._nonlocal_names)
|
||||
stdlib.free(src_buf_r)
|
||||
|
||||
# 释放拓扑排序资源
|
||||
if class_names_buf is not None:
|
||||
mb.free(class_names_buf)
|
||||
if parent_names_buf is not None:
|
||||
mb.free(parent_names_buf)
|
||||
if def_sha1s_buf is not None:
|
||||
mb.free(def_sha1s_buf)
|
||||
if topo_order is not None:
|
||||
mb.free(topo_order)
|
||||
if topo_count_box is not None:
|
||||
mb.free(topo_count_box)
|
||||
|
||||
# ============================================================
|
||||
# Phase 1b: 全量翻译(struct 已注册,走 existing 路径翻译方法体)
|
||||
# ============================================================
|
||||
@@ -380,12 +890,17 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
stdlib.free(text_path)
|
||||
# text.ll 不存在,需要重新翻译
|
||||
rp: str = entry.RelPath
|
||||
stdio.printf("[Phase1] text.ll 不存在,重新翻译: %s (sha1=%s)\n", rp, sha1)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "text.ll 不存在,重新翻译: %s (sha1=%s)", rp, sha1)
|
||||
VLogger.info(fb, "Phase1")
|
||||
else:
|
||||
# stub 不存在,需要翻译
|
||||
rp: str = entry.RelPath
|
||||
stdio.printf("[Phase1] 翻译: %s (sha1=%s)\n", rp, sha1)
|
||||
stdio.fflush(0)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "翻译: %s (sha1=%s)", rp, sha1)
|
||||
VLogger.info(fb, "Phase1")
|
||||
|
||||
stdlib.free(stub_path)
|
||||
|
||||
@@ -393,13 +908,17 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
file_path: str = entry.Path
|
||||
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
|
||||
if f is None:
|
||||
stdio.printf("[Phase1] 无法打开(None): %s\n", file_path)
|
||||
stdio.fflush(0)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "无法打开(None): %s", file_path)
|
||||
VLogger.error(fb, "Phase1")
|
||||
failed += 1
|
||||
continue
|
||||
if f.closed:
|
||||
stdio.printf("[Phase1] 无法打开: %s\n", file_path)
|
||||
stdio.fflush(0)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "无法打开: %s", file_path)
|
||||
VLogger.error(fb, "Phase1")
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
@@ -412,8 +931,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
|
||||
f.close()
|
||||
if bytes_read <= 0:
|
||||
stdio.printf("[Phase1] 读取失败: %s\n", file_path)
|
||||
stdio.fflush(0)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "读取失败: %s", file_path)
|
||||
VLogger.error(fb, "Phase1")
|
||||
stdlib.free(src_buf)
|
||||
failed += 1
|
||||
continue
|
||||
@@ -423,6 +944,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
src_buf[SRC_BUF_SIZE - 1] = 0
|
||||
|
||||
# 解析 AST
|
||||
stdio.printf("[P1B-1] parse %s\n", entry.RelPath)
|
||||
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
|
||||
if lx is None:
|
||||
stdlib.free(src_buf)
|
||||
@@ -431,14 +953,18 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
ast._lexer_init(lx, src_buf, mb)
|
||||
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
|
||||
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
|
||||
stdio.printf("[P1B-1a] parse-done %s\n", entry.RelPath)
|
||||
if tree is None:
|
||||
stdio.printf("[Phase1] AST 解析失败: %s\n", file_path)
|
||||
stdio.fflush(0)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "AST 解析失败: %s", file_path)
|
||||
VLogger.error(fb, "Phase1")
|
||||
stdlib.free(src_buf)
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
# 生成 .pyi 存根文件(直接遍历 AST,不依赖 PythonToStubConverter)
|
||||
stdio.printf("[P1B-2] pyi %s\n", entry.RelPath)
|
||||
pyi_buf: bytes = stdlib.malloc(PYI_BUF_SIZE)
|
||||
if pyi_buf is not None:
|
||||
pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst(mb, tree, entry.RelPath, pyi_buf, PYI_BUF_SIZE)
|
||||
@@ -453,6 +979,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
stdlib.free(pyi_buf)
|
||||
|
||||
# 翻译 AST → LLVM IR
|
||||
stdio.printf("[P1B-3] translate %s\n", entry.RelPath)
|
||||
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
|
||||
if tr is None:
|
||||
stdlib.free(src_buf)
|
||||
@@ -461,11 +988,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
tr.ModuleSha1 = sha1
|
||||
tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry.RelPath)
|
||||
HandlesType.set_current_file(file_path)
|
||||
HandlesType.set_current_module_sha1(sha1)
|
||||
HandlesType.clear_cdefine_constants()
|
||||
HandlesStruct.reset_visible_structs(mb, 0)
|
||||
ret: int = tr.translate(tree)
|
||||
stdio.printf("[P1B-3a] translate-done %s ret=%d\n", entry.RelPath, ret)
|
||||
if ret != 0:
|
||||
stdio.printf("[Phase1] 翻译失败: %s\n", file_path)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "翻译失败: %s", file_path)
|
||||
VLogger.error(fb, "Phase1")
|
||||
stdlib.free(src_buf)
|
||||
if tr._global_names is not None:
|
||||
stdlib.free(tr._global_names)
|
||||
@@ -475,7 +1007,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
continue
|
||||
|
||||
# dump stub IR (declarations only)
|
||||
PHASE1_IR_SIZE: t.CSizeT = 262144
|
||||
stdio.printf("[P1B-4] stub %s\n", entry.RelPath)
|
||||
PHASE1_IR_SIZE: t.CSizeT = 1048576
|
||||
stub_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE)
|
||||
if stub_buf is None:
|
||||
stdlib.free(src_buf)
|
||||
@@ -500,6 +1033,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
stdlib.free(stub_buf)
|
||||
|
||||
# dump text IR (definitions only)
|
||||
stdio.printf("[P1B-5] text %s\n", entry.RelPath)
|
||||
text_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE)
|
||||
if text_buf is None:
|
||||
stdlib.free(src_buf)
|
||||
@@ -544,7 +1078,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
|
||||
translated += 1
|
||||
|
||||
stdio.printf("[Phase1] 完成: 翻译=%d 跳过=%d 失败=%d\n", translated, skipped, failed)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "完成: 翻译=%d 跳过=%d 失败=%d", translated, skipped, failed)
|
||||
VLogger.info(fb, "Phase1")
|
||||
|
||||
# 释放可达 SHA1 集合(如果分配了)
|
||||
if reachable_set is not None:
|
||||
|
||||
@@ -17,6 +17,7 @@ import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HandlesTranslator
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.Handles.HandlesImports as HandlesImports
|
||||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
import lib.core.BuildPipeline as BuildPipeline
|
||||
import lib.core.StubMerger as StubMerger
|
||||
import lib.Projectrans.Utils as Utils
|
||||
@@ -138,7 +139,10 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy | t.CPtr,
|
||||
ent.Path = full_path
|
||||
ent.Sha1 = sha1_val
|
||||
file_count += 1
|
||||
stdio.printf("[project] %s (sha1=%s)\n", fname, sha1_val)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "%s (sha1=%s)", fname, sha1_val)
|
||||
VLogger.info(fb, "project")
|
||||
stdlib.free(sbuf)
|
||||
# full_path 不释放:ent.Path 引用它
|
||||
|
||||
@@ -165,10 +169,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
Returns: 0 成功,非 0 失败
|
||||
"""
|
||||
if source_dir is None:
|
||||
stdio.printf("[project] source_dir 为空\n")
|
||||
VLogger.error("source_dir 为空", "project")
|
||||
return 1
|
||||
|
||||
stdio.printf("[project] 多文件项目编译: %s\n", source_dir)
|
||||
stdio.printf("[DBG] RunMultiFileProject enter src=%s\n", source_dir)
|
||||
stdio.fflush(0)
|
||||
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "多文件项目编译: %s", source_dir)
|
||||
VLogger.info(fb, "project")
|
||||
|
||||
# === 1. 递归扫描 source_dir 下的 .py 文件(包括子目录)===
|
||||
entry_size: t.CSizeT = SrcFileEntry.__sizeof__()
|
||||
@@ -178,7 +188,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
string.memset(entries, 0, MAX_SRC_FILES * entry_size)
|
||||
|
||||
file_count: int = _ScanDirForPyFiles(mb, source_dir, entries, entry_size, 0, MAX_SRC_FILES)
|
||||
stdio.printf("[project] 共 %d 个源文件\n", file_count)
|
||||
stdio.printf("[DBG] scan done file_count=%d\n", file_count)
|
||||
stdio.fflush(0)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "共 %d 个源文件", file_count)
|
||||
VLogger.info(fb, "project")
|
||||
|
||||
if file_count == 0:
|
||||
stdlib.free(entries)
|
||||
@@ -191,34 +206,47 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
BuildPipeline.ensure_dir(temp_dir)
|
||||
BuildPipeline.ensure_dir(output_dir)
|
||||
|
||||
# 追加 App 源文件 SHA1 到 _sha1_map.txt(供日志转存和符号查找使用)
|
||||
# 格式: {sha1}:App/{filename}\n (与 includes 条目格式对应)
|
||||
# 追加 App 源文件 SHA1 到 _sha1_map.txt(人类可读输出,程序内部不读取此文件)
|
||||
# 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py)
|
||||
# 同步追加到内存存储器(AppendToSha1MapStore,供跨模块 CDefine 查找)
|
||||
td_len_am: t.CSizeT = string.strlen(temp_dir)
|
||||
src_dir_len_am: t.CSizeT = string.strlen(source_dir)
|
||||
map_path_am: bytes = stdlib.malloc(td_len_am + 32)
|
||||
if map_path_am is not None:
|
||||
viperlib.snprintf(map_path_am, td_len_am + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
mf: fileio.File | t.CPtr = fileio.File(map_path_am, fileio.MODE.A)
|
||||
line_am: bytes = stdlib.malloc(512)
|
||||
# 先遍历一次填充内存存储器(不依赖文件 I/O),再写入 _sha1_map.txt
|
||||
app_filled: int = 0
|
||||
for i in range(file_count):
|
||||
ea_am: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||||
ent_am: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_am, t.CPtr))
|
||||
if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None:
|
||||
continue
|
||||
# 计算相对路径(去除 source_dir 前缀,保留目录结构)
|
||||
p_str: str = ent_am.Path
|
||||
p_len: t.CSizeT = string.strlen(p_str)
|
||||
rel_path_am: str = p_str
|
||||
if p_len > src_dir_len_am + 1:
|
||||
if string.strncmp(p_str, source_dir, src_dir_len_am) == 0:
|
||||
rel_path_am = p_str + src_dir_len_am + 1
|
||||
# 同步追加到内存存储器(供跨模块 CDefine 查找)
|
||||
# rel_path_am 格式如 "lib/core/StubMerger.py",与 _sha1_map.txt 一致
|
||||
if StubMerger.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) == 0:
|
||||
app_filled += 1
|
||||
# 写入 _sha1_map.txt(人类可读输出)
|
||||
if not mf.closed and line_am is not None:
|
||||
viperlib.snprintf(line_am, 512, "%s:%s\n", ent_am.Sha1, rel_path_am)
|
||||
ll_am: t.CSizeT = string.strlen(line_am)
|
||||
mf.write(line_am, ll_am)
|
||||
if line_am is not None:
|
||||
stdlib.free(line_am)
|
||||
if not mf.closed:
|
||||
line_am: bytes = stdlib.malloc(512)
|
||||
if line_am is not None:
|
||||
for i in range(file_count):
|
||||
ea_am: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||||
ent_am: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_am, t.CPtr))
|
||||
if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None:
|
||||
continue
|
||||
# 从路径提取文件名(最后一个 / 或 \\ 之后的部分)
|
||||
p_str: str = ent_am.Path
|
||||
p_len: t.CSizeT = string.strlen(p_str)
|
||||
f_start: t.CSizeT = 0
|
||||
for j in range(p_len):
|
||||
if p_str[j] == '/' or p_str[j] == '\\':
|
||||
f_start = j + 1
|
||||
viperlib.snprintf(line_am, 512, "%s:App/%s\n", ent_am.Sha1, p_str + f_start)
|
||||
ll_am: t.CSizeT = string.strlen(line_am)
|
||||
mf.write(line_am, ll_am)
|
||||
stdlib.free(line_am)
|
||||
mf.close()
|
||||
stdio.printf("[project] 已追加 %d 个 App 文件到 _sha1_map.txt\n", file_count)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "已追加 %d 个 App 文件到 _sha1_map.txt 和内存存储器", app_filled)
|
||||
VLogger.info(fb, "project")
|
||||
stdlib.free(map_path_am)
|
||||
|
||||
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用)
|
||||
@@ -231,6 +259,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
# 追加用户源文件的 (模块名, SHA1) 到全局映射
|
||||
# 使跨模块方法调用能通过 from_imports + _lookup_module_sha1 找到正确的 SHA1
|
||||
if pb_sha1_arr is not None and pb_mod_arr is not None:
|
||||
src_dir_len_us: t.CSizeT = string.strlen(source_dir)
|
||||
for i in range(file_count):
|
||||
ea_us: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||||
ent_us: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_us, t.CPtr))
|
||||
@@ -238,24 +267,26 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
continue
|
||||
if pb_inc_count >= StubMerger.MAX_INCLUDES:
|
||||
break
|
||||
# 从路径提取模块名(文件名去掉 .py 后缀)
|
||||
# 计算相对路径并转换为点分模块名(保留完整包路径)
|
||||
path_str: str = ent_us.Path
|
||||
path_len: t.CSizeT = string.strlen(path_str)
|
||||
fname_start: t.CSizeT = 0
|
||||
for j in range(path_len):
|
||||
if path_str[j] == '/' or path_str[j] == '\\':
|
||||
fname_start = j + 1
|
||||
mod_len: t.CSizeT = path_len - fname_start
|
||||
if mod_len < 4:
|
||||
path_len_us: t.CSizeT = string.strlen(path_str)
|
||||
rel_path_us: str = path_str
|
||||
if path_len_us > src_dir_len_us + 1:
|
||||
if string.strncmp(path_str, source_dir, src_dir_len_us) == 0:
|
||||
rel_path_us = path_str + src_dir_len_us + 1
|
||||
# 使用 _PathToModuleName 转换为点分模块名(处理 __init__.py → 包名)
|
||||
mod_name_us: str = StubMerger._PathToModuleName(rel_path_us)
|
||||
if mod_name_us is None:
|
||||
continue
|
||||
mod_len -= 3
|
||||
if mod_len >= 64:
|
||||
mod_len = 63
|
||||
# 写入模块名
|
||||
mod_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 64
|
||||
for k in range(mod_len):
|
||||
pb_mod_arr[mod_idx + k] = path_str[fname_start + k]
|
||||
pb_mod_arr[mod_idx + mod_len] = '\0'
|
||||
mn_len_us: t.CSizeT = string.strlen(mod_name_us)
|
||||
if mn_len_us >= 64:
|
||||
string.strncpy(pb_mod_arr + mod_idx, mod_name_us, 63)
|
||||
pb_mod_arr[mod_idx + 63] = '\0'
|
||||
else:
|
||||
string.strcpy(pb_mod_arr + mod_idx, mod_name_us)
|
||||
stdlib.free(mod_name_us)
|
||||
# 写入 SHA1
|
||||
sha1_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 17
|
||||
string.strcpy(pb_sha1_arr + sha1_idx, ent_us.Sha1)
|
||||
@@ -265,36 +296,60 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
# === 2. Phase A: 为每个文件生成 stub + text ===
|
||||
if do_phase1 != 0:
|
||||
stdio.printf("[DBG] before Phase A\n")
|
||||
stdio.fflush(0)
|
||||
if log is not None:
|
||||
log.banner("Phase A: 生成 stub + text")
|
||||
|
||||
# === Phase A-pre: 预注册所有源文件的 struct/enum/union ===
|
||||
# 解决循环引用问题:circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义
|
||||
# 仅注册 struct/enum/union(declare_only=1),不翻译方法体
|
||||
stdio.printf("[Phase A-pre] 预注册 struct/enum/union...\n")
|
||||
# 多遍扫描:解决跨文件继承的字母序问题(如 HandlesAnnAssign.py 在 HandlesBase.py 之前,
|
||||
# AnnAssignHandle 继承 Mixin 时 Mixin 未注册)。每遍注册新类后,下一遍子类可继承。
|
||||
VLogger.info("预注册 struct/enum/union...", "PhaseA")
|
||||
src_dir_len_pre: t.CSizeT = string.strlen(source_dir)
|
||||
for i in range(file_count):
|
||||
ea_pre: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||||
ent_pre: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_pre, t.CPtr))
|
||||
if ent_pre is None or ent_pre.Path is None:
|
||||
continue
|
||||
pkg_pre: str = None
|
||||
if string.strlen(ent_pre.Path) > src_dir_len_pre + 1:
|
||||
rel_path_pre: str = ent_pre.Path + src_dir_len_pre + 1
|
||||
pkg_pre = HandlesImports.compute_package_from_relpath(mb, rel_path_pre)
|
||||
tr_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1)
|
||||
if tr_pre is None:
|
||||
stdio.printf("[Phase A-pre] 警告: 预注册失败: %s\n", ent_pre.Path)
|
||||
stdio.printf("[Phase A-pre] 预注册完成\n")
|
||||
PHASE_A_PRE_MAX_PASSES: t.CInt = 3
|
||||
for pass_i in range(PHASE_A_PRE_MAX_PASSES):
|
||||
struct_count_before: int = HandlesStruct.get_struct_count()
|
||||
stdio.printf("[DBG] PhaseA-pre pass=%d struct_count_before=%d\n", pass_i, struct_count_before)
|
||||
stdio.fflush(0)
|
||||
for i in range(file_count):
|
||||
ea_pre: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||||
ent_pre: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_pre, t.CPtr))
|
||||
if ent_pre is None or ent_pre.Path is None:
|
||||
continue
|
||||
pkg_pre: str = None
|
||||
if string.strlen(ent_pre.Path) > src_dir_len_pre + 1:
|
||||
rel_path_pre: str = ent_pre.Path + src_dir_len_pre + 1
|
||||
pkg_pre = HandlesImports.compute_package_from_relpath(mb, rel_path_pre)
|
||||
stdio.printf("[DBG] PhaseA-pre pass=%d file=%s\n", pass_i, ent_pre.Path)
|
||||
stdio.fflush(0)
|
||||
tr_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1)
|
||||
if tr_pre is None:
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "预注册失败: %s", ent_pre.Path)
|
||||
VLogger.warning(fb, "PhaseA")
|
||||
struct_count_after: int = HandlesStruct.get_struct_count()
|
||||
stdio.printf("[DBG] PhaseA-pre pass=%d done struct_count_after=%d\n", pass_i, struct_count_after)
|
||||
stdio.fflush(0)
|
||||
# 收敛检查:本遍没有新结构体注册 → 所有类已注册
|
||||
if struct_count_after == struct_count_before:
|
||||
break
|
||||
VLogger.info("预注册完成", "PhaseA")
|
||||
|
||||
PHASE_A_IR_SIZE: t.CSizeT = 262144
|
||||
PHASE_A_IR_SIZE: t.CSizeT = 1048576
|
||||
td_len_pa: t.CSizeT = string.strlen(temp_dir)
|
||||
|
||||
stdio.printf("[DBG] PhaseA full-translate start\n")
|
||||
stdio.fflush(0)
|
||||
for i in range(file_count):
|
||||
ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||||
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
|
||||
if ent is None or ent.Path is None:
|
||||
continue
|
||||
stdio.printf("[DBG] PhaseA file=%s\n", ent.Path)
|
||||
stdio.fflush(0)
|
||||
|
||||
# 计算源文件的包名(相对 source_dir 的目录部分)
|
||||
src_dir_len_pa: t.CSizeT = string.strlen(source_dir)
|
||||
@@ -352,12 +407,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
stdlib.free(deps_path_a)
|
||||
|
||||
if do_phase2 == 0:
|
||||
stdio.printf("[project] Phase A 完成(仅 stub 生成)\n")
|
||||
VLogger.info("Phase A 完成(仅 stub 生成)", "project")
|
||||
stdlib.free(entries)
|
||||
return 0
|
||||
|
||||
# === 3. Phase B: 编译每个文件为 .obj ===
|
||||
if do_phase2 != 0:
|
||||
stdio.printf("[DBG] before Phase B\n")
|
||||
stdio.fflush(0)
|
||||
if log is not None:
|
||||
log.banner("Phase B: 编译 .obj")
|
||||
|
||||
@@ -384,28 +441,51 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
|
||||
if ent is None or ent.Path is None or ent.Sha1 is None:
|
||||
continue
|
||||
stdio.printf("[DBG] PhaseB file=%s\n", ent.Path)
|
||||
stdio.fflush(0)
|
||||
|
||||
# 组合本地 stub + 所有依赖 stub + 本地 text → 完整 IR
|
||||
stdio.printf("[DBG] PhaseB step=alloc combined_ir\n")
|
||||
stdio.fflush(0)
|
||||
combined_ir: bytes = stdlib.malloc(COMBINED_IR_SIZE)
|
||||
if combined_ir is None:
|
||||
stdio.printf("[Phase B] combined_ir 分配失败: %s\n", ent.Path)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "combined_ir 分配失败: %s", ent.Path)
|
||||
VLogger.error(fb, "PhaseB")
|
||||
continue
|
||||
stdio.printf("[DBG] PhaseB step=BuildCombinedIR sha1=%s\n", ent.Sha1)
|
||||
stdio.fflush(0)
|
||||
combined_len: t.CSizeT = StubMerger.BuildCombinedIR(temp_dir, ent.Sha1, combined_ir, COMBINED_IR_SIZE)
|
||||
stdio.printf("[DBG] PhaseB step=BuildCombinedIR done len=%d\n", combined_len)
|
||||
stdio.fflush(0)
|
||||
if combined_len == 0:
|
||||
stdio.printf("[Phase B] BuildCombinedIR 失败: %s\n", ent.Path)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", ent.Path)
|
||||
VLogger.error(fb, "PhaseB")
|
||||
stdlib.free(combined_ir)
|
||||
continue
|
||||
|
||||
# 编译为 .obj
|
||||
stdio.printf("[DBG] PhaseB step=compile_module_to_obj\n")
|
||||
stdio.fflush(0)
|
||||
cret: int = BuildPipeline.compile_module_to_obj(
|
||||
combined_ir, combined_len, temp_dir, output_dir, ent.Sha1,
|
||||
cc_cmd, cc_flags)
|
||||
stdio.printf("[DBG] PhaseB step=compile_module_to_obj done cret=%d\n", cret)
|
||||
stdio.fflush(0)
|
||||
stdlib.free(combined_ir)
|
||||
if cret != 0:
|
||||
stdio.printf("[FATAL][Phase B] llc 编译失败,终止编译: %s\n", ent.Path)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "llc 编译失败,终止编译: %s", ent.Path)
|
||||
VLogger.error(fb, "PhaseB")
|
||||
sys.exit(1)
|
||||
|
||||
compiled_count += 1
|
||||
stdio.printf("[DBG] PhaseB step=obj_path construct\n")
|
||||
stdio.fflush(0)
|
||||
|
||||
# 构造 .obj 路径,检测是否是 main 模块(test_main.py 或 main.py)
|
||||
od_len: t.CSizeT = string.strlen(output_dir)
|
||||
@@ -424,7 +504,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
if op_sliced_len < 512:
|
||||
string.strcpy(main_obj_path, obj_path_sliced)
|
||||
else:
|
||||
stdio.printf("[Phase B] 警告: main_obj_path 缓冲区不足\n")
|
||||
VLogger.warning("main_obj_path 缓冲区不足", "PhaseB")
|
||||
else:
|
||||
# 其他模块: 追加到 obj_paths
|
||||
if obj_pos + op_sliced_len + 2 < OBJ_PATHS_SIZE:
|
||||
@@ -435,13 +515,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
obj_pos += op_sliced_len
|
||||
obj_paths[obj_pos] = '\0'
|
||||
else:
|
||||
stdio.printf("[Phase B] 警告: .obj 路径缓冲区不足\n")
|
||||
VLogger.warning(".obj 路径缓冲区不足", "PhaseB")
|
||||
stdlib.free(obj_path_sliced)
|
||||
stdio.printf("[DBG] PhaseB step=obj_path done\n")
|
||||
stdio.fflush(0)
|
||||
|
||||
stdio.printf("[Phase B] 编译完成: %d/%d\n", compiled_count, file_count)
|
||||
stdio.printf("[DBG] PhaseB loop done compiled_count=%d\n", compiled_count)
|
||||
stdio.fflush(0)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "编译完成: %d/%d", compiled_count, file_count)
|
||||
VLogger.success(fb, "PhaseB")
|
||||
|
||||
if compiled_count == 0:
|
||||
stdio.printf("[Phase B] 无成功编译的文件\n")
|
||||
VLogger.error("无成功编译的文件", "PhaseB")
|
||||
stdlib.free(entries)
|
||||
return 1
|
||||
|
||||
@@ -449,198 +536,191 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
# includes.binary 可能缺少某些 includes .obj(如 testcheck.py),
|
||||
# 这些文件被用户项目导入但未被 TransPyV 自身依赖,Projectrans.py 未编译它们。
|
||||
# 检测并编译缺失的 includes 文件到 output_dir,加入链接命令。
|
||||
# 数据来源:StubMerger 全局内存存储器(不读取 _sha1_map.txt 文件)
|
||||
if includes_dir is not None and includes_binary_dir is not None:
|
||||
stdio.printf("[DBG] PhaseB+ start\n")
|
||||
stdio.fflush(0)
|
||||
inc_compiled: int = 0
|
||||
td_len_mi: t.CSizeT = string.strlen(temp_dir)
|
||||
map_path_mi: bytes = stdlib.malloc(td_len_mi + 32)
|
||||
if map_path_mi is not None:
|
||||
viperlib.snprintf(map_path_mi, td_len_mi + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
mapf_mi: fileio.File | t.CPtr = fileio.File(map_path_mi, fileio.MODE.R)
|
||||
if not mapf_mi.closed:
|
||||
map_buf_mi: bytes = stdlib.malloc(65536)
|
||||
if map_buf_mi is not None:
|
||||
map_br_mi: t.CInt64T = mapf_mi.read_all(map_buf_mi, 65536)
|
||||
mapf_mi.close()
|
||||
if map_br_mi > 0:
|
||||
if map_br_mi < 65536:
|
||||
map_buf_mi[map_br_mi] = '\0'
|
||||
else:
|
||||
map_buf_mi[65535] = '\0'
|
||||
map_len_mi: t.CSizeT = map_br_mi
|
||||
mpos_mi: t.CSizeT = 0
|
||||
while mpos_mi < map_len_mi:
|
||||
ml_start_mi: t.CSizeT = mpos_mi
|
||||
while mpos_mi < map_len_mi:
|
||||
if map_buf_mi[mpos_mi] == '\n':
|
||||
break
|
||||
mpos_mi += 1
|
||||
ml_len_mi: t.CSizeT = mpos_mi - ml_start_mi
|
||||
mpos_mi += 1
|
||||
# 从全局存储器获取 SHA1/模块名/rel_path 数组(直接访问器,避免 box 解引用问题)
|
||||
store_count_mi: int = StubMerger.GetSha1StoreCount()
|
||||
stdio.printf("[DBG] PhaseB+ store_count=%d\n", store_count_mi)
|
||||
stdio.fflush(0)
|
||||
if store_count_mi > 0:
|
||||
store_sha1_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
|
||||
store_mod_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreModArrPtr()
|
||||
store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
|
||||
stdio.printf("[DBG] PhaseB+ arrs ok sha1=%p mod=%p rel=%p\n", store_sha1_arr_mi, store_mod_arr_mi, store_rel_arr_mi)
|
||||
stdio.fflush(0)
|
||||
for si_mi in range(store_count_mi):
|
||||
# 获取当前条目的 SHA1 和 rel_path
|
||||
inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(si_mi) * 17
|
||||
inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_REL_PATH_LEN
|
||||
stdio.printf("[DBG] PhaseB+ iter=%d sha1=%s rel=%s\n", si_mi, inc_sha1_mi, inc_rel_mi)
|
||||
stdio.fflush(0)
|
||||
|
||||
# 最小长度: 16(sha1)+1(:)+9(includes/)+1+1+1 = 29
|
||||
if ml_len_mi < 26:
|
||||
continue
|
||||
if map_buf_mi[ml_start_mi + 16] != ':':
|
||||
continue
|
||||
rp_start_mi: t.CSizeT = ml_start_mi + 17
|
||||
if string.strncmp(map_buf_mi + rp_start_mi, "includes/", 9) != 0:
|
||||
continue
|
||||
# 检查 .obj 是否已存在于 includes.binary
|
||||
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
|
||||
check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35)
|
||||
if check_pat_mi is None:
|
||||
continue
|
||||
viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi)
|
||||
check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
obj_exists_mi: int = 0
|
||||
if check_fd_mi is not None:
|
||||
string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
|
||||
if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE:
|
||||
w32.win32file.FindClose(check_h_mi)
|
||||
obj_exists_mi = 1
|
||||
if obj_exists_mi != 0:
|
||||
continue
|
||||
|
||||
# 提取 SHA1
|
||||
inc_sha1_mi: str = stdlib.malloc(17)
|
||||
if inc_sha1_mi is None:
|
||||
continue
|
||||
string.strncpy(inc_sha1_mi, map_buf_mi + ml_start_mi, 16)
|
||||
inc_sha1_mi[16] = '\0'
|
||||
# .obj 不存在,需要编译
|
||||
# 构造源文件路径: {includes_dir}/{rel_path}
|
||||
rel_path_len_mi: t.CSizeT = string.strlen(inc_rel_mi)
|
||||
inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir)
|
||||
src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1)
|
||||
if src_fp_mi is None:
|
||||
continue
|
||||
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1,
|
||||
"%s/%s", includes_dir, inc_rel_mi)
|
||||
|
||||
# 检查 .obj 是否已存在于 includes.binary
|
||||
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
|
||||
check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35)
|
||||
if check_pat_mi is None:
|
||||
continue
|
||||
viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi)
|
||||
check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
obj_exists_mi: int = 0
|
||||
if check_fd_mi is not None:
|
||||
string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
|
||||
if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE:
|
||||
w32.win32file.FindClose(check_h_mi)
|
||||
obj_exists_mi = 1
|
||||
if obj_exists_mi != 0:
|
||||
continue
|
||||
# 检查是否为声明文件(只有 declare 没有实质 define)
|
||||
# 判断方法: text.ll 中若有混淆函数 define(含 @")则为实现文件
|
||||
is_decl_mi: int = -1
|
||||
tpath_mi: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
|
||||
if tpath_mi is not None:
|
||||
tf_mi: fileio.File | t.CPtr = fileio.File(tpath_mi, fileio.MODE.R)
|
||||
if not tf_mi.closed:
|
||||
is_decl_mi = 1
|
||||
tbuf_mi: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
||||
if tbuf_mi is not None:
|
||||
tbr_mi: t.CInt64T = tf_mi.read_all(tbuf_mi, StubMerger.STUB_READ_BUF_SIZE)
|
||||
if tbr_mi > 0:
|
||||
if tbr_mi < StubMerger.STUB_READ_BUF_SIZE:
|
||||
tbuf_mi[tbr_mi] = '\0'
|
||||
else:
|
||||
tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||||
# 逐行扫描: 找 define 行中含 @" 的(混淆函数名)
|
||||
tpos_mi: t.CSizeT = 0
|
||||
while tpos_mi < tbr_mi:
|
||||
tls_mi: t.CSizeT = tpos_mi
|
||||
while tpos_mi < tbr_mi:
|
||||
if tbuf_mi[tpos_mi] == '\n':
|
||||
break
|
||||
tpos_mi += 1
|
||||
tll_mi: t.CSizeT = tpos_mi - tls_mi
|
||||
if tpos_mi < tbr_mi:
|
||||
tpos_mi += 1
|
||||
if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0:
|
||||
# 临时在行尾加 \0 供 strstr 使用
|
||||
saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi]
|
||||
tbuf_mi[tls_mi + tll_mi] = '\0'
|
||||
if string.strstr(tbuf_mi + tls_mi, "@\"") is not None:
|
||||
tbuf_mi[tls_mi + tll_mi] = saved_mi
|
||||
is_decl_mi = 0
|
||||
break
|
||||
tbuf_mi[tls_mi + tll_mi] = saved_mi
|
||||
stdlib.free(tbuf_mi)
|
||||
tf_mi.close()
|
||||
stdlib.free(tpath_mi)
|
||||
# is_decl_mi == -1: text.ll 不存在(Phase1 未翻译此文件,可能不被项目导入)
|
||||
# is_decl_mi == 1: 声明文件(仅 declare 无 define)
|
||||
# 两种情况都跳过:不编译未被 Phase1 翻译的 includes 文件
|
||||
if is_decl_mi != 0:
|
||||
continue
|
||||
|
||||
# .obj 不存在,需要编译
|
||||
# 构造源文件路径: {includes_dir}/{rel_path_without_includes_prefix}
|
||||
rel_path_len_mi: t.CSizeT = ml_len_mi - 26
|
||||
inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir)
|
||||
src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1)
|
||||
if src_fp_mi is None:
|
||||
continue
|
||||
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1,
|
||||
"%s/%s", includes_dir, map_buf_mi + rp_start_mi + 9)
|
||||
# 尝试 BuildCombinedIR(stub/text 应已由 Phase1 生成)
|
||||
inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE)
|
||||
inc_combined_len_mi: t.CSizeT = 0
|
||||
if inc_combined_mi is not None:
|
||||
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
|
||||
|
||||
# 检查是否为声明文件(只有 declare 没有实质 define)
|
||||
# 判断方法: text.ll 中若有混淆函数 define(含 @\")则为实现文件
|
||||
is_decl_mi: int = -1
|
||||
tpath_mi: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
|
||||
if tpath_mi is not None:
|
||||
tf_mi: fileio.File | t.CPtr = fileio.File(tpath_mi, fileio.MODE.R)
|
||||
if not tf_mi.closed:
|
||||
is_decl_mi = 1
|
||||
tbuf_mi: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
||||
if tbuf_mi is not None:
|
||||
tbr_mi: t.CInt64T = tf_mi.read_all(tbuf_mi, StubMerger.STUB_READ_BUF_SIZE)
|
||||
if tbr_mi > 0:
|
||||
if tbr_mi < StubMerger.STUB_READ_BUF_SIZE:
|
||||
tbuf_mi[tbr_mi] = '\0'
|
||||
else:
|
||||
tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||||
# 逐行扫描: 找 define 行中含 @\" 的(混淆函数名)
|
||||
tpos_mi: t.CSizeT = 0
|
||||
while tpos_mi < tbr_mi:
|
||||
tls_mi: t.CSizeT = tpos_mi
|
||||
while tpos_mi < tbr_mi:
|
||||
if tbuf_mi[tpos_mi] == '\n':
|
||||
break
|
||||
tpos_mi += 1
|
||||
tll_mi: t.CSizeT = tpos_mi - tls_mi
|
||||
if tpos_mi < tbr_mi:
|
||||
tpos_mi += 1
|
||||
if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0:
|
||||
# 临时在行尾加 \0 供 strstr 使用
|
||||
saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi]
|
||||
tbuf_mi[tls_mi + tll_mi] = '\0'
|
||||
if string.strstr(tbuf_mi + tls_mi, "@\"") is not None:
|
||||
tbuf_mi[tls_mi + tll_mi] = saved_mi
|
||||
is_decl_mi = 0
|
||||
break
|
||||
tbuf_mi[tls_mi + tll_mi] = saved_mi
|
||||
stdlib.free(tbuf_mi)
|
||||
tf_mi.close()
|
||||
stdlib.free(tpath_mi)
|
||||
if is_decl_mi == 1:
|
||||
continue
|
||||
# 如果 stub/text 不存在,翻译源文件并保存 stub + text,然后重试
|
||||
if inc_combined_len_mi == 0 and inc_combined_mi is not None:
|
||||
# 计算 includes 文件的包名(相对 includes_dir 的目录部分)
|
||||
inc_pkg_mi: str = None
|
||||
if string.strlen(src_fp_mi) > inc_dir_len_mi + 1:
|
||||
inc_rel_mi2: str = src_fp_mi + inc_dir_len_mi + 1
|
||||
inc_pkg_mi = HandlesImports.compute_package_from_relpath(mb, inc_rel_mi2)
|
||||
tr_mi: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, src_fp_mi, inc_sha1_mi, inc_pkg_mi)
|
||||
if tr_mi is not None:
|
||||
PBP_IR_SIZE: t.CSizeT = 1048576
|
||||
# 保存 stub.ll (切片路径)
|
||||
inc_stub_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
|
||||
if inc_stub_buf is not None:
|
||||
tr_mi.dump_ir(inc_stub_buf, PBP_IR_SIZE, llvmlite.OUTPUT_STUB)
|
||||
inc_stub_len: t.CSizeT = string.strlen(inc_stub_buf)
|
||||
inc_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll")
|
||||
if inc_stub_path is not None:
|
||||
isf: fileio.File | t.CPtr = fileio.File(inc_stub_path, fileio.MODE.W)
|
||||
if not isf.closed:
|
||||
isf.write(inc_stub_buf, inc_stub_len)
|
||||
isf.close()
|
||||
stdlib.free(inc_stub_path)
|
||||
stdlib.free(inc_stub_buf)
|
||||
# 保存 text.ll (切片路径)
|
||||
inc_text_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
|
||||
if inc_text_buf is not None:
|
||||
tr_mi.dump_ir(inc_text_buf, PBP_IR_SIZE, llvmlite.OUTPUT_TEXT)
|
||||
inc_text_len: t.CSizeT = string.strlen(inc_text_buf)
|
||||
inc_text_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
|
||||
if inc_text_path is not None:
|
||||
itf: fileio.File | t.CPtr = fileio.File(inc_text_path, fileio.MODE.W)
|
||||
if not itf.closed:
|
||||
itf.write(inc_text_buf, inc_text_len)
|
||||
itf.close()
|
||||
stdlib.free(inc_text_path)
|
||||
stdlib.free(inc_text_buf)
|
||||
# 重试 BuildCombinedIR
|
||||
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
|
||||
|
||||
# 尝试 BuildCombinedIR(stub/text 应已由 Phase1 生成)
|
||||
inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE)
|
||||
inc_combined_len_mi: t.CSizeT = 0
|
||||
if inc_combined_mi is not None:
|
||||
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
|
||||
if inc_combined_len_mi == 0:
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", src_fp_mi)
|
||||
VLogger.error(fb, "PhaseB+")
|
||||
if inc_combined_mi is not None:
|
||||
stdlib.free(inc_combined_mi)
|
||||
continue
|
||||
|
||||
# 如果 stub/text 不存在,翻译源文件并保存 stub + text,然后重试
|
||||
if inc_combined_len_mi == 0 and inc_combined_mi is not None:
|
||||
# 计算 includes 文件的包名(相对 includes_dir 的目录部分)
|
||||
inc_pkg_mi: str = None
|
||||
if string.strlen(src_fp_mi) > inc_dir_len_mi + 1:
|
||||
inc_rel_mi: str = src_fp_mi + inc_dir_len_mi + 1
|
||||
inc_pkg_mi = HandlesImports.compute_package_from_relpath(mb, inc_rel_mi)
|
||||
tr_mi: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, src_fp_mi, inc_sha1_mi, inc_pkg_mi)
|
||||
if tr_mi is not None:
|
||||
PBP_IR_SIZE: t.CSizeT = 262144
|
||||
# 保存 stub.ll (切片路径)
|
||||
inc_stub_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
|
||||
if inc_stub_buf is not None:
|
||||
tr_mi.dump_ir(inc_stub_buf, PBP_IR_SIZE, llvmlite.OUTPUT_STUB)
|
||||
inc_stub_len: t.CSizeT = string.strlen(inc_stub_buf)
|
||||
inc_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll")
|
||||
if inc_stub_path is not None:
|
||||
isf: fileio.File | t.CPtr = fileio.File(inc_stub_path, fileio.MODE.W)
|
||||
if not isf.closed:
|
||||
isf.write(inc_stub_buf, inc_stub_len)
|
||||
isf.close()
|
||||
stdlib.free(inc_stub_path)
|
||||
stdlib.free(inc_stub_buf)
|
||||
# 保存 text.ll (切片路径)
|
||||
inc_text_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
|
||||
if inc_text_buf is not None:
|
||||
tr_mi.dump_ir(inc_text_buf, PBP_IR_SIZE, llvmlite.OUTPUT_TEXT)
|
||||
inc_text_len: t.CSizeT = string.strlen(inc_text_buf)
|
||||
inc_text_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
|
||||
if inc_text_path is not None:
|
||||
itf: fileio.File | t.CPtr = fileio.File(inc_text_path, fileio.MODE.W)
|
||||
if not itf.closed:
|
||||
itf.write(inc_text_buf, inc_text_len)
|
||||
itf.close()
|
||||
stdlib.free(inc_text_path)
|
||||
stdlib.free(inc_text_buf)
|
||||
# 重试 BuildCombinedIR
|
||||
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
|
||||
# 编译为 .obj
|
||||
inc_cret_mi: int = BuildPipeline.compile_module_to_obj(
|
||||
inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi,
|
||||
cc_cmd, cc_flags)
|
||||
stdlib.free(inc_combined_mi)
|
||||
if inc_cret_mi != 0:
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "llc 编译失败,终止编译: %s", src_fp_mi)
|
||||
VLogger.error(fb, "PhaseB+")
|
||||
sys.exit(1)
|
||||
|
||||
if inc_combined_len_mi == 0:
|
||||
stdio.printf("[Phase B+] BuildCombinedIR 失败: %s\n", src_fp_mi)
|
||||
if inc_combined_mi is not None:
|
||||
stdlib.free(inc_combined_mi)
|
||||
continue
|
||||
inc_compiled += 1
|
||||
|
||||
# 编译为 .obj
|
||||
inc_cret_mi: int = BuildPipeline.compile_module_to_obj(
|
||||
inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi,
|
||||
cc_cmd, cc_flags)
|
||||
stdlib.free(inc_combined_mi)
|
||||
if inc_cret_mi != 0:
|
||||
stdio.printf("[FATAL][Phase B+] llc 编译失败,终止编译: %s\n", src_fp_mi)
|
||||
sys.exit(1)
|
||||
# 添加到 obj_paths (切片路径)
|
||||
od_len_mi: t.CSizeT = string.strlen(output_dir)
|
||||
inc_obj_sliced: str = StubMerger._sliced_path(output_dir, od_len_mi, inc_sha1_mi, "obj")
|
||||
if inc_obj_sliced is not None:
|
||||
inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced)
|
||||
if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE:
|
||||
if obj_pos > 0:
|
||||
obj_paths[obj_pos] = ' '
|
||||
obj_pos += 1
|
||||
string.strcpy(obj_paths + obj_pos, inc_obj_sliced)
|
||||
obj_pos += inc_obj_len
|
||||
obj_paths[obj_pos] = '\0'
|
||||
stdlib.free(inc_obj_sliced)
|
||||
|
||||
inc_compiled += 1
|
||||
|
||||
# 添加到 obj_paths (切片路径)
|
||||
od_len_mi: t.CSizeT = string.strlen(output_dir)
|
||||
inc_obj_sliced: str = StubMerger._sliced_path(output_dir, od_len_mi, inc_sha1_mi, "obj")
|
||||
if inc_obj_sliced is not None:
|
||||
inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced)
|
||||
if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE:
|
||||
if obj_pos > 0:
|
||||
obj_paths[obj_pos] = ' '
|
||||
obj_pos += 1
|
||||
string.strcpy(obj_paths + obj_pos, inc_obj_sliced)
|
||||
obj_pos += inc_obj_len
|
||||
obj_paths[obj_pos] = '\0'
|
||||
stdlib.free(inc_obj_sliced)
|
||||
|
||||
stdio.printf("[Phase B+] 编译缺失 includes: %d 个\n", inc_compiled)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "编译缺失 includes: %d 个", inc_compiled)
|
||||
VLogger.info(fb, "PhaseB+")
|
||||
|
||||
# === 4. Phase C: 链接所有 .obj → .exe ===
|
||||
stdio.printf("[DBG] PhaseC start\n")
|
||||
stdio.fflush(0)
|
||||
if log is not None:
|
||||
log.banner("Phase C: 链接")
|
||||
|
||||
@@ -674,20 +754,33 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
final_obj_paths[fop_pos] = '\0'
|
||||
|
||||
obj_paths_len: t.CSizeT = fop_pos
|
||||
stdio.printf("[DBG] PhaseC before link_objs_to_exe len=%d\n", obj_paths_len)
|
||||
stdio.fflush(0)
|
||||
lret: int = BuildPipeline.link_objs_to_exe(
|
||||
final_obj_paths, obj_paths_len,
|
||||
linker_cmd, linker_flags, exe_path,
|
||||
includes_binary_dir)
|
||||
stdio.printf("[DBG] PhaseC link_objs_to_exe done lret=%d\n", lret)
|
||||
stdio.fflush(0)
|
||||
|
||||
if lret == 0:
|
||||
stdio.printf("输出: %s\n", exe_path)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "输出: %s", exe_path)
|
||||
VLogger.success(fb, "PhaseC")
|
||||
if args.get_bool("run"):
|
||||
stdio.printf("[run] 执行: %s\n", exe_path)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "执行: %s", exe_path)
|
||||
VLogger.info(fb, "run")
|
||||
rp: subprocess.CompletedProcess | t.CPtr = subprocess.run(exe_path, False, False)
|
||||
if rp is not None:
|
||||
stdio.printf("[run] 退出码: %d\n", rp.returncode)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "退出码: %d", rp.returncode)
|
||||
VLogger.info(fb, "run")
|
||||
else:
|
||||
stdio.printf("[Phase C] 链接失败\n")
|
||||
VLogger.error("链接失败", "PhaseC")
|
||||
stdlib.free(entries)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import w32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
import lib.core.Handles.HandlesImports as HandlesImports
|
||||
@@ -39,10 +40,20 @@ _mbuddy: memhub.MemManager | t.CPtr
|
||||
# 最大 includes SHA1 数(用于过滤)
|
||||
MAX_INCLUDES_SHA1: t.CDefine = 256
|
||||
# stub 读取缓冲区大小
|
||||
STUB_READ_BUF_SIZE: t.CDefine = 262144
|
||||
STUB_READ_BUF_SIZE: t.CDefine = 1048576
|
||||
# 最大 includes 条目数
|
||||
MAX_INCLUDES: t.CDefine = 256
|
||||
|
||||
# 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
|
||||
# Phase1 扫描完成后填充,供所有函数使用
|
||||
_sha1_store_arr: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_count: int # 条目数
|
||||
|
||||
# rel_path 最大长度
|
||||
MAX_REL_PATH_LEN: t.CDefine = 256
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free)
|
||||
@@ -217,77 +228,31 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _load_includes_sha1_set - 读取 _sha1_map.txt,收集 includes/ 开头的 SHA1
|
||||
# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1
|
||||
#
|
||||
# _sha1_map.txt 格式:{sha16}:{rel_path}
|
||||
# 仅收集 rel_path 以 "includes/" 开头的 SHA1,用于过滤 stub 文件,
|
||||
# 避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。
|
||||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
||||
# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。
|
||||
# ============================================================
|
||||
def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr,
|
||||
temp_dir: str,
|
||||
sha1_set: str) -> int:
|
||||
"""读取 _sha1_map.txt,将 includes/ 开头的 SHA1 写入 sha1_set
|
||||
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
|
||||
|
||||
pool/temp_dir 参数保留以兼容现有调用者,但不再使用。
|
||||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
||||
sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null)
|
||||
返回找到的 includes SHA1 数量,-1 表示错误"""
|
||||
if pool is None or temp_dir is None or sha1_set is None:
|
||||
if sha1_set is None:
|
||||
return -1
|
||||
|
||||
# 构造路径 temp_dir/_sha1_map.txt(使用 stdlib.malloc 避免 mbuddy 池耗尽)
|
||||
dir_len: t.CSizeT = string.strlen(temp_dir)
|
||||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||||
if map_path is None:
|
||||
if _sha1_store_arr is None:
|
||||
return -1
|
||||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
|
||||
# 打开文件
|
||||
f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R)
|
||||
if f.closed:
|
||||
return -1
|
||||
|
||||
# 读取内容(使用 stdlib.malloc 避免 mbuddy 池耗尽)
|
||||
MAP_BUF_SIZE: t.CSizeT = 65536
|
||||
content: str = stdlib.malloc(MAP_BUF_SIZE)
|
||||
if content is None:
|
||||
f.close()
|
||||
return -1
|
||||
bytes_read: t.CInt64T = f.read_all(content, MAP_BUF_SIZE)
|
||||
f.close()
|
||||
if bytes_read <= 0:
|
||||
return -1
|
||||
if bytes_read < MAP_BUF_SIZE:
|
||||
content[bytes_read] = '\0'
|
||||
else:
|
||||
content[MAP_BUF_SIZE - 1] = '\0'
|
||||
|
||||
# 解析行: {sha1}:{rel_path}
|
||||
count: int = 0
|
||||
pos: t.CSizeT = 0
|
||||
content_len: t.CSizeT = bytes_read
|
||||
while pos < content_len:
|
||||
# 找行尾
|
||||
line_start: t.CSizeT = pos
|
||||
while pos < content_len:
|
||||
if content[pos] == '\n':
|
||||
break
|
||||
pos += 1
|
||||
line_len: t.CSizeT = pos - line_start
|
||||
pos += 1 # skip \n
|
||||
|
||||
# 最小长度: 16(sha1) + 1(:) + 9(includes/) = 26
|
||||
if line_len < 26:
|
||||
continue
|
||||
# 检查第17个字符是 ':'
|
||||
if content[line_start + 16] != ':':
|
||||
continue
|
||||
|
||||
# 检查 rel_path 是否以 "includes/" 开头
|
||||
rel_start: t.CSizeT = line_start + 17
|
||||
if string.strncmp(content + rel_start, "includes/", 9) == 0:
|
||||
if count < MAX_INCLUDES_SHA1:
|
||||
string.strncpy(sha1_set + count * 17, content + line_start, 16)
|
||||
sha1_set[count * 17 + 16] = '\0'
|
||||
count += 1
|
||||
|
||||
for i in range(_sha1_store_count):
|
||||
if count >= MAX_INCLUDES_SHA1:
|
||||
break
|
||||
idx: t.CSizeT = t.CSizeT(i) * 17
|
||||
string.strcpy(sha1_set + t.CSizeT(count) * 17, _sha1_store_arr + idx)
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
@@ -305,10 +270,10 @@ def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||
|
||||
|
||||
# ============================================================
|
||||
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt
|
||||
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出)
|
||||
#
|
||||
# TransPyV.exe 自己生成 _sha1_map.txt(不再依赖 Projectrans.py 预生成),
|
||||
# 确保所有 includes 文件的 SHA1 都被记录,供 StubMerger 加载 stub 时过滤。
|
||||
# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。
|
||||
# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器。
|
||||
#
|
||||
# Args:
|
||||
# mb: 内存池
|
||||
@@ -322,7 +287,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
|
||||
scan_result: IncludesScanner.ScanResult | t.CPtr,
|
||||
filter_set: t.CChar | t.CPtr,
|
||||
filter_count: int) -> int:
|
||||
"""将 includes 扫描结果写入 _sha1_map.txt(供 StubMerger 使用)
|
||||
"""将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器)
|
||||
|
||||
若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。
|
||||
"""
|
||||
@@ -367,6 +332,212 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PopulateSha1MapStore - 从扫描结果填充全局 SHA1 映射存储器
|
||||
#
|
||||
# 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
|
||||
# 模块名通过 _PathToModuleName(includes/rel_path) 计算。
|
||||
# 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
|
||||
#
|
||||
# Args:
|
||||
# scan_result: IncludesScanner 扫描结果
|
||||
#
|
||||
# Returns:
|
||||
# 填充的条目数,-1 表示失败
|
||||
# ============================================================
|
||||
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> int:
|
||||
"""从扫描结果填充全局 SHA1 映射存储器
|
||||
|
||||
遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
|
||||
模块名通过 _PathToModuleName(includes/rel_path) 计算。
|
||||
内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
|
||||
"""
|
||||
global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count
|
||||
if scan_result is None:
|
||||
return -1
|
||||
# 释放旧存储器
|
||||
if _sha1_store_arr is not None:
|
||||
stdlib.free(_sha1_store_arr)
|
||||
if _sha1_store_mod is not None:
|
||||
stdlib.free(_sha1_store_mod)
|
||||
if _sha1_store_rel is not None:
|
||||
stdlib.free(_sha1_store_rel)
|
||||
_sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17)
|
||||
_sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64)
|
||||
_sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN)
|
||||
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
|
||||
return -1
|
||||
string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17)
|
||||
string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64)
|
||||
string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN)
|
||||
_sha1_store_count = 0
|
||||
entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
|
||||
for i in range(scan_result.Count):
|
||||
if _sha1_store_count >= MAX_INCLUDES:
|
||||
break
|
||||
ea: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_p
|
||||
ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
|
||||
if ent is None or ent.Sha1 is None or ent.RelPath is None:
|
||||
continue
|
||||
# 构建完整路径 includes/{RelPath}(_PathToModuleName 需要)
|
||||
rp_len: t.CSizeT = string.strlen(ent.RelPath)
|
||||
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
|
||||
if full_path_buf is None:
|
||||
continue
|
||||
viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath)
|
||||
mod_name: str = _PathToModuleName(full_path_buf)
|
||||
stdlib.free(full_path_buf)
|
||||
if mod_name is not None:
|
||||
idx: t.CSizeT = t.CSizeT(_sha1_store_count) * 17
|
||||
string.strcpy(_sha1_store_arr + idx, ent.Sha1)
|
||||
idx2: t.CSizeT = t.CSizeT(_sha1_store_count) * 64
|
||||
mn_len: t.CSizeT = string.strlen(mod_name)
|
||||
if mn_len < 64:
|
||||
string.strcpy(_sha1_store_mod + idx2, mod_name)
|
||||
else:
|
||||
string.strncpy(_sha1_store_mod + idx2, mod_name, 63)
|
||||
_sha1_store_mod[idx2 + 63] = '\0'
|
||||
stdlib.free(mod_name)
|
||||
# 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用)
|
||||
idx3: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN
|
||||
if rp_len < MAX_REL_PATH_LEN:
|
||||
string.strcpy(_sha1_store_rel + idx3, ent.RelPath)
|
||||
else:
|
||||
string.strncpy(_sha1_store_rel + idx3, ent.RelPath, MAX_REL_PATH_LEN - 1)
|
||||
_sha1_store_rel[idx3 + MAX_REL_PATH_LEN - 1] = '\0'
|
||||
_sha1_store_count += 1
|
||||
return _sha1_store_count
|
||||
|
||||
|
||||
# ============================================================
|
||||
# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器
|
||||
#
|
||||
# Phase1 调用 PopulateSha1MapStore 填充 includes 文件,
|
||||
# Phase2 扫描 App 源文件后调用此函数追加 App 模块条目,
|
||||
# 确保编译 App 自身时跨模块 CDefine 查找能命中 App 模块
|
||||
# (如 StubMerger.MAX_INCLUDES 在编译 Phase2 时通过
|
||||
# _build_source_path_from_sha1 查找 StubMerger 的源路径)。
|
||||
#
|
||||
# 若存储器未初始化(PopulateSha1MapStore 未调用),自动初始化。
|
||||
# 若 SHA1 已存在(去重),跳过。
|
||||
#
|
||||
# Args:
|
||||
# sha1: SHA1 字符串(16 字符 + null)
|
||||
# rel_path: 相对路径(如 "lib/core/StubMerger.py",
|
||||
# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
|
||||
#
|
||||
# Returns:
|
||||
# 0 成功(含去重跳过),1 失败
|
||||
# ============================================================
|
||||
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
"""追加条目到全局 SHA1 映射存储器(Phase2 用)"""
|
||||
global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count
|
||||
if sha1 is None or rel_path is None:
|
||||
return 1
|
||||
# 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用)
|
||||
if _sha1_store_arr is None:
|
||||
_sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17)
|
||||
_sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64)
|
||||
_sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN)
|
||||
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
|
||||
return 1
|
||||
string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17)
|
||||
string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64)
|
||||
string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN)
|
||||
_sha1_store_count = 0
|
||||
if _sha1_store_count >= MAX_INCLUDES:
|
||||
return 1
|
||||
# 去重:若 SHA1 已存在则跳过
|
||||
for i in range(_sha1_store_count):
|
||||
sidx_d: t.CSizeT = t.CSizeT(i) * 17
|
||||
if string.strcmp(_sha1_store_arr + sidx_d, sha1) == 0:
|
||||
return 0
|
||||
# 模块名:rel_path "lib/core/StubMerger.py" → "lib.core.StubMerger"
|
||||
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
|
||||
if full_path_buf is None:
|
||||
return 1
|
||||
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
|
||||
mod_name: str = _PathToModuleName(full_path_buf)
|
||||
stdlib.free(full_path_buf)
|
||||
if mod_name is None:
|
||||
return 1
|
||||
# 写入 SHA1
|
||||
idx_a: t.CSizeT = t.CSizeT(_sha1_store_count) * 17
|
||||
string.strcpy(_sha1_store_arr + idx_a, sha1)
|
||||
# 写入模块名
|
||||
idx_m: t.CSizeT = t.CSizeT(_sha1_store_count) * 64
|
||||
mn_len: t.CSizeT = string.strlen(mod_name)
|
||||
if mn_len < 64:
|
||||
string.strcpy(_sha1_store_mod + idx_m, mod_name)
|
||||
else:
|
||||
string.strncpy(_sha1_store_mod + idx_m, mod_name, 63)
|
||||
_sha1_store_mod[idx_m + 63] = '\0'
|
||||
stdlib.free(mod_name)
|
||||
# 写入相对路径
|
||||
idx_r: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN
|
||||
rp_len: t.CSizeT = string.strlen(rel_path)
|
||||
if rp_len < MAX_REL_PATH_LEN:
|
||||
string.strcpy(_sha1_store_rel + idx_r, rel_path)
|
||||
else:
|
||||
string.strncpy(_sha1_store_rel + idx_r, rel_path, MAX_REL_PATH_LEN - 1)
|
||||
_sha1_store_rel[idx_r + MAX_REL_PATH_LEN - 1] = '\0'
|
||||
_sha1_store_count += 1
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# GetSha1StoreCount - 获取全局存储器条目数
|
||||
# ============================================================
|
||||
def GetSha1StoreCount() -> int:
|
||||
"""返回全局 SHA1 存储器的条目数"""
|
||||
return _sha1_store_count
|
||||
|
||||
|
||||
def GetSha1StoreArrPtr() -> bytes | t.CPtr:
|
||||
"""返回 SHA1 数组指针(每个条目 17 字节)"""
|
||||
return _sha1_store_arr
|
||||
|
||||
|
||||
def GetSha1StoreModArrPtr() -> bytes | t.CPtr:
|
||||
"""返回模块名数组指针(每个条目 64 字节)"""
|
||||
return _sha1_store_mod
|
||||
|
||||
|
||||
def GetSha1StoreRelArrPtr() -> bytes | t.CPtr:
|
||||
"""返回相对路径数组指针(每个条目 MAX_REL_PATH_LEN 字节)"""
|
||||
return _sha1_store_rel
|
||||
|
||||
|
||||
# ============================================================
|
||||
# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用)
|
||||
#
|
||||
# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。
|
||||
# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*MAX_REL_PATH_LEN]
|
||||
# 内存归全局存储器所有,调用者不要 free
|
||||
# ============================================================
|
||||
def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int:
|
||||
"""获取全局存储器数组指针
|
||||
|
||||
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针,
|
||||
函数将存储器数组指针写入这些位置。
|
||||
返回条目数,-1 失败
|
||||
"""
|
||||
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None:
|
||||
return -1
|
||||
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
|
||||
return -1
|
||||
sp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_sha1_arr, t.CPtr))
|
||||
if sp is not None:
|
||||
sp[0] = _sha1_store_arr
|
||||
mp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_mod_arr, t.CPtr))
|
||||
if mp is not None:
|
||||
mp[0] = _sha1_store_mod
|
||||
rp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_rel_arr, t.CPtr))
|
||||
if rp is not None:
|
||||
rp[0] = _sha1_store_rel
|
||||
return _sha1_store_count
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
|
||||
#
|
||||
@@ -499,6 +670,39 @@ def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
|
||||
#
|
||||
# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.py
|
||||
# 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer 子模块类型。
|
||||
# 当模块名包含 '.' 时,检查其顶层包名是否在 deps_buf 中。
|
||||
#
|
||||
# 例如:
|
||||
# deps_buf = "ast stdio string"
|
||||
# mod_name = "ast.lexer" → 顶层包 "ast" 在 deps_buf 中 → 返回 1
|
||||
# mod_name = "ast.token" → 顶层包 "ast" 在 deps_buf 中 → 返回 1
|
||||
# mod_name = "foo.bar" → 顶层包 "foo" 不在 deps_buf 中 → 返回 0
|
||||
# ============================================================
|
||||
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
|
||||
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
|
||||
if deps_buf is None or mod_name is None:
|
||||
return 0
|
||||
# 查找第一个 '.',提取顶层包名
|
||||
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
|
||||
if dot_pos is None:
|
||||
return 0
|
||||
pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(mod_name))
|
||||
if pkg_len == 0 or pkg_len >= 256:
|
||||
return 0
|
||||
# 构造顶层包名(截断到 '.' 之前)
|
||||
# 直接在 mod_name 上临时截断检查,避免分配
|
||||
saved_ch: t.CChar = mod_name[pkg_len]
|
||||
mod_name[pkg_len] = '\0'
|
||||
result: int = HandlesImports.is_module_imported(deps_buf, mod_name)
|
||||
mod_name[pkg_len] = saved_ch
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque)
|
||||
#
|
||||
@@ -806,7 +1010,70 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
|
||||
in_body = 1
|
||||
continue
|
||||
|
||||
# 跳过其他行(global 带初值定义、type 定义等,stub 已有 external 声明)
|
||||
# 提取类型定义(%... = type { ... } 或 %... = type < ... >)
|
||||
# stub.ll 中只有 opaque 声明,需要从 text.ll 中提取完整定义
|
||||
# 用于 alloca 和 getelementptr 等需要完整类型的指令
|
||||
if ll > 0 and dep_buf[ls] == '%':
|
||||
eq_td: t.CSizeT = ls + 1
|
||||
while eq_td < ls + ll:
|
||||
if dep_buf[eq_td] == '=':
|
||||
break
|
||||
eq_td += 1
|
||||
if eq_td < ls + ll:
|
||||
# 检查是否为完整定义(= type { 或 = type <)
|
||||
dep_is_full_td: int = 0
|
||||
if eq_td + 8 <= ls + ll:
|
||||
if dep_buf[eq_td + 2] == 't' and dep_buf[eq_td + 3] == 'y' \
|
||||
and dep_buf[eq_td + 4] == 'p' and dep_buf[eq_td + 5] == 'e' \
|
||||
and dep_buf[eq_td + 6] == ' ':
|
||||
if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<':
|
||||
dep_is_full_td = 1
|
||||
if dep_is_full_td == 1:
|
||||
# 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀
|
||||
saved_eq_td: t.CChar = dep_buf[eq_td]
|
||||
dep_buf[eq_td] = '\0'
|
||||
# 1. 注释掉 out_buf 中的 opaque 声明(如果有)
|
||||
_CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls)
|
||||
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
|
||||
has_full_td: int = _HasFullTypeDefinition(out_buf, dep_buf + ls)
|
||||
dep_buf[eq_td] = saved_eq_td
|
||||
if has_full_td == 0:
|
||||
# 追加完整类型定义
|
||||
if out_pos + ll + 2 < out_size:
|
||||
string.strncpy(out_buf + out_pos, dep_buf + ls, ll)
|
||||
out_pos += ll
|
||||
out_buf[out_pos] = '\n'
|
||||
out_pos += 1
|
||||
out_buf[out_pos] = '\0'
|
||||
continue
|
||||
|
||||
# 提取全局变量定义(@... = ...)
|
||||
# 依赖 text.ll 中的全局变量带初值定义(如 @_mbuddy = global ...)
|
||||
# stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义
|
||||
# 注释掉 out_buf 中的同名行(external 声明或旧定义),追加 text.ll 中的完整定义
|
||||
if ll > 0 and dep_buf[ls] == '@':
|
||||
eq_gv: t.CSizeT = ls + 1
|
||||
while eq_gv < ls + ll:
|
||||
if dep_buf[eq_gv] == '=':
|
||||
break
|
||||
eq_gv += 1
|
||||
if eq_gv < ls + ll:
|
||||
# 截断到 '=' 之前(不含 '='),得到全局变量名 @name
|
||||
saved_gv: t.CChar = dep_buf[eq_gv]
|
||||
dep_buf[eq_gv] = '\0'
|
||||
# 注释掉 out_buf 中的同名行(external 声明或旧定义)
|
||||
_CommentOutGlobalInBuf(out_buf, dep_buf + ls)
|
||||
dep_buf[eq_gv] = saved_gv
|
||||
# 追加完整定义
|
||||
if out_pos + ll + 2 < out_size:
|
||||
string.strncpy(out_buf + out_pos, dep_buf + ls, ll)
|
||||
out_pos += ll
|
||||
out_buf[out_pos] = '\n'
|
||||
out_pos += 1
|
||||
out_buf[out_pos] = '\0'
|
||||
continue
|
||||
|
||||
# 跳过其他行
|
||||
return out_pos
|
||||
|
||||
|
||||
@@ -826,13 +1093,14 @@ def _PathToModuleName(path: str) -> str:
|
||||
if path is None:
|
||||
return None
|
||||
plen: t.CSizeT = string.strlen(path)
|
||||
# 跳过 includes/ 前缀
|
||||
prefix: str = "includes/"
|
||||
# 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容)
|
||||
prefix_len: t.CSizeT = 9
|
||||
path_start: t.CSizeT = 0
|
||||
if plen > prefix_len and string.strncmp(path, prefix, prefix_len) == 0:
|
||||
path_start = prefix_len
|
||||
plen = plen - prefix_len
|
||||
if plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
|
||||
psep_ch: t.CChar = path[8]
|
||||
if psep_ch == '/' or psep_ch == '\\':
|
||||
path_start = prefix_len
|
||||
plen = plen - prefix_len
|
||||
# 去掉 .py 后缀
|
||||
if plen > 3 and path[path_start + plen - 3] == '.' and path[path_start + plen - 2] == 'p' and path[path_start + plen - 1] == 'y':
|
||||
plen -= 3
|
||||
@@ -869,94 +1137,33 @@ def _PathToModuleName(path: str) -> str:
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _BuildIncludesSha1Map - 从 _sha1_map.txt 构建 sha1 → module_name 映射
|
||||
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
|
||||
#
|
||||
# _sha1_map.txt 格式: {sha1}:includes/{rel_path}\n
|
||||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
||||
# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。
|
||||
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
|
||||
# ============================================================
|
||||
def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
|
||||
sha1_list: t.CChar | t.CPtr,
|
||||
mod_list: t.CChar | t.CPtr) -> int:
|
||||
"""从 _sha1_map.txt 构建 sha1→module_name 映射,返回条目数"""
|
||||
if temp_dir is None or sha1_list is None or mod_list is None:
|
||||
return 0
|
||||
"""从全局内存存储器复制 sha1→module_name 映射到调用者数组
|
||||
|
||||
map_path: bytes = stdlib.malloc(td_len + 32)
|
||||
if map_path is None:
|
||||
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
|
||||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
||||
"""
|
||||
if sha1_list is None or mod_list is None:
|
||||
return 0
|
||||
viperlib.snprintf(map_path, td_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
mf: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R)
|
||||
if mf.closed:
|
||||
stdlib.free(map_path)
|
||||
if _sha1_store_arr is None or _sha1_store_mod is None:
|
||||
return 0
|
||||
map_buf: bytes = stdlib.malloc(STUB_READ_BUF_SIZE)
|
||||
if map_buf is None:
|
||||
mf.close()
|
||||
stdlib.free(map_path)
|
||||
return 0
|
||||
map_br: t.CInt64T = mf.read_all(map_buf, STUB_READ_BUF_SIZE)
|
||||
mf.close()
|
||||
stdlib.free(map_path)
|
||||
if map_br <= 0:
|
||||
stdlib.free(map_buf)
|
||||
return 0
|
||||
if map_br < STUB_READ_BUF_SIZE:
|
||||
map_buf[map_br] = '\0'
|
||||
else:
|
||||
map_buf[STUB_READ_BUF_SIZE - 1] = '\0'
|
||||
|
||||
count: int = 0
|
||||
pos: t.CSizeT = 0
|
||||
# 在循环外分配 sha1 缓冲区,避免反复 malloc/free 导致堆碎片化
|
||||
sha1: t.CChar | t.CPtr = stdlib.malloc(17)
|
||||
if sha1 is None:
|
||||
stdlib.free(map_buf)
|
||||
return 0
|
||||
while pos < map_br and count < MAX_INCLUDES:
|
||||
# 提取 SHA1(16 hex)
|
||||
string.strncpy(sha1, map_buf + pos, 16)
|
||||
sha1[16] = '\0'
|
||||
pos += 16
|
||||
# 跳过 ':'
|
||||
if pos < map_br and map_buf[pos] == ':':
|
||||
pos += 1
|
||||
# 提取路径(到 \r、\n 或 \0)
|
||||
# 注意: Windows CRLF 换行符是 \r\n,必须同时检查 \r 避免路径包含 \r
|
||||
path_start: t.CSizeT = pos
|
||||
while pos < map_br and map_buf[pos] != '\n' and map_buf[pos] != '\r' and map_buf[pos] != '\0':
|
||||
pos += 1
|
||||
path_len: t.CSizeT = pos - path_start
|
||||
# 跳过 \r\n 或 \n
|
||||
if pos < map_br and map_buf[pos] == '\r':
|
||||
pos += 1
|
||||
if pos < map_br and map_buf[pos] == '\n':
|
||||
pos += 1
|
||||
if path_len > 0:
|
||||
# 仅收集 includes/ 前缀的条目(用户文件如 App/ 不应进入 includes 映射)
|
||||
# 否则用户文件会被误判为 includes,导致 is_module_imported 模块名不匹配而跳过加载
|
||||
if path_len >= 9 and string.strncmp(map_buf + path_start, "includes/", 9) == 0:
|
||||
path_buf: t.CChar | t.CPtr = stdlib.malloc(path_len + 1)
|
||||
if path_buf is not None:
|
||||
string.strncpy(path_buf, map_buf + path_start, path_len)
|
||||
path_buf[path_len] = '\0'
|
||||
mod_name: str = _PathToModuleName(path_buf)
|
||||
stdlib.free(path_buf)
|
||||
if mod_name is not None:
|
||||
# 存储 sha1 和 mod_name 到数组中
|
||||
idx: t.CSizeT = t.CSizeT(count) * 17
|
||||
string.strcpy(sha1_list + idx, sha1)
|
||||
idx2: t.CSizeT = t.CSizeT(count) * 64
|
||||
mn_len: t.CSizeT = string.strlen(mod_name)
|
||||
if mn_len < 64:
|
||||
string.strcpy(mod_list + idx2, mod_name)
|
||||
else:
|
||||
string.strncpy(mod_list + idx2, mod_name, 63)
|
||||
mod_list[idx2 + 63] = '\0'
|
||||
stdlib.free(mod_name)
|
||||
count += 1
|
||||
stdlib.free(sha1)
|
||||
stdlib.free(map_buf)
|
||||
for i in range(_sha1_store_count):
|
||||
if count >= MAX_INCLUDES:
|
||||
break
|
||||
idx: t.CSizeT = t.CSizeT(i) * 17
|
||||
idx2: t.CSizeT = t.CSizeT(i) * 64
|
||||
string.strcpy(sha1_list + t.CSizeT(count) * 17, _sha1_store_arr + idx)
|
||||
string.strcpy(mod_list + t.CSizeT(count) * 64, _sha1_store_mod + idx2)
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
@@ -977,16 +1184,33 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
|
||||
# ============================================================
|
||||
def _FindSha1ByModName(sha1_arr: t.CChar | t.CPtr, mod_arr: t.CChar | t.CPtr,
|
||||
count: int, mod_name: str) -> str:
|
||||
"""从模块名查找 SHA1(精确匹配)"""
|
||||
"""从模块名查找 SHA1(精确匹配 + 包名回退)
|
||||
|
||||
精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
|
||||
支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
|
||||
"""
|
||||
if sha1_arr is None or mod_arr is None or mod_name is None:
|
||||
return None
|
||||
if count <= 0:
|
||||
return None
|
||||
# 第一遍:精确匹配
|
||||
for i in range(count):
|
||||
idx: t.CSizeT = t.CSizeT(i) * 64
|
||||
if string.strcmp(mod_arr + idx, mod_name) == 0:
|
||||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||||
return sha1_arr + sidx
|
||||
# 第二遍:包名回退("ast" → "ast.__init__")
|
||||
nm_len: t.CSizeT = string.strlen(mod_name)
|
||||
init_buf: bytes = stdlib.malloc(nm_len + 12)
|
||||
if init_buf is not None:
|
||||
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
|
||||
for i in range(count):
|
||||
idx2: t.CSizeT = t.CSizeT(i) * 64
|
||||
if string.strcmp(mod_arr + idx2, init_buf) == 0:
|
||||
sidx2: t.CSizeT = t.CSizeT(i) * 17
|
||||
stdlib.free(init_buf)
|
||||
return sha1_arr + sidx2
|
||||
stdlib.free(init_buf)
|
||||
return None
|
||||
|
||||
|
||||
@@ -1027,13 +1251,13 @@ def _AddSha1ToSet(set_buf: t.CChar | t.CPtr, count: int, sha1: str) -> int:
|
||||
#
|
||||
# 算法(工作列表):
|
||||
# 1. 扫描 source_dir 下的 .py 文件,解析 AST 获取 _imported_modules
|
||||
# 2. 构建 SHA1→module_name 映射(从 _sha1_map.txt)
|
||||
# 2. 构建 SHA1→module_name 映射(从全局内存存储器)
|
||||
# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名
|
||||
#
|
||||
# Args:
|
||||
# mb: 内存池
|
||||
# source_dir: 源文件目录(Config.SourceDir)
|
||||
# temp_dir: 临时目录(_sha1_map.txt 和 .deps.txt 所在位置)
|
||||
# temp_dir: 临时目录(.deps.txt 所在位置;_sha1_map.txt 仅人类可读输出,不读取)
|
||||
# reachable_set: 输出参数,可达 SHA1 集合缓冲区(MAX_INCLUDES_SHA1 * 17 字节)
|
||||
#
|
||||
# Returns:
|
||||
@@ -1060,11 +1284,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
return -1
|
||||
map_count: int = _BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr)
|
||||
if map_count <= 0:
|
||||
stdio.printf("[Reachable] 无法构建 SHA1 映射\n")
|
||||
VLogger.error("无法构建 SHA1 映射", "Reachable")
|
||||
stdlib.free(sha1_arr)
|
||||
stdlib.free(mod_arr)
|
||||
return -1
|
||||
stdio.printf("[Reachable] SHA1 映射: %d 个\n", map_count)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count)
|
||||
VLogger.debug(fb, "Reachable")
|
||||
|
||||
# 2. 扫描 source_dir 下的 .py 文件,收集直接依赖
|
||||
dir_len: t.CSizeT = string.strlen(source_dir)
|
||||
@@ -1099,7 +1326,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
|
||||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == win32base.INVALID_HANDLE_VALUE:
|
||||
stdio.printf("[Reachable] 未找到 .py 文件: %s\n", pattern)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "未找到 .py 文件: %s", pattern)
|
||||
VLogger.warning(fb, "Reachable")
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
stdlib.free(sha1_arr)
|
||||
@@ -1176,7 +1406,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
worklist[wl_len - 1] = '\0'
|
||||
wl_len -= 1
|
||||
|
||||
stdio.printf("[Reachable] 源文件直接依赖: '%s'\n", worklist)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "源文件直接依赖: '%s'", worklist)
|
||||
VLogger.debug(fb, "Reachable")
|
||||
|
||||
# 2.5. 全局预加入基础容器模块(_list/_dict/json)
|
||||
#
|
||||
@@ -1223,7 +1456,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
worklist[wl_len] = ' '
|
||||
wl_len += 1
|
||||
worklist[wl_len] = '\0'
|
||||
stdio.printf("[Reachable] 预加入容器模块后: '%s'\n", worklist)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist)
|
||||
VLogger.debug(fb, "Reachable")
|
||||
|
||||
# 3. 工作列表算法:递归收集可达 SHA1
|
||||
processed: bytes = stdlib.malloc(8192)
|
||||
@@ -1261,13 +1497,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
if HandlesImports.is_module_imported(processed, mod_buf) != 0:
|
||||
already = 1
|
||||
if already == 0:
|
||||
# 加入 processed
|
||||
if proc_len + name_len + 1 < 8192:
|
||||
string.strcpy(processed + proc_len, mod_buf)
|
||||
proc_len += name_len
|
||||
processed[proc_len] = ' '
|
||||
proc_len += 1
|
||||
processed[proc_len] = '\0'
|
||||
# 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug)
|
||||
if processed is not None:
|
||||
if proc_len + name_len + 1 < 8192:
|
||||
string.strcpy(processed + proc_len, mod_buf)
|
||||
proc_len = proc_len + name_len
|
||||
processed[proc_len] = ' '
|
||||
proc_len = proc_len + 1
|
||||
processed[proc_len] = '\0'
|
||||
|
||||
# 查找 SHA1
|
||||
found_sha1: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf)
|
||||
@@ -1289,17 +1526,18 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
deps_buf[dbr] = '\0'
|
||||
else:
|
||||
deps_buf[2047] = '\0'
|
||||
# 追加到 worklist(确保前面有空格分隔符,避免模块名合并)
|
||||
# 追加到 worklist(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug)
|
||||
dl: t.CSizeT = string.strlen(deps_buf)
|
||||
if dl > 0 and wl_len + dl + 2 < 8192:
|
||||
if wl_len > 0 and worklist[wl_len - 1] != ' ':
|
||||
if worklist is not None:
|
||||
if dl > 0 and wl_len + dl + 2 < 8192:
|
||||
if wl_len > 0 and worklist[wl_len - 1] != ' ':
|
||||
worklist[wl_len] = ' '
|
||||
wl_len = wl_len + 1
|
||||
string.strcpy(worklist + wl_len, deps_buf)
|
||||
wl_len = wl_len + dl
|
||||
worklist[wl_len] = ' '
|
||||
wl_len += 1
|
||||
string.strcpy(worklist + wl_len, deps_buf)
|
||||
wl_len += dl
|
||||
worklist[wl_len] = ' '
|
||||
wl_len += 1
|
||||
worklist[wl_len] = '\0'
|
||||
wl_len = wl_len + 1
|
||||
worklist[wl_len] = '\0'
|
||||
stdlib.free(deps_buf)
|
||||
else:
|
||||
df.close()
|
||||
@@ -1310,15 +1548,19 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
stdlib.free(worklist)
|
||||
stdlib.free(processed)
|
||||
|
||||
stdio.printf("[Reachable] 可达 SHA1: %d 个\n", reachable_count)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "可达 SHA1: %d 个", reachable_count)
|
||||
VLogger.debug(fb, "Reachable")
|
||||
return reachable_count
|
||||
|
||||
|
||||
# ============================================================
|
||||
# BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR
|
||||
#
|
||||
# 按需加载:读取 deps.txt 获取导入模块名,通过 _sha1_map.txt 查找 SHA1,
|
||||
# 按需加载:读取 deps.txt 获取导入模块名,通过内存存储器查找 SHA1,
|
||||
# 仅加载实际使用的依赖 stub(而非全部扫描)。
|
||||
# _sha1_map.txt 仅作为人类可读输出,程序内部不读取。
|
||||
# ============================================================
|
||||
def BuildCombinedIR(temp_dir: str, local_sha1: str,
|
||||
out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT:
|
||||
@@ -1387,7 +1629,7 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
|
||||
|
||||
# 2. 按需加载依赖 stubs(根据 deps.txt 过滤,而非扫描全部)
|
||||
# 先加载依赖 stub(含 type 定义),确保 type 定义在本地 text.ll 的 define 块之前
|
||||
# 2a. 构建 includes SHA1 → module_name 映射(从 _sha1_map.txt)
|
||||
# 2a. 构建 includes SHA1 → module_name 映射(从全局内存存储器)
|
||||
sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17)
|
||||
mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64)
|
||||
inc_count: int = 0
|
||||
@@ -1436,8 +1678,17 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
|
||||
is_include = 1
|
||||
if deps_loaded != 0:
|
||||
idx_mi: t.CSizeT = t.CSizeT(ii) * 64
|
||||
if HandlesImports.is_module_imported(deps_buf, mod_arr + idx_mi) != 0:
|
||||
dep_mod_name: str = mod_arr + idx_mi
|
||||
if HandlesImports.is_module_imported(deps_buf, dep_mod_name) != 0:
|
||||
should_load = 1
|
||||
else:
|
||||
# 传递性依赖:deps.txt 只记录直接导入(如 ast),
|
||||
# 但包的 __init__.py 内部 from .lexer import Lexer
|
||||
# 会让 IR 引用 ast.lexer 子模块类型。
|
||||
# 检查 dep_mod_name 是否是已导入包的子模块
|
||||
# (如 ast.lexer 是 ast 的子模块)
|
||||
if _is_submodule_of_imported(deps_buf, dep_mod_name) != 0:
|
||||
should_load = 1
|
||||
break
|
||||
if is_include == 0:
|
||||
# 非 includes stub(用户文件),总是加载
|
||||
@@ -1492,7 +1743,10 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
|
||||
if chk_path is not None:
|
||||
chk_f: fileio.File | t.CPtr = fileio.File(chk_path, fileio.MODE.R)
|
||||
if chk_f.closed:
|
||||
stdio.printf("[FATAL][BuildCombinedIR] 依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译\n", mod_nm, found_s, chk_path)
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译", mod_nm, found_s, chk_path)
|
||||
VLogger.error(fb, "BuildCombinedIR")
|
||||
sys.exit(1)
|
||||
chk_f.close()
|
||||
stdlib.free(chk_path)
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdio
|
||||
import stdlib
|
||||
import memhub
|
||||
import sys
|
||||
import w32.win32console as w32cmd
|
||||
import w32.win32file as w32file
|
||||
import w32.win32base as w32base
|
||||
import w32.win32base as win32base
|
||||
|
||||
|
||||
# ============================================================
|
||||
# VLogger - 原生日志系统(Windows 控制台彩色输出)
|
||||
#
|
||||
# 输出格式与 TPC (lib/core/VLogger.py) 对齐:
|
||||
# ├ INFO: message
|
||||
# ├ INFO[category]: message
|
||||
# ├ WARN: message
|
||||
# ├ ERROR: message
|
||||
# ├ SUCCESS: message
|
||||
# ├ DEBUG: message
|
||||
# ╠ CRITICAL: message (红底白字,致命错误)
|
||||
# ╠ CRITICAL[category]: message
|
||||
#
|
||||
# 分段着色:前缀符号(├/╠) 用级别色,LEVEL 标签用亮色,消息用默认色
|
||||
# CRITICAL 级别使用 ╠ 前缀和红底白字标签,与 TPC critical 对齐
|
||||
# ============================================================
|
||||
|
||||
# 日志级别
|
||||
@@ -18,6 +33,7 @@ class LogLevel(t.CEnum):
|
||||
WARNING = 2
|
||||
ERROR = 3
|
||||
SUCCESS = 4
|
||||
CRITICAL = 5
|
||||
|
||||
|
||||
# Win32 控制台前景色属性
|
||||
@@ -60,42 +76,72 @@ class Logger:
|
||||
return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
|
||||
return 0
|
||||
|
||||
def _log(self, level: int, prefix: str, msg: str, color: WORD,
|
||||
# ============================================================
|
||||
# _log - 核心日志输出(分段着色)
|
||||
#
|
||||
# 格式: ├ LEVEL: msg 或 ├ LEVEL[category]: msg
|
||||
# 分三段着色:
|
||||
# 1. 前缀符号 ├ (sym_color)
|
||||
# 2. LEVEL 标签 (level_color)
|
||||
# 3. 消息内容 (默认白色)
|
||||
# ============================================================
|
||||
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str,
|
||||
sym_color: WORD, level_color: WORD,
|
||||
category: str = "") -> int:
|
||||
if level < self._level:
|
||||
return 0
|
||||
self._set_color(color)
|
||||
# 前缀符号(如 ├)
|
||||
self._set_color(sym_color)
|
||||
stdio.printf("%s ", prefix_sym)
|
||||
# LEVEL 标签(亮色)
|
||||
self._set_color(level_color)
|
||||
if category is not None:
|
||||
if category[0] != 0:
|
||||
stdio.printf("%s[%s] %s\n", prefix, category, msg)
|
||||
stdio.printf("%s[%s]: ", level_str, category)
|
||||
else:
|
||||
stdio.printf("%s %s\n", prefix, msg)
|
||||
stdio.printf("%s: ", level_str)
|
||||
else:
|
||||
stdio.printf("%s %s\n", prefix, msg)
|
||||
stdio.printf("%s: ", level_str)
|
||||
# 消息内容(默认色)
|
||||
self._reset_color()
|
||||
stdio.printf("%s\n", msg)
|
||||
return 0
|
||||
|
||||
def debug(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.DEBUG, "[DEBUG]", msg,
|
||||
FOREGROUND_INTENSITY, category)
|
||||
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg,
|
||||
FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
|
||||
|
||||
def info(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.INFO, "[INFO]", msg,
|
||||
return self._log(LogLevel.INFO, "├", "INFO", msg,
|
||||
FOREGROUND_GREEN,
|
||||
FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||||
|
||||
def warning(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.WARNING, "[WARN]", msg,
|
||||
return self._log(LogLevel.WARNING, "├", "WARN", msg,
|
||||
FOREGROUND_YELLOW,
|
||||
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
|
||||
category)
|
||||
|
||||
def error(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.ERROR, "[ERROR]", msg,
|
||||
FOREGROUND_RED | FOREGROUND_INTENSITY, category)
|
||||
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg,
|
||||
FOREGROUND_RED,
|
||||
FOREGROUND_RED | FOREGROUND_INTENSITY, category)
|
||||
# 错误立即终止进程(避免错误后继续执行导致连锁崩溃)
|
||||
sys.exit(1)
|
||||
return ret
|
||||
|
||||
def success(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.SUCCESS, "[SUCCESS]", msg,
|
||||
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg,
|
||||
FOREGROUND_GREEN,
|
||||
FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||||
|
||||
def critical(self, msg: str, category: str = "") -> int:
|
||||
# CRITICAL 使用 ╠ 前缀(红底白字标签,对齐 TPC critical 风格)
|
||||
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg,
|
||||
FOREGROUND_RED,
|
||||
BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY,
|
||||
category)
|
||||
|
||||
def set_level(self, level: int) -> int:
|
||||
self._level = level
|
||||
return 0
|
||||
@@ -105,8 +151,8 @@ class Logger:
|
||||
# ============================================================
|
||||
|
||||
def banner(self, msg: str) -> int:
|
||||
"""输出分节标题(青色高亮)。"""
|
||||
self._set_color(FOREGROUND_CYAN | FOREGROUND_INTENSITY)
|
||||
"""输出分节标题(蓝色高亮,与 TPC banner 风格对齐)。"""
|
||||
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n=== %s ===\n\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
@@ -148,9 +194,28 @@ _mbuddy: memhub.MemManager | t.CPtr
|
||||
# 全局 logger 指针
|
||||
_g_logger: Logger | t.CPtr
|
||||
|
||||
# 全局格式化缓冲区(用于带变量的日志消息,1024 字节)
|
||||
_fmt_buf: t.CChar | t.CPtr
|
||||
|
||||
|
||||
def fmt_buf() -> t.CChar | t.CPtr:
|
||||
"""获取全局格式化缓冲区(1024 字节)。
|
||||
|
||||
用法:
|
||||
buf: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if buf is not None:
|
||||
viperlib.snprintf(buf, 1024, "翻译: %s (sha1=%s)", rp, sha1)
|
||||
log.info(buf, "Phase1")
|
||||
"""
|
||||
global _fmt_buf
|
||||
if _fmt_buf is None:
|
||||
_fmt_buf = stdlib.malloc(1024)
|
||||
return _fmt_buf
|
||||
|
||||
|
||||
def get_logger() -> Logger | t.CPtr:
|
||||
"""获取全局 logger 实例。若不存在则通过 _mbuddy 分配并初始化。"""
|
||||
global _g_logger
|
||||
if _g_logger is None:
|
||||
if _mbuddy is None:
|
||||
return None
|
||||
@@ -167,5 +232,63 @@ def get_logger() -> Logger | t.CPtr:
|
||||
|
||||
def set_logger(logger: Logger | t.CPtr) -> int:
|
||||
"""设置全局 logger 实例。"""
|
||||
global _g_logger
|
||||
_g_logger = logger
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐)
|
||||
#
|
||||
# 调用方式: VLogger.info("msg", "category")
|
||||
# 无需先 get_logger(),方便各模块直接使用
|
||||
# ============================================================
|
||||
|
||||
def info(msg: str, category: str = "") -> int:
|
||||
"""输出 INFO 级别日志。"""
|
||||
log: Logger | t.CPtr = get_logger()
|
||||
if log is not None:
|
||||
return log.info(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def warning(msg: str, category: str = "") -> int:
|
||||
"""输出 WARN 级别日志。"""
|
||||
log: Logger | t.CPtr = get_logger()
|
||||
if log is not None:
|
||||
return log.warning(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def error(msg: str, category: str = "") -> int:
|
||||
"""输出 ERROR 级别日志并立即终止进程。"""
|
||||
log: Logger | t.CPtr = get_logger()
|
||||
if log is not None:
|
||||
return log.error(msg, category)
|
||||
# logger 不可用时也直接退出
|
||||
sys.exit(1)
|
||||
return 0
|
||||
|
||||
|
||||
def success(msg: str, category: str = "") -> int:
|
||||
"""输出 SUCCESS 级别日志。"""
|
||||
log: Logger | t.CPtr = get_logger()
|
||||
if log is not None:
|
||||
return log.success(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def critical(msg: str, category: str = "") -> int:
|
||||
"""输出 CRITICAL 级别日志(致命错误,红底白字)。"""
|
||||
log: Logger | t.CPtr = get_logger()
|
||||
if log is not None:
|
||||
return log.critical(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def debug(msg: str, category: str = "") -> int:
|
||||
"""输出 DEBUG 级别日志。"""
|
||||
log: Logger | t.CPtr = get_logger()
|
||||
if log is not None:
|
||||
return log.debug(msg, category)
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user