fix(tpv): 修常量折成0致alloc(0)与多处fail-fast,并落地热点优化

TypeGenCDef: CDefine 表名深拷贝(vpi 注入名为零拷贝指针且 vpi_buf 提前释放,悬空名致常量查出错误值 0)

VpiReader: 模块级 SHA1_LEN(16) 改名 SHA1_HEX_LEN,消除与 Phase1.SHA1_LEN(17) 的跨模块同名冲突

CallGenCall: 实参翻译失败/漏传必需实参改 fail-fast,禁用零值兜底(堵住 SHA1_LEN 未解析变 alloc 0 的通路)

MatchGenMixin: 序列 match 的 case _ 分支补容量守卫(原先会越界写穿 pool 块、破坏空闲链)

Phase1Main/Phase1Scan: 预扫描缓冲分配失败改 fail-fast;加临时取证打点

CallGenBase: find_func_in_module 4 趟链表扫描合并为 2 趟

ImportsGenMixin: import 查找加哈希索引(键为 NUL 结尾副本,未命中回退原扫描)

TypeGenBase/TypeGenResolve: 库 typedef 按模块一次性门禁与缓存

VpiReader: vpi 注解解析补 rowptr[T] 分支
This commit is contained in:
TermiNexus committed 2026-09-13 17:50:03 +08:00
1 parent af2c66e7d7
commit 0dd7511a9b
11 files changed
+666 -136

No files matched your search

