取消了 i8* == i8* 实际使用 strcmp 的规则,修复了诸多 Bug

This commit is contained in:
TermiNexus committed 2026-07-30 21:56:19 +08:00
1 parent d227bee139
commit bfb55d68f4
18 files changed
+397 -254

No files matched your search

+6 -6
View File
@@ -7,9 +7,9 @@ import viperlib
# ============================================================
# StubGen.Converter - pyi 存根文件生成器
# StubGen.Converter - vpi 存根文件生成器
#
# 从 AST 直接生成 .pyi 存根文件,不依赖 CPython 的 PythonToStubConverter。
# 从 AST 直接生成 .vpi 存根文件,不依赖 CPython 的 PythonToStubConverter。
# 遍历 Module 节点的 children,提取函数签名、类定义、变量声明、
# import 语句,生成 Python 类型存根文件。
#
@@ -20,7 +20,7 @@ import viperlib
# ============================================================
# _eval_binop_int / _eval_binop_valid - 递归计算 BinOp 常量值
#
# 用于 .pyi 存根文件生成时计算 BinOp 表达式(如 A | B, A + B)的整数值。
# 用于 .vpi 存根文件生成时计算 BinOp 表达式(如 A | B, A + B)的整数值。
# 支持 BitOr/BitAnd/Add/Sub 以及嵌套的 Name(引用已注册的 CDefine 常量)。
# ============================================================
def _eval_binop_valid(node: ast.AST | t.CPtr) -> int:
@@ -105,7 +105,7 @@ _g_pyi_cdefine_found: int = 0
def _lookup_cdefine_for_pyi(name: str) -> int:
"""从本地 CDefine 表查找常量值(用于 .pyi 生成时的 BinOp 计算)"""
"""从本地 CDefine 表查找常量值(用于 .vpi 生成时的 BinOp 计算)"""
import lib.core.Handles.HandlesType as HandlesType
val: int = HandlesType.lookup_cdefine_constant(name)
if HandlesType.is_cdefine_found() != 0:
@@ -472,7 +472,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
# ============================================================
# _GeneratePyiFromAst - 从 AST 生成 .pyi 存根文件
# _GeneratePyiFromAst - 从 AST 生成 .vpi 存根文件
#
# 遍历 AST Module 节点,提取函数签名、类定义、变量声明、import 语句,
# 生成 Python 类型存根文件内容到 buf。
@@ -490,7 +490,7 @@ def _PyiClass(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
rel_path: str, buf: bytes,
buf_size: t.CSizeT) -> t.CSizeT:
"""从 AST 生成 .pyi 存根文件内容到 buf"""
"""从 AST 生成 .vpi 存根文件内容到 buf"""
if tree is None or buf is None or buf_size == 0:
return 0
+1 -1
View File
@@ -2,5 +2,5 @@
# StubGen 包入口
#
# 子模块通过绝对导入使用(import lib.StubGen.Converter as Converter),
# 此 __init__.py 不做 re-export,避免引入未使用的依赖。
# 此 __init__.vp 不做 re-export,避免引入未使用的依赖。
# ============================================================
+1 -1
View File
@@ -8,7 +8,7 @@ import lib.core.VLogger as VLogger
# lib 包入口 - 声明全局 _mbuddy 内存池指针
# ============================================================
# 全局 mbuddy 内存池指针(由 App/main.py 初始化后注入)
# 全局 mbuddy 内存池指针(由 App/main.vp 初始化后注入)
# 注意: 使用 MemBuddy 类型而非 MemManager,避免虚函数分派不工作时调用父类 alloc 返回 None
_mbuddy: memhub.MemBuddy | t.CPtr
+5 -2
View File
@@ -97,8 +97,11 @@ def extract_cdefine_int_value(val_node: ast.AST | t.CPtr) -> int:
# Case 4: Call — 如 t.CUnsignedLong(-11)
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(val_node)
if cl.args is not None and cl.args.__len__() > 0:
first_arg: ast.AST | t.CPtr = cl.args.get(0)
# 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
cl_args_list: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cl_args_list is not None and cl_args_list.__len__() > 0:
first_arg: ast.AST | t.CPtr = cl_args_list.get(0)
arg_val: int = extract_cdefine_int_value(first_arg)
return arg_val
return 0
+29 -2
View File
@@ -242,7 +242,7 @@ class AugAssignHandle(HandlesBase.Mixin):
# ============================================================
# _apply_aug_op - 应用增强赋值的二元运算
#
# 支持指针算术: ptr += int / ptr -= int
# 支持浮点运算、指针算术: ptr += int / ptr -= int
# 整数运算自动类型提升
# ============================================================
def _apply_aug_op(pool: memhub.MemBuddy | t.CPtr,
@@ -250,9 +250,36 @@ def _apply_aug_op(pool: memhub.MemBuddy | t.CPtr,
op: int,
lhs: llvmlite.Value | t.CPtr,
rhs: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""应用增强赋值的二元运算(指针算术 + 整数运算)"""
"""应用增强赋值的二元运算(浮点 + 指针算术 + 整数运算)"""
lhs_bits: int = HandlesExpr.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = HandlesExpr.get_llvm_type_bits(rhs.Ty)
lhs_fbits: int = HandlesExpr.get_llvm_float_bits(lhs.Ty)
rhs_fbits: int = HandlesExpr.get_llvm_float_bits(rhs.Ty)
# 浮点运算: 任一操作数为浮点时,使用浮点指令(必须在指针算术之前检查,
# 因为 float 的 int bits 为 0,会被误认为指针)
if lhs_fbits != 0 or rhs_fbits != 0:
# 确定目标浮点类型(使用较大的位宽)
target_fbits: int = lhs_fbits
if rhs_fbits > target_fbits:
target_fbits = rhs_fbits
target_float_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
if target_fbits == 32:
target_float_ty = llvmlite.Float(pool)
# 将两个操作数转换为目标浮点类型(int→float via si2fp, float→float via fpext/fptrunc)
lhs = HandlesExpr.coerce_to_type(builder, lhs, target_float_ty)
rhs = HandlesExpr.coerce_to_type(builder, rhs, target_float_ty)
if op == ast.OpKind.Add:
return llvmlite.build_fadd(builder, lhs, rhs)
if op == ast.OpKind.Sub:
return llvmlite.build_fsub(builder, lhs, rhs)
if op == ast.OpKind.Mult:
return llvmlite.build_fmul(builder, lhs, rhs)
if op == ast.OpKind.Div:
return llvmlite.build_fdiv(builder, lhs, rhs)
if op == ast.OpKind.Mod:
return llvmlite.build_frem(builder, lhs, rhs)
return None
# 指针算术: ptr += int / ptr -= int
if lhs_bits == 0 and rhs_bits != 0:
+2 -3
View File
@@ -1842,9 +1842,8 @@ def _clone_annotation(pool: memhub.MemBuddy | t.CPtr,
tpn: str = tp_names.get(i)
if tpn is not None and string.strcmp(nm.id, tpn) == 0:
ta: str = type_args.get(i)
# 类型实参可能是复合类型(如 "AST|t.CPtr"),用 _parse_type_string_ptr 解析
# 使用 _ptr 包装避免旧编译器对 ast.AST|t.CPtr 返回类型推断为 i32 的 bug
parsed_node: ast.AST | t.CPtr = HandlesType._parse_type_string_ptr(pool, ta, nm.ctx)
# 类型实参可能是复合类型(如 "AST|t.CPtr"),用 _parse_type_string 解析
parsed_node: ast.AST | t.CPtr = HandlesType._parse_type_string(pool, ta, nm.ctx)
return parsed_node
return ast.Name(pool, nm.id, nm.ctx)
return None
+131 -20
View File
@@ -146,6 +146,37 @@ def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
return 0
# ============================================================
# get_ptr_level - 计算指针层级
#
# 返回值:
# 0 = 非指针(数值/结构体等)
# 1 = 一级指针(如 i8*, Struct*)
# 2 = 二级指针(如 i8**, Struct**)
# 3+ = 更高级指针
#
# 用于比较运算的类型安全检查:
# - 数值(0) vs 数值(0) → 允许
# - 一级指针(1) vs 一级指针(1) → 允许
# - 数值(0) vs 指针(1+) → 禁止(fast-fail)
# - 不同级指针(1 vs 2) → 禁止(fast-fail)
# ============================================================
def get_ptr_level(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针..."""
if ty is None:
return 0
level: int = 0
cur: llvmlite.LLVMType | t.CPtr = ty
while cur is not None:
match cur:
case llvmlite.LLVMType.Ptr(pointee):
level += 1
cur = pointee
case _:
break
return level
# ============================================================
# 检查类型是否为 Array(独立函数,避免嵌套 match 的编译器 BUG)
# ============================================================
@@ -252,6 +283,11 @@ def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr,
# 浮点转换
val_fbits: int = get_llvm_float_bits(val.Ty)
target_fbits: int = get_llvm_float_bits(target_ty)
# float ↔ ptr: 禁止(无意义的转换,fast-fail 防止误走入 build_load 路径)
if (val_fbits != 0 and is_ptr_type(target_ty) != 0) or \
(is_ptr_type(val.Ty) != 0 and target_fbits != 0):
VLogger.error("coerce_to_type: 不支持的类型转换 float↔ptr", "Coerce")
return val
if val_fbits != 0 and target_fbits != 0:
if val_fbits == target_fbits:
return val
@@ -316,6 +352,8 @@ def create_global_string(builder: llvmlite.IRBuilder | t.CPtr,
# 去掉 private 链接,和函数一致:stub 中 declare,text 中 define
llvmlite.global_set_unnamed_addr(gv, 1)
llvmlite.global_set_constant(gv, 1)
# LLVM 22+ 不允许 external linkage 全局变量带初始值,设为 internal
gv.Linkage = "internal"
gv.Initializer = escaped
arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty)
@@ -825,7 +863,7 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
# 语义依据: Python 中 == None / != None 默认走身份比较,不调用 __eq__/__ne__
lhs_node: ast.AST | t.CPtr = cmp.left
cmp_with_none: int = 0
if op == ast.OpKind.Eq or op == ast.OpKind.Ne:
if op == ast.OpKind.Eq or op == ast.OpKind.Ne or op == ast.OpKind.Is or op == ast.OpKind.IsNot:
if rhs_node is not None and rhs_node.kind() == ast.ASTKind.Constant:
rhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rhs_node)
if rhs_cn is not None and rhs_cn.const_kind == ast.CONST_NONE:
@@ -884,18 +922,42 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
# `x is not y` → 指针/值不等比较
predicate = llvmlite.ICMP_NE
# 隐式类型转换:当一侧是指针(i8*),另一侧是整数时,
# 从指针加载第一个字节,使两侧类型一致
lhs_is_ptr: int = is_ptr_type(lhs.Ty)
rhs_is_ptr: int = is_ptr_type(rhs.Ty)
if lhs_is_ptr != 0 and rhs_is_ptr == 0:
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
lhs = llvmlite.build_load(builder, i8_ty, lhs)
elif rhs_is_ptr != 0 and lhs_is_ptr == 0:
i8_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
rhs = llvmlite.build_load(builder, i8_ty2, rhs)
# === 类型安全检查(BUG2 修复)===
# 规则:
# - 数值(0) vs 数值(0) → 允许(无关类型,按位宽 sext 对齐)
# - 一级指针(1) vs 一级指针(1) → 允许(无关 pointee 类型,地址比较)
# - 数值(0) vs 指针(1+) → 禁止(fast-fail,除 None 比较外)
# - 不同级指针(1 vs 2 等) → 禁止(fast-fail)
#
# None 比较跳过此检查(None 被翻译为 null 指针或 0,与任意指针/数值比较合法)
if cmp_with_none == 0:
lhs_ptr_level: int = get_ptr_level(lhs.Ty)
rhs_ptr_level: int = get_ptr_level(rhs.Ty)
if lhs_ptr_level != rhs_ptr_level:
# 类型不匹配:数值vs指针 或 不同级指针
VLogger.error(
"比较类型不匹配: lhs 指针层级=%d, rhs 指针层级=%d (禁止数值与指针、不同级指针比较)" % (lhs_ptr_level, rhs_ptr_level),
"Compare")
return None
else:
# None 比较: None 被翻译为 i8* null(一级指针)
# 如果 lhs 是指针,将 rhs (i8* null) bitcast 为 lhs 的指针类型
# 如果 lhs 是数值,将 rhs 转为数值 0(走正常整数比较)
lhs_ptr_level_none: int = get_ptr_level(lhs.Ty)
rhs_ptr_level_none: int = get_ptr_level(rhs.Ty)
if lhs_ptr_level_none != 0 and rhs_ptr_level_none != 0:
# 两边都是指针,类型可能不同(如 lhs=Struct*, rhs=i8* null)
# 由下方 bitcast 对齐处理,此处不处理
pass
elif lhs_ptr_level_none != 0 and rhs_ptr_level_none == 0:
# lhs 是指针, rhs 是数值 → 将 rhs 转为同层级 null 指针
rhs = llvmlite.ConstNull(pool, lhs.Ty, None)
elif rhs_ptr_level_none != 0 and lhs_ptr_level_none == 0:
# lhs 是数值, rhs 是 i8* null → 将 rhs 转为数值 0(保持 lhs 不变)
rhs = llvmlite.const_int32(pool, 0)
# 类型对齐:icmp 要求两边类型相同,将较窄的整数 sext 到较宽的类型
# 类型对齐:icmp 要求两边类型相同
# 仅对数值类型做 sext 对齐(指针类型无需对齐,LLVM 允许同层级指针直接 icmp)
lhs_bits: int = get_llvm_type_bits(lhs.Ty)
rhs_bits: int = get_llvm_type_bits(rhs.Ty)
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
@@ -904,6 +966,52 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
else:
rhs = llvmlite.build_sext(builder, rhs, lhs.Ty)
# 指针类型对齐:同层级但不同 pointee 类型的指针需要 bitcast 统一类型
# 例如 i8* vs Struct* → 将 rhs bitcast 为 lhs 的类型
if lhs_bits == 0 and rhs_bits == 0:
lhs_is_ptr_now: int = is_ptr_type(lhs.Ty)
rhs_is_ptr_now: int = is_ptr_type(rhs.Ty)
if lhs_is_ptr_now != 0 and rhs_is_ptr_now != 0:
# 两边都是指针,用 TypePrint 比较类型字符串
lhs_ty_buf: t.CChar | t.CPtr = pool.alloc(128)
rhs_ty_buf: t.CChar | t.CPtr = pool.alloc(128)
if lhs_ty_buf is not None and rhs_ty_buf is not None:
llvmlite.TypePrint(lhs_ty_buf, 128, lhs.Ty, pool)
llvmlite.TypePrint(rhs_ty_buf, 128, rhs.Ty, pool)
if string.strcmp(lhs_ty_buf, rhs_ty_buf) != 0:
rhs = llvmlite.build_bitcast(builder, rhs, lhs.Ty)
# 浮点比较:任一操作数为浮点类型时使用 fcmp 而非 icmp
lhs_fbits: int = get_llvm_float_bits(lhs.Ty)
rhs_fbits: int = get_llvm_float_bits(rhs.Ty)
if lhs_fbits != 0 or rhs_fbits != 0:
# 浮点类型对齐(fpext/fptrunc)
if lhs_fbits != 0 and rhs_fbits != 0 and lhs_fbits != rhs_fbits:
if lhs_fbits < rhs_fbits:
lhs = llvmlite.build_fpext(builder, lhs, rhs.Ty)
else:
rhs = llvmlite.build_fptrunc(builder, rhs, lhs.Ty)
elif lhs_fbits != 0 and rhs_fbits == 0:
# int → float
rhs = llvmlite.build_si2fp(builder, rhs, lhs.Ty)
elif rhs_fbits != 0 and lhs_fbits == 0:
lhs = llvmlite.build_si2fp(builder, lhs, rhs.Ty)
# 映射比较谓词到 fcmp
fpredicate: int = llvmlite.FCMP_OEQ
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
fpredicate = llvmlite.FCMP_OEQ
elif op == ast.OpKind.Ne or op == ast.OpKind.IsNot:
fpredicate = llvmlite.FCMP_ONE
elif op == ast.OpKind.Lt:
fpredicate = llvmlite.FCMP_OLT
elif op == ast.OpKind.Le:
fpredicate = llvmlite.FCMP_OLE
elif op == ast.OpKind.Gt:
fpredicate = llvmlite.FCMP_OGT
elif op == ast.OpKind.Ge:
fpredicate = llvmlite.FCMP_OGE
return llvmlite.build_fcmp(builder, fpredicate, lhs, rhs)
return llvmlite.build_icmp(builder, predicate, lhs, rhs)
@@ -1096,9 +1204,9 @@ def translate_boolop(builder: llvmlite.IRBuilder | t.CPtr,
# ============================================================
# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .py 文件路径
# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .vp/.py 文件路径
#
# .pyi 文件可能未生成(Phase1 未输出 pyi),此时回退读取源 .py 文件。
# .vpi 文件可能未生成(Phase1 未输出 vpi),此时回退读取源 .py 文件。
# 从 StubMerger 全局存储器查找 SHA1 对应的相对路径,
# 根据 rel_path 前缀选择基础目录:
# - rel_path 以 "lib/" 开头 → App 源文件,用 {Config.SourceDir}/{rel_path}
@@ -1180,6 +1288,9 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
3. imported_modules 遍历(import X):当通过 import X 导入模块时,
模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用)
"""
# 入口处清零标志,确保所有早退路径(return -1)都返回"未找到"状态
# 避免递归调用时前一次成功的标志残留导致误判
HandlesType.set_cdefine_found(0)
if name is None:
return -1
if from_imports is None:
@@ -1273,7 +1384,7 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
if pyi_path is None:
return -1
# 7. 读取 vpi 文件;若不存在或内容过短(空 stub)则回退读取源 .py 文件
# 7. 读取 vpi 文件;若不存在或内容过短(空 stub)则回退读取源 .vp/.py 文件
PYI_READ_BUF_SIZE: t.CSizeT = 65536
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
if pyi_buf is None:
@@ -1283,7 +1394,7 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
need_py_fallback: int = 0
bytes_read: LONG = 0
if pf.closed:
# .vpi 文件不存在:回退读取源 .py 文件
# .vpi 文件不存在:回退读取源 .vp/.py 文件
need_py_fallback = 1
else:
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
@@ -1474,7 +1585,7 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
# 9.5 如果当前模块文件中没找到,递归查找子模块
# 处理 from .X import NAME 和 from .X import * 的情况
# 例如 ast.__init__.py 中 from .base import CONST_INT, ...
# 例如 ast.__init__.vp 中 from .base import CONST_INT, ...
# 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1
if result_found == 0:
name_len_rc: t.CSizeT = string.strlen(name)
@@ -1725,7 +1836,7 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_val)
# 本地表未找到:尝试跨模块 .pyi 查找(保证多模块查表存表一致)
# 本地表未找到:尝试跨模块 .vpi 查找(保证多模块查表存表一致)
# 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找,
# 避免对普通变量名(mb/name/ptr/pool/self 等)误触发,造成严重性能损耗
if _is_cdefine_name_pattern(nm_id) != 0:
@@ -2478,7 +2589,7 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
trans.SymTab, nm_ma.id)
if ma_is_var is None:
# 先检查是否是 CDefine 编译期常量 (如 ast.CONST_INT / win32base.INVALID_HANDLE_VALUE)
# 模块属性访问的 CDefine 常量需要跨模块查找源 .py / .pyi 文件
# 模块属性访问的 CDefine 常量需要跨模块查找源 .vp / .vpi 文件
cdef_ma_val: int = HandlesType.lookup_cdefine_constant(at.attr)
cd_local: int = HandlesType.is_cdefine_found()
if cd_local == 0:
@@ -2601,7 +2712,7 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
return llvmlite.build_load(builder, load_ty_nested, gv_ref_nested)
# 未找到全局: 尝试 CDefine 常量查找
# 对于 w32.win32base.INVALID_HANDLE_VALUE 等嵌套模块属性访问,
# 先从限定模块的 .pyi 文件中查找 CDefine 常量值并内联
# 先从限定模块的 .vpi 文件中查找 CDefine 常量值并内联
cdef_val_nested: int = HandlesType.lookup_cdefine_constant(at.attr)
if HandlesType.is_cdefine_found() == 0:
# 构建临时 from_imports "attr:module" 用于跨模块查找
+138 -135
View File
@@ -384,113 +384,116 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr,
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 返回 i8* 的函数(指针返回值,截断会导致错误)
if bare_name == "strchr" or func_name == "strchr":
if string.strcmp(bare_name, "strchr") == 0 or string.strcmp(func_name, "strchr") == 0:
return i8_ptr_ty
if bare_name == "strrchr" or func_name == "strrchr":
if string.strcmp(bare_name, "strrchr") == 0 or string.strcmp(func_name, "strrchr") == 0:
return i8_ptr_ty
if bare_name == "strstr" or func_name == "strstr":
if string.strcmp(bare_name, "strstr") == 0 or string.strcmp(func_name, "strstr") == 0:
return i8_ptr_ty
if bare_name == "strcpy" or func_name == "strcpy":
if string.strcmp(bare_name, "strcpy") == 0 or string.strcmp(func_name, "strcpy") == 0:
return i8_ptr_ty
if bare_name == "strncpy" or func_name == "strncpy":
if string.strcmp(bare_name, "strncpy") == 0 or string.strcmp(func_name, "strncpy") == 0:
return i8_ptr_ty
if bare_name == "memset" or func_name == "memset":
if string.strcmp(bare_name, "memset") == 0 or string.strcmp(func_name, "memset") == 0:
return i8_ptr_ty
if bare_name == "memset32" or func_name == "memset32":
if string.strcmp(bare_name, "memset32") == 0 or string.strcmp(func_name, "memset32") == 0:
return i8_ptr_ty
if bare_name == "memcpy" or func_name == "memcpy":
if string.strcmp(bare_name, "memcpy") == 0 or string.strcmp(func_name, "memcpy") == 0:
return i8_ptr_ty
if bare_name == "memmove" or func_name == "memmove":
if string.strcmp(bare_name, "memmove") == 0 or string.strcmp(func_name, "memmove") == 0:
return i8_ptr_ty
# JSON 解析函数返回 JsonValue*(指针)
# from json.__parser import parse as json_parse → 别名 json_parse 也需覆盖
if bare_name == "parse" or func_name == "parse" or func_name == "json_parse":
if string.strcmp(bare_name, "parse") == 0 or string.strcmp(func_name, "parse") == 0 or string.strcmp(func_name, "json_parse") == 0:
return i8_ptr_ty
# MemBuddy 方法(当类型信息丢失时可能走外部函数路径)
if func_name == "alloc":
if string.strcmp(func_name, "alloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "calloc":
if string.strcmp(func_name, "calloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "realloc":
if string.strcmp(func_name, "realloc") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "alloc_buf":
if string.strcmp(func_name, "alloc_buf") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# VLogger 模块函数(跨模块调用时 stub 未注入,返回指针被截断为 i32 导致崩溃)
if func_name == "get_logger":
if string.strcmp(func_name, "get_logger") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "fmt_buf":
if string.strcmp(func_name, "fmt_buf") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# Config 模块函数(跨模块调用时 stub 未注入,返回 i8* 被 inttoptr i32 截断导致路径损坏)
if func_name == "get_includes_binary_dir":
if string.strcmp(func_name, "get_includes_binary_dir") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# HandlesType 模块函数(返回 ast.AST|t.CPtr 联合类型,跨模块推断为 i32 导致指针截断)
if string.strcmp(bare_name, "_parse_type_string") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 返回 i64 的函数
if func_name == "strlen":
if string.strcmp(func_name, "strlen") == 0:
return llvmlite.Int64(pool)
if func_name == "atoll":
if string.strcmp(func_name, "atoll") == 0:
return llvmlite.Int64(pool)
# 返回 double 的函数
if func_name == "atof":
if string.strcmp(func_name, "atof") == 0:
return llvmlite.Double(pool)
# 返回 i8 的函数
if func_name == "samestr":
if string.strcmp(func_name, "samestr") == 0:
return llvmlite.Int8(pool)
# Win32 API 返回指针(VOIDPTR/HANDLE)的函数
# stubs 翻译时未注入,需推断返回 i8* 避免指针被截断为 i32 导致解引用崩溃
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "VirtualAlloc":
if string.strcmp(func_name, "VirtualAlloc") == 0:
return i8_ptr_ty
if func_name == "HeapAlloc":
if string.strcmp(func_name, "HeapAlloc") == 0:
return i8_ptr_ty
if func_name == "HeapReAlloc":
if string.strcmp(func_name, "HeapReAlloc") == 0:
return i8_ptr_ty
if func_name == "HeapCreate":
if string.strcmp(func_name, "HeapCreate") == 0:
return i8_ptr_ty
if func_name == "GetProcessHeap":
if string.strcmp(func_name, "GetProcessHeap") == 0:
return i8_ptr_ty
if func_name == "GlobalAlloc":
if string.strcmp(func_name, "GlobalAlloc") == 0:
return i8_ptr_ty
if func_name == "GlobalFree":
if string.strcmp(func_name, "GlobalFree") == 0:
return i8_ptr_ty
if func_name == "GlobalLock":
if string.strcmp(func_name, "GlobalLock") == 0:
return i8_ptr_ty
if func_name == "LocalAlloc":
if string.strcmp(func_name, "LocalAlloc") == 0:
return i8_ptr_ty
if func_name == "LocalFree":
if string.strcmp(func_name, "LocalFree") == 0:
return i8_ptr_ty
if func_name == "GetProcAddress":
if string.strcmp(func_name, "GetProcAddress") == 0:
return i8_ptr_ty
if func_name == "GetModuleHandleA":
if string.strcmp(func_name, "GetModuleHandleA") == 0:
return i8_ptr_ty
if func_name == "GetModuleHandleW":
if string.strcmp(func_name, "GetModuleHandleW") == 0:
return i8_ptr_ty
if func_name == "LoadLibraryA":
if string.strcmp(func_name, "LoadLibraryA") == 0:
return i8_ptr_ty
if func_name == "LoadLibraryW":
if string.strcmp(func_name, "LoadLibraryW") == 0:
return i8_ptr_ty
# Win32 Handle 返回函数(HANDLE = void* = i8*)
if func_name == "GetStdHandle":
if string.strcmp(func_name, "GetStdHandle") == 0:
return i8_ptr_ty
if func_name == "CreateFileA":
if string.strcmp(func_name, "CreateFileA") == 0:
return i8_ptr_ty
if func_name == "CreateFileW":
if string.strcmp(func_name, "CreateFileW") == 0:
return i8_ptr_ty
if func_name == "FindFirstFileA":
if string.strcmp(func_name, "FindFirstFileA") == 0:
return i8_ptr_ty
if func_name == "FindFirstFileW":
if string.strcmp(func_name, "FindFirstFileW") == 0:
return i8_ptr_ty
# Win32 API 返回 i64 (SIZE_T) 的函数
if func_name == "VirtualQuery":
if string.strcmp(func_name, "VirtualQuery") == 0:
return llvmlite.Int64(pool)
if func_name == "HeapSize":
if string.strcmp(func_name, "HeapSize") == 0:
return llvmlite.Int64(pool)
if func_name == "HeapCompact":
if string.strcmp(func_name, "HeapCompact") == 0:
return llvmlite.Int64(pool)
if func_name == "GlobalSize":
if string.strcmp(func_name, "GlobalSize") == 0:
return llvmlite.Int64(pool)
# llvmlite build_* 函数返回类型推断
@@ -498,10 +501,10 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr,
# 大多数 build_* 函数返回 Value*(指针),少数终止指令返回 void
if string.strncmp(func_name, "build_", 6) == 0:
# 返回 void 的 build_* 函数(终止指令和无返回值指令)
if func_name == "build_ret" or func_name == "build_ret_void" or \
func_name == "build_store" or func_name == "build_br" or \
func_name == "build_cond_br" or func_name == "build_unreachable" or \
func_name == "build_switch":
if string.strcmp(func_name, "build_ret") == 0 or string.strcmp(func_name, "build_ret_void") == 0 or \
string.strcmp(func_name, "build_store") == 0 or string.strcmp(func_name, "build_br") == 0 or \
string.strcmp(func_name, "build_cond_br") == 0 or string.strcmp(func_name, "build_unreachable") == 0 or \
string.strcmp(func_name, "build_switch") == 0:
return llvmlite.Void(pool)
# 其他 build_* 函数返回 Value*(指针)
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
@@ -511,19 +514,19 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr,
# new_param_node/new_phi_incoming 返回 ParamNode*/PhiIncoming*(指针)
# const_int32/const_int64/ConstFloat/ConstZero/ConstNull/ConstInt 返回 Value*(指针)
# function_get_name/param_get_ty 等访问器返回指针
if func_name == "Int1" or func_name == "Int8" or func_name == "Int16" or \
func_name == "Int32" or func_name == "Int64" or func_name == "Ptr" or \
func_name == "Func" or func_name == "Array" or func_name == "Struct" or \
func_name == "Void" or func_name == "Half" or func_name == "Float" or \
func_name == "Double" or func_name == "FP128" or func_name == "Label" or \
func_name == "new_param_node" or func_name == "new_phi_incoming" or \
func_name == "const_int32" or func_name == "const_int64" or \
func_name == "ConstFloat" or func_name == "ConstZero" or \
func_name == "ConstNull" or func_name == "ConstInt" or \
func_name == "function_get_name" or func_name == "param_get_ty" or \
func_name == "value_get_ty" or func_name == "value_get_next" or \
func_name == "function_get_ret_ty" or func_name == "function_get_params" or \
func_name == "function_get_param_head" or func_name == "param_get_next":
if string.strcmp(func_name, "Int1") == 0 or string.strcmp(func_name, "Int8") == 0 or string.strcmp(func_name, "Int16") == 0 or \
string.strcmp(func_name, "Int32") == 0 or string.strcmp(func_name, "Int64") == 0 or string.strcmp(func_name, "Ptr") == 0 or \
string.strcmp(func_name, "Func") == 0 or string.strcmp(func_name, "Array") == 0 or string.strcmp(func_name, "Struct") == 0 or \
string.strcmp(func_name, "Void") == 0 or string.strcmp(func_name, "Half") == 0 or string.strcmp(func_name, "Float") == 0 or \
string.strcmp(func_name, "Double") == 0 or string.strcmp(func_name, "FP128") == 0 or string.strcmp(func_name, "Label") == 0 or \
string.strcmp(func_name, "new_param_node") == 0 or string.strcmp(func_name, "new_phi_incoming") == 0 or \
string.strcmp(func_name, "const_int32") == 0 or string.strcmp(func_name, "const_int64") == 0 or \
string.strcmp(func_name, "ConstFloat") == 0 or string.strcmp(func_name, "ConstZero") == 0 or \
string.strcmp(func_name, "ConstNull") == 0 or string.strcmp(func_name, "ConstInt") == 0 or \
string.strcmp(func_name, "function_get_name") == 0 or string.strcmp(func_name, "param_get_ty") == 0 or \
string.strcmp(func_name, "value_get_ty") == 0 or string.strcmp(func_name, "value_get_next") == 0 or \
string.strcmp(func_name, "function_get_ret_ty") == 0 or string.strcmp(func_name, "function_get_params") == 0 or \
string.strcmp(func_name, "function_get_param_head") == 0 or string.strcmp(func_name, "param_get_next") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 默认 i32(整数):与本模块函数定义的默认返回类型一致(HandlesFunctions.py line 324)
@@ -579,7 +582,7 @@ def _get_external_func_param_ty(pool: memhub.MemBuddy | t.CPtr,
api_count: t.CSizeT = win32_apis.__len__()
for ai in range(api_count):
api_name: str = win32_apis.get(ai)
if api_name == func_name:
if string.strcmp(api_name, func_name) == 0:
return i64_ty
return None
@@ -926,81 +929,81 @@ def _resolve_register(attr_name: str) -> str:
return ""
# Clobber 破坏描述符
if attr_name == "CLOBBER_MEMORY": return "memory"
if attr_name == "CLOBBER_CC": return "cc"
if attr_name == "CLOBBER_RAX": return "rax"
if attr_name == "CLOBBER_RBX": return "rbx"
if attr_name == "CLOBBER_RCX": return "rcx"
if attr_name == "CLOBBER_RDX": return "rdx"
if attr_name == "CLOBBER_RSI": return "rsi"
if attr_name == "CLOBBER_RDI": return "rdi"
if attr_name == "CLOBBER_RBP": return "rbp"
if attr_name == "CLOBBER_RSP": return "rsp"
if attr_name == "CLOBBER_R8": return "r8"
if attr_name == "CLOBBER_R9": return "r9"
if attr_name == "CLOBBER_R10": return "r10"
if attr_name == "CLOBBER_R11": return "r11"
if attr_name == "CLOBBER_R12": return "r12"
if attr_name == "CLOBBER_R13": return "r13"
if attr_name == "CLOBBER_R14": return "r14"
if attr_name == "CLOBBER_R15": return "r15"
if attr_name == "CLOBBER_EAX": return "eax"
if attr_name == "CLOBBER_EBX": return "ebx"
if attr_name == "CLOBBER_ECX": return "ecx"
if attr_name == "CLOBBER_EDX": return "edx"
if attr_name == "CLOBBER_ESI": return "esi"
if attr_name == "CLOBBER_EDI": return "edi"
if attr_name == "CLOBBER_DX": return "dx"
if attr_name == "CLOBBER_AX": return "ax"
if attr_name == "CLOBBER_CX": return "cx"
if attr_name == "CLOBBER_SI": return "si"
if attr_name == "CLOBBER_DI": return "di"
if string.strcmp(attr_name, "CLOBBER_MEMORY") == 0: return "memory"
if string.strcmp(attr_name, "CLOBBER_CC") == 0: return "cc"
if string.strcmp(attr_name, "CLOBBER_RAX") == 0: return "rax"
if string.strcmp(attr_name, "CLOBBER_RBX") == 0: return "rbx"
if string.strcmp(attr_name, "CLOBBER_RCX") == 0: return "rcx"
if string.strcmp(attr_name, "CLOBBER_RDX") == 0: return "rdx"
if string.strcmp(attr_name, "CLOBBER_RSI") == 0: return "rsi"
if string.strcmp(attr_name, "CLOBBER_RDI") == 0: return "rdi"
if string.strcmp(attr_name, "CLOBBER_RBP") == 0: return "rbp"
if string.strcmp(attr_name, "CLOBBER_RSP") == 0: return "rsp"
if string.strcmp(attr_name, "CLOBBER_R8") == 0: return "r8"
if string.strcmp(attr_name, "CLOBBER_R9") == 0: return "r9"
if string.strcmp(attr_name, "CLOBBER_R10") == 0: return "r10"
if string.strcmp(attr_name, "CLOBBER_R11") == 0: return "r11"
if string.strcmp(attr_name, "CLOBBER_R12") == 0: return "r12"
if string.strcmp(attr_name, "CLOBBER_R13") == 0: return "r13"
if string.strcmp(attr_name, "CLOBBER_R14") == 0: return "r14"
if string.strcmp(attr_name, "CLOBBER_R15") == 0: return "r15"
if string.strcmp(attr_name, "CLOBBER_EAX") == 0: return "eax"
if string.strcmp(attr_name, "CLOBBER_EBX") == 0: return "ebx"
if string.strcmp(attr_name, "CLOBBER_ECX") == 0: return "ecx"
if string.strcmp(attr_name, "CLOBBER_EDX") == 0: return "edx"
if string.strcmp(attr_name, "CLOBBER_ESI") == 0: return "esi"
if string.strcmp(attr_name, "CLOBBER_EDI") == 0: return "edi"
if string.strcmp(attr_name, "CLOBBER_DX") == 0: return "dx"
if string.strcmp(attr_name, "CLOBBER_AX") == 0: return "ax"
if string.strcmp(attr_name, "CLOBBER_CX") == 0: return "cx"
if string.strcmp(attr_name, "CLOBBER_SI") == 0: return "si"
if string.strcmp(attr_name, "CLOBBER_DI") == 0: return "di"
# Output 输出约束
if attr_name == "OUTPUT_REG": return "=r"
if attr_name == "OUTPUT_MEM": return "=m"
if attr_name == "OUTPUT_EAX": return "=a"
if attr_name == "OUTPUT_EBX": return "=b"
if attr_name == "OUTPUT_ECX": return "=c"
if attr_name == "OUTPUT_EDX": return "=d"
if attr_name == "OUTPUT_ESI": return "=S"
if attr_name == "OUTPUT_EDI": return "=D"
if attr_name == "OUTPUT_RAX": return "=a"
if attr_name == "OUTPUT_RBX": return "=b"
if attr_name == "OUTPUT_RCX": return "=c"
if attr_name == "OUTPUT_RDX": return "=d"
if attr_name == "OUTPUT_RSI": return "=S"
if attr_name == "OUTPUT_RDI": return "=D"
if string.strcmp(attr_name, "OUTPUT_REG") == 0: return "=r"
if string.strcmp(attr_name, "OUTPUT_MEM") == 0: return "=m"
if string.strcmp(attr_name, "OUTPUT_EAX") == 0: return "=a"
if string.strcmp(attr_name, "OUTPUT_EBX") == 0: return "=b"
if string.strcmp(attr_name, "OUTPUT_ECX") == 0: return "=c"
if string.strcmp(attr_name, "OUTPUT_EDX") == 0: return "=d"
if string.strcmp(attr_name, "OUTPUT_ESI") == 0: return "=S"
if string.strcmp(attr_name, "OUTPUT_EDI") == 0: return "=D"
if string.strcmp(attr_name, "OUTPUT_RAX") == 0: return "=a"
if string.strcmp(attr_name, "OUTPUT_RBX") == 0: return "=b"
if string.strcmp(attr_name, "OUTPUT_RCX") == 0: return "=c"
if string.strcmp(attr_name, "OUTPUT_RDX") == 0: return "=d"
if string.strcmp(attr_name, "OUTPUT_RSI") == 0: return "=S"
if string.strcmp(attr_name, "OUTPUT_RDI") == 0: return "=D"
# Input/Register 输入约束
if attr_name == "REG_ANY": return "r"
if attr_name == "REG_EAX": return "a"
if attr_name == "REG_EBX": return "b"
if attr_name == "REG_ECX": return "c"
if attr_name == "REG_EDX": return "d"
if attr_name == "REG_ESI": return "S"
if attr_name == "REG_EDI": return "D"
if attr_name == "REG_RAX": return "a"
if attr_name == "REG_RBX": return "b"
if attr_name == "REG_RCX": return "c"
if attr_name == "REG_RDX": return "d"
if attr_name == "REG_RSI": return "S"
if attr_name == "REG_RDI": return "D"
if string.strcmp(attr_name, "REG_ANY") == 0: return "r"
if string.strcmp(attr_name, "REG_EAX") == 0: return "a"
if string.strcmp(attr_name, "REG_EBX") == 0: return "b"
if string.strcmp(attr_name, "REG_ECX") == 0: return "c"
if string.strcmp(attr_name, "REG_EDX") == 0: return "d"
if string.strcmp(attr_name, "REG_ESI") == 0: return "S"
if string.strcmp(attr_name, "REG_EDI") == 0: return "D"
if string.strcmp(attr_name, "REG_RAX") == 0: return "a"
if string.strcmp(attr_name, "REG_RBX") == 0: return "b"
if string.strcmp(attr_name, "REG_RCX") == 0: return "c"
if string.strcmp(attr_name, "REG_RDX") == 0: return "d"
if string.strcmp(attr_name, "REG_RSI") == 0: return "S"
if string.strcmp(attr_name, "REG_RDI") == 0: return "D"
if attr_name == "INPUT_REG": return "r"
if attr_name == "INPUT_MEM": return "m"
if attr_name == "INPUT_EAX": return "a"
if attr_name == "INPUT_EBX": return "b"
if attr_name == "INPUT_ECX": return "c"
if attr_name == "INPUT_EDX": return "d"
if attr_name == "INPUT_ESI": return "S"
if attr_name == "INPUT_EDI": return "D"
if attr_name == "INPUT_RAX": return "a"
if attr_name == "INPUT_RBX": return "b"
if attr_name == "INPUT_RCX": return "c"
if attr_name == "INPUT_RDX": return "d"
if attr_name == "INPUT_RSI": return "S"
if attr_name == "INPUT_RDI": return "D"
if string.strcmp(attr_name, "INPUT_REG") == 0: return "r"
if string.strcmp(attr_name, "INPUT_MEM") == 0: return "m"
if string.strcmp(attr_name, "INPUT_EAX") == 0: return "a"
if string.strcmp(attr_name, "INPUT_EBX") == 0: return "b"
if string.strcmp(attr_name, "INPUT_ECX") == 0: return "c"
if string.strcmp(attr_name, "INPUT_EDX") == 0: return "d"
if string.strcmp(attr_name, "INPUT_ESI") == 0: return "S"
if string.strcmp(attr_name, "INPUT_EDI") == 0: return "D"
if string.strcmp(attr_name, "INPUT_RAX") == 0: return "a"
if string.strcmp(attr_name, "INPUT_RBX") == 0: return "b"
if string.strcmp(attr_name, "INPUT_RCX") == 0: return "c"
if string.strcmp(attr_name, "INPUT_RDX") == 0: return "d"
if string.strcmp(attr_name, "INPUT_RSI") == 0: return "S"
if string.strcmp(attr_name, "INPUT_RDI") == 0: return "D"
return ""
@@ -3770,7 +3773,7 @@ def _translate_call_with_kwargs(pool: memhub.MemBuddy | t.CPtr,
call_ret_ty = _infer_external_func_ret_ty(pool, func_name)
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
if func_name == "memcpy" and actual_count >= 3:
if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3:
return _emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst1, mc_src1, mc_num1)
return llvmlite.build_call(builder, call_name, head, actual_count, call_ret_ty, 0)
@@ -4932,7 +4935,7 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# 直接调用
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
if func_name == "memcpy" and can >= 3:
if string.strcmp(func_name, "memcpy") == 0 and can >= 3:
return _emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst2, mc_src2, mc_num2)
# 别名导入重新推断返回类型:
# from X import parse as json_parse → func_name="json_parse" 时 _infer_external_func_ret_ty 返回默认 i32
+2 -2
View File
@@ -336,7 +336,7 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr,
is_extern_decl = 1
# 计算 mangled name
if has_extern != 0 or has_state != 0 or has_export != 0 or fd.name == "main":
if has_extern != 0 or has_state != 0 or has_export != 0 or string.strcmp(fd.name, "main") == 0:
mangled_name: str = fd.name
else:
mangled_name = _mangle_func_name(trans, fd.name, 0)
@@ -468,7 +468,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
# SHA1 命名空间:t.CExtern/t.State/t.CExport 不加前缀,直接用裸名
# (裸名映射到 C 标准库符号,带 sha1 前缀会导致 undefined reference)
# main 函数也不加前缀(程序入口点)
if has_extern != 0 or has_state != 0 or has_export != 0 or fd.name == "main":
if has_extern != 0 or has_state != 0 or has_export != 0 or string.strcmp(fd.name, "main") == 0:
mangled_name: str = fd.name
else:
mangled_name: str = _mangle_func_name(trans, fd.name, 0)
+6 -6
View File
@@ -103,7 +103,7 @@ def add_from_import(pool: memhub.MemBuddy | t.CPtr,
entry: t.CChar | t.CPtr = pool.alloc(256)
if entry is None:
return from_imports
if original_name is not None and original_name != local_name:
if original_name is not None and string.strcmp(original_name, local_name) != 0:
viperlib.snprintf(entry, 256, "%s:%s:%s", local_name, module_name, original_name)
else:
viperlib.snprintf(entry, 256, "%s:%s", local_name, module_name)
@@ -262,7 +262,7 @@ def lookup_from_import(from_imports: str, name: str,
# ============================================================
# 全局 re-export 映射表(module_sha1 + func_name → source_module)
#
# 当 ast/__init__.py 中有 from .lexer import _lexer_init 时,
# 当 ast/__init__.vp 中有 from .lexer import _lexer_init 时,
# _lexer_init 被 re-export 为 ast._lexer_init。
# 跨模块调用 ast._lexer_init(...) 需使用 ast.lexer 的 SHA1 混淆,
# 而非 ast 的 SHA1。
@@ -427,10 +427,10 @@ def _resolve_relative_module(pool: memhub.MemBuddy | t.CPtr,
# 对于顶级文件(无目录分隔符),返回 None
#
# 示例:
# "llvmlite/__init__.py" → "llvmlite"
# "llvmlite/__types.py" → "llvmlite"
# "ast/parser.py" → "ast"
# "ast.py" → None(顶级文件)
# "llvmlite/__init__.vp" → "llvmlite"
# "llvmlite/__types.vp" → "llvmlite"
# "ast/parser.vp" → "ast"
# "ast.vp" → None(顶级文件)
# ============================================================
def compute_package_from_relpath(pool: memhub.MemBuddy | t.CPtr,
rel_path: str) -> str:
+5 -10
View File
@@ -378,17 +378,12 @@ def _parse_type_string(pool: memhub.MemBuddy | t.CPtr,
# ============================================================
# _parse_type_string_ptr - _parse_type_string 的 t.CPtr 返回类型包装
# _parse_type_string_ptr 已移除
#
# 旧编译器对 ast.AST | t.CPtr 跨模块返回类型推断有 bug(解析为 i32),
# 用 t.CPtr 包装绕过,外部模块应调用此函数而非 _parse_type_string
# 原先用 t.CPtr 包装绕过旧编译器对 ast.AST|t.CPtr 跨模块返回类型
# 推断为 i32 的 bug。现在已在 _infer_external_func_ret_ty 中注册
# _parse_type_string 返回 i8*,外部模块直接调用 _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
# ============================================================
@@ -467,7 +462,7 @@ def fatal_error(node: ast.AST | t.CPtr, msg: str):
file_name = "<unknown>"
stdio.printf("Traceback (most recent call last):\n")
stdio.printf(' File "%s", line %d\n', file_name, lineno)
if lineno > 0 and file_name != "<unknown>":
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
src_line: str = _read_source_line(file_name, lineno)
if src_line is not None:
stdio.printf(" %s\n", src_line)
+8 -8
View File
@@ -15,7 +15,7 @@ import lib.core.VLogger as VLogger
# ============================================================
# IncludesScanner - includes 目录递归扫描
#
# 递归扫描 includes 目录,收集所有 .py 文件路径和 SHA1,
# 递归扫描 includes 目录,收集所有 .vp/.py 文件路径和 SHA1,
# 为 Phase1 stub 生成和 Phase2 stub 合并提供基础数据。
#
# 使用 Win32 FindFirstFileA/FindNextFileA 实现目录遍历。
@@ -90,7 +90,7 @@ def add_file_entry(result: ScanResult | t.CPtr,
if result.Count >= result.Capacity:
return 1
# 计算模块名:rel_path 中的 / 替换为 .,去掉 .py 扩展名
# 计算模块名:rel_path 中的 / 替换为 .,去掉 .vp/.py 扩展名
rel_len: t.CSizeT = string.strlen(rel_path)
mod_buf: str = stdlib.malloc(rel_len + 1)
if mod_buf is None:
@@ -160,7 +160,7 @@ def compute_file_sha1(pool: memhub.MemBuddy | t.CPtr,
if f.closed:
return None
# 分配读取缓冲区(128KB,足够大多数 .py 文件)
# 分配读取缓冲区(128KB,足够大多数 .vp/.py 文件)
# 使用 stdlib.malloc 避免 mbuddy 池耗尽(每文件 128KB,60+ 文件会耗尽 16MB 池)
BUF_SIZE: t.CSizeT = 131072
buf: bytes = stdlib.malloc(BUF_SIZE)
@@ -221,13 +221,13 @@ def compute_file_sha1(pool: memhub.MemBuddy | t.CPtr,
# scan_directory_recursive - 递归扫描目录
#
# 使用 FindFirstFileA/FindNextFileA 遍历目录树,
# 对每个 .py 文件计算 SHA1 并添加到结果中。
# 对每个 .vp/.py 文件计算 SHA1 并添加到结果中。
# ============================================================
def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr,
root_dir: str,
rel_prefix: str,
result: ScanResult | t.CPtr) -> int:
"""递归扫描目录,收集 .py 文件"""
"""递归扫描目录,收集 .vp/.py 文件"""
if pool is None or root_dir is None or result is None:
return 1
@@ -329,11 +329,11 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr,
# ============================================================
# scan_includes - 扫描 includes 目录入口
#
# 扫描指定的 includes 目录,返回所有 .py 文件的路径和 SHA1。
# 扫描指定的 includes 目录,返回所有 .vp/.py 文件的路径和 SHA1。
# ============================================================
def scan_includes(pool: memhub.MemBuddy | t.CPtr,
includes_dir: str) -> ScanResult | t.CPtr:
"""扫描 includes 目录,返回所有 .py 文件的扫描结果"""
"""扫描 includes 目录,返回所有 .vp/.py 文件的扫描结果"""
if pool is None or includes_dir is None:
return None
@@ -360,6 +360,6 @@ def find_entry_by_module(result: ScanResult | t.CPtr,
entry: FileEntry | t.CPtr = (FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry is not None:
if entry.ModuleName is not None:
if entry.ModuleName == module_name:
if string.strcmp(entry.ModuleName, module_name) == 0:
return entry
return None
+4 -4
View File
@@ -29,7 +29,7 @@ _mbuddy: memhub.MemBuddy | t.CPtr
# 源代码缓冲区大小(1MB)
SRC_BUF_SIZE: t.CDefine = 1048576
# pyi 缓冲区大小(256KB)
# vpi 缓冲区大小(256KB)
PYI_BUF_SIZE: t.CSizeT = 262144
# 拓扑排序相关常量
@@ -480,7 +480,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
# ============================================================
# RunPhase1 - Phase1: 扫描 includes 目录,按需翻译并生成 stub
#
# 对每个 .py 文件检查 stub 是否已存在,若不存在则翻译并分离 stub。
# 对每个 .vp/.py 文件检查 stub 是否已存在,若不存在则翻译并分离 stub。
# "按需翻译":仅对 stub 不存在的文件执行翻译,避免重复工作。
#
# Args:
@@ -684,7 +684,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
if reachable_count > 0:
use_reachable = 1
# 用可达集合重新生成 _sha1_map.txt(按图求索的最终产物)
# Phase B+ 只遍历这些条目,避免编译不需要的 includes(如 asm.py)
# Phase B+ 只遍历这些条目,避免编译不需要的 includes(如 asm.vp)
StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, reachable_set, reachable_count)
# 重新加载 sha1_set,使后续 Phase 1a-pre/1a/1b 的过滤也使用可达集合
string.memset(sha1_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
@@ -702,7 +702,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# 只处理可达文件,避免翻译不需要的 includes(如 Test 不依赖 ast 模块)。
#
# 拓扑排序:预扫描所有文件收集类继承关系,按依赖顺序处理文件,
# 确保父类所在文件先于子类所在文件处理(如 base.py 先于 astaux.py)。
# 确保父类所在文件先于子类所在文件处理(如 base.vp 先于 astaux.vp)。
# ============================================================
p1a_reg: int = 0
p1a_skp: int = 0
+36 -22
View File
@@ -45,10 +45,10 @@ class SrcFileEntry:
# ============================================================
# _ScanDirForPyFiles - 递归扫描目录,收集 .py 文件到 entries
# _ScanDirForPyFiles - 递归扫描目录,收集 .vp/.py 文件到 entries
#
# 替代旧的非递归 FindFirstFileA(dir/*.py) 扫描,支持子目录
# (如 App/lib/core/Handles/)。遇到目录递归扫描,遇到 .py
# (如 App/lib/core/Handles/)。遇到目录递归扫描,遇到 .vp/.py
# 文件读取内容、计算 SHA1、加入 entries。
#
# Args:
@@ -67,7 +67,7 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy | t.CPtr,
entries: SrcFileEntry | t.CPtr,
entry_size: t.CSizeT,
file_count: int, max_files: int) -> int:
"""递归扫描目录,收集 .py 文件到 entries,返回新的 file_count"""
"""递归扫描目录,收集 .vp/.py 文件到 entries,返回新的 file_count"""
if dir_path is None or entries is None:
return file_count
if file_count >= max_files:
@@ -184,7 +184,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
viperlib.snprintf(fb, 1024, "多文件项目编译: %s", source_dir)
VLogger.info(fb, "project")
# === 1. 递归扫描 source_dir 下的 .py 文件(包括子目录)===
# === 1. 递归扫描 source_dir 下的 .vp/.py 文件(包括子目录)===
entry_size: t.CSizeT = SrcFileEntry.__sizeof__()
entries: SrcFileEntry | t.CPtr = stdlib.malloc(MAX_SRC_FILES * entry_size)
if entries is None:
@@ -222,7 +222,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
VLogger.info(fb_inc, "project")
# 追加 App 源文件 SHA1 到 _sha1_map.txt(人类可读输出,程序内部不读取此文件)
# 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py)
# 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.vp)
# 同步追加到内存存储器(AppendToSha1MapStore,供跨模块 CDefine 查找)
td_len_am: t.CSizeT = string.strlen(temp_dir)
src_dir_len_am: t.CSizeT = string.strlen(source_dir)
@@ -246,7 +246,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
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 一致
# rel_path_am 格式如 "lib/core/StubMerger.vp",与 _sha1_map.txt 一致
if StubMerger.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) == 0:
app_filled += 1
# 写入 _sha1_map.txt(人类可读输出)
@@ -289,7 +289,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
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 → 包名)
# 使用 _PathToModuleName 转换为点分模块名(处理 __init__.vp → 包名)
mod_name_us: str = StubMerger._PathToModuleName(rel_path_us)
if mod_name_us is None:
continue
@@ -369,7 +369,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# === Phase A-pre: 预注册所有源文件的 struct/enum/union ===
# 解决循环引用问题:circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义
# 仅注册 struct/enum/union(declare_only=1),不翻译方法体
# 多遍扫描:解决跨文件继承的字母序问题(如 HandlesAnnAssign.py 在 HandlesBase.py 之前,
# 多遍扫描:解决跨文件继承的字母序问题(如 HandlesAnnAssign.vp/.py 在 HandlesBase.vp/.py 之前,
# AnnAssignHandle 继承 Mixin 时 Mixin 未注册)。每遍注册新类后,下一遍子类可继承。
VLogger.info("预注册 struct/enum/union...", "PhaseA")
src_dir_len_pre: t.CSizeT = string.strlen(source_dir)
@@ -879,7 +879,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
else:
exe_path = linker_output
# 构造最终 .obj 路径列表:main_obj_path 在前,其他 .obj 在后
# 构造最终 .obj 路径列表:main_obj_path 在前,reachable .obj 在后
final_obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE + 512)
if final_obj_paths is None:
stdlib.free(entries)
@@ -891,32 +891,46 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
mlen: t.CSizeT = string.strlen(main_obj_path)
string.strcpy(final_obj_paths, main_obj_path)
fop_pos = mlen
if obj_paths[0] != '\0':
# 按图链接:遍历 reachable_set,只收集可达 includes 的 .obj
# 不再全量收集 includes.binary 下的所有 .obj
if reachable_set is not None and reachable_count > 0:
ibd_len_pc: t.CSizeT = string.strlen(includes_binary_dir)
ri_pc: int
for ri_pc in range(reachable_count):
rsha1_pc: str = reachable_set + t.CSizeT(ri_pc) * 17
if rsha1_pc[0] == '\0':
continue
obj_path_pc: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_pc, rsha1_pc, "obj")
if obj_path_pc is not None:
# 检查 .obj 文件是否存在
cf_pc: fileio.File | t.CPtr = fileio.File(obj_path_pc, fileio.MODE.R)
if not cf_pc.closed:
cf_pc.close()
# .obj 存在,追加到 final_obj_paths
op_len_pc: t.CSizeT = string.strlen(obj_path_pc)
if fop_pos + op_len_pc + 2 < OBJ_PATHS_SIZE + 512:
if fop_pos > 0:
final_obj_paths[fop_pos] = ' '
fop_pos += 1
# 追加其他 .obj
if obj_paths[0] != '\0':
string.strcpy(final_obj_paths + fop_pos, obj_paths)
fop_pos += string.strlen(obj_paths)
string.strcpy(final_obj_paths + fop_pos, obj_path_pc)
fop_pos += op_len_pc
final_obj_paths[fop_pos] = '\0'
stdlib.free(obj_path_pc)
final_obj_paths[fop_pos] = '\0'
obj_paths_len: t.CSizeT = fop_pos
# 暂时禁用按图链接:始终全量链接 includes.binary 中的 .obj
# 按图链接在 --clean 后 deps.txt 缺失时无法正确工作(跨包传递依赖丢失)
# 按图编译(Phase B+)仍保留:只编译 reachable_set 中的缺失 includes
# TODO: 待 deps.txt 生成机制完善后(如 Phase A 翻译所有 includes 的 imports),重新启用按图链接
# 释放 reachable_set(Phase B+ 已使用完毕)
# 释放 reachable_set(Phase C 已使用完毕)
if reachable_set is not None:
stdlib.free(reachable_set)
reachable_set = None
# 始终传 includes_binary_dir 给 link_objs_to_exe,由其全量收集 .obj
# 按图链接:不传 includes_binary_dir(传 None),避免全量收集
lret: int = BuildPipeline.link_objs_to_exe(
final_obj_paths, obj_paths_len,
linker_cmd, linker_flags, exe_path,
includes_binary_dir)
None)
if lret == 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
+11 -19
View File
@@ -51,9 +51,6 @@ _sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个)
_sha1_store_count: int # 条目数
# 可达集合是否不完整(1=有 includes deps.txt 缺失,Phase C 应回退全量链接)
_g_reachable_incomplete: int
# includes 依赖内存存储(不依赖 deps.txt 过程文件)
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入此处
# _BuildReachableSha1Set 从此处读取依赖,不读 deps.txt
@@ -436,7 +433,7 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in
#
# Args:
# sha1: SHA1 字符串(16 字符 + null)
# rel_path: 相对路径(如 "lib/core/StubMerger.py",
# rel_path: 相对路径(如 "lib/core/StubMerger.vp",
# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
#
# Returns:
@@ -465,7 +462,7 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
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"
# 模块名:rel_path "lib/core/StubMerger.vp" → "lib.core.StubMerger"
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
if full_path_buf is None:
return 1
@@ -506,11 +503,6 @@ def GetSha1StoreCount() -> int:
return _sha1_store_count
def IsReachableIncomplete() -> int:
"""返回可达集合是否不完整(1=有 deps.txt 缺失,应回退全量链接)"""
return _g_reachable_incomplete
# ============================================================
# PopulateIncludesDeps - 将 includes 文件的依赖存入内存
#
@@ -791,7 +783,7 @@ def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
# ============================================================
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
#
# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.py
# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp
# 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer 子模块类型。
# 当模块名包含 '.' 时,检查其顶层包名是否在 deps_buf 中。
#
@@ -1198,15 +1190,15 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
# ============================================================
# _PathToModuleName - 将文件路径转换为模块名
#
# 输入: "includes/stdio.py" 或 "includes/lib/core/Handles/HandlesType.py"
# 输入: "includes/stdio.vp" 或 "includes/lib/core/Handles/HandlesType.vp"
# 输出: "stdio" 或 "lib.core.Handles.HandlesType"
# 去除 includes/ 前缀和 .py 后缀,/ 替换为 .
# 去除 includes/ 前缀和 .vp/.py 后缀,/ 替换为 .
# 返回 stdlib.malloc 分配的字符串,调用者负责释放
# ============================================================
def _PathToModuleName(path: str) -> str:
"""将文件路径转换为模块名
特殊处理 __init__.py:includes/ast/__init__.py → ast(而非 ast.__init__)
特殊处理 __init__.vp:includes/ast/__init__.vp → ast(而非 ast.__init__)
"""
if path is None:
return None
@@ -1225,10 +1217,10 @@ def _PathToModuleName(path: str) -> str:
plen -= 3
if plen == 0:
return None
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.py)
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.vp)
# /__init__ 是 9 个字符:/ _ _ i n i t _ _
# 如果是,去掉分隔符+__init__ 后缀,模块名就是包名(如 ast/__init__ → ast)
# 注意:顶级 __init__.py(plen=8)不会进入此分支,因为 plen > 9 才检查
# 注意:顶级 __init__.vp(plen=8)不会进入此分支,因为 plen > 9 才检查
# 同时支持 / 和 \ 两种分隔符(TPC 写入的路径可能混合使用)
if plen > 9:
tail_off: t.CSizeT = path_start + plen - 9
@@ -1369,7 +1361,7 @@ def _AddSha1ToSet(set_buf: t.CChar | t.CPtr, count: int, sha1: str) -> int:
# 翻译不需要的 includes(如 Test 不依赖 llvmlite,则不翻译)。
#
# 算法(工作列表):
# 1. 扫描 source_dir 下的 .py 文件,解析 AST 获取 _imported_modules
# 1. 扫描 source_dir 下的 .vp/.py 文件,解析 AST 获取 _imported_modules
# 2. 构建 SHA1→module_name 映射(从全局内存存储器)
# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名
#
@@ -1672,7 +1664,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
# deps_str 为空字符串视为无依赖(合法情况,如叶子模块)
else:
# 依赖未在内存中找到
# 按需过滤: 检查是否是 App 源文件(source_dir 下的 .py 文件)
# 按需过滤: 检查是否是 App 源文件(source_dir 下的 .vp/.py 文件)
# App 源文件不需要加入 reachable_set(只用于 includes 过滤),跳过
sd_len_ff: t.CSizeT = string.strlen(source_dir)
mb_len_ff: t.CSizeT = string.strlen(mod_buf)
@@ -1852,7 +1844,7 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
should_load = 1
else:
# 传递性依赖:deps.txt 只记录直接导入(如 ast),
# 但包的 __init__.py 内部 from .lexer import Lexer
# 但包的 __init__.vp 内部 from .lexer import Lexer
# 会让 IR 引用 ast.lexer 子模块类型。
# 检查 dep_mod_name 是否是已导入包的子模块
# (如 ast.lexer 是 ast 的子模块)
+1 -1
View File
@@ -5,5 +5,5 @@ from stdint import *
# core 包入口
#
# 子模块通过绝对导入使用(import lib.core.X as X),
# 此 __init__.py 不做 re-export,避免引入未使用的依赖。
# 此 __init__.vp 不做 re-export,避免引入未使用的依赖。
# ============================================================
+10 -10
View File
@@ -91,17 +91,17 @@ def main() -> int:
# 注册参数(与 Projectrans.py main() 一致)
parser.add_argument("--project", None, argparse.STRING, 0, None, False,
argparse.STORE, "project.json 路径(默认查找当前目录)")
argparse.STORE, "project.vpj 路径(默认查找当前目录)")
parser.add_argument("--src", None, argparse.STRING, 0, None, False,
argparse.STORE, "源文件目录(覆盖 project.json)")
argparse.STORE, "源文件目录(覆盖 project.vpj)")
parser.add_argument("--temp", None, argparse.STRING, 0, None, False,
argparse.STORE, "声明接口临时目录(覆盖 project.json)")
argparse.STORE, "声明接口临时目录(覆盖 project.vpj)")
parser.add_argument("--output", None, argparse.STRING, 0, None, False,
argparse.STORE, "输出目录(覆盖 project.json)")
argparse.STORE, "输出目录(覆盖 project.vpj)")
parser.add_argument("--phase", None, argparse.STRING, 0, None, False,
argparse.STORE, "阶段: 1=生成声明, 2=翻译+编译, all=全部")
parser.add_argument("--cc", None, argparse.STRING, 0, None, False,
argparse.STORE, "LLVM 编译器命令(覆盖 project.json)")
argparse.STORE, "LLVM 编译器命令(覆盖 project.vpj)")
parser.add_argument("--clean", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "清理 output 和 temp 目录")
parser.add_argument("--run", None, argparse.BOOL, 0, None, False,
@@ -276,7 +276,7 @@ def main() -> int:
log.info(fb3, "phase")
# === Phase1: 扫描 includes(按需翻译)===
# 对 includes 目录中每个 .py 文件,若 stub 不存在则翻译并分离 stub
# 对 includes 目录中每个 .vp/.py 文件,若 stub 不存在则翻译并分离 stub
if do_phase1 != 0 and Config.IncludesDir is not None:
ph1_temp: str = Config.TempDir
if ph1_temp is None:
@@ -292,7 +292,7 @@ def main() -> int:
src_path: str = args.get_str("src")
# === 多文件项目模式 ===
# 当 --project 指定但 --src 未指定时,扫描 source_dir 下所有 .py 文件编译
# 当 --project 指定但 --src 未指定时,扫描 source_dir 下所有 .vp/.py 文件编译
if src_path is None and project is not None and Config.SourceDir is not None:
mf_temp: str = Config.TempDir if Config.TempDir is not None else "."
mf_output: str = Config.OutputDir if Config.OutputDir is not None else "."
@@ -404,12 +404,12 @@ def main() -> int:
log.banner("编译管线")
# module_name 用源代码内容的 SHA1(前 16 字符),与 Projectrans.py 一致
# output_name 从文件名推导(如 test.py -> test.exe),保持可读性
# output_name 从文件名推导(如 test.vp -> test.exe),保持可读性
module_name: str = Utils.compute_sha1(mb, src_buf)
if module_name is None:
module_name = "main"
output_name: str = "test_prog.exe"
# 从文件名提取 output_name(去掉路径和 .py 扩展名)
# 从文件名提取 output_name(去掉路径和 .vp/.py 扩展名)
src_len: t.CSizeT = string.strlen(src_path)
if src_len > 0:
# 找到最后一个 / 或 \ 之后的部分
@@ -423,7 +423,7 @@ def main() -> int:
break
if i == 0:
base_start = 0
# 找到 .py 扩展名
# 找到 .vp/.py 扩展名
base_end: t.CSizeT = src_len
j: t.CSizeT = base_start
while j < src_len:
+1 -2
View File
@@ -3,8 +3,7 @@
"name": "TransPyV",
"version": "1.0.0",
"source_dir": "./App",
"temp_dir": "./temp",
"output_dir": "./output",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-relocation-model=pic"]