refactor(tpv): SHA1 前缀判定收敛到 GenApi 唯一真源(C10/D3 收尾)

- CallGenCall: list 泛型前缀剥离(719) 与类型名提 sha1(907/1190) 改 GenApi.sha1_prefix_len,不再盲取前 16 字节当 sha1

- CallGenCBuiltin._asm_name_is_mangled / CallGenLib._is_sha1_mangled_call: 改真源(去大写 A-F、去扫描任意位置 '.')

- CallGenFd: 类型名提 sha1 加前缀格式校验(裸名不再误取前 16 字节)

- CallGenMixin: CExport declare 前缀判定改真源

- StructGenMixin / TypeGenResolve: 类型名、模块段判定改真源;StructGenMixin 补漏掉的 GenApi import(L596/L943 引用原会 fail-fast)
This commit is contained in:
TermiNexus committed 2026-09-12 12:59:01 +08:00
1 parent 471bedbcf4
commit 7e68e76ec4
7 files changed
+56 -65

No files matched your search

+5 -15
View File
@@ -1017,10 +1017,12 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
def _asm_name_is_mangled(self, name: str) -> int:
"""判断 LLVM 名称是否带 SHA1 混淆头(16 hex + '.'),返回 1=是
"""判断 LLVM 名称是否带 SHA1 混淆头("{16位小写hex}."),返回 1=是
兼容 make_global_ref 生成的 '@"sha1.name"' 引用名和 GlobalVariable
裸名 "sha1.name"。'@name' 裸引用与局部 SSA(%N)都不算混淆。
判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:不再接受大写 hex,
也不再扫描任意位置的 '.' 后回看 16 字节,避免 "a.b..." 形态误判)。
"""
if name is None:
return 0
@@ -1029,22 +1031,10 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
# 去掉 '@"..."' 包装(引用名)
if nlen >= 3 and n[0] == '@' and n[1] == '"' and n[nlen - 1] == '"':
n = n + 2
nlen -= 3
elif nlen >= 1 and n[0] == '@':
n = n + 1
nlen -= 1
if nlen <= 17:
return 0
for i in range(16, nlen):
if n[i] == '.':
ok: int = 1
for j in range(16):
cc: char = n[i - 16 + j]
if not (('0' <= cc <= '9') or ('a' <= cc <= 'f') or ('A' <= cc <= 'F')):
ok = 0
break
if ok != 0:
return 1
if GenApi.sha1_prefix_len(n) != 0:
return 1
return 0
+16 -11
View File
@@ -714,11 +714,12 @@ class CallGenCall(CallGenBase.CallGenBase):
# 保证 get/append 等 list 内置方法一律命中、绝不进入 MRO 解析。
if method_name is not None and class_name is not None:
lb_cls: str = class_name
# 剥离跨模块特化名 sha1 前缀 "{16hex}."({sha}.list[...] 形态)
if string.strncmp(lb_cls, "list", 4) != 0 \
and string.strlen(lb_cls) > 17 and lb_cls[16] == '.':
if string.strncmp(lb_cls + 17, "list", 4) == 0:
lb_cls = lb_cls + 17
# 剥离跨模块特化名 sha1 前缀 "{16hex}."({sha}.list[...] 形态);
# 前缀长度判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛)。
lb_pfx_d: int = GenApi.sha1_prefix_len(lb_cls)
if string.strncmp(lb_cls, "list", 4) != 0 and lb_pfx_d != 0:
if string.strncmp(lb_cls + lb_pfx_d, "list", 4) == 0:
lb_cls = lb_cls + lb_pfx_d
if self.Gen.CallGenProp._is_list_builtin_method(lb_cls, method_name) != 0:
# 双指针槽形态(rowptr[list[...]] 局部变量 alloca Ptr(Ptr(Struct))
# 直接传入时先 load 一层得到对象指针 Ptr(Struct),与分派方
@@ -903,8 +904,10 @@ class CallGenCall(CallGenBase.CallGenBase):
match mc_struct_ty:
case llvmlite.LLVMType.Struct(_fs_sp, _fc_sp, type_name_sp):
if type_name_sp is not None and type_name_sp[0] != '\0':
tl_sp: sizet = string.strlen(type_name_sp)
if tl_sp >= 16:
# 仅当类型名确为 "{16hex}.{Class}" 才提取 sha1;
# 判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛),
# 避免把裸类名前 16 字节误当 sha1。
if GenApi.sha1_prefix_len(type_name_sp) != 0:
sb_sp: str = pool.alloc(17)
if sb_sp is not None:
string.strncpy(sb_sp, type_name_sp, 16)
@@ -1186,8 +1189,9 @@ class CallGenCall(CallGenBase.CallGenBase):
match mc_struct_ty:
case llvmlite.LLVMType.Struct(_fields_st, _fcount_st, type_name_st):
if type_name_st is not None and type_name_st[0] != '\0':
type_len_st: sizet = string.strlen(type_name_st)
if type_len_st >= 16:
# 仅当类型名确为 "{16hex}.{Class}" 才提取 sha1;
# 判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛)。
if GenApi.sha1_prefix_len(type_name_st) != 0:
sha1_from_ty: str = pool.alloc(17)
if sha1_from_ty is not None:
string.strncpy(sha1_from_ty, type_name_st, 16)
@@ -1203,8 +1207,9 @@ class CallGenCall(CallGenBase.CallGenBase):
viperlib.snprintf(mangled_anc, ml_anc, "%s.%s.%s",
ret_anc_sha1, ret_anc_name, method_name)
call_name = mangled_anc
# 类型名提取的 sha1 无法保证为真实模块 sha1(跨模块 stub 类型名
# 带前缀但 find_struct 未注册时仍可用),此处不校验格式
# 类型名提取的 sha1 已按 "{16hex}." 前缀格式校验(sha1_prefix_len);
# 不校验它是否为真实已注册模块 sha1(跨模块 stub 类型名带前缀但
# find_struct 未注册时仍可用)
elif ret_anc_name is not None and ret_anc_sha1 is None:
# fail-fast:方法所属类既查不到 SHA1 又从类型名提取失败,
# 禁止静默裸名(否则链接期 undefined symbol 难以定位)
+12 -8
View File
@@ -7,6 +7,7 @@ import memhub
import string
import viperlib
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenApi as GenApi
from . import CallGenBase
@@ -176,14 +177,17 @@ class CallGenFd(CallGenBase.CallGenBase):
# 为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标
# 路径而非 list 内联 __getitem__(printf %s 解引用字符值崩溃)。
# 从定义模块 text.ll 读取精确返回类型(对齐普通方法调用 _translate_method_call)。
fd_sha1: str = pool.alloc(17)
if fd_sha1 is not None:
string.strncpy(fd_sha1, fd_fname, 16)
fd_sha1[16] = '\0'
fd_exact: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
pool, fd_sha1, fd_cls, method_name)
if fd_exact is not None:
fd_ret_ty = fd_exact
# 仅当函数名确为 "{16hex}.{func}" 才提取模块 sha1(判定走唯一真源
# GenApi.sha1_prefix_len,C10 收敛),避免把裸名前 16 字节误当 sha1。
if GenApi.sha1_prefix_len(fd_fname) != 0:
fd_sha1: str = pool.alloc(17)
if fd_sha1 is not None:
string.strncpy(fd_sha1, fd_fname, 16)
fd_sha1[16] = '\0'
fd_exact: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
pool, fd_sha1, fd_cls, method_name)
if fd_exact is not None:
fd_ret_ty = fd_exact
fd_total: int = 1 + fd_can
return llvmlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
# ============================================================
+6 -14
View File
@@ -7,6 +7,7 @@ import string
import stdlib
import lib.core.Gen.Translator as HT
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.core.Gen.GenApi as GenApi
from . import CallGenBase
@@ -636,26 +637,17 @@ class CallGenLib(CallGenBase.CallGenBase):
return None
def _is_sha1_mangled_call(self, call_name: str) -> int:
"""判断 call_name 是否为跨模块 Viper 函数("{16位hex}.func" 形式)
"""判断 call_name 是否为跨模块 Viper 函数("{16位小写hex}.func" 形式)
C 库函数(strlen 等裸名)和 Win32 API(VirtualAlloc 等裸名)返回 0,
不参与精确返回类型解析(走 _infer_external_func_ret_ty 白名单)。
判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:不再接受大写 hex)。
"""
if call_name is None:
return 0
first_dot: str = string.strchr(call_name, '.')
if first_dot is None:
return 0
pre_len: sizet = sizet(uint64(first_dot) - uint64(call_name))
if pre_len != 16:
return 0
for hx in range(16):
ch: int = call_name[hx]
if ch < 48 or ch > 57:
if ch < 97 or ch > 102:
if ch < 65 or ch > 70:
return 0
return 1
if GenApi.sha1_prefix_len(call_name) != 0:
return 1
return 0
def _emit_llvm_memcpy_intrinsic(self, pool: rowptr[memhub.MemBuddy],
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
+9 -10
View File
@@ -1254,16 +1254,15 @@ class CallGenMixin(CallGenBase.CallGenBase):
# (该分支仅 found_func None 的模块限定路径),这里对带前缀
# 的 declare 提取 sha1 再查 CExport 注册表,命中则改用裸名。
if found_func.IsDeclared != 0:
dot_pos_cx: str = string.strchr(found_name, 46)
if dot_pos_cx is not None:
cx_pre_len: sizet = string.strlen(found_name) - string.strlen(dot_pos_cx)
if cx_pre_len == 16:
cx_sha1_buf: str = pool.alloc(17)
if cx_sha1_buf is not None:
string.memcpy(cx_sha1_buf, found_name, 16)
cx_sha1_buf[16] = '\0'
if self.Gen.CallGenBase.is_cexport_func(cx_sha1_buf, func_name) != 0:
call_name = func_name
# 判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:此前只判
# 首个 '.' 前恰 16 字节、不校验 hex 形态)。
if GenApi.sha1_prefix_len(found_name) != 0:
cx_sha1_buf: str = pool.alloc(17)
if cx_sha1_buf is not None:
string.memcpy(cx_sha1_buf, found_name, 16)
cx_sha1_buf[16] = '\0'
if self.Gen.CallGenBase.is_cexport_func(cx_sha1_buf, func_name) != 0:
call_name = func_name
else:
# 跨模块函数调用:found_func 为 None 时,检查是否是模块限定调用
# 如 string.strchr(...) → @{sha1_of_string}.strchr
+5 -6
View File
@@ -14,6 +14,7 @@ import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.Type.TypeSpec as TSpec
import lib.core.Gen.GenApi as GenApi
# ============================================================
# StructGenMixin - 结构体注册/字段查找 Mixin(对象化 P2-2,wiki/14-oo-refactor.md)
@@ -797,13 +798,11 @@ class StructGenMixin:
# "Thread") 命中 vthreading 的 entry → 其 arg@3 被当成
# sched.Thread.arg(应 4)→ th.func 被 th.arg=None 覆盖 →
# _thread_entry call 0 → #UD/#GP)。strncmp 不分配临时缓冲。
# 前缀判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:此前只判
# 首个 '.' 前恰 16 字节、不校验 hex 形态)。
ty_has_sha1_lf: int = 0
dot_ty_lf: str = string.strchr(ty_name, '.')
if dot_ty_lf is not None:
pre_len_lf: sizet = sizet(
uint64(dot_ty_lf) - uint64(ty_name))
if pre_len_lf == 16:
ty_has_sha1_lf = 1
if GenApi.sha1_prefix_len(ty_name) != 0:
ty_has_sha1_lf = 1
for i in range(self.StructCount):
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
if entry is not None and entry.Name is not None:
+3 -1
View File
@@ -2144,7 +2144,9 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# SHA1 前缀形态优先用完整名 "{sha1}.{Class}" 精确命中定义侧
# struct(get_struct_type 类名回退可能命中跨模块同名类)
full_attr_ty: llvmlite.LLVMType | t.CPtr = None
if is_builtin_attr == 0 and string.strlen(val_name.id) == 16:
# val_name.id 是模块名段:恰为 16 位小写 hex 才是 SHA1 别名
#(判定走唯一真源 GenApi.is_sha1_hex16,C10 收敛)。
if is_builtin_attr == 0 and GenApi.is_sha1_hex16(val_name.id) != 0:
full_q: str = self._concat_sha1_class(pool, val_name.id, at.attr)
if full_q is not None:
full_attr_ty = self.Gen.StructGen.get_struct_type(full_q)