+100 -32
View File
@@ -437,6 +437,59 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
p1a_skp: int = 0 p1a_skp: int = 0
p1a_fl: int = 0 p1a_fl: int = 0
# ============================================================
# [临时取证打点] Phase1 早期扁平缓冲分配失败现场快照(定位后清理)。
# 现场:class_names_buf/parent_names_buf 均成功,def_sha1s_buf 却返回 NULL;
# 此时主池(1GiB)远未耗尽 → 非 OOM。本打点仅只读 mb 状态 + 打印,
# 绝不改变任何分配行为与逻辑。三维度判定:
# ① OOM:dump_stats 显示 used≈total、free≈0;
# ② mb 损坏:usable 基址/大小荒谬、self_check != 0、max_order 异常;
# ③ size/order 异常:本次请求 size 算出的 order > max_order。
# 用 VLogger.info(非终止;VLogger.error 会 sys.exit(1) 掩盖后续打点)。
# ============================================================
if mb is not None:
# 先做带环检测的 self_check:free list 被写坏成环时 dump_stats 的
# free_bytes/free_count 无步数上限会死循环(打点自身卡死),故
# self_check 返回 4(成环)时跳过 dump_stats。
p1_sc: int = mb.self_check()
if p1_sc != 4:
mb.dump_stats("phase1-pre")
else:
VLogger.info("[取证] self_check=4(free list 成环),跳过 dump_stats 防死循环", "Phase1")
p1_mo: int = mb.max_order
if p1_mo < 0 or p1_mo > 32:
p1_mo = 32 # 钳制:max_order 被写坏时防本打点自身越界读 free_lists
p1_fl_head: uint64 = uint64(mb.free_lists[p1_mo])
p1_mb_addr: uint64 = uint64(mb)
p1_base_addr: uint64 = uint64(mb.base)
p1_usable_addr: uint64 = uint64(mb.usable)
p1_chkbase_addr: uint64 = uint64(mb.chk_base)
VLogger.info(
f"[取证] mb={p1_mb_addr} base={p1_base_addr} usable={p1_usable_addr} "
f"usable_size={mb.usable_size} max_order={mb.max_order} self_check={p1_sc} "
f"detection={mb.detection}", "Phase1")
VLogger.info(
f"[取证] chk_base={p1_chkbase_addr} chk_depth={mb.chk_depth} "
f"chk_cap={mb.chk_cap} free_lists[clamp={p1_mo}]={p1_fl_head}",
"Phase1")
p1_sz_cn: sizet = sizet(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
p1_sz_ds: sizet = sizet(Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
p1_sz_tp: sizet = sizet(4 * Phase1.MAX_FILE_DEPS)
p1_sz_bx: sizet = sizet(4)
p1_o_cn: int = mb._order_for_size(p1_sz_cn + 16)
p1_o_ds: int = mb._order_for_size(p1_sz_ds + 16)
p1_o_tp: int = mb._order_for_size(p1_sz_tp + 16)
p1_o_bx: int = mb._order_for_size(p1_sz_bx + 16)
p1_fl_cn: uint64 = uint64(mb.free_lists[p1_o_cn])
p1_fl_ds: uint64 = uint64(mb.free_lists[p1_o_ds])
VLogger.info(
f"[取证] order(class_names {p1_sz_cn}B+16)={p1_o_cn} fl={p1_fl_cn} "
f"order(def_sha1s {p1_sz_ds}B+16)={p1_o_ds} fl={p1_fl_ds} "
f"order(topo {p1_sz_tp}B+16)={p1_o_tp} order(box {p1_sz_bx}B+16)={p1_o_bx} "
f"(fl==0 只说明该阶空、仍可由更高阶拆分;若 alloc 失败而 dump_stats 的 free>0 才是元数据损坏铁证,"
f"任一 order>max_order={mb.max_order} 则是 size 异常)",
"Phase1")
# 预扫描类继承关系,构建拓扑顺序(用扁平字节数组,避免结构体 str 指针 NULL 崩溃) # 预扫描类继承关系,构建拓扑顺序(用扁平字节数组,避免结构体 str 指针 NULL 崩溃)
class_names_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN) class_names_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
parent_names_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN) parent_names_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
@@ -451,42 +504,57 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
string.memset(generic_file_count_box, 0, 4) string.memset(generic_file_count_box, 0, 4)
topo_count: int = 0 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: # 三块扁平缓冲是 Phase1Scan._ScanClassInheritance 的契约前置缓冲:函数
string.memset(class_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN) # 入口已对 NULL 做 fail-fast 检查(本处分配失败若静默跳过整段,将退化为
string.memset(parent_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN) # 字母序并掩盖 OOM 等内存故障)。故分配失败时逐块显式报错终止(VLogger.error
string.memset(def_sha1s_buf, 0, Phase1.MAX_CLASSES * Phase1.SHA1_LEN) # 内部 sys.exit(1)),绝不静默回退。
string.memset(topo_order, 0, 4 * Phase1.MAX_FILE_DEPS) # 注:topo_order/topo_count_box 仅供后续 Phase1Topo._TopoSortFiles 使用,
string.memset(topo_count_box, 0, 4) # 不属 _ScanClassInheritance 契约,故不并入此处终止逻辑;其有效性由
if generic_file_indices is not None: # _TopoSortFiles 入口自身校验(out_order/out_count 为 NULL 即返回 -1 并终止)。
string.memset(generic_file_indices, 0, 4 * Phase1.MAX_FILE_DEPS) if class_names_buf is None:
if generic_file_count_box is not None: VLogger.error("类继承预扫描扁平缓冲分配失败:class_names_buf 为 NULL,终止编译", "Phase1")
string.memset(generic_file_count_box, 0, 4) if parent_names_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败:parent_names_buf 为 NULL,终止编译", "Phase1")
if def_sha1s_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败:def_sha1s_buf 为 NULL,终止编译", "Phase1")
class_count_scanned: int = Phase1Scan._ScanClassInheritance( string.memset(class_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
string.memset(parent_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
string.memset(def_sha1s_buf, 0, Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
if topo_order is not None:
string.memset(topo_order, 0, 4 * Phase1.MAX_FILE_DEPS)
if topo_count_box is not None:
string.memset(topo_count_box, 0, 4)
if generic_file_indices is not None:
string.memset(generic_file_indices, 0, 4 * Phase1.MAX_FILE_DEPS)
if generic_file_count_box is not None:
string.memset(generic_file_count_box, 0, 4)
class_count_scanned: int = Phase1Scan._ScanClassInheritance(
mb, result, reachable_set, reachable_count,
use_reachable, sha1_set, set_count,
class_names_buf, parent_names_buf, def_sha1s_buf,
generic_file_indices, generic_file_count_box)
if class_count_scanned > 0:
td_len_ts_call: sizet = string.strlen(temp_dir)
ret_topo: int = Phase1Topo._TopoSortFiles(
mb, result, reachable_set, reachable_count, mb, result, reachable_set, reachable_count,
use_reachable, sha1_set, set_count, use_reachable, sha1_set, set_count,
class_names_buf, parent_names_buf, def_sha1s_buf, class_names_buf, parent_names_buf, def_sha1s_buf, class_count_scanned,
generic_file_indices, generic_file_count_box) topo_order, topo_count_box,
temp_dir, td_len_ts_call)
if class_count_scanned > 0: if ret_topo == 0:
td_len_ts_call: sizet = string.strlen(temp_dir) tc_ptr: t.CPtr = (int | t.CPtr)(t.CVoid(topo_count_box, t.CPtr))
ret_topo: int = Phase1Topo._TopoSortFiles( if tc_ptr is not None:
mb, result, reachable_set, reachable_count, topo_count = tc_ptr[0]
use_reachable, sha1_set, set_count, VLogger.info(f"拓扑排序: {class_count_scanned} 个类, {topo_count} 个文件按依赖顺序处理", "Phase1")
class_names_buf, parent_names_buf, def_sha1s_buf, class_count_scanned,
topo_order, topo_count_box,
temp_dir, td_len_ts_call)
if ret_topo == 0:
tc_ptr: t.CPtr = (int | t.CPtr)(t.CVoid(topo_count_box, t.CPtr))
if tc_ptr is not None:
topo_count = tc_ptr[0]
VLogger.info(f"拓扑排序: {class_count_scanned} 个类, {topo_count} 个文件按依赖顺序处理", "Phase1")
else:
VLogger.error("拓扑排序失败(类继承依赖无法解析),终止编译", "Phase1")
return 1
else: else:
VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1") VLogger.error("拓扑排序失败(类继承依赖无法解析),终止编译", "Phase1")
topo_count = 0 return 1
else:
VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1")
topo_count = 0
# ============================================================ # ============================================================
# Phase 1a-pre-generic-all: mark1 前全量预注册 includes 泛型模板 # Phase 1a-pre-generic-all: mark1 前全量预注册 includes 泛型模板
+8 -1
View File
@@ -76,8 +76,15 @@ def _ScanClassInheritance(mb: rowptr[memhub.MemBuddy],
generic_file_indices: t.CPtr, generic_file_indices: t.CPtr,
generic_file_count_box: t.CPtr) -> int: generic_file_count_box: t.CPtr) -> int:
"""预扫描 includes 文件,收集类继承关系""" """预扫描 includes 文件,收集类继承关系"""
if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: if result is None:
return -1 return -1
# class_names_buf/parent_names_buf/def_sha1s_buf 由调用方 mb.alloc 分配
# (Phase1Main 预扫描前),本函数将其地址直接作为 strcpy 的目的缓冲
# (name_dst/parent_dst/sha1_dst)。分配失败返回 NULL 时目的指针为近零地址
# → strcpy 写零页(ASan access-violation)。按 fail-fast 规则显式报错终止
# (VLogger.error 内部 sys.exit(1)),不静默返回 -1 交由调用方宽容跳过。
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
VLogger.error("类继承预扫描扁平缓冲分配失败(class_names_buf/parent_names_buf/def_sha1s_buf 为 NULL)", "Phase1")
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__() entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
class_count: int = 0 class_count: int = 0
+33 -7
View File
@@ -904,6 +904,26 @@ def _resolve_single_vpi_type_str(pool: rowptr[memhub.MemBuddy],
# None 与 void 同义,归一到 Void。 # None 与 void 同义,归一到 Void。
if string.strcmp(seg, "None") == 0: if string.strcmp(seg, "None") == 0:
return llvmlite.Void(pool) return llvmlite.Void(pool)
# rowptr[T] 内建裸指针(等价 t.CPtr[T] → Ptr(T)):vpi 返回/参数注解按源码
# 原样保留 Subscript 形态(如 tokens.vp `new_token -> rowptr[Token]`)。
# 此前无此分支 → 整个注解解析返回 None → text.ll 尚未生成(合法时序:依赖
# 模块尚未翻译,占位 text.ll 为空)时跨模块调用 fail-fast 误报
# (includes/ast/lexer.vp new_token 根因)。与定义侧 TypeGenResolve
# resolve_annotation_type 的 rowptr 分支同口径:Ptr(resolve(T))。
if string.strncmp(seg, "rowptr[", 7) == 0:
seg_len_rp: sizet = string.strlen(seg)
if seg_len_rp > 8 and seg[seg_len_rp - 1] == ']':
inner_len_rp: sizet = seg_len_rp - 8
inner_buf_rp: bytes = stdlib.malloc(inner_len_rp + 1)
if inner_buf_rp is not None:
string.strncpy(inner_buf_rp, seg + 7, inner_len_rp)
inner_buf_rp[inner_len_rp] = '\0'
inner_ty_rp: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(
pool, inner_buf_rp)
stdlib.free(inner_buf_rp)
if inner_ty_rp is not None:
return llvmlite.Ptr(pool, inner_ty_rp)
return None
# t.XXX / c.XXX 内置类型映射 # t.XXX / c.XXX 内置类型映射
if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0: if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0:
attr_nm: str = seg + 2 attr_nm: str = seg + 2
@@ -1112,8 +1132,14 @@ def _resolve_vpi_ret_annot_str(pool: rowptr[memhub.MemBuddy],
# .vpi 读取缓冲区大小(256KB,与 PYI_BUF_SIZE 一致) # .vpi 读取缓冲区大小(256KB,与 PYI_BUF_SIZE 一致)
VPI_READ_BUF_SIZE: sizet = 262144 VPI_READ_BUF_SIZE: sizet = 262144
# SHA1 字符串长度(16 hex 字符) # SHA1 hex 字符数(16,仅文件名中的十六进制位,不含结尾 NUL)
SHA1_LEN: sizet = 16 #
# 注意:本常量原名 SHA1_LEN,与 lib.Phase.Phase1 的常量宏
# Phase1.SHA1_LEN: t.CDefine = 17(16 字符 + NUL 的存储步长)**同名但语义不同**。
# 同名跨模块符号会造成 CDefine 解析被遮蔽/串值(Phase1.MAX_CLASSES * Phase1.SHA1_LEN
# 被解析成 0 → mb.alloc(0) → 分配失败)。故改为唯一名 SHA1_HEX_LEN 消除冲突;
# 权威定义仍分别是:本处 16(hex 位数)、Phase1.SHA1_LEN 17(存储步长)。
SHA1_HEX_LEN: sizet = 16
# ============================================================ # ============================================================
@@ -1135,7 +1161,7 @@ def _extract_sha1_from_path(vpi_path: str) -> str:
return None return None
# 查找最后一个路径分隔符 / 或 \ # 查找最后一个路径分隔符 / 或 \
total_len: sizet = string.strlen(vpi_path) total_len: sizet = string.strlen(vpi_path)
if total_len < SHA1_LEN + 4: # 至少需要 "xxxx.vpi" 的长度 if total_len < SHA1_HEX_LEN + 4: # 至少需要 "xxxx.vpi" 的长度
return None return None
# 从后向前查找最后一个 / 或 \ # 从后向前查找最后一个 / 或 \
sep_pos: sizet = total_len sep_pos: sizet = total_len
@@ -1145,14 +1171,14 @@ def _extract_sha1_from_path(vpi_path: str) -> str:
if ch == '/' or ch == '\\': if ch == '/' or ch == '\\':
sep_pos += 1 sep_pos += 1
break break
if sep_pos + SHA1_LEN > total_len: if sep_pos + SHA1_HEX_LEN > total_len:
return None return None
# 分配缓冲区并复制 SHA1(16 字符 + null 终止符) # 分配缓冲区并复制 SHA1(16 字符 + null 终止符)
sha1_buf: str = stdlib.malloc(SHA1_LEN + 1) sha1_buf: str = stdlib.malloc(SHA1_HEX_LEN + 1)
if sha1_buf is None: if sha1_buf is None:
return None return None
string.strncpy(sha1_buf, vpi_path + sep_pos, SHA1_LEN) string.strncpy(sha1_buf, vpi_path + sep_pos, SHA1_HEX_LEN)
sha1_buf[SHA1_LEN] = '\0' sha1_buf[SHA1_HEX_LEN] = '\0'
return sha1_buf return sha1_buf
+55 -1
View File
@@ -1791,8 +1791,62 @@ class AssignGenMixin:
pass pass
if precise_rhs_ty is not None and alloca.Ty is not None: if precise_rhs_ty is not None and alloca.Ty is not None:
alloca_pt: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee alloca_pt: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
# 禁止「降级重建」:RHS 的 pointee 若为**裸泛型基类**(丢类型
# 实参,如 {sha1}.list / {sha1}.GSList),以它重建 alloca 会把后
# 注解已解析出的「带类型实参特化」(如 list[ast.ASTptr])覆盖成裸
# 基类 → 之后 .get() 按错误元素类型特化(list[int].get 返回 i32)
# → inttoptr 把 i32 当指针(截断)→ translate_stmt 读垃圾 AST 节
# 点崩溃。本重建本意是「注解退化成裸基类/i8*,RHS 构造出精确特化」
# 的**反向升级**(见上方注释),因此仅在 RHS 不是裸泛型基类时才执行。
# 现场根因:AST.children 字段注册为裸 list*(其注解
# list[rowptr[AST]] | t.CPtr 在字段注册期未解析出带实参特化),
# 故 `body_list: list[ast.AST | t.CPtr] | t.CPtr = mc.children`
# 的 RHS 静态类型退化为裸 list*,触发本降级重建 →
# MatchGenMixin._handle_renum_match 崩溃(IR 实证:
# %"body_list.1" = alloca %"….list"* 被使用,%body_list 特化 alloca
# 成死代码;.get 被分派到 list[int].get 返回 i32 → inttoptr 截断)。
# 裸泛型基类不可作为 receiver 静态类型,否则泛型方法分派必错。
# 取注解侧泛型基名(Base ∈ list/dict/...,仅 Name 形态的
# Subscript 一侧),用于下方**不依赖泛型模板注册表**的方向判定。
ann_gen_base: str = None
if annot is not None:
ag_side: ast.AST | t.CPtr = annot
if ag_side.kind() == ast.ASTKind.BinOp:
ag_bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(ag_side)
if ag_bop.op == ast.OpKind.BitOr:
if ag_bop.left is not None and ag_bop.left.kind() == ast.ASTKind.Subscript:
ag_side = ag_bop.left
elif ag_bop.right is not None and ag_bop.right.kind() == ast.ASTKind.Subscript:
ag_side = ag_bop.right
if ag_side.kind() == ast.ASTKind.Subscript:
ag_sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(ag_side)
if ag_sub.value is not None and ag_sub.value.kind() == ast.ASTKind.Name:
ag_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ag_sub.value)
if ag_nm.id is not None:
ann_gen_base = ag_nm.id
rb_is_bare_generic: int = 0
if alloca_pt is not None \
and uint64(alloca_pt) != uint64(precise_rhs_ty):
rhs_pt_rb: llvmlite.LLVMType | t.CPtr = precise_rhs_ty.Pointee
if rhs_pt_rb is not None:
rb_nm: str = llvmlite.get_struct_name(rhs_pt_rb)
if rb_nm is not None:
rb_pf: int = GenApi.sha1_prefix_len(rb_nm)
rb_short: str = rb_nm + rb_pf if rb_pf != 0 else rb_nm
if string.strchr(rb_short, '[') is None:
# ① 裸名命中泛型模板注册表(list/GSList/...)
if self.Gen.ClassGenGeneric._find_generic_template(rb_short) is not None:
rb_is_bare_generic = 1
# ② 与注解同基名:注解是 `Base[...]`(带类型实参,
# 已比 RHS 的裸 Base 精确)⇒ 本次重建方向是**降级**,
# 必须禁止。此判定不依赖泛型模板注册表(list/dict 等
# 内建容器未登记时①会漏判,②仍能命中)。
elif ann_gen_base is not None \
and string.strcmp(rb_short, ann_gen_base) == 0:
rb_is_bare_generic = 1
if alloca_pt is not None and \ if alloca_pt is not None and \
uint64(alloca_pt) != uint64(precise_rhs_ty): uint64(alloca_pt) != uint64(precise_rhs_ty) \
and rb_is_bare_generic == 0:
new_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry( new_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(
builder, precise_rhs_ty) builder, precise_rhs_ty)
if new_alloca is not None: if new_alloca is not None:
+37 -38
View File
@@ -522,37 +522,44 @@ class CallGenBase:
if mod is None or name is None: if mod is None or name is None:
return None return None
name_len: sizet = string.strlen(name) name_len: sizet = string.strlen(name)
# 热点优化:原实现把「精确匹配」+「本模块同名 mangled」+「后缀匹配
# define 优先」+「后缀匹配 declare 兜底」拆成 4 趟完整链表扫描,每趟
# 对每个节点调用一次 function_get_name/function_get_next(实测二者各
# ~1.046 亿次的热点来源)。合并为 2 趟,链表顺序与优先级语义不变:
# 趟 1:精确匹配(命中即返回,优先级最高,与原第一遍一致)+ 收集
# 首个本模块同名 mangled 候选(原"第一遍半",仅在无精确匹配
# 时使用);
# 趟 2:收集首个 IsDeclared==0 后缀命中(原第二遍优先)与首个任意
# 后缀命中(原第三遍兜底),扫描结束后按原优先级返回。
cur: llvmlite.Function | t.CPtr = mod.FuncHead cur: llvmlite.Function | t.CPtr = mod.FuncHead
# 第一遍:精确匹配 mangle_hit: llvmlite.Function | t.CPtr = None
while cur is not None: while cur is not None:
cur_name: str = llvmlite.function_get_name(cur) cur_name: str = llvmlite.function_get_name(cur)
if cur_name is not None: if cur_name is not None:
if string.strcmp(cur_name, name) == 0: if string.strcmp(cur_name, name) == 0:
return cur return cur
cur = llvmlite.function_get_next(cur) # 本模块同名候选:仅接受恰为 "{owner_sha1}.{name}" 的形态
# 第一遍半(owner_sha1 提供时):本模块同名函数优先。 # (mangled 模块级函数名含且只含一个 '.'),不匹配
# 仅接受恰为 "{owner_sha1}.{name}" 的形态(mangled 模块级函数名含 # "{sha1}.Class.method" 等方法名。是否 IsDeclared==0 均可返回:
# 且只含一个 '.'),不匹配 "{sha1}.Class.method" 等方法名。是否 # 前向 declare 与 define 同名同符号。只在 owner_sha1 提供且尚未
# IsDeclared==0 均可返回:前向 declare 与 define 同名同符号。 # 命中时计算,避免多做一次 strlen。
if owner_sha1 is not None: if owner_sha1 is not None and mangle_hit is None:
cur = mod.FuncHead cur_len_o: sizet = string.strlen(cur_name)
while cur is not None:
cur_name_o: str = llvmlite.function_get_name(cur)
if cur_name_o is not None:
cur_len_o: sizet = string.strlen(cur_name_o)
if cur_len_o == 17 + name_len: if cur_len_o == 17 + name_len:
if string.strncmp(cur_name_o, owner_sha1, 16) == 0 and cur_name_o[16] == '.': if string.strncmp(cur_name, owner_sha1, 16) == 0 and cur_name[16] == '.':
suffix_start_o: sizet = cur_len_o - name_len suffix_start_o: sizet = cur_len_o - name_len
if cur_name_o[suffix_start_o - 1] == '.': if cur_name[suffix_start_o - 1] == '.':
match_o: int = 1 match_o: int = 1
for i in range(name_len): for i in range(name_len):
if cur_name_o[suffix_start_o + i] != name[i]: if cur_name[suffix_start_o + i] != name[i]:
match_o = 0 match_o = 0
break break
if match_o == 1: if match_o == 1:
return cur mangle_hit = cur
cur = llvmlite.function_get_next(cur) cur = llvmlite.function_get_next(cur)
# 第二遍:后缀匹配(.name) if mangle_hit is not None:
return mangle_hit
# 后缀匹配(.name)
# 不跳过 declare:方法 stub 也是 declare,需要通过后缀匹配找到 # 不跳过 declare:方法 stub 也是 declare,需要通过后缀匹配找到
# 调用处(translate_call 等)负责判断 IsDeclared 并决定使用裸名还是 sha1 前缀名 # 调用处(translate_call 等)负责判断 IsDeclared 并决定使用裸名还是 sha1 前缀名
# #
@@ -562,6 +569,9 @@ class CallGenBase:
# 匹配按链表顺序先命中跨模块 declare → 参数按 Lexer* coerce → Parser* 被 # 匹配按链表顺序先命中跨模块 declare → 参数按 Lexer* coerce → Parser* 被
# bitcast 成 Lexer* → lexer 版 _advance 按 Lexer 布局操作 Parser 指针, # bitcast 成 Lexer* → lexer 版 _advance 按 Lexer 布局操作 Parser 指针,
# cur 永不推进 → parse 无限循环。本模块同名 define 必须优先。 # cur 永不推进 → parse 无限循环。本模块同名 define 必须优先。
# 无同名 define 时回退到 stub declare(跨模块方法/函数调用)。
declare_hit: llvmlite.Function | t.CPtr = None
any_hit: llvmlite.Function | t.CPtr = None
cur = mod.FuncHead cur = mod.FuncHead
while cur is not None: while cur is not None:
cur_name2: str = llvmlite.function_get_name(cur) cur_name2: str = llvmlite.function_get_name(cur)
@@ -576,27 +586,16 @@ class CallGenBase:
match = 0 match = 0
break break
if match == 1: if match == 1:
if cur.IsDeclared == 0: # 首个任意后缀命中(原第三遍兜底)
return cur if any_hit is None:
any_hit = cur
# 首个 IsDeclared==0 命中(原第二遍优先)
if cur.IsDeclared == 0 and declare_hit is None:
declare_hit = cur
cur = llvmlite.function_get_next(cur) cur = llvmlite.function_get_next(cur)
# 本模块无同名 define:回退到 stub declare(跨模块方法/函数调用) if declare_hit is not None:
cur = mod.FuncHead return declare_hit
while cur is not None: return any_hit
cur_name3: str = llvmlite.function_get_name(cur)
if cur_name3 is not None:
cur_len3: sizet = string.strlen(cur_name3)
if cur_len3 > name_len + 1:
suffix_start3: sizet = cur_len3 - name_len
if cur_name3[suffix_start3 - 1] == '.':
match3: int = 1
for i in range(name_len):
if cur_name3[suffix_start3 + i] != name[i]:
match3 = 0
break
if match3 == 1:
return cur
cur = llvmlite.function_get_next(cur)
return None
# ============================================================ # ============================================================
+50 -9
View File
@@ -1353,8 +1353,9 @@ class CallGenCall(CallGenBase.CallGenBase):
arg_count_eff: int = 0 arg_count_eff: int = 0
if kwc > 0 and found_func is not None: if kwc > 0 and found_func is not None:
# 关键字参数:遍历函数定义参数(跳过 self),位置参数优先,其余按名匹配 # 关键字参数:遍历函数定义参数(跳过 self),位置参数优先,其余按名匹配。
# 缺失参数用零值兜底,保持 call 参数数量与 define 一致 # 未提供的形参按类型补占位零值以保持参数序列位置(见下方 (b)/(c) 注释);
# 实参表达式翻译失败则 fail-fast,绝不静默零值替换。
cur_param_k: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) cur_param_k: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
if cur_param_k is not None: if cur_param_k is not None:
cur_param_k = llvmlite.param_get_next(cur_param_k) # 跳过 self cur_param_k = llvmlite.param_get_next(cur_param_k) # 跳过 self
@@ -1381,9 +1382,19 @@ class CallGenCall(CallGenBase.CallGenBase):
arg_val_k = self.Gen.ExprGen.translate_value( arg_val_k = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg_node_k, None, 0, trans, pty_k0) builder, pool, mod, arg_node_k, None, 0, trans, pty_k0)
if arg_val_k is None: if arg_val_k is None:
# 缺失参数:零值兜底(保持参数数量匹配)。 if arg_node_k is not None:
# kw 省略的形参可能有默认值(查不到默认表 ≠ 无默认值), # (a) 实参节点存在但表达式翻译失败(返回 None)→ fail-fast。
# 零值兜底与原逻辑一致,禁止 fail-fast 误杀。 # 禁止静默零值替换:那会把未解析实参(如跨模块同名冲突的
# Phase1.SHA1_LEN)伪造成合法实参。
self.Gen.TypeGenErr.fatal_error(
arg_node_k,
f"方法调用 '{class_name}.{method_name}' 第 {pi_k + 1} 个形参"
f"的实参表达式翻译失败(返回 None,禁止按类型零值兜底)",
"TypeError")
continue
# (b)/(c) 关键字/位置都未提供该形参:按形参类型补占位零值以保持
# 参数序列位置(避免后续实参左移错位)。此占位不是"表达式翻译
# 失败"的兜底(那条通路已在上方 fail-fast)。
pty_k: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param_k) pty_k: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param_k)
if pty_k is not None: if pty_k is not None:
arg_val_k = llvmlite.ConstZero(pool, pty_k) arg_val_k = llvmlite.ConstZero(pool, pty_k)
@@ -1403,6 +1414,13 @@ class CallGenCall(CallGenBase.CallGenBase):
for ai in range(can): for ai in range(can):
arg_node: ast.AST | t.CPtr = cargs.get(ai) arg_node: ast.AST | t.CPtr = cargs.get(ai)
if arg_node is None: if arg_node is None:
# (a) 实参节点缺失(AST 参数列表空洞)→ fail-fast,禁止静默跳过
# (跳过会减少 arg_count_eff,随后被"缺参默认填充"用零值补齐)。
self.Gen.TypeGenErr.fatal_error(
cl,
f"方法调用 '{class_name}.{method_name}' 第 {ai + 1} 个实参"
f"节点缺失(AST 参数列表空洞,无法翻译)",
"TypeError")
continue continue
# 先取形参类型:单引号单字符字面量(CONST_CHAR)传给 str/指针 # 先取形参类型:单引号单字符字面量(CONST_CHAR)传给 str/指针
# 形参时需物化 1 字符字符串(translate_constant 依据 target_ty 判定), # 形参时需物化 1 字符字符串(translate_constant 依据 target_ty 判定),
@@ -1422,6 +1440,16 @@ class CallGenCall(CallGenBase.CallGenBase):
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg_node, None, 0, trans, pty) builder, pool, mod, arg_node, None, 0, trans, pty)
if arg_val is None: if arg_val is None:
# (a) 实参表达式翻译失败(translate_value 返回 None)→ fail-fast。
# 禁止 continue 跳过:跳过会减少 arg_count_eff,随后"缺参默认
# 填充"用类型零值补齐该形参 —— 正是把未解析实参(如跨模块同名
# 冲突的 Phase1.SHA1_LEN)静默变成 alloc(..., 0) 的兜底通路。
# 用实参节点带出行号,报出被调用者与实参序号。
self.Gen.TypeGenErr.fatal_error(
arg_node,
f"方法调用 '{class_name}.{method_name}' 第 {ai + 1} 个实参"
f"表达式翻译失败(返回 None,禁止按类型零值兜底)",
"TypeError")
continue continue
# 按形参类型 coerce(self 是形参 0,第 ai 个实参对应形参 ai+1) # 按形参类型 coerce(self 是形参 0,第 ai 个实参对应形参 ai+1)
# 修复: CDefine/i32 常量传给 i64 形参(如 MemBuddy.__init__ 的 size: sizet) # 修复: CDefine/i32 常量传给 i64 形参(如 MemBuddy.__init__ 的 size: sizet)
@@ -1552,10 +1580,23 @@ class CallGenCall(CallGenBase.CallGenBase):
total_count += 1 total_count += 1
mdf_pi += 1 mdf_pi += 1
continue continue
# 无默认值时用零值兜底(需要 found_func 获取参数类型)。 # (c) 已知默认值区间(DefaultCount>0)且该形参序号落在区间之前
# 默认参数表按裸名注册(如 "__init__"),跨模块 "ClassName.method" # ⇒ 该形参确无默认值 ⇒ 调用点漏传必需实参。禁止零值兜底(历史
# 查找常查不到——查不到 ≠ 无默认值(如 Buf.__init__(data, capacity) # 兜底会把漏传实参静默变成 0),fail-fast 报出被调用者与形参序号。
# 缺的 length=0/owned=False 恰为零值),零值兜底语义一致。 if mdf_default_count > 0 and mdf_pi < mdf_offset:
self.Gen.TypeGenErr.fatal_error(
cl,
f"方法调用 '{lookup_name}' 漏传必需实参:第 {mdf_pi + 1} 个形参"
f"无默认值(调用点仅提供 {arg_count_eff} 个实参,"
f"该函数前 {mdf_offset} 个形参为必需)",
"TypeError")
mdf_pi += 1
continue
# (b) 其余缺参只能来自"默认值信息缺失"(默认参数表按裸名注册、
# 跨模块 "ClassName.method" 查找常查不到,vpi 也可能未登记:查不到
# ≠ 无默认值,故无法判定)。此时按声明形参类型补零,仅为对齐
# call/declare 的形参数量、填补尾随的声明可选参数;这**不是**
# "表达式翻译失败"的兜底 —— 那条通路已在实参装配处 fail-fast。
if found_func is not None: if found_func is not None:
param_node_z: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) param_node_z: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
skip_z: int = 0 skip_z: int = 0
+178
View File
@@ -3,6 +3,7 @@ import t, c
from stdint import * from stdint import *
import ast import ast
import memhub import memhub
import hashtable
import string import string
import stdlib import stdlib
import viperlib import viperlib
@@ -48,11 +49,132 @@ class ImportsGenMixin:
ReexportSrcs: bytes ReexportSrcs: bytes
ReexportCount: int ReexportCount: int
# --- 导入查找索引(把 O(串长) 的逐字符扫描降为 O(名字长) 哈希查找) ---
# imported_modules/_from_imports 是空格拼接的扁平串("a b c" / "n:m[:orig]"),
# 长度可达数千字符,而每个 Name 节点都会多次查询。索引与「当前串指针」绑定:
# 调用方查询的 trans._imported_modules/_from_imports 总是 add_* 刚生成的当前串,
# 故命中索引快路径;其余来源(stub 期 stdlib malloc 缓冲等)指针不匹配 → 回退
# 原逐字符扫描,语义完全不变。索引表用 PermPool 分配(跨文件/Phase 存活),由
# 对应 add_* 增量维护(非每文件重建);key/value 均是指向该扁平串内部的指针,
# 串在文件级 reset 前始终有效。
CurImportedModulesPtr: str
CurImportedModulesNames: hashtable.HashTable | t.CPtr
CurFromImportsPtr: str
CurFromImportsMods: hashtable.HashTable | t.CPtr
CurFromImportsOrigs: hashtable.HashTable | t.CPtr
# 注意:不声明 CurrentFile 字段——TypeGenMixin 已声明(Generator 展平块 # 注意:不声明 CurrentFile 字段——TypeGenMixin 已声明(Generator 展平块
# 只有一份,跨父类不去重规则下重复声明会导致字段错位)。本 Mixin 的 # 只有一份,跨父类不去重规则下重复声明会导致字段错位)。本 Mixin 的
# set_current_file 与 CurrentFile 读取统一经 HandlesType 委托(延迟 import # set_current_file 与 CurrentFile 读取统一经 HandlesType 委托(延迟 import
# 规避 HandlesType↔HandlesImports 循环依赖)。 # 规避 HandlesType↔HandlesImports 循环依赖)。
# ============================================================
# _index_imported_module - 增量维护 imported_modules 查找索引
#
# old_ptr 为追加前的串指针(None = 首个模块),new_ptr 为追加后的新串,
# name_ptr 指向新串内的新模块名(key 直接引用串内部,无需复制)。
# 仅当 old_ptr 恰为当前索引绑定的串时复用其索引(正常路径:add_* 始终以
# trans 的当前串为输入);否则放弃索引绑定并回退逐字符扫描(语义不变)。
# ============================================================
def _index_imported_module(self, old_ptr: str, new_ptr: str,
name_ptr: str) -> None:
"""增量更新 imported_modules 索引(复用旧索引或新建,失败则解除绑定)"""
if new_ptr is None or name_ptr is None:
return
perm: rowptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
if perm is None:
return
idx: hashtable.HashTable | t.CPtr = None
if old_ptr is None:
# 新文件/新链首:建全新索引(旧索引随旧串一起废弃)
idx = hashtable.HashTable(perm)
elif old_ptr == self.CurImportedModulesPtr and self.CurImportedModulesNames is not None:
# 正常路径:复用当前串的索引并追加(不重建,内存 O(导入数))
idx = self.CurImportedModulesNames
else:
# 旧串非当前索引串(异常来源):解除绑定,回退逐字符扫描
self.CurImportedModulesPtr = None
self.CurImportedModulesNames = None
return
if idx is None:
self.CurImportedModulesPtr = None
self.CurImportedModulesNames = None
return
# 同名已在(重复导入)则跳过;否则写入(key/value 均指向 new_ptr 内部)
if idx.__contains__(name_ptr) == 0:
idx.set_str(name_ptr, name_ptr)
self.CurImportedModulesPtr = new_ptr
self.CurImportedModulesNames = idx
# ============================================================
# _index_from_import - 增量维护 from_imports 查找索引
#
# entry_off 为新增 entry("local:module[:orig]")在新串内相对首字节的偏移。
# 模块表 key=local_name → 指向模块名切片;orig 表 key=local_name → 指向原始名
# 切片(无别名时写入 None,以"名存在但无原始名"区别于未命中)。
# 语义对齐逐字符扫描:精确名首次命中为准(不覆盖),star 模块名以最后一条为准。
# ============================================================
def _index_from_import(self, old_ptr: str, new_ptr: str, entry_off: sizet,
local_name: str, module_name: str,
original_name: str) -> None:
"""增量更新 from_imports 索引(复用旧索引或新建,失败则解除绑定)"""
if new_ptr is None or local_name is None or module_name is None:
return
perm: rowptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
if perm is None:
return
idx_m: hashtable.HashTable | t.CPtr = None
idx_o: hashtable.HashTable | t.CPtr = None
if old_ptr is None:
idx_m = hashtable.HashTable(perm)
idx_o = hashtable.HashTable(perm)
elif old_ptr == self.CurFromImportsPtr \
and self.CurFromImportsMods is not None \
and self.CurFromImportsOrigs is not None:
idx_m = self.CurFromImportsMods
idx_o = self.CurFromImportsOrigs
else:
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
return
if idx_m is None or idx_o is None:
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
return
local_len: sizet = string.strlen(local_name)
mod_len: sizet = string.strlen(module_name)
# 哈希键必须是 NUL 结尾串:扁平串内 local 段以 ':' 结尾(非 NUL),若直接把
# 串内 local 段指针当键,FNV 会哈希到 "local:module..." 整段,与查询名
# "local" 永不相等 → 索引恒 miss(_op_head NameError 根因)。故用 PermPool
# 分配 NUL 结尾的稳定副本作键(与索引表同寿命),值仍指向扁平串内模块名切片。
key_ptr: str = perm.alloc(local_len + 1)
if key_ptr is None:
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
return
string.strncpy(key_ptr, local_name, local_len)
key_ptr[local_len] = '\0'
mod_ptr: str = new_ptr + entry_off + local_len + 1
if local_len == 1 and local_name[0] == '*':
# star import:模块名以最后一条为准(覆盖旧值,对齐 star_mod 语义)
idx_m.set_str(key_ptr, mod_ptr)
else:
if idx_m.__contains__(key_ptr) == 0:
idx_m.set_str(key_ptr, mod_ptr)
if idx_o.__contains__(key_ptr) == 0:
if original_name is not None and string.strcmp(original_name, local_name) != 0 \
and string.strlen(original_name) > 0:
idx_o.set_str(key_ptr, mod_ptr + mod_len + 1)
else:
# 首次出现即无别名(或别名为空)→ 该名查询返回 None(与逐字符扫描一致)
idx_o.set_str(key_ptr, None)
self.CurFromImportsPtr = new_ptr
self.CurFromImportsMods = idx_m
self.CurFromImportsOrigs = idx_o
# ============================================================ # ============================================================
# 添加已导入模块名 # 添加已导入模块名
# ============================================================ # ============================================================
@@ -67,6 +189,8 @@ class ImportsGenMixin:
buf: str = pool.alloc(nlen + 1) buf: str = pool.alloc(nlen + 1)
if buf is not None: if buf is not None:
string.strcpy(buf, name) string.strcpy(buf, name)
# 索引:首个模块名(key 指向 buf 内部)
self._index_imported_module(None, buf, buf)
return buf return buf
return None return None
else: else:
@@ -78,6 +202,8 @@ class ImportsGenMixin:
string.strcpy(buf2, imported_modules) string.strcpy(buf2, imported_modules)
buf2[old_len] = ' ' buf2[old_len] = ' '
string.strcpy(buf2 + old_len + 1, name) string.strcpy(buf2 + old_len + 1, name)
# 索引:复用旧串索引并追加新名字(key 指向 buf2 内的 name 段)
self._index_imported_module(imported_modules, buf2, buf2 + old_len + 1)
return buf2 return buf2
return imported_modules return imported_modules
@@ -88,6 +214,14 @@ class ImportsGenMixin:
"""检查模块是否已导入(词边界精确匹配,避免子串误匹配)""" """检查模块是否已导入(词边界精确匹配,避免子串误匹配)"""
if name is None or imported_modules is None: if name is None or imported_modules is None:
return 0 return 0
# [索引快路径] 传入串即当前索引绑定的串(trans 字段的常态)→ O(名字长) 哈希查;
# 指针不匹配(stub 期 malloc 缓冲等)→ 回退下方逐字符扫描,语义不变。
if imported_modules == self.CurImportedModulesPtr \
and self.CurImportedModulesNames is not None:
if self.CurImportedModulesNames.__contains__(name) != 0:
return 1
# 索引未命中:绝不据此返回 0(索引不完整会误判"未导入"),
# 落到下方原逐字符扫描按真实串内容判定(正确性优先 | 性能次之)。
name_len: sizet = string.strlen(name) name_len: sizet = string.strlen(name)
cur: str = imported_modules cur: str = imported_modules
total_len: sizet = string.strlen(imported_modules) total_len: sizet = string.strlen(imported_modules)
@@ -175,6 +309,8 @@ class ImportsGenMixin:
else: else:
viperlib.snprintf(entry, 256, "%s:%s", local_name, module_name) viperlib.snprintf(entry, 256, "%s:%s", local_name, module_name)
if from_imports is None: if from_imports is None:
# 索引:首条 entry(local 从 entry 首字节开始,entry_off=0)
self._index_from_import(None, entry, 0, local_name, module_name, original_name)
return entry return entry
else: else:
old_len: sizet = string.strlen(from_imports) old_len: sizet = string.strlen(from_imports)
@@ -185,6 +321,9 @@ class ImportsGenMixin:
string.strcpy(buf, from_imports) string.strcpy(buf, from_imports)
buf[old_len] = ' ' buf[old_len] = ' '
string.strcpy(buf + old_len + 1, entry) string.strcpy(buf + old_len + 1, entry)
# 索引:复用旧串索引并追加(entry 从 buf[old_len+1] 开始)
self._index_from_import(from_imports, buf, old_len + 1,
local_name, module_name, original_name)
return buf return buf
return from_imports return from_imports
@@ -199,6 +338,15 @@ class ImportsGenMixin:
"""查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到""" """查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到"""
if local_name is None or from_imports is None: if local_name is None or from_imports is None:
return None return None
# [索引快路径] 传入串即当前索引绑定的串 → O(名字长) 哈希查;否则回退扫描。
# orig 表:名存在但存 None = 首次出现即无别名 → 返回 None(与扫描一致)。
if from_imports == self.CurFromImportsPtr and self.CurFromImportsOrigs is not None:
if self.CurFromImportsOrigs.__contains__(local_name) != 0:
v_o: t.CPtr = self.CurFromImportsOrigs.__getitem__(local_name)
if v_o is not None:
return (str | t.CPtr)(v_o)
# 索引未命中 | 值为 None(首个即无别名):绝不据此直接返回 None——
# 真实串可能仍含该名,落到下方原逐字符扫描按串内容判定(正确性优先)。
name_len: sizet = string.strlen(local_name) name_len: sizet = string.strlen(local_name)
cur: str = from_imports cur: str = from_imports
ci: sizet = 0 ci: sizet = 0
@@ -273,6 +421,22 @@ class ImportsGenMixin:
""" """
if name is None or from_imports is None: if name is None or from_imports is None:
return None return None
# [索引快路径] 传入串即当前索引绑定的串 → O(名字长) 哈希查;否则回退扫描。
# name 恰为 "*" 时与扫描一致:不做精确匹配,直接走 star 回退。
if from_imports == self.CurFromImportsPtr and self.CurFromImportsMods is not None:
name_is_star: int = 0
if string.strlen(name) == 1 and name[0] == '*':
name_is_star = 1
if name_is_star == 0:
v_f: t.CPtr = self.CurFromImportsMods.__getitem__(name)
if v_f is not None:
return (str | t.CPtr)(v_f)
if allow_star_fallback != 0:
sv_f: t.CPtr = self.CurFromImportsMods.__getitem__("*")
if sv_f is not None:
return (str | t.CPtr)(sv_f)
# 索引精确名 | star 均未命中:绝不据此直接返回 None(索引不完整会漏报
# "名已 import",_op_head NameError 即此类),落到下方原逐字符扫描按串内容判定。
name_len: sizet = string.strlen(name) name_len: sizet = string.strlen(name)
cur: str = from_imports cur: str = from_imports
ci: sizet = 0 ci: sizet = 0
@@ -527,6 +691,14 @@ class ImportsGenMixin:
# ============================================================ # ============================================================
def set_current_file(self, file_path: str) -> None: def set_current_file(self, file_path: str) -> None:
"""设置当前正在编译的文件名(与 HandlesType.set_current_file 同步调用)""" """设置当前正在编译的文件名(与 HandlesType.set_current_file 同步调用)"""
# 文件级失效:索引绑定的是上一文件 arena 内的串指针,本文件 reset 后该
# 地址可能被新分配复用(如 _merge_template_imports 的合并串)→ 必须解绑,
# 否则可能命中陈旧索引返回错误结果。本文件的索引由后续 add_* 重新建立。
self.CurImportedModulesPtr = None
self.CurImportedModulesNames = None
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
self.Gen.TypeGenResolve.set_current_file(file_path) self.Gen.TypeGenResolve.set_current_file(file_path)
# ============================================================ # ============================================================
@@ -834,4 +1006,10 @@ class ImportsGenMixin:
self.ReexportFuncs = None self.ReexportFuncs = None
self.ReexportSrcs = None self.ReexportSrcs = None
self.ReexportCount = 0 self.ReexportCount = 0
# 导入查找索引字段(索引表由 add_* 增量创建,PermPool 分配)
self.CurImportedModulesPtr = None
self.CurImportedModulesNames = None
self.CurFromImportsPtr = None
self.CurFromImportsMods = None
self.CurFromImportsOrigs = None
return 0 return 0
+12
View File
@@ -845,6 +845,18 @@ class MatchGenMixin:
first_check: int = -1 first_check: int = -1
i: int = 0 i: int = 0
while i < case_count: while i < case_count:
# 容量防线(与 _translate_match_simple / match-list 路径的
# `collected < MAX_MATCH_CASES` 守卫对齐):默认 case(case _)
# 分支此前**不校验** collected 就写 check_arr/body_arr/def_arr/
# nb_arr/guard_arr[collected],case 数超过 MAX_MATCH_CASES 时
# 越界写穿 pool 相邻块(可破坏相邻空闲块的 free-list next 指针)。
# 不静默丢弃 case(会产出漏 case 的错误代码),直接 fail-fast。
if collected >= MAX_MATCH_CASES:
self.Gen.TypeGenErr.fatal_error(
node,
f"match case 数量超过上限 MAX_MATCH_CASES={MAX_MATCH_CASES}",
"OverflowError")
break
case_node: ast.AST | t.CPtr = cases_list.get(i) case_node: ast.AST | t.CPtr = cases_list.get(i)
if case_node is None or case_node.kind() != ast.ASTKind.MatchCase: if case_node is None or case_node.kind() != ast.ASTKind.MatchCase:
i = i + 1 i = i + 1
+16 -2
View File
@@ -6,6 +6,7 @@ from stdint import *
import ast import ast
import llvmlite import llvmlite
import memhub import memhub
import hashtable
import string import string
import viperlib import viperlib
import stdio import stdio
@@ -93,7 +94,17 @@ class TypeGenBase:
# 用 malloc 而非 pool list——pool 每文件 reset_to 后 list 悬空(clear_ctypedefs # 用 malloc 而非 pool list——pool 每文件 reset_to 后 list 悬空(clear_ctypedefs
# 未覆盖的路径读到已释放内存 → strcmp 崩溃,1.log 根因);malloc 跨 pool # 未覆盖的路径读到已释放内存 → strcmp 崩溃,1.log 根因);malloc 跨 pool
# 生命周期存活,clear_ctypedefs 释放置空。 # 生命周期存活,clear_ctypedefs 释放置空。
ImportAliasBuf: bytes ImportAliasBuf: bytes
# --- 库 typedef 按模块一次性门禁(_lookup_library_typedef 性能优化) ---
# 库模块 typedef 缓存(PermPool 永久分配,跨文件/Phase 存活):
# LibTypedefScannedMods: key=库模块名 → 已扫描标记(命中=该模块源已读并入缓存);
# LibTypedefTypeMap: key="模块名|typedef 名" → 源中原始类型表达式。
# 原实现每次查询都读 64KB 库源全文扫描(每个 Name 注解兜底连试 stdint/
# w32.win32base 两次)→ 单文件 translate 7–12s 根因。缓存按模块名(内容)
# 索引,库源单次编译内不变,故命中结果与逐次读源等价。| 不用全局裸表变量。
LibTypedefScannedMods: hashtable.HashTable | t.CPtr
LibTypedefTypeMap: hashtable.HashTable | t.CPtr
# --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) --- # --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) ---
ConstEvalOk: int ConstEvalOk: int
@@ -137,7 +148,10 @@ class TypeGenBase:
self.LastBuiltinsTypedefResult = None self.LastBuiltinsTypedefResult = None
self.LastBuiltinsTypedefNameBuf = None self.LastBuiltinsTypedefNameBuf = None
self.LastBuiltinsTypedefResultBuf = None self.LastBuiltinsTypedefResultBuf = None
self.ImportAliasBuf = None self.ImportAliasBuf = None
# 库 typedef 持久缓存字段(不随 clear_ctypedefs 复位 → 跨文件/Phase 存活)
self.LibTypedefScannedMods = None
self.LibTypedefTypeMap = None
self.ConstEvalOk = 0 self.ConstEvalOk = 0
self.CurrentFile = None self.CurrentFile = None
self.Specs = None self.Specs = None
+34 -4
View File
@@ -34,13 +34,27 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
sys.maxsize = 9223372036854775807)必须全精度保存,否则 %d 32 位截断 sys.maxsize = 9223372036854775807)必须全精度保存,否则 %d 32 位截断
成 -1(0xFFFFFFFF)→ 读回 4294967295(SysLibTest maxsize FAIL 根因)。 成 -1(0xFFFFFFFF)→ 读回 4294967295(SysLibTest maxsize FAIL 根因)。
0xFFFFFFFF 仍存为 "4294967295",常量生成端 const_int32 内部截断回 -1。 0xFFFFFFFF 仍存为 "4294967295",常量生成端 const_int32 内部截断回 -1。
名称深拷贝到 pool(与 register_xmod_cdefine 对 key 深拷贝同一教训):
调用方传入的 name 可能指向临时缓冲——VpiReader 注入 .vpi 时 Name.id 是
零拷贝指向 vpi_buf(见 ParseVpiIntoModule 的 token 零拷贝注释),而
vpi_buf 在注入函数末尾即 stdlib.free。只存指针会让 CDefineNames 持有
悬空名:之后 lookup 的 strcmp 若命中被回收/复用内存里恰好出现的同名文本,
就会返回别的条目的值(Phase1.SHA1_LEN=17 被读成 0 的根因)。
""" """
if name is None:
return
if self.CDefineNames is None: if self.CDefineNames is None:
self.CDefineNames = list[str](pool, 64) self.CDefineNames = list[str](pool, 64)
self.CDefineValues = list[str](pool, 64) self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64) self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64) self.CDefineStrFlags = list[int](pool, 64)
self.CDefineNames.append(name) name_copy: str = pool.alloc(string.strlen(name) + 1)
if name_copy is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 名称缓冲区分配失败", "MemoryError")
return
string.strcpy(name_copy, name)
self.CDefineNames.append(name_copy)
val_buf: str = pool.alloc(32) val_buf: str = pool.alloc(32)
if val_buf is not None: if val_buf is not None:
viperlib.snprintf(val_buf, 32, "%lld", value) viperlib.snprintf(val_buf, 32, "%lld", value)
@@ -60,13 +74,23 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
"""注册浮点 CDefine 编译期常量(如 U_M_PI: t.CDefine = 3.14159...) """注册浮点 CDefine 编译期常量(如 U_M_PI: t.CDefine = 3.14159...)
值以 %f 文本存储并标记浮点,供 translate_value 返回浮点常量。 值以 %f 文本存储并标记浮点,供 translate_value 返回浮点常量。
名称同样深拷贝到 pool(理由见 register_cdefine_constant:注入 .vpi 时
Name.id 指向的 vpi_buf 会被释放,只存指针会留下悬空名)。
""" """
if name is None:
return
if self.CDefineNames is None: if self.CDefineNames is None:
self.CDefineNames = list[str](pool, 64) self.CDefineNames = list[str](pool, 64)
self.CDefineValues = list[str](pool, 64) self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64) self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64) self.CDefineStrFlags = list[int](pool, 64)
self.CDefineNames.append(name) name_copy_d: str = pool.alloc(string.strlen(name) + 1)
if name_copy_d is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 浮点 '{name}' 名称缓冲区分配失败", "MemoryError")
return
string.strcpy(name_copy_d, name)
self.CDefineNames.append(name_copy_d)
val_buf: str = pool.alloc(32) val_buf: str = pool.alloc(32)
if val_buf is not None: if val_buf is not None:
# 用 CRT stdio.snprintf 而非 viperlib.snprintf:viperlib 自实现格式化器的 # 用 CRT stdio.snprintf 而非 viperlib.snprintf:viperlib 自实现格式化器的
@@ -89,7 +113,8 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
"""注册字符串 CDefine 编译期常量(如 ZLIB_VERSION: t.CDefine = "1.3.2") """注册字符串 CDefine 编译期常量(如 ZLIB_VERSION: t.CDefine = "1.3.2")
字符串值深拷贝到 pool(AST 节点的 str_val 可能被 pool 操作覆写, 字符串值深拷贝到 pool(AST 节点的 str_val 可能被 pool 操作覆写,
与浮点 CDefine 的恢复同理)。 与浮点 CDefine 的恢复同理)。名称同样深拷贝(理由见
register_cdefine_constant:注入 .vpi 的 Name.id 指向会被释放的 vpi_buf)。
""" """
if pool is None or name is None: if pool is None or name is None:
return return
@@ -101,7 +126,12 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
self.CDefineValues = list[str](pool, 64) self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64) self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64) self.CDefineStrFlags = list[int](pool, 64)
self.CDefineNames.append(name) ncopy_s: str = pool.alloc(string.strlen(name) + 1)
if ncopy_s is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串 '{name}' 名称缓冲区分配失败", "MemoryError")
return
string.strcpy(ncopy_s, name)
self.CDefineNames.append(ncopy_s)
vlen: sizet = string.strlen(value) vlen: sizet = string.strlen(value)
vcopy: str = pool.alloc(vlen + 1) vcopy: str = pool.alloc(vlen + 1)
if vcopy is None: if vcopy is None:
+143 -42
View File
@@ -1,4 +1,5 @@
import lib.core.Gen.GenTypes as GenTypes import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Generator as Generator import lib.core.Gen.Generator as Generator
import lib.core.Gen.Type.TypeGenCType as TypeGenCType import lib.core.Gen.Type.TypeGenCType as TypeGenCType
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
@@ -7,6 +8,7 @@ from stdint import *
import ast import ast
import llvmlite import llvmlite
import memhub import memhub
import hashtable
import string import string
import viperlib import viperlib
import stdio import stdio
@@ -1054,6 +1056,83 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if b_final_r is not None: if b_final_r is not None:
return b_final_r return b_final_r
return None return None
def _register_lib_typedef_cache(self, mod: str, name_src: str,
name_len: sizet, type_str: str) -> None:
"""把库模块源中的一条 typedef 写入持久缓存(PermPool,同名首次为准)
key = "模块名|typedef 名"(模块名/typedef 名均为标识符,不含 '|')。
首次扫描某模块时对该模块全部 typedef 调用一次;结果跨文件/Phase 存活,
后续同模块查询只查表,不再读库源。
"""
if mod is None or name_src is None or type_str is None or name_len == 0:
return
perm: rowptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
if perm is None:
return
if self.LibTypedefTypeMap is None:
self.LibTypedefTypeMap = hashtable.HashTable(perm)
if self.LibTypedefTypeMap is None:
return
mod_len: sizet = string.strlen(mod)
key_len: sizet = mod_len + 1 + name_len
key_buf: str = perm.alloc(key_len + 1)
if key_buf is None:
return
string.strncpy(key_buf, mod, mod_len)
key_buf[mod_len] = '|'
string.strncpy(key_buf + mod_len + 1, name_src, name_len)
key_buf[key_len] = '\0'
# 同名首次为准(与逐次读源命中第一处声明的语义一致)
if self.LibTypedefTypeMap.__contains__(key_buf) != 0:
return
tlen: sizet = string.strlen(type_str)
type_copy: str = perm.alloc(tlen + 1)
if type_copy is None:
return
string.strcpy(type_copy, type_str)
self.LibTypedefTypeMap.set_str(key_buf, type_copy)
def _mark_lib_module_scanned(self, mod: str) -> None:
"""置库模块「已扫描」标记(PermPool 持久):其后同模块查询只查缓存表"""
if mod is None:
return
# 缓存表未建成(PermPool 分配失败)时不得置标记——否则后续查询只查空表
# 会误返回 None(改变语义);此时不标记、留待下次重试读源。
if self.LibTypedefTypeMap is None:
return
perm: rowptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
if perm is None:
return
if self.LibTypedefScannedMods is None:
self.LibTypedefScannedMods = hashtable.HashTable(perm)
if self.LibTypedefScannedMods is None:
return
if self.LibTypedefScannedMods.__contains__(mod) != 0:
return
mlen: sizet = string.strlen(mod)
mcopy: str = perm.alloc(mlen + 1)
if mcopy is None:
return
string.strcpy(mcopy, mod)
self.LibTypedefScannedMods.set_str(mcopy, mcopy)
def _lookup_lib_typedef_cache(self, mod: str, attr: str) -> str:
"""查持久缓存表:命中返回该模块源中的原始类型表达式,未命中返回 None"""
if self.LibTypedefTypeMap is None or mod is None or attr is None:
return None
mod_len: sizet = string.strlen(mod)
attr_len: sizet = string.strlen(attr)
key_len: sizet = mod_len + 1 + attr_len
key_buf: bytes = stdlib.malloc(key_len + 1)
if key_buf is None:
return None
string.strncpy(key_buf, mod, mod_len)
key_buf[mod_len] = '|'
string.strncpy(key_buf + mod_len + 1, attr, attr_len)
key_buf[key_len] = '\0'
v: t.CPtr = self.LibTypedefTypeMap.__getitem__(key_buf)
stdlib.free(key_buf)
if v is None:
return None
return (str | t.CPtr)(v)
def _lookup_library_typedef(self, pool: rowptr[memhub.MemBuddy], def _lookup_library_typedef(self, pool: rowptr[memhub.MemBuddy],
mod: str, attr: str) -> str: mod: str, attr: str) -> str:
"""查库模块源文件中 "attr: t.CTypedef = TYPE" 并注册,返回 TYPE 或 None """查库模块源文件中 "attr: t.CTypedef = TYPE" 并注册,返回 TYPE 或 None
@@ -1062,6 +1141,11 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
`size_t: t.CTypedef = stdint.size_t` 的 stdint.size_t 部分): `size_t: t.CTypedef = stdint.size_t` 的 stdint.size_t 部分):
直接读模块源扫描 typedef(权威定义),miss 不查表——表内可能是 直接读模块源扫描 typedef(权威定义),miss 不查表——表内可能是
builtins 的间接引用(stdint.size_t → 自身)会自引用死循环。 builtins 的间接引用(stdint.size_t → 自身)会自引用死循环。
[按模块一次性门禁] 首次遇到某库模块:读一次源、把该模块全部 typedef
写入持久缓存(PermPool)并置「已扫描」标记;之后同一模块的查询只查
缓存表,不再做文件 I/O 与全文扫描。缓存按模块名(内容)索引,库源在
单次编译内不变,故命中值与逐次读源等价。| miss 仍返回 None。
""" """
if mod is None or attr is None: if mod is None or attr is None:
return None return None
@@ -1071,6 +1155,14 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
m_real: str = self._resolve_import_alias(mod) m_real: str = self._resolve_import_alias(mod)
if m_real is None: if m_real is None:
m_real = mod m_real = mod
# [门禁命中] 该库模块源已扫描过 → 只查持久缓存表,不再读文件;
# 命中值仍注册进当前文件 CTypedef 表(保持原实现每次读源命中的副作用)。
if self.LibTypedefScannedMods is not None:
if self.LibTypedefScannedMods.__contains__(m_real) != 0:
ts_hit: str = self._lookup_lib_typedef_cache(m_real, attr)
if ts_hit is not None:
self.Gen.TypeGenCType.register_ctypedef(pool, attr, ts_hit)
return ts_hit
m_sha1: str = StubMergerStore.LookupSha1ByModName(m_real) m_sha1: str = StubMergerStore.LookupSha1ByModName(m_real)
m_path: str = self._build_lib_source_path(m_real, m_sha1) m_path: str = self._build_lib_source_path(m_real, m_sha1)
if m_path is None: if m_path is None:
@@ -1097,7 +1189,9 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
m_i: sizet = 0 m_i: sizet = 0
m_ls: sizet = 0 m_ls: sizet = 0
m_res: str = None m_res: str = None
while m_i <= m_total and m_res is None: # 全文件扫描(不再在首次命中处提前退出):既为该模块建立完整持久缓存,
# 又用与原实现相同的比较取 attr 的首次命中值(保证首次调用返回值不变)。
while m_i <= m_total:
if m_i == m_total or m_buf[m_i] == '\n': if m_i == m_total or m_buf[m_i] == '\n':
m_line_len: sizet = m_i - m_ls m_line_len: sizet = m_i - m_ls
if m_line_len > 0: if m_line_len > 0:
@@ -1115,39 +1209,46 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
m_nl: sizet = m_j m_nl: sizet = m_j
while m_nl > 0 and m_buf[m_ls + m_nl - 1] == ' ': while m_nl > 0 and m_buf[m_ls + m_nl - 1] == ' ':
m_nl -= 1 m_nl -= 1
if m_nl == attr_len: m_rest: str = m_buf + m_ls + m_j + 1
m_nm_ok: int = 1 if string.strstr(m_rest, "t.CTypedef") is not None:
m_ki: sizet = 0 m_eq: str = string.strstr(m_rest, "=")
while m_ki < attr_len: if m_eq is not None:
if m_buf[m_ls + m_ki] != attr[m_ki]: # '=' 相对行首偏移;值段严格限定在行尾
m_nm_ok = 0 # (m_line_len)内,防止跨行吞换行+后续内容
break m_eq_off: sizet = sizet(uint64(m_eq) - uint64(m_buf)) - m_ls
m_ki += 1 m_vs: sizet = m_eq_off + 1
if m_nm_ok == 1: while m_vs < m_line_len and m_buf[m_ls + m_vs] == ' ':
m_rest: str = m_buf + m_ls + m_j + 1 m_vs += 1
if string.strstr(m_rest, "t.CTypedef") is not None: m_ve: sizet = m_line_len
m_eq: str = string.strstr(m_rest, "=") while m_ve > m_vs and (m_buf[m_ls + m_ve - 1] == ' '
if m_eq is not None: or m_buf[m_ls + m_ve - 1] == '\r'):
# '=' 相对行首偏移;值段严格限定在行尾 m_ve -= 1
# (m_line_len)内,防止跨行吞换行+后续内容 if m_ve > m_vs:
m_eq_off: sizet = sizet(uint64(m_eq) - uint64(m_buf)) - m_ls m_vlen: sizet = m_ve - m_vs
m_vs: sizet = m_eq_off + 1 m_tb: str = pool.alloc(m_vlen + 1)
while m_vs < m_line_len and m_buf[m_ls + m_vs] == ' ': if m_tb is not None:
m_vs += 1 string.strncpy(m_tb, m_buf + m_ls + m_vs, m_vlen)
m_ve: sizet = m_line_len m_tb[m_vlen] = '\0'
while m_ve > m_vs and (m_buf[m_ls + m_ve - 1] == ' ' # 写入持久缓存(首次遇到的名字为准)
or m_buf[m_ls + m_ve - 1] == '\r'): self._register_lib_typedef_cache(
m_ve -= 1 m_real, m_buf + m_ls, m_nl, m_tb)
if m_ve > m_vs: # 原实现语义:attr 的首次匹配值
m_vlen: sizet = m_ve - m_vs if m_res is None and m_nl == attr_len:
m_tb: str = pool.alloc(m_vlen + 1) m_nm_ok: int = 1
if m_tb is not None: m_ki: sizet = 0
string.strncpy(m_tb, m_buf + m_ls + m_vs, m_vlen) while m_ki < attr_len:
m_tb[m_vlen] = '\0' if m_buf[m_ls + m_ki] != attr[m_ki]:
self.Gen.TypeGenCType.register_ctypedef(pool, attr, m_tb) m_nm_ok = 0
break
m_ki += 1
if m_nm_ok == 1:
m_res = m_tb m_res = m_tb
m_ls = m_i + 1 m_ls = m_i + 1
m_i += 1 m_i += 1
# 置该模块已扫描标记:同模块后续查询只查持久缓存表,不再重复读源
self._mark_lib_module_scanned(m_real)
if m_res is not None:
self.Gen.TypeGenCType.register_ctypedef(pool, attr, m_res)
stdlib.free(m_buf) stdlib.free(m_buf)
return m_res return m_res
def _expand_typedef_ref(self, pool: rowptr[memhub.MemBuddy], def _expand_typedef_ref(self, pool: rowptr[memhub.MemBuddy],
@@ -1625,15 +1726,15 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return self.extract_class_name_from_annotation( return self.extract_class_name_from_annotation(
sub.slice, imported_modules, pool, from_imports) sub.slice, imported_modules, pool, from_imports)
return None return None
# [D25 根因修复] 递归取 base 时必须把 `from_imports` 一并下传: # [D25 根因修复] 递归取 base 时必须把 `from_imports` 一并下传:
# 此前这里丢掉了第 4 个参数,导致同一份注解在「派发名(本函数)/ # 此前这里丢掉了第 4 个参数,导致同一份注解在「派发名(本函数)/
# 查表键(_resolve_generic_subscript_ty)/ 注册键(_specialize_generic_class)」 # 查表键(_resolve_generic_subscript_ty)/ 注册键(_specialize_generic_class)」
# 三处算出的 mangled 名可能不同 —— 例如 `list[ast.AST | t.CPtr]` 的 # 三处算出的 mangled 名可能不同 —— 例如 `list[ast.AST | t.CPtr]` 的
# 元素段在带/不带 from_imports 时规范化结果不一致,进而出现 # 元素段在带/不带 from_imports 时规范化结果不一致,进而出现
# 「alloca 是裸 list*、pass1 按 list[ast.ASTptr] 解释、pass2 按 # 「alloca 是裸 list*、pass1 按 list[ast.ASTptr] 解释、pass2 按
# list[ast.ClassDefptr] 解释」的分裂(MatchGenMixin._handle_renum_match # list[ast.ClassDefptr] 解释」的分裂(MatchGenMixin._handle_renum_match
# 崩溃根因,见 Debugger/_Type.md D25)。其余调用点(1625、1650 等)均已传。 # 崩溃根因,见 Debugger/_Type.md D25)。其余调用点(1625、1650 等)均已传。
base_name: str = self.extract_class_name_from_annotation( base_name: str = self.extract_class_name_from_annotation(
sub.value, imported_modules, pool, from_imports) sub.value, imported_modules, pool, from_imports)
if base_name is None: if base_name is None:
return None return None
@@ -2202,8 +2303,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# SHA1 前缀形态优先用完整名 "{sha1}.{Class}" 精确命中定义侧 # SHA1 前缀形态优先用完整名 "{sha1}.{Class}" 精确命中定义侧
# struct(get_struct_type 类名回退可能命中跨模块同名类) # struct(get_struct_type 类名回退可能命中跨模块同名类)
full_attr_ty: llvmlite.LLVMType | t.CPtr = None full_attr_ty: llvmlite.LLVMType | t.CPtr = None
# val_name.id 是模块名段:恰为 16 位小写 hex 才是 SHA1 别名 # val_name.id 是模块名段:恰为 16 位小写 hex 才是 SHA1 别名
#(判定走唯一真源 GenApi.is_sha1_hex16,C10 收敛)。 #(判定走唯一真源 GenApi.is_sha1_hex16,C10 收敛)。
if is_builtin_attr == 0 and GenApi.is_sha1_hex16(val_name.id) != 0: 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) full_q: str = self._concat_sha1_class(pool, val_name.id, at.attr)
if full_q is not None: if full_q is not None: