fix(tpv): ASan 运行库改由 project.vpj 声明; read_all 容量语义修正与超限源文件 fail-fast

- --asan: 移除 BuildPipeline 中硬编码的 clang64 libclang_rt 注入, 改由工程在 linker.static_libs / dynamic_libs / flags 中自行声明; 新增 Config.asan_runtime_declared(), main.vp 在未声明时 fail-fast 并列出所需运行库, 不再给含混的 undefined reference
- read_all: 容量参数含终止符(最多读 cap-1 字节), 故删除 12 处恒不可达的 else 截断兜底; 并在读取前按 f.size() fail-fast 拒绝超限源文件(main.vp / BuildPipeline / Phase1Main / Phase1Scan / Phase2), 避免静默截断后报出莫名其妙的语法错误
- project.vpj(根 / Test / NegativeTest / Sha1Test): linker.cmd 由裸 clang++ 改为绝对路径并补 -fuse-ld=lld
This commit is contained in:
TermiNexus committed 2026-09-10 22:33:18 +08:00
1 parent 76f03b5987
commit a5260f4441
18 files changed
+119 -97

No files matched your search

+16 -23
View File
@@ -36,7 +36,8 @@ import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy | t.CPtr
# ASan 模式(--asan):对 .ll 先 opt -passes=asan 插桩,链接加 -fsanitize=address
# ASan 模式(--asan):对 .ll 先 opt -passes=asan 插桩;ASan 运行库由
# project.vpj 的 linker.static_libs / dynamic_libs 显式声明(TPV 不注入)
_asan_mode: bool = 0
# --phase2command xxx.bat:不执行 llc/链接命令,只把 Phase B/B+/C 的
@@ -84,6 +85,12 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
if file_path is None: return None
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
if f.closed: return None
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被**静默截断**;
# 这里先按文件大小拒绝, 否则拿截断后的源码去 parse 只会得到莫名其妙的语法错误
if f.size() >= SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "fileio")
f.close()
return None
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
if src_buf is None:
f.close()
@@ -95,8 +102,6 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
return None
if bytes_read < SRC_BUF_SIZE:
src_buf[bytes_read] = 0
else:
src_buf[SRC_BUF_SIZE - 1] = 0
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
ast._lexer_init(lx, src_buf, mb)
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
@@ -760,18 +765,13 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
# 注意: .obj 文件必须在 -l 库标志之前,否则链接器无法解析符号依赖
# linker_static_libs / linker_dynamic_libs(project.vpj 显式指定的 .a/.obj/.dll/.so)
# 同样位于 -l 之前;动态库作为输入文件由 GNU ld 提取导入符号
# ASan 模式:mingw64 clang 的 -fsanitize=address 在 mingw64/lib 下找不到
# asan 运行库(实际在 clang64),改为显式链接 clang64 的 libclang_rt
# (DLL 版,运行时需 D:/msys64/clang64/bin 在 PATH)
# ASan 模式:ASan 运行库**不再**由 TPV 硬编码。clang 没有给
# x86_64-w64-windows-gnu 出 ASan 运行库,只能借用 clang64(MSVC ABI) 的
# libclang_rt,但那是工程级选择(且会牵进 MSVC 专有的 .drectve 指令),
# 不该由所有工程无条件承担 —— 改由 project.vpj 的
# linker.static_libs / linker.dynamic_libs 显式声明,main.vp 在 --asan
# 下会用 Config.asan_runtime_declared 校验是否声明过。
use_flags: str = linker_flags
if _asan_mode != 0:
fl_len: t.CSizeT = len(linker_flags) + 256
use_flags = _mbuddy.alloc(fl_len)
if use_flags is None:
return 1
viperlib.snprintf(use_flags, fl_len,
"%s -L D:/msys64/clang64/lib/clang/22/lib/windows -l:libclang_rt.asan_dynamic-x86_64.dll.a -l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a",
linker_flags)
static_libs: str = linker_static_libs
if static_libs is None:
static_libs = ""
@@ -1009,16 +1009,9 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT,
extra_len = collect_obj_files(includes_binary_dir, extra_objs, EXTRA_BUF_SIZE)
# 构造命令: clang++ obj_paths extra_objs -o linker_output linker_flags
# ASan 模式:显式链接 clang64 的 libclang_rt(同 link_obj_to_exe)
# ASan 运行库同样不再由 TPV 注入(在 link_obj_to_exe 里已有说明):
# 由 project.vpj 声明,--asan 下由 main.vp 校验。
use_flags: str = linker_flags
if _asan_mode != 0:
fl_len: t.CSizeT = len(linker_flags) + 256
use_flags = _mbuddy.alloc(fl_len)
if use_flags is None:
return 1
viperlib.snprintf(use_flags, fl_len,
"%s -L D:/msys64/clang64/lib/clang/22/lib/windows -l:libclang_rt.asan_dynamic-x86_64.dll.a -l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a",
linker_flags)
static_libs2: str = linker_static_libs
if static_libs2 is None:
static_libs2 = ""
+20 -8
View File
@@ -567,6 +567,13 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_fl += 1
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f_r.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_r}", "Phase1")
f_r.close()
p1a_fl += 1
continue
src_buf_r: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf_r is None:
f_r.close()
@@ -581,8 +588,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
continue
if bytes_read_r < Phase1.SRC_BUF_SIZE:
src_buf_r[bytes_read_r] = 0
else:
src_buf_r[Phase1.SRC_BUF_SIZE - 1] = 0
# 解析 AST
lx_r: ast.Lexer | t.CPtr = ast.new_lexer(mb)
@@ -718,6 +723,12 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 无法打开源文件", "Phase1")
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f_pv.size() >= Phase1.SRC_BUF_SIZE:
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限", "Phase1")
f_pv.close()
continue
src_buf_pv: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf_pv is None:
f_pv.close()
@@ -733,8 +744,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
continue
if bytes_read_pv < Phase1.SRC_BUF_SIZE:
src_buf_pv[bytes_read_pv] = 0
else:
src_buf_pv[Phase1.SRC_BUF_SIZE - 1] = 0
lx_pv: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if lx_pv is None:
stdlib.free(src_buf_pv)
@@ -848,8 +857,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
if vrd_1b > 0:
if vrd_1b < 1048576:
vbuf_1b[vrd_1b] = 0
else:
vbuf_1b[1048576 - 1] = 0
vtotal_1b: t.CSizeT = string.strlen(vbuf_1b)
vpos_1b: t.CSizeT = 0
while vpos_1b < vtotal_1b:
@@ -913,6 +920,13 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
failed += 1
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
f.close()
failed += 1
continue
src_buf: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf is None:
f.close()
@@ -928,8 +942,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
continue
if bytes_read < Phase1.SRC_BUF_SIZE:
src_buf[bytes_read] = 0
else:
src_buf[Phase1.SRC_BUF_SIZE - 1] = 0
# 解析 AST
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
+11 -4
View File
@@ -111,6 +111,12 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
if f.closed:
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
f.close()
continue
src_buf: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf is None:
f.close()
@@ -123,8 +129,6 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
continue
if bytes_read < Phase1.SRC_BUF_SIZE:
src_buf[bytes_read] = 0
else:
src_buf[Phase1.SRC_BUF_SIZE - 1] = 0
# 解析 AST
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
@@ -293,6 +297,11 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
f_pg: fileio.File | t.CPtr = fileio.File(file_path_pg, fileio.MODE.R)
if f_pg.closed:
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_pg}", "Phase1")
f_pg.close()
continue
src_buf_pg: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf_pg is None:
f_pg.close()
@@ -304,8 +313,6 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
continue
if bytes_read_pg < Phase1.SRC_BUF_SIZE:
src_buf_pg[bytes_read_pg] = 0
else:
src_buf_pg[Phase1.SRC_BUF_SIZE - 1] = 0
# 文本扫描筛选:非泛型文件跳过(不 parse、不驻留,防 OOM)
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0:
stdlib.free(src_buf_pg)
+18 -14
View File
@@ -291,6 +291,11 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
f_pa: fileio.File | t.CPtr = fileio.File(ent_pa.Path, fileio.MODE.R)
if f_pa.closed:
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f_pa.size() >= SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {SRC_BUF_SIZE} 字节上限, 拒绝读取: {ent_pa.Path}", "PhaseA")
f_pa.close()
continue
src_buf_pa: bytes = stdlib.malloc(SRC_BUF_SIZE)
if src_buf_pa is None:
f_pa.close()
@@ -302,8 +307,6 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
continue
if bytes_read_pa < SRC_BUF_SIZE:
src_buf_pa[bytes_read_pa] = 0
else:
src_buf_pa[SRC_BUF_SIZE - 1] = 0
# 解析 AST(只解析,不完整翻译;用永久池使模板节点跨 mark2 reset 存活)
perm_pa: memhub.MemBuddy | t.CPtr = CompilerFactory.factory.PermPool
if perm_pa is None:
@@ -500,13 +503,13 @@ def _PhaseADumpNode(temp_dir: str, td_len: t.CSizeT, sha1: str,
if not df.closed:
deps = stdlib.malloc(PA_DEPS_MAX_STR)
if deps is not None:
rl: t.CInt64T = df.read_all(deps, PA_DEPS_MAX_STR - 1)
rl: t.CInt64T = df.read_all(deps, PA_DEPS_MAX_STR)
if rl < 0:
rl = 0
if rl >= PA_DEPS_MAX_STR - 1:
deps[PA_DEPS_MAX_STR - 1] = 0
else:
deps[rl] = 0
# read_all 的容量参数就是缓冲区容量(含终止符): 传真实容量
# MAX 时最多读 MAX-1 字节, 故 rl 恒 <= MAX-1, 在界内直接写
# 终止符即可(原来传 MAX-1 白扔一个字节, 且还有个死分支)
deps[rl] = 0
df.close()
stdlib.free(dpath)
if deps is None or deps[0] == 0:
@@ -633,14 +636,13 @@ def _PhaseADumpMroPending(temp_dir: str, td_len: t.CSizeT, sha1: str) -> None:
if buf is None:
rf.close()
return
n: t.CInt64T = rf.read_all(buf, 8191)
# read_all 的容量参数就是缓冲区容量(含终止符), 传真实容量 8192 而不是 8191
n: t.CInt64T = rf.read_all(buf, 8192)
rf.close()
if n < 0:
n = 0
if n < 8191:
buf[n] = 0
else:
buf[8191] = 0
nsz: t.CSizeT = t.CSizeT(n)
pos: t.CSizeT = 0
hits: int = 0
@@ -930,6 +932,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | 无法打开源文件", "PhaseA")
continue
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
if f_vp2.size() >= SRC_BUF_SIZE:
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | 源文件超过 {SRC_BUF_SIZE} 字节上限", "PhaseA")
f_vp2.close()
continue
src_buf_vp2: bytes = stdlib.malloc(SRC_BUF_SIZE)
if src_buf_vp2 is None:
f_vp2.close()
@@ -945,8 +953,6 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
continue
if bytes_read_vp2 < SRC_BUF_SIZE:
src_buf_vp2[bytes_read_vp2] = 0
else:
src_buf_vp2[SRC_BUF_SIZE - 1] = 0
lx_vp2: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if lx_vp2 is None:
stdlib.free(src_buf_vp2)
@@ -1743,8 +1749,6 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
if tbuf_mi is not None and 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 行即为实现文件
# (不再要求行中含 @",因为 memhub 等模块的函数名
# 如 @ca40915f11b8b6ad._align_up 不带引号但仍是实现文件)
+26
View File
@@ -641,6 +641,32 @@ def get_includes_binary_dir() -> str:
return buf
def asan_runtime_declared() -> int:
"""project.vpj 是否已声明 ASan 运行库(1 = 声明过)
TPV 不再硬编码 clang64 的 libclang_rt 路径:clang 没有给
x86_64-w64-windows-gnu 目标出 ASan 运行库,只能借用 clang64(MSVC ABI)
的那两份(会牵进 MSVC 专有的 .drectve 链接指令),这是工程级选择,
必须由工程自己声明。
三种写法都认(和 openssl 那种「写在 flags 里」的写法兼容):
* linker.static_libs 里的 .a / .dll.a
* linker.dynamic_libs 里的 .dll / .so
* linker.flags 里的 -L… -l:…(如 openssl 的 -lssl -lcrypto)
判定方式:以上任一处出现 "asan" 即视为已声明。
"""
if LinkerStaticLibs is not None:
if string.strstr(LinkerStaticLibs, "asan") is not None:
return 1
if LinkerDynamicLibs is not None:
if string.strstr(LinkerDynamicLibs, "asan") is not None:
return 1
if LinkerFlags is not None:
if string.strstr(LinkerFlags, "asan") is not None:
return 1
return 0
def print_config():
"""打印当前配置"""
if ProjectName is not None:
-2
View File
@@ -143,8 +143,6 @@ def read_file_to_buf(path: str, buf_size: t.CSizeT) -> bytes:
return None
if br < buf_size:
buf[br] = 0
else:
buf[buf_size - 1] = 0
return buf
-8
View File
@@ -652,8 +652,6 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int:
if bytes_read > 0:
if bytes_read < VPI_READ_BUF_SIZE:
vpi_buf[bytes_read] = 0
else:
vpi_buf[VPI_READ_BUF_SIZE - 1] = 0
_cache_vpi_return_annots_text(vpi_buf, src_sha1)
stdlib.free(vpi_buf)
stdlib.free(vpi_path)
@@ -712,8 +710,6 @@ def _ensure_vpi_param_annot_loaded(src_sha1: str, temp_dir: str) -> int:
if bytes_read > 0:
if bytes_read < VPI_READ_BUF_SIZE:
vpi_buf[bytes_read] = 0
else:
vpi_buf[VPI_READ_BUF_SIZE - 1] = 0
_cache_vpi_param_annots_text(vpi_buf, src_sha1)
stdlib.free(vpi_buf)
stdlib.free(vpi_path)
@@ -1316,8 +1312,6 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy | t.CPtr,
_vpi_typedef_visited_mark(sha1_r)
if bytes_read_r < VPI_READ_BUF_SIZE:
vpi_buf_r[bytes_read_r] = 0
else:
vpi_buf_r[VPI_READ_BUF_SIZE - 1] = 0
lx_r: ast.Lexer | t.CPtr = ast.new_lexer(pool)
if lx_r is not None:
ast._lexer_init(lx_r, vpi_buf_r, pool)
@@ -1463,8 +1457,6 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
return 0
if bytes_read < VPI_READ_BUF_SIZE:
vpi_buf[bytes_read] = 0
else:
vpi_buf[VPI_READ_BUF_SIZE - 1] = 0
# 2. 解析为 AST
lx: ast.Lexer | t.CPtr = ast.new_lexer(pool)
@@ -302,8 +302,6 @@ def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
return out_pos
if dep_br_ls < StubMerger.STUB_READ_BUF_SIZE:
dep_buf[dep_br_ls] = '\0'
else:
dep_buf[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
# 注释掉依赖 stub 中的字符串常量行(模块内部的,不需要)
# 简单方案:搜索 "= external unnamed_addr constant" 并将行首的 @ 改为 ;@
@@ -499,8 +497,6 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
return out_pos
if text_br < StubMerger.STUB_READ_BUF_SIZE:
dep_buf[text_br] = '\0'
else:
dep_buf[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
dep_pos_lt: t.CSizeT = 0
in_body: int = 0
@@ -1303,8 +1299,6 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
return 0
if stub_br < StubMerger.STUB_READ_BUF_SIZE:
stub_content[stub_br] = '\0'
else:
stub_content[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
slen: t.CSizeT = stub_br
# 修复本地 stub.ll 中字符串常量的非法 IR:
# llvmlite OUTPUT_STUB 模式输出 "= external unnamed_addr constant" 的非法形式。
@@ -1361,8 +1355,6 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
if deps_br > 0:
if deps_br < 4096:
deps_buf[deps_br] = '\0'
else:
deps_buf[4095] = '\0'
deps_loaded = 1
elif deps_br == 0:
# 空 deps.txt:无 import 的包标记文件(如 lib/core/Gen/__init__.vp、
@@ -1536,8 +1528,6 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
if text_br > 0:
if text_br < StubMerger.STUB_READ_BUF_SIZE:
text_content[text_br] = '\0'
else:
text_content[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
# 行级去重追加(与 _LoadAndAppendStub 相同逻辑)
tx_pos: t.CSizeT = 0
tx_skip: int = 1
-2
View File
@@ -65,8 +65,6 @@ def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int:
return 1
if tbr_d < StubMerger.STUB_READ_BUF_SIZE:
tbuf_d[tbr_d] = '\0'
else:
tbuf_d[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
# 逐行扫描:找 "define " 行即为实现文件
tpos_d: t.CSizeT = 0
while tpos_d < tbr_d:
-2
View File
@@ -242,8 +242,6 @@ class AssignGenMixin:
return 0
if nrd < BUF_SZ:
buf[nrd] = 0
else:
buf[BUF_SZ - 1] = 0
name_len: t.CSizeT = string.strlen(name)
total_len: t.CSizeT = string.strlen(buf)
-2
View File
@@ -205,8 +205,6 @@ class CallGenBase:
return None
if nrd < READ_SZ:
buf[nrd] = 0
else:
buf[READ_SZ - 1] = 0
name_len: t.CSizeT = string.strlen(func_name)
# 模块自含定义标志:vpi 中行首 "def {func_name}(" 命中且未被同名
# from-import 覆盖 → 该模块自身定义 func_name,不是 re-export 源。
-4
View File
@@ -325,8 +325,6 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
else:
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
# vpi(存根签名文件)中的 CDefine 声明可能只带名字不带值
# (Converter 旧版只对 CONST_INT/BOOL 等输出 "= value",CONST_FLOAT
# 等不输出 → numpy 的 pi/e 在 stub 中无值),真正的值在源
@@ -402,8 +400,6 @@ class ExprGenCdef(ExprGenBase.ExprGenBase):
return -1
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
# 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
# 格式: NAME: t.CDefine = value
-4
View File
@@ -901,8 +901,6 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return None
if b_read < LONG(BUF_B):
b_buf[t.CSizeT(b_read)] = 0
else:
b_buf[BUF_B - 1] = 0
# 逐行扫描并注册 builtins 全部 "NAME: t.CTypedef = TYPE"(一次读文件
# 注册全部,置 scanned 标记后同文件后续查表即中;对齐
# register_ctypedef_from_source 的文本匹配策略)。已存在的表条目
@@ -1079,8 +1077,6 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return None
if m_read < LONG(BUF_L):
m_buf[t.CSizeT(m_read)] = 0
else:
m_buf[BUF_L - 1] = 0
attr_len: t.CSizeT = string.strlen(attr)
m_total: t.CSizeT = string.strlen(m_buf)
m_i: t.CSizeT = 0
+20 -6
View File
@@ -142,7 +142,7 @@ def main() -> int:
parser.add_argument("--run", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Run the generated executable after compilation")
parser.add_argument("--rebuild-includes", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Delete the includes.binary precompiled cache and rebuild all the includes")
parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Clear .transpyc_cache global cache")
parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "ASan mode: .ll first opt -passes=asan then link -fsanitize=address")
parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "ASan mode: .ll first opt -passes=asan; ASan runtime libs must be declared in project.vpj (linker.static_libs)")
parser.add_argument("--phase2command", None, argparse.STRING, 0, None, False, argparse.STORE, "Do not run llc/link: write Phase2 llc(opt+llc) and clang link commands to the given .bat path")
parser.add_argument("--nowarn-register", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Suppress Phase A dependency WARN")
parser.add_argument("--detection", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Enable MemBuddy allocation detection (0xCDCD markers + record-table probe for out-of-bounds writes; applies to both mb and perm_pool)")
@@ -220,7 +220,7 @@ Target: %s
if args.get_bool("asan"):
BuildPipeline._asan_mode = True
Config._inc_bin_suffix = ".binary_asan"
log.info("ASan 模式已启用(opt -passes=asan + libclang_rt)", "asan")
log.info("ASan 模式已启用(opt -passes=asan 插桩; 运行库需 project.vpj 声明)", "asan")
# ASan 模式清理 asan includes 缓存(强制重建 asan 插桩 obj)
mf_inc_bin_asan: str = Config.get_includes_binary_dir()
if mf_inc_bin_asan is not None:
@@ -291,6 +291,17 @@ Target: %s
Config.resolve_paths(proj_dir)
Config.print_config()
# --asan 要求工程自己在 project.vpj 里声明 ASan 运行库(TPV 不再硬编码
# clang64 的 libclang_rt 路径):clang 没有给 x86_64-w64-windows-gnu 出
# ASan 运行库,只能借用 clang64(MSVC ABI) 的那两份,是否承担这个选择
# 属于工程自身的事,故在此 fail-fast 而不是给个含混的 undefined reference。
if BuildPipeline._asan_mode != 0 and Config.asan_runtime_declared() == 0:
log.error("启用 --asan 需要在 project.vpj 的 linker.static_libs 里声明 ASan 运行库:", "asan")
log.error(" D:/msys64/clang64/lib/clang/22/lib/windows/libclang_rt.asan_dynamic-x86_64.dll.a", "asan")
log.error(" D:/msys64/clang64/lib/clang/22/lib/windows/libclang_rt.asan_dynamic_runtime_thunk-x86_64.a", "asan")
log.error("运行时还需要 PATH 里有 D:/msys64/clang64/bin(libclang_rt.asan_dynamic-x86_64.dll)", "asan")
return 1
# === --clean: 清理 temp、output 和 includes.binary 目录 ===
if _gb_clean:
log.info("清理临时目录...", "clean")
@@ -407,6 +418,13 @@ Target: %s
log.error(f"无法打开文件: {src_path}", "fileio")
argparse.release(args)
return 1
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被**静默截断**;
# 这里先按文件大小拒绝, 否则拿截断后的源码去 parse 只会得到莫名其妙的语法错误
if f.size() >= SRC_BUF_SIZE:
log.error(f"源文件超过 {SRC_BUF_SIZE} 字节上限, 拒绝读取: {src_path}", "fileio")
f.close()
argparse.release(args)
return 1
# 分配源代码缓冲区
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
if src_buf is None:
@@ -424,10 +442,6 @@ Target: %s
# 添加 null 终止符,确保 strlen 和 SHA1 计算正确
if bytes_read < SRC_BUF_SIZE:
src_buf[bytes_read] = 0
else:
src_buf[SRC_BUF_SIZE - 1] = 0
log.info("文件读取完成", "fileio")
log.info(f"读取 {bytes_read} 字节", "fileio")
# 解析 AST(拆分为词法+语法两阶段,便于调试)
ast._init_tables(mb)
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
+2 -2
View File
@@ -9,8 +9,8 @@
"flags": ["-filetype=obj", "-relocation-model=pic"]
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition"],
"cmd": "D:/msys64/mingw64/bin/clang++.exe",
"flags": ["-fuse-ld=lld", "-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition"],
"output": "neg_app.exe"
},
"includes": [
+2 -2
View File
@@ -8,8 +8,8 @@
"flags": ["-filetype=obj", "-relocation-model=pic"]
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition"],
"cmd": "D:/msys64/mingw64/bin/clang++.exe",
"flags": ["-fuse-ld=lld", "-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition"],
"output": "sha1test.exe"
},
"includes": [
+2 -2
View File
@@ -9,8 +9,8 @@
"flags": ["-filetype=obj", "-relocation-model=pic"]
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition"],
"cmd": "D:/msys64/mingw64/bin/clang++.exe",
"flags": ["-fuse-ld=lld", "-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition"],
"output": "app.exe"
},
"includes": [
+2 -2
View File
@@ -9,8 +9,8 @@
"flags": ["-filetype=obj", "-relocation-model=pic"]
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition", "-Wl,--stack,0x1000000", "-Wl,--no-insert-timestamp"],
"cmd": "D:/msys64/mingw64/bin/clang++.exe",
"flags": ["-fuse-ld=lld", "-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition", "-Wl,--stack,0x1000000", "-Wl,--no-insert-timestamp"],
"output": "TransPyV.exe"
},
"includes": [