docs(tpv): R11 注释与 docstring 英文化并压缩到 2 行内
按 wiki/新规范:注释只讲作用、不掺杂历史原因、避免超长、尽可能英文(含三引号 __doc__)。全文 79 个 .vp:# 注释 16269 行(中文 13808)与 docstring(中文 1381 处)全部改英文;超 2 行的注释块(2093)与 docstring(397)全部压到 2 行。运行时可见文本未动(fatal_error/VLogger 的中文报错文案、stdio.printf 的横幅格式串)。校验:屏蔽注释与三引号后逐行比对各文件代码骨架,0 漂移。验证:自举重建 153.48s 通过(跨模块内联完成);TPV 顺序回归 77/77 全通过。
This commit is contained in:
1 parent
a2874a14e6
commit
0f4aeeceff
79 files changed
+10090
-20352
No files matched your search
+211
-493
File diff suppressed because it is too large.
Load diff
+142
-217
@@ -30,21 +30,17 @@ import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _InjectedCreatesCycle - 容器模块自动注入前的破环检查
|
||||
#
|
||||
# 注入 _list/_dict 前检查被注入模块自身的依赖是否已含当前模块,命中(如 memhub↔_list)则跳过注入——
|
||||
# 否则 Phase A 多遍收敛死锁("无法完成翻译");去掉环边后 _list 仍因当前模块被其依赖而处于闭包,list 类仍由 Phase1b 注册。
|
||||
# ============================================================
|
||||
# _InjectedCreatesCycle - cycle check before auto-injecting a container module
|
||||
# (_list/_dict); a hit (e.g. memhub<->_list) skips injection to avoid a Phase A deadlock.
|
||||
def _InjectedCreatesCycle(entry: rawptr[IncludesScanner.FileEntry], mod_name: str) -> int:
|
||||
"""注入 mod_name 到当前模块前检查是否成环,返回 1=成环需跳过注入"""
|
||||
"""Return 1 if injecting mod_name into the current module would create a cycle"""
|
||||
if entry is None or entry.RelPath is None or mod_name is None: return 0
|
||||
inj_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
|
||||
if inj_sha1 is None: return 0
|
||||
inj_deps: str = StubMergerStore.LookupIncludesDeps(inj_sha1)
|
||||
if inj_deps is None: return 0
|
||||
rp: str = entry.RelPath
|
||||
# 当前模块名 = RelPath 基名(最后 '/' 或 '\\' 之后)去扩展名
|
||||
# Current module name = basename of RelPath (after the last '/' or '\'), without extension
|
||||
base: str = string.strrchr(rp, '/')
|
||||
if base is None: base = string.strrchr(rp, '\\')
|
||||
if base is not None: base = base + 1
|
||||
@@ -76,7 +72,7 @@ def _InjectedCreatesCycle(entry: rawptr[IncludesScanner.FileEntry], mod_name: st
|
||||
|
||||
|
||||
def _DepsContainsName(deps: str, name: str) -> int:
|
||||
"""deps 字符串(空格分隔模块名)是否含 name token(词边界精确匹配)"""
|
||||
"""Return 1 if the space-separated deps string contains the name token exactly"""
|
||||
if deps is None or name is None: return 0
|
||||
nl: sizet = string.strlen(name)
|
||||
dl: sizet = string.strlen(deps)
|
||||
@@ -91,7 +87,7 @@ def _DepsContainsName(deps: str, name: str) -> int:
|
||||
|
||||
|
||||
def _DepsRemoveName(deps: str, name: str) -> int:
|
||||
"""从 deps 字符串移除 name token(原地重建,返回 1=有改动)"""
|
||||
"""Remove the name token from the deps string in place; returns 1 if changed"""
|
||||
if deps is None or name is None: return 0
|
||||
nl: sizet = string.strlen(name)
|
||||
dl: sizet = string.strlen(deps)
|
||||
@@ -119,7 +115,7 @@ def _DepsRemoveName(deps: str, name: str) -> int:
|
||||
|
||||
|
||||
def _RewriteDepsTxt(temp_dir: str, sha1: str, deps_str: str) -> None:
|
||||
"""重写 {temp_dir}/{sha1前2位}/{sha1}.deps.txt(内存破环后同步磁盘)"""
|
||||
"""Rewrite the on-disk deps.txt file to match the in-memory deps after breaking a cycle"""
|
||||
if temp_dir is None or sha1 is None or deps_str is None: return
|
||||
td_len: sizet = string.strlen(temp_dir)
|
||||
dpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "deps.txt")
|
||||
@@ -133,17 +129,7 @@ def _RewriteDepsTxt(temp_dir: str, sha1: str, deps_str: str) -> None:
|
||||
|
||||
|
||||
def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
|
||||
"""扫描后统一清理 _list/_dict 注入环边。
|
||||
|
||||
Phase1a-pre 全量扫描用 FindFirstFileA(文件系统序),注入 _list/_dict 时
|
||||
被注入模块(_list/_dict)自身的 deps 未必已注册 → _InjectedCreatesCycle
|
||||
内联检查可能漏判(如 _list 显式 import memhub,而 memhub 的 list[str]
|
||||
注入 _list 未被拦截)→ memhub↔_list 2-环 → Phase A 永不收敛。此处扫描
|
||||
结束后全量检查:凡某模块 M 的 deps 含 _list/_dict,且 _list/_dict 的 deps
|
||||
含 M → 从 M 的 deps 移除注入边(list 类仍由 Phase1b 经闭包注册)。
|
||||
内存破环后同步重写 {temp_dir}/deps.txt,保证磁盘与内存一致(Phase2 依赖
|
||||
图诊断回退读 deps.txt)。
|
||||
"""
|
||||
"""Remove _list/_dict injection cycle edges after scanning and sync deps.txt"""
|
||||
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: return 0
|
||||
cnt: sizet = StubMerger._includes_deps_count
|
||||
ci: sizet
|
||||
@@ -152,7 +138,7 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
|
||||
deps_m: str = StubMerger._includes_deps_str + sizet(ci) * StubMerger.MAX_DEPS_STR_LEN
|
||||
if sha1_m is None or deps_m is None: continue
|
||||
if string.strstr(deps_m, "_list") is None and string.strstr(deps_m, "_dict") is None: continue
|
||||
# 该模块名(RelPath 基名去扩展名)
|
||||
# Module name (basename of RelPath without extension)
|
||||
rel_m: str = StubMergerStore.LookupSha1RelPath(sha1_m)
|
||||
if rel_m is None: continue
|
||||
base_m: str = string.strrchr(rel_m, '/')
|
||||
@@ -171,16 +157,16 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
|
||||
if nm_m is None: continue
|
||||
string.strncpy(nm_m, base_m, 128)
|
||||
nm_m[128] = '\0'
|
||||
# _list ↔ M 环
|
||||
# _list <-> M cycle
|
||||
if string.strstr(deps_m, "_list") is not None:
|
||||
lst_sha1: str = StubMergerStore.LookupSha1ByModName("_list")
|
||||
if lst_sha1 is not None:
|
||||
lst_deps: str = StubMergerStore.LookupIncludesDeps(lst_sha1)
|
||||
if lst_deps is not None and _DepsContainsName(lst_deps, nm_m) != 0:
|
||||
if _DepsRemoveName(deps_m, "_list") != 0:
|
||||
# 内存破环后同步重写 deps.txt(诊断回退路径读文件)
|
||||
# Rewrite deps.txt after breaking the cycle in memory (diagnostic fallback reads the file)
|
||||
_RewriteDepsTxt(temp_dir, sha1_m, deps_m)
|
||||
# _dict ↔ M 环
|
||||
# _dict <-> M cycle
|
||||
if string.strstr(deps_m, "_dict") is not None:
|
||||
dct_sha1: str = StubMergerStore.LookupSha1ByModName("_dict")
|
||||
if dct_sha1 is not None:
|
||||
@@ -191,18 +177,10 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _BuildReachableResult - 从 Sha1Store 构建可达 FileEntry 列表
|
||||
#
|
||||
# 依赖图按需扫描(StubMergerBuild._BuildReachableSha1Set)只把「可达 includes + App」登记到全局 Sha1Store;
|
||||
# 本函数把 incdir 非空条目(includes)重建为 Phase1 1a/1b-pre/1b 需要的 FileEntry 列表。
|
||||
# App 条目(incdir 空)不进入 result——Phase B 已编译其 .obj,加入会导致 Phase B+ 重复编译 → 重复符号。
|
||||
#
|
||||
# Args: mb 内存池;includes_dir includes 根目录(仅用于失败诊断,路径以 store 为准)
|
||||
# Returns: ScanResult 指针(stdlib 分配,调用方负责释放);失败返回 None
|
||||
# ============================================================
|
||||
# _BuildReachableResult - rebuild the reachable FileEntry list from the Sha1Store.
|
||||
# Only incdir-nonempty (includes) entries enter result; App entries would cause duplicate symbols.
|
||||
def _BuildReachableResult(mb: memhub.MemBuddy*, includes_dir: str) -> rawptr[IncludesScanner.ScanResult]:
|
||||
"""从 Sha1Store(仅可达 includes + App)构建可达 FileEntry 列表"""
|
||||
"""Build the reachable FileEntry list from the Sha1Store (reachable includes only)"""
|
||||
if mb is None: return None
|
||||
store_count: sizet = sizet(StubMerger._sha1_store_count)
|
||||
if store_count <= 0 or StubMerger._sha1_store_arr is None: return None
|
||||
@@ -213,18 +191,18 @@ def _BuildReachableResult(mb: memhub.MemBuddy*, includes_dir: str) -> rawptr[Inc
|
||||
for i in range(store_count):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if StubMerger._sha1_store_arr[sidx] == '\0': continue
|
||||
# 仅 includes 条目(incdir 非空);App 条目不进入 result
|
||||
# Only includes entries (non-empty incdir); App entries do not enter result
|
||||
iidx: sizet = sizet(i) * StubMerger.MAX_INC_DIR_LEN
|
||||
if StubMerger._sha1_store_incdir[iidx] == '\0': continue
|
||||
eaddr: uint64 = uint64(result.Entries) + uint64(result.Count) * entry_size
|
||||
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](eaddr)
|
||||
if entry is None: continue
|
||||
# SHA1 深拷贝(store 槽 17 字节)
|
||||
# Deep-copy SHA1 (store slot is 17 bytes)
|
||||
sha1_buf: bytes = stdlib.malloc(17)
|
||||
if sha1_buf is None: continue
|
||||
string.strcpy(sha1_buf, StubMerger._sha1_store_arr + sidx)
|
||||
entry.Sha1 = sha1_buf
|
||||
# RelPath / IncDir 深拷贝
|
||||
# Deep-copy RelPath / IncDir
|
||||
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
||||
rel_len: sizet = string.strlen(StubMerger._sha1_store_rel + ridx)
|
||||
inc_len: sizet = string.strlen(StubMerger._sha1_store_incdir + iidx)
|
||||
@@ -239,7 +217,7 @@ def _BuildReachableResult(mb: memhub.MemBuddy*, includes_dir: str) -> rawptr[Inc
|
||||
string.strcpy(inc_buf, StubMerger._sha1_store_incdir + iidx)
|
||||
entry.RelPath = rel_buf
|
||||
entry.IncDir = inc_buf
|
||||
# 绝对路径 = inc_dir + '/' + rel_path(与 _TryResolveModuleFile 拼接一致)
|
||||
# Absolute path = inc_dir + '/' + rel_path (matches _TryResolveModuleFile)
|
||||
abs_buf: bytes = stdlib.malloc(inc_len + 1 + rel_len + 1)
|
||||
if abs_buf is None:
|
||||
stdlib.free(sha1_buf)
|
||||
@@ -250,7 +228,7 @@ def _BuildReachableResult(mb: memhub.MemBuddy*, includes_dir: str) -> rawptr[Inc
|
||||
abs_buf[inc_len] = '/'
|
||||
string.strcpy(abs_buf + inc_len + 1, rel_buf)
|
||||
entry.Path = abs_buf
|
||||
# 模块名(store 登记时保存的绝对模块名)
|
||||
# Module name (absolute module name saved when registering in the store)
|
||||
midx: sizet = sizet(i) * 64
|
||||
mod_len: sizet = string.strlen(StubMerger._sha1_store_mod + midx)
|
||||
if mod_len > 0:
|
||||
@@ -262,43 +240,33 @@ def _BuildReachableResult(mb: memhub.MemBuddy*, includes_dir: str) -> rawptr[Inc
|
||||
return result
|
||||
|
||||
# ============================================================
|
||||
# Phase1Main - Phase1 主流程(扫描 → 预注册 → 1a/1b 翻译 → 生成 stub)
|
||||
#
|
||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
|
||||
# ============================================================
|
||||
# Phase1Main - Phase1 main flow (scan -> pre-register -> 1a/1b translate -> generate stub)
|
||||
|
||||
# ============================================================
|
||||
# RunPhase1 - Phase1: 扫描 includes 目录,按需翻译并生成 stub
|
||||
#
|
||||
# 对每个 .vp/.py 文件检查 stub 是否已存在,不存在才翻译并分离 stub(避免重复工作)。
|
||||
#
|
||||
# Args: mb 内存池;includes_dir includes 目录路径;temp_dir 临时目录(保存 stub);log 日志器
|
||||
# Returns: 0 成功,非 0 失败
|
||||
# ============================================================
|
||||
# RunPhase1 - scan the includes dir; translate and split stubs only when missing.
|
||||
def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawptr[VLogger.Logger]) -> int:
|
||||
"""Phase1: 扫描 includes 目录,按需翻译并生成 stub"""
|
||||
"""Phase1: scan the includes directory and translate/generate stubs on demand"""
|
||||
if includes_dir is None or temp_dir is None:
|
||||
VLogger.error("includes_dir 或 temp_dir 为空,无法运行 Phase1", "Phase1")
|
||||
return 1
|
||||
|
||||
# 设置全局 temp_dir(供跨模块 CDefine 查找使用)
|
||||
# Set the global temp_dir (used for cross-module CDefine lookup)
|
||||
GenApi.set_temp_dir(temp_dir)
|
||||
# 同步设置 HandlesExprCall 的 temp_dir(供 vpi stub 返回注解缓存按需加载)。
|
||||
# 必须在 includes 翻译前设置:Phase1 翻译 includes 时「同模块方法调用(定义在后)」需读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi 兜底,Phase2 的 SetTempDir 太晚。
|
||||
# Also set HandlesExprCall's temp_dir (for the on-demand vpi return-annotation cache);
|
||||
# must run before includes translation since Phase2's SetTempDir is too late.
|
||||
GenApi.SetTempDir(temp_dir)
|
||||
|
||||
# 确保 temp 目录存在(build_dir/temp 可能尚未创建)
|
||||
# Ensure the temp dir exists (build_dir/temp may not exist yet)
|
||||
BuildPipeline.ensure_dir(temp_dir)
|
||||
|
||||
log.banner("Phase1: 扫描 includes(按需翻译)")
|
||||
|
||||
# 初始化 AST 表(只需一次):必须在依赖图 BFS 扫描之前调用。
|
||||
# _init_tables 懒分配的关键字/操作符表(全局 _kw_head 等)若首次触发在 BFS 的文件级 mark 之后,reset_to 会回收表节点 → 全局悬空 → 后续 tokenize 崩溃。
|
||||
# Init the AST tables once, before the dependency-graph BFS: if _init_tables first runs
|
||||
# after the BFS file-level mark, reset_to frees the tables and later tokenize crashes.
|
||||
ast._init_tables(mb)
|
||||
|
||||
# 依赖图按需扫描(App 优先模型):先扫 App 源文件(Config.SourceDir)的 import 依赖作为初始队列,
|
||||
# BFS 按文件系统解析 includes 文件(只对可达者读文件+算 SHA1+解析 import)并登记 Sha1Store/deps/写 deps.txt;
|
||||
# 未引用的 includes 从不读取。
|
||||
# On-demand dependency scan (App-first): seed the queue from App imports, then BFS-resolve
|
||||
# reachable includes only (read + SHA1 + import parse); unreferenced ones are never read.
|
||||
t_scan0: uint64 = w32.win32base.GetTickCount64()
|
||||
reachable_set: str = None
|
||||
reachable_count: int = 0
|
||||
@@ -326,58 +294,48 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
t_scan1: uint64 = w32.win32base.GetTickCount64()
|
||||
VLogger.info(f"依赖图扫描总耗时: {t_scan1 - t_scan0} ms(可达 {reachable_count} 个 includes)", "Phase1")
|
||||
|
||||
# 从 Sha1Store(仅可达 includes + App)构造可达 FileEntry 列表:
|
||||
# 后续 1a/1b-pre/1b 直接遍历 result(incdir 非空条目=includes),
|
||||
# use_reachable 过滤幂等保留(result 即可达子集)
|
||||
# Build the reachable FileEntry list from the Sha1Store; 1a/1b-pre/1b iterate result
|
||||
# (non-empty incdir = includes). use_reachable filtering is idempotent.
|
||||
result: rawptr[IncludesScanner.ScanResult] = _BuildReachableResult(mb, includes_dir)
|
||||
if result is None:
|
||||
VLogger.error("可达 FileEntry 列表构建失败,终止编译", "Phase1")
|
||||
return 1
|
||||
VLogger.info(f"可达 includes 条目: {result.Count}", "Phase1")
|
||||
|
||||
# 扫描后统一清理 _list/_dict 注入环边(_list↔memhub 等 2-环)。
|
||||
# 依赖图按需扫描顺序不可靠,内联检查可能漏判,此处全量清理保证
|
||||
# 依赖图无注入环,Phase A 多遍可收敛。破环后同步重写 deps.txt。
|
||||
# After scanning, clear _list/_dict injection cycle edges (e.g. _list<->memhub) so the
|
||||
# graph has none and Phase A converges; deps.txt is rewritten in sync.
|
||||
_BreakContainerCyclesInDeps(temp_dir)
|
||||
|
||||
# 写 _sha1_map.txt(仅可达 includes,人类可读输出;机器分析使用内存存储器)
|
||||
# Write _sha1_map.txt (reachable includes only, human-readable; analysis uses the in-memory store)
|
||||
StubMergerCollect.WriteIncludesSha1Map(mb, temp_dir, result, reachable_set, reachable_count)
|
||||
|
||||
# 兼容旧引用:sha1_set/set_count 对齐可达集合(use_reachable=1 时不被使用)
|
||||
# Legacy compat: sha1_set/set_count alias the reachable set (unused when use_reachable=1)
|
||||
sha1_set: str = reachable_set
|
||||
set_count: int = reachable_count
|
||||
# 模块 SHA1 映射已由依赖图扫描登记到 StubMerger 全局存储器(仅可达 includes + App),
|
||||
# HandlesExprCall._lookup_module_sha1 直接查全局存储器。
|
||||
# Module SHA1 map is registered into StubMerger's global store by the dependency scan;
|
||||
# HandlesExprCall._lookup_module_sha1 queries that store directly.
|
||||
|
||||
# FileEntry 结构体大小(Phase 1a 和 1b 共用)
|
||||
# FileEntry struct size (shared by Phase 1a and 1b)
|
||||
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Phase 1a: 注册可达 includes 文件的 struct/enum/union
|
||||
#
|
||||
# 解决字母序依赖问题:1a 先把所有 struct/enum/union 注册到全局表,1b 全量翻译时 struct 已注册、走 existing 路径只翻译方法体;
|
||||
# 只处理可达文件;处理前按类继承关系拓扑排序,确保父类所在文件先于子类所在文件。
|
||||
# ============================================================
|
||||
# Phase 1a: register struct/enum/union for reachable includes (fixes alphabetical-order deps).
|
||||
p1a_reg: int = 0
|
||||
p1a_skp: int = 0
|
||||
p1a_fl: int = 0
|
||||
|
||||
# ============================================================
|
||||
# [临时取证打点] Phase1 早期扁平缓冲分配失败现场快照(定位后清理);仅只读 mb 状态 + 打印,不改变任何分配行为与逻辑。
|
||||
# 三维度判定:① OOM(dump_stats 显示 used≈total、free≈0);② mb 损坏(usable 基址/大小荒谬、self_check != 0);③ 本次请求 size 算出的 order > max_order。
|
||||
# 用 VLogger.info(非终止;VLogger.error 会 sys.exit(1) 掩盖后续打点)。
|
||||
# ============================================================
|
||||
# [temp diagnostic] Snapshot mb state if the early Phase1 flat buffer allocation fails.
|
||||
if mb is not None:
|
||||
# 先做带环检测的 self_check:free list 被写坏成环时 dump_stats 的
|
||||
# free_bytes/free_count 无步数上限会死循环(打点自身卡死),故
|
||||
# self_check 返回 4(成环)时跳过 dump_stats。
|
||||
# Run a cycle-detecting self_check first: a corrupted free list would loop forever in
|
||||
# dump_stats, so skip dump_stats when self_check returns 4 (cycle).
|
||||
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_mo = 32 # Clamp: prevents this probe from out-of-bounds reading free_lists if max_order is corrupted
|
||||
p1_fl_head: uint64 = uint64(mb.free_lists[p1_mo])
|
||||
p1_mb_addr: uint64 = uint64(mb)
|
||||
p1_base_addr: uint64 = uint64(mb.base)
|
||||
@@ -397,7 +355,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
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 崩溃)
|
||||
# Pre-scan class inheritance and build the topo order (flat byte arrays avoid str-pointer NULL crashes)
|
||||
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)
|
||||
def_sha1s_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
|
||||
@@ -409,9 +367,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
if generic_file_count_box is not None: string.memset(generic_file_count_box, 0, 4)
|
||||
topo_count: int = 0
|
||||
|
||||
# 三块扁平缓冲是 Phase1Scan._ScanClassInheritance 的契约前置缓冲:该函数入口对 NULL fail-fast(静默跳过整段会退化为字母序并掩盖 OOM 等内存故障),
|
||||
# 故分配失败时逐块显式报错终止(VLogger.error 内部 sys.exit(1)),绝不静默回退。
|
||||
# 注:topo_order/topo_count_box 仅供 Phase1Topo._TopoSortFiles 使用,有效性由其入口自身校验(NULL 即返回 -1 并终止),不并入此处终止逻辑。
|
||||
# These three flat buffers are Phase1Scan._ScanClassInheritance's contract pre-buffers; it
|
||||
# fails fast on NULL, so report and abort per buffer on allocation failure (never fall back).
|
||||
if class_names_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败:class_names_buf 为 NULL,终止编译", "Phase1")
|
||||
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")
|
||||
@@ -441,32 +398,23 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
topo_count = 0
|
||||
|
||||
# ============================================================
|
||||
# Phase 1a-pre-generic-all: mark1 前全量预注册 includes 泛型模板
|
||||
#
|
||||
# 非可达泛型文件(_list/_dict/linkedlist/mlirlite 等)的模板若在 Phase2(mark2 之后)才注册会被 reset 回收 → list[int]/dict[...] 特化失败。
|
||||
# 故对全部 includes 轻量预注册,使模板表与节点都落在 mark1 之前(_PreRegisterAllGenericTemplates 内有 generic marker 文本预筛,不 parse 全部 AST,OOM 风险可控)。
|
||||
# Phase 1a-pre-generic-all: pre-register all includes' generic templates before mark1.
|
||||
full_scan_g: rawptr[IncludesScanner.ScanResult] = IncludesScanner.scan_includes(mb, includes_dir)
|
||||
if full_scan_g is not None: Phase1Scan._PreRegisterAllGenericTemplates(mb, full_scan_g)
|
||||
else: Phase1Scan._PreRegisterAllGenericTemplates(mb, result)
|
||||
|
||||
# ============================================================
|
||||
# Phase 1a: 注册 struct/enum/union(mark1 前,全量 includes)
|
||||
#
|
||||
# OOM 治理:1a 对全部 includes parse 完整 AST,若驻留到 mark1 会累积至 1GB 主池耗尽。故:
|
||||
# 1) StdlibAlloc=1:1a 注册的跨文件持久数据(StructTable 表、Fields/Name/ModuleSha1、struct_ty 深拷贝、枚举/Renum/泛型模板等)全部落 perm_pool(永不 reset);
|
||||
# 2) 每文件翻译完 reset_to(文件级 mark):只回收该文件的 AST/lexer/token/IR 等瞬时数据,注册数据不受影响。
|
||||
# mark1 仍在 1a 结束后打,1b 每文件 reset_to(mark1) 语义不变。
|
||||
# ============================================================
|
||||
# Phase 1a: register struct/enum/union (before mark1, all includes) with OOM control.
|
||||
GenApi.set_stdlib_alloc(1)
|
||||
|
||||
# 遍历文件:优先使用拓扑顺序,回退到字母序
|
||||
# Iterate files: topo order preferred, falling back to alphabetical
|
||||
iter_count: int = topo_count if topo_count > 0 else result.Count
|
||||
# 1a 阶段总耗时起点(struct/enum/union 注册)
|
||||
# 1a total-time start (struct/enum/union registration)
|
||||
t_a0: uint64 = w32.win32base.GetTickCount64()
|
||||
for i in range(iter_count):
|
||||
# 每文件级 mark:翻译完回收该文件 AST(注册数据在 perm_pool)
|
||||
# Per-file mark: free this file's AST after translating (registered data is in perm_pool)
|
||||
p1a_mark: sizet = mb.mark()
|
||||
# 获取文件索引:拓扑顺序或字母序
|
||||
# Get the file index: topo order or alphabetical
|
||||
file_idx: int = i
|
||||
if topo_count > 0:
|
||||
od_addr: uint64 = uint64(topo_order) + i * 4
|
||||
@@ -484,7 +432,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
p1a_fl += 1
|
||||
continue
|
||||
|
||||
# 按需翻译过滤(拓扑排序已过滤,但字母序回退需要检查)
|
||||
# On-demand filter (topo already filtered; the alphabetical fallback must check)
|
||||
if topo_count == 0:
|
||||
if use_reachable != 0:
|
||||
if StubMergerCollect._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0:
|
||||
@@ -495,17 +443,17 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
p1a_skp += 1
|
||||
continue
|
||||
|
||||
# 单文件计时起点(读文件 + parse 段,定位 1a 卡点)
|
||||
# Per-file timer start (read + parse, to locate 1a stalls)
|
||||
t_fa0: uint64 = w32.win32base.GetTickCount64()
|
||||
|
||||
# 读取文件内容
|
||||
# Read file contents
|
||||
file_path_r: str = entry_r.Path
|
||||
f_r: rawptr[fileio.File] = fileio.File(file_path_r, fileio.MODE.R)
|
||||
if f_r.closed:
|
||||
p1a_fl += 1
|
||||
continue
|
||||
|
||||
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
|
||||
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||
if f_r.size() >= Phase1.SRC_BUF_SIZE:
|
||||
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_r}", "Phase1")
|
||||
f_r.close()
|
||||
@@ -526,7 +474,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
continue
|
||||
if bytes_read_r < Phase1.SRC_BUF_SIZE: src_buf_r[bytes_read_r] = 0
|
||||
|
||||
# 解析 AST
|
||||
# Parse AST
|
||||
lx_r: ast.Lexer* = ast.new_lexer(mb)
|
||||
if lx_r is None:
|
||||
stdlib.free(src_buf_r)
|
||||
@@ -540,10 +488,10 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
p1a_fl += 1
|
||||
continue
|
||||
|
||||
# 计时点 1:读文件 + parse 完成
|
||||
# Timer 1: read + parse done
|
||||
t_fa1: uint64 = w32.win32base.GetTickCount64()
|
||||
|
||||
# 创建 Translator,设置 _declare_only=1(注册 struct/enum/union)
|
||||
# Create Translator with _declare_only=1 (register struct/enum/union)
|
||||
tr_r: rawptr[HandlesTranslator.Translator] = HandlesTranslator.Translator()
|
||||
if tr_r is None:
|
||||
stdlib.free(src_buf_r)
|
||||
@@ -559,34 +507,32 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
GenApi.clear_ctypedefs()
|
||||
GenApi.reset_visible_structs(mb, 0)
|
||||
ret_r: int = tr_r.translate(tree_r)
|
||||
# 计时点 2:translate(declare=1) 返回
|
||||
# Timer 2: translate(declare=1) returned
|
||||
t_fa2: uint64 = w32.win32base.GetTickCount64()
|
||||
if ret_r != 0: p1a_fl += 1
|
||||
else: p1a_reg += 1
|
||||
|
||||
# 逐文件进度日志(parse/translate 两段,定位 1a 卡点)
|
||||
# Per-file progress log (parse/translate segments, to locate 1a stalls)
|
||||
VLogger.info(f"[{i+1}/{iter_count}] 1a: {file_path_r} (parse={t_fa1-t_fa0} ms, translate={t_fa2-t_fa1} ms, total={t_fa2-t_fa0} ms)", "Phase1")
|
||||
|
||||
# 释放 Translator 的 C malloc 资源
|
||||
# Release the Translator's C malloc resources
|
||||
if tr_r._global_names is not None: stdlib.free(tr_r._global_names)
|
||||
if tr_r._nonlocal_names is not None: stdlib.free(tr_r._nonlocal_names)
|
||||
stdlib.free(src_buf_r)
|
||||
|
||||
# [TEMP-DIAG] 1a 每文件哨兵:校验本文件窗口 [p1a_mark, chk_depth) 内所有存活块的块尾 canary。
|
||||
# 位置必须在 reset_to 之前:reset_to 会把 chk_depth 截断回 p1a_mark,之后再查同一 mark 时 check_alive_blocks 直接 return 0(窗口为空、必然漏报)。
|
||||
# p1a_mark 在循环体首行捕获(本文件 L597),故窗口恰为「本文件 parse + translate(declare=1)」期间分配的块。
|
||||
# 仅 --detection-deep(mb.deep_detect != 0)时执行(每文件 O(n) 遍历活块);读法同本文件既有 mb.detection:直接读 MemBuddy 实例的公开 int 字段。
|
||||
# [TEMP-DIAG] Per-file sentinel: check the tail canary of live blocks in [p1a_mark,
|
||||
# chk_depth). Must run before reset_to; only under --detection-deep (mb.deep_detect != 0).
|
||||
if mb.deep_detect != 0:
|
||||
chk_rc_1a: int = mb.check_alive_blocks(p1a_mark)
|
||||
if chk_rc_1a != 0:
|
||||
VLogger.error(f"内存损坏(1a 每文件后续, rc={chk_rc_1a}): {file_path_r}", "Phase1")
|
||||
stdlib.exit(1)
|
||||
|
||||
# 回收本文件 AST/lexer/token/IR 等瞬时数据(注册数据已在 perm_pool,
|
||||
# reset 不影响;IncludesScanner result 等在 p1a_mark 前分配同样保留)
|
||||
# Reclaim this file's transient AST/lexer/token/IR (registered data is in perm_pool, so
|
||||
# reset does not affect it; IncludesScanner result predates p1a_mark and is kept too).
|
||||
mb.reset_to(p1a_mark)
|
||||
|
||||
# 1a 阶段总耗时(struct/enum/union 注册,含每文件读+parse+翻译声明)
|
||||
# 1a total time (struct/enum/union registration, incl. per-file read+parse+declaration)
|
||||
t_a1: uint64 = w32.win32base.GetTickCount64()
|
||||
VLogger.info(f"1a 总耗时: {t_a1-t_a0} ms (注册 struct/enum, reg={p1a_reg} skp={p1a_skp})", "Phase1")
|
||||
|
||||
@@ -594,33 +540,26 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
VLogger.error(f"Phase 1a 有 {p1a_fl} 个文件 struct 注册失败", "Phase1")
|
||||
return 1
|
||||
|
||||
# 释放拓扑排序资源(topo_order/topo_count_box 保留到 Phase 1b 结束,
|
||||
# 1b 按拓扑序翻译需要它们;class/def 数组 1b 不再使用)
|
||||
# Release topo-sort resources (topo_order/topo_count_box stay until Phase 1b ends;
|
||||
# the class/def arrays are no longer used by 1b).
|
||||
if class_names_buf is not None: mb.free(class_names_buf)
|
||||
if parent_names_buf is not None: mb.free(parent_names_buf)
|
||||
if def_sha1s_buf is not None: mb.free(def_sha1s_buf)
|
||||
if generic_file_indices is not None: mb.free(generic_file_indices)
|
||||
if generic_file_count_box is not None: mb.free(generic_file_count_box)
|
||||
|
||||
# ============================================================
|
||||
# Phase 1b: 全量翻译(struct 已注册,走 existing 路径翻译方法体)
|
||||
# ============================================================
|
||||
# OOM 回收:mark1 在 1a-pre/泛型预注册/1a 全部结束后打,所有跨文件持久表(_struct_table/_enum_table/_generic_class_nodes/_renum_*/关键字表等)都落在 mark1 之前;
|
||||
# 1b 每文件翻译完后 reset_to(mark1) 回收该文件的 AST/IR/Handler/dump 临时,避免 90 文件累积导致 1GB 池 OOM。
|
||||
# Phase 1b: full translation (structs already registered; translate method bodies).
|
||||
# mark1 precedes all cross-file persistent tables; reset per file to avoid pool exhaustion.
|
||||
mark1: sizet = mb.mark()
|
||||
|
||||
# 1b 阶段新注册的 struct/enum 条目改 stdlib 分配(跨文件永久存活,不被 reset 回收)
|
||||
# Struct/enum entries registered in 1b use stdlib allocation (permanent, not reset)
|
||||
GenApi.set_stdlib_alloc(1)
|
||||
|
||||
# ============================================================
|
||||
# Phase 1b-pre: 预生成所有待翻译文件的 .vpi stub
|
||||
#
|
||||
# 依赖模块的 .vpi 必须在翻译前可用:1b 主循环按扫描顺序(非拓扑)翻译,若依赖模块后翻译则 LoadDepVpiIntoModule 读不到 .vpi
|
||||
# → declare 未注入 → 跨模块调用的返回类型推断错误(如误推为 Ptr 而 define 返回 i8*)→ 运行时返回值截断崩溃。
|
||||
# ============================================================
|
||||
# Phase 1b-pre: pre-generate .vpi stubs for all files to translate (deps must exist first).
|
||||
p1bpre_failed: int = 0
|
||||
dir_len_pv: sizet = string.strlen(temp_dir)
|
||||
# 1b-pre 阶段总耗时起点(vpi 预生成)
|
||||
# 1b-pre total-time start (vpi pre-generation)
|
||||
t_pv0: uint64 = w32.win32base.GetTickCount64()
|
||||
for i_pv in range(result.Count):
|
||||
entry_addr_pv: uint64 = uint64(result.Entries) + i_pv * entry_size
|
||||
@@ -633,7 +572,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
if use_reachable != 0: in_set_pv = StubMergerCollect._is_in_sha1_set(sha1_pv, reachable_set, reachable_count)
|
||||
else: in_set_pv = StubMergerCollect._is_in_sha1_set(sha1_pv, sha1_set, set_count)
|
||||
if in_set_pv == 0: continue
|
||||
# vpi 已存在则跳过
|
||||
# Skip if the vpi already exists
|
||||
vpi_path_pv: str = StubMergerCollect._sliced_path(temp_dir, dir_len_pv, sha1_pv, "vpi")
|
||||
if vpi_path_pv is None:
|
||||
p1bpre_failed += 1
|
||||
@@ -645,14 +584,14 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
stdlib.free(vpi_path_pv)
|
||||
continue
|
||||
stdlib.free(vpi_path_pv)
|
||||
# 读取文件、解析 AST、生成 vpi
|
||||
# Read the file, parse the AST, generate the vpi
|
||||
file_path_pv: str = entry_pv.Path
|
||||
f_pv: rawptr[fileio.File] = fileio.File(file_path_pv, fileio.MODE.R)
|
||||
if f_pv.closed:
|
||||
p1bpre_failed += 1
|
||||
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 无法打开源文件", "Phase1")
|
||||
continue
|
||||
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
|
||||
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||
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")
|
||||
@@ -684,10 +623,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
if tree_pv is None:
|
||||
stdlib.free(src_buf_pv)
|
||||
p1bpre_failed += 1
|
||||
# 解析失败:**日志行内联 行/列 + 原因**,并附完整 Python 风格 traceback
|
||||
# (Traceback + File/行 + 源码行 + caret + SyntaxError: 消息;消息含 token
|
||||
# 类型与前/后 token 附注,见 includes/ast/parser.vp 的 _record_parse_err_ctx
|
||||
# / parse_tokens 兜底补记),与单文件管线 BuildPipeline 同一条上报路径。
|
||||
# Parse failure: the log line inlines line/col + reason, plus a full Python-style
|
||||
# traceback (same reporting path as the single-file BuildPipeline).
|
||||
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | AST 解析失败(第 {ast.get_parse_err_line()} 行第 {ast.get_parse_err_col()} 列: {ast.get_parse_err_msg()})", "Phase1")
|
||||
GenApi.dump_parse_traceback(file_path_pv)
|
||||
continue
|
||||
@@ -707,19 +644,18 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
p1bpre_failed += 1
|
||||
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | pyi 缓冲分配失败(OOM)", "Phase1")
|
||||
stdlib.free(src_buf_pv)
|
||||
# [TEMP-DIAG] 1b-pre 每文件哨兵(同上):校验本文件窗口 [mark1, chk_depth) 内所有存活块的块尾 canary。
|
||||
# 同样必须在 reset_to(mark1) 之前(否则窗口被截断为空)。mark1 在 1b 段前捕获(本文件 L756),是 1b/1b-pre 的统一窗口起点。
|
||||
# 仅 --detection-deep(mb.deep_detect != 0)时执行,读法同上方 1a 哨兵。
|
||||
# [TEMP-DIAG] 1b-pre per-file sentinel (as above): check the tail canary of live blocks
|
||||
# in [mark1, chk_depth). Must run before reset_to(mark1); only under --detection-deep.
|
||||
if mb.deep_detect != 0:
|
||||
chk_rc_pv: int = mb.check_alive_blocks(mark1)
|
||||
if chk_rc_pv != 0:
|
||||
VLogger.error(f"内存损坏(1b-pre 每文件后续, rc={chk_rc_pv}): {file_path_pv}", "Phase1")
|
||||
stdlib.exit(1)
|
||||
|
||||
# 回收本文件 AST(vpi 已写盘,AST 不再需要;mark1 后无其他持久数据)
|
||||
# Reclaim this file's AST (the vpi is written; nothing else persists after mark1)
|
||||
mb.reset_to(mark1)
|
||||
|
||||
# 1b-pre 阶段总耗时(vpi 预生成)
|
||||
# 1b-pre total time (vpi pre-generation)
|
||||
t_pv1: uint64 = w32.win32base.GetTickCount64()
|
||||
VLogger.info(f"1b-pre 总耗时: {t_pv1-t_pv0} ms (vpi 预生成)", "Phase1")
|
||||
|
||||
@@ -727,18 +663,18 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
VLogger.error(f"Phase 1b-pre 有 {p1bpre_failed} 个文件 vpi 预生成失败(失败文件与原因见上方逐条 [Phase 1b-pre] 日志)", "Phase1")
|
||||
return 1
|
||||
|
||||
# 遍历每个文件(拓扑序优先:父类所在文件先翻译,确保跨模块 vtable
|
||||
# 生成时依赖模块的函数 TypeStr 已注册;topo_count==0 回退扫描顺序)
|
||||
# Iterate each file (topo order preferred so base-class files translate first and their
|
||||
# TypeStr is registered for cross-module vtables; fall back to scan order when topo_count==0).
|
||||
translated: int = 0
|
||||
skipped: int = 0
|
||||
failed: int = 0
|
||||
# 1b 阶段总耗时起点(全量翻译)
|
||||
# 1b total-time start (full translation)
|
||||
t_b0: uint64 = w32.win32base.GetTickCount64()
|
||||
|
||||
loop_n_b: int = result.Count
|
||||
if topo_count > 0: loop_n_b = topo_count
|
||||
for i in range(loop_n_b):
|
||||
# 获取 entry(拓扑序:file_idx 从 topo_order 取,否则用扫描索引)
|
||||
# Get the entry (topo: file_idx from topo_order, else the scan index)
|
||||
file_idx_b: int = i
|
||||
if topo_count > 0:
|
||||
od_addr_b: uint64 = uint64(topo_order) + sizet(i) * 4
|
||||
@@ -755,7 +691,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
# 检查 SHA1 是否在翻译集合中(拓扑序已过滤可达;仅回退扫描顺序时检查)
|
||||
# Check whether the SHA1 is in the translate set (topo already filtered; only the fallback checks)
|
||||
in_set: int = 1
|
||||
if topo_count == 0:
|
||||
in_set = 0
|
||||
@@ -765,7 +701,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
# 构造 stub 路径: {temp_dir}/{sha1}.stub.ll
|
||||
# Build the stub path: {temp_dir}/{sha1}.stub.ll
|
||||
dir_len: sizet = string.strlen(temp_dir)
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
stub_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "stub.ll")
|
||||
@@ -773,9 +709,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
# 检查 stub 是否已存在(按需翻译:跳过已存在的)
|
||||
# 例外:含 class 定义的模块必须翻译——类注册只在翻译时发生,增量编译跳过会导致跨模块类构造 find_struct_by_module 落空 → 返回类型 fail-fast。
|
||||
# 判据:扫描 1b-pre 生成的 vpi 是否有 "class " 行(vpi 为解析后存根,精确无注释/字符串误判)。
|
||||
# Skip already-existing stubs, except modules with class definitions: class registration
|
||||
# only happens at translation, so skipping breaks cross-module find_struct_by_module.
|
||||
force_translate: int = 0
|
||||
vpi_path_1b: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi")
|
||||
if vpi_path_1b is not None:
|
||||
@@ -803,22 +738,22 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
else: vf_1b.close()
|
||||
stdlib.free(vpi_path_1b)
|
||||
if force_translate == 1:
|
||||
# 含 class:强制翻译(覆盖 stub/text,重新注册类)
|
||||
# Has class: force translation (overwrite stub/text, re-register classes)
|
||||
rp: str = entry.RelPath
|
||||
VLogger.info(f"含 class 定义,强制翻译: {rp}(sha1={sha1})", "Phase1")
|
||||
else:
|
||||
# 检查 stub 是否已存在(按需翻译:跳过已存在的)
|
||||
# Skip already-existing stubs (on-demand translation)
|
||||
sf: rawptr[fileio.File] = fileio.File(stub_path, fileio.MODE.R)
|
||||
if not sf.closed:
|
||||
sf.close()
|
||||
# 检查 text.ll 是否也存在(虚表扫描需要 text.ll)
|
||||
# Check that text.ll exists too (vtable scan needs it)
|
||||
text_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "text.ll")
|
||||
if text_path is not None:
|
||||
tf: rawptr[fileio.File] = fileio.File(text_path, fileio.MODE.R)
|
||||
if not tf.closed:
|
||||
tf.close()
|
||||
# sidecar({sha1}.meta)与在线 FULL 产物({sha1}.full.ll)也必须存在:
|
||||
# 缺失则重新翻译以补写(否则 Phase B+/C 的声明判定与编译拿不到数据)。
|
||||
# The sidecar ({sha1}.meta) and online FULL artifact ({sha1}.full.ll) must also exist;
|
||||
# otherwise re-translate to regenerate them for Phase B+/C.
|
||||
meta_path_1b: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "meta")
|
||||
meta_ok_1b: int = 0
|
||||
if meta_path_1b is not None:
|
||||
@@ -840,20 +775,20 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
stdlib.free(stub_path)
|
||||
continue
|
||||
stdlib.free(text_path)
|
||||
# text.ll 不存在,需要重新翻译
|
||||
# text.ll missing, re-translate
|
||||
rp: str = entry.RelPath
|
||||
VLogger.info(f"text.ll 不存在,重新翻译: {rp}(sha1={sha1})", "Phase1")
|
||||
else:
|
||||
# stub 不存在,需要翻译
|
||||
# stub missing, translate
|
||||
rp: str = entry.RelPath
|
||||
VLogger.info(f"翻译: {rp}(sha1={sha1})", "Phase1")
|
||||
|
||||
stdlib.free(stub_path)
|
||||
|
||||
# 单文件计时起点:读文件(parse/translate/dump 三段,对齐 Phase2 插桩)
|
||||
# Per-file timer start: read (parse/translate/dump segments, matching Phase2 instrumentation)
|
||||
t_f0: uint64 = w32.win32base.GetTickCount64()
|
||||
|
||||
# 读取文件内容
|
||||
# Read file contents
|
||||
file_path: str = entry.Path
|
||||
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
|
||||
if f is None:
|
||||
@@ -865,7 +800,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
|
||||
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||
if f.size() >= Phase1.SRC_BUF_SIZE:
|
||||
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
|
||||
f.close()
|
||||
@@ -887,7 +822,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
continue
|
||||
if bytes_read < Phase1.SRC_BUF_SIZE: src_buf[bytes_read] = 0
|
||||
|
||||
# 解析 AST
|
||||
# Parse AST
|
||||
lx: ast.Lexer* = ast.new_lexer(mb)
|
||||
if lx is None:
|
||||
stdlib.free(src_buf)
|
||||
@@ -897,23 +832,22 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
tokens: ast.Token* = ast.tokenize(lx)
|
||||
tree: ast.AST* = ast.parse_tokens(mb, tokens)
|
||||
if tree is None:
|
||||
# 解析失败:行/列 + 原因一并进日志行(原因由 parser.vp 的 _record_* /
|
||||
# parse_tokens 兜底补记提供)
|
||||
# Parse failure: line/col + reason go into the log line (reason supplied by parser.vp's
|
||||
# _record_* / parse_tokens fallback).
|
||||
VLogger.error(f"AST 解析失败: {file_path}(第 {ast.get_parse_err_line()} 行第 {ast.get_parse_err_col()} 列: {ast.get_parse_err_msg()})", "Phase1")
|
||||
stdlib.free(src_buf)
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
# 计时点 1:读文件 + lexer/tokenize/parse 完成
|
||||
# Timer 1: read + lexer/tokenize/parse done
|
||||
t_f1: uint64 = w32.win32base.GetTickCount64()
|
||||
|
||||
# 文本扫描注册 CTypedef:AST 字符串字段(attr/id)token 零拷贝指向 src_buf,跨阶段内容不稳定(悬空),读 attr 会 0xC0000005;
|
||||
# 故改用源码文本逐行匹配 "NAME: t.CTypedef = TYPE",在 src_buf 有效期内注册。
|
||||
# Register CTypedef by scanning source text: AST string fields (attr/id) point into src_buf
|
||||
# and dangle across phases (reading attr faults), so match "NAME: t.CTypedef = TYPE" lines.
|
||||
GenApi.RegisterCTypedefFromSource(mb, src_buf)
|
||||
|
||||
# 生成 .vpi 存根文件(直接遍历 AST,不依赖 PythonToStubConverter)
|
||||
# 1b-pre 已为所有待翻译文件生成 vpi,此处仅兜底:vpi 缺失才生成
|
||||
# (消除 1b-pre 与 1b 对同一文件的重复 _GeneratePyiFromAst 工作)
|
||||
# Generate the .vpi stub (walk the AST directly). 1b-pre already generated one, so this
|
||||
# is only a fallback when the vpi is missing.
|
||||
vpi_path_1b: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi")
|
||||
if vpi_path_1b is not None:
|
||||
vf_1b: rawptr[fileio.File] = fileio.File(vpi_path_1b, fileio.MODE.R)
|
||||
@@ -933,7 +867,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
stdlib.free(pyi_buf)
|
||||
stdlib.free(vpi_path_1b)
|
||||
|
||||
# 翻译 AST → LLVM IR
|
||||
# Translate AST -> LLVM IR
|
||||
tr: rawptr[HandlesTranslator.Translator] = HandlesTranslator.Translator()
|
||||
if tr is None:
|
||||
stdlib.free(src_buf)
|
||||
@@ -942,10 +876,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
tr.ModuleSha1 = sha1
|
||||
tr.CurrentPackage = GenApi.compute_package_from_relpath(mb, entry.RelPath)
|
||||
GenApi.set_current_file_type(file_path)
|
||||
# --traceback: mirror the DWARF switch into the Gen-layer emitter (the
|
||||
# same BuildPipeline._traceback_mode that gates -frame-pointer=all /
|
||||
# dbghelp / the Install() ctor). include/*.vp modules are compiled here,
|
||||
# so this is what gives their frames DWARF line info.
|
||||
# --traceback: mirror the DWARF switch into the Gen-layer emitter (same
|
||||
# BuildPipeline._traceback_mode that gates -frame-pointer=all / dbghelp / Install()).
|
||||
DebugInfoGen.SetEnabled(BuildPipeline._traceback_mode)
|
||||
GenApi.set_current_file_imports(file_path)
|
||||
GenApi.set_current_module_sha1(sha1)
|
||||
@@ -953,44 +885,40 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
GenApi.clear_ctypedefs()
|
||||
GenApi.reset_visible_structs(mb, 0)
|
||||
|
||||
# 翻译前哨兵:AST 解析(lexer/tokenize/parse_tokens)同样在 pool 分配,
|
||||
# 若解析器越界写破坏了存活块 header,此处立即定位到当前文件
|
||||
# (崩溃点漂移排查:把"翻译过程中随机崩"变成确定性定位)
|
||||
# Pre-translate sentinel: parsing also allocates from the pool, so a parser overrun that
|
||||
# corrupts a live block header is pinned to the current file (deterministic crash location).
|
||||
pre_rc: int = mb.check_alive_blocks(mark1)
|
||||
if pre_rc != 0:
|
||||
VLogger.error(f"内存损坏(翻译前存活块, rc={pre_rc}): {file_path}", "Phase1")
|
||||
stdlib.exit(1)
|
||||
|
||||
ret: int = tr.translate(tree)
|
||||
# 计时点 2:translate 返回
|
||||
# Timer 2: translate returned
|
||||
t_f2: uint64 = w32.win32base.GetTickCount64()
|
||||
# 诊断:translate 正常返回(若挂起在 translate 内部,此行不会打印,
|
||||
# 用于区分"translate 内部挂起"与"后续 dump 挂起")
|
||||
# Diagnostic: translate returned normally (if it hangs inside, this line is not printed,
|
||||
# distinguishing an internal hang from a later dump hang).
|
||||
if ret == 0: VLogger.info(f"翻译完成(translate返回): {file_path}", "Phase1")
|
||||
if ret != 0:
|
||||
VLogger.error(f"翻译失败: {file_path}", "Phase1") # error 终止
|
||||
VLogger.error(f"翻译失败: {file_path}", "Phase1") # error terminates
|
||||
stdlib.free(src_buf)
|
||||
if tr._global_names is not None: stdlib.free(tr._global_names)
|
||||
if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names)
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
# --traceback: inject the startup Install() ctor when translating traceback.vp
|
||||
# (its stub/text.ll below are what Phase2 reuses to build the .obj). The
|
||||
# injector lives in the Gen layer (already depends on mlirlite), so this
|
||||
# module gains no cross-module mlirlite symbol dependency.
|
||||
# --traceback: inject the startup Install() ctor when translating traceback.vp;
|
||||
# the injector lives in the Gen layer, adding no cross-module mlirlite dependency.
|
||||
if BuildPipeline._traceback_mode != 0 and BuildPipeline._tb_basename_is_traceback(file_path) != 0: HandlesTranslator.InjectTracebackInstallCtor(tr)
|
||||
|
||||
# 诊断:pre_rc 哨兵在 translate 前、覆盖不到 translate 期间的 AST/IR 写穿,故 dump 前立即检查存活块 header/canary
|
||||
# (rc=6 块尾 canary 损坏 = 越界写穿数据区),把「dump 时链表成环卡死」变成「定位到 translate 期间写穿发生的文件」。
|
||||
# Diagnostic: the pre-translate sentinel misses write-throughs during translate, so check
|
||||
# live block headers/canaries before dump (rc=6 means a tail canary was overwritten).
|
||||
post_rc: int = mb.check_alive_blocks(mark1)
|
||||
if post_rc != 0: VLogger.error(f"内存损坏(translate后存活块, rc={post_rc}): {file_path}", "Phase1")
|
||||
|
||||
# dump stub IR(declarations only)
|
||||
# 1MB 不够:ast/parser.vp 的 text.ll 超 1MB 被截断 → 合并 IR 尾部丢字
|
||||
# (opt: "end of file in global variable name"),提至 8MB。
|
||||
# Dump stub IR (declarations only). 1MB is not enough: ast/parser.vp's text.ll exceeds it
|
||||
# and is truncated (opt: "end of file in global variable name"), so use 8MB.
|
||||
PHASE1_IR_SIZE: sizet = 8388608
|
||||
# FULL dump = stub + text(同一模块),留 2× 余量
|
||||
# FULL dump = stub + text (same module), with 2x headroom
|
||||
PHASE1_FULL_SIZE: sizet = 16777216
|
||||
stub_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE)
|
||||
if stub_buf is None:
|
||||
@@ -1034,11 +962,11 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
stdlib.free(text_path_p1)
|
||||
stdlib.free(text_buf)
|
||||
|
||||
# save {sha1}.meta(结构化 sidecar:函数清单/结构体布局/虚表,替代从 IR 文本反推)
|
||||
# save {sha1}.meta (structured sidecar: function list/struct layout/vtables, no IR-text reverse-engineering)
|
||||
StubMergerIR.WriteModuleMeta(tr.Module, temp_dir, dir_len_p1, sha1)
|
||||
|
||||
# save {sha1}.full.ll(在线 FULL 产物:模型在线时的自足单模块 IR,供 Phase B 直接编译,
|
||||
# 取代 stub + 依赖 stub/text 文本合并)
|
||||
# save {sha1}.full.ll (the online FULL artifact: a self-contained single-module IR for
|
||||
# Phase B to compile directly, replacing the stub + dependency text merge).
|
||||
full_buf_p1: bytes = stdlib.malloc(PHASE1_FULL_SIZE)
|
||||
if full_buf_p1 is None: VLogger.error("full.ll 缓冲区分配失败", "Phase1")
|
||||
tr.dump_ir(full_buf_p1, PHASE1_FULL_SIZE, mlirlite.OUTPUT_FULL)
|
||||
@@ -1052,15 +980,15 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
stdlib.free(full_path_p1)
|
||||
stdlib.free(full_buf_p1)
|
||||
|
||||
# --traceback: 取出本模块的调试调用点记录(mlirlite 累积)并写 <sha1>.vpcall,
|
||||
# 供链接后 WriteStackMap 结合 objdump 还原调用点行号。关闭时不产生任何旁文件。
|
||||
# --traceback: take this module's debug call-site records (accumulated in mlirlite) and
|
||||
# write <sha1>.vpcall for WriteStackMap to reconstruct call-site lines. No file when off.
|
||||
if BuildPipeline._traceback_mode != 0:
|
||||
vpcall_text_p1: str = mlirlite.DebugTakeText()
|
||||
if vpcall_text_p1 is not None:
|
||||
BuildPipeline.WriteCallSidecar(temp_dir, sha1, vpcall_text_p1)
|
||||
stdlib.free(vpcall_text_p1)
|
||||
|
||||
# 计时点 3:dump(stub+text)完成;输出单文件三段耗时
|
||||
# Timer 3: dump (stub+text) done; print the per-file three-segment timing
|
||||
t_f3: uint64 = w32.win32base.GetTickCount64()
|
||||
VLogger.info(f"[{i+1}/{loop_n_b}] 翻译: {file_path} (parse={t_f1-t_f0} ms, translate={t_f2-t_f1} ms, dump={t_f3-t_f2} ms, total={t_f3-t_f0} ms)", "Phase1")
|
||||
|
||||
@@ -1075,33 +1003,30 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
df_p1.close()
|
||||
stdlib.free(deps_path_p1)
|
||||
|
||||
# 释放 Translator 的 C malloc 资源
|
||||
# Release the Translator's C malloc resources
|
||||
if tr._global_names is not None: stdlib.free(tr._global_names)
|
||||
if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names)
|
||||
|
||||
# 所有 dump_ir 完成,src_buf 不再需要
|
||||
# All dump_ir calls done, src_buf no longer needed
|
||||
stdlib.free(src_buf)
|
||||
|
||||
translated += 1
|
||||
|
||||
# OOM 回收:回收本文件翻译产生的瞬时数据(AST/tokens/lexer/IR/Handler/dump 临时);持久表(struct/enum/REnum/spec 缓存/全局函数名等)已迁出 reset 区间(stdlib),
|
||||
# reset_to(mark1) 只回收 mark1 之后分配、未被单独 free 的块。
|
||||
#
|
||||
# 内存哨兵(崩溃点漂移排查):reset 前检查存活块 header(抓越界写),reset 后检查 free list 完整性(抓链表损坏);任一损坏立即终止,把「随机崩溃漂移」变成「确定性定位到当前文件」。
|
||||
# rc 语义(check_alive_blocks):1=块地址低于 usable,2=高于 usable+size,3=order 为负,4=order 超 max_order,5=块未对齐到 order 大小,6=块尾 canary 损坏(越界写穿数据区)
|
||||
# rc 语义(self_check):1/2=越界,3=对齐错,4=free list 成环/超步数
|
||||
# OOM reclamation: free this file's transient AST/tokens/lexer/IR/dump data (persistent
|
||||
# tables moved to stdlib). Memory sentinels locate overruns deterministically.
|
||||
chk_rc: int = mb.check_alive_blocks(mark1)
|
||||
if chk_rc != 0: VLogger.error(f"内存损坏(存活块 header, rc={chk_rc}): {file_path}", "Phase1")
|
||||
# 越界写是崩溃根因(与回收/UAF 无关,已由 A/B 实验确认)——恢复 reset_to,改用块尾 canary(check_alive_blocks 校验)把越界写事发地定位到具体文件。
|
||||
# Out-of-bounds writes are the crash root cause (unrelated to reclamation/UAF, confirmed
|
||||
# by A/B tests); reset_to plus the tail canary pins the offending file.
|
||||
mb.reset_to(mark1)
|
||||
sc_rc: int = mb.self_check()
|
||||
if sc_rc != 0: VLogger.error(f"内存损坏(free list, rc={sc_rc}): {file_path}", "Phase1")
|
||||
|
||||
# 1b 阶段总耗时(全量翻译 + dump)
|
||||
# 1b total time (full translation + dump)
|
||||
t_b1: uint64 = w32.win32base.GetTickCount64()
|
||||
VLogger.info(f"1b 总耗时: {t_b1-t_b0} ms (全量翻译, trans={translated} skip={skipped} fail={failed})", "Phase1")
|
||||
|
||||
# 释放 Phase 1b 使用的拓扑排序资源
|
||||
# Release the topo-sort resources used by Phase 1b
|
||||
if topo_order is not None: mb.free(topo_order)
|
||||
if topo_count_box is not None: mb.free(topo_count_box)
|
||||
|
||||
@@ -1111,7 +1036,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: rawpt
|
||||
VLogger.error(f"Phase1 有 {failed} 个文件翻译失败", "Phase1")
|
||||
return 1
|
||||
|
||||
# 释放可达 SHA1 集合(如果分配了)
|
||||
# Release the reachable SHA1 set (if allocated)
|
||||
if reachable_set is not None: stdlib.free(reachable_set)
|
||||
|
||||
return 0
|
||||
@@ -31,41 +31,28 @@ import lib.Phase.Phase1 as Phase1
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Phase1Scan - 扫描类继承关系、泛型类标记与容器使用
|
||||
#
|
||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
|
||||
# ============================================================
|
||||
# Phase1Scan - scan class inheritance, generic-class markers and container usage.
|
||||
# Functions live in this module; globals are accessed cross-module via Phase1._xxx.
|
||||
|
||||
# 拓扑排序使用扁平字节数组存储类信息和文件依赖,
|
||||
# 避免结构体中的 str 指针字段在 mb.alloc 零填充时为 NULL 导致崩溃。
|
||||
# 类名/父类名存储在 class_names_buf/parent_names_buf(每个 Phase1.CLASS_NAME_LEN 字节),
|
||||
# 定义 SHA1 存储在 def_sha1s_buf(每个 Phase1.SHA1_LEN 字节)。
|
||||
# Topological sort stores class info/deps in flat byte arrays (names in
|
||||
# class_names_buf/parent_names_buf, SHA1s in def_sha1s_buf) to avoid NULL str fields.
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _ScanClassInheritance - 预扫描 includes 文件,收集类继承关系
|
||||
#
|
||||
# 遍历所有可达 includes 文件解析 AST,收集 ClassDef 的类名与父类名存入扁平字节数组,返回收集到的类数量。
|
||||
#
|
||||
# Args: mb 内存池;result IncludesScanner 扫描结果;reachable_set 可达 SHA1 集合;reachable_count 可达 SHA1 数量;use_reachable 是否使用可达集合过滤;
|
||||
# sha1_set SHA1 集合(use_reachable=0 时使用);set_count SHA1 集合数量;class_names_buf 类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节);
|
||||
# parent_names_buf 父类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节,无父类填 '\0');def_sha1s_buf 定义 SHA1 扁平数组(每个 Phase1.SHA1_LEN 字节)
|
||||
# Returns: 收集到的类数量,-1 失败
|
||||
# ============================================================
|
||||
# _ScanClassInheritance - pre-scan includes files, parse ASTs and collect ClassDef
|
||||
# names/parent names into flat byte arrays; returns the class count (-1 on failure).
|
||||
def _ScanClassInheritance(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResult], reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, generic_file_indices: t.CPtr, generic_file_count_box: t.CPtr) -> int:
|
||||
"""预扫描 includes 文件,收集类继承关系"""
|
||||
"""Pre-scan includes files and collect class inheritance info."""
|
||||
if result is None: return -1
|
||||
# class_names_buf/parent_names_buf/def_sha1s_buf 由调用方 mb.alloc 分配(Phase1Main 预扫描前),本函数把其地址直接作为 strcpy 目的缓冲;
|
||||
# 分配失败返回 NULL 时目的指针为近零地址 → strcpy 写零页(ASan access-violation),故按 fail-fast 显式报错终止(VLogger.error 内部 sys.exit(1)),不静默返回 -1。
|
||||
# The three flat buffers are mb.alloc'ed by the caller; a NULL buffer would make
|
||||
# strcpy write the zero page, so fail-fast abort instead of returning -1 silently.
|
||||
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__()
|
||||
class_count: int = 0
|
||||
|
||||
for i in range(result.Count):
|
||||
# 文件级 mark:收集完类继承信息后立即回收本文件 AST(可达 includes 全量 parse 的 AST 若驻留到 mark1 会耗尽 1GB 主池 OOM);
|
||||
# class_names_buf/parent_names_buf/def_sha1s_buf 与 generic_file_indices 在调用前分配、位于本 mark 之前,reset 不影响。
|
||||
# Per-file mark: reclaim this file's AST right after collecting inheritance
|
||||
# info (keeping all ASTs alive until mark1 would OOM the 1GB main pool).
|
||||
scan_mark: sizet = mb.mark()
|
||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
|
||||
@@ -74,18 +61,18 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.S
|
||||
sha1_e: str = entry.Sha1
|
||||
file_has_generic: int = 0
|
||||
|
||||
# 按需翻译过滤
|
||||
# on-demand translation filter
|
||||
in_set: int = 0
|
||||
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||
if in_set == 0: continue
|
||||
|
||||
# 读取文件
|
||||
# read the file
|
||||
file_path: str = entry.Path
|
||||
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
|
||||
if f.closed: continue
|
||||
|
||||
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
|
||||
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||
if f.size() >= Phase1.SRC_BUF_SIZE:
|
||||
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
|
||||
f.close()
|
||||
@@ -103,7 +90,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.S
|
||||
continue
|
||||
if bytes_read < Phase1.SRC_BUF_SIZE: src_buf[bytes_read] = 0
|
||||
|
||||
# 解析 AST
|
||||
# parse the AST
|
||||
lx: ast.Lexer* = ast.new_lexer(mb)
|
||||
if lx is None:
|
||||
stdlib.free(src_buf)
|
||||
@@ -115,7 +102,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.S
|
||||
stdlib.free(src_buf)
|
||||
continue
|
||||
|
||||
# 遍历 AST 顶层节点,收集 ClassDef
|
||||
# walk top-level AST nodes, collect ClassDef
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
cn: sizet = len(ch)
|
||||
@@ -128,7 +115,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.S
|
||||
cd: ast.ClassDef* = ast.ClassDef*(child)
|
||||
if cd is None or cd.name is None: continue
|
||||
|
||||
# 检测泛型类(有 type_params),记录文件索引以供 Phase 1a 预注册
|
||||
# detect generic classes (type_params); record the file index for Phase 1a
|
||||
if file_has_generic == 0:
|
||||
tp_scan: list[str]* = cd.type_params
|
||||
if tp_scan is not None:
|
||||
@@ -145,7 +132,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.S
|
||||
|
||||
if class_count >= Phase1.MAX_CLASSES: break
|
||||
|
||||
# 填充扁平数组:类名(偏移 = class_count * Phase1.CLASS_NAME_LEN)
|
||||
# fill flat array: class name (offset = class_count * Phase1.CLASS_NAME_LEN)
|
||||
name_dst: str = class_names_buf + class_count * Phase1.CLASS_NAME_LEN
|
||||
nm_len: sizet = string.strlen(cd.name)
|
||||
if nm_len >= Phase1.CLASS_NAME_LEN:
|
||||
@@ -153,11 +140,11 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.S
|
||||
name_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
|
||||
else: string.strcpy(name_dst, cd.name)
|
||||
|
||||
# 填充扁平数组:父类名(无父类填 '\0')
|
||||
# fill flat array: parent name (no parent -> '\0')
|
||||
parent_dst: str = parent_names_buf + class_count * Phase1.CLASS_NAME_LEN
|
||||
parent_dst[0] = '\0'
|
||||
parent_nm: str = GenApi._get_parent_class(cd, None)
|
||||
# parent_nm 是 AST 节点内部指针,不需要释放
|
||||
# parent_nm is an internal AST pointer; no need to free
|
||||
if parent_nm is not None:
|
||||
pnm_len: sizet = string.strlen(parent_nm)
|
||||
if pnm_len >= Phase1.CLASS_NAME_LEN:
|
||||
@@ -165,92 +152,75 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.S
|
||||
parent_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
|
||||
else: string.strcpy(parent_dst, parent_nm)
|
||||
|
||||
# 填充扁平数组:定义 SHA1(偏移 = class_count * Phase1.SHA1_LEN)
|
||||
# fill flat array: defining SHA1 (offset = class_count * Phase1.SHA1_LEN)
|
||||
sha1_dst: str = def_sha1s_buf + class_count * Phase1.SHA1_LEN
|
||||
string.strcpy(sha1_dst, sha1_e)
|
||||
|
||||
class_count += 1
|
||||
|
||||
stdlib.free(src_buf)
|
||||
# 回收本文件 AST/lexer/token(类名/父类/SHA1 已拷入扁平数组)
|
||||
# reclaim this file's AST/lexer/token (names/SHA1 already copied into flat arrays)
|
||||
mb.reset_to(scan_mark)
|
||||
|
||||
return class_count
|
||||
# ============================================================
|
||||
# _BufHasGenericClassMarker - 文本扫描检测文件是否含泛型类标记
|
||||
#
|
||||
# 泛型类(PEP 695)语法: class Name[T]:(类名后紧跟 '[')。逐字节扫描找词边界 'class' 关键字,其后标识符后非空白是 '[' → 命中。
|
||||
# 用于 _PreRegisterAllGenericTemplates 预注册前筛选,避免 parse 全部 98 个 includes 致 AST 驻留 mark1 前把 pool 推到 OOM。
|
||||
# 误报(字符串/注释里的 "class X[")无害(多 parse 一个文件);漏报有害(泛型模板未预注册 → 悬空复发),故保守只匹配单行形式。
|
||||
# ============================================================
|
||||
# _BufHasGenericClassMarker - text scan for a generic-class marker (PEP 695 "class
|
||||
# Name["). Used to pre-filter files before registering generic templates.
|
||||
def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
|
||||
"""文本扫描:buf 中是否有 'class 标识符[' 泛型类标记,返回 1/0"""
|
||||
"""Return 1 if buf contains a 'class identifier[' generic-class marker, else 0."""
|
||||
if buf is None or blen <= 6: return 0
|
||||
pos: sizet = 0
|
||||
while pos + 5 < blen:
|
||||
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' and buf[pos + 3] == 's' and buf[pos + 4] == 's':
|
||||
# class 前是词边界(行首或非标识符字符)
|
||||
# 'class' preceded by a word boundary (line start or non-identifier)
|
||||
prev_ok: int = 1
|
||||
if pos > 0:
|
||||
pc: char = buf[pos - 1]
|
||||
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
|
||||
if prev_ok != 0:
|
||||
p: sizet = pos + 5
|
||||
# 跳过空白
|
||||
# skip whitespace
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||
# 读类名标识符
|
||||
# read the class-name identifier
|
||||
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
|
||||
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or(buf[p] >= '0' and buf[p] <= '9') or buf[p] == '_'):
|
||||
p += 1
|
||||
# 类名后非空白是 '[' → 泛型类
|
||||
# non-space after the name is '[' -> generic class
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||
if p < blen and buf[p] == '[': return 1
|
||||
pos += 1
|
||||
else: pos += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _BufHasGenericFuncMarker - 文本扫描检测文件是否含泛型函数标记
|
||||
#
|
||||
# 泛型函数(PEP 695)语法: def name[(函数名后紧跟 '[',如 def f[T](...):)。
|
||||
# 与 _BufHasGenericClassMarker 同款逐字节词边界扫描;用于泛型函数模板的
|
||||
# 轻量预筛(避免为无泛型函数的文件 parse AST)。误报无害、漏报有害(模板未
|
||||
# 预注册),故保守只匹配单行形式。
|
||||
# ============================================================
|
||||
# _BufHasGenericFuncMarker - text scan for a generic-function marker (PEP 695
|
||||
# "def name["); cheap pre-filter before parsing for generic function templates.
|
||||
def _BufHasGenericFuncMarker(buf: bytes, blen: sizet) -> int:
|
||||
"""文本扫描:buf 中是否有 'def 标识符[' 泛型函数标记,返回 1/0"""
|
||||
"""Return 1 if buf contains a 'def identifier[' generic-function marker, else 0."""
|
||||
if buf is None or blen <= 4: return 0
|
||||
pos: sizet = 0
|
||||
while pos + 3 < blen:
|
||||
if buf[pos] == 'd' and buf[pos + 1] == 'e' and buf[pos + 2] == 'f':
|
||||
# def 前是词边界(行首或非标识符字符)
|
||||
# 'def' preceded by a word boundary (line start or non-identifier)
|
||||
prev_ok: int = 1
|
||||
if pos > 0:
|
||||
pc: char = buf[pos - 1]
|
||||
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
|
||||
if prev_ok != 0:
|
||||
p: sizet = pos + 3
|
||||
# 跳过空白
|
||||
# skip whitespace
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||
# 读函数名标识符
|
||||
# read the function-name identifier
|
||||
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
|
||||
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or(buf[p] >= '0' and buf[p] <= '9') or buf[p] == '_'):
|
||||
p += 1
|
||||
# 函数名后非空白是 '[' → 泛型函数
|
||||
# non-space after the name is '[' -> generic function
|
||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||
if p < blen and buf[p] == '[': return 1
|
||||
pos += 1
|
||||
else: pos += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _PreRegisterAllGenericTemplates - 预注册 includes 泛型类模板(mark1 前)
|
||||
#
|
||||
# 泛型模板表(GenericClassNames/GenericClassNodes/GenericClassSha1s/GenericClassFiles + 模板节点)是懒分配的跨文件持久表,
|
||||
# 首次注册必须落在 mark1 之前才能跨 Phase1 1b(reset_to(mark1))与 Phase2(reset_to(mark2))存活;否则表与节点被 reset 回收,
|
||||
# _find_generic_template 遍历悬空内存 strcmp 崩溃。本函数先文本扫描(_BufHasGenericClassMarker,不 parse)筛选出含泛型类的文件,
|
||||
# 仅对命中文件用**永久池(perm_pool)**解析(模板节点与表直接进 perm_pool,跨 mark 永久存活、不占主 pool);重复注册由 _register_generic_template 内部查重跳过。
|
||||
# ============================================================
|
||||
# _PreRegisterAllGenericTemplates - pre-register includes generic templates before
|
||||
# mark1 so the lazy tables survive later resets; parses only marker-matched files.
|
||||
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResult]) -> int:
|
||||
"""预注册全部 includes 文件的泛型类模板,返回处理到的泛型类数量"""
|
||||
"""Pre-register generic templates from all includes files and return the count."""
|
||||
if result is None or mb is None: return 0
|
||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if perm is None: return 0
|
||||
@@ -260,11 +230,11 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: rawptr[Include
|
||||
entry_addr_pg: uint64 = uint64(result.Entries) + i * p1e_size
|
||||
entry_pg: rawptr[IncludesScanner.FileEntry] = (rawptr[IncludesScanner.FileEntry])(entry_addr_pg)
|
||||
if entry_pg is None or entry_pg.Sha1 is None: continue
|
||||
# 读取文件
|
||||
# read the file
|
||||
file_path_pg: str = entry_pg.Path
|
||||
f_pg: rawptr[fileio.File] = fileio.File(file_path_pg, fileio.MODE.R)
|
||||
if f_pg.closed: continue
|
||||
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
|
||||
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
|
||||
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_pg}", "Phase1")
|
||||
f_pg.close()
|
||||
@@ -279,12 +249,12 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: rawptr[Include
|
||||
stdlib.free(src_buf_pg)
|
||||
continue
|
||||
if bytes_read_pg < Phase1.SRC_BUF_SIZE: src_buf_pg[bytes_read_pg] = 0
|
||||
# 文本扫描筛选:非泛型文件跳过(不 parse、不驻留,防 OOM)。
|
||||
# 泛型类(class X[)与泛型函数(def f[)任一命中即进入 parse。
|
||||
# Text-scan filter: skip non-generic files (no parse, no residency, avoids
|
||||
# OOM). A class or function marker hit selects the file for parsing.
|
||||
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0 and _BufHasGenericFuncMarker(src_buf_pg, bytes_read_pg) == 0:
|
||||
stdlib.free(src_buf_pg)
|
||||
continue
|
||||
# 泛型文件:永久池解析 → 注册模板(节点/表进 perm_pool,跨 reset 存活)
|
||||
# generic file: parse in perm_pool and register templates (survives reset)
|
||||
lx_pg: ast.Lexer* = ast.new_lexer(perm)
|
||||
if lx_pg is None:
|
||||
stdlib.free(src_buf_pg)
|
||||
@@ -295,7 +265,7 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: rawptr[Include
|
||||
if tree_pg is None:
|
||||
stdlib.free(src_buf_pg)
|
||||
continue
|
||||
# 遍历顶层 ClassDef/FunctionDef,注册泛型模板(type_params 非 None)
|
||||
# walk top-level ClassDef/FunctionDef, register generic templates (type_params != None)
|
||||
ch_pg: list[ast.AST*]* = tree_pg.children
|
||||
if ch_pg is not None:
|
||||
cn_pg: sizet = len(ch_pg)
|
||||
@@ -323,33 +293,20 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: rawptr[Include
|
||||
if handled_count > 0: VLogger.info(f"mark1 前永久池预注册 {handled_count} 个 includes 泛型模板(类+函数)", "Phase1")
|
||||
else: VLogger.info(f"mark1 前永久池预注册 0 个泛型模板(扫描 {result.Count} 个文件,可能 marker 未命中或全量扫描失败)", "Phase1")
|
||||
return handled_count
|
||||
# ============================================================
|
||||
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
|
||||
#
|
||||
# Args: class_names_buf 类名扁平数组;def_sha1s_buf 定义 SHA1 扁平数组;class_count 类数量;class_name 要查找的类名
|
||||
# Returns: SHA1 字符串指针(指向 def_sha1s_buf 内部偏移),None 未找到
|
||||
# ============================================================
|
||||
# _FindClassDefSha1 - look up a class name's defining file SHA1 in the flat arrays;
|
||||
# returns a pointer into def_sha1s_buf, or None if not found.
|
||||
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, class_name: str) -> str:
|
||||
"""查找类名对应的定义文件 SHA1"""
|
||||
"""Find a class name's defining file SHA1 in the flat arrays."""
|
||||
if class_names_buf is None or def_sha1s_buf is None or class_name is None: return None
|
||||
for i in range(class_count):
|
||||
name_ptr: str = class_names_buf + i * Phase1.CLASS_NAME_LEN
|
||||
if string.strcmp(name_ptr, class_name) == 0: return def_sha1s_buf + i * Phase1.SHA1_LEN
|
||||
return None
|
||||
# ============================================================
|
||||
# _ScanTreeForContainers - 递归扫描 AST 检测 list/dict 容器使用
|
||||
#
|
||||
# TPV 的 AST 中语句块挂在 children,而表达式/注解树全部以结构体字段挂载(value/slice/func/args/annotation/targets/returns/bases/left/right 等);
|
||||
# 只递归 children 会漏掉 list[...]*、list[...](...)、字段注解等非语句块位置,故每个节点先递归表达式承载字段、再递归 children。
|
||||
# 命中即置 need_list/need_dict;误报无害(最多多编译两个空壳模块)。
|
||||
# ============================================================
|
||||
# _ScanTreeForContainers - recursively scan the AST for list/dict container usage:
|
||||
# recurse expression/annotation struct fields first, then statement-block children.
|
||||
|
||||
def _AnnIsContainer(ann: ast.AST*) -> int:
|
||||
"""注解是否为 list/dict 容器(Name 裸名或 Subscript list[...]/dict[...])
|
||||
|
||||
数组初始化(t.CArray[...] / 裸数组注解)返回 0,避免 _ScanTreeForContainers
|
||||
把数组字面量误判为 list 容器使用(FIX 2026-08-30,联动 Translator.vp 同修)。
|
||||
"""
|
||||
"""Return 1 if the annotation is a list/dict container, else 0."""
|
||||
while ann is not None:
|
||||
ak_c: int = ann.kind()
|
||||
if ak_c == ast.ASTKind.Name:
|
||||
@@ -366,9 +323,9 @@ def _AnnIsContainer(ann: ast.AST*) -> int:
|
||||
|
||||
|
||||
def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr, depth: int = 0):
|
||||
"""递归扫描 AST(字段 + 语句块),检测 list/dict 容器使用"""
|
||||
# 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限
|
||||
# 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。
|
||||
"""Recursively scan the AST for list/dict container usage."""
|
||||
# Recursion depth guard: a self-referential AST cycle would recurse until the
|
||||
# stack is exhausted; fail-fast abort.
|
||||
if depth > 128: VLogger.error("容器扫描递归过深(AST 环或嵌套超过 128 层)", "Phase1")
|
||||
if node is None: return
|
||||
k_s: int = node.kind()
|
||||
@@ -465,13 +422,8 @@ def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box:
|
||||
nv_s: sizet = len(bvs_s)
|
||||
i_v2: sizet
|
||||
for i_v2 in range(nv_s): _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
|
||||
# `[...]`/`{}` 字面量不触发 _list/_dict 导入:字面量广泛用于编译期常量列表(c.Asm 的 op/inp/out CLOBBER 参数)与数组数据初始化,并非容器使用;
|
||||
# 容器的真实使用由类型位置检测覆盖(Name/Subscript 注解、Call.func 构造名)。
|
||||
# elif k_s == ast.ASTKind.List:
|
||||
# need_list_box[0] = 1
|
||||
# elif k_s == ast.ASTKind.Dict:
|
||||
# need_dict_box[0] = 1
|
||||
# 递归语句块子节点
|
||||
# `[...]`/`{}` literals do not trigger _list/_dict imports (compile-time constant lists, array init);
|
||||
# recurse into statement-block children.
|
||||
if node.children is not None:
|
||||
ch_s: list[ast.AST*]* = node.children
|
||||
n_s: sizet = len(ch_s)
|
||||
|
||||
@@ -27,32 +27,19 @@ import lib.Phase.Phase1.Phase1Scan as Phase1Scan
|
||||
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
|
||||
import lib.Phase.Phase1 as Phase1
|
||||
|
||||
# ============================================================
|
||||
# Phase1Topo - 按类继承与 import 依赖对源文件做拓扑排序
|
||||
#
|
||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
|
||||
# ============================================================
|
||||
# Phase1Topo - topologically sort source files by class inheritance and import dependencies.
|
||||
# Functions live in this module (accessed via its prefix); globals go through Phase1._xxx.
|
||||
|
||||
# ============================================================
|
||||
# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序(Kahn)
|
||||
#
|
||||
# 步骤:1) 从扁平类信息数组构建「文件 SHA1 → 依赖文件 SHA1 集合」映射;2) 计算每个文件入度(DepCount,初始化 InDegree = DepCount);
|
||||
# 3) 入度为 0 的先处理,处理后减少依赖它的文件入度;4) 重复直到全部处理完成。
|
||||
# 依赖方向是「父类所在文件 → 子类所在文件」,即父类文件必须先处理。
|
||||
#
|
||||
# Args: mb 内存池;result IncludesScanner 扫描结果;reachable_set 可达 SHA1 集合;reachable_count 可达 SHA1 数量;use_reachable 是否用可达集合过滤;
|
||||
# sha1_set SHA1 集合;set_count SHA1 集合数量;class_names_buf 类名扁平数组;parent_names_buf 父类名扁平数组;def_sha1s_buf 定义 SHA1 扁平数组;
|
||||
# class_count 类数量;out_order 输出拓扑顺序的文件索引数组(调用者分配,int 数组);out_count 输出文件数量
|
||||
# Returns: 0 成功,-1 失败
|
||||
# ============================================================
|
||||
# _TopoSortFiles - topologically sort files by class inheritance (Kahn).
|
||||
# Builds a "file SHA1 -> dependency SHA1 set" map, computes in-degrees (parent-file first), then outputs the order; returns 0 or -1.
|
||||
def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResult], reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, out_order: t.CPtr, out_count: t.CPtr, temp_dir: str, td_len_ts: sizet) -> int:
|
||||
"""根据类继承关系对文件进行拓扑排序"""
|
||||
"""Topologically sort files by class inheritance and import dependencies."""
|
||||
if result is None or out_order is None or out_count is None: return -1
|
||||
|
||||
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
|
||||
# 1. 分配扁平数组:文件 SHA1、依赖 SHA1 列表、依赖数量、入度、处理状态
|
||||
# 用扁平数组代替 FileDepEntry 结构体,避免 str 指针字段 NULL 崩溃
|
||||
# 1. Allocate flat arrays: file SHA1, dependency SHA1 list, dep counts, in-degrees, processed flags.
|
||||
# Flat arrays replace a FileDepEntry struct to avoid NULL str-pointer fields crashing.
|
||||
file_sha1s_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
||||
file_deps_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||
file_dep_counts: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
@@ -74,7 +61,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
string.memset(file_processed, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||
|
||||
file_count: int = 0
|
||||
# sha1→file_index 映射(简单线性查找,文件数不多)
|
||||
# sha1 -> file_index map (simple linear search; file count is small)
|
||||
for i in range(result.Count):
|
||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
|
||||
@@ -88,42 +75,42 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
|
||||
if file_count >= Phase1.MAX_FILE_DEPS: break
|
||||
|
||||
# 初始化扁平数组:文件 SHA1(偏移 = file_count * Phase1.SHA1_LEN)
|
||||
# DepCount/InDegree/Processed 已由 memset 清零
|
||||
# Initialize the flat array: file SHA1 (offset = file_count * Phase1.SHA1_LEN).
|
||||
# DepCount/InDegree/Processed are already zeroed by memset.
|
||||
sha1_dst: str = file_sha1s_buf + file_count * Phase1.SHA1_LEN
|
||||
string.strcpy(sha1_dst, sha1_e)
|
||||
|
||||
file_count += 1
|
||||
|
||||
# 2. 构建依赖边:对每个类,找到父类定义所在文件,建立 子文件→父文件 依赖
|
||||
# 2. Build dependency edges: for each class, find the parent's defining file and add a child->parent edge
|
||||
for ci in range(class_count):
|
||||
# 读取父类名(偏移 = ci * Phase1.CLASS_NAME_LEN)
|
||||
# Read parent class name (offset = ci * Phase1.CLASS_NAME_LEN)
|
||||
parent_ptr: str = parent_names_buf + ci * Phase1.CLASS_NAME_LEN
|
||||
if parent_ptr[0] == '\0': continue
|
||||
|
||||
# 读取子类定义 SHA1(偏移 = ci * Phase1.SHA1_LEN)
|
||||
# Read child class definition SHA1 (offset = ci * Phase1.SHA1_LEN)
|
||||
child_def_sha1: str = def_sha1s_buf + ci * Phase1.SHA1_LEN
|
||||
|
||||
# 查找父类定义所在文件的 SHA1
|
||||
# Find the SHA1 of the file defining the parent class
|
||||
parent_sha1: str = Phase1Scan._FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr)
|
||||
if parent_sha1 is None:
|
||||
continue # 父类不在 includes 中(如 Object、CEnum 等标记基类)
|
||||
continue # parent not in includes (e.g. Object, CEnum marker base classes)
|
||||
|
||||
# 父类文件 SHA1 不能等于子类文件 SHA1(同文件内继承不需要依赖)
|
||||
# The parent file SHA1 must differ from the child's (same-file inheritance needs no dependency)
|
||||
if string.strcmp(parent_sha1, child_def_sha1) == 0: continue
|
||||
|
||||
# 找到子类文件对应的索引,添加父文件 SHA1 到依赖列表
|
||||
# Find the child file's index and add the parent file SHA1 to its dependency list
|
||||
for fi in range(file_count):
|
||||
fsha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
||||
if string.strcmp(fsha1, child_def_sha1) != 0: continue
|
||||
|
||||
# 读取当前依赖数量
|
||||
# Read the current dependency count
|
||||
cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4
|
||||
cnt_ptr: t.CPtr = (int*)(cnt_addr)
|
||||
if cnt_ptr is None: break
|
||||
cur_cnt: int = cnt_ptr[0]
|
||||
|
||||
# 检查依赖是否已添加(去重)
|
||||
# Check whether the dependency is already present (dedupe)
|
||||
already: int = 0
|
||||
for di in range(cur_cnt):
|
||||
dep_slot: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
||||
@@ -138,11 +125,8 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
cnt_ptr[0] = cur_cnt + 1
|
||||
break
|
||||
|
||||
# 2.5 补充 import 依赖边:被 import 的模块先完整翻译。
|
||||
#
|
||||
# 仅按类继承建边不够:跨模块全局变量 stub、跨模块 vtable TypeStr、泛型模板都要求依赖模块先翻译,而它们之间可能无类继承关系。
|
||||
# deps(_includes_deps_*)由 Phase 1a-pre 填充(含容器按需注入的 _list/_dict);用 _BuildIncludesSha1Map 复制局部 sha1→模块名 数组
|
||||
# (与 _BuildReachableSha1Set 的 BFS 一致),避免直接跨函数传全局 store 返回值导致地址异常。
|
||||
# 2.5 Add import dependency edges: imported modules must be fully translated first.
|
||||
# Class inheritance alone is insufficient (cross-module globals/vtable TypeStr/generics need deps); deps come from Phase 1a-pre, mapped via _BuildIncludesSha1Map.
|
||||
sha1_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
mod_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
map_count_ts: int = 0
|
||||
@@ -168,11 +152,11 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
dsha1_imp: str = StubMergerIR._FindSha1ByModName(sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp)
|
||||
stdlib.free(dname_imp)
|
||||
if dsha1_imp is None: continue
|
||||
# 找依赖文件在列表中的索引(跳过自身)
|
||||
# Find the dependency file's index in the list (skip self)
|
||||
for fj_imp in range(file_count):
|
||||
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0: continue
|
||||
if fj_imp == fi_imp: break
|
||||
# 去重 + 加边:fi_imp 依赖 fj_imp(InDegree[fi_imp]++)
|
||||
# Dedupe and add the edge: fi_imp depends on fj_imp (InDegree[fi_imp]++)
|
||||
cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4
|
||||
cnt_ptr_imp: t.CPtr = (int*)(cnt_addr_imp)
|
||||
if cnt_ptr_imp is None: break
|
||||
@@ -189,12 +173,12 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
cnt_ptr_imp[0] = cur_cnt_imp + 1
|
||||
break
|
||||
|
||||
# 释放 import 边用的局部映射数组
|
||||
# Free the local mapping arrays used for import edges
|
||||
if sha1_arr_ts is not None: stdlib.free(sha1_arr_ts)
|
||||
if mod_arr_ts is not None: stdlib.free(mod_arr_ts)
|
||||
|
||||
# 3. Kahn 算法:InDegree = DepCount(依赖多少个父文件)
|
||||
# 依赖为 0 的先处理,处理后减少依赖它的文件入度
|
||||
# 3. Kahn: InDegree = DepCount (number of parent files depended on).
|
||||
# Process zero-dependency files first, then decrement the in-degree of dependents.
|
||||
for fi in range(file_count):
|
||||
cnt_addr3: uint64 = uint64(file_dep_counts) + fi * 4
|
||||
cnt_ptr3: t.CPtr = (int*)(cnt_addr3)
|
||||
@@ -202,46 +186,46 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
ind_ptr3: t.CPtr = (int*)(ind_addr3)
|
||||
if cnt_ptr3 is not None and ind_ptr3 is not None: ind_ptr3[0] = cnt_ptr3[0]
|
||||
|
||||
# 4. 拓扑排序输出
|
||||
# 4. Topological sort output
|
||||
out_idx: int = 0
|
||||
changed: int = 1
|
||||
while changed != 0:
|
||||
changed = 0
|
||||
for fi in range(file_count):
|
||||
# 检查处理状态
|
||||
# Check processed flag
|
||||
proc_addr4: uint64 = uint64(file_processed) + fi * 4
|
||||
proc_ptr4: t.CPtr = (int*)(proc_addr4)
|
||||
if proc_ptr4 is None or proc_ptr4[0] != 0: continue
|
||||
# 检查入度
|
||||
# Check in-degree
|
||||
ind_addr4: uint64 = uint64(file_indegrees) + fi * 4
|
||||
ind_ptr4: t.CPtr = (int*)(ind_addr4)
|
||||
if ind_ptr4 is None or ind_ptr4[0] > 0: continue
|
||||
|
||||
# 入度为 0,可以处理
|
||||
# 找到对应的 result.Entries 索引
|
||||
# In-degree 0: can be processed
|
||||
# Find the matching result.Entries index
|
||||
target_sha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
||||
for ri in range(result.Count):
|
||||
ea_r: uint64 = uint64(result.Entries) + ri * entry_size
|
||||
ent_r: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea_r)
|
||||
if ent_r is not None and ent_r.Sha1 is not None:
|
||||
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
|
||||
# 写入输出索引数组
|
||||
# Write into the output index array
|
||||
out_idx_addr: uint64 = uint64(out_order) + out_idx * 4
|
||||
out_int_ptr: t.CPtr = (int*)(out_idx_addr)
|
||||
if out_int_ptr is not None: out_int_ptr[0] = ri
|
||||
out_idx += 1
|
||||
break
|
||||
|
||||
# 标记已处理
|
||||
# Mark as processed
|
||||
proc_ptr4[0] = 1
|
||||
changed = 1
|
||||
|
||||
# 减少依赖该文件的其他文件的入度
|
||||
# Decrement the in-degree of files that depend on this one
|
||||
for fj in range(file_count):
|
||||
proc_addr5: uint64 = uint64(file_processed) + fj * 4
|
||||
proc_ptr5: t.CPtr = (int*)(proc_addr5)
|
||||
if proc_ptr5 is None or proc_ptr5[0] != 0: continue
|
||||
# 读取 fj 的依赖数量
|
||||
# Read fj's dependency count
|
||||
cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4
|
||||
cnt_ptr5: t.CPtr = (int*)(cnt_addr5)
|
||||
if cnt_ptr5 is None: continue
|
||||
@@ -254,10 +238,8 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
if ind_ptr5 is not None: ind_ptr5[0] = ind_ptr5[0] - 1
|
||||
break
|
||||
|
||||
# 处理循环依赖:环内优先输出被依赖者(被其余剩余文件依赖数量多者先)。
|
||||
#
|
||||
# 容器按需注入(string 用 list 泛型 → 依赖 _list)与真实 import 会形成伪环,Kahn 无法完全归零;若按扫描顺序追加,
|
||||
# 依赖者会先于被依赖者翻译 → 跨模块 vtable TypeStr 未注册 → 误杀。
|
||||
# Handle cycles: within a cycle emit the more-depended-upon files first.
|
||||
# On-demand container injection (e.g. string -> _list) plus real imports form pseudo-cycles Kahn cannot fully drain; scan order would translate dependents first -> unregistered cross-module vtable TypeStr.
|
||||
if out_idx < file_count:
|
||||
rem_idx: t.CPtr = stdlib.malloc(4 * file_count)
|
||||
rem_depcnt: t.CPtr = stdlib.malloc(4 * file_count)
|
||||
@@ -270,7 +252,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
rem_idx[rem_count] = fi
|
||||
rem_depcnt[rem_count] = 0
|
||||
rem_count += 1
|
||||
# 计算每个剩余文件的被依赖数(被其余剩余文件依赖)
|
||||
# Compute each remaining file's dependents count (among the remaining files)
|
||||
for i_a in range(rem_count):
|
||||
fi_a: int = rem_idx[i_a]
|
||||
target_sha1_a: str = file_sha1s_buf + fi_a * Phase1.SHA1_LEN
|
||||
@@ -286,7 +268,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
if string.strcmp(dep_slot_b, target_sha1_a) == 0:
|
||||
rem_depcnt[i_a] = rem_depcnt[i_a] + 1
|
||||
break
|
||||
# 冒泡降序(被依赖数多者在前)
|
||||
# Bubble sort descending (more dependents first)
|
||||
for i_a in range(rem_count):
|
||||
for j_a in range(rem_count - 1):
|
||||
if rem_depcnt[j_a] < rem_depcnt[j_a + 1]:
|
||||
@@ -296,7 +278,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
tmp_d: int = rem_depcnt[j_a]
|
||||
rem_depcnt[j_a] = rem_depcnt[j_a + 1]
|
||||
rem_depcnt[j_a + 1] = tmp_d
|
||||
# 按序输出剩余文件
|
||||
# Emit the remaining files in order
|
||||
for i_a in range(rem_count):
|
||||
fi_a2: int = rem_idx[i_a]
|
||||
proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4
|
||||
@@ -317,11 +299,11 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: rawptr[IncludesScanner.ScanResu
|
||||
stdlib.free(rem_idx)
|
||||
stdlib.free(rem_depcnt)
|
||||
|
||||
# 写入输出数量
|
||||
# Write the output count
|
||||
out_count_ptr: t.CPtr = (int*)(out_count)
|
||||
if out_count_ptr is not None: out_count_ptr[0] = out_idx
|
||||
|
||||
# 释放扁平数组
|
||||
# Free the flat arrays
|
||||
mb.free(file_sha1s_buf)
|
||||
mb.free(file_deps_buf)
|
||||
mb.free(file_dep_counts)
|
||||
|
||||
@@ -2,18 +2,17 @@ import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# 源代码缓冲区大小(1MB)
|
||||
# Source buffer size (1MB)
|
||||
define SRC_BUF_SIZE = 1048576
|
||||
|
||||
# vpi 缓冲区大小(256KB)
|
||||
# 编译期常量,用 define 而非模块级全局:模块级同名全局会在多个模块各发
|
||||
# 一份强定义符号(@PYI_BUF_SIZE),llvm-link 报 symbol multiply defined。
|
||||
# vpi buffer size (256KB).
|
||||
# Compile-time constant via define: a module-level global would emit a strong symbol per module, causing llvm-link errors.
|
||||
define PYI_BUF_SIZE = 262144
|
||||
|
||||
# 拓扑排序相关常量
|
||||
# Topological sort constants
|
||||
define MAX_CLASSES = 512
|
||||
define MAX_FILE_DEPS = 256
|
||||
define MAX_DEP_EDGES = 16
|
||||
|
||||
+221
-276
File diff suppressed because it is too large.
Load diff
+88
-180
@@ -11,57 +11,42 @@ from json import JsonValue, JSON_OBJECT, JSON_INT
|
||||
import w32.fileio as fileio
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Config - 工程配置
|
||||
# ============================================================
|
||||
# Config - project configuration
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: rawptr[memhub.MemManager]
|
||||
|
||||
# 配置值(从 project.vpj 加载);默认 None/0:未加载时字段为 null,调用方须 is not None 检查兜底(否则垃圾指针 → 崩溃)
|
||||
# Config values (loaded from project.vpj); default None/0: callers must guard with `is not None` (else garbage pointer crash)
|
||||
SourceDir: str = None
|
||||
BuildDir: str = None
|
||||
TempDir: str = None # = {BuildDir}/temp(自动计算)
|
||||
OutputDir: str = None # = {BuildDir}/output(自动计算)
|
||||
TempDir: str = None # = {BuildDir}/temp (auto-computed)
|
||||
OutputDir: str = None # = {BuildDir}/output (auto-computed)
|
||||
ProjectName: str = None
|
||||
ProjectVersion: str = None
|
||||
CompilerCmd: str = None
|
||||
CompilerFlags: str = None # 编译器参数(空格分隔,从 compiler.flags 数组拼接)
|
||||
CompilerFlags: str = None # compiler flags (space-separated, joined from compiler.flags array)
|
||||
LinkerCmd: str = None
|
||||
LinkerFlags: str = None # 链接器参数(空格分隔,从 linker.flags 数组拼接)
|
||||
LinkerFlags: str = None # linker flags (space-separated, joined from linker.flags array)
|
||||
LinkerOutput: str = None
|
||||
LinkerStaticLibs: str = None # 显式静态链接文件(空格分隔绝对路径,从 linker.static_libs 数组)
|
||||
LinkerDynamicLibs: str = None # 显式动态链接文件(空格分隔绝对路径,从 linker.dynamic_libs 数组,.dll/.so)
|
||||
LinkerStaticLibs: str = None # explicit static libs (space-separated abs paths, from linker.static_libs array)
|
||||
LinkerDynamicLibs: str = None # explicit dynamic libs (space-separated abs paths, from linker.dynamic_libs array, .dll/.so)
|
||||
TargetTriple: str = None
|
||||
TargetDataLayout: str = None
|
||||
ProjectDir: str = None # project.vpj 所在目录(resolve_paths 设置)
|
||||
Sha1SliceLevel: int = 0 # SHA1 切片层数(目录分散等级)
|
||||
ProjectDir: str = None # directory containing project.vpj (set by resolve_paths)
|
||||
Sha1SliceLevel: int = 0 # SHA1 slice level (directory dispersion depth)
|
||||
StrictMode: int = 0
|
||||
IncludesDir: str = None # includes 目录路径(从 includes 数组首元素,兼容单目录场景)
|
||||
IncludesDirs: str = None # 全部 includes 目录(空格分隔,多 includes 目录支持)
|
||||
IncludesDir: str = None # includes dir path (first element of includes array, single-dir compat)
|
||||
IncludesDirs: str = None # all includes dirs (space-separated, multi-dir support)
|
||||
|
||||
# includes 预编译缓存目录后缀(asan 模式用 .binary_asan 隔离 asan 插桩 obj,避免与非 asan obj 混链)
|
||||
# includes prebuilt cache dir suffix (asan mode uses .binary_asan to isolate instrumented obj)
|
||||
_inc_bin_suffix: str = ".binary"
|
||||
|
||||
# ============================================================
|
||||
# 发射后端:**单端 MLIR**(2026-10-03 起)
|
||||
#
|
||||
# 全树发射器已由 includes/llvmlite 改为 includes/mlirlite(`import mlirlite`),
|
||||
# 不再有 `--backend` 开关、不再有模块别名:mlirlite 发射的是 MLIR 文本,
|
||||
# 故 BuildPipeline 恒在 opt/llc 之前插入
|
||||
# mlir-opt(校验)→ mlir-translate --mlir-to-llvmir(降级回 LLVM IR)。
|
||||
# ============================================================
|
||||
# Emission backend: single-track MLIR (since 2026-10-03).
|
||||
# The tree now uses includes/mlirlite; BuildPipeline always runs mlir-opt -> mlir-translate to lower back to LLVM IR.
|
||||
|
||||
|
||||
def Load_project_config(path: str) -> int:
|
||||
"""加载工程配置文件(project.vpj)
|
||||
|
||||
Args:
|
||||
path: 配置文件路径
|
||||
|
||||
Returns:
|
||||
0 表示成功,非 0 表示失败
|
||||
"""
|
||||
"""Load the project config file (project.vpj); 0=success, non-zero=failure."""
|
||||
global SourceDir, BuildDir, TempDir, OutputDir, ProjectName, ProjectVersion
|
||||
global CompilerCmd, CompilerFlags, LinkerCmd, LinkerFlags, LinkerOutput, LinkerStaticLibs, LinkerDynamicLibs
|
||||
global TargetTriple, TargetDataLayout, Sha1SliceLevel, StrictMode
|
||||
@@ -70,12 +55,12 @@ def Load_project_config(path: str) -> int:
|
||||
if path is None: return 1
|
||||
if _mbuddy is None: return 1
|
||||
|
||||
# 打开文件
|
||||
# Open file
|
||||
f: rawptr[fileio.File] = fileio.File(path, fileio.MODE.R)
|
||||
if f is None: return 1
|
||||
if f.closed: return 1
|
||||
|
||||
# 分配读取缓冲区
|
||||
# Allocate read buffer
|
||||
CFG_BUF_SIZE: sizet = 8192
|
||||
buf: bytes = _mbuddy.alloc(CFG_BUF_SIZE)
|
||||
if buf is None:
|
||||
@@ -87,15 +72,15 @@ def Load_project_config(path: str) -> int:
|
||||
|
||||
if bytes_read <= 0: return 1
|
||||
|
||||
# 解析 JSON
|
||||
# Parse JSON
|
||||
root: JsonValue* = json_parse(_mbuddy, buf)
|
||||
if root is None: return 1
|
||||
if not root.is_object(): return 1
|
||||
|
||||
# 读取顶层字段(使用显式 __getitem__ 调用,兼容旧编译器二进制)
|
||||
# Read top-level fields (explicit __getitem__ calls for old compiler binaries)
|
||||
sd_val: JsonValue* = root["source_dir"]
|
||||
SourceDir = sd_val.as_string() if sd_val is not None else None
|
||||
# build_dir: 统一构建目录(默认 ./.tpv_build),temp 和 output 作为其子目录
|
||||
# build_dir: unified build dir (default ./.tpv_build); temp and output are subdirs
|
||||
bd_val: JsonValue* = root["build_dir"]
|
||||
if bd_val is not None and bd_val.is_string(): BuildDir = bd_val.as_string()
|
||||
else: BuildDir = "./.tpv_build"
|
||||
@@ -104,35 +89,35 @@ def Load_project_config(path: str) -> int:
|
||||
ver_val: JsonValue* = root["version"]
|
||||
ProjectVersion = ver_val.as_string() if ver_val is not None else None
|
||||
|
||||
# 读取 compiler 子对象
|
||||
# Read compiler sub-object
|
||||
compiler: JsonValue* = root["compiler"]
|
||||
if compiler is not None and compiler.is_object():
|
||||
cc_cmd_val: JsonValue* = compiler["cmd"]
|
||||
CompilerCmd = cc_cmd_val.as_string() if cc_cmd_val is not None else None
|
||||
# 读取 compiler.flags 数组,拼接成空格分隔的字符串
|
||||
# Read compiler.flags array, join into a space-separated string
|
||||
cc_flags_val: JsonValue* = compiler["flags"]
|
||||
CompilerFlags = _join_flags_array(cc_flags_val)
|
||||
|
||||
# 读取 linker 子对象
|
||||
# Read linker sub-object
|
||||
linker: JsonValue* = root["linker"]
|
||||
if linker is not None and linker.is_object():
|
||||
ld_cmd_val: JsonValue* = linker["cmd"]
|
||||
LinkerCmd = ld_cmd_val.as_string() if ld_cmd_val is not None else None
|
||||
ld_out_val: JsonValue* = linker["output"]
|
||||
LinkerOutput = ld_out_val.as_string() if ld_out_val is not None else None
|
||||
# 读取 linker.flags 数组,拼接成空格分隔的字符串
|
||||
# Read linker.flags array, join into a space-separated string
|
||||
ld_flags_val: JsonValue* = linker["flags"]
|
||||
LinkerFlags = _join_flags_array(ld_flags_val)
|
||||
# 读取 linker.static_libs 数组:显式静态链接文件(.a/.obj),
|
||||
# 先拼接原样字符串,resolve_paths 中再基于 project_dir 解析绝对路径
|
||||
# Read linker.static_libs array: explicit static libs (.a/.obj); joined raw here,
|
||||
# then resolved to abs paths against project_dir in resolve_paths.
|
||||
sl_val: JsonValue* = linker["static_libs"]
|
||||
LinkerStaticLibs = _join_flags_array(sl_val)
|
||||
# 读取 linker.dynamic_libs 数组:显式动态链接文件(.dll/.so),
|
||||
# 同样先拼接原样字符串,resolve_paths 中基于 project_dir 解析绝对路径
|
||||
# Read linker.dynamic_libs array: explicit dynamic libs (.dll/.so); joined raw here,
|
||||
# then resolved to abs paths against project_dir in resolve_paths.
|
||||
dl_val: JsonValue* = linker["dynamic_libs"]
|
||||
LinkerDynamicLibs = _join_flags_array(dl_val)
|
||||
|
||||
# 读取 target 子对象
|
||||
# Read target sub-object
|
||||
target: JsonValue* = root["target"]
|
||||
if target is not None and target.is_object():
|
||||
tgt_triple_val: JsonValue* = target["triple"]
|
||||
@@ -140,8 +125,8 @@ def Load_project_config(path: str) -> int:
|
||||
tgt_dl_val: JsonValue* = target["datalayout"]
|
||||
TargetDataLayout = tgt_dl_val.as_string() if tgt_dl_val is not None else None
|
||||
|
||||
# 读取 options 子对象
|
||||
# Sha1SliceLevel: SHA1 切片层数(默认 1,每层取 SHA1 前 2 字符作为子目录:0=不切片, 1=b7/, 2=b7/90/)
|
||||
# Read options sub-object
|
||||
# Sha1SliceLevel: SHA1 slice level (default 1; each level uses 2 SHA1 chars: 0=none, 1=b7/, 2=b7/90/)
|
||||
options: JsonValue* = root["options"]
|
||||
Sha1SliceLevel = 1
|
||||
StrictMode = 1
|
||||
@@ -151,7 +136,7 @@ def Load_project_config(path: str) -> int:
|
||||
sm: JsonValue* = options["strict_mode"]
|
||||
if sm is not None and sm.is_bool(): StrictMode = 1 if sm.as_bool() else 0
|
||||
|
||||
# 读取 includes 数组(支持多目录:首元素存 IncludesDir,全部拼接存 IncludesDirs)
|
||||
# Read includes array (multi-dir: first element -> IncludesDir, all joined -> IncludesDirs)
|
||||
includes_val: JsonValue* = root["includes"]
|
||||
if includes_val is not None and includes_val.is_array():
|
||||
arr_len: sizet = len(includes_val)
|
||||
@@ -165,7 +150,7 @@ def Load_project_config(path: str) -> int:
|
||||
inc_str: str = inc_item.as_string()
|
||||
inc_slen: sizet = string.strlen(inc_str)
|
||||
if inc_i == 0: IncludesDir = inc_str
|
||||
# 拼接进 IncludesDirs(空格分隔)
|
||||
# Append into IncludesDirs (space-separated)
|
||||
if inc_pos + inc_slen + 1 < 2048:
|
||||
if inc_pos > 0:
|
||||
inc_buf[inc_pos] = ' '
|
||||
@@ -180,24 +165,14 @@ def Load_project_config(path: str) -> int:
|
||||
|
||||
|
||||
def _join_flags_array(flags_val: JsonValue*) -> str:
|
||||
"""将 JSON 字符串数组拼接成空格分隔的字符串
|
||||
|
||||
用于 linker.flags 和 compiler.flags 数组。
|
||||
用 while + 索引遍历,避免 list 迭代器 bug。
|
||||
|
||||
Args:
|
||||
flags_val: JsonValue,应为字符串数组
|
||||
|
||||
Returns:
|
||||
空格分隔的字符串,或 None 如果 flags_val 为 None 或非数组或空数组
|
||||
"""
|
||||
"""Join a JSON string array into a space-separated string; None if empty/invalid."""
|
||||
if flags_val is None: return None
|
||||
if not flags_val.is_array(): return None
|
||||
|
||||
arr_len: sizet = len(flags_val)
|
||||
if arr_len == 0: return None
|
||||
|
||||
# 第一遍:计算总长度(所有 flag 长度 + 空格分隔符)
|
||||
# First pass: compute total length (all flags + space separators)
|
||||
total_len: sizet = 0
|
||||
for i in range(arr_len):
|
||||
item: JsonValue* = flags_val.get_item(i)
|
||||
@@ -208,11 +183,11 @@ def _join_flags_array(flags_val: JsonValue*) -> str:
|
||||
|
||||
if total_len == 0: return None
|
||||
|
||||
# 分配缓冲区(total_len 已含每个 flag 后的空格,最后一个是 '\0')
|
||||
# Allocate buffer (total_len includes a trailing space per flag; last one becomes '\0')
|
||||
buf: str = _mbuddy.alloc(total_len + 1)
|
||||
if buf is None: return None
|
||||
|
||||
# 第二遍:拼接(flag 之间用空格分隔)
|
||||
# Second pass: join (flags separated by spaces)
|
||||
pos: sizet = 0
|
||||
for i in range(arr_len):
|
||||
item = flags_val.get_item(i)
|
||||
@@ -230,31 +205,15 @@ def _join_flags_array(flags_val: JsonValue*) -> str:
|
||||
|
||||
|
||||
def _join_path(rel: str, project_dir: str) -> str:
|
||||
"""将相对路径与 project_dir 拼接为绝对路径
|
||||
|
||||
- ./App + Test → Test/App
|
||||
- ./temp + Test → Test/temp
|
||||
- ../includes + Test → Test/../includes(OS 解析为 includes)
|
||||
- 绝对路径保持不变
|
||||
- None 返回 None
|
||||
|
||||
拼接结果经 _normalize_path 规范化:./Test/../includes → ./includes,
|
||||
消除 .. 段,保证路径显示干净且相对寻址正确。
|
||||
|
||||
含 %VAR% 的路径(如 repl.vpj 的 build_dir = %temp%/viperepl/.tpv_build)
|
||||
先经 fileio 路径层展开为实路径:TempDir/OutputDir 等由 BuildDir 派生,
|
||||
且后续消费者不止文件打开(ensure_dir 直接调 CreateDirectoryA、
|
||||
collect_obj_files 调 FindFirstFileA、命令串走 CreateProcessA),
|
||||
故展开必须发生在**配置路径设置层**而不是逐个调用点。
|
||||
"""
|
||||
"""Join a relative path with project_dir, expanding env vars and normalizing it."""
|
||||
if rel is None: return None
|
||||
# 环境变量展开:仅在含 '%' 时生效(fileio.ExpandEnvironmentPath 自带
|
||||
# "失败/无 '%' 返回 None" 契约),展开失败时保持原串不变
|
||||
# Env var expansion: only when '%' present (fileio.ExpandEnvironmentPath returns None on
|
||||
# failure/no '%'); on failure keep the original string unchanged.
|
||||
ep: str = fileio.ExpandEnvironmentPath(rel)
|
||||
if ep is not None: rel = ep
|
||||
# 绝对路径(以 / 或 \ 开头)保持不变
|
||||
# Absolute paths (starting with / or \) stay unchanged
|
||||
if rel[0] == '/' or rel[0] == '\\': return rel
|
||||
# 路径已含 project_dir 前缀时不再拼接,避免双份路径(./TransPyV/TransPyV/.tpv_build2);边界取到目录分隔符或串尾
|
||||
# Skip joining when rel already has the project_dir prefix (avoids double paths); boundary at separator or end
|
||||
if project_dir is not None:
|
||||
pdl: sizet = string.strlen(project_dir)
|
||||
if pdl > 0 and string.strncmp(rel, project_dir, pdl) == 0:
|
||||
@@ -263,7 +222,7 @@ def _join_path(rel: str, project_dir: str) -> str:
|
||||
rel_len: sizet = string.strlen(rel)
|
||||
dir_len: sizet = string.strlen(project_dir)
|
||||
|
||||
# 处理 ../ 或 ..\\ 前缀:拼接 project_dir/../...
|
||||
# Handle ../ or ..\ prefix: join project_dir/../...
|
||||
if rel_len >= 3 and rel[0] == '.' and rel[1] == '.':
|
||||
if rel[2] == '/' or rel[2] == '\\':
|
||||
if dir_len == 0: return rel
|
||||
@@ -273,42 +232,36 @@ def _join_path(rel: str, project_dir: str) -> str:
|
||||
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel)
|
||||
return _normalize_path(buf)
|
||||
|
||||
# 处理 ./ 或 .\\ 前缀:拼接 project_dir + rel[2:]
|
||||
# Handle ./ or .\ prefix: join project_dir + rel[2:]
|
||||
rel_off: sizet = 0
|
||||
if rel_len >= 2 and rel[0] == '.':
|
||||
if rel[1] == '/' or rel[1] == '\\': rel_off = 2
|
||||
else:
|
||||
return rel # 不是 ./ 开头,保持不变
|
||||
return rel # not starting with ./: keep unchanged
|
||||
else:
|
||||
return rel # 不是 . 开头,保持不变
|
||||
return rel # not starting with .: keep unchanged
|
||||
|
||||
if dir_len == 0:
|
||||
# project_dir 为空(当前目录),去掉 ./ 前缀即可
|
||||
# project_dir empty (current dir): just strip the ./ prefix
|
||||
return rel + rel_off
|
||||
# 拼接: project_dir + "/" + rel[rel_off:]
|
||||
# Join: project_dir + "/" + rel[rel_off:]
|
||||
total_len = dir_len + 1 + (rel_len - rel_off) + 1
|
||||
buf = _mbuddy.alloc(total_len)
|
||||
if buf is None:
|
||||
return rel # 分配失败,返回原路径
|
||||
return rel # allocation failed: return original path
|
||||
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel + rel_off)
|
||||
return _normalize_path(buf)
|
||||
|
||||
|
||||
def _normalize_path(path: str) -> str:
|
||||
"""规范化路径:解析 . 和 .. 段,合并多余分隔符
|
||||
|
||||
例: ./test/BuddyTest/../../includes → ./includes
|
||||
./test/BuddyTest/App → ./test/BuddyTest/App
|
||||
绝对路径(/ 或 \\ 开头,含盘符 C:)保持不变。
|
||||
返回 _mbuddy 分配的字符串;失败时返回原路径。
|
||||
"""
|
||||
"""Normalize a path by resolving . and .. segments; absolute paths pass through."""
|
||||
if path is None: return None
|
||||
plen: sizet = string.strlen(path)
|
||||
if plen == 0: return path
|
||||
# 绝对路径(/ 或 \ 开头,或盘符如 C:)不规范化
|
||||
# Absolute paths (/ or \ start, or drive letter like C:) are not normalized
|
||||
if path[0] == '/' or path[0] == '\\': return path
|
||||
if plen >= 2 and path[1] == ':': return path
|
||||
# 输出缓冲(最坏情况 = 输入长度 + 可能的前缀 ./)
|
||||
# Output buffer (worst case = input length + possible ./ prefix)
|
||||
out: str = _mbuddy.alloc(plen + 3)
|
||||
if out is None: return path
|
||||
seg_start: sizet = 0
|
||||
@@ -321,26 +274,26 @@ def _normalize_path(path: str) -> str:
|
||||
if ch == '/' or ch == '\\' or ch == '\0':
|
||||
seg_len: sizet = i - seg_start
|
||||
if seg_len > 0:
|
||||
# 单段 ".":跳过
|
||||
# Single "." segment: skip
|
||||
if seg_len == 1 and path[seg_start] == '.': pass
|
||||
# 单段 "..":弹出上一段;前导 ".." 无段可弹时必须保留(对齐 Python os.path.normpath)
|
||||
# Single ".." segment: pop previous; leading ".." with nothing to pop must be kept (like Python normpath)
|
||||
elif seg_len == 2 and path[seg_start] == '.' and path[seg_start + 1] == '.':
|
||||
# 末段是否已经是前导 ".."(独立一段:要么是开头,要么前面是分隔符)
|
||||
# Whether the last segment is already a leading ".." (standalone: at start or preceded by a separator)
|
||||
last_is_dotdot: int = 0
|
||||
if out_len >= 2 and out[out_len - 1] == '.' and out[out_len - 2] == '.':
|
||||
if out_len == 2: last_is_dotdot = 1
|
||||
elif out[out_len - 3] == '/' or out[out_len - 3] == '\\': last_is_dotdot = 1
|
||||
if out_len > 0 and last_is_dotdot == 0:
|
||||
# 回退到上一个分隔符
|
||||
# Back up to the previous separator
|
||||
k: sizet = out_len
|
||||
while k > 0 and out[k - 1] != '/' and out[k - 1] != '\\': k -= 1
|
||||
if k > 0: out_len = k - 1
|
||||
else: out_len = 0
|
||||
# 仍有剩余段时保持分隔符逻辑
|
||||
# Keep separator logic when segments remain
|
||||
if out_len > 0: has_seg = 1
|
||||
else: has_seg = 0
|
||||
else:
|
||||
# 没有普通段可弹:把 ".." 当普通段追加(前导 .. 累积保留)
|
||||
# No normal segment to pop: append ".." as a normal segment (keep leading ..)
|
||||
if has_seg > 0:
|
||||
out[out_len] = '/'
|
||||
out_len += 1
|
||||
@@ -349,7 +302,7 @@ def _normalize_path(path: str) -> str:
|
||||
out_len += 2
|
||||
has_seg = 1
|
||||
else:
|
||||
# 普通段:追加分隔符 + 段
|
||||
# Normal segment: append separator + segment
|
||||
if has_seg > 0:
|
||||
out[out_len] = '/'
|
||||
out_len += 1
|
||||
@@ -362,11 +315,11 @@ def _normalize_path(path: str) -> str:
|
||||
seg_start = i + 1
|
||||
i += 1
|
||||
if has_seg == 0:
|
||||
# 全部段被消掉(如 ./..):返回当前目录
|
||||
# All segments consumed (e.g. ./..): return current dir
|
||||
return "./"
|
||||
# 相对路径补回 ./ 前缀(与原路径风格一致;以 .. 开头的除外)
|
||||
# Re-add ./ prefix for relative paths (matches original style; except those starting with ..)
|
||||
if out[0] != '.' and out[0] != '/':
|
||||
# 右移 2 位留出 "./"(从后往前拷贝,避免覆盖源字符)
|
||||
# Shift right by 2 to make room for "./" (copy back-to-front to avoid overwriting)
|
||||
mv: sizet = out_len
|
||||
while mv > 0:
|
||||
mv -= 1
|
||||
@@ -379,17 +332,7 @@ def _normalize_path(path: str) -> str:
|
||||
|
||||
|
||||
def resolve_paths(project_dir: str) -> int:
|
||||
"""解析并规范化工程路径(src/build/includes)
|
||||
|
||||
将 project.vpj 中的相对路径(以 ./ 开头)转换为基于 project_dir 的路径。
|
||||
BuildDir 解析后,TempDir = {BuildDir}/temp,OutputDir = {BuildDir}/output。
|
||||
|
||||
Args:
|
||||
project_dir: 工程根目录(project.vpj 所在目录)
|
||||
|
||||
Returns:
|
||||
0 表示成功,非 0 表示失败
|
||||
"""
|
||||
"""Resolve and normalize project paths (src/build/includes); 0=success."""
|
||||
global SourceDir, BuildDir, TempDir, OutputDir, IncludesDir, IncludesDirs
|
||||
global ProjectDir, LinkerStaticLibs, LinkerDynamicLibs
|
||||
if project_dir is None: return 1
|
||||
@@ -400,7 +343,7 @@ def resolve_paths(project_dir: str) -> int:
|
||||
SourceDir = _join_path(SourceDir, project_dir)
|
||||
BuildDir = _join_path(BuildDir, project_dir)
|
||||
|
||||
# TempDir = {BuildDir}/temp(单端 MLIR 恒追加 _mlir 隔离后缀)
|
||||
# TempDir = {BuildDir}/temp (single-track MLIR always appends the _mlir isolation suffix)
|
||||
if BuildDir is not None:
|
||||
bd_len: sizet = string.strlen(BuildDir)
|
||||
suf_dir: str = mlir_dir_suffix()
|
||||
@@ -415,8 +358,8 @@ def resolve_paths(project_dir: str) -> int:
|
||||
viperlib.snprintf(output_buf, bd_len + suf_dir_len + 8, "%s/output%s", BuildDir, suf_dir)
|
||||
OutputDir = output_buf
|
||||
|
||||
# includes 路径可能是 ../includes 形式,需要基于 project_dir 解析
|
||||
# IncludesDir(首元素)与 IncludesDirs(全部,空格分隔)逐项规范化
|
||||
# includes paths may be of the form ../includes, resolved against project_dir.
|
||||
# Normalize IncludesDir (first element) and IncludesDirs (all, space-separated) item by item.
|
||||
if IncludesDirs is not None:
|
||||
id_total: sizet = string.strlen(IncludesDirs)
|
||||
id_out: str = _mbuddy.alloc(id_total * 3 + 64)
|
||||
@@ -446,7 +389,7 @@ def resolve_paths(project_dir: str) -> int:
|
||||
id_out[id_pos] = '\0'
|
||||
IncludesDirs = id_out
|
||||
if IncludesDir is not None: IncludesDir = _join_path(IncludesDir, project_dir)
|
||||
# linker.static_libs 显式静态链接文件路径基于 project_dir 逐段解析为绝对路径
|
||||
# linker.static_libs explicit paths resolved segment-by-segment to abs paths against project_dir
|
||||
if LinkerStaticLibs is not None:
|
||||
sl_total: sizet = string.strlen(LinkerStaticLibs)
|
||||
sl_out: str = _mbuddy.alloc(sl_total * 3 + 64)
|
||||
@@ -475,8 +418,7 @@ def resolve_paths(project_dir: str) -> int:
|
||||
sl_pos += jl
|
||||
sl_out[sl_pos] = '\0'
|
||||
LinkerStaticLibs = sl_out
|
||||
# linker.dynamic_libs 显式动态链接文件(.dll/.so)同样基于 project_dir
|
||||
# 逐段解析为绝对路径
|
||||
# linker.dynamic_libs explicit files (.dll/.so) likewise resolved to abs paths against project_dir.
|
||||
if LinkerDynamicLibs is not None:
|
||||
dl_total: sizet = string.strlen(LinkerDynamicLibs)
|
||||
dl_out: str = _mbuddy.alloc(dl_total * 3 + 64)
|
||||
@@ -505,16 +447,13 @@ def resolve_paths(project_dir: str) -> int:
|
||||
dl_pos += jl
|
||||
dl_out[dl_pos] = '\0'
|
||||
LinkerDynamicLibs = dl_out
|
||||
# linker.flags 中 -T <脚本> 的路径同样基于 project_dir 解析,否则 ld.lld 按进程 cwd 找不到脚本
|
||||
# -T <script> paths in linker.flags also resolved against project_dir, else ld.lld misses them
|
||||
_resolve_linker_t_paths(project_dir)
|
||||
return 0
|
||||
|
||||
|
||||
def _join_path_abs(rel: str, project_dir: str) -> str:
|
||||
"""将相对路径基于 project_dir 拼接为绝对路径(用于 -T 链接脚本等)
|
||||
|
||||
已为绝对路径(/ \\ 开头)或盘符形式(x:)时原样返回。
|
||||
"""
|
||||
"""Join a relative path under project_dir into an absolute path; absolutes unchanged."""
|
||||
if rel is None: return None
|
||||
rel_len: sizet = string.strlen(rel)
|
||||
if rel_len == 0: return rel
|
||||
@@ -530,12 +469,7 @@ def _join_path_abs(rel: str, project_dir: str) -> str:
|
||||
|
||||
|
||||
def _resolve_linker_t_paths(project_dir: str) -> None:
|
||||
"""将 LinkerFlags 中 `-T <相对路径>` 的链接脚本路径解析为绝对路径
|
||||
|
||||
ld.lld 的 -T 参数若是相对路径,将按 subprocess 进程 cwd 解析而找不到
|
||||
脚本。逐 token 扫描 LinkerFlags,遇 -T 其后 token 若非绝对路径则基于
|
||||
project_dir 拼接(与 resolve_paths 对其他相对路径的处理保持一致)。
|
||||
"""
|
||||
"""Resolve relative `-T <script>` paths in LinkerFlags against project_dir."""
|
||||
global LinkerFlags
|
||||
if LinkerFlags is None or project_dir is None: return
|
||||
fl_total: sizet = string.strlen(LinkerFlags)
|
||||
@@ -545,9 +479,9 @@ def _resolve_linker_t_paths(project_dir: str) -> None:
|
||||
out[0] = '\0'
|
||||
out_pos: sizet = 0
|
||||
i: sizet = 0
|
||||
prev_is_t: int = 0 # 前一 token 是否为 -T
|
||||
prev_is_t: int = 0 # whether the previous token is -T
|
||||
while i < fl_total:
|
||||
# 跳过前导空格
|
||||
# Skip leading spaces
|
||||
while i < fl_total and LinkerFlags[i] == ' ': i += 1
|
||||
if i >= fl_total: break
|
||||
start: sizet = i
|
||||
@@ -555,7 +489,7 @@ def _resolve_linker_t_paths(project_dir: str) -> None:
|
||||
tok_len: sizet = i - start
|
||||
is_t_flag: int = 0
|
||||
if tok_len == 2 and LinkerFlags[start] == '-' and LinkerFlags[start + 1] == 'T': is_t_flag = 1
|
||||
# 输出分隔符(前一 token 之后)
|
||||
# Output separator (after the previous token)
|
||||
if out_pos > 0:
|
||||
out[out_pos] = ' '
|
||||
out_pos += 1
|
||||
@@ -563,7 +497,7 @@ def _resolve_linker_t_paths(project_dir: str) -> None:
|
||||
string.strcpy(out + out_pos, "-T")
|
||||
out_pos += 2
|
||||
elif prev_is_t != 0:
|
||||
# -T 后的脚本路径:非绝对路径则基于 project_dir 拼接
|
||||
# Script path after -T: join against project_dir if not absolute
|
||||
tok_buf: str = _mbuddy.alloc(tok_len + 1)
|
||||
if tok_buf is not None:
|
||||
string.memcpy(tok_buf, LinkerFlags + start, tok_len)
|
||||
@@ -588,13 +522,7 @@ def _resolve_linker_t_paths(project_dir: str) -> None:
|
||||
|
||||
|
||||
def get_includes_binary_dir() -> str:
|
||||
"""获取 includes.binary 目录路径(includes 目录 + 缓存后缀)
|
||||
|
||||
includes 目录如 ../includes,默认返回 ../includes.binary;
|
||||
asan 模式(set_inc_bin_suffix(".binary_asan"))返回 ../includes.binary_asan,
|
||||
隔离 asan 插桩 obj,避免与非 asan obj 混链。
|
||||
再追加单端 MLIR 后缀(如 .binary_asan_mlir):隔离旧 LLVM 后端留下的缓存 obj。
|
||||
"""
|
||||
"""Return the includes binary cache dir (includes dir + cache and MLIR suffixes)."""
|
||||
if IncludesDir is None: return None
|
||||
inc_len: sizet = string.strlen(IncludesDir)
|
||||
suf_len: sizet = string.strlen(_inc_bin_suffix)
|
||||
@@ -606,32 +534,15 @@ def get_includes_binary_dir() -> str:
|
||||
return buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 单端 MLIR 的目录隔离后缀
|
||||
#
|
||||
# 发射器固定为 mlirlite(MLIR 文本),故后缀恒为 "_mlir":TempDir/OutputDir/
|
||||
# includes 缓存都落在 *_mlir 目录,与旧 LLVM 后端留下的 temp/output/.binary
|
||||
# 缓存彻底分离(否则增量构建会复用旧的 LLVM IR .ll / obj,喂给 mlir-opt 必炸)。
|
||||
# ============================================================
|
||||
# Directory isolation suffix for single-track MLIR.
|
||||
# Emitter is always mlirlite, so the suffix is "_mlir": Temp/Output/includes caches live under *_mlir, separate from old LLVM caches.
|
||||
def mlir_dir_suffix() -> str:
|
||||
"""单端 MLIR 的目录隔离后缀(恒为 "_mlir")"""
|
||||
"""Directory isolation suffix for the MLIR backend (always "_mlir")."""
|
||||
return "_mlir"
|
||||
|
||||
|
||||
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" 即视为已声明。
|
||||
"""
|
||||
"""Whether project.vpj declares an ASan runtime; 1=declared."""
|
||||
if LinkerStaticLibs is not None:
|
||||
if string.strstr(LinkerStaticLibs, "asan") is not None: return 1
|
||||
if LinkerDynamicLibs is not None:
|
||||
@@ -642,7 +553,7 @@ def asan_runtime_declared() -> int:
|
||||
|
||||
|
||||
def print_config():
|
||||
"""打印当前配置"""
|
||||
"""Print the current configuration."""
|
||||
if ProjectName is not None: VLogger.info(f"project: {ProjectName} v{ProjectVersion if ProjectVersion is not None else "?"}", "config")
|
||||
if SourceDir is not None: VLogger.info(f"source_dir: {SourceDir}", "config")
|
||||
if BuildDir is not None: VLogger.info(f"build_dir: {BuildDir}", "config")
|
||||
@@ -654,17 +565,14 @@ def print_config():
|
||||
VLogger.info(f"sha1_slice_level: {Sha1SliceLevel}", "config")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# slice_subdir - 每层取 SHA1 的 2 字符作为子目录名(level=0 不切片, 1→"b7", 2→"b7/90")
|
||||
# Args: sha1(至少 2*level 字符)/ level(切片层数)
|
||||
# Returns: 子目录路径字符串(mbuddy 分配,无需释放),level<=0 时返回 None
|
||||
# ============================================================
|
||||
# slice_subdir - use 2 SHA1 chars per level as subdir names (level=0 none, 1->"b7", 2->"b7/90").
|
||||
# Args: sha1 (>= 2*level chars) / level. Returns mbuddy-allocated subdir path, or None if level<=0.
|
||||
def slice_subdir(sha1: str, level: int) -> str:
|
||||
"""根据 SHA1 和切片层数返回子目录路径"""
|
||||
"""Return the SHA1 slice subdir path for the given level."""
|
||||
if level <= 0 or sha1 is None: return None
|
||||
if _mbuddy is None: return None
|
||||
# 每层 2 字符 + 分隔符,最后无分隔符 + null
|
||||
# buf 大小: level * 2 + (level - 1) + 1 = level * 3
|
||||
# 2 chars per level + separators; no trailing separator, then null
|
||||
# buf size: level * 2 + (level - 1) + 1 = level * 3
|
||||
buf_len: sizet = sizet(level * 3)
|
||||
buf: str = _mbuddy.alloc(buf_len)
|
||||
if buf is None: return None
|
||||
|
||||
@@ -10,33 +10,23 @@ import w32.win32base
|
||||
import w32.fileio as fileio
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Utils - 工程工具函数(SHA1 计算、目录清理等)
|
||||
# ============================================================
|
||||
# Utils - project utility functions (SHA1 hashing, directory cleanup)
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
|
||||
# ============================================================
|
||||
# nibble_to_hex_char - 半字节转十六进制字符
|
||||
# ============================================================
|
||||
# nibble_to_hex_char - convert a nibble to a hex character
|
||||
def nibble_to_hex_char(n: int, uppercase: int) -> char:
|
||||
"""0-15 → '0'-'9'/'a'-'f'(uppercase=1 时用 'A'-'F')"""
|
||||
"""Convert a 0-15 nibble to a hex character (uppercase=1 uses 'A'-'F')."""
|
||||
if n < 10: return '0' + n
|
||||
if uppercase != 0: return 'A' + (n - 10)
|
||||
return 'a' + (n - 10)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# bytes_to_hex - 字节序列转十六进制字符串
|
||||
# ============================================================
|
||||
# bytes_to_hex - convert a byte sequence to a hex string
|
||||
def bytes_to_hex(pool: memhub.MemBuddy*, digest: bytes, nbytes: sizet, uppercase: int, use_malloc: int) -> str:
|
||||
"""将 digest 前 nbytes 字节转为 2*nbytes 个十六进制字符(含 null 终止)
|
||||
|
||||
use_malloc=1 时用 stdlib.malloc(调用方负责 free),否则 pool.alloc。
|
||||
合并自 Utils.compute_sha1 / IncludesScanner.compute_file_sha1 的重复 hex 转换。
|
||||
"""
|
||||
"""Convert the first nbytes of digest to a null-terminated hex string."""
|
||||
if digest is None or nbytes <= 0: return None
|
||||
hex_len: sizet = nbytes * 2 + 1
|
||||
hex_buf: str = None
|
||||
@@ -52,47 +42,37 @@ def bytes_to_hex(pool: memhub.MemBuddy*, digest: bytes, nbytes: sizet, uppercase
|
||||
return hex_buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# compute_sha1 - 计算字符串的 SHA1,返回前 16 个十六进制字符(与 Projectrans.py 一致)
|
||||
# ============================================================
|
||||
# compute_sha1 - compute a string SHA1, return the first 16 hex chars
|
||||
def compute_sha1(pool: memhub.MemBuddy*, content: str) -> str:
|
||||
"""计算字符串的 SHA1,返回前 16 个十六进制字符(用于文件命名)"""
|
||||
"""Compute the SHA1 of a string, returning its first 16 hex chars."""
|
||||
if content is None or pool is None: return None
|
||||
|
||||
# 构造 sha1 对象(__init__ 会初始化 state/count/buf)
|
||||
# ctx 在栈上分配(alloca),函数内使用完即丢弃,无需堆分配
|
||||
# ctx is stack-allocated (alloca); no heap allocation needed
|
||||
ctx: rawptr[hashlib.sha1] = hashlib.sha1()
|
||||
if ctx is None: return None
|
||||
|
||||
# 计算摘要
|
||||
# Compute the digest
|
||||
ctx.update(content)
|
||||
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
|
||||
if digest is None: return None
|
||||
ctx.final(digest)
|
||||
|
||||
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符)
|
||||
# Convert to a hex string (first 8 bytes = 16 hex chars)
|
||||
return bytes_to_hex(pool, digest, 8, 0, 0)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# is_vp_at - 检查路径段是否以 .vp 后缀结尾
|
||||
# ============================================================
|
||||
# is_vp_at - check whether a path segment ends with .vp
|
||||
def is_vp_at(p: str, start: sizet, plen: sizet) -> int:
|
||||
"""检查 p[start, start+plen) 是否以 .vp 后缀结尾(1=是,0=否)。
|
||||
|
||||
不再识别 .py(移除 vp -> py 后缀识别回退:Viper 源文件仅 .vp)。
|
||||
"""
|
||||
"""Return 1 if p[start, start+plen) ends with the .vp suffix."""
|
||||
if p is None or plen <= 3: return 0
|
||||
base: sizet = start + plen - 3
|
||||
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p': return 1
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# JoinPath - 拼接两个路径段为 a/b
|
||||
# ============================================================
|
||||
# JoinPath - join two path segments as a/b
|
||||
def JoinPath(a: str, b: str) -> str:
|
||||
"""拼接路径段为 "a/b"(stdlib.malloc 分配,调用方负责 free),失败返回 None"""
|
||||
"""Join two path segments as "a/b"; caller frees the malloc'd result."""
|
||||
if a is None or b is None: return None
|
||||
a_len: sizet = string.strlen(a)
|
||||
b_len: sizet = string.strlen(b)
|
||||
@@ -103,15 +83,9 @@ def JoinPath(a: str, b: str) -> str:
|
||||
return buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# read_file_to_buf - 读取整个文件到缓冲区(null 终止)
|
||||
# ============================================================
|
||||
# read_file_to_buf - read a whole file into a null-terminated buffer
|
||||
def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
|
||||
"""读取文件到 stdlib.malloc 缓冲区并保证 null 终止
|
||||
|
||||
返回缓冲区(调用方负责 free);打开失败/空文件/分配失败返回 None。
|
||||
合并自 Phase2/StubMerger/IncludesScanner 的重复"打开+读取+终止"序列。
|
||||
"""
|
||||
"""Read a whole file into a null-terminated malloc buffer, or None."""
|
||||
if path is None or buf_size <= 0: return None
|
||||
sf: rawptr[fileio.File] = fileio.File(path, fileio.MODE.R)
|
||||
if sf.closed: return None
|
||||
@@ -128,19 +102,12 @@ def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
|
||||
return buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PathToModuleName - 文件路径转模块名
|
||||
# ============================================================
|
||||
# PathToModuleName - convert a file path to a module name
|
||||
def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
||||
"""将文件路径转换为模块名(stdlib.malloc 分配,调用方负责 free)
|
||||
|
||||
strip_includes_prefix=1 时跳过 includes/ 前缀(如 includes/ast/__init__.vp → ast)。
|
||||
特殊处理 __init__.vp:pkg/__init__.vp → pkg(而非 pkg.__init__)。
|
||||
合并自 StubMerger._PathToModuleName / IncludesScanner 内联模块名计算。
|
||||
"""
|
||||
"""Convert a file path to a dotted module name; caller frees the result."""
|
||||
if path is None: return None
|
||||
plen: sizet = string.strlen(path)
|
||||
# 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容)
|
||||
# Skip the includes/ or includes\ prefix
|
||||
prefix_len: sizet = 9
|
||||
path_start: sizet = 0
|
||||
if strip_includes_prefix != 0 and plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
|
||||
@@ -148,19 +115,18 @@ def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
||||
if psep_ch == '/' or psep_ch == '\\':
|
||||
path_start = prefix_len
|
||||
plen = plen - prefix_len
|
||||
# 去掉 .vp 后缀(Viper 源文件仅 .vp,不再识别 .py)
|
||||
# Strip the .vp suffix
|
||||
if is_vp_at(path, path_start, plen) != 0: plen -= 3
|
||||
if plen == 0: return None
|
||||
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.vp)
|
||||
# 同时支持 / 和 \ 两种分隔符(TPC 写入的路径可能混合使用)
|
||||
# Check for a trailing /__init__ or \__init__ (package __init__.vp)
|
||||
if plen > 9:
|
||||
tail_off: sizet = path_start + plen - 9
|
||||
sep_char: char = path[tail_off]
|
||||
if (sep_char == '/' or sep_char == '\\') and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
|
||||
# 去掉 分隔符+__init__ 后缀,plen 就是包名部分长度
|
||||
# Drop the separator + __init__ suffix
|
||||
plen = tail_off - path_start
|
||||
if plen == 0: return None
|
||||
# 复制并替换 / 为 .
|
||||
# Copy and replace / with .
|
||||
result: str = stdlib.malloc(plen + 1)
|
||||
if result is None: return None
|
||||
for ri in range(plen):
|
||||
@@ -171,11 +137,9 @@ def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CleanDir - 递归删除目录中所有文件和子目录(保留 dir_path 本身;用于 --clean)
|
||||
# ============================================================
|
||||
# CleanDir - recursively delete all files/subdirs under dir_path (for --clean)
|
||||
def CleanDir(dir_path: str) -> int:
|
||||
"""递归删除目录中所有文件和子目录,保留 dir_path 本身,返回删除的文件数,-1 表示错误"""
|
||||
"""Recursively delete files/subdirs under dir_path, returning the count."""
|
||||
if dir_path is None: return -1
|
||||
dir_len: sizet = string.strlen(dir_path)
|
||||
pattern: bytes = stdlib.malloc(dir_len + 8)
|
||||
@@ -198,23 +162,23 @@ def CleanDir(dir_path: str) -> int:
|
||||
while True:
|
||||
fname: str = find_data.cFileName
|
||||
if fname is not None:
|
||||
# 跳过 . 和 ..
|
||||
# Skip . and ..
|
||||
if fname[0] == '.':
|
||||
if fname[1] == '\0': fname = None
|
||||
elif fname[1] == '.' and fname[2] == '\0': fname = None
|
||||
if fname is not None:
|
||||
full_path: bytes = JoinPath(dir_path, fname)
|
||||
if full_path is not None:
|
||||
# 检查是否为目录(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
# Check whether this is a directory
|
||||
is_dir: uint32 = find_data.dwFileAttributes & w32.win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||
if is_dir != 0:
|
||||
# 递归清理子目录中的文件
|
||||
# Recurse into the subdirectory
|
||||
sub_deleted: int = CleanDir(full_path)
|
||||
if sub_deleted > 0: deleted += sub_deleted
|
||||
# 删除空子目录(RemoveDirectoryA 要求目录为空)
|
||||
# Remove the now-empty subdirectory
|
||||
w32.win32file.RemoveDirectoryA(full_path)
|
||||
else:
|
||||
# 删除文件
|
||||
# Delete the file
|
||||
if w32.win32file.DeleteFileA(full_path) != 0: deleted += 1
|
||||
stdlib.free(full_path)
|
||||
|
||||
|
||||
+137
-226
@@ -10,18 +10,13 @@ import stdlib
|
||||
import sys
|
||||
|
||||
# ============================================================
|
||||
# StubGen.Converter - vpi 存根文件生成器
|
||||
# ============================================================
|
||||
# StubGen.Converter - .vpi stub file generator
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _eval_binop_int / _eval_binop_valid - 递归计算 BinOp 常量值
|
||||
#
|
||||
# 用于 .vpi 存根文件生成时计算 BinOp 表达式(如 A | B, A + B)的整数值。
|
||||
# 支持 BitOr/BitAnd/Add/Sub 以及嵌套的 Name(引用已注册的 CDefine 常量)。
|
||||
# ============================================================
|
||||
# _eval_binop_int / _eval_binop_valid - recursively evaluate BinOp constant values.
|
||||
# Handles BitOr/BitAnd/Add/Sub and nested Name refs to registered CDefine constants.
|
||||
def _eval_binop_valid(node: ast.AST*) -> int:
|
||||
"""检查 BinOp 节点是否可计算(所有叶子节点都是常量或已注册的 CDefine)"""
|
||||
"""Return 1 if a BinOp node is fully computable from constants or registered CDefines"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
@@ -39,10 +34,10 @@ def _eval_binop_valid(node: ast.AST*) -> int:
|
||||
if _eval_binop_valid(bop.right) != 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Name:
|
||||
# Name 引用:检查是否是已注册的 CDefine 常量
|
||||
# Name reference: check whether it is a registered CDefine constant
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None:
|
||||
# 从本地 CDefine 表查找
|
||||
# Look up in the local CDefine table
|
||||
val: int = _lookup_cdefine_for_pyi(nm.id)
|
||||
if _pyi_cdefine_found() != 0: return 1
|
||||
return 0
|
||||
@@ -50,7 +45,7 @@ def _eval_binop_valid(node: ast.AST*) -> int:
|
||||
|
||||
|
||||
def _eval_binop_int(node: ast.AST*) -> int64:
|
||||
"""递归计算 BinOp 节点的整数值(64 位,避免大整数 CDefine 截断)"""
|
||||
"""Recursively evaluate a BinOp node as a 64-bit integer value"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
@@ -87,23 +82,24 @@ def _eval_binop_int(node: ast.AST*) -> int64:
|
||||
return 0
|
||||
|
||||
|
||||
# CDefine 查找的前向声明(实际实现在 HandlesType)
|
||||
# 避免循环依赖,使用弱引用
|
||||
# Forward declaration for CDefine lookup (real impl in HandlesType);
|
||||
# weak refs avoid circular dependencies.
|
||||
_g_pyi_cdefine_found: int = 0
|
||||
|
||||
# 本文件 CDefine 值缓存(vpi 生成专用):vpi 生成早于翻译阶段注册(全局表为空),_GeneratePyiFromAst 开头按声明顺序注册,Name 引用先查本缓存。
|
||||
# 两个缓存必须是 C 数组(int64* / i8**)而非 list[T]——list[int].get 会误解析为 list[str].get(返回元素地址)、list[str] 联合接收者的 .get 会误分派为 HashTable.get(索引 inttoptr 成 key 指针 → 零页崩溃);索引一律用 t.CPtr(base + i*8) + e[0]。
|
||||
# Per-file CDefine value cache (vpi only): vpi runs before the translate phase, so
|
||||
# _GeneratePyiFromAst registers in order; C arrays dodge list.get misdispatch.
|
||||
_g_pyi_cdef_names: str* = None
|
||||
_g_pyi_cdef_vals: int64* = None
|
||||
_g_pyi_cdef_count: int = 0
|
||||
|
||||
|
||||
def _lookup_cdefine_for_pyi(name: str) -> int64:
|
||||
"""从本文件缓存 / 本地 CDefine 表查找常量值(用于 .vpi 生成时的 BinOp 计算)"""
|
||||
"""Look up a constant value from this file's CDefine cache or the local CDefine table"""
|
||||
global _g_pyi_cdefine_found
|
||||
# 优先查本文件 CDefine 缓存(vpi 生成早于翻译阶段注册,全局表为空)
|
||||
# Look up this file's CDefine cache first (globals are empty during vpi gen)
|
||||
if _g_pyi_cdef_names is not None and _g_pyi_cdef_vals is not None:
|
||||
# 循环上限用 _g_pyi_cdef_count,且绝不能用 len()/.get:i8** 指针数组上 len() 回退 strlen(基址当字符串算长度 → 垃圾小值 → 靠后注册项查不到),.get 会被误分派为 HashTable.get(索引 inttoptr 成 key 指针 → 零页崩溃);用 t.CPtr(base + i*8) + e[0] 依次取名。
|
||||
# Bound loop by _g_pyi_cdef_count; never len()/.get on i8** (len falls back
|
||||
# to strlen, .get misdispatches to HashTable.get) -- index via t.CPtr(base + i*8).
|
||||
pn: sizet = sizet(_g_pyi_cdef_count)
|
||||
for pi in range(pn):
|
||||
entry_pc: str* = (str*)(uint64(_g_pyi_cdef_names) + pi * 8)
|
||||
@@ -120,14 +116,14 @@ def _lookup_cdefine_for_pyi(name: str) -> int64:
|
||||
|
||||
|
||||
def _eval_cdefine_value_cache(node: ast.AST*) -> int64:
|
||||
"""求值 CDefine 值(Constant/UnaryOp/BinOp/Call/Name),供 vpi 预处理注册缓存"""
|
||||
"""Evaluate a CDefine value for preprocessing registration (Constant/UnaryOp/BinOp/Call/Name)"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn_c: ast.Constant* = ast.Constant*(node)
|
||||
if cn_c.const_kind == ast.CONST_INT: return cn_c.int_val
|
||||
if cn_c.const_kind == ast.CONST_BOOL: return cn_c.int_val
|
||||
# STR/FLOAT/NONE 不参与整数 BinOp 引用(注册 0 无害)
|
||||
# STR/FLOAT/NONE do not participate in integer BinOp refs (registering 0 is harmless)
|
||||
return 0
|
||||
if k == ast.ASTKind.UnaryOp: return _eval_binop_int(node)
|
||||
if k == ast.ASTKind.BinOp: return _eval_binop_int(node)
|
||||
@@ -143,18 +139,14 @@ def _eval_cdefine_value_cache(node: ast.AST*) -> int64:
|
||||
|
||||
|
||||
def _pyi_cdefine_found() -> int:
|
||||
"""返回上次 _lookup_cdefine_for_pyi 的查找结果状态"""
|
||||
"""Return the result status of the last _lookup_cdefine_for_pyi call"""
|
||||
return _g_pyi_cdefine_found
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiEmit - 追加字符串到缓冲区
|
||||
#
|
||||
# 将 text 追加到 buf 的 pos 位置,返回新的 pos。
|
||||
# 自动处理缓冲区溢出(截断)。
|
||||
# ============================================================
|
||||
# _PyiEmit - append text to buf at pos, returning the new pos (truncates on overflow)
|
||||
def _PyiEmit(buf: bytes, buf_size: sizet, pos: sizet, text: str) -> sizet:
|
||||
"""追加 text 到 buf 的 pos 位置,返回新的 pos"""
|
||||
"""Append text to buf at pos and return the new pos"""
|
||||
if buf is None or text is None or pos >= buf_size: return pos
|
||||
tlen: sizet = string.strlen(text)
|
||||
if tlen == 0: return pos
|
||||
@@ -165,12 +157,9 @@ def _PyiEmit(buf: bytes, buf_size: sizet, pos: sizet, text: str) -> sizet:
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiIndent - 追加缩进空格
|
||||
#
|
||||
# 追加 level*4 个空格到 buf 的 pos 位置。
|
||||
# ============================================================
|
||||
# _PyiIndent - append level*4 spaces to buf at pos
|
||||
def _PyiIndent(buf: bytes, buf_size: sizet, pos: sizet, level: int) -> sizet:
|
||||
"""追加 level*4 个空格到 buf,返回新的 pos"""
|
||||
"""Append level*4 spaces to buf at pos and return the new pos"""
|
||||
i: int = 0
|
||||
while i < level:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " ")
|
||||
@@ -178,19 +167,10 @@ def _PyiIndent(buf: bytes, buf_size: sizet, pos: sizet, level: int) -> sizet:
|
||||
return pos
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiBinOpOpText - 输出二元运算符两侧的分隔文本
|
||||
#
|
||||
# 与 TPC StubGen/_GetTypeString 的 BinOp 运算符表逐字节一致(" | " / " + " /
|
||||
# " - " / " * " / " / " / " % " / " & " / " >> ")。支持集必须两侧相同,否则
|
||||
# 同一份源码写出的 .vpi 注解文本不同——旧实现恒写 " | ",使
|
||||
# zhuff.vp `t.CArray[zhuff_decode_node, 2 * ZHUFF_MAX_CODES]` 写成
|
||||
# `2 | ZHUFF_MAX_CODES`(读回端按位或算成 15,正确值 30 → 数组长度错)。
|
||||
# 表外运算符 fail-fast:静默写错运算符会生成错误的类型/长度文本,
|
||||
# 让下游按错误结果解析(与 TPC 落到未知节点 fail-fast 同语义)。
|
||||
# ============================================================
|
||||
# _PyiBinOpOpText - emit the " OP " text for a binary operator.
|
||||
# Kept byte-identical to TPC's BinOp operator table; unknown ops fail fast.
|
||||
def _PyiBinOpOpText(buf: bytes, buf_size: sizet, pos: sizet, op: int, node: ast.AST*) -> sizet:
|
||||
"""按运算符种类输出 " OP " 文本,表外运算符 fail-fast"""
|
||||
"""Emit the " OP " text for a binary operator; unknown operators fail fast"""
|
||||
if op == ast.OpKind.BitOr: return _PyiEmit(buf, buf_size, pos, " | ")
|
||||
if op == ast.OpKind.Add: return _PyiEmit(buf, buf_size, pos, " + ")
|
||||
if op == ast.OpKind.Sub: return _PyiEmit(buf, buf_size, pos, " - ")
|
||||
@@ -200,24 +180,15 @@ def _PyiBinOpOpText(buf: bytes, buf_size: sizet, pos: sizet, op: int, node: ast.
|
||||
if op == ast.OpKind.BitAnd: return _PyiEmit(buf, buf_size, pos, " & ")
|
||||
if op == ast.OpKind.RShift: return _PyiEmit(buf, buf_size, pos, " >> ")
|
||||
GenApi.fatal_error(node, "vpi 类型注解节点类型未知", "TypeError")
|
||||
return pos # 不可达(fatal_error 已退出)
|
||||
return pos # unreachable (fatal_error already exited)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiTypeStr - 递归生成类型注解字符串
|
||||
#
|
||||
# 将 AST 类型注解节点转换为字符串表示,写入 buf 的 pos 位置。
|
||||
# 支持集与 TPC StubGen/_GetTypeString 等价:
|
||||
# Name, Attribute, Subscript, Tuple, BinOp, UnaryOp, Compare, BoolOp,
|
||||
# Constant, List, Call
|
||||
# 差异项:TPV 额外支持裸 Tuple(TPC 仅在 Subscript.slice 位置接受 Tuple);
|
||||
# TPC 额外有 Index 分支(vast 遗留类,解析器不产出该节点)。
|
||||
# ============================================================
|
||||
# _PyiTypeStr - recursively render an AST annotation node into buf at pos.
|
||||
# Support set matches TPC; TPV additionally accepts a bare Tuple.
|
||||
def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth: int = 0) -> sizet:
|
||||
"""递归生成类型注解字符串,返回新的 pos"""
|
||||
# 递归深度守卫:AST 损坏成自引用环(Attribute.value/Subscript.slice/
|
||||
# BinOp.left 等回指祖先)时无限递归直至栈耗尽(Phase1 vpi 生成最先
|
||||
# 执行,早于翻译阶段)。fail-fast 终止,不静默回退。
|
||||
"""Recursively render a type annotation node into buf and return the new pos"""
|
||||
# Recursion guard: a self-referential AST would recurse until stack exhaustion.
|
||||
# Fail fast instead of silently falling back.
|
||||
if depth > 64:
|
||||
VLogger.error("vpi 类型注解递归过深(AST 环或嵌套超过 64 层)", "StubGen")
|
||||
sys.exit(1)
|
||||
@@ -225,13 +196,13 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
|
||||
k: int = annot.kind()
|
||||
|
||||
# Name: 简单类型名 (如 int, str, MyClass)
|
||||
# Name: simple type name (e.g. int, str, MyClass)
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(annot)
|
||||
if nm.id is not None: return _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
return _PyiEmit(buf, buf_size, pos, "int")
|
||||
|
||||
# Attribute: 属性引用 (如 int, memhub.MemBuddy)
|
||||
# Attribute: attribute reference (e.g. int, memhub.MemBuddy)
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(annot)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, at.value, depth + 1)
|
||||
@@ -239,7 +210,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
if at.attr is not None: pos = _PyiEmit(buf, buf_size, pos, at.attr)
|
||||
return pos
|
||||
|
||||
# Subscript: 下标类型 (如 list[int], t.CArray[elem, count])
|
||||
# Subscript: subscripted type (e.g. list[int], t.CArray[elem, count])
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(annot)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, sub.value, depth + 1)
|
||||
@@ -248,9 +219,8 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "]")
|
||||
return pos
|
||||
|
||||
# BinOp: 二元运算 (联合类型 int | str;CArray 计数表达式 MAX_ORDER + 1、
|
||||
# 2 * ZHUFF_MAX_CODES;条件宏实参 MASK_BYTE & 0x0F)。
|
||||
# 运算符文本按 _PyiBinOpOpText 输出,与 TPC 的运算符表逐字节一致。
|
||||
# BinOp: union types and CArray count expressions (MAX_ORDER + 1,
|
||||
# 2 * ZHUFF_MAX_CODES); operator text via _PyiBinOpOpText, byte-identical to TPC.
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(annot)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, bop.left, depth + 1)
|
||||
@@ -258,9 +228,8 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, bop.right, depth + 1)
|
||||
return pos
|
||||
|
||||
# UnaryOp: 一元运算 (计数表达式 -1 / ~0;条件宏实参 not ct.isptr(x))。
|
||||
# 输出与 TPC 的 UnaryOp 分支逐字节一致:Not → "not "、Invert → "~"、
|
||||
# USub → "-";其余一元运算符两侧都不支持 → fail-fast(不静默写错符号)。
|
||||
# UnaryOp: count expressions (-1 / ~0) and macro args (not ct.isptr(x)).
|
||||
# Not/Invert/USub match TPC; other unary ops fail fast.
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
uop_t: ast.UnaryOp* = ast.UnaryOp*(annot)
|
||||
if uop_t.op == ast.OpKind.Not: pos = _PyiEmit(buf, buf_size, pos, "not ")
|
||||
@@ -268,12 +237,11 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
elif uop_t.op == ast.OpKind.USub: pos = _PyiEmit(buf, buf_size, pos, "-")
|
||||
else:
|
||||
GenApi.fatal_error(annot, "vpi 类型注解节点类型未知", "TypeError")
|
||||
return pos # 不可达(fatal_error 已退出)
|
||||
return pos # unreachable (fatal_error already exited)
|
||||
return _PyiTypeStr(buf, buf_size, pos, uop_t.operand, depth + 1)
|
||||
|
||||
# Compare: 比较表达式 (条件宏实参 MAX_SIZE > 512 / val == 1)。
|
||||
# 运算符文本与 TPC 的 Compare 分支逐字节一致:== != < <= > >=;
|
||||
# 表外运算符(is/in 等)两侧一致输出 " ? "。
|
||||
# Compare: comparison expressions (MAX_SIZE > 512 / val == 1).
|
||||
# == != < <= > >= match TPC; unknown ops emit " ? ".
|
||||
if k == ast.ASTKind.Compare:
|
||||
cmp_t: rawptr[ast.Compare] = rawptr[ast.Compare](annot)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, cmp_t.left, depth + 1)
|
||||
@@ -291,14 +259,14 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
elif oid_ct == ast.OpKind.Gt: pos = _PyiEmit(buf, buf_size, pos, " > ")
|
||||
elif oid_ct == ast.OpKind.Ge: pos = _PyiEmit(buf, buf_size, pos, " >= ")
|
||||
else: pos = _PyiEmit(buf, buf_size, pos, " ? ")
|
||||
# TPC 按 ops 的下标取 comparators[i];此处加长度守卫,
|
||||
# 畸形 AST(ops/comparators 不等长)不越界。
|
||||
# TPC indexes comparators[i] by ops; guard the length here so a
|
||||
# malformed AST (unequal ops/comparators) cannot overrun.
|
||||
if cmps_ct is not None and oi_ct < len(cmps_ct): pos = _PyiTypeStr(buf, buf_size, pos, cmps_ct.get(oi_ct), depth + 1)
|
||||
oi_ct += 1
|
||||
return pos
|
||||
|
||||
# BoolOp: 布尔运算 (条件宏实参 ENABLED and MAX_SIZE > 0 / A or B)。
|
||||
# 与 TPC 的 BoolOp 分支一致:and → " and "、or → " or "。
|
||||
# BoolOp: boolean ops (ENABLED and MAX_SIZE > 0 / A or B).
|
||||
# Matches TPC: and -> " and ", or -> " or ".
|
||||
if k == ast.ASTKind.BoolOp:
|
||||
bool_t: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](annot)
|
||||
vals_bt: list[ast.AST*]* = bool_t.values
|
||||
@@ -313,7 +281,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
vi_bt += 1
|
||||
return pos
|
||||
|
||||
# Constant: 字符串前向引用 (如 'MyClass')、None 或数字常量(CArray count)
|
||||
# Constant: string forward refs (e.g. 'MyClass'), None or numeric constants
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(annot)
|
||||
if cn.const_kind == ast.CONST_STR:
|
||||
@@ -322,10 +290,8 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "'")
|
||||
return pos
|
||||
if cn.const_kind == ast.CONST_NONE: return _PyiEmit(buf, buf_size, pos, "None")
|
||||
# 数字常量(t.CArray[uint32, 64] 的 count 等):输出数值本身,
|
||||
# 而不是回退 int——否则 vpi 生成 "t.CArray[..., int]" 在加载
|
||||
# stub 时触发 TypeError(count 必须是整数常量、None、CDefine 常量名
|
||||
# 或常量表达式)。
|
||||
# Numeric constants (e.g. the count in t.CArray[uint32, 64]): emit the value
|
||||
# itself; falling back to int makes stub loading raise TypeError.
|
||||
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_CHAR:
|
||||
num_buf_ct: str = stdlib.malloc(24)
|
||||
if num_buf_ct is not None:
|
||||
@@ -335,10 +301,8 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
return pos
|
||||
return _PyiEmit(buf, buf_size, pos, "int")
|
||||
|
||||
# List: 函数指针参数表 (如 t.Callable[[t.CPtr, ULONG], BOOL] 的内层
|
||||
# [t.CPtr, ULONG];t.Callable[[], int] 的空表)。TPV 的 AST 把 Callable
|
||||
# 的参数表成型为 List(TypeGenResolve 的类型名提取同样按 List 递归),
|
||||
# 故必须输出 "[a, b]" 形式——缺失该分支时该节点落入未知类型 fail-fast。
|
||||
# List: function pointer parameter table (e.g. the inner [t.CPtr, ULONG] of
|
||||
# t.Callable[[t.CPtr, ULONG], BOOL]); must emit "[a, b]" or it fails fast.
|
||||
if k == ast.ASTKind.List:
|
||||
lst: ast.List* = ast.List*(annot)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "[")
|
||||
@@ -354,7 +318,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "]")
|
||||
return pos
|
||||
|
||||
# Tuple: 多参数下标 (如 Dict[str, int])
|
||||
# Tuple: multi-argument subscript (e.g. Dict[str, int])
|
||||
if k == ast.ASTKind.Tuple:
|
||||
tup: ast.Tuple* = ast.Tuple*(annot)
|
||||
if tup.elts is not None:
|
||||
@@ -368,7 +332,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
ti += 1
|
||||
return pos
|
||||
|
||||
# Call: 函数调用表达式 (用于装饰器如 @t.NoVTable())
|
||||
# Call: call expression (used by decorators such as @t.NoVTable())
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(annot)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, cl.func, depth + 1)
|
||||
@@ -385,30 +349,23 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ")")
|
||||
return pos
|
||||
|
||||
# 未知节点类型 fail-fast:走到此处说明 AST 损坏或出现未支持的注解形态,报错退出(静默写 "int" 会生成错误签名,让下游按错误类型解析)
|
||||
# Unknown node type: fail fast rather than silently writing "int" and producing a wrong signature.
|
||||
GenApi.fatal_error(annot, "vpi 类型注解节点类型未知", "TypeError")
|
||||
return _PyiEmit(buf, buf_size, pos, "int") # 不可达(fatal_error 已退出)
|
||||
return _PyiEmit(buf, buf_size, pos, "int") # unreachable (fatal_error already exited)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiDefaultStr - 生成默认值表达式文本
|
||||
#
|
||||
# 用于 vpi 签名 '= <default>' 输出(如 depth: int = 0)。
|
||||
# vpi stub 必须保留默认值:VpiReader 解析后注册默认参数表,
|
||||
# 跨模块缺参调用(如 resolve_annotation_type 的 depth=0)才能
|
||||
# 用默认值填充,否则被调函数读到栈垃圾(假报递归过深/崩溃)。
|
||||
# 支持: Constant(int/str/bool/None), Name(CDefine 常量), UnaryOp。
|
||||
# ============================================================
|
||||
# _PyiDefaultStr - render a default-value expression for '= <default>' in vpi.
|
||||
# Stubs must keep defaults so VpiReader can register the default-parameter table.
|
||||
def _PyiDefaultStr(buf: bytes, buf_size: sizet, pos: sizet, node: ast.AST*, depth: int = 0) -> sizet:
|
||||
"""生成默认值表达式文本到 buf,返回新的 pos"""
|
||||
# 递归深度守卫:AST 损坏成自引用环时 fail-fast 终止(同 _PyiTypeStr)
|
||||
"""Render a default-value expression into buf and return the new pos"""
|
||||
# Recursion guard: fail fast on a self-referential AST (same as _PyiTypeStr)
|
||||
if depth > 64:
|
||||
VLogger.error("vpi 默认值表达式递归过深(AST 环或嵌套超过 64 层)", "StubGen")
|
||||
sys.exit(1)
|
||||
if node is None: return pos
|
||||
|
||||
k: int = node.kind()
|
||||
# Constant: 数字/字符串/bool/None 直接输出字面量
|
||||
# Constant: emit numeric/string/bool/None literals directly
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_CHAR:
|
||||
@@ -428,13 +385,13 @@ def _PyiDefaultStr(buf: bytes, buf_size: sizet, pos: sizet, node: ast.AST*, dept
|
||||
if cn.const_kind == ast.CONST_NONE: return _PyiEmit(buf, buf_size, pos, "None")
|
||||
return pos
|
||||
|
||||
# Name: CDefine 常量名(如 share: ULONG = SHARE_READ)
|
||||
# Name: CDefine constant name (e.g. share: ULONG = SHARE_READ)
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None: return _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
return pos
|
||||
|
||||
# UnaryOp: 一元运算(如 -1, ~0)
|
||||
# UnaryOp: unary operation (e.g. -1, ~0)
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
uop: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
if uop.op == ast.OpKind.USub: pos = _PyiEmit(buf, buf_size, pos, "-")
|
||||
@@ -444,19 +401,14 @@ def _PyiDefaultStr(buf: bytes, buf_size: sizet, pos: sizet, node: ast.AST*, dept
|
||||
if uop.operand is not None: return _PyiDefaultStr(buf, buf_size, pos, uop.operand, depth + 1)
|
||||
return pos
|
||||
|
||||
# 其它(BinOp 等复杂表达式):不输出,保持无默认值(fail-fast 由调用侧兜底)
|
||||
# Other complex expressions (BinOp etc.): emit nothing; caller enforces fail-fast
|
||||
return pos
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiRejectCReturnDecorator - 装饰器列表里出现 @c.CReturn 即编译期报错
|
||||
#
|
||||
# @c.CReturn 已移除:多返回值统一用返回注解 `-> tuple[ty1, ty2, ...]`
|
||||
# (既有 tuple → struct 通路保留)。vpi 里仍带该装饰器意味着存根过旧,
|
||||
# fail-fast 提示重新生成,不静默忽略。返回 0 = 装饰器列表里没有 CReturn。
|
||||
# ============================================================
|
||||
# _PyiRejectCReturnDecorator - fail if @c.CReturn appears in the decorator list.
|
||||
# The decorator was removed; a stale stub must be regenerated. 0 = none found.
|
||||
def _PyiRejectCReturnDecorator(node: ast.AST*, decorators: list[ast.AST*]*) -> int:
|
||||
"""装饰器列表里出现 @c.CReturn 即 fatal_error(返回 0 = 无该装饰器)"""
|
||||
"""Fail fast on a @c.CReturn decorator (returns 0 when absent)"""
|
||||
if decorators is None: return 0
|
||||
dn: sizet = len(decorators)
|
||||
di: sizet = 0
|
||||
@@ -464,7 +416,7 @@ def _PyiRejectCReturnDecorator(node: ast.AST*, decorators: list[ast.AST*]*) -> i
|
||||
dnode: ast.AST* = decorators.get(di)
|
||||
di += 1
|
||||
if dnode is None: continue
|
||||
# 兼容 @c.CReturn(...)(Call(func=Attribute))与裸 @c.CReturn(Attribute)两形态
|
||||
# Handles both @c.CReturn(...) (Call with func=Attribute) and bare @c.CReturn
|
||||
dattr: ast.Attribute* = None
|
||||
if dnode.kind() == ast.ASTKind.Call:
|
||||
dcall: ast.Call* = ast.Call*(dnode)
|
||||
@@ -478,13 +430,10 @@ def _PyiRejectCReturnDecorator(node: ast.AST*, decorators: list[ast.AST*]*) -> i
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiFunction - 生成函数签名
|
||||
#
|
||||
# 格式: def name(param: Type, ...) -> ReturnType: pass
|
||||
# ============================================================
|
||||
# _PyiFunction - emit a function signature (def name(...) -> Ret: pass)
|
||||
def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*, indent_level: int, class_name: str) -> sizet:
|
||||
"""生成函数签名到 buf,返回新的 pos"""
|
||||
# 装饰器
|
||||
"""Emit a function signature into buf and return the new pos"""
|
||||
# Decorators
|
||||
if fn.decorator_list is not None:
|
||||
fn_decorator_list: list[ast.AST*]* = fn.decorator_list
|
||||
dn: sizet = len(fn_decorator_list)
|
||||
@@ -503,19 +452,18 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*,
|
||||
if fn.name is not None: pos = _PyiEmit(buf, buf_size, pos, fn.name)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "(")
|
||||
|
||||
# 参数列表
|
||||
# Parameter list
|
||||
args_node: ast.AST* = fn.args
|
||||
if args_node is not None:
|
||||
args: ast.Arguments* = ast.Arguments*(args_node)
|
||||
first: int = 1
|
||||
|
||||
# 位置参数
|
||||
# Positional parameters
|
||||
if args.args is not None:
|
||||
args_args: list[ast.AST*]* = args.args
|
||||
an: sizet = len(args_args)
|
||||
# 默认值右对齐到 args 尾部:defaults[i] 对应 args[an - dn + i]。
|
||||
# 必须输出 '= <default>',否则 VpiReader 解析出的默认参数表无条目,跨模块缺参调用(如 depth=0)读到栈垃圾。
|
||||
# arg_defaults 必须内联声明:联合接收者的 .get 会被误分派,预声明 + 重赋值会解析成 list[int].get 返回 i32 截断指针而崩溃。
|
||||
# Defaults right-align to args tail: defaults[i] maps to args[an - dn + i];
|
||||
# emit '= <default>' and declare arg_defaults inline to avoid .get misdispatch.
|
||||
arg_defaults: list[ast.AST*]* = args.defaults
|
||||
dn: sizet = 0
|
||||
if arg_defaults is not None: dn = len(arg_defaults)
|
||||
@@ -554,29 +502,22 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*,
|
||||
|
||||
pos = _PyiEmit(buf, buf_size, pos, ")")
|
||||
|
||||
# 返回类型
|
||||
# Return type
|
||||
if fn.returns is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " -> ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, fn.returns)
|
||||
else:
|
||||
# 无 `->` 注解时若带 @c.CReturn(...):该装饰器已移除,fail-fast 提示重生成 vpi
|
||||
# Without a `->` annotation, @c.CReturn is removed: fail fast to regenerate vpi
|
||||
_PyiRejectCReturnDecorator(fn, fn.decorator_list)
|
||||
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": pass\n")
|
||||
return pos
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiClass - 生成类定义
|
||||
#
|
||||
# 格式:
|
||||
# class Name(Base):
|
||||
# field: Type
|
||||
# def method(self: Name, ...) -> RetType: pass
|
||||
# ============================================================
|
||||
# _PyiClass - emit a class definition (fields and method stubs).
|
||||
def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, indent_level: int) -> sizet:
|
||||
"""生成类定义到 buf,返回新的 pos"""
|
||||
# 装饰器
|
||||
"""Emit a class definition into buf and return the new pos"""
|
||||
# Decorators
|
||||
if cls.decorator_list is not None:
|
||||
cls_decorator_list: list[ast.AST*]* = cls.decorator_list
|
||||
dcn: sizet = len(cls_decorator_list)
|
||||
@@ -594,17 +535,10 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
pos = _PyiEmit(buf, buf_size, pos, "class ")
|
||||
if cls.name is not None: pos = _PyiEmit(buf, buf_size, pos, cls.name)
|
||||
|
||||
# PEP 695 类型参数 (class Foo[T]:)
|
||||
# PEP 695 type parameters (class Foo[T]:)
|
||||
if cls.type_params is not None:
|
||||
# ClassDef.type_params 的真身是 rawptr[list[str]](**名字串**列表,
|
||||
# 见 includes/ast/stmts.vp:475-483 与 parser.vp:3619-3663 的
|
||||
# `list[str](ps.pool, 8)`),不是 `list[rawptr[AST]]`(那是 decorator_list
|
||||
# 的类型)。原注解从 decorator_list 抄错成 list[ast.AST*]* ⇒ .get() 被解析到
|
||||
# list[ast.AST*].get,而实参是 list[str]* ⇒ 严格规则下 fail-fast:
|
||||
# "调用 ...list[AST*].get 第 0 个实参 %"...list[str]"* 不能隐式转为形参
|
||||
# %"...list[AST*]"*"
|
||||
# 全仓其余 8 处 type_params 均为 list[str]*(VpiReader/Phase1Scan/Phase2/
|
||||
# ClassGenDef/ClassGenGeneric 等),此处对齐。
|
||||
# ClassDef.type_params is really rawptr[list[str]] (a list of name strings),
|
||||
# not list[rawptr[AST]] (that is decorator_list's type); align with the rest of the repo.
|
||||
cls_type_params: list[str]* = cls.type_params
|
||||
tpn: sizet = len(cls_type_params)
|
||||
if tpn > 0:
|
||||
@@ -617,7 +551,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
tpi += 1
|
||||
pos = _PyiEmit(buf, buf_size, pos, "]")
|
||||
|
||||
# 基类
|
||||
# Base classes
|
||||
if cls.bases is not None:
|
||||
cls_bases: list[ast.AST*]* = cls.bases
|
||||
bn: sizet = len(cls_bases)
|
||||
@@ -633,7 +567,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
|
||||
pos = _PyiEmit(buf, buf_size, pos, ":\n")
|
||||
|
||||
# 类成员
|
||||
# Class members
|
||||
has_member: int = 0
|
||||
|
||||
if cls.children is not None:
|
||||
@@ -645,10 +579,10 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
if stmt is not None:
|
||||
sk: int = stmt.kind()
|
||||
|
||||
# 方法
|
||||
# Methods
|
||||
if sk == ast.ASTKind.FunctionDef:
|
||||
mfn: ast.FunctionDef* = ast.FunctionDef*(stmt)
|
||||
# __init__ 方法:先提取 self.x: Type 属性
|
||||
# __init__ method: first extract self.x: Type attributes
|
||||
if mfn.name is not None and string.strcmp(mfn.name, "__init__") == 0:
|
||||
if mfn.children is not None:
|
||||
mfn_children: list[ast.AST*]* = mfn.children
|
||||
@@ -660,7 +594,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
iaa: ast.AnnAssign* = ast.AnnAssign*(ibody)
|
||||
if iaa.target is not None and iaa.target.kind() == ast.ASTKind.Attribute:
|
||||
iat: ast.Attribute* = ast.Attribute*(iaa.target)
|
||||
# 检查 target.value 是否为 Name("self")
|
||||
# Check whether target.value is Name("self")
|
||||
if iat.value is not None and iat.value.kind() == ast.ASTKind.Name:
|
||||
iavn: ast.Name* = ast.Name*(iat.value)
|
||||
if iavn.id is None or string.strcmp(iavn.id, "self") != 0:
|
||||
@@ -676,7 +610,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
pos = _PyiFunction(buf, buf_size, pos, mfn, indent_level + 1, cls.name)
|
||||
has_member = 1
|
||||
|
||||
# 带注解的属性
|
||||
# Annotated attributes
|
||||
elif sk == ast.ASTKind.AnnAssign:
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||||
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
|
||||
@@ -688,14 +622,14 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
has_member = 1
|
||||
|
||||
# 嵌套类
|
||||
# Nested classes
|
||||
elif sk == ast.ASTKind.ClassDef:
|
||||
ncls: ast.ClassDef* = ast.ClassDef*(stmt)
|
||||
pos = _PyiClass(buf, buf_size, pos, ncls, indent_level + 1)
|
||||
has_member = 1
|
||||
ci += 1
|
||||
|
||||
# 空类补 pass
|
||||
# Emit pass for an empty class
|
||||
if has_member == 0:
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level + 1)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "pass\n")
|
||||
@@ -703,36 +637,25 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
return pos
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _GeneratePyiFromAst - 遍历 AST Module 节点,提取函数签名/类定义/变量声明/import 语句,生成 .vpi 存根文件内容到 buf
|
||||
#
|
||||
# Args: mb 内存池(保留以符合调用约定,实际未使用);tree AST Module 根节点;rel_path 源文件路径(仅用于报错归因的"当前文件",应传绝对路径以便 fatal_error 读取源码行);buf 输出缓冲区;buf_size 缓冲区大小
|
||||
# Returns: 写入的字节数(0 表示失败)
|
||||
# ============================================================
|
||||
# _GeneratePyiFromAst - walk the AST Module and emit .vpi stub content into buf.
|
||||
# Returns bytes written (0 on failure); rel_path should be absolute for error attribution.
|
||||
def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf: bytes, buf_size: sizet) -> sizet:
|
||||
"""从 AST 生成 .vpi 存根文件内容到 buf"""
|
||||
"""Generate .vpi stub content from an AST into buf"""
|
||||
if tree is None or buf is None or buf_size == 0: return 0
|
||||
|
||||
# 错误归因:把"当前文件"指向正在生成 stub 的这份(rel_path 非 None 时)。
|
||||
# 本函数内触发的 fatal_error(如 L227/L303/L430 的注解节点类型未知)应指向
|
||||
# 本源文件,而非上一次 set_current_file 的残留文件。
|
||||
# 调用侧必须传**绝对路径**:fatal_error 的 _read_source_line 直接用该路径
|
||||
# 打开文件读取行,传相对路径会打不开 ⇒ 误报"无法读取该行源码"。
|
||||
# 放在函数入口而非各分支:全函数共用同一套遍历/报错路径,逐分支设置会漏掉
|
||||
# 中途新增的分支;入口设一次覆盖全部(含后续所有 fatal_error / push_trace)。
|
||||
# rel_path 为 None 时不改写 CurrentFile(保持上一次设置值):调用侧确实拿不到
|
||||
# 路径时,宁可沿用"上一次设置的文件"也不要把归因抹成 None。
|
||||
# Error attribution: point "current file" at the file being stubbed. Callers must pass
|
||||
# an absolute path (fatal_error opens it directly). Set once at entry to cover all branches.
|
||||
if rel_path is not None: GenApi.set_current_file_type(rel_path)
|
||||
|
||||
pos: sizet = 0
|
||||
|
||||
# 预处理:按声明顺序注册本文件 CDefine 到 _g_pyi 缓存(vpi 生成早于翻译阶段注册,直接查全局表为空会使 BinOp 内 Name 引用退化为 0,跨模块读回错误常量)。
|
||||
# 必须带 global 声明:TPV 语义下函数内对模块级变量赋值无 global 时会创建局部变量,_lookup_cdefine_for_pyi 读到的仍是模块级 None,缓存永不命中。
|
||||
# Pre-pass: register this file's CDefines into the _g_pyi cache in declaration order
|
||||
# (vpi runs before the translate phase; without this, BinOp Name refs degrade to 0).
|
||||
global _g_pyi_cdef_names, _g_pyi_cdef_vals, _g_pyi_cdef_count
|
||||
if tree.children is not None:
|
||||
pre_children: list[ast.AST*]* = tree.children
|
||||
pcn: sizet = len(pre_children)
|
||||
# 第一遍:数本文件 t.CDefine 声明数(C 数组需先定长分配)
|
||||
# Pass 1: count t.CDefine declarations (C arrays need a fixed size first)
|
||||
_g_pyi_cdef_count = 0
|
||||
for pci0 in range(pcn):
|
||||
stmt_pc0: ast.AST* = pre_children.get(pci0)
|
||||
@@ -746,7 +669,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
if _g_pyi_cdef_count > 0:
|
||||
_g_pyi_cdef_names = (str*)(mb.alloc(sizet(_g_pyi_cdef_count) * 8))
|
||||
_g_pyi_cdef_vals = (int64*)(mb.alloc(sizet(_g_pyi_cdef_count) * 8))
|
||||
# 第二遍:按声明顺序注册(BinOp 内 Name 引用依赖先前注册项)
|
||||
# Pass 2: register in declaration order (BinOp Name refs depend on earlier entries)
|
||||
pci: sizet = 0
|
||||
cdi: int = 0
|
||||
for pci in range(pcn):
|
||||
@@ -764,7 +687,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
_g_pyi_cdef_vals[cdi] = _eval_cdefine_value_cache(aa_pc.value)
|
||||
cdi += 1
|
||||
|
||||
# 文件头
|
||||
# File header
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\"\"\"Auto-generated Python stub file\"\"\"\n\n")
|
||||
|
||||
if tree.children is None:
|
||||
@@ -774,7 +697,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
tree_children: list[ast.AST*]* = tree.children
|
||||
n: sizet = len(tree_children)
|
||||
|
||||
# 第一遍扫描:检测 import t/c
|
||||
# Pass 1: detect import t/c
|
||||
has_import_t: int = 0
|
||||
has_import_c: int = 0
|
||||
for i in range(n):
|
||||
@@ -790,24 +713,24 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
if string.strcmp(al.name, "t") == 0: has_import_t = 1
|
||||
elif string.strcmp(al.name, "c") == 0: has_import_c = 1
|
||||
|
||||
# 补充缺失的 import t/c
|
||||
# Add missing import t/c
|
||||
if has_import_t == 0 and has_import_c == 0: pos = _PyiEmit(buf, buf_size, pos, "import t, c\n\n")
|
||||
elif has_import_t == 0: pos = _PyiEmit(buf, buf_size, pos, "import t\n\n")
|
||||
elif has_import_c == 0: pos = _PyiEmit(buf, buf_size, pos, "import c\n\n")
|
||||
else: pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
# 第二遍扫描:生成声明
|
||||
# Pass 2: emit declarations
|
||||
for i in range(n):
|
||||
stmt = tree_children.get(i)
|
||||
if stmt is not None:
|
||||
k: int = stmt.kind()
|
||||
|
||||
# Import 语句
|
||||
# Import statement
|
||||
if k == ast.ASTKind.Import:
|
||||
imp2: ast.Import* = ast.Import*(stmt)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "import ")
|
||||
# fail-fast:别名列表为空 ⇒ 写出 `import ` 这种残缺行,import 名字
|
||||
# 全丢,包重导出注册随之失效(与下面逐个别名为空的检查互补)。
|
||||
# fail-fast: an empty alias list would write a broken `import ` line and
|
||||
# lose all names, breaking package re-export registration.
|
||||
if imp2.names is None or len(imp2.names) == 0:
|
||||
GenApi.fatal_error(stmt, "vpi 生成:import 语句别名列表为空(应为 ≥1 个别名)——AST 构造/解析异常", "NameError")
|
||||
if imp2.names is not None:
|
||||
@@ -817,15 +740,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
while ai2 < an2:
|
||||
if ai2 > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
al2: ast.Alias* = ast.Alias*(imp2_names.get(ai2))
|
||||
# fail-fast:别名节点或其名字为空 ⇒ 绝不静默写出 "import , "。
|
||||
# vpi 里 import 行的名字是**包重导出注册的唯一输入**
|
||||
# (VpiReader._inject_reexport_class_declare 靠它把子模块里的类以
|
||||
# 包 __init__ 的 sha1 注册别名 entry),名字静默丢失会让 `包.类`
|
||||
# 形式的模块限定注解解析落空,下游报出与根因无关的 NameError
|
||||
# (实测:ast/__init__.vpi 的 `from .base import , , ,` 名字全空
|
||||
# ⇒ ast.AST 报"方法 'Result.__init__' 形参 'elem' 类型注解解析失败")。
|
||||
# 两种成因分开报,便于一次定位:节点指针为空 = Alias 构造失败;
|
||||
# 节点在而 name 为空 = _copy_str/__init__ 未生效。
|
||||
# fail-fast: never silently write "import , ". Import names are the
|
||||
# only input for package re-export registration. Report the two causes separately.
|
||||
if al2 is None:
|
||||
GenApi.fatal_error(stmt, "vpi 生成:import 别名**节点指针为空**(Alias(...) 构造未产出对象:" "pool.alloc 失败或 __init__ 未执行)——import 名字丢失会破坏包重导出注册", "NameError")
|
||||
if al2.name is None or al2.name[0] == '\0':
|
||||
@@ -837,7 +753,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
ai2 += 1
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
# ImportFrom 语句
|
||||
# ImportFrom statement
|
||||
elif k == ast.ASTKind.ImportFrom:
|
||||
ifr: ast.ImportFrom* = ast.ImportFrom*(stmt)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "from ")
|
||||
@@ -847,9 +763,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
lvl -= 1
|
||||
if ifr.module is not None: pos = _PyiEmit(buf, buf_size, pos, ifr.module)
|
||||
pos = _PyiEmit(buf, buf_size, pos, " import ")
|
||||
# fail-fast:同 Import 分支——`from X import *` 走的是 Alias("*"),
|
||||
# 列表为空或名字为空都说明 AST 坏;静默写出 `from X import ` 会让
|
||||
# 包重导出注册失效(star 也用于 re-export 判断)。
|
||||
# fail-fast: same as Import; `from X import *` uses Alias("*"). An empty
|
||||
# list or empty name means a broken AST and would break re-export registration.
|
||||
if ifr.names is None or len(ifr.names) == 0:
|
||||
GenApi.fatal_error(stmt, "vpi 生成:from-import 语句别名列表为空(应为 ≥1 个别名,含 '*')" "——AST 构造/解析异常", "NameError")
|
||||
if ifr.names is not None:
|
||||
@@ -859,7 +774,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
while ai3 < an3:
|
||||
if ai3 > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
al3: ast.Alias* = ast.Alias*(ifr_names.get(ai3))
|
||||
# fail-fast:同 Import 分支(两种成因分开报,一次定位)。
|
||||
# fail-fast: same as Import (report the two causes separately).
|
||||
if al3 is None:
|
||||
GenApi.fatal_error(stmt, "vpi 生成:from-import 别名**节点指针为空**(Alias(...) 构造未产出对象:" "pool.alloc 失败或 __init__ 未执行)——包重导出注册会因此失效", "NameError")
|
||||
if al3.name is None or al3.name[0] == '\0':
|
||||
@@ -871,23 +786,20 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
ai3 += 1
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
# 函数定义(_PyiFunction 已以 "\n" 收尾,不再补空行)
|
||||
# Function definition (_PyiFunction already ends with a newline)
|
||||
elif k == ast.ASTKind.FunctionDef:
|
||||
fn: ast.FunctionDef* = ast.FunctionDef*(stmt)
|
||||
# 泛型函数模板(def f[T])不发 vpi:模板签名含未绑定类型形参 T,
|
||||
# 注入侧无法解析(旧行为会写出 `def f(x: T)` 的错误/失败签名)。
|
||||
# 特化体按调用点单态化生成({模板SHA1}.f[args]),其签名由编译器
|
||||
# 内存中的模板表 + 实参跨模块复现(见 ClassGenGeneric.
|
||||
# _ensure_generic_func_declare),不落 vpi。
|
||||
# Generic function templates (def f[T]) emit no vpi: the signature has an
|
||||
# unbound T. Specializations are monomorphized per call site, not here.
|
||||
if fn.type_params is not None: continue
|
||||
pos = _PyiFunction(buf, buf_size, pos, fn, 0, "")
|
||||
|
||||
# 类定义
|
||||
# Class definition
|
||||
elif k == ast.ASTKind.ClassDef:
|
||||
cls: ast.ClassDef* = ast.ClassDef*(stmt)
|
||||
pos = _PyiClass(buf, buf_size, pos, cls, 0)
|
||||
|
||||
# 带注解的变量声明
|
||||
# Annotated variable declaration
|
||||
elif k == ast.ASTKind.AnnAssign:
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||||
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
|
||||
@@ -896,9 +808,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
pos = _PyiEmit(buf, buf_size, pos, nm.id)
|
||||
pos = _PyiEmit(buf, buf_size, pos, ": ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, aa.annotation)
|
||||
# 如果有值(如 CDefine 常量),输出 = value
|
||||
# CTypedef 特殊处理:值是类型表达式(如 VOIDPTR / t.CPtr),
|
||||
# 不是整数,用 _PyiTypeStr 输出类型字符串
|
||||
# If there is a value (e.g. a CDefine constant) emit = value.
|
||||
# CTypedef is special: its value is a type expression, rendered by _PyiTypeStr.
|
||||
is_ctypedef_ann: int = 0
|
||||
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Attribute:
|
||||
ctd_at: ast.Attribute* = ast.Attribute*(aa.annotation)
|
||||
@@ -916,7 +827,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
viperlib.snprintf(num_buf, 32, " = %lld", cv.int_val)
|
||||
pos = _PyiEmit(buf, buf_size, pos, num_buf)
|
||||
elif cv.const_kind == ast.CONST_FLOAT:
|
||||
# 浮点 CDefine(numpy 的 pi/e 等)必须输出值:读回端「vpi 中任一 CDefine 带值 → 不读源文件」的启发式会被同模块 int CDefine 挡住,无值的 pi 解析不到会退化成 @pi 全局指针导致 float<->ptr 报错;%.17g 保证 double 全精度往返(与读回端缓存格式一致)。
|
||||
# Float CDefines (e.g. numpy pi/e) must emit a value or read-back
|
||||
# degrades to a global pointer; %.17g round-trips double exactly.
|
||||
fbuf_c: bytes = mb.alloc(64)
|
||||
if fbuf_c is not None:
|
||||
viperlib.snprintf(fbuf_c, 64, " = %.17g", cv.float_val)
|
||||
@@ -925,15 +837,15 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
if cv.int_val != 0: pos = _PyiEmit(buf, buf_size, pos, " = True")
|
||||
else: pos = _PyiEmit(buf, buf_size, pos, " = False")
|
||||
elif cv.const_kind == ast.CONST_STR:
|
||||
# 字符串 CDefine(pyzlib.ZLIB_VERSION = "1.3.2" 等)必须输出值,让 stub 自包含(读回端按目标名判定会回退源文件)
|
||||
# String CDefines (e.g. pyzlib.ZLIB_VERSION) must emit a value so the stub is self-contained
|
||||
sbuf_c: bytes = mb.alloc(string.strlen(cv.str_val) + 6)
|
||||
if sbuf_c is not None:
|
||||
viperlib.snprintf(sbuf_c, string.strlen(cv.str_val) + 6, " = \"%s\"", cv.str_val)
|
||||
pos = _PyiEmit(buf, buf_size, pos, sbuf_c)
|
||||
elif cv.const_kind == ast.CONST_NONE: pos = _PyiEmit(buf, buf_size, pos, " = None")
|
||||
elif aa.value is not None and aa.value.kind() == ast.ASTKind.UnaryOp:
|
||||
# 处理负值常量(如 INVALID_HANDLE_VALUE = -1)
|
||||
# UnaryOp(USub, Constant(1)) → -1
|
||||
# Negative constant (e.g. INVALID_HANDLE_VALUE = -1):
|
||||
# UnaryOp(USub, Constant(1)) -> -1
|
||||
uop_cv: ast.UnaryOp* = ast.UnaryOp*(aa.value)
|
||||
num_buf_u: bytes = mb.alloc(32)
|
||||
if uop_cv.op == ast.OpKind.USub and uop_cv.operand is not None and uop_cv.operand.kind() == ast.ASTKind.Constant:
|
||||
@@ -943,8 +855,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
viperlib.snprintf(num_buf_u, 32, " = %lld", neg_val)
|
||||
pos = _PyiEmit(buf, buf_size, pos, num_buf_u)
|
||||
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Call:
|
||||
# 处理类型构造函数常量(如 t.CUnsignedLong(-11))
|
||||
# 提取第一个参数的整数值并输出
|
||||
# Type-constructor constant (e.g. t.CUnsignedLong(-11)):
|
||||
# extract and emit the integer value of the first argument.
|
||||
call_cv: ast.Call* = ast.Call*(aa.value)
|
||||
if call_cv.args is not None:
|
||||
call_args_cv: list[ast.AST*]* = call_cv.args
|
||||
@@ -959,9 +871,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
viperlib.snprintf(num_buf_c, 32, " = %lld", ca_cv.int_val)
|
||||
pos = _PyiEmit(buf, buf_size, pos, num_buf_c)
|
||||
elif ca_cv.const_kind == ast.CONST_FLOAT:
|
||||
# Call 包装的浮点常量(如 PI: t.CDefine = float(3.14159...)):
|
||||
# stub 必须输出值,否则读回端 vpi 无值 → 退化 0.0。
|
||||
# %.17g 保证 double 全精度往返(与上方 CONST_FLOAT 分支一致)。
|
||||
# Call-wrapped float constant (e.g. float(3.14159...)): emit the
|
||||
# value or read-back degrades to 0.0; %.17g round-trips double.
|
||||
fbuf_cc: bytes = mb.alloc(64)
|
||||
if fbuf_cc is not None:
|
||||
viperlib.snprintf(fbuf_cc, 64, " = %.17g", ca_cv.float_val)
|
||||
@@ -976,24 +887,24 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
viperlib.snprintf(num_buf_cu, 32, " = %lld", neg_c_val)
|
||||
pos = _PyiEmit(buf, buf_size, pos, num_buf_cu)
|
||||
elif uoc.const_kind == ast.CONST_FLOAT:
|
||||
# 负浮点 Call 包装(如 t.CDefine = float(-1.5)):
|
||||
# 同 CONST_FLOAT,%.17g 全精度往返
|
||||
# Negative Call-wrapped float (e.g. float(-1.5)):
|
||||
# same as CONST_FLOAT, %.17g round-trips exactly.
|
||||
neg_cf: double = -uoc.float_val
|
||||
fbuf_cu: bytes = mb.alloc(64)
|
||||
if fbuf_cu is not None:
|
||||
viperlib.snprintf(fbuf_cu, 64, " = %.17g", neg_cf)
|
||||
pos = _PyiEmit(buf, buf_size, pos, fbuf_cu)
|
||||
elif arg_kind == ast.ASTKind.BinOp:
|
||||
# Call 内部的 BinOp(如 t.CUnsignedLong(A | B))
|
||||
# 递归计算 BinOp 值
|
||||
# BinOp inside a Call (e.g. t.CUnsignedLong(A | B)):
|
||||
# recursively evaluate the BinOp value.
|
||||
binop_val_bo: int64 = _eval_binop_int(first_arg)
|
||||
num_buf_bo: bytes = mb.alloc(32)
|
||||
if (binop_val_bo != 0 or _eval_binop_valid(first_arg) != 0) and num_buf_bo is not None:
|
||||
viperlib.snprintf(num_buf_bo, 32, " = %lld", binop_val_bo)
|
||||
pos = _PyiEmit(buf, buf_size, pos, num_buf_bo)
|
||||
elif aa.value is not None and aa.value.kind() == ast.ASTKind.BinOp:
|
||||
# 处理 BinOp 常量(如 FOREGROUND_COMBO = FOREGROUND_RED | FOREGROUND_GREEN)
|
||||
# 递归计算 BinOp 值并输出
|
||||
# BinOp constant (e.g. FOREGROUND_COMBO = FOREGROUND_RED | FOREGROUND_GREEN):
|
||||
# recursively evaluate and emit it.
|
||||
binop_val_av: int64 = _eval_binop_int(aa.value)
|
||||
if binop_val_av != 0 or _eval_binop_valid(aa.value) != 0:
|
||||
num_buf_b: bytes = mb.alloc(32)
|
||||
@@ -1001,15 +912,15 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
viperlib.snprintf(num_buf_b, 32, " = %lld", binop_val_av)
|
||||
pos = _PyiEmit(buf, buf_size, pos, num_buf_b)
|
||||
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Name:
|
||||
# 处理 Name 引用常量(如 ALIAS = OTHER_CONST)
|
||||
# 输出引用的名字,让消费方在本地查找
|
||||
# Name reference constant (e.g. ALIAS = OTHER_CONST):
|
||||
# emit the referenced name for the consumer to resolve locally.
|
||||
name_val: ast.Name* = ast.Name*(aa.value)
|
||||
if name_val.id is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " = ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, name_val.id)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
# 全局赋值(无注解)— 从 Constant value 推断类型
|
||||
# Global assignment (no annotation) - infer type from the Constant value
|
||||
elif k == ast.ASTKind.Assign:
|
||||
asg: ast.Assign* = ast.Assign*(stmt)
|
||||
if asg.targets is not None and asg.value is not None:
|
||||
@@ -1039,7 +950,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
elif inferred == 5: pos = _PyiEmit(buf, buf_size, pos, "None")
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
# NUL 终止
|
||||
# NUL terminator
|
||||
if pos < buf_size: buf[pos] = 0
|
||||
else: buf[buf_size - 1] = 0
|
||||
return pos
|
||||
+570
-1128
File diff suppressed because it is too large.
Load diff
@@ -25,16 +25,10 @@ import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
# ============================================================
|
||||
# _IsAppModule - 模块名是否为 App 源文件(store 条目 incdir 为空)
|
||||
#
|
||||
# 依赖图 BFS 中识别 App 中间节点:App 文件不入 reachable_set(Phase B
|
||||
# 已编译其 .obj),但其 includes 依赖必须继续展开。App 条目由 App 扫描
|
||||
# 阶段用 AppendToSha1MapStore 登记(incdir 留空);includes 条目用
|
||||
# AddIncludeToSha1MapStore 登记(incdir 非空)。
|
||||
# _IsAppModule - whether a module name is an App source file (store entry with empty incdir)
|
||||
# ============================================================
|
||||
def _IsAppModule(mod_name: str) -> int:
|
||||
"""mod_name 是否为 App 源文件(store 命中且 incdir 为空)"""
|
||||
"""Return whether mod_name is an App source file (store hit with empty incdir)."""
|
||||
if mod_name is None: return 0
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return 0
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
@@ -44,15 +38,8 @@ def _IsAppModule(mod_name: str) -> int:
|
||||
if StubMerger._sha1_store_incdir[iidx] == '\0': return 1
|
||||
return 0
|
||||
return 0
|
||||
# ============================================================
|
||||
# _AppendDepsToWorklist - 追加空格分隔模块名到 worklist(逐模块去重)
|
||||
#
|
||||
# worklist 是 BFS 队列(空格分隔);deps_str 中的模块若已在 worklist/processed 中则不重复追加,
|
||||
# 否则 BFS 沿共享依赖(builtins/stdlib 等)反复累积会致 REACH_WORKLIST_BUF 溢出。
|
||||
#
|
||||
# Args: worklist 队列缓冲(原地修改);wl_len 当前长度(值);deps_str 要追加的模块名(空格分隔);processed 已处理集合(空格分隔,查重)
|
||||
# Returns: 追加后的新长度(>=0);溢出/参数错误返回 -1(调用方 fail-fast)
|
||||
# ============================================================
|
||||
# _AppendDepsToWorklist - append space-separated module names to the worklist (dedup against worklist/processed).
|
||||
# Returns the new length (>=0), or -1 on overflow/arg error (caller fail-fast).
|
||||
def _AppendDepsToWorklist(worklist: bytes, wl_len: sizet, deps_str: str, processed: str) -> int:
|
||||
if worklist is None or deps_str is None: return -1
|
||||
cur_len: sizet = wl_len
|
||||
@@ -90,20 +77,10 @@ def _AppendDepsToWorklist(worklist: bytes, wl_len: sizet, deps_str: str, process
|
||||
stdlib.free(dmod)
|
||||
return int(cur_len)
|
||||
|
||||
# ============================================================
|
||||
# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需扫描;未引用的 includes 从不读取)
|
||||
#
|
||||
# App 优先模型:从 app_deps 出发 BFS,对每个模块名按文件系统解析到 includes 文件(ResolveModuleToPath),只对可达 includes
|
||||
# 读文件 + 算 SHA1 + 解析 import,补充依赖图并登记 Sha1Store/deps 存储器/写 deps.txt;Phase 1b / Phase B+ 只处理可达集合中的文件。
|
||||
#
|
||||
# 算法(工作列表 BFS):1) App 扫描(app_deps 为空时)递归扫 source_dir,declare_only=2 解析 import(相对导入 resolve 成绝对模块名)
|
||||
# + 容器注入(_list/_dict)→ worklist 初始队列,App 文件登记 store(incdir 空)+ 填 deps;2) builtins 预加入队列;3) BFS 直到闭包。
|
||||
#
|
||||
# Args: mb 内存池;source_dir App 源文件目录(Config.SourceDir);temp_dir 临时目录(deps.txt 所在位置);reachable_set 输出参数,可达 SHA1 集合缓冲区(MAX_INCLUDES_SHA1 * 17 字节);app_deps App 源文件直接依赖模块名(空格分隔),非空则跳过 App 扫描;includes_dirs includes 根目录(空格分隔)
|
||||
# Returns: 可达 SHA1 数量(>0 成功;0 无 includes 依赖;<0 失败)
|
||||
# ============================================================
|
||||
# _BuildReachableSha1Set - build the reachable SHA1 set (on-demand dep-graph scan; unreferenced includes are never read).
|
||||
# BFS from app_deps: resolve each module to an includes file and read+hash+parse only reachable ones; returns count (>0 ok, 0 none, <0 fail).
|
||||
def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, reachable_set: str, app_deps: str = None, includes_dirs: str = None) -> int:
|
||||
"""构建可达 SHA1 集合(依赖图按需扫描,App 优先)"""
|
||||
"""Build the reachable SHA1 set by on-demand dependency-graph scanning."""
|
||||
if source_dir is None or temp_dir is None or reachable_set is None: return -1
|
||||
if includes_dirs is None:
|
||||
VLogger.error("includes_dirs 缺失,无法按需解析依赖图", "Reachable")
|
||||
@@ -111,13 +88,13 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
|
||||
SRC_BUF_SIZE_R: sizet = 1048576
|
||||
|
||||
# 工作列表(空格分隔的模块名)
|
||||
# Worklist (space-separated module names)
|
||||
worklist: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF)
|
||||
if worklist is None: return -1
|
||||
worklist[0] = '\0'
|
||||
wl_len: sizet = 0
|
||||
|
||||
# 1. App 扫描(app_deps 非空时跳过,直接用调用方提供的初始队列)
|
||||
# 1. App scan (skipped when app_deps is non-empty; use the caller-provided initial queue directly)
|
||||
if app_deps is None or app_deps[0] == '\0':
|
||||
VLogger.info("App 扫描:解析 source_dir 的 import 依赖", "Reachable")
|
||||
scan_res_r: rawptr[IncludesScanner.ScanResult] = IncludesScanner.scan_includes(mb, source_dir)
|
||||
@@ -126,7 +103,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
for e_i_r in range(scan_res_r.Count):
|
||||
e_r: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](uint64(scan_res_r.Entries) + uint64(e_i_r) * es_r)
|
||||
if e_r is None or e_r.Path is None: continue
|
||||
# 文件级 mark:parse 完立即回收 AST(App 全量 parse 驻留会 OOM)
|
||||
# File-level mark: reclaim the AST right after parsing (keeping all App ASTs resident would OOM)
|
||||
app_mark: sizet = mb.mark()
|
||||
sbuf: bytes = Utils.read_file_to_buf(e_r.Path, SRC_BUF_SIZE_R)
|
||||
if sbuf is None:
|
||||
@@ -142,8 +119,8 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
tr: rawptr[HandlesTranslator.Translator] = HandlesTranslator.Translator()
|
||||
if tr is not None:
|
||||
tr._declare_only = 2
|
||||
# 源文件包名:用 RelPath(相对 source_dir,支持子目录),
|
||||
# 保证 from .xxx import 相对导入解析正确
|
||||
# Source package name from RelPath (relative to source_dir, supports subdirs),
|
||||
# so relative imports like `from .xxx` resolve correctly
|
||||
tr.CurrentPackage = GenApi.compute_package_from_relpath(mb, e_r.RelPath)
|
||||
GenApi.set_current_file_type(e_r.Path)
|
||||
GenApi.set_current_file_imports(e_r.Path)
|
||||
@@ -153,16 +130,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
GenApi.reset_visible_structs(mb, 0)
|
||||
ret_a: int = tr.translate(tree)
|
||||
if ret_a == 0:
|
||||
# 容器使用检测:App 文件用 list/dict(裸名注解/构造)
|
||||
# 时注入 _list/_dict 依赖(对齐原实现)
|
||||
# Container use detection: inject _list/_dict deps when an App file uses list/dict (bare annotation/ctor)
|
||||
tr._inject_auto_container_visibility(mb, tree)
|
||||
# App 文件登记 store(incdir 空)+ 填 deps:
|
||||
# BFS 展开 App 中间节点的 includes 依赖时经
|
||||
# LookupIncludesDeps(app_sha1) 读取
|
||||
# Register the App file in the store (empty incdir) and fill deps; read by
|
||||
# LookupIncludesDeps(app_sha1) when BFS expands its includes deps
|
||||
if e_r.Sha1 is not None:
|
||||
StubMergerStore.AppendToSha1MapStore(e_r.Sha1, e_r.RelPath)
|
||||
StubMergerStore.PopulateIncludesDeps(e_r.Sha1, tr._imported_modules)
|
||||
# 收集 deps 到初始队列(逐模块去重)
|
||||
# Collect deps into the initial queue (dedup per module)
|
||||
if tr._imported_modules is not None:
|
||||
im_len_a: sizet = string.strlen(tr._imported_modules)
|
||||
im_pos_a: sizet = 0
|
||||
@@ -189,13 +164,13 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
wl_len += 1
|
||||
worklist[wl_len] = '\0'
|
||||
stdlib.free(im_buf_a)
|
||||
# 释放 Translator 资源
|
||||
# Release Translator resources
|
||||
if tr._global_names is not None: stdlib.free(tr._global_names)
|
||||
if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names)
|
||||
stdlib.free(sbuf)
|
||||
mb.reset_to(app_mark)
|
||||
else:
|
||||
# app_deps 已提供(Phase2 场景):直接作为初始队列
|
||||
# app_deps already provided (Phase2): use it directly as the initial queue
|
||||
ad_len: sizet = string.strlen(app_deps)
|
||||
if ad_len > 0 and ad_len < StubMerger.REACH_WORKLIST_BUF:
|
||||
string.strcpy(worklist, app_deps)
|
||||
@@ -205,25 +180,20 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
stdlib.free(worklist)
|
||||
return -1
|
||||
|
||||
# 去掉末尾多余空格
|
||||
# Trim the trailing extra space
|
||||
if wl_len > 0 and worklist[wl_len - 1] == ' ':
|
||||
worklist[wl_len - 1] = '\0'
|
||||
wl_len -= 1
|
||||
|
||||
# 2. 预加入基础模块(builtins)
|
||||
# builtins(内置函数模块)始终纳入编译链。_list/_dict 仅在显式
|
||||
# list/dict 注解/构造时经 _inject_auto_container_visibility 注入
|
||||
# (list 字面量赋给 t.CArray 走数组初始化,不触发 _list)。
|
||||
# processed 尚未分配,此处传 None 仅按 worklist 查重。
|
||||
# 2. Pre-add base modules (builtins): builtins is always compiled in; _list/_dict are injected only for
|
||||
# explicit list/dict annotations/ctors (list literals to t.CArray are array init and do not trigger _list).
|
||||
wl_len = _AppendDepsToWorklist(worklist, wl_len, "builtins", None)
|
||||
if wl_len < 0:
|
||||
stdlib.free(worklist)
|
||||
return -1
|
||||
|
||||
# 2b. --traceback: force the traceback runtime module into the reachable set
|
||||
# (TPC: used_includes.add('traceback')). Its Install() symbol and the
|
||||
# @llvm.global_ctors ctor are only produced by compiling this module, so a
|
||||
# missing file is fail-fast rather than a silent "feature off" build.
|
||||
# 2b. --traceback: force the traceback runtime module into the reachable set (TPC: used_includes.add('traceback')).
|
||||
# Its Install() symbol and the @llvm.global_ctors ctor come only from compiling this module, so a missing file is fail-fast.
|
||||
if BuildPipeline._traceback_mode != 0:
|
||||
tb_abs: t.CPtr = stdlib.malloc(8)
|
||||
tb_rel: t.CPtr = stdlib.malloc(8)
|
||||
@@ -250,7 +220,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
return -1
|
||||
wl_len = sizet(tb_wl)
|
||||
|
||||
# 3. 工作列表 BFS:模块名 → 文件系统解析 → 读+hash+parse import
|
||||
# 3. Worklist BFS: module name -> filesystem resolution -> read+hash+parse imports
|
||||
processed: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF)
|
||||
if processed is None:
|
||||
stdlib.free(worklist)
|
||||
@@ -268,7 +238,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
|
||||
while wl_pos_slot[0] < wl_len:
|
||||
pos: sizet = wl_pos_slot[0]
|
||||
# 提取一个模块名(到空格或末尾)
|
||||
# Extract one module name (up to a space or the end)
|
||||
name_start: sizet = pos
|
||||
while pos < wl_len and worklist[pos] != ' ': pos += 1
|
||||
name_len: sizet = pos - name_start
|
||||
@@ -276,17 +246,17 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
wl_pos_slot[0] = pos
|
||||
if name_len == 0: continue
|
||||
|
||||
# 复制模块名
|
||||
# Copy the module name
|
||||
mod_buf: bytes = stdlib.malloc(name_len + 1)
|
||||
if mod_buf is None: continue
|
||||
string.strncpy(mod_buf, worklist + name_start, name_len)
|
||||
mod_buf[name_len] = '\0'
|
||||
|
||||
# 已处理去重
|
||||
# Dedup against already processed
|
||||
if GenApi.is_module_imported(processed, mod_buf) != 0:
|
||||
stdlib.free(mod_buf)
|
||||
continue
|
||||
# 加入 processed
|
||||
# Add to processed
|
||||
if proc_len + name_len + 1 < StubMerger.REACH_WORKLIST_BUF:
|
||||
string.strcpy(processed + proc_len, mod_buf)
|
||||
proc_len = proc_len + name_len
|
||||
@@ -298,8 +268,8 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
stdlib.free(mod_buf)
|
||||
continue
|
||||
|
||||
# App 中间节点:只展开其 includes 依赖,不入 reachable_set
|
||||
# (Phase B 已编译 App 的 .obj,加入会导致 Phase B+ 重复编译 → 重复符号)
|
||||
# App intermediate node: only expand its includes deps, do not add to reachable_set
|
||||
# (Phase B already compiled its .obj; adding it would recompile and duplicate symbols)
|
||||
if _IsAppModule(mod_buf) != 0:
|
||||
app_sha1: str = StubMergerStore.LookupSha1ByModName(mod_buf)
|
||||
if app_sha1 is not None:
|
||||
@@ -316,7 +286,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
stdlib.free(mod_buf)
|
||||
continue
|
||||
|
||||
# includes:按文件系统按需解析(未引用的 includes 根本不触碰)
|
||||
# includes: resolve on demand via the filesystem (unreferenced includes are never touched)
|
||||
out_abs: t.CPtr = stdlib.malloc(8)
|
||||
out_rel: t.CPtr = stdlib.malloc(8)
|
||||
out_inc: t.CPtr = stdlib.malloc(8)
|
||||
@@ -326,12 +296,8 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
if out_inc is not None: stdlib.free(out_inc)
|
||||
stdlib.free(mod_buf)
|
||||
continue
|
||||
# 输出槽防御性初始化(成功路径必全写;未找到/未命中时仅 free 槽本身)
|
||||
# 槽联合必须用 (sizet*):t.CPtr* 会被折叠成 t.CPtr,
|
||||
# [0] 变成 1 字节访问 → 指针被截断(0x2e 崩溃根因)。CSizeT 成员为
|
||||
# 8 字节:写侧 sizet(ptr),读侧必须用 rawptr(slot[0]) 做 u64→
|
||||
# 指针转换;str(slot[0]) 是内置"数字转字符串"(snprintf 到栈缓冲并返回
|
||||
# 该栈地址),拿到的是十进制文本+栈指针 → free 即 bad-free。
|
||||
# Defensive init of the output slots (the success path always writes all). The slot union must use (sizet*):
|
||||
# t.CPtr* collapses to t.CPtr and [0] becomes a 1-byte access, truncating the pointer (0x2e crash root cause).
|
||||
abs_slot: sizet* = (sizet*)(out_abs)
|
||||
rel_slot: sizet* = (sizet*)(out_rel)
|
||||
inc_slot: sizet* = (sizet*)(out_inc)
|
||||
@@ -340,7 +306,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
inc_slot[0] = 0
|
||||
rcode: int = IncludesScanner.ResolveModuleToPath(includes_dirs, mod_buf, out_abs, out_rel, out_inc)
|
||||
if rcode != 0:
|
||||
# 内建模块(t/c/builtins 等内部库)或未找到:静默跳过
|
||||
# Builtin module (t/c/builtins internal libs) or not found: silently skip
|
||||
stdlib.free(out_abs)
|
||||
stdlib.free(out_rel)
|
||||
stdlib.free(out_inc)
|
||||
@@ -350,7 +316,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
rel_path: str = rawptr(rel_slot[0])
|
||||
inc_dir: str = rawptr(inc_slot[0])
|
||||
|
||||
# 读文件 + 计算 SHA1(CRLF→LF,与 IncludesScanner 一致)
|
||||
# Read the file and compute SHA1 (CRLF->LF, matching IncludesScanner)
|
||||
sha1_r2: str = IncludesScanner.compute_file_sha1(mb, abs_path)
|
||||
if sha1_r2 is None:
|
||||
stdlib.free(abs_path)
|
||||
@@ -362,7 +328,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
stdlib.free(mod_buf)
|
||||
continue
|
||||
|
||||
# 已在可达集合(前序 BFS 已 parse 过):只展开其 deps
|
||||
# Already in the reachable set (parsed by an earlier BFS): only expand its deps
|
||||
if StubMergerCollect._is_in_sha1_set(sha1_r2, reachable_set, reachable_count) != 0:
|
||||
deps_ex: str = StubMergerStore.LookupIncludesDeps(sha1_r2)
|
||||
if deps_ex is not None:
|
||||
@@ -389,12 +355,12 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
stdlib.free(mod_buf)
|
||||
continue
|
||||
|
||||
# 加入可达集合 + 登记 Sha1Store(仅可达 includes)
|
||||
# Add to the reachable set and register in Sha1Store (reachable includes only)
|
||||
reachable_count = StubMergerIR._AddSha1ToSet(reachable_set, reachable_count, sha1_r2)
|
||||
StubMergerStore.AddIncludeToSha1MapStore(sha1_r2, rel_path, inc_dir, mod_buf)
|
||||
|
||||
# 文件级 mark:parse import 后立即回收 AST(可达 includes 全量
|
||||
# parse 的 AST 若驻留 → 主池 OOM)
|
||||
# File-level mark: reclaim the AST right after parsing imports (keeping all reachable includes
|
||||
# ASTs resident would OOM the main pool)
|
||||
scan_mark_r: sizet = mb.mark()
|
||||
sbuf_r: bytes = Utils.read_file_to_buf(abs_path, SRC_BUF_SIZE_R)
|
||||
if sbuf_r is None:
|
||||
@@ -414,9 +380,8 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
tokens_r: ast.Token* = ast.tokenize(lx_r)
|
||||
tree_r = ast.parse_tokens(mb, tokens_r)
|
||||
if tree_r is None:
|
||||
# parse_tokens keeps the error detail (line/col/message) in the global
|
||||
# channel only, so print the unified parse traceback before the one-line
|
||||
# log; without it a reachable-includes syntax error is undiagnosable.
|
||||
# parse_tokens keeps the error detail (line/col/message) only in the global channel, so print the unified
|
||||
# parse traceback before the one-line log; without it a reachable-includes syntax error is undiagnosable.
|
||||
GenApi.dump_parse_traceback(abs_path)
|
||||
VLogger.error(f"可达 includes 解析失败: {abs_path}", "Reachable")
|
||||
stdlib.free(abs_path)
|
||||
@@ -446,7 +411,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
continue
|
||||
tr_r2.ModuleSha1 = sha1_r2
|
||||
tr_r2._declare_only = 2
|
||||
# 包名:相对 includes 根目录的 RelPath(支持 from .xxx import 相对导入)
|
||||
# Package name: RelPath relative to the includes root (supports relative imports like `from .xxx`)
|
||||
tr_r2.CurrentPackage = GenApi.compute_package_from_relpath(mb, rel_path)
|
||||
GenApi.set_current_file_type(abs_path)
|
||||
GenApi.set_current_file_imports(abs_path)
|
||||
@@ -472,11 +437,11 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
stdlib.free(wl_pos)
|
||||
mb.reset_to(scan_mark_r)
|
||||
return -1
|
||||
# 容器按需注入(includes 用 list/dict 泛型时追加 _list/_dict 到依赖,
|
||||
# 对齐 App 层 _inject_auto_container_visibility;内联破环防 _list/_dict 环)
|
||||
# Inject containers on demand (append _list/_dict when includes use list/dict generics);
|
||||
# inlined to break the _list/_dict cycle
|
||||
tr_r2._inject_auto_container_visibility(mb, tree_r)
|
||||
|
||||
# 填内存依赖 + 写 deps.txt(供依赖图展开与 Phase B 按需加载)
|
||||
# Fill in-memory deps and write deps.txt (for dep-graph expansion and on-demand Phase B loading)
|
||||
deps_r2: str = tr_r2._imported_modules
|
||||
StubMergerStore.PopulateIncludesDeps(sha1_r2, deps_r2)
|
||||
if deps_r2 is not None:
|
||||
@@ -489,7 +454,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
if dl_w > 0: df_w.write(deps_r2, dl_w)
|
||||
df_w.close()
|
||||
stdlib.free(deps_path_w)
|
||||
# 追加依赖到 worklist(去重防溢出)
|
||||
# Append deps to the worklist (dedup to prevent overflow)
|
||||
ret_w: int = _AppendDepsToWorklist(worklist, wl_len, deps_r2, processed)
|
||||
if ret_w < 0:
|
||||
stdlib.free(abs_path)
|
||||
@@ -508,7 +473,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
wl_len = sizet(ret_w)
|
||||
else: VLogger.debug(f"无依赖: {rel_path}", "Reachable")
|
||||
|
||||
# 释放 Translator 的 C malloc 资源 + 回收本文件 AST
|
||||
# Free the Translator's C malloc resources and reclaim this file's AST
|
||||
if tr_r2._global_names is not None: stdlib.free(tr_r2._global_names)
|
||||
if tr_r2._nonlocal_names is not None: stdlib.free(tr_r2._nonlocal_names)
|
||||
stdlib.free(sbuf_r)
|
||||
@@ -527,16 +492,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
stdlib.free(wl_pos)
|
||||
return reachable_count
|
||||
|
||||
# ============================================================
|
||||
# LoadFullIR - 读 {temp_dir}/{sha1}.full.ll 到 out_buf(④-c:合并期改用在线 FULL 产物)
|
||||
#
|
||||
# 契约:返回写入长度(sizet;0=失败),out_buf 以 '\0' 结尾。
|
||||
# full.ll 由翻译点在模型在线时 dump_ir(OUTPUT_FULL) 产出 —— 自足的单模块 IR
|
||||
# (类型定义 + external 声明 + 定义 + DI 一次齐全,类型定义天然在声明之前),
|
||||
# 无需再把 stub + 依赖 stub/text 文本合并。
|
||||
# ============================================================
|
||||
# LoadFullIR - read {temp_dir}/{sha1}.full.ll into out_buf (④-c: merge phase uses the online FULL artifact).
|
||||
# Contract: returns bytes written (sizet; 0=failure); out_buf is '\0'-terminated. full.ll is a self-contained single-module IR.
|
||||
def LoadFullIR(temp_dir: str, sha1: str, out_buf: bytes, out_size: sizet) -> sizet:
|
||||
"""读取 {sha1}.full.ll 到 out_buf;返回长度(0=失败)"""
|
||||
"""Read {sha1}.full.ll into out_buf; returns the length (0 on failure)."""
|
||||
if temp_dir is None or sha1 is None or out_buf is None or out_size == 0: return 0
|
||||
td_len: sizet = string.strlen(temp_dir)
|
||||
p: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "full.ll")
|
||||
@@ -555,77 +514,64 @@ def LoadFullIR(temp_dir: str, sha1: str, out_buf: bytes, out_size: sizet) -> siz
|
||||
out_buf[rd] = '\0'
|
||||
return sizet(rd)
|
||||
|
||||
# ============================================================
|
||||
# LoadDepVpiIntoModule - 加载依赖模块的 .vpi 文件,注入 declare 到当前模块
|
||||
#
|
||||
# 遍历 imported_modules(空格分隔的模块名):LookupSha1ByModName 取 SHA1 → _sliced_path 构造 .vpi 路径 → VpiReader.ParseVpiIntoModule 解析并注入 declare。
|
||||
#
|
||||
# 必须在 _translate_module_level 中、Import 处理完成后、translate_children 之前调用,确保跨模块函数调用时 declare 已存在。
|
||||
#
|
||||
# Args: pool 内存池;mod 当前 LLVM 模块;imported_modules 已导入模块名字符串(空格分隔);from_imports from-import 映射字符串("name:module name:module" 格式);temp_dir temp 目录路径;trans 翻译器对象
|
||||
# Returns: 成功加载的 .vpi 文件数
|
||||
# ============================================================
|
||||
# LoadDepVpiIntoModule - load dependency modules' .vpi files and inject their declares into the current module.
|
||||
# Call after Import handling and before translate_children; returns the number of successfully loaded .vpi files.
|
||||
def LoadDepVpiIntoModule(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, imported_modules: str, from_imports: str, temp_dir: str, trans: rawptr[HandlesTranslator.Translator]) -> int:
|
||||
"""加载依赖模块的 .vpi 文件,将函数声明注入到当前模块"""
|
||||
"""Load dependency modules' .vpi files and inject their declares into the current module."""
|
||||
if pool is None or mod is None or temp_dir is None or trans is None: return 0
|
||||
if imported_modules is None: return 0
|
||||
|
||||
# 每个模块的 stub 注入前重置 typedef 递归注册的防环标记:
|
||||
# CTypedef 表每文件隔离(translate 前 clear),visited 若跨模块残留会
|
||||
# 导致 stdint 等 typedef 只注册一次、后续模块注入再次解析时表里为空
|
||||
# → 参数类型退化 i8_ptr_ty → 整数实参误报 CastError(FuncTest 根因)。
|
||||
# Reset the typedef recursion guard before injecting each module's stub: the CTypedef table is
|
||||
# per-file, so stale visited state registers stdint typedefs only once and degrades param types (FuncTest root cause).
|
||||
VpiReader.ResetVpiTypedefVisited()
|
||||
# 按需注入防重入表同周期重置:注入目标 mod 每模块不同,跨模块沿用会让
|
||||
# 后续模块命中缓存、拿不到本模块需要的 declare(包重导出按需注入用)。
|
||||
# Reset the on-demand injection reentry table too: the target mod differs per module, so carrying it
|
||||
# over would cache-hit and miss the declares this module needs.
|
||||
VpiReader.ResetVpiInjectVisited()
|
||||
|
||||
td_len: sizet = string.strlen(temp_dir)
|
||||
total_len: sizet = string.strlen(imported_modules)
|
||||
success_count: int = 0
|
||||
|
||||
# 遍历 imported_modules 中的每个模块名(空格分隔)
|
||||
# Iterate each module name in imported_modules (space-separated)
|
||||
ci: sizet = 0
|
||||
while ci < total_len:
|
||||
# 跳过前导空格
|
||||
# Skip leading spaces
|
||||
while ci < total_len and imported_modules[ci] == ' ': ci += 1
|
||||
if ci >= total_len: break
|
||||
# 提取模块名(到下一个空格或字符串末尾)
|
||||
# Extract the module name (up to the next space or end of string)
|
||||
word_start: sizet = ci
|
||||
while ci < total_len and imported_modules[ci] != ' ': ci += 1
|
||||
word_len: sizet = ci - word_start
|
||||
if word_len == 0: continue
|
||||
|
||||
# 复制模块名到临时缓冲区并 null 终止
|
||||
# Copy the module name into a temp buffer and null-terminate it
|
||||
mod_nm_buf: str = stdlib.malloc(word_len + 1)
|
||||
if mod_nm_buf is None: continue
|
||||
string.strncpy(mod_nm_buf, imported_modules + word_start, word_len)
|
||||
mod_nm_buf[word_len] = '\0'
|
||||
|
||||
# 用 LookupSha1ByModName 查找模块的 SHA1
|
||||
# Look up the module's SHA1 via LookupSha1ByModName
|
||||
sha1_val: str = StubMergerStore.LookupSha1ByModName(mod_nm_buf)
|
||||
|
||||
# 改名别名(import X as Y,如 HT→lib.core.Handles.HandlesTranslator):
|
||||
# 精确/包名回退均失败时从 from_imports 查别名映射再查 SHA1,
|
||||
# 否则该类模块的 vpi/stub 不注入 → 跨模块符号解析失败
|
||||
# Import alias (import X as Y, e.g. HT->lib.core.Handles.HandlesTranslator): when exact/package
|
||||
# lookups fail, resolve the alias via from_imports; otherwise its vpi/stub is not injected and cross-module symbols fail.
|
||||
if sha1_val is None and from_imports is not None: sha1_val = StubMergerStore.LookupSha1ByAlias(from_imports, mod_nm_buf)
|
||||
|
||||
# 如果精确查找失败,尝试包名回退(如 "ast" → "ast.__init__")
|
||||
# LookupSha1ByModName 内部已处理包名回退,此处无需重复
|
||||
# Exact lookup failed: package fallback (e.g. "ast" -> "ast.__init__") is already handled inside LookupSha1ByModName
|
||||
|
||||
if sha1_val is not None:
|
||||
# 构造 .vpi 文件路径
|
||||
# Build the .vpi file path
|
||||
vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1_val, "vpi")
|
||||
if vpi_path is not None:
|
||||
# 检查 .vpi 文件是否存在(可读)
|
||||
# Check that the .vpi file exists (is readable)
|
||||
vf: rawptr[fileio.File] = fileio.File(vpi_path, fileio.MODE.R)
|
||||
if not vf.closed:
|
||||
vf.close()
|
||||
# 调用 VpiReader 解析 .vpi 并注入 declare
|
||||
# (错误上下文帧由调用方 HandlesTranslator 维护)
|
||||
# Parse the .vpi via VpiReader and inject declares (the error context frame is kept by the caller HandlesTranslator)
|
||||
ret: int = VpiReader.ParseVpiIntoModule(pool, mod, vpi_path, trans)
|
||||
if ret == 1: success_count += 1
|
||||
stdlib.free(vpi_path)
|
||||
# sha1_val 指向内部存储,无需 free
|
||||
# sha1_val points into internal storage, no free needed
|
||||
|
||||
stdlib.free(mod_nm_buf)
|
||||
|
||||
|
||||
@@ -25,38 +25,25 @@ import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
# ============================================================
|
||||
# StubMergerCollect - 切片路径构建与 stub 文件 SHA1 收集
|
||||
#
|
||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。
|
||||
# ============================================================
|
||||
# StubMergerCollect - slice-path construction and stub-file SHA1 collection.
|
||||
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
|
||||
|
||||
# ============================================================
|
||||
# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free;level>0 时自动调 BuildPipeline.ensure_dir 建子目录)
|
||||
#
|
||||
# 按 Config.Sha1SliceLevel 分散到 SHA1 前缀子目录:level=0 → {temp_dir}/{sha1}.{ext};level=1 → {temp_dir}/b7/{sha1}.{ext};level=2 → {temp_dir}/b7/90/{sha1}.{ext}
|
||||
#
|
||||
# Args: temp_dir 基础目录;td_len temp_dir 长度(避免重复 strlen);sha1 SHA1 字符串;ext 文件扩展名(如 "stub.ll")
|
||||
# Returns: 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败
|
||||
# ============================================================
|
||||
# _sliced_path - build a slice path (stdlib.malloc; caller frees; creates subdir when level>0).
|
||||
# Shards by Config.Sha1SliceLevel into SHA1-prefix subdirs; returns the full path or None on failure.
|
||||
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
||||
"""构建切片路径(stdlib.malloc 分配,调用者负责 free)
|
||||
|
||||
level>0 时自动调用 ensure_dir 创建子目录。
|
||||
"""
|
||||
"""Build a SHA1-sliced path (stdlib.malloc; caller frees; creates the subdir)."""
|
||||
if temp_dir is None or sha1 is None or ext is None: return None
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
ext_len: sizet = string.strlen(ext)
|
||||
|
||||
# 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free)
|
||||
# Subdir prefix: reuse Config.slice_subdir (the authoritative SHA1 slice impl; mbuddy-allocated, no free)
|
||||
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
||||
sub_len: sizet = 0
|
||||
if subdir_buf is not None:
|
||||
sub_len = string.strlen(subdir_buf)
|
||||
|
||||
# 确保子目录存在: {temp_dir}/{subdir}
|
||||
# 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True),
|
||||
# 进而读取端读不到文件返回 0("加载 IR 失败")。
|
||||
# Ensure subdir {temp_dir}/{subdir} exists.
|
||||
# Otherwise fileio.File(MODE.W) fails silently (file.closed==True) and the reader returns 0.
|
||||
dir_path_len: sizet = td_len + sub_len + 2
|
||||
dir_path: bytes = stdlib.malloc(dir_path_len)
|
||||
if dir_path is not None:
|
||||
@@ -64,7 +51,7 @@ def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
||||
BuildPipeline.ensure_dir(dir_path)
|
||||
stdlib.free(dir_path)
|
||||
|
||||
# 构建完整路径
|
||||
# Build the full path
|
||||
path: str = None
|
||||
path_len: sizet = 0
|
||||
if sub_len > 0:
|
||||
@@ -78,19 +65,10 @@ def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
||||
|
||||
return path
|
||||
|
||||
# ============================================================
|
||||
# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1
|
||||
#
|
||||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
||||
# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。
|
||||
# ============================================================
|
||||
# _load_includes_sha1_set - collect includes SHA1 from the global in-memory store.
|
||||
# Source is PopulateSha1MapStore (not _sha1_map.txt); filters stubs to avoid linking TransPyV's own lib/* stubs.
|
||||
def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str) -> int:
|
||||
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
|
||||
|
||||
pool/temp_dir 参数保留以兼容现有调用者,但不再使用。
|
||||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
||||
sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null)
|
||||
返回找到的 includes SHA1 数量,-1 表示错误"""
|
||||
"""Copy includes SHA1s from the global store into sha1_set; returns the count, -1 on error."""
|
||||
if sha1_set is None: return -1
|
||||
if StubMerger._sha1_store_arr is None: return -1
|
||||
count: int = 0
|
||||
@@ -100,40 +78,30 @@ def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str
|
||||
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||
count += 1
|
||||
return count
|
||||
# ============================================================
|
||||
# _is_in_sha1_set - 检查 SHA1 是否在集合中
|
||||
# ============================================================
|
||||
# _is_in_sha1_set - check whether a SHA1 is in the set.
|
||||
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||
"""检查 sha1 是否在集合中"""
|
||||
"""Whether sha1 is present in the set."""
|
||||
if sha1 is None or sha1_set is None: return 0
|
||||
for i in range(set_count):
|
||||
if string.strcmp(sha1_set + i * 17, sha1) == 0: return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(仅人类可读的调试输出,程序内部不读取,
|
||||
# 机器分析用 PopulateSha1MapStore 填充的全局内存存储器)
|
||||
#
|
||||
# Args: mb 内存池;temp_dir 临时目录(_sha1_map.txt 写入位置);scan_result IncludesScanner 扫描结果
|
||||
# Returns: 0 成功,非 0 失败
|
||||
# ============================================================
|
||||
# WriteIncludesSha1Map - write includes scan results to _sha1_map.txt (human-readable debug output only).
|
||||
# Internally the in-memory store filled by PopulateSha1MapStore is used; returns 0 on success, non-zero on failure.
|
||||
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: rawptr[IncludesScanner.ScanResult], filter_set: str, filter_count: int) -> int:
|
||||
"""将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器)
|
||||
|
||||
若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。
|
||||
"""
|
||||
"""Write the includes scan result to _sha1_map.txt (human-readable debug output)."""
|
||||
if temp_dir is None or scan_result is None: return 1
|
||||
|
||||
# 构造路径 temp_dir/_sha1_map.txt
|
||||
# Build the path temp_dir/_sha1_map.txt
|
||||
dir_len: sizet = string.strlen(temp_dir)
|
||||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||||
if map_path is None: return 1
|
||||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
|
||||
# 打开文件写入(CREATE_ALWAYS)
|
||||
# Open the file for writing (CREATE_ALWAYS)
|
||||
f: rawptr[fileio.File] = fileio.File(map_path, fileio.MODE.W)
|
||||
if f.closed: return 1
|
||||
|
||||
# 写入每个 include 条目: {sha1}:includes/{rel_path}\n
|
||||
# Write each include entry: {sha1}:includes/{rel_path}\n
|
||||
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
line_buf: bytes = stdlib.malloc(512)
|
||||
if line_buf is None:
|
||||
@@ -145,7 +113,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: rawpt
|
||||
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
|
||||
ent_w: rawptr[IncludesScanner.FileEntry] = (rawptr[IncludesScanner.FileEntry])(ea_w)
|
||||
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: continue
|
||||
# 若提供过滤集合,只写入集合中的条目
|
||||
# When a filter set is provided, write only its entries
|
||||
if filter_set is not None and filter_count > 0:
|
||||
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: continue
|
||||
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
|
||||
|
||||
@@ -26,29 +26,12 @@ import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StubMergerIR - 模块结构化 sidecar({sha1}.meta)写出/读取、子模块判定与模块名-SHA1 映射工具
|
||||
# ============================================================
|
||||
# StubMergerIR - module structured sidecar ({sha1}.meta) write/read, submodule checks and module-name/SHA1 mapping helpers
|
||||
|
||||
# ============================================================
|
||||
# WriteModuleMeta - 落盘 {sha1}.meta(模块结构化 sidecar:函数清单 + 结构体布局 + 虚表)
|
||||
#
|
||||
# 行格式(每行一条,纯文本、无嵌套括号):
|
||||
# FUNC <mangled> DEF|DECL DEF=本模块定义了该符号(有函数体,等同 .text.ll 的 define 行)
|
||||
# STRUCT <sha1>.<类名> FIELDS <n> PACKED <0|1> 命名结构体字段数/是否 packed
|
||||
# STRUCT <sha1>.<类名> OPAQUE 前向声明(opaque,无 body)
|
||||
# VTABLE <sha1>.<类名> SLOTS <n> 虚表槽数(全局 @<类名>.vtable 的元素数)
|
||||
# 数据源均为翻译期模型(Module.FuncHead / NamedTypeHead / GlobalHead),与 dump_ir 同源,
|
||||
# 取代「从 IR 文本反推 declare-vs-define / 结构体布局 / 虚表槽数」。
|
||||
#
|
||||
# 写不出(路径/打开失败)即 fail-fast:缺 meta 会使 Phase B+/C 的声明文件判定与
|
||||
# REPL 的布局恢复失效。
|
||||
#
|
||||
# Args: mod 已翻译完成的 LLVM 模块;temp_dir 基础目录;td_len temp_dir 长度;sha1 模块 SHA1
|
||||
# Returns: 0=成功(失败经 VLogger.error 终止进程)
|
||||
# ============================================================
|
||||
# WriteModuleMeta - write {sha1}.meta (structured sidecar: funcs + struct layout + vtables).
|
||||
# Lines: FUNC <mangled> DEF|DECL, STRUCT <sha1>.<name> FIELDS <n> PACKED <0|1>|OPAQUE, VTABLE <sha1>.<name> SLOTS <n>.
|
||||
def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
"""把本模块的函数定义/结构体布局/虚表清单写入 {sha1}.meta"""
|
||||
"""Write the module's func defines, struct layouts and vtables to {sha1}.meta."""
|
||||
if mod is None or temp_dir is None or sha1 is None:
|
||||
VLogger.error("meta 写出:参数为空", "Meta")
|
||||
return 1
|
||||
@@ -80,7 +63,7 @@ def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha
|
||||
mf.write(line, string.strlen(line))
|
||||
stdlib.free(line)
|
||||
cur = mlirlite.function_get_next(cur)
|
||||
# 2) 结构体布局:STRUCT <name> FIELDS <n> PACKED <0|1>(opaque → STRUCT <name> OPAQUE)
|
||||
# 2) Struct layout: STRUCT <name> FIELDS <n> PACKED <0|1> (opaque -> STRUCT <name> OPAQUE)
|
||||
nt: mlirlite.NamedTypeNode* = mod.NamedTypeHead
|
||||
nt_steps: sizet = 0
|
||||
while nt is not None:
|
||||
@@ -101,7 +84,7 @@ def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha
|
||||
mf.write(lb, string.strlen(lb))
|
||||
stdlib.free(lb)
|
||||
nt = nt.Next
|
||||
# 3) 虚表槽数:VTABLE <class> SLOTS <n>(全局名 <class>.vtable,Ty 匿名结构体 fcount=槽数)
|
||||
# 3) Vtable slots: VTABLE <class> SLOTS <n> (global <class>.vtable; Ty anonymous struct fcount = slots)
|
||||
gv: mlirlite.GlobalVariable* = mod.GlobalHead
|
||||
gv_steps: sizet = 0
|
||||
while gv is not None:
|
||||
@@ -133,13 +116,10 @@ def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _MetaHasDef - 读 {sha1}.meta,判断模块是否含函数定义("FUNC ... DEF" 行)
|
||||
#
|
||||
# Returns: 1=含 DEF(实现文件);0=可读但无 DEF(纯声明);-1=meta 不存在/读失败。
|
||||
# ============================================================
|
||||
# _MetaHasDef - read {sha1}.meta to check for a function definition ("FUNC ... DEF" line).
|
||||
# Returns: 1=has DEF (impl file); 0=readable but no DEF (decl-only); -1=missing/unreadable.
|
||||
def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
"""读 {sha1}.meta:含 "FUNC ... DEF" 行返回 1,无则 0,文件不可读 -1"""
|
||||
"""Read {sha1}.meta: 1 if it has a FUNC ... DEF line, 0 if none, -1 if unreadable."""
|
||||
if temp_dir is None or sha1 is None: return -1
|
||||
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
||||
if mpath is None: return -1
|
||||
@@ -152,11 +132,11 @@ def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
mf.close()
|
||||
if mbuf is None: return -1
|
||||
if mbr <= 0:
|
||||
# 空 meta:无 DEF,纯声明
|
||||
# Empty meta: no DEF, decl-only
|
||||
stdlib.free(mbuf)
|
||||
return 0
|
||||
if mbr < StubMerger.STUB_READ_BUF_SIZE: mbuf[mbr] = '\0'
|
||||
# 逐行扫描:行尾 " DEF" 即为实现文件
|
||||
# Scan line by line: a trailing " DEF" marks an impl file
|
||||
mpos: sizet = 0
|
||||
while mpos < mbr:
|
||||
mls: sizet = mpos
|
||||
@@ -174,53 +154,35 @@ def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# IsDeclOnlyModule - 判断模块是否为纯声明文件(读 {sha1}.meta 的结构化定义清单)
|
||||
#
|
||||
# 返回 1 = 纯声明(无 FUNC ... DEF,如全 pass 的模块,无需 .obj);0 = 实现文件(含 FUNC ... DEF);
|
||||
# -1 = meta 不存在/无法判断。供 Phase B+/C 区分「声明文件无需 .obj」与「实现文件缺失」,
|
||||
# 取代原扫描 text.ll 的 "define " 行。
|
||||
# ============================================================
|
||||
# IsDeclOnlyModule - whether a module is decl-only (reads the structured definition list in {sha1}.meta).
|
||||
# 1=decl-only (no FUNC ... DEF, e.g. all-pass modules, no .obj needed); 0=impl; -1=unknown/missing.
|
||||
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
"""判断模块是否为纯声明文件(meta 无 FUNC ... DEF 行)"""
|
||||
"""Whether the module is decl-only (its meta has no FUNC ... DEF line)."""
|
||||
h: int = _MetaHasDef(temp_dir, td_len, sha1)
|
||||
if h < 0: return -1
|
||||
if h == 1: return 0
|
||||
return 1
|
||||
# ============================================================
|
||||
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
|
||||
#
|
||||
# deps_buf 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer
|
||||
# 子模块类型;模块名含 '.' 时检查其顶层包名是否在 deps_buf 中(deps_buf = "ast stdio string" 时 "ast.lexer" → 1,"foo.bar" → 0)。
|
||||
# ============================================================
|
||||
# _is_submodule_of_imported - check whether a module name is a submodule of an imported package.
|
||||
# deps_buf holds only direct imports; a name with '.' is matched by its top-level package (e.g. "ast.lexer" -> 1).
|
||||
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
|
||||
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
|
||||
"""Whether mod_name is a submodule of a package imported in deps_buf."""
|
||||
if deps_buf is None or mod_name is None: return 0
|
||||
# 查找第一个 '.',提取顶层包名
|
||||
# Find the first '.' and extract the top-level package name
|
||||
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
|
||||
if dot_pos is None: return 0
|
||||
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
|
||||
if pkg_len == 0 or pkg_len >= 256: return 0
|
||||
# 构造顶层包名(截断到 '.' 之前)
|
||||
# 直接在 mod_name 上临时截断检查,避免分配
|
||||
# Build the top-level package name (truncated before '.') by temporary in-place
|
||||
# truncation to avoid an allocation
|
||||
saved_ch: char = mod_name[pkg_len]
|
||||
mod_name[pkg_len] = '\0'
|
||||
result: int = GenApi.is_module_imported(deps_buf, mod_name)
|
||||
mod_name[pkg_len] = saved_ch
|
||||
return result
|
||||
# ============================================================
|
||||
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
|
||||
#
|
||||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
||||
# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。
|
||||
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
|
||||
# ============================================================
|
||||
# _BuildIncludesSha1Map - build the sha1 -> module_name map from the global in-memory store.
|
||||
# Source is the store filled by PopulateSha1MapStore (no _sha1_map.txt read); returns the entry count.
|
||||
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet, sha1_list: str, mod_list: str) -> int:
|
||||
"""从全局内存存储器复制 sha1→module_name 映射到调用者数组
|
||||
|
||||
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
|
||||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
||||
"""
|
||||
"""Copy the sha1 to module_name map from the global store into caller arrays."""
|
||||
if sha1_list is None or mod_list is None: return 0
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return 0
|
||||
count: int = 0
|
||||
@@ -232,29 +194,21 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet, sha1_list: str, mod_list
|
||||
string.strcpy(mod_list + sizet(count) * 64, StubMerger._sha1_store_mod + idx2)
|
||||
count += 1
|
||||
return count
|
||||
# ============================================================
|
||||
# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配;供依赖图分析「模块名 → SHA1」)
|
||||
#
|
||||
# Args: sha1_arr SHA1 数组(每个 17 字节);mod_arr 模块名数组(每个 64 字节);count 映射条目数;mod_name 要查找的模块名
|
||||
# Returns: SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None
|
||||
# ============================================================
|
||||
# _FindSha1ByModName - look up a SHA1 by module name (exact match) for dependency graph analysis.
|
||||
# sha1_arr holds 17-byte entries, mod_arr 64-byte names; returns a pointer into sha1_arr, or None.
|
||||
def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -> str:
|
||||
"""从模块名查找 SHA1(精确匹配 + 包名回退)
|
||||
|
||||
精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
|
||||
支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
|
||||
"""
|
||||
"""Look up a SHA1 by module name, falling back to the .__init__ package form."""
|
||||
if sha1_arr is None or mod_arr is None or mod_name is None: return None
|
||||
if count <= 0: return None
|
||||
# 第一遍:精确匹配
|
||||
# Pass 1: exact match
|
||||
for i in range(count):
|
||||
idx: sizet = sizet(i) * 64
|
||||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
|
||||
# (热点:本函数全表逐条 strcmp 查名)。
|
||||
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs
|
||||
# (hot spot: this function strcmp-scans the whole table per lookup).
|
||||
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
|
||||
sidx: sizet = sizet(i) * 17
|
||||
return sha1_arr + sidx
|
||||
# 第二遍:包名回退("ast" → "ast.__init__")
|
||||
# Pass 2: package fallback ("ast" -> "ast.__init__")
|
||||
nm_len: sizet = string.strlen(mod_name)
|
||||
init_buf: bytes = stdlib.malloc(nm_len + 12)
|
||||
if init_buf is not None:
|
||||
@@ -267,14 +221,10 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -
|
||||
return sha1_arr + sidx2
|
||||
stdlib.free(init_buf)
|
||||
return None
|
||||
# ============================================================
|
||||
# _AddSha1ToSet - 将 SHA1 加入集合(去重;已在集合中则 count 不变)
|
||||
#
|
||||
# Args: set_buf SHA1 集合缓冲区(每个 17 字节);count 当前集合中的 SHA1 数量;sha1 要加入的 SHA1
|
||||
# Returns: 新的 count
|
||||
# ============================================================
|
||||
# _AddSha1ToSet - add a SHA1 to a set (deduped; count unchanged if already present).
|
||||
# set_buf holds 17-byte entries; returns the new count.
|
||||
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
|
||||
"""将 SHA1 加入集合,返回新 count"""
|
||||
"""Add a SHA1 to the set (deduped) and return the new count."""
|
||||
if set_buf is None or sha1 is None: return count
|
||||
for i in range(count):
|
||||
idx: sizet = sizet(i) * 17
|
||||
|
||||
@@ -26,37 +26,21 @@ import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StubMergerStore - 全局 SHA1 映射存储器(sha1→模块名/相对路径)读写
|
||||
# ============================================================
|
||||
# StubMergerStore - global SHA1 map store (sha1 -> module name / rel path) read/write
|
||||
|
||||
# ============================================================
|
||||
# 导入上下文记账(单一真源)
|
||||
#
|
||||
# 「AST 导入语句 → imported_modules / from_imports」的记账口径必须唯一。
|
||||
# 翻译期(ImportsGenMixin.translate_import)与 vpi 注入期(VpiReader 顶层
|
||||
# 遍历)此前各写一份:注入期漏掉 `import X as Y → from_imports "Y:X"`(别名
|
||||
# 反查模块真名的唯一入口,见 LookupModNameByAlias)以及 `import a.b.c →
|
||||
# imported_modules "a"`(顶层包名放行 is_module_imported)。前者使注解里的
|
||||
# `TSpec.TypeSpec*` 在 _forward_declare_ptr_from_node 4a'/4b 全落空 → 退化
|
||||
# 为 Ptr(i8)(现场:GenTypes.VarEntry.Spec.PtrLevel 属性访问失败);后者使
|
||||
# 包限定解析在 stub 上下文里被误判"未导入"。
|
||||
#
|
||||
# 现统一到本模块,两侧只保留各自的特有副作用(SHA1 校验 / reexport 注册 /
|
||||
# add_visible_struct),字符串记账一律走下面这组函数。ImportCtx 只承载两个
|
||||
# 上下文串(Viper 无多返回值,用 pool 分配的小 struct 传出)。
|
||||
# ============================================================
|
||||
# Import-context accounting, single source of truth: AST import statements map to
|
||||
# imported_modules / from_imports; both translate-time and vpi-injection use these helpers.
|
||||
|
||||
class ImportCtx:
|
||||
"""导入上下文容器:两个空格分隔串(ImportedModules / FromImports)"""
|
||||
"""Import context container: two space-separated lists (ImportedModules / FromImports)"""
|
||||
ImportedModules: str
|
||||
FromImports: str
|
||||
|
||||
|
||||
# ============================================================
|
||||
# AppendImportedModule - 追加模块名到空格分隔列表(纯记账,无索引副作用)
|
||||
# ============================================================
|
||||
# AppendImportedModule - append module name to space-separated list (pure bookkeeping)
|
||||
def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
|
||||
"""追加模块名到空格分隔列表(pool 分配),返回新列表"""
|
||||
"""Append a module name to a space-separated list (pool-allocated); returns the new list"""
|
||||
if name is None: return cur
|
||||
if cur is None:
|
||||
nlen: sizet = string.strlen(name)
|
||||
@@ -73,15 +57,10 @@ def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
|
||||
return buf2
|
||||
|
||||
|
||||
# ============================================================
|
||||
# AppendFromImport - 追加 from-import 映射("local:mod[:orig]" 空格分隔)
|
||||
#
|
||||
# [D7·截断纪律] 旧实现固定 256 缓冲 + snprintf:三者合计超长即被静默截断,
|
||||
# 该条目在查找侧仍被当作合法条目解析出错误的 local/mod/orig ⇒ 跨模块类名/
|
||||
# 别名还原取到错名字(污染 Spec.SourceName)。此处按精确长度分配,无上限。
|
||||
# ============================================================
|
||||
# AppendFromImport - append a from-import mapping ("local:mod[:orig]") to a space-separated list.
|
||||
# Exact-size allocation (no fixed 256 buffer) so overlong entries are never silently truncated.
|
||||
def AppendFromImport(pool: memhub.MemBuddy*, cur: str, local: str, mod: str, orig: str) -> str:
|
||||
"""追加 from-import 映射(pool 分配),返回新列表"""
|
||||
"""Append a from-import mapping (pool-allocated); returns the new list"""
|
||||
if local is None or mod is None: return cur
|
||||
llen: sizet = string.strlen(local)
|
||||
mlen: sizet = string.strlen(mod)
|
||||
@@ -113,10 +92,9 @@ def AppendFromImport(pool: memhub.MemBuddy*, cur: str, local: str, mod: str, ori
|
||||
|
||||
|
||||
# ============================================================
|
||||
# AllocImportCtx - 用两个既有上下文串构造记账容器
|
||||
# ============================================================
|
||||
# AllocImportCtx - build the accounting container from two existing context strings
|
||||
def AllocImportCtx(pool: memhub.MemBuddy*, imported_modules: str, from_imports: str) -> ImportCtx*:
|
||||
"""构造导入上下文容器(pool 分配);两串可为 None"""
|
||||
"""Build an import context container (pool-allocated); either list may be None"""
|
||||
ctx: ImportCtx* = pool.alloc(ImportCtx.__sizeof__())
|
||||
if ctx is None: return None
|
||||
ctx.ImportedModules = imported_modules
|
||||
@@ -124,16 +102,10 @@ def AllocImportCtx(pool: memhub.MemBuddy*, imported_modules: str, from_imports:
|
||||
return ctx
|
||||
|
||||
|
||||
# ============================================================
|
||||
# NotePlainImport - 记账 `import X` / `import X as Y`(就地更新 ctx)
|
||||
#
|
||||
# 有别名:追加别名 `Y`,并写 from-import 映射 "Y:X"——这就是 `LookupModNameByAlias`
|
||||
# 与 `_forward_declare_ptr_from_node` 4a' 反查模块真名的唯一来源,绝不可省。
|
||||
# 无别名:`import a.b.c` 时把顶层包名 `a` 也追加(使 a.b.c 作为属性访问 base
|
||||
# 时 is_module_imported("a") 放行)。
|
||||
# ============================================================
|
||||
# NotePlainImport - record `import X` / `import X as Y` in place on ctx.
|
||||
# Aliased: append alias Y + from-import "Y:X" (sole source for reverse module lookup).
|
||||
def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*, name: str, asname: str) -> int:
|
||||
"""记账单条 import(就地更新 ctx),返回 0"""
|
||||
"""Record a single import in place on ctx; returns 0"""
|
||||
if ctx is None or name is None: return 1
|
||||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, name)
|
||||
if asname is not None:
|
||||
@@ -152,22 +124,12 @@ def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*, name: str, asname:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PopulateSha1MapStore - 遍历 ScanResult.Entries 构建 sha1→module_name 映射,填充全局存储器
|
||||
#
|
||||
# 模块名通过 _PathToModuleName(includes/rel_path) 计算;内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
|
||||
# Args: scan_result IncludesScanner 扫描结果
|
||||
# Returns: 填充的条目数,-1 表示失败
|
||||
# ============================================================
|
||||
# PopulateSha1MapStore - build sha1 -> module_name map from ScanResult.Entries into the global store.
|
||||
# Memory via stdlib.malloc (global, not freed with mbuddy). Returns entry count, -1 on failure.
|
||||
def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int:
|
||||
"""从扫描结果填充全局 SHA1 映射存储器
|
||||
|
||||
遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
|
||||
模块名通过 _PathToModuleName(includes/rel_path) 计算。
|
||||
内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
|
||||
"""
|
||||
"""Populate the global SHA1 store from a scan result; returns the entry count or -1"""
|
||||
if scan_result is None: return -1
|
||||
# 释放旧存储器
|
||||
# free the old store
|
||||
if StubMerger._sha1_store_arr is not None: stdlib.free(StubMerger._sha1_store_arr)
|
||||
if StubMerger._sha1_store_mod is not None: stdlib.free(StubMerger._sha1_store_mod)
|
||||
if StubMerger._sha1_store_rel is not None: stdlib.free(StubMerger._sha1_store_rel)
|
||||
@@ -191,7 +153,7 @@ def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int
|
||||
if ent is None or ent.Sha1 is None or ent.RelPath is None:
|
||||
p_bad += 1
|
||||
continue
|
||||
# 构建完整路径 includes/{RelPath}(_PathToModuleName 需要)
|
||||
# build full path includes/{RelPath} (needed by _PathToModuleName)
|
||||
rp_len: sizet = string.strlen(ent.RelPath)
|
||||
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
|
||||
if full_path_buf is None:
|
||||
@@ -210,13 +172,13 @@ def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int
|
||||
string.strncpy(StubMerger._sha1_store_mod + idx2, mod_name, 63)
|
||||
StubMerger._sha1_store_mod[idx2 + 63] = '\0'
|
||||
stdlib.free(mod_name)
|
||||
# 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用)
|
||||
# save rel_path (used by Phase2 3.5 to compile missing includes)
|
||||
idx3: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_rel + idx3, ent.RelPath, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||
StubMerger._sha1_store_rel[idx3 + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||
# 保存所属 includes 根目录(Phase2 预注册/按需编译拼路径用)
|
||||
# save owning includes root dir (Phase2 pre-register / on-demand path building)
|
||||
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||
if ent.IncDir is not None:
|
||||
icd_len: sizet = string.strlen(ent.IncDir)
|
||||
@@ -227,18 +189,12 @@ def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int
|
||||
StubMerger._sha1_store_count += 1
|
||||
if p_bad > 0: VLogger.error(f"includes SHA1 存储有 {p_bad} 个条目失败", "StubMerger")
|
||||
return StubMerger._sha1_store_count
|
||||
# ============================================================
|
||||
# AppendToSha1MapStore - 追加 App 模块条目到全局 SHA1 映射存储器(Phase1 先用 PopulateSha1MapStore 填 includes,
|
||||
# Phase2 扫描 App 源文件后调用本函数追加,否则编译 App 自身时跨模块 CDefine 查找命中不到 App 模块)
|
||||
#
|
||||
# 存储器未初始化时自动初始化;SHA1 已存在(去重)则跳过。
|
||||
# Args: sha1 SHA1 字符串(16 字符 + null);rel_path 相对路径(如 "lib/core/StubMerger.vp",不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
|
||||
# Returns: 0 成功(含去重跳过),1 失败
|
||||
# ============================================================
|
||||
# AppendToSha1MapStore - append an App module entry to the global SHA1 store (Phase2 use);
|
||||
# auto-initializes if empty, skips on duplicate SHA1. Returns 0 ok, 1 failed.
|
||||
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
"""追加条目到全局 SHA1 映射存储器(Phase2 用)"""
|
||||
"""Append an entry to the global SHA1 store (Phase2)"""
|
||||
if sha1 is None or rel_path is None: return 1
|
||||
# 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用)
|
||||
# init store if empty (Phase2 may run before Phase1)
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
@@ -251,15 +207,15 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# 模块名:rel_path "lib/core/StubMerger.vp" → "lib.core.StubMerger"
|
||||
# module name: 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
|
||||
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
|
||||
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
|
||||
stdlib.free(full_path_buf)
|
||||
if mod_name is None: return 1
|
||||
# 去重:SHA1 与模块名都相同才跳过——同一 SHA1 可能先以全路径模块名注册(App 源文件在 includes 目录下),
|
||||
# App 追加的短模块名必须保留,否则 import 该模块的精确查找失败、后缀匹配会劫持到其它同名模块。
|
||||
# Dedup only when both SHA1 and module name match: the same SHA1 may be registered first
|
||||
# under a full-path name; the App short name must stay or exact lookup breaks.
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
sidx_d: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||||
@@ -267,10 +223,10 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
|
||||
stdlib.free(mod_name)
|
||||
return 0
|
||||
# 写入 SHA1
|
||||
# write SHA1
|
||||
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||||
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
||||
# 写入模块名
|
||||
# write module name
|
||||
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||||
mn_len: sizet = string.strlen(mod_name)
|
||||
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||||
@@ -278,30 +234,24 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
||||
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
||||
stdlib.free(mod_name)
|
||||
# 写入相对路径
|
||||
# write rel path
|
||||
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||
rp_len: sizet = string.strlen(rel_path)
|
||||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||
# App 源文件不属于任何 includes 目录:IncDir 留空(Phase2 回退 includes_dir 参数)
|
||||
# App sources belong to no includes dir: leave IncDir empty (Phase2 falls back to includes_dir)
|
||||
idx_inc_a: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||
StubMerger._sha1_store_incdir[idx_inc_a] = '\0'
|
||||
StubMerger._sha1_store_count += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# AddIncludeToSha1MapStore - 单条登记 includes 条目到全局 SHA1 映射存储器
|
||||
#
|
||||
# 依赖图按需扫描(_BuildReachableSha1Set)解析到可达 includes 文件后逐条登记,未引用的 includes 不进 store;
|
||||
# App 源文件仍用 AppendToSha1MapStore(inc_dir 留空)。
|
||||
# Args: sha1 SHA1 字符串(16 字符 + null);rel_path 相对 includes 根目录的路径(如 "ast/parser.vp");inc_dir 所属 includes 根目录;mod_name 模块名(如 "ast.parser")
|
||||
# Returns: 0 成功(含去重跳过),1 失败
|
||||
# ============================================================
|
||||
# AddIncludeToSha1MapStore - register one includes entry into the global SHA1 store (on-demand).
|
||||
# Unreferenced includes stay out; App sources still use AppendToSha1MapStore (empty inc_dir).
|
||||
def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: str) -> int:
|
||||
"""单条登记 includes 条目到全局 SHA1 映射存储器(依赖图按需扫描用)"""
|
||||
"""Register one includes entry in the global SHA1 store (on-demand scan)"""
|
||||
if sha1 is None or rel_path is None or mod_name is None: return 1
|
||||
# 存储器未初始化:自动初始化
|
||||
# init store if empty
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
@@ -314,30 +264,30 @@ def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: s
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# 去重:SHA1 与模块名都相同才跳过(对齐 AppendToSha1MapStore)
|
||||
# dedup when both SHA1 and module name match (aligned with AppendToSha1MapStore)
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
sidx_d: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||||
midx_d: sizet = sizet(i) * 64
|
||||
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0: return 0
|
||||
# 写 SHA1
|
||||
# write SHA1
|
||||
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||||
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
||||
# 写模块名
|
||||
# write module name
|
||||
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||||
mn_len: sizet = string.strlen(mod_name)
|
||||
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
||||
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
||||
# 写相对路径
|
||||
# write rel path
|
||||
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||
rp_len: sizet = string.strlen(rel_path)
|
||||
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||||
else:
|
||||
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||
# 写所属 includes 根目录(非空 = includes 条目;App 条目留空)
|
||||
# write owning includes root (non-empty = includes entry; App entries empty)
|
||||
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||
if inc_dir is not None:
|
||||
icd_len: sizet = string.strlen(inc_dir)
|
||||
@@ -349,41 +299,19 @@ def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: s
|
||||
StubMerger._sha1_store_count += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# GetSha1StoreCount - 获取全局存储器条目数
|
||||
# ============================================================
|
||||
# GetSha1StoreCount - return the global store entry count
|
||||
def GetSha1StoreCount() -> int:
|
||||
"""返回全局 SHA1 存储器的条目数"""
|
||||
"""Return the global SHA1 store entry count"""
|
||||
return StubMerger._sha1_store_count
|
||||
# ============================================================
|
||||
# LookupSha1ByModName - 从模块名查找 SHA1(直接查全局存储器)
|
||||
#
|
||||
# 精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
|
||||
# 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
|
||||
#
|
||||
# Returns:
|
||||
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
|
||||
# ============================================================
|
||||
# LookupSha1ByModName - find SHA1 by module name; falls back to "{mod_name}.__init__".
|
||||
# Returns a pointer into the store (do not free), None if not found.
|
||||
def LookupSha1ByModName(mod_name: str) -> str:
|
||||
"""从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName"""
|
||||
"""Look up a SHA1 by module name (exact then package fallback); delegates to _FindSha1ByModName"""
|
||||
return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 泛型类归属表(泛型类名 → 定义模块 SHA1)——仅 REPL 会话登记/查询
|
||||
#
|
||||
# 为什么需要:REPL 的 includes 会话初始化只注入 `.vpi` 的类(泛型类被跳过),
|
||||
# 不跑批处理的 _PreRegister*GenericTemplates ⇒ 会话内泛型模板表为空 ⇒ 注解里的
|
||||
# `list[rawptr[AST]]` 等泛型下标解析(`TypeGenResolve._resolve_generic_subscript_ty`)
|
||||
# 恒 miss。会话侧的最后兜底要按**定义侧特化命名** "%{模板模块 sha1}.{mangled}"
|
||||
# 造 opaque 具名类型(实证:`%"b56d2f995104690d.list[rawptr[AST]]"`),因此需要
|
||||
# "泛型类定义在哪个模块"这一事实——由注入期扫 `.vpi` 的 ClassDef 登记(见
|
||||
# VpiReader._repl_stub_collect_module)。
|
||||
#
|
||||
# 归属模块 sha1 无法从类名反推(模块名取自文件路径,class list ← _list.vp;用户
|
||||
# 泛型类更是名字与模块名无关),故必须显式登记,不做"按名字猜"。
|
||||
# 批处理不登记(表恒空 ⇒ 查询恒 None ⇒ 批处理路径一字不改)。
|
||||
# 容量:includes 现有 6 个泛型类定义(list/dict 系 + Vector/GSList/ndarray),取 32 槽。
|
||||
# ============================================================
|
||||
# Generic-class owner table (generic class name -> defining module SHA1), REPL-session only;
|
||||
# needed because REPL skips generic templates, so generic subscript resolution would otherwise miss.
|
||||
define GENERIC_OWNER_MAX = 32
|
||||
_generic_owner_names: bytes = None
|
||||
_generic_owner_sha1s: bytes = None
|
||||
@@ -391,7 +319,7 @@ _generic_owner_count: int = 0
|
||||
|
||||
|
||||
def ResetGenericOwners() -> None:
|
||||
"""清空泛型类归属表(REPL 会话初始化开始时调用一次)"""
|
||||
"""Clear the generic-class owner table (called once at REPL session start)"""
|
||||
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
||||
if _generic_owner_names is not None:
|
||||
stdlib.free(_generic_owner_names)
|
||||
@@ -403,10 +331,7 @@ def ResetGenericOwners() -> None:
|
||||
|
||||
|
||||
def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
|
||||
"""登记"泛型类名 → 定义模块 SHA1";1=已登记(含重复)/ 0=失败(参数/满/同名异 sha1)
|
||||
|
||||
同名异 sha1(跨模块同名泛型类)保留首个,与泛型模板表按类名去重的口径一致。
|
||||
"""
|
||||
"""Register generic-class-name to defining-module SHA1; 1 = registered, 0 = failed"""
|
||||
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
||||
if cls_name is None or mod_sha1 is None: return 0
|
||||
if cls_name[0] == '\0' or mod_sha1[0] == '\0': return 0
|
||||
@@ -430,20 +355,16 @@ def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
|
||||
|
||||
|
||||
def LookupGenericOwnerSha1(cls_name: str) -> str:
|
||||
"""按泛型类名取定义模块 SHA1(返回内部指针,无需释放;未登记 None)"""
|
||||
"""Look up the defining-module SHA1 by generic class name (internal pointer, no free)"""
|
||||
if cls_name is None or _generic_owner_names is None: return None
|
||||
i_lg: int
|
||||
for i_lg in range(_generic_owner_count):
|
||||
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0: return _generic_owner_sha1s + sizet(i_lg) * 17
|
||||
return None
|
||||
# ============================================================
|
||||
# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1(用于 from .X import Y:impf.module 是 ".X",而映射表键是 "pkg.X")
|
||||
#
|
||||
# 包含三种匹配方式:精确、后缀、反向后缀。
|
||||
# Returns: SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
|
||||
# ============================================================
|
||||
# LookupSha1ByModNameSuffix - find SHA1 by suffix; tries exact, suffix, reverse-suffix. None if absent.
|
||||
def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
||||
"""后缀匹配查找模块 SHA1"""
|
||||
"""Look up a module SHA1 by suffix match"""
|
||||
if mod_suffix is None: return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||||
if StubMerger._sha1_store_count <= 0: return None
|
||||
@@ -455,19 +376,19 @@ def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
||||
mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf
|
||||
mod_len_sf: sizet = string.strlen(mod_name_sf)
|
||||
if mod_len_sf == 0: continue
|
||||
# 精确匹配
|
||||
# exact match
|
||||
if mod_len_sf == suffix_len_sf:
|
||||
if string.strcmp(mod_name_sf, mod_suffix) == 0:
|
||||
sidx_sf: sizet = sizet(i_sf) * 17
|
||||
return StubMerger._sha1_store_arr + sidx_sf
|
||||
# 后缀匹配:mod_name 以 "." + mod_suffix 结尾
|
||||
# suffix match: mod_name ends with "." + mod_suffix
|
||||
if mod_len_sf > suffix_len_sf:
|
||||
if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.':
|
||||
cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf
|
||||
if string.strcmp(cmp_start_sf, mod_suffix) == 0:
|
||||
sidx2_sf: sizet = sizet(i_sf) * 17
|
||||
return StubMerger._sha1_store_arr + sidx2_sf
|
||||
# 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾
|
||||
# reverse suffix match: mod_suffix ends with "." + mod_name
|
||||
if suffix_len_sf > mod_len_sf:
|
||||
if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.':
|
||||
cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf
|
||||
@@ -476,29 +397,24 @@ def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
||||
return StubMerger._sha1_store_arr + sidx_rev_sf
|
||||
return None
|
||||
# ============================================================
|
||||
# LookupSha1ByAlias - 从 from_imports 别名映射解析模块 SHA1
|
||||
#
|
||||
# from_imports 条目格式 "local:module" 或 "local:module:orig"(空格分隔,由 ImportsGenMixin.add_from_import 写入);
|
||||
# 命中 local==alias 时对 module 执行 LookupSha1ByModName。
|
||||
# Returns: SHA1 字符串指针(指向全局存储器内部),None 未找到
|
||||
# ============================================================
|
||||
# LookupSha1ByAlias - resolve SHA1 from from_imports alias "local:module[:orig]" entries.
|
||||
def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
||||
"""从 from_imports 别名映射解析模块 SHA1(改名别名 import X as Y 场景)"""
|
||||
"""Resolve a module SHA1 from a from-import alias mapping (import X as Y)"""
|
||||
if from_imports is None or alias is None: return None
|
||||
alias_len: sizet = string.strlen(alias)
|
||||
if alias_len == 0: return None
|
||||
total_len: sizet = string.strlen(from_imports)
|
||||
ci: sizet = 0
|
||||
while ci < total_len:
|
||||
# 跳过前导空格
|
||||
# skip leading spaces
|
||||
while ci < total_len and from_imports[ci] == ' ': ci += 1
|
||||
if ci >= total_len: break
|
||||
# 条目起点 ci,找 ':'(local_name 结束)
|
||||
# entry starts at ci; find ':' (end of local_name)
|
||||
colon: sizet = ci
|
||||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
|
||||
local_len: sizet = colon - ci
|
||||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||||
# 比较 local_name == alias
|
||||
# compare local_name == alias
|
||||
Matched: int = 1
|
||||
li: sizet = 0
|
||||
while li < alias_len:
|
||||
@@ -507,7 +423,7 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
||||
break
|
||||
li += 1
|
||||
if Matched == 1:
|
||||
# 提取 module(colon+1 到下一个 ':' 或空格)
|
||||
# extract module (from colon+1 to next ':' or space)
|
||||
m_start: sizet = colon + 1
|
||||
m_end: sizet = m_start
|
||||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
|
||||
@@ -520,33 +436,28 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
||||
stdlib.free(mbuf)
|
||||
return res
|
||||
return None
|
||||
# 跳到下一条目
|
||||
# advance to next entry
|
||||
while ci < total_len and from_imports[ci] != ' ': ci += 1
|
||||
return None
|
||||
# ============================================================
|
||||
# LookupModNameByAlias - 从 from_imports 别名映射解析模块真名
|
||||
#
|
||||
# import X as Y 时 ImportsGenMixin.add_from_import 写入 "Y:X"(Y=别名,X=模块真名);
|
||||
# 本函数返回别名 Y 对应的模块真名 X(供限定名解析 "Y.Class" → 按 X 模块归属匹配)。
|
||||
# Returns: 模块名字符串(stdlib 分配,调用方负责 free),None 未找到
|
||||
# ============================================================
|
||||
# LookupModNameByAlias - return real module name X for alias Y (from_imports "Y:X"; caller frees).
|
||||
def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
||||
"""从 from_imports 别名映射解析模块真名(import X as Y → Y:X)"""
|
||||
"""Resolve the real module name for an alias from from_imports (import X as Y -> Y:X)"""
|
||||
if from_imports is None or alias is None: return None
|
||||
alias_len: sizet = string.strlen(alias)
|
||||
if alias_len == 0: return None
|
||||
total_len: sizet = string.strlen(from_imports)
|
||||
ci: sizet = 0
|
||||
while ci < total_len:
|
||||
# 跳过前导空格
|
||||
# skip leading spaces
|
||||
while ci < total_len and from_imports[ci] == ' ': ci += 1
|
||||
if ci >= total_len: break
|
||||
# 条目起点 ci,找 ':'(local_name 结束)
|
||||
# entry starts at ci; find ':' (end of local_name)
|
||||
colon: sizet = ci
|
||||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
|
||||
local_len: sizet = colon - ci
|
||||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||||
# 比较 local_name == alias
|
||||
# compare local_name == alias
|
||||
Matched: int = 1
|
||||
li: sizet = 0
|
||||
while li < alias_len:
|
||||
@@ -555,7 +466,7 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
||||
break
|
||||
li += 1
|
||||
if Matched == 1:
|
||||
# 提取 module(colon+1 到下一个 ':' 或空格)
|
||||
# extract module (from colon+1 to next ':' or space)
|
||||
m_start: sizet = colon + 1
|
||||
m_end: sizet = m_start
|
||||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
|
||||
@@ -566,20 +477,15 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
||||
mbuf[m_end - m_start] = '\0'
|
||||
return mbuf
|
||||
return None
|
||||
# 跳到下一条目
|
||||
# advance to next entry
|
||||
while ci < total_len and from_imports[ci] != ' ': ci += 1
|
||||
return None
|
||||
# ============================================================
|
||||
# PopulateIncludesDeps - 将 includes 文件的依赖存入内存
|
||||
#
|
||||
# Phase B+ 前用 declare_only 翻译 includes 文件、提取 _imported_modules 后调用本函数存入内存;_BuildReachableSha1Set 从内存读取,不读 deps.txt。
|
||||
# Args: sha1 includes 文件的 SHA1(16字符);deps_str 依赖模块名字符串(空格分隔,如 "t c stdint string")
|
||||
# Returns: 0 成功,非 0 失败
|
||||
# ============================================================
|
||||
# PopulateIncludesDeps - store includes deps in memory (deps_str space-separated). 0 ok, non-0 fail.
|
||||
def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
|
||||
"""将 includes 文件的依赖存入内存"""
|
||||
"""Store includes-file dependencies in memory"""
|
||||
if sha1 is None: return 1
|
||||
# 延迟初始化
|
||||
# lazy init
|
||||
if StubMerger._includes_deps_sha1 is None:
|
||||
StubMerger._includes_deps_sha1 = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._includes_deps_str = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
|
||||
@@ -588,14 +494,14 @@ def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
|
||||
string.memset(StubMerger._includes_deps_str, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
|
||||
StubMerger._includes_deps_count = 0
|
||||
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# 去重:若 SHA1 已存在则跳过
|
||||
# dedup: skip if SHA1 already present
|
||||
for i in range(StubMerger._includes_deps_count):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0: return 0
|
||||
# 写入 SHA1
|
||||
# write SHA1
|
||||
idx: sizet = sizet(StubMerger._includes_deps_count) * 17
|
||||
string.strcpy(StubMerger._includes_deps_sha1 + idx, sha1)
|
||||
# 写入依赖字符串
|
||||
# write deps string
|
||||
idx2: sizet = sizet(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
|
||||
if deps_str is not None:
|
||||
dl: sizet = string.strlen(deps_str)
|
||||
@@ -605,16 +511,9 @@ def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
|
||||
StubMerger._includes_deps_count = StubMerger._includes_deps_count + 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# LookupIncludesDeps - 从内存查找 includes 文件的依赖
|
||||
#
|
||||
# Args:
|
||||
# sha1: includes 文件的 SHA1(16字符)
|
||||
#
|
||||
# Returns:
|
||||
# 依赖模块名字符串指针(空格分隔),None 表示未找到
|
||||
# ============================================================
|
||||
# LookupIncludesDeps - look up includes deps by SHA1 in memory; returns space-separated deps or None.
|
||||
def LookupIncludesDeps(sha1: str) -> str:
|
||||
"""从内存查找 includes 文件的依赖"""
|
||||
"""Look up includes-file dependencies in memory"""
|
||||
if sha1 is None or StubMerger._includes_deps_sha1 is None: return None
|
||||
if StubMerger._includes_deps_count <= 0: return None
|
||||
for i in range(StubMerger._includes_deps_count):
|
||||
@@ -624,30 +523,21 @@ def LookupIncludesDeps(sha1: str) -> str:
|
||||
return StubMerger._includes_deps_str + idx2
|
||||
return None
|
||||
def GetSha1StoreArrPtr() -> bytes:
|
||||
"""返回 SHA1 数组指针(每个条目 17 字节)"""
|
||||
"""Return the SHA1 array pointer (17 bytes per entry)"""
|
||||
return StubMerger._sha1_store_arr
|
||||
def GetSha1StoreModArrPtr() -> bytes:
|
||||
"""返回模块名数组指针(每个条目 64 字节)"""
|
||||
"""Return the module-name array pointer (64 bytes per entry)"""
|
||||
return StubMerger._sha1_store_mod
|
||||
def GetSha1StoreRelArrPtr() -> bytes:
|
||||
"""返回相对路径数组指针(每个条目 StubMerger.MAX_REL_PATH_LEN 字节)"""
|
||||
"""Return the rel-path array pointer (StubMerger.MAX_REL_PATH_LEN bytes per entry)"""
|
||||
return StubMerger._sha1_store_rel
|
||||
def GetSha1StoreIncdirArrPtr() -> bytes:
|
||||
"""返回所属 includes 根目录数组指针(每个条目 StubMerger.MAX_INC_DIR_LEN 字节,空串表示不属于任何 includes 目录)"""
|
||||
"""Return the owning includes-root array pointer (empty string = not in any includes dir)"""
|
||||
return StubMerger._sha1_store_incdir
|
||||
# ============================================================
|
||||
# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用)
|
||||
#
|
||||
# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。
|
||||
# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*StubMerger.MAX_REL_PATH_LEN]
|
||||
# 内存归全局存储器所有,调用者不要 free
|
||||
# ============================================================
|
||||
# GetSha1StoreArr - return the store's sha1/mod/rel array pointers and entry count; do not free.
|
||||
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
|
||||
"""获取全局存储器数组指针
|
||||
|
||||
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 槽的地址(t.CPtr*),
|
||||
函数把存储器数组指针写入这些槽。返回条目数,-1 失败
|
||||
"""
|
||||
"""Get the global store array pointers and entry count; returns -1 on failure"""
|
||||
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: return -1
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None: return -1
|
||||
sp: rawptr[t.CPtr] = out_sha1_arr
|
||||
@@ -659,14 +549,9 @@ def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], o
|
||||
return StubMerger._sha1_store_count
|
||||
|
||||
# ============================================================
|
||||
# LookupSha1RelPath - 通过 SHA1 查找相对路径
|
||||
#
|
||||
# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free),用于把 sha1 转成人类可读的文件路径。
|
||||
# Args: sha1 SHA1 字符串(16 字符)
|
||||
# Returns: 相对路径字符串指针(只读),None=未找到
|
||||
# ============================================================
|
||||
# LookupSha1RelPath - return the read-only rel_path pointer for a SHA1 (no free), None if missing.
|
||||
def LookupSha1RelPath(sha1: str) -> str:
|
||||
"""通过 SHA1 查找相对路径,返回只读指针,None=未找到"""
|
||||
"""Return the read-only rel-path pointer for a SHA1, or None if missing"""
|
||||
if sha1 is None: return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None: return None
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
@@ -678,24 +563,9 @@ def LookupSha1RelPath(sha1: str) -> str:
|
||||
|
||||
|
||||
# ============================================================
|
||||
# LookupModNameBySha1 - 通过 SHA1 反查模块名
|
||||
#
|
||||
# 用途:判定"本 stub 是否某包的 __init__"(模块名以 ".__init__" 结尾)——该事实
|
||||
# **不依赖 sha1→rel_path 字段**:rel_path 反查在部分运行形态下拿不到条目 ⇒
|
||||
# VpiReader 原来的 `strstr(rel_path, "__init__.vp")` 判定恒 false ⇒ 包重导出类的
|
||||
# 别名 entry 静默不登记 ⇒ `ast.ClassDef` 这类**限定名在泛型实参里保留包前缀**,
|
||||
# 与构造函数侧(真定义模块前缀)分裂成两个类型(现场 ClassGenGeneric.vp:92 的
|
||||
# CastError;幽灵名 `list[1abcd5277fa442de.ClassDef*]` 全产物 opaque)。
|
||||
# 模块名与 rel_path 是同一次登记写入的(PopulateSha1MapStore /
|
||||
# AppendToSha1MapStore),但模块名可用即足以作出判定与取包前缀。
|
||||
#
|
||||
# 同一 SHA1 可能有多条(App 源文件按全路径模块名与短模块名各登记一次):返回
|
||||
# **第一条命中**;各条的包前缀一致,调用方只取"去 .__init__ 后的前缀"。
|
||||
# Args: sha1 SHA1 字符串(16 字符)
|
||||
# Returns: 模块名字符串指针(只读,不要 free),None=未找到
|
||||
# ============================================================
|
||||
# LookupModNameBySha1 - reverse lookup module name by SHA1 (read-only ptr, no free); first hit wins.
|
||||
def LookupModNameBySha1(sha1: str) -> str:
|
||||
"""通过 SHA1 查找模块名,返回只读指针,None=未找到"""
|
||||
"""Return the read-only module-name pointer for a SHA1, or None if missing"""
|
||||
if sha1 is None or sha1[0] == '\0': return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
|
||||
@@ -25,24 +25,13 @@ import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
# ============================================================
|
||||
# StubMergerTopo - 对可达 includes 做逆拓扑排序(被依赖者先编译)
|
||||
#
|
||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。
|
||||
# ============================================================
|
||||
# StubMergerTopo - reverse-topological sort of reachable includes (dependencies first).
|
||||
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
|
||||
|
||||
# ============================================================
|
||||
# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先)
|
||||
#
|
||||
# 跨模块 vtable 生成要求被依赖模块先完整翻译(子类 vtable 槽位需父类方法的类型已注册到全局表)。
|
||||
# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中);入度为 0(不依赖任何可达模块)者先输出,
|
||||
# 输出后对依赖它的模块减入度,入度归 0 者入队。reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。
|
||||
#
|
||||
# Args: store_count / store_sha1_arr / store_mod_arr store 元数据;reachable_set / reachable_count 可达集合(_BuildReachableSha1Set 输出);sorted_idx 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引
|
||||
# Returns: 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译)
|
||||
# ============================================================
|
||||
# BuildTopoSortedIndices - reverse-topological sort of reachable includes (dependencies first).
|
||||
# Uses Kahn's algorithm; reachable_count <= 0 sorts all stores; cycles are appended in store order.
|
||||
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_arr: bytes, reachable_set: bytes, reachable_count: int, sorted_idx: sizet*) -> int:
|
||||
"""对可达 includes 逆拓扑排序(被依赖者先),store 索引写入 sorted_idx"""
|
||||
"""Reverse-topologically sort reachable includes (dependencies first); store indices go to sorted_idx."""
|
||||
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None: return 0
|
||||
indegree: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
|
||||
queue: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
|
||||
@@ -52,13 +41,13 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
if queue is not None: stdlib.free(queue)
|
||||
if rel_idx is not None: stdlib.free(rel_idx)
|
||||
return 0
|
||||
# 清零 indegree/queue/rel_idx(malloc 返回未初始化内存,Kahn 依赖入度初值 0)
|
||||
# Zero indegree/queue/rel_idx (malloc memory is uninitialized)
|
||||
zi: int = 0
|
||||
for zi in range(store_count + 1):
|
||||
indegree[zi] = 0
|
||||
queue[zi] = 0
|
||||
rel_idx[zi] = 0
|
||||
# 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为)
|
||||
# Collect reachable store indices (all stores when reachable_count<=0)
|
||||
rel_count: int = 0
|
||||
i_t: int
|
||||
if reachable_set is None or reachable_count <= 0:
|
||||
@@ -71,7 +60,7 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
rel_idx[rel_count] = i_t
|
||||
rel_count += 1
|
||||
|
||||
# 计算入度:m 在可达集合内的直接依赖数
|
||||
# Compute indegree: number of direct deps of m within the reachable set
|
||||
for i_r in range(rel_count):
|
||||
si: int = rel_idx[i_r]
|
||||
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si) * 17)
|
||||
@@ -91,11 +80,11 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf)
|
||||
stdlib.free(dname_buf)
|
||||
if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
|
||||
# 中间 int 变量累加:绕过联合类型(CSizeT*)+ 整数的指针算术编译 bug
|
||||
# Use an intermediate int to work around a union-pointer arithmetic codegen bug
|
||||
indg: int = indegree[si]
|
||||
indg = indg + 1
|
||||
indegree[si] = indg
|
||||
# Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译
|
||||
# Kahn: nodes with indegree 0 (no reachable deps) are output first
|
||||
q_tail: int = 0
|
||||
for i_r in range(rel_count):
|
||||
si2: int = rel_idx[i_r]
|
||||
@@ -103,14 +92,14 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
queue[q_tail] = si2
|
||||
q_tail += 1
|
||||
out_count: int = 0
|
||||
# Kahn 主循环必须用 while 而非 for range:循环体内会动态入队(for range 上界只求值一次,
|
||||
# 动态入队的模块永远不会被处理,且 indegree>0 兜底也捞不回 → 拓扑漏项)
|
||||
# The Kahn main loop must use while, not for range: the body enqueues dynamically,
|
||||
# and a for range bound is evaluated once, so newly queued modules would be skipped.
|
||||
q_head: int = 0
|
||||
while q_head < q_tail:
|
||||
m: int = queue[q_head]
|
||||
sorted_idx[out_count] = m
|
||||
out_count += 1
|
||||
# 对依赖 m 的可达模块减入度
|
||||
# Decrement indegree of reachable modules that depend on m
|
||||
mod_m: str = store_mod_arr + sizet(m) * 64
|
||||
mod_m_len: sizet = string.strlen(mod_m)
|
||||
for i_r in range(rel_count):
|
||||
@@ -129,7 +118,7 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
if dlen3 == 0: continue
|
||||
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0: found_dep = 1
|
||||
if found_dep == 1:
|
||||
# 中间 int 变量递减:绕过联合类型指针算术编译 bug
|
||||
# Use an intermediate int decrement to work around a union-pointer arithmetic codegen bug
|
||||
indg3: int = indegree[si3]
|
||||
indg3 = indg3 - 1
|
||||
indegree[si3] = indg3
|
||||
@@ -137,7 +126,7 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
queue[q_tail] = si3
|
||||
q_tail += 1
|
||||
q_head += 1
|
||||
# 有环时剩余条目按 store 顺序补齐(避免漏编译)
|
||||
# On cycles, append remaining entries in store order to avoid skipping compilation
|
||||
for i_r in range(rel_count):
|
||||
si4: int = rel_idx[i_r]
|
||||
if indegree[si4] > 0:
|
||||
|
||||
@@ -19,47 +19,41 @@ import lib.Projectrans.Utils as Utils
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StubMerger - stub 声明合并模块(Phase2 核心)
|
||||
#
|
||||
# 从 temp 目录加载所有 .stub.ll,把依赖模块的声明合并到主 IR 的 target triple 之后,使链接器能解析跨模块符号引用。
|
||||
# 合并策略:扫描 temp 目录收集 {sha1}.stub.ll → 读取内容 → 过滤去重(避免重复声明)→ 插入主 IR。
|
||||
# ============================================================
|
||||
# StubMerger - merges dependency stub declarations into the main IR (Phase2 core):
|
||||
# collect {sha1}.stub.ll from temp, dedup, then insert after the target triple so cross-module symbols link.
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# 最大 includes SHA1 数(用于过滤)
|
||||
# Max includes SHA1 entries (for filtering)
|
||||
define MAX_INCLUDES_SHA1 = 1024
|
||||
# stub/text 读取缓冲区大小
|
||||
# 1MB 不够:ast/parser.vp 的 text.ll >1MB,读取本地 text.ll 单次截断
|
||||
# → 尾部丢字(opt: "end of file in global variable name")。
|
||||
# Stub/text read buffer size. 1MB is too small: text.ll files exceed 1MB, so a single
|
||||
# truncated read drops trailing data (opt: "end of file in global variable name").
|
||||
define STUB_READ_BUF_SIZE = 8388608
|
||||
# 最大 includes 条目数
|
||||
# Max includes entries
|
||||
define MAX_INCLUDES = 1024
|
||||
# 可达性分析 worklist/processed 缓冲区大小(8192 不够:递归扫描 source_dir
|
||||
# 后 worklist 含全部 App 文件 imports 的拼接 + BFS 依赖展开,追加82 溢出)
|
||||
# Reachability worklist/processed buffer size. 8192 is too small: after scanning
|
||||
# source_dir it holds all App imports plus BFS expansion, overflowing on append 82.
|
||||
define REACH_WORKLIST_BUF = 65536
|
||||
|
||||
# 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
|
||||
# Phase1 扫描完成后填充,供所有函数使用
|
||||
_sha1_store_arr: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个)
|
||||
_sha1_store_incdir: bytes # 所属 includes 根目录数组(每个 512 字节,MAX_INCLUDES 个,多 includes 支持)
|
||||
_sha1_store_count: int # 条目数
|
||||
# Global SHA1 map store (in memory, no dependency on _sha1_map.txt).
|
||||
# Filled after the Phase1 scan and shared by all functions.
|
||||
_sha1_store_arr: bytes # SHA1 array (17 bytes each, MAX_INCLUDES entries)
|
||||
_sha1_store_mod: bytes # Module-name array (64 bytes each, MAX_INCLUDES entries)
|
||||
_sha1_store_rel: bytes # Relative-path array (256 bytes each, MAX_INCLUDES entries)
|
||||
_sha1_store_incdir: bytes # Owning includes-root array (512 bytes each, MAX_INCLUDES entries, multi-includes)
|
||||
_sha1_store_count: int # Entry count
|
||||
|
||||
# includes 依赖内存存储(不依赖 deps.txt 过程文件)
|
||||
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入此处
|
||||
# _BuildReachableSha1Set 从此处读取依赖,不读 deps.txt
|
||||
_includes_deps_sha1: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
|
||||
_includes_deps_str: bytes # 依赖字符串数组(每个 512 字节,MAX_INCLUDES 个)
|
||||
_includes_deps_count: int # 条目数
|
||||
# In-memory includes dependency store (no dependency on the deps.txt file).
|
||||
# Before Phase B+ all includes are translated in declare_only mode; _BuildReachableSha1Set reads deps from here.
|
||||
_includes_deps_sha1: bytes # SHA1 array (17 bytes each, MAX_INCLUDES entries)
|
||||
_includes_deps_str: bytes # Dependency-string array (512 bytes each, MAX_INCLUDES entries)
|
||||
_includes_deps_count: int # Entry count
|
||||
|
||||
# 单条依赖字符串最大长度
|
||||
# Max length of a single dependency string
|
||||
define MAX_DEPS_STR_LEN = 512
|
||||
|
||||
# rel_path 最大长度
|
||||
# Max rel_path length
|
||||
define MAX_REL_PATH_LEN = 256
|
||||
# includes 根目录最大长度(绝对路径,多 includes 支持)
|
||||
# Max includes-root path length (absolute path, multi-includes)
|
||||
define MAX_INC_DIR_LEN = 512
|
||||
+3
-5
@@ -4,10 +4,8 @@ import memhub
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
# ============================================================
|
||||
# lib 包入口 - 声明全局 _mbuddy 内存池指针
|
||||
# ============================================================
|
||||
# lib package entry - declares the global _mbuddy memory-pool pointer.
|
||||
|
||||
# 全局 mbuddy 内存池指针(由 App/main.vp 初始化后注入)
|
||||
# 注意: 使用 MemBuddy 类型而非 MemManager,避免虚函数分派不工作时调用父类 alloc 返回 None
|
||||
# Global mbuddy memory-pool pointer (injected by App/main.vp after init).
|
||||
# Uses MemBuddy, not MemManager, to avoid returning None when virtual dispatch is unavailable.
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
@@ -2,22 +2,20 @@ import t, c
|
||||
import memhub
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CompilerFactory - 编译器结构体工厂(对齐 TPC Translator 装配模式)
|
||||
# ============================================================
|
||||
# CompilerFactory - compiler struct factory (aligned with TPC Translator assembly).
|
||||
novirtual class CompilerFactory:
|
||||
"""编译器结构体工厂(持有主池/永久池等共享对象)"""
|
||||
Pool: memhub.MemBuddy* # 主内存池(瞬时数据,mark/reset 回收)
|
||||
PermPool: memhub.MemBuddy* # 永久内存池(持久数据,从不 reset)
|
||||
"""Compiler struct factory holding shared objects such as the main and permanent pools."""
|
||||
Pool: memhub.MemBuddy* # Main memory pool (transient data; reclaimed by mark/reset)
|
||||
PermPool: memhub.MemBuddy* # Permanent memory pool (persistent data; never reset)
|
||||
|
||||
# ---- REnum 变体注册表(编译期单例持有,与 perm_pool 的 struct entry 同寿命)----
|
||||
# ---- REnum variant registry (compile-time singleton; same lifetime as perm_pool struct entries)----
|
||||
RenumClassNames: sizet*
|
||||
RenumClassVariantCounts: sizet*
|
||||
RenumVariantNames: sizet*
|
||||
RenumVariantTags: sizet*
|
||||
RenumClassCount: int
|
||||
|
||||
# ---- REnum 变体字段别名表(同上,必须同寿命;_bind_variant_fields 查字段真实类型)----
|
||||
# ---- REnum variant field alias table (same lifetime; _bind_variant_fields resolves real field types)----
|
||||
RenumAliasClass: sizet*
|
||||
RenumAliasField: sizet*
|
||||
RenumAliasIndex: sizet*
|
||||
@@ -30,5 +28,5 @@ novirtual class CompilerFactory:
|
||||
self.Pool = pool
|
||||
self.PermPool = perm_pool
|
||||
|
||||
# 全局工厂单例
|
||||
# Global factory singleton
|
||||
factory: CompilerFactory* = None
|
||||
+373
-1040
File diff suppressed because it is too large.
Load diff
+182
-275
File diff suppressed because it is too large.
Load diff
@@ -6,32 +6,30 @@ import memhub
|
||||
import mlirlite
|
||||
|
||||
|
||||
# ============================================================
|
||||
# BaseGenMixin - 生成器基座(状态容器)
|
||||
# ============================================================
|
||||
# BaseGenMixin - generator base (state container).
|
||||
novirtual class BaseGenMixin:
|
||||
"""生成器状态容器(基座)"""
|
||||
"""Generator state container (base)."""
|
||||
|
||||
# 内存/IR 状态
|
||||
# Memory / IR state
|
||||
Gen: Generator.Generator*
|
||||
Pool: memhub.MemBuddy* # 内存池
|
||||
Module: mlirlite.LLVMModule* # 当前 LLVM 模块
|
||||
Builder: mlirlite.IRBuilder* # 当前 IRBuilder
|
||||
# 符号表/函数表
|
||||
Pool: memhub.MemBuddy* # Memory pool
|
||||
Module: mlirlite.LLVMModule* # Current LLVM module
|
||||
Builder: mlirlite.IRBuilder* # Current IRBuilder
|
||||
# Symbol / function tables
|
||||
SymTab: rawptr[GenTypes.SymbolTable]
|
||||
Funcs: GenTypes.FuncEntry*
|
||||
FuncCount: int
|
||||
# 导入/模块信息
|
||||
# Import / module info
|
||||
ImportedModules: str
|
||||
FromImports: str
|
||||
ModuleSha1: str
|
||||
# 当前翻译上下文
|
||||
# Current translation context
|
||||
CurFunc: mlirlite.Function*
|
||||
CurBuilder: mlirlite.IRBuilder*
|
||||
CurClassName: str
|
||||
|
||||
def InitBaseGen(self, pool: memhub.MemBuddy*) -> int:
|
||||
"""初始化生成器状态(默认值;子类构造时调用)"""
|
||||
"""Initialize generator state to defaults; called from subclass constructors."""
|
||||
if self is None: return 1
|
||||
self.Pool = pool
|
||||
self.Module = None
|
||||
|
||||
@@ -22,16 +22,12 @@ import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenBase - CallGenMixin 拆分 1/9(最底层父类):模块符号查找 +
|
||||
# 返回类型解析基础设施
|
||||
# ============================================================
|
||||
# CallGenBase - split 1/9 of CallGenMixin (bottom parent): module symbol
|
||||
# lookup + return-type resolution infrastructure
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 常量(Gen 层各处容量上限)
|
||||
# ============================================================
|
||||
# 全局函数表 / 默认参数表容量
|
||||
# Constants (capacity limits used across the Gen layer)
|
||||
# Global func table / defaults table capacity
|
||||
define GLOBAL_FUNC_MAX = 8192
|
||||
define GLOBAL_DEFAULTS_MAX = 4096
|
||||
define _CEXPORT_MAX = 256
|
||||
@@ -42,90 +38,80 @@ define ASM_ARGS_BUF_SIZE = 512
|
||||
define ASM_LINE_BUF_SIZE = 4096
|
||||
define LLVMIR_MAX_OPS = 16
|
||||
define LLVMIR_TEMPLATE_BUF = 512
|
||||
# 闭包名表大小(消费方:CallGenLeafKit._ensure_closure_names/register_closure_name)
|
||||
# Closure name table size (CallGenLeafKit._ensure_closure_names/register_closure_name)
|
||||
define MAX_CLOSURE_NAMES = 128
|
||||
# 函数指针签名表大小(消费方:CallGenLeafKit._ensure_funcptr_sigs/register_func_ptr_sig)
|
||||
# Function-pointer signature table size (CallGenLeafKit._ensure_funcptr_sigs/register_func_ptr_sig)
|
||||
define MAX_FUNC_PTR_SIGS = 64
|
||||
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32。
|
||||
# 消费方:CallGenLeafKit._collect_subclass_vtable_classes)
|
||||
# isinstance subclass collection cap (bounds check); used by
|
||||
# CallGenLeafKit._collect_subclass_vtable_classes
|
||||
define ISINSTANCE_CLASS_MAX = 64
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenBase - 最底层父类(字段 + 模块查找 + 返回类型解析)
|
||||
# ============================================================
|
||||
# CallGenBase - bottom parent class (fields + module lookup + return-type resolution)
|
||||
novirtual class CallGenBase:
|
||||
"""调用分派最底层父类(字段 + 模块符号查找 + 返回类型解析基础设施)"""
|
||||
"""Base class for call dispatch (fields + module symbol lookup + return-type resolution infrastructure)"""
|
||||
|
||||
# --- c.cexport 注册表(原 _g_cexport_sha1_arr/_g_cexport_func_arr/_g_cexport_count) ---
|
||||
# --- c.cexport registry (was _g_cexport_sha1_arr/_g_cexport_func_arr/_g_cexport_count) ---
|
||||
Gen: Generator.Generator*
|
||||
CexportSha1Arr: bytes = None
|
||||
CexportFuncArr: bytes = None
|
||||
CexportCount: int = 0
|
||||
|
||||
# --- static 符号注册表(`static` 修饰符 / @t.CStatic / t.CStatic 标记的符号 →
|
||||
# 定义模块 SHA1)。语义 = 禁止跨模块使用(C 的 internal linkage):定义侧登记,
|
||||
# 跨模块引用侧(mod.name 取值 / mod.fn() 调用 / from mod import name)拦截报错。
|
||||
# 与 CexportSha1Arr 同款 PermPool 定长槽(跨 mark/reset 存活)。
|
||||
# --- static symbol registry (static / @t.CStatic -> defining module SHA1),
|
||||
# meaning no cross-module use (C internal linkage); PermPool fixed slots.
|
||||
StaticSha1Arr: bytes = None
|
||||
StaticNameArr: bytes = None
|
||||
StaticCount: int = 0
|
||||
|
||||
# --- LLVMIR 寄存器序号 ---
|
||||
# --- LLVMIR register sequence ---
|
||||
LLVMIRRegSeq: int = 0
|
||||
|
||||
# --- temp 目录 ---
|
||||
# --- temp directory ---
|
||||
TempDir: str = None
|
||||
TempDirLen: sizet = 0
|
||||
|
||||
# --- 闭包名表(原 _g_closure_names/_g_closure_count) ---
|
||||
# --- closure name table (was _g_closure_names/_g_closure_count) ---
|
||||
ClosureNames: str = None
|
||||
ClosureCount: int = 0
|
||||
|
||||
# --- 函数指针签名表(原 _g_funcptr_names/_g_funcptr_rets/_g_funcptr_count) ---
|
||||
# --- function-pointer signature table (was _g_funcptr_names/_g_funcptr_rets/_g_funcptr_count) ---
|
||||
FuncptrNames: str = None
|
||||
FuncptrRets: uint64* = None
|
||||
FuncptrCount: int = 0
|
||||
|
||||
# --- 全局函数注册表(原 _global_funcs/_global_func_count) ---
|
||||
# --- global function registry (was _global_funcs/_global_func_count) ---
|
||||
GlobalFuncs: GenTypes.FuncEntry* = None
|
||||
GlobalFuncCount: int = 0
|
||||
|
||||
# --- 全局函数表"名 → 槽下标"索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) ---
|
||||
# GlobalFuncs 只追加不重排 ⇒ 槽下标稳定;Built == GlobalFuncCount 才可信,否则回退线性扫描。
|
||||
# --- global func table "name -> slot index" index (open addressing; slot
|
||||
# stores index+1, 0=empty). GlobalFuncs only appends; Built==Count is trusted.
|
||||
GlobalFuncNameIdx: sizet* = None
|
||||
GlobalFuncNameIdxCap: int = 0
|
||||
GlobalFuncNameIdxBuilt: int = -1
|
||||
|
||||
# --- 全局默认参数表(原 _global_defaults/_global_defaults_count) ---
|
||||
# --- global defaults table (was _global_defaults/_global_defaults_count) ---
|
||||
GlobalDefaults: GenTypes.FuncDefaultsEntry* = None
|
||||
GlobalDefaultsCount: int = 0
|
||||
|
||||
# --- 依赖模块 text.ll「方法返回类型文本」缓存(_LookupMethodRetTyFromTextLL 性能优化) ---
|
||||
# key = define 的完整 mangled 函数名("{sha1}.{后缀}"),值只存文本(绝不存 LLVMType*,其分配在每文件 reset 失效的 pool 中);表与键/值均 PermPool 分配,命中后用调用方 pool 重建类型,建表失败即不缓存并回退原扫描。
|
||||
# --- cached method return-type text from dependency text.ll (perf opt for
|
||||
# _LookupMethodRetTyFromTextLL); key = full mangled name, value = text only.
|
||||
MethodRetTyMap: hashtable.HashTable* = None
|
||||
|
||||
def InitCallGen(self) -> int:
|
||||
"""初始化调用 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
|
||||
"""Initialize the call mixin fields (inlined by subclass constructors since NoVTable does not forward inherited symbols)"""
|
||||
if self is None: return 1
|
||||
return 0
|
||||
|
||||
def _lookup_module_sha1(self, mod_name: str) -> str:
|
||||
"""查找模块名对应的 SHA1,找不到返回 None
|
||||
委托给 StubMerger.LookupSha1ByModName(精确匹配 + 包名回退)。
|
||||
"""
|
||||
"""Look up the SHA1 for a module name via StubMerger.LookupSha1ByModName (exact match + package fallback); None if absent."""
|
||||
return StubMergerStore.LookupSha1ByModName(mod_name)
|
||||
|
||||
def _lookup_module_sha1_suffix(self, mod_suffix: str) -> str:
|
||||
"""后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "mlirlite.__types"
|
||||
用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点),
|
||||
去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。
|
||||
委托给 StubMerger.LookupSha1ByModNameSuffix(精确 + 后缀 + 反向后缀)。
|
||||
"""
|
||||
"""Look up a module SHA1 by suffix (e.g. "__types" matches "mlirlite.__types") for relative imports, via StubMerger.LookupSha1ByModNameSuffix."""
|
||||
return StubMergerStore.LookupSha1ByModNameSuffix(mod_suffix)
|
||||
|
||||
def _module_package_from_sha1(self, pool: memhub.MemBuddy*, mod_sha1: str) -> str:
|
||||
"""从 SHA1 存储器的 rel_path 计算模块所属包名(目录部分,'/' → '.')"""
|
||||
"""Compute the module's package name (directory part, '/' to '.') from the SHA1 store's rel_path"""
|
||||
if pool is None or mod_sha1 is None: return None
|
||||
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
|
||||
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
|
||||
@@ -151,15 +137,9 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def _lookup_reexport_from_vpi(self, pool: memhub.MemBuddy*, mod_sha1: str, func_name: str) -> str:
|
||||
"""re-export 表未命中时,从模块 .vpi 按需解析 from-import 定位定义模块
|
||||
includes 模块(如 mlirlite/__init__)的 stub/text 已存在时不会被按需
|
||||
全量翻译(Phase2 3.5 仅在 stub/text 缺失时翻译),而 re-export 表只在
|
||||
declare_only=0 的全量翻译中注册(HandleImportFromNames),因此表可能为空。
|
||||
本函数解析该模块 .vpi 的 "from .X import ... func_name ..." 语句,
|
||||
返回定义模块名(如 mlirlite/__init__ 的 .__builder → "mlirlite.__builder")。
|
||||
"""
|
||||
"""On a re-export table miss, parse the module's .vpi from-import statements to locate the defining module (the table may be empty since it is only populated during full translation)."""
|
||||
if pool is None or mod_sha1 is None or func_name is None: return None
|
||||
# star import 候选模块累积(空格分隔):from X import * 无法确定归属,收集全部候选由调用方逐个验证
|
||||
# accumulate star-import candidates (space-separated); caller validates each
|
||||
star_cands: str = None
|
||||
star_cands_len: sizet = 0
|
||||
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
|
||||
@@ -184,8 +164,8 @@ novirtual class CallGenBase:
|
||||
return None
|
||||
if nrd < READ_SZ: buf[nrd] = 0
|
||||
name_len: sizet = string.strlen(func_name)
|
||||
# 模块自含定义标志:vpi 中行首 "def {func_name}(" 命中且未被同名
|
||||
# from-import 覆盖 → 该模块自身定义 func_name,不是 re-export 源。
|
||||
# own-definition flag: a line-start "def {func_name}(" in vpi not overridden
|
||||
# by a same-named from-import means this module defines it (not a re-export).
|
||||
has_own_def: int = 0
|
||||
total: sizet = string.strlen(buf)
|
||||
pos: sizet = 0
|
||||
@@ -194,7 +174,7 @@ novirtual class CallGenBase:
|
||||
while pos < total and buf[pos] != '\n': pos += 1
|
||||
ll: sizet = pos - ls
|
||||
if pos < total: pos += 1
|
||||
# 自含定义检测(须在 from-import 行匹配之前,对所有行生效):模块级 def 行首无缩进,类内方法 def 有缩进不会误命中
|
||||
# own-def detection (before from-import match): module-level def has no indent
|
||||
if ll > 4 and buf[ls] == 'd' and buf[ls + 1] == 'e' and buf[ls + 2] == 'f' and buf[ls + 3] == ' ':
|
||||
fstart_od: sizet = ls + 4
|
||||
fend_od: sizet = fstart_od
|
||||
@@ -210,10 +190,10 @@ novirtual class CallGenBase:
|
||||
fdi_od += 1
|
||||
if fd_eq_od != 0: has_own_def = 1
|
||||
continue
|
||||
# 行必须以 "from " 开头
|
||||
# line must start with "from "
|
||||
if ll < 6: continue
|
||||
if buf[ls] != 'f' or buf[ls + 1] != 'r' or buf[ls + 2] != 'o' or buf[ls + 3] != 'm' or buf[ls + 4] != ' ': continue
|
||||
# 找 " import " 分隔符
|
||||
# locate the " import " separator
|
||||
imp_pos: sizet = ls + 5
|
||||
while imp_pos + 7 <= ls + ll:
|
||||
if buf[imp_pos] == ' ' and buf[imp_pos + 1] == 'i' and buf[imp_pos + 2] == 'm' and buf[imp_pos + 3] == 'p' and buf[imp_pos + 4] == 'o' and buf[imp_pos + 5] == 'r' and buf[imp_pos + 6] == 't' and buf[imp_pos + 7] == ' ':
|
||||
@@ -222,9 +202,9 @@ novirtual class CallGenBase:
|
||||
if imp_pos + 7 > ls + ll: continue
|
||||
mod_start: sizet = ls + 5
|
||||
mod_end: sizet = imp_pos
|
||||
# 名称列表从 "import " 之后(逗号分隔,含 "A as B" 取原名 A)
|
||||
# name list after "import " (comma-separated; "A as B" takes original A)
|
||||
nm_start: sizet = imp_pos + 8
|
||||
# star import 支持(from X import *):首个 star 行未必是定义模块,累积全部候选,扫描完由调用方按 vpi 返回注解逐个验证
|
||||
# star import (from X import *): collect all candidates; caller verifies
|
||||
if nm_start < ls + ll and buf[nm_start] == '*':
|
||||
star_dot: int = 0
|
||||
if buf[mod_start] == '.': star_dot = 1
|
||||
@@ -245,7 +225,7 @@ novirtual class CallGenBase:
|
||||
if sres is not None:
|
||||
viperlib.snprintf(sres, srl, "%s.%s", spkg, star_buf)
|
||||
star_buf = sres
|
||||
# 累积候选(空格分隔)
|
||||
# accumulate candidates (space-separated)
|
||||
sbl: sizet = string.strlen(star_buf)
|
||||
new_cands: str = pool.alloc(star_cands_len + sbl + 2)
|
||||
if new_cands is not None:
|
||||
@@ -281,7 +261,7 @@ novirtual class CallGenBase:
|
||||
m_ok = 0
|
||||
break
|
||||
if m_ok == 1:
|
||||
# 命中:解析模块名(前导点 → 相对导入,补包前缀)
|
||||
# hit: parse module name (leading dot -> relative, add pkg prefix)
|
||||
has_dot: int = 0
|
||||
if buf[mod_start] == '.': has_dot = 1
|
||||
mod_buf: str = pool.alloc(mod_end - mod_start + 1)
|
||||
@@ -313,7 +293,7 @@ novirtual class CallGenBase:
|
||||
stdlib.free(buf)
|
||||
return mod_buf
|
||||
scan = n_end
|
||||
# 全部行扫描完毕:仅有 star import 时返回累积候选(调用方逐个验证定义模块);模块自含 def {func_name} 时本模块即定义者,返回 None 保持归属
|
||||
# scan done: return star candidates, or None if this module defines it
|
||||
if star_cands is not None:
|
||||
stdlib.free(buf)
|
||||
if has_own_def == 0: return star_cands
|
||||
@@ -323,7 +303,7 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def register_cexport_func(self, sha1: str, func_name: str) -> int:
|
||||
"""注册 CExport 函数到全局表(统一写入 Gen.CallGenBase 真源,跨子对象共享)"""
|
||||
"""Register a CExport function into the global table (single source of truth in Gen.CallGenBase)"""
|
||||
if self.Gen is None or sha1 is None or func_name is None: return 0
|
||||
if self.Gen.CallGenBase.CexportSha1Arr is None:
|
||||
self.Gen.CallGenBase.CexportSha1Arr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 17)
|
||||
@@ -333,7 +313,7 @@ novirtual class CallGenBase:
|
||||
string.memset(self.Gen.CallGenBase.CexportFuncArr, 0, _CEXPORT_MAX * 64)
|
||||
if self.Gen.CallGenBase.CexportCount < _CEXPORT_MAX:
|
||||
idx: sizet = sizet(self.Gen.CallGenBase.CexportCount) * 17
|
||||
# 槽位定长 17/64 字节,strcpy 无界会越界写相邻槽(stdlib 堆区)
|
||||
# fixed 17/64-byte slots; unbounded strcpy would overrun adjacent slots
|
||||
string.strncpy(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1, 16)
|
||||
self.Gen.CallGenBase.CexportSha1Arr[idx + 16] = '\0'
|
||||
idx2: sizet = sizet(self.Gen.CallGenBase.CexportCount) * 64
|
||||
@@ -345,13 +325,7 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def is_cexport_func(self, sha1: str, func_name: str) -> int:
|
||||
"""检查 (sha1, func_name) 是否是 CExport 函数(统一读 Gen.CallGenBase 真源)
|
||||
|
||||
注册表未命中时回退查 vpi 返回注解:跨模块编译存在时序差(如 apic.vp
|
||||
先于 mm.vp 翻译时 mm 的 CExport 尚未 register_cexport_func → mm.malloc
|
||||
误用 @1fdad1c76fb116bc.malloc 而非裸名 @malloc → ld.lld undefined
|
||||
symbol: 1fdad1c76fb116bc.malloc)。
|
||||
"""
|
||||
"""Whether (sha1, func_name) is a CExport function (reads the Gen.CallGenBase source of truth), falling back to the vpi return annotation on a registry miss."""
|
||||
if self.Gen is None or sha1 is None or func_name is None: return 0
|
||||
if self.Gen.CallGenBase.CexportSha1Arr is not None:
|
||||
for i in range(self.Gen.CallGenBase.CexportCount):
|
||||
@@ -362,17 +336,10 @@ novirtual class CallGenBase:
|
||||
return self._is_cexport_func_from_vpi(sha1, func_name)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# RegisterStaticSymbol / IsStaticSymbol - static 符号注册表
|
||||
#
|
||||
# `static def f` / `static x: int` / `@t.CStatic` / `x: t.CStatic | T` 四种写法
|
||||
# 在解析层已归一为同一套标记(t.CStatic),语义 = 禁止跨模块使用(C 的
|
||||
# internal linkage)。定义侧翻译时登记 (符号名, 定义模块 SHA1);跨模块引用侧
|
||||
# (mod.name 取值 / mod.fn() 调用 / from mod import name)用 IsStaticSymbol
|
||||
# 判定并 fail-fast。
|
||||
# ============================================================
|
||||
# RegisterStaticSymbol / IsStaticSymbol - static symbol registry: the four
|
||||
# syntaxes all normalize to t.CStatic; meaning = no cross-module use.
|
||||
def RegisterStaticSymbol(self, sha1: str, name: str) -> int:
|
||||
"""登记 static 符号(定义模块 SHA1 + 符号名);1=已登记(含幂等重复)/ 0=失败"""
|
||||
"""Register a static symbol (defining module SHA1 + symbol name); 1=registered (idempotent) / 0=failure"""
|
||||
if self.Gen is None or sha1 is None or name is None: return 0
|
||||
if self.Gen.CallGenBase.StaticSha1Arr is None:
|
||||
self.Gen.CallGenBase.StaticSha1Arr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 17)
|
||||
@@ -380,7 +347,7 @@ novirtual class CallGenBase:
|
||||
if self.Gen.CallGenBase.StaticSha1Arr is None or self.Gen.CallGenBase.StaticNameArr is None: return 0
|
||||
string.memset(self.Gen.CallGenBase.StaticSha1Arr, 0, _CEXPORT_MAX * 17)
|
||||
string.memset(self.Gen.CallGenBase.StaticNameArr, 0, _CEXPORT_MAX * 64)
|
||||
# 幂等:同 (sha1, name) 重复登记(跨遍重译 / 同文件多次进入)直接命中返回
|
||||
# idempotent: repeated (sha1, name) registration returns a hit
|
||||
for i in range(self.Gen.CallGenBase.StaticCount):
|
||||
si: sizet = sizet(i) * 17
|
||||
if string.strcmp(self.Gen.CallGenBase.StaticSha1Arr + si, sha1) == 0:
|
||||
@@ -388,7 +355,7 @@ novirtual class CallGenBase:
|
||||
if string.strcmp(self.Gen.CallGenBase.StaticNameArr + sn, name) == 0: return 1
|
||||
if self.Gen.CallGenBase.StaticCount < _CEXPORT_MAX:
|
||||
idx: sizet = sizet(self.Gen.CallGenBase.StaticCount) * 17
|
||||
# 槽位定长 17/64 字节,strcpy 无界会越界写相邻槽(stdlib 堆区)
|
||||
# fixed 17/64-byte slots; unbounded strcpy would overrun adjacent slots
|
||||
string.strncpy(self.Gen.CallGenBase.StaticSha1Arr + idx, sha1, 16)
|
||||
self.Gen.CallGenBase.StaticSha1Arr[idx + 16] = '\0'
|
||||
idx2: sizet = sizet(self.Gen.CallGenBase.StaticCount) * 64
|
||||
@@ -400,12 +367,7 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def IsStaticSymbol(self, sha1: str, name: str) -> int:
|
||||
"""(sha1, name) 是否为已登记的 static 符号(跨模块引用拦截判据)
|
||||
|
||||
与 is_cexport_func 的区别:cexport 是"导出裸名"(跨模块**允许**),
|
||||
static 是"禁止跨模块"(跨模块**必须报错**);两者判据方向相反,
|
||||
但同用 (sha1, 符号名) 精确配对,避免同名符号跨模块误判。
|
||||
"""
|
||||
"""Whether (sha1, name) is a registered static symbol, the cross-module reference blocker; pairs (sha1, name) exactly to avoid same-name mistakes."""
|
||||
if self.Gen is None or sha1 is None or name is None: return 0
|
||||
if self.Gen.CallGenBase.StaticSha1Arr is None: return 0
|
||||
for i in range(self.Gen.CallGenBase.StaticCount):
|
||||
@@ -417,10 +379,7 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def _is_cexport_func_from_vpi(self, sha1: str, func_name: str) -> int:
|
||||
"""vpi 返回注解含 t.CExport → 定义端用裸名(跨模块调用无 sha1 头)
|
||||
|
||||
对齐 _is_state_func_from_vpi(State 标记)的 vpi 按需解析路径。
|
||||
"""
|
||||
"""The vpi return annotation contains t.CExport, so the defining side uses a bare name (no sha1 prefix cross-module)."""
|
||||
if sha1 is None or func_name is None: return 0
|
||||
if self.Gen is None or self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen <= 0: return 0
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
|
||||
@@ -431,42 +390,33 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def _is_state_func_from_vpi(self, pool: memhub.MemBuddy*, sha1: str, func_name: str) -> int:
|
||||
"""vpi 返回注解含 t.State → 外部 C 函数(定义端用裸名,跨模块调用无 sha1 头)
|
||||
|
||||
t.State(同 t.CExtern/t.CExport)是外部函数标记:定义端生成裸名
|
||||
@PyList_Size(链接外部库 libpython),跨模块调用若生成 @{sha1}.PyList_Size
|
||||
→ ld.lld undefined symbol(CPythonTest PyList_Size 根因)。CExport 走
|
||||
is_cexport_func 注册表,State 函数未注册 → 按需解析 vpi 返回注解判断。
|
||||
"""
|
||||
"""The vpi return annotation contains t.State, an external C function marker, so bare names are used cross-module to avoid undefined symbols."""
|
||||
if pool is None or sha1 is None or func_name is None: return 0
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
|
||||
state_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
|
||||
if state_annot is None: return 0
|
||||
# 返回注解含 "State"(如 "sizet | t.State")→ 外部 C 函数
|
||||
# ret annotation contains "State" -> external C function
|
||||
if string.strstr(state_annot, "State") is not None: return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _lookup_mod_sha1(self, pool: memhub.MemBuddy*, from_imports: str, class_name: str, allow_star_fallback: int = 1) -> str:
|
||||
"""从 from_imports 查找类所属模块的 SHA1,返回 None=未找到
|
||||
|
||||
allow_star_fallback: 传 0 禁用 star import 回退(用于明确模块名/函数名查找)
|
||||
"""
|
||||
"""Look up the SHA1 of the module owning a class found in from_imports; None if not found. allow_star_fallback=0 disables star-import fallback."""
|
||||
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, class_name, allow_star_fallback)
|
||||
if mod_name_raw is None: return None
|
||||
return self._lookup_sha1_from_modname_raw(pool, mod_name_raw)
|
||||
|
||||
|
||||
def _lookup_sha1_from_modname_raw(self, pool: memhub.MemBuddy*, mod_name_raw: str) -> str:
|
||||
"""从原始模块名查找 SHA1,返回 None=未找到"""
|
||||
# 手动找到模块名结束位置(空格、null、或 ':' 别名分隔符),复制到新缓冲区
|
||||
"""Look up SHA1 from a raw module name; None if not found"""
|
||||
# find module-name end (space/null/':' alias sep), copy into a new buffer
|
||||
mod_len: sizet = 0
|
||||
while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':': mod_len += 1
|
||||
mod_name_buf: str = pool.alloc(mod_len + 1)
|
||||
if mod_name_buf is None: return None
|
||||
for mi in range(mod_len): mod_name_buf[mi] = mod_name_raw[mi]
|
||||
mod_name_buf[mod_len] = '\0'
|
||||
# 去掉相对导入的前导点
|
||||
# strip leading dots of relative import
|
||||
base_mod: str = mod_name_buf
|
||||
while base_mod[0] == '.': base_mod = base_mod + 1
|
||||
sha1: str = self._lookup_module_sha1(base_mod)
|
||||
@@ -475,16 +425,7 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def _FuncTyFromFunction(self, pool: memhub.MemBuddy*, f_ref: mlirlite.Function*) -> mlirlite.LLVMType*:
|
||||
"""由 mlirlite.Function 的形参链表(Param 链)构造 LLVMType.Func 类型。
|
||||
|
||||
为什么不能直接把 Param 链交给 mlirlite.Func:`Function.Params` 是 **Param** 链
|
||||
(__function.vp,`Param.Ty: rawptr[LLVMType]`),而 `LLVMType.Func` 的类型构造链表是
|
||||
**ParamNode** 链(__types.vp,由 new_param_node 建、paramnode_set_next 串)。两者是
|
||||
互不相关的 class —— 严格指针规则下不可互传(旧的无条件 bitcast 掩盖了这点,且两者
|
||||
字段顺序不同,bitcast 后读 Ty/Name 都是错位数据)。
|
||||
这里逐个 new_param_node 重建 ParamNode 链再交给 Func:语义等价(两侧都只取各参数的
|
||||
类型),类型也对得上 Func(params: rawptr[ParamNode]) 的形参。
|
||||
"""
|
||||
"""Build an LLVMType.Func from a mlirlite.Function's Param chain, rebuilding it as a ParamNode chain since the two list types are unrelated."""
|
||||
pn_head: mlirlite.ParamNode* = None
|
||||
pn_prev: mlirlite.ParamNode* = None
|
||||
p_cnt: sizet = 0
|
||||
@@ -502,29 +443,11 @@ novirtual class CallGenBase:
|
||||
return mlirlite.Func(pool, f_ref.RetTy, pn_head, p_cnt)
|
||||
|
||||
def find_func_in_module(self, mod: mlirlite.LLVMModule*, name: str, owner_sha1: str = None) -> mlirlite.Function*:
|
||||
"""在模块的函数链表中按名称查找函数
|
||||
|
||||
查找策略:
|
||||
1. 精确匹配(如 "strlen" 匹配 @strlen,有 t.CExport 的函数)
|
||||
2. 后缀匹配(如 "atoi" 匹配 @"83210a72e829a086.atoi",无 t.CExport 的函数)
|
||||
|
||||
owner_sha1: 当前模块 SHA1。非 None 时在本模块 define 判定之前先做
|
||||
"本模块同名函数"优先扫描(mangled 名以 "{owner_sha1}." 开头且恰为
|
||||
"{owner_sha1}.{name}")。裸名调用应优先解析为当前模块自身函数——
|
||||
其前向 declare 在函数体翻译时可能尚未激活(IsDeclared 仍为 1),
|
||||
pass2 的 IsDeclared==0 判定会跳过它,pass3 又会按链表顺序误绑排在
|
||||
FuncHead 前面的跨模块 stub declare(lexer.vp _advance 根因)。
|
||||
|
||||
使用 mlirlite.function_get_name/next 访问器绕过 stub 类型限制
|
||||
(stub 类型字段不足且字段类型可能错误,直接字段访问会被静默跳过)。
|
||||
"""
|
||||
"""Find a function by name in the module's function list: exact then suffix match, honoring owner_sha1 for same-module priority."""
|
||||
if mod is None or name is None: return None
|
||||
name_len: sizet = string.strlen(name)
|
||||
# 热点优化:原「趟 1 精确匹配+本模块同名、趟 2 declare/任意后缀」两趟完整链表扫描
|
||||
# 合并为一趟——同一节点只取一次 function_get_name/NameLen/next(原两趟各取一次,
|
||||
# 该链表平均数百节点且本函数每次调用都全链遍历,PROF 中本函数及其访问器约占 10%)。
|
||||
# 优先级与逐趟结果完全一致:精确匹配命中即返回 > 本模块同名 > declare 后缀 > 任意后缀;
|
||||
# 同类候选均取链表首个命中。
|
||||
# Hot-path: merge the former two full list passes into one, taking
|
||||
# function_get_name/NameLen/next once per node; priority order unchanged.
|
||||
cur: mlirlite.Function* = mod.FuncHead
|
||||
mangle_hit: mlirlite.Function* = None
|
||||
declare_hit: mlirlite.Function* = None
|
||||
@@ -532,11 +455,12 @@ novirtual class CallGenBase:
|
||||
while cur is not None:
|
||||
cur_name: str = mlirlite.function_get_name(cur)
|
||||
if cur_name is not None:
|
||||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp(mangled 名以 sha1 十六进制打头,裸名多为字母/下划线)
|
||||
# fast path: skip strcmp when first chars differ (mangled names start
|
||||
# with sha1 hex, bare names with letters/underscore)
|
||||
if cur_name[0] == name[0] and string.strcmp(cur_name, name) == 0: return cur
|
||||
# 名字长度取 Function.NameLen(创建时一次算好),免去对热链每个节点重复 strlen
|
||||
# use Function.NameLen (computed once) instead of per-node strlen
|
||||
cur_len: sizet = mlirlite.function_get_name_len(cur)
|
||||
# 本模块同名候选:仅接受恰为 "{owner_sha1}.{name}" 的形态(不匹配 "{sha1}.Class.method");declare 与 define 同名同符号,是否 IsDeclared==0 均可返回
|
||||
# own-module candidate: exactly "{owner_sha1}.{name}" (not "{sha1}.Class.method")
|
||||
if owner_sha1 is not None and mangle_hit is None and cur_len == 17 + name_len:
|
||||
if string.strncmp(cur_name, owner_sha1, 16) == 0 and cur_name[16] == '.':
|
||||
suffix_start_o: sizet = cur_len - name_len
|
||||
@@ -547,7 +471,7 @@ novirtual class CallGenBase:
|
||||
match_o = 0
|
||||
break
|
||||
if match_o == 1: mangle_hit = cur
|
||||
# 后缀匹配(.name):不跳过 declare(方法 stub 也是 declare,由调用处判断 IsDeclared 决定用裸名或 sha1 前缀名)
|
||||
# suffix match (.name): do not skip declares (method stubs are declares too)
|
||||
if cur_len > name_len + 1:
|
||||
suffix_start: sizet = cur_len - name_len
|
||||
if cur_name[suffix_start - 1] == '.':
|
||||
@@ -557,9 +481,9 @@ novirtual class CallGenBase:
|
||||
Matched = 0
|
||||
break
|
||||
if Matched == 1:
|
||||
# 首个任意后缀命中(原第三遍兜底)
|
||||
# first any-suffix hit (former pass-3 fallback)
|
||||
if any_hit is None: any_hit = cur
|
||||
# 首个 IsDeclared==0 命中(原第二遍优先)
|
||||
# first IsDeclared==0 hit (former pass-2 priority)
|
||||
if cur.IsDeclared == 0 and declare_hit is None: declare_hit = cur
|
||||
cur = mlirlite.function_get_next(cur)
|
||||
if mangle_hit is not None: return mangle_hit
|
||||
@@ -567,57 +491,50 @@ novirtual class CallGenBase:
|
||||
return any_hit
|
||||
|
||||
|
||||
# _ensure_method_declare:found_func 为 None(stub 未注入或跨模块调用)时在当前模块创建 declare,避免 llc 报 undefined value
|
||||
# _ensure_method_declare: create a declare in the current module when the stub is missing
|
||||
def _ensure_method_declare(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, call_name: str, ret_ty: mlirlite.LLVMType*, param_head: mlirlite.Value*, param_count: int):
|
||||
"""为跨模块方法调用创建 declare 声明
|
||||
|
||||
若模块中已存在同名 declare 且参数数量较少(例如 File.__init__ 的
|
||||
stub 仅 3 个参数,而实际调用传入 4 个),则向现有 declare 追加缺失
|
||||
参数,避免 llc 报 `argument invalid for parameter type` 之类的错误。
|
||||
已存在的 define 不修改(其参数由定义决定)。
|
||||
"""
|
||||
"""Create a declare for a cross-module method call, appending missing params to an existing shorter declare (defines are left untouched)."""
|
||||
if pool is None or mod is None or call_name is None: return
|
||||
# 链遍历哨兵(fail-fast):FuncHead/Params 的 Next 被越界写破坏成环时
|
||||
# 无上限遍历会死循环(builtins.vp dump 卡死根因排查),超限即报错终止。
|
||||
# list-walk sentinel (fail-fast): an unbounded walk would loop forever if a
|
||||
# Next pointer were corrupted into a cycle, so abort past the limit.
|
||||
DECLARE_CHAIN_LIMIT: int = 100000
|
||||
# 检查模块中是否已有同名函数(declare 或 define)
|
||||
# check whether a same-named function (declare or define) already exists
|
||||
existing: mlirlite.Function* = mod.FuncHead
|
||||
existing_steps: int = 0
|
||||
while existing is not None:
|
||||
existing_steps += 1
|
||||
if existing_steps > DECLARE_CHAIN_LIMIT:
|
||||
stdio.printf("[CALLGEN] 链表异常: module funcs(call_name=%s,遍历超限,终止)\n", call_name)
|
||||
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印)
|
||||
# converge: longjmp back to the REPL loop (diagnostic already printed)
|
||||
VLogger.repl_abort()
|
||||
if existing.Name is not None:
|
||||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
|
||||
# fast path: skip strcmp when first chars differ
|
||||
if existing.Name[0] == call_name[0] and string.strcmp(existing.Name, call_name) == 0:
|
||||
# 已存在:仅当是 declare 且参数数量不足时追加缺失参数
|
||||
# exists: append missing params only if the declare has too few
|
||||
if existing.IsDeclared == 1:
|
||||
# 变长函数(*args,IsVarArg=1)不追加参数:多余实参由
|
||||
# 变参机制(...)承接,追加有类型参数会导致调用侧把变参
|
||||
# 强转为追加的类型(如 i8 → i64),%d/%x 等格式读错。
|
||||
# variadic functions (*args) get no params appended: extra
|
||||
# args are handled by "..." and typed params would corrupt them.
|
||||
if existing.IsVarArg == 1: return
|
||||
# 遍历现有参数链表计数(避免依赖 GSList.Count 字段的类型推断)
|
||||
# count existing params by walking (avoid GSList.Count type inference)
|
||||
cur_count: int = 0
|
||||
cur_p: mlirlite.Param* = mlirlite.function_get_param_head(existing)
|
||||
while cur_p is not None:
|
||||
cur_count += 1
|
||||
if cur_count > DECLARE_CHAIN_LIMIT:
|
||||
stdio.printf("[CALLGEN] 链表异常: func params(call_name=%s,参数链遍历超限,终止)\n", call_name)
|
||||
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印)
|
||||
# converge: longjmp back to the REPL loop (diagnostic already printed)
|
||||
VLogger.repl_abort()
|
||||
cur_p = cur_p.Next
|
||||
# 只追加缺失的参数(cur_count < param_count 时)
|
||||
# append only the missing params
|
||||
if cur_count < param_count:
|
||||
# 定位到 param_head 的第 cur_count 个节点开始追加
|
||||
# start at param_head's cur_count-th node
|
||||
pnode2: mlirlite.Value* = param_head
|
||||
pi2: int = 0
|
||||
# 跳过已有的 cur_count 个参数
|
||||
# skip the existing cur_count params
|
||||
while pnode2 is not None and pi2 < cur_count:
|
||||
pnode2 = pnode2.Next
|
||||
pi2 += 1
|
||||
# 追加剩余参数
|
||||
# append the remaining params
|
||||
while pnode2 is not None and pi2 < param_count:
|
||||
if pnode2.Ty is not None:
|
||||
param2: mlirlite.Param* = mlirlite.new_param(pool, pnode2.Ty, None)
|
||||
@@ -626,25 +543,21 @@ novirtual class CallGenBase:
|
||||
pi2 += 1
|
||||
return
|
||||
existing = existing.Next
|
||||
# 裸名 C 库函数(如 string.memset → @memset):call_name 无 sha1 前缀,无法提取定义侧模块查 vpi 参数注解,故按标准签名建 declare(与 CallGenLib._ensure_c_lib_declare 白名单一致)
|
||||
# bare C library fn (e.g. string.memset -> @memset): declare by standard signature
|
||||
if call_name is not None and string.strchr(call_name, 46) is None:
|
||||
std_lib_fn: mlirlite.Function* = self.Gen.CallGenLib._ensure_c_lib_declare(pool, mod, call_name)
|
||||
if std_lib_fn is not None: return
|
||||
# 创建函数声明
|
||||
# create the function declaration
|
||||
func: mlirlite.Function* = mlirlite.new_function(pool, call_name, ret_ty)
|
||||
if func is None: return
|
||||
func.IsDeclared = 1
|
||||
# 定义侧参数类型优先:从 call_name 提取 {sha1}.{cls}.{method} 查 vpi 参数注解取定义侧类型。
|
||||
# · 整数:仅当定义宽度更宽时采用定义宽度(反方向由 CallGenLib._coerce_args_to_def_params 在调用点先窄化实参)
|
||||
# · 指针:**一律采用定义侧类型**——declare 被同模块所有调用点共用,若沿用"首个调用点的实参类型",
|
||||
# 后续调用传入别的具体指针类型就会被这条被冻住的形参类型 coerce,报"非基本类型指针间隐式转换"
|
||||
# (现场 VarGenMixin.repl_ensure_entry 的 node: ast.AST*:先被 translate_annassign 以 AnnAssign*
|
||||
# 建 declare,后 translate_assign 传 AST* 即报"源类型 …AST* → 目标类型 …AnnAssign*")
|
||||
# Prefer definition-side param types (from {sha1}.{cls}.{method} vpi
|
||||
# annotations): wider ints win, pointers always use the definition side.
|
||||
sha1_d: str = None
|
||||
cls_d: str = None
|
||||
meth_d: str = None
|
||||
if call_name is not None:
|
||||
# SHA1 前缀判定走唯一真源 GenApi.sha1_prefix_len,前缀长度取其返回值(不硬编码 17/16)
|
||||
# SHA1 prefix via single source GenApi.sha1_prefix_len (no hardcoded 17/16)
|
||||
sha1_pfx_d: int = GenApi.sha1_prefix_len(call_name)
|
||||
if sha1_pfx_d != 0:
|
||||
pfx_len_d: sizet = sizet(sha1_pfx_d)
|
||||
@@ -654,7 +567,7 @@ novirtual class CallGenBase:
|
||||
sha1_buf_d[pfx_len_d - 1] = '\0'
|
||||
sha1_d = sha1_buf_d
|
||||
rest_d: str = call_name + pfx_len_d
|
||||
# 取末个 '.':method 不含 '.',首个 '.' 会把含模块限定名的类型实参截断
|
||||
# take the last '.': method has no dot; the first would truncate module-qualified args
|
||||
dot2_d: str = string.strrchr(rest_d, '.')
|
||||
if dot2_d is not None:
|
||||
cls_len_d: sizet = uint64(dot2_d) - uint64(rest_d)
|
||||
@@ -670,7 +583,7 @@ novirtual class CallGenBase:
|
||||
if sha1_d is not None and meth_d is not None: prm_annots_d = self._vpi_method_param_annot(pool, sha1_d, cls_d, meth_d)
|
||||
i32_ty_d: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ptr_d: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# 添加参数(declare 中参数名无意义,设为 None 只输出类型)
|
||||
# add params (names are meaningless in a declare; None emits types only)
|
||||
pnode: mlirlite.Value* = param_head
|
||||
pi: int = 0
|
||||
while pnode is not None and pi < param_count:
|
||||
@@ -686,7 +599,7 @@ novirtual class CallGenBase:
|
||||
case _:
|
||||
pass
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
# 指针形参:定义侧为准(见上方注释)。定义侧解析不出指针时保留实参类型
|
||||
# pointer params: prefer definition side; keep arg type on miss
|
||||
match seg_ty_d:
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
param_ty_d = seg_ty_d
|
||||
@@ -703,43 +616,43 @@ novirtual class CallGenBase:
|
||||
if cls_d is not None: stdlib.free(cls_d)
|
||||
mlirlite.module_add_function(mod, func)
|
||||
|
||||
# _lookup_method_ret_ty_via_vpi:按 模块SHA1 + 类名 + 方法名 查 vpi 返回注解缓存并解析为 LLVMType,未命中返回 None(不硬编码白名单,类型始终来自定义处)
|
||||
# _lookup_method_ret_ty_via_vpi: resolve method return type from the vpi annotation cache
|
||||
|
||||
# ============================================================
|
||||
def _lookup_method_ret_ty_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> mlirlite.LLVMType*:
|
||||
"""从 vpi stub 返回注解缓存获取方法返回类型,未命中返回 None"""
|
||||
"""Get a method return type from the vpi stub annotation cache; None on miss"""
|
||||
if pool is None or cls_sha1 is None or method_name is None: return None
|
||||
# 函数内懒 import:VpiReader imports HandlesExprCall,顶层 import 会成环
|
||||
# lazy import inside the function: VpiReader imports HandlesExprCall, a top-level import would cycle
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi
|
||||
# 覆盖"模块自身翻译时 stub 尚未注入"(同模块方法调用,定义在后)的场景
|
||||
# on-demand load: read {temp_dir}/{sha1[0:2]}/{sha1}.vpi on cache miss,
|
||||
# covering method calls whose stub is not yet injected.
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
|
||||
annot: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, cls_name, method_name)
|
||||
if annot is None: return None
|
||||
return VpiReader._resolve_vpi_ret_annot_str(pool, annot, i32_ty, i8_ptr_ty)
|
||||
|
||||
# _vpi_method_param_annot:按需加载并查询 vpi 参数注解串('@' 分隔,含首参 self 空串占位),供调用方补全 declare/call 缺失参数;未命中返回 None
|
||||
# _vpi_method_param_annot: on-demand lookup of the vpi param annotation string
|
||||
def _vpi_method_param_annot(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> str:
|
||||
"""按需加载并查询 vpi 参数注解串,未命中返回 None"""
|
||||
"""Load and query the vpi parameter annotation string on demand; None on miss"""
|
||||
if pool is None or cls_sha1 is None or cls_name is None or method_name is None: return None
|
||||
# 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi
|
||||
# on-demand load: read {temp_dir}/{sha1[0:2]}/{sha1}.vpi on cache miss
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
|
||||
return VpiReader._lookup_vpi_param_annots(cls_sha1, cls_name, method_name)
|
||||
|
||||
# _vpi_resolve_param_annot_ty:解析 vpi 参数注解段为 LLVMType,失败返回 None(调用方以 i8* 兜底)
|
||||
# _vpi_resolve_param_annot_ty: parse a vpi param annotation segment into LLVMType
|
||||
def _vpi_resolve_param_annot_ty(self, pool: memhub.MemBuddy*, seg: str) -> mlirlite.LLVMType*:
|
||||
"""解析单个 vpi 参数注解段为 LLVMType,失败返回 None"""
|
||||
"""Parse a single vpi parameter annotation segment into an LLVMType; None on failure"""
|
||||
if pool is None or seg is None: return None
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
return VpiReader._resolve_vpi_ret_annot_str(pool, seg, i32_ty, i8_ptr_ty)
|
||||
|
||||
# _special_method_ret_ty:编译器内置特殊方法的固定返回类型——__before_init__/__init__ → void,__new__ → Ptr(StructTy),非特殊返回 None(vpi 无条目,固定即可避免 fail-fast 误报)
|
||||
# _special_method_ret_ty: fixed return types for compiler built-in special methods
|
||||
|
||||
# ============================================================
|
||||
def _special_method_ret_ty(self, pool: memhub.MemBuddy*, class_name: str, method_name: str) -> mlirlite.LLVMType*:
|
||||
"""编译器内置特殊方法的固定返回类型;非特殊返回 None"""
|
||||
"""Fixed return type for compiler builtin special methods; None for non-special ones"""
|
||||
if pool is None or method_name is None: return None
|
||||
if string.strcmp(method_name, "__before_init__") == 0 or string.strcmp(method_name, "__init__") == 0: return mlirlite.Void(pool)
|
||||
if string.strcmp(method_name, "__new__") == 0:
|
||||
@@ -749,44 +662,40 @@ novirtual class CallGenBase:
|
||||
return None
|
||||
return None
|
||||
|
||||
# _fail_method_ret_ty:方法返回类型无法精确获取时 ERROR + 错误堆栈终止(node 为调用点 AST,用于定位),绝不静默默认 i32
|
||||
# _fail_method_ret_ty: ERROR + abort when the method return type cannot be resolved
|
||||
|
||||
# ============================================================
|
||||
def _fail_method_ret_ty(self, method_name: str, node: ast.AST* = None) -> mlirlite.LLVMType*:
|
||||
"""方法返回类型无法精确获取时 fail-fast(ERROR + 错误堆栈)
|
||||
|
||||
返回类型来源(按序):当前模块函数 → text.ll define → vpi stub 返回注解缓存。
|
||||
全部未命中说明方法定义缺失或 stub 损坏,立即终止暴露,绝不静默默认 i32。
|
||||
"""
|
||||
"""Fail fast when a method return type cannot be resolved precisely (module function, text.ll define, then vpi cache); never default to i32."""
|
||||
VLogger.error(f"方法返回类型无法精确获取(函数/text.ll/vpi stub 均未命中): {method_name}", "HandlesExprCall")
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"方法返回类型无法精确获取,默认 i32 会截断指针/i64: {method_name}", "TypeError")
|
||||
return None
|
||||
|
||||
# SetTempDir:设置 temp 目录路径(Phase A 翻译前调用,供读取依赖模块 text.ll)
|
||||
# SetTempDir: set the temp dir path (called before Phase A to read dependency text.ll)
|
||||
|
||||
# ============================================================
|
||||
def SetTempDir(self, temp_dir: str) -> None:
|
||||
"""设置 temp 目录路径(统一写入 Gen.CallGenBase 真源,跨子对象共享)"""
|
||||
"""Set the temp directory path (single source of truth in Gen.CallGenBase)"""
|
||||
if self.Gen is None: return
|
||||
self.Gen.CallGenBase.TempDir = temp_dir
|
||||
if temp_dir is not None: self.Gen.CallGenBase.TempDirLen = string.strlen(temp_dir)
|
||||
else: self.Gen.CallGenBase.TempDirLen = 0
|
||||
|
||||
# _ParseLLVMRetTyStr:把 LLVM IR 返回类型字符串(define 与 @ 之间)转为 LLVMType——void / iN / double / float,含 * 者返回通用 Ptr(Int8);无法解析返回 None
|
||||
# _ParseLLVMRetTyStr: convert an LLVM IR return type string to LLVMType
|
||||
|
||||
# ============================================================
|
||||
def _ParseLLVMRetTyStr(self, pool: memhub.MemBuddy*, buf: bytes, start: sizet, end: sizet) -> mlirlite.LLVMType*:
|
||||
"""将 LLVM IR 返回类型字符串转换为 mlirlite.LLVMType"""
|
||||
"""Convert an LLVM IR return type string into a mlirlite.LLVMType"""
|
||||
if pool is None or buf is None or start >= end: return None
|
||||
|
||||
# 去除前导空白
|
||||
# trim leading whitespace
|
||||
s: sizet = start
|
||||
while s < end:
|
||||
c: int = buf[s]
|
||||
if c != ' ' and c != '\t': break
|
||||
s += 1
|
||||
|
||||
# 去除尾部空白
|
||||
# trim trailing whitespace
|
||||
e: sizet = end
|
||||
while e > s:
|
||||
c2: int = buf[e - 1]
|
||||
@@ -796,12 +705,10 @@ novirtual class CallGenBase:
|
||||
ty_len: sizet = e - s
|
||||
if ty_len == 0: return None
|
||||
|
||||
# struct 字面量 {T1, T2, ...}(tuple 打包返回类型)必须在 * 检查之前,否则内含 * 会被误判为 i8*(破坏 tuple 解包)
|
||||
# struct literal {T1, T2, ...} must be checked before "*", else inner * misfires
|
||||
if s < e and buf[s] == '{':
|
||||
# 字段链表节点必须是 mlirlite.ParamNode(new_param_node 返回 rawptr[ParamNode],
|
||||
# paramnode_set_next / Struct 的形参也都是 rawptr[ParamNode])。原先误注解为
|
||||
# Function*(与 ParamNode 是不相关类),严格指针规则下这两处的实参传递会被判
|
||||
# 「struct↔struct 隐式转换已禁止」;改注解即根治,不需要也不应该加强转。
|
||||
# Field list nodes must be mlirlite.ParamNode; the old Function*
|
||||
# annotation triggered "struct<->struct implicit conversion forbidden".
|
||||
th_st: mlirlite.ParamNode* = None
|
||||
tt_st: mlirlite.ParamNode* = None
|
||||
tc_st: int = 0
|
||||
@@ -823,22 +730,13 @@ novirtual class CallGenBase:
|
||||
tc_st += 1
|
||||
if th_st is None or tc_st == 0: return None
|
||||
st_ty_st: mlirlite.LLVMType* = mlirlite.Struct(pool, th_st, tc_st, None)
|
||||
# 指针到 struct({i32, i8}*):返回 Ptr(Struct);纯 tuple 打包
|
||||
# ({i8*, i16})无尾部 *,返回 Struct 本身。
|
||||
# {i32, i8}* -> Ptr(Struct); a plain tuple {i8*, i16} has no trailing
|
||||
# * and returns the Struct itself.
|
||||
if ps_st < e and buf[ps_st] == '*': return mlirlite.Ptr(pool, st_ty_st)
|
||||
return st_ty_st
|
||||
|
||||
# 命名类型(%"name" / %name)带指针:先解析到真实结构体/枚举类型再包 Ptr。
|
||||
# 为什么不许坍缩成通用 i8*:返回类型是「指向类对象」时,坍缩后调用方变量
|
||||
# 的 alloca 变成 i8*,下标语义随之从「类的 __getitem__」掉到「字节下标」。
|
||||
# 现场(Test/FakeDuckTest/App/main.vp:115 `p0: str = parts[0]`):
|
||||
# includes/_fakeduck.vp `def split(self, delimiter: str) -> list[str]*`,
|
||||
# 定义侧 text.ll 为
|
||||
# define %"<list_sha1>.list[str]"* @"<sha1>._str.split"(...)
|
||||
# 本函数若把 `%"<list_sha1>.list[str]"*` 当通用指针,parts 的 alloca 即
|
||||
# 退化为 i8* ⇒ parts[0] 取到整数 ⇒ 赋给 str 报
|
||||
# 「整数隐式转指针已禁止」(报文指不到真因)。
|
||||
# 解析不到登记项时保持原「通用指针」行为(名字可能来自尚未登记的模块)。
|
||||
# Named type (%"name") with pointers: resolve to the real struct/enum type
|
||||
# before wrapping Ptr; unknown names keep the generic i8* behavior.
|
||||
if s < e and buf[s] == '%':
|
||||
nm_s: sizet = s + 1
|
||||
nm_e: sizet = e
|
||||
@@ -868,8 +766,7 @@ novirtual class CallGenBase:
|
||||
nm_i += 1
|
||||
return nm_wrap
|
||||
|
||||
# 检查是否含 * (指针类型) → 返回 i8* (通用指针)
|
||||
# 包括 i8*, i32*, { i32, i8 }*,以及上面未登记命名的 %name*
|
||||
# contains "*" -> generic i8* (i8*, i32*, { i32, i8 }*, unregistered %name*)
|
||||
ci: sizet = s
|
||||
while ci < e:
|
||||
if buf[ci] == '*': return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
@@ -902,24 +799,12 @@ novirtual class CallGenBase:
|
||||
if bits == 32: return mlirlite.Int32(pool)
|
||||
if bits == 64: return mlirlite.Int64(pool)
|
||||
|
||||
# 无法解析
|
||||
# unparsable
|
||||
return None
|
||||
|
||||
# ============================================================
|
||||
def _ParseMlirRetTyStr(self, pool: memhub.MemBuddy*, buf: bytes, start: sizet, end: sizet) -> mlirlite.LLVMType*:
|
||||
"""把 mlirlite 的 MLIR 返回类型文本转成 mlirlite.LLVMType
|
||||
|
||||
覆盖 `llvm.func` 签名里会出现的形态(includes/mlirlite/README.md §2.1):
|
||||
none → Void
|
||||
iN → Int1/8/16/32/64
|
||||
f16/f32/f64/f128 → Half/Float/Double/FP128
|
||||
!llvm.ptr → Ptr(Int8)(MLIR 指针**不透明**,pointee 已丢)
|
||||
!llvm.array<N x T> → Array(T', N)
|
||||
!llvm.struct<(T1, …)> → 字面结构体
|
||||
!llvm.struct<"名", (T1, …)> → 已登记具名结构体(未登记返回 None,不臆造)
|
||||
|
||||
无法解析返回 None(与 _ParseLLVMRetTyStr 同约定:宁缺毋错,不静默降级)。
|
||||
"""
|
||||
"""Convert mlirlite's MLIR return-type text into a mlirlite.LLVMType; None if unparseable, never a silent degradation."""
|
||||
if pool is None or buf is None or start >= end: return None
|
||||
s: sizet = start
|
||||
while s < end and (buf[s] == ' ' or buf[s] == '\t'): s += 1
|
||||
@@ -927,9 +812,9 @@ novirtual class CallGenBase:
|
||||
while e > s and (buf[e - 1] == ' ' or buf[e - 1] == '\t' or buf[e - 1] == '{'): e -= 1
|
||||
if s >= e: return None
|
||||
n: sizet = e - s
|
||||
# none → Void(MLIR 的 void 记法)
|
||||
# none -> Void (MLIR void notation)
|
||||
if n == 4 and string.strncmp(buf + s, "none", 4) == 0: return mlirlite.Void(pool)
|
||||
# 标量:iN / fN
|
||||
# scalar: iN / fN
|
||||
if buf[s] == 'i' or buf[s] == 'f':
|
||||
bits: int = 0
|
||||
di: sizet = s + 1
|
||||
@@ -954,7 +839,7 @@ novirtual class CallGenBase:
|
||||
if bits == 64: return mlirlite.Double(pool)
|
||||
if bits == 128: return mlirlite.FP128(pool)
|
||||
return None
|
||||
# 不透明指针 → 通用 i8*
|
||||
# opaque pointer -> generic i8*
|
||||
if n >= 9 and string.strncmp(buf + s, "!llvm.ptr", 9) == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# !llvm.array<N x T>
|
||||
if n >= 12 and string.strncmp(buf + s, "!llvm.array<", 12) == 0:
|
||||
@@ -988,7 +873,7 @@ novirtual class CallGenBase:
|
||||
et_a: mlirlite.LLVMType* = self._ParseMlirRetTyStr(pool, buf, xs + 1, iea)
|
||||
if et_a is None: return None
|
||||
return mlirlite.Array(pool, et_a, cnt_a)
|
||||
# !llvm.struct<...>(匿名体 / 具名内联体)
|
||||
# !llvm.struct<...> (anonymous body / named inline body)
|
||||
if n >= 13 and string.strncmp(buf + s, "!llvm.struct<", 13) == 0:
|
||||
is_s: sizet = s + 13
|
||||
depth_s: int = 0
|
||||
@@ -1023,7 +908,7 @@ novirtual class CallGenBase:
|
||||
if a >= b or buf[a] != '(' or buf[b - 1] != ')':
|
||||
if nm_buf is not None: stdlib.free(nm_buf)
|
||||
return None
|
||||
# 字段表:按顶层逗号切分(括号/尖括号计入深度)
|
||||
# field list: split by top-level commas (parens/angle brackets count depth)
|
||||
head: mlirlite.ParamNode* = None
|
||||
tail: mlirlite.ParamNode* = None
|
||||
cnt_f: int = 0
|
||||
@@ -1062,7 +947,7 @@ novirtual class CallGenBase:
|
||||
elif c3 == ')' or c3 == '>':
|
||||
if depth_f > 0: depth_f -= 1
|
||||
cur_f += 1
|
||||
# 具名:按登记表回查真实类型;未登记返回 None(不臆造类型对象)
|
||||
# named: look up the real type in the registry; None if unregistered
|
||||
if nm_buf is not None:
|
||||
reg: mlirlite.LLVMType* = GenApi.get_struct_type(nm_buf)
|
||||
stdlib.free(nm_buf)
|
||||
@@ -1070,9 +955,9 @@ novirtual class CallGenBase:
|
||||
return mlirlite.Struct(pool, head, cnt_f, None)
|
||||
return None
|
||||
|
||||
# _LookupCachedRetTyText:查「依赖模块 text.ll 方法返回类型文本」持久缓存——键为 define 完整 mangled 名("{sha1}.{class}.{method}",bare 模式 "{sha1}.{method}"),命中返回文本,未命中/表未建返回 None
|
||||
# _LookupCachedRetTyText: lookup the persistent "dependency text.ll return type" cache
|
||||
def _LookupCachedRetTyText(self, class_sha1: str, class_name: str, method_name: str, use_bare: int) -> str:
|
||||
"""查返回类型文本缓存:命中返回文本,未命中返回 None"""
|
||||
"""Look up the return-type text cache: return the text on a hit, None on a miss"""
|
||||
if self.Gen is None or class_sha1 is None or method_name is None: return None
|
||||
ht: hashtable.HashTable* = self.Gen.CallGenBase.MethodRetTyMap
|
||||
if ht is None: return None
|
||||
@@ -1080,7 +965,7 @@ novirtual class CallGenBase:
|
||||
cls_len: sizet = 0
|
||||
if class_name is not None: cls_len = string.strlen(class_name)
|
||||
meth_len: sizet = string.strlen(method_name)
|
||||
# 长度公式与 _LookupMethodRetTyFromTextLL 构造 search_buf 时完全一致
|
||||
# key length formula matches search_buf construction in _LookupMethodRetTyFromTextLL
|
||||
key_len: sizet = sha1_len + meth_len + 2
|
||||
if use_bare == 0: key_len = sha1_len + cls_len + meth_len + 4
|
||||
key_buf: bytes = stdlib.malloc(key_len)
|
||||
@@ -1093,12 +978,12 @@ novirtual class CallGenBase:
|
||||
if v is None: return None
|
||||
return rawptr(v)
|
||||
|
||||
# _CacheModuleRetTyTexts:单趟扫描某模块 text.ll——把全部 define 行的返回类型写入持久缓存(键/值深拷贝进 PermPool,同名只记首次,只登记 "{sha1}." 开头的本模块 mangled 名),并在同一趟里返回首个匹配 search_buf 的 define 的返回类型(未匹配返回 None);仅在成功读到 text.ll 后调用,负结果绝不缓存
|
||||
# _CacheModuleRetTyTexts: one pass to register all define lines and find the first match
|
||||
def _CacheModuleRetTyTexts(self, class_sha1: str, text_buf: bytes, text_len: sizet, pool: memhub.MemBuddy*, search_buf: str, search_str_len: sizet) -> mlirlite.LLVMType*:
|
||||
"""单趟「登记 + 查找」text.ll 全部 define 行(建表/分配失败时只查找不登记)"""
|
||||
"""Single-pass register and lookup over all text.ll define lines (lookup only if table allocation fails)"""
|
||||
if text_buf is None or text_len == 0: return None
|
||||
# 合并原「先全量登记一遍、再二次扫描查找」的两趟全文件遍历(PROF:两次扫描合计约 14%)。
|
||||
# 建表失败(PermPool 未就绪等)不影响查找:cache_on=0 时只走同趟查找。
|
||||
# Merge the former two passes (register all, then scan again) into one
|
||||
# file traversal; caching failure does not affect the in-pass lookup.
|
||||
cache_on: int = 0
|
||||
ht: hashtable.HashTable* = None
|
||||
perm: memhub.MemBuddy* = None
|
||||
@@ -1122,12 +1007,8 @@ novirtual class CallGenBase:
|
||||
if text_buf[le] == '\n': break
|
||||
le += 1
|
||||
ll: sizet = le - ls
|
||||
# 函数定义行两种形态都要认:
|
||||
# LLVM 文本:define <ret> @"名"(...) (不缩进)
|
||||
# MLIR 文本: llvm.func @"名"(...) -> <ret> { (module{} 内带缩进;void 省略 ->)
|
||||
# 本函数是 text.ll 的**回退**读取方(首选 _lookup_method_ret_ty_via_vpi);
|
||||
# 单端 MLIR 后 text.ll 里已无 define 行,只认 LLVM 形态会让回退恒 None
|
||||
# ⇒ 指针返回值退回默认 i32 被 inttoptr 截断。
|
||||
# Accept both function-def line forms: LLVM "define <ret> @name(...)"
|
||||
# and MLIR "llvm.func @name(...) -> <ret> {" (void omits "->").
|
||||
fs: sizet = ls
|
||||
while fs < ls + ll and (text_buf[fs] == ' ' or text_buf[fs] == '\t'): fs += 1
|
||||
is_mlir_line: int = 0
|
||||
@@ -1147,8 +1028,8 @@ novirtual class CallGenBase:
|
||||
elif ll >= 11 and fs + 11 <= ls + ll and string.strncmp(text_buf + fs, "llvm.func @", 11) == 0:
|
||||
matched = 1
|
||||
is_mlir_line = 1
|
||||
at_pos = fs + 10 # "llvm.func " 之后即 '@'
|
||||
# 返回类型:` -> <ret>`(到行尾或 " {" 之前);无 `->` = void
|
||||
at_pos = fs + 10 # right after "llvm.func " is '@'
|
||||
# return type: ` -> <ret>` (up to line end or " {"); no `->` = void
|
||||
ar_pos: sizet = at_pos
|
||||
arrow: sizet = 0
|
||||
while ar_pos + 2 < ls + ll:
|
||||
@@ -1176,7 +1057,7 @@ novirtual class CallGenBase:
|
||||
if name_start < ls + ll and text_buf[name_start] == '"':
|
||||
name_start += 1
|
||||
quoted = 1
|
||||
# 定界:带引号名到 '"',裸名到 '('(与查找侧边界判定等价)
|
||||
# delimit: quoted name to '"', bare name to '(' (matches lookup side)
|
||||
name_end: sizet = name_start
|
||||
if quoted != 0:
|
||||
while name_end < ls + ll:
|
||||
@@ -1187,18 +1068,17 @@ novirtual class CallGenBase:
|
||||
if text_buf[name_end] == '(': break
|
||||
name_end += 1
|
||||
name_len: sizet = name_end - name_start
|
||||
# 只登记本模块 mangled 名 "{sha1}.{后缀}"(查询键恒为此前缀);表不可用时跳过登记
|
||||
# register only own-module mangled names "{sha1}.{suffix}"; skip when table unavailable
|
||||
if cache_on != 0 and name_len > sha1_len + 1:
|
||||
if string.strncmp(text_buf + name_start, class_sha1, sha1_len) == 0 and text_buf[name_start + sha1_len] == '.':
|
||||
key_buf: bytes = stdlib.malloc(name_len + 1)
|
||||
if key_buf is not None:
|
||||
string.strncpy(key_buf, text_buf + name_start, name_len)
|
||||
key_buf[name_len] = '\0'
|
||||
# 同名首次为准(不被后续同名覆盖,与首个命中一致)
|
||||
# first name wins (not overwritten by later same names)
|
||||
if ht.__contains__(key_buf) == 0:
|
||||
# 缓存值:LLVM 形态存返回类型原文;MLIR 形态存
|
||||
# '#' + MLIR 类型文本('#' 前缀标记形态,供 _ParseMlirRetTyStr
|
||||
# 分派;LLVM 类型文本不可能以 '#' 开头,无歧义)
|
||||
# cached value: LLVM form stores the raw return type; MLIR
|
||||
# form stores '#' + type text, dispatched by _ParseMlirRetTyStr.
|
||||
rt_len: sizet = 0
|
||||
if is_mlir_line != 0:
|
||||
if mlir_none != 0:
|
||||
@@ -1218,8 +1098,8 @@ novirtual class CallGenBase:
|
||||
tcopy[rt_len] = '\0'
|
||||
ht.set_str(kcopy, tcopy)
|
||||
stdlib.free(key_buf)
|
||||
# 同趟查找:首个与 search_buf 匹配、且后随 '"' / '(' / 行尾的定义行
|
||||
# (判定与原二次扫描逐字一致),先命中者为准
|
||||
# in-pass lookup: first define line matching search_buf and followed by
|
||||
# '"' / '(' / line end wins.
|
||||
if result_ty is None and name_start + search_str_len <= ls + ll:
|
||||
if string.strncmp(text_buf + name_start, search_buf, search_str_len) == 0:
|
||||
end_ch: int = 0
|
||||
@@ -1232,30 +1112,26 @@ novirtual class CallGenBase:
|
||||
pos = le + 1
|
||||
return result_ty
|
||||
|
||||
# _LookupMethodRetTyFromTextLL:读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),按 "{sha1}.{class_name}.{method_name}" 搜索 define 行并解析返回类型;未找到/文件不存在返回 None
|
||||
# _LookupMethodRetTyFromTextLL: read {temp_dir}/{sha1[0:2]}/{sha1}.text.ll and find the define line
|
||||
|
||||
# ============================================================
|
||||
def _LookupMethodRetTyFromTextLL(self, pool: memhub.MemBuddy*, class_sha1: str, class_name: str, method_name: str, use_bare: int = 0) -> mlirlite.LLVMType*:
|
||||
"""从依赖模块的 text.ll 中查找方法返回类型
|
||||
|
||||
use_bare: 1=按模块级函数查找(搜索 "{sha1}.{method_name}" define,
|
||||
用于 new_lexer/tokenize 等模块函数;class_name 忽略)。
|
||||
"""
|
||||
"""Look up a method return type in a dependency module's text.ll; use_bare=1 searches a module-level "{sha1}.{method_name}" define."""
|
||||
if pool is None or class_sha1 is None or method_name is None: return None
|
||||
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return None
|
||||
|
||||
sha1_len: sizet = string.strlen(class_sha1)
|
||||
if sha1_len < 2: return None
|
||||
|
||||
# 缓存命中:O(1) 取回返回类型文本并用调用方 pool 重建类型直接返回;未命中走下方原实现
|
||||
# cache hit: O(1) text retrieval, rebuilt with the caller pool; miss falls through
|
||||
cached_text: str = self._LookupCachedRetTyText(class_sha1, class_name, method_name, use_bare)
|
||||
if cached_text is not None:
|
||||
# '#' 前缀 = MLIR 类型文本(_CacheModuleRetTyTexts 写入时打的形态标记)
|
||||
# '#' prefix = MLIR type text (form marker written by _CacheModuleRetTyTexts)
|
||||
if cached_text[0] == '#': return self._ParseMlirRetTyStr(pool, cached_text, 1, string.strlen(cached_text))
|
||||
return self._ParseLLVMRetTyStr(pool, cached_text, 0, string.strlen(cached_text))
|
||||
|
||||
# 构造 text.ll 路径 (level=1): {temp_dir}/{sha1[0:2]}/{sha1}.text.ll
|
||||
# 使用 subdir 缓冲区避免 %c 格式化问题(与 StubMerger._sliced_path 一致)
|
||||
# build text.ll path: {temp_dir}/{sha1[0:2]}/{sha1}.text.ll; use a subdir
|
||||
# buffer to avoid %c formatting issues (as in StubMerger._sliced_path)
|
||||
path_len: sizet = self.Gen.CallGenBase.TempDirLen + sha1_len + 14
|
||||
path_buf: bytes = stdlib.malloc(path_len)
|
||||
if path_buf is None: return None
|
||||
@@ -1269,7 +1145,7 @@ novirtual class CallGenBase:
|
||||
viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll", self.Gen.CallGenBase.TempDir, subdir_buf, class_sha1)
|
||||
stdlib.free(subdir_buf)
|
||||
|
||||
# 按文件实际大小读取 text.ll(固定缓冲区会截断大模块导致末尾定义查不到);64MB 上限防御异常超大文件
|
||||
# read text.ll by actual size (a fixed buffer would truncate large modules); 64MB cap
|
||||
tf: rawptr[fileio.File] = fileio.File(path_buf, fileio.MODE.R)
|
||||
if tf.closed:
|
||||
stdlib.free(path_buf)
|
||||
@@ -1294,7 +1170,7 @@ novirtual class CallGenBase:
|
||||
return None
|
||||
read_buf[text_len] = '\0'
|
||||
|
||||
# 构造搜索模式: "{sha1}.{class_name}.{method_name}" 或 bare 模式 "{sha1}.{method_name}"
|
||||
# build search pattern: "{sha1}.{class_name}.{method_name}" or bare "{sha1}.{method_name}"
|
||||
cls_len: sizet = 0
|
||||
if class_name is not None: cls_len = string.strlen(class_name)
|
||||
meth_len: sizet = string.strlen(method_name)
|
||||
@@ -1310,19 +1186,19 @@ novirtual class CallGenBase:
|
||||
viperlib.snprintf(search_buf, search_len, "%s.%s.%s", class_sha1, class_name, method_name)
|
||||
search_str_len: sizet = string.strlen(search_buf)
|
||||
|
||||
# 单趟扫描 text.ll:登记其全部 define 行供后续 O(1) 命中,并同趟取出首个匹配项。
|
||||
# 文件长度直接用 read_all 的返回值,省掉一次整文件 strlen。
|
||||
# single pass over text.ll: register all define lines for later O(1) hits and
|
||||
# return the first match in the same pass; reuse read_all's return length.
|
||||
result_ty: mlirlite.LLVMType* = self._CacheModuleRetTyTexts(class_sha1, read_buf, sizet(text_len), pool, search_buf, search_str_len)
|
||||
|
||||
stdlib.free(search_buf)
|
||||
stdlib.free(read_buf)
|
||||
return result_ty
|
||||
|
||||
# _dep_text_ll_exists:探测 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll 是否存在(1=存在),用于区分「依赖已编译但函数缺失」与「依赖模块尚未翻译」
|
||||
# _dep_text_ll_exists: probe whether {temp_dir}/{sha1[0:2]}/{sha1}.text.ll exists
|
||||
|
||||
# ============================================================
|
||||
def _dep_text_ll_exists(self, pool: memhub.MemBuddy*, sha1: str) -> int:
|
||||
"""探测 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll 是否存在,1=存在"""
|
||||
"""Probe whether {temp_dir}/{sha1[0:2]}/{sha1}.text.ll exists, 1=yes"""
|
||||
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0 or sha1 is None: return 0
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
if sha1_len < 2: return 0
|
||||
@@ -1338,17 +1214,17 @@ novirtual class CallGenBase:
|
||||
subdir_buf[2] = '\0'
|
||||
viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll", self.Gen.CallGenBase.TempDir, subdir_buf, sha1)
|
||||
stdlib.free(subdir_buf)
|
||||
# 存在性判定用 GetFileAttributesA(属性查询,不开文件句柄)
|
||||
# existence check via GetFileAttributesA (no file handle opened)
|
||||
attr: ULONG = win32file.GetFileAttributesA(path_buf)
|
||||
exists: int = 1
|
||||
# INVALID_FILE_ATTRIBUTES = 不存在/不可访问 → 0。
|
||||
# INVALID_FILE_ATTRIBUTES = missing/inaccessible -> 0.
|
||||
if attr == 0xFFFFFFFF: exists = 0
|
||||
# 原实现以只读 CreateFileA 打开:目录会被拒绝(未带 FILE_FLAG_BACKUP_SEMANTICS)
|
||||
# → 0,故此处显式排除目录以保持「是可读常规文件」的等价判定。
|
||||
# The original CreateFileA rejected directories; exclude them explicitly to
|
||||
# preserve the equivalent "readable regular file" semantics.
|
||||
if (attr & win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: exists = 0
|
||||
stdlib.free(path_buf)
|
||||
return exists
|
||||
|
||||
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助:把 obj.prop / obj.prop=v / del obj.prop / Class.method() 翻译为 {sha1}.ClassName.prop 系列函数调用(元数据表由 HandlesClassDef 填充)
|
||||
# property (@property/@x.setter/@x.deleter) and static/class method call helpers
|
||||
|
||||
# _translate_property_call:翻译属性方法调用——优先用模块中已有函数(精确/后缀匹配),否则用类所属模块 SHA1 构造 "{sha1}.ClassName.method";call_head/call_count 为已构建好的参数链表(如 getter 只有 obj 一个参数)
|
||||
# _translate_property_call: prefer an existing module function, else build "{sha1}.ClassName.method"
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -12,23 +12,21 @@ import lib.core.Gen.GenApi as GenApi
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenFd - CallGenMixin 拆分(阶段 B):FakeDuck / requires 字段注入 / provider 栈
|
||||
# ============================================================
|
||||
# CallGenFd - split from CallGenMixin (stage B): FakeDuck / requires field injection / provider stack
|
||||
novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
"""FakeDuck / requires 字段注入 / provider 栈"""
|
||||
"""FakeDuck / requires field injection / provider stack"""
|
||||
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""FakeDuck: str 变量方法调用解析到扩展类 _Type/_str,返回 call 结果或 None"""
|
||||
"""FakeDuck: resolve a str variable's method call to extension class _Type/_str, return the call result or None"""
|
||||
if pool is None or builder is None or cl is None or trans is None: return None
|
||||
if method_name is None: return None
|
||||
# 仅对 str 类型触发(AnnotClassName == "str",对象是 char*);
|
||||
# cls_name 为 None(表达式接收者解析失败)时也尝试
|
||||
# Only trigger for str (AnnotClassName == "str", object is char*);
|
||||
# also try when cls_name is None (receiver resolution failed).
|
||||
if cls_name is not None and string.strcmp(cls_name, "str") != 0: return None
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return None
|
||||
fd_at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if fd_at is None or fd_at.value is None or fd_at.attr is None: return None
|
||||
if string.strcmp(fd_at.attr, method_name) != 0: return None
|
||||
# 候选扩展类:先 _Type(a.type_id())后 _str(a.upper() 等)
|
||||
# Candidate extension classes: _Type first (a.type_id()), then _str (a.upper() etc.)
|
||||
fd_func: mlirlite.Function* = None
|
||||
fd_cls: str = None
|
||||
fd_key: str = pool.alloc(160)
|
||||
@@ -42,28 +40,28 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
fd_cls = fd_cn
|
||||
break
|
||||
if fd_func is None or fd_cls is None: return None
|
||||
# 扩展类结构体必须已注册(GEP/临时影子需要类型)
|
||||
# The extension class struct must be registered (GEP/temp shadow needs a type)
|
||||
fd_sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(fd_cls)
|
||||
if fd_sty is None: return None
|
||||
# 扩展类字段数:有字段(_str)时 self 用影子结构体指针;
|
||||
# 无字段(_Type)时 self 用对象值 bitcast 到扩展类指针(对齐 TPC L2856-2876)
|
||||
# Extension class field count: with fields (_str) self uses a shadow struct pointer;
|
||||
# without fields (_Type) self bitcasts the object value to the extension pointer (matches TPC L2856-2876).
|
||||
fd_entry_fd: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(fd_sty)
|
||||
fd_has_fields: int = 0
|
||||
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0: fd_has_fields = 1
|
||||
# self 参数:优先使用已有影子 {var}__meta__(fd2 创建,仅当扩展类有字段时)
|
||||
# self arg: prefer an existing shadow {var}__meta__ (created by fd2, only when the extension class has fields)
|
||||
fd_self: mlirlite.Value* = None
|
||||
if fd_at.value.kind() == ast.ASTKind.Name:
|
||||
fd_onm: ast.Name* = ast.Name*(fd_at.value)
|
||||
if fd_onm.id is not None:
|
||||
# pool 持久缓冲(f-string 栈缓冲在函数返回后失效)
|
||||
# Pool-persistent buffer (an f-string stack buffer would expire after return)
|
||||
fd_mn: str = pool.alloc(256)
|
||||
if fd_mn is not None: viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
|
||||
fd_meta: mlirlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_mn)
|
||||
if fd_meta is not None and fd_has_fields != 0: fd_self = fd_meta
|
||||
if fd_self is None:
|
||||
if fd_has_fields != 0:
|
||||
# 有字段扩展类且无影子(字符串字面量等):入口创建临时影子并初始化
|
||||
# 先确保 _str 完整定义(跨模块结构体默认 opaque,alloca 报 unsized)
|
||||
# Fielded extension class with no shadow (string literal etc.): create and init a temp
|
||||
# shadow at entry; first ensure full _str definition (cross-module structs default to opaque and alloca would report unsized).
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty)
|
||||
fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty)
|
||||
if fd_self is None: return None
|
||||
@@ -78,15 +76,15 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
fd_bp: mlirlite.Value* = mlirlite.build_gep_struct(builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
|
||||
fd_null: mlirlite.Value* = mlirlite.ConstNull(pool, fd_bfe.Ty, "null")
|
||||
if fd_bp is not None and fd_null is not None: mlirlite.build_store(builder, fd_null, fd_bp)
|
||||
# FakeDuck: with 上下文自动注入 requires 字段(如 __mbuddy__)
|
||||
# FakeDuck: auto-inject requires fields from the with context (e.g. __mbuddy__)
|
||||
self._inject_requires_fields(trans, pool, builder, mod, fd_self, fd_cls)
|
||||
else:
|
||||
# 无字段扩展类(_Type):对象值 bitcast 到扩展类指针
|
||||
# Fieldless extension class (_Type): bitcast the object value to the extension pointer
|
||||
fd_obj_v: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
|
||||
if fd_obj_v is None: return None
|
||||
fd_self = mlirlite.build_bitcast(builder, fd_obj_v, mlirlite.Ptr(pool, fd_sty))
|
||||
if fd_self is None: return None
|
||||
# 构建参数链表: self → 其余参数
|
||||
# Build the argument list: self -> remaining args
|
||||
mlirlite.value_set_next(fd_self, None)
|
||||
fd_head: mlirlite.Value* = fd_self
|
||||
fd_tail: mlirlite.Value* = fd_self
|
||||
@@ -97,9 +95,8 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
fd_arg: ast.AST* = fd_args.get(fd_ai)
|
||||
if fd_arg is not None:
|
||||
fd_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_arg, None, 0, trans)
|
||||
# char 常量参数 → 单字符字符串(char 隐式转 str,
|
||||
# 如 s1.split(','):',' 是 i32 值 44,传给 str 参数须先字符串化,
|
||||
# 否则运行时 strlen/strncmp 解引用 44 崩溃)
|
||||
# char constant arg -> single-char string (implicit char->str, e.g. s1.split(',') where
|
||||
# ',' is i32 44); passing it raw to a str param makes strlen/strncmp deref 44 and crash.
|
||||
if fd_arg.kind() == ast.ASTKind.Constant:
|
||||
fd_ac: ast.Constant* = ast.Constant*(fd_arg)
|
||||
if fd_ac.const_kind == ast.CONST_CHAR:
|
||||
@@ -112,12 +109,12 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
mlirlite.value_set_next(fd_av, None)
|
||||
mlirlite.value_set_next(fd_tail, fd_av)
|
||||
fd_tail = fd_av
|
||||
# 返回类型与调用名(来自 find_func_in_module 命中的函数定义)
|
||||
# Return type and call name (from the function definition hit by find_func_in_module)
|
||||
fd_ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(fd_func)
|
||||
if fd_ret_ty is None: fd_ret_ty = mlirlite.Int32(pool)
|
||||
fd_fname: str = mlirlite.function_get_name(fd_func)
|
||||
if fd_fname is None: return None
|
||||
# 返回类型精确化:跨模块 declare 的 list[T] 特化返回类型可能被简化为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标路径而非 list 内联 __getitem__(printf %s 崩溃)。权威来源为定义模块 vpi(未命中回退 text.ll);仅当函数名确为 "{16hex}.{func}"(判定走唯一真源 GenApi.sha1_prefix_len)才提取模块 sha1
|
||||
# Refine return type: cross-module declare may simplify a list[T] specialization return type to Ptr(i8), degrading the caller's alloca type -> parts[0] takes the char-index path instead of list inline __getitem__ (printf %s crash). Authority is the defining module's vpi (fallback text.ll); extract the module sha1 only when the name is "{16hex}.{func}" (via GenApi.sha1_prefix_len)
|
||||
if GenApi.sha1_prefix_len(fd_fname) != 0:
|
||||
fd_sha1: str = pool.alloc(17)
|
||||
if fd_sha1 is not None:
|
||||
@@ -128,27 +125,16 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
if fd_exact is not None: fd_ret_ty = fd_exact
|
||||
fd_total: int = 1 + fd_can
|
||||
return mlirlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
|
||||
# ============================================================
|
||||
# _inject_requires_fields - FakeDuck: with 上下文注入 requires 字段
|
||||
#
|
||||
# 对齐 TPC HandlesExprCall.py L4021-4079:
|
||||
# 对类 __requires__ 元数据(fd1 注册)中的每个字段,运行时调用
|
||||
# _find_provider(field) 查找 with 上下文提供的指针;非 NULL 时
|
||||
# bitcast 到字段类型写入 meta_ptr 对应槽位(条件注入,避免覆盖
|
||||
# 用户已手动设置的值)。_find_provider 未注册(未 import
|
||||
# _withcontent)时跳过注入,不影响非 with 场景。
|
||||
# ============================================================
|
||||
# _inject_requires_fields - FakeDuck: inject requires fields from the with context.
|
||||
# For each class __requires__ field (registered by fd1), call _find_provider(field) at runtime; if non-NULL bitcast and store it (conditional inject); skip when _find_provider is unregistered.
|
||||
|
||||
# ============================================================
|
||||
def _inject_requires_fields(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, meta_ptr: mlirlite.Value*, class_name: str) -> int:
|
||||
"""在 with 上下文注入 requires 字段,返回注入字段数"""
|
||||
"""Inject requires fields from the with context, return the injected field count"""
|
||||
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||
if meta_ptr is None or class_name is None: return 0
|
||||
# _withcontent._find_provider(跨模块):仅当本模块可见(_withcontent
|
||||
# 被 import,vpi stub declare 已注入)时才注入。对齐 TPC L4048-4050
|
||||
# (Gen.functions.get('_find_provider') 不存在则跳过)——否则非 with
|
||||
# 项目(如 HashTableTest 未 import _withcontent)也会为每个 _str(...)
|
||||
# 构造发射 {sha1}._find_provider 调用 → ld.lld undefined symbol。
|
||||
# _withcontent._find_provider (cross-module): inject only when visible in this module
|
||||
# (matches TPC L4048-4050); otherwise non-with projects would emit undefined {sha1}._find_provider calls.
|
||||
fd_fn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_find_provider")
|
||||
if fd_fn_probe is None: return 0
|
||||
fd_fname: str = mlirlite.function_get_name(fd_fn_probe)
|
||||
@@ -169,20 +155,20 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
if fname is None: continue
|
||||
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(sty, fname)
|
||||
if fe is None or fe.Ty is None: continue
|
||||
# 参数:字段名字符串(i8*)
|
||||
# Arg: field name string (i8*)
|
||||
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
|
||||
if fd_fstr is None: continue
|
||||
mlirlite.value_set_next(fd_fstr, None)
|
||||
self._ensure_method_declare(pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1)
|
||||
fd_ptr: mlirlite.Value* = mlirlite.build_call(builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
|
||||
if fd_ptr is None: continue
|
||||
# 条件注入:ptr != NULL 才写入(保留用户手动设置的值)
|
||||
# Conditional inject: store only when ptr != NULL (preserve user-set values)
|
||||
fd_null: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty_j, "null")
|
||||
if fd_null is None: continue
|
||||
fd_cmp: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, fd_ptr, fd_null)
|
||||
if fd_cmp is None: continue
|
||||
# 块名唯一化(create_block 不自动编号,同名 label 在文本 IR 中
|
||||
# 冲突 → llc "Terminator found in the middle of a basic block")
|
||||
# Unique block names (create_block does not auto-number; duplicate labels in textual IR
|
||||
# conflict -> llc "Terminator found in the middle of a basic block").
|
||||
vtc2: int = trans._label_counter
|
||||
trans._label_counter = vtc2 + 1
|
||||
fd_bb_buf2: str = pool.alloc(64)
|
||||
@@ -193,36 +179,29 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
fd_inject_bb: rawptr[mlirlite.BasicBlock] = mlirlite.create_block(pool, cur_func, fd_bb_buf2)
|
||||
if fd_skip_bb is None or fd_inject_bb is None: continue
|
||||
mlirlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
|
||||
# inject 块:bitcast + GEP + store,跳回 skip
|
||||
# inject block: bitcast + GEP + store, then jump back to skip
|
||||
mlirlite.position_at_end(builder, fd_inject_bb)
|
||||
fd_cast: mlirlite.Value* = mlirlite.build_bitcast(builder, fd_ptr, fe.Ty)
|
||||
if fd_cast is not None:
|
||||
fd_fp: mlirlite.Value* = mlirlite.build_gep_struct(builder, sty, fe.Ty, meta_ptr, fe.Index)
|
||||
if fd_fp is not None: mlirlite.build_store(builder, fd_cast, fd_fp)
|
||||
mlirlite.build_br(builder, fd_skip_bb)
|
||||
# skip 块:恢复 CurBlock,继续后续代码生成
|
||||
# skip block: restore CurBlock and continue codegen
|
||||
mlirlite.position_at_end(builder, fd_skip_bb)
|
||||
injected += 1
|
||||
return injected
|
||||
# ============================================================
|
||||
# _emit_provider_push / _emit_provider_pop - FakeDuck: with 上下文
|
||||
# provider push/pop(__provides__ 元数据,fd1 注册)
|
||||
#
|
||||
# 对齐 TPC HandlesWith.py L138-174:
|
||||
# with 进入时对每个提供字段生成 _push_provider(field_str, ctx_i8);
|
||||
# 退出时生成等量 _pop_provider()。运行时 _withcontent 模块负责
|
||||
# 维护 TLS provider 栈,供 _inject_requires_fields 查找。
|
||||
# ============================================================
|
||||
# _emit_provider_push / _emit_provider_pop - FakeDuck: with-context provider push/pop (__provides__ metadata).
|
||||
# Matches TPC HandlesWith.py L138-174: push _push_provider(field, ctx_i8) on entry and equal _pop_provider() on exit.
|
||||
|
||||
# ============================================================
|
||||
def _emit_provider_push(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, ctx_val: mlirlite.Value*) -> int:
|
||||
"""生成 _push_provider(field, ctx_i8) 调用(每提供字段一次),返回字段数"""
|
||||
"""Emit a _push_provider(field, ctx_i8) call per provided field, return the field count"""
|
||||
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||
if cls_name is None or ctx_val is None: return 0
|
||||
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
|
||||
if fd_pc <= 0: return 0
|
||||
# _withcontent._push_provider(跨模块,同 _find_provider 的可见性守卫:
|
||||
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
|
||||
# _withcontent._push_provider (cross-module, same visibility guard as _find_provider:
|
||||
# skip when this module does not import _withcontent, to avoid undefined symbol).
|
||||
fd_pfn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_push_provider")
|
||||
if fd_pfn_probe is None: return 0
|
||||
fd_pfn: str = mlirlite.function_get_name(fd_pfn_probe)
|
||||
@@ -233,7 +212,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
if i8_ptr_ty_p is None: return 0
|
||||
ret_i32_p: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if ret_i32_p is None: return 0
|
||||
# ctx 值 bitcast 到 i8*(provider 栈存 void*)
|
||||
# Bitcast the ctx value to i8* (provider stack stores void*)
|
||||
fd_ctx_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
|
||||
if fd_ctx_i8 is None: return 0
|
||||
for pi in range(fd_pc):
|
||||
@@ -247,13 +226,13 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
mlirlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
|
||||
return fd_pc
|
||||
def _emit_provider_pop(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str) -> int:
|
||||
"""生成 _pop_provider() 调用(每提供字段一次),返回字段数"""
|
||||
"""Emit a _pop_provider() call per provided field, return the field count"""
|
||||
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||
if cls_name is None: return 0
|
||||
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
|
||||
if fd_pc <= 0: return 0
|
||||
# _withcontent._pop_provider(跨模块,同 _find_provider 的可见性守卫:
|
||||
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
|
||||
# _withcontent._pop_provider (cross-module, same visibility guard as _find_provider:
|
||||
# skip when this module does not import _withcontent, to avoid undefined symbol).
|
||||
fd_pon_probe: mlirlite.Function* = self.find_func_in_module(mod, "_pop_provider")
|
||||
if fd_pon_probe is None: return 0
|
||||
fd_pon: str = mlirlite.function_get_name(fd_pon_probe)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -14,13 +14,12 @@ from . import CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenLib - CallGenMixin 拆分(阶段 C):C 库声明 / 返回类型推断 /
|
||||
# ============================================================
|
||||
# CallGenLib - CallGenMixin split (stage C): C-lib declares / return-type inference
|
||||
novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
"""C 库声明 / 返回类型推断 / kwargs 调用辅助(继承 Base)"""
|
||||
"""C-lib declarations / return-type inference / kwargs call helpers (extends Base)"""
|
||||
|
||||
def _ensure_c_lib_declare(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str) -> mlirlite.Function*:
|
||||
"""为已知的 C 库函数创建 declare 声明"""
|
||||
"""Create a declare for a known C-lib function"""
|
||||
if pool is None or mod is None or name is None: return None
|
||||
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
@@ -29,7 +28,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
|
||||
ret_ty: mlirlite.LLVMType* = None
|
||||
params: int = 0 # 参数数量
|
||||
params: int = 0 # parameter count
|
||||
|
||||
if string.strcmp(name, "strlen") == 0 or string.strcmp(name, "strlength") == 0:
|
||||
ret_ty = i64_ty
|
||||
@@ -78,10 +77,10 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
params = 1
|
||||
else: return None
|
||||
|
||||
# 创建 declare
|
||||
# create the declare
|
||||
fn: mlirlite.Function* = mlirlite.create_declare(pool, mod, name, ret_ty)
|
||||
if fn is None: return None
|
||||
# 添加参数
|
||||
# add parameters
|
||||
if params == 1:
|
||||
if string.strcmp(name, "abs") == 0: mlirlite.add_param(pool, fn, i32_ty, "n")
|
||||
elif string.strcmp(name, "labs") == 0: mlirlite.add_param(pool, fn, i64_ty, "n")
|
||||
@@ -101,19 +100,11 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
return fn
|
||||
|
||||
def _promote_clib_call_args(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, call_name: str, head: mlirlite.Value*, count: int) -> mlirlite.Value*:
|
||||
"""裸名 C 库函数实参按标准签名提升(B3)。
|
||||
|
||||
跨模块裸名调用(string.memset → @memset)时 _ensure_method_declare 用
|
||||
实参类型建 declare:第三参字面量 32=i32 把定义 sizet(i64) 固化窄
|
||||
→ declare/call @memset(i8*,i32,i32) vs 定义 @memset(i8*,i32,i64),
|
||||
ABI 宽度不一致(VKernel process.vp B3)。调用点先把第三参窄整数
|
||||
实参提升到 i64,declare 与 call 均按 i64 生成 → 与定义一致。
|
||||
未命中白名单原样返回 head。
|
||||
"""
|
||||
"""Promote bare-name C-lib call arguments to their standard signature widths"""
|
||||
if pool is None or builder is None or call_name is None or head is None: return head
|
||||
# 仅裸名(无 '.' 分隔 → 无 sha1 前缀);带前缀走 vpi 定义侧解析
|
||||
# bare names only (no '.' -> no sha1 prefix); prefixed ones use vpi definition-side parsing
|
||||
if string.strchr(call_name, 46) is not None: return head
|
||||
# 白名单:size_t(i64) 长度参数所在下标(与 _ensure_c_lib_declare 一致;vsnprintf 第二参即 sizet 长度)
|
||||
# whitelist: index of the size_t (i64) length param (matches _ensure_c_lib_declare)
|
||||
size_idx: int = -1
|
||||
if string.strcmp(call_name, "memset") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "memcpy") == 0: size_idx = 2
|
||||
@@ -124,7 +115,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
elif string.strcmp(call_name, "strncat") == 0: size_idx = 2
|
||||
elif string.strcmp(call_name, "vsnprintf") == 0: size_idx = 1
|
||||
if size_idx < 0 or count <= size_idx: return head
|
||||
# 定位长度参数(index size_idx)
|
||||
# locate the length param (index size_idx)
|
||||
cur: mlirlite.Value* = head
|
||||
idx: int = 0
|
||||
while cur is not None and idx < size_idx:
|
||||
@@ -137,15 +128,15 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
case mlirlite.LLVMType.Int(bits):
|
||||
if bits >= 64: return head
|
||||
if cur.IsConst != 0:
|
||||
# 常量:直接 i64 常量(位模式保留,无符号长度语义)
|
||||
# constant: use an i64 constant directly (bit pattern kept, unsigned length)
|
||||
promoted = mlirlite.const_int(pool, 64, cur.IntVal)
|
||||
else:
|
||||
# SSA 值:zext 到 i64(size_t 无符号长度)
|
||||
# SSA value: zext to i64 (size_t is an unsigned length)
|
||||
promoted = mlirlite.build_zext(builder, cur, i64_ty)
|
||||
case _:
|
||||
return head
|
||||
if promoted is None: return head
|
||||
# 重建实参链表:index size_idx 替换为提升后的值
|
||||
# rebuild the arg list: replace index size_idx with the promoted value
|
||||
new_head: mlirlite.Value* = None
|
||||
new_tail: mlirlite.Value* = None
|
||||
cur2: mlirlite.Value* = head
|
||||
@@ -160,22 +151,17 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
i2 += 1
|
||||
return new_head
|
||||
|
||||
# _coerce_args_to_def_params:按 vpi 定义侧形参注解把跨模块调用实参 coerce 到目标类型并重建实参链,使 call/declare/define 宽度一致;支持 {sha1}.{func} 与 {sha1}.{cls}.{method} 两形态(段下标与实参下标一一对应),裸名 C 库走 _promote_clib_call_args/_ensure_c_lib_declare,未命中原样保留
|
||||
# _coerce_args_to_def_params: coerce cross-module call args to vpi definition-side param
|
||||
# types so call/declare/define widths match; bare C-lib names use the C-lib path instead.
|
||||
def _coerce_args_to_def_params(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, call_name: str, head: mlirlite.Value*, count: int, cargs: list[ast.AST*]* = None, self_off: int = 0, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""把跨模块调用实参 coerce 到 vpi 定义侧形参类型,返回重建的实参链
|
||||
|
||||
cargs/self_off/trans:实参 AST 列表与其在实参链中的偏移(模块级函数 0、
|
||||
方法 1——链首元素是 self,而 vpi 段 0 即 self 占位段)。仅用于非常量
|
||||
(SSA)整型扩宽时的**源符号性**判定(与 ExprGenBase._zext_or_sext_val
|
||||
同源);缺省时该判定退化为有符号(sext),与 coerce_to_type 默认一致。
|
||||
"""
|
||||
"""Coerce cross-module call args to vpi definition-side param types; returns the rebuilt chain"""
|
||||
if pool is None or builder is None or call_name is None or head is None: return head
|
||||
if self._is_sha1_mangled_call(call_name) == 0: return head
|
||||
# 拆 {sha1}.{rest}:rest 含 '.' 则为 {cls}.{method},否则为 {func}
|
||||
# split {sha1}.{rest}: if rest has '.', it is {cls}.{method}, else {func}
|
||||
rest: str = call_name + 17
|
||||
cls_nm: str = None
|
||||
meth_nm: str = rest
|
||||
# 取末个 '.':method 不含 '.',首个 '.' 会把含模块限定名的类型实参截断
|
||||
# take the last '.': the method has none, and the first '.' would truncate qualified type args
|
||||
dot_r: str = string.strrchr(rest, 46)
|
||||
if dot_r is not None:
|
||||
cls_len: sizet = uint64(dot_r) - uint64(rest)
|
||||
@@ -195,10 +181,10 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
return head
|
||||
string.strncpy(sha1_buf, call_name, 16)
|
||||
sha1_buf[16] = '\0'
|
||||
# 直接 ensure 加载 + 直查缓存(不用 _vpi_method_param_annot:其 cls_name is None 时恒返回 None,模块级函数永不生效)
|
||||
# ensure-load + direct cache lookup (not _vpi_method_param_annot, which misses module funcs)
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.Gen.CallGenBase.TempDir)
|
||||
prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm)
|
||||
# 泛型特化类({sha}.list[str].__new__):定义侧注解写在模板类上,精确键必遗漏 ⇒ tpl_src=1 时退查模板注解并替换类型形参(VpiReader._resolve_vpi_param_annot_seg_template)
|
||||
# generic-specialized class: annotations live on the template class, so fall back to template lookup
|
||||
tpl_src: int = 0
|
||||
if prm_annots is None:
|
||||
if cls_nm is not None and string.strchr(cls_nm, 91) is not None: tpl_src = 1
|
||||
@@ -218,20 +204,12 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
if tpl_src != 0: tgt_ty = VpiReader._resolve_vpi_param_annot_seg_template(pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty)
|
||||
else: tgt_ty = VpiReader._resolve_vpi_param_annot_seg(pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty)
|
||||
if tgt_ty is not None and cur.Ty is not None:
|
||||
# 实参 AST(源符号性判定用):链下标 idx 减 self 偏移即 AST 下标
|
||||
# Argument AST (for source-symbolic checks): AST index = chain index idx - self offset
|
||||
arg_idx: int = idx - self_off
|
||||
arg_node: ast.AST* = None
|
||||
if cargs is not None and arg_idx >= 0 and arg_idx < len(cargs): arg_node = cargs.get(arg_idx)
|
||||
# 「str / bytes 不是通用指针通配」:`void*` / `t.CPtr` / `rawptr` 允许
|
||||
# 隐式转为任意指针类型,但 `str` / `bytes` 不允许——即便它们在 LLVM
|
||||
# 层同为 i8*。跨模块实参在这条路径上原本只做 bitcast、完全不过指针
|
||||
# 转换校验,于是"把结构体/类/数组/函数指针传给 str 形参"会被静默放行
|
||||
# ⇒ 后续把该指针当 C 字符串用时生成非法 IR(现场:把 Function* 传给
|
||||
# `callee: str`,发射出 `call i32 @(i32 7)` 空符号名,直到 llc 才炸)。
|
||||
# 判据:定义侧形参注解**恰为**裸 str/bytes(组合段如 `str*` 不
|
||||
# 等值、不受影响),且实参是指向聚合类型的指针。判据与报错统一委托
|
||||
# ExprGenBase._str_rejects_ptr_kind / fail_if_str_annot_agg_ptr(单一
|
||||
# 真源,字符数组 [N x i8] 衰减与其在同模块路径同口径豁免)。
|
||||
# "str / bytes are not generic pointer wildcards": void* / t.CPtr / rawptr may
|
||||
# convert to any pointer, but str/bytes may not, even though both are i8* at LLVM level.
|
||||
if self.Gen.ExprGenBase._str_rejects_ptr_kind(cur) != 0:
|
||||
if VpiReader._vpi_param_annot_seg_is(sha1_buf, cls_nm, meth_nm, idx, "str") != 0 or VpiReader._vpi_param_annot_seg_is(sha1_buf, cls_nm, meth_nm, idx, "bytes") != 0:
|
||||
self.Gen.ExprGenBase.fail_if_str_annot_agg_ptr(cur, 1, arg_node)
|
||||
@@ -245,16 +223,10 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return new_head
|
||||
|
||||
# _coerce_arg_value_to_ty:单个实参 Value 转换到目标形参类型——整型同宽原样、窄化 trunc/按宽度重建常量、扩宽按源符号性 zext/sext;同层指针类型不一致则 bitcast 到目标类型;其余原样返回(不可转换亦不改型)
|
||||
# _coerce_arg_value_to_ty: convert one arg Value to the target param type -- same-width ints
|
||||
# pass through, narrow/wide ints trunc/extend, mismatched pointers bitcast; else unchanged.
|
||||
def _coerce_arg_value_to_ty(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, tgt_ty: mlirlite.LLVMType*, node: ast.AST* = None, trans: HT.Translator* = None, tpl_src: int = 0) -> mlirlite.Value*:
|
||||
"""把单个实参 Value 转换到目标形参类型,无法安全转换时原样返回
|
||||
|
||||
node/trans:实参 AST 与其翻译器(仅 SSA 整型扩宽的源符号性判定用,
|
||||
委托 ExprGenBase._zext_or_sext_val;缺省退化为有符号 sext)。
|
||||
tpl_src:目标类型来自**泛型模板类**注解(特化名精确键遗漏 → 退查模板 +
|
||||
类型形参替换,见 VpiReader._resolve_vpi_param_annot_seg_template)⇒
|
||||
指针口径亦可对齐定义侧(同为具体结构体指针但基类型不同时 bitcast)。
|
||||
"""
|
||||
"""Convert one argument Value to the target param type; leaves it unchanged when unsafe"""
|
||||
if pool is None or builder is None or val is None or tgt_ty is None or val.Ty is None: return val
|
||||
match val.Ty:
|
||||
case mlirlite.LLVMType.Int(bits_a):
|
||||
@@ -268,12 +240,12 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
else:
|
||||
nv_t: mlirlite.Value* = mlirlite.build_trunc(builder, val, tgt_ty)
|
||||
if nv_t is not None: return nv_t
|
||||
# 扩宽(实参窄于形参):编译期常量按十进制值重建,各自保持原类型数值,无需 AST 判符号性
|
||||
# widening (arg narrower than param): rebuild constants by decimal value
|
||||
if val.IsConst != 0:
|
||||
nv_w: mlirlite.Value* = mlirlite.const_int(pool, bits_b, val.IntVal)
|
||||
if nv_w is not None: return nv_w
|
||||
return val
|
||||
# 非常量(SSA)扩宽:扩宽方向由源符号性决定(i1/i8、无符号源 → zext,否则 sext),使 declare/call/define 三者同宽
|
||||
# SSA widening: direction from source signedness (i1/i8/unsigned -> zext, else sext)
|
||||
if bits_b > bits_a:
|
||||
if self.Gen.ExprGenBase._zext_or_sext_val(pool, val, node, trans) != 0:
|
||||
nv_zw: mlirlite.Value* = mlirlite.build_zext(builder, val, tgt_ty)
|
||||
@@ -287,20 +259,15 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
match tgt_ty:
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
# 同层指针:实参类型与定义侧形参类型不一致就 bitcast 对齐(一致则不比,
|
||||
# 避免生成非法 `bitcast T* to T*`)。跨模块调用的 call/declare 都以
|
||||
# 定义侧为准(declare 见 CallGenBase._ensure_method_declare),故这里
|
||||
# 必须对齐:首个调用点由它建立 declare、不走 coerce_to_type,原样传具体
|
||||
# 指针就会与 declare 的类型不一致(llc 报 argument type mismatch)。
|
||||
# 原判据只覆盖"一侧为 i8*"与"模板注解来源",漏了两侧同为具体结构体指针
|
||||
# 但基类型不同的情形(实参 MemBuddy* vs 定义侧形参 MemManager*)。
|
||||
# same-level pointers: bitcast when the arg type differs from the definition
|
||||
# side (aligned so the first call site's declare matches; llc otherwise errors).
|
||||
if self.Gen.AssignGen._stg_ty_equal(pool, val.Ty, tgt_ty) == 0:
|
||||
nv_p: mlirlite.Value* = mlirlite.build_bitcast(builder, val, tgt_ty)
|
||||
if nv_p is not None: return nv_p
|
||||
case _:
|
||||
pass
|
||||
case _:
|
||||
# 聚合值 → 指针:rvalue 结构体/数组落栈取址(FuncGenDef 形参一律 lowering 为 Ptr,跨模块时上游 coerce_to_type 不参与)
|
||||
# aggregate -> pointer: spill rvalue struct/array and take its address (params lower to Ptr)
|
||||
match tgt_ty:
|
||||
case mlirlite.LLVMType.Ptr(_):
|
||||
vk_a: int = val.Ty.kind()
|
||||
@@ -314,32 +281,20 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
return val
|
||||
|
||||
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy*, func_name: str, node: ast.AST*, fatal_if_unknown: int) -> mlirlite.LLVMType*:
|
||||
"""根据函数名推断外部 includes 函数的返回类型
|
||||
|
||||
支持三种函数名形式:
|
||||
1. 裸名: "parse", "strlen"
|
||||
2. 别名: "json_parse"(from X import parse as json_parse)
|
||||
3. 带 SHA1 前缀: "240a9a4157959a9f.parse"(跨模块调用 mangled name)
|
||||
|
||||
fatal_if_unknown: 白名单全部未命中时的行为。
|
||||
=1(纯裸名外部函数,无法收敛)→ fatal_error fail-fast(默认 i32
|
||||
会截断指针/i64,静默生成导致运行时数据损坏,不兜底原则)。
|
||||
=0(Phase A 中间遍,_deps_pending 下遍重译可收敛)→ 返回默认
|
||||
i32 占位,不输出 WARN(下遍依赖就绪后走 text.ll/vpi 精确解析)。
|
||||
"""
|
||||
"""Infer the return type of an external includes function; a whitelist miss may fail fast"""
|
||||
if pool is None or func_name is None: return mlirlite.Int32(pool)
|
||||
|
||||
# 提取裸函数名:去掉 SHA1 前缀(如 "240a9a4157959a9f.parse" → "parse")
|
||||
# SHA1 前缀是 16 位十六进制 + '.',检查是否有 '.' 分隔
|
||||
# extract the bare function name by stripping the SHA1 prefix
|
||||
# (16 hex digits + '.'), e.g. "240a9a4157959a9f.parse" -> "parse".
|
||||
bare_name: str = func_name
|
||||
dot_pos: str = string.strrchr(func_name, 46) # 46 = ord('.')
|
||||
if dot_pos is not None:
|
||||
# '.' 后的部分是裸函数名
|
||||
# the part after '.' is the bare function name
|
||||
bare_name = dot_pos + 1
|
||||
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# 返回 i8* 的函数(指针返回值,截断会导致错误)
|
||||
# functions returning i8* (truncating the pointer would be wrong)
|
||||
if string.strcmp(bare_name, "strchr") == 0 or string.strcmp(func_name, "strchr") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strrchr") == 0 or string.strcmp(func_name, "strrchr") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "strstr") == 0 or string.strcmp(func_name, "strstr") == 0: return i8_ptr_ty
|
||||
@@ -349,43 +304,38 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
if string.strcmp(bare_name, "memset32") == 0 or string.strcmp(func_name, "memset32") == 0: return i8_ptr_ty
|
||||
if string.strcmp(bare_name, "memcpy") == 0 or string.strcmp(func_name, "memcpy") == 0: return i8_ptr_ty
|
||||
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 也需覆盖
|
||||
# JSON parse functions return JsonValue* (pointer); include the json_parse alias
|
||||
# (from json.__parser import parse as 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 方法(当类型信息丢失时可能走外部函数路径)
|
||||
# 存疑
|
||||
# malloc:裸名 CExport 跨模块调用(uhci/desktop 等 mm.malloc → @malloc)
|
||||
# 在模块归属解析失败(mod_ret_sha1=None)或 vpi/text.ll 未命中时回退到
|
||||
# 此白名单。缺失导致返回类型被推断为 i32 → call i32 @malloc 截断 64 位
|
||||
# 指针 → mm.malloc 实际返回非空但调用方判 None(uhci frame list /
|
||||
# desktop bg_row_buf 根因)。calloc/realloc/alloc 均已登记,malloc 遗漏。
|
||||
# MemBuddy methods (may use the external-function path when type info is lost).
|
||||
# malloc: bare-name CExport cross-module call (uhci/desktop mm.malloc -> @malloc), else ret is inferred i32 and the 64-bit pointer truncates.
|
||||
if string.strcmp(bare_name, "malloc") == 0 or string.strcmp(func_name, "malloc") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "alloc") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "calloc") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "realloc") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "alloc_buf") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# VLogger 模块函数(跨模块调用时 stub 未注入,返回指针被截断为 i32 导致崩溃)
|
||||
# VLogger module funcs (stubs not injected cross-module, pointers truncated to i32)
|
||||
if string.strcmp(func_name, "get_logger") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "fmt_buf") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# Config 模块函数(跨模块调用时 stub 未注入,返回 i8* 被 inttoptr i32 截断导致路径损坏)
|
||||
# Config module funcs (stubs not injected cross-module, i8* truncated to i32)
|
||||
if string.strcmp(func_name, "get_includes_binary_dir") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# HandlesType 模块函数(返回 ast.AST* 联合类型,跨模块推断为 i32 导致指针截断)
|
||||
# HandlesType module func (returns ast.AST* union; inferred as i32 cross-module)
|
||||
if string.strcmp(bare_name, "_parse_type_string") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# 返回 i64 的函数
|
||||
# Functions returning i64
|
||||
if string.strcmp(func_name, "strlen") == 0: return mlirlite.Int64(pool)
|
||||
if string.strcmp(func_name, "atoll") == 0: return mlirlite.Int64(pool)
|
||||
# mlirlite 函数名长度访问器返回 sizet(i64):默认 i32 与 declare 的 i64 签名不一致,须登记
|
||||
# mlirlite name-length accessors return sizet (i64); the default i32 clashes with the declare
|
||||
if string.strcmp(bare_name, "function_get_name_len") == 0 or string.strcmp(func_name, "function_get_name_len") == 0: return mlirlite.Int64(pool)
|
||||
|
||||
# 返回 double 的函数
|
||||
# Functions returning double
|
||||
if string.strcmp(func_name, "atof") == 0: return mlirlite.Double(pool)
|
||||
|
||||
# 返回 i8 的函数
|
||||
# Functions returning i8
|
||||
if string.strcmp(func_name, "samestr") == 0: return mlirlite.Int8(pool)
|
||||
|
||||
# Win32 API 返回指针(VOIDPTR/HANDLE)的函数
|
||||
# stubs 翻译时未注入,需推断返回 i8* 避免指针被截断为 i32 导致解引用崩溃
|
||||
# Win32 APIs returning a pointer (VOIDPTR/HANDLE); no stubs are injected, so infer
|
||||
# i8* to keep the pointer from being truncated to i32 and crashing on dereference.
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(func_name, "VirtualAlloc") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "HeapAlloc") == 0: return i8_ptr_ty
|
||||
@@ -400,69 +350,58 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
if string.strcmp(func_name, "GetProcAddress") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetModuleHandleA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetModuleHandleW") == 0: return i8_ptr_ty
|
||||
# GetCommandLineA/W 返回 LPSTR(i8*):缺则 64 位指针被截成 i32,解引用读到无效地址
|
||||
# GetCommandLineA/W return LPSTR (i8*); without it a 64-bit pointer is truncated to i32
|
||||
if string.strcmp(func_name, "GetCommandLineA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "GetCommandLineW") == 0: return i8_ptr_ty
|
||||
# gethostbyname 返回 hostent*(i8*)
|
||||
# gethostbyname returns hostent* (i8*)
|
||||
if string.strcmp(func_name, "gethostbyname") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LoadLibraryA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "LoadLibraryW") == 0: return i8_ptr_ty
|
||||
# Win32 Handle 返回函数(HANDLE = void* = i8*)
|
||||
# Win32 handle-returning functions (HANDLE = void* = i8*)
|
||||
if string.strcmp(func_name, "GetStdHandle") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "CreateFileA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "CreateFileW") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "FindFirstFileA") == 0: return i8_ptr_ty
|
||||
if string.strcmp(func_name, "FindFirstFileW") == 0: return i8_ptr_ty
|
||||
|
||||
# Win32 API 返回 i64 (SIZE_T) 的函数
|
||||
# Win32 APIs returning i64 (SIZE_T)
|
||||
if string.strcmp(func_name, "VirtualQuery") == 0: return mlirlite.Int64(pool)
|
||||
if string.strcmp(func_name, "HeapSize") == 0: return mlirlite.Int64(pool)
|
||||
if string.strcmp(func_name, "HeapCompact") == 0: return mlirlite.Int64(pool)
|
||||
if string.strcmp(func_name, "GlobalSize") == 0: return mlirlite.Int64(pool)
|
||||
|
||||
# mlirlite build_* 返回类型推断:多数返回 Value*,少数终止指令返回 void(stubs 未注入时防指针截断)
|
||||
# mlirlite build_* return-type inference: most return Value*, terminators return void
|
||||
if string.strncmp(func_name, "build_", 6) == 0:
|
||||
# 返回 void 的 build_* 函数(终止指令和无返回值指令)
|
||||
# build_* returning void (terminators and void-returning ops)
|
||||
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 mlirlite.Void(pool)
|
||||
# 其他 build_* 函数返回 Value*(指针)
|
||||
# other build_* return Value* (pointer)
|
||||
return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# mlirlite 类型工厂 / 常量构造 / 访问器函数返回指针(LLVMType*/Value*):Int*/Ptr/Func/Array/Struct/Void 等、const_*/Const*、function_get_*/param_get_*
|
||||
# mlirlite type factories / const constructors / accessors return pointers (LLVMType* / Value*)
|
||||
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 mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# 默认 i32 占位:fatal_if_unknown=1 → fail-fast 报错;=0(Phase A 中间遍)保持占位,不输出 WARN
|
||||
# Default i32 placeholder: fatal_if_unknown=1 fails fast; =0 (Phase A mid-pass) stays silent.
|
||||
if fatal_if_unknown != 0: self.Gen.TypeGenErr.fatal_error(node, f"外部函数返回类型未登记且无法收敛推断(默认 i32 会截断指针/i64,需提供 stub 声明或加入白名单): {func_name}", "TypeError")
|
||||
return mlirlite.Int32(pool)
|
||||
def _get_external_func_param_ty(self, pool: memhub.MemBuddy*, func_name: str, param_idx: int) -> mlirlite.LLVMType*:
|
||||
"""获取外部函数第 param_idx 个参数的期望类型,None 表示用字面量原类型
|
||||
|
||||
⚠ **兜底专用**:调用方应先按**定义侧 vpi 形参注解**取真实类型
|
||||
(CallGenMixin 的模块限定调用分支,见 _resolve_vpi_param_annot_seg),
|
||||
只有注解不可得时才落到本白名单。
|
||||
|
||||
本白名单把 Win32 API 的**所有**非指针参数一律当 i64,其依据是旧口径
|
||||
"DWORD/ULONG/SIZE_T 在 stubs 翻译时都被映射为 i64";该口径**已过时**
|
||||
(现实:SIZE_T→i64、ULONG/DWORD→i32),若据此提升实参会让 call(i64)
|
||||
与 declare(i32) 不匹配(mlir-opt: "llvm.call op operand type mismatch",
|
||||
现场 FuncTest VirtualAlloc)。故仅在拿不到定义侧注解时才用。
|
||||
"""
|
||||
"""Return the expected type of an external function's param_idx-th parameter, or None (fallback only)"""
|
||||
if pool is None or func_name is None: return None
|
||||
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
|
||||
# Win32 API 函数列表:所有非指针参数在 declare 中都是 i64
|
||||
# (因为 ULONG/DWORD/SIZE_T 在 LLVM stubs 翻译时都被映射为 i64)
|
||||
# Win32 API list: every non-pointer parameter is i64 in declare
|
||||
# (ULONG/DWORD/SIZE_T are all mapped to i64 during LLVM stub translation)
|
||||
win32_apis: list[str]* = ["VirtualAlloc", "VirtualFree", "VirtualProtect", "VirtualQuery", "VirtualLock", "VirtualUnlock", "HeapCreate", "HeapDestroy", "HeapAlloc", "HeapReAlloc", "HeapFree", "HeapSize", "HeapValidate", "HeapCompact", "GlobalAlloc", "GlobalFree", "GlobalLock", "GlobalUnlock", "GlobalSize", "LocalAlloc", "LocalFree", "GetLastError", "SetLastError", "Sleep", "SleepEx", "GetTickCount", "GetProcessHeap"]
|
||||
|
||||
# list 迭代不支持,用 range + get 替代
|
||||
# list iteration is unsupported; use range + get instead
|
||||
api_count: sizet = len(win32_apis)
|
||||
for ai in range(api_count):
|
||||
api_name: str = win32_apis.get(ai)
|
||||
if string.strcmp(api_name, func_name) == 0: return i64_ty
|
||||
|
||||
# 内存分配函数:size 参数声明为 i64(size_t),裸名回退时不提升则 call 与 define 宽度不匹配;calloc/realloc 的 size 在 idx 1
|
||||
# Allocators: size params are declared i64 (size_t); without promotion on bare-name fallback call/define widths mismatch; calloc/realloc size is idx 1
|
||||
if string.strcmp(func_name, "malloc") == 0 or string.strcmp(func_name, "alloc") == 0 or string.strcmp(func_name, "alloc_buf") == 0:
|
||||
if param_idx == 0: return i64_ty
|
||||
if string.strcmp(func_name, "calloc") == 0:
|
||||
@@ -472,28 +411,19 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
|
||||
return None
|
||||
def _is_sha1_mangled_call(self, call_name: str) -> int:
|
||||
"""判断 call_name 是否为跨模块 Viper 函数("{16位小写hex}.func" 形式)
|
||||
|
||||
C 库函数(strlen 等裸名)和 Win32 API(VirtualAlloc 等裸名)返回 0,
|
||||
不参与精确返回类型解析(走 _infer_external_func_ret_ty 白名单)。
|
||||
判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:不再接受大写 hex)。
|
||||
"""
|
||||
"""Return whether call_name is a cross-module Viper function ("{16 hex}.func")"""
|
||||
if call_name is None: return 0
|
||||
if GenApi.sha1_prefix_len(call_name) != 0: return 1
|
||||
return 0
|
||||
def _emit_llvm_memcpy_intrinsic(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, dst_val: mlirlite.Value*, src_val: mlirlite.Value*, num_val: mlirlite.Value*) -> mlirlite.Value*:
|
||||
"""将 memcpy 调用映射到 @llvm.memcpy 内联函数
|
||||
|
||||
直接接收三个 Value 参数,避免通过 Next 链表遍历(共享 alloca 对象的
|
||||
Next 字段可能被其他操作污染,导致链表断裂)。
|
||||
"""
|
||||
"""Map a memcpy call to the @llvm.memcpy intrinsic"""
|
||||
if pool is None or builder is None or mod is None: return None
|
||||
if dst_val is None or src_val is None or num_val is None: return None
|
||||
|
||||
# 在函数顶部声明 void_ty,避免后续遮蔽
|
||||
# Declare void_ty at the top of the function to avoid later shadowing
|
||||
void_ty: mlirlite.LLVMType* = mlirlite.Void(pool)
|
||||
|
||||
# 声明 @llvm.memcpy(如未声明)
|
||||
# Declare @llvm.memcpy (if not already declared)
|
||||
memcpy_func: mlirlite.Function* = self.find_func_in_module(mod, "llvm.memcpy")
|
||||
if memcpy_func is None:
|
||||
memcpy_func = mlirlite.create_declare(pool, mod, "llvm.memcpy", void_ty)
|
||||
@@ -506,8 +436,8 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
mlirlite.add_param(pool, memcpy_func, i64_ty_d, "len")
|
||||
mlirlite.add_param(pool, memcpy_func, i1_ty_d, "isvolatile")
|
||||
|
||||
# 类型转换: dst, src → i8*; num → i64
|
||||
# 注意: 使用 value_get_ty 访问器获取 Ty,避免本地 Value 类型字段索引偏移
|
||||
# Coercions: dst, src -> i8*; num -> i64
|
||||
# Use the value_get_ty accessor for Ty to avoid local Value field-index offset
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
|
||||
@@ -536,11 +466,11 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
|
||||
if dst_coerced is None or src_coerced is None or num_coerced is None: return None
|
||||
|
||||
# 添加 i1 false 参数(isvolatile)— llvm.memcpy 需要 4 个参数
|
||||
# Append the i1 false arg (isvolatile) - llvm.memcpy needs 4 parameters
|
||||
false_val: mlirlite.Value* = mlirlite.ConstInt(pool, mlirlite.Int1(pool), 0, "false")
|
||||
|
||||
# 使用 includes 中的访问器函数构建参数链表,避免本地 Value 类型字段索引偏移
|
||||
# (f26cd4c78b4d554c.Value 缺少 GSListNode 的 Next 字段,直接访问 .Next 会修改 .Ty)
|
||||
# Build the arg list via include accessors to avoid local Value field-index offset
|
||||
# (f26cd4c78b4d554c.Value lacks GSListNode.Next; direct .Next access corrupts .Ty)
|
||||
mlirlite.value_set_next(dst_coerced, src_coerced)
|
||||
mlirlite.value_set_next(src_coerced, num_coerced)
|
||||
if false_val is not None:
|
||||
@@ -548,38 +478,27 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
mlirlite.value_set_next(false_val, None)
|
||||
else: mlirlite.value_set_next(num_coerced, None)
|
||||
|
||||
# 使用 build_call 发射 call 指令(内部用 includes Value 类型访问字段,索引正确)
|
||||
# _ll_name_needs_quote 已对 llvm. 前缀豁免引号
|
||||
# Emit the call via build_call (it accesses include Value fields with correct indices)
|
||||
# _ll_name_needs_quote already exempts the llvm. prefix from quoting
|
||||
result: mlirlite.Value* = mlirlite.build_call(builder, "llvm.memcpy", dst_coerced, 4, void_ty, 0)
|
||||
|
||||
# 返回 dst(匹配 C memcpy 语义: 返回 dest 指针)
|
||||
# Return dst (matches C memcpy semantics: returns the dest pointer)
|
||||
return dst_coerced
|
||||
# _translate_call_with_kwargs:带关键字参数的函数调用,按 found_func.Params 顺序构建实参链表(位置参数优先,否则按参数名匹配关键字)
|
||||
# _translate_call_with_kwargs: keyword-arg call; builds args in found_func.Params order (positional first, else match by param name)
|
||||
|
||||
# ============================================================
|
||||
def _translate_call_with_kwargs(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, func_name: str, found_func: mlirlite.Function*, cargs: list[ast.AST*]*, can: sizet, keywords: list[ast.AST*]*, kw_count: sizet, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""带关键字参数的函数调用,按参数顺序构建调用链表
|
||||
|
||||
使用 mlirlite 访问器函数绕过 stub 类型限制:
|
||||
found_func.Params/RetTy/Name 和 cur_param.Name/Ty/Next 在 stub
|
||||
类型中字段不足,直接访问会被 TransPyC 静默跳过。
|
||||
"""
|
||||
"""Translate a call with keyword arguments, building args in parameter order"""
|
||||
head: mlirlite.Value* = None
|
||||
tail: mlirlite.Value* = None
|
||||
actual_count: sizet = 0
|
||||
# memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染)
|
||||
# memcpy-only: collect independent refs of the first three Values (avoid shared alloca Next pollution)
|
||||
mc_dst1: mlirlite.Value* = None
|
||||
mc_src1: mlirlite.Value* = None
|
||||
mc_num1: mlirlite.Value* = None
|
||||
|
||||
# 省略参数补齐用的默认参数表查询(与 CallGenMixin 非 kwargs 路径同一真源)。
|
||||
# 根因:本路径此前对"位置/关键字均未提供的形参"直接跳过 → 调用实参数 <
|
||||
# define 形参数,被调函数按 x64 ABI 读未写寄存器/栈槽(remote.one(x = 1)
|
||||
# 的 cap 读成 0/垃圾,DefaultArgTest 断言失败)。
|
||||
# 定义模块 SHA1 取 found_func 的 mangled 名前缀 "{sha1}.func"(普通跨模块/
|
||||
# 同模块函数),取不到再回退当前模块 SHA1(同模块调用)。默认值表按
|
||||
# 「名 + 定义模块 SHA1」登记(_register_global_defaults),注册时已对常量
|
||||
# 快照,跨 1b reset 永久存活 ⇒ 跨模块也能取到定义处的真实默认值。
|
||||
# Default-arg lookup for omitted params (same source as CallGenMixin non-kwargs path):
|
||||
# module SHA1 is the "{sha1}.func" prefix or current module; the name+SHA1-keyed snapshot survives resets.
|
||||
kw_callee_sha: str = None
|
||||
kw_found_name: str = mlirlite.function_get_name(found_func)
|
||||
if kw_found_name is not None:
|
||||
@@ -599,7 +518,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
kw_default_off = kw_gd.ParamCount - kw_gd.DefaultCount
|
||||
if kw_default_off < 0: kw_default_off = 0
|
||||
|
||||
# 遍历函数参数链表(使用访问器获取 Head)
|
||||
# Walk the function parameter list (accessor gets Head)
|
||||
param_idx: sizet = 0
|
||||
cur_param: mlirlite.Param* = mlirlite.function_get_param_head(found_func)
|
||||
|
||||
@@ -607,13 +526,13 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
arg_val: mlirlite.Value* = None
|
||||
param_ty: mlirlite.LLVMType* = mlirlite.param_get_ty(cur_param)
|
||||
if param_idx < can:
|
||||
# 优先使用位置参数
|
||||
# Prefer the positional argument
|
||||
arg: ast.AST* = cargs.get(param_idx)
|
||||
if arg is not None:
|
||||
# 传形参类型(char 字面量 → str 形参需物化字符串,见 translate_constant)
|
||||
# Pass the param type (char literal -> str param materializes a string; see translate_constant)
|
||||
arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans, param_ty)
|
||||
else:
|
||||
# 查找匹配的关键字参数
|
||||
# Find the matching keyword argument
|
||||
param_name: str = mlirlite.param_get_name(cur_param)
|
||||
if param_name is not None:
|
||||
for ki in range(kw_count):
|
||||
@@ -623,9 +542,8 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, kw.value, None, 0, trans, param_ty)
|
||||
break
|
||||
|
||||
# 位置/关键字均未提供该形参:用**定义处**的默认值补齐(默认值右对齐,
|
||||
# 索引 = param_idx - param_count + default_count)。缺省且定义处无默认值
|
||||
# 可用时 fail-fast——绝不静默漏参或填零(漏参会令被调函数按 ABI 读垃圾)。
|
||||
# Param supplied by neither position nor keyword: fill with the definition-site default
|
||||
# (index = param_idx - param_count + default_count); else fail-fast, never silently drop/zero.
|
||||
if arg_val is None:
|
||||
if kw_default_count > 0 and int(param_idx) >= kw_default_off:
|
||||
kw_di: int = int(param_idx) - kw_default_off
|
||||
@@ -637,13 +555,13 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
self.Gen.TypeGenErr.fatal_error(kw_node, f"调用 '{func_name}' 的某个形参既未按位置/关键字传入," f"定义处也无默认值可填充(拒绝静默漏参/填零)", "TypeError")
|
||||
|
||||
if arg_val is not None:
|
||||
# 类型转换(按参数类型,使用访问器)
|
||||
# Coerce by parameter type (via accessor)
|
||||
if param_ty is not None: arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, param_ty)
|
||||
arg_val.Next = None
|
||||
if head is None: head = arg_val
|
||||
else: tail.Next = arg_val
|
||||
tail = arg_val
|
||||
# memcpy 专用: 保存前三个参数的独立引用
|
||||
# memcpy-only: save independent refs of the first three args
|
||||
if actual_count == 0: mc_dst1 = arg_val
|
||||
elif actual_count == 1: mc_src1 = arg_val
|
||||
elif actual_count == 2: mc_num1 = arg_val
|
||||
@@ -652,18 +570,18 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
param_idx += 1
|
||||
cur_param = mlirlite.param_get_next(cur_param)
|
||||
|
||||
# declare 与 define 统一用 found_func.Name:普通函数带 SHA1 前缀,t.CExtern/t.State/t.CExport 用裸名(用裸名会造成 call 与 define 符号名不一致)
|
||||
# declare and define both use found_func.Name: normal funcs carry the SHA1 prefix, t.CExtern/t.State/t.CExport use bare names (bare names desync call/define symbols)
|
||||
call_name: str = func_name
|
||||
found_name: str = mlirlite.function_get_name(found_func)
|
||||
if found_name is not None: call_name = found_name
|
||||
|
||||
# 返回类型(使用访问器)
|
||||
# Return type (via accessor)
|
||||
call_ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(found_func)
|
||||
if call_ret_ty is None:
|
||||
# found_func 已有 declare 却读不到返回类型(访问器异常,罕见防御):
|
||||
# 白名单未命中也不 fail-fast,保持默认 i32(有声明即有类型来源)。
|
||||
# found_func has a declare but return type is unreadable (accessor anomaly, rare guard):
|
||||
# even if the whitelist misses, do not fail-fast; keep the default i32.
|
||||
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name, None, 0)
|
||||
|
||||
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
|
||||
# memcpy -> @llvm.memcpy intrinsic (avoid a missing cross-module @memcpy declare under monomorphization)
|
||||
if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3: return self._emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst1, mc_src1, mc_num1)
|
||||
return mlirlite.build_call(builder, call_name, head, actual_count, call_ret_ty, 0)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -13,49 +13,35 @@ import lib.core.Gen.Translator as HT
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenProp - CallGenMixin 拆分(阶段 B):属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)
|
||||
# CallGenProp - split from CallGenMixin (stage B): property access (getter/setter/deleter / static class calls / list builtins)
|
||||
# ============================================================
|
||||
novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
"""属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)"""
|
||||
"""Property access (getter/setter/deleter / static class calls / list builtins)"""
|
||||
|
||||
# ============================================================
|
||||
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
|
||||
#
|
||||
# 元数据表由 HandlesClassDef 在类翻译/预注册阶段填充,
|
||||
# 这里负责把 obj.prop / obj.prop=v / del obj.prop / Class.method()
|
||||
# 翻译为对应的 {sha1}.ClassName.prop 系列函数调用。
|
||||
# ============================================================
|
||||
# Property methods (@property/@x.setter/@x.deleter) and static/class call helpers.
|
||||
# Translate obj.prop / obj.prop=v / del obj.prop / Class.method() into the matching {sha1}.ClassName.prop call.
|
||||
|
||||
# ============================================================
|
||||
# _translate_property_call — 翻译属性方法调用
|
||||
#
|
||||
# 与 _translate_method_call 的调用名/返回类型解析逻辑一致:
|
||||
# 1. 优先用模块中已有的函数(精确/后缀匹配,来自 stub 注入)
|
||||
# 2. 否则用类所属模块的 SHA1 构造 "{sha1}.ClassName.method" 调用名
|
||||
# 3. 返回类型:函数实类型 → text.ll 查找 → 按方法名推断(默认 i32)
|
||||
#
|
||||
# call_head/call_count: 已构建好的参数链表(如 getter 只有 obj 一个参数)
|
||||
# ============================================================
|
||||
# _translate_property_call - translate a property/static method call.
|
||||
# Resolve the call name like _translate_method_call; call_head/call_count is the prebuilt arg list.
|
||||
def _translate_property_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, call_head: mlirlite.Value*, call_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""翻译属性/静态方法调用,返回 call 结果 Value"""
|
||||
"""Translate a property/static method call, return the call result Value"""
|
||||
if pool is None or builder is None or class_name is None or method_name is None: return None
|
||||
|
||||
# 构造查找名: ClassName.method_name
|
||||
# Build the lookup name: ClassName.method_name
|
||||
lookup_name: str = pool.alloc(128)
|
||||
if lookup_name is None: return None
|
||||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
|
||||
|
||||
# 查找函数(精确/后缀匹配)
|
||||
# Look up the function (exact/suffix match)
|
||||
found_func: mlirlite.Function* = self.find_func_in_module(mod, lookup_name)
|
||||
|
||||
# 确定调用名
|
||||
# Determine the call name
|
||||
call_name: str = lookup_name
|
||||
if found_func is not None:
|
||||
found_name: str = mlirlite.function_get_name(found_func)
|
||||
if found_name is not None: call_name = found_name
|
||||
else:
|
||||
# 未找到:用类所属模块的 SHA1 构造调用名
|
||||
# Not found: build the call name from the class's owning module SHA1
|
||||
cls_sha1_pc: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
||||
if cls_sha1_pc is not None:
|
||||
mlen_pc: sizet = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
|
||||
@@ -64,7 +50,7 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
viperlib.snprintf(mangled_pc, mlen_pc, "%s.%s", cls_sha1_pc, lookup_name)
|
||||
call_name = mangled_pc
|
||||
|
||||
# 确定返回类型
|
||||
# Determine the return type
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
call_ret_ty: mlirlite.LLVMType* = i32_ty
|
||||
if found_func is not None:
|
||||
@@ -75,23 +61,21 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
if sp_ty1 is not None: call_ret_ty = sp_ty1
|
||||
else:
|
||||
cls_sha1_rt: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
||||
# 权威来源:vpi stub 返回注解缓存(方法定义处,不硬编码白名单)
|
||||
# Authoritative source: vpi stub return-annotation cache (in place, no hardcoded whitelist)
|
||||
vpi_ret_ty: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, cls_sha1_rt, class_name, method_name)
|
||||
if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty
|
||||
else:
|
||||
# 回退:依赖模块 text.ll 的 define 行解析(vpi 未覆盖的历史形态)
|
||||
# Fallback: parse the module text.ll define line (legacy form not covered by vpi)
|
||||
looked_up_ty: mlirlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cls_sha1_rt, class_name, method_name)
|
||||
if looked_up_ty is not None: call_ret_ty = looked_up_ty
|
||||
else:
|
||||
# Phase A 依赖未就绪(cls_sha1_rt None / 定义模块 text.ll 未生成):
|
||||
# 保持默认 i32(build_call(Void) 返回 None 会破坏表达式翻译)。
|
||||
# Phase A deps not ready (cls_sha1_rt None / the defining module's text.ll missing):
|
||||
# keep the i32 default (build_call(Void) returning None would break expression translation).
|
||||
if cls_sha1_rt is None or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0: call_ret_ty = mlirlite.Int32(pool)
|
||||
else: call_ret_ty = self._fail_method_ret_ty(method_name)
|
||||
|
||||
# 参数类型对齐:按目标函数声明的参数类型转换实参(与 _translate_method_call
|
||||
# 一致)。例如 from_long(10) 的 i32 字面量 → i64 形参(v: t.CLong),
|
||||
# 不转换则 call(i32) 与 declare(i64) 不匹配,llc 报参数类型错误。
|
||||
# 目标函数优先用已找到的 found_func,否则按 mangled 调用名查模块 declare。
|
||||
# Coerce args to the declared param types (same as _translate_method_call); target func: found_func,
|
||||
# else the mangled module declare. Otherwise call(i32) vs declare(i64) mismatches and llc errors.
|
||||
tgt_func_pc: mlirlite.Function* = found_func
|
||||
if tgt_func_pc is None: tgt_func_pc = self.find_func_in_module(mod, call_name)
|
||||
if tgt_func_pc is not None:
|
||||
@@ -103,7 +87,7 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
if param_ty_pc is not None and cur_arg_pc.Ty is not None:
|
||||
coerced_pc: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, cur_arg_pc, param_ty_pc)
|
||||
if coerced_pc is not None and coerced_pc != cur_arg_pc:
|
||||
# 保持链表完整性:新 Value 继承原节点的 Next,前驱指向新节点
|
||||
# Keep the list intact: the new Value inherits the node's Next; predecessor points to it
|
||||
mlirlite.value_set_next(coerced_pc, cur_arg_pc.Next)
|
||||
if prev_pc is None: call_head = coerced_pc
|
||||
else: mlirlite.value_set_next(prev_pc, coerced_pc)
|
||||
@@ -112,20 +96,14 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
cur_arg_pc = cur_arg_pc.Next
|
||||
cur_param_pc = mlirlite.param_get_next(cur_param_pc)
|
||||
|
||||
# 确保 declare 存在,然后调用
|
||||
# Ensure the declare exists, then call
|
||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count)
|
||||
return mlirlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
|
||||
# ============================================================
|
||||
# _resolve_obj_class_name — 解析对象表达式的类名(短名)
|
||||
#
|
||||
# 优先从变量注解(AnnotClassName)解析(如 "memhub.MemBuddy" → "MemBuddy"),
|
||||
# 失败时从 LLVM 类型解析(Ptr(Struct) → 类名)。
|
||||
# 供属性访问/赋值/删除识别 property 时使用。
|
||||
# ============================================================
|
||||
# _resolve_obj_class_name - resolve an object expression's class short name.
|
||||
# Prefer the variable annotation (AnnotClassName); fall back to the LLVM type (Ptr(Struct)).
|
||||
|
||||
# ============================================================
|
||||
def _resolve_obj_class_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, obj_node: ast.AST*) -> str:
|
||||
"""解析对象表达式的类名(短名),失败返回 None"""
|
||||
"""Resolve an object expression's class short name, None on failure"""
|
||||
if pool is None or trans is None or obj_node is None: return None
|
||||
if obj_node.kind() == ast.ASTKind.Name:
|
||||
obj_nm: ast.Name* = ast.Name*(obj_node)
|
||||
@@ -133,11 +111,8 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
var_entry: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, obj_nm.id)
|
||||
annot_val: str = self.Gen.VarGen.var_annot_class_name(var_entry)
|
||||
if annot_val is not None:
|
||||
# [D32] 取「模块限定名」末段时只按**括号外**的 '.' 切分。
|
||||
# 此前用 string.strrchr 在整个串上找最后一个 '.':泛型特化名
|
||||
# "list[e94ef9e139461915.AST*]" 的 '.' 落在 '[' 内 ⇒
|
||||
# 返回 "AST*]" 这种垃圾短名 ⇒ 后续按它查表必落空,
|
||||
# 进而可能被兜底成任意特化(.get() 误分派)。
|
||||
# [D32] Split the qualified name only on '.' outside brackets: the old strrchr over the
|
||||
# whole string picked the '.' inside "list[sha1.AST*]", yielding a bogus short name.
|
||||
n_av: sizet = string.strlen(annot_val)
|
||||
last_dot_av: sizet = n_av
|
||||
depth_av: int = 0
|
||||
@@ -151,93 +126,70 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
if last_dot_av < n_av: return annot_val + last_dot_av + 1
|
||||
return annot_val
|
||||
return None
|
||||
# ============================================================
|
||||
# _try_property_getter_attr — 尝试将 obj.attr 翻译为 property getter 调用
|
||||
#
|
||||
# 检查 obj 的类是否有 @property/@x.getter 的 attr 方法;
|
||||
# 若有则生成 call Class.attr(obj) 并返回调用结果 Value;否则返回 None。
|
||||
# ============================================================
|
||||
# _try_property_getter_attr - try to translate obj.attr into a property getter call.
|
||||
# If the class has an @property/@x.getter for attr, emit call Class.attr(obj); else return None.
|
||||
|
||||
# ============================================================
|
||||
def _try_property_getter_attr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, at_node: ast.Attribute*, obj_ptr: mlirlite.Value*, trans: HT.Translator*, fallback_class_name: str = None) -> mlirlite.Value*:
|
||||
"""尝试 property getter 属性访问,返回调用结果 Value;非 getter 返回 None
|
||||
|
||||
fallback_class_name: 对象表达式非 Name 时从 LLVM 类型解析出的类名兜底。
|
||||
"""
|
||||
"""Try a property getter access, return the call result Value; None if not a getter."""
|
||||
if at_node is None or at_node.attr is None or obj_ptr is None or trans is None: return None
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, at_node.value)
|
||||
if class_name is None: class_name = fallback_class_name
|
||||
if class_name is None: return None
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, at_node.attr)
|
||||
if meta & GenTypes.META_PROP_GETTER == 0: return None
|
||||
# 单参数调用: obj → getter
|
||||
# Single-arg call: obj -> getter
|
||||
mlirlite.value_set_next(obj_ptr, None)
|
||||
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans)
|
||||
# ============================================================
|
||||
# _try_property_setter — 尝试将 obj.attr = v 翻译为 property setter 调用
|
||||
#
|
||||
# 检查 obj 的类是否有 @x.setter 的 attr 方法;
|
||||
# 若有则生成 call Class.attr$set(obj, v),返回 1;否则返回 0。
|
||||
# ============================================================
|
||||
# _try_property_setter - try to translate obj.attr = v into a property setter call.
|
||||
# If the class has an @x.setter for attr, emit call Class.attr$set(obj, v) and return 1; else 0.
|
||||
|
||||
# ============================================================
|
||||
def _try_property_setter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, rhs_val: mlirlite.Value*, trans: HT.Translator*) -> int:
|
||||
"""尝试 property setter 赋值,返回 1=已处理(生成调用)"""
|
||||
"""Try a property setter assignment, return 1=handled (call emitted)"""
|
||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||
if target_attr is None or target_attr.attr is None or rhs_val is None: return 0
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
||||
if class_name is None: return 0
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
||||
if meta & GenTypes.META_PROP_SETTER == 0: return 0
|
||||
# 翻译对象值(self 参数)
|
||||
# Translate the object value (self argument)
|
||||
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
|
||||
if obj_val is None: return 0
|
||||
# 构建参数链表: obj → rhs
|
||||
# Build the argument list: obj -> rhs
|
||||
mlirlite.value_set_next(obj_val, None)
|
||||
mlirlite.value_set_next(rhs_val, None)
|
||||
mlirlite.value_set_next(obj_val, rhs_val)
|
||||
# setter 函数名带 $set 后缀
|
||||
# The setter function name carries the $set suffix
|
||||
setter_name_buf: str = pool.alloc(128)
|
||||
if setter_name_buf is None: return 0
|
||||
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
|
||||
self._translate_property_call(pool, builder, mod, class_name, setter_name_buf, obj_val, 2, trans)
|
||||
return 1
|
||||
# ============================================================
|
||||
# _try_property_deleter — 尝试将 del obj.attr 翻译为 property deleter 调用
|
||||
#
|
||||
# 检查 obj 的类是否有 @x.deleter 的 attr 方法;
|
||||
# 若有则生成 call Class.attr$del(obj),返回 1;否则返回 0。
|
||||
# ============================================================
|
||||
# _try_property_deleter - try to translate del obj.attr into a property deleter call.
|
||||
# If the class has an @x.deleter for attr, emit call Class.attr$del(obj) and return 1; else 0.
|
||||
|
||||
# ============================================================
|
||||
def _try_property_deleter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, trans: HT.Translator*) -> int:
|
||||
"""尝试 property deleter 删除,返回 1=已处理(生成调用)"""
|
||||
"""Try a property deleter, return 1=handled (call emitted)"""
|
||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||
if target_attr is None or target_attr.attr is None: return 0
|
||||
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
||||
if class_name is None: return 0
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
||||
if meta & GenTypes.META_PROP_DELETER == 0: return 0
|
||||
# 翻译对象值(self 参数)
|
||||
# Translate the object value (self argument)
|
||||
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
|
||||
if obj_val is None: return 0
|
||||
mlirlite.value_set_next(obj_val, None)
|
||||
# deleter 函数名带 $del 后缀
|
||||
# The deleter function name carries the $del suffix
|
||||
del_name_buf: str = pool.alloc(128)
|
||||
if del_name_buf is None: return 0
|
||||
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
|
||||
self._translate_property_call(pool, builder, mod, class_name, del_name_buf, obj_val, 1, trans)
|
||||
return 1
|
||||
# ============================================================
|
||||
# _translate_static_class_call — 翻译 ClassName.method(args) 静态/类方法调用
|
||||
#
|
||||
# @staticmethod: 直接 call Class.method(args...),无 self
|
||||
# @classmethod: 在参数前插入栈上分配的类实例指针(与 TPC 一致)
|
||||
# ============================================================
|
||||
# _translate_static_class_call - translate ClassName.method(args) for static/class methods.
|
||||
# @staticmethod: call Class.method(args...). @classmethod: prepend a stack-allocated class pointer.
|
||||
|
||||
# ============================================================
|
||||
def _translate_static_class_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""翻译类名静态/类方法调用,返回 call 结果 Value;非静态/类方法返回 None"""
|
||||
"""Translate a ClassName static/class method call, return the call result Value; None otherwise"""
|
||||
if pool is None or builder is None or mod is None or trans is None: return None
|
||||
if class_name is None or method_name is None or cl is None: return None
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, method_name)
|
||||
@@ -247,11 +199,11 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
if meta & GenTypes.META_CLASS != 0: is_class = 1
|
||||
if is_static == 0 and is_class == 0: return None
|
||||
|
||||
# 构建参数链表
|
||||
# Build the argument list
|
||||
head: mlirlite.Value* = None
|
||||
total_count: int = 0
|
||||
|
||||
# @classmethod: 插入栈上分配的类实例指针作为 cls 参数
|
||||
# @classmethod: prepend a stack-allocated class pointer as the cls argument
|
||||
if is_class != 0:
|
||||
struct_ty_cc: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
|
||||
if struct_ty_cc is None: return None
|
||||
@@ -261,7 +213,7 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
head = cls_ptr
|
||||
total_count = 1
|
||||
|
||||
# 翻译调用参数
|
||||
# Translate the call arguments
|
||||
cargs: list[ast.AST*]* = cl.args
|
||||
can: sizet = 0
|
||||
if cargs is not None: can = len(cargs)
|
||||
@@ -280,32 +232,18 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
tail = arg_val
|
||||
total_count += 1
|
||||
|
||||
# 零参 @staticmethod(如 PyListObj.new()):head 保持 None 是合法的
|
||||
# 无参调用,照常走 _translate_property_call(SHA1 前缀名 + 精确返回类型)。
|
||||
# 若此处 return None,调用方回退通用路径把方法名当裸函数解析,生成
|
||||
# call i32 @new() → 链接 undefined reference(CPythonTest 根因)。
|
||||
# @classmethod 恒有 cls_ptr(head 非 None),不受影响。
|
||||
# Zero-arg @staticmethod (e.g. PyListObj.new()): head=None is a valid no-arg call, so still route
|
||||
# through _translate_property_call; returning None would fall back to bare-name lookup and fail.
|
||||
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans)
|
||||
# ============================================================
|
||||
# _is_list_builtin_method - 判断是否是 list 泛型内置容器方法
|
||||
#
|
||||
# list[T] 是内置泛型容器(_list.vp),其方法(append/get/set/pop/clear/
|
||||
# __iter__/__len__/__getitem__/__setitem__)由 translate_call 的 list
|
||||
# 内置分支内联 GEP 处理,不注册为方法函数。若 obj.method() 检测把
|
||||
# list 特化 struct(如 list[AST*])误当普通结构体走
|
||||
# _translate_method_call,会查不到 append 返回类型 → fail-fast
|
||||
# (NewAstTest body.append 根因)。此判断用于跳过 method_call 路径。
|
||||
#
|
||||
# 返回: 1=list 内置方法(应落到 list 内置分支),0=普通方法
|
||||
# ============================================================
|
||||
# _is_list_builtin_method - test whether (class_name, method_name) is a list generic builtin.
|
||||
# list[T] methods are inlined by translate_call's list branch, not registered as functions.
|
||||
|
||||
# ============================================================
|
||||
def _is_list_builtin_method(self, class_name: str, method_name: str) -> int:
|
||||
"""判断 (class_name, method_name) 是否是 list 泛型内置方法"""
|
||||
"""Test whether (class_name, method_name) is a list generic builtin method"""
|
||||
if class_name is None or method_name is None: return 0
|
||||
# 类名必须以 "list" 开头(list / list[AST*] 特化名)
|
||||
# Class name must start with "list" (list / list[AST*] specializations)
|
||||
if string.strncmp(class_name, "list", 4) != 0: return 0
|
||||
# 方法名必须是 list 内置方法集合
|
||||
# Method name must be one of the list builtin methods
|
||||
if string.strcmp(method_name, "append") == 0: return 1
|
||||
if string.strcmp(method_name, "get") == 0: return 1
|
||||
if string.strcmp(method_name, "set") == 0: return 1
|
||||
@@ -317,22 +255,18 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
if string.strcmp(method_name, "__setitem__") == 0: return 1
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _strip_module_sha1_prefix - 去跨模块特化名的 sha1 前缀
|
||||
#
|
||||
# 接收者实际结构体名常带 "{16位小写hex}." 前缀(如
|
||||
# "e94ef9e139461915.list[ast.ASTptr]");判定基名 | 特化名前必须先剥离。
|
||||
# 前缀长度判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛)。
|
||||
# ============================================================
|
||||
# _strip_module_sha1_prefix - strip the cross-module specialization sha1 prefix.
|
||||
# Receiver struct names often carry a "{16 hex}." prefix; use GenApi.sha1_prefix_len (C10).
|
||||
def _strip_module_sha1_prefix(self, name: str) -> str:
|
||||
"""去除 "{16hex}." 前缀(无前缀则原样返回;返回原串内部子串)"""
|
||||
"""Strip the "{16hex}." prefix (return the input unchanged if absent; returns a substring of the input)"""
|
||||
if name is None: return None
|
||||
pfx: int = GenApi.sha1_prefix_len(name)
|
||||
return name + pfx if pfx != 0 else name
|
||||
|
||||
# _resolve_generic_receiver_name:解析接收者实际泛型特化名(IR 真值)——接收者实际 LLVM 结构体类型名比可能退化的注解/注册名可靠,故泛型判定以此为准:去 sha1 前缀取 '[' 前基名,基名是已注册泛型模板时含类型实参 ⇒ 返回实际特化名,无类型实参 ⇒ fail-fast(D25);基名非泛型模板或实际类型不可得 ⇒ 返回 None(调用方各自回退)。与 CallGenCall._translate_method_call 的 D25 分派修复共用本函数(唯一口径)
|
||||
# _resolve_generic_receiver_name - resolve the receiver's actual generic specialization name (IR truth).
|
||||
# Strip the sha1 prefix, take the base before '['; bare generic base => fail-fast (D25), else None.
|
||||
def _resolve_generic_receiver_name(self, pool: memhub.MemBuddy*, struct_ty: mlirlite.LLVMType*, method_name: str, cl: ast.Call*) -> str:
|
||||
"""返回实际泛型特化名(去 sha1)或 None;裸泛型基类 fail-fast"""
|
||||
"""Return the actual generic specialization name (sha1 stripped) or None; a bare generic base fails fast"""
|
||||
if struct_ty is None: return None
|
||||
ty_nm: str = mlirlite.get_struct_name(struct_ty)
|
||||
if ty_nm is None or ty_nm[0] == '\0': return None
|
||||
@@ -346,24 +280,25 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
if base_nm is None: return None
|
||||
string.strncpy(base_nm, short_nm, bare_len)
|
||||
base_nm[bare_len] = '\0'
|
||||
# 基名非已注册泛型模板(普通类接收者)⇒ 非泛型,交由调用方原口径处理
|
||||
# Base is not a registered generic template (plain class receiver) => not generic; return None
|
||||
if self.Gen.ClassGenGeneric._find_generic_template(base_nm) is None: return None
|
||||
if lb is not None: return short_nm
|
||||
# 裸泛型基类(无类型实参):接收者元素类型未知 → fail-fast(D25 口径)
|
||||
# Bare generic base (no type args): element type unknown -> fail-fast (D25)
|
||||
self.Gen.TypeGenErr.fatal_error(cl, f"方法 '{method_name}' 的接收者实际结构体类型是裸泛型基类 " f"'{short_nm}*'(丢失类型实参),元素类型未知;" f"禁止静默按默认元素类型处理(元素按 i32 处理会截断指针)," f"请让该变量的注解/类型带上实参(如 list[ast.AST*])", "TypeError")
|
||||
return None
|
||||
|
||||
# _is_list_builtin_receiver:基于接收者实际类型判定 list 内建方法,判据与 _resolve_generic_receiver_name 同源——优先采用接收者实际 LLVM 结构体类型名(去 sha1 判基名),不可得时回退注解名/注册名判定。返回 1=list 内置方法 / 0=非
|
||||
# _is_list_builtin_receiver - test list builtin methods from the receiver's actual type.
|
||||
# Prefer the actual LLVM struct name (sha1 stripped); fall back to the annotation/registered name.
|
||||
def _is_list_builtin_receiver(self, pool: memhub.MemBuddy*, recv_val: mlirlite.Value*, fallback_name: str, method_name: str, cl: ast.Call*) -> int:
|
||||
"""基于接收者实际类型判定 list 内建方法(实际类型不可得回退注解名)"""
|
||||
"""Test list builtin methods from the receiver's actual type (fall back to the annotation name if unavailable)"""
|
||||
sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(recv_val)
|
||||
if sty is not None:
|
||||
ty_nm: str = mlirlite.get_struct_name(sty)
|
||||
if ty_nm is not None and ty_nm[0] != '\0':
|
||||
# 泛型模板接收者:裸基类 fail-fast;含实参用实际特化名
|
||||
# Generic template receiver: bare base fail-fast; with type args use the actual specialization
|
||||
act_nm: str = self._resolve_generic_receiver_name(pool, sty, method_name, cl)
|
||||
if act_nm is not None: return self._is_list_builtin_method(act_nm, method_name)
|
||||
# 非泛型模板接收者:用去 sha1 后的实际短名判定(不复用退化注解名)
|
||||
# Non-generic template receiver: judge by the actual short name (sha1 stripped)
|
||||
return self._is_list_builtin_method(self._strip_module_sha1_prefix(ty_nm), method_name)
|
||||
# 实际类型不可得 → 回退注解名/注册名判定(信息缺失下的既有行为)
|
||||
# Actual type unavailable -> fall back to the annotation/registered name
|
||||
return self._is_list_builtin_method(fallback_name, method_name)
|
||||
@@ -9,105 +9,76 @@ import lib.core.Gen.Translator as HT
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenVa - CallGenMixin 拆分(阶段 B):va_arg 变长参数(start/arg/end / 变长调用提升)
|
||||
# CallGenVa - CallGenMixin split (stage B): va_arg variadics (start/arg/end / variadic call promotion)
|
||||
# ============================================================
|
||||
novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
"""va_arg 变长参数(start/arg/end / 变长调用提升)"""
|
||||
"""va_arg variadics (start/arg/end and variadic call promotion)."""
|
||||
|
||||
# ============================================================
|
||||
# _func_has_eh_params - 被调函数签名是否含 try-except EH 输出参数
|
||||
#
|
||||
# EH 参数(%__eh_msg_out__/__eh_code_out__)由 translate_function_def/
|
||||
# _translate_method 在含 raise 的函数签名末尾注入(main 除外)。
|
||||
# _func_has_eh_params - whether callee signature has try-except EH out params (main excluded)
|
||||
# ============================================================
|
||||
|
||||
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, arg_node: ast.AST*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""获取 va_list 变量的 alloca 指针(不 load)
|
||||
|
||||
va_arg/va_start/va_end 需要的是 va_list 对象的地址(alloca 指针),
|
||||
而非 va_list 的值。若 arg_node 是 Name,查符号表获取 alloca;
|
||||
否则用 translate_value 翻译(调用者负责确保传入的是指针)。
|
||||
"""
|
||||
"""Return the alloca pointer of a va_list variable without loading it."""
|
||||
if arg_node is None: return None
|
||||
# Name 节点:直接查符号表获取 alloca 指针(不 load)
|
||||
# Name node: look up the alloca pointer from the symbol table (no load)
|
||||
if arg_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(arg_node)
|
||||
if nm.id is not None:
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is not None: return alloca
|
||||
# 回退:translate_value(可能返回 load 后的值,仅适用于已是指针的情况)
|
||||
# Fallback: translate_value (may return a loaded value; only valid when already a pointer)
|
||||
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, funcs_ptr, func_count, trans)
|
||||
def _resolve_va_arg_type(self, pool: memhub.MemBuddy*, type_node: ast.AST*, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||||
"""解析 va_arg/arg 的类型参数为 LLVMType
|
||||
|
||||
支持的类型表达式:
|
||||
int / int32 → i32
|
||||
int64 → i64
|
||||
double → double
|
||||
t.CPtr / str → i8*
|
||||
str / bytes → i8*
|
||||
无类型参数时默认 i32(vsnprintf 中 va_arg(ap) 常用于读取 i32)
|
||||
"""
|
||||
"""Resolve the va_arg/arg type argument to an LLVMType, defaulting to i32."""
|
||||
if type_node is None: return mlirlite.Int32(pool)
|
||||
# 使用 HandlesType.resolve_annotation_type 解析类型表达式
|
||||
# Resolve the type expression via HandlesType.resolve_annotation_type
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if resolved is not None: return resolved
|
||||
# 类型节点存在但解析失败:fail-fast,绝不回退 i32(会按错宽度读变参槽位)
|
||||
# Type node present but unresolvable: fail-fast, never fall back to i32 (would read the vararg slot at the wrong width)
|
||||
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg 类型解析失败", "TypeError")
|
||||
return mlirlite.Int32(pool)
|
||||
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""va_arg(ap, type) / va_arg(ap) → %N = va_arg <ap_ty> <ap>, <type>
|
||||
|
||||
生成 LLVM va_arg 指令,从 va_list 读取下一个变长参数。
|
||||
"""
|
||||
"""Emit a va_arg instruction reading the next variadic argument from the va_list."""
|
||||
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# 第一个参数:va_list(求值为 i8* 地址值,对齐 TPC _HandleVaArgLlvm)
|
||||
# 当前函数 *args:translate_value 经 vararg 名映射直接返回 va_list 缓冲区地址;
|
||||
# 转发场景(sprintf → vsnprintf 的 ap 参数):translate_value load 参数得到地址值。
|
||||
# First arg: va_list (evaluate to an i8* address; aligned with TPC _HandleVaArgLlvm). In own *args
|
||||
# translate_value returns the buffer address; in forwarding (sprintf -> vsnprintf) it loads the param.
|
||||
ap_node: ast.AST* = cargs.get(0)
|
||||
ap_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ap_node, None, 0, trans)
|
||||
if ap_val is None: return mlirlite.const_int32(pool, 0)
|
||||
ap_ptr: mlirlite.Value* = ap_val
|
||||
|
||||
# 第二个参数:目标类型(可选,默认 i32)
|
||||
# Second arg: target type (optional, defaults to i32)
|
||||
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if can >= 2:
|
||||
type_node: ast.AST* = cargs.get(1)
|
||||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||||
|
||||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(对齐 TPC emit_va_arg L72-73),
|
||||
# 再 trunc 回目标宽度——否则返回 8 字节含高位垃圾,%d/%i 语义错误
|
||||
# (viperlib vsnprintf %d 非 long 分支 `int64(va_arg(ap, int))`
|
||||
# 编译成直接 va_arg i64,高 32 位未定义 → 负数当大正数 A6 根因)。
|
||||
# Win64 vararg slots are 8 bytes: read sub-64-bit ints as i64 (TPC emit_va_arg L72-73)
|
||||
# then trunc to target width; otherwise high bits are garbage (%d/%i wrong, A6 root cause).
|
||||
read_ty: mlirlite.LLVMType* = arg_ty
|
||||
abits_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||||
if abits_va != 0 and abits_va < 64:
|
||||
read_ty = mlirlite.Int64(pool)
|
||||
va_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, ap_ptr, read_ty)
|
||||
if va_raw is None: return mlirlite.const_int32(pool, 0)
|
||||
# 8 字节槽 → 目标宽度(低 bits 位);调用方 int64(...) 会再 sext/zext
|
||||
# 8-byte slot -> target width (low bits); caller int64(...) re-applies sext/zext
|
||||
return mlirlite.build_trunc(builder, va_raw, arg_ty)
|
||||
|
||||
# 生成 va_arg 指令
|
||||
# Emit the va_arg instruction
|
||||
return mlirlite.build_va_arg(builder, ap_ptr, read_ty)
|
||||
def _translate_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""arg(type) → %N = va_arg <va_list_ty> <trans._va_list_ptr>, <type>
|
||||
|
||||
使用当前函数的隐式 va_list(trans._va_list_ptr),
|
||||
简化 *args 函数体内的 va_arg 调用。
|
||||
"""
|
||||
"""Emit va_arg using the current function's implicit va_list (trans._va_list_ptr)."""
|
||||
if trans._va_list_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# 第一个参数:目标类型(可选,默认 i32)
|
||||
# First arg: target type (optional, defaults to i32)
|
||||
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if can >= 1 and cargs is not None:
|
||||
type_node: ast.AST* = cargs.get(0)
|
||||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||||
|
||||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(与 _translate_va_arg_sugar 一致),
|
||||
# 再 trunc 回目标宽度(%d/%i 高位垃圾 A6 修复,见 _translate_va_arg_sugar)
|
||||
# Win64 vararg slots are 8 bytes: read sub-64-bit ints as i64 (same as _translate_va_arg_sugar),
|
||||
# then trunc to target width (%d/%i high-bit garbage fix, see _translate_va_arg_sugar).
|
||||
read_ty: mlirlite.LLVMType* = arg_ty
|
||||
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||||
if abits_arg != 0 and abits_arg < 64:
|
||||
@@ -116,21 +87,18 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
if arg_raw is None: return mlirlite.const_int32(pool, 0)
|
||||
return mlirlite.build_trunc(builder, arg_raw, arg_ty)
|
||||
|
||||
# 生成 va_arg 指令(使用 trans._va_list_ptr 作为 va_list 地址)
|
||||
# Emit va_arg, using trans._va_list_ptr as the va_list address
|
||||
return mlirlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
|
||||
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""va_start(ap, last_arg) → call void @llvm.va_start(i8* %ap)
|
||||
|
||||
初始化 va_list。第二个参数 last_arg 在 LLVM intrinsic 中不使用(C 宏兼容)。
|
||||
"""
|
||||
"""Emit llvm.va_start to initialize the va_list (the second C-ABI argument is unused)."""
|
||||
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# 第一个参数:va_list 指针(获取 alloca,不 load)
|
||||
# First arg: va_list pointer (get its alloca, no load)
|
||||
ap_node: ast.AST* = cargs.get(0)
|
||||
ap_ptr: mlirlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||||
if ap_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# bitcast 到 i8*(@llvm.va_start 接受 i8*)
|
||||
# bitcast to i8* (@llvm.va_start takes i8*)
|
||||
i8_ty_vs: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty_vs: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_vs)
|
||||
ap_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
|
||||
@@ -140,18 +108,15 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
mlirlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
|
||||
return mlirlite.const_int32(pool, 0)
|
||||
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""va_end(ap) → call void @llvm.va_end(i8* %ap)
|
||||
|
||||
清理 va_list。在大多数平台上是 no-op,但调用它是良好的 C ABI 习惯。
|
||||
"""
|
||||
"""Emit llvm.va_end to clean up the va_list (a no-op on most platforms)."""
|
||||
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# 第一个参数:va_list 指针(获取 alloca,不 load)
|
||||
# First arg: va_list pointer (get its alloca, no load)
|
||||
ap_node: ast.AST* = cargs.get(0)
|
||||
ap_ptr: mlirlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||||
if ap_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||
|
||||
# bitcast 到 i8*(@llvm.va_end 接受 i8*)
|
||||
# bitcast to i8* (@llvm.va_end takes i8*)
|
||||
i8_ty_ve: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty_ve: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_ve)
|
||||
ap_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
|
||||
@@ -161,7 +126,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
mlirlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
|
||||
return mlirlite.const_int32(pool, 0)
|
||||
def _get_func_fixed_param_count(self, func: mlirlite.Function*) -> int:
|
||||
"""获取函数的固定参数数量(遍历 Params 链表)"""
|
||||
"""Return the count of fixed params by walking the Params list."""
|
||||
if func is None: return 0
|
||||
count: int = 0
|
||||
param: mlirlite.Param* = mlirlite.function_get_param_head(func)
|
||||
@@ -170,13 +135,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
param = mlirlite.param_get_next(param)
|
||||
return count
|
||||
def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int:
|
||||
"""已知变参 C 库函数固定参数个数(-1=未知)
|
||||
|
||||
跨模块 stub 未注入时 find_func_in_module 返回 None(CallGenMixin
|
||||
注释:stubs 在翻译后才注入,翻译时跨模块函数 found_func 恒 None),
|
||||
无法从函数对象获取 IsVarArg/固定参数个数。这些 t.CExport 变参函数
|
||||
(viperlib 定义)签名固定,按表回退(对齐 TPC _C_LIB_FUNCS 机制)。
|
||||
"""
|
||||
"""Return the fixed-param count for known variadic C functions (-1 if unknown)."""
|
||||
if name is None: return -1
|
||||
if string.strcmp(name, "snprintf") == 0: return 3
|
||||
if string.strcmp(name, "sprintf") == 0: return 2
|
||||
@@ -184,11 +143,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
return -1
|
||||
|
||||
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy*, name: str, idx: int) -> mlirlite.LLVMType*:
|
||||
"""已知变参 C 库函数第 idx 个固定参数类型(变参位置/未知返回 None)
|
||||
|
||||
固定参数类型是 C ABI 硬约束(snprintf 的 size 为 i64),stub 未注入
|
||||
时按表 coerce,避免 call(i32 1024) 与 declare(i64 size) 不匹配。
|
||||
"""
|
||||
"""Return the type of the idx-th fixed param of a known variadic C function."""
|
||||
if pool is None or name is None or idx < 0: return None
|
||||
i8_ptr_kv: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
i64_kv: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
@@ -203,29 +158,22 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
return None
|
||||
return None
|
||||
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, call_name: str, head: mlirlite.Value*, can: sizet, arg_nodes: list[ast.AST*]* = None, self_offset: int = 0, trans: HT.Translator* = None) -> int:
|
||||
"""检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升
|
||||
|
||||
返回: is_variadic (1=变长调用, 0=普通调用)
|
||||
副作用: 修改 head 链表中超过固定参数数量的节点(C ABI 提升)
|
||||
|
||||
arg_nodes: 实参 AST 链表(不含 self),用于 i8/i16 提升的无符号性判断
|
||||
self_offset: Value 链中 self 占用的偏移(方法调用=1,普通函数=0)
|
||||
trans: 供 _node_type_unsigned 查询变量注解
|
||||
"""
|
||||
"""Promote variadic args of a callee to the C ABI; returns 1 if variadic else 0."""
|
||||
if mod is None or call_name is None or head is None: return 0
|
||||
# 查找函数对象
|
||||
# Look up the function object
|
||||
func: mlirlite.Function* = self.find_func_in_module(mod, call_name)
|
||||
fixed_count: int = -1
|
||||
if func is not None and func.IsVarArg == 1: fixed_count = self._get_func_fixed_param_count(func)
|
||||
else:
|
||||
# 两种情况回退已知变参 C 库函数签名表(snprintf 等):func 为 None(跨模块 stub 未注入,翻译时 found_func 恒 None);func 存在但 IsVarArg=0(该 declare 由 _ensure_method_declare 按实参类型临时创建,无 ... 标记,不代表真实签名)。否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 → va_arg(i64) 高 4 字节读残留栈垃圾
|
||||
# Fall back to the known variadic C-lib signature table (snprintf etc.) when func is None (stub not yet injected)
|
||||
# or IsVarArg=0 (ad hoc declare); otherwise i32 varargs aren't promoted and the Win64 8-byte slot keeps only 4 bytes.
|
||||
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
|
||||
if fixed_count < 0: return 0
|
||||
|
||||
# 无变长参数(实参数量 <= 固定参数数量),仍需 is_variadic=1
|
||||
# No varargs (arg count <= fixed count) but still report is_variadic=1
|
||||
if can <= fixed_count: return 1
|
||||
|
||||
# 遍历到第 fixed_count 个节点(固定参数的最后一个)
|
||||
# Walk to the fixed_count-th node (the last fixed param)
|
||||
cur: mlirlite.Value* = head
|
||||
prev: mlirlite.Value* = None
|
||||
idx: int = 0
|
||||
@@ -234,8 +182,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
cur = cur.Next
|
||||
idx += 1
|
||||
|
||||
# cur 指向第 fixed_count+1 个节点(第一个变长参数)
|
||||
# 对变长参数进行 C ABI 提升
|
||||
# cur now points at the first vararg; apply C ABI promotion to all varargs
|
||||
i32_ty_promote: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
double_ty_promote: mlirlite.LLVMType* = mlirlite.Double(pool)
|
||||
v_idx: int = fixed_count
|
||||
@@ -245,9 +192,8 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
# C ABI integer promotion: i8/i16 → i32
|
||||
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty)
|
||||
if arg_bits != 0 and arg_bits < 32:
|
||||
# 无符号整型(uint8/CUInt16T 等)→ zext;有符号/未知 → sext。
|
||||
# 修复: printf("%02x", buf[i]) 中 buf[i] 是 CUInt8T,sext 得
|
||||
# 0xFFFFFFF3 打印 fffffff3(哈希摘要输出垃圾),应 zext 得 0xF3。
|
||||
# Unsigned ints (uint8/CUInt16T etc.) -> zext; signed/unknown -> sext.
|
||||
# Fix: printf("%02x", buf[i]) with CUInt8T must zext to 0xF3, not sext to 0xFFFFFFF3.
|
||||
is_u_va: int = 0
|
||||
if arg_nodes is not None and trans is not None:
|
||||
ast_idx_va: int = v_idx - self_offset
|
||||
@@ -257,11 +203,8 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0: is_u_va = 1
|
||||
if is_u_va != 0: promoted = mlirlite.build_zext(builder, cur, i32_ty_promote)
|
||||
else:
|
||||
# is_u_va 判断失败(arg_nodes/trans 缺失或变量注解查不到)时,
|
||||
# i8 按系统编程惯例默认 zext(对齐 should_zext_widen 的
|
||||
# "i8 → zext(系统编程中 i8 几乎总是无符号字节)"策略),
|
||||
# 否则 printf("%02x", buf[i]) 中 uint8 字节被 sext 成
|
||||
# 0xFFFFFFF3,哈希摘要输出 ffffffXX 垃圾。
|
||||
# If is_u_va is unknown (missing arg_nodes/trans or no annotation), default i8 to zext
|
||||
# (aligned with should_zext_widen); sext would print uint8 bytes as ffffffXX garbage.
|
||||
if self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty) == 8: promoted = mlirlite.build_zext(builder, cur, i32_ty_promote)
|
||||
else: promoted = self.Gen.ExprGenBase.coerce_to_type(builder, cur, i32_ty_promote, None)
|
||||
if promoted is not None:
|
||||
@@ -274,11 +217,8 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||
if promoted is not None:
|
||||
promoted.Next = next_node
|
||||
if prev is not None: prev.Next = promoted
|
||||
# Win64 变参槽位 8 字节:i8/i16/i32 → i64
|
||||
# TPV va_arg 读取端统一按 i64 读(_translate_va_arg_sugar),
|
||||
# 调用方必须写满 8 字节槽,否则高 4 字节残留旧栈数据,%d 读到
|
||||
# 0x7ff700000052 类垃圾(clean 日志根因;对齐 CallGenMixin
|
||||
# L798-811 的 found_func.IsVarArg 提升)。
|
||||
# Win64 vararg slots are 8 bytes: i8/i16/i32 -> i64. The va_arg read side always reads i64
|
||||
# (_translate_va_arg_sugar), so callers must fill all 8 bytes or stale stack data remains.
|
||||
i64_ty_promote: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
|
||||
if pb_va != 0 and pb_va < 64:
|
||||
|
||||
@@ -11,17 +11,16 @@ import lib.core.Gen.GenTypes as GenTypes
|
||||
from . import CallGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# CallGenVt - CallGenMixin 拆分(阶段 B):vtable 槽位写入
|
||||
# CallGenVt - CallGenMixin split (phase B): vtable slot writing
|
||||
# ============================================================
|
||||
novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
"""vtable 槽位写入"""
|
||||
"""vtable slot writing."""
|
||||
|
||||
# _write_vtable_slot / _write_vtable_attr:把模块级函数指针写入对象 vtable 的方法槽位(devfs 驱动注册模式,对齐 TPC HandlesAssign.py L905-949 写时复制)——vtable 方法不是数据字段,get_attribute_ptr 查不到;对象 vtable 仍指向类默认 vtable 时先复制一份(internal 全局 + llvm.memcpy)并让对象指向副本,再 GEP method_index 槽位 store i8*。多次写入互不覆盖
|
||||
# _write_vtable_slot / _write_vtable_attr: write a module-level function pointer into an object vtable slot via copy-on-write (vtable slots are not data fields, so get_attribute_ptr cannot find them).
|
||||
|
||||
# ============================================================
|
||||
def _write_vtable_slot(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, obj_ptr: mlirlite.Value*, class_name: str, method_index: int, func_val: mlirlite.Value*, trans: HT.Translator*) -> int:
|
||||
"""生成 vtable 槽位写入(写时复制),成功返回 1"""
|
||||
"""Emit a copy-on-write vtable slot write."""
|
||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||
if obj_ptr is None or class_name is None or func_val is None: return 0
|
||||
if method_index < 0 or trans._cur_func is None: return 0
|
||||
@@ -33,9 +32,7 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
if i8_ptr_ptr_ty is None: return 0
|
||||
method_count: int = self.Gen.StructGen.get_vtable_method_count(class_name)
|
||||
if method_count <= 0: return 0
|
||||
# 构造 vtable 类型 { i8*, i8*, ... }
|
||||
# vtable 类型的字段链表是 mlirlite.ParamNode(new_param_node / paramnode_set_next /
|
||||
# Struct 形参均为 rawptr[ParamNode])——原先误注解为 Function*。
|
||||
# Build the vtable type { i8*, i8*, ... }; its field list uses mlirlite.ParamNode, not Function*.
|
||||
vt_first: mlirlite.ParamNode* = None
|
||||
vt_prev: mlirlite.ParamNode* = None
|
||||
for vi in range(method_count):
|
||||
@@ -46,18 +43,17 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
vt_prev = pn
|
||||
vtable_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, vt_first, method_count, None)
|
||||
if vtable_ty is None: return 0
|
||||
# 对象 vtable 槽位(对象头 → i8**,vtable 指针总是第一个字段)
|
||||
# Object vtable slot (object header -> i8**; the vtable pointer is always the first field).
|
||||
vtable_slot: mlirlite.Value* = mlirlite.build_bitcast(builder, obj_ptr, i8_ptr_ptr_ty)
|
||||
if vtable_slot is None: return 0
|
||||
vtable_ptr: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
||||
if vtable_ptr is None: return 0
|
||||
# 类默认 vtable 地址(比较用)
|
||||
# Address of the class default vtable (for comparison).
|
||||
class_vt: mlirlite.Value* = self.Gen.CallGenLeafKit._make_vtable_ref_i8(pool, builder, mod, class_name, trans)
|
||||
if class_vt is None: return 0
|
||||
cmp_vt: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, vtable_ptr, class_vt)
|
||||
if cmp_vt is None: return 0
|
||||
# 块名唯一化(create_block 不自动编号;同函数多次 vtable 写入(如
|
||||
# devfs open/close/read/write 连续赋值)同名 label 冲突)
|
||||
# Unique block names: create_block does not auto-number, so repeated vtable writes in one function would collide.
|
||||
vtc3: int = trans._label_counter
|
||||
trans._label_counter = vtc3 + 1
|
||||
vt_bb_buf3: str = pool.alloc(64)
|
||||
@@ -68,7 +64,7 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
vt_skip_bb: rawptr[mlirlite.BasicBlock] = mlirlite.create_block(pool, trans._cur_func, vt_bb_buf3)
|
||||
if vt_copy_bb is None or vt_skip_bb is None: return 0
|
||||
mlirlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
|
||||
# copy 块:创建 internal 副本全局(zeroinit)+ memcpy + 对象指向副本
|
||||
# copy block: create internal copy global (zeroinit) + memcpy + repoint object at it.
|
||||
mlirlite.position_at_end(builder, vt_copy_bb)
|
||||
vtc: int = trans._label_counter
|
||||
trans._label_counter = vtc + 1
|
||||
@@ -80,10 +76,7 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
mlirlite.module_add_global(mod, vcopy_gv)
|
||||
vcopy_gv.Linkage = None
|
||||
vcopy_gv.Initializer = "zeroinitializer"
|
||||
# 引用名必须带 @ 前缀:SSAValue 名原样输出,裸名
|
||||
# devfs_file.vtable.copy.71 无 @ → llc "expected value token"
|
||||
# (devfs vtable.copy 根因;全局定义 @name 是 new_global_variable
|
||||
# 输出时加的,引用侧不自动加)
|
||||
# Reference names need the @ prefix: SSAValue emits the name verbatim (unlike new_global_variable, it adds no @).
|
||||
vcopy_ref_nm: str = pool.alloc(272)
|
||||
if vcopy_ref_nm is None: vcopy_ref_nm = vcopy_buf
|
||||
else: viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
|
||||
@@ -91,9 +84,7 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
if vcopy_ref is not None:
|
||||
vcopy_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, vcopy_ref, i8_ptr_ty)
|
||||
if vcopy_i8 is not None:
|
||||
# ConstInt 的 name 必须是数字文本(如 "56"),不能是
|
||||
# 标识符 "vtsz"——参数经 Name 原样输出,裸名 vtsz 无 %
|
||||
# → llc "expected value token"(devfs memcpy 根因)
|
||||
# ConstInt's name must be numeric text (e.g. "56"); an identifier would emit verbatim without % and break llc.
|
||||
vtsz_buf: str = pool.alloc(32)
|
||||
if vtsz_buf is not None:
|
||||
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
|
||||
@@ -101,7 +92,7 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
if vt_size is not None: self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, vcopy_i8, class_vt, vt_size)
|
||||
mlirlite.build_store(builder, vcopy_i8, vtable_slot)
|
||||
mlirlite.build_br(builder, vt_skip_bb)
|
||||
# skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*)
|
||||
# skip block: reload vtable -> GEP the slot -> store the function pointer (i8*).
|
||||
mlirlite.position_at_end(builder, vt_skip_bb)
|
||||
vt_ptr2: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
||||
if vt_ptr2 is None: return 0
|
||||
@@ -113,12 +104,12 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
if fptr_i8 is not None: mlirlite.build_store(builder, fptr_i8, vt_slotp)
|
||||
return 1
|
||||
def _write_vtable_attr(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, rhs_val: mlirlite.Value*, trans: HT.Translator*) -> int:
|
||||
"""属性写入 obj.method = func:检测 vtable 方法并生成槽位写入(成功返回 1)"""
|
||||
"""Handle obj.method = func writes that target a vtable slot."""
|
||||
if node is None or builder is None or pool is None or mod is None: return 0
|
||||
if rhs_val is None or trans is None: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at is None or at.value is None or at.attr is None: return 0
|
||||
# 解析类名(AnnotClassName;去模块前缀)
|
||||
# Resolve the class name (AnnotClassName; strip the module prefix).
|
||||
class_nm_v: str = None
|
||||
if at.value.kind() == ast.ASTKind.Name:
|
||||
nm_v: ast.Name* = ast.Name*(at.value)
|
||||
@@ -126,7 +117,7 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
ve_v: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_v.id)
|
||||
if ve_v is not None: class_nm_v = self.Gen.VarGen.var_annot_class_name(ve_v)
|
||||
elif at.value.kind() == ast.ASTKind.Attribute:
|
||||
# 跨 Mixin:_resolve_attr_field_class 定义在 ExprGenAttr,经 self.Gen 调用
|
||||
# Cross-mixin: _resolve_attr_field_class lives in ExprGenAttr, called via self.Gen.
|
||||
class_nm_v = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, at.value)
|
||||
if class_nm_v is None: return 0
|
||||
fd_dot_v: str = string.strrchr(class_nm_v, '.')
|
||||
@@ -134,12 +125,12 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
if self.Gen.StructGen.get_struct_type(class_nm_v) is None: return 0
|
||||
vm_idx_v: int = self.Gen.StructGen.get_vtable_method_index(class_nm_v, at.attr)
|
||||
if vm_idx_v < 0: return 0
|
||||
# 解析 obj_ptr(对齐 get_attribute_ptr 前半段:Name 直接查 alloca)
|
||||
# Resolve obj_ptr (same as the first half of get_attribute_ptr: Name looks up the alloca directly).
|
||||
obj_ptr_v: mlirlite.Value* = None
|
||||
if at.value.kind() == ast.ASTKind.Name: obj_ptr_v = self.Gen.VarGen.lookup_var(trans.SymTab, (ast.Name*(at.value)).id)
|
||||
else: obj_ptr_v = self.Gen.ExprGen.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||||
if obj_ptr_v is None or obj_ptr_v.Ty is None: return 0
|
||||
# X* 变量(Ptr(Ptr))load 解引用取对象指针
|
||||
# X* variable (Ptr(Ptr)): load-dereference to get the object pointer.
|
||||
match obj_ptr_v.Ty:
|
||||
case mlirlite.LLVMType.Ptr(inner_v):
|
||||
match inner_v:
|
||||
|
||||
@@ -12,161 +12,135 @@ import lib.core.Gen.Generator as Generator
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 本地常量(阶段 D 拆分时从 ClassGenMixin 顶层遗漏,补回:
|
||||
# 方法元数据/类元数据/装饰器/异常表容量与编码;本地副本避免跨模块 CDefine 查找)
|
||||
# ============================================================
|
||||
# 方法元数据 flag 位(对齐 TPC HandlesBase _METHOD_META_*)
|
||||
# Local constants: metadata/decorator/exception table sizes and method metadata flag bits.
|
||||
define META_TLS = 32
|
||||
# 类编译期元数据表容量上限(__provides__/__requires__/__require_must__)
|
||||
# Class compile-time metadata table capacity (__provides__/__requires__/__require_must__).
|
||||
define CLASS_META_MAX = 256
|
||||
# 自定义装饰器 wrapper 表容量上限(带 @decor 的函数数)
|
||||
# Custom-decorator wrapper table capacity (number of @decor functions).
|
||||
define DECOR_FUNC_MAX = 64
|
||||
# 装饰器表槽内函数名/wrapper 名长度(含 NUL)
|
||||
# Decorator table slot length for function/wrapper names (incl. NUL).
|
||||
define DECOR_NAME_LEN = 128
|
||||
# 异常类注册表容量上限(class X(Exception) 自定义异常类)
|
||||
# Exception class registry capacity (user classes subclassing Exception).
|
||||
define EXCEPTION_MAX = 64
|
||||
# 异常类表槽内类名/父类名长度(含 NUL)
|
||||
# Exception table slot length for class/parent names (incl. NUL).
|
||||
define EXCEPTION_NAME_LEN = 128
|
||||
# 内置异常编码(对齐 TPC HandlesBase.py EXCEPTION_CODE_MAP);
|
||||
# KeyError=6 IOError=7 OSError=8 AssertionError=9 Exception=99;用户类从 100 起
|
||||
# Builtin exception codes (mirrors TPC EXCEPTION_CODE_MAP); user classes start at 100.
|
||||
# KeyError=6 IOError=7 OSError=8 AssertionError=9 Exception=99.
|
||||
define BUILTIN_EH_VALUE_ERROR = 1
|
||||
define BUILTIN_EH_RUNTIME_ERROR = 3
|
||||
define BUILTIN_EH_EXCEPTION = 99
|
||||
define EH_USER_CODE_START = 100
|
||||
# 方法元数据表容量(原 HandlesClassDef.META_MAX/META_KEY_LEN,阶段 D 拆分遗漏)
|
||||
# Method metadata table sizes (formerly HandlesClassDef.META_MAX/META_KEY_LEN).
|
||||
define META_MAX = 512
|
||||
define META_KEY_LEN = 128
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ClassGenBase - ClassGenMixin 拆分(阶段 D):字段 + 方法元数据/异常/继承检测/装饰器(Meta)
|
||||
# ============================================================
|
||||
# ClassGenBase - fields plus metadata/exception/inheritance/decorator helpers.
|
||||
novirtual class ClassGenBase:
|
||||
"""字段 + 方法元数据/异常/继承检测/装饰器(Meta)"""
|
||||
"""Fields plus method-metadata, exception, inheritance and decorator helpers."""
|
||||
|
||||
# --- 方法元数据表(原 _g_method_meta_keys/_g_method_meta_flags/_g_method_meta_count) ---
|
||||
# Method metadata table (formerly _g_method_meta_keys/_flags/_count).
|
||||
Gen: Generator.Generator*
|
||||
MethodMetaKeys: bytes
|
||||
MethodMetaFlags: bytes
|
||||
MethodMetaCount: int
|
||||
|
||||
# --- REnum 变体表(原 _renum_class_names/_renum_class_variant_counts/
|
||||
# _renum_variant_names/_renum_variant_tags/_renum_class_count) ---
|
||||
# REnum variant table (formerly _renum_class_names/_variant_counts/_variant_names/_variant_tags/_renum_class_count).
|
||||
RenumClassNames: sizet*
|
||||
RenumClassVariantCounts: sizet*
|
||||
RenumVariantNames: sizet*
|
||||
RenumVariantTags: sizet*
|
||||
RenumClassCount: int
|
||||
|
||||
# --- REnum 字段别名表(原 _renum_alias_class/_renum_alias_field/
|
||||
# _renum_alias_index/_renum_alias_count) ---
|
||||
# REnum field alias table (formerly _renum_alias_class/_alias_field/_alias_index/_alias_count).
|
||||
RenumAliasClass: sizet*
|
||||
RenumAliasField: sizet*
|
||||
RenumAliasIndex: sizet*
|
||||
# 变体成员实际 LLVM 类型(对齐 TPC class_members):注册时按注解解析,
|
||||
# perm_pool 深拷贝持久化。match 绑定用真实成员类型 bitcast 槽位地址
|
||||
# (Struct.Name: i8*,否则按槽位 i64 读 → is/subscript/strlen 失败)。
|
||||
# REnum variant member LLVM types (mirrors TPC class_members): deep-copied into perm_pool.
|
||||
# match binds slots by the real member type, not the raw i64 slot width.
|
||||
RenumAliasTy: sizet*
|
||||
# 别名所属变体名(perm_pool 拷贝):同一 payload 位置跨变体类型不同
|
||||
# (Int.Bits=i32 vs Struct.Fields=Ptr),match 绑定按 (class, 变体, 位置)
|
||||
# 唯一定位成员类型。
|
||||
# Alias variant name (perm_pool copy): the same payload offset has a different type
|
||||
# per variant, so match looks up the member type by (class, variant, offset).
|
||||
RenumAliasVariant: sizet*
|
||||
RenumAliasCount: int
|
||||
|
||||
# --- 泛型特化缓存(原 _spec_keys/_spec_names/_spec_count) ---
|
||||
# --- Generic specialization cache (formerly _spec_keys/_spec_names/_spec_count). ---
|
||||
SpecKeys: sizet*
|
||||
SpecNames: sizet*
|
||||
SpecCount: int
|
||||
|
||||
# --- 类编译期元数据表(__provides__/__requires__/__require_must__) ---
|
||||
# perm_pool 分配(跨 mark/reset 存活):每槽 kind(4B) + cls(64B) + field(64B)
|
||||
# FakeDuck:_str.__requires__=['__mbuddy__'] 等(FakeDuck 移植点 1)
|
||||
# --- Class compile-time metadata table (__provides__/__requires__/__require_must__).
|
||||
# perm_pool-allocated: kind(4B) + cls(64B) + field(64B) per slot, e.g. FakeDuck __requires__.
|
||||
ClassMetaKinds: int*
|
||||
ClassMetaClss: bytes
|
||||
ClassMetaFields: bytes
|
||||
ClassMetaCount: int
|
||||
|
||||
# --- 自定义装饰器 wrapper 表(@log/@skip/@repeat 等 → __decor_wrap_{name}) ---
|
||||
# perm_pool 分配(跨 mark/reset 存活):每槽函数名(128B) + wrapper 名(128B)
|
||||
# 对齐 TPC HandlesFunctions.py L1621-1631 的 _decorated_funcs +
|
||||
# DecoratorPass.py 的 wrapper 生成。调用点重写经
|
||||
# lookup_decor_wrapper(translate_call 拦截原函数名 → wrapper 名)。
|
||||
# --- Custom decorator wrapper table (@log/@skip/@repeat -> __decor_wrap_{name}).
|
||||
# translate_call rewrites the original name to the wrapper via lookup_decor_wrapper.
|
||||
DecoratorFuncCount: int
|
||||
DecoratorFuncNames: bytes
|
||||
DecoratorWrapNames: bytes
|
||||
# 装饰器首个常量参数(@repeat(3) 的 3),无参数为 0(每槽 8B,i64)
|
||||
# First constant decorator arg (the 3 in @repeat(3)); 0 when none (8B i64 per slot).
|
||||
DecoratorArgs: int64*
|
||||
|
||||
# --- 异常类注册表(try-except,对齐 TPC HandlesClassDef.py L47-61) ---
|
||||
# perm_pool 分配(跨 mark/reset 存活):每槽类名(128B) + 父类名(128B) + 编码(4B)
|
||||
# 用户异常类编码从 100 递增;内置编码(ValueError=1 等)不在此表
|
||||
# (_lookup_exception_code 走内置硬编码)。异常类不生成 struct。
|
||||
# --- Exception class registry for try/except (mirrors TPC HandlesClassDef).
|
||||
# perm_pool-allocated slots: class name(128B) + parent(128B) + code(4B); user codes from 100.
|
||||
ExceptionCount: int
|
||||
ExceptionNames: bytes
|
||||
ExceptionParents: bytes
|
||||
ExceptionCodes: int*
|
||||
# 下一个待分配的异常编码(用户类,从 100 起)
|
||||
# Next exception code to assign (user classes, starting at 100).
|
||||
NextExceptionCode: int
|
||||
# 递归收集子孙编码用的临时计数(collect_descendant_codes 返回后无效)。
|
||||
# 必须与其它字段一起声明在类顶:方法中间的字段注解可能被编译器
|
||||
# 当作局部声明处理,破坏 Mixin 布局/this_adjust 对齐。
|
||||
# Temp counter for collect_descendant_codes (invalid after it returns).
|
||||
# Must be declared at class top; mid-method field annotations break Mixin layout.
|
||||
_exception_codes_tmp_count: int
|
||||
|
||||
# --- 泛型特化递归深度(泛型循环套用防护) ---
|
||||
# _specialize_generic_class 进入时 +1、退出时 -1;超过上限立即报错终止
|
||||
# (防止 F-bounded 等循环特化把编译变成死循环)
|
||||
# --- Generic specialization recursion depth (guards against cyclic specialization).
|
||||
# Incremented on entry, decremented on exit; exceeding the limit aborts.
|
||||
GenericSpecDepth: int
|
||||
|
||||
# --- 泛型模板表(原 HandlesClassDef._generic_class_names/
|
||||
# _generic_class_nodes/_generic_class_sha1s) ---
|
||||
# --- Generic template tables (formerly HandlesClassDef._generic_class_names/
|
||||
# _generic_class_nodes/_generic_class_sha1s). ---
|
||||
GenericClassNames: list[str]*
|
||||
GenericClassNodes: list[ast.ClassDef*]*
|
||||
GenericClassSha1s: list[str]*
|
||||
# 泛型模板来源文件路径(与上面三表同序)。特化翻译模板方法体时 node.lineno 是模板
|
||||
# 文件行号,若 CurrentFile 仍是调用方文件,fatal_error 会把模板行号套到调用方文件上
|
||||
# 显示错位。此表用于特化期间临时切换 CurrentFile 到模板文件,翻译完恢复。
|
||||
# Source file path per template (same order as above): specialization temporarily
|
||||
# switches CurrentFile to it so template line numbers in errors stay correct.
|
||||
GenericClassFiles: list[str]*
|
||||
# 泛型模板类型参数名表(与 GenericClassNames 同序,stdlib 深拷贝)。
|
||||
# 模板节点 type_params 是 Phase 1 pool 分配的 list,跨 Phase 引用时内容
|
||||
# 可能失效(VectorGenericTest 特化 Vector[int].__init__ 的 T.__sizeof__()
|
||||
# NameError 根因方向);注册时展平深拷贝到 stdlib,特化时从本表恢复。
|
||||
# 每槽指向一个空格分隔、NUL 结尾的 stdlib 缓冲区(如 "T" 或 "T K")。
|
||||
# Template type-param names (same order as GenericClassNames, deep-copied to stdlib):
|
||||
# Phase 1 type_params may expire across phases, so they are flattened here.
|
||||
GenericClassTpParams: sizet*
|
||||
|
||||
# --- 泛型函数模板表(与上面 GenericClass* 同构:键 "{sha1}.{fname}" 定位
|
||||
# 模板节点/来源 SHA1/来源文件/类型参数名)。
|
||||
# 必须与其它字段一起声明在类顶——方法中间的字段注解可能被编译器当作
|
||||
# 局部声明处理,破坏 Mixin 布局/this_adjust 对齐(见 _exception_codes_tmp_count)。
|
||||
# 之前只写了 __before_init__ 里的赋值而漏了这里,导致 ClassGenGeneric
|
||||
# 上的读取触发 fast-fail:属性 'GenericFuncKeys' 在类中找不到。 ---
|
||||
# --- Generic function template tables, keyed "{sha1}.{fname}" to template
|
||||
# node/SHA1/file/type-params; must stay at class top for Mixin layout. ---
|
||||
GenericFuncKeys: list[str]*
|
||||
GenericFuncNodes: list[ast.FunctionDef*]*
|
||||
GenericFuncSha1s: list[str]*
|
||||
GenericFuncFiles: list[str]*
|
||||
GenericFuncTpParams: sizet*
|
||||
|
||||
# --- vtable 方法收集表(原 _g_vt_full_names/_g_vt_method_names/
|
||||
# _g_vt_parent_sha1s/_g_vt_method_count) ---
|
||||
# --- vtable method collection tables (formerly _g_vt_full_names/
|
||||
# _g_vt_method_names/_g_vt_parent_sha1s/_g_vt_method_count). ---
|
||||
VtFullNames: sizet*
|
||||
VtMethodNames: sizet*
|
||||
VtParentSha1s: sizet*
|
||||
VtMethodCount: int
|
||||
# ============================================================
|
||||
# 以下方法由 P5-2 分簇迁移填充(函数体来自 HandlesClassDef.vp,
|
||||
# 本模块函数调用改 self.,外部模块调用保留模块前缀)
|
||||
# ============================================================
|
||||
# Methods below migrated in P5-2 from HandlesClassDef.vp; intra-module calls use self.
|
||||
|
||||
def _is_cenum_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
|
||||
"""检查 base 节点是否为 t.CEnum,返回 1=是 / 0=否"""
|
||||
"""Return 1 if the base node is t.CEnum, else 0."""
|
||||
if base_node is None: return 0
|
||||
k: int = base_node.kind()
|
||||
# Name('CEnum') 形式(from t import CEnum)
|
||||
# Name('CEnum') form (from t import CEnum).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(base_node)
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, "CEnum") == 0: return 1
|
||||
# 别名形式: from t import CEnum as en; class Color(en)
|
||||
# 解析别名原始名(en → CEnum)再比较
|
||||
# Alias form: from t import CEnum as en; class Color(en).
|
||||
# Resolve the original name (en -> CEnum) before comparing.
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
|
||||
if alias_orig is not None:
|
||||
@@ -174,16 +148,16 @@ novirtual class ClassGenBase:
|
||||
while alias_orig[og_len_ce] != ' ' and alias_orig[og_len_ce] != '\0': og_len_ce += 1
|
||||
if og_len_ce == 5 and string.strncmp(alias_orig, "CEnum", 5) == 0: return 1
|
||||
return 0
|
||||
# Attribute(t.CEnum) 形式
|
||||
# Attribute(t.CEnum) form.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||||
if at.attr is not None and string.strcmp(at.attr, "CEnum") == 0: return 1
|
||||
return 0
|
||||
return 0
|
||||
def _is_enum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""检查 ClassDef 是否为 CEnum:继承自 t.CEnum 或 @t.CEnum 装饰器形式"""
|
||||
"""Return 1 if the ClassDef is a CEnum (subclasses t.CEnum or uses @t.CEnum)."""
|
||||
if cd is None: return 0
|
||||
# 装饰器形式: @t.CEnum class Color:(无基类,等价 class Color(t.CEnum):)
|
||||
# Decorator form: @t.CEnum class Color: (equivalent to subclassing t.CEnum).
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "CEnum") == 1: return 1
|
||||
if cd.bases is None: return 0
|
||||
@@ -196,15 +170,15 @@ novirtual class ClassGenBase:
|
||||
if self._is_cenum_base(base_node, trans) == 1: return 1
|
||||
return 0
|
||||
def _is_cunion_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
|
||||
"""检查 base 节点是否为 t.CUnion,返回 1=是 / 0=否"""
|
||||
"""Return 1 if the base node is t.CUnion, else 0."""
|
||||
if base_node is None: return 0
|
||||
k: int = base_node.kind()
|
||||
# Name('CUnion') 形式(from t import CUnion)
|
||||
# Name('CUnion') form (from t import CUnion).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(base_node)
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, "CUnion") == 0: return 1
|
||||
# 别名形式: from t import CUnion as un; class Data(un)
|
||||
# Alias form: from t import CUnion as un; class Data(un).
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
|
||||
if alias_orig is not None:
|
||||
@@ -212,16 +186,16 @@ novirtual class ClassGenBase:
|
||||
while alias_orig[og_len_cu] != ' ' and alias_orig[og_len_cu] != '\0': og_len_cu += 1
|
||||
if og_len_cu == 6 and string.strncmp(alias_orig, "CUnion", 6) == 0: return 1
|
||||
return 0
|
||||
# Attribute(t.CUnion) 形式
|
||||
# Attribute(t.CUnion) form.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||||
if at.attr is not None and string.strcmp(at.attr, "CUnion") == 0: return 1
|
||||
return 0
|
||||
return 0
|
||||
def _is_union_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""检查 ClassDef 是否为 CUnion:继承自 t.CUnion 或 @t.CUnion 装饰器形式"""
|
||||
"""Return 1 if the ClassDef is a CUnion (subclasses t.CUnion or uses @t.CUnion)."""
|
||||
if cd is None: return 0
|
||||
# 装饰器形式: @t.CUnion class Data:
|
||||
# Decorator form: @t.CUnion class Data:
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "CUnion") == 1: return 1
|
||||
if cd.bases is None: return 0
|
||||
@@ -234,15 +208,15 @@ novirtual class ClassGenBase:
|
||||
if self._is_cunion_base(base_node, trans) == 1: return 1
|
||||
return 0
|
||||
def _is_crenum_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
|
||||
"""检查 base 节点是否为 t.REnum,返回 1=是 / 0=否"""
|
||||
"""Return 1 if the base node is t.REnum, else 0."""
|
||||
if base_node is None: return 0
|
||||
k: int = base_node.kind()
|
||||
# Name('REnum') 形式(from t import REnum)
|
||||
# Name('REnum') form (from t import REnum).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(base_node)
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, "REnum") == 0: return 1
|
||||
# 别名形式: from t import REnum as ren; class Opt(ren)
|
||||
# Alias form: from t import REnum as ren; class Opt(ren).
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
|
||||
if alias_orig is not None:
|
||||
@@ -250,16 +224,16 @@ novirtual class ClassGenBase:
|
||||
while alias_orig[og_len_cr] != ' ' and alias_orig[og_len_cr] != '\0': og_len_cr += 1
|
||||
if og_len_cr == 5 and string.strncmp(alias_orig, "REnum", 5) == 0: return 1
|
||||
return 0
|
||||
# Attribute(t.REnum) 形式
|
||||
# Attribute(t.REnum) form.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||||
if at.attr is not None and string.strcmp(at.attr, "REnum") == 0: return 1
|
||||
return 0
|
||||
return 0
|
||||
def _is_renum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""检查 ClassDef 是否为 REnum:继承自 t.REnum 或 @t.REnum 装饰器形式"""
|
||||
"""Return 1 if the ClassDef is a REnum (subclasses t.REnum or uses @t.REnum)."""
|
||||
if cd is None: return 0
|
||||
# 装饰器形式: @t.REnum class X:
|
||||
# Decorator form: @t.REnum class X:
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "REnum") == 1: return 1
|
||||
if cd.bases is None: return 0
|
||||
@@ -272,7 +246,7 @@ novirtual class ClassGenBase:
|
||||
if self._is_crenum_base(base_node, trans) == 1: return 1
|
||||
return 0
|
||||
def _has_decorator(self, decorator_list: list[ast.AST*]*, deco_name: str) -> int:
|
||||
"""检查 decorator_list 中是否有指定装饰器名"""
|
||||
"""Return 1 if decorator_list contains the given decorator name."""
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
@@ -280,25 +254,22 @@ novirtual class ClassGenBase:
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
k: int = deco.kind()
|
||||
# @t.NoVTable 形式(Attribute)
|
||||
# @t.NoVTable form (Attribute).
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(deco)
|
||||
if at.attr is not None and string.strcmp(at.attr, deco_name) == 0:
|
||||
# 检查 value 是 Name('t')
|
||||
# Check that value is Name('t').
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
if vn.id is not None and string.strcmp(vn.id, "t") == 0: return 1
|
||||
return 0
|
||||
# @NoVTable 形式(Name,from t import NoVTable)
|
||||
# @NoVTable form (Name; from t import NoVTable).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(deco)
|
||||
if nm.id is not None and string.strcmp(nm.id, deco_name) == 0: return 1
|
||||
return 0
|
||||
def _tattr_name(self, node: ast.AST*, attr_name: str) -> int:
|
||||
"""从 t.attr.<name> / t.attr.<name>(args) 节点提取最外层属性名并比较。
|
||||
|
||||
Call(t.attr.aligned(16))→ 递归取 func;Attribute(t.attr.packed)→ 取 attr。
|
||||
"""
|
||||
"""Extract the outermost attribute name from t.attr.<name> / t.attr.<name>(args) and compare it."""
|
||||
if node is None: return 0
|
||||
if node.kind() == ast.ASTKind.Call:
|
||||
cl2: ast.Call* = ast.Call*(node)
|
||||
@@ -310,10 +281,7 @@ novirtual class ClassGenBase:
|
||||
return 1 if string.strcmp(at2.attr, attr_name) == 0 else 0
|
||||
return 0
|
||||
def _class_has_tattr(self, decorator_list: list[ast.AST*]*, attr_name: str) -> int:
|
||||
"""检查装饰器列表是否含 t.attr.<attr_name>(@c.Attribute(t.attr.xxx) 或 @t.attr.xxx)。
|
||||
|
||||
覆盖 packed/aligned/naked/section 等所有 t.attr 属性(B5)。
|
||||
"""
|
||||
"""Return 1 if decorator_list contains t.attr.<attr_name> (@c.Attribute(t.attr.xxx) or @t.attr.xxx)."""
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
@@ -323,7 +291,7 @@ novirtual class ClassGenBase:
|
||||
k: int = deco.kind()
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(deco)
|
||||
# @c.Attribute(t.attr.xxx) 形式:func 是 Attribute(c, "Attribute")
|
||||
# @c.Attribute(t.attr.xxx) form: func is Attribute(c, "Attribute").
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: continue
|
||||
at0: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue
|
||||
@@ -333,11 +301,11 @@ novirtual class ClassGenBase:
|
||||
ag: ast.AST* = cl.args.get(ai)
|
||||
if ag is not None and self._tattr_name(ag, attr_name) != 0: return 1
|
||||
else:
|
||||
# @t.attr.xxx 直接形式
|
||||
# Direct @t.attr.xxx form.
|
||||
if self._tattr_name(deco, attr_name) != 0: return 1
|
||||
return 0
|
||||
def _tattr_aligned_int(self, node: ast.AST*) -> int:
|
||||
"""t.attr.aligned(N) → N(整数参数),非 aligned 或无参返回 0"""
|
||||
"""Return N for t.attr.aligned(N); 0 when not aligned or the argument is missing."""
|
||||
if node is None: return 0
|
||||
if node.kind() == ast.ASTKind.Call:
|
||||
cl2: ast.Call* = ast.Call*(node)
|
||||
@@ -352,7 +320,7 @@ novirtual class ClassGenBase:
|
||||
return int(cn.int_val)
|
||||
return 0
|
||||
def _class_aligned_value(self, decorator_list: list[ast.AST*]*) -> int:
|
||||
"""取 @c.Attribute(t.attr.aligned(N)) 的对齐字节数,无 aligned 返回 0"""
|
||||
"""Return the alignment bytes from @c.Attribute(t.attr.aligned(N)); 0 when absent."""
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
@@ -376,7 +344,7 @@ novirtual class ClassGenBase:
|
||||
if av2 > 0: return av2
|
||||
return 0
|
||||
def _extract_func_meta(self, decorator_list: list[ast.AST*]*) -> int:
|
||||
"""从装饰器列表提取元数据位掩码,返回 0=无装饰器元数据"""
|
||||
"""Build the method-metadata bitmask from decorators; 0=no decorator metadata."""
|
||||
meta: int = 0
|
||||
if decorator_list is None: return meta
|
||||
dn: sizet = len(decorator_list)
|
||||
@@ -399,17 +367,17 @@ novirtual class ClassGenBase:
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
is_prop_name: int = 0
|
||||
if vn.id is not None and string.strcmp(vn.id, "property") == 0: is_prop_name = 1
|
||||
# @t.TLS 一次性初始化标记(对齐 TPC HandlesFunctions.py
|
||||
# L1223-1231:value 必须为 Name('t');@TLS 裸名走上面 Name 分支)
|
||||
# @t.TLS one-time init marker (mirrors TPC HandlesFunctions):
|
||||
# value must be Name('t'); bare @TLS is handled by the Name branch.
|
||||
if vn.id is not None and string.strcmp(vn.id, "t") == 0 and string.strcmp(at.attr, "TLS") == 0: meta |= META_TLS
|
||||
# @property.setter 或 @x.setter/getter/deleter 都识别
|
||||
# Recognize @property.setter and @x.setter/getter/deleter.
|
||||
if is_prop_name != 0 or string.strcmp(at.attr, "setter") == 0 or string.strcmp(at.attr, "getter") == 0 or string.strcmp(at.attr, "deleter") == 0:
|
||||
if string.strcmp(at.attr, "setter") == 0: meta |= GenTypes.META_PROP_SETTER
|
||||
elif string.strcmp(at.attr, "getter") == 0: meta |= GenTypes.META_PROP_GETTER
|
||||
elif string.strcmp(at.attr, "deleter") == 0: meta |= GenTypes.META_PROP_DELETER
|
||||
return meta
|
||||
def register_class_meta(self, pool: memhub.MemBuddy*, kind: int, cls: str, field: str) -> int:
|
||||
"""注册类编译期元数据字段(__provides__/__requires__/__require_must__,幂等)"""
|
||||
"""Register a class compile-time metadata field (__provides__/__requires__/__require_must__), idempotent."""
|
||||
if pool is None or cls is None or field is None: return -1
|
||||
if self.ClassMetaKinds is None:
|
||||
self.ClassMetaKinds = CompilerFactory.factory.PermPool.alloc(CLASS_META_MAX * 4)
|
||||
@@ -421,7 +389,7 @@ novirtual class ClassGenBase:
|
||||
string.memset(self.ClassMetaFields, 0, CLASS_META_MAX * 64)
|
||||
self.ClassMetaCount = 0
|
||||
if self.ClassMetaCount >= CLASS_META_MAX: return -1
|
||||
# 幂等:同 (kind, cls, field) 跳过
|
||||
# Idempotent: skip duplicate (kind, cls, field).
|
||||
for i in range(self.ClassMetaCount):
|
||||
if self.ClassMetaKinds[i] == kind:
|
||||
if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0:
|
||||
@@ -435,7 +403,7 @@ novirtual class ClassGenBase:
|
||||
self.ClassMetaCount += 1
|
||||
return 0
|
||||
def class_meta_count(self, kind: int, cls: str) -> int:
|
||||
"""返回某类某元数据类型的字段数"""
|
||||
"""Return the field count of the given metadata kind for a class."""
|
||||
if self.ClassMetaKinds is None or cls is None: return 0
|
||||
n_cm: int = 0
|
||||
for i in range(self.ClassMetaCount):
|
||||
@@ -443,7 +411,7 @@ novirtual class ClassGenBase:
|
||||
if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0: n_cm += 1
|
||||
return n_cm
|
||||
def class_meta_field_at(self, kind: int, cls: str, idx: int) -> str:
|
||||
"""返回某类某元数据类型的第 idx 个字段名"""
|
||||
"""Return the idx-th field name of the given metadata kind for a class."""
|
||||
if self.ClassMetaKinds is None or cls is None: return None
|
||||
n_cm2: int = 0
|
||||
for i in range(self.ClassMetaCount):
|
||||
@@ -453,11 +421,7 @@ novirtual class ClassGenBase:
|
||||
n_cm2 += 1
|
||||
return None
|
||||
def register_decorator(self, pool: memhub.MemBuddy*, func_name: str, wrapper_name: str, arg0: int64) -> int:
|
||||
"""注册自定义装饰器函数(@log/@skip/@repeat(3) → __decor_wrap_{name})
|
||||
|
||||
对齐 TPC HandlesFunctions.py L1621-1631 的 _decorated_funcs 注册。
|
||||
arg0 为装饰器首个常量参数(@repeat(3) 的 3),无参数为 0。
|
||||
"""
|
||||
"""Register a custom decorator function (@log/@skip/@repeat(3) -> __decor_wrap_{name}); arg0 is its first constant arg (0 when none)."""
|
||||
if pool is None or func_name is None or wrapper_name is None: return -1
|
||||
if self.DecoratorFuncNames is None:
|
||||
self.DecoratorFuncNames = CompilerFactory.factory.PermPool.alloc(DECOR_FUNC_MAX * DECOR_NAME_LEN)
|
||||
@@ -469,7 +433,7 @@ novirtual class ClassGenBase:
|
||||
string.memset(self.DecoratorArgs, 0, DECOR_FUNC_MAX * 8)
|
||||
self.DecoratorFuncCount = 0
|
||||
if self.DecoratorFuncCount >= DECOR_FUNC_MAX: return -1
|
||||
# 幂等:同名函数已注册则跳过(多遍扫描)
|
||||
# Idempotent: skip an already-registered function name (multi-pass scan).
|
||||
for i in range(self.DecoratorFuncCount):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return 0
|
||||
idx_dc: sizet = sizet(self.DecoratorFuncCount) * DECOR_NAME_LEN
|
||||
@@ -481,22 +445,21 @@ novirtual class ClassGenBase:
|
||||
self.DecoratorFuncCount += 1
|
||||
return 0
|
||||
def lookup_decor_wrapper(self, func_name: str) -> str:
|
||||
"""按原函数名查 wrapper 名,未注册返回 None"""
|
||||
"""Look up the wrapper name by original function name; None when not registered."""
|
||||
if self.DecoratorFuncNames is None or func_name is None: return None
|
||||
for i in range(self.DecoratorFuncCount):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return self.DecoratorWrapNames + i * DECOR_NAME_LEN
|
||||
return None
|
||||
def lookup_decor_arg0(self, func_name: str) -> int64:
|
||||
"""按原函数名查装饰器首个常量参数(无参数为 0)"""
|
||||
"""Look up a decorator's first constant arg by original function name (0 when none)."""
|
||||
if self.DecoratorFuncNames is None or func_name is None: return 0
|
||||
for i in range(self.DecoratorFuncCount):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return self.DecoratorArgs[i]
|
||||
return 0
|
||||
# ============================================================
|
||||
# 异常类注册表(try-except,对齐 TPC HandlesClassDef.py L47-61)
|
||||
# ============================================================
|
||||
# Exception class registry for try/except (mirrors TPC HandlesClassDef).
|
||||
def _ensure_exception_tables(self) -> int:
|
||||
"""懒初始化异常表(perm_pool),返回 0 成功"""
|
||||
"""Lazily initialize the exception tables (perm_pool); 0=success."""
|
||||
if self.ExceptionNames is not None: return 0
|
||||
pp_e: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if pp_e is None: return -1
|
||||
@@ -511,10 +474,10 @@ novirtual class ClassGenBase:
|
||||
self.NextExceptionCode = EH_USER_CODE_START
|
||||
return 0
|
||||
def register_exception(self, class_name: str, parent_name: str) -> int:
|
||||
"""注册用户异常类并分配编码(幂等:同名跳过)。返回编码或 -1"""
|
||||
"""Register a user exception class and assign its code (idempotent by name); returns the code or -1."""
|
||||
if class_name is None: return -1
|
||||
if self._ensure_exception_tables() != 0: return -1
|
||||
# 幂等:已注册则返回既有编码(多遍扫描)
|
||||
# Idempotent: return the existing code if already registered (multi-pass).
|
||||
for i in range(self.ExceptionCount):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, class_name) == 0: return self.ExceptionCodes[i]
|
||||
if self.ExceptionCount >= EXCEPTION_MAX: return -1
|
||||
@@ -530,7 +493,7 @@ novirtual class ClassGenBase:
|
||||
self.ExceptionCount += 1
|
||||
return code_e
|
||||
def _builtin_exception_code(self, name: str) -> int:
|
||||
"""内置异常编码(对齐 TPC HandlesBase.py EXCEPTION_CODE_MAP),非内置返回 0"""
|
||||
"""Return the builtin exception code (per TPC EXCEPTION_CODE_MAP); 0 when not builtin."""
|
||||
if name is None: return 0
|
||||
if string.strcmp(name, "ValueError") == 0: return 1
|
||||
if string.strcmp(name, "TypeError") == 0: return 2
|
||||
@@ -544,7 +507,7 @@ novirtual class ClassGenBase:
|
||||
if string.strcmp(name, "Exception") == 0: return 99
|
||||
return 0
|
||||
def lookup_exception_code(self, name: str) -> int:
|
||||
"""查异常编码:内置表优先,其次用户异常表,都未命中返回 0"""
|
||||
"""Look up an exception code: builtin table first, then user table; 0 when neither matches."""
|
||||
if name is None: return 0
|
||||
bi: int = self._builtin_exception_code(name)
|
||||
if bi != 0: return bi
|
||||
@@ -553,7 +516,7 @@ novirtual class ClassGenBase:
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: return self.ExceptionCodes[i]
|
||||
return 0
|
||||
def lookup_exception_parent(self, name: str) -> str:
|
||||
"""查用户异常类的父类名,非用户异常返回 None"""
|
||||
"""Look up a user exception's parent name; None when it is not a user exception."""
|
||||
if name is None or self.ExceptionNames is None: return None
|
||||
for i in range(self.ExceptionCount):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0:
|
||||
@@ -562,7 +525,7 @@ novirtual class ClassGenBase:
|
||||
return None
|
||||
return None
|
||||
def is_exception_name(self, name: str) -> int:
|
||||
"""name 是否是内置或用户异常类名(1=是 / 0=否)"""
|
||||
"""Return 1 if name is a builtin or user exception class name."""
|
||||
if name is None: return 0
|
||||
if self._builtin_exception_code(name) != 0: return 1
|
||||
if self.ExceptionNames is None: return 0
|
||||
@@ -570,19 +533,17 @@ novirtual class ClassGenBase:
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: return 1
|
||||
return 0
|
||||
def collect_descendant_codes(self, name: str, out_codes: int*, max_codes: int) -> int:
|
||||
"""把 name 及其所有子孙异常类的编码收集到 out_codes,返回数量
|
||||
(对齐 TPC HandlesTry.py _get_all_match_codes/_collect_descendant_codes:
|
||||
编译期静态展开继承树,except X 匹配 X 及全部子孙)。"""
|
||||
"""Collect the codes of name and all its descendant exceptions into out_codes; returns the count."""
|
||||
if name is None or out_codes is None: return 0
|
||||
self._exception_codes_tmp_count = 0
|
||||
self._collect_desc_codes_rec(name, out_codes, max_codes, 0)
|
||||
return self._exception_codes_tmp_count
|
||||
def _collect_desc_codes_rec(self, name: str, out_codes: int*, max_codes: int, depth: int) -> None:
|
||||
"""递归收集 name 编码 + 全部子孙编码(BFS 语义对齐 TPC)"""
|
||||
"""Recursively collect name's code plus all descendant codes."""
|
||||
if depth > 32: return
|
||||
code_e: int = self.lookup_exception_code(name)
|
||||
if code_e != 0 and self._exception_codes_tmp_count < max_codes:
|
||||
# 去重(继承环防护)
|
||||
# Deduplicate (guards against inheritance cycles).
|
||||
dup: int = 0
|
||||
for i in range(self._exception_codes_tmp_count):
|
||||
if out_codes[i] == code_e:
|
||||
@@ -591,7 +552,7 @@ novirtual class ClassGenBase:
|
||||
if dup == 0:
|
||||
out_codes[self._exception_codes_tmp_count] = code_e
|
||||
self._exception_codes_tmp_count += 1
|
||||
# 遍历全部用户异常,找以 name 为父类的子类递归收集
|
||||
# Recurse over user exceptions whose parent is name.
|
||||
if self.ExceptionNames is None: return
|
||||
for i in range(self.ExceptionCount):
|
||||
cname_e: str = self.ExceptionNames + i * EXCEPTION_NAME_LEN
|
||||
@@ -599,7 +560,7 @@ novirtual class ClassGenBase:
|
||||
if pname_e is not None and pname_e[0] != '\0':
|
||||
if string.strcmp(pname_e, name) == 0: self._collect_desc_codes_rec(cname_e, out_codes, max_codes, depth + 1)
|
||||
def _is_exception_class_def(self, cd: ast.ClassDef*) -> int:
|
||||
"""类定义是否是异常类:基类是 Exception 或已注册异常类(1=是)"""
|
||||
"""Return 1 if the class def is an exception class (base is Exception or a registered exception)."""
|
||||
if cd is None or cd.bases is None: return 0
|
||||
bases_e: list[ast.AST*]* = cd.bases
|
||||
bn_e: sizet = len(bases_e)
|
||||
@@ -616,7 +577,7 @@ novirtual class ClassGenBase:
|
||||
if self.is_exception_name(base_name_e) != 0: return 1
|
||||
return 0
|
||||
def _get_exception_parent_name(self, cd: ast.ClassDef*) -> str:
|
||||
"""返回异常类定义的真实父类名(第一个非 Exception 的已注册异常类),无则 None"""
|
||||
"""Return the real parent name of an exception class (first registered non-Exception base); None otherwise."""
|
||||
if cd is None or cd.bases is None: return None
|
||||
bases_e2: list[ast.AST*]* = cd.bases
|
||||
bn_e2: sizet = len(bases_e2)
|
||||
@@ -632,13 +593,13 @@ novirtual class ClassGenBase:
|
||||
if string.strcmp(base_name_e2, "Exception") != 0 and self.is_exception_name(base_name_e2) != 0: return base_name_e2
|
||||
return None
|
||||
def _register_exception_class_def(self, trans: HT.Translator*, cd: ast.ClassDef*) -> int:
|
||||
"""注册异常类(分配编码 + 记录父类),不生成 struct,返回 0"""
|
||||
"""Register an exception class (assign code + record parent) without emitting a struct; returns 0."""
|
||||
if cd is None or cd.name is None: return 0
|
||||
parent_e: str = self._get_exception_parent_name(cd)
|
||||
self.register_exception(cd.name, parent_e)
|
||||
return 0
|
||||
def _register_class_meta_list(self, pool: memhub.MemBuddy*, kind: int, cls: str, value_node: ast.AST*) -> None:
|
||||
"""解析类体 __provides__/__requires__ 的 list[str] 字面量并注册"""
|
||||
"""Parse a class-body __provides__/__requires__ list[str] literal and register its entries."""
|
||||
if value_node is None or cls is None: return
|
||||
if value_node.kind() != ast.ASTKind.List: return
|
||||
lst_rcm: ast.List* = ast.List*(value_node)
|
||||
@@ -651,43 +612,40 @@ novirtual class ClassGenBase:
|
||||
ec_rcm: ast.Constant* = ast.Constant*(el_rcm)
|
||||
if ec_rcm.str_val is not None: self.register_class_meta(pool, kind, cls, ec_rcm.str_val)
|
||||
def register_method_meta(self, class_name: str, method_name: str, meta: int) -> None:
|
||||
"""注册方法元数据(幂等)"""
|
||||
"""Register method metadata (idempotent)."""
|
||||
if class_name is None or method_name is None or meta == 0: return
|
||||
# 懒初始化
|
||||
# Lazy initialization.
|
||||
if self.MethodMetaKeys is None:
|
||||
self.MethodMetaKeys = CompilerFactory.factory.PermPool.alloc(META_MAX * META_KEY_LEN)
|
||||
self.MethodMetaFlags = CompilerFactory.factory.PermPool.alloc(META_MAX * 4)
|
||||
if self.MethodMetaKeys is None or self.MethodMetaFlags is None: return
|
||||
string.memset(self.MethodMetaKeys, 0, META_MAX * META_KEY_LEN)
|
||||
string.memset(self.MethodMetaFlags, 0, META_MAX * 4)
|
||||
# 构造 key "ClassName.method"
|
||||
# Build key "ClassName.method".
|
||||
cl_len: sizet = string.strlen(class_name)
|
||||
mn_len: sizet = string.strlen(method_name)
|
||||
key_buf: bytes = stdlib.malloc(cl_len + mn_len + 2)
|
||||
if key_buf is None: return
|
||||
viperlib.snprintf(key_buf, cl_len + mn_len + 2, "%s.%s", class_name, method_name)
|
||||
# 去重更新
|
||||
# Update duplicate entry.
|
||||
i: int
|
||||
for i in range(self.MethodMetaCount):
|
||||
if string.strcmp(self.MethodMetaKeys + i * META_KEY_LEN, key_buf) == 0:
|
||||
flags_ptr: t.CPtr = (int*)(uint64(self.MethodMetaFlags) + i * 4)
|
||||
# 按位或合并:同一属性名先 @property(getter=4) 再 @x.setter(8)/
|
||||
# @x.deleter(16) 会多次注册(如 Config.value / Counter.value)。
|
||||
# 若整值替换,后注册的位会覆盖 GETTER 位 → _try_property_getter_attr
|
||||
# 不再拦截 obj.prop → 落入字段查找失败返回 None → 上游 mlirlite
|
||||
# 收到 NULL Value → 编译器进程段错误(BuiltinDecoratorTest Phase A 崩溃)。
|
||||
# OR-combine flags: a getter then setter/deleter registers repeatedly,
|
||||
# so whole-value replacement would drop GETTER and crash downstream mlirlite.
|
||||
flags_ptr[0] = flags_ptr[0] | meta
|
||||
stdlib.free(key_buf)
|
||||
return
|
||||
if self.MethodMetaCount < META_MAX:
|
||||
# 槽位定长 META_KEY_LEN=128 字节,key 超长时 strncpy 钳制防越界写相邻槽
|
||||
# Slot is META_KEY_LEN=128 bytes; clamp strncpy to avoid overflowing the next slot.
|
||||
string.strncpy(self.MethodMetaKeys + self.MethodMetaCount * META_KEY_LEN, key_buf, META_KEY_LEN - 1)
|
||||
flags_ptr2: t.CPtr = (int*)(uint64(self.MethodMetaFlags) + self.MethodMetaCount * 4)
|
||||
flags_ptr2[0] = meta
|
||||
self.MethodMetaCount += 1
|
||||
stdlib.free(key_buf)
|
||||
def lookup_method_meta(self, class_name: str, method_name: str) -> int:
|
||||
"""查找方法元数据位掩码,返回 0=未注册或非装饰器方法"""
|
||||
"""Look up the method-metadata bitmask; 0=not registered or not a decorator method."""
|
||||
if class_name is None or method_name is None: return 0
|
||||
if self.MethodMetaKeys is None or self.MethodMetaCount <= 0: return 0
|
||||
cl_len: sizet = string.strlen(class_name)
|
||||
@@ -705,17 +663,7 @@ novirtual class ClassGenBase:
|
||||
stdlib.free(key_buf)
|
||||
return found
|
||||
def _is_marker_base(self, base_name: str) -> int:
|
||||
"""检查基类名是否为标记基类,返回 1=是 / 0=否
|
||||
|
||||
含 t.Struct:t.py 未定义 Struct 类(不被编译),`class X(t.Struct)`
|
||||
的 Struct 只是"普通结构体"标记。若不当成标记基类,会被误判为真实父类:
|
||||
- 自动启用虚表 → 结构体多出 i8* 槽位(与 TPC 的 {i32,i32} 布局不符)
|
||||
- Phase A-pre(declare_only=1)父类延迟检查找不到 "Struct" → 类被延迟注册
|
||||
→ forward_declare 时类型未注册 → 裸类形参退化为 i32 → 函数签名与
|
||||
函数体 alloca(Ptr(struct))不一致 → llc 报 "'%p' defined with type
|
||||
'i32' but expected 'ptr'"。
|
||||
与 TPC(_resolve_base_kind 对 t.Struct 返回 None → 无父类)保持一致。
|
||||
"""
|
||||
"""Return 1 if the base name is a marker base class (Object/CVTable/Exception/CEnum/Enum/CStruct/Struct/CUnion/REnum)."""
|
||||
if base_name is None: return 0
|
||||
if string.strcmp(base_name, "Object") == 0: return 1
|
||||
if string.strcmp(base_name, "CVTable") == 0: return 1
|
||||
@@ -728,53 +676,43 @@ novirtual class ClassGenBase:
|
||||
if string.strcmp(base_name, "REnum") == 0: return 1
|
||||
return 0
|
||||
def _get_base_name(self, base_node: ast.AST*, pool: memhub.MemBuddy* = None, from_imports: str = None) -> str:
|
||||
"""从 base AST 节点提取基类名,返回 None=失败
|
||||
|
||||
对于 Subscript 节点,需要 pool 分配内存拼接特化名;
|
||||
pool=None 时降级返回基类名(不含类型实参)
|
||||
|
||||
类型实参必须与**唯一生成器**(ClassGenGeneric._mangle_generic_class_name)
|
||||
同口径:本函数产出的父类特化名要拿去 find_struct_by_module 查、并写入
|
||||
`entry.ParentName`,而注册名由生成器(内部经 _norm_type_arg_name 归一)
|
||||
产出 ⇒ 这里同样归一,否则父类 `GSListNode[Value]` 与注册的
|
||||
`GSListNode[{sha1}.Value]` 不同名 ⇒ 父类查找落空/继承链断裂。
|
||||
"""
|
||||
"""Extract the base class name from a base AST node; Subscript nodes also build the specialized name; None on failure."""
|
||||
if base_node is None: return None
|
||||
k: int = base_node.kind()
|
||||
# Name(id) 形式
|
||||
# Name(id) form.
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(base_node)
|
||||
return nm.id
|
||||
# Attribute(attr) 形式
|
||||
# Attribute(attr) form.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||||
return at.attr
|
||||
# Subscript(value, slice) 形式 — 如 GSListNode[Value]
|
||||
# Subscript(value, slice) form, e.g. GSListNode[Value].
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(base_node)
|
||||
if sub is None or sub.value is None or sub.slice is None: return None
|
||||
# 提取基类名(如 "GSListNode")
|
||||
# Extract the base class name (e.g. "GSListNode").
|
||||
base_nm: str = self._get_base_name(sub.value, pool, from_imports)
|
||||
if base_nm is None: return None
|
||||
# 提取类型实参名(如 "Value"),并与唯一生成器同口径归一
|
||||
# Extract the type arg name (e.g. "Value") and normalize it like the generator.
|
||||
slice_k: int = sub.slice.kind()
|
||||
arg_nm: str = None
|
||||
if slice_k == ast.ASTKind.Name:
|
||||
sl_nm: ast.Name* = ast.Name*(sub.slice)
|
||||
arg_nm = sl_nm.id
|
||||
# 如果无法提取类型实参,返回基类名(降级)
|
||||
# Fall back to the base name when the type arg cannot be extracted.
|
||||
if arg_nm is None: return base_nm
|
||||
# 无 pool 时返回基类名(降级,无法拼接特化名)
|
||||
# Without a pool, fall back to the base name (cannot build the specialized name).
|
||||
if pool is None: return base_nm
|
||||
arg_norm: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, arg_nm, from_imports)
|
||||
if arg_norm is not None: arg_nm = arg_norm
|
||||
# 拼接特化名 "GSListNode[Value]"
|
||||
# Build the specialized name "GSListNode[Value]".
|
||||
base_len: sizet = string.strlen(base_nm)
|
||||
arg_len: sizet = string.strlen(arg_nm)
|
||||
total_len: sizet = base_len + arg_len + 3 # "[" + arg + "]" + NUL
|
||||
mangled: str = pool.alloc(total_len)
|
||||
if mangled is None:
|
||||
return base_nm # 降级
|
||||
return base_nm # fallback
|
||||
mangled[0] = '\0'
|
||||
string.strcat(mangled, base_nm)
|
||||
string.strcat(mangled, "[")
|
||||
@@ -783,7 +721,7 @@ novirtual class ClassGenBase:
|
||||
return mangled
|
||||
return None
|
||||
def _get_parent_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> str:
|
||||
"""获取 ClassDef 的真实父类名,返回 None=无真实父类"""
|
||||
"""Return the ClassDef's real parent name; None when there is no real parent."""
|
||||
if cd is None or cd.bases is None: return None
|
||||
pool: memhub.MemBuddy* = None
|
||||
if trans is not None: pool = trans.Pool
|
||||
@@ -797,26 +735,18 @@ novirtual class ClassGenBase:
|
||||
if trans is not None: _fi_pc = trans._from_imports
|
||||
bname: str = self._get_base_name(base_node, pool, _fi_pc)
|
||||
if bname is not None:
|
||||
# 基类别名解析(from t import CStruct as cs → cs 实际是 CStruct)。
|
||||
# 不解析会把别名当真实父类 → find_struct("cs") 失败 → 类 defer 永不注册。
|
||||
# Resolve base aliases (from t import CStruct as cs -> cs is CStruct).
|
||||
# Otherwise find_struct("cs") fails and the class is deferred forever.
|
||||
bname = self._resolve_base_alias(bname, trans)
|
||||
if self._is_marker_base(bname) == 0: return bname
|
||||
return None
|
||||
def _resolve_base_alias(self, base_name: str, trans: HT.Translator* = None) -> str:
|
||||
"""解析基类别名:from t import CStruct as cs → 基类 cs 实际是 CStruct。
|
||||
|
||||
别名不解析时 "cs" 会被当作真实父类名 → find_struct("cs") 失败 →
|
||||
PhaseA 预注册把整类 defer(永不注册)→ 变量注解 X 解析失败
|
||||
(EdgeCaseTest Test12 class CsPoint(cs): 根因)。
|
||||
"""
|
||||
"""Resolve a base alias (from t import CStruct as cs -> base cs is actually CStruct)."""
|
||||
if base_name is None or trans is None or trans._from_imports is None: return base_name
|
||||
orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, base_name)
|
||||
if orig is not None:
|
||||
# lookup_from_import_original 返回 from_imports 内部指针且未 NUL 结尾
|
||||
# (如 "CStruct myint:t:CInt CChar:t CPtr:t" 整串直到真正的 NUL)。
|
||||
# 复制到独立缓冲并按空格截断出干净原始名——否则 _is_marker_base 的
|
||||
# strcmp 对整串比较失败 → 别名基类(CStruct as cs)被当真实父类 →
|
||||
# find_struct 失败 → 整类 defer 永不注册 → 变量注解解析失败。
|
||||
# lookup_from_import_original returns a non-NUL-terminated interior pointer;
|
||||
# copy and cut at the space so strcmp gets a clean original name.
|
||||
og_len: sizet = 0
|
||||
while orig[og_len] != ' ' and orig[og_len] != '\0': og_len += 1
|
||||
if og_len > 0:
|
||||
@@ -827,7 +757,7 @@ novirtual class ClassGenBase:
|
||||
return og_buf
|
||||
return base_name
|
||||
def _collect_parents(self, pool: memhub.MemBuddy*, cd: ast.ClassDef*, parents_buf: sizet*, max_parents: int, trans: HT.Translator* = None) -> int:
|
||||
"""收集所有非 marker 父类名到 parents_buf,返回数量"""
|
||||
"""Collect all non-marker parent names into parents_buf and return the count."""
|
||||
if pool is None or cd is None or cd.bases is None or parents_buf is None: return 0
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
@@ -840,14 +770,14 @@ novirtual class ClassGenBase:
|
||||
if trans is not None: _fi_cp = trans._from_imports
|
||||
bname: str = self._get_base_name(base_node, pool, _fi_cp)
|
||||
if bname is not None:
|
||||
# 基类别名解析(同 _get_parent_class,见 _resolve_base_alias)
|
||||
# Resolve base alias (same as _get_parent_class, see _resolve_base_alias).
|
||||
bname = self._resolve_base_alias(bname, trans)
|
||||
if self._is_marker_base(bname) == 0:
|
||||
parents_buf[count] = sizet(bname)
|
||||
count += 1
|
||||
return count
|
||||
def _field_name_exists(self, names_buf: sizet*, count: int, name: str) -> int:
|
||||
"""检查字段名是否已在 names_buf 中(先声明父类优先,重复跳过)"""
|
||||
"""Return 1 if the field name already exists in names_buf (first-declared parent wins; duplicates skipped)."""
|
||||
if names_buf is None or name is None: return 0
|
||||
for fi in range(count):
|
||||
addr: sizet = names_buf[fi]
|
||||
@@ -856,7 +786,7 @@ novirtual class ClassGenBase:
|
||||
if existing is not None and string.strcmp(existing, name) == 0: return 1
|
||||
return 0
|
||||
def _class_body_has_field(self, cd: ast.ClassDef*) -> int:
|
||||
"""检查类体是否声明了字段(AnnAssign),0=纯方法/无字段类"""
|
||||
"""Return 1 if the class body declares fields (AnnAssign); 0=methods-only/no-field class."""
|
||||
if cd is None or cd.children is None: return 0
|
||||
cn: sizet = len(cd.children)
|
||||
for ci in range(cn):
|
||||
@@ -865,7 +795,7 @@ novirtual class ClassGenBase:
|
||||
if stmt.kind() == ast.ASTKind.AnnAssign: return 1
|
||||
return 0
|
||||
def _trigger_base_specialization(self, trans: HT.Translator*, cd: ast.ClassDef*):
|
||||
"""触发 base 中 Subscript 的泛型特化(如 GSListNode[Value])"""
|
||||
"""Trigger generic specialization for Subscript bases (e.g. GSListNode[Value])."""
|
||||
if trans is None or cd is None or cd.bases is None: return
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
@@ -878,26 +808,26 @@ novirtual class ClassGenBase:
|
||||
if bk != ast.ASTKind.Subscript: continue
|
||||
sub: ast.Subscript* = ast.Subscript*(base_node)
|
||||
if sub is None or sub.value is None or sub.slice is None: continue
|
||||
# 提取泛型类名(如 "GSListNode")
|
||||
# Extract the generic class name (e.g. "GSListNode").
|
||||
gen_class_name: str = self._get_base_name(sub.value, pool)
|
||||
if gen_class_name is None: continue
|
||||
# 跳过标记基类
|
||||
# Skip marker base classes.
|
||||
if self._is_marker_base(gen_class_name) == 1: continue
|
||||
# 提取类型实参名(如 "Value")
|
||||
# Extract the type arg name (e.g. "Value").
|
||||
slice_k: int = sub.slice.kind()
|
||||
arg_nm: str = None
|
||||
if slice_k == ast.ASTKind.Name:
|
||||
sl_nm: ast.Name* = ast.Name*(sub.slice)
|
||||
arg_nm = sl_nm.id
|
||||
if arg_nm is None: continue
|
||||
# 构建 type_args 列表(elem_size=8:str 是指针,1 会导致双元素重叠损坏)
|
||||
# Build type_args (elem_size=8: str is a pointer; 1 would overlap elements).
|
||||
type_args: list[str]* = list[str](pool, 8)
|
||||
if type_args is None: continue
|
||||
type_args.append(arg_nm)
|
||||
# 触发特化(_specialize_generic_class 有缓存,重复调用安全)
|
||||
# Trigger specialization (cached, so repeated calls are safe).
|
||||
self.Gen.ClassGenGeneric._specialize_generic_class(trans, gen_class_name, type_args)
|
||||
def _has_any_func_cvtable(self, cd: ast.ClassDef*) -> int:
|
||||
"""检查类中是否有任何函数标记了 @t.CVTable,返回 1=有 / 0=无"""
|
||||
"""Return 1 if any non-constructor function in the class is marked @t.CVTable."""
|
||||
if cd is None or cd.children is None: return 0
|
||||
children: list[ast.AST*]* = cd.children
|
||||
cn: sizet = len(children)
|
||||
@@ -907,7 +837,7 @@ novirtual class ClassGenBase:
|
||||
if stmt.kind() != ast.ASTKind.FunctionDef: continue
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(stmt)
|
||||
if fd is None or fd.name is None: continue
|
||||
# 跳过构造函数
|
||||
# Skip constructors.
|
||||
if string.strcmp(fd.name, "__init__") == 0: continue
|
||||
if string.strcmp(fd.name, "__before_init__") == 0: continue
|
||||
if string.strcmp(fd.name, "__new__") == 0: continue
|
||||
@@ -915,86 +845,83 @@ novirtual class ClassGenBase:
|
||||
if self._has_decorator(fd.decorator_list, "CVTable") == 1: return 1
|
||||
return 0
|
||||
def _should_method_be_virtual(self, cd: ast.ClassDef*, fd: ast.FunctionDef*, trans: HT.Translator* = None) -> int:
|
||||
"""判断方法是否应该进入虚表,返回 1=包含 / 0=排除"""
|
||||
"""Decide whether a method belongs in the vtable; 1=include, 0=exclude."""
|
||||
if fd is None: return 0
|
||||
# 1. 检查函数级 @t.NoVTable(最高优先级,排除)
|
||||
# 1. Function-level @t.NoVTable has top priority (exclude).
|
||||
if fd.decorator_list is not None:
|
||||
if self._has_decorator(fd.decorator_list, "NoVTable") == 1: return 0
|
||||
# 2. 检查函数级 @t.CVTable(明确包含)
|
||||
# 2. Function-level @t.CVTable (explicitly include).
|
||||
if self._has_decorator(fd.decorator_list, "CVTable") == 1: return 1
|
||||
|
||||
# 3. 检查类级别 @t.CVTable
|
||||
# 3. Class-level @t.CVTable.
|
||||
if cd is not None and cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "CVTable") == 1: return 1
|
||||
|
||||
# 4. 检查继承关系
|
||||
# 4. Check inheritance.
|
||||
parent_name: str = self._get_parent_class(cd, trans)
|
||||
if parent_name is not None:
|
||||
# 用 SHA1 感知查找父类,规避跨模块同名找错
|
||||
# SHA1-aware parent lookup to avoid cross-module same-name collisions.
|
||||
is_nvt: int = 0
|
||||
if trans is not None:
|
||||
p_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(parent_name, trans.ModuleSha1)
|
||||
if p_entry is None:
|
||||
# 跨模块父类回退全局查找(父类注册在父模块 SHA1 下,如 linkedlist.LinkedNode),
|
||||
# 与字段展平(1.5 节)的回退保持一致,否则父类 NoVTable 被误判为有虚表 → 子类插 vptr
|
||||
# 导致子类布局比父类多一个指针,父类方法按无 vptr 布局操作错位
|
||||
# Fall back to a global lookup for cross-module parents; consistent with
|
||||
# field flattening, otherwise a NoVTable parent is misread as having a vptr.
|
||||
p_entry = self.Gen.StructGen.find_struct(parent_name)
|
||||
if p_entry is not None: is_nvt = p_entry.IsNoVTable
|
||||
else: is_nvt = self.Gen.StructGen.is_novtable_by_name(parent_name)
|
||||
if is_nvt != 1:
|
||||
# 继承链统一虚表:父类有虚表 → 方法进虚表;父类无虚表 → 静态方法。
|
||||
# 与 _detect_vtable_status 规则一致,避免父类无 vptr 布局/子类有 vptr 的错位。
|
||||
# vtable follows the parent: parent virtual -> method is virtual, else static.
|
||||
# Matches _detect_vtable_status to keep parent/child layouts aligned.
|
||||
if p_entry is None:
|
||||
return 1 # 父类信息未知,保守进虚表
|
||||
return 1 # unknown parent: conservatively make it virtual
|
||||
if p_entry.HasVTable == 1: return 1
|
||||
return 0
|
||||
|
||||
# 5. 默认排除
|
||||
# 5. Default: exclude.
|
||||
return 0
|
||||
def _detect_vtable_status(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""检测类是否应该启用虚表"""
|
||||
"""Detect whether the class should enable a vtable."""
|
||||
if cd is None: return 0
|
||||
|
||||
# 检查类装饰器
|
||||
# Check class decorators.
|
||||
if cd.decorator_list is not None:
|
||||
# @t.CVTable 优先级最高,直接启用
|
||||
# @t.CVTable has top priority: enable directly.
|
||||
if self._has_decorator(cd.decorator_list, "CVTable") == 1: return 1
|
||||
# @t.NoVTable:类级别禁用,但检查函数级 @t.CVTable
|
||||
# @t.NoVTable disables at class level, but still check function-level @t.CVTable.
|
||||
if self._has_decorator(cd.decorator_list, "NoVTable") == 1: return self._has_any_func_cvtable(cd)
|
||||
|
||||
# 检查是否有真实父类(继承自动启用 CVTable)
|
||||
# Check for a real parent (inheritance enables CVTable automatically).
|
||||
parent_name: str = self._get_parent_class(cd, trans)
|
||||
if parent_name is not None:
|
||||
# 用 SHA1 感知查找父类,规避跨模块同名找错
|
||||
# SHA1-aware parent lookup to avoid cross-module same-name collisions.
|
||||
is_nvt: int = 0
|
||||
if trans is not None:
|
||||
p_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(parent_name, trans.ModuleSha1)
|
||||
if p_entry is None:
|
||||
# 跨模块父类回退全局查找(同 _detect_vtable_status 的回退)
|
||||
# Fall back to a global lookup for cross-module parents.
|
||||
p_entry = self.Gen.StructGen.find_struct(parent_name)
|
||||
if p_entry is not None: is_nvt = p_entry.IsNoVTable
|
||||
else: is_nvt = self.Gen.StructGen.is_novtable_by_name(parent_name)
|
||||
if is_nvt == 1:
|
||||
# 父类是 NoVTable,检查函数级 @t.CVTable
|
||||
# Parent is NoVTable; check function-level @t.CVTable.
|
||||
return self._has_any_func_cvtable(cd)
|
||||
# 继承链统一虚表:父类有虚表 → 子类启用;父类无虚表 → 子类静态。
|
||||
# 若父类无 vtable 而子类有,父类方法按无 vptr 布局(self.field@0)操作
|
||||
# 子类实例(有 vptr@0)会字段错位 → 运行时错误(父类方法读 vptr 当字段)。
|
||||
if p_entry is None: return 1 # 父类信息未知(跨模块/未注册),保守启用虚表
|
||||
# vtable follows the parent: parent virtual -> child virtual, else static.
|
||||
# A vptr mismatch would shift child fields and corrupt runtime access.
|
||||
if p_entry is None: return 1 # unknown/missing parent: conservatively enable vtable
|
||||
if p_entry.HasVTable == 1: return 1
|
||||
return 0
|
||||
|
||||
# 默认(无装饰器无继承),检查函数级 @t.CVTable
|
||||
# Default (no decorator, no parent): check function-level @t.CVTable.
|
||||
return self._has_any_func_cvtable(cd)
|
||||
def InitClassGen(self) -> int:
|
||||
"""初始化 ClassGen 族字段(构造时由 Generator 调用)"""
|
||||
"""Initialize the ClassGen family fields (called by Generator during construction)."""
|
||||
if self is None: return 1
|
||||
self.MethodMetaKeys = None
|
||||
self.MethodMetaFlags = None
|
||||
self.MethodMetaCount = 0
|
||||
# [D30 根治] REnum 变体注册表挂在编译期单例 factory 上(perm_pool 后端),
|
||||
# 与 struct entry 同寿命:此处**恢复**而非清零。清零会让新 Generator
|
||||
# 丢掉前序已注册的 REnum 变体(`bits` NameError 根因),见 CompilerFactory。
|
||||
# REnum variant registry lives on the compile-time factory (perm_pool), so it is
|
||||
# restored here, not cleared: clearing loses variants registered by earlier work.
|
||||
self.RenumClassNames = CompilerFactory.factory.RenumClassNames
|
||||
self.RenumClassVariantCounts = CompilerFactory.factory.RenumClassVariantCounts
|
||||
self.RenumVariantNames = CompilerFactory.factory.RenumVariantNames
|
||||
|
||||
@@ -14,17 +14,13 @@ import lib.core.CompilerFactory as CompilerFactory
|
||||
from . import ClassGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ClassGenDef - ClassGenMixin 拆分(阶段 D):translate_class_def 主入口
|
||||
# ============================================================
|
||||
# ClassGenDef - ClassGenMixin split (stage D): translate_class_def entry point
|
||||
novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
"""translate_class_def 主入口"""
|
||||
"""Entry point for translate_class_def."""
|
||||
|
||||
# ============================================================
|
||||
# translate_class_def — 翻译 class 定义(簇 D)
|
||||
# ============================================================
|
||||
# translate_class_def - translate a class definition (cluster D)
|
||||
def translate_class_def(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 class 定义,返回 0"""
|
||||
"""Translate a class definition; returns 0."""
|
||||
if node is None or trans is None: return 0
|
||||
|
||||
cd: ast.ClassDef* = ast.ClassDef*(node)
|
||||
@@ -34,67 +30,52 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
class_name: str = cd.name
|
||||
|
||||
if trans.ModuleSha1 is None:
|
||||
# fail-fast:翻译类时模块 SHA1 缺失(命名空间隔离依赖它)——
|
||||
# fail-fast: ModuleSha1 missing while translating a class (namespace isolation depends on it)
|
||||
VLogger.error("翻译类时 ModuleSha1 缺失", "CLASS")
|
||||
self.Gen.TypeGenErr.fatal_error(cd, "翻译类时 ModuleSha1 缺失", "TypeError")
|
||||
|
||||
# 命名空间隔离:标记为当前文件可见(必须在 existing 检查之前,
|
||||
# 因为 Phase B 时结构体已注册会提前 return,否则永远无法标记本地类)
|
||||
# Namespace isolation: mark as visible in the current file. Must precede
|
||||
# the existing check, or Phase B's early return would never mark it.
|
||||
self.Gen.StructGen.add_visible_struct(pool, class_name)
|
||||
# 登记"本模块声明了这个类"(键 "{SHA1}.{类名}",与 struct 注册状态无关):
|
||||
# 泛型类型实参命名规范化(TypeGenResolve._norm_type_arg_name 规则 5.5)据此
|
||||
# 判定裸类名归属 —— 类体里的**自引用**(如 includes/ast/base.vp
|
||||
# `AST.children: list[rawptr[AST]]`)解析时本类尚未 register_struct,
|
||||
# 只查注册表会留在裸名 ⇒ 与后续带 SHA1 的形式分裂成两个特化名。
|
||||
# Record that this module declares the class (key "{SHA1}.{name}", independent
|
||||
# of struct registration) so bare self-references resolve to the right module.
|
||||
self.Gen.StructGen.mark_module_class(pool, trans.ModuleSha1, class_name)
|
||||
|
||||
# 枚举类:交给 HandlesEnum 处理
|
||||
# 分派歧义检测(fail-fast):REnum 优先于 CEnum/CUnion。若某类同时被判为枚举,
|
||||
# 说明被 `_is_enum_class` 抢先截胡 → 走 _translate_enum_def ⇒ 变体表永不注册
|
||||
# ⇒ _is_renum_class_name 恒 0、变体构造拿不到 tag。两者语义互斥,禁止静默按枚举处理。
|
||||
# Enum class: handled by HandlesEnum. Fail-fast on dispatch ambiguity: REnum
|
||||
# takes priority over CEnum/CUnion, whose semantics are mutually exclusive.
|
||||
if self.Gen.ClassGenBase._is_enum_class(cd, trans) == 1:
|
||||
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1:
|
||||
self.Gen.TypeGenErr.fatal_error(cd, f"类 '{class_name}' 同时被判定为 CEnum 与 REnum(分派歧义)," f"REnum 变体表将无法注册", "TypeError")
|
||||
return self.Gen.ClassGenEnum._translate_enum_def(trans, cd)
|
||||
|
||||
# 联合体类:交给联合体处理
|
||||
# Union class: handled by the union path
|
||||
if self.Gen.ClassGenBase._is_union_class(cd, trans) == 1: return self.Gen.ClassGenEnum._translate_union_def(trans, cd)
|
||||
|
||||
# REnum 类:交给 REnum 处理({i32 tag, max_payload} 布局)
|
||||
# REnum class: handled by REnum ({i32 tag, max_payload} layout)
|
||||
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1: return self.Gen.ClassGenEnum._translate_renum_def(trans, cd)
|
||||
|
||||
# 泛型类:存储为模板,不发射 IR(等实例化时特化)
|
||||
# 用局部变量接收 cd.type_params,触发类型推断
|
||||
# Generic class: stored as a template, no IR emitted until instantiation.
|
||||
# Bind cd.type_params to a local to drive type inference.
|
||||
tp_val: list[str]* = cd.type_params
|
||||
if self.Gen.ClassGenGeneric._is_generic_class(tp_val) == 1:
|
||||
self.Gen.ClassGenGeneric._register_generic_template(pool, cd, trans.ModuleSha1)
|
||||
return 0
|
||||
|
||||
# 异常类(try-except):注册编码+继承树,不生成 struct
|
||||
# (对齐 TPC HandlesClassDef.py L509-511:_is_exception_class →
|
||||
# _RegisterExceptionClass → return,异常类不编译为结构体)
|
||||
# Exception class (try-except): register encoding and inheritance, no struct
|
||||
# (aligned with TPC HandlesClassDef.py L509-511).
|
||||
if self.Gen.ClassGenBase._is_exception_class_def(cd) == 1: return self.Gen.ClassGenBase._register_exception_class_def(trans, cd)
|
||||
|
||||
# 嵌套类 / 嵌套 struct:类体里再写 class / struct 此前被**静默丢弃**
|
||||
# (字段循环只认 AnnAssign、_translate_oop_methods 只认 FunctionDef),
|
||||
# 现按既有扁平命名先例发射为独立结构体 `{外类}_{内类}`(与 TPC 的
|
||||
# union/renum 嵌套命名 `{ClassName}_{Nested}` 同源)。
|
||||
# 放在此处(enum/union/renum/泛型/异常分派之后、existing 检查之前):
|
||||
# 位于**所有提前 return 之前**,故 declare_only=1/0 两条路径都会递归;
|
||||
# 而 REnum/union 的嵌套类已由各自分支按"变体"处理并提前 return,不会重复发射。
|
||||
# Nested class/struct: emit as a separate struct `{Outer}_{Inner}`, before all
|
||||
# early returns so both declare_only paths recurse and REnum/union don't repeat.
|
||||
self._translate_nested_classdefs(trans, cd)
|
||||
|
||||
# 检查是否已注册(用 SHA1 区分跨模块同名类)
|
||||
# Check if already registered (SHA1 distinguishes same-named classes across modules)
|
||||
existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
|
||||
# 空类注册不完整检测:existing FieldCount==0 但源类体声明了字段(vpi 注入/注册缺陷)。
|
||||
# 标记 _deps_pending 延迟会让空 entry 持久存在、多遍死锁不收敛 ⇒ 改为中和旧 entry
|
||||
# (置空 Name + Ty=None,使 find_struct_by_module 不再命中),走下方完整注册路径重建。
|
||||
# Incomplete empty-class registration: existing FieldCount==0 but the body has
|
||||
# fields, so neutralize the stale entry and rebuild via the full path below.
|
||||
needs_rereg: int = 0
|
||||
# 前向声明占位命中:本类此前被"指向未注册类的指针"注解按名字前向声明过
|
||||
# (StructGenMixin.ensure_forward_class 建了 {sha}.Class 不透明占位)。
|
||||
# 此处**不早退**,走下方"原位填充"分支——把字段体写进同一个 Ty 对象,
|
||||
# 保住指针身份,使所有已发出的 Ptr(占位.Ty) 引用(如 CallGenBase.Gen)
|
||||
# 在填充后自动指向完整定义(对齐 TPC 的 opaque 占位 + set_body 填充)。
|
||||
# Forward-declaration placeholder hit: do not early-return, fall into the
|
||||
# in-place fill branch below so Ptr(placeholder.Ty) refs stay valid.
|
||||
fill_fwd: int = 0
|
||||
if existing is not None:
|
||||
if existing.IsForwardDecl != 0: fill_fwd = 1
|
||||
@@ -105,18 +86,13 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
existing.FieldCount = 0
|
||||
needs_rereg = 1
|
||||
if existing is not None and needs_rereg == 0 and fill_fwd == 0:
|
||||
# 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法
|
||||
# Struct already registered (Phase A-pre or Phase B retranslation), but methods still need translating here
|
||||
if existing.Ty is not None:
|
||||
# Phase 1b(declare_only=0):existing.Ty 是 Phase 1a 注册的 struct_ty,
|
||||
# 其 Name(如 "sha1.ClassName")指向 Phase 1a 的 pool 内存。虽然 mark1
|
||||
# 之前分配不被 reset 回收,但 struct_ty 会被后续文件翻译引用(vtable/
|
||||
# self 参数类型打印),部分实现下 pool 块被复用导致 Name 悬空崩溃。
|
||||
# 保险起见:深拷贝 Name 到 stdlib,确保跨文件永久存活。
|
||||
# Phase 1b (declare_only=0): existing.Ty's Name points into Phase 1a pool
|
||||
# memory, so deep-copy the Name to stdlib to keep it alive cross-file.
|
||||
if trans._declare_only == 0:
|
||||
# 深拷贝整个 existing.Ty 到 stdlib:1a pool Ty 跨 1b reset 悬空,
|
||||
# _ensure_field_types_in_module 遍历悬空字段 ParamNode 链表
|
||||
# 读悬空挂起(G3 LinkedNode.prepend 挂起根因)。DeepCopyType
|
||||
# 递归深拷贝字段链表/子类型/Name 到 stdlib(深度上限+链守卫)。
|
||||
# Deep-copy existing.Ty to stdlib: the 1a pool Ty dangles across the
|
||||
# 1b reset, hanging when the dangling field chain is walked.
|
||||
ty_deep: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, existing.Ty)
|
||||
if ty_deep is not None: existing.Ty = ty_deep
|
||||
old_name: str = mlirlite.get_struct_name(existing.Ty)
|
||||
@@ -125,56 +101,38 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
on_copy: str = CompilerFactory.factory.PermPool.alloc(on_len + 1)
|
||||
if on_copy is not None:
|
||||
string.strcpy(on_copy, old_name)
|
||||
# in-place 覆盖所指 REnum 值(保留 existing.Ty 的指针同一性:
|
||||
# Phase A 已把它登记进 named-type 表,换指针旧条目仍指向悬空
|
||||
# Name)。REnum 变体构造返回 **boxed 指针**,`*槽 = <boxed 变体>`
|
||||
# 属「构造物化」(boxed 指针 → 值槽的整对象拷贝,语义即
|
||||
# load+store),按严格规则③的构造物化例外放行——与
|
||||
# c.DerefAs(ptr, <boxed 变体>) 等价,用 * 写法更直白。
|
||||
# In-place overwrite keeps existing.Ty's pointer identity (Phase A
|
||||
# registered it); *= a boxed REnum variant is a load+store materialization.
|
||||
*existing.Ty = mlirlite.LLVMType.Struct(mlirlite.get_struct_fields(existing.Ty), mlirlite.get_struct_fcount(existing.Ty), on_copy)
|
||||
# Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行)
|
||||
# Phase B: register the existing named struct into the current module
|
||||
mlirlite.module_add_named_type(trans.Module, pool, existing.Ty)
|
||||
# B5:existing 路径(Phase B 已注册类)同样需设置 packed/aligned——
|
||||
# Phase A 注册时虽已设置,但 Phase B 重新 add_named_type 后节点
|
||||
# IsPacked=0,打印输出 `{...}`(idt_entry packed 未生效根因)。
|
||||
# B5: the existing path also needs packed/aligned set, since Phase B's
|
||||
# re-add_named_type resets IsPacked=0 and prints `{...}` (idt_entry root cause).
|
||||
if self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed") != 0:
|
||||
mlirlite.module_set_named_type_packed(trans.Module, pool, existing.Ty, 1)
|
||||
# StructTable 每 Phase 可能重建(existing 是 Phase B 新 entry),
|
||||
# 必须补设 IsPacked——否则跨模块 stub 注入(ensure_struct_def
|
||||
# _in_module)按 entry.IsPacked 判定 packed 时读 0 → stub 非 packed。
|
||||
# StructTable may be rebuilt per phase, so IsPacked must be re-set or
|
||||
# cross-module stub injection reads 0 and emits a non-packed stub.
|
||||
existing.IsPacked = 1
|
||||
# 字段类型补齐(与下方"全新定义"路径同口径):值类型字段(结构体本体 /
|
||||
# 结构体数组)必须在当前模块有**完整定义**,只给 opaque 会让本类 unsized
|
||||
# ⇒ 本模块内对本类做带字段下标的 GEP 时 llc 报
|
||||
# "base element of getelementptr must be sized"(现场:includes/
|
||||
# condition.vp 的 Condition{cond_var: CONDITION_VARIABLE,
|
||||
# cs: CRITICAL_SECTION},后两者定义在 w32/win32sync.vp,新近由
|
||||
# `t.CTypedef = VOIDPTR` 改成 class)。指针字段只需 opaque 声明。
|
||||
# 本条路径是 includes 在 Phase A 预注册、Phase B 重新翻译时走的
|
||||
# 早退分支(不经过下方全新定义路径的字段扫描循环)。
|
||||
# Complete field types like the fresh-definition path: value-type fields
|
||||
# need full definitions here or this class becomes unsized for GEP.
|
||||
self.Gen.StructGen._ensure_field_types_in_module(pool, trans.Module, existing.Ty)
|
||||
align_ex: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list)
|
||||
if align_ex > 0 and existing.Align == 0: existing.Align = align_ex
|
||||
# Phase A-pre(declare_only=1)多遍扫描时,类已注册但不应翻译方法体
|
||||
# Phase B(declare_only=0)时全量翻译方法体
|
||||
# Phase A-pre (declare_only=1): class registered, skip method bodies.
|
||||
# Phase B (declare_only=0): translate method bodies in full.
|
||||
if trans._declare_only == 1: self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name, 1)
|
||||
else: self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name)
|
||||
# SHA1 已在 register_struct 时设置,无需再补
|
||||
# SHA1 is already set during register_struct, no need to re-set
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 触发 base 中 Subscript 的泛型特化(如 GSListNode[Value])
|
||||
# 必须在 _detect_vtable_status 和父类字段继承之前调用,
|
||||
# 否则 find_struct_by_module("GSListNode[Value]") 返回 None
|
||||
# ============================================================
|
||||
# Trigger generic specialization of Subscripts in base (e.g. GSListNode[Value])
|
||||
# before _detect_vtable_status and field inheritance, or the lookup returns None.
|
||||
self.Gen.ClassGenBase._trigger_base_specialization(trans, cd)
|
||||
|
||||
# ============================================================
|
||||
# VTable 检测:判断是否启用虚表
|
||||
# ============================================================
|
||||
# VTable detection: decide whether a vtable is enabled
|
||||
has_vtable: int = self.Gen.ClassGenBase._detect_vtable_status(cd, trans)
|
||||
parent_name: str = self.Gen.ClassGenBase._get_parent_class(cd, trans)
|
||||
# 多继承:收集所有真实父类名(跳过 marker base),供字段展平/vtable/MRO 使用
|
||||
# Multiple inheritance: collect real parent names (skip marker bases) for flatten/vtable/MRO
|
||||
parents_buf: sizet* = pool.alloc(8 * GenTypes.PARENT_MAX)
|
||||
parents_count: int = 0
|
||||
if parents_buf is not None: parents_count = self.Gen.ClassGenBase._collect_parents(pool, cd, parents_buf, GenTypes.PARENT_MAX, trans)
|
||||
@@ -182,40 +140,27 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
if cd.decorator_list is not None:
|
||||
if self.Gen.ClassGenBase._has_decorator(cd.decorator_list, "NoVTable") == 1: is_novtable_deco = 1
|
||||
|
||||
# ============================================================
|
||||
# PhaseA 预注册延迟检查:父类未注册时延迟注册当前类
|
||||
#
|
||||
# 根因:AssignHandle(HandlesBase.Mixin) 有自己的字段 _CurrentClass,
|
||||
# 当 Mixin 未注册时(字母序:HandlesAssign.py 在 HandlesBase.py 之前),
|
||||
# field_count=1(不是0),field_count==0 静默返回不触发,
|
||||
# AssignHandle 被注册为只有 _CurrentClass 字段(缺 Trans)。
|
||||
# 后续遍 existing is not None,不补字段 → attribute ptr is None: self.Trans
|
||||
#
|
||||
# 修复:declare_only==1 且父类未注册时,延迟注册到下一遍。
|
||||
# 多遍 PhaseA-pre 会在后续遍中重试(父类已注册后完整注册)。
|
||||
# Phase B(declare_only==0)不延迟,避免死锁。
|
||||
# ============================================================
|
||||
# PhaseA pre-registration deferral: when the parent is unregistered, defer the
|
||||
# current class to a later pass. Phase B (declare_only==0) never defers.
|
||||
if trans._declare_only == 1 and parent_name is not None:
|
||||
parent_check: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(parent_name, trans.ModuleSha1)
|
||||
if parent_check is None: parent_check = self.Gen.StructGen.find_struct(parent_name)
|
||||
if parent_check is None: return 0
|
||||
|
||||
# ============================================================
|
||||
# 1. 遍历 class body,收集字段信息
|
||||
# ============================================================
|
||||
# 用 CSizeT 数组存储指针值(64 位系统上 8 字节)
|
||||
# 1. Walk the class body and collect field info
|
||||
# Use CSizeT arrays to store pointer values (8 bytes on 64-bit systems)
|
||||
field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_defaults_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_annot_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# 字段 volatile 标记缓冲(1=字段注解联合含 t.CVolatile);与 field_annot_buf
|
||||
# 并行,取值 0/1(sizet 数组复用,避免另设类型)。
|
||||
# Field volatile flag buffer (1 = annotation union contains t.CVolatile), parallel
|
||||
# to field_annot_buf with values 0/1.
|
||||
field_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# 字段「所指物 volatile」标记缓冲(1=字段注解为 rawptr[t.CVolatile | T],
|
||||
# 等价 C 的 volatile T*);与 field_volatile_buf 并行,取值 0/1。
|
||||
# Field pointee-volatile flag buffer (1 = rawptr[t.CVolatile | T], i.e. volatile T*),
|
||||
# parallel to field_volatile_buf with values 0/1.
|
||||
field_pointee_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# 字段 const 对象标记缓冲(1=字段注解含 t.CConst 且字段类型非指针);
|
||||
# 与 field_volatile_buf 并行,取值 0/1。
|
||||
# Field const-object flag buffer (1 = annotation has t.CConst and the type is not
|
||||
# a pointer), parallel to field_volatile_buf with values 0/1.
|
||||
field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None: return 0
|
||||
if field_annot_buf is None or field_volatile_buf is None: return 0
|
||||
@@ -230,34 +175,23 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
|
||||
field_count: int = 0
|
||||
|
||||
# ============================================================
|
||||
# 1.5 继承父类字段(字段展平)
|
||||
# ============================================================
|
||||
#
|
||||
# 父类字段排在子类字段之前,结构体布局:
|
||||
# [vtable_ptr?, parent_field1, ..., child_field1, ...]
|
||||
# 跳过父类的 __vtable__ 字段(子类会有自己的 vtable 指针)
|
||||
# ============================================================
|
||||
# 多继承字段展平:遍历所有父类(声明顺序),字段名去重(先声明优先)
|
||||
# 1.5 Inherit parent fields (field flattening): parent fields precede child
|
||||
# fields, layout [vtable_ptr?, parent..., child...]; dedup by name (first wins).
|
||||
if parents_count > 0 and parents_buf is not None:
|
||||
for pi in range(parents_count):
|
||||
pname_addr: sizet = parents_buf[pi]
|
||||
if pname_addr == 0: continue
|
||||
pname: str = rawptr(pname_addr)
|
||||
if pname is None: continue
|
||||
# 用 SHA1 感知查找父类,规避跨模块同名找错
|
||||
# SHA1-aware parent lookup to avoid picking a same-named class in another module
|
||||
parent_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(pname, trans.ModuleSha1)
|
||||
if parent_entry is None:
|
||||
# 父类可能定义在另一个模块(不同 SHA1,如 HandlesBase.Mixin),
|
||||
# find_struct_by_module 不回退全局查找,此处手动回退到 find_struct,
|
||||
# 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败
|
||||
# The parent may live in another module (different SHA1), and
|
||||
# find_struct_by_module does not fall back, so query find_struct manually.
|
||||
parent_entry = self.Gen.StructGen.find_struct(pname)
|
||||
if parent_entry is None:
|
||||
# 父类尚未注册(Phase A 按文件序,子类模块可能先于父类模块):此时
|
||||
# **不能**继续注册——StructTable 在 StdlibAlloc=1 时用 perm_pool 分配,
|
||||
# 残缺条目跨 pass 存活,后续 pass 的 existing 早退永远无法修复 → 子类
|
||||
# __init__ 写父类字段每遍永久失败。整类 defer(不注册);模块级变量/函数
|
||||
# 参数解析失败静默降级 i32,不致命。
|
||||
# Parent not yet registered: do not register a partial entry (perm_pool
|
||||
# entries survive passes and the existing early-return never repairs it).
|
||||
return 0
|
||||
pfc: int = parent_entry.FieldCount
|
||||
for pfi in range(pfc):
|
||||
@@ -265,9 +199,9 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
if pfe is None: continue
|
||||
pfn: str = self.Gen.StructGen.get_field_name_ptr(pfe)
|
||||
if pfn is None: continue
|
||||
# 跳过父类的 vtable 指针字段
|
||||
# Skip the parent's vtable pointer field
|
||||
if string.strcmp(pfn, "__vtable__") == 0: continue
|
||||
# 多继承去重:字段名已存在跳过(先声明父类优先)
|
||||
# Multi-inheritance dedup: skip existing field names (earlier parent wins)
|
||||
if self.Gen.ClassGenBase._field_name_exists(field_names_buf, field_count, pfn) != 0: continue
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
pft: mlirlite.LLVMType* = self.Gen.StructGen.get_field_type_ptr(pfe)
|
||||
@@ -281,11 +215,11 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
stored_val: sizet = sizet(pfa)
|
||||
field_annot_buf[field_count] = stored_val
|
||||
else: field_annot_buf[field_count] = 0
|
||||
# 继承父类字段的 volatile 标记(字段展平后语义不变)
|
||||
# Inherit the volatile flag (semantics unchanged after flattening)
|
||||
field_volatile_buf[field_count] = sizet(pfe.IsVolatile)
|
||||
# 继承父类字段的「所指物 volatile」标记(展平后语义不变)
|
||||
# Inherit the pointee-volatile flag (unchanged after flattening)
|
||||
field_pointee_volatile_buf[field_count] = sizet(pfe.PointeeVolatile)
|
||||
# 继承父类字段的 const 对象标记(展平后语义不变)
|
||||
# Inherit the const-object flag (unchanged after flattening)
|
||||
field_const_buf[field_count] = sizet(pfe.IsConst)
|
||||
field_count += 1
|
||||
|
||||
@@ -304,22 +238,17 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||||
if aa is None or aa.target is None: continue
|
||||
|
||||
# 获取字段名
|
||||
# Get the field name
|
||||
if aa.target.kind() != ast.ASTKind.Name: continue
|
||||
tgt: ast.Name* = ast.Name*(aa.target)
|
||||
if tgt.id is None: continue
|
||||
|
||||
# 类体 CDefine 常量(任意位置,2026-08-10):注册到全局 CDefine 表。
|
||||
# 方法内裸名(如 ISINSTANCE_CLASS_MAX)经 translate_name_value 查
|
||||
# CDefine 表解析为常量值;类体开头的常量(MRO_MAX 等)由早期预注册
|
||||
# 路径注册,但方法之后的 AnnAssign 不经过那些路径 → 此处统一兜底注册。
|
||||
# Class-body CDefine constant (any position): register into the global CDefine
|
||||
# table so bare names inside methods resolve; catches post-method AnnAssigns.
|
||||
if self.Gen.AssignGen.is_cdefine_annotation(aa.annotation) != 0:
|
||||
if aa.value is not None: self.Gen.AssignGen._register_cdefine_from_value(pool, tgt.id, aa.value, aa.annotation)
|
||||
# CDefine 字段类型固定 i32,直接收集后跳过通用注解解析:
|
||||
# resolve_annotation_type 不识别 "CDefine"(map_t_type/CTypedef
|
||||
# 表均无此名),类体尾部常量(ISINSTANCE_CLASS_MAX 是首个触发
|
||||
# 该路径的实例)Phase B 会 NameError: 't.CDefine' is not defined。
|
||||
# 与类头常量(MRO_MAX 等)的 i32 字段语义保持一致。
|
||||
# CDefine field type is fixed i32, collected directly to skip generic annotation
|
||||
# resolution (which does not know "CDefine") and avoid a Phase B NameError.
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
cdef_ty_l: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if cdef_ty_l is not None:
|
||||
@@ -330,10 +259,8 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
field_count += 1
|
||||
continue
|
||||
|
||||
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
|
||||
# 这些字段只用于 with 上下文的静态可达性检查,不生成运行时代码
|
||||
# (FakeDuck:_str.__requires__=['__mbuddy__'])。此处解析 list[str]
|
||||
# 字面量注册到持久元数据表,供 requires 注入 / with provider 查询。
|
||||
# Skip compile-time metadata fields (__provides__/__requires__/__require_must__):
|
||||
# they only feed static with-context reachability checks, no runtime code.
|
||||
if string.strcmp(tgt.id, "__provides__") == 0:
|
||||
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_PROVIDES, class_name, aa.value)
|
||||
continue
|
||||
@@ -344,33 +271,21 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRE_MUST, class_name, aa.value)
|
||||
continue
|
||||
|
||||
# 解析字段类型
|
||||
# Resolve the field type
|
||||
field_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
# void 不是合法字段类型(用户 2026-10-01:void 只能用于 `void*` 与
|
||||
# `typedef 不透明结构体 = void`);`void*` 已在上游归并为 i8*,不会命中。
|
||||
# 放任会按 LLVM void 建字段槽 ⇒ 结构体布局非法(拖到 llc 才暴露),
|
||||
# 故此处编译期 fail-fast。
|
||||
# void is not a valid field type (only for `void*` and opaque typedefs); letting
|
||||
# it through builds an illegal layout, so fail-fast at compile time here.
|
||||
if field_ty is not None and self.Gen.StmtGenDecl._is_void_type(field_ty) != 0: self.Gen.TypeGenErr.fatal_error(aa, f"字段 '{tgt.id}' 不能是 void 类型(void 只能用于 void* 与 typedef 别名)", "TypeError")
|
||||
# 单参 t.CArray[elem] + 列表字面量 → 静态数组 [N x elem]
|
||||
# (与局部 AnnAssign 路径一致,对齐 TPC ParseListAnnotation 语义):
|
||||
# 否则字段保持 Ptr(elem)(如 t.CArray[str] → i8**),
|
||||
# for x in self.field 会误走指针迭代(元素非整数 → null 终止符
|
||||
# 检查失败报错)。推断为静态数组后走数组迭代(按长度 N)。
|
||||
# Single-arg t.CArray[elem] with a list literal -> static array [N x elem] (like
|
||||
# local AnnAssign); otherwise it stays Ptr(elem) and for-in iterates pointers wrongly.
|
||||
if field_ty is not None: field_ty = self.Gen.AssignGen._infer_static_array_ty_from_literal(pool, aa.annotation, aa.value, field_ty)
|
||||
if field_ty is None:
|
||||
# BUG 5 方案1修改点1:消除 i32 静默回退
|
||||
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,延迟到下一遍
|
||||
# Phase B(declare_only=0):真正的类型解析错误,fast-fail 终止该类翻译
|
||||
# 不再用 i32 回退(会导致结构体布局错误 + GEP 索引错乱)
|
||||
# 例外:泛型 Subscript(如 list[AST*])未特化时回退为 Ptr(i8),
|
||||
# 因为泛型特化可能延迟到构造调用时。AnnotClassName 会携带 mangled name
|
||||
# 供 translate_attribute 做后续 bitcast。
|
||||
# BUG 5 fix: remove the silent i32 fallback. Phase A-pre defers to the next
|
||||
# pass; Phase B fast-fails. Generic Subscripts fall back to Ptr(i8) instead.
|
||||
is_generic_sub: int = 0
|
||||
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript:
|
||||
# t.CArray[elem, N](含裸 CArray[elem, N])是定长值数组,非泛型堆对象——
|
||||
# 元素类型未就绪时若回退 Ptr(i8) 会注册成退化字段布局,Phase B 复用该 entry
|
||||
# 后 GEP 元素成 i8、字段访问/赋值全部失效。仅真正的泛型类(list[T]/Vector[T]
|
||||
# 等堆对象)未特化才回退 Ptr(i8)(AnnotClassName 携带 mangled name 供后续恢复)。
|
||||
# t.CArray[elem, N] is a fixed-size value array, not a generic heap object;
|
||||
# falling back to Ptr(i8) would register a degraded layout Phase B reuses.
|
||||
sub_g: ast.Subscript* = ast.Subscript*(aa.annotation)
|
||||
is_carray_sub_g: int = 0
|
||||
if sub_g is not None and sub_g.value is not None:
|
||||
@@ -383,47 +298,44 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
is_carray_sub_g = 1
|
||||
if is_carray_sub_g == 0: is_generic_sub = 1
|
||||
if is_generic_sub != 0:
|
||||
# 泛型未特化:回退为 Ptr(i8),依赖 AnnotClassName 做后续类型恢复
|
||||
# Generic not specialized: fall back to Ptr(i8); AnnotClassName restores it later
|
||||
field_ty = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
else:
|
||||
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,
|
||||
# 静默 return 0 让多遍收敛机制生效(下一遍依赖类已注册即可成功)
|
||||
# Phase A-pre (declare_only=1): a dependency may not be registered yet, so
|
||||
# silently return 0 and let the multi-pass convergence retry.
|
||||
if trans._declare_only == 1: return 0
|
||||
# Phase A(declare_only=0,Phase2.vp L573 多遍):依赖模块按
|
||||
# 文件序可能晚于本模块翻译(如 TypeGenMixin.vp G < HandlesType.vp
|
||||
# H,TraceFrames 注解的 HandlesType.TraceFrame 未注册)→ 不在本遍
|
||||
# 以残缺字段注册结构体。
|
||||
# Phase A (declare_only=0, multi-pass): a dependency module may translate
|
||||
# after this one, so do not register the struct with incomplete fields.
|
||||
return 0
|
||||
# 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时
|
||||
# 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底)
|
||||
# Old behavior fast-failed with NameError (removed: it misreported while
|
||||
# dependencies were pending; the convergence check handles real errors).
|
||||
|
||||
# 存储到数组(指针转 CSizeT)
|
||||
# Store into the array (pointer cast to CSizeT)
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
diag_name_val: sizet = sizet(tgt.id)
|
||||
field_names_buf[field_count] = diag_name_val
|
||||
field_types_buf[field_count] = sizet(field_ty)
|
||||
# 存储默认值 AST 指针(None=无默认值)
|
||||
# Store the default-value AST pointer (None = no default)
|
||||
if aa.value is not None: field_defaults_buf[field_count] = sizet(aa.value)
|
||||
else: field_defaults_buf[field_count] = 0
|
||||
# 提取原始类型注解的类名(联合类型简化为 Ptr(i8) 时回退查找结构体)
|
||||
# BUG 1 方案3:传 pool 使泛型 Subscript 返回 mangled name(如 "list[ast.AST*]")
|
||||
# Extract the class name from the raw annotation (used to find the struct when a
|
||||
# union degrades to Ptr(i8)); pass pool so generic Subscripts return a mangled name.
|
||||
if aa.annotation is not None:
|
||||
annot_cn: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(aa.annotation, trans._imported_modules, pool, trans._from_imports)
|
||||
if annot_cn is not None:
|
||||
diag_cn_val: sizet = sizet(annot_cn)
|
||||
field_annot_buf[field_count] = diag_cn_val
|
||||
else: field_annot_buf[field_count] = 0
|
||||
# t.CVolatile 存储类标记 → 该字段经对象访问的 load/store 带
|
||||
# volatile(如 Spinlock.locked: t.CVolatile | t.CInt)。
|
||||
# t.CVolatile storage-class flag: loads/stores through this field are
|
||||
# volatile (e.g. Spinlock.locked: t.CVolatile | t.CInt).
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "CVolatile", 0) != 0: field_volatile_buf[field_count] = 1
|
||||
else: field_volatile_buf[field_count] = 0
|
||||
# rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
|
||||
# volatile T*)→ 经该字段解引用(self.ptr[i]/self.ptr.field)
|
||||
# 的 load/store 带 volatile。
|
||||
# rawptr[t.CVolatile | T] (volatile pointee, i.e. C volatile T*): derefs
|
||||
# through this field (self.ptr[i]/self.ptr.field) are volatile.
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0: field_pointee_volatile_buf[field_count] = 1
|
||||
else: field_pointee_volatile_buf[field_count] = 0
|
||||
# const 对象字段(注解含 t.CConst 且字段类型非指针)→ 经对象给该
|
||||
# 字段赋值 fail-fast。判据与 TPC 的 Gen.field_const_flags 一致。
|
||||
# const object field (annotation has t.CConst, non-pointer type): assigning
|
||||
# through the object fail-fasts, matching TPC's Gen.field_const_flags.
|
||||
if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, field_ty) != 0: field_const_buf[field_count] = 1
|
||||
else: field_const_buf[field_count] = 0
|
||||
else:
|
||||
@@ -434,23 +346,19 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
field_count += 1
|
||||
|
||||
if field_count == 0:
|
||||
# PhaseA 预注册时,父类可能尚未注册(字母序单遍扫描),
|
||||
# 导致子类无法继承父类字段 → field_count==0。
|
||||
# 静默返回,Phase2.py 的多遍 PhaseA-pre 会在后续遍中重试。
|
||||
# During PhaseA pre-registration the parent may be unregistered (single alphabetic
|
||||
# pass), so field_count==0; return silently and retry in a later pass.
|
||||
if trans._declare_only == 1 and parent_name is not None: return 0
|
||||
# 无字段类(接口/标记类):不能整体跳过——类可能只有方法
|
||||
# (如多继承中的 ShapeM/Vec2Extra),需注册类并翻译方法。
|
||||
# 空 struct 在 LLVM 中合法({});@t.CVTable 类由下方补 vtable 指针字段。
|
||||
# Fieldless class (interface/marker): do not skip it, as it may hold only methods
|
||||
# (e.g. ShapeM/Vec2Extra); an empty struct is legal in LLVM.
|
||||
|
||||
# ============================================================
|
||||
# 2. 创建 LLVM StructType
|
||||
# ============================================================
|
||||
# 构建 ParamNode 链表
|
||||
# 2. Create the LLVM StructType
|
||||
# Build the ParamNode chain
|
||||
first_node: rawptr[mlirlite.ParamNode] = None
|
||||
prev_node: rawptr[mlirlite.ParamNode] = None
|
||||
total_field_count: int = field_count
|
||||
|
||||
# 如果有虚表,首字段添加 i8* vtable 指针
|
||||
# With a vtable, add an i8* vtable pointer as the first field
|
||||
if has_vtable == 1:
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
vtable_node: rawptr[mlirlite.ParamNode] = pool.alloc(mlirlite.ParamNode.__sizeof__())
|
||||
@@ -468,7 +376,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
if fty_addr == 0: continue
|
||||
fty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr)
|
||||
|
||||
# 创建 ParamNode
|
||||
# Create the ParamNode
|
||||
pnode: rawptr[mlirlite.ParamNode] = pool.alloc(mlirlite.ParamNode.__sizeof__())
|
||||
if pnode is None: continue
|
||||
string.memset(pnode, 0, mlirlite.ParamNode.__sizeof__())
|
||||
@@ -478,10 +386,8 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
if prev_node is not None: prev_node.Next = pnode
|
||||
prev_node = pnode
|
||||
|
||||
# 构造命名结构体类型名: "sha1.ClassName" 或 "ClassName"
|
||||
# 注意: Phase 1b(declare_only=0)时 struct_ty 会注册到全局表并被后续
|
||||
# 文件翻译引用(vtable/self 参数类型打印),其 Name 必须跨 1b 每文件
|
||||
# reset_to(mark1) 存活 → 用 stdlib 深拷贝;Phase 1a 仍用 pool(无需跨文件)。
|
||||
# Build the named struct type name "sha1.ClassName" or "ClassName". In Phase 1b the
|
||||
# Name must survive per-file reset_to(mark1), so deep-copy to stdlib; 1a uses pool.
|
||||
type_name: str = class_name
|
||||
if trans.ModuleSha1 is not None:
|
||||
name_buf: str = pool.alloc(64)
|
||||
@@ -495,31 +401,22 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
string.strcpy(tn_copy, type_name)
|
||||
type_name = tn_copy
|
||||
|
||||
# 创建 StructType(命名结构体)
|
||||
# Create the StructType (named struct)
|
||||
struct_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, first_node, total_field_count, type_name)
|
||||
if struct_ty is None:
|
||||
VLogger.error(f"failed to create StructType for {class_name}", "CLASS")
|
||||
return 0
|
||||
|
||||
# Phase 1b(declare_only=0)新注册的 struct_ty 会被注册到全局表并被后续文件
|
||||
# 翻译引用(vtable/self 参数类型打印/find_struct_by_type 指针匹配)。LLVMType
|
||||
# 对象和 Fields 链是 pool 分配的,本文件 reset_to(mark1) 后悬空。深拷贝整棵树
|
||||
# 到 stdlib,并替换 struct_ty 指针,保证 entry.Ty 与调用方持有的 struct_ty
|
||||
# 指向同一个 stdlib 拷贝(指针匹配不失效)且跨文件永久存活。
|
||||
# StdlibAlloc=1(Phase2 mark2 后)也必须深拷贝:Phase A-pre-inc(declare_only=1)
|
||||
# 预注册 includes 时若仍用 pool struct_ty,reset_to(mark2) 后 entry.Ty 悬空 →
|
||||
# 后续文件字段类型引用(resolve → Ptr(entry.Ty))→ opaque 扫描遍历悬空 fields
|
||||
# 崩溃(LoopTest _scan_type_for_cross_module_refs 0xFFFF 根因)。
|
||||
# Deep-copy the whole struct_ty tree to stdlib and replace the pointer so entry.Ty
|
||||
# and callers share one surviving copy; required too under StdlibAlloc=1.
|
||||
if trans._declare_only == 0 or self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
st_copy: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, struct_ty)
|
||||
if st_copy is not None:
|
||||
struct_ty = st_copy
|
||||
type_name = mlirlite.get_struct_name(struct_ty)
|
||||
|
||||
# 前向声明占位命中:**原位填充**——保持 existing.Ty 指针身份不变,仅把刚构建
|
||||
# 的字段体写进同一个类型对象(名称沿用占位的 "{sha}.Class")。此后所有已发出的
|
||||
# Ptr(占位.Ty) 引用(如 CallGenBase.Gen)自动指向完整定义。这一步必须早于
|
||||
# module_add_named_type:避免同一名字先以占位名注册、又以新对象重复注册。
|
||||
# Forward-declaration hit: in-place fill keeps existing.Ty's identity, writing the
|
||||
# field body into the same object before module_add_named_type to avoid re-registration.
|
||||
if fill_fwd != 0 and existing is not None and existing.Ty is not None:
|
||||
_fwd_name: str = mlirlite.get_struct_name(existing.Ty)
|
||||
_fwd_fields: rawptr[mlirlite.ParamNode] = mlirlite.get_struct_fields(struct_ty)
|
||||
@@ -530,63 +427,53 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
existing.IsForwardDecl = 0
|
||||
existing.FieldCount = 0
|
||||
|
||||
# 注册命名结构体到模块(输出 %"name" = type { ... } 定义行)
|
||||
# Register the named struct into the module (emit the %"name" = type { ... } line)
|
||||
mlirlite.module_add_named_type(trans.Module, pool, struct_ty)
|
||||
|
||||
# B5:@c.Attribute(t.attr.packed) 打包结构体 → 类型定义输出 <{...}>
|
||||
# (LLVM packed struct,无字段填充,GEP 按 packed 布局计算偏移)。
|
||||
# idt/gdt/elf/fat32 等二进制格式结构体依赖它读写硬件/磁盘布局。
|
||||
# B5: @c.Attribute(t.attr.packed) emits <{...}> (LLVM packed struct, no padding) as
|
||||
# needed by binary-format structs like idt/gdt/elf/fat32.
|
||||
is_packed_cd: int = self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed")
|
||||
if is_packed_cd != 0: mlirlite.module_set_named_type_packed(trans.Module, pool, struct_ty, 1)
|
||||
|
||||
# 扫描字段类型:跨模块结构体引用在当前模块补声明
|
||||
# 例如 list[int].__pool__ 引用 memhub.MemManager,需要在当前模块添加 %"sha1.MemManager" = type opaque
|
||||
# Scan field types and declare cross-module struct refs in the current module, e.g.
|
||||
# list[int].__pool__ referencing memhub.MemManager needs a %"sha1.MemManager" opaque.
|
||||
for fi in range(field_count):
|
||||
fty_addr_op: sizet = field_types_buf[fi]
|
||||
if fty_addr_op == 0: continue
|
||||
fty_op: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr_op)
|
||||
if self.Gen.ExprGenBase.is_ptr_type(fty_op) != 0:
|
||||
# 指针字段:指向物只需 opaque 声明(GEP 不按字段下标解引用指向物本体)
|
||||
# Pointer field: the pointee only needs an opaque declaration (GEP does not index into it)
|
||||
mlirlite.module_ensure_opaque_for_type(trans.Module, pool, fty_op)
|
||||
else:
|
||||
# 值类型字段(结构体本体 / 结构体数组):必须复制**完整定义**。只给
|
||||
# opaque 会让**本类**变成 unsized(LLVM:含 unsized 成员的结构体自身
|
||||
# unsized)⇒ 本模块内对本类做带字段下标的 GEP 时 llc 报
|
||||
# "base element of getelementptr must be sized"。
|
||||
# 现场:includes/condition.vp 的 Condition{cond_var: CONDITION_VARIABLE,
|
||||
# cs: CRITICAL_SECTION}——后两者定义在 w32/win32sync.vp,且新近由
|
||||
# `t.CTypedef = VOIDPTR` 改成 class(指针层级收紧后 c.Addr 需要一层指针)。
|
||||
# _ensure_field_types_in_module 负责数组元素结构体的完整定义。
|
||||
# Value-type field (struct body / struct array): copy the full definition, or
|
||||
# this class becomes unsized and llc rejects field-indexed GEPs.
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, trans.Module, fty_op)
|
||||
self.Gen.StructGen._ensure_field_types_in_module(pool, trans.Module, fty_op)
|
||||
|
||||
# ============================================================
|
||||
# 3. 注册到 HandlesStruct
|
||||
# ============================================================
|
||||
# 3. Register into HandlesStruct
|
||||
entry: GenTypes.StructEntry* = None
|
||||
if fill_fwd != 0 and existing is not None:
|
||||
# 前向声明占位:字段体已原位写入 existing.Ty,登记条目沿用 existing
|
||||
# (不新建条目/Ty,否则已发出的 Ptr(占位.Ty) 引用会指向旧空对象)。
|
||||
# Forward-declaration placeholder: the field body is already in existing.Ty, so
|
||||
# reuse existing (a new entry/Ty would orphan issued Ptr(placeholder.Ty) refs).
|
||||
entry = existing
|
||||
else: entry = self.Gen.StructGen.register_struct(pool, class_name, struct_ty, trans.ModuleSha1)
|
||||
if entry is None:
|
||||
VLogger.error(f"failed to register {class_name}", "CLASS")
|
||||
return 0
|
||||
|
||||
# B5:记录 packed 标记到 StructEntry(供 __sizeof__/字段布局等派生查询)
|
||||
# B5: record the packed flag into StructEntry for __sizeof__/layout queries
|
||||
if is_packed_cd != 0: entry.IsPacked = 1
|
||||
# B5:记录 aligned 对齐值(@c.Attribute(t.attr.aligned(N)),如 fpu_context 16 对齐),
|
||||
# 供全局变量/局部 alloca 生成时施加对齐。
|
||||
# B5: record the aligned value (@c.Attribute(t.attr.aligned(N)), e.g. fpu_context 16)
|
||||
# for global/local alloca alignment.
|
||||
align_cd: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list)
|
||||
if align_cd > 0: entry.Align = align_cd
|
||||
|
||||
# 标记 VTable 状态(直接用 entry,规避跨模块同名 find_struct 找错)
|
||||
# Mark vtable status (use entry directly to avoid same-named cross-module lookup errors)
|
||||
if has_vtable == 1: entry.HasVTable = 1
|
||||
if is_novtable_deco == 1: entry.IsNoVTable = 1
|
||||
if parent_name is not None:
|
||||
# 1b 阶段父类名是 pool 字符串,reset 后悬空;_translate_oop_methods/
|
||||
# _detect_vtable_status 会在后续文件 find_struct(ParentName) strcmp 解引用
|
||||
# → 用 stdlib 拷贝保证跨文件存活。
|
||||
# In 1b the parent name is a pool string that dangles after reset, and later files
|
||||
# strcmp it via find_struct, so copy it to stdlib for cross-file survival.
|
||||
if self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
pn_len: sizet = string.strlen(parent_name)
|
||||
pn_buf: str = CompilerFactory.factory.PermPool.alloc(pn_len + 1)
|
||||
@@ -594,11 +481,11 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
string.strcpy(pn_buf, parent_name)
|
||||
entry.ParentName = pn_buf
|
||||
else: entry.ParentName = parent_name
|
||||
# 多继承:设置父类数组(供 MRO/字段/vtable 查询,stdlib 深拷贝跨文件存活)
|
||||
# Multiple inheritance: set the parent array for MRO/field/vtable queries (stdlib-deep-copied)
|
||||
if parents_count > 0 and parents_buf is not None: self.Gen.StructGen.set_parent_names(pool, class_name, parents_buf, parents_count)
|
||||
|
||||
# 添加字段信息
|
||||
# 如果有虚表,先添加 __vtable__ 字段占位(索引 0),用户字段从索引 1 开始
|
||||
# Add field info. With a vtable, add the __vtable__ placeholder first (index 0),
|
||||
# so user fields start at index 1.
|
||||
if has_vtable == 1:
|
||||
i8_ptr_ty2: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
self.Gen.StructGen.add_field(pool, entry, "__vtable__", i8_ptr_ty2, None, None)
|
||||
@@ -610,63 +497,44 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
fty_addr2: sizet = field_types_buf[fi]
|
||||
if fty_addr2 == 0: continue
|
||||
fty2: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr2)
|
||||
# 恢复默认值 AST 指针
|
||||
# Restore the default-value AST pointer
|
||||
fdef_addr: sizet = field_defaults_buf[fi]
|
||||
fdef: ast.AST* = None
|
||||
if fdef_addr != 0: fdef = (ast.AST*)(fdef_addr)
|
||||
# 恢复注解类名指针
|
||||
# Restore the annotation class-name pointer
|
||||
fannot_addr: sizet = field_annot_buf[fi]
|
||||
fannot: str = None
|
||||
if fannot_addr != 0: fannot = str(fannot_addr)
|
||||
if fname is not None and fty2 is not None:
|
||||
fidx_add: int = self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
|
||||
# t.CVolatile 字段标记(field_volatile_buf[fi] 与 add_field 顺序一一对应)
|
||||
# t.CVolatile field flag (field_volatile_buf[fi] corresponds 1:1 with add_field order)
|
||||
if fidx_add >= 0 and field_volatile_buf[fi] != 0:
|
||||
fe_vol: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_vol is not None: fe_vol.IsVolatile = 1
|
||||
# rawptr[t.CVolatile | T] 字段标记(field_pointee_volatile_buf[fi] 与
|
||||
# add_field 顺序一一对应):经该字段解引用时 load/store 带 volatile。
|
||||
# rawptr[t.CVolatile | T] field flag (field_pointee_volatile_buf[fi] corresponds
|
||||
# 1:1 with add_field): derefs through it load/store with volatile.
|
||||
if fidx_add >= 0 and field_pointee_volatile_buf[fi] != 0:
|
||||
fe_pv: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_pv is not None: fe_pv.PointeeVolatile = 1
|
||||
# const 对象字段标记(field_const_buf[fi] 与 add_field 顺序一一对应)
|
||||
# const object field flag (field_const_buf[fi] corresponds 1:1 with add_field order)
|
||||
if fidx_add >= 0 and field_const_buf[fi] != 0:
|
||||
fe_c: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_c is not None: fe_c.IsConst = 1
|
||||
|
||||
# Phase 1a 声明模式:只注册 struct + 设置 OOP 标志,不翻译方法体
|
||||
# Phase 1a declaration mode: only register the struct and OOP flags, no method bodies
|
||||
if trans._declare_only == 1:
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name, 1)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 4. OOP 方法处理:扫描 FunctionDef,标记 OOP,翻译方法,生成 __before_init__
|
||||
# ============================================================
|
||||
# 4. OOP methods: scan FunctionDefs, mark OOP, translate methods, emit __before_init__
|
||||
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name)
|
||||
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _translate_nested_classdefs - 递归翻译类体内的嵌套 class / struct
|
||||
#
|
||||
# 命名与引用口径(用户 2026-10-01 定,与 TPC union/renum 嵌套命名同源):
|
||||
# · 内层结构体 IR 名 = `{sha1}.{外类}_{内类}`(sha1 = 与外层相同的定义模块);
|
||||
# · 引用两种写法都可:限定名 `外类.内类`(Attribute 兜底,见 TypeGenResolve)
|
||||
# 与扁平名 `外类_内类`(裸名直查结构体表即可命中);
|
||||
# · 多层嵌套自然递归为 `A_B_C`(递归时子节点名已被改写为 A_B)。
|
||||
# 实现选择"临时改写子节点 name 再递归"而非另写发射器:本模块所有名字派生点
|
||||
# (IR 名 {sha}.{名}、register_struct/add_field、AnnotClassName、方法符号名
|
||||
# `{类}.{方法}` 的 mangling、mark_module_class 键)都读 cd.name ⇒ 改名即全链路
|
||||
# 一致,且复用继承展平/packed/OOP/vtable 等全部既有逻辑,零新增判据。
|
||||
# 两个必须的护栏:
|
||||
# ① 扁平名字符串分配在 PermPool —— 它要跨 Phase(1a/1b)存活,trans.Pool 会随
|
||||
# mark/reset 回收成悬空串(后续 strcmp 读悬空内存)。
|
||||
# ② 递归返回后**还原"正在定义类"窗口**(CurDefModuleSha1/CurDefClassName 会被
|
||||
# 内层 mark_module_class 覆盖)——不还原会让外层随后的字段注解解析误判
|
||||
# "当前定义类"(规则 5.5 的自引用窗口据此判定,误判会冻结裸名票名)。
|
||||
# ============================================================
|
||||
# _translate_nested_classdefs - recursively translate nested classes/structs. Rewrite
|
||||
# child names to flat `{Outer}_{Inner}` (PermPool) then recurse, restoring the def window.
|
||||
def _translate_nested_classdefs(self, trans: HT.Translator*, cd: ast.ClassDef*) -> None:
|
||||
"""把本类体内的嵌套 ClassDef 逐个按 `{外类}_{内类}` 递归翻译"""
|
||||
"""Recursively translate nested ClassDefs under the flat name {Outer}_{Inner}."""
|
||||
if trans is None or cd is None or cd.name is None: return
|
||||
children: list[ast.AST*]* = cd.children
|
||||
if children is None: return
|
||||
@@ -679,8 +547,8 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
if child_ast.kind() != ast.ASTKind.ClassDef: continue
|
||||
child: ast.ClassDef* = ast.ClassDef*(child_ast)
|
||||
if child is None or child.name is None: continue
|
||||
# 扁平名 `{外类}_{内类}`(优先 PermPool:名字要跨 Phase 存活;
|
||||
# PermPool 缺失时退回本遍池,行为与旧路径一致)
|
||||
# Flat name `{Outer}_{Inner}` (prefer PermPool so it survives phases; fall back to
|
||||
# the pass pool when missing, matching the old path).
|
||||
flat_pool: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if flat_pool is None: flat_pool = trans.Pool
|
||||
if flat_pool is None: continue
|
||||
@@ -690,7 +558,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
flat_buf: str = flat_pool.alloc(flat_len + 1)
|
||||
if flat_buf is None: continue
|
||||
viperlib.snprintf(flat_buf, flat_len + 1, "%s_%s", cd.name, child.name)
|
||||
# 改写子节点名 → 递归(内部一律以扁平名注册/发射)
|
||||
# Rewrite the child name then recurse (everything registers/emits with the flat name)
|
||||
old_name: str = child.name
|
||||
child.name = flat_buf
|
||||
saved_def_sha1: str = self.Gen.StructGen.CurDefModuleSha1
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -2,8 +2,6 @@ import t, c
|
||||
from stdint import *
|
||||
from . import ClassGenBase
|
||||
|
||||
# ============================================================
|
||||
# ClassGenMixin - 类定义/继承/泛型/vtable/enum/union/renum
|
||||
# ============================================================
|
||||
# ClassGenMixin - class definition / inheritance / generics / vtable / enum / union / renum.
|
||||
novirtual class ClassGenMixin(ClassGenBase.ClassGenBase):
|
||||
"""类定义处理 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
|
||||
"""Class-definition mixin (top level after the stage-D split; methods live in sub-mixins)."""
|
||||
File diff suppressed because it is too large.
Load diff
@@ -8,25 +8,8 @@ import viperlib
|
||||
import w32.win32file as w32file
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
# ============================================================
|
||||
# DebugInfoGen - function-level DWARF metadata emitter (mlirlite text IR)
|
||||
#
|
||||
# Node layout mirrors TPC lib/core/LLVMCG/DebugInfo.py node-for-node:
|
||||
# !DIFile -> !{ null } -> !DISubroutineType -> distinct !DICompileUnit
|
||||
# -> distinct !DISubprogram (per define) -> !DILocation (per statement)
|
||||
# module tail: !llvm.dbg.cu + !llvm.module.flags (Debug Info Version 3)
|
||||
#
|
||||
# Enabled only while --traceback is on: the Phase layer mirrors
|
||||
# BuildPipeline._traceback_mode here via SetEnabled(). While disabled every
|
||||
# entry point returns before touching any mlirlite DI API, so the emitted IR
|
||||
# stays byte-identical to the pre-DWARF output.
|
||||
#
|
||||
# Scope/statement hooks are driven from the Gen layer:
|
||||
# Translator.translate -> BeginModule / Finalize
|
||||
# FuncGenDef.translate_function_def / ClassGenVTable._translate_method /
|
||||
# FuncGenMixin (nested + lambda) -> PushFunction / PopFunction
|
||||
# StmtGenMixin.translate_stmt -> SetStatement
|
||||
# ============================================================
|
||||
# DebugInfoGen - emits function-level DWARF metadata (mlirlite text IR), mirroring TPC LLVMCG/DebugInfo.py node-for-node.
|
||||
# Active only under --traceback (SetEnabled); hooks come from the Gen layer. When disabled every entry point returns before touching DI APIs, keeping IR byte-identical.
|
||||
|
||||
# ---- module-scope state (reset per module by BeginModule) ----
|
||||
_g_enabled: int = 0
|
||||
@@ -43,12 +26,8 @@ _g_cur_entry_ref: str = None
|
||||
_g_cur_builder: mlirlite.IRBuilder* = None
|
||||
_g_depth: int = 0
|
||||
|
||||
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder)
|
||||
# per level. +1 so 0 means "no enclosing function" (sp ids are >= 0). The saved
|
||||
# builder is what makes SetStatement reject a builder that is not the one the
|
||||
# current DISubprogram was pushed on (e.g. an async state machine emitted inside
|
||||
# a bracketed function): such instructions must carry no !dbg, otherwise LLVM's
|
||||
# verifier rejects "!dbg attachment points at wrong subprogram for function".
|
||||
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder) per level; +1 lets 0 mean "no enclosing function".
|
||||
# The saved builder makes SetStatement reject a builder not pushed by the current DISubprogram (e.g. an async state machine in a bracketed function); such instrs must carry no !dbg or LLVM's verifier fails.
|
||||
define DBG_STACK_MAX = 256
|
||||
_g_stk_sp: sizet* = None
|
||||
_g_stk_ref: str = None
|
||||
@@ -63,16 +42,13 @@ _g_loc_ref: str = None
|
||||
_g_loc_used: bytes = None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SetEnabled / IsEnabled - mirror the --traceback switch
|
||||
# ============================================================
|
||||
# SetEnabled / IsEnabled - mirror the --traceback switch (flag -> _g_enabled, forwarded to mlirlite's debug call-site recorder).
|
||||
def SetEnabled(flag: bool) -> int:
|
||||
"""Enable/disable DI emission (mirrors BuildPipeline._traceback_mode)."""
|
||||
global _g_enabled
|
||||
if flag: _g_enabled = 1
|
||||
else: _g_enabled = 0
|
||||
# 把同一开关转达给 mlirlite 的调试调用点记录(--traceback 时我们自产
|
||||
# <sha1>.vpcall 旁文件,供 WriteStackMap 结合 objdump 还原调用点行号)。
|
||||
# Forward the same switch to mlirlite's debug call-site recorder (under --traceback we emit a <sha1>.vpcall side file for WriteStackMap+objdump).
|
||||
mlirlite.DebugSetEnabled(flag)
|
||||
return 0
|
||||
|
||||
@@ -81,9 +57,7 @@ def IsEnabled() -> int:
|
||||
return _g_enabled
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers
|
||||
# ============================================================
|
||||
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers.
|
||||
def _EnsureStorage() -> int:
|
||||
global _g_stk_sp, _g_stk_ref, _g_stk_bld, _g_loc_key, _g_loc_ref, _g_loc_used
|
||||
if _g_stk_sp is not None: return 0
|
||||
@@ -119,9 +93,7 @@ def _RefOf(pool: memhub.MemBuddy*, node_id: int) -> str:
|
||||
return buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# BeginModule - start of one module translation
|
||||
# ============================================================
|
||||
# BeginModule - start of one module translation.
|
||||
def BeginModule(mod: mlirlite.LLVMModule*, file_path: str) -> int:
|
||||
"""Reset per-module DI state. Call once at the start of each module."""
|
||||
global _g_mod, _g_file_path, _g_abs_file_path, _g_finalized, _g_file_id, _g_null_id
|
||||
@@ -131,8 +103,7 @@ def BeginModule(mod: mlirlite.LLVMModule*, file_path: str) -> int:
|
||||
_g_mod = mod
|
||||
_g_file_path = file_path
|
||||
_g_abs_file_path = None
|
||||
# 模块边界兜底:翻译中途失败可能残留未 Pop 的调试函数上下文,先清空,
|
||||
# 避免把上一个模块的 caller 误算到本模块的调用点上。
|
||||
# Module-boundary fallback: clear any un-Pop'd debug function context (mid-translation failure) so the previous module's caller is not attributed here.
|
||||
mlirlite.DebugResetFunc()
|
||||
_g_finalized = 0
|
||||
_g_file_id = -1
|
||||
@@ -147,25 +118,14 @@ def BeginModule(mod: mlirlite.LLVMModule*, file_path: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _EnsureModuleNodes - DIFile / !{ null } / DISubroutineType / DICompileUnit
|
||||
# (created lazily at the first define, once per module)
|
||||
# ============================================================
|
||||
# _EnsureModuleNodes - lazily create DIFile / !{ null } / DISubroutineType / DICompileUnit at the first define (once per module).
|
||||
def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
||||
global _g_file_id, _g_null_id, _g_subtype_id, _g_cu_id, _g_abs_file_path
|
||||
if _g_cu_id >= 0: return 0
|
||||
mod: mlirlite.LLVMModule* = _g_mod
|
||||
if mod is None: return 1
|
||||
# DIFile: **绝对**路径(正斜杠),filename 保留整条路径。
|
||||
# 为什么必须绝对:本工程给 CurrentFile 的是**项目相对**路径(如
|
||||
# "./Test/LLvmLiteTest/App/main.vp"),LLVM 会把该目录写成 CU 的 DW_AT_comp_dir,
|
||||
# 而主文件条目用 `dir_index: 0` 指向 comp_dir;stackmap 写入端只解析
|
||||
# `llvm-dwarfdump --debug-line`(其中**没有** comp_dir)⇒ 行记录只能退化成裸文件名
|
||||
# "main.vp":运行期既显示不全,也无法按路径重读源码行(实证:曾整段栈都打印
|
||||
# `File "main.vp", line N` 而无源码行)。
|
||||
# 绝对化后,无论 LLVM 保留整条 name(写入端 `_tb_is_abs_path` 命中 ⇒ 原样采用)
|
||||
# 还是把它拆成 dir+basename(dir_index 指向同一个绝对目录 ⇒ 拼回绝对路径),
|
||||
# 结果都是绝对路径 —— 故本改动不需要动写入端。
|
||||
# DIFile: absolute path (forward slashes), filename keeps the whole path.
|
||||
# Must be absolute: relative paths make LLVM's DWARF line records degrade to a bare "main.vp" that runtime cannot reload; absolutizing works whether LLVM keeps the full name or splits dir+basename.
|
||||
src: str = _g_file_path
|
||||
if src is None: src = ""
|
||||
slen: sizet = string.strlen(src)
|
||||
@@ -177,25 +137,23 @@ def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
||||
if slen > 0: string.strncpy(nbuf, src, slen)
|
||||
nbuf[slen] = '\0'
|
||||
if slen > 0 and nbuf[0] != '/' and not (slen >= 2 and nbuf[1] == ':'):
|
||||
# 相对路径 → 前置当前工作目录。用 w32file 低层调用(同 Repl.vp),
|
||||
# 不经 os._mbuddy:编译器进程未必初始化过那个内存管理器。
|
||||
# Relative path -> prefix CWD via a w32file low-level call (as in Repl.vp), not os._mbuddy which the compiler process may not have initialized.
|
||||
CwdBuf: bytes = pool.alloc(4112)
|
||||
if CwdBuf is not None:
|
||||
CwdBuf[0] = '\0'
|
||||
nc: ULONG = w32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
|
||||
if nc > 0:
|
||||
# 防御:缓冲区不足时 GetCurrentDirectoryA 的写入内容未定义,强制补 NUL
|
||||
# Defensive: when the buffer is too small GetCurrentDirectoryA writes undefined content; force a NUL.
|
||||
CwdBuf[4110] = '\0'
|
||||
viperlib.snprintf(nbuf, cap_n, "%s/%s", CwdBuf, src)
|
||||
# 归一所有反斜杠(CWD 段同样可能含 '\')
|
||||
# Normalize all backslashes (the CWD segment may contain '\' too).
|
||||
flen: sizet = string.strlen(nbuf)
|
||||
ni: sizet = 0
|
||||
for ni in range(flen):
|
||||
if nbuf[ni] == '\\': nbuf[ni] = '/'
|
||||
# 保存绝对路径供 mlirlite 调试调用点记录复用(.vpcall 旁文件里的文件路径口径
|
||||
# 必须与 DIFile 一致:绝对 + 正斜杠,运行期才能按路径重读源码行)。
|
||||
# Save the absolute path for mlirlite's debug call-site recorder (.vpcall file paths must match DIFile: absolute + forward slash).
|
||||
_g_abs_file_path = nbuf
|
||||
# filename 取整条路径;directory 另存其目录前缀(两段各自 NUL 结尾,互不污染)
|
||||
# filename = whole path; directory = its prefix (both NUL-terminated, non-overlapping).
|
||||
fname: str = nbuf
|
||||
directory: str = ""
|
||||
slash: str = string.strrchr(nbuf, '/')
|
||||
@@ -246,18 +204,14 @@ def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _LocRefAt - read a cached DILocation ref string pointer
|
||||
# ============================================================
|
||||
# _LocRefAt - read a cached DILocation ref string pointer.
|
||||
def _LocRefAt(slot: int) -> str:
|
||||
slotp: str* = (str*)(uint64(_g_loc_ref) + sizet(slot) * 8)
|
||||
r: str = slotp[0]
|
||||
return r
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once
|
||||
# ============================================================
|
||||
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once.
|
||||
def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str:
|
||||
if line <= 0: return _g_cur_entry_ref
|
||||
c: int = col
|
||||
@@ -294,16 +248,9 @@ def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str
|
||||
return lref
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PushFunction - create the DISubprogram for one define
|
||||
# ============================================================
|
||||
# PushFunction - create the DISubprogram for one define.
|
||||
def PushFunction(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, func: mlirlite.Function*, line: int, col: int) -> int:
|
||||
"""Attach a DISubprogram to one define and set the builder's entry DILocation.
|
||||
|
||||
Returns 1 when pushed (caller must call PopFunction), 0 when DI is disabled
|
||||
or not applicable. Every instruction emitted afterwards on `builder` carries
|
||||
", !dbg !N" until the next SetStatement.
|
||||
"""
|
||||
"""Attach a DISubprogram to one define and set the builder's entry DILocation."""
|
||||
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
||||
if _g_enabled == 0: return 0
|
||||
if pool is None or builder is None or func is None or _g_mod is None: return 0
|
||||
@@ -349,15 +296,12 @@ def PushFunction(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, func: mli
|
||||
return 0
|
||||
_g_cur_entry_ref = entry_ref
|
||||
mlirlite.builder_set_debug_location(builder, entry_ref)
|
||||
# 进入本 define:通知 mlirlite 调试调用点记录当前函数符号/文件/定义行,
|
||||
# 并写一条 F 记录(供 .vpcall 旁文件 + WriteStackMap 生成函数入口兜底行)。
|
||||
# Enter this define: tell mlirlite's call-site recorder the current symbol/file/def-line and write an F record (for the .vpcall file + WriteStackMap entry fallback line).
|
||||
mlirlite.DebugNoteFunc(name, _g_abs_file_path, dline)
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PopFunction - restore the enclosing function's DI context
|
||||
# ============================================================
|
||||
# PopFunction - restore the enclosing function's DI context.
|
||||
def PopFunction(builder: mlirlite.IRBuilder*) -> int:
|
||||
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
||||
if _g_enabled == 0: return 0
|
||||
@@ -369,22 +313,16 @@ def PopFunction(builder: mlirlite.IRBuilder*) -> int:
|
||||
bslot: str* = (str*)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
|
||||
_g_cur_builder = (mlirlite.IRBuilder*)(bslot[0])
|
||||
if builder is not None: mlirlite.builder_clear_debug_location(builder)
|
||||
# 离开本 define:恢复 mlirlite 记录的外层函数上下文(嵌套 define 用)。
|
||||
# Leave this define: restore mlirlite's outer function context (for nested defines).
|
||||
mlirlite.DebugClearFunc()
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SetStatement - point the current builder at a statement DILocation
|
||||
# ============================================================
|
||||
# SetStatement - point the current builder at a statement DILocation.
|
||||
def SetStatement(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, line: int, col: int) -> int:
|
||||
"""Update the builder's DILocation to the current statement (line precision).
|
||||
|
||||
No-op outside a DI-attached define (module-level code / synthetic defines),
|
||||
so instructions outside a function body never reference a function's scope.
|
||||
"""
|
||||
"""Update the builder's DILocation to the current statement (line precision)."""
|
||||
if _g_enabled == 0 or pool is None or builder is None: return 0
|
||||
# 记录当前语句行:随后本语句内 build_call 产生的 C 记录以它作为调用点行。
|
||||
# Record the current statement line: C records from build_call in this statement use it as the call-site line.
|
||||
mlirlite.DebugNoteLine(line)
|
||||
if _g_cur_sp < 0: return 0
|
||||
ref: str = _EnsureLocation(pool, _g_cur_sp, line, col)
|
||||
@@ -393,9 +331,7 @@ def SetStatement(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, line: int
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _ModuleHasDefine - does the module emit any define (IsDeclared == 0)?
|
||||
# ============================================================
|
||||
def _ModuleHasDefine(mod: mlirlite.LLVMModule*) -> int:
|
||||
if mod is None: return 0
|
||||
cur: mlirlite.Function* = mod.FuncHead
|
||||
@@ -408,9 +344,7 @@ def _ModuleHasDefine(mod: mlirlite.LLVMModule*) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir
|
||||
# ============================================================
|
||||
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir.
|
||||
def Finalize(pool: memhub.MemBuddy*) -> int:
|
||||
"""Module-level DI tail; idempotent per module, no-op when no SP was emitted."""
|
||||
global _g_finalized
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -14,50 +14,37 @@ import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 指针间隐式转换判定码(coerce_to_type 的「指针 → 指针」分支使用)
|
||||
#
|
||||
# 由 _ptr_cast_reason 统一给出,供报错说明「哪一级不允许 + 为什么」:
|
||||
# PC_OK 允许(含规则 1 通用指针通配、规则 4 完全相同不视为转换)
|
||||
# PC_DEPTH 指针层数不同(禁)
|
||||
# PC_NONBASIC 被指类型是非基本类型(结构体/类/联合/数组等):非基本类型与其
|
||||
# 指针之间、以及不同非基本类型的指针之间一律不允许(规则 2/3)
|
||||
# PC_LOSSY 被指类型之间转换有损(整数→浮点位宽不足 / 浮点→整数)(规则 2)
|
||||
# ============================================================
|
||||
# Pointer-to-pointer cast reason codes (coerce_to_type ptr -> ptr branch).
|
||||
# PC_OK allowed; PC_DEPTH depth mismatch; PC_NONBASIC non-basic pointee; PC_LOSSY lossy.
|
||||
define PC_OK = 0
|
||||
define PC_DEPTH = 1
|
||||
define PC_NONBASIC = 2
|
||||
define PC_LOSSY = 3
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenBase - ExprGenMixin 拆分(阶段 D):表达式基础工具(类型判断/指针层级/coerce)
|
||||
# ============================================================
|
||||
# ExprGenBase - expression basics split out of ExprGenMixin (stage D):
|
||||
# type checks / pointer level / coerce.
|
||||
novirtual class ExprGenBase:
|
||||
"""表达式基础工具(类型判断/指针层级/coerce)"""
|
||||
"""Expression basics: type checks / pointer levels / coercion."""
|
||||
|
||||
# --- docstring 登记表字段(__doc__ 魔法属性支持)---
|
||||
# --- docstring registry fields (__doc__ magic attribute support) ---
|
||||
Gen: Generator.Generator*
|
||||
DocKeys: bytes
|
||||
DocValues: bytes
|
||||
DocCount: int
|
||||
|
||||
# --- OOM 诊断:跨模块 CDefine 查找调用计数(CONST_INT 参数翻译疑似循环查找) ---
|
||||
# --- OOM diag: cross-module CDefine lookup counter (suspected CONST_INT loop) ---
|
||||
DiagCDefineCount: int
|
||||
# --- OOM 诊断:translate_name_value 调用计数(顶层 CDefine 查找 14593 次来源) ---
|
||||
# --- OOM diag: translate_name_value call counter (source of 14593 lookups) ---
|
||||
DiagNameValueCount: int
|
||||
# --- OOM 诊断:translate_value 调用计数(CONST_INT 循环调用源定位) ---
|
||||
# --- OOM diag: translate_value call counter (locates CONST_INT call loop) ---
|
||||
DiagTranslateValueCount: int
|
||||
# ============================================================
|
||||
# P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数,
|
||||
# 本模块函数调用改 self.,外部模块调用保留模块前缀)
|
||||
# ============================================================
|
||||
# P3 migrated methods (bodies from HandlesExpr.vp module functions;
|
||||
# self. for local calls, module prefix for external ones).
|
||||
|
||||
# ============================================================
|
||||
# 从 AST 节点提取函数名
|
||||
# ============================================================
|
||||
# Extract the function name from an AST node.
|
||||
def get_func_name(self, func_node: ast.AST*) -> str:
|
||||
"""从函数节点提取函数名"""
|
||||
"""Extract the function name from an AST node."""
|
||||
if func_node is None: return None
|
||||
k: int = func_node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
@@ -67,11 +54,9 @@ novirtual class ExprGenBase:
|
||||
at: ast.Attribute* = ast.Attribute*(func_node)
|
||||
return at.attr
|
||||
return None
|
||||
# ============================================================
|
||||
# 转义 LLVM IR 字符串字面量
|
||||
# ============================================================
|
||||
# Escape an LLVM IR string literal.
|
||||
def escape_llvm_string(self, pool: memhub.MemBuddy*, src: str) -> str:
|
||||
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
|
||||
"""Convert a string into an LLVM IR string literal."""
|
||||
if src is None: return None
|
||||
slen: sizet = string.strlen(src)
|
||||
alloc_size: sizet = 4 * slen + 8
|
||||
@@ -107,122 +92,71 @@ novirtual class ExprGenBase:
|
||||
pos += 1
|
||||
buf[pos] = '\0'
|
||||
return buf
|
||||
# ============================================================
|
||||
# 获取 LLVM 整数类型的位宽
|
||||
#
|
||||
# [S4 切读·位宽块] 实现已收拢到 `TSpec.SpecTypeBits`(该块在 TypeSpec 模块中的
|
||||
# **唯一实现**),此处只做委托,不再保留同码副本(TypeGenErr.get_llvm_type_bits
|
||||
# 同样委托到它)。两份副本一旦分叉,会出现「同一类型在两处算出不同位宽」的
|
||||
# 隐蔽不一致。私有调用点(self.get_llvm_type_bits)保持原样不受影响。
|
||||
# ============================================================
|
||||
# Get the bit width of an LLVM integer type.
|
||||
# Delegates to TSpec.SpecTypeBits (single source; avoids diverging duplicates).
|
||||
def get_llvm_type_bits(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托 SpecTypeBits)"""
|
||||
"""Bit width of an LLVM integer type; 0 for non-integers (delegates to SpecTypeBits)."""
|
||||
return TSpec.SpecTypeBits(ty)
|
||||
# ============================================================
|
||||
# 获取 LLVM 浮点类型的位宽
|
||||
# ============================================================
|
||||
# Get the bit width of an LLVM floating-point type.
|
||||
def get_llvm_float_bits(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0"""
|
||||
"""Bit width of an LLVM float type; 0 for non-floats."""
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Float(bits):
|
||||
return bits
|
||||
case _:
|
||||
return 0
|
||||
# ============================================================
|
||||
# is_ptr_type - 检查类型是否为 Ptr
|
||||
#
|
||||
# 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。
|
||||
# ============================================================
|
||||
# is_ptr_type - check whether a type is Ptr (1=yes, 0=no);
|
||||
# a widely used type-check helper (50+ call sites).
|
||||
def is_ptr_type(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
|
||||
"""Check whether ty is a Ptr type; returns 1=yes / 0=no."""
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Ptr(pointee):
|
||||
return 1
|
||||
case _:
|
||||
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)
|
||||
# ============================================================
|
||||
# get_ptr_level - compute pointer level (0=non-ptr, 1/2/3+=ptr depth);
|
||||
# used by comparison checks to allow matching levels and reject mixed ones.
|
||||
def get_ptr_level(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针...
|
||||
|
||||
[S3 切读·指针层级块] 实现已收拢到 `TSpec.SpecTypePtrLevel`
|
||||
(该块在 TypeSpec 模块中的**唯一实现**),此处只做委托,不再保留同码副本。
|
||||
原实现里记录的「match codegen 缺陷」规避说明随实现一并移入 TypeSpec。
|
||||
"""
|
||||
"""Compute pointer level (0=non-ptr, 1=ptr, 2=ptr-to-ptr); delegates to TSpec.SpecTypePtrLevel."""
|
||||
return TSpec.SpecTypePtrLevel(ty)
|
||||
# ============================================================
|
||||
# _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0
|
||||
#
|
||||
# 用于指针判空比较(ptr != 0 / ptr == 0):指针与字面量 0 比较时
|
||||
# 0 视为 NULL,允许并转成 null 指针(比较类型检查的例外)。
|
||||
# ============================================================
|
||||
# _is_zero_int_literal - is the AST node the integer literal 0?
|
||||
# Used for ptr != 0 / ptr == 0, where 0 means NULL and is allowed.
|
||||
def _is_zero_int_literal(self, node: ast.AST*) -> int:
|
||||
"""判断节点是否为整数字面量 0,返回 1=是 / 0=否"""
|
||||
"""Is the node the integer literal 0; returns 1=yes / 0=no."""
|
||||
if node is None: return 0
|
||||
if node.kind() != ast.ASTKind.Constant: return 0
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
if cn is None or cn.const_kind != ast.CONST_INT: return 0
|
||||
if cn.int_val == 0: return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# is_array_type - 检查类型是否为 Array
|
||||
#
|
||||
# 返回 1=是, 0=否。类型检查工具函数。
|
||||
# ============================================================
|
||||
# is_array_type - check whether a type is Array (1=yes, 0=no).
|
||||
def is_array_type(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否"""
|
||||
"""Check whether ty is an Array type; returns 1=yes / 0=no."""
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Array(_, _):
|
||||
return 1
|
||||
case _:
|
||||
return 0
|
||||
# ============================================================
|
||||
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
|
||||
#
|
||||
# 处理两种情况:
|
||||
# - ty 直接是 Struct → 检查是否注册
|
||||
# - ty 是 Ptr(Struct) → 检查 pointee 是否注册
|
||||
#
|
||||
# 返回类名字符串或 None。用于 subscript 操作中判断是否需要
|
||||
# 转发到 __getitem__/__setitem__ 而非生成 GEP。
|
||||
# ============================================================
|
||||
# _get_custom_struct_cls_nm - class name if ty (or Ptr(ty)) is a registered struct.
|
||||
# Used by subscript to decide __getitem__/__setitem__ forwarding vs raw GEP.
|
||||
def _get_custom_struct_cls_nm(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> str:
|
||||
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None"""
|
||||
"""Class name if ty or its pointee is a registered struct; else None."""
|
||||
if ty is None: return None
|
||||
# 直接检查 ty 是否是注册的结构体
|
||||
# Check whether ty itself is a registered struct.
|
||||
cls_nm_direct: str = self.Gen.StructGen.get_class_name_by_type(pool, ty)
|
||||
if cls_nm_direct is not None: return cls_nm_direct
|
||||
# 如果 ty 是 Ptr,检查 pointee
|
||||
# If ty is Ptr, check the pointee.
|
||||
if self.is_ptr_type(ty) != 0:
|
||||
pointee_cs: mlirlite.LLVMType* = ty.Pointee
|
||||
if pointee_cs is not None: return self.Gen.StructGen.get_class_name_by_type(pool, pointee_cs)
|
||||
return None
|
||||
# ============================================================
|
||||
# _is_list_subscript_base - 判断下标基座是否为 list[T] 变量
|
||||
#
|
||||
# 属性访问的 Subscript 基座(如 lst[i].x)需要区分:
|
||||
# - list[T] 变量:translate_subscript 走 __getitem__ 内联返回元素地址
|
||||
# - 普通结构体指针数组(如 pts: Point*):get_subscript_ptr 返回元素地址
|
||||
# 路由错误会让 list[T] 被当作结构体数组 GEP,访问错位崩溃。
|
||||
# ============================================================
|
||||
# _is_list_subscript_base - is the subscript base a list[T] variable?
|
||||
# Wrong routing would treat list[T] as a struct array GEP and crash.
|
||||
def _is_list_subscript_base(self, pool: memhub.MemBuddy*, trans: HT.Translator*, sub_node: ast.AST*) -> int:
|
||||
"""判断下标基座是否为 list[T] 变量(1=是)"""
|
||||
"""Is the subscript base a list[T] variable; 1=yes / 0=no."""
|
||||
if sub_node is None or trans is None: return 0
|
||||
sub: ast.Subscript* = ast.Subscript*(sub_node)
|
||||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||||
@@ -245,11 +179,9 @@ novirtual class ExprGenBase:
|
||||
case _:
|
||||
pass
|
||||
return 0
|
||||
# ============================================================
|
||||
# _sub_base_class_name - 解析下标基座变量的结构体类名
|
||||
# ============================================================
|
||||
# _sub_base_class_name - resolve the struct class name of a subscript base.
|
||||
def _sub_base_class_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, sub_node: ast.AST*) -> str:
|
||||
"""解析下标基座变量的结构体类名(无则 None)"""
|
||||
"""Resolve the struct class name of a subscript base; None if absent."""
|
||||
if sub_node is None or trans is None: return None
|
||||
sub: ast.Subscript* = ast.Subscript*(sub_node)
|
||||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name: return None
|
||||
@@ -264,17 +196,10 @@ novirtual class ExprGenBase:
|
||||
inner: mlirlite.LLVMType* = pointee.Pointee
|
||||
if inner is not None: return self.Gen.StructGen.get_class_name_by_type(pool, inner)
|
||||
return self.Gen.StructGen.get_class_name_by_type(pool, pointee)
|
||||
# ============================================================
|
||||
# _sub_base_uses_getitem - 下标基座是否应走 __getitem__ 语义
|
||||
#
|
||||
# 属性访问的 Subscript 基座路由依据:
|
||||
# - list[T] / OOP 定义 __getitem__ 的类(如 dict[key])→ translate_value
|
||||
# (translate_subscript 走 __getitem__ 内联/方法调用返回元素值或地址)
|
||||
# - 普通结构体指针数组(如 pts: Point*)→ get_subscript_ptr
|
||||
# (GEP 元素地址,避免结构体值 base=非指针 报错)
|
||||
# ============================================================
|
||||
# _sub_base_uses_getitem - should the subscript base use __getitem__ semantics?
|
||||
# list[T] / classes defining __getitem__ yes; plain struct-ptr arrays use GEP.
|
||||
def _sub_base_uses_getitem(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, sub_node: ast.AST*) -> int:
|
||||
"""下标基座应走 __getitem__ 语义时返回 1,否则 0(应走 GEP 地址语义)"""
|
||||
"""Returns 1 if the subscript base uses __getitem__ semantics, else 0 (GEP address)."""
|
||||
if self._is_list_subscript_base(pool, trans, sub_node) != 0: return 1
|
||||
cls_ugi: str = self._sub_base_class_name(pool, trans, sub_node)
|
||||
if cls_ugi is None: return 0
|
||||
@@ -283,35 +208,23 @@ novirtual class ExprGenBase:
|
||||
viperlib.snprintf(lookup_ugi, 128, "%s.__getitem__", cls_ugi)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_ugi) is not None: return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用
|
||||
#
|
||||
# 用于支持 X* 类型变量的属性访问:
|
||||
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
|
||||
# - 指针类型变量 (r: Vec2*): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
|
||||
# ============================================================
|
||||
# _deref_if_ptr_ptr - if obj_ptr is Ptr(Ptr(...)), load one level to deref.
|
||||
# Supports attribute access on X* variables while leaving value vars alone.
|
||||
def _deref_if_ptr_ptr(self, builder: mlirlite.IRBuilder*, obj_ptr: mlirlite.Value*) -> mlirlite.Value*:
|
||||
"""如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用获取内层指针"""
|
||||
"""If obj_ptr is Ptr(Ptr(...)), load once to obtain the inner pointer."""
|
||||
if obj_ptr is None or obj_ptr.Ty is None: return obj_ptr
|
||||
if self.is_ptr_type(obj_ptr.Ty) == 0: return obj_ptr
|
||||
pointee: mlirlite.LLVMType* = obj_ptr.Ty.Pointee
|
||||
if pointee is None: return obj_ptr
|
||||
if self.is_ptr_type(pointee) == 0: return obj_ptr
|
||||
# obj_ptr 是 Ptr(Ptr(...)),load 解引用
|
||||
# obj_ptr is Ptr(Ptr(...)); load to dereference.
|
||||
loaded: mlirlite.Value* = mlirlite.build_load(builder, pointee, obj_ptr)
|
||||
if loaded is None: return obj_ptr
|
||||
return loaded
|
||||
# ============================================================
|
||||
# _value_node_of - 把「外层语句节点」下沉到其「值子表达式节点」
|
||||
#
|
||||
# 诊断错位修复(2026-09-28):赋值/返回等路径传给 coerce_to_type 的 node 常是
|
||||
# 外层语句节点(实测 Assign,kind=3,col_offset=-1),导致报错打成
|
||||
# `出错表达式: 表达式 kind=3(列 -1)`,指不到真正出错的子表达式。
|
||||
# 此处把语句节点(Assign/AnnAssign/AugAssign/Expr/Return)换成其 value 子节点,
|
||||
# 使报错定位到真正被转换的那个值表达式。非语句节点原样返回。
|
||||
# ============================================================
|
||||
# _value_node_of - drill an outer statement node down to its value sub-expression.
|
||||
# Fixes error locations pointing at the Assign node instead of the real value.
|
||||
def _value_node_of(self, node: ast.AST*) -> ast.AST*:
|
||||
"""语句节点 → 其值子表达式节点;非语句/无值则原样返回"""
|
||||
"""Drill a statement node down to its value sub-expression; returns node if none."""
|
||||
if node is None: return node
|
||||
k: int = node.kind()
|
||||
v: ast.AST* = None
|
||||
@@ -322,52 +235,21 @@ novirtual class ExprGenBase:
|
||||
elif k == ast.ASTKind.Return: v = (ast.Return*(node)).value
|
||||
if v is None: return node
|
||||
return v
|
||||
# ============================================================
|
||||
# IsNoneValue - 判据:源值是否为 None(空值常量),返回 1=是 / 0=否
|
||||
#
|
||||
# None 是特殊的空值常量:语义上「无值」,按语言规则**允许隐式转换为任意
|
||||
# 类型**——目标类型取其空值/零值(整数/浮点→0,指针→空指针,聚合→零初始化)。
|
||||
# 故「指针↔整数」「指针↔非指针值」「float↔ptr」这类针对**真实类型值**的
|
||||
# 隐式转换禁令不应拦 None:那些禁令防的是「有类型信息的真实值被静默当成
|
||||
# 另一种类型」,而 None 本就没有具体类型信息,赋任何类型都只是「置空/置零」。
|
||||
#
|
||||
# 真实用例(用户实际踩到):
|
||||
# VKernel/Kernel/intr/syscall.vp:87 `p.fd_used[fd] = None`
|
||||
# (fd_used 是 [N]uint64 之类的整数数组/字段)此前被
|
||||
# 「指针隐式转整数已禁止」拦成 CastError,本修正后应输出整数 0。
|
||||
#
|
||||
# 两条判据(任一命中即视为 None):
|
||||
# ① AST 层面:node 是 Constant 且 const_kind == ast.CONST_NONE
|
||||
# ② 值层面(主判据):已是生成好的 null 常量——None 经 translate_constant
|
||||
# 物化为 ConstNull(i8*)(指针类型 + 编译期零 + IsConst=1)。指针类型的
|
||||
# 编译期零常量即空指针(ConstNull / 指针型 ConstZero 皆如此);整数
|
||||
# 常量 0 的 Ty 是整数类型,不会误命中。
|
||||
# ② 为主判据的原因:赋值路径传给 coerce_to_type 的 node 常是外层语句节点
|
||||
# (实测上述用例 node 是 Assign,kind=3,col_offset=-1),拿不到 Constant
|
||||
# 节点,只能靠已经生成好的值来判定。
|
||||
# ============================================================
|
||||
# IsNoneValue - is val/node the None (empty) constant? 1=yes / 0=no.
|
||||
# None carries no type info, so it may be implicitly converted to any target.
|
||||
def IsNoneValue(self, val: mlirlite.Value*, node: ast.AST* = None) -> int:
|
||||
"""判据:val/node 是否表示 None(空值常量),1=是 / 0=否"""
|
||||
# 判据①:AST 是 None 字面量
|
||||
"""Is val/node the None constant; returns 1=yes / 0=no."""
|
||||
# Criterion 1: the AST node is a None literal.
|
||||
if node is not None and node.kind() == ast.ASTKind.Constant:
|
||||
cn_nv: ast.Constant* = ast.Constant*(node)
|
||||
if cn_nv is not None and cn_nv.const_kind == ast.CONST_NONE: return 1
|
||||
# 判据②:已生成好的 null 常量(指针类型 + 编译期零)
|
||||
# Criterion 2: an already-built null constant (ptr type + compile-time zero).
|
||||
if val is not None and val.IsConst == 1 and val.IntVal == 0 and self.is_ptr_type(val.Ty) != 0: return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _NoneValueAsTarget - 把 None(空值)物化为目标类型的空值/零值
|
||||
#
|
||||
# 仅在 coerce_to_type 判定源值是 None 时调用。构造手法沿用本仓既有写法:
|
||||
# 整数目标 → ConstInt(ty, 0)(零值)
|
||||
# 浮点目标 → ConstFloat(ty, 0.0)(零值)
|
||||
# 聚合目标(Struct/Array)→ ConstZero(ty)(zeroinitializer)
|
||||
# 其余(Void 等)→ ConstZero(ty) 兜底
|
||||
# 指针目标不在此处处理:coerce_to_type 的「指针→指针 bitcast」路径已能
|
||||
# 接受 None(i8* null → T*),保留其原有 IR 形态。
|
||||
# ============================================================
|
||||
# _NoneValueAsTarget - materialize None as the target type's zero/empty value.
|
||||
# Used only when coerce_to_type has detected the source is None.
|
||||
def _NoneValueAsTarget(self, builder: mlirlite.IRBuilder*, target_ty: mlirlite.LLVMType*) -> mlirlite.Value*:
|
||||
"""把 None 转换为目标类型的空值/零值"""
|
||||
"""Materialize None as the target type's zero/empty value."""
|
||||
if target_ty is None: return None
|
||||
pool: memhub.MemBuddy* = builder.Pool
|
||||
t_bits: int = self.get_llvm_type_bits(target_ty)
|
||||
@@ -375,56 +257,31 @@ novirtual class ExprGenBase:
|
||||
t_fbits: int = self.get_llvm_float_bits(target_ty)
|
||||
if t_fbits != 0: return mlirlite.ConstFloat(pool, target_ty, 0.0)
|
||||
return mlirlite.ConstZero(pool, target_ty)
|
||||
# ============================================================
|
||||
# _is_universal_ptr_ty - 判据:类型是否为「通用指针」(t.CPtr / i8* = void*)
|
||||
#
|
||||
# 规则 1(通用指针是通配):t.CPtr 与 i8*(void*,裸 str/bytes 那一族的指针形态)
|
||||
# 视为「任意指针」,与任何 T* 双向隐式互转都放行。
|
||||
# 判据:单层 Ptr 且 pointee 为 i8(t.CPtr = Ptr(Int8);Ptr(Void) 工厂已回退为
|
||||
# Ptr(Int8),故 void* 在 LLVM 层同样表现为 Ptr(i8))。char* 同属 i8*,一并视为通用指针。
|
||||
# 多级指针(如 i8**)不是通用指针(需逐级比较 pointee)。
|
||||
# 返回 1=是 / 0=否。
|
||||
# ============================================================
|
||||
# _is_universal_ptr_ty - is ty a universal pointer (t.CPtr / i8* = void*)?
|
||||
# Rule 1: universal pointers are wildcards, freely interconvertible with any T*.
|
||||
def _is_universal_ptr_ty(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""ty 是否为通用指针(i8*/t.CPtr),1=是 / 0=否"""
|
||||
"""Is ty a universal pointer (i8* / t.CPtr); 1=yes / 0=no."""
|
||||
if ty is None or self.is_ptr_type(ty) == 0: return 0
|
||||
if self.get_ptr_level(ty) != 1: return 0
|
||||
pointee: mlirlite.LLVMType* = ty.Pointee
|
||||
if pointee is None: return 1
|
||||
if self.get_llvm_type_bits(pointee) == 8: return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# 「str / bytes 不是通用指针通配」——规则与统一 fail-fast
|
||||
#
|
||||
# 用户规则:`void*` / `t.CPtr` / `rawptr` 允许隐式转为任意指针类型;
|
||||
# `str` / `bytes` 不允许——**即便它们在 LLVM 层同为 i8***(_is_universal_ptr_ty
|
||||
# 只看"一级指针 + 8 位被指类型",无法区分 CPtr 与 str),所以 str-ness 只能由
|
||||
# 调用方从**注解来源**给出:赋值侧 trans.CurAssignAnnot、返回侧
|
||||
# trans.CurFuncRetAnnot、形参侧 vpi/函数签名注解段。
|
||||
#
|
||||
# 只认**裸名**:`str` / `bytes`。`str*` 的语义是 i8**(指针槽,
|
||||
# 见 AssignGenMixin._is_ptr_element_annotation),不是字符串值本体,不适用。
|
||||
# ============================================================
|
||||
# "str / bytes are not universal pointers" - rule and unified fail-fast.
|
||||
# They share i8* in IR but are not wildcards, so the annotation source decides.
|
||||
def _annot_name_is_bare_str(self, txt: str) -> int:
|
||||
"""注解**文本**是否为裸 str / bytes;1=是 / 0=否"""
|
||||
"""Is the annotation text a bare str / bytes; 1=yes / 0=no."""
|
||||
if txt is None or txt[0] == 0: return 0
|
||||
if string.strcmp(txt, "str") == 0 or string.strcmp(txt, "bytes") == 0: return 1
|
||||
return 0
|
||||
def _annot_is_bare_str(self, annot: ast.AST*) -> int:
|
||||
"""注解 AST 是否为裸 str / bytes(Name 形态);1=是 / 0=否"""
|
||||
"""Is the annotation AST a bare str / bytes Name; 1=yes / 0=no."""
|
||||
if annot is None or annot.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(annot)
|
||||
if nm is None or nm.id is None: return 0
|
||||
return self._annot_name_is_bare_str(nm.id)
|
||||
def _str_rejects_ptr_kind(self, val: mlirlite.Value*) -> int:
|
||||
"""val 是否为「str/bytes 不应接受」的聚合指针(1/2/3=是,0=否)
|
||||
|
||||
1 = 结构体/类/联合指针;2 = 非字符数组指针;3 = 函数指针。
|
||||
**字符数组([N x i8])豁免**:它自身就是字符串本体,其指针在 C 语义下
|
||||
天然衰减为 char*/str(现场:includes/os/_win32.vp:207
|
||||
`name: str = find_data.cFileName`,WIN32_FIND_DATAA.cFileName 是 [260 x i8])。
|
||||
多级指针/基本类型指针一律 0(不在本规则范围内)。
|
||||
"""
|
||||
"""Is val an aggregate pointer that str/bytes must reject (1/2/3=yes, 0=no); char arrays exempt."""
|
||||
if val is None or val.Ty is None: return 0
|
||||
if self.is_ptr_type(val.Ty) == 0: return 0
|
||||
pointee: mlirlite.LLVMType* = val.Ty.Pointee
|
||||
@@ -441,82 +298,51 @@ novirtual class ExprGenBase:
|
||||
return 2
|
||||
return 0
|
||||
def fail_if_str_annot_agg_ptr(self, val: mlirlite.Value*, annot_is_str: int, node: ast.AST* = None) -> int:
|
||||
"""注解侧是裸 str/bytes 而值是指向聚合类型的指针 → fail-fast(返回 1)
|
||||
|
||||
判据见 _str_rejects_ptr_kind(结构体/类/联合/非字符数组/函数指针拒绝;
|
||||
字符数组衰减豁免)。显式转换(如 t.CPtr(p))的产物 pointee 是 i8,
|
||||
不触发本检查,故显式转换不受影响。
|
||||
"""
|
||||
"""Fail fast (return 1) when a bare str/bytes annotation holds an aggregate pointer."""
|
||||
if annot_is_str == 0 or val is None or val.Ty is None: return 0
|
||||
if self._str_rejects_ptr_kind(val) == 0: return 0
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), "str/bytes 不接受指向非基本类型(结构体/类/非字符数组/函数)的指针:两者在 IR 层" "同为 i8*,但语义不同(str/bytes 不是通用指针通配)。若确需传递指针," "请改用 t.CPtr/rawptr 形参,或显式转换(如 t.CPtr(p))", "CastError")
|
||||
return 1
|
||||
def fail_if_str_annot_ast_agg_ptr(self, val: mlirlite.Value*, annot: ast.AST*, node: ast.AST* = None) -> int:
|
||||
"""注解 AST 形态的入口:注解为裸 str/bytes 且值是指向聚合类型的指针 → fail-fast
|
||||
|
||||
供注解来源是 AST 的三条路径复用:赋值侧 aa.annotation、返回侧
|
||||
trans.CurFuncRetAnnot。形参侧注解来源是文本(vpi 段),直接调
|
||||
fail_if_str_annot_agg_ptr 传 1 即可。
|
||||
"""
|
||||
"""AST-annotation entry: fail fast when a bare str/bytes annotation holds an aggregate pointer."""
|
||||
return self.fail_if_str_annot_agg_ptr(val, self._annot_is_bare_str(annot), node)
|
||||
# ============================================================
|
||||
# _is_basic_scalar_ty - 判据:类型是否为基本类型(整数/浮点),1=是 / 0=否
|
||||
#
|
||||
# 非基本类型 = 结构体/类/联合/数组/函数等(LLVM 层 Struct/Array/Func)。
|
||||
# 规则 2/3:非基本类型一律不允许参与指针间隐式转换(除完全相同类型,见规则 4)。
|
||||
# ============================================================
|
||||
# _is_basic_scalar_ty - is ty a basic scalar (int/float)? 1=yes / 0=no.
|
||||
# Non-basic types (struct/array/func) may not take part in implicit ptr casts.
|
||||
def _is_basic_scalar_ty(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""ty 是否为基本类型(整数/浮点),1=是 / 0=否"""
|
||||
"""Is ty a basic scalar (int/float); 1=yes / 0=no."""
|
||||
if ty is None: return 0
|
||||
if self.get_llvm_type_bits(ty) != 0: return 1
|
||||
if self.get_llvm_float_bits(ty) != 0: return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _scalar_convert_reason - 基本标量 T → U 是否无损(沿用 coerce_to_type 既有规则)
|
||||
#
|
||||
# 整数/浮点:沿用既有规则(高→低允许安全降级、低→高无损;整数→浮点需浮点位宽
|
||||
# ≥ 整数位宽;浮点→整数不允许)。
|
||||
# 返回 PC_OK(无损/允许)或 PC_LOSSY(有损/禁止)。
|
||||
# ============================================================
|
||||
# _scalar_convert_reason - is the basic scalar conversion T -> U lossless?
|
||||
# Returns PC_OK (lossless/allowed) or PC_LOSSY (lossy/forbidden).
|
||||
def _scalar_convert_reason(self, src_pt: mlirlite.LLVMType*, dst_pt: mlirlite.LLVMType*) -> int:
|
||||
"""基本标量 src_pt → dst_pt 的隐式转换判定码(PC_OK / PC_LOSSY)"""
|
||||
"""Implicit basic-scalar conversion code src_pt -> dst_pt (PC_OK / PC_LOSSY)."""
|
||||
s_int: int = self.get_llvm_type_bits(src_pt)
|
||||
d_int: int = self.get_llvm_type_bits(dst_pt)
|
||||
s_flt: int = self.get_llvm_float_bits(src_pt)
|
||||
d_flt: int = self.get_llvm_float_bits(dst_pt)
|
||||
# 整数 ↔ 整数:高→低安全降级、低→高无损,均允许
|
||||
# int <-> int: narrowing and widening are both allowed.
|
||||
if s_int != 0 and d_int != 0: return PC_OK
|
||||
# 浮点 ↔ 浮点:允许
|
||||
# float <-> float: allowed.
|
||||
if s_flt != 0 and d_flt != 0: return PC_OK
|
||||
# 整数 → 浮点:浮点位宽 ≥ 整数位宽才无损
|
||||
# int -> float: lossless only when float width >= int width.
|
||||
if s_int != 0 and d_flt != 0:
|
||||
if d_flt >= s_int: return PC_OK
|
||||
return PC_LOSSY
|
||||
# 浮点 → 整数:有损,禁止
|
||||
# float -> int: lossy, forbidden.
|
||||
if s_flt != 0 and d_int != 0: return PC_LOSSY
|
||||
return PC_OK
|
||||
# ============================================================
|
||||
# _class_name_of_ptr_pointee - 指针被指类型 → 已注册类名
|
||||
#
|
||||
# 复用 StructGen.get_class_name_by_type(内部即 find_struct_by_type 首注册
|
||||
# 优先匹配 + 按名/后缀回退),把 LLVM 被指类型映射到类名;非结构体/未注册
|
||||
# 返回 None。不新造表。
|
||||
# ============================================================
|
||||
# _class_name_of_ptr_pointee - map a pointer's pointee type to a registered class name.
|
||||
# Reuses StructGen.get_class_name_by_type; returns None for non-struct/unregistered.
|
||||
def _class_name_of_ptr_pointee(self, pool: memhub.MemBuddy*, pt: mlirlite.LLVMType*) -> str:
|
||||
"""指针被指类型映射到已注册类名;失败返回 None"""
|
||||
"""Map a pointer's pointee type to a registered class name; None on failure."""
|
||||
if pt is None: return None
|
||||
return self.Gen.StructGen.get_class_name_by_type(pool, pt)
|
||||
# ============================================================
|
||||
# _is_inheritance_upcast - 继承上转判据(Derived* → Base*,含多级)
|
||||
#
|
||||
# 规则 2 的豁免:两侧被指类型都解析到已注册类时,若**目标类名出现在源类名
|
||||
# 的 MRO 链「自身之后」**(即目标为源的基类/祖先)⇒ 允许隐式上转。
|
||||
# 复用既有继承判据(StructGen.get_class_name_by_type + get_mro_chain),
|
||||
# 不引入新表。下转(Base* → Derived*:派生类不在基类 MRO 链里)与无关
|
||||
# 类型(Foo* ↔ Bar*)都不命中 ⇒ 返回 0,仍按规则 2/3 禁止。
|
||||
# ============================================================
|
||||
# _is_inheritance_upcast - Derived* -> Base* (incl. multi-level) exemption of rule 2.
|
||||
# Allowed when the target class appears after the source in its MRO chain.
|
||||
def _is_inheritance_upcast(self, pool: memhub.MemBuddy*, src_pt: mlirlite.LLVMType*, dst_pt: mlirlite.LLVMType*) -> int:
|
||||
"""派生类指针 → 基类指针(含多级):1=允许上转 / 0=否"""
|
||||
"""Derived* -> Base* upcast (incl. multi-level); 1=allowed / 0=no."""
|
||||
src_name: str = self._class_name_of_ptr_pointee(pool, src_pt)
|
||||
if src_name is None: return 0
|
||||
dst_name: str = self._class_name_of_ptr_pointee(pool, dst_pt)
|
||||
@@ -524,26 +350,17 @@ novirtual class ExprGenBase:
|
||||
if string.strcmp(src_name, dst_name) == 0: return 0
|
||||
chain: sizet* = self.Gen.StructGen.get_mro_chain(pool, src_name)
|
||||
if chain is None: return 0
|
||||
# chain[0] = 自身;父类/祖先在后,遇 0 终止。只查「自身之后」的祖先。
|
||||
# chain[0] = self; ancestors follow, terminated by 0. Only look after self.
|
||||
i: int = 1
|
||||
while chain[i] != 0:
|
||||
anc: str = rawptr(chain[i])
|
||||
if anc is not None and string.strcmp(anc, dst_name) == 0: return 1
|
||||
i += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _is_array_decay - 数组退让判据([N]T* → T*,单向)
|
||||
#
|
||||
# 规则 2 的豁免:源被指类型是**定长数组** Array(T, N),且目标被指类型恰为该
|
||||
# 数组的**元素类型** T ⇒ 允许隐式转换(数组指针 → 元素指针,C/C++ 同口径)。
|
||||
# 多维数组按「最外层退让一层」自然处理:Array(Array(T, M), N) 的元素是
|
||||
# Array(T, M) ⇒ Array(Array(T,M),N)* → Array(T,M)* 命中;→ T* 不命中
|
||||
# (元素是 Array(T,M) ≠ T)。反向 T* → Array(T,N)* 因源被指类型非数组而不命中。
|
||||
# 元素类型同一性复用本仓既有 AssignGen._stg_ty_equal(内容比较),不新造表。
|
||||
# 返回 1=允许退让 / 0=否。
|
||||
# ============================================================
|
||||
# _is_array_decay - [N]T* -> T* one-way array decay exemption of rule 2.
|
||||
# Matches when the source pointee is a fixed array and the target is its element.
|
||||
def _is_array_decay(self, pool: memhub.MemBuddy*, src_pt: mlirlite.LLVMType*, dst_pt: mlirlite.LLVMType*) -> int:
|
||||
"""源被指类型是定长数组且目标被指类型是其元素类型:1=是 / 0=否"""
|
||||
"""Source pointee is a fixed array and target is its element type; 1=yes / 0=no."""
|
||||
if src_pt is None or dst_pt is None: return 0
|
||||
if self.is_array_type(src_pt) == 0: return 0
|
||||
elem: mlirlite.LLVMType* = None
|
||||
@@ -554,82 +371,56 @@ novirtual class ExprGenBase:
|
||||
return 0
|
||||
if elem is None: return 0
|
||||
return self.Gen.AssignGen._stg_ty_equal(pool, elem, dst_pt)
|
||||
# ============================================================
|
||||
# _pointee_convert_reason - 被指类型 src_pt → dst_pt(T*→U* 的 T→U 关系)判定码
|
||||
#
|
||||
# 规则 2:T* → U*(T、U 都非通用指针)允许 ⇔ 被指类型 T → U 无损。
|
||||
# 豁免一:T、U 都是已注册类且 U 是 T 的基类(含多级)时,Derived* → Base*
|
||||
# 继承上转允许(见 _is_inheritance_upcast)。
|
||||
# 豁免二:T 是定长数组、U 恰为其元素类型时,数组退让 [N]T* → T* 允许(单向,
|
||||
# 见 _is_array_decay)。反向 T* → [N]T* 不命中,仍禁止。
|
||||
# 规则 3:非基本类型 T 与其指针 T* 之间(值/指针互换)一律禁止——此处体现为
|
||||
# 被指类型一侧是「非基本值类型」、另一侧是「其指针(或其它的基本/非基本类型)」
|
||||
# 时一律不匹配 ⇒ PC_NONBASIC。
|
||||
# 被指类型仍是指针(T** → U**):递归按指针规则判。
|
||||
# ============================================================
|
||||
# _pointee_convert_reason - reason code for pointee T -> U (the T->U of T* -> U*).
|
||||
# Applies rule 2 with the inheritance-upcast and array-decay exemptions.
|
||||
def _pointee_convert_reason(self, pool: memhub.MemBuddy*, src_pt: mlirlite.LLVMType*, dst_pt: mlirlite.LLVMType*) -> int:
|
||||
"""被指类型 src_pt → dst_pt 的判定码(PC_OK/PC_NONBASIC/PC_LOSSY/PC_DEPTH)"""
|
||||
"""Conversion code for pointee src_pt -> dst_pt (PC_OK/PC_NONBASIC/PC_LOSSY/PC_DEPTH)."""
|
||||
if src_pt is None or dst_pt is None: return PC_OK
|
||||
s_is_ptr: int = self.is_ptr_type(src_pt)
|
||||
d_is_ptr: int = self.is_ptr_type(dst_pt)
|
||||
if s_is_ptr != 0 or d_is_ptr != 0:
|
||||
# 一侧是指针、另一侧不是(如被指类型 Foo vs Foo*)→ 非基本类型值/指针互换,禁止
|
||||
# One side ptr, other not (e.g. Foo vs Foo*): non-basic swap, forbidden.
|
||||
if s_is_ptr == 0 or d_is_ptr == 0: return PC_NONBASIC
|
||||
# 两侧都是指针:递归
|
||||
# Both sides are pointers: recurse.
|
||||
return self._ptr_cast_reason(pool, src_pt, dst_pt)
|
||||
# 一侧是非基本类型(结构体/类/联合/数组/函数)→ 一般禁止
|
||||
# One side is non-basic (struct/class/union/array/func): generally forbidden.
|
||||
if self._is_basic_scalar_ty(src_pt) == 0 or self._is_basic_scalar_ty(dst_pt) == 0:
|
||||
# 豁免一:Derived* → Base* 继承上转(含多级)允许(C++/GCC 同口径)
|
||||
# Exemption 1: Derived* -> Base* inheritance upcast (incl. multi-level).
|
||||
if self._is_inheritance_upcast(pool, src_pt, dst_pt) != 0: return PC_OK
|
||||
# 豁免二:数组退让(单向)——源被指类型是定长数组 Array(T, N)、目标被指
|
||||
# 类型恰为其元素类型 T ⇒ 允许(数组指针 → 元素指针)。多维按最外层退让
|
||||
# 一层;反向 T → Array(T, N) 与其它非基本类型规则不变,仍禁止。
|
||||
# Exemption 2: one-way array decay [N]T* -> T* (source pointee is a
|
||||
# fixed array, target pointee is its element type).
|
||||
if self._is_array_decay(pool, src_pt, dst_pt) != 0: return PC_OK
|
||||
return PC_NONBASIC
|
||||
return self._scalar_convert_reason(src_pt, dst_pt)
|
||||
# ============================================================
|
||||
# _ptr_cast_reason - 指针 → 指针隐式转换的判定码(新规则单一真源)
|
||||
#
|
||||
# 规则 1:通用指针(t.CPtr / i8*)与任何 T* 双向互转都放行 ⇒ PC_OK。
|
||||
# 规则 2:T* → U*(T、U 都非通用指针)允许 ⇔ 被指类型 T → U 无损;非基本类型
|
||||
# 一律不允许。
|
||||
# 规则 3:非基本类型 T 与其指针 T* 之间一律禁止。
|
||||
# 规则 4:完全相同类型(指针深度与被指类型都相同,含 Foo* → Foo*)不算转换 ⇒ PC_OK。
|
||||
# 返回 PC_OK(允许)/ PC_DEPTH / PC_NONBASIC / PC_LOSSY。
|
||||
# ============================================================
|
||||
# _ptr_cast_reason - reason code for implicit ptr -> ptr casts (single source of truth).
|
||||
# Rules 1-4: wildcard ptr, lossless pointee, non-basic forbidden, identity allowed.
|
||||
def _ptr_cast_reason(self, pool: memhub.MemBuddy*, src_ty: mlirlite.LLVMType*, dst_ty: mlirlite.LLVMType*) -> int:
|
||||
"""指针 → 指针对隐式转换的判定码(PC_OK 表示允许)"""
|
||||
"""Reason code for implicit ptr -> ptr casts (PC_OK means allowed)."""
|
||||
if src_ty is None or dst_ty is None: return PC_OK
|
||||
slev: int = self.get_ptr_level(src_ty)
|
||||
dlev: int = self.get_ptr_level(dst_ty)
|
||||
if slev == 0 or dlev == 0: return PC_OK
|
||||
# 层数不同 → 禁止(无论被指类型)
|
||||
# Different depth: forbidden regardless of pointee.
|
||||
if slev != dlev: return PC_DEPTH
|
||||
# 规则 4:完全相同类型(层数 + 逐级被指类型都相同)不算转换
|
||||
# Rule 4: identical types (same depth and pointee) are not a conversion.
|
||||
if self.Gen.AssignGen._stg_ty_equal(pool, src_ty, dst_ty) != 0: return PC_OK
|
||||
# 规则 1:通用指针是通配(i8* ↔ 任何 T*)
|
||||
# Rule 1: universal pointers are wildcards (i8* <-> any T*).
|
||||
if self._is_universal_ptr_ty(src_ty) != 0 or self._is_universal_ptr_ty(dst_ty) != 0: return PC_OK
|
||||
# 规则 2/3:逐级比较被指类型
|
||||
# Rules 2/3: compare pointee level by level.
|
||||
return self._pointee_convert_reason(pool, src_ty.Pointee, dst_ty.Pointee)
|
||||
# ============================================================
|
||||
# _type_text - LLVM 类型的可读文本(TypePrint),供报文使用(失败返回 "?")
|
||||
# ============================================================
|
||||
# _type_text - readable LLVM type text via TypePrint ("?" on failure).
|
||||
def _type_text(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> str:
|
||||
"""LLVM 类型文本(TypePrint);失败返回 "?" """
|
||||
"""Readable LLVM type text via TypePrint; "?" on failure."""
|
||||
if ty is None: return "?"
|
||||
buf: str = pool.alloc(512)
|
||||
if buf is None: return "?"
|
||||
mlirlite.TypePrint(buf, 512, ty, pool)
|
||||
if buf[0] == 0: return "?"
|
||||
return buf
|
||||
# ============================================================
|
||||
# _ptr_cast_reason_text - 把指针间隐式转换的禁止原因写成可读文本
|
||||
#
|
||||
# 报文写清「哪一级不允许 + 为什么(非基本类型 / 有损 / 层数不同)」,并给出
|
||||
# 源/目标 LLVM 类型文本。返回值会逃逸 ⇒ 经 escape_fstring_copy 拷到 stdlib 堆。
|
||||
# ============================================================
|
||||
# _ptr_cast_reason_text - human-readable text for a forbidden ptr -> ptr cast.
|
||||
# Names the level and why, plus source/target types; result is heap-copied.
|
||||
def _ptr_cast_reason_text(self, code: int, pool: memhub.MemBuddy*, src_ty: mlirlite.LLVMType*, dst_ty: mlirlite.LLVMType*) -> str:
|
||||
"""指针间隐式转换禁止原因的可读文本(含源/目标类型)"""
|
||||
"""Human-readable reason for a forbidden ptr -> ptr cast (includes source/target types)."""
|
||||
why: str = "原因未知"
|
||||
if code == PC_DEPTH: why = f"指针层数不同(源 {self.get_ptr_level(src_ty)} 级 → 目标 {self.get_ptr_level(dst_ty)} 级)"
|
||||
elif code == PC_NONBASIC: why = "被指类型是非基本类型(结构体/类/联合/数组等):非基本类型与其指针之间、以及不同非基本类型的指针之间一律不允许隐式转换"
|
||||
@@ -637,19 +428,10 @@ novirtual class ExprGenBase:
|
||||
s_txt: str = self._type_text(pool, src_ty)
|
||||
d_txt: str = self._type_text(pool, dst_ty)
|
||||
return GenApi.escape_fstring_copy(f"指针隐式转指针已禁止:{why};源类型 {s_txt} → 目标类型 {d_txt}")
|
||||
# ============================================================
|
||||
# _ctor_call_agg_ty - 判据:node 是否为「类/结构体构造调用」;是则返回其聚合类型
|
||||
#
|
||||
# 规则 3(非基本类型值 ↔ 指针的隐式转换一律禁止)的**唯一例外**=「构造物化」:
|
||||
# `p: Point = Point()` —— 右值来自该类型的构造调用(构造语义,不是类型转换)。
|
||||
# 本判据在 **AST 层**复用本仓既有「Call 的被调方是类/构造」识别:
|
||||
# - 先经 _value_node_of 把外层语句节点(AnnAssign/Assign 等)下沉到真正的右值;
|
||||
# - callee 裸名/属性名 → StructGen.find_struct(已注册结构体/类)→ 其聚合类型;
|
||||
# - 泛型类模板(Box(42))特化名需类型实参推断,静态取不到类型 → 保守返回 None。
|
||||
# 非构造调用(普通函数/方法/未知符号)一律返回 None(→ 不进豁免,照常报错)。
|
||||
# ============================================================
|
||||
# _ctor_call_agg_ty - aggregate type if node is a class/struct constructor call.
|
||||
# The only exception to rule 3 ("construction materialization"); else None.
|
||||
def _ctor_call_agg_ty(self, node: ast.AST*) -> mlirlite.LLVMType*:
|
||||
"""node(或其值子节点)是构造调用 → 返回其构造的聚合类型;否则 None"""
|
||||
"""Aggregate type if node (or its value child) is a constructor call; else None."""
|
||||
n: ast.AST* = self._value_node_of(node)
|
||||
if n is None or n.kind() != ast.ASTKind.Call: return None
|
||||
cl: ast.Call* = ast.Call*(n)
|
||||
@@ -664,16 +446,11 @@ novirtual class ExprGenBase:
|
||||
if callee is None: return None
|
||||
ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(callee)
|
||||
if ent is not None and ent.Ty is not None: return ent.Ty
|
||||
# REnum 变体构造 `EnumType.Variant(...)`:被调方是「基础枚举.变体」,变体名不是
|
||||
# 结构体名(find_struct 必 miss);但构造结果物化的就是**基础枚举**的整对象
|
||||
# (boxed 指针指向该枚举结构体)⇒ 返回该 REnum 的聚合类型,使「构造物化」豁免
|
||||
# 覆盖 `*槽 = <boxed 变体>`。否则该写法会被「指针 → 非基本类型值」禁令拦下
|
||||
# (现场 ClassGenDef.vp:128 `*existing.Ty = mlirlite.LLVMType.Struct(...)`)。
|
||||
# REnum variant ctor `EnumType.Variant(...)`: the callee is not a struct
|
||||
# name, but materializes the base enum, so return that aggregate type.
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
# 取「变体名左边那一段」作为枚举类名:被调方形如 `<限定链>.<变体>`,
|
||||
# EnumType.Variant → 限定段是裸名 EnumType
|
||||
# mlirlite.LLVMType.Struct → 限定段是属性 LLVMType(**不是**链根
|
||||
# mlirlite,早期实现走到链根导致漏判)
|
||||
# Take the segment left of the variant name as the enum class name
|
||||
# (the qualifier, not the chain root which caused earlier misses).
|
||||
qual: ast.AST* = (ast.Attribute*(cl.func)).value
|
||||
base_nm: str = None
|
||||
if qual is not None:
|
||||
@@ -683,122 +460,76 @@ novirtual class ExprGenBase:
|
||||
rn_ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(base_nm)
|
||||
if rn_ent is not None and rn_ent.Ty is not None: return rn_ent.Ty
|
||||
return None
|
||||
# ============================================================
|
||||
# _same_agg_ty - 两个聚合类型是否为「同一非基本类型」(构造物化豁免的类型匹配)
|
||||
# ============================================================
|
||||
# _same_agg_ty - are two aggregate types the same (type match for ctor exemption)?
|
||||
def _same_agg_ty(self, pool: memhub.MemBuddy*, a: mlirlite.LLVMType*, b: mlirlite.LLVMType*) -> int:
|
||||
"""a、b 是否为同一聚合类型,1=是 / 0=否"""
|
||||
"""Are a and b the same aggregate type; 1=yes / 0=no."""
|
||||
if a is None or b is None: return 0
|
||||
return self.Gen.AssignGen._stg_ty_equal(pool, a, b)
|
||||
# ============================================================
|
||||
# 类型强制转换:整数 sext/trunc,浮点 fpext/fptrunc,int↔float si2fp/fp2si
|
||||
# ============================================================
|
||||
# Type coercion: int sext/trunc, float fpext/fptrunc, int<->float si2fp/fp2si.
|
||||
def coerce_to_type(self, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, target_ty: mlirlite.LLVMType*, node: ast.AST* = None, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""将 val 强制转换为 target_ty
|
||||
|
||||
整数: sext(扩展)/ trunc(截断)
|
||||
浮点: fpext(扩展)/ fptrunc(截断)
|
||||
int→float: sitofp
|
||||
float→int: fptosi
|
||||
|
||||
node: AST 节点(用于错误定位),可为 None
|
||||
"""
|
||||
"""Coerce val to target_ty: int sext/trunc, float fpext/fptrunc, int/float conversions."""
|
||||
if val is None or target_ty is None: return val
|
||||
# [FIX 2026-09-28] None(空值常量)允许隐式转换为任意类型:None 无语义
|
||||
# 类型信息,赋给任意类型只是「置空/置零」,故下方所有针对**真实类型值**
|
||||
# 的隐式转换禁令(指针→整数 / 整数→指针 / 指针→非指针值 / float↔ptr)
|
||||
# 对 None 一律短路放行。统一收敛到 IsNoneValue 单一判据,避免遗漏。
|
||||
# 指针目标仍走原有「指针→指针 bitcast」路径(保留既有 IR 形态);
|
||||
# 非指针目标直接物化为目标类型的零值/空值。
|
||||
# 真实用例:`p.fd_used[fd] = None`(fd_used 为整数数组字段)曾被
|
||||
# 「指针隐式转整数已禁止」拦成 CastError。
|
||||
# [FIX 2026-09-28] None may convert to any type: it carries no type info,
|
||||
# so all "real value" implicit-cast bans short-circuit for it (IsNoneValue).
|
||||
if self.IsNoneValue(val, node) != 0:
|
||||
if self.is_ptr_type(target_ty) != 0: return mlirlite.build_bitcast(builder, val, target_ty)
|
||||
return self._NoneValueAsTarget(builder, target_ty)
|
||||
# void 类型的值**不允许被当作值使用**(用户 2026-10-01:void 只能用于 `void*`
|
||||
# 与 `typedef 不透明结构体 = void`)。任何把 void 值 coerce 到非 void 目标
|
||||
# (赋值 / 传参 / 返回 / 二元运算对齐)都是无意义操作 ⇒ fail-fast,而不是
|
||||
# 按 LLVM void 硬拼出非法 IR("load void"/"bitcast void")。void → void
|
||||
# 属「无值传递」,放行。
|
||||
# A void-typed value may not be used as a value: coerce to a non-void target
|
||||
# fails fast instead of emitting illegal IR (load void / bitcast void).
|
||||
if self.Gen.StmtGenDecl._is_void_type(val.Ty) != 0:
|
||||
if self.Gen.StmtGenDecl._is_void_type(target_ty) == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), "void 类型的值不能参与任何操作(void 只能用于 void* 与 typedef 别名)", "TypeError")
|
||||
return val
|
||||
# 函数 → 指针: bitcast(如 CreateThread(..., _thread_alloc_free, ...) 传函数指针)
|
||||
# 函数 Value 的 Ty 是 Func,既不是整数也不是指针,需在整数/浮点转换之前处理
|
||||
# func -> ptr: bitcast (function Values have Func type, handled before
|
||||
# the integer/float conversions).
|
||||
match val.Ty:
|
||||
case mlirlite.LLVMType.Func(_, _, _):
|
||||
if self.is_ptr_type(target_ty) != 0: return mlirlite.build_bitcast(builder, val, target_ty)
|
||||
return val
|
||||
case _:
|
||||
pass
|
||||
# 整数转换
|
||||
# Integer conversion.
|
||||
val_bits: int = self.get_llvm_type_bits(val.Ty)
|
||||
target_bits: int = self.get_llvm_type_bits(target_ty)
|
||||
# 整数 → 指针: 禁止隐式转换(FIX 2026-08-30),唯一豁免是 C 空指针常量 0
|
||||
# (NULL: t.CDefine = 0 / 字面量 0)——C 语义中 0 赋指针即 null pointer
|
||||
# constant,inttoptr 0 生成合法空指针,不属于"整数当指针"的类型信息缺失。
|
||||
# 原为 inttoptr 兜底(跨模块方法返回 i64/i32 默认推断时静默转指针),
|
||||
# 掩盖类型信息缺失——如 `BootInfo = saved_rdi`(整数 → bootinfo*)。
|
||||
# 按"按需过滤/不兜底/fail-fast"原则:非常量整数必须显式强转
|
||||
# (t.CPtr(x) 无类型指针右值可隐式转为左侧类型化指针,走指针→指针
|
||||
# bitcast 路径)。
|
||||
# int -> ptr: implicit conversion forbidden (FIX 2026-08-30); only the C
|
||||
# null pointer constant 0 is exempt. Non-constant ints need an explicit cast.
|
||||
if val_bits != 0 and self.is_ptr_type(target_ty) != 0:
|
||||
# [FIX 2026-08-31] C 空指针常量豁免:编译期常量 0 赋给指针 → 空指针。
|
||||
# NULL 是 t.CDefine = 0(如 self.handle = NULL / CreateThread(NULL,...)),
|
||||
# 属标准 C 空指针用法,须放行而非报 CastError。先拓宽到 i64
|
||||
# (inttoptr 要求同宽,对齐 MatchGenMixin 模式),再 inttoptr 成空指针。
|
||||
# [FIX 2026-08-31] C null pointer constant exemption: compile-time 0
|
||||
# assigned to a pointer becomes a null pointer (widen to i64, then inttoptr).
|
||||
if val.IsConst == 1 and val.IntVal == 0: return mlirlite.build_inttoptr(builder, mlirlite.const_int64(builder.Pool, 0), target_ty)
|
||||
m_i2p: str = "整数隐式转指针已禁止:必须显式强转为指针(如 t.CPtr(x))才能把整数赋给指针类型"
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), m_i2p, "CastError")
|
||||
return val
|
||||
# Pointer -> integer: rejected (symmetric with the integer -> pointer rule above).
|
||||
# No implicit pointer <-> integer conversion is allowed; an explicit cast is
|
||||
# required. (The old code silently did ptrtoint, and for 8-bit targets fell
|
||||
# through to the build_load branch below, copying the pointee's first byte.)
|
||||
# Pointer -> integer: rejected (symmetric with the int -> ptr rule above).
|
||||
# An explicit cast is required; the old code silently did ptrtoint.
|
||||
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
m_p2i: str = "指针隐式转整数已禁止:必须显式强转(如 uint64(p) / int64(p) / int(p))才能把指针赋给整数类型"
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), m_p2i, "CastError")
|
||||
return val
|
||||
if val_bits != 0 and target_bits != 0:
|
||||
if val_bits == target_bits: return val
|
||||
# 目标 i1(bool / t.CBool):**真值归一化** `x != 0 → 1/0`(C 6.3.1.2 的
|
||||
# _Bool 转换语义),不是 trunc(取最低位)。判据与 `bool(x)`
|
||||
# (CallGenDispatch.vp:255-281)、条件归一化(AsyncGenMixin.vp:1611
|
||||
# _normalize_cond)、and/or 操作数三处**同一**:同一个运行时值 2 在
|
||||
# `if 2:` 里为真,赋给 bool 变量/字段/形参必须同样为真。此处此前走 trunc
|
||||
# ⇒ 值 2 取最低位得 0 → false,与条件上下文结论相反(静默语义分裂);
|
||||
# 值 3 得 true 而同值域下 `!= 0` 亦为 true,故只在偶数非零时暴露。
|
||||
# 归一化不丢位 ⇒ 下方「窄化截断溢出」常量诊断对 i1 目标不适用(先返回)。
|
||||
# Target i1 (bool / t.CBool): truth normalization `x != 0 -> 1/0`
|
||||
# (C 6.3.1.2), not trunc; keeps parity with `bool(x)` and condition paths.
|
||||
if target_bits == 1:
|
||||
zero_i1: mlirlite.Value* = mlirlite.const_int(builder.Pool, val_bits, 0)
|
||||
return mlirlite.build_icmp(builder, mlirlite.ICMP_NE, val, zero_i1)
|
||||
if val_bits < target_bits:
|
||||
# 统一 zext/sext(问题2修正):i1/i8 默认 zext;AST 节点解析为
|
||||
# 无符号类型(t.CUIntXXT 强转/无符号类型名)走 zext;否则 sext。
|
||||
# 此前一律 sext(i1 除外)→ uint32 等无符号值赋值/传参被符号
|
||||
# 扩展(高位置 1,如 0xFFFFFFFF 变 0xFFFFFFFFFFFFFFFF),与比较路径
|
||||
# should_zext_widen(i8 默认 zext)规则相反 → 隐蔽数据损坏。
|
||||
# trans 必须下传:_node_type_unsigned 对 Name/Subscript 形态需经
|
||||
# trans.SymTab 查变量注解(uint16/sec:uint32 等)才能判定无符号,
|
||||
# 否则返回未知 → 退回 sext(无符号扩展缺陷根因)。
|
||||
# Unified zext/sext: i1/i8 default zext, unsigned AST nodes zext,
|
||||
# otherwise sext. trans is required so unsigned annotations resolve.
|
||||
if self._zext_or_sext_val(builder.Pool, val, node, trans) != 0: return mlirlite.build_zext(builder, val, target_ty)
|
||||
return mlirlite.build_sext(builder, val, target_ty)
|
||||
# 窄化截断诊断(_Type.md 问题 8):宽→窄 trunc 静默丢位。
|
||||
# 编译期已知常量且正溢出目标位宽无符号范围 → fail-fast(确定性数据
|
||||
# 损坏,如 buf[idx] = 0x1FF 写入 i8 槽位被静默截断)。负常量按模
|
||||
# 2^bits 语义放行(-1 → 0xFF 合法);非常量运行时窄化保留 trunc
|
||||
# (值域可能安全,无法静态判定)。
|
||||
# Narrowing diagnostic (_Type.md issue 8): a known constant exceeding the
|
||||
# target unsigned range fails fast; negative constants wrap (mod 2^bits).
|
||||
if val.IsConst == 1 and val.IntVal > 0:
|
||||
target_max_u: uint64 = (uint64(1) << target_bits) - 1
|
||||
if uint64(val.IntVal) > target_max_u:
|
||||
max_print: int64 = int64(target_max_u)
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"窄化截断溢出:常量 {val.IntVal} 超出目标类型 {target_bits} 位范围 [0, {max_print}],赋值将被静默截断", "OverflowError")
|
||||
return mlirlite.build_trunc(builder, val, target_ty)
|
||||
# 浮点转换
|
||||
# Float conversion.
|
||||
val_fbits: int = self.get_llvm_float_bits(val.Ty)
|
||||
target_fbits: int = self.get_llvm_float_bits(target_ty)
|
||||
# float ↔ ptr: 禁止(无意义的转换,fast-fail 防止误走入 build_load 路径)
|
||||
# float <-> ptr: forbidden (fast-fail to avoid the bogus build_load path).
|
||||
if (val_fbits != 0 and self.is_ptr_type(target_ty) != 0) or (self.is_ptr_type(val.Ty) != 0 and target_fbits != 0):
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), "unsupported type conversion: float <-> ptr", "CastError")
|
||||
return val
|
||||
@@ -806,16 +537,17 @@ novirtual class ExprGenBase:
|
||||
if val_fbits == target_fbits: return val
|
||||
if val_fbits < target_fbits: return mlirlite.build_fpext(builder, val, target_ty)
|
||||
return mlirlite.build_fptrunc(builder, val, target_ty)
|
||||
# int → float
|
||||
# int -> float
|
||||
if val_bits != 0 and target_fbits != 0: return mlirlite.build_si2fp(builder, val, target_ty)
|
||||
# float → int
|
||||
# float -> int
|
||||
if val_fbits != 0 and target_bits != 0: return mlirlite.build_fp2si(builder, val, target_ty)
|
||||
# 聚合值 → 指针:落栈 materialize 临时对象后传其地址。FuncGenDef 已确认「裸类/数组形参一律 lowering 为 Ptr(struct/array)」——TPV 内部函数/方法不存在"按值传结构体"的形参(唯一例外:C ABI 外部声明的裸结构体形参按值,见 VpiReader._AggregateParamTy「三处必须同步」)。故调用点的 rvalue 聚合(函数调用返回值、下标元素 load 等)必须落栈取址,否则实参类型与指针形参失配。alloca 走入口块(支配性安全),生命周期覆盖整个调用,pointee 与聚合类型一致时直接用 alloca 地址
|
||||
# Aggregate value -> pointer: spill a temp to the entry block and pass its
|
||||
# address (TPV never passes aggregates by value).
|
||||
if self.is_ptr_type(val.Ty) == 0 and self.is_ptr_type(target_ty) != 0:
|
||||
vk_agg: int = val.Ty.kind()
|
||||
if vk_agg == mlirlite.LLVMType.Struct or vk_agg == mlirlite.LLVMType.Array:
|
||||
# 规则 3:非基本类型的「值 → 指针」隐式转换(Foo → Foo*)一律禁止,
|
||||
# 唯一例外=「构造物化」:右值来自该类型的构造调用时放行(见 _ctor_call_agg_ty)。
|
||||
# Rule 3: implicit value -> ptr for non-basic types (Foo -> Foo*) is
|
||||
# forbidden, except "construction materialization" (see _ctor_call_agg_ty).
|
||||
ctor_v2p: mlirlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||||
if self._same_agg_ty(builder.Pool, ctor_v2p, val.Ty) == 0:
|
||||
m_v2p: str = "非基本类型的值/指针隐式转换已禁止(构造物化除外):不能把非基本类型值隐式转为指针(Foo → Foo*),如需取址请显式 c.addr(...) / &v,或显式强转"
|
||||
@@ -833,15 +565,13 @@ novirtual class ExprGenBase:
|
||||
bc_agg: mlirlite.Value* = mlirlite.build_bitcast(builder, tmp_agg, target_ty)
|
||||
if bc_agg is not None: return bc_agg
|
||||
return tmp_agg
|
||||
# Pointer -> non-pointer value: aggregate targets keep the implicit load **only**
|
||||
# for 「构造物化」(a constructor returning Ptr(Struct) held in a Struct-typed
|
||||
# variable is reinterpreted in place); a real Foo* value must be explicitly
|
||||
# dereferenced. Scalar integer targets were already rejected above.
|
||||
# Pointer -> non-pointer value: aggregate targets keep the implicit load only
|
||||
# for construction materialization; real Foo* values must be dereferenced.
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
target_kind: int = target_ty.kind()
|
||||
if target_kind == mlirlite.LLVMType.Struct or target_kind == mlirlite.LLVMType.Array:
|
||||
# 规则 3:非基本类型的「指针 → 值」隐式转换(Foo* → Foo)一律禁止,
|
||||
# 唯一例外=「构造物化」:右值来自该类型的构造调用时放行(见 _ctor_call_agg_ty)。
|
||||
# Rule 3: implicit ptr -> value for non-basic types (Foo* -> Foo) is
|
||||
# forbidden, except "construction materialization" (see _ctor_call_agg_ty).
|
||||
ctor_p2v: mlirlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||||
if self._same_agg_ty(builder.Pool, ctor_p2v, target_ty) == 0:
|
||||
m_p2v: str = "非基本类型的值/指针隐式转换已禁止(构造物化除外):不能把指针类型隐式转为非基本类型值(Foo* → Foo),如需解引用请显式 c.deref(p) / *p,或显式强转"
|
||||
@@ -851,12 +581,8 @@ novirtual class ExprGenBase:
|
||||
return mlirlite.build_load(builder, target_ty, val)
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), f"指针隐式转非指针值已禁止:必须显式解引用(*p / *p)或显式强转(uint64(p))", "CastError")
|
||||
return val
|
||||
# 指针 → 指针: 隐式转换须满足两条新规则(见 _ptr_cast_reason 单一真源):
|
||||
# 规则 1 通用指针(t.CPtr / i8*)与任何 T* 双向互转都放行;
|
||||
# 规则 2 T* → U* 允许 ⇔ 被指类型 T → U 无损,非基本类型一律禁止;
|
||||
# 规则 3 非基本类型 T 与其指针 T* 之间一律禁止;
|
||||
# 规则 4 完全相同类型(含 Foo* → Foo*)不算转换,照常通过。
|
||||
# 违反者沿用既有 fail-fast 风格报错(CastError + 「…已禁止:」前缀)。
|
||||
# ptr -> ptr: implicit conversion must satisfy the new rules (see
|
||||
# _ptr_cast_reason); violators fail fast with a CastError.
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) != 0:
|
||||
pc: int = self._ptr_cast_reason(builder.Pool, val.Ty, target_ty)
|
||||
if pc != PC_OK:
|
||||
@@ -867,25 +593,16 @@ novirtual class ExprGenBase:
|
||||
return val
|
||||
|
||||
def _zext_or_sext_val(self, pool: memhub.MemBuddy*, val: mlirlite.Value*, node: ast.AST* = None, trans: HT.Translator* = None) -> int:
|
||||
"""统一整数拓宽方向:1=zext(无符号语义),0=sext
|
||||
|
||||
委托到 ExprGenOps.should_zext_widen(拓宽方向的**单一真源**),避免两套
|
||||
规则各自演化导致同一「窄→宽」在 coerce 路径与比较/二元路径结论相反。
|
||||
coerce 路径无「宽侧值」上下文,wide_val 传 None 使「宽侧常量超窄侧有符号
|
||||
范围」那条启发式自然失效;其余三条(i1/i8、源无符号、CDefine 高位常量)
|
||||
与旧实现一致或更强。
|
||||
|
||||
trans 供 _node_type_unsigned 经 SymTab 解析变量注解的符号性(Name/
|
||||
Subscript 形态必需);省略则仅能识别显式 t.CUIntXXT 强转。
|
||||
"""
|
||||
"""Unified integer widening direction: 1=zext (unsigned), 0=sext; delegates to ExprGenOps.should_zext_widen."""
|
||||
if val is None or val.Ty is None: return 0
|
||||
return self.Gen.ExprGenOps.should_zext_widen(pool, val, None, node, trans)
|
||||
|
||||
# translate_ptr_arith:指针算术 ptr±int,用 getelementptr(按 pointee 元素大小缩放索引,对齐 TPC ExprGen.emit_binary_op / HandlesAugAssign.py);数组 pointee([N x ty]*)先 decay 为元素指针(GEP 0,0),避免以整个数组为步长
|
||||
# translate_ptr_arith: ptr +/- int via getelementptr (index scaled by pointee
|
||||
# size); array pointees ([N x ty]*) decay to element pointers first.
|
||||
def translate_ptr_arith(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, ptr_val: mlirlite.Value*, int_val: mlirlite.Value*, op: int) -> mlirlite.Value*:
|
||||
"""指针算术: ptr + int / ptr - int → GEP(按元素大小缩放索引)"""
|
||||
"""Pointer arithmetic: ptr +/- int via GEP with the index scaled by element size."""
|
||||
if ptr_val is None or int_val is None or ptr_val.Ty is None: return None
|
||||
# 提取 pointee 类型(非指针 → 不处理返回 None)
|
||||
# Extract the pointee type (non-pointer returns None).
|
||||
elem_ty: mlirlite.LLVMType* = None
|
||||
match ptr_val.Ty:
|
||||
case mlirlite.LLVMType.Ptr(pointee):
|
||||
@@ -893,16 +610,16 @@ novirtual class ExprGenBase:
|
||||
case _:
|
||||
return None
|
||||
if elem_ty is None: return None
|
||||
# 索引统一到 i64(对齐 TPC zext 到 i64)
|
||||
# Normalize the index to i64.
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
idx: mlirlite.Value* = self.coerce_to_type(builder, int_val, i64_ty, None)
|
||||
if idx is None: return None
|
||||
# ptr - int → 负索引(LLVM GEP 索引为加法语义)
|
||||
# ptr - int -> negative index (LLVM GEP indices are additive).
|
||||
if op == ast.OpKind.Sub:
|
||||
neg_idx: mlirlite.Value* = mlirlite.build_sub(builder, mlirlite.const_int64(pool, 0), idx)
|
||||
if neg_idx is None: return None
|
||||
idx = neg_idx
|
||||
# 数组 pointee → 先 decay 为元素指针(getelementptr [N x ty], [N x ty]* %p, i32 0, i32 0)
|
||||
# Array pointee: decay to an element pointer first (GEP 0,0).
|
||||
if self.is_array_type(elem_ty) != 0:
|
||||
inner_ty: mlirlite.LLVMType* = None
|
||||
match elem_ty:
|
||||
@@ -916,7 +633,7 @@ novirtual class ExprGenBase:
|
||||
return mlirlite.build_gep(builder, inner_ty, decayed, idx)
|
||||
return mlirlite.build_gep(builder, elem_ty, ptr_val, idx)
|
||||
def InitExprGen(self) -> int:
|
||||
"""初始化 ExprGen 族字段(构造时由 Generator 调用)"""
|
||||
"""Initialize ExprGen family fields (called by Generator at construction)."""
|
||||
if self is None: return 1
|
||||
self.DocKeys = None
|
||||
self.DocValues = None
|
||||
|
||||
@@ -18,23 +18,23 @@ import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenCdef - ExprGenMixin 拆分(阶段 D):跨模块 CDefine 查找
|
||||
# ============================================================
|
||||
# ExprGenCdef - ExprGenMixin split (stage D): cross-module CDefine lookup
|
||||
novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
"""跨模块 CDefine 查找"""
|
||||
"""Cross-module CDefine lookup"""
|
||||
|
||||
# _build_source_path_from_sha1:通过 SHA1 从 StubMerger 全局存储器构建源 .vp/.py 文件路径(.vpi 未生成时回退读源 .py);rel_path 以 "lib/" 开头 → {Config.SourceDir}/{rel_path},其他 → {Config.IncludesDir}/{rel_path}。返回 stdlib.malloc 分配的路径字符串(调用者负责 free),失败返回 None
|
||||
# _build_source_path_from_sha1: build a source .vp/.py path from SHA1 via the StubMerger
|
||||
# global store (.vpi missing -> source .py); "lib/..." -> SourceDir, else IncludesDir.
|
||||
def _build_source_path_from_sha1(self, sha1: str) -> str:
|
||||
"""通过 SHA1 从全局存储器构建源 .py 文件路径"""
|
||||
"""Build a source .py path from a SHA1 via the global store"""
|
||||
if sha1 is None: return None
|
||||
if Config.IncludesDir is None: return None
|
||||
# 从 StubMerger 全局存储器获取相对路径数组
|
||||
# get the rel-path array from the StubMerger global store
|
||||
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
|
||||
count: int = StubMergerStore.GetSha1StoreCount()
|
||||
if rel_arr_ptr is None or count <= 0: return None
|
||||
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
|
||||
if sha1_arr_ptr is None: return None
|
||||
# 遍历查找匹配的 SHA1
|
||||
# iterate to find the matching SHA1
|
||||
idx_s: int = -1
|
||||
for i in range(count):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
@@ -44,12 +44,13 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
idx_s = i
|
||||
break
|
||||
if idx_s < 0: return None
|
||||
# 获取相对路径(MAX_REL_PATH_LEN=256,避免跨模块 CDefine 解析问题)
|
||||
# get the rel path (MAX_REL_PATH_LEN=256, avoids cross-module CDefine issues)
|
||||
MAX_RPL: sizet = 256
|
||||
ridx: sizet = sizet(idx_s) * MAX_RPL
|
||||
rel_path: str = rel_arr_ptr + ridx
|
||||
if rel_path[0] == '\0': return None
|
||||
# 选择基础目录(优先级):1) 条目存储的 includes 根目录 IncDir(绝对路径,支持多 includes 目录,避免"首 includes 目录"启发式对非首目录文件失效);2) rel_path 前缀启发式——以 "lib/" 开头 → App 源文件(SourceDir);"includes/" 开头 → includes 文件(去掉前缀);其他无前缀路径先试 SourceDir,不存在再试 IncludesDir
|
||||
# choose base dir: 1) stored includes root IncDir (absolute, supports multiple dirs);
|
||||
# 2) prefix heuristic: "lib/" -> SourceDir, "includes/" -> IncludesDir, else SourceDir then IncludesDir.
|
||||
base_dir: str = None
|
||||
rel_used: str = rel_path
|
||||
inc_arr_ptr: bytes = StubMergerStore.GetSha1StoreIncdirArrPtr()
|
||||
@@ -65,14 +66,14 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
rel_used = rel_path + 9
|
||||
base_dir = Config.IncludesDir
|
||||
if base_dir is None: base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
|
||||
# 构建完整路径: {base_dir}/{rel_used}
|
||||
# build full path: {base_dir}/{rel_used}
|
||||
base_len: sizet = string.strlen(base_dir)
|
||||
rel_len: sizet = string.strlen(rel_used)
|
||||
path_len: sizet = base_len + rel_len + 2
|
||||
buf: str = stdlib.malloc(path_len)
|
||||
if buf is None: return None
|
||||
viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_used)
|
||||
# 存在性检查:SourceDir 下不存在且 IncludesDir 不同 → 重试 IncludesDir
|
||||
# existence check: if missing under SourceDir and IncludesDir differs, retry IncludesDir
|
||||
pf_chk: rawptr[fileio.File] = fileio.File(buf, fileio.MODE.R)
|
||||
if pf_chk.closed:
|
||||
pf_chk.close()
|
||||
@@ -94,59 +95,33 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
return None
|
||||
else: pf_chk.close()
|
||||
return buf
|
||||
# ============================================================
|
||||
# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量
|
||||
#
|
||||
# 当当前模块的 CDefine 表中找不到某个 Name 时,从 from_imports
|
||||
# 解析出源模块,再从该模块的 pyi 文件中解析 CDefine 常量值。
|
||||
# 找到后缓存到当前模块的 CDefine 表中,避免重复查找。
|
||||
#
|
||||
# pyi 文件格式: "NAME: t.CDefine = value"
|
||||
# ============================================================
|
||||
# _lookup_cross_module_cdefine - cross-module CDefine lookup: resolve the source module
|
||||
# from from_imports, parse its pyi "NAME: t.CDefine = value", then cache in the current module.
|
||||
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*, name: str, from_imports: str, imported_modules: str = None, depth: int = 0, node: ast.AST* = None) -> int64:
|
||||
"""跨模块查找 CDefine 常量,返回值(64 位)或 -1(未找到)
|
||||
|
||||
查找顺序:
|
||||
1. from_imports 精确匹配(from X import NAME)
|
||||
2. from_imports star 回退(from X import *)
|
||||
3. imported_modules 遍历(import X):当通过 import X 导入模块时,
|
||||
模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用)
|
||||
|
||||
depth: 递归深度(子模块递归/imported_modules 遍历的内部调用递增)。
|
||||
node: 触发查找的 AST 节点(递归内传 None,line 0;顶层调用点传实际节点)。
|
||||
"""
|
||||
# 递归深度守卫:子模块递归(from .X import NAME re-export 链)与
|
||||
# imported_modules 遍历组合可能形成循环(模块间互相 from import),
|
||||
# 无上限时无限递归 → 每次递归 pool.alloc 缓冲(rc_sub_buf/rc_full_mod/
|
||||
# rc_fi/im_buf/sub_fi)→ O(n²) 累积 → 主池 1.5GB OOM
|
||||
# (NewAstTest CONST_INT 单次查找 14593 次调用,fi 恒为 CONST_INT:ast)。
|
||||
# 正常跨模块 CDefine 查找深度 < 8(如 ast → ast.base),32 上限 fail-fast
|
||||
# 暴露循环 import 链(而非静默 OOM)。报错带 node 行号 + 当前递归模块链。
|
||||
"""Look up a CDefine constant across modules, returning its 64-bit value or -1"""
|
||||
# recursion-depth guard: re-export chains + imported_modules walk can loop and OOM;
|
||||
# cap at 32 to fail fast on cyclic imports (error carries node line + module chain).
|
||||
if depth > 32:
|
||||
err_buf: str = pool.alloc(256)
|
||||
if err_buf is not None:
|
||||
viperlib.snprintf(err_buf, 256, "跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export): %s (depth=%d fi=%s)", name, depth, from_imports if from_imports is not None else "(nil)")
|
||||
self.Gen.TypeGenErr.fatal_error(node, err_buf, "InternalCompilerError")
|
||||
self.Gen.TypeGenErr.fatal_error(node, "跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export)", "InternalCompilerError")
|
||||
# 入口处清零标志,确保所有早退路径(return -1)都返回"未找到"状态
|
||||
# 避免递归调用时前一次成功的标志残留导致误判
|
||||
# clear the found flag at entry so every early return reports "not found",
|
||||
# avoiding a stale success flag left over from a previous recursive call.
|
||||
self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
if name is None: return -1
|
||||
if from_imports is None: from_imports = ""
|
||||
|
||||
# 1. 从 from_imports 查找名称对应的模块名
|
||||
# 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导
|
||||
# 1. resolve the module name for `name` from from_imports; try exact match first
|
||||
# (allow_star_fallback=0) so a star import cannot mislead CDefine constants.
|
||||
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 0)
|
||||
if mod_name_raw is None:
|
||||
# 精确匹配失败:尝试 star import 回退
|
||||
# exact match failed: try the star import fallback
|
||||
mod_name_raw = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 1)
|
||||
|
||||
# 截断模块名到 NUL 结尾:lookup_from_import 返回指向 from_imports 内部的
|
||||
# 指针(非 NUL 结尾,后随空格分隔的其他条目,如 "ast CONST_STR:ast ...")。
|
||||
# 不截断会污染 base_mod:strlen 读到整个 from_imports 末尾 → rc_full_mod
|
||||
# 每层累积整个列表 + ".base"(dump:ast.base.base.base 无限递归根因),
|
||||
# 且缓存 key 错乱("CONST_INT@ast CONST_STR:ast ...")无法命中。
|
||||
# 与 _lookup_sha1_from_modname_raw 的截断逻辑一致(空格/冒号/NUL 分隔)。
|
||||
# truncate module name at NUL: lookup_from_import returns a pointer into from_imports
|
||||
# (not NUL-terminated); match _lookup_sha1_from_modname_raw (space/colon/NUL delimited).
|
||||
base_mod: str = None
|
||||
if mod_name_raw is not None:
|
||||
mbl: sizet = 0
|
||||
@@ -159,13 +134,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
base_mod = mb_buf
|
||||
while base_mod is not None and base_mod[0] == '.': base_mod = base_mod + 1
|
||||
|
||||
# 跨模块 CDefine 查找缓存(perm_pool 永久,跨文件复用):
|
||||
# 首次查找成功后结果缓存,后续文件(含 reset_to(mark2)/模块切换)直接命中,
|
||||
# 避免 CONST_INT 跨模块递归查找反复触发(NewAstTest OOM 根因——
|
||||
# 14593 次调用全部重复递归 ast → ast.base → ...)。
|
||||
# key = "name@源模块"(源模块维度,用去点后的 base_mod 保证一致),
|
||||
# 保持模块隔离:不同源模块的同名 CDefine 值不同时不串值。
|
||||
# 仅 ALL_CAPS 全局唯一宏名才进入本函数(_is_cdefine_name_pattern 已过滤)。
|
||||
# cross-module CDefine cache (perm_pool, reused across files): key = "name@srcModule"
|
||||
# keeps modules isolated so same-name constants from different modules do not cross.
|
||||
xkey_buf: str = None
|
||||
if base_mod is not None:
|
||||
xk_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
@@ -183,17 +153,16 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i)
|
||||
return string.atoll(xval)
|
||||
|
||||
# 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6)
|
||||
# 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中,
|
||||
# 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules
|
||||
# 2. from_imports failed: fall back to the imported_modules walk (step 9.6);
|
||||
# a star fallback may name a wrong module, so keep scanning imported_modules.
|
||||
if mod_name_raw is None:
|
||||
# from_imports 完全没有匹配(精确和 star 都失败)
|
||||
# 直接进入 imported_modules 遍历(步骤 9.6)
|
||||
# from_imports matched nothing (exact and star both failed):
|
||||
# go straight to the imported_modules walk (step 9.6).
|
||||
if imported_modules is None: return -1
|
||||
# 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历
|
||||
# 设置 result_found=0 跳过步骤 3-9,直接到 9.6
|
||||
# skip reading the from_imports module; go directly to the step 9.6 walk.
|
||||
# result_found=0 skips steps 3-9 and jumps to 9.6.
|
||||
self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
# 直接遍历 imported_modules
|
||||
# iterate imported_modules directly
|
||||
im_len: sizet = string.strlen(imported_modules)
|
||||
im_ci: sizet = 0
|
||||
while im_ci < im_len:
|
||||
@@ -224,20 +193,20 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0: return sub_val
|
||||
return -1
|
||||
|
||||
# 2. 截断模块名 → 去前导点 → 精确+后缀查找 SHA1(复用 HandlesExprCall 公共逻辑)
|
||||
# truncate module name, strip leading dots, find SHA1 via exact+suffix (shared logic)
|
||||
sha1: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw)
|
||||
if sha1 is None: return -1
|
||||
|
||||
# 5. 获取 temp_dir
|
||||
# 5. get temp_dir
|
||||
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
|
||||
if temp_dir is None: return -1
|
||||
|
||||
# 6. 构建 vpi 文件路径
|
||||
# 6. build the vpi file path
|
||||
td_len: sizet = string.strlen(temp_dir)
|
||||
pyi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "vpi")
|
||||
if pyi_path is None: return -1
|
||||
|
||||
# 7. 读取 vpi 文件;若不存在或内容过短(空 stub)则回退读取源 .vp/.py 文件
|
||||
# 7. read vpi; if missing or too short (empty stub), fall back to the source .vp/.py
|
||||
PYI_READ_BUF_SIZE: sizet = 65536
|
||||
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
|
||||
if pyi_buf is None:
|
||||
@@ -247,25 +216,17 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
need_py_fallback: int = 0
|
||||
bytes_read: LONG = 0
|
||||
if pf.closed:
|
||||
# .vpi 文件不存在:回退读取源 .vp/.py 文件
|
||||
# .vpi missing: fall back to the source .vp/.py
|
||||
need_py_fallback = 1
|
||||
else:
|
||||
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
|
||||
pf.close()
|
||||
# vpi 内容过短(< 10 字节)视为空 stub,回退 .py
|
||||
# vpi too short (< 10 bytes): treat as empty stub, fall back to .py
|
||||
if bytes_read < 10: need_py_fallback = 1
|
||||
else:
|
||||
if bytes_read < PYI_READ_BUF_SIZE: pyi_buf[bytes_read] = 0
|
||||
# vpi(存根签名文件)中的 CDefine 声明可能只带名字不带值
|
||||
# (Converter 旧版只对 CONST_INT/BOOL 等输出 "= value",CONST_FLOAT
|
||||
# 等不输出 → numpy 的 pi/e 在 stub 中无值),真正的值在源
|
||||
# .vp/.py 文件中。旧启发式"vpi 中任一 CDefine 带值 → 不读源"是错的:
|
||||
# 模块同时含 int CDefine(带值,如 numpy.MAX_NDIM=4)与 float
|
||||
# CDefine(无值,如 pi)时,int 行让 cd_sig=1 而漏回退源 → pi
|
||||
# 解析失败 → 退化 @pi 全局指针 → float<->ptr 报错(NumpyTest 根因)。
|
||||
# 改为按目标名判定:仅当 vpi 中 name 的 CDefine 行带值时才信任
|
||||
# vpi,否则(name 行无值 / name 不在 vpi / 整个 vpi 无带值 CDefine)
|
||||
# 一律回退读源文件。
|
||||
# vpi stubs may declare a CDefine name without a value; the real value lives
|
||||
# in the source .vp/.py, so trust vpi only when the target name's line has a value.
|
||||
cd_sig: int = 0
|
||||
name_has_val: int = 0
|
||||
cs_name_len: sizet = string.strlen(name)
|
||||
@@ -273,7 +234,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
cs_i: sizet = 0
|
||||
while cs_i + 9 < cs_total:
|
||||
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and pyi_buf[cs_i + 4] == 'e' and pyi_buf[cs_i + 5] == 'f' and pyi_buf[cs_i + 6] == 'i' and pyi_buf[cs_i + 7] == 'n' and pyi_buf[cs_i + 8] == 'e'):
|
||||
# 检查该行剩余部分是否有 '='(带值声明才在 vpi 中匹配)
|
||||
# check whether the rest of the line has '=' (only valued decls match in vpi)
|
||||
cd_eq: int = 0
|
||||
cs_j: sizet = cs_i + 9
|
||||
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
|
||||
@@ -283,7 +244,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
cs_j += 1
|
||||
if cd_eq != 0:
|
||||
cd_sig = 1
|
||||
# 定位该行行首,检查是否以 "name:" 开头(目标名带值)
|
||||
# locate line start; check it begins with "name:" (target has a value)
|
||||
cs_line_start: sizet = cs_i
|
||||
while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n': cs_line_start -= 1
|
||||
if cs_i - cs_line_start >= cs_name_len + 1:
|
||||
@@ -298,11 +259,11 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
stdlib.free(pyi_path)
|
||||
|
||||
if need_py_fallback != 0:
|
||||
# 从 SHA1 存储器查找相对路径,构建 {base_dir}/{rel_path}
|
||||
# look up the rel path from the SHA1 store; build {base_dir}/{rel_path}
|
||||
src_path: str = self._build_source_path_from_sha1(sha1)
|
||||
if src_path is None:
|
||||
# 存储器中找不到 SHA1 = 编译器 bug(Phase1/Phase2 未填充存储器)
|
||||
# 报错终止,不静默返回 -1(避免后续生成错误的 @UNDEFINED 全局变量引用)
|
||||
# SHA1 absent from the store = compiler bug (Phase1/Phase2 not populated):
|
||||
# report and abort instead of silently returning -1 (avoids bad @UNDEFINED refs).
|
||||
err_buf: str = pool.alloc(512)
|
||||
if err_buf is not None:
|
||||
viperlib.snprintf(err_buf, 512, "跨模块 CDefine 查找失败:SHA1 %s 不在存储器中(base_mod=%s name=%s)。" "Phase1/Phase2 未正确填充 PopulateSha1MapStore/AppendToSha1MapStore", sha1, base_mod, name)
|
||||
@@ -321,8 +282,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
return -1
|
||||
if bytes_read < PYI_READ_BUF_SIZE: pyi_buf[bytes_read] = 0
|
||||
|
||||
# 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
|
||||
# 格式: NAME: t.CDefine = value
|
||||
# 8. search pyi_buf for the "name: t.CDefine = value" line
|
||||
# format: NAME: t.CDefine = value
|
||||
name_len: sizet = string.strlen(name)
|
||||
total_len: sizet = string.strlen(pyi_buf)
|
||||
pos: sizet = 0
|
||||
@@ -331,15 +292,15 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
res_is_double: int = 0
|
||||
res_is_str: int = 0
|
||||
while pos < total_len:
|
||||
# 找到行首
|
||||
# find line start
|
||||
line_start: sizet = pos
|
||||
# 找到行尾
|
||||
# find line end
|
||||
while pos < total_len and pyi_buf[pos] != '\n': pos += 1
|
||||
line_len: sizet = pos - line_start
|
||||
if pos < total_len:
|
||||
pos += 1 # 跳过 '\n'
|
||||
pos += 1 # skip '\n'
|
||||
|
||||
# 检查行是否以 "name:" 开头
|
||||
# check whether the line starts with "name:"
|
||||
if line_len < name_len + 1: continue
|
||||
match_ok: int = 1
|
||||
for ki in range(name_len):
|
||||
@@ -349,8 +310,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if match_ok == 0: continue
|
||||
if pyi_buf[line_start + name_len] != ':': continue
|
||||
|
||||
# 检查行是否包含 "t.CDefine"
|
||||
# 在行中查找 "t.CDefine"
|
||||
# check the line contains "t.CDefine"
|
||||
# scan the line for "t.CDefine"
|
||||
has_cdefine: int = 0
|
||||
ci: sizet = line_start + name_len + 1
|
||||
while ci + 8 <= line_start + line_len:
|
||||
@@ -360,39 +321,38 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
ci += 1
|
||||
if has_cdefine == 0: continue
|
||||
|
||||
# 找到 "=" 后面的值
|
||||
# find the value after "="
|
||||
eq_pos: sizet = line_start + name_len + 1
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=': eq_pos += 1
|
||||
if eq_pos >= line_start + line_len: continue
|
||||
eq_pos += 1 # 跳过 '='
|
||||
eq_pos += 1 # skip '='
|
||||
|
||||
# 跳过空格
|
||||
# skip spaces
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
|
||||
# 检查是否是 t.CUnsignedLong(...) / t.CLong(...) / int(...) 等类型构造函数
|
||||
# 格式: t.CUnsignedLong(-11) / t.CUnsignedLong(0xFFFFFFFF)
|
||||
# 如果是,跳过 "t.CXxx(" 前缀,解析括号内的值,忽略结尾 ')'
|
||||
# check for type-ctor forms like t.CUnsignedLong(...) / t.CLong(...) / int(...);
|
||||
# if so, skip the "t.CXxx(" prefix, parse inside the parens, ignore trailing ')'.
|
||||
is_type_ctor: int = 0
|
||||
if eq_pos + 2 < line_start + line_len:
|
||||
if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
|
||||
# 找到 '(' 的位置
|
||||
# find the '(' position
|
||||
paren_pos: sizet = eq_pos + 2
|
||||
while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(': paren_pos += 1
|
||||
if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
|
||||
is_type_ctor = 1
|
||||
eq_pos = paren_pos + 1 # 跳过 '('
|
||||
# 跳过括号内可能的前导空格
|
||||
eq_pos = paren_pos + 1 # skip '('
|
||||
# skip possible leading spaces inside the parens
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
|
||||
# 括号包裹标记:(1 << 23) / (512) / (A | B)
|
||||
# paren-wrapped flag: (1 << 23) / (512) / (A | B)
|
||||
is_paren_expr: int = 0
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '(':
|
||||
is_paren_expr = 1
|
||||
eq_pos += 1
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
|
||||
# === 解析左操作数 token(数字 / hex / 负号 / 名称) ===
|
||||
# 负号标记
|
||||
# === parse left operand token (number / hex / sign / name) ===
|
||||
# sign flag
|
||||
is_neg: int = 0
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
|
||||
is_neg = 1
|
||||
@@ -403,18 +363,17 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
is_float_val: int = 0
|
||||
is_name_val: int = 0
|
||||
is_str_val: int = 0
|
||||
# 字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2"):读引号内内容。
|
||||
# 跨模块读回此前不支持引号 → val_str_len=0 被跳过 → "跨模块 CDefine
|
||||
# 未找到"(test_pyzlib.vp pyzlib.ZLIB_VERSION 根因)
|
||||
# string value (e.g. ZLIB_VERSION = "1.3.2"): read inside the quotes; without this,
|
||||
# cross-module read-back skipped it and reported "CDefine not found".
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
|
||||
is_str_val = 1
|
||||
eq_pos += 1 # 跳过开引号
|
||||
eq_pos += 1 # skip opening quote
|
||||
val_str_start = eq_pos
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '"':
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
|
||||
eq_pos += 1 # 跳过闭引号
|
||||
eq_pos += 1 # skip closing quote
|
||||
if eq_pos + 1 < line_start + line_len:
|
||||
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
|
||||
is_hex = 1
|
||||
@@ -428,7 +387,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
else: break
|
||||
# 数字/hex 字符续读(字符串值已整体读取,跳过)
|
||||
# continue reading digit/hex chars (string values are read whole, skipped)
|
||||
while eq_pos < line_start + line_len and is_name_val == 0 and is_str_val == 0:
|
||||
ch: char = pyi_buf[eq_pos]
|
||||
if is_hex == 1:
|
||||
@@ -441,7 +400,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
elif ch == '.' and is_float_val == 0:
|
||||
# 浮点值:'3.14159'('.' 只允许一次)
|
||||
# float value: '3.14159' ('.' allowed only once)
|
||||
is_float_val = 1
|
||||
val_str_len += 1
|
||||
eq_pos += 1
|
||||
@@ -449,39 +408,38 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
|
||||
if val_str_len == 0 and is_str_val == 0: continue
|
||||
|
||||
# 复制左 token 到缓冲区
|
||||
# copy the left token into a buffer
|
||||
val_buf: str = pool.alloc(val_str_len + 1)
|
||||
if val_buf is None: continue
|
||||
for vi in range(val_str_len): val_buf[vi] = pyi_buf[val_str_start + vi]
|
||||
val_buf[val_str_len] = '\0'
|
||||
|
||||
# 左 token 求值
|
||||
# evaluate the left token
|
||||
lval: int64 = 0
|
||||
l_is_double: int = 0
|
||||
ldbl: double = 0.0
|
||||
if is_str_val != 0:
|
||||
# 字符串 CDefine(如 ZLIB_VERSION = "1.3.2"):注册字符串值
|
||||
# (含空串 ""),result_found 标记 + res_is_str 供尾部跳过整数
|
||||
# 注册/缓存(否则 register_cdefine_constant 把字符串当 0 整数)
|
||||
# string CDefine (e.g. ZLIB_VERSION = "1.3.2"): register the string value
|
||||
# (including ""), mark result_found + res_is_str so the tail skips integers.
|
||||
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, val_buf)
|
||||
result_val = 0
|
||||
result_found = 1
|
||||
res_is_str = 1
|
||||
elif is_name_val != 0:
|
||||
# True/False 布尔字面量 → 1/0(对齐 TPC:bool 在 CDefine 语境按整数处理)
|
||||
# True/False literal -> 1/0 (TPC treats bool as integer in CDefine context)
|
||||
if string.strcmp(val_buf, "True") == 0: lval = 1
|
||||
elif string.strcmp(val_buf, "False") == 0: lval = 0
|
||||
else:
|
||||
# 名称引用:查当前 CDefine 表(如 FOREGROUND_COMBO = FOREGROUND_RED)
|
||||
# name reference: look up the current CDefine table (e.g. FOREGROUND_COMBO)
|
||||
lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||||
# 名称未注册:该行无法求值,跳过
|
||||
# name not registered: cannot evaluate, skip the line
|
||||
continue
|
||||
elif is_hex == 1:
|
||||
# 手动解析十六进制(跳过 0x 前缀)——64 位累加,
|
||||
# 0xFFFFFFFF=4294967295 保持全精度(常量生成端 i32 截断回 -1)
|
||||
# parse hex manually (skip the 0x prefix) with 64-bit accumulation so
|
||||
# 0xFFFFFFFF=4294967295 keeps full precision (constant side truncates to i32 -1).
|
||||
hex_result: int64 = 0
|
||||
hex_buf: str = val_buf + 2 # 跳过 "0x"
|
||||
hex_buf: str = val_buf + 2 # skip "0x"
|
||||
for hi in range(val_str_len - 2):
|
||||
hc: char = hex_buf[hi]
|
||||
hd: int = 0
|
||||
@@ -500,10 +458,10 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
lval = -lval
|
||||
if l_is_double != 0: ldbl = -ldbl
|
||||
|
||||
# 跳过空格,检查是否有二元运算符
|
||||
# skip spaces; check for a binary operator
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
|
||||
# 解析运算符(<< >> | & ^ + - * /)
|
||||
# parse operator (<< >> | & ^ + - * /)
|
||||
cd_op: int = 0
|
||||
if eq_pos + 1 < line_start + line_len:
|
||||
if pyi_buf[eq_pos] == '<' and pyi_buf[eq_pos + 1] == '<':
|
||||
@@ -536,7 +494,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
eq_pos += 1
|
||||
|
||||
if cd_op != 0:
|
||||
# === 解析右操作数 token ===
|
||||
# === parse right operand token ===
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
r_neg: int = 0
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
|
||||
@@ -572,7 +530,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
eq_pos += 1
|
||||
else: break
|
||||
if r_len == 0:
|
||||
# 运算符后无右操作数:无法求值,跳过该行
|
||||
# no right operand after operator: cannot evaluate, skip the line
|
||||
continue
|
||||
r_buf: str = pool.alloc(r_len + 1)
|
||||
if r_buf is None: continue
|
||||
@@ -580,13 +538,13 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
r_buf[r_len] = '\0'
|
||||
rval: int64 = 0
|
||||
if r_name != 0:
|
||||
# True/False 布尔字面量 → 1/0
|
||||
# True/False literal -> 1/0
|
||||
if string.strcmp(r_buf, "True") == 0: rval = 1
|
||||
elif string.strcmp(r_buf, "False") == 0: rval = 0
|
||||
else:
|
||||
rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||||
# 名称未注册:无法求值,跳过该行
|
||||
# name not registered: cannot evaluate, skip the line
|
||||
continue
|
||||
elif r_hex != 0:
|
||||
rhex_v: int64 = 0
|
||||
@@ -602,7 +560,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
rval = rhex_v
|
||||
else: rval = string.atoll(r_buf)
|
||||
if r_neg != 0: rval = -rval
|
||||
# 计算二元表达式结果(对齐 TPC ConstEvaluator)
|
||||
# compute the binary expression result (aligned with TPC ConstEvaluator)
|
||||
if cd_op == 1: result_val = lval << rval
|
||||
elif cd_op == 2: result_val = lval >> rval
|
||||
elif cd_op == 3: result_val = lval | rval
|
||||
@@ -617,14 +575,13 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
result_found = 1
|
||||
break
|
||||
else:
|
||||
# 单值(无运算符):括号内单值如 (512) / (-11) 也走这里
|
||||
# single value (no operator): parenthesized values like (512) / (-11) also land here
|
||||
if is_paren_expr != 0:
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == ')': eq_pos += 1
|
||||
if is_float_val != 0:
|
||||
# 浮点 CDefine(如 U_M_PI = 3.14159...):
|
||||
# 值缓存为 double(register_cdefine_double),供调用处生成
|
||||
# double 常量;整数部分供 int 路径使用(is_cdefine_double 标志)。
|
||||
# float CDefine (e.g. U_M_PI = 3.14159...): cache as double for double
|
||||
# constant generation; int part serves the int path (is_cdefine_double flag).
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl)
|
||||
result_val = lval
|
||||
result_found = 1
|
||||
@@ -634,35 +591,27 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
result_found = 1
|
||||
break
|
||||
|
||||
# 9.5 如果当前模块文件中没找到,递归查找子模块
|
||||
# 处理 from .X import NAME 和 from .X import * 的情况
|
||||
# 例如 ast.__init__.vp 中 from .base import CONST_INT, ...
|
||||
# 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1
|
||||
# 9.5 if not found in this module, recurse into submodules for `from .X import NAME` /
|
||||
# `from .X import *` (e.g. ast.__init__ re-exports CONST_INT from .base).
|
||||
if result_found == 0:
|
||||
# base_mod 已在函数开头计算(mod_name_raw 去前导点),
|
||||
# 供子模块递归构建 "base_mod.X" 全名。
|
||||
# base_mod was computed at function entry (mod_name_raw with leading dots removed);
|
||||
# the submodule recursion uses it to build the full "base_mod.X" name.
|
||||
name_len_rc: sizet = string.strlen(name)
|
||||
rc_pos: sizet = 0
|
||||
while rc_pos < total_len and result_found == 0:
|
||||
# 找到行首
|
||||
# find line start
|
||||
rc_line_start: sizet = rc_pos
|
||||
# 找到行尾
|
||||
# find line end
|
||||
while rc_pos < total_len and pyi_buf[rc_pos] != '\n': rc_pos += 1
|
||||
rc_line_len: sizet = rc_pos - rc_line_start
|
||||
if rc_pos < total_len: rc_pos += 1
|
||||
|
||||
# 检查行是否以 "from " 开头(模块导入)
|
||||
# check whether the line starts with "from " (module import)
|
||||
if rc_line_len < 12: continue
|
||||
if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or pyi_buf[rc_line_start+4] != ' ': continue
|
||||
|
||||
# 提取模块路径 token(从 rc_line_start+5 到空格/tab 为止,跳过前导点)
|
||||
# 两种写法都要支持:
|
||||
# 相对导入 `from .base import X` → 拼 base_mod.base
|
||||
# 绝对导入 `from vqt6._types import *` → 直接用 token 自身
|
||||
# 此前只认相对导入(硬要求 rc_line_start+5 是 '.'),绝对写法被
|
||||
# 直接跳过 → vqt6 包(__init__.vp 用 `from vqt6._types import *`
|
||||
# 重导出 QT_ALIGN_* 等)里的 CDefine 全部查不到 →
|
||||
# "跨模块 CDefine 未找到: 'vqt6.QT_ALIGN_CENTER'"。
|
||||
# extract the module token (skip leading dots); support both relative
|
||||
# `from .base import X` (join base_mod.base) and absolute `from vqt6._types import *`.
|
||||
rc_is_rel: int = 0
|
||||
rc_sub_start: sizet = rc_line_start + 5
|
||||
while rc_sub_start < rc_line_start + rc_line_len and pyi_buf[rc_sub_start] == '.':
|
||||
@@ -673,7 +622,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
rc_sub_len: sizet = rc_sub_end - rc_sub_start
|
||||
if rc_sub_len == 0: continue
|
||||
|
||||
# 检查行是否包含 "import"
|
||||
# check whether the line contains "import"
|
||||
rc_has_import: int = 0
|
||||
rc_ipos: sizet = rc_sub_end
|
||||
while rc_ipos + 6 <= rc_line_start + rc_line_len:
|
||||
@@ -683,14 +632,13 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
rc_ipos += 1
|
||||
if rc_has_import == 0: continue
|
||||
|
||||
# 检查 NAME 是否在导入列表中,或导入是 *
|
||||
# 处理多行导入:from .X import (... NAME, ...)
|
||||
# 从 "import" 之后扫描,如果有 "(" 则扫描到 ")" 为止
|
||||
# check NAME is in the import list, or the import is *; handle multi-line
|
||||
# imports: scan from "import" up to ")" when a "(" is present.
|
||||
rc_imp_start: sizet = rc_ipos + 6
|
||||
rc_star: int = 0
|
||||
rc_name_match: int = 0
|
||||
|
||||
# 确定扫描范围:单行或跨行(括号内)
|
||||
# determine the scan range: single line or multi-line (inside parens)
|
||||
rc_scan_end: sizet = rc_line_start + rc_line_len
|
||||
rc_has_paren: int = 0
|
||||
rc_pp: sizet = rc_imp_start
|
||||
@@ -701,7 +649,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if pyi_buf[rc_pp] == '*': rc_star = 1
|
||||
rc_pp += 1
|
||||
if rc_has_paren != 0:
|
||||
# 多行导入:从 "(" 之后扫描到 ")" 为止
|
||||
# multi-line import: scan from after "(" to ")"
|
||||
rc_scan_end = rc_pp + 1
|
||||
rc_paren_closed: int = 0
|
||||
rc_sp: sizet = rc_pp + 1
|
||||
@@ -713,12 +661,12 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
rc_sp += 1
|
||||
if rc_paren_closed == 0: rc_scan_end = total_len
|
||||
|
||||
# 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 *
|
||||
# scan from rc_imp_start to rc_scan_end for NAME and *
|
||||
rc_cpos: sizet = rc_imp_start
|
||||
while rc_cpos < rc_scan_end:
|
||||
rc_ch: char = pyi_buf[rc_cpos]
|
||||
if rc_ch == '*': rc_star = 1
|
||||
# 检查 NAME 是否匹配(作为完整单词)
|
||||
# check NAME matches as a whole word
|
||||
if rc_cpos + name_len_rc <= rc_scan_end:
|
||||
rc_match: int = 1
|
||||
rc_ki: sizet
|
||||
@@ -727,13 +675,13 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
rc_match = 0
|
||||
break
|
||||
if rc_match == 1:
|
||||
# 检查前字符是否是非标识符字符
|
||||
# check the preceding char is a non-identifier char
|
||||
rc_before_ok: int = 0
|
||||
if rc_cpos == rc_imp_start: rc_before_ok = 1
|
||||
else:
|
||||
rc_before_ch: char = pyi_buf[rc_cpos - 1]
|
||||
if rc_before_ch == ' ' or rc_before_ch == '\t' or rc_before_ch == '(' or rc_before_ch == ',' or rc_before_ch == '\n' or rc_before_ch == '\r': rc_before_ok = 1
|
||||
# 检查后字符是否是非标识符字符
|
||||
# check the following char is a non-identifier char
|
||||
rc_after_ok: int = 0
|
||||
rc_after_pos: sizet = rc_cpos + name_len_rc
|
||||
if rc_after_pos >= rc_scan_end: rc_after_ok = 1
|
||||
@@ -745,22 +693,15 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
|
||||
if rc_star == 0 and rc_name_match == 0: continue
|
||||
|
||||
# 复制子模块名到新缓冲区(pyi_buf + rc_sub_start 不是 NUL 结尾)
|
||||
# copy the submodule name into a new buffer (pyi_buf + rc_sub_start is not NUL-terminated)
|
||||
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
|
||||
if rc_sub_buf is None: continue
|
||||
rc_si: sizet
|
||||
for rc_si in range(rc_sub_len): rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
|
||||
rc_sub_buf[rc_sub_len] = '\0'
|
||||
|
||||
# 构建完整模块名:
|
||||
# 相对导入(rc_is_rel=1)→ <当前模块所属包> + "." + token
|
||||
# 基准必须是"包"(rel_path 的目录部分)而不是模块名本身:
|
||||
# pkg/sub/mod.vp 里的 `from .X import Y` 指向 pkg.sub.X,
|
||||
# 用 base_mod("pkg.sub.mod")会拼成 pkg.sub.mod.X(错)。
|
||||
# __init__.vp 时包名 == 模块名,此前只在这种情况碰巧正确
|
||||
# (CallGenBase._lookup_reexport_from_vpi 用的是包名,此处对齐)。
|
||||
# 绝对导入(rc_is_rel=0)→ 直接用 token(vqt6._types)
|
||||
# 自导入(目标模块 == base_mod)跳过,避免无意义递归
|
||||
# build the full module name: relative import -> package + "." + token (the base
|
||||
# must be the package, not the module name); absolute import -> token as-is.
|
||||
rc_full_mod: str = None
|
||||
if rc_is_rel != 0:
|
||||
rc_base_mod: str = base_mod
|
||||
@@ -775,22 +716,20 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if string.strcmp(rc_sub_buf, base_mod) == 0: continue
|
||||
rc_full_mod = rc_sub_buf
|
||||
|
||||
# 防循环:若 base_mod 已以 "."+rc_sub_buf 结尾(如 ast.base 再拼
|
||||
# .base → ast.base.base),说明子模块递归在重复追加同一模块段
|
||||
# (模块间循环 re-export / 后缀匹配容错命中自身),跳过该次递归
|
||||
# (NewAstTest CONST_INT 查找 dump:ast.base.base.base 无限增长根因)。
|
||||
# loop guard: if base_mod already ends with "."+rc_sub_buf (ast.base + .base),
|
||||
# the recursion is re-appending the same segment -> skip (infinite-growth root cause).
|
||||
rc_blen: sizet = string.strlen(base_mod)
|
||||
if rc_blen > rc_sub_len + 1:
|
||||
rc_tail: str = base_mod + (rc_blen - rc_sub_len)
|
||||
if rc_tail[0] == '.' and string.strncmp(rc_tail + 1, rc_sub_buf, rc_sub_len) == 0: continue
|
||||
|
||||
# 构建临时 from_imports "name:full_mod"
|
||||
# build a temporary from_imports "name:full_mod"
|
||||
rc_fi_len: sizet = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
|
||||
rc_fi: str = pool.alloc(rc_fi_len)
|
||||
if rc_fi is None: continue
|
||||
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
|
||||
|
||||
# 递归调用查找子模块
|
||||
# recursive call to search the submodule
|
||||
rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
result_val = rc_sub_val
|
||||
@@ -798,10 +737,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
|
||||
stdlib.free(pyi_buf)
|
||||
|
||||
# 9.6 from_imports star fallback 模块中未找到:回退到 imported_modules 遍历
|
||||
# 当 `from stdint import *` 导致 star fallback 返回 stdint,但目标常量
|
||||
# (如 SHARE_READ)实际在 `import w32.fileio` 导入的模块中时,
|
||||
# 需要遍历 imported_modules 逐个查找
|
||||
# 9.6 star-fallback module had no match: fall back to walking imported_modules,
|
||||
# e.g. `from stdint import *` names stdint but SHARE_READ lives in `import w32.fileio`.
|
||||
if result_found == 0 and imported_modules is not None:
|
||||
im2_len: sizet = string.strlen(imported_modules)
|
||||
im2_ci: sizet = 0
|
||||
@@ -834,13 +771,12 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
result_val = sub_val2
|
||||
result_found = 1
|
||||
|
||||
# 10. 缓存到当前模块的 CDefine 表中,并写入跨模块 perm_pool 缓存
|
||||
# 浮点 CDefine 已在匹配循环中 register_cdefine_double 缓存
|
||||
# (res_is_double=1),此处只缓存整数 CDefine,避免覆盖 double。
|
||||
# 10. cache into the current module's CDefine table and the cross-module perm_pool;
|
||||
# float CDefines are already cached as double, so only integers are written here.
|
||||
if result_found != 0:
|
||||
if res_is_str != 0:
|
||||
# 字符串 CDefine:值已在匹配循环 register_cdefine_string(val_buf
|
||||
# 是命中行的字符串内容,pool 分配跨函数有效),此处只写跨模块缓存
|
||||
# string CDefine: the value was already registered in the match loop (val_buf is
|
||||
# the hit line's content, valid across the function); only write the cross cache.
|
||||
if base_mod is not None:
|
||||
ck_len_s: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
ckey_s: str = pool.alloc(ck_len_s)
|
||||
@@ -849,9 +785,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_s, val_buf, 0, 1)
|
||||
elif res_is_double == 0:
|
||||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, result_val)
|
||||
# 跨模块查找缓存(perm_pool 永久,跨文件复用):整数
|
||||
# key 与入口查询一致:"name@源模块"(用截断后的 base_mod,
|
||||
# 与 mod_name_raw 一致——两者都是去点后的模块名,保证 key 匹配)
|
||||
# cross-module cache (perm_pool, reused across files), integers: key matches the
|
||||
# entry query "name@srcModule" (truncated base_mod, leading dots removed).
|
||||
if base_mod is not None:
|
||||
ck_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
ckey_buf: str = pool.alloc(ck_len)
|
||||
@@ -862,7 +797,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
viperlib.snprintf(cbuf_i, 32, "%lld", result_val)
|
||||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_buf, cbuf_i, 0, 0)
|
||||
else:
|
||||
# 浮点:从本地表取回 %.17g 文本,写入跨模块缓存(保精度)
|
||||
# float: fetch %.17g text from the local table and write the cross cache (lossless)
|
||||
if base_mod is not None:
|
||||
ck_len2: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
ckey2: str = pool.alloc(ck_len2)
|
||||
@@ -878,20 +813,10 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
else: self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
|
||||
return result_val
|
||||
# ============================================================
|
||||
# _is_cdefine_name_pattern - 检查名字是否符合 CDefine 命名约定
|
||||
#
|
||||
# CDefine 常量(如 FOREGROUND_GREEN, STD_OUTPUT_HANDLE, SCOPE_MODULE)
|
||||
# 都遵循 ALL_CAPS 约定:仅含大写字母 A-Z、数字 0-9、下划线 _,
|
||||
# 且至少含一个大写字母。
|
||||
#
|
||||
# Python 关键字(match, if, for)和变量名(self, pool, builder)
|
||||
# 都含小写字母,不会匹配,从而避免 fail-fast 误报。
|
||||
#
|
||||
# 返回: 1=符合 CDefine 命名约定, 0=不符合
|
||||
# ============================================================
|
||||
# _is_cdefine_name_pattern - CDefine names follow ALL_CAPS (A-Z, 0-9, _ with >=1 uppercase);
|
||||
# lowercase keywords/variables won't match, avoiding fail-fast false positives. 1=match, 0=no.
|
||||
def _is_cdefine_name_pattern(self, name: str) -> int:
|
||||
"""检查名字是否符合 CDefine 命名约定(全大写+下划线+数字)"""
|
||||
"""Return 1 if name follows the CDefine convention (uppercase letters, digits, underscore)"""
|
||||
if name is None: return 0
|
||||
has_upper: int = 0
|
||||
i: sizet = 0
|
||||
@@ -902,65 +827,16 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
i += 1
|
||||
return has_upper
|
||||
# ============================================================
|
||||
# 翻译变量引用(Name 节点)→ load
|
||||
# ============================================================
|
||||
# Translate a variable reference (Name node) -> load
|
||||
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, value: int64) -> mlirlite.Value*:
|
||||
"""CDefine 整数常量 → LLVM 常量(对齐 TPC TypeConvert 的 64 位升级)
|
||||
|
||||
**使用处宽度("W_min + 使用处宽度"规则)与物化宽度的关系**(与 TPC 同口径,
|
||||
对照表见 Debugger/define-constants.md):
|
||||
· 本函数是"取不到使用处宽度"的物化点(调用方 ExprGenName/ExprGenAttr 只有
|
||||
常量名与值,没有目标类型)⇒ 按语言默认整型宽度物化:能落进 32 位 ⇒ i32,
|
||||
否则 i64。**不**降到 W_min——降到 i8/i16 会把窄立即数喂进 i32 上下文与
|
||||
C 默认实参提升(printf 变参),属静默降级/ABI 破坏。
|
||||
· "使用处更宽 ⇒ 以该宽度物化"由**消费端**实现:translate_binop 按较宽侧
|
||||
zext/sext/coerce_to_type 对齐,赋值/形参按目标存储类型 coerce。
|
||||
· "使用处更窄 ⇒ fail-fast"在**注册处与声明处**实现:
|
||||
AssignGenMixin.require_cdefine_storable(显式类型 T 的位宽)与
|
||||
require_cdefine_use_width(`a: T = C`);超 64 位字面量由
|
||||
_int_text_exceeds_64 以**源码文本**判定(lexer 的 atoll 会在字面量层
|
||||
回绕,回绕后的 int64 不可信,故本函数不承担溢出判定)。
|
||||
|
||||
规则(与 translate_constant 的 i32 溢出升级一致,但保留 0xFFFFFFFF 语义):
|
||||
值超 32 位无符号范围(> 0xFFFFFFFF)→ i64 全精度常量
|
||||
(如 sys.maxsize = 9223372036854775807);
|
||||
值低于 i32 下限(< -2147483648)→ i64 全精度常量。符号位为 1 的 64 位
|
||||
模式(如 MEMBUDDY_CANARY_PATTERN = 0xA5A5A5A5A5A5A5A5)以 %lld 文本
|
||||
存回即负数 -6510615555426900571,仅按 u32 截断回比较会漏判;
|
||||
否则 → i32 常量,内部 int 截断(0xFFFFFFFF → -1,保持既有
|
||||
INVALID_HANDLE_VALUE / WAIT_FAILED = -1 语义不变,避免 i32 4294967295
|
||||
生成非法 LLVM IR 文本 "i32 4294967295")。
|
||||
|
||||
注意:不能用 `value > 4294967295` 直接比较——TPV 编译自身源码时大数字面量
|
||||
(> 0x7FFFFFFF)在字面量层回绕成 i32(sext 后等于 -1),阈值恒成立导致所有
|
||||
正数 CDefine 都被升 i64(INVALID_FILE_ATTRIBUTES 0xFFFFFFFF 从 i32 -1 变
|
||||
i64 4294967295 → 与 _get_attrs 的 sext(-1) 比较失败,SysLibTest exists FAIL 根因)。
|
||||
改用截断回比较:uint32(value) 回绕后还原,与 value 不等即超 u32 范围。
|
||||
|
||||
负数侧漏判的现场:`(0xA5A5A5A5 << 32) | 0xA5A5A5A5`(includes/memhub.vp:123)
|
||||
经 CDefine 求值(ExprGenCdef 内 lval << rval 的 int64 回绕)得
|
||||
-6510615555426900571,再经 TypeGenCDef.register_cdefine_constant 的
|
||||
"%lld" 文本存回(TypeGenCDef.vp:156)仍为负数 → 原判据物化成 i32
|
||||
-1515870811:写入路径 inttoptr i32(x86 movl 零扩展)实存
|
||||
0x00000000A5A5A5A5,比较/打印路径 sext i32→i64 期望 0xFFFFFFFFA5A5A5A5
|
||||
⇒ MemBuddy canary 校验 rc=6 假阳性。下限判据与既有
|
||||
ExprGenConst.vp:108 / CallGenLeafKit.vp:1477 的写法一致。
|
||||
"""
|
||||
"""Materialize a CDefine integer constant as an LLVM constant (TPC TypeConvert aligned)"""
|
||||
if (value >= 0 and uint64(uint32(value)) != value) or value < -2147483648: return mlirlite.const_int64(pool, value)
|
||||
return mlirlite.const_int32(pool, int(value))
|
||||
|
||||
# ============================================================
|
||||
# cdefine_use_width_value - "使用处宽度"下的 CDefine 常量物化
|
||||
# (与 TPC HandlesExpr._HandleNameLlvm + ConstWidthHint 同口径)
|
||||
# ============================================================
|
||||
# cdefine_use_width_value - materialize CDefine constants at the use-site width (TPC-aligned)
|
||||
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
|
||||
"""把 CDefine 值归一到 bits 位的**有符号表示**(LLVM 整数字面量口径)。
|
||||
|
||||
前提:调用方已用 W_min 判据保证 value 落在
|
||||
[-(2^(bits-1)), 2^bits-1](两解释口径)。非负但最高位为 1 的值
|
||||
(如 0xFFFFFFFF)转成同一位模式的负数(i32 4294967295 → i32 -1),
|
||||
与 _const_int_for_cdefine 既有的 i32 截断口径一致,避免生成非法 IR 文本。
|
||||
"""
|
||||
"""Normalize a CDefine value to the signed representation of the given bit width"""
|
||||
if bits == 8:
|
||||
if value > 127: return value - 256
|
||||
return value
|
||||
@@ -973,31 +849,13 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
return value
|
||||
|
||||
def cdefine_use_width_value(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, target_ty: mlirlite.LLVMType*, use_ctx: str = None) -> mlirlite.Value*:
|
||||
"""CDefine 整数常量在**已知使用处类型**时的物化(与 TPC 同口径)。
|
||||
|
||||
返回 None ⇒ "取不到使用处宽度"(调用方沿用 _const_int_for_cdefine 的
|
||||
语言默认整型宽度 32/64)。只当 target_ty 是 i8/i16/i32/i64 时启用
|
||||
(i1 是比较/逻辑结果、指针不是整数使用处 ⇒ 落回默认,否则 `C == 0`
|
||||
之类会拿到位宽 1 而误报"使用处宽度不足"):
|
||||
使用处更宽(bits >= W_min)⇒ 按 bits 就地物化(立即数升宽无损);
|
||||
使用处更窄(bits < W_min)⇒ fail-fast(替换静默截断)。
|
||||
只被 CDefine 整数物化点调用 ⇒ 不改变非常量实参/返回值的既有 IR。
|
||||
|
||||
use_ctx:**真实站点种类**标签(如 "局部变量注解" / "模块级全局" / "形参" /
|
||||
"返回注解"),由 `translate_value` 的调用方按站点下传;None ⇒ 报文不写
|
||||
括注(**不得**再写死"形参 / 返回类型"——AnnAssign 的 `a: T = C` 现场也被
|
||||
本函数拦到时,硬编码标签会给出错误站点)。
|
||||
"""
|
||||
"""Materialize a CDefine integer constant at a known use-site width, or None on fallback"""
|
||||
bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(target_ty)
|
||||
if bits != 8 and bits != 16 and bits != 32 and bits != 64: return None
|
||||
wmin: int = self.Gen.AssignGen.define_min_width(value)
|
||||
if bits < wmin:
|
||||
# "该使用处可表示的范围"与判据同一口径 = TPC `DefineWidthLoHi`
|
||||
# = [-(2^(bits-1)), 2^bits - 1](有符号/无符号两种解释的并集;
|
||||
# 判据本身只按**位宽**比较,故报文必须报"位宽 w 的可接受值域")。
|
||||
# 字面量纪律见 AssignGenMixin.define_min_width:2147483648 /
|
||||
# 4294967295 会被 TPC 压成 i32 并在提升时 sext,故用安全字面量
|
||||
# 2147483647 的算术等价式构造。
|
||||
# "representable range at the use site" uses the same rule as the check = TPC
|
||||
# DefineWidthLoHi = [-(2^(bits-1)), 2^bits - 1]; literals built from safe 2147483647.
|
||||
m31: int64 = 2147483647
|
||||
lo: int64 = -m31 - 1
|
||||
hi: int64 = m31 * 2 + 1
|
||||
@@ -1009,10 +867,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
hi = 65535
|
||||
ctx: str = ""
|
||||
if use_ctx is not None:
|
||||
# 本语言的 str 是 i8*,**没有** `str + str` 的拼接语义(`+` 会退化
|
||||
# 成裸指针 add → "invalid operand type for instruction")。
|
||||
# 构建字符串一律用本仓库惯用法:f-string 插值 / string.strcat /
|
||||
# viperlib.snprintf。
|
||||
# this language's str is i8* with no `str + str`: `+` degrades to pointer add.
|
||||
# Build strings with f-string interpolation / string.strcat / viperlib.snprintf.
|
||||
ctx = f"({use_ctx})"
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] define 常量使用处宽度不足: 常量 '{name}' 的实际值 {value} " f"需要至少 {wmin} 位(最小可容纳宽度 W_min)," f"但使用处{ctx}只提供 {bits} 位" f"(该位宽按有符号/无符号两种解释可接受的范围: {lo}..{hi})。" f"禁止静默截断/降级,已终止编译。" f"修法: 把使用处{ctx}的类型加宽到至少 {wmin} 位" f"(如 int{wmin}T / CUInt{wmin}T 及更宽类型),或改用更小的常量值。", "TypeError")
|
||||
return None
|
||||
|
||||
@@ -15,32 +15,21 @@ import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
|
||||
# ============================================================
|
||||
# docstring 登记表容量常量(与 ExprGenMixin 保持一致,存疑
|
||||
# ============================================================
|
||||
# docstring registry capacity constants (kept in sync with ExprGenMixin)
|
||||
define DOC_MAX = 512
|
||||
define DOC_KEY_SZ = 192
|
||||
define DOC_VAL_SZ = 4096
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenCls - ExprGenMixin 拆分(阶段 D):类名推断/docstring 登记
|
||||
# ============================================================
|
||||
# ExprGenCls - split of ExprGenMixin (stage D): class-name inference / docstring registry
|
||||
novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
"""类名推断/docstring 登记"""
|
||||
"""Expression class inference and docstring registry."""
|
||||
|
||||
# ============================================================
|
||||
# docstring 提取/登记/查找(__doc__ 魔法属性支持)
|
||||
#
|
||||
# 预注册阶段(BuildPipeline.TranslateFileGetTrans)扫描所有
|
||||
# App/includes 文件的 AST,提取函数/类/方法的 docstring 内容
|
||||
# 登记到全局表(key = "{module_sha1}.{symbol}");__doc__ 属性
|
||||
# 访问时查表,命中则当前模块内联生成字符串常量(无跨模块 IR
|
||||
# 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。
|
||||
# ============================================================
|
||||
# docstring extract/register/lookup (__doc__ support): pre-registration scans
|
||||
# ASTs into a global "{module_sha1}.{symbol}" table; lookups inline the string.
|
||||
|
||||
def _extract_docstring_from_body(self, body: list[ast.AST*]*) -> str:
|
||||
"""从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容"""
|
||||
"""Extract a docstring from a body when body[0] is an Expr(Constant str)."""
|
||||
if body is None or len(body) == 0: return None
|
||||
first: ast.AST* = body.get(0)
|
||||
if first is None or first.kind() != ast.ASTKind.Expr: return None
|
||||
@@ -50,7 +39,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
if cn is None or cn.const_kind != ast.CONST_STR: return None
|
||||
return cn.str_val
|
||||
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
|
||||
"""登记 docstring(key="{module_sha1}.{symbol}"),查重幂等"""
|
||||
"""Register a docstring under key "{module_sha1}.{symbol}", deduplicated."""
|
||||
if module_sha1 is None or symbol is None or doc is None: return
|
||||
if string.strlen(doc) == 0: return
|
||||
if self.DocKeys is None:
|
||||
@@ -74,7 +63,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
self.DocCount += 1
|
||||
stdlib.free(key_buf)
|
||||
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
|
||||
"""查找 docstring 内容(key="{module_sha1}.{symbol}"),未找到返回 None"""
|
||||
"""Look up a docstring by "{module_sha1}.{symbol}", or None on miss."""
|
||||
if self.DocKeys is None or module_sha1 is None or symbol is None: return None
|
||||
ml: sizet = string.strlen(module_sha1)
|
||||
sl: sizet = string.strlen(symbol)
|
||||
@@ -88,7 +77,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
stdlib.free(key_buf)
|
||||
return None
|
||||
def extract_module_docstrings(self, tree: ast.AST*, module_sha1: str) -> None:
|
||||
"""扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)"""
|
||||
"""Scan a module AST and register docstrings of its functions, classes and methods."""
|
||||
if tree is None or module_sha1 is None: return
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is None: return
|
||||
@@ -109,7 +98,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
cd_body0: list[ast.AST*]* = cd.children
|
||||
cdoc: str = self._extract_docstring_from_body(cd_body0)
|
||||
if cdoc is not None: self.register_docstring(module_sha1, cd.name, cdoc)
|
||||
# 类方法 docstring(key="{ClassName}.{method}")
|
||||
# class method docstring (key="{ClassName}.{method}")
|
||||
cd_body: list[ast.AST*]* = cd.children
|
||||
if cd_body is not None:
|
||||
cbn: sizet = len(cd_body)
|
||||
@@ -128,46 +117,38 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
self.register_docstring(module_sha1, sym_buf, mdoc)
|
||||
stdlib.free(sym_buf)
|
||||
def _resolve_doc_symbol_name(self, trans: HT.Translator*, name_id: str, out_mod: str, out_sym: str) -> int:
|
||||
"""解析 Name 符号的 docstring 归属,写入 out_mod/out_sym,成功返回 1
|
||||
|
||||
判定顺序:本地变量(函数变量)→ 结构体类(含跨模块预注册)→ 模块名。
|
||||
"""
|
||||
"""Resolve a Name symbol's docstring owner into out_mod/out_sym; return 1 on success."""
|
||||
if trans is None or name_id is None or out_mod is None or out_sym is None: return 0
|
||||
if trans.ModuleSha1 is None: return 0
|
||||
# 1. 本地变量(函数变量/全局变量)
|
||||
# 1. local variable (function/global)
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, name_id) is not None:
|
||||
string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 2. 结构体类(entry.ModuleSha1 区分跨模块预注册的类)
|
||||
# 2. struct class (entry.ModuleSha1 distinguishes cross-module pre-registration)
|
||||
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(name_id)
|
||||
if se is not None:
|
||||
if se.ModuleSha1 is not None: string.strcpy(out_mod, se.ModuleSha1)
|
||||
else: string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 3. 模块名(模块本身,少见);用 StubMerger 模块级函数(避免
|
||||
# 跨 Mixin self 调用生成不存在符号)
|
||||
# 3. module name (rare); use StubMerger module-level functions to avoid a
|
||||
# cross-Mixin self call that would emit a nonexistent symbol.
|
||||
mod_sha1: str = StubMergerStore.LookupSha1ByModName(name_id)
|
||||
if mod_sha1 is None: mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(name_id)
|
||||
if mod_sha1 is not None:
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
# 4. 兜底当前模块
|
||||
# 4. fall back to the current module
|
||||
string.strcpy(out_mod, trans.ModuleSha1)
|
||||
string.strcpy(out_sym, name_id)
|
||||
return 1
|
||||
def _resolve_doc_symbol_attr(self, trans: HT.Translator*, at: ast.Attribute*, out_mod: str, out_sym: str) -> int:
|
||||
"""解析嵌套 Attribute 链的 docstring 归属,写入 out_mod/out_sym
|
||||
|
||||
支持形态:remote.remote_func / remote.RemoteClass /
|
||||
remote.RemoteClass.remote_method / LocalClass.local_method 等。
|
||||
最左 Name 判定:模块名 → 该模块 sha1;变量/类 → 类所属模块 sha1。
|
||||
"""
|
||||
"""Resolve a nested Attribute chain's docstring owner into out_mod/out_sym."""
|
||||
if trans is None or at is None or out_mod is None or out_sym is None: return 0
|
||||
if trans.ModuleSha1 is None: return 0
|
||||
# 收集 Attribute 链(最外层 attr 在前)
|
||||
# collect the Attribute chain (outermost attr first)
|
||||
DOC_CHAIN_MAX: int = 8
|
||||
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
|
||||
if items is None: return 0
|
||||
@@ -179,7 +160,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
string.strcpy(items + n_items * 128, cat.attr)
|
||||
n_items += 1
|
||||
cur = cat.value
|
||||
# 最左应为 Name
|
||||
# leftmost must be a Name
|
||||
head_id: str = None
|
||||
if cur is not None and cur.kind() == ast.ASTKind.Name:
|
||||
hnm: ast.Name* = ast.Name*(cur)
|
||||
@@ -187,7 +168,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
if head_id is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
# head 判定:模块名 or 变量/类
|
||||
# head decision: module name or variable/class
|
||||
is_mod: int = 0
|
||||
mod_sha1: str = None
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, head_id) is None:
|
||||
@@ -195,7 +176,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
if mod_sha1 is None: mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(head_id)
|
||||
if mod_sha1 is not None: is_mod = 1
|
||||
if is_mod != 0:
|
||||
# 模块限定:symbol = items 反转(不含 head_id)
|
||||
# module-qualified: symbol = items reversed (excluding head_id)
|
||||
if n_items == 0:
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
string.strcpy(out_sym, head_id)
|
||||
@@ -216,7 +197,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
stdlib.free(sym_buf)
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
# 变量/类:symbol = head_id + '.' + items 反转
|
||||
# variable/class: symbol = head_id + '.' + items reversed
|
||||
total2: sizet = string.strlen(head_id)
|
||||
for j in range(n_items): total2 += string.strlen(items + j * 128) + 1
|
||||
sym2: bytes = stdlib.malloc(total2 + 1)
|
||||
@@ -235,27 +216,15 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
stdlib.free(sym2)
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
# ============================================================
|
||||
# P3 迁移方法(来自 HandlesExprOps.vp:运算符重载 + 二元运算)
|
||||
# ============================================================
|
||||
# P3 migrated methods (from HandlesExprOps.vp: operator overload + binary ops)
|
||||
|
||||
# ============================================================
|
||||
# infer_expr_class - 从表达式推断结构体类名(共享推断)
|
||||
#
|
||||
# 无注解赋值(HandlesAssign)与方法调用分派(CallGenMixin)共用:
|
||||
# 联合类型 X* 简化为 Ptr(i8),不含类名,需按表达式结构推断:
|
||||
# Call → 类实例化(func 为已注册结构体名);func 为 Attribute
|
||||
# 时是方法调用,经 vpi 返回注解缓存解析返回类名
|
||||
# Name → 源变量的 AnnotClassName
|
||||
# BinOp/UnaryOp → lhs 类名 + 存在对应 dunder 方法(返回同类对象)
|
||||
# Attribute → 字段注解类名(_resolve_attr_field_class)
|
||||
# 仅采用已注册结构体;无法精确推断返回 None(fail-fast 由调用方暴露)。
|
||||
# ============================================================
|
||||
# infer_expr_class - infer the struct class name from an expression (shared by
|
||||
# unannotated assignment and method dispatch); returns None if undecidable.
|
||||
def infer_expr_class(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator*) -> str:
|
||||
"""从表达式推断结构体类名,无法推断返回 None"""
|
||||
"""Infer the struct class name from an expression, or None if undecidable."""
|
||||
if node is None or trans is None: return None
|
||||
k: int = node.kind()
|
||||
# Call: 类实例化 / 方法调用
|
||||
# Call: class instantiation / method call
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl is None or cl.func is None: return None
|
||||
@@ -265,11 +234,11 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
if self.Gen.StructGen.find_struct(cand) is not None: return cand
|
||||
return None
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
# 方法调用 a.cross(b):vpi 返回注解缓存精确解析返回类名
|
||||
# (方法可返回非同类对象,如 Transform.world_position → 'Vec3')。
|
||||
# Method call a.cross(b): resolve the return class from the vpi return
|
||||
# annotation cache (may differ from the receiver class).
|
||||
ca: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if ca is None or ca.attr is None or ca.value is None: return None
|
||||
# 模块级函数调用(Module.func()):接收者是已导入模块名而非类对象
|
||||
# module-level call (Module.func()): receiver is an imported module name
|
||||
if ca.value.kind() == ast.ASTKind.Name:
|
||||
mod_nm2: str = (ast.Name*(ca.value)).id
|
||||
if mod_nm2 is not None and trans._from_imports is not None:
|
||||
@@ -290,14 +259,14 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
ret_cls: str = None
|
||||
if cls_sha1 is not None:
|
||||
temp_dir: str = None
|
||||
# 跨子对象访问经 self.Gen 取 CallGenBase.TempDir(双向引用)
|
||||
# cross sub-object access via self.Gen for CallGenBase.TempDir
|
||||
if self.Gen.CallGenBase.TempDir is not None: temp_dir = self.Gen.CallGenBase.TempDir
|
||||
if temp_dir is not None: VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
|
||||
annot_ret: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, obj_cls, ca.attr)
|
||||
if annot_ret is not None: ret_cls = self._extract_annot_class_name(pool, annot_ret)
|
||||
return ret_cls
|
||||
return None
|
||||
# Name: 源变量注解
|
||||
# Name: source variable annotation
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm is None or nm.id is None: return None
|
||||
@@ -305,15 +274,12 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
if ve is None: return None
|
||||
acn_n: str = self.Gen.VarGen.var_annot_class_name(ve)
|
||||
if acn_n is None: return None
|
||||
# 去模块前缀:json.JsonValue → JsonValue。AnnotClassName 可能带
|
||||
# 模块限定(extract_class_name_from_annotation 的 Attribute 分支返回
|
||||
# "memhub.MemBuddy" 形式),而 find_struct/_cls_has_dunder/
|
||||
# get_struct_sha1 查找都用裸类名(struct 注册名)。泛型名
|
||||
# (list[str] 含 '[')不截断(方括号内可能有点号)。
|
||||
# Strip the module prefix (json.JsonValue -> JsonValue): the lookups use
|
||||
# bare names; generic names containing '[' are not truncated.
|
||||
acn_dot_n: str = string.strrchr(acn_n, 46)
|
||||
if acn_dot_n is not None and string.strchr(acn_n, 91) is None: acn_n = acn_dot_n + 1
|
||||
return acn_n
|
||||
# BinOp/UnaryOp: 运算符重载返回同类对象
|
||||
# BinOp/UnaryOp: operator overload returns the same class object
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
if bop is None or bop.left is None: return None
|
||||
@@ -330,24 +296,19 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
opr_cls: str = self.infer_expr_class(pool, mod, uop.operand, trans)
|
||||
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0: return None
|
||||
return opr_cls
|
||||
# Attribute: 字段注解类名(属性链递归,如 p = r.origin)
|
||||
# Attribute: field annotation class name (recursive chain, e.g. p = r.origin)
|
||||
if k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node)
|
||||
return None
|
||||
# ============================================================
|
||||
# _cls_has_dunder - 检查类(含继承链)是否存在 dunder/方法
|
||||
#
|
||||
# 与 try_operator_overload 的查找一致:当前模块内定义或跨模块类有
|
||||
# SHA1(stub 中声明)即视为存在。仅在确实存在时采用推断类名,
|
||||
# 避免把原生运算误标为类类型。
|
||||
# ============================================================
|
||||
# _cls_has_dunder - whether a class (or its MRO ancestors) has the dunder/method;
|
||||
# defined in this module or declared in a cross-module stub SHA1.
|
||||
def _cls_has_dunder(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, class_name: str, dunder: str) -> int:
|
||||
"""类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1"""
|
||||
"""Return 1 if the class or its MRO ancestors define the dunder method."""
|
||||
if class_name is None or dunder is None or mod is None: return 0
|
||||
lookup_name: str = pool.alloc(128)
|
||||
if lookup_name is None: return 0
|
||||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None: return 1
|
||||
# 继承链祖先查找
|
||||
# walk MRO ancestors
|
||||
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
|
||||
mi: int = 1
|
||||
while mro_ptr is not None and mro_ptr[mi] != 0:
|
||||
@@ -358,19 +319,13 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, anc_lookup) is not None: return 1
|
||||
mi += 1
|
||||
# 跨模块:类有 SHA1(stub 中声明该方法)视为存在
|
||||
# cross-module: a class with a SHA1 (method declared in a stub) counts as existing
|
||||
if self.Gen.StructGen.get_struct_sha1(class_name) is not None: return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# _extract_annot_class_name - 从返回注解文本提取结构体类名
|
||||
#
|
||||
# vpi 返回注解文本(如 "'Vec3' | CPtr")按 '|' 分段,每段去除
|
||||
# 空格/引号后查结构体注册表:命中即类名(内置类型 str/int/
|
||||
# CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合
|
||||
# 多候选时取首个命中段。
|
||||
# ============================================================
|
||||
# _extract_annot_class_name - extract a registered struct class name from a vpi
|
||||
# return annotation, splitting on '|' and taking the first hit.
|
||||
def _extract_annot_class_name(self, pool: memhub.MemBuddy*, annot: str) -> str:
|
||||
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
|
||||
"""Extract a registered struct class name from return annotation text, or None."""
|
||||
if pool is None or annot is None: return None
|
||||
alen: sizet = string.strlen(annot)
|
||||
if alen == 0: return None
|
||||
@@ -383,39 +338,35 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
s: sizet = start
|
||||
e: sizet = i
|
||||
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1
|
||||
# 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态
|
||||
# `{sha}.AST*` 按 '|' 分段能落到 `{sha}.AST` 命中),与
|
||||
# ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径;
|
||||
# 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。
|
||||
# Also strip a trailing '*' (e.g. `{sha}.AST*`), matching the tail
|
||||
# stripping in ExprGenName._list_elem_obj_ty_from_struct_name.
|
||||
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1
|
||||
seglen: sizet = e - s
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
if self.Gen.StructGen.find_struct(seg) is not None: return seg
|
||||
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中
|
||||
# 时去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
|
||||
# On a find_struct miss, retry with the last '.'-prefix (module
|
||||
# qualifier) stripped: memhub.MemBuddy -> MemBuddy.
|
||||
seg_dot2: str = string.strrchr(seg, 46)
|
||||
if seg_dot2 is not None and seg_dot2 != seg:
|
||||
seg_bare2: str = seg_dot2 + 1
|
||||
if self.Gen.StructGen.find_struct(seg_bare2) is not None: return seg_bare2
|
||||
start = i + 1
|
||||
# 最后一段(无 '|' 后缀)
|
||||
# last segment (no '|' suffix)
|
||||
s: sizet = start
|
||||
e: sizet = alen
|
||||
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1
|
||||
# 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态
|
||||
# `{sha}.AST*` 按 '|' 分段能落到 `{sha}.AST` 命中),与
|
||||
# ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径;
|
||||
# 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。
|
||||
# Also strip a trailing '*' (e.g. `{sha}.AST*`), matching the tail
|
||||
# stripping in ExprGenName._list_elem_obj_ty_from_struct_name.
|
||||
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1
|
||||
seglen: sizet = e - s
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
if self.Gen.StructGen.find_struct(seg) is not None: return seg
|
||||
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中时
|
||||
# 去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
|
||||
# On a find_struct miss, retry with the last '.'-prefix (module
|
||||
# qualifier) stripped: memhub.MemBuddy -> MemBuddy.
|
||||
seg_dot3: str = string.strrchr(seg, 46)
|
||||
if seg_dot3 is not None and seg_dot3 != seg:
|
||||
seg_bare3: str = seg_dot3 + 1
|
||||
|
||||
@@ -13,17 +13,13 @@ import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenConst - ExprGenMixin 拆分(阶段 D):常量/全局字符串/list 字面量翻译
|
||||
# ============================================================
|
||||
# ExprGenConst - split from ExprGenMixin (stage D): constants / global strings / list literals
|
||||
novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
"""常量/全局字符串/list 字面量翻译"""
|
||||
"""Translation of constants, global strings and list literals."""
|
||||
|
||||
# ============================================================
|
||||
# 创建全局字符串常量
|
||||
# ============================================================
|
||||
# Create global string constant
|
||||
def create_global_string(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, str_val: str, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""创建全局字符串常量并返回 i8* bitcast"""
|
||||
"""Create a global string constant and return its i8* bitcast."""
|
||||
escaped: str = self.escape_llvm_string(pool, str_val)
|
||||
if escaped is None: return None
|
||||
|
||||
@@ -33,11 +29,11 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
arr_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, count)
|
||||
|
||||
# 使用模块级计数器(trans._label_counter)避免跨函数命名冲突
|
||||
# Use the module-level counter (trans._label_counter) to avoid cross-function naming conflicts
|
||||
str_idx: int = trans._label_counter
|
||||
trans._label_counter += 1
|
||||
|
||||
# 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名
|
||||
# Prefix string names with the SHA1 (same rule as function export) to avoid cross-module collisions
|
||||
gv_name: str = pool.alloc(48)
|
||||
if gv_name is None: return None
|
||||
if trans.ModuleSha1 is not None: viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
|
||||
@@ -47,10 +43,10 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
if gv is None: return None
|
||||
mlirlite.module_add_global(mod, gv)
|
||||
|
||||
# 去掉 private 链接,和函数一致:stub 中 declare,text 中 define
|
||||
# Drop private linkage, same as funcs: declare in stub, define in text
|
||||
mlirlite.global_set_unnamed_addr(gv, 1)
|
||||
mlirlite.global_set_constant(gv, 1)
|
||||
# LLVM 22+ 不允许 external linkage 全局变量带初始值,设为 internal
|
||||
# LLVM 22+ forbids initializers on external-linkage globals, so set internal
|
||||
gv.Linkage = "internal"
|
||||
gv.Initializer = escaped
|
||||
|
||||
@@ -64,32 +60,25 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
|
||||
bc: mlirlite.Value* = mlirlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
|
||||
return bc
|
||||
# ============================================================
|
||||
# 翻译常量表达式
|
||||
# ============================================================
|
||||
# Translate constant expressions
|
||||
def translate_constant(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*:
|
||||
"""翻译常量(int/str/bool)"""
|
||||
"""Translate a constant node (int/str/bool)."""
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
if cn is None: return None
|
||||
ck: int = cn.const_kind
|
||||
if cn.const_kind == ast.CONST_INT:
|
||||
# 位宽选择:
|
||||
# 1) 目标上下文是 >32 位整数时直接取 i64 —— 必需:词法层 atoll 把
|
||||
# `0xFFFFFFFFFFFFFFFF` 按有符号 int64 回绕成 -1,仅按"是否超出
|
||||
# i32 范围"判定会把它物化成 i32 -1,随后强转路径 zext 到 i64
|
||||
# → 0x00000000FFFFFFFF(N1:THREAD_CREATE 失败码错)。
|
||||
# const_int 按十进制值打印,故 i64 -1 即全 1,语义正确。
|
||||
# 2) 否则维持旧规则:超出 i32 范围用 i64,其余 i32。
|
||||
# Bit width: (1) if the target context is >32-bit int, use i64 directly - required
|
||||
# since atoll wraps 0xFFFF... to -1 as signed int64; (2) else i64 if out of i32 range, otherwise i32.
|
||||
iv: int64 = cn.int_val
|
||||
if target_ty is not None and self.is_ptr_type(target_ty) == 0 and self.get_llvm_type_bits(target_ty) > 32: return mlirlite.const_int64(pool, iv)
|
||||
if iv > 2147483647 or iv < -2147483648: return mlirlite.const_int64(pool, iv)
|
||||
return mlirlite.const_int32(pool, iv)
|
||||
elif cn.const_kind == ast.CONST_FLOAT:
|
||||
# 浮点常量默认创建为 double(64 位),赋值时由 coerce_to_type 自动 fptrunc
|
||||
# Float constants default to double (64-bit); coerce_to_type fptruncs on assignment
|
||||
double_ty: mlirlite.LLVMType* = mlirlite.Double(pool)
|
||||
return mlirlite.ConstFloat(pool, double_ty, cn.float_val)
|
||||
elif cn.const_kind == ast.CONST_CHAR:
|
||||
# 单引号单字符字面量(CONST_CHAR):默认按字符值(i32,coerce 截断为 i8);但当目标类型是指针(str/bytes/t.CPtr)时须物化为 1 字符字符串,否则 coerce inttoptr 把字符值当指针(如 '"'=0x22)→ 运行期 strlen 访问低地址崩溃。1a 阶段(declare_only=1)依赖函数 found_func=None,target_ty 未对齐即视为垃圾跳过,避免 is_ptr_type 解引用崩溃
|
||||
# Single-char literal (CONST_CHAR): default as char value (i32, coerce truncates to i8); if the target type is a pointer (str/bytes/t.CPtr) materialize a 1-char string, else inttoptr treats the char code as a pointer -> strlen crash. In 1a stage (declare_only=1) found_func=None so a misaligned target_ty is treated as garbage and skipped
|
||||
if target_ty is not None and (uint64(target_ty) & 7) == 0 and self.is_ptr_type(target_ty) != 0:
|
||||
cbuf: bytes = stdlib.malloc(2)
|
||||
if cbuf is None: return mlirlite.const_int32(pool, 0)
|
||||
@@ -109,7 +98,7 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
if cn.int_val != 0: return mlirlite.const_int32(pool, 1)
|
||||
return mlirlite.const_int32(pool, 0)
|
||||
elif cn.const_kind == ast.CONST_NONE:
|
||||
# None → i8* null(空指针常量),用于 `p is None` / `p is not None` 比较
|
||||
# None -> i8* null (null pointer constant) for `p is None` / `p is not None` comparisons
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
if i8_ty is None: return None
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
@@ -117,17 +106,10 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
rv_none: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||||
return rv_none
|
||||
return None
|
||||
# ============================================================
|
||||
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
|
||||
#
|
||||
# 用于 list 字面量 [a, b, c] 的元素类型推断
|
||||
# ============================================================
|
||||
# _infer_elem_type_from_value - infer the list element type name from an LLVM Value.
|
||||
# Used for element type inference of list literals [a, b, c].
|
||||
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy*, value: mlirlite.Value*) -> str:
|
||||
"""从 LLVM Value 的类型推断 list 元素类型名
|
||||
|
||||
fail-fast:值类型缺失/无法映射时返回 None(调用方携带节点报错终止),
|
||||
绝不静默回退 "str"(掩盖上游 Value 类型缺失根因)。
|
||||
"""
|
||||
"""Infer the list element type name from an LLVM Value; None if unmappable."""
|
||||
if value is None or value.Ty is None: return None
|
||||
ty: mlirlite.LLVMType* = value.Ty
|
||||
bits: int = self.get_llvm_type_bits(ty)
|
||||
@@ -143,20 +125,10 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
if fbits == 64: return "CDouble"
|
||||
if fbits == 32: return "CFloat"
|
||||
return None
|
||||
# ============================================================
|
||||
# _find_pool_var — 查找上下文中的 pool 变量
|
||||
#
|
||||
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
|
||||
# ============================================================
|
||||
# _find_pool_var - find the pool variable in the context.
|
||||
# Tries "pool", "_mbuddy", "mbuddy", "mb" in order.
|
||||
def _find_pool_var(self, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""查找上下文中的 pool 变量,返回 alloca 或 None
|
||||
|
||||
查找顺序: pool → _mbuddy → mbuddy → mb
|
||||
兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
|
||||
注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理),
|
||||
此兜底仅用于函数内的动态 list 字面量(如 op=[...])。
|
||||
@_mbuddy 由入口模块(如 Phase2.py)定义并初始化,其他模块以 external 引用。
|
||||
"""
|
||||
"""Find the pool variable in context (pool/_mbuddy/mbuddy/mb), else fall back."""
|
||||
if trans is None: return None
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
|
||||
if alloca is not None: return alloca
|
||||
@@ -166,42 +138,38 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
if alloca is not None: return alloca
|
||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mb")
|
||||
if alloca is not None: return alloca
|
||||
# 兜底: 创建当前模块 mangled 的 @_mbuddy 定义(linkonce_odr + zeroinitializer)
|
||||
# 对齐模块级全局带 SHA1 前缀命名:裸名 @_mbuddy 依赖链接合并共享
|
||||
# (--allow-multiple-definition),带前缀后每模块独立。linkonce_odr 允许
|
||||
# 链接器合并同模板重复特化定义,zeroinitializer 保证编译链接通过。
|
||||
# 运行时若该模块未在入口 main 赋值 _mbuddy(null pool 上动态建 list),
|
||||
# 由真实崩溃暴露(fail-fast 哲学,不掩盖上游未初始化)。
|
||||
# Fallback: create a module-mangled @_mbuddy definition (linkonce_odr + zeroinitializer),
|
||||
# matching the SHA1-prefixed global naming; a real crash exposes an uninitialized pool (fail-fast).
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
if pool is None or trans.Module is None or trans.SymTab is None: return None
|
||||
# 先检查是否已注册到模块作用域(避免重复创建全局变量)
|
||||
# First check whether it is already registered in the module scope (avoid duplicate globals)
|
||||
existing: mlirlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
|
||||
if existing is not None: return existing
|
||||
# 创建 i8* 类型(t.CPtr = i8*)
|
||||
# Create the i8* type (t.CPtr = i8*)
|
||||
i8_ty_fb: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty_fb: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_fb)
|
||||
# 当前模块 mangled 名(@<模块sha1>._mbuddy)
|
||||
# Module-mangled name (@<module-sha1>._mbuddy)
|
||||
mb_name_fb: str = self.Gen.FuncGenBase._mangle_name(trans, "_mbuddy")
|
||||
# 创建 linkonce_odr 定义(可被链接器合并,自给自足保证链接通过)
|
||||
# Create a linkonce_odr definition (linker-mergeable, self-contained for linking)
|
||||
gv_ext: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
|
||||
if gv_ext is not None:
|
||||
gv_ext.Linkage = "linkonce_odr"
|
||||
gv_ext.Initializer = "zeroinitializer"
|
||||
mlirlite.module_add_global(trans.Module, gv_ext)
|
||||
# 创建 Value 引用(@<模块sha1>._mbuddy, 类型为 i8**)
|
||||
# Create the Value reference (@<module-sha1>._mbuddy, type i8**)
|
||||
i8_ptr_ptr_ty_fb: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty_fb)
|
||||
ref_name_fb: str = pool.alloc(96)
|
||||
if ref_name_fb is not None:
|
||||
# mangled 名带 SHA1 前缀(数字开头),引用必须加引号(见 make_global_ref)
|
||||
# Mangled names start with a digit (SHA1 prefix), so the reference must be quoted (see make_global_ref)
|
||||
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mb_name_fb) != 0: viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
|
||||
else: viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
|
||||
gv_ref_fb: mlirlite.Value* = mlirlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
|
||||
# 注册到模块作用域,避免重复创建
|
||||
# Register in the module scope to avoid duplicate creation
|
||||
self.Gen.VarGen.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
|
||||
return gv_ref_fb
|
||||
# translate_list_literal:把 list 字面量 [a, b, c] 翻译为 list[T](pool) 构造 + append 调用序列。elem_type_name 为元素类型名(如 "str"/"int"),None 表示从元素推断。返回 list 对象指针(Ptr(list[T] struct)),失败返回 None
|
||||
# translate_list_literal - lower [a, b, c] to list[T](pool) construction + append calls; elem_type_name None means infer from elements; returns Ptr(list[T] struct) or None
|
||||
def translate_list_literal(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, elem_type_name: str, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
|
||||
"""Lower a list literal to list[T](pool) construction plus append calls."""
|
||||
if node is None or trans is None: return None
|
||||
|
||||
list_node: ast.List* = ast.List*(node)
|
||||
@@ -211,7 +179,7 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
elts_count: sizet = 0
|
||||
if elts is not None: elts_count = len(elts)
|
||||
|
||||
# 推断元素类型
|
||||
# Infer element type
|
||||
inferred_type_name: str = elem_type_name
|
||||
first_elem_val: mlirlite.Value* = None
|
||||
if inferred_type_name is None:
|
||||
@@ -219,29 +187,29 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
first_elem_node: ast.AST* = elts.get(0)
|
||||
first_elem_val = self.Gen.ExprGen.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
|
||||
if first_elem_val is None or first_elem_val.Ty is None:
|
||||
# fail-fast:首元素翻译失败/Value 类型缺失,立即终止编译(暴露上游根因)
|
||||
# fail-fast: first-element translation failed/Value type missing -> abort now (surface root cause)
|
||||
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
|
||||
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
|
||||
if inferred_type_name is None:
|
||||
# fail-fast:首元素 Value 类型无法映射为 list 元素类型,立即终止编译
|
||||
# fail-fast: first-element Value type cannot map to a list element type -> abort now
|
||||
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
|
||||
else:
|
||||
# 空 list 无元素可推断,保持默认 str(list[str])
|
||||
# Empty list has no element to infer, keep default str (list[str])
|
||||
inferred_type_name = "str"
|
||||
|
||||
# 查找 pool 变量
|
||||
# Find the pool variable
|
||||
pool_alloca: mlirlite.Value* = self._find_pool_var(trans)
|
||||
if pool_alloca is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal requires pool variable in context")
|
||||
return None
|
||||
|
||||
# load pool 变量值(i8*)
|
||||
# Load the pool variable value (i8*)
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
pool_val: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, pool_alloca)
|
||||
if pool_val is None: return None
|
||||
|
||||
# 特化 list[T] 类(elem_size=8:str 是指针,4 会导致双元素重叠损坏)
|
||||
# Specialize the list[T] class (elem_size=8: str is a pointer; 4 would overlap elements)
|
||||
type_args: list[str]* = list[str](pool, 8)
|
||||
type_args.append(inferred_type_name)
|
||||
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, "list", type_args)
|
||||
@@ -249,7 +217,7 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal generic specialization failed")
|
||||
return None
|
||||
|
||||
# 查找 struct entry
|
||||
# Find the struct entry
|
||||
struct_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
|
||||
if struct_entry is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "list literal specialized class not registered")
|
||||
@@ -257,16 +225,14 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
struct_ty: mlirlite.LLVMType* = struct_entry.Ty
|
||||
if struct_ty is None: return None
|
||||
|
||||
# alloca 临时 list 空间
|
||||
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
|
||||
# alloca temporary list storage; ensure the cross-module struct definition is
|
||||
# fully available in the current module (so alloca can size it).
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
|
||||
tmp: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
|
||||
if tmp is None: return None
|
||||
|
||||
# 存储指针:默认用 alloca,如果有 __new__ 则用 __new__ 返回的指针
|
||||
# __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用
|
||||
# (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致,
|
||||
# 避免为无 __new__ 的类生成过度声明)
|
||||
# Storage pointer: alloca by default, or the pointer returned by __new__ if present.
|
||||
# __new__ is optional and only called when the generic template defines it (consistent with ctor lowering).
|
||||
storage_ptr: mlirlite.Value* = tmp
|
||||
has_new_flag: int = 0
|
||||
if struct_entry is not None: has_new_flag = struct_entry.HasNew
|
||||
@@ -278,10 +244,10 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
|
||||
if new_ptr is not None: storage_ptr = new_ptr
|
||||
|
||||
# 调用 __before_init__(storage_ptr)
|
||||
# Call __before_init__(storage_ptr)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
|
||||
|
||||
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
|
||||
# Call __init__(storage_ptr, pool_val, elem_size=0)
|
||||
init_args: sizet* = pool.alloc(8 * 32)
|
||||
if init_args is None: return None
|
||||
string.memset(init_args, 0, 8 * 32)
|
||||
@@ -290,9 +256,9 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
init_args[1] = sizet(elem_size_val)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
|
||||
|
||||
# 对每个元素调用 append(storage_ptr, elem_val)
|
||||
# Call append(storage_ptr, elem_val) for each element
|
||||
if elts_count > 0:
|
||||
# 第一个元素可能已经翻译过
|
||||
# The first element may already be translated
|
||||
if first_elem_val is not None:
|
||||
append_args: sizet* = pool.alloc(8 * 32)
|
||||
if append_args is not None:
|
||||
@@ -300,7 +266,7 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
append_args[0] = sizet(first_elem_val)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
|
||||
|
||||
# 翻译剩余元素
|
||||
# Translate the remaining elements
|
||||
for i in range(1, elts_count):
|
||||
elem_node_i: ast.AST* = elts.get(i)
|
||||
elem_val_i: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
|
||||
|
||||
@@ -8,43 +8,30 @@ import lib.core.Gen.Translator as HT
|
||||
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
|
||||
# ============================================================
|
||||
# f-string 翻译常量(与 ExprGenMixin 保持一致,本地副本避免跨模块
|
||||
# CDefine 查找 bug:ExprGenFstr(F) 在 ExprGenMixin(M) 之前按文件序
|
||||
# 编译时,跨模块 CDefine 全局表未注册 → NameError 未定义)
|
||||
# ============================================================
|
||||
# f-string translation constants (local copy of ExprGenMixin's to avoid a cross-module
|
||||
# CDefine lookup bug: ExprGenFstr compiles before ExprGenMixin, so the table is unregistered).
|
||||
define FSTR_BUF_SIZE = 1024
|
||||
define FSTR_SPEC_MAX = 64
|
||||
define FSTR_FMT_MAX = 64
|
||||
|
||||
# ============================================================
|
||||
# ExprGenFstr - ExprGenMixin 拆分(阶段 D):f-string/格式化值翻译
|
||||
# ============================================================
|
||||
# ExprGenFstr - ExprGenMixin split (stage D): f-string/formatted-value translation
|
||||
novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
"""f-string/格式化值翻译"""
|
||||
"""f-string and formatted-value translation."""
|
||||
|
||||
# ============================================================
|
||||
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
|
||||
#
|
||||
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
|
||||
# 栈缓冲(每站点独立,线程安全);结果是**函数内临时值,禁止逃逸**
|
||||
# (return / 存字段 / 存全局 由 GenApi.fail_if_fstring_escape 拦;
|
||||
# 需逃逸用 GenApi.escape_fstring_copy 显式拷贝)。
|
||||
# 之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
|
||||
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
|
||||
# ============================================================
|
||||
# translate_joined_str - f-string translation (aligned with TPC _HandleJoinedStrLlvm):
|
||||
# join literals and format specs, snprintf into a per-site i8* stack buffer (no escape).
|
||||
def translate_joined_str(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""f-string → snprintf 生成 i8* 栈缓冲(函数内临时值,禁止逃逸)"""
|
||||
"""Lower an f-string to snprintf into a per-site i8* stack buffer."""
|
||||
if node is None or builder is None or trans is None: return None
|
||||
js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](node)
|
||||
if js is None or js.values is None: return None
|
||||
|
||||
# 编译期格式串缓冲(pool 分配)
|
||||
# Compile-time format string buffer (pool-allocated)
|
||||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
||||
if fmt_buf is None: return None
|
||||
fmt_buf[0] = '\0'
|
||||
|
||||
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
|
||||
# Runtime argument list (linked by Value.Next, matching the printf call)
|
||||
arg_head: mlirlite.Value* = None
|
||||
arg_tail: mlirlite.Value* = None
|
||||
arg_count: int = 0
|
||||
@@ -64,23 +51,23 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
if fv.value is None: continue
|
||||
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
|
||||
if val is None:
|
||||
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
|
||||
# fail-fast: a failed formatted-value translation must abort compilation
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
|
||||
continue
|
||||
# 对象定义了 __str__ 且未指定格式 → 用其返回的字符串(对齐 TPC)
|
||||
# Object defines __str__ and no format is given -> use its returned string (like TPC)
|
||||
if fv.format_spec is None:
|
||||
str_ov_fs: mlirlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
|
||||
if str_ov_fs is not None: val = str_ov_fs
|
||||
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
|
||||
# Determine the C format specifier (format_spec first, else infer from the value type)
|
||||
c_fmt: str = None
|
||||
if fv.format_spec is not None: c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||||
if c_fmt is None: c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||||
if c_fmt is None:
|
||||
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
|
||||
# fail-fast: cannot infer the f-string format (missing value type); abort
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||||
# 追加格式说明符
|
||||
# Append the format specifier
|
||||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1: string.strcat(fmt_buf, c_fmt)
|
||||
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
|
||||
# Convert the argument type (i8/i16->i32, bool->i32, f32->f64)
|
||||
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
||||
if cast_val is None: cast_val = val
|
||||
cast_val.Next = None
|
||||
@@ -91,7 +78,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
|
||||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||||
def translate_formatted_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
|
||||
"""Translate a bare FormattedValue without any literal parts."""
|
||||
if node is None or builder is None or trans is None: return None
|
||||
fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](node)
|
||||
if fv is None or fv.value is None: return None
|
||||
@@ -103,10 +90,10 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
arg_count: int = 0
|
||||
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
|
||||
if val is None:
|
||||
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
|
||||
# fail-fast: a failed bare FormattedValue translation must abort compilation
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
|
||||
else:
|
||||
# 对象定义了 __str__ 且未指定格式 → 用其返回的字符串(对齐 TPC)
|
||||
# Object defines __str__ and no format is given -> use its returned string (like TPC)
|
||||
if fv.format_spec is None:
|
||||
str_ov_sf: mlirlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
|
||||
if str_ov_sf is not None: val = str_ov_sf
|
||||
@@ -114,7 +101,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
if fv.format_spec is not None: c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||||
if c_fmt is None: c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||||
if c_fmt is None:
|
||||
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
|
||||
# fail-fast: cannot infer the f-string format (missing value type); abort
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1: string.strcat(fmt_buf, c_fmt)
|
||||
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
||||
@@ -125,47 +112,31 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
arg_count = 1
|
||||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||||
def _fstr_finalize(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, fmt_buf: str, arg_head: mlirlite.Value*, arg_tail: mlirlite.Value*, arg_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""f-string 尾部:全局格式串 + 每站点栈缓冲 + snprintf,返回 i8*(函数内临时值)"""
|
||||
"""Emit the final snprintf over the global format and per-site buffer."""
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
if fmt_buf is None or fmt_buf[0] == '\0':
|
||||
# 空 f-string(f"")→ 空字符串常量
|
||||
# Empty f-string (f"") -> empty string constant
|
||||
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, "", trans)
|
||||
# 格式串 → 全局字符串常量(i8*)
|
||||
# Format string -> global string constant (i8*)
|
||||
fmt_ptr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fmt_buf, trans)
|
||||
if fmt_ptr is None: return None
|
||||
# 结果缓冲:**每站点独立栈缓冲**(entry 块 alloca;每次翻译本站点各建一个,
|
||||
# 站点之间不共享 ⇒ 同函数多个 f-string 不再互相覆盖)。
|
||||
#
|
||||
# 语义(用户 2026-09-27 决定):f-string 结果是**当前函数内的临时值**。
|
||||
# · 栈缓冲 ⇒ 线程安全(每个线程各自一份栈帧,同站点并发也不串味)
|
||||
# + 零运行时依赖(内核没有 C-ABI malloc,也不能假设任何分配器);
|
||||
# · 函数返回即失效 ⇒ **禁止逃逸**:return / 存字段 / 存全局 / 存容器一律
|
||||
# 由 GenApi.fail_if_fstring_escape 在翻译期 fail-fast 拦下。历史事故:
|
||||
# 旧实现让调用方把该栈指针 return 出去,再作 %s 喂 snprintf ⇒
|
||||
# ASan stack-buffer-underflow in vsnprintf(READ size 1,栈左红区)。
|
||||
# · 确需逃逸时**显式分配后拷贝**:GenApi.escape_fstring_copy
|
||||
# (stdlib.malloc + string.strcpy);编译器自身的报文构造即此写法
|
||||
# (AssignGenMixin.const_assign_msg)。
|
||||
# Result buffer: a per-site entry alloca (thread-safe, no runtime dependency), so the
|
||||
# f-string result is a function-local temporary that must not escape; copy to escape.
|
||||
buf_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
|
||||
buf_alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty)
|
||||
if buf_alloca is None: return None
|
||||
zero_i32: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
buf_ptr: mlirlite.Value* = mlirlite.build_gep_array(builder, buf_ty, i8_ty, buf_alloca, zero_i32)
|
||||
if buf_ptr is None: return None
|
||||
# f-string 降级为 snprintf 调用,而 snprintf 的声明在 includes/stdio.vp:7
|
||||
# (`extern def snprintf(buf, size, fmt, *args) -> int`)。源文件若未
|
||||
# import 任何声明它的模块,当前模块就没有 @snprintf 的 declare,问题会
|
||||
# 被拖到后端才炸(llc: "use of undefined value '@snprintf'")——按"隐式
|
||||
# 提供"原则在使用点补 declare(先例:AsyncGenMixin 的 GetTickCount64)。
|
||||
# 签名三段固定参数(i8*, i64, i8*)+ 变参,与下面的 build_call 一致。
|
||||
# f-string lowers to snprintf, declared in includes/stdio.vp. If the source imports no
|
||||
# module declaring it, add the declare here (implicit-provide); i8*, i64, i8* + varargs.
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, "snprintf") is None:
|
||||
snprintf_decl: mlirlite.Function* = mlirlite.create_declare(pool, mod, "snprintf", i32_ty)
|
||||
if snprintf_decl is not None:
|
||||
snprintf_decl.IsVarArg = 1
|
||||
# buf/fmt 是指针参数(i8*),**不是**元素类型 i8(buf_ty 的 elem):
|
||||
# 调用侧传 buf_ptr/fmt_ptr(i8*),declare 若写成裸 i8 → MLIR
|
||||
# 'llvm.call' operand type mismatch '!llvm.ptr' != 'i8'(ZlibDbg 根因)。
|
||||
# buf/fmt are pointer params (i8*), not the element type i8: a bare i8 declare
|
||||
# would cause an MLIR 'llvm.call' operand type mismatch ('!llvm.ptr' != 'i8').
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
mlirlite.add_param(pool, snprintf_decl, i8_ptr_ty, "buf")
|
||||
mlirlite.add_param(pool, snprintf_decl, mlirlite.Int64(pool), "size")
|
||||
@@ -179,7 +150,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
mlirlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
|
||||
return buf_ptr
|
||||
def _fstr_append_literal(self, fmt_buf: str, lit: str) -> None:
|
||||
"""追加字面量到格式串(% 转义为 %%)"""
|
||||
"""Append a literal to the format string, escaping percent signs."""
|
||||
if fmt_buf is None or lit is None: return
|
||||
cur_len: sizet = string.strlen(fmt_buf)
|
||||
rem: sizet = FSTR_BUF_SIZE - 1 - cur_len
|
||||
@@ -201,15 +172,15 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
si += 1
|
||||
fmt_buf[cur_len] = '\0'
|
||||
def _fstr_append_raw(self, fmt_buf: str, s: str) -> None:
|
||||
"""追加原始字符串(不做 % 转义)"""
|
||||
"""Append a raw string without percent escaping."""
|
||||
if fmt_buf is None or s is None: return
|
||||
cur_len: sizet = string.strlen(fmt_buf)
|
||||
src_len: sizet = string.strlen(s)
|
||||
if cur_len + src_len < FSTR_BUF_SIZE: string.strcpy(fmt_buf + cur_len, s)
|
||||
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy*, spec_node: ast.AST*, val: mlirlite.Value*, arg_node: ast.AST*) -> str:
|
||||
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
|
||||
"""Convert a Python format_spec into a C printf format string."""
|
||||
if spec_node is None or pool is None or val is None: return None
|
||||
# 1. 提取 spec 字符串(JoinedStr 仅常量拼接)
|
||||
# 1. Extract the spec string (JoinedStr with constants only)
|
||||
spec: str = pool.alloc(FSTR_SPEC_MAX)
|
||||
if spec is None: return None
|
||||
spec[0] = '\0'
|
||||
@@ -233,7 +204,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
slen: sizet = string.strlen(spec)
|
||||
if slen == 0: return None
|
||||
|
||||
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
|
||||
# 2. Parse segment by segment: [fill]align / sign / # / 0 / width / , / .precision / type
|
||||
pos: sizet = 0
|
||||
align_char: char = 0
|
||||
if pos < slen:
|
||||
@@ -266,7 +237,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
if pos < slen and spec[pos] == '0':
|
||||
string.strcat(c_flags, "0")
|
||||
pos += 1
|
||||
# 左对齐 '<'/'^' → '-'(与 0 同时存在时 C 语义 '-' 优先)
|
||||
# Left align '<'/'^' -> '-' (C semantics give '-' priority over '0')
|
||||
if align_char == '<' or align_char == '^': string.strcat(c_flags, "-")
|
||||
# width digits
|
||||
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
||||
@@ -275,7 +246,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
c_width[wl] = spec[pos]
|
||||
c_width[wl + 1] = '\0'
|
||||
pos += 1
|
||||
# comma ',' → C printf 不支持千分位,忽略
|
||||
# comma ',' -> C printf has no thousands separator, ignore
|
||||
if pos < slen and spec[pos] == ',': pos += 1
|
||||
# .precision
|
||||
if pos < slen and spec[pos] == '.':
|
||||
@@ -288,28 +259,26 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
c_prec[pl + 1] = '\0'
|
||||
pos += 1
|
||||
if string.strlen(c_prec) == 1:
|
||||
return None # 只有 '.' 无效
|
||||
return None # a lone '.' is invalid
|
||||
# type char
|
||||
fmt_type: char = 0
|
||||
if pos < slen:
|
||||
fmt_type = spec[pos]
|
||||
pos += 1
|
||||
if pos != slen: return None # 多余字符无法解析
|
||||
if pos != slen: return None # extra characters cannot be parsed
|
||||
|
||||
# 3. 长度修饰(按值类型位宽)
|
||||
# 3. Length modifier (by value type bit width)
|
||||
len_mod: str = pool.alloc(4)
|
||||
if len_mod is None: return None
|
||||
len_mod[0] = '\0'
|
||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
# 32 位**不写**长度修饰符(N14):`{x:08X}` 的实参是 i32,此前推导出
|
||||
# `l` → 格式串 `%08lX`(long)与实参宽度不符(LP64 下 printf 按 64 位
|
||||
# 读参 → 变参错位)。与同文件无 spec 路径 `_fstr_infer_c_fmt`(32 位用
|
||||
# `%u/%d`)以及 TPC 侧 HandlesExprFormat.py 保持一致。
|
||||
# Do not write a length modifier for 32-bit (N14): `{x:08X}` passes an i32, but `l`
|
||||
# would make %08lX (long) mismatch on LP64 and misread the varargs.
|
||||
if ibits == 64: string.strcpy(len_mod, "ll")
|
||||
elif ibits == 16: string.strcpy(len_mod, "h")
|
||||
elif ibits == 8: string.strcpy(len_mod, "hh")
|
||||
|
||||
# 4. 组装 C 格式串
|
||||
# 4. Assemble the C format string
|
||||
out: str = pool.alloc(FSTR_FMT_MAX)
|
||||
if out is None: return None
|
||||
out[0] = '\0'
|
||||
@@ -318,7 +287,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
string.strcat(out, c_width)
|
||||
string.strcat(out, c_prec)
|
||||
if fmt_type == 0:
|
||||
# 无 type:按值类型推断
|
||||
# No type: infer from the value type
|
||||
fbits2: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||
if ibits != 0:
|
||||
if ibits == 8: string.strcat(out, "c")
|
||||
@@ -340,7 +309,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
elif fmt_type == 's': string.strcat(out, "s")
|
||||
elif fmt_type == '%': string.strcat(out, "%")
|
||||
elif fmt_type == 'b':
|
||||
return None # 二进制不支持
|
||||
return None # binary is unsupported
|
||||
elif fmt_type == 'd' or fmt_type == 'i':
|
||||
string.strcat(out, len_mod)
|
||||
string.strcat(out, "d")
|
||||
@@ -354,7 +323,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
else: return None
|
||||
return out
|
||||
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy*, val: mlirlite.Value*, arg_node: ast.AST*, trans: HT.Translator*) -> str:
|
||||
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
|
||||
"""Infer the C printf format from the value type when no format_spec is given."""
|
||||
if val is None or pool is None: return None
|
||||
out: str = pool.alloc(16)
|
||||
if out is None: return None
|
||||
@@ -362,7 +331,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||
if ibits == 1: return "%d" # bool
|
||||
if ibits == 8: return "%c" # 字符
|
||||
if ibits == 8: return "%c" # character
|
||||
if ibits != 0:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
|
||||
if ibits == 64: string.strcpy(out, "%llu")
|
||||
@@ -373,13 +342,13 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
return out
|
||||
if fbits != 0: return "%f"
|
||||
if self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0: return "%s"
|
||||
# fail-fast:值类型缺失(非整数/浮点/指针),无法推断 C 格式,立即终止编译
|
||||
# fail-fast: missing value type (not int/float/pointer); cannot infer the C format
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||||
return None
|
||||
def _fstr_cast_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, val: mlirlite.Value*, c_fmt: str, arg_node: ast.AST*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
|
||||
"""Cast the value to the type required by the C format specifier."""
|
||||
if val is None or c_fmt is None: return None
|
||||
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
|
||||
# Find the format type char (last letter, ignoring l/h length modifiers)
|
||||
fmt_char: char = 0
|
||||
flen: sizet = string.strlen(c_fmt)
|
||||
fi: sizet = flen
|
||||
|
||||
@@ -7,52 +7,30 @@ import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# docstring 登记表容量(__doc__ 魔法属性支持)
|
||||
#
|
||||
# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的
|
||||
# 函数/类/方法 docstring,幂等查重)
|
||||
# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度
|
||||
# DOC_VAL_SZ: docstring 内容缓冲宽度
|
||||
# ============================================================
|
||||
# docstring registry capacities (__doc__ magic attribute support).
|
||||
# DOC_MAX: max entries; DOC_KEY_SZ: key "{module_sha1}.{symbol}" width; DOC_VAL_SZ: content width.
|
||||
define DOC_MAX = 512
|
||||
define DOC_KEY_SZ = 192
|
||||
define DOC_VAL_SZ = 4096
|
||||
|
||||
# ============================================================
|
||||
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
|
||||
#
|
||||
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
|
||||
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
|
||||
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
|
||||
# ============================================================
|
||||
# f-string translation constants (aligned with TPC HandlesExprFormat).
|
||||
# FSTR_BUF_SIZE: runtime stack buffer; FSTR_SPEC_MAX: format_spec parse width; FSTR_FMT_MAX: printf format width.
|
||||
define FSTR_BUF_SIZE = 1024
|
||||
define FSTR_SPEC_MAX = 64
|
||||
define FSTR_FMT_MAX = 64
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenMixin - 表达式处理 Mixin(对象化 P3)
|
||||
# ============================================================
|
||||
# ExprGenMixin - expression handling mixin (objectified P3).
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenMixin - 表达式/常量/下标/属性访问/运算符
|
||||
# ============================================================
|
||||
# ExprGenMixin - expressions / constants / subscripts / attributes / operators.
|
||||
novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
|
||||
"""表达式处理 Mixin(纯方法化迁移,无状态字段)"""
|
||||
"""Expression-handling mixin (pure methods, no state fields)."""
|
||||
|
||||
# ============================================================
|
||||
# 翻译值表达式(RHS 分派)— 模块级版本
|
||||
# ============================================================
|
||||
# Translate a value expression (RHS dispatch) - module-level version.
|
||||
def translate_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
|
||||
"""翻译值表达式为 LLVM Value(深度守卫包装器,原逻辑见 _translate_value_impl)
|
||||
|
||||
target_ty: 目标类型上下文(如调用形参类型/赋值目标类型),用于单引号
|
||||
单字符字面量(CONST_CHAR)在字符串目标下物化为 1 字符字符串(对齐 TPC)。
|
||||
use_ctx: 使用处**真实站点种类**标签("局部变量注解" / "模块级全局" /
|
||||
"形参" / "返回注解" …),只被 CDefine 使用处宽度不足报文读取;不参与判定。
|
||||
"""
|
||||
"""Translate a value expression to an LLVM Value (depth-guard wrapper; see _translate_value_impl).
|
||||
target_ty = assignment/parameter type context; use_ctx = real call site for CDefine width checks."""
|
||||
if trans is not None:
|
||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(node, "表达式翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
|
||||
trans.TranslateDepth += 1
|
||||
@@ -61,7 +39,7 @@ novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
|
||||
return rv
|
||||
|
||||
def _translate_value_impl(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
|
||||
"""翻译值表达式为 LLVM Value"""
|
||||
"""Translate a value expression to an LLVM Value."""
|
||||
if node is None: return None
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
@@ -81,11 +59,8 @@ novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
|
||||
elif k == ast.ASTKind.JoinedStr: return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.FormattedValue: return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.Await:
|
||||
# 协程 await(TPV-M1):await 目标是 async 函数调用时内联翻译
|
||||
# (子帧创建 + resume 到 DONE + 取子帧 ret 存父帧 await 槽 + 父挂起),
|
||||
# 返回子协程 ret 值(i32)。顶层 await(AnnAssign/Assign 的 value 直接
|
||||
# 是 Await)由 AsyncGen 段翻译特判处理,本分支覆盖表达式上下文中的
|
||||
# await(M1 覆盖有限场景,详见 AsyncGenMixin._emit_await)。
|
||||
# Coroutine await (TPV-M1): inline-translate when the await target is an async call.
|
||||
# Returns the child coroutine's i32 ret; top-level await is handled by AsyncGen.
|
||||
aw: rawptr[ast.Await] = rawptr[ast.Await](node)
|
||||
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
|
||||
aw_cl: ast.Call* = ast.Call*(aw.value)
|
||||
@@ -95,10 +70,8 @@ novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
|
||||
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0: return self.Gen.AsyncGen._emit_await(builder, pool, mod, trans, aw, aw_nm.id)
|
||||
return None
|
||||
elif k == ast.ASTKind.Starred:
|
||||
# *x 在值上下文(如 printf("%d", *k) 实参):解析器把调用参数位的
|
||||
# '*' 按 Python *args 解包语义归类为 Starred,但 Viper 未实现解包,
|
||||
# 全代码库无调用点使用;按 C 语义视为单目解引用(*ptr → load pointee,
|
||||
# 对齐 UnaryOp(Deref))。构造 Deref UnaryOp 复用既有翻译/错误路径。
|
||||
# *x in a value context: the parser tags '*' as Starred, but Viper has no unpacking.
|
||||
# Treat it as C unary deref (*ptr -> load pointee) by building a Deref UnaryOp.
|
||||
st: rawptr[ast.Starred] = rawptr[ast.Starred](node)
|
||||
if st is None or st.value is None: return None
|
||||
deref_node: ast.AST* = ast.UnaryOp(pool, ast.OpKind.Deref, st.value)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+216
-443
File diff suppressed because it is too large.
Load diff
@@ -12,22 +12,14 @@ import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||||
|
||||
# ============================================================
|
||||
# FuncGenBase - FuncGenMixin 拆分(阶段 D):函数属性 / 命名 / mangle / create_function 基础工具
|
||||
# ============================================================
|
||||
# FuncGenBase - split from FuncGenMixin (stage D): attrs / naming / mangle / create_function helpers.
|
||||
novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
"""函数属性 / 命名 / mangle / create_function 基础工具"""
|
||||
"""Shared helpers for function attributes, naming, mangling and creation."""
|
||||
|
||||
# ============================================================
|
||||
# extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性
|
||||
#
|
||||
# 支持 @c.Attribute(t.attr.xxx()) 和 @c.Attribute(t.attr.xxx) 形式
|
||||
# 也支持 @c.Attribute(t.attr.llvm.xxx) 形式
|
||||
#
|
||||
# 返回 LLVM IR 属性字符串(如 "alwaysinline nounwind"),无属性返回 None
|
||||
# ============================================================
|
||||
# extract_func_attrs - extract c.Attribute attrs from decorator_list.
|
||||
# Supports @c.Attribute(t.attr.xxx()/t.attr.xxx/t.attr.llvm.xxx); returns LLVM IR attr string or None.
|
||||
def extract_func_attrs(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""从 decorator_list 提取 c.Attribute 属性,返回 LLVM IR 属性字符串"""
|
||||
"""Extract c.Attribute attrs from decorators; return the LLVM IR attr string."""
|
||||
if decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
@@ -41,11 +33,8 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
dk: int = deco.kind()
|
||||
# 直接形式 `@t.attr.<name>`(与类侧 ClassGenBase._has_decorator 同口径,
|
||||
# 类定义早就接受两种写法):`inline def f(...)` 合成的就是这一形态,
|
||||
# 且**不要求** import c。此前只认 c.Attribute(...) ⇒ 没有 import c 的文件
|
||||
# 会静默不内联。不支持的属性名由 _append_llvm_attr 返回 0 自然忽略
|
||||
# (如 @t.CVTable / @t.NoVTable / packed / aligned 对函数无意义)。
|
||||
# Direct form `@t.attr.<name>` (same as ClassGenBase._has_decorator): `inline def f`
|
||||
# synthesizes it and does NOT require `import c`; unsupported names are ignored.
|
||||
if dk == ast.ASTKind.Attribute:
|
||||
dan: str = self._get_attr_name_from_node(deco)
|
||||
if dan is not None and string.strcmp(dan, "section") != 0:
|
||||
@@ -57,7 +46,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
cfk: int = call_node.func.kind()
|
||||
if cfk != ast.ASTKind.Attribute: continue
|
||||
|
||||
# 检测 c.Attribute
|
||||
# Detect c.Attribute
|
||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
||||
if func_attr.attr is None: continue
|
||||
if string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
||||
@@ -70,33 +59,27 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c")
|
||||
if imp_c == 0: continue
|
||||
|
||||
# 遍历参数提取属性
|
||||
# Iterate args and extract attrs
|
||||
if call_node.args is None: continue
|
||||
args_list: list[ast.AST*]* = call_node.args
|
||||
an: sizet = len(args_list)
|
||||
for ai in range(an):
|
||||
arg_node: ast.AST* = args_list.get(ai)
|
||||
if arg_node is None: continue
|
||||
# 提取属性名(t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
|
||||
# Extract attr name (t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
|
||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||
if attr_name is None: continue
|
||||
# B5:section 字符串属性(t.attr.section(".text.startup"))不能内联
|
||||
# 在函数签名行(llc: expected string constant),必须走属性组
|
||||
# #N(function_set_attr_group)→ 由 FuncGenDef 单独提取处理。
|
||||
# section string attr cannot be inlined on the signature line (llc: expected
|
||||
# string constant); it must go through attr group #N, handled by FuncGenDef.
|
||||
if string.strcmp(attr_name, "section") == 0: continue
|
||||
# 映射到 LLVM 属性名并追加
|
||||
# Map to LLVM attr name and append
|
||||
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
|
||||
if ar != 0: found_any = 1
|
||||
|
||||
if found_any == 0: return None
|
||||
return attrs_buf
|
||||
def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""提取 @c.Attribute(t.attr.section("xxx")) 为属性组文本 '"section"="xxx"'。
|
||||
|
||||
LLVM 字符串属性(带值)必须用属性组 #N(函数行输出 #N + 函数尾
|
||||
attributes #N = { ... }),不能内联(llc: expected string constant)。
|
||||
main.vp boot 函数 t.attr.section(".text.startup") 供链接器按段放置。
|
||||
"""
|
||||
"""Extract @c.Attribute(t.attr.section(...)) as the attr-group text."""
|
||||
if pool is None or decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
@@ -123,31 +106,25 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
return sec_buf
|
||||
return None
|
||||
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
|
||||
"""从 AST 节点提取属性名
|
||||
|
||||
支持:
|
||||
- Call(func=Attribute(...)): t.attr.always_inline() -> "always_inline"
|
||||
- Attribute: t.attr.packed -> "packed"
|
||||
- Attribute(t.attr.llvm.xxx): t.attr.llvm.nounwind -> "nounwind"
|
||||
"""
|
||||
"""Extract the attribute name from a Call or Attribute AST node."""
|
||||
if node is None: return None
|
||||
k: int = node.kind()
|
||||
|
||||
# Call 节点: t.attr.always_inline()
|
||||
# Call node: t.attr.always_inline()
|
||||
if k == ast.ASTKind.Call:
|
||||
call: ast.Call* = ast.Call*(node)
|
||||
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
|
||||
at: ast.Attribute* = ast.Attribute*(call.func)
|
||||
return at.attr
|
||||
|
||||
# Attribute 节点: t.attr.packed 或 t.attr.llvm.nounwind
|
||||
# Attribute node: t.attr.packed or t.attr.llvm.nounwind
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at2: ast.Attribute* = ast.Attribute*(node)
|
||||
return at2.attr
|
||||
|
||||
return None
|
||||
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
|
||||
"""取 t.attr.xxx("val") 调用的首参字符串常量(如 section(".text.startup"))"""
|
||||
"""Return the first positional string constant of a t.attr.xxx(...) call."""
|
||||
if node is None or node.kind() != ast.ASTKind.Call: return None
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.args is None or len(cl.args) == 0: return None
|
||||
@@ -157,13 +134,10 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
if cn.const_kind != ast.CONST_STR: return None
|
||||
return cn.str_val
|
||||
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
|
||||
"""将属性名映射到 LLVM 属性名并追加到缓冲区
|
||||
|
||||
返回 1 表示已追加,0 表示不支持该属性
|
||||
"""
|
||||
"""Map an attribute name to its LLVM name and append it; 1=appended, 0=unsupported."""
|
||||
if attrs_buf is None or attr_name is None: return 0
|
||||
|
||||
# 先确定映射的 LLVM 属性名
|
||||
# First determine the mapped LLVM attr name
|
||||
llvm_name: str = None
|
||||
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
|
||||
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
|
||||
@@ -176,37 +150,32 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
|
||||
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
|
||||
else:
|
||||
# packed/aligned/section/visibility/weak 等不支持作为函数内联属性
|
||||
# packed/aligned/section/visibility/weak are unsupported as inline function attrs
|
||||
return 0
|
||||
|
||||
# 支持的属性:如果缓冲区非空,先追加空格分隔
|
||||
# Supported attr: append a space separator if the buffer is non-empty
|
||||
if attrs_buf[0] != '\0': self._append_str(attrs_buf, " ", 256)
|
||||
self._append_str(attrs_buf, llvm_name, 256)
|
||||
return 1
|
||||
def _append_str(self, buf: str, s: str, max_len: sizet = 256) -> None:
|
||||
"""将字符串追加到 buf 末尾(有界,防 strcpy 越界写穿定长缓冲)"""
|
||||
"""Append a string to buf with a bounded write to avoid overflow."""
|
||||
if buf is None or s is None: return
|
||||
# 防御:TPV 方法默认参数注册存在缺陷(缺参调用 max_len 落到 0),
|
||||
# 显式兜底为 256,避免有界写入检查误判丢弃全部属性文本。
|
||||
# Defensive: TPV default-arg registration is buggy (missing max_len falls to 0);
|
||||
# fall back to 256 so the bounded-write check does not drop all attr text.
|
||||
if max_len == 0: max_len = 256
|
||||
cur_len: sizet = string.strlen(buf)
|
||||
s_len: sizet = string.strlen(s)
|
||||
if cur_len + s_len + 1 > max_len: return # 无剩余容量:丢弃本次追加,避免越界写 存疑
|
||||
if cur_len + s_len + 1 > max_len: return # No remaining capacity: drop this append to avoid a bounds write (uncertain)
|
||||
string.strcpy(buf + cur_len, s)
|
||||
# ============================================================
|
||||
# _mangle_name - 为名称添加 SHA1 前缀
|
||||
#
|
||||
# 返回 "sha1.name" 格式的混淆名。若 ModuleSha1 为 None 或名称已带
|
||||
# SHA1 前缀(16 hex + '.'),则原样返回。
|
||||
# ============================================================
|
||||
# _mangle_name - prepend a SHA1 prefix to a name.
|
||||
# Returns "sha1.name"; if ModuleSha1 is None or the name already has a SHA1 prefix, returns as-is.
|
||||
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
|
||||
"""为任意名称添加 SHA1 前缀(不做 main/export 检查)"""
|
||||
"""Prepend the SHA1 prefix to a name (no main/export check)."""
|
||||
if trans is None or name is None: return name
|
||||
if trans.ModuleSha1 is None: return name
|
||||
name_len: sizet = string.strlen(name)
|
||||
# 已带 SHA1 前缀("{16hex}.")则跳过
|
||||
# [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`。旧判据是 `name_len > 17`,
|
||||
# 与真源的 `>= 17` 只差"前缀之后为空串"这一退化形态(无实际意义),一并对齐。
|
||||
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||
# truth GenApi.sha1_prefix_len (aligned with the ">= 17" check).
|
||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||
sha1: str = trans.ModuleSha1
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
@@ -216,18 +185,14 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# ============================================================
|
||||
# _mangle_name_with_sha1 — 用指定 SHA1 为名称添加前缀
|
||||
#
|
||||
# 用于跨模块 vtable 继承: 继承方法需要用父模块的 SHA1 做 mangling,
|
||||
# 而非当前模块的 SHA1。
|
||||
# ============================================================
|
||||
# _mangle_name_with_sha1 - prepend the given SHA1 to a name.
|
||||
# Used for cross-module vtable inheritance: inherited methods mangle with the parent module's SHA1.
|
||||
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
|
||||
"""用指定 SHA1 为名称添加前缀(不做 main/export 检查)"""
|
||||
"""Prepend the given SHA1 to a name (no main/export check)."""
|
||||
if sha1 is None or name is None: return name
|
||||
name_len: sizet = string.strlen(name)
|
||||
# 已带 SHA1 前缀("{16hex}.")则跳过
|
||||
# [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`(口径对齐同 _mangle_name)。
|
||||
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||
# truth GenApi.sha1_prefix_len (same check as _mangle_name).
|
||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
@@ -236,30 +201,18 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# ============================================================
|
||||
# _mangle_func_name - 为函数名添加 SHA1 前缀(含 main/export 检查)
|
||||
#
|
||||
# 规则:
|
||||
# - main 函数不加前缀(程序入口点)
|
||||
# - has_export 非 0 时不加前缀(t.CExport 导出函数)
|
||||
# - 已带 SHA1 前缀的不再加
|
||||
# - ModuleSha1 为 None 时不加前缀
|
||||
# ============================================================
|
||||
# _mangle_func_name - prepend a SHA1 prefix to a function name (with main/export checks).
|
||||
# No prefix for main, when has_export != 0 (t.CExport), when already prefixed, or when ModuleSha1 is None.
|
||||
def _mangle_func_name(self, trans: HT.Translator*, name: str, has_export: int) -> str:
|
||||
"""为函数名添加 SHA1 前缀(检查 main/export)"""
|
||||
"""Prepend the SHA1 prefix to a function name (checks main/export)."""
|
||||
if trans is None or name is None: return name
|
||||
if has_export != 0: return name
|
||||
if string.strcmp(name, "main") == 0: return name
|
||||
return self._mangle_name(trans, name)
|
||||
# ============================================================
|
||||
# _body_has_raise - 函数体是否含 raise(非 StopIteration)
|
||||
#
|
||||
# 对齐 TPC HandlesFunctions.py L216-243 _body_contains_raise:函数签名
|
||||
# 追加 __eh_msg_out__/__eh_code_out__ 输出参数的条件。v1 只扫函数体
|
||||
# 顶层 raise(嵌套 if/for/try 内的 raise 未覆盖,后续扩展)。
|
||||
# ============================================================
|
||||
# _body_has_raise - whether the body contains raise (non-StopIteration).
|
||||
# Matches TPC HandlesFunctions.py _body_contains_raise; v1 only scans top-level raise, not nested ones.
|
||||
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
|
||||
"""函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否"""
|
||||
"""Whether the body has a top-level non-StopIteration raise; 1=yes, 0=no."""
|
||||
if fd is None or fd.children is None: return 0
|
||||
body_f: list[ast.AST*]* = fd.children
|
||||
bn_f: sizet = len(body_f)
|
||||
@@ -281,23 +234,16 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||
if is_stop_f == 0: return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# RejectCReturnDecorator - @c.CReturn 装饰器已移除,遇到即编译期报错
|
||||
#
|
||||
# 多返回值唯一入口是返回注解 `-> tuple[ty1, ty2, ...]`(tuple → struct 通路
|
||||
# 逐字保留)。任何装饰器形态出现 CReturn 一律 fail-fast,不静默忽略。
|
||||
# 供 FuncGenDef(pre_scan 前向声明 / translate_function_def 定义两阶段)复用;
|
||||
# Converter 为模块级函数,同报文另写一份(见 StubGen/Converter.vp)。
|
||||
# 无 CReturn 返回 0。
|
||||
# ============================================================
|
||||
# RejectCReturnDecorator - @c.CReturn was removed; fail at compile time if seen.
|
||||
# Multi-return goes only through `-> tuple[...]`; any CReturn form is fail-fast, shared with FuncGenDef.
|
||||
def RejectCReturnDecorator(self, node: ast.AST*, decorators: list[ast.AST*]*) -> int:
|
||||
"""装饰器列表里出现 @c.CReturn 即 fatal_error(返回 0 = 无该装饰器)"""
|
||||
"""Fail if @c.CReturn appears in the decorators; 0=none found."""
|
||||
if decorators is None: return 0
|
||||
rcr_dn: sizet = len(decorators)
|
||||
for rcr_i in range(rcr_dn):
|
||||
rcr_deco: ast.AST* = decorators.get(rcr_i)
|
||||
if rcr_deco is None: continue
|
||||
# 兼容 @c.CReturn(...)(Call(func=Attribute))与裸 @c.CReturn(Attribute)两形态
|
||||
# Handle both @c.CReturn(...) (Call(func=Attribute)) and bare @c.CReturn (Attribute)
|
||||
rcr_attr: ast.Attribute* = None
|
||||
if rcr_deco.kind() == ast.ASTKind.Call:
|
||||
rcr_call: ast.Call* = ast.Call*(rcr_deco)
|
||||
@@ -308,11 +254,9 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` 已移除:请改用返回注解 `-> tuple[ty1, ty2, ...]`(多返回值打包为 struct 返回)", "TypeError")
|
||||
return 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# 创建 LLVM 函数(简单版本,仅声明)
|
||||
# ============================================================
|
||||
# Create LLVM function (simple version, declaration only)
|
||||
def create_function(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str, args_node: ast.AST*, ret_ty: mlirlite.LLVMType*) -> mlirlite.Function*:
|
||||
"""创建 LLVM 函数并添加参数"""
|
||||
"""Create an LLVM function and add its parameters."""
|
||||
if name is None or mod is None: return None
|
||||
|
||||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, name, ret_ty)
|
||||
@@ -327,7 +271,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
for ai in range(an):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# t\.CVoid 表示空参:跳过
|
||||
# t.CVoid means no args: skip
|
||||
if arg.annotation is not None:
|
||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
||||
at_cf: ast.Attribute* = ast.Attribute*(arg.annotation)
|
||||
@@ -336,9 +280,8 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
|
||||
if resolved is None:
|
||||
# [S4 fail-fast] 有注解但解析失败:不再静默用 i32。
|
||||
# 本函数不传 trans/imports 上下文,解析失败即调用方未补齐
|
||||
# 上下文(暴露上游,不由本函数兜底)。
|
||||
# [S4 fail-fast] Annotation present but unresolvable: no silent i32 fallback.
|
||||
# This fn lacks trans/imports context, so failure surfaces upstream.
|
||||
self.Gen.TypeGenErr.fatal_error(arg, f"函数 '{name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError")
|
||||
return None
|
||||
param_ty = resolved
|
||||
|
||||
+208
-366
File diff suppressed because it is too large.
Load diff
@@ -16,31 +16,21 @@ import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FuncGenMixin - 函数/方法声明与翻译 Mixin
|
||||
# ============================================================
|
||||
# FuncGenMixin - function/method declaration and translation Mixin
|
||||
|
||||
# 作用域类型常量 存疑
|
||||
# Scope type constant (uncertain)
|
||||
define SCOPE_FUNCTION = 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FuncGenMixin - 函数属性/命名/声明/翻译
|
||||
# ============================================================
|
||||
# FuncGenMixin - function attribute/naming/declaration/translation
|
||||
novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
"""函数/方法翻译 Mixin(纯方法化迁移,无状态字段)"""
|
||||
"""Function/method translation Mixin (stateless)"""
|
||||
|
||||
# ============================================================
|
||||
# translate_nested_function_def - 嵌套函数提升 + 闭包创建
|
||||
#
|
||||
# 将嵌套函数提升为顶层函数 @__closure_{name}(i8* %env) -> i32,
|
||||
# 在父函数中创建闭包对象 {i8* fn_ptr, i8* env_ptr} 并存储到
|
||||
# 以函数名命名的局部变量中。
|
||||
#
|
||||
# 闭包结构: {i8* fn_ptr, i8* env_ptr} (16 字节, malloc 分配)
|
||||
# Env 结构: {i8* ptr0, i8* ptr1, ...} (每个 nonlocal 变量 8 字节,指向父函数 alloca)
|
||||
# ============================================================
|
||||
# translate_nested_function_def - hoist a nested function to a top-level
|
||||
# @__closure_{name}(i8* %env) and build its closure {fn_ptr, env_ptr} in the parent.
|
||||
def translate_nested_function_def(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译嵌套函数定义:提升为顶层函数 + 创建闭包"""
|
||||
"""Translate a nested function definition: hoist to a top-level function and build a closure"""
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(node)
|
||||
if fd is None or fd.name is None: return 0
|
||||
|
||||
@@ -52,18 +42,18 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
|
||||
# 1. 构建提升后的函数名: __closure_{name}(加 SHA1 前缀避免跨模块冲突)
|
||||
# 1. Build the promoted name __closure_{name} (SHA1 prefix avoids cross-module clashes)
|
||||
promoted_name: str = pool.alloc(64)
|
||||
if promoted_name is not None: viperlib.snprintf(promoted_name, 64, "__closure_%s", func_name)
|
||||
mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, promoted_name)
|
||||
|
||||
# 2. 创建提升后的函数: define i32 @__closure_{name}(i8* %env, args...)
|
||||
# 2. Create the promoted function: define i32 @__closure_{name}(i8* %env, args...)
|
||||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, mangled_promoted, i32_ty)
|
||||
if func is None: return 0
|
||||
mlirlite.add_param(pool, func, i8_ptr_ty, "%env")
|
||||
|
||||
# 2a. 嵌套函数参数:加入提升函数签名(i8* %env, 参数...),
|
||||
# 类型存入 param_tys 缓冲(CVoid 跳过,与 alloca 创建索引对齐)
|
||||
# 2a. Nested function params join the promoted signature (i8* %env, args...);
|
||||
# types go into the param_tys buffer (CVoid skipped, aligning with alloca indices).
|
||||
imported_modules: str = trans._imported_modules
|
||||
from_imports: str = trans._from_imports
|
||||
param_tys: t.CPtr = pool.alloc(8 * 64)
|
||||
@@ -99,7 +89,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
mlirlite.add_param(pool, func, pty_n, pname_n)
|
||||
param_count += 1
|
||||
|
||||
# 3. 创建 entry 块 + builder
|
||||
# 3. Create the entry block and builder
|
||||
entry_blk: rawptr[mlirlite.BasicBlock] = mlirlite.create_block(pool, func, "entry")
|
||||
if entry_blk is None: return 0
|
||||
func_builder: mlirlite.IRBuilder* = mlirlite.new_builder(pool, func)
|
||||
@@ -109,18 +99,18 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
# --traceback: promoted nested closure gets its own DISubprogram.
|
||||
dbg_pushed_n: int = DebugInfoGen.PushFunction(pool, func_builder, func, node.lineno, node.col_offset + 1)
|
||||
|
||||
# 4. 进入嵌套函数作用域(嵌套符号表)
|
||||
# 4. Enter the nested function scope (nested symbol table)
|
||||
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION)
|
||||
|
||||
# 5. 创建 _env_ptr alloca 并存储 %env 参数
|
||||
# 5. Create the _env_ptr alloca and store the %env parameter
|
||||
env_alloca: mlirlite.Value* = mlirlite.build_alloca(func_builder, i8_ptr_ty)
|
||||
if env_alloca is not None:
|
||||
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", env_alloca)
|
||||
env_param_val: mlirlite.Value* = mlirlite.SSAValue(pool, i8_ptr_ty, "%env")
|
||||
mlirlite.build_store(func_builder, env_param_val, env_alloca)
|
||||
|
||||
# 5a. 创建嵌套函数参数 alloca(函数体内参数名可引用,如
|
||||
# accumulate(n) 内 for i in range(n)、set_result(val) 内 result = val)
|
||||
# 5a. Create alloca for nested function params so the body can reference them
|
||||
# (e.g. for i in range(n) inside accumulate(n), result = val inside set_result(val)).
|
||||
if fd.args is not None:
|
||||
ags_p: ast.Arguments* = ast.Arguments*(fd.args)
|
||||
if ags_p.args is not None:
|
||||
@@ -145,15 +135,14 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if arg_p.annotation is not None:
|
||||
cls_nm_p: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(arg_p.annotation, imported_modules, pool, trans._from_imports)
|
||||
if cls_nm_p is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, arg_p.arg, cls_nm_p)
|
||||
# const 形参:沿用 TPC 形参的历史判据(只看注解是否含
|
||||
# t.CConst,不看指针层数),两侧逐条对齐(见
|
||||
# Debugger/type-descriptors.md 已知差异)。
|
||||
# const param: keep TPC's historical test (only whether the annotation contains
|
||||
# t.CConst, not the pointer depth); both sides align per type-descriptors.md.
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(arg_p.annotation, "CConst", 0) != 0: self.Gen.VarGen.set_var_const(trans.SymTab, arg_p.arg)
|
||||
# t.CVolatile 形参:声明期标记(与 FuncGenDef 主路径同款),
|
||||
# 函数体内该参数名的 load/store 带 volatile。
|
||||
# t.CVolatile param: mark at declaration (same as the FuncGenDef main path) so
|
||||
# load/store of this param name in the body carry volatile.
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(arg_p.annotation, "CVolatile", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, arg_p.arg)
|
||||
# rawptr[t.CVolatile | T] 形参(volatile 指针指向物):
|
||||
# 函数体内该参数解引用带 volatile(与 FuncGenDef 主路径同款)。
|
||||
# rawptr[t.CVolatile | T] param (volatile pointee): dereferences in the body carry
|
||||
# volatile (same as the FuncGenDef main path).
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(arg_p.annotation) != 0: self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, arg_p.arg)
|
||||
pname2_n: str = pool.alloc(32)
|
||||
if pname2_n is not None: viperlib.snprintf(pname2_n, 32, "%%%s", arg_p.arg)
|
||||
@@ -162,25 +151,24 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
mlirlite.build_store(func_builder, p_val, p_alloca, vol_pn)
|
||||
idx_p += 1
|
||||
|
||||
# 6. 保存父函数作用域状态(仅非变量表相关)
|
||||
# 6. Save the parent function scope state (variable-table independent)
|
||||
old_func: mlirlite.Function* = trans._cur_func
|
||||
old_builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
old_func_name: str = trans._cur_func_name
|
||||
old_global_count: int = trans._global_name_count
|
||||
old_nonlocal_count: int = trans._nonlocal_name_count
|
||||
old_env_count: int = trans._closure_env_count
|
||||
# break/continue 目标与 match-case 标记:新函数作用域必须清空(见下方赋值),
|
||||
# 否则嵌套函数体内的 break/continue 会继承外层函数的跳转目标(跨函数 br)
|
||||
# break/continue targets and match-case flag must be cleared for the new scope (see
|
||||
# assignments below); otherwise a nested break/continue inherits the outer target.
|
||||
old_break_bb: rawptr[mlirlite.BasicBlock] = trans._break_bb
|
||||
old_continue_bb: rawptr[mlirlite.BasicBlock] = trans._continue_bb
|
||||
old_insw: int = trans._in_switch_case
|
||||
# nonlocal 集合独立缓冲:嵌套函数注册 nonlocal 写入共享缓冲 [0] 会
|
||||
# 覆盖父函数集合(ClosureTest2 Test 7 不同变量场景:level2 的 b 覆盖
|
||||
# level1 的 a → level1 env[0] 存错)。嵌套函数用独立缓冲,翻译完恢复。
|
||||
# Separate nonlocal buffer: sharing [0] would overwrite the parent's set (ClosureTest2
|
||||
# Test 7: level2's b clobbers level1's a). Restored after translation.
|
||||
saved_nl_names: t.CPtr = trans._nonlocal_names
|
||||
saved_nl_types: t.CPtr = trans._nonlocal_types
|
||||
|
||||
# 7. 切换到嵌套函数作用域 + 清空 scope names
|
||||
# 7. Switch to the nested function scope and clear scope names
|
||||
trans._cur_func = func
|
||||
trans._cur_builder = func_builder
|
||||
trans._cur_func_name = func_name
|
||||
@@ -188,15 +176,14 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._break_bb = None
|
||||
trans._continue_bb = None
|
||||
trans._in_switch_case = 0
|
||||
# 嵌套函数独立 nonlocal 缓冲(避免覆盖父函数集合)
|
||||
# Independent nonlocal buffers for the nested function (avoid clobbering the parent set)
|
||||
trans._nonlocal_names = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8)
|
||||
trans._nonlocal_types = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8)
|
||||
if trans._nonlocal_names is not None: string.memset(trans._nonlocal_names, 0, HT.MAX_GLOBAL_NAMES * 8)
|
||||
if trans._nonlocal_types is not None: string.memset(trans._nonlocal_types, 0, HT.MAX_GLOBAL_NAMES * 8)
|
||||
|
||||
# 8. 预扫描函数体:
|
||||
# a. 先预扫描 Global/Nonlocal 声明(Python 语义:对整个函数作用域生效)
|
||||
# b. 再预扫描 AnnAssign 为局部变量提前创建 alloca
|
||||
# 8. Pre-scan the body: (a) Global/Nonlocal declarations (Python: whole scope),
|
||||
# (b) AnnAssign locals to create their allocas early.
|
||||
body: list[ast.AST*]* = fd.children
|
||||
if body is not None:
|
||||
bn: sizet = len(body)
|
||||
@@ -207,25 +194,22 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
|
||||
|
||||
# 8c. 隐式捕获扫描:嵌套函数读取的外层函数局部变量注册为 nonlocal
|
||||
# (Python 语义,无需显式 nonlocal;否则 read 经 lookup_var
|
||||
# 跨函数命中父函数 alloca → SSA 编号错乱 → llc undefined value)。
|
||||
# 分支内 AnnAssign 局部已由 pre_scan_allocas 递归预注册
|
||||
# (StmtGenDeclMixin),本扫描经 lookup_current 自然排除。
|
||||
# 8c. Implicit capture scan: outer locals read by the nested function become nonlocal
|
||||
# (Python semantics). Branch AnnAssign locals are already pre-registered and excluded.
|
||||
if body is not None:
|
||||
bnc: sizet = len(body)
|
||||
for bic in range(bnc):
|
||||
stmtc: ast.AST* = body.get(bic)
|
||||
if stmtc is not None: self._closure_scan_captures(trans, stmtc)
|
||||
|
||||
# 9. 翻译函数体(Nonlocal 语句会填充 _nonlocal_names)
|
||||
# 9. Translate the body (Nonlocal statements fill _nonlocal_names)
|
||||
if body is not None:
|
||||
bn2: sizet = len(body)
|
||||
for bi2 in range(bn2):
|
||||
stmt2: ast.AST* = body.get(bi2)
|
||||
if stmt2 is not None: self.Gen.StmtGen.translate_stmt(trans, stmt2)
|
||||
|
||||
# 10. 隐式 ret
|
||||
# 10. Implicit return
|
||||
last_is_return: int = 0
|
||||
if body is not None:
|
||||
bn3: sizet = len(body)
|
||||
@@ -236,10 +220,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
zero_val: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
mlirlite.build_ret(func_builder, zero_val)
|
||||
|
||||
# 11. 保存嵌套函数的 nonlocal 信息
|
||||
# 11. Save the nested function's nonlocal info
|
||||
nested_nonlocal_count: int = trans._nonlocal_name_count
|
||||
|
||||
# 12. 退出嵌套函数作用域,回到父函数作用域(保留 _nonlocal_names 用于 env 创建)
|
||||
# 12. Leave the nested function scope, back to the parent (keep _nonlocal_names for env)
|
||||
self.Gen.VarGen.exit_scope(trans.SymTab)
|
||||
trans._cur_func = old_func
|
||||
trans._cur_builder = old_builder
|
||||
@@ -247,12 +231,12 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._continue_bb = old_continue_bb
|
||||
trans._in_switch_case = old_insw
|
||||
if dbg_pushed_n != 0: DebugInfoGen.PopFunction(func_builder)
|
||||
# NOTE: _nonlocal_names 和 _nonlocal_name_count 仍为嵌套函数的值
|
||||
# NOTE: _nonlocal_names and _nonlocal_name_count still hold the nested function's values
|
||||
|
||||
# 13. 在父函数中创建闭包对象
|
||||
# 13. Create the closure object in the parent function
|
||||
parent_builder: mlirlite.IRBuilder* = old_builder
|
||||
if parent_builder is None:
|
||||
# 无 builder(模块级嵌套函数?)→ 无法创建闭包,仅恢复状态
|
||||
# No builder (module-level nested function?) - cannot create a closure, only restore state
|
||||
trans._cur_func_name = old_func_name
|
||||
trans._global_name_count = old_global_count
|
||||
trans._nonlocal_name_count = old_nonlocal_count
|
||||
@@ -261,8 +245,8 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._nonlocal_types = saved_nl_types
|
||||
return 0
|
||||
|
||||
# 13a. 创建 env: malloc(8 * nested_nonlocal_count) — env 存储指针数组
|
||||
# 每个 nonlocal 变量占 8 字节(i8*),指向父函数的 alloca(共享存储)
|
||||
# 13a. Create env: malloc(8 * nested_nonlocal_count) - an array of pointers, one
|
||||
# 8-byte i8* slot per nonlocal variable pointing at the parent's alloca (shared).
|
||||
env_ptr: mlirlite.Value* = None
|
||||
if nested_nonlocal_count > 0:
|
||||
env_size: int64 = nested_nonlocal_count * 8
|
||||
@@ -271,28 +255,19 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
env_size_val.Next = None
|
||||
env_ptr = mlirlite.build_call(parent_builder, "malloc", env_size_val, 1, i8_ptr_ty, 0)
|
||||
|
||||
# 为每个 nonlocal 变量建立堆存储并注册到 env:
|
||||
# 运行时 env 链式共享(无需编译期映射——映射存编译期 Value 对象
|
||||
# 地址会被 inttoptr 当运行时堆地址解引用崩溃;存整型也拿不到
|
||||
# 运行时 malloc 结果):
|
||||
# - 变量是父函数局部(lookup_current 在当前作用域找到 alloca)
|
||||
# → 新建堆(malloc + 拷贝当前值),并把父函数 VarEntry.Alloca
|
||||
# 重定向到堆——父函数后续读写走共享堆
|
||||
# - 变量是父函数的 nonlocal(多层嵌套,当前作用域无 alloca)
|
||||
# → 从父函数 env 读该变量的堆地址(_load_raw_var_ptr),
|
||||
# env[ni] = 同一堆地址(运行时链式共享,各层写同一堆)
|
||||
# Build heap storage per nonlocal and register it into env (runtime env chaining, no
|
||||
# compile-time map): parent local -> new heap + redirect; parent nonlocal -> reuse heap.
|
||||
if env_ptr is not None:
|
||||
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
|
||||
for ni in range(nested_nonlocal_count):
|
||||
# 从 _nonlocal_names 缓冲区读取名称
|
||||
# Read the name from the _nonlocal_names buffer
|
||||
name_addr: uint64 = uint64(trans._nonlocal_names) + ni * 8
|
||||
nl_slot: str* = (str*)(name_addr)
|
||||
nl_name: str = nl_slot[0]
|
||||
if nl_name is None: continue
|
||||
heap_store: mlirlite.Value* = None
|
||||
# 1. 优先查全局堆地址映射(_prepare_chain_nonlocals 已为
|
||||
# 链内 nonlocal 建堆并 store 到 @nlheap.*;不同层捕获
|
||||
# 不同变量时(ClosureTest2 Test 7)level2 的 b 经此共享)
|
||||
# 1. Prefer the global heap-address map (_prepare_chain_nonlocals already built heaps
|
||||
# for chain nonlocals and stored @nlheap.*; shared across layers, e.g. Test 7).
|
||||
gname_ck: str = self._nl_heap_global_name(pool, trans, nl_name)
|
||||
if gname_ck is not None and self._nl_heap_global_exists(mod, gname_ck) != 0:
|
||||
gv_ref0: mlirlite.Value* = self._nl_heap_global_ref(pool, trans, nl_name)
|
||||
@@ -300,30 +275,30 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if heap_store is None:
|
||||
local_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, nl_name)
|
||||
if local_alloca is not None and local_alloca.Ty is not None:
|
||||
# 父函数局部变量:新建堆存储 + 拷贝当前值 + 重定向
|
||||
# Parent local: new heap storage + copy current value + redirect
|
||||
nl_load_ty: mlirlite.LLVMType* = local_alloca.Ty.Pointee
|
||||
if nl_load_ty is not None:
|
||||
cur_val_nl: mlirlite.Value* = mlirlite.build_load(parent_builder, nl_load_ty, local_alloca)
|
||||
# 堆存储大小按类型字节数:数组(如 [4 x i32]=16 字节)
|
||||
# 不能固定 8 字节(越界写崩溃);指针/标量 ≥ 8 字节
|
||||
# Heap size uses the type's byte size: arrays (e.g. [4 x i32]=16 bytes) must not be
|
||||
# fixed at 8 (out-of-bounds write); pointers/scalars are >= 8 bytes.
|
||||
st_size: int = self._type_byte_size(pool, nl_load_ty)
|
||||
st_size_val: mlirlite.Value* = mlirlite.const_int64(pool, st_size)
|
||||
if st_size_val is not None: st_size_val.Next = None
|
||||
var_storage: mlirlite.Value* = mlirlite.build_call(parent_builder, "malloc", st_size_val, 1, i8_ptr_ty, 0)
|
||||
if var_storage is not None:
|
||||
# 拷贝当前值到堆存储
|
||||
# Copy the current value to heap storage
|
||||
st_slot_nl: mlirlite.Value* = mlirlite.build_bitcast(parent_builder, var_storage, mlirlite.Ptr(pool, nl_load_ty))
|
||||
if st_slot_nl is not None and cur_val_nl is not None: mlirlite.build_store(parent_builder, cur_val_nl, st_slot_nl)
|
||||
# 父函数 VarEntry.Alloca 重定向到堆(父函数读写走共享堆)
|
||||
# Redirect the parent's VarEntry.Alloca to the heap (parent reads/writes share it)
|
||||
ve_nl: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nl_name)
|
||||
heap_typed: mlirlite.Value* = mlirlite.build_bitcast(parent_builder, var_storage, mlirlite.Ptr(pool, nl_load_ty))
|
||||
if ve_nl is not None and heap_typed is not None: ve_nl.Alloca = heap_typed
|
||||
# 记录到全局映射(后续层/父函数可见)
|
||||
# Record into the global map (visible to later layers/parent)
|
||||
gv_ref_new: mlirlite.Value* = None
|
||||
if gname_ck is not None and self._nl_heap_global_exists(mod, gname_ck) == 0:
|
||||
gv_new: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, gname_ck, i8_ptr_ty)
|
||||
if gv_new is not None:
|
||||
# 清除 external linkage + 零初始化(防 undefined reference)
|
||||
# Clear external linkage + zero-init (prevents undefined reference)
|
||||
gv_new.Linkage = None
|
||||
gv_new.Initializer = "null"
|
||||
mlirlite.module_add_global(mod, gv_new)
|
||||
@@ -331,23 +306,23 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if gv_ref_new is not None: mlirlite.build_store(parent_builder, var_storage, gv_ref_new)
|
||||
heap_store = var_storage
|
||||
else:
|
||||
# 父函数中该变量是 nonlocal(多层嵌套):从父函数 env
|
||||
# 读堆地址(运行时链式共享同一堆),不新建
|
||||
# In the parent this variable is nonlocal (multi-level nesting): read the heap
|
||||
# address from the parent env (chained sharing), do not allocate.
|
||||
heap_store = self.Gen.StmtGenDecl._load_raw_var_ptr(trans, nl_name)
|
||||
if heap_store is None: continue
|
||||
# env[ni] = 堆存储地址(共享)
|
||||
# env[ni] = heap storage address (shared)
|
||||
offset_val: mlirlite.Value* = mlirlite.const_int32(pool, ni * 8)
|
||||
slot: mlirlite.Value* = mlirlite.build_gep(parent_builder, i8_ty, env_ptr, offset_val)
|
||||
if slot is not None:
|
||||
slot_typed: mlirlite.Value* = mlirlite.build_bitcast(parent_builder, slot, i8_ptr_ptr_ty)
|
||||
if slot_typed is not None: mlirlite.build_store(parent_builder, heap_store, slot_typed)
|
||||
|
||||
# 13b. 创建闭包结构: malloc(16)
|
||||
# 13b. Create the closure struct: malloc(16)
|
||||
closure_size_val: mlirlite.Value* = mlirlite.const_int64(pool, 16)
|
||||
if closure_size_val is not None: closure_size_val.Next = None
|
||||
closure_ptr: mlirlite.Value* = mlirlite.build_call(parent_builder, "malloc", closure_size_val, 1, i8_ptr_ty, 0)
|
||||
if closure_ptr is None:
|
||||
# malloc 失败,恢复状态
|
||||
# malloc failed, restore state
|
||||
trans._cur_func_name = old_func_name
|
||||
trans._global_name_count = old_global_count
|
||||
trans._nonlocal_name_count = old_nonlocal_count
|
||||
@@ -356,26 +331,25 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._nonlocal_types = saved_nl_types
|
||||
return 0
|
||||
|
||||
# 13c. 存储 fn_ptr 到 offset 0
|
||||
# 创建函数指针类型 i32(i8*)*
|
||||
# 13c. Store fn_ptr at offset 0; create the function pointer type i32(i8*)*.
|
||||
fn_param_node: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
|
||||
fn_func_ty: mlirlite.LLVMType* = mlirlite.Func(pool, i32_ty, fn_param_node, 1)
|
||||
fn_func_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, fn_func_ty)
|
||||
|
||||
# 创建函数引用 Value(类型为 i32(i8*)*,使用 SHA1 混淆名)
|
||||
# Create the function reference Value (type i32(i8*)*, using the SHA1 mangled name)
|
||||
fn_ptr_name: str = pool.alloc(64)
|
||||
if fn_ptr_name is not None:
|
||||
# SHA1 前缀名含 '.' 需要加引号(如 @"sha1.__closure_func")
|
||||
# SHA1-prefixed names contain '.' so they need quoting (e.g. @"sha1.__closure_func")
|
||||
if string.strchr(mangled_promoted, '.') is not None: viperlib.snprintf(fn_ptr_name, 64, "@\"%s\"", mangled_promoted)
|
||||
else: viperlib.snprintf(fn_ptr_name, 64, "@%s", mangled_promoted)
|
||||
fn_ptr_val: mlirlite.Value* = mlirlite.SSAValue(pool, fn_func_ptr_ty, fn_ptr_name)
|
||||
|
||||
# bitcast 函数指针到 i8*(闭包存储 i8* 类型)
|
||||
# bitcast the function pointer to i8* (the closure stores i8*)
|
||||
fn_as_i8ptr: mlirlite.Value* = mlirlite.build_bitcast(parent_builder, fn_ptr_val, i8_ptr_ty)
|
||||
fn_slot: mlirlite.Value* = mlirlite.build_bitcast(parent_builder, closure_ptr, mlirlite.Ptr(pool, i8_ptr_ty))
|
||||
if fn_slot is not None and fn_as_i8ptr is not None: mlirlite.build_store(parent_builder, fn_as_i8ptr, fn_slot)
|
||||
|
||||
# 13d. 存储 env_ptr 到 offset 8
|
||||
# 13d. Store env_ptr at offset 8
|
||||
eight_val: mlirlite.Value* = mlirlite.const_int32(pool, 8)
|
||||
env_slot_addr: mlirlite.Value* = mlirlite.build_gep(parent_builder, i8_ty, closure_ptr, eight_val)
|
||||
if env_slot_addr is not None:
|
||||
@@ -383,20 +357,20 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if env_slot is not None:
|
||||
if env_ptr is not None: mlirlite.build_store(parent_builder, env_ptr, env_slot)
|
||||
else:
|
||||
# 无 nonlocal 变量,存储 null
|
||||
# No nonlocal variables, store null
|
||||
null_val: mlirlite.Value* = mlirlite.SSAValue(pool, i8_ptr_ty, "null")
|
||||
mlirlite.build_store(parent_builder, null_val, env_slot)
|
||||
|
||||
# 13e. 将闭包指针存储到父函数的局部变量 {func_name}
|
||||
# 13e. Store the closure pointer into the parent's local variable {func_name}
|
||||
closure_alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(parent_builder, i8_ptr_ty)
|
||||
if closure_alloca is not None:
|
||||
mlirlite.build_store(parent_builder, closure_ptr, closure_alloca)
|
||||
self.Gen.VarGen.define_var(trans.SymTab, func_name, closure_alloca)
|
||||
# 注册闭包变量名(供调用时区分闭包 vs 函数指针变量,
|
||||
# 两者 alloca 类型同为 i8**,_is_func_ptr_alloca 无法区分)
|
||||
# Register the closure variable name so calls can tell a closure from a plain function
|
||||
# pointer variable (both allocas are i8**, which _is_func_ptr_alloca cannot distinguish).
|
||||
self.Gen.CallGenLeafKit.register_closure_name(func_name)
|
||||
|
||||
# 14. 恢复 scope names(含 nonlocal 集合指针)
|
||||
# 14. Restore scope names (including the nonlocal set pointers)
|
||||
trans._cur_func_name = old_func_name
|
||||
trans._global_name_count = old_global_count
|
||||
trans._nonlocal_name_count = old_nonlocal_count
|
||||
@@ -406,35 +380,31 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
|
||||
return 1
|
||||
|
||||
# _closure_scan_captures - 递归扫描嵌套函数体中的隐式捕获变量
|
||||
# Python 语义:嵌套函数读取外层函数局部变量无需显式 nonlocal。仅按显式 nonlocal 注册
|
||||
# 捕获时,无 nonlocal 的 read 会在翻译时经 lookup_var 跨函数命中父函数 alloca(SSA 编号
|
||||
# 属于父函数 → llc "use of undefined value")。故对嵌套 def 做等价预扫描:把"读取的外层
|
||||
# 函数局部变量"注册为 nonlocal(复用 env 共享堆机制),使 read 走 _load_raw_var_ptr 从堆读。
|
||||
# 注意:注册顺序必须与 step 13 的 env 构建一致(本扫描在 body 翻译前执行,顺序稳定)。
|
||||
# _closure_scan_captures - recursively register implicit captures of the nested body.
|
||||
# Order must match step 13's env build; runs before body translation, so it is stable.
|
||||
def _closure_scan_captures(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""递归扫描节点中的隐式捕获并注册为 nonlocal"""
|
||||
"""Recursively register implicit captures in a nested body as nonlocal"""
|
||||
if node is None or trans is None: return 1
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is None: return 1
|
||||
# 当前作用域已定义(参数 / AnnAssign 局部,含分支内)→ 非捕获
|
||||
# Defined in the current scope (param / AnnAssign local, incl. branches) - not a capture
|
||||
if self.Gen.VarGen.lookup_current(trans.SymTab, nm.id) is not None: return 1
|
||||
# 已注册 nonlocal(显式 nonlocal 语句 / 已捕获)→ 跳过
|
||||
# Already a registered nonlocal (explicit statement / already captured) - skip
|
||||
if GenApi.is_nonlocal_name(trans, nm.id) != 0: return 1
|
||||
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
if ve is None or ve.Alloca is None or ve.Alloca.Ty is None: return 1
|
||||
nl_ty: mlirlite.LLVMType* = ve.Alloca.Ty.Pointee
|
||||
if nl_ty is None: return 1
|
||||
# 模块级全局直接解析,无需 env 捕获
|
||||
# Module-level globals resolve directly, no env capture needed
|
||||
mod_gv: mlirlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id)
|
||||
if mod_gv is not None:
|
||||
if uint64(mod_gv) == uint64(ve.Alloca): return 1
|
||||
# 外层函数局部变量 → 隐式捕获(注册为 nonlocal)
|
||||
# Outer-function local -> implicit capture (register as nonlocal)
|
||||
GenApi.add_nonlocal_name(trans, nm.id, nl_ty)
|
||||
return 1
|
||||
# ---- 语句 ----
|
||||
# ---- Statements ----
|
||||
if k == ast.ASTKind.Assign:
|
||||
asn: ast.Assign* = ast.Assign*(node)
|
||||
if asn.value is not None: self._closure_scan_captures(trans, asn.value)
|
||||
@@ -445,7 +415,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
return 1
|
||||
if k == ast.ASTKind.AugAssign:
|
||||
aun: rawptr[ast.AugAssign] = rawptr[ast.AugAssign](node)
|
||||
# 目标同时是读(先读旧值),参与捕获
|
||||
# The target is also read (reads the old value), so it participates in capture
|
||||
if aun.target is not None: self._closure_scan_captures(trans, aun.target)
|
||||
if aun.value is not None: self._closure_scan_captures(trans, aun.value)
|
||||
return 1
|
||||
@@ -461,7 +431,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
return 1
|
||||
if k == ast.ASTKind.For:
|
||||
for_n: ast.For* = ast.For*(node)
|
||||
# 循环目标是新局部变量,不参与捕获
|
||||
# The loop target is a new local, not captured
|
||||
if for_n.iter is not None: self._closure_scan_captures(trans, for_n.iter)
|
||||
if for_n.children is not None:
|
||||
bn_f: sizet = len(for_n.children)
|
||||
@@ -546,7 +516,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
bn_mcb: sizet = len(mc_c.children)
|
||||
for bi_mcb in range(bn_mcb): self._closure_scan_captures(trans, mc_c.children.get(bi_mcb))
|
||||
return 1
|
||||
# ---- 表达式 ----
|
||||
# ---- Expressions ----
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
if bop.left is not None: self._closure_scan_captures(trans, bop.left)
|
||||
@@ -698,16 +668,12 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
return 1
|
||||
|
||||
|
||||
# _type_byte_size - 计算 LLVM 类型占用的字节大小(递归)
|
||||
# 用于 nonlocal 共享堆存储分配:数组(如 [4 x i32]=16 字节)不能用固定 8 字节
|
||||
# (越界写破坏 canary/邻块 → 运行时崩溃)。
|
||||
# 注:`case _`(Void/Struct/Func 等)仍返回 8——不是静默兜底,而是**算不出**
|
||||
# (结构体尺寸需查布局表,本函数只做 LLVMType 递归);改 fatal 会拒绝
|
||||
# "nonlocal 结构体"这一合法用法,需先补布局查询能力。
|
||||
# _type_byte_size - compute an LLVM type's byte size recursively (for nonlocal shared
|
||||
# heap: arrays must not be fixed at 8). `case _` returns 8 because size is unknown.
|
||||
def _type_byte_size(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> int:
|
||||
"""计算 LLVM 类型占用的字节大小(递归)"""
|
||||
"""Compute an LLVM type's byte size recursively"""
|
||||
if ty is None:
|
||||
# [S4 fail-fast] 类型缺失 → 不再保守 8 字节(分配不足会越界写邻块)
|
||||
# [S4 fail-fast] Missing type -> no longer assume 8 bytes (under-allocation overruns neighbors)
|
||||
self.Gen.TypeGenErr.fatal_error(None, "nonlocal 共享存储:类型缺失,无法计算尺寸", "TypeError")
|
||||
return 0
|
||||
match ty:
|
||||
@@ -728,13 +694,9 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
return 8
|
||||
|
||||
# ============================================================
|
||||
# _nl_heap_global_name - nonlocal 堆地址全局映射变量名
|
||||
#
|
||||
# 变量名: nlheap_{sha1}.{name}(全局变量,跨函数可见;SSA 编号是函数
|
||||
# 局部的,跨函数传递堆地址只能经全局变量/模块级映射)。
|
||||
# ============================================================
|
||||
# _nl_heap_global_name - global map name nlheap_{sha1}.{name} for a nonlocal heap address
|
||||
def _nl_heap_global_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, name: str) -> str:
|
||||
"""构造 nonlocal 堆地址全局映射变量名 nlheap_{sha1}.{name}"""
|
||||
"""Build the nonlocal heap-address global map name nlheap_{sha1}.{name}"""
|
||||
if pool is None or trans is None or name is None: return None
|
||||
mangled: str = self.Gen.FuncGenBase._mangle_name(trans, name)
|
||||
buf: str = pool.alloc(160)
|
||||
@@ -742,10 +704,9 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
return buf
|
||||
|
||||
# ============================================================
|
||||
# _nl_heap_global_ref - 全局映射变量引用 Value(i8**)
|
||||
# ============================================================
|
||||
# _nl_heap_global_ref - reference Value (i8**) for the global map variable
|
||||
def _nl_heap_global_ref(self, pool: memhub.MemBuddy*, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||
"""获取 nonlocal 堆地址全局映射变量的引用(@"..." 引号包裹)"""
|
||||
"""Get a reference Value for the nonlocal heap-address global map variable"""
|
||||
if pool is None or trans is None or name is None: return None
|
||||
gname: str = self._nl_heap_global_name(pool, trans, name)
|
||||
if gname is None: return None
|
||||
@@ -757,10 +718,9 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
return mlirlite.SSAValue(pool, mlirlite.Ptr(pool, i8_ptr_ty), ref_buf)
|
||||
|
||||
# ============================================================
|
||||
# _nl_heap_global_exists - 检查 nonlocal 堆地址全局映射变量是否存在
|
||||
# ============================================================
|
||||
# _nl_heap_global_exists - check whether the global map variable already exists
|
||||
def _nl_heap_global_exists(self, mod: mlirlite.LLVMModule*, gname: str) -> int:
|
||||
"""检查全局映射变量 gname 是否已在模块中,1=存在"""
|
||||
"""Return 1 if the global map variable gname already exists in the module"""
|
||||
if mod is None or gname is None: return 0
|
||||
gcur: mlirlite.GlobalVariable* = mod.GlobalHead
|
||||
while gcur is not None:
|
||||
@@ -769,13 +729,9 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _collect_chain_nonlocals - 递归收集嵌套函数链内 nonlocal 声明
|
||||
#
|
||||
# 遍历 node(FunctionDef)的 children:遇嵌套 FunctionDef 递归,
|
||||
# 遇 Nonlocal 语句收集变量名(去重)。返回更新后的计数。
|
||||
# ============================================================
|
||||
# _collect_chain_nonlocals - recursively gather nonlocal names in the nested chain (deduped)
|
||||
def _collect_chain_nonlocals(self, trans: HT.Translator*, node: ast.AST*, names: t.CPtr, count: int) -> int:
|
||||
"""递归收集 node(FunctionDef)链内所有 nonlocal 声明名(去重)"""
|
||||
"""Recursively collect all nonlocal declaration names in a nested FunctionDef chain (deduped)"""
|
||||
if node is None or names is None: return count
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(node)
|
||||
if fd is None or fd.children is None: return count
|
||||
@@ -807,21 +763,13 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
# ============================================================
|
||||
# _prepare_chain_nonlocals - 预扫描嵌套函数链内 nonlocal 并建堆
|
||||
#
|
||||
# 在翻译嵌套函数定义(translate_nested_function_def)之前调用。
|
||||
# 场景(ClosureTest2 Test 7):level1→a、level2→b、level3→c_val 各自
|
||||
# nonlocal 不同变量,都定义在最外层函数——level1 env 无 b 槽,level2
|
||||
# 无法经 env 链获取 b 的堆。此处在最外层函数中为链内所有 nonlocal
|
||||
# 建堆 + 重定向 VarEntry + 把堆地址 store 到模块级映射
|
||||
# @nlheap.{sha1}.{name}(全局变量跨函数可见),内层闭包创建时从映射 load。
|
||||
# ============================================================
|
||||
# _prepare_chain_nonlocals - pre-scan the nested chain's nonlocals and build heaps.
|
||||
# Called before translate_nested_function_def so inner closures can load from @nlheap.*.
|
||||
def _prepare_chain_nonlocals(self, trans: HT.Translator*, node: ast.AST*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, i8_ty: mlirlite.LLVMType*, i8_ptr_ty: mlirlite.LLVMType*) -> int:
|
||||
"""预收集嵌套函数链内 nonlocal 并建堆,1 成功"""
|
||||
"""Pre-collect nonlocals in a nested function chain and build their heaps; returns 1 on success"""
|
||||
if node is None or trans is None or builder is None or mod is None: return 0
|
||||
# 每函数只预建一次:多个嵌套函数定义共享同一堆(防重复 malloc +
|
||||
# 二次重定向 VarEntry.Alloca 导致兄弟闭包各持一堆,计数不同步)
|
||||
# Build only once per function so sibling nested defs share one heap (avoids double
|
||||
# malloc and double VarEntry.Alloca redirect desyncing counters).
|
||||
if trans._nl_chain_done != 0: return 1
|
||||
if trans._nl_chain_names is None:
|
||||
trans._nl_chain_names = pool.alloc(8 * 64)
|
||||
@@ -833,17 +781,15 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
nm_slot: str* = str*(uint64(trans._nl_chain_names) + ci * 8)
|
||||
nl_name: str = nm_slot[0]
|
||||
if nl_name is None: continue
|
||||
# 只在"定义它的函数"(当前顶层作用域有 alloca)中建堆
|
||||
# Build the heap only in the defining function (current top scope has an alloca)
|
||||
local_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, nl_name)
|
||||
if local_alloca is None or local_alloca.Ty is None: continue
|
||||
nl_ty: mlirlite.LLVMType* = local_alloca.Ty.Pointee
|
||||
if nl_ty is None: continue
|
||||
gname: str = self._nl_heap_global_name(pool, trans, nl_name)
|
||||
if gname is None: continue
|
||||
# 建堆(按类型字节大小)并拷贝当前值——每次本函数执行都新建共享堆,并 store 到
|
||||
# 模块级映射(跨函数可见)。不能因映射变量已存在就跳过:跳过会让本函数运行时
|
||||
# @nlheap.* 保持上一函数的堆地址,env 读到错堆。映射变量仅首次 add(模块去重),
|
||||
# store 指令每次照发。
|
||||
# Build the heap and copy the current value each time this function runs, then store
|
||||
# into the module map; do not skip just because the map variable exists.
|
||||
st_size: int = self._type_byte_size(pool, nl_ty)
|
||||
st_size_val: mlirlite.Value* = mlirlite.const_int64(pool, st_size)
|
||||
if st_size_val is not None: st_size_val.Next = None
|
||||
@@ -852,12 +798,12 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
cur_val: mlirlite.Value* = mlirlite.build_load(builder, nl_ty, local_alloca)
|
||||
st_slot: mlirlite.Value* = mlirlite.build_bitcast(builder, var_storage, mlirlite.Ptr(pool, nl_ty))
|
||||
if st_slot is not None and cur_val is not None: mlirlite.build_store(builder, cur_val, st_slot)
|
||||
# 重定向 VarEntry(父函数读写走共享堆)
|
||||
# Redirect VarEntry so the parent reads/writes the shared heap
|
||||
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nl_name)
|
||||
if ve is not None:
|
||||
heap_typed: mlirlite.Value* = mlirlite.build_bitcast(builder, var_storage, mlirlite.Ptr(pool, nl_ty))
|
||||
if heap_typed is not None: ve.Alloca = heap_typed
|
||||
# 创建/复用全局映射变量并 store 堆地址(跨函数可见)
|
||||
# Create/reuse the global map variable and store the heap address (cross-function visible)
|
||||
gv_found: mlirlite.GlobalVariable* = None
|
||||
gcur: mlirlite.GlobalVariable* = mod.GlobalHead
|
||||
while gcur is not None:
|
||||
@@ -868,8 +814,8 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if gv_found is None:
|
||||
gv_found = mlirlite.new_global_variable(pool, gname, i8_ptr_ty)
|
||||
if gv_found is not None:
|
||||
# 清除 external linkage 并给零初始化,否则打印为 external
|
||||
# 声明、store 引用无定义体 → 链接 undefined reference
|
||||
# Clear external linkage and zero-init, otherwise it prints as an external
|
||||
# declaration with no definition -> link-time undefined reference.
|
||||
gv_found.Linkage = None
|
||||
gv_found.Initializer = "null"
|
||||
mlirlite.module_add_global(mod, gv_found)
|
||||
@@ -878,19 +824,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if gv_ref is not None: mlirlite.build_store(builder, var_storage, gv_ref)
|
||||
return 1
|
||||
|
||||
# ============================================================
|
||||
# translate_lambda - 翻译 lambda 表达式
|
||||
#
|
||||
# 与 translate_nested_function_def 同架构(TPV 闭包 {i8* fn_ptr,
|
||||
# i8* env_ptr},16 字节):
|
||||
# 1. 提升函数 __lambda_{id}(i8* %env, [args...]) — 恒返回 i32
|
||||
# 2. 捕获变量按值拷贝到堆存储(env[i] = 堆地址),避免闭包返回
|
||||
# 后父栈帧销毁导致悬垂
|
||||
# 3. 创建闭包 {fn_ptr, env_ptr},返回 i8* 闭包指针
|
||||
# 赋值语句据此创建 i8** alloca,调用走 translate_closure_call
|
||||
# ============================================================
|
||||
# translate_lambda - translate a lambda into a closure, same architecture as
|
||||
# translate_nested_function_def: hoisted __lambda_{id}, captured heap slots, {fn,env}.
|
||||
def translate_lambda(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""翻译 lambda 表达式,返回闭包指针(i8*)"""
|
||||
"""Translate a lambda expression and return the closure pointer (i8*)"""
|
||||
if node is None or trans is None or builder is None: return None
|
||||
lam: rawptr[ast.Lambda] = rawptr[ast.Lambda](node)
|
||||
if lam is None: return None
|
||||
@@ -900,10 +837,8 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
|
||||
|
||||
# 1. lambda id + 提升函数名
|
||||
# 注意:计数器放 trans(_lambda_count)而非 self——Mixin 类自身无字段,
|
||||
# self.新字段在 TPC 编译 Mixin 时查 class_members 失败触发未完成的
|
||||
# _PreRegisterClassMembers fallback 崩溃(FuncGen.py L437)
|
||||
# 1. Lambda id + promoted function name. The counter lives on trans (_lambda_count):
|
||||
# a new self field would crash TPC's Mixin class_members lookup (FuncGen.py L437).
|
||||
lambda_id: int = trans._lambda_count
|
||||
trans._lambda_count += 1
|
||||
pname_buf: str = pool.alloc(64)
|
||||
@@ -911,11 +846,11 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
viperlib.snprintf(pname_buf, 64, "__lambda_%d", lambda_id)
|
||||
mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, pname_buf)
|
||||
|
||||
# 2. 解析参数(名称 + 类型,类型默认 i32,有注解则解析)
|
||||
param_names: str = pool.alloc(32 * 64) # 64 参数 × 32 字节名
|
||||
# 2. Resolve params (name + type; default i32, resolved from the annotation)
|
||||
param_names: str = pool.alloc(32 * 64) # 64 params x 32-byte names
|
||||
if param_names is None: return None
|
||||
string.memset(param_names, 0, 32 * 64)
|
||||
param_tys: t.CPtr = pool.alloc(8 * 64) # LLVMType 指针槽
|
||||
param_tys: t.CPtr = pool.alloc(8 * 64) # LLVMType pointer slots
|
||||
if param_tys is None: return None
|
||||
string.memset(param_tys, 0, 8 * 64)
|
||||
param_count: int = 0
|
||||
@@ -933,9 +868,9 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if arg_n.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg_n.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if resolved is None:
|
||||
# fail-fast:lambda 参数有注解但类型解析失败,不再静默保持 i32
|
||||
# fail-fast: annotated lambda param failed to resolve; do not silently keep i32
|
||||
self.Gen.TypeGenErr.fatal_error(arg_n, f"参数 '{arg_n.arg}' 类型解析失败: {self.Gen.TypeGenResolve.annotation_to_str(arg_n.annotation)}", "NameError")
|
||||
resolved = i32_ty # 不可达(fatal_error 已退出)
|
||||
resolved = i32_ty # unreachable (fatal_error already exited)
|
||||
if resolved is not None:
|
||||
match resolved:
|
||||
case mlirlite.LLVMType.Struct(_, _, _):
|
||||
@@ -948,19 +883,19 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
ty_slot_p[0] = p_ty
|
||||
param_count += 1
|
||||
|
||||
# 3. 收集捕获变量(body 中的外部 Name 引用)
|
||||
cap_names: str = pool.alloc(64 * 64) # 64 捕获 × 64 字节名
|
||||
# 3. Collect captured variables (outer Name references in the body)
|
||||
cap_names: str = pool.alloc(64 * 64) # 64 captures x 64-byte names
|
||||
if cap_names is None: return None
|
||||
string.memset(cap_names, 0, 64 * 64)
|
||||
cap_allocas: t.CPtr = pool.alloc(8 * 64) # 父函数 alloca 指针槽
|
||||
cap_allocas: t.CPtr = pool.alloc(8 * 64) # Parent alloca pointer slots
|
||||
if cap_allocas is None: return None
|
||||
string.memset(cap_allocas, 0, 8 * 64)
|
||||
cap_tys: t.CPtr = pool.alloc(8 * 64) # 捕获变量类型槽
|
||||
cap_tys: t.CPtr = pool.alloc(8 * 64) # Capture variable type slots
|
||||
if cap_tys is None: return None
|
||||
string.memset(cap_tys, 0, 8 * 64)
|
||||
cap_count: int = self._lambda_scan_body(trans, lam.body, param_names, param_count, cap_names, cap_allocas, cap_tys, 0)
|
||||
|
||||
# 4. 创建提升函数 __lambda_{id}(i8* %env, [args...])
|
||||
# 4. Create the promoted function __lambda_{id}(i8* %env, [args...])
|
||||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, mangled_promoted, i32_ty)
|
||||
if func is None: return None
|
||||
mlirlite.add_param(pool, func, i8_ptr_ty, "%env")
|
||||
@@ -974,7 +909,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
viperlib.snprintf(pname_l, 32, "%%%s", param_names + pi2 * 32)
|
||||
mlirlite.add_param(pool, func, p_ty2, pname_l)
|
||||
|
||||
# 5. entry 块 + builder
|
||||
# 5. Entry block + builder
|
||||
entry_blk: rawptr[mlirlite.BasicBlock] = mlirlite.create_block(pool, func, "entry")
|
||||
if entry_blk is None: return None
|
||||
func_builder: mlirlite.IRBuilder* = mlirlite.new_builder(pool, func)
|
||||
@@ -984,25 +919,25 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
# --traceback: promoted lambda gets its own DISubprogram.
|
||||
dbg_pushed_l: int = DebugInfoGen.PushFunction(pool, func_builder, func, node.lineno, node.col_offset + 1)
|
||||
|
||||
# 6. 进入 lambda 作用域
|
||||
# 6. Enter the lambda scope
|
||||
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION)
|
||||
|
||||
# 7. _env_ptr alloca(存储 %env 参数)
|
||||
# 7. _env_ptr alloca (stores the %env parameter)
|
||||
env_alloca: mlirlite.Value* = mlirlite.build_alloca(func_builder, i8_ptr_ty)
|
||||
if env_alloca is not None:
|
||||
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", env_alloca)
|
||||
env_param_val: mlirlite.Value* = mlirlite.SSAValue(pool, i8_ptr_ty, "%env")
|
||||
mlirlite.build_store(func_builder, env_param_val, env_alloca)
|
||||
|
||||
# 8. 保存父函数状态并切换到提升函数
|
||||
# 8. Save parent state and switch to the promoted function
|
||||
old_func: mlirlite.Function* = trans._cur_func
|
||||
old_builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
old_func_name: str = trans._cur_func_name
|
||||
old_global_count: int = trans._global_name_count
|
||||
old_nonlocal_count: int = trans._nonlocal_name_count
|
||||
old_env_count: int = trans._closure_env_count
|
||||
# break/continue 目标与 match-case 标记:新函数作用域必须清空(见下方赋值),
|
||||
# 否则 lambda 体内的 break/continue 会继承外层函数的跳转目标(跨函数 br)
|
||||
# break/continue targets and match-case flag must be cleared for the new scope (see
|
||||
# assignments below); otherwise a lambda break/continue inherits the outer target.
|
||||
old_break_bb: rawptr[mlirlite.BasicBlock] = trans._break_bb
|
||||
old_continue_bb: rawptr[mlirlite.BasicBlock] = trans._continue_bb
|
||||
old_insw: int = trans._in_switch_case
|
||||
@@ -1014,11 +949,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._continue_bb = None
|
||||
trans._in_switch_case = 0
|
||||
|
||||
# 9. 捕获变量:从 env 堆槽加载值到局部 alloca
|
||||
# 9. Captured variables: load values from env heap slots into local allocas
|
||||
for ci in range(cap_count):
|
||||
# 直接 str 指针偏移取捕获名(与 param_names + pi*32 一致)。
|
||||
# 注意:不能用 (str)(cap_addr) 整数转换——TPV 将其编译为 itoa(整数转字符串),
|
||||
# 会把 cap_slot 变成数字串,define_var 键名错乱,lambda body lookup 无法命中。
|
||||
# Take the capture name via str pointer offset (like param_names + pi*32). Do not use
|
||||
# (str)(cap_addr): TPV compiles that to itoa, corrupting define_var keys.
|
||||
cap_slot: str = cap_names + ci * 64
|
||||
if cap_slot[0] == '\0': continue
|
||||
ty_addr3: uint64 = uint64(cap_tys) + ci * 8
|
||||
@@ -1039,15 +973,15 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
mlirlite.build_store(func_builder, cap_val, var_alloca)
|
||||
self.Gen.VarGen.define_var(trans.SymTab, cap_slot, var_alloca)
|
||||
|
||||
# 10. 参数 alloca
|
||||
# 10. Parameter allocas
|
||||
for pi3 in range(param_count):
|
||||
ty_addr4: uint64 = uint64(param_tys) + pi3 * 8
|
||||
ty_slot4: str* = (str*)(ty_addr4)
|
||||
p_ty3: mlirlite.LLVMType* = (mlirlite.LLVMType*)(ty_slot4[0])
|
||||
if p_ty3 is None:
|
||||
# fail-fast:lambda 参数类型槽缺失(内部状态不一致),不再静默保持 i32
|
||||
# fail-fast: lambda param type slot missing (inconsistent state); do not keep i32
|
||||
self.Gen.TypeGenErr.fatal_error(lam, f"lambda 参数 '{param_names + pi3 * 32}' 类型信息缺失", "InternalCompilerError")
|
||||
p_ty3 = i32_ty # 不可达(fatal_error 已退出)
|
||||
p_ty3 = i32_ty # unreachable (fatal_error already exited)
|
||||
pname_ref: str = pool.alloc(32)
|
||||
if pname_ref is not None:
|
||||
viperlib.snprintf(pname_ref, 32, "%%%s", param_names + pi3 * 32)
|
||||
@@ -1057,7 +991,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
mlirlite.build_store(func_builder, param_ref, arg_alloca)
|
||||
self.Gen.VarGen.define_var(trans.SymTab, param_names + pi3 * 32, arg_alloca)
|
||||
|
||||
# 11. 翻译 body 表达式,coerce 到 i32 后 ret
|
||||
# 11. Translate the body expression, coerce to i32 and return
|
||||
body_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(func_builder, pool, mod, lam.body, None, 0, trans)
|
||||
if body_val is not None and body_val.Ty is not None:
|
||||
if self.Gen.ExprGenBase.get_llvm_type_bits(body_val.Ty) == 32 and self.Gen.ExprGenBase.is_ptr_type(body_val.Ty) == 0: mlirlite.build_ret(func_builder, body_val)
|
||||
@@ -1065,13 +999,13 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
ret_val: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(func_builder, body_val, i32_ty, lam.body)
|
||||
if ret_val is not None: mlirlite.build_ret(func_builder, ret_val)
|
||||
else:
|
||||
# fail-fast:lambda 体类型转换失败,不再静默 ret 0
|
||||
# fail-fast: lambda body coercion failed; do not silently return 0
|
||||
self.Gen.TypeGenErr.fatal_error(lam.body, "lambda 体表达式翻译失败", "TypeError")
|
||||
else:
|
||||
# fail-fast:lambda 体翻译失败,不再静默 ret 0
|
||||
# fail-fast: lambda body translation failed; do not silently return 0
|
||||
self.Gen.TypeGenErr.fatal_error(lam.body, "lambda 体表达式翻译失败", "TypeError")
|
||||
|
||||
# 12. 退出 lambda 作用域,恢复父函数状态
|
||||
# 12. Leave the lambda scope and restore the parent state
|
||||
self.Gen.VarGen.exit_scope(trans.SymTab)
|
||||
trans._cur_func = old_func
|
||||
trans._cur_builder = old_builder
|
||||
@@ -1084,7 +1018,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._closure_env_count = old_env_count
|
||||
if dbg_pushed_l != 0: DebugInfoGen.PopFunction(func_builder)
|
||||
|
||||
# 13. 在父函数中创建 env(每个捕获变量 8 字节堆存储,env[i] = 堆地址)
|
||||
# 13. Create env in the parent (8-byte heap per capture, env[i] = heap address)
|
||||
env_ptr: mlirlite.Value* = None
|
||||
if cap_count > 0:
|
||||
env_size: int64 = cap_count * 8
|
||||
@@ -1093,7 +1027,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
env_ptr = mlirlite.build_call(builder, "malloc", env_size_val, 1, i8_ptr_ty, 0)
|
||||
if env_ptr is not None:
|
||||
for ci3 in range(cap_count):
|
||||
# 与步骤 9 一致:str 指针偏移判空,避免整数转换 itoa 语义
|
||||
# Same as step 9: null-check via str pointer offset, avoiding itoa semantics
|
||||
cap_slot3: str = cap_names + ci3 * 64
|
||||
if cap_slot3[0] == '\0': continue
|
||||
al_addr: uint64 = uint64(cap_allocas) + ci3 * 8
|
||||
@@ -1104,7 +1038,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
ty_slot5: str* = (str*)(ty_addr5)
|
||||
cap_ty2: mlirlite.LLVMType* = (mlirlite.LLVMType*)(ty_slot5[0])
|
||||
if cap_ty2 is None: continue
|
||||
# 堆存储大小:位宽/8,至少 8 字节(容纳指针/标量)
|
||||
# Heap size: bits/8, at least 8 bytes (fits pointers/scalars)
|
||||
ty_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cap_ty2)
|
||||
st_size: int = 8
|
||||
if ty_bits > 64: st_size = ty_bits // 8
|
||||
@@ -1122,13 +1056,13 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
slot_typed2: mlirlite.Value* = mlirlite.build_bitcast(builder, slot2, i8_ptr_ptr_ty)
|
||||
if slot_typed2 is not None: mlirlite.build_store(builder, var_storage, slot_typed2)
|
||||
|
||||
# 14. 在父函数中创建闭包 malloc(16)
|
||||
# 14. Create the closure in the parent: malloc(16)
|
||||
closure_size_val: mlirlite.Value* = mlirlite.const_int64(pool, 16)
|
||||
if closure_size_val is not None: closure_size_val.Next = None
|
||||
closure_ptr: mlirlite.Value* = mlirlite.build_call(builder, "malloc", closure_size_val, 1, i8_ptr_ty, 0)
|
||||
if closure_ptr is None: return None
|
||||
|
||||
# 15. [0] = fn_ptr(函数引用 bitcast 到 i8*)
|
||||
# 15. [0] = fn_ptr (function reference bitcast to i8*)
|
||||
fn_param_node: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
|
||||
fn_func_ty: mlirlite.LLVMType* = mlirlite.Func(pool, i32_ty, fn_param_node, 1)
|
||||
fn_func_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, fn_func_ty)
|
||||
@@ -1141,7 +1075,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
fn_slot: mlirlite.Value* = mlirlite.build_bitcast(builder, closure_ptr, mlirlite.Ptr(pool, i8_ptr_ty))
|
||||
if fn_slot is not None and fn_as_i8ptr is not None: mlirlite.build_store(builder, fn_as_i8ptr, fn_slot)
|
||||
|
||||
# 16. [8] = env_ptr(无捕获存 null)
|
||||
# 16. [8] = env_ptr (store null when there are no captures)
|
||||
eight_val: mlirlite.Value* = mlirlite.const_int32(pool, 8)
|
||||
env_slot_addr: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, closure_ptr, eight_val)
|
||||
if env_slot_addr is not None:
|
||||
@@ -1152,35 +1086,30 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
null_val: mlirlite.Value* = mlirlite.SSAValue(pool, i8_ptr_ty, "null")
|
||||
mlirlite.build_store(builder, null_val, env_slot)
|
||||
|
||||
# 17. 返回闭包指针(i8*)
|
||||
# 17. Return the closure pointer (i8*)
|
||||
return closure_ptr
|
||||
|
||||
# ============================================================
|
||||
# _lambda_scan_body - 递归扫描 lambda body 中的 Name 引用
|
||||
#
|
||||
# 收集外部变量(排除 lambda 参数、模块级全局、已捕获项),
|
||||
# 记录到 cap_names/cap_allocas/cap_tys 三个并行缓冲。
|
||||
# 返回更新后的捕获数量(递归调用逐层累加)。
|
||||
# ============================================================
|
||||
# _lambda_scan_body - recursively collect outer Name refs into cap_names/cap_allocas/cap_tys
|
||||
def _lambda_scan_body(self, trans: HT.Translator*, node: ast.AST*, param_names: str, param_count: int, cap_names: str, cap_allocas: t.CPtr, cap_tys: t.CPtr, cap_count: int) -> int:
|
||||
"""递归扫描 lambda body 中的 Name 引用,收集捕获变量"""
|
||||
"""Recursively scan lambda body Name references and collect captured variables"""
|
||||
if node is None or trans is None: return cap_count
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is None: return cap_count
|
||||
# 排除 lambda 参数
|
||||
# Skip lambda parameters
|
||||
for pi in range(param_count):
|
||||
if string.strcmp(param_names + pi * 32, nm.id) == 0: return cap_count
|
||||
# 已捕获跳过
|
||||
# Skip already-captured names
|
||||
for ci in range(cap_count):
|
||||
if string.strcmp(cap_names + ci * 64, nm.id) == 0: return cap_count
|
||||
# 查当前作用域链
|
||||
# Look up the current scope chain
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is None or alloca.Ty is None: return cap_count
|
||||
pointee: mlirlite.LLVMType* = alloca.Ty.Pointee
|
||||
if pointee is None: return cap_count
|
||||
# 跳过模块级全局(body 直接解析全局,无需 env 捕获)
|
||||
# Skip module-level globals (the body resolves them directly)
|
||||
mod_gv: mlirlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id)
|
||||
if mod_gv is not None:
|
||||
if uint64(mod_gv) == uint64(alloca): return cap_count
|
||||
@@ -1191,7 +1120,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
ty_slot: str* = (str*)(uint64(cap_tys) + cap_count * 8)
|
||||
ty_slot[0] = pointee
|
||||
return cap_count + 1
|
||||
# 递归遍历常见表达式子节点
|
||||
# Recurse into common expression children
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
cc1: int = self._lambda_scan_body(trans, bop.left, param_names, param_count, cap_names, cap_allocas, cap_tys, cap_count)
|
||||
|
||||
+161
-237
@@ -10,40 +10,29 @@ import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Generator as Generator
|
||||
|
||||
# ============================================================
|
||||
# GenApi - 管线/保留文件访问 Gen 的模块级委托入口(去除 Handles 层)
|
||||
#
|
||||
# 由脚本从 Gen 子对象同名方法自动生成:签名取真源方法(含参数类型注解),
|
||||
# 函数体经 _g_gen 单例调用 gen 子对象。main/BuildPipeline 注入 _g_gen。
|
||||
# ============================================================
|
||||
# GenApi - module-level delegate entry for pipeline/reserved-file access to Gen
|
||||
# (drops the Handles layer); generated from Gen sub-object methods via _g_gen.
|
||||
|
||||
|
||||
# 全局 gen 单例(main/BuildPipeline 创建 gen 后注入)
|
||||
# Global gen singleton (injected by main/BuildPipeline after creating gen)
|
||||
_g_gen: Generator.Generator* = None
|
||||
|
||||
|
||||
# ---------------- set_temp_dir(委托 TypeGenBase.set_temp_dir) ----------------
|
||||
# ---------------- set_temp_dir (delegates to TypeGenBase.set_temp_dir) ----------------
|
||||
def set_temp_dir(temp_dir: str) -> None:
|
||||
"""委托到 gen.set_temp_dir(原 Handles 壳)"""
|
||||
"""Delegate to gen.set_temp_dir"""
|
||||
if _g_gen is not None: _g_gen.TypeGenBase.set_temp_dir(temp_dir)
|
||||
|
||||
|
||||
# ---------------- set_current_file_imports(委托 ImportsGenMixin.set_current_file) ----------------
|
||||
# ---------------- set_current_file_imports (delegates to ImportsGenMixin.set_current_file) ----------------
|
||||
def set_current_file_imports(file_path: str) -> None:
|
||||
"""委托到 gen.set_current_file(原 Handles 壳)"""
|
||||
"""Delegate to gen.set_current_file"""
|
||||
if _g_gen is not None: _g_gen.ImportsGen.set_current_file(file_path)
|
||||
|
||||
|
||||
# ---------------- set_current_file_type(委托 TypeGenResolve.set_current_file) ----------------
|
||||
# ---------------- set_current_file_type (delegates to TypeGenResolve.set_current_file) ----------------
|
||||
def set_current_file_type(file_path: str) -> None:
|
||||
"""委托到 gen.set_current_file —— 报错归因的"当前文件"写入通道(原 Handles 壳)
|
||||
|
||||
与 fatal_error 同因同解:Converter 的 vpi 生成(Phase 1)、Phase A-pre-vpi
|
||||
跑在 BuildPipeline 注入 GenApi._g_gen 之前(BuildPipeline.vp 的
|
||||
_GeneratePyiFromAst 调用点早于 GenApi._g_gen 赋值),此时 _g_gen 为空;
|
||||
写侧若缺兜底会静默 no-op → fatal_error 的 get_current_file 读到的仍是
|
||||
上一个文件(归因错位)。故回退读 Generator 模块单例(main.vp 启动即注入)。
|
||||
"""
|
||||
"""Set the current file for attribution via gen, falling back to the Generator singleton."""
|
||||
if _g_gen is not None:
|
||||
_g_gen.TypeGenResolve.set_current_file(file_path)
|
||||
return
|
||||
@@ -51,54 +40,46 @@ def set_current_file_type(file_path: str) -> None:
|
||||
if gg is not None: gg.TypeGenResolve.set_current_file(file_path)
|
||||
|
||||
|
||||
# ---------------- set_current_module_sha1(委托 TypeGenBase.set_current_module_sha1) ----------------
|
||||
# ---------------- set_current_module_sha1 (delegates to TypeGenBase.set_current_module_sha1) ----------------
|
||||
def set_current_module_sha1(sha1: str) -> None:
|
||||
"""委托到 gen.set_current_module_sha1(原 Handles 壳)"""
|
||||
"""Delegate to gen.set_current_module_sha1"""
|
||||
if _g_gen is not None: _g_gen.TypeGenBase.set_current_module_sha1(sha1)
|
||||
|
||||
|
||||
# ---------------- clear_trace(委托 TypeGenBase.clear_trace) ----------------
|
||||
# ---------------- clear_trace (delegates to TypeGenBase.clear_trace) ----------------
|
||||
def clear_trace() -> None:
|
||||
"""委托到 gen.clear_trace(原 Handles 壳)"""
|
||||
"""Delegate to gen.clear_trace"""
|
||||
if _g_gen is not None: _g_gen.TypeGenBase.clear_trace()
|
||||
|
||||
|
||||
# ---------------- clear_cdefine_constants(委托 TypeGenCDef.clear_cdefine_constants) ----------------
|
||||
# ---------------- clear_cdefine_constants (delegates to TypeGenCDef.clear_cdefine_constants) ----------------
|
||||
def clear_cdefine_constants() -> None:
|
||||
"""委托到 gen.clear_cdefine_constants(原 Handles 壳)"""
|
||||
"""Delegate to gen.clear_cdefine_constants"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCDef.clear_cdefine_constants()
|
||||
|
||||
|
||||
# ---------------- clear_ctypedefs(委托 TypeGenCType.clear_ctypedefs) ----------------
|
||||
# ---------------- clear_ctypedefs (delegates to TypeGenCType.clear_ctypedefs) ----------------
|
||||
def clear_ctypedefs() -> None:
|
||||
"""委托到 gen.clear_ctypedefs(原 Handles 壳)"""
|
||||
"""Delegate to gen.clear_ctypedefs"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCType.clear_ctypedefs()
|
||||
|
||||
|
||||
# ---------------- RegisterCTypedefFromSource(委托 TypeGenCType.register_ctypedef_from_source) ----------------
|
||||
# ---------------- RegisterCTypedefFromSource (delegates to TypeGenCType.register_ctypedef_from_source) ----------------
|
||||
def RegisterCTypedefFromSource(pool: memhub.MemBuddy*, src: str) -> int:
|
||||
"""委托到 gen.register_ctypedef_from_source(原 Handles 壳)"""
|
||||
"""Delegate to gen.register_ctypedef_from_source"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCType.register_ctypedef_from_source(pool, src)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- dump_parse_traceback(委托 TypeGenErr.dump_parse_traceback) ----------------
|
||||
# ---------------- dump_parse_traceback (delegates to TypeGenErr.dump_parse_traceback) ----------------
|
||||
def dump_parse_traceback(file_path: str) -> None:
|
||||
"""委托到 gen.dump_parse_traceback(原 Handles 壳)"""
|
||||
"""Delegate to gen.dump_parse_traceback"""
|
||||
if _g_gen is not None: _g_gen.TypeGenErr.dump_parse_traceback(file_path)
|
||||
|
||||
|
||||
# ---------------- fatal_error(委托 TypeGenErr.fatal_error) ----------------
|
||||
# ---------------- fatal_error (delegates to TypeGenErr.fatal_error) ----------------
|
||||
def fatal_error(node: ast.AST*, msg: str, err_type: str = "SyntaxError") -> None:
|
||||
"""委托到 gen.TypeGenErr.fatal_error —— 模块级函数的报错通道
|
||||
|
||||
VpiReader / Converter 等模块级函数没有 self.Gen,无法直接调用
|
||||
fatal_error(模块级函数也不应为了报错而持有 gen),统一经此入口。
|
||||
Converter 的 vpi 生成(Phase 1)跑在 BuildPipeline 注入 GenApi._g_gen
|
||||
之前(BuildPipeline.vp 的 _GeneratePyiFromAst 调用点早于 GenApi._g_gen
|
||||
赋值),故 GenApi._g_gen 为空时回退读 Generator 模块单例(main.vp 启动
|
||||
即注入),避免报错静默 no-op。
|
||||
"""
|
||||
"""Report an error through gen.TypeGenErr, falling back to the Generator singleton."""
|
||||
if _g_gen is not None:
|
||||
_g_gen.TypeGenErr.fatal_error(node, msg, err_type)
|
||||
return
|
||||
@@ -106,303 +87,271 @@ def fatal_error(node: ast.AST*, msg: str, err_type: str = "SyntaxError") -> None
|
||||
if gg is not None: gg.TypeGenErr.fatal_error(node, msg, err_type)
|
||||
|
||||
|
||||
# ---------------- map_t_type(委托 TypeGenResolve.map_t_type) ----------------
|
||||
# ---------------- map_t_type (delegates to TypeGenResolve.map_t_type) ----------------
|
||||
def map_t_type(pool: memhub.MemBuddy*, type_name: str) -> mlirlite.LLVMType*:
|
||||
"""委托到 gen.map_t_type(原 Handles 壳)"""
|
||||
"""Delegate to gen.map_t_type"""
|
||||
if _g_gen is not None: _g_gen.TypeGenResolve.map_t_type(pool, type_name)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- resolve_typedef_or_map(委托 TypeGenResolve.resolve_typedef_or_map) ----------------
|
||||
# ---------------- resolve_typedef_or_map (delegates to TypeGenResolve.resolve_typedef_or_map) ----------------
|
||||
def resolve_typedef_or_map(pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*:
|
||||
"""委托到 gen.resolve_typedef_or_map(CTypedef 动态表优先,miss 回退 map_t_type)"""
|
||||
"""Delegate to gen.resolve_typedef_or_map: CTypedef table first, then map_t_type."""
|
||||
if _g_gen is not None: return _g_gen.TypeGenResolve.resolve_typedef_or_map(pool, name, imported_modules, from_imports, trans)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- infer_external_func_ret_ty(委托 CallGenLib._infer_external_func_ret_ty) ----------------
|
||||
# ---------------- infer_external_func_ret_ty (delegates to CallGenLib._infer_external_func_ret_ty) ----------------
|
||||
def infer_external_func_ret_ty(pool: memhub.MemBuddy*, func_name: str) -> mlirlite.LLVMType*:
|
||||
"""委托到 gen.CallGenLib._infer_external_func_ret_ty(外部函数返回类型白名单)
|
||||
|
||||
vpi 注入 extern declare 时返回类型解析 miss 的兜底:FindFirstFileA/
|
||||
GetStdHandle 等 Win32 HANDLE 返回函数 → i8*,避免默认 i32/void 导致
|
||||
声明签名错误(运行期 FindFirstFileA 句柄丢失 → 文件存在性判断失效根因)。
|
||||
fatal_if_unknown=0:白名单未命中返回默认 i32(不 fail-fast,注入上下文
|
||||
无法收敛判断交给调用侧)。
|
||||
"""
|
||||
"""Infer an external function return type from the whitelist, defaulting to i32."""
|
||||
if _g_gen is not None: return _g_gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, None, 0)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- get_current_file(委托 TypeGenResolve.get_current_file) ----------------
|
||||
# ---------------- get_current_file (delegates to TypeGenResolve.get_current_file) ----------------
|
||||
def get_current_file() -> str:
|
||||
"""委托到 gen.get_current_file(原 Handles 壳)"""
|
||||
"""Delegate to gen.get_current_file"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenResolve.get_current_file()
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- _is_ctypedef_annotation(委托 TypeGenCType._is_ctypedef_annotation) ----------------
|
||||
# ---------------- _is_ctypedef_annotation (delegates to TypeGenCType._is_ctypedef_annotation) ----------------
|
||||
def _is_ctypedef_annotation(annotation: ast.AST*) -> int:
|
||||
"""委托到 gen._is_ctypedef_annotation(原 Handles 壳)"""
|
||||
"""Delegate to gen._is_ctypedef_annotation"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenCType._is_ctypedef_annotation(annotation)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- resolve_annotation_type(委托 TypeGenResolve.resolve_annotation_type) ----------------
|
||||
# ---------------- resolve_annotation_type (delegates to TypeGenResolve.resolve_annotation_type) ----------------
|
||||
def resolve_annotation_type(pool: memhub.MemBuddy*, node: ast.AST*, imported_modules: str, from_imports: str, trans: HT.Translator* = None, depth: int = 0) -> mlirlite.LLVMType*:
|
||||
"""委托到 gen.resolve_annotation_type(原 Handles 壳)"""
|
||||
"""Delegate to gen.resolve_annotation_type"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenResolve.resolve_annotation_type(pool, node, imported_modules, from_imports, trans, depth)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- has_decorator_marker(委托 TypeGenErr.has_decorator_marker) ----------------
|
||||
# ---------------- has_decorator_marker (delegates to TypeGenErr.has_decorator_marker) ----------------
|
||||
def has_decorator_marker(node: ast.AST*, marker_name: str, depth: int = 0) -> int:
|
||||
"""委托到 gen.has_decorator_marker(原 Handles 壳)"""
|
||||
"""Delegate to gen.has_decorator_marker"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenErr.has_decorator_marker(node, marker_name, depth)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- _get_type_name_from_annotation(委托 TypeGenResolve._get_type_name_from_annotation) ----------------
|
||||
# ---------------- _get_type_name_from_annotation (delegates to TypeGenResolve._get_type_name_from_annotation) ----------------
|
||||
def _get_type_name_from_annotation(pool: memhub.MemBuddy*, node: ast.AST*) -> str:
|
||||
"""委托到 gen._get_type_name_from_annotation(原 Handles 壳)"""
|
||||
"""Delegate to gen._get_type_name_from_annotation"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenResolve._get_type_name_from_annotation(pool, node)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- register_ctypedef(委托 TypeGenCType.register_ctypedef) ----------------
|
||||
# ---------------- register_ctypedef (delegates to TypeGenCType.register_ctypedef) ----------------
|
||||
def register_ctypedef(pool: memhub.MemBuddy*, name: str, type_str: str):
|
||||
"""委托到 gen.register_ctypedef(原 Handles 壳)"""
|
||||
"""Delegate to gen.register_ctypedef"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCType.register_ctypedef(pool, name, type_str)
|
||||
|
||||
|
||||
# ---------------- lookup_cdefine_constant(委托 TypeGenCDef.lookup_cdefine_constant) ----------------
|
||||
# ---------------- lookup_cdefine_constant (delegates to TypeGenCDef.lookup_cdefine_constant) ----------------
|
||||
def lookup_cdefine_constant(name: str) -> int64:
|
||||
"""委托到 gen.lookup_cdefine_constant(原 Handles 壳)"""
|
||||
"""Delegate to gen.lookup_cdefine_constant"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenCDef.lookup_cdefine_constant(name)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- is_cdefine_found(委托 TypeGenCDef.is_cdefine_found) ----------------
|
||||
# ---------------- is_cdefine_found (delegates to TypeGenCDef.is_cdefine_found) ----------------
|
||||
def is_cdefine_found() -> int:
|
||||
"""委托到 gen.is_cdefine_found(原 Handles 壳)"""
|
||||
"""Delegate to gen.is_cdefine_found"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenCDef.is_cdefine_found()
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- register_cdefine_constant(委托 TypeGenCDef.register_cdefine_constant) ----------------
|
||||
# ---------------- register_cdefine_constant (delegates to TypeGenCDef.register_cdefine_constant) ----------------
|
||||
def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64):
|
||||
"""委托到 gen.register_cdefine_constant(原 Handles 壳)"""
|
||||
"""Delegate to gen.register_cdefine_constant"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value)
|
||||
|
||||
|
||||
# ---------------- register_cdefine_double(委托 TypeGenCDef.register_cdefine_double) ----------------
|
||||
# ---------------- register_cdefine_double (delegates to TypeGenCDef.register_cdefine_double) ----------------
|
||||
def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double):
|
||||
"""委托到 gen.register_cdefine_double(原 Handles 壳)"""
|
||||
"""Delegate to gen.register_cdefine_double"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value)
|
||||
|
||||
|
||||
# ---------------- is_cdefine_annotation(委托 AssignGenMixin.is_cdefine_annotation) ----------------
|
||||
# ---------------- is_cdefine_annotation (delegates to AssignGenMixin.is_cdefine_annotation) ----------------
|
||||
def is_cdefine_annotation(annot: ast.AST*) -> int:
|
||||
"""委托到 gen.is_cdefine_annotation(原 Handles 壳)"""
|
||||
"""Delegate to gen.is_cdefine_annotation"""
|
||||
if _g_gen is not None: return _g_gen.AssignGen.is_cdefine_annotation(annot)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- extract_cdefine_int_value(委托 AssignGenMixin.extract_cdefine_int_value) ----------------
|
||||
# ---------------- extract_cdefine_int_value (delegates to AssignGenMixin.extract_cdefine_int_value) ----------------
|
||||
def extract_cdefine_int_value(val_node: ast.AST*) -> int64:
|
||||
"""委托到 gen.extract_cdefine_int_value(原 Handles 壳)"""
|
||||
"""Delegate to gen.extract_cdefine_int_value"""
|
||||
if _g_gen is not None: return _g_gen.AssignGen.extract_cdefine_int_value(val_node)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- push_trace(委托 TypeGenBase.push_trace) ----------------
|
||||
# ---------------- push_trace (delegates to TypeGenBase.push_trace) ----------------
|
||||
def push_trace(file_name: str, lineno: int, col_start: int = 0, col_end: int = 0):
|
||||
"""委托到 gen.push_trace(原 Handles 壳)"""
|
||||
"""Delegate to gen.push_trace"""
|
||||
if _g_gen is not None: _g_gen.TypeGenBase.push_trace(file_name, lineno, col_start, col_end)
|
||||
|
||||
|
||||
# ---------------- pop_trace(委托 TypeGenBase.pop_trace) ----------------
|
||||
# ---------------- pop_trace (delegates to TypeGenBase.pop_trace) ----------------
|
||||
def pop_trace():
|
||||
"""委托到 gen.pop_trace(原 Handles 壳)"""
|
||||
"""Delegate to gen.pop_trace"""
|
||||
if _g_gen is not None: _g_gen.TypeGenBase.pop_trace()
|
||||
|
||||
|
||||
# ---------------- get_temp_dir(委托 TypeGenBase.get_temp_dir) ----------------
|
||||
# ---------------- get_temp_dir (delegates to TypeGenBase.get_temp_dir) ----------------
|
||||
def get_temp_dir() -> str:
|
||||
"""委托到 gen.get_temp_dir(原 Handles 壳)"""
|
||||
"""Delegate to gen.get_temp_dir"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenBase.get_temp_dir()
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- reset_visible_structs(委托 StructGenMixin.reset_visible_structs) ----------------
|
||||
# ---------------- reset_visible_structs (delegates to StructGenMixin.reset_visible_structs) ----------------
|
||||
def reset_visible_structs(pool: memhub.MemBuddy*, strict: int):
|
||||
"""委托到 gen.reset_visible_structs(原 Handles 壳)"""
|
||||
"""Delegate to gen.reset_visible_structs"""
|
||||
if _g_gen is not None: _g_gen.StructGen.reset_visible_structs(pool, strict)
|
||||
|
||||
|
||||
# ---------------- set_stdlib_alloc(委托 StructGenMixin.set_stdlib_alloc) ----------------
|
||||
# ---------------- set_stdlib_alloc (delegates to StructGenMixin.set_stdlib_alloc) ----------------
|
||||
def set_stdlib_alloc(flag: int):
|
||||
"""委托到 gen.set_stdlib_alloc(原 Handles 壳)"""
|
||||
"""Delegate to gen.set_stdlib_alloc"""
|
||||
if _g_gen is not None: _g_gen.StructGen.set_stdlib_alloc(flag)
|
||||
|
||||
|
||||
# ---------------- get_struct_count(委托 StructGenMixin.get_struct_count) ----------------
|
||||
# ---------------- get_struct_count (delegates to StructGenMixin.get_struct_count) ----------------
|
||||
def get_struct_count() -> int:
|
||||
"""委托到 gen.get_struct_count(原 Handles 壳)"""
|
||||
"""Delegate to gen.get_struct_count"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.get_struct_count()
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- reset_module_methods_emitted(委托 StructGenMixin.reset_module_methods_emitted) ----------------
|
||||
# ---------------- reset_module_methods_emitted (delegates to StructGenMixin.reset_module_methods_emitted) ----------------
|
||||
def reset_module_methods_emitted(sha1: str) -> int:
|
||||
"""委托到 gen.reset_module_methods_emitted(原 Handles 壳)"""
|
||||
"""Delegate to gen.reset_module_methods_emitted"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.reset_module_methods_emitted(sha1)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- get_struct_type(委托 StructGenMixin.get_struct_type) ----------------
|
||||
# ---------------- get_struct_type (delegates to StructGenMixin.get_struct_type) ----------------
|
||||
def get_struct_type(class_name: str) -> mlirlite.LLVMType*:
|
||||
"""委托到 gen.get_struct_type(原 Handles 壳)"""
|
||||
"""Delegate to gen.get_struct_type"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.get_struct_type(class_name)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- get_enum_base_type(委托 StmtGenDeclMixin.get_enum_base_type) ----------------
|
||||
# ---------------- get_enum_base_type (delegates to StmtGenDeclMixin.get_enum_base_type) ----------------
|
||||
def get_enum_base_type(enum_name: str) -> mlirlite.LLVMType*:
|
||||
"""委托到 gen.get_enum_base_type(原 Handles 壳)
|
||||
|
||||
枚举(t.CEnum)注册在独立的 EnumTable(EnumEntry),不进 StructTable,
|
||||
get_struct_type 查不到。vpi 返回注解如 "types.FRESULT" 解析时 struct
|
||||
落空必须回退枚举表取 BaseTy(通常 i32),否则跨模块函数返回类型
|
||||
无法精确获取 → text.ll 缺失(依赖环)时永久 pending(Phase A 死锁根因)。
|
||||
"""
|
||||
"""Delegate to gen.get_enum_base_type, falling back to the enum table's base type."""
|
||||
if _g_gen is not None: return _g_gen.StmtGenDecl.get_enum_base_type(enum_name)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- add_visible_struct(委托 StructGenMixin.add_visible_struct) ----------------
|
||||
# ---------------- add_visible_struct (delegates to StructGenMixin.add_visible_struct) ----------------
|
||||
def add_visible_struct(pool: memhub.MemBuddy*, name: str):
|
||||
"""委托到 gen.add_visible_struct(原 Handles 壳)"""
|
||||
"""Delegate to gen.add_visible_struct"""
|
||||
if _g_gen is not None: _g_gen.StructGen.add_visible_struct(pool, name)
|
||||
|
||||
|
||||
# ---------------- find_struct_by_module(委托 StructGenMixin.find_struct_by_module) ----------------
|
||||
# ---------------- find_struct_by_module (delegates to StructGenMixin.find_struct_by_module) ----------------
|
||||
def find_struct_by_module(name: str, sha1: str) -> GenTypes.StructEntry*:
|
||||
"""委托到 gen.find_struct_by_module(原 Handles 壳)"""
|
||||
"""Delegate to gen.find_struct_by_module"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.find_struct_by_module(name, sha1)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- mark_module_class(委托 StructGenMixin.mark_module_class) ----------------
|
||||
# ---------------- mark_module_class (delegates to StructGenMixin.mark_module_class) ----------------
|
||||
def mark_module_class(pool: memhub.MemBuddy*, sha1: str, name: str) -> None:
|
||||
"""登记"{sha1}.{类名}"(= 该模块声明了这个类),并把 CurDefModuleSha1/CurDefClassName
|
||||
指向它。注入期(VpiReader)没有"类定义窗口",必须靠这一步让
|
||||
`_norm_type_arg_name` 规则 5.5 的证据链能被查询(否则裸类名实参取不到定义模块
|
||||
SHA1 ⇒ 特化名冻结成裸名版本,与该类自身翻译期的带前缀形式分裂)。"""
|
||||
"""Register "{sha1}.{class}" for this module and point the current definition fields at it."""
|
||||
if _g_gen is not None: _g_gen.StructGen.mark_module_class(pool, sha1, name)
|
||||
|
||||
|
||||
# ---------------- register_file_classes(委托 StructGenMixin.register_file_classes) ----------------
|
||||
# ---------------- register_file_classes (delegates to StructGenMixin.register_file_classes) ----------------
|
||||
def register_file_classes(pool: memhub.MemBuddy*, sha1: str, tree: ast.AST*) -> None:
|
||||
"""文件级预登记本文件顶层 ClassDef 的类名 + 本文件模块 SHA1(规则 5.5 第五类证据)。
|
||||
|
||||
由 Translator.translate 的 prologue 调用(在翻译模块级变量/前向声明**之前**),
|
||||
使"模块级语句早于类定义"这一窗口里裸类名也能判出归属。"""
|
||||
"""Pre-register top-level ClassDef names with this file's module SHA1."""
|
||||
if _g_gen is not None: _g_gen.StructGen.register_file_classes(pool, sha1, tree)
|
||||
|
||||
|
||||
# ---------------- register_file_funcs / is_file_func(委托 StructGenMixin) ----------------
|
||||
# ---------------- register_file_funcs / is_file_func (delegates to StructGenMixin) ----------------
|
||||
def register_file_funcs(pool: memhub.MemBuddy*, tree: ast.AST*) -> None:
|
||||
"""文件级预登记本文件顶层 FunctionDef 的函数名。
|
||||
|
||||
与 register_file_classes 同址调用(Translator 文件入口 prologue,早于依赖 vpi
|
||||
注入):注入侧据此跳过"本文件已声明的裸名",避免依赖的 extern 声明顶掉本文件
|
||||
自己的 extern 声明(现场 includes/socket.vp connect 被 w32.winsock2 顶掉)。"""
|
||||
"""Pre-register this file's top-level FunctionDef names to guard its own extern declarations."""
|
||||
if _g_gen is not None: _g_gen.StructGen.register_file_funcs(pool, tree)
|
||||
|
||||
|
||||
def is_file_func(name: str) -> int:
|
||||
"""裸名是否为**本文件**顶层声明的函数(1=是 / 0=否)"""
|
||||
"""Return 1 if name is a top-level function declared in this file, else 0."""
|
||||
if _g_gen is not None: return _g_gen.StructGen.is_file_func(name)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- find_struct_by_type(委托 StructGenMixin.find_struct_by_type) ----------------
|
||||
# ---------------- find_struct_by_type (delegates to StructGenMixin.find_struct_by_type) ----------------
|
||||
def find_struct_by_type(struct_ty: mlirlite.LLVMType*) -> GenTypes.StructEntry*:
|
||||
"""委托到 gen.find_struct_by_type(原 Handles 壳)"""
|
||||
"""Delegate to gen.find_struct_by_type"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.find_struct_by_type(struct_ty)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- register_struct(委托 StructGenMixin.register_struct) ----------------
|
||||
# ---------------- register_struct (delegates to StructGenMixin.register_struct) ----------------
|
||||
def register_struct(pool: memhub.MemBuddy*, name: str, struct_ty: mlirlite.LLVMType*, sha1: str = None) -> GenTypes.StructEntry*:
|
||||
"""委托到 gen.register_struct(原 Handles 壳)"""
|
||||
"""Delegate to gen.register_struct"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.register_struct(pool, name, struct_ty, sha1)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- add_field(委托 StructGenMixin.add_field) ----------------
|
||||
# ---------------- add_field (delegates to StructGenMixin.add_field) ----------------
|
||||
def add_field(pool: memhub.MemBuddy*, struct_entry: GenTypes.StructEntry*, field_name: str, field_ty: mlirlite.LLVMType*) -> int:
|
||||
"""委托到 gen.add_field(原 Handles 壳)"""
|
||||
"""Delegate to gen.add_field"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.add_field(pool, struct_entry, field_name, field_ty)
|
||||
return -1
|
||||
|
||||
|
||||
# ---------------- register_struct_alias(委托 StructGenMixin.register_struct_alias) ----------------
|
||||
# ---------------- register_struct_alias (delegates to StructGenMixin.register_struct_alias) ----------------
|
||||
def register_struct_alias(pool: memhub.MemBuddy*, name: str, sha1: str, src_entry: GenTypes.StructEntry*) -> GenTypes.StructEntry*:
|
||||
"""委托到 gen.StructGen.register_struct_alias(包重导出类别名注册,原 Handles 壳)"""
|
||||
"""Delegate to gen.StructGen.register_struct_alias for package re-export aliases."""
|
||||
if _g_gen is not None: return _g_gen.StructGen.register_struct_alias(pool, name, sha1, src_entry)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- get_stdlib_alloc(委托 StructGenMixin.get_stdlib_alloc) ----------------
|
||||
# ---------------- get_stdlib_alloc (delegates to StructGenMixin.get_stdlib_alloc) ----------------
|
||||
def get_stdlib_alloc() -> int:
|
||||
"""委托到 gen.get_stdlib_alloc(原 Handles 壳)"""
|
||||
"""Delegate to gen.get_stdlib_alloc"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.get_stdlib_alloc()
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
# ---------------- compute_package_from_relpath(委托 ImportsGenMixin.compute_package_from_relpath) ----------------
|
||||
# ---------------- compute_package_from_relpath (delegates to ImportsGenMixin.compute_package_from_relpath) ----------------
|
||||
def compute_package_from_relpath(pool: memhub.MemBuddy*, rel_path: str) -> str:
|
||||
"""委托到 gen.compute_package_from_relpath(原 Handles 壳)"""
|
||||
"""Delegate to gen.compute_package_from_relpath"""
|
||||
if _g_gen is not None: return _g_gen.ImportsGen.compute_package_from_relpath(pool, rel_path)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- is_module_imported(委托 ImportsGenMixin.is_module_imported) ----------------
|
||||
# ---------------- is_module_imported (delegates to ImportsGenMixin.is_module_imported) ----------------
|
||||
def is_module_imported(imported_modules: str, name: str) -> int:
|
||||
"""委托到 gen.is_module_imported(原 Handles 壳)"""
|
||||
"""Delegate to gen.is_module_imported"""
|
||||
if _g_gen is not None: return _g_gen.ImportsGen.is_module_imported(imported_modules, name)
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SHA1 前缀识别的**唯一真源**:两种书写形态各给一个判定。
|
||||
#
|
||||
# sha1_prefix_len(name) → 完整名 "{16位小写hex}.Class" ⇒ 返回 17(前缀字节数),否则 0。
|
||||
# 判据三条缺一不可:长度 >= 17、第 17 字节是 '.'、前 16 字节全为小写 hex。
|
||||
# is_sha1_hex16(seg) → 单独的模块段("16位小写hex")⇒ 1/0;用于已切成两段后判 mod 段。
|
||||
#
|
||||
# 必须唯一:多处同义拷贝若口径不同(省略 hex 校验 / 允许大写 / 只要 len >= 16 就盲取前 16 字节),
|
||||
# 同一名字在不同路径会被判成"是/不是 SHA1 前缀",产生「重复加前缀 / 把 SHA1 当模块名再查 /
|
||||
# 该跳过 store 查询的没跳过」等隐蔽不一致(跨模块同名类归属错乱、泛型实参解析退化)。
|
||||
# ============================================================
|
||||
# Single source of truth for SHA1-prefix recognition: sha1_prefix_len(name) returns
|
||||
# 17 for "{16 lowercase hex}.Class", else 0; is_sha1_hex16(seg) checks a bare segment.
|
||||
def sha1_prefix_len(name: str) -> int:
|
||||
"""名字若是 "{16位小写hex}." 形态返回 17(前缀字节数),否则返回 0"""
|
||||
"""Return 17 if name starts with "{16 lowercase hex}.", otherwise 0."""
|
||||
if name is None: return 0
|
||||
if string.strlen(name) < 17: return 0
|
||||
if name[16] != '.': return 0
|
||||
i: sizet
|
||||
for i in range(16):
|
||||
c: int = name[i]
|
||||
# 48-57 = '0'-'9',97-102 = 'a'-'f'
|
||||
# 48-57 = '0'-'9', 97-102 = 'a'-'f'
|
||||
if not ((c >= 48 and c <= 57) or (c >= 97 and c <= 102)): return 0
|
||||
return 17
|
||||
|
||||
|
||||
def is_sha1_hex16(seg: str) -> int:
|
||||
"""seg 是否恰为 16 位小写 hex(SHA1 模块段),1=是 / 0=否"""
|
||||
"""Return 1 if seg is exactly 16 lowercase hex chars, else 0."""
|
||||
if seg is None: return 0
|
||||
if string.strlen(seg) != 16: return 0
|
||||
i: sizet
|
||||
@@ -412,88 +361,71 @@ def is_sha1_hex16(seg: str) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
# ---------------- add_imported_module(委托 ImportsGenMixin.add_imported_module) ----------------
|
||||
# ---------------- add_imported_module (delegates to ImportsGenMixin.add_imported_module) ----------------
|
||||
def add_imported_module(pool: memhub.MemBuddy*, imported_modules: str, name: str) -> str:
|
||||
"""委托到 gen.add_imported_module(原 Handles 壳)"""
|
||||
"""Delegate to gen.add_imported_module"""
|
||||
if _g_gen is not None: return _g_gen.ImportsGen.add_imported_module(pool, imported_modules, name)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- translate_import(委托 ImportsGenMixin.translate_import) ----------------
|
||||
# ---------------- translate_import (delegates to ImportsGenMixin.translate_import) ----------------
|
||||
def translate_import(trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""委托到 gen.translate_import(原 Handles 壳)"""
|
||||
"""Delegate to gen.translate_import"""
|
||||
if _g_gen is not None: return _g_gen.ImportsGen.translate_import(trans, node)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- translate_import_from_module(委托 ImportsGenMixin.translate_import_from_module) ----------------
|
||||
# ---------------- translate_import_from_module (delegates to ImportsGenMixin.translate_import_from_module) ----------------
|
||||
def translate_import_from_module(trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""委托到 gen.translate_import_from_module(原 Handles 壳)"""
|
||||
"""Delegate to gen.translate_import_from_module"""
|
||||
if _g_gen is not None: return _g_gen.ImportsGen.translate_import_from_module(trans, node)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- translate_import_from_names(委托 ImportsGenMixin.translate_import_from_names) ----------------
|
||||
# ---------------- translate_import_from_names (delegates to ImportsGenMixin.translate_import_from_names) ----------------
|
||||
def translate_import_from_names(trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""委托到 gen.translate_import_from_names(原 Handles 壳)"""
|
||||
"""Delegate to gen.translate_import_from_names"""
|
||||
if _g_gen is not None: return _g_gen.ImportsGen.translate_import_from_names(trans, node)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- _get_parent_class(委托 ClassGenBase._get_parent_class) ----------------
|
||||
# ---------------- _get_parent_class (delegates to ClassGenBase._get_parent_class) ----------------
|
||||
def _get_parent_class(cd: ast.ClassDef*, trans: HT.Translator* = None) -> str:
|
||||
"""委托到 gen._get_parent_class(原 Handles 壳)"""
|
||||
"""Delegate to gen._get_parent_class"""
|
||||
if _g_gen is not None: return _g_gen.ClassGenBase._get_parent_class(cd, trans)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- _register_generic_template(委托 ClassGenGeneric._register_generic_template) ----------------
|
||||
# ---------------- _register_generic_template (delegates to ClassGenGeneric._register_generic_template) ----------------
|
||||
def _register_generic_template(pool: memhub.MemBuddy*, cd: ast.ClassDef*, module_sha1: str):
|
||||
"""委托到 gen._register_generic_template(原 Handles 壳)"""
|
||||
"""Delegate to gen._register_generic_template"""
|
||||
if _g_gen is not None: _g_gen.ClassGenGeneric._register_generic_template(pool, cd, module_sha1)
|
||||
|
||||
|
||||
# ---------------- _register_generic_func_template(委托 ClassGenGeneric._register_generic_func_template) ----------------
|
||||
# ---------------- _register_generic_func_template (delegates to ClassGenGeneric._register_generic_func_template) ----------------
|
||||
def _register_generic_func_template(pool: memhub.MemBuddy*, fd: ast.FunctionDef*, module_sha1: str):
|
||||
"""委托到 gen.ClassGenGeneric._register_generic_func_template(扫描预注册用)"""
|
||||
"""Delegate to gen.ClassGenGeneric._register_generic_func_template for scan pre-registration."""
|
||||
if _g_gen is not None: _g_gen.ClassGenGeneric._register_generic_func_template(pool, fd, module_sha1)
|
||||
|
||||
|
||||
# ---------------- _mangle_name_with_sha1(委托 FuncGenBase._mangle_name_with_sha1) ----------------
|
||||
# ---------------- _mangle_name_with_sha1 (delegates to FuncGenBase._mangle_name_with_sha1) ----------------
|
||||
def _mangle_name_with_sha1(sha1: str, name: str) -> str:
|
||||
"""委托到 gen._mangle_name_with_sha1(原 Handles 壳)"""
|
||||
"""Delegate to gen._mangle_name_with_sha1"""
|
||||
if _g_gen is not None: return _g_gen.FuncGenBase._mangle_name_with_sha1(sha1, name)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- forward_declare_functions(委托 FuncGenDef.forward_declare_functions) ----------------
|
||||
# ---------------- forward_declare_functions (delegates to FuncGenDef.forward_declare_functions) ----------------
|
||||
def forward_declare_functions(trans: HT.Translator*, tree: ast.AST*) -> int:
|
||||
"""委托到 gen.forward_declare_functions(原 Handles 壳)"""
|
||||
"""Delegate to gen.forward_declare_functions"""
|
||||
if _g_gen is not None: return _g_gen.FuncGenDef.forward_declare_functions(trans, tree)
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# f-string 逃逸规则(判据 + 拷贝出口,本文件为唯一实现)
|
||||
#
|
||||
# f-string 的结果指针指向**当前函数的栈缓冲**(见 ExprGenFstr._fstr_finalize):
|
||||
# · 栈缓冲 ⇒ 线程安全(每线程各自栈帧)+ 零运行时依赖(内核无 C-ABI 分配器);
|
||||
# · 函数返回即失效 ⇒ 把该指针带出函数的写法都是悬空读。历史事故:调用方
|
||||
# `return f"..."` 后由上层当 %s 喂 snprintf ⇒ ASan stack-buffer-underflow
|
||||
# in vsnprintf(READ size 1,地址落在栈左红区)。
|
||||
#
|
||||
# 因此:逃逸位置一律 fail-fast;确需逃逸时用 escape_fstring_copy 显式拷贝。
|
||||
#
|
||||
# 覆盖范围与残留(诚实记录):本判据覆盖 return / 赋值到字段、元素、全局这类
|
||||
# **直接逃逸**;把 f-string 传进"会保存它"的函数、或放进容器后带走容器,属于
|
||||
# 间接逃逸,静态判不出,仍靠规则约束(用 escape_fstring_copy 显式拷贝)。
|
||||
# ============================================================
|
||||
# f-string escape rules: results point at the current function's stack buffer, so
|
||||
# escaping positions fail-fast; use escape_fstring_copy to copy explicitly.
|
||||
def fail_if_fstring_escape(node: ast.AST*, where: str) -> int:
|
||||
"""value 位置是 f-string(JoinedStr / 裸 FormattedValue,含元组元素)却处于逃逸位置 → fail-fast
|
||||
|
||||
where: 逃逸位置描述(如 "函数返回值" / "字段赋值" / "全局变量初始化")。
|
||||
恒返回 0(命中即终止编译,不返回非 0 值)。
|
||||
"""
|
||||
"""Fail fast when an f-string sits in an escaping position such as a return value."""
|
||||
if node is None: return 0
|
||||
hit: int = 0
|
||||
k: int = node.kind()
|
||||
@@ -518,12 +450,7 @@ def fail_if_fstring_escape(node: ast.AST*, where: str) -> int:
|
||||
|
||||
|
||||
def escape_fstring_copy(src: str) -> str:
|
||||
"""把 f-string 结果拷贝到 stdlib 堆并返回(f-string 逃逸的**唯一**出口)。
|
||||
|
||||
f-string 本身是函数内临时值(栈缓冲);要把它带出函数(return / 存字段 /
|
||||
存全局 / 放进长期容器)必须显式拷贝。分配用 stdlib.malloc(CRT),
|
||||
生命周期由调用方决定——编译器自身的报文构造只在错误路径使用,允许不回收。
|
||||
"""
|
||||
"""Copy an f-string result onto the stdlib heap and return it; the only escape path."""
|
||||
if src is None: return None
|
||||
n: sizet = string.strlen(src)
|
||||
out: str = stdlib.malloc(n + 1)
|
||||
@@ -532,131 +459,128 @@ def escape_fstring_copy(src: str) -> str:
|
||||
return out
|
||||
|
||||
|
||||
# ---------------- create_wrapper_main(委托 TranslatorGenMixin.create_wrapper_main) ----------------
|
||||
# ---------------- create_wrapper_main (delegates to TranslatorGenMixin.create_wrapper_main) ----------------
|
||||
def create_wrapper_main(trans: HT.Translator*, tree: ast.AST*, i32_ty: mlirlite.LLVMType*) -> int:
|
||||
"""委托到 gen.create_wrapper_main(原 Handles 壳)"""
|
||||
"""Delegate to gen.create_wrapper_main"""
|
||||
if _g_gen is not None: return _g_gen.TranslatorGen.create_wrapper_main(trans, tree, i32_ty)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- handle_module_level_var(委托 TranslatorGenMixin.handle_module_level_var) ----------------
|
||||
# ---------------- handle_module_level_var (delegates to TranslatorGenMixin.handle_module_level_var) ----------------
|
||||
def handle_module_level_var(trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""委托到 gen.handle_module_level_var(原 Handles 壳)"""
|
||||
"""Delegate to gen.handle_module_level_var"""
|
||||
if _g_gen is not None: return _g_gen.TranslatorGen.handle_module_level_var(trans, node)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- translate_children(委托 TranslatorGenMixin.translate_children) ----------------
|
||||
# ---------------- translate_children (delegates to TranslatorGenMixin.translate_children) ----------------
|
||||
def translate_children(trans: HT.Translator*, tree: ast.AST*) -> int:
|
||||
"""委托到 gen.translate_children(原 Handles 壳)"""
|
||||
"""Delegate to gen.translate_children"""
|
||||
if _g_gen is not None: return _g_gen.TranslatorGen.translate_children(trans, tree)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- init_symbol_table(委托 VarGenMixin.init_symbol_table) ----------------
|
||||
# ---------------- init_symbol_table (delegates to VarGenMixin.init_symbol_table) ----------------
|
||||
def init_symbol_table(pool: memhub.MemBuddy*) -> rawptr[GenTypes.SymbolTable]:
|
||||
"""委托到 gen.init_symbol_table(原 Handles 壳)"""
|
||||
"""Delegate to gen.init_symbol_table"""
|
||||
if _g_gen is not None: return _g_gen.VarGen.init_symbol_table(pool)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- SetTempDir(委托 CallGenBase.SetTempDir) ----------------
|
||||
# ---------------- SetTempDir (delegates to CallGenBase.SetTempDir) ----------------
|
||||
def SetTempDir(temp_dir: str):
|
||||
"""委托到 gen.SetTempDir(原 Handles 壳)"""
|
||||
"""Delegate to gen.SetTempDir"""
|
||||
if _g_gen is not None: _g_gen.CallGenBase.SetTempDir(temp_dir)
|
||||
|
||||
|
||||
# ---------------- find_func_in_module(委托 CallGenBase.find_func_in_module) ----------------
|
||||
# ---------------- find_func_in_module (delegates to CallGenBase.find_func_in_module) ----------------
|
||||
def find_func_in_module(mod: mlirlite.LLVMModule*, name: str) -> mlirlite.Function*:
|
||||
"""委托到 gen.find_func_in_module(原 Handles 壳)"""
|
||||
"""Delegate to gen.find_func_in_module"""
|
||||
if _g_gen is not None: return _g_gen.CallGenBase.find_func_in_module(mod, name)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- add_func_to_table(委托 CallGenLeafKit.add_func_to_table) ----------------
|
||||
# ---------------- add_func_to_table (delegates to CallGenLeafKit.add_func_to_table) ----------------
|
||||
def add_func_to_table(funcs: GenTypes.FuncEntry*, func_count: int, name: str, func: mlirlite.Function*, max_count: int, defaults: t.CPtr = None, default_count: int = 0, param_count: int = 0, pool: memhub.MemBuddy* = None, def_sha1: str = None) -> int:
|
||||
"""委托到 gen.add_func_to_table(原 Handles 壳)
|
||||
|
||||
def_sha1:定义该函数的模块 SHA1(依赖注入路径必须传,避免默认值被挂到导入方名下)
|
||||
"""
|
||||
"""Delegate to gen.add_func_to_table; def_sha1 is the defining module's SHA1."""
|
||||
if _g_gen is not None: return _g_gen.CallGenLeafKit.add_func_to_table(funcs, func_count, name, func, max_count, defaults, default_count, param_count, pool, def_sha1)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- init_func_table(委托 CallGenLeafKit.init_func_table) ----------------
|
||||
# ---------------- init_func_table (delegates to CallGenLeafKit.init_func_table) ----------------
|
||||
def init_func_table(pool: memhub.MemBuddy*, max_count: int) -> GenTypes.FuncEntry*:
|
||||
"""委托到 gen.init_func_table(原 Handles 壳)"""
|
||||
"""Delegate to gen.init_func_table"""
|
||||
if _g_gen is not None: return _g_gen.CallGenLeafKit.init_func_table(pool, max_count)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- translate_call(委托 CallGenMixin.translate_call) ----------------
|
||||
# ---------------- translate_call (delegates to CallGenMixin.translate_call) ----------------
|
||||
def translate_call(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""委托到 gen.translate_call(原 Handles 壳)"""
|
||||
"""Delegate to gen.translate_call"""
|
||||
if _g_gen is not None: return _g_gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- translate_printf_call(委托 CallGenLeafKit.translate_printf_call) ----------------
|
||||
# ---------------- translate_printf_call (delegates to CallGenLeafKit.translate_printf_call) ----------------
|
||||
def translate_printf_call(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator* = None):
|
||||
"""委托到 gen.translate_printf_call(原 Handles 壳)"""
|
||||
"""Delegate to gen.translate_printf_call"""
|
||||
if _g_gen is not None: _g_gen.CallGenLeafKit.translate_printf_call(pool, builder, mod, cl, trans)
|
||||
|
||||
|
||||
# ---------------- translate_print_call(委托 CallGenLeafKit.translate_print_call) ----------------
|
||||
# ---------------- translate_print_call (delegates to CallGenLeafKit.translate_print_call) ----------------
|
||||
def translate_print_call(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator* = None):
|
||||
"""委托到 gen.translate_print_call(原 Handles 壳)"""
|
||||
"""Delegate to gen.translate_print_call"""
|
||||
if _g_gen is not None: _g_gen.CallGenLeafKit.translate_print_call(pool, builder, mod, cl, trans)
|
||||
|
||||
|
||||
# ---------------- add_global_name(委托 TranslatorGenMixin.add_global_name) ----------------
|
||||
# ---------------- add_global_name (delegates to TranslatorGenMixin.add_global_name) ----------------
|
||||
def add_global_name(trans: HT.Translator*, name: str):
|
||||
"""委托到 gen.TranslatorGen.add_global_name(原 Handles 壳)"""
|
||||
"""Delegate to gen.TranslatorGen.add_global_name"""
|
||||
if _g_gen is not None: _g_gen.TranslatorGen.add_global_name(trans, name)
|
||||
|
||||
|
||||
# ---------------- is_global_name(委托 TranslatorGenMixin.is_global_name) ----------------
|
||||
# ---------------- is_global_name (delegates to TranslatorGenMixin.is_global_name) ----------------
|
||||
def is_global_name(trans: HT.Translator*, name: str) -> int:
|
||||
"""委托到 gen.TranslatorGen.is_global_name(原 Handles 壳)"""
|
||||
"""Delegate to gen.TranslatorGen.is_global_name"""
|
||||
if _g_gen is not None: return _g_gen.TranslatorGen.is_global_name(trans, name)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- add_nonlocal_name(委托 TranslatorGenMixin.add_nonlocal_name) ----------------
|
||||
# ---------------- add_nonlocal_name (delegates to TranslatorGenMixin.add_nonlocal_name) ----------------
|
||||
def add_nonlocal_name(trans: HT.Translator*, name: str, var_ty: mlirlite.LLVMType* = None):
|
||||
"""委托到 gen.TranslatorGen.add_nonlocal_name(原 Handles 壳)"""
|
||||
"""Delegate to gen.TranslatorGen.add_nonlocal_name"""
|
||||
if _g_gen is not None: _g_gen.TranslatorGen.add_nonlocal_name(trans, name, var_ty)
|
||||
|
||||
|
||||
# ---------------- get_nonlocal_type(委托 TranslatorGenMixin.get_nonlocal_type) ----------------
|
||||
# ---------------- get_nonlocal_type (delegates to TranslatorGenMixin.get_nonlocal_type) ----------------
|
||||
def get_nonlocal_type(trans: HT.Translator*, name: str) -> mlirlite.LLVMType*:
|
||||
"""委托到 gen.TranslatorGen.get_nonlocal_type(原 Handles 壳)"""
|
||||
"""Delegate to gen.TranslatorGen.get_nonlocal_type"""
|
||||
if _g_gen is not None: return _g_gen.TranslatorGen.get_nonlocal_type(trans, name)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- is_nonlocal_name(委托 TranslatorGenMixin.is_nonlocal_name) ----------------
|
||||
# ---------------- is_nonlocal_name (delegates to TranslatorGenMixin.is_nonlocal_name) ----------------
|
||||
def is_nonlocal_name(trans: HT.Translator*, name: str) -> int:
|
||||
"""委托到 gen.TranslatorGen.is_nonlocal_name(原 Handles 壳)"""
|
||||
"""Delegate to gen.TranslatorGen.is_nonlocal_name"""
|
||||
if _g_gen is not None: return _g_gen.TranslatorGen.is_nonlocal_name(trans, name)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- get_nonlocal_index(委托 TranslatorGenMixin.get_nonlocal_index) ----------------
|
||||
# ---------------- get_nonlocal_index (delegates to TranslatorGenMixin.get_nonlocal_index) ----------------
|
||||
def get_nonlocal_index(trans: HT.Translator*, name: str) -> int:
|
||||
"""委托到 gen.TranslatorGen.get_nonlocal_index(原 Handles 壳)"""
|
||||
"""Delegate to gen.TranslatorGen.get_nonlocal_index"""
|
||||
if _g_gen is not None: return _g_gen.TranslatorGen.get_nonlocal_index(trans, name)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- clear_scope_names(委托 TranslatorGenMixin.clear_scope_names) ----------------
|
||||
# ---------------- clear_scope_names (delegates to TranslatorGenMixin.clear_scope_names) ----------------
|
||||
def clear_scope_names(trans: HT.Translator*):
|
||||
"""委托到 gen.TranslatorGen.clear_scope_names(原 Handles 壳)"""
|
||||
"""Delegate to gen.TranslatorGen.clear_scope_names"""
|
||||
if _g_gen is not None: _g_gen.TranslatorGen.clear_scope_names(trans)
|
||||
|
||||
|
||||
# ---------------- update_global_func_spec(委托 CallGenLeafKit.update_global_func_spec) ----------------
|
||||
# ---------------- update_global_func_spec (delegates to CallGenLeafKit.update_global_func_spec) ----------------
|
||||
def update_global_func_spec(name: str, func: mlirlite.Function*) -> int:
|
||||
"""委托到 gen.CallGenLeafKit.update_global_func_spec(原 ExprCallTable 壳)"""
|
||||
"""Delegate to gen.CallGenLeafKit.update_global_func_spec."""
|
||||
if _g_gen is not None: return _g_gen.CallGenLeafKit.update_global_func_spec(name, func)
|
||||
return 0
|
||||
+164
-287
@@ -3,28 +3,22 @@ from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
# TypeSpec:统一类型描述符(Debugger/_Type.md 第 6 节设计)。
|
||||
# 别名用 TSpec:模块内类名同为 TypeSpec,别名同名会被解析器误判为类静态方法。
|
||||
# TypeSpec: unified type descriptor (Debugger/_Type.md section 6).
|
||||
# Alias TSpec because the in-module class name is also TypeSpec (same name would be a static method).
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
|
||||
|
||||
# ============================================================
|
||||
# GenTypes - Gen 侧共享数据类型与常量(去除 Handles 层迁移目标)
|
||||
#
|
||||
# 原分布于 Handles 目录的数据类与常量集中于此,供 Gen 子对象、
|
||||
# 管线与保留实体文件引用。删除 Handles 目录后统一经本模块访问。
|
||||
# ============================================================
|
||||
# GenTypes - Gen-side shared data types and constants (migrated out of the Handles layer)
|
||||
|
||||
# ============================================================
|
||||
# 类编译期元数据(原 HandlesType.CLASS_META_*)
|
||||
# ============================================================
|
||||
# Class compile-time metadata (formerly HandlesType.CLASS_META_*)
|
||||
define CLASS_META_PROVIDES = 1
|
||||
define CLASS_META_REQUIRES = 2
|
||||
define CLASS_META_REQUIRE_MUST = 3
|
||||
|
||||
# ============================================================
|
||||
# 属性/静态/类方法元数据(原 HandlesClassDef.META_*)
|
||||
# ============================================================
|
||||
# Attribute/static/class-method metadata (formerly HandlesClassDef.META_*)
|
||||
define META_STATIC = 1
|
||||
define META_CLASS = 2
|
||||
define META_PROP_GETTER = 4
|
||||
@@ -32,25 +26,22 @@ define META_PROP_SETTER = 8
|
||||
define META_PROP_DELETER = 16
|
||||
|
||||
# ============================================================
|
||||
# 结构体容量(原 HandlesStruct.FIELD_MAX/FIELD_NAME_MAX/PARENT_MAX/MRO_MAX)
|
||||
# ============================================================
|
||||
# Struct capacities (formerly HandlesStruct.FIELD_MAX/...)
|
||||
define FIELD_MAX = 256
|
||||
define FIELD_NAME_MAX = 64
|
||||
define PARENT_MAX = 64
|
||||
define MRO_MAX = 64
|
||||
|
||||
# ============================================================
|
||||
# for 迭代序列种类(原 HandlesFor.SEQ_KIND_*)
|
||||
# ============================================================
|
||||
# for-iteration sequence kinds (formerly HandlesFor.SEQ_KIND_*)
|
||||
define SEQ_KIND_RANGE = 1
|
||||
define SEQ_KIND_STR = 2
|
||||
define SEQ_KIND_LIST = 3
|
||||
|
||||
# ============================================================
|
||||
# TraceFrame - 仿 Python Traceback 调用栈帧(原 HandlesType.TraceFrame)
|
||||
# ============================================================
|
||||
# TraceFrame - Python-Traceback-like call frame (formerly HandlesType.TraceFrame)
|
||||
class TraceFrame:
|
||||
"""仿 Python Traceback 调用栈帧(push_trace/pop_trace 使用)"""
|
||||
"""Python-Traceback-like call frame used by push_trace/pop_trace"""
|
||||
file_name: str
|
||||
lineno: int
|
||||
col_start: int
|
||||
@@ -58,43 +49,30 @@ class TraceFrame:
|
||||
|
||||
|
||||
# ============================================================
|
||||
# VarEntry - 变量表条目(原 HandlesVar.VarEntry)
|
||||
# ============================================================
|
||||
# VarEntry - variable table entry (formerly HandlesVar.VarEntry)
|
||||
class VarEntry:
|
||||
"""变量表条目"""
|
||||
"""Variable table entry"""
|
||||
Name: str
|
||||
Alloca: mlirlite.Value*
|
||||
Used: int
|
||||
# TypeSpec(S3 起为**唯一类型承载**):由 add_var 用 SpecFromLLVMType 从
|
||||
# alloca.Ty 的 pointee 填充(PermPool 分配 ⇒ 跨文件安全),注解侧再由
|
||||
# set_var_annot_class_name 经 SpecWithSign / SpecWithSource 补整数符号性与
|
||||
# 注解来源名(Spec.SourceName,原 AnnotClassName 字段已并入),由
|
||||
# set_var_ptr_element 经 SpecWithElemBits 补「指针元素」语义(原
|
||||
# IsPtrElement 字段已并入 Spec.ElemBits)。
|
||||
# TypeSpec (sole type carrier since S3): filled by add_var via SpecFromLLVMType from
|
||||
# alloca.Ty's pointee (PermPool, cross-file safe); annotation side augments sign/source/elem.
|
||||
Spec: TSpec.TypeSpec*
|
||||
# 1=该变量的 load/store 带 LLVM volatile 限定符(注解联合含 t.CVolatile)。
|
||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
|
||||
# 1=load/store of this variable carries LLVM volatile (annotation union has t.CVolatile).
|
||||
# Appended at the end so existing field offsets are unchanged.
|
||||
IsVolatile: int
|
||||
# 1=该变量是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
|
||||
# volatile T*):解引用(p[i] / p.field / c.Deref(p))的 load/store 带 LLVM
|
||||
# volatile,禁止优化器重排序/消除对所指对象的每次访问。注意与 IsVolatile
|
||||
# 的区别:IsVolatile 限定"指针变量自身"的读写,本字段限定"指针所指物"。
|
||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
|
||||
# 1=this variable is rawptr[t.CVolatile | T] (volatile pointee, like C's volatile T*):
|
||||
# its dereferences carry volatile. Unlike IsVolatile, which limits the pointer variable itself.
|
||||
PointeeVolatile: int
|
||||
# 1=该变量是 **const 对象**(注解含 t.CConst 且声明类型**不是指针**):
|
||||
# 整个作用域内禁止对该变量名赋值 / 增量赋值 / 元素赋值(fail-fast)。
|
||||
# 与 TPC 的 Gen.var_const_flags / global_const_flags 同判据、同报文,见
|
||||
# Debugger/type-descriptors.md。指针类型(如 `p: t.CConst | T*`)的
|
||||
# CConst 只约束 pointee,不使指针变量自身不可写,故不置 1。
|
||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
|
||||
# 1=this variable is a const object (annotation has t.CConst and the type is not a
|
||||
# pointer): assignment to it anywhere in scope fails fast. Pointer CConst only limits the pointee.
|
||||
IsConst: int
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Scope - 作用域节点(原 HandlesVar.Scope)
|
||||
# ============================================================
|
||||
# Scope - scope node (formerly HandlesVar.Scope)
|
||||
novirtual class Scope:
|
||||
"""作用域节点"""
|
||||
"""Scope node"""
|
||||
Parent: Scope*
|
||||
Vars: VarEntry*
|
||||
VarCount: int
|
||||
@@ -102,333 +80,232 @@ novirtual class Scope:
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SymbolTable - 嵌套作用域符号表(原 HandlesVar.SymbolTable)
|
||||
# ============================================================
|
||||
# SymbolTable - nested-scope symbol table (formerly HandlesVar.SymbolTable)
|
||||
novirtual class SymbolTable:
|
||||
"""嵌套作用域符号表"""
|
||||
"""Nested-scope symbol table"""
|
||||
Pool: memhub.MemBuddy*
|
||||
Root: Scope*
|
||||
Current: Scope*
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FieldEntry - 字段信息条目(原 HandlesStruct.FieldEntry)
|
||||
# ============================================================
|
||||
# FieldEntry - field info entry (formerly HandlesStruct.FieldEntry)
|
||||
class FieldEntry:
|
||||
Name: str # 字段名(字符串)
|
||||
Index: int # 字段在结构体中的索引
|
||||
Ty: mlirlite.LLVMType* # 字段的 LLVM 类型
|
||||
DefaultVal: ast.AST* # 默认值 AST 节点(None=无默认值)
|
||||
# TypeSpec(S3 起为**唯一类型承载**):由 add_field 用 SpecFromLLVMType 从
|
||||
# field_ty 填充(PermPool 分配 ⇒ 跨 1b reset 安全,无需 DeepCopyType),
|
||||
# 注解侧再经 SpecWithSign / SpecWithSource 补符号性与注解来源名
|
||||
# (Spec.SourceName,原 AnnotClassName 字段已并入)。
|
||||
Name: str # Field name (string)
|
||||
Index: int # Field index within the struct
|
||||
Ty: mlirlite.LLVMType* # Field LLVM type
|
||||
DefaultVal: ast.AST* # Default-value AST node (None=no default)
|
||||
# TypeSpec (sole type carrier since S3): filled by add_field via SpecFromLLVMType from
|
||||
# field_ty (PermPool, safe across 1b reset); annotation side adds sign/source.
|
||||
Spec: TSpec.TypeSpec*
|
||||
# 1=该字段经对象访问的 load/store 带 LLVM volatile 限定符
|
||||
# (字段注解联合含 t.CVolatile,如 Spinlock.locked: t.CVolatile | t.CInt)。
|
||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
|
||||
# 1=load/store of this field via an object carries LLVM volatile (field annotation union
|
||||
# has t.CVolatile, e.g. Spinlock.locked). Appended at the end so offsets are unchanged.
|
||||
IsVolatile: int
|
||||
# 1=该字段是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
|
||||
# volatile T*):经该字段解引用(self.ptr[i] / self.ptr.field)的 load/store
|
||||
# 带 LLVM volatile。与 IsVolatile 的区别同 VarEntry.PointeeVolatile。
|
||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
|
||||
# 1=this field is rawptr[t.CVolatile | T] (volatile pointee): dereferences via it
|
||||
# (self.ptr[i] / self.ptr.field) carry volatile. Same distinction as VarEntry.PointeeVolatile.
|
||||
PointeeVolatile: int
|
||||
# 1=该字段是 **const 对象**(注解含 t.CConst 且字段类型**不是指针**):
|
||||
# 经对象给该字段赋值(obj.f = ... / self.f = ...)即 fail-fast,与
|
||||
# VarEntry.IsConst 同判据、同报文(TPC 侧对应 Gen.field_const_flags)。
|
||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
|
||||
# 1=this field is a const object (annotation has t.CConst and the type is not a pointer):
|
||||
# assigning it via an object fails fast, same as VarEntry.IsConst.
|
||||
IsConst: int
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StructEntry - 结构体注册条目(原 HandlesStruct.StructEntry)
|
||||
# ============================================================
|
||||
# StructEntry - struct registration entry (formerly HandlesStruct.StructEntry)
|
||||
novirtual class StructEntry:
|
||||
Name: str # 类名
|
||||
Name: str # Class name
|
||||
Ty: mlirlite.LLVMType* # LLVM StructType
|
||||
FieldCount: int # 字段数量
|
||||
Fields: FieldEntry* # 字段数组(FIELD_MAX 个槽位)
|
||||
IsUnion: int # 1=联合体 / 0=普通结构体
|
||||
IsPacked: int # 1=@c.Attribute(t.attr.packed) 打包结构体(无字段填充)
|
||||
Align: int # @c.Attribute(t.attr.aligned(N)) 对齐字节数(0=默认,如 fpu_context=16)
|
||||
IsOOP: int # 1=OOP结构体(有方法) / 0=纯内存结构体
|
||||
HasInit: int # 1=有__init__方法 / 0=无
|
||||
HasNew: int # 1=有__new__方法 / 0=无
|
||||
HasVTable: int # 1=有虚表 / 0=无虚表
|
||||
IsNoVTable: int # 1=明确标记 @t.NoVTable / 0=未标记
|
||||
ParentName: str # 父类名(None=无父类,多继承时为第一个父类,兼容旧调用)
|
||||
ParentNames: sizet* # 多继承父类名数组(每元素 str 指针,ParentCount 个;stdlib 深拷贝)
|
||||
ParentCount: int # 父类数量(0=无父类,1=单继承,>1=多继承)
|
||||
VTableMethodCount: int # 虚表中的方法数量
|
||||
VTableMethods: str # 虚表方法名数组(每个方法名 str,VTableMethodCount 个)
|
||||
ModuleSha1: str # 定义该类的模块 SHA1(None=无 SHA1)
|
||||
MethodsEmitted: int # 1=方法体已翻译 / 0=未译(Phase 1a mark_only 未译,Phase B 据此补译)
|
||||
SpecTriggerSha1: str # 泛型特化类的触发文件 SHA1(None=非特化类)。特化类 ModuleSha1=模板
|
||||
# sha1(方法名带模板前缀),作废重译时 reset_module_methods_emitted
|
||||
# 按触发文件 sha1 精确重置 MethodsEmitted,防特化方法体残留 1 不补译
|
||||
# → 链接 undefined symbol(list[ast.ClassDef*].* 根因)
|
||||
IsForwardDecl: int # 1=前向声明占位条目(Ty=不透明具名结构体 {sha}.Class,FieldCount=0;
|
||||
# 定义侧随后**原位填充**同一 Ty 对象并清零本标记)。
|
||||
# 语义对齐 TPC:未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充。
|
||||
FieldCount: int # Field count
|
||||
Fields: FieldEntry* # Field array (FIELD_MAX slots)
|
||||
IsUnion: int # 1=union / 0=normal struct
|
||||
IsPacked: int # 1=@c.Attribute(t.attr.packed) packed struct (no padding)
|
||||
Align: int # @c.Attribute(t.attr.aligned(N)) alignment in bytes (0=default)
|
||||
IsOOP: int # 1=OOP struct (has methods) / 0=plain memory struct
|
||||
HasInit: int # 1=has __init__ / 0=no
|
||||
HasNew: int # 1=has __new__ / 0=no
|
||||
HasVTable: int # 1=has vtable / 0=no vtable
|
||||
IsNoVTable: int # 1=explicitly @t.NoVTable / 0=not marked
|
||||
ParentName: str # Parent name (None=none; first parent for multiple inheritance)
|
||||
ParentNames: sizet* # Multi-inheritance parent name array (str pointers; stdlib-deep-copied)
|
||||
ParentCount: int # Parent count (0=none, 1=single, >1=multiple)
|
||||
VTableMethodCount: int # Number of methods in the vtable
|
||||
VTableMethods: str # Vtable method name array (VTableMethodCount str)
|
||||
ModuleSha1: str # Module SHA1 defining this class (None=no SHA1)
|
||||
MethodsEmitted: int # 1=method bodies translated / 0=not (Phase 1a mark_only; Phase B fills in)
|
||||
SpecTriggerSha1: str # Trigger-file SHA1 for a generic specialization (None=not specialized).
|
||||
# ModuleSha1 is the template sha1; reset_module_methods_emitted resets
|
||||
# MethodsEmitted by trigger sha1 to avoid leftover 1 / undefined symbol.
|
||||
IsForwardDecl: int # 1=forward-declaration placeholder (Ty is opaque named struct, FieldCount=0).
|
||||
# The definition side later fills the same Ty in place and clears this flag.
|
||||
|
||||
|
||||
# ============================================================
|
||||
# EnumMember - 枚举成员(原 HandlesEnum.EnumMember)
|
||||
# ============================================================
|
||||
# EnumMember - enum member (formerly HandlesEnum.EnumMember)
|
||||
class EnumMember:
|
||||
Name: str # 成员名(字符串)
|
||||
Value: int64 # 成员的整数值
|
||||
Ty: mlirlite.LLVMType* # 成员的 LLVM 类型(用于混用类型场景)
|
||||
Name: str # Member name (string)
|
||||
Value: int64 # Member integer value
|
||||
Ty: mlirlite.LLVMType* # Member LLVM type (for mixed-type cases)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# EnumEntry - 枚举类型条目(原 HandlesEnum.EnumEntry)
|
||||
# ============================================================
|
||||
# EnumEntry - enum type entry (formerly HandlesEnum.EnumEntry)
|
||||
novirtual class EnumEntry:
|
||||
Name: str # 枚举类名
|
||||
BaseTy: mlirlite.LLVMType* # 基准类型(所有成员类型中最大的)
|
||||
MemberCount: int # 成员数量
|
||||
Members: EnumMember* # 成员数组(ENUM_MEMBER_MAX 个槽位)
|
||||
Name: str # Enum class name
|
||||
BaseTy: mlirlite.LLVMType* # Base type (largest among member types)
|
||||
MemberCount: int # Member count
|
||||
Members: EnumMember* # Member array (ENUM_MEMBER_MAX slots)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# AsmOperand - 内联汇编操作数条目(原 HandlesExprCall.AsmOperand)
|
||||
# ============================================================
|
||||
# AsmOperand - inline-asm operand entry (formerly HandlesExprCall.AsmOperand)
|
||||
class AsmOperand:
|
||||
"""内联汇编操作数条目"""
|
||||
Value: mlirlite.Value* # 输入值或输出 alloca
|
||||
Constraint: str # 约束字符串 (如 "r", "=r")
|
||||
IsOutput: int # 1=输出, 0=输入
|
||||
"""Inline-asm operand entry"""
|
||||
Value: mlirlite.Value* # Input value or output alloca
|
||||
Constraint: str # Constraint string (e.g. "r", "=r")
|
||||
IsOutput: int # 1=output, 0=input
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FuncEntry - 函数表条目(原 HandlesExprCall.FuncEntry)
|
||||
# ============================================================
|
||||
# FuncEntry - function table entry (formerly HandlesExprCall.FuncEntry)
|
||||
novirtual class FuncEntry:
|
||||
"""函数表条目"""
|
||||
"""Function table entry"""
|
||||
Name: str
|
||||
Func: mlirlite.Function*
|
||||
Defaults: t.CPtr # defaults 列表节点指针 (list[AST*]),None=无默认值
|
||||
DefaultCount: int # 默认值数量
|
||||
ParamCount: int # 形参数量(不含 self)
|
||||
# TypeSpec(S3 条目双写):Kind=TS_FUNC 的**整签名**(Ret/Params/ParamCount/IsVarArg
|
||||
# 都在其内),由 update_global_func_spec 在形参 add_param 完成后用 NewTypeFunc 构造。
|
||||
# PermPool 驻留 ⇒ 跨 1b reset 安全。单字段即可完整表达签名,与 VarEntry/FieldEntry
|
||||
# 的 Spec 命名一致,避免参数数组重复。
|
||||
Defaults: t.CPtr # Defaults list node pointer (list[AST*]); None=no defaults
|
||||
DefaultCount: int # Default count
|
||||
ParamCount: int # Param count (excluding self)
|
||||
# TypeSpec (dual-write since S3): the full signature of Kind=TS_FUNC (Ret/Params/...)
|
||||
# built by update_global_func_spec after add_param; PermPool-resident, safe across 1b reset.
|
||||
Spec: TSpec.TypeSpec*
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FuncDefaultsEntry - 默认参数表条目(原 HandlesExprCall.FuncDefaultsEntry)
|
||||
# ============================================================
|
||||
# FuncDefaultsEntry - default-argument entry (formerly HandlesExprCall.FuncDefaultsEntry)
|
||||
novirtual class FuncDefaultsEntry:
|
||||
"""默认参数表条目"""
|
||||
"""Default-argument table entry"""
|
||||
Name: str
|
||||
ModuleSha1: str # 定义该函数的模块 SHA1(限定名去重,避免跨模块同名串味)
|
||||
Defaults: t.CPtr # defaults 列表节点指针 (list[AST*])(pool 内,可能被 reset)
|
||||
ModuleSha1: str # Module SHA1 defining this function (dedupes qualified names)
|
||||
Defaults: t.CPtr # Defaults list node pointer (list[AST*]) (in pool, may be reset)
|
||||
DefaultCount: int
|
||||
ParamCount: int # 形参数量(不含 self)
|
||||
# 常量快照(stdlib 分配,跨文件永久存活):
|
||||
# 注册时提取每个默认值 Constant 的值,跨模块调用时优先用快照重建常量 IR,
|
||||
# 避免读取已被 1b reset_to(mark1) 回收的 pool 内 AST。
|
||||
ConstKinds: int* # const_kind 数组(DefaultCount 个 i32)
|
||||
ConstInts: int64* # int_val 数组(DefaultCount 个 i64)
|
||||
ConstStrs: sizet* # str_val 指针数组(DefaultCount 个 i8*)
|
||||
ParamCount: int # Param count (excluding self)
|
||||
# Constant snapshot (stdlib-allocated, permanent): extracted at registration so
|
||||
# cross-module calls rebuild constant IR without touching a reset AST.
|
||||
ConstKinds: int* # const_kind array (DefaultCount i32)
|
||||
ConstInts: int64* # int_val array (DefaultCount i64)
|
||||
ConstStrs: sizet* # str_val pointer array (DefaultCount i8*)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# REPL 会话(M2/M3):ctx 缓冲区布局 + 会话表条目 + 结果区头
|
||||
#
|
||||
# ctx 缓冲区由宿主(lib/core/Repl.vp)在 PermPool 一次性分配,片段函数以首形参
|
||||
# 接收(注解 bytes ⇒ i8*),同一进程内宿主与翻译器共享同一块内存。
|
||||
#
|
||||
# 布局(用户拍板子约定;字节全小端,无符号/有符号按位模式):
|
||||
#
|
||||
# 偏移 0 magic(u32 = 'REPL' = 0x5245504C) 4 version(u32 = 2)
|
||||
# 偏移 8 result_kind(i32) 12 保留(0)
|
||||
# 偏移 16 result_a(u64) 24 result_b(u64)
|
||||
# 偏移 32 var_count(i32) 36 last_frag(i32)
|
||||
# 偏移 40..63 保留(0)
|
||||
# 偏移 64 + i*16 第 i 个会话变量的槽(步长 16 字节),i < REPL_VAR_CAP
|
||||
# 偏移 1088 + j*16 第 j 个会话函数的指针槽(步长 16 字节),j < REPL_FUNC_CAP
|
||||
#
|
||||
# 版本与向后兼容(version 1 → 2,**纯尾部追加**):
|
||||
# v1 = 头 64B + 变量槽区 1024B(共 1088B);v2 在 1088 之后追加函数指针槽区
|
||||
# (REPL_FUNC_CAP*REPL_FUNC_SLOT_SIZE)。前 1088 字节的字段偏移/宽度/语义逐字
|
||||
# 未变 ⇒ 按 v1 生成的旧片段 DLL 与 v2 宿主混用仍然正确(旧片段只读写
|
||||
# < 1088 的字节);新片段读函数指针槽时宿主必为 v2 ⇒ 无版本协商需求。
|
||||
# 头 +4 写 2 仅作"含函数指针槽区"的显式标记(片段侧不校验该字段)。
|
||||
#
|
||||
# result_kind:
|
||||
# 0 = none 本片段无结果(声明类片段)
|
||||
# 1 = int/uint result_a = 值(补码位模式,宿主按有符号 64 位回显)
|
||||
# 2 = float64 result_a = IEEE754 double 位模式(宿主重解释后回显)
|
||||
# 3 = str/bytes result_a = 指针;result_b = 长度(0 = 以 NUL 结尾,宿主按
|
||||
# C 字符串处理——片段侧不额外调 strlen,故长度未知时写 0)
|
||||
# 4 = struct/其它 result_a = 值地址(结构体值装箱后的盒子地址 / 非字符串指针)
|
||||
#
|
||||
# 槽内存放形态(ReplVarEntry.Kind):
|
||||
# SCALAR — 标量/指针(≤8 字节)的值直接存槽;
|
||||
# BOX — 结构体值:片段侧 malloc 盒子(值存盒内),槽内存盒子指针。
|
||||
# 槽步长 16 字节(结构体/字符串统一走指针 ⇒ 宿主无需知道类型);负载一律
|
||||
# ≤8 字节且 8 字节对齐即合法(MemBuddy 用户指针实测 8 字节对齐)。
|
||||
# 槽数上限 REPL_VAR_CAP ⇒ 变量区尺寸 = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE。
|
||||
#
|
||||
# 函数指针槽(M3):槽内是会话函数在**定义片段 DLL** 里的入口地址(void*,8 字节;
|
||||
# DLL 常驻不卸载 ⇒ 地址在会话期内有效)。宿主在片段 DLL 加载成功后
|
||||
# GetProcAddress(裸名) 取得地址并写入槽;后续片段的调用点从槽 load 地址后
|
||||
# 间接调用 ⇒ 函数代码只有一份(在定义片段里),调用点不复制代码。
|
||||
# ============================================================
|
||||
define REPL_CTX_MARKER = 1397571916 # 宿主侧对象标记(自比对;非 ctx 头字段)
|
||||
define REPL_CTX_MAGIC = 0x5245504C # 'REPL'(ctx 头 +0,小端字节序为 R E P L)
|
||||
define REPL_CTX_VERSION = 2 # ctx 头 +4(1 → 2:尾部追加函数指针槽区)
|
||||
define REPL_SLOT_SIZE = 16 # 每个会话变量占用字节数(步长)
|
||||
define REPL_SLOT_BASE = 64 # 槽区起点 = 固定头字节数(= +40..63 保留之后)
|
||||
define REPL_CTX_PARAM = "__repl_ctx__" # 包裹模板里 ctx 形参名(与 Repl.vp 同源)
|
||||
define REPL_VAR_CAP = 64 # 会话变量上限(变量区 = 64 + 64*16 = 1088 字节)
|
||||
# 会话函数指针槽区(M3):起点紧跟变量区,步长与变量槽一致(16B,低 8 字节存地址)
|
||||
# REPL session (M2/M3): ctx buffer layout, session table entries, and result header.
|
||||
define REPL_CTX_MARKER = 1397571916 # Host-side object marker (self-comparison; not a ctx header field)
|
||||
define REPL_CTX_MAGIC = 0x5245504C # 'REPL' (ctx header +0, little-endian bytes R E P L)
|
||||
define REPL_CTX_VERSION = 2 # ctx header +4 (1 -> 2: function pointer slot area appended at the tail)
|
||||
define REPL_SLOT_SIZE = 16 # Bytes per session variable (stride)
|
||||
define REPL_SLOT_BASE = 64 # Slot area start = fixed header bytes (after the +40..63 reserved area)
|
||||
define REPL_CTX_PARAM = "__repl_ctx__" # Wrapper-template ctx parameter name (same source as Repl.vp)
|
||||
define REPL_VAR_CAP = 64 # Max session variables (var area = 64 + 64*16 = 1088 bytes)
|
||||
# Session function pointer slot area (M3): starts right after the var area, 16B stride (low 8 bytes hold the address)
|
||||
define REPL_FUNC_BASE = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
|
||||
define REPL_FUNC_SLOT_SIZE = 16 # 每个会话函数占用字节数(步长)
|
||||
define REPL_FUNC_CAP = 32 # 会话函数上限(函数区 = 32*16 = 512 字节)
|
||||
# ctx 总字节数 = 64 + 64*16 + 32*16 = 1600(宿主 Repl._REPL_CTX_SIZE 必须一致)
|
||||
define REPL_FUNC_SLOT_SIZE = 16 # Bytes per session function (stride)
|
||||
define REPL_FUNC_CAP = 32 # Max session functions (func area = 32*16 = 512 bytes)
|
||||
# Total ctx bytes = 64 + 64*16 + 32*16 = 1600 (host Repl._REPL_CTX_SIZE must match)
|
||||
define REPL_CTX_SIZE = 1600
|
||||
# 会话函数的形参上限(签名 token 数组定长;超出 ⇒ 注册期明确报错)
|
||||
# Max session function params (fixed-size signature token array; overflow errors at registration)
|
||||
define REPL_FUNC_PARAM_MAX = 8
|
||||
# 片段入口函数名前缀(Repl.vp 生成 "__repl_frag_%d";用于区分"片段入口函数"与
|
||||
# 被提升的会话函数本体——前者是会话变量写入的作用域,后者只读会话槽)
|
||||
# Fragment entry function name prefix (Repl.vp generates "__repl_frag_%d"); distinguishes
|
||||
# the fragment entry function (writes session vars) from the hoisted session function body.
|
||||
define REPL_ENTRY_PREFIX = "__repl_frag_"
|
||||
# 会话模块 SHA1(M4):片段翻译器的 ModuleSha1 取此**稳定**值 ⇒ 片段内定义的类型
|
||||
# 名形如 "<16位hex>.<Type>",且**跨片段逐字一致**——这是"先前片段定义的类在后续
|
||||
# 片段可见/可用"的基础(类型名是跨片段查找的唯一键)。
|
||||
# 派生:sha1("TransPyV-REPL-Session").hexdigest()[:16]
|
||||
# 得 71f67b7cb405f0e4(一次性计算,见 repl-design.md §M4 的可复算命令)。
|
||||
# 形态:16 位小写 hex 与 includes 模块 SHA1 同形 ⇒ 被既有判定
|
||||
# GenApi.is_sha1_hex16 / StructGen.find_struct 的"模块段即 SHA1"分支
|
||||
# 直接当作模块限定名处理(无需新增解析规则)。
|
||||
# 反例(为什么不给每个片段各设一个 sha1):类型名会随片段漂移,先前片段注册的
|
||||
# 结构体/方法在后续片段的 find_struct_by_module 全部落空。
|
||||
# 与裸名约定的关系:`export def`(含片段入口与会话函数)走 has_export 分支取裸名
|
||||
# (FuncGenDef.vp:482-485),**不受**该值影响;受影响的只有类/方法/模块级全局
|
||||
# 等经 _mangle_name/_mangle_name_with_sha1 命名的符号。
|
||||
# Session module SHA1 (M4): fragments use this stable value so type names are byte-identical
|
||||
# across fragments, which is the key for classes defined earlier to be visible later.
|
||||
define REPL_SESSION_SHA1 = "71f67b7cb405f0e4"
|
||||
|
||||
# ctx 头字段偏移(与 ReplResult 字段顺序/宽度一一对应)
|
||||
# ctx header field offsets (one-to-one with ReplResult field order/width)
|
||||
define REPL_RES_KIND_OFF = 8 # i32
|
||||
define REPL_RES_A_OFF = 16 # u64
|
||||
define REPL_RES_B_OFF = 24 # u64
|
||||
define REPL_RES_VARCOUNT_OFF = 32 # i32
|
||||
define REPL_RES_LASTFRAG_OFF = 36 # i32
|
||||
|
||||
# 结果 Kind(宿主按此回显)
|
||||
define REPL_RES_NONE = 0 # 本片段无结果(如仅声明)
|
||||
define REPL_RES_INT = 1 # 整数(读 result_a 位模式)
|
||||
define REPL_RES_FLOAT = 2 # float64(读 result_a 的 double 位模式)
|
||||
define REPL_RES_STR = 3 # str/bytes(读 result_a 指针 + result_b 长度)
|
||||
define REPL_RES_OBJ = 4 # 结构体/其它指针(读 result_a 地址)
|
||||
# Result kind (the host echoes accordingly)
|
||||
define REPL_RES_NONE = 0 # No result for this fragment (e.g. declaration only)
|
||||
define REPL_RES_INT = 1 # Integer (read result_a bit pattern)
|
||||
define REPL_RES_FLOAT = 2 # float64 (read result_a's double bit pattern)
|
||||
define REPL_RES_STR = 3 # str/bytes (read result_a pointer + result_b length)
|
||||
define REPL_RES_OBJ = 4 # Struct/other pointer (read result_a address)
|
||||
|
||||
# 会话变量类型标签(ReplVarEntry.TyTag)
|
||||
# Session variable type tags (ReplVarEntry.TyTag)
|
||||
define REPL_TY_INT = 1
|
||||
define REPL_TY_FLOAT = 2
|
||||
define REPL_TY_STR = 3 # i8*(str/bytes)
|
||||
define REPL_TY_PTR = 4 # 其他指针(Bits=pointee 位宽 / TyName=结构体名)
|
||||
define REPL_TY_STRUCT = 5 # 结构体值(按声明类型内联在槽里;≤ 槽容量)
|
||||
define REPL_TY_STR = 3 # i8* (str/bytes)
|
||||
define REPL_TY_PTR = 4 # Other pointer (Bits=pointee width / TyName=struct name)
|
||||
define REPL_TY_STRUCT = 5 # Struct value (inlined in the slot per declared type; <= slot capacity)
|
||||
|
||||
# 会话变量存放形态(ReplVarEntry.Kind)
|
||||
define REPL_KIND_UNTYPED = 0 # 已登记槽位但尚未定型(未赋值/无注解)
|
||||
define REPL_KIND_SCALAR = 1 # 值内联存槽(标量/指针/≤16B 结构体值)
|
||||
# Session variable storage kind (ReplVarEntry.Kind)
|
||||
define REPL_KIND_UNTYPED = 0 # Slot registered but not yet typed (unassigned / no annotation)
|
||||
define REPL_KIND_SCALAR = 1 # Value inlined in the slot (scalar/pointer/<=16B struct value)
|
||||
|
||||
# 会话函数签名的返回类型 token(M3;只用于 ReplFuncEntry.RetTag)
|
||||
define REPL_TY_VOID = 0 # 无返回值(void;不作为形参 token)
|
||||
# Session function signature return-type token (M3; used only for ReplFuncEntry.RetTag)
|
||||
define REPL_TY_VOID = 0 # No return value (void; not used as a param token)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ReplVarEntry - REPL 会话变量条目(跨片段存活,表本体在 PermPool)
|
||||
# ============================================================
|
||||
# ReplVarEntry - REPL session variable entry (survives across fragments; table in PermPool)
|
||||
novirtual class ReplVarEntry:
|
||||
"""REPL 会话变量条目:名字 + ctx 槽偏移 + 定型信息
|
||||
|
||||
Name/TyName 必须是 PermPool 深拷贝——宿主的 AST 名字串属于片段 pool,
|
||||
行末 reset_to 后即失效,直接存指针会读到已回收内存。
|
||||
"""
|
||||
Name: str # 变量名(PermPool 深拷贝,NUL 结尾)
|
||||
TyName: str # 结构体名(TyTag=PTR 且 pointee 为结构体 / TyTag=STRUCT);否则 None
|
||||
Offset: int # ctx 内槽偏移(REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
|
||||
"""REPL session variable entry: name + ctx slot offset + type info"""
|
||||
Name: str # Variable name (PermPool deep copy, NUL-terminated)
|
||||
TyName: str # Struct name (TyTag=PTR with struct pointee / TyTag=STRUCT); else None
|
||||
Offset: int # Slot offset within ctx (REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
|
||||
Kind: int # REPL_KIND_*
|
||||
TyTag: int # REPL_TY_*
|
||||
Bits: int # 标量位宽;TyTag=PTR 时是 pointee 位宽(0=未知)
|
||||
Bits: int # Scalar bit width; for TyTag=PTR the pointee width (0=unknown)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ReplFuncEntry - REPL 会话函数条目(M3;跨片段存活,表本体在 PermPool)
|
||||
#
|
||||
# 语义:片段 N 里 `def f(...)` 被提升为片段模块里的**顶层 export def**(裸名导出),
|
||||
# 该 DLL 加载后宿主 GetProcAddress 取到地址并写入 ctx 的函数指针槽;本条目记录
|
||||
# "名字 → 符号名 + 定义片段号 + 槽偏移 + 签名 token",后续片段的调用点据此
|
||||
# 从槽 load 地址并间接调用(与 M2 的会话变量槽同理:单一真相 + 间接寻址)。
|
||||
#
|
||||
# 签名 token(ParamTags/ParamBits 每形参 1 字节、RetTag/RetBits)与来源:
|
||||
# 定义片段翻译出的 IR 里 `define <ret> @f(i8* %__repl_ctx__, <P1> %p1, ...)`
|
||||
# 是 ABI 的唯一真相(注解 → LLVM 类型由 codegen 解析),宿主在注册前解析该
|
||||
# define 行并**只接受**可往返重建的 token 集合:
|
||||
# REPL_TY_INT(bits=1/8/16/32/64) / REPL_TY_FLOAT(bits=32/64) / REPL_TY_STR(i8*)
|
||||
# 其余类型(结构体值、数组、多级指针等)⇒ 注册期明确报错,绝不静默按 i32 兜底。
|
||||
# ============================================================
|
||||
# ReplFuncEntry - REPL session function entry (M3; survives fragments, in PermPool).
|
||||
# Maps name -> symbol + fragment no + slot offset + signature token for indirect calls.
|
||||
novirtual class ReplFuncEntry:
|
||||
"""REPL 会话函数条目:名字 + 导出符号名 + ctx 函数指针槽 + 签名 token"""
|
||||
Name: str # 会话函数名(= 用户写的裸名;PermPool 深拷贝,NUL 结尾)
|
||||
SymName: str # 片段 DLL 里的导出符号名(PermPool 深拷贝;M3 = 裸名)
|
||||
FragNo: int # 定义该函数的片段号(重定义时更新为最新片段号)
|
||||
SlotOff: int # ctx 内函数指针槽偏移(REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
|
||||
"""REPL session function entry: name + export symbol + ctx function-pointer slot + signature token"""
|
||||
Name: str # Session function name (= the bare name the user wrote; PermPool copy)
|
||||
SymName: str # Export symbol name in the fragment DLL (PermPool copy; M3 = bare name)
|
||||
FragNo: int # Fragment number defining this function (updated on redefinition)
|
||||
SlotOff: int # Function pointer slot offset within ctx (REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
|
||||
RetTag: int # REPL_TY_VOID / REPL_TY_INT / REPL_TY_FLOAT / REPL_TY_STR
|
||||
RetBits: int # 对齐 ReplVarEntry.Bits 的口径(VOID 时为 0)
|
||||
ParamCount: int # 形参数(**不含**注入的 ctx 首形参)
|
||||
ParamTags: str # PermPool 字节数组(每形参 1 字节 tag,REPL_FUNC_PARAM_MAX 个)
|
||||
ParamBits: str # PermPool 字节数组(每形参 1 字节 bits,REPL_FUNC_PARAM_MAX 个)
|
||||
RetBits: int # Same measure as ReplVarEntry.Bits (0 for VOID)
|
||||
ParamCount: int # Param count (NOT including the injected ctx first param)
|
||||
ParamTags: str # PermPool byte array (1 tag per param, REPL_FUNC_PARAM_MAX entries)
|
||||
ParamBits: str # PermPool byte array (1 bits byte per param, REPL_FUNC_PARAM_MAX entries)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ReplCtx - REPL 会话上下文(宿主建、翻译器读)
|
||||
#
|
||||
# 开关语义:trans.ReplCtx != None ⇒ REPL 模式(仅 Repl.vp 翻译片段前置位,
|
||||
# 翻译后清空)。Gen 侧全部拦截点都以此字段为唯一守卫——普通编译路径下该字段
|
||||
# 恒为 None,逐字走原路径。
|
||||
# ============================================================
|
||||
# ReplCtx - REPL session context (host-built, translator-read). trans.ReplCtx != None
|
||||
# enables REPL mode and guards all Gen-side interception points; otherwise the old path is used.
|
||||
novirtual class ReplCtx:
|
||||
"""REPL 模式上下文(宿主持有;翻译器只读)"""
|
||||
Marker: int # REPL_CTX_MARKER(防误传非 ReplCtx 对象)
|
||||
Table: rawptr[ReplVarEntry] # 会话表数组(PermPool,容量 Cap)
|
||||
Count: int # 已登记条目数(= 已分配槽数)
|
||||
Cap: int # 容量(REPL_VAR_CAP)
|
||||
CtxParam: str # 片段函数 ctx 形参名(PermPool 深拷贝)
|
||||
SlotSize: int # 槽步长(REPL_SLOT_SIZE)
|
||||
# 影子条目(宿主建、PermPool):仅供"裸表达式结果"做类型分类用,不占会话槽、
|
||||
# 不进表(Count 不计入),Name 固定为 "<expr>" 以便错误消息可读。
|
||||
"""REPL mode context (host-owned, translator-read-only)"""
|
||||
Marker: int # REPL_CTX_MARKER (guards against a non-ReplCtx object)
|
||||
Table: rawptr[ReplVarEntry] # Session table array (PermPool, capacity Cap)
|
||||
Count: int # Registered entry count (= allocated slots)
|
||||
Cap: int # Capacity (REPL_VAR_CAP)
|
||||
CtxParam: str # Fragment function ctx param name (PermPool copy)
|
||||
SlotSize: int # Slot stride (REPL_SLOT_SIZE)
|
||||
# Shadow entry (host-built, PermPool): only classifies bare-expression results; it
|
||||
# occupies no session slot and is not counted; Name is fixed to "<expr>".
|
||||
Scratch: rawptr[ReplVarEntry]
|
||||
# 宿主提供的永久池:会话表条目名/结构体名等**跨片段存活**的数据必须在此池
|
||||
# 深拷贝(片段的 trans.Pool 行末 reset_to 即回收,直接存其指针会悬垂)。
|
||||
# Permanent pool provided by the host: cross-fragment data (session names, struct
|
||||
# names) must be deep-copied here, since a fragment's trans.Pool is reset each line.
|
||||
Perm: memhub.MemBuddy*
|
||||
# 会话函数表(M3;宿主写、Gen 只读):数组容量 FuncCap,已登记 FuncCount 条。
|
||||
# 条目只在"定义片段 DLL 加载成功且 GetProcAddress 命中"后由宿主追加 ⇒ 片段
|
||||
# 翻译期看到的表恒为"先前片段的已就绪函数"(本片段新定义的同名函数走直接调用)。
|
||||
# Session function table (M3; host-writes, Gen-reads): capacity FuncCap, FuncCount
|
||||
# registered. Entries are appended only after a fragment DLL loads and GetProcAddress hits.
|
||||
FuncTable: rawptr[ReplFuncEntry]
|
||||
FuncCount: int
|
||||
FuncCap: int # 容量(REPL_FUNC_CAP)
|
||||
FuncCap: int # Capacity (REPL_FUNC_CAP)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ReplResult - ctx 头(宿主按字段读;片段按 REPL_RES_*_OFF 字节偏移写)
|
||||
#
|
||||
# 字段顺序/宽度即 ctx 布局:Magic(+0,i32) Version(+4,i32) Kind(+8,i32)
|
||||
# Reserved0(+12,i32) A(+16,i64) B(+24,i64) VarCount(+32,i32) LastFrag(+36,i32)。
|
||||
# 宿主另有"同一头的浮点视图"(把 +16 的 8 字节按 double 读回,见 Repl.vp)。
|
||||
# ============================================================
|
||||
# ReplResult - ctx header (host reads by field; a fragment writes by REPL_RES_*_OFF).
|
||||
# Field order/width is the ctx layout: Magic, Version, Kind, Reserved0, A, B, VarCount, LastFrag.
|
||||
novirtual class ReplResult:
|
||||
"""ctx 固定头(前 64 字节中的前 40 字节;+40..63 为保留)"""
|
||||
Magic: int # REPL_CTX_MAGIC('REPL')
|
||||
"""Fixed ctx header (first 40 of the 64 header bytes; +40..63 reserved)"""
|
||||
Magic: int # REPL_CTX_MAGIC ('REPL')
|
||||
Version: int # REPL_CTX_VERSION
|
||||
Kind: int # REPL_RES_*
|
||||
Reserved0: int # 保留(0)
|
||||
Reserved0: int # Reserved (0)
|
||||
A: int64 # result_a
|
||||
B: int64 # result_b
|
||||
VarCount: int # 已分配槽数(宿主写,供检视)
|
||||
LastFrag: int # 最近执行的片段号(宿主写,供检视)
|
||||
VarCount: int # Allocated slot count (host-written, for inspection)
|
||||
LastFrag: int # Most recent fragment number (host-written, for inspection)
|
||||
@@ -55,13 +55,11 @@ import lib.core.Gen.VarGenMixin as VarGenMixin
|
||||
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Generator - 生成器(双向引用组合架构)
|
||||
# ============================================================
|
||||
# Generator - generator (bidirectional-reference composition architecture)
|
||||
novirtual class Generator:
|
||||
"""生成器:持所有 GenMixin 子对象指针(双向引用组合)"""
|
||||
"""Generator holding pointers to all GenMixin sub-objects (bidirectional composition)."""
|
||||
|
||||
# ---- 子对象指针(46 个,永久池) ----
|
||||
# ---- sub-object pointers (46, permanent pool) ----
|
||||
BaseGen: rawptr[BaseGenMixin.BaseGenMixin]
|
||||
TypeGenBase: rawptr[TypeGenBase.TypeGenBase]
|
||||
TypeGen: rawptr[TypeGenMixin.TypeGenMixin]
|
||||
@@ -112,7 +110,7 @@ novirtual class Generator:
|
||||
|
||||
def __init__(self, pool: memhub.MemBuddy*):
|
||||
if self is None: return
|
||||
# ---- alloc 46 个子对象 + 回指(永久池,fail-fast) ----
|
||||
# ---- alloc 46 sub-objects + back-pointers (permanent pool, fail-fast) ----
|
||||
SubBaseGen: rawptr[BaseGenMixin.BaseGenMixin] = CompilerFactory.factory.PermPool.alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
|
||||
if SubBaseGen is None:
|
||||
stdio.printf("FAIL: BaseGenMixin alloc failed\n")
|
||||
@@ -443,7 +441,7 @@ novirtual class Generator:
|
||||
SubAsyncGen.Gen = self
|
||||
self.AsyncGen = SubAsyncGen
|
||||
|
||||
# ---- 初始化各子对象字段(Init* 方法内联调用) ----
|
||||
# ---- initialize sub-object fields (Init* methods called inline) ----
|
||||
SubBaseGen.InitBaseGen(pool)
|
||||
SubTypeGenBase.InitTypeGen()
|
||||
SubCallGenBase.InitCallGen()
|
||||
@@ -455,7 +453,5 @@ novirtual class Generator:
|
||||
SubAssignGen.InitAssignGen()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 全局生成器单例(组合架构兼容层)
|
||||
# ============================================================
|
||||
# global generator singleton (composition-architecture compatibility layer)
|
||||
_g_gen: Generator* = None
|
||||
+146
-338
@@ -17,71 +17,52 @@ import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ImportsGenMixin - 导入处理 Mixin
|
||||
#
|
||||
# 职责:模块导入表(imported_modules/from_imports)维护、re-export 登记、
|
||||
# 相对导入解析与模块 SHA1 校验。@t.NoVTable:静态绑定(MRO 直接调用)。
|
||||
# ============================================================
|
||||
# ImportsGenMixin - import handling mixin. Maintains the imported_modules/from_imports
|
||||
# tables, re-export registration, relative import resolution and module SHA1 checks.
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 常量(与 HandlesImports 保持一致)
|
||||
# ============================================================
|
||||
# Constants (kept consistent with HandlesImports)
|
||||
define MAX_REEXPORT = 512
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ImportsGenMixin - 导入处理
|
||||
# ============================================================
|
||||
# ImportsGenMixin - import handling
|
||||
novirtual class ImportsGenMixin:
|
||||
"""导入处理 Mixin(P6 方法化)"""
|
||||
"""Import handling mixin."""
|
||||
|
||||
# --- 全局 re-export 映射表(原 _g_reexport_sha1s/_g_reexport_funcs/
|
||||
# _g_reexport_srcs/_g_reexport_count) ---
|
||||
# --- Global re-export map tables (_g_reexport_sha1s/funcs/srcs/count) ---
|
||||
Gen: Generator.Generator*
|
||||
ReexportSha1s: bytes
|
||||
ReexportFuncs: bytes
|
||||
ReexportSrcs: bytes
|
||||
ReexportCount: int
|
||||
|
||||
# --- 导入查找索引(把 O(串长) 的逐字符扫描降为 O(名字长) 哈希查找) ---
|
||||
# imported_modules/_from_imports 是空格拼接的扁平串("a b c" / "n:m[:orig]"),
|
||||
# 长度可达数千字符,而每个 Name 节点都会多次查询。索引与「当前串指针」绑定:
|
||||
# 调用方查询的总是 add_* 刚生成的当前串,故命中索引快路径;其余来源(stub 期
|
||||
# stdlib malloc 缓冲等)指针不匹配 → 回退逐字符扫描,语义完全不变。
|
||||
# 索引表用 PermPool 分配(跨文件/Phase 存活),由对应 add_* 增量维护;key/value
|
||||
# 均为指向该扁平串内部的指针,串在文件级 reset 前始终有效。
|
||||
# --- Import lookup index (turns O(string) scanning into O(name) hashing) ---
|
||||
# Bound to the current string pointer; a mismatch falls back to scanning. PermPool-allocated.
|
||||
CurImportedModulesPtr: str
|
||||
CurImportedModulesNames: hashtable.HashTable*
|
||||
CurFromImportsPtr: str
|
||||
CurFromImportsMods: hashtable.HashTable*
|
||||
CurFromImportsOrigs: hashtable.HashTable*
|
||||
|
||||
# 注意:不声明 CurrentFile 字段——TypeGenMixin 已声明(Generator 展平块
|
||||
# 只有一份,跨父类不去重规则下重复声明会导致字段错位)。本 Mixin 的
|
||||
# set_current_file 与 CurrentFile 读取统一经 HandlesType 委托(延迟 import
|
||||
# 规避 HandlesType↔HandlesImports 循环依赖)。
|
||||
# Note: CurrentFile is not declared here (TypeGenMixin owns it; a duplicate would
|
||||
# misalign fields). Access goes through HandlesType to avoid a circular import.
|
||||
|
||||
# _index_imported_module - 增量维护 imported_modules 查找索引
|
||||
# old_ptr 为追加前的串指针(None = 首个模块),new_ptr 为追加后的新串,
|
||||
# name_ptr 指向新串内的新模块名(key 直接引用串内部,无需复制)。
|
||||
# 仅当 old_ptr 恰为当前索引绑定的串时复用其索引;否则放弃索引绑定并回退
|
||||
# 逐字符扫描(语义不变)。
|
||||
# _index_imported_module - incrementally maintain the imported_modules index.
|
||||
# Reuse the bound index when old_ptr matches, else unbind and fall back to scanning.
|
||||
def _index_imported_module(self, old_ptr: str, new_ptr: str, name_ptr: str) -> None:
|
||||
"""增量更新 imported_modules 索引(复用旧索引或新建,失败则解除绑定)"""
|
||||
"""Incrementally update the imported_modules index, reusing it when possible."""
|
||||
if new_ptr is None or name_ptr is None: return
|
||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if perm is None: return
|
||||
idx: hashtable.HashTable* = None
|
||||
if old_ptr is None:
|
||||
# 新文件/新链首:建全新索引(旧索引随旧串一起废弃)
|
||||
# New file / chain head: build a fresh index (the old one is discarded with its string)
|
||||
idx = hashtable.HashTable(perm)
|
||||
elif old_ptr == self.CurImportedModulesPtr and self.CurImportedModulesNames is not None:
|
||||
# 正常路径:复用当前串的索引并追加(不重建,内存 O(导入数))
|
||||
# Normal path: reuse the current string's index and append (no rebuild, O(imports) memory)
|
||||
idx = self.CurImportedModulesNames
|
||||
else:
|
||||
# 旧串非当前索引串(异常来源):解除绑定,回退逐字符扫描
|
||||
# Old string is not the bound one (unexpected source): unbind and fall back to scanning
|
||||
self.CurImportedModulesPtr = None
|
||||
self.CurImportedModulesNames = None
|
||||
return
|
||||
@@ -89,18 +70,15 @@ novirtual class ImportsGenMixin:
|
||||
self.CurImportedModulesPtr = None
|
||||
self.CurImportedModulesNames = None
|
||||
return
|
||||
# 同名已在(重复导入)则跳过;否则写入(key/value 均指向 new_ptr 内部)
|
||||
# Skip if the name already exists (duplicate import); otherwise write (key/value point inside 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 模块名以最后一条为准。
|
||||
# _index_from_import - incrementally maintain the from_imports index. entry_off is
|
||||
# the new entry's offset; module/orig tables key by local_name (first hit wins).
|
||||
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 索引(复用旧索引或新建,失败则解除绑定)"""
|
||||
"""Incrementally update the from_imports index, reusing it when possible."""
|
||||
if new_ptr is None or local_name is None or module_name is None: return
|
||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if perm is None: return
|
||||
@@ -124,10 +102,8 @@ novirtual class ImportsGenMixin:
|
||||
return
|
||||
local_len: sizet = string.strlen(local_name)
|
||||
mod_len: sizet = string.strlen(module_name)
|
||||
# 哈希键必须是 NUL 结尾串:扁平串内 local 段以 ':' 结尾(非 NUL),直接当键会让
|
||||
# FNV 哈希到 "local:module..." 整段,与查询名 "local" 永不相等 → 索引恒 miss。
|
||||
# 故用 PermPool 分配 NUL 结尾的稳定副本作键(与索引表同寿命),值仍指向扁平串
|
||||
# 内模块名切片。
|
||||
# The hash key must be NUL-terminated: the flat string's local segment ends with ':',
|
||||
# so hashing it directly never matches. Allocate a stable NUL-terminated copy.
|
||||
key_ptr: str = perm.alloc(local_len + 1)
|
||||
if key_ptr is None:
|
||||
self.CurFromImportsPtr = None
|
||||
@@ -138,53 +114,45 @@ novirtual class ImportsGenMixin:
|
||||
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 语义)
|
||||
# star import: the last entry's module name wins (overwrite, matching 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(与逐字符扫描一致)
|
||||
# First occurrence has no alias (or empty alias) -> query returns None, matching scanning
|
||||
idx_o.set_str(key_ptr, None)
|
||||
self.CurFromImportsPtr = new_ptr
|
||||
self.CurFromImportsMods = idx_m
|
||||
self.CurFromImportsOrigs = idx_o
|
||||
|
||||
# ============================================================
|
||||
# 添加已导入模块名
|
||||
# ============================================================
|
||||
# Add an imported module name
|
||||
def add_imported_module(self, pool: memhub.MemBuddy*, imported_modules: str, name: str) -> str:
|
||||
"""记录已导入的模块名,返回新的 imported_modules 字符串指针
|
||||
|
||||
字符串记账走 StubMergerStore.AppendImportedModule(单一真源,与 vpi
|
||||
注入期同源);本方法只额外维护查询索引。
|
||||
"""
|
||||
"""Record an imported module name and return the new imported_modules string."""
|
||||
if name is None: return imported_modules
|
||||
old_len: sizet = 0
|
||||
if imported_modules is not None: old_len = string.strlen(imported_modules)
|
||||
buf: str = StubMergerStore.AppendImportedModule(pool, imported_modules, name)
|
||||
if buf is None: return imported_modules
|
||||
if imported_modules is None:
|
||||
# 索引:首个模块名(key 指向 buf 内部)
|
||||
# Index: first module name (key points inside buf)
|
||||
self._index_imported_module(None, buf, buf)
|
||||
else:
|
||||
# 索引:复用旧串索引并追加新名字(key 指向 buf 内的 name 段)
|
||||
# Index: reuse the old string's index and append the new name (key points inside buf)
|
||||
self._index_imported_module(imported_modules, buf, buf + old_len + 1)
|
||||
return buf
|
||||
|
||||
# ============================================================
|
||||
# 检查模块是否已导入
|
||||
# ============================================================
|
||||
# Check whether a module has been imported
|
||||
def is_module_imported(self, imported_modules: str, name: str) -> int:
|
||||
"""检查模块是否已导入(词边界精确匹配,避免子串误匹配)"""
|
||||
"""Check whether a module is imported, matching on word boundaries."""
|
||||
if name is None or imported_modules is None: return 0
|
||||
# [索引快路径] 传入串即当前索引绑定的串(trans 字段的常态)→ O(名字长) 哈希查;
|
||||
# 指针不匹配(stub 期 malloc 缓冲等)→ 回退下方逐字符扫描,语义不变。
|
||||
# [Index fast path] When the input is the bound current string, do an O(name) hash
|
||||
# lookup; otherwise fall back to the scanning below, semantics unchanged.
|
||||
if imported_modules == self.CurImportedModulesPtr and self.CurImportedModulesNames is not None:
|
||||
if self.CurImportedModulesNames.__contains__(name) != 0: return 1
|
||||
# 索引未命中:绝不据此返回 0(索引不完整会误判"未导入"),
|
||||
# 落到下方原逐字符扫描按真实串内容判定(正确性优先 | 性能次之)。
|
||||
# Index miss never returns 0 (an incomplete index would misreport "not imported");
|
||||
# fall through to scanning on the real string content (correctness first).
|
||||
name_len: sizet = string.strlen(name)
|
||||
cur: str = imported_modules
|
||||
total_len: sizet = string.strlen(imported_modules)
|
||||
@@ -204,24 +172,9 @@ novirtual class ImportsGenMixin:
|
||||
if Matched == 1: return 1
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 检查模块是否已导入或全局已登记(包子模块限定解析用)
|
||||
# ============================================================
|
||||
# Check whether a module is imported or globally registered (package submodule resolution)
|
||||
def is_module_imported_or_known(self, imported_modules: str, name: str) -> int:
|
||||
"""已导入或可访问的模块(包子模块限定解析用)。
|
||||
|
||||
包子模块场景:`import intr` 只注册包名 "intr",但 intr.gdt / intr.idt
|
||||
等子模块由包 __init__.vp 的 from . import X 在全局注册——模块限定
|
||||
调用/属性访问(intr.gdt.init())的 is_module_imported 校验必须放行,
|
||||
否则解析退化到函数名后缀匹配劫持其他模块同名函数
|
||||
(serial.init 劫持 intr.gdt.init → GDT/IDT 未初始化根因)。
|
||||
|
||||
放行条件(保持单层名 fast-fail 语义不变):
|
||||
1. 名字已在 _imported_modules(直接 import);
|
||||
2. 名字在 Sha1Store 全局已登记,且其任一祖先包名已导入
|
||||
(import intr → intr.gdt 可访问;`this.this()` 但 import this
|
||||
被注释 → this 未导入 → 仍 fast-fail 报"模块未导入")。
|
||||
"""
|
||||
"""Return 1 when the module is imported or reachable through an imported package."""
|
||||
if self.is_module_imported(imported_modules, name) != 0: return 1
|
||||
if name is None: return 0
|
||||
if StubMergerStore.LookupSha1ByModName(name) is None: return 0
|
||||
@@ -239,67 +192,52 @@ novirtual class ImportsGenMixin:
|
||||
dot_pos += 1
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 添加 from-import 名称映射
|
||||
# ============================================================
|
||||
# Add a from-import name mapping
|
||||
def add_from_import(self, pool: memhub.MemBuddy*, from_imports: str, local_name: str, module_name: str, original_name: str = None) -> str:
|
||||
"""添加 from-import 映射,返回新的 from_imports 字符串
|
||||
|
||||
格式:
|
||||
无别名: "local_name:module_name"
|
||||
有别名: "local_name:module_name:original_name"
|
||||
(original_name 为源模块中的真实函数名,用于跨模块 SHA1 修饰)
|
||||
|
||||
字符串记账走 StubMergerStore.AppendFromImport(单一真源,与 vpi 注入期
|
||||
同源;按精确长度分配,无旧的 256 缓冲静默截断);本方法只额外维护索引。
|
||||
"""
|
||||
"""Add a from-import mapping and return the new from_imports string."""
|
||||
if local_name is None or module_name is None: return from_imports
|
||||
old_len: sizet = 0
|
||||
if from_imports is not None: old_len = string.strlen(from_imports)
|
||||
buf: str = StubMergerStore.AppendFromImport(pool, from_imports, local_name, module_name, original_name)
|
||||
if buf is None: return from_imports
|
||||
# 索引:首条 entry_off=0;追加时 entry 从 buf[old_len+1] 开始
|
||||
# Index: the first entry has entry_off=0; an appended entry starts at buf[old_len+1]
|
||||
entry_off: sizet = 0
|
||||
if from_imports is not None: entry_off = old_len + 1
|
||||
self._index_from_import(from_imports, buf, entry_off, local_name, module_name, original_name)
|
||||
return buf
|
||||
|
||||
# ============================================================
|
||||
# 查找 from-import 名称 → 返回原始函数名(别名场景)
|
||||
#
|
||||
# 对于 from X import Y as Z,返回 Y(源模块中的真实函数名)。
|
||||
# 无别名时返回 None(local_name 即为原始名)。
|
||||
# ============================================================
|
||||
# Look up a from-import name and return the original function name (alias case):
|
||||
# for `from X import Y as Z` return Y; return None when there is no alias.
|
||||
def lookup_from_import_original(self, from_imports: str, local_name: str) -> str:
|
||||
"""查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到"""
|
||||
"""Return the original function name for a from-import alias, or None."""
|
||||
if local_name is None or from_imports is None: return None
|
||||
# [索引快路径] 传入串即当前索引绑定的串 → O(名字长) 哈希查;否则回退扫描。
|
||||
# orig 表:名存在但存 None = 首次出现即无别名 → 返回 None(与扫描一致)。
|
||||
# [Index fast path] When the input is the bound current string, do an O(name) hash
|
||||
# lookup; else fall back to scanning. orig None means a first occurrence with no alias.
|
||||
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 rawptr(v_o)
|
||||
# 索引未命中 | 值为 None(首个即无别名):绝不据此直接返回 None——
|
||||
# 真实串可能仍含该名,落到下方原逐字符扫描按串内容判定(正确性优先)。
|
||||
# Index miss or None value never returns None directly (the real string may still
|
||||
# contain the name); fall through to scanning (correctness first).
|
||||
name_len: sizet = string.strlen(local_name)
|
||||
cur: str = from_imports
|
||||
ci: sizet = 0
|
||||
total_len: sizet = string.strlen(from_imports)
|
||||
while ci < total_len:
|
||||
# 跳过前导空格
|
||||
# Skip leading spaces
|
||||
while ci < total_len and cur[ci] == ' ': ci += 1
|
||||
if ci >= total_len: break
|
||||
# 找到第一个 ':' 的位置
|
||||
# Find the first ':'
|
||||
colon1: sizet = ci
|
||||
while colon1 < total_len and cur[colon1] != ':' and cur[colon1] != ' ': colon1 += 1
|
||||
if colon1 >= total_len or cur[colon1] != ':': break
|
||||
entry_name_len: sizet = colon1 - ci
|
||||
# 跳过 star import
|
||||
# Skip star imports
|
||||
if entry_name_len == 1 and cur[ci] == '*':
|
||||
ci = colon1
|
||||
while ci < total_len and cur[ci] != ' ': ci += 1
|
||||
continue
|
||||
# 比较名称
|
||||
# Compare the name
|
||||
if entry_name_len == name_len:
|
||||
Matched: int = 1
|
||||
ei: sizet = 0
|
||||
@@ -309,44 +247,34 @@ novirtual class ImportsGenMixin:
|
||||
break
|
||||
ei += 1
|
||||
if Matched == 1:
|
||||
# 找到匹配,检查是否有第三个字段(原始名)
|
||||
# Match found; check for a third field (the original name)
|
||||
pos: sizet = colon1 + 1
|
||||
# 跳过模块名
|
||||
# Skip the module name
|
||||
while pos < total_len and cur[pos] != ':' and cur[pos] != ' ' and cur[pos] != '\0': pos += 1
|
||||
if pos < total_len and cur[pos] == ':':
|
||||
# 有第三个字段: original_name
|
||||
# Third field present: original_name
|
||||
orig_start: sizet = pos + 1
|
||||
orig_end: sizet = orig_start
|
||||
while orig_end < total_len and cur[orig_end] != ' ' and cur[orig_end] != '\0': orig_end += 1
|
||||
orig_len: sizet = orig_end - orig_start
|
||||
if orig_len > 0:
|
||||
orig_buf: str = cur + orig_start
|
||||
# 返回指向内部的指针(调用方需在使用期内保持 from_imports 有效)
|
||||
# Return an interior pointer (caller keeps from_imports alive during use)
|
||||
return orig_buf
|
||||
return None
|
||||
return None
|
||||
# 跳到下一个条目
|
||||
# Advance to the next entry
|
||||
ci = colon1
|
||||
while ci < total_len and cur[ci] != ' ': ci += 1
|
||||
return None
|
||||
|
||||
# ============================================================
|
||||
# 查找 from-import 名称 → 返回模块名或 None
|
||||
#
|
||||
# allow_star_fallback: 是否允许 star import 回退(默认 1=允许)。
|
||||
# 当查询明确模块名(如 "BuildPipeline")时,应传 0 禁用回退,
|
||||
# 避免被 star import 模块名误导(如 from stdint import * 后
|
||||
# 查询 "BuildPipeline" 错误回退到 "stdint")。
|
||||
# ============================================================
|
||||
# Look up a from-import name and return the module name, or None.
|
||||
# allow_star_fallback=1 permits a star-import fallback; pass 0 to disable it.
|
||||
def lookup_from_import(self, from_imports: str, name: str, allow_star_fallback: int = 1) -> str:
|
||||
"""查找 from-import 名称,返回模块名或 None
|
||||
|
||||
支持 star import: 如果 from_imports 中有 "*:module" 条目,
|
||||
且未找到精确名称匹配,且 allow_star_fallback != 0,则返回 star import 的模块名。
|
||||
"""
|
||||
"""Return the module name for a from-import, with optional star fallback."""
|
||||
if name is None or from_imports is None: return None
|
||||
# [索引快路径] 传入串即当前索引绑定的串 → O(名字长) 哈希查;否则回退扫描。
|
||||
# name 恰为 "*" 时与扫描一致:不做精确匹配,直接走 star 回退。
|
||||
# [Index fast path] When the input is the bound current string, do an O(name) hash
|
||||
# lookup; else fall back to scanning. name=="*" skips exact match and goes to 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
|
||||
@@ -356,29 +284,29 @@ novirtual class ImportsGenMixin:
|
||||
if allow_star_fallback != 0:
|
||||
sv_f: t.CPtr = self.CurFromImportsMods.__getitem__("*")
|
||||
if sv_f is not None: return rawptr(sv_f)
|
||||
# 索引精确名 | star 均未命中:绝不据此直接返回 None(索引不完整会漏报
|
||||
# "名已 import",_op_head NameError 即此类),落到下方原逐字符扫描按串内容判定。
|
||||
# Neither exact nor star hit in the index never returns None (an incomplete index
|
||||
# misses imported names, causing _op_head NameError); fall through to scanning.
|
||||
name_len: sizet = string.strlen(name)
|
||||
cur: str = from_imports
|
||||
ci: sizet = 0
|
||||
total_len: sizet = string.strlen(from_imports)
|
||||
star_mod: str = None
|
||||
while ci < total_len:
|
||||
# 跳过前导空格
|
||||
# Skip leading spaces
|
||||
while ci < total_len and cur[ci] == ' ': ci += 1
|
||||
if ci >= total_len: break
|
||||
# 找到 ':' 的位置
|
||||
# Find the ':' position
|
||||
colon_pos: sizet = ci
|
||||
while colon_pos < total_len and cur[colon_pos] != ':' and cur[colon_pos] != ' ': colon_pos += 1
|
||||
if colon_pos >= total_len or cur[colon_pos] != ':': break
|
||||
entry_name_len: sizet = colon_pos - ci
|
||||
# 检测 star import ("*:module")
|
||||
# Detect star import ("*:module")
|
||||
if entry_name_len == 1 and cur[ci] == '*':
|
||||
mod_start: sizet = colon_pos + 1
|
||||
mod_end: sizet = mod_start
|
||||
while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0' and cur[mod_end] != ':': mod_end += 1
|
||||
star_mod = cur + mod_start
|
||||
# 比较名称
|
||||
# Compare the name
|
||||
elif entry_name_len == name_len:
|
||||
Matched: int = 1
|
||||
ei: sizet = 0
|
||||
@@ -390,29 +318,22 @@ novirtual class ImportsGenMixin:
|
||||
if Matched == 1:
|
||||
mod_start2: sizet = colon_pos + 1
|
||||
mod_end2: sizet = mod_start2
|
||||
# 模块名结束于: 空格、null、或第二个':'(别名格式的分隔符)
|
||||
# The module name ends at a space, null, or a second ':' (alias separator)
|
||||
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':': mod_end2 += 1
|
||||
return cur + mod_start2
|
||||
# 跳到下一个条目
|
||||
# Advance to the next entry
|
||||
ci = colon_pos
|
||||
while ci < total_len and cur[ci] != ' ': ci += 1
|
||||
# 未找到精确匹配,仅在允许时回退到 star import
|
||||
# No exact match; fall back to star import only when allowed
|
||||
if allow_star_fallback == 0: return None
|
||||
return star_mod
|
||||
|
||||
# ============================================================
|
||||
# register_reexport - 注册 re-export 映射
|
||||
#
|
||||
# 在 HandleImportFromNames 中调用:当处理 from .X import Y 时,
|
||||
# 记录当前模块(通过 ModuleSha1)re-export 了函数 Y,源模块为 X。
|
||||
#
|
||||
# 幂等:重复注册相同映射不会增加条目。
|
||||
# 内存用 stdlib.malloc 分配(全局存储器,跨 Phase 持久化)。
|
||||
# ============================================================
|
||||
# register_reexport - register a re-export mapping (called from HandleImportFromNames
|
||||
# for `from .X import Y`). Idempotent; memory is stdlib.malloc'd to persist across phases.
|
||||
def register_reexport(self, mod_sha1: str, func_name: str, source_module: str) -> int:
|
||||
"""注册 re-export 映射(幂等),返回 0 成功,-1 失败"""
|
||||
"""Register a re-export mapping; returns 0 on success, -1 on failure."""
|
||||
if mod_sha1 is None or func_name is None or source_module is None: return -1
|
||||
# 懒初始化
|
||||
# Lazy initialization
|
||||
if self.ReexportSha1s is None:
|
||||
self.ReexportSha1s = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 17)
|
||||
self.ReexportFuncs = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 64)
|
||||
@@ -422,17 +343,17 @@ novirtual class ImportsGenMixin:
|
||||
string.memset(self.ReexportFuncs, 0, MAX_REEXPORT * 64)
|
||||
string.memset(self.ReexportSrcs, 0, MAX_REEXPORT * 64)
|
||||
if self.ReexportCount >= MAX_REEXPORT: return -1
|
||||
# 幂等检查:查找是否已存在相同映射
|
||||
# Idempotency check: look for an existing identical mapping
|
||||
for i in range(self.ReexportCount):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
fidx: sizet = sizet(i) * 64
|
||||
if string.strcmp(self.ReexportSha1s + sidx, mod_sha1) == 0:
|
||||
if string.strcmp(self.ReexportFuncs + fidx, func_name) == 0:
|
||||
# 已存在,更新 source_module(以防变化;64 字节槽,strncpy 钳制)
|
||||
# Already exists; update source_module (64-byte slot, strncpy-clamped)
|
||||
string.strncpy(self.ReexportSrcs + fidx, source_module, 63)
|
||||
self.ReexportSrcs[fidx + 63] = '\0'
|
||||
return 0
|
||||
# 添加新条目(槽位定长 17/64 字节,strcpy 无界会越界写相邻槽,全部 strncpy 钳制)
|
||||
# Add a new entry (fixed 17/64-byte slots; unbounded strcpy would overrun, so all strncpy)
|
||||
idx2: sizet = sizet(self.ReexportCount) * 17
|
||||
fidx2: sizet = sizet(self.ReexportCount) * 64
|
||||
string.strncpy(self.ReexportSha1s + idx2, mod_sha1, 16)
|
||||
@@ -444,14 +365,10 @@ novirtual class ImportsGenMixin:
|
||||
self.ReexportCount += 1
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# lookup_reexport - 查找 re-export 映射
|
||||
#
|
||||
# 给定模块 SHA1 和函数名,返回源模块名(如 "ast.lexer")或 None。
|
||||
# 调用方通过 _lookup_module_sha1(source_module) 获取源模块的 SHA1。
|
||||
# ============================================================
|
||||
# lookup_reexport - given a module SHA1 and function name, return the source module
|
||||
# name (e.g. "ast.lexer") or None; the caller resolves its SHA1 separately.
|
||||
def lookup_reexport(self, mod_sha1: str, func_name: str) -> str:
|
||||
"""查找 re-export 映射,返回源模块名或 None"""
|
||||
"""Return the source module name for a re-export mapping, or None."""
|
||||
if mod_sha1 is None or func_name is None: return None
|
||||
if self.ReexportSha1s is None or self.ReexportCount <= 0: return None
|
||||
for i in range(self.ReexportCount):
|
||||
@@ -461,37 +378,17 @@ novirtual class ImportsGenMixin:
|
||||
if string.strcmp(self.ReexportFuncs + fidx, func_name) == 0: return self.ReexportSrcs + fidx
|
||||
return None
|
||||
|
||||
# ============================================================
|
||||
# _resolve_relative_module - 解析相对导入为完整模块名
|
||||
#
|
||||
# 对于 from .X import Y(level=1, module="X")在包 pkg 中:
|
||||
# 解析为 "pkg.X"
|
||||
# 对于 from . import Y(level=1, module=None)在包 pkg 中:
|
||||
# 解析为 "pkg"
|
||||
# 对于 from ..X import Y(level=2, module="X")在包 pkg.sub 中:
|
||||
# 解析为 "pkg.X"(先从 pkg.sub 上溯一级到 pkg,再追加 .X)
|
||||
#
|
||||
# Args:
|
||||
# pool: 内存池
|
||||
# current_package: 当前文件所属包名(如 "mlirlite"),None 表示无包
|
||||
# level: 相对导入级别(0=绝对,1=., 2=..)
|
||||
# module: ImportFrom 的 module 字段(可能为 None)
|
||||
#
|
||||
# Returns:
|
||||
# 解析后的完整模块名;绝对导入(level<=0)直接返回 module;
|
||||
# 无法解析时返回 module(回退到原始值)
|
||||
# ============================================================
|
||||
# _resolve_relative_module - resolve a relative import into a full module name:
|
||||
# `from .X import Y` -> "pkg.X", `from . import Y` -> "pkg", `from ..X` -> "pkg.X".
|
||||
def _resolve_relative_module(self, pool: memhub.MemBuddy*, current_package: str, level: int, module: str) -> str:
|
||||
"""解析相对导入为完整模块名"""
|
||||
"""Resolve a relative import into a full module name."""
|
||||
if level <= 0: return module
|
||||
if current_package is None:
|
||||
# 无包上下文(includes 顶级文件 / App 顶层文件):
|
||||
# `from .X import Y` 的语义 = "同目录/根目录的 X 模块"
|
||||
# (如 includes/viperstring.vp 的 `from .string import *`)。
|
||||
# 直接返回 module 是正确解析,不是降级——禁止 fail-fast。
|
||||
# No package context (top-level includes/App files): `from .X import Y` means
|
||||
# "the X module in the same/root directory", so returning module is correct.
|
||||
return module
|
||||
|
||||
# 从 current_package 开始,上溯 (level-1) 级
|
||||
# Climb (level-1) levels up from current_package
|
||||
pkg: str = current_package
|
||||
up: int = level - 1
|
||||
while up > 0:
|
||||
@@ -505,12 +402,11 @@ novirtual class ImportsGenMixin:
|
||||
found = 1
|
||||
i += 1
|
||||
if found == 0:
|
||||
# 无更多父级(相对导入级别超过包深度,上溯到根目录):
|
||||
# 回退到 module 原始值(根目录模块语义),后续查找失败
|
||||
# 由 import SHA1 检查明确报"模块未找到",此处不误杀。
|
||||
# No more parents (the relative level exceeds the package depth): fall back to
|
||||
# the original module; a later import SHA1 check reports "module not found".
|
||||
pkg = None
|
||||
break
|
||||
# 截断到最后一个 '.' 处
|
||||
# Truncate at the last '.'
|
||||
new_pkg: str = pool.alloc(last_dot + 1)
|
||||
if new_pkg is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"相对导入解析失败:level={level} module='{module}'", "ImportError")
|
||||
@@ -521,12 +417,12 @@ novirtual class ImportsGenMixin:
|
||||
up -= 1
|
||||
|
||||
if module is None:
|
||||
# from . import Y → 模块就是包本身
|
||||
# from . import Y -> the module is the package itself
|
||||
return pkg
|
||||
if pkg is None:
|
||||
# 包已上溯到空(相对导入越界):回退到 module 原始值
|
||||
# The package climbed to empty (relative import out of range): fall back to module
|
||||
return module
|
||||
# 拼接 pkg + "." + module
|
||||
# Concatenate pkg + "." + module
|
||||
pkg_len2: sizet = string.strlen(pkg)
|
||||
mod_len: sizet = string.strlen(module)
|
||||
buf: str = pool.alloc(pkg_len2 + 1 + mod_len + 1)
|
||||
@@ -538,23 +434,13 @@ novirtual class ImportsGenMixin:
|
||||
string.strcpy(buf + pkg_len2 + 1, module)
|
||||
return buf
|
||||
|
||||
# ============================================================
|
||||
# compute_package_from_relpath - 从相对路径计算包名
|
||||
#
|
||||
# 包名 = 文件所在目录路径,将 / 和 \ 替换为 .
|
||||
# 对于顶级文件(无目录分隔符),返回 None
|
||||
#
|
||||
# 示例:
|
||||
# "mlirlite/__init__.vp" → "mlirlite"
|
||||
# "mlirlite/__types.vp" → "mlirlite"
|
||||
# "ast/parser.vp" → "ast"
|
||||
# "ast.vp" → None(顶级文件)
|
||||
# ============================================================
|
||||
# compute_package_from_relpath - derive the package name from a relative path by taking
|
||||
# the directory and replacing / and \ with '.'. Top-level files (no separator) -> None.
|
||||
def compute_package_from_relpath(self, pool: memhub.MemBuddy*, rel_path: str) -> str:
|
||||
"""从相对路径计算包名(目录部分,分隔符替换为 .)"""
|
||||
"""Derive the package name from a relative path by replacing separators with dots."""
|
||||
if rel_path is None: return None
|
||||
rlen: sizet = string.strlen(rel_path)
|
||||
# 找最后一个 / 或 \
|
||||
# Find the last / or \
|
||||
last_sep: sizet = 0
|
||||
found: int = 0
|
||||
i: sizet = 0
|
||||
@@ -565,9 +451,9 @@ novirtual class ImportsGenMixin:
|
||||
found = 1
|
||||
i += 1
|
||||
if found == 0:
|
||||
# 无目录分隔符 → 顶级文件,无包
|
||||
# No directory separator -> top-level file, no package
|
||||
return None
|
||||
# 复制目录部分,将 / 和 \ 替换为 .
|
||||
# Copy the directory part, replacing / and \ with .
|
||||
dir_len: sizet = last_sep
|
||||
buf: str = pool.alloc(dir_len + 1)
|
||||
if buf is None: return None
|
||||
@@ -580,18 +466,12 @@ novirtual class ImportsGenMixin:
|
||||
buf[dir_len] = '\0'
|
||||
return buf
|
||||
|
||||
# ============================================================
|
||||
# set_current_file - 设置当前编译的文件名
|
||||
#
|
||||
# 与 TypeGenMixin.set_current_file 行为一致(写 CurrentFile 字段),
|
||||
# BuildPipeline/Phase1 通过 HandlesImports.set_current_file 委托调用。
|
||||
# 经 HandlesType 委托(延迟 import 规避循环依赖),字段由 TypeGenMixin 持有。
|
||||
# ============================================================
|
||||
# set_current_file - set the file being compiled, matching TypeGenMixin's behavior;
|
||||
# delegated through HandlesType (lazy import avoids a circular dependency).
|
||||
def set_current_file(self, file_path: str) -> None:
|
||||
"""设置当前正在编译的文件名(与 HandlesType.set_current_file 同步调用)"""
|
||||
# 文件级失效:索引绑定的是上一文件 arena 内的串指针,本文件 reset 后该
|
||||
# 地址可能被新分配复用(如 _merge_template_imports 的合并串)→ 必须解绑,
|
||||
# 否则可能命中陈旧索引返回错误结果。本文件的索引由后续 add_* 重新建立。
|
||||
"""Set the file currently being compiled, synced with HandlesType."""
|
||||
# File-level invalidation: the index binds a string pointer from the previous file's
|
||||
# arena that may be reused after reset, so unbind; add_* rebuilds it for this file.
|
||||
self.CurImportedModulesPtr = None
|
||||
self.CurImportedModulesNames = None
|
||||
self.CurFromImportsPtr = None
|
||||
@@ -599,24 +479,17 @@ novirtual class ImportsGenMixin:
|
||||
self.CurFromImportsOrigs = None
|
||||
self.Gen.TypeGenResolve.set_current_file(file_path)
|
||||
|
||||
# ============================================================
|
||||
# _report_import_error - 本地实现 Python 风格报错(避免循环依赖)
|
||||
#
|
||||
# 无法访问 HandlesType.fatal_error(HandlesType import 了本模块,
|
||||
# 若本模块再 import HandlesType 会形成循环依赖),这里用简化格式:
|
||||
# Traceback (most recent call last):
|
||||
# File "<当前文件>", line <N>
|
||||
# ImportError: <msg>
|
||||
# ============================================================
|
||||
# _report_import_error - local Python-style error reporter to avoid a circular import
|
||||
# with HandlesType. Emits a Traceback/File/ImportError block and aborts.
|
||||
def _report_import_error(self, node: ast.AST*, msg: str) -> None:
|
||||
"""以 Python 风格报错并退出(本地实现,避免循环依赖)"""
|
||||
"""Report a Python-style import error and abort."""
|
||||
stdio.printf("Traceback (most recent call last):\n")
|
||||
lineno: int = 0
|
||||
if node is not None: lineno = node.lineno
|
||||
file_name: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
if file_name is None: file_name = "<unknown>"
|
||||
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||||
# 显示行内内容(读源文件第 lineno 行的实际代码,便于快速定位)
|
||||
# Show the source line's content (read line lineno for quick location)
|
||||
if lineno > 0 and file_name is not None:
|
||||
f_err: rawptr[fileio.File] = fileio.File(file_name, fileio.MODE.R)
|
||||
if not f_err.closed:
|
||||
@@ -637,17 +510,13 @@ novirtual class ImportsGenMixin:
|
||||
stdlib.free(line_buf_err)
|
||||
f_err.close()
|
||||
stdio.printf("ImportError: %s\n", msg)
|
||||
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印),批处理下终止
|
||||
# Termination convergence: longjmp back to the REPL loop in a session, else abort
|
||||
VLogger.repl_abort()
|
||||
|
||||
# ============================================================
|
||||
# _is_local_module_file - 当前文件同目录存在 {mod_name}.vp/.py
|
||||
#
|
||||
# 多文件项目(如 Test/G4/App 的 caller1.vp import g4node)的兄弟模块
|
||||
# 不在 includes 扫描存储器中(SHA1 在项目源文件列表),检查应豁免。
|
||||
# ============================================================
|
||||
# _is_local_module_file - a sibling {mod_name}.vp/.py exists next to the current file
|
||||
# (e.g. caller1.vp importing g4node); such modules are exempt from the SHA1 check.
|
||||
def _is_local_module_file(self, mod_name: str) -> int:
|
||||
"""当前文件同目录存在 {mod_name}.vp/.py → 项目内兄弟模块"""
|
||||
"""Return 1 when a sibling {mod_name}.vp/.py exists next to the current file."""
|
||||
cur: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
if cur is None or mod_name is None: return 0
|
||||
cur_len: sizet = string.strlen(cur)
|
||||
@@ -663,7 +532,7 @@ novirtual class ImportsGenMixin:
|
||||
path_len: sizet = sep_pos + mod_len + 5
|
||||
path_buf: bytes = stdlib.malloc(path_len + 1)
|
||||
if path_buf is None: return 0
|
||||
# .vp 文件(Viper 源仅 .vp,不再回退 .py)
|
||||
# .vp file (Viper sources are .vp only, no .py fallback)
|
||||
string.strncpy(path_buf, cur, sep_pos)
|
||||
path_buf[sep_pos] = '\0'
|
||||
string.strcat(path_buf, mod_name)
|
||||
@@ -676,32 +545,19 @@ novirtual class ImportsGenMixin:
|
||||
stdlib.free(path_buf)
|
||||
return 0
|
||||
|
||||
# _check_import_sha1 - 导入时校验模块 SHA1 是否已注册(fast-fail)
|
||||
# includes 路径配置错误时扫描结果为 0、模块不注册 SHA1,若延迟到实际跨模块调用才报错
|
||||
# 会误导("跨模块调用 'bd.alloc' 找不到模块 SHA1"),故在 import 语句处立即检测。
|
||||
# t/c 是内部库,不参与 includes 扫描,豁免检查。
|
||||
# _check_import_sha1 - validate at import time that the module SHA1 is registered
|
||||
# (fast-fail). Reports immediately instead of misleading the user later.
|
||||
def _check_import_sha1(self, node: ast.AST*, mod_name: str) -> int:
|
||||
"""导入时校验模块 SHA1 是否已注册,未注册则立即报错退出
|
||||
|
||||
Args:
|
||||
node: Import/ImportFrom AST 节点(提供行号)
|
||||
mod_name: 导入的模块名(如 "memhub"、"w32.fileio")
|
||||
|
||||
Returns:
|
||||
1=检查通过;模块未注册时直接报错退出(不会返回 0)
|
||||
"""
|
||||
"""Fail-fast when an imported module's SHA1 is not registered."""
|
||||
if mod_name is None: return 1
|
||||
# t/c 是内部库,不参与 includes 扫描,豁免检查
|
||||
# ctraits 是纯编译期宏库(.vpi 后缀,IncludesScanner 不扫描 .vpi),豁免检查
|
||||
# viperbuild 是编译器内建宏库(无磁盘模块、无 .vpi;配置由 Pass 0 的
|
||||
# lib.core.builtin.ViperBuild 从依赖闭包提取),同样豁免检查
|
||||
# t/c are internal libraries exempt from includes scanning; so are ctraits (a .vpi
|
||||
# compile-time macro lib) and viperbuild (a compiler builtin macro lib).
|
||||
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0 or string.strcmp(mod_name, "ctraits") == 0: return 1
|
||||
if string.strcmp(mod_name, "viperbuild") == 0: return 1
|
||||
mod_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
|
||||
if mod_sha1 is None:
|
||||
# App 兄弟模块豁免:当前文件同目录存在 {mod_name}.vp/.py
|
||||
# (如 Test/G4/App/caller1.vp import g4node——同项目源文件,
|
||||
# SHA1 在项目文件列表而非 includes 扫描存储器,不应报错)
|
||||
# App sibling exemption: a {mod_name}.vp/.py exists next to the current file
|
||||
# (SHA1 lives in the project file list, not the includes store).
|
||||
if self._is_local_module_file(mod_name) != 0: return 1
|
||||
err_buf: str = stdlib.malloc(256)
|
||||
if err_buf is not None:
|
||||
@@ -710,23 +566,9 @@ novirtual class ImportsGenMixin:
|
||||
else: self._report_import_error(node, "导入模块 SHA1 未注册(includes 扫描未找到该模块)")
|
||||
return 1
|
||||
|
||||
# ============================================================
|
||||
# translate_import - 处理 import 语句,更新 trans._imported_modules
|
||||
# (迁移自 HandlesImports.ImportsHandle.HandleImport)
|
||||
# ============================================================
|
||||
# translate_import - handle import statements and update trans._imported_modules
|
||||
def translate_import(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""处理 import 语句,返回 0
|
||||
|
||||
记账走 StubMergerStore.NotePlainImport(**该约定的唯一实现**):与 vpi
|
||||
注入期(VpiReader 顶层遍历)同口径,包含
|
||||
· `import X as Y` → from_imports 追加 "Y:X"(别名反查模块真名的唯一
|
||||
入口,见 StubMergerStore.LookupModNameByAlias);
|
||||
· `import a.b.c` → imported_modules 追加顶层包名 "a"。
|
||||
两侧各自只保留特有副作用(此处:导入 SHA1 校验)。
|
||||
注:NotePlainImport 为纯记账,不再续接 imported/from 两个查询索引 ⇒ 这两
|
||||
个串上的查找走 is_module_imported / lookup_from_import 的逐字符扫描回退
|
||||
(与 vpi 注入期上下文同款,正确性不受影响)。
|
||||
"""
|
||||
"""Translate an import statement and update trans._imported_modules."""
|
||||
imp: ast.Import* = ast.Import*(node)
|
||||
if imp is None: return 0
|
||||
names: list[ast.AST*]* = imp.names
|
||||
@@ -738,42 +580,29 @@ novirtual class ImportsGenMixin:
|
||||
for ni in range(nn):
|
||||
alias: ast.Alias* = ast.Alias*(names.get(ni))
|
||||
if alias is not None and alias.name is not None:
|
||||
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
|
||||
# declare_only=1/2 是 struct 预注册/import 扫描阶段:此时 SHA1 表
|
||||
# 尚未包含后续才追加的模块(如 App 源文件),检查会造成误报。
|
||||
# Validate the module SHA1 at import time (fast-fail, full translation only):
|
||||
# declare_only=1/2 is the pre-registration scan and would false-positive.
|
||||
if trans._declare_only == 0: self._check_import_sha1(node, alias.name)
|
||||
StubMergerStore.NotePlainImport(pool_val, ctx, alias.name, alias.asname)
|
||||
trans._imported_modules = ctx.ImportedModules
|
||||
trans._from_imports = ctx.FromImports
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# translate_import_from_module - 处理 from X import Y 的模块部分
|
||||
# (迁移自 HandlesImports.ImportsHandle.HandleImportFromModule)
|
||||
#
|
||||
# 对相对导入(level > 0),使用 trans.CurrentPackage 解析为
|
||||
# 完整模块名(如 __types → mlirlite.__types),确保 .deps.txt
|
||||
# 记录的模块名与 SHA1 映射表一致。
|
||||
# ============================================================
|
||||
# translate_import_from_module - handle the module part of `from X import Y`.
|
||||
# Relative imports (level>0) resolve via trans.CurrentPackage to a full module name.
|
||||
def translate_import_from_module(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""处理 from X import Y 的模块部分,更新 trans._imported_modules"""
|
||||
"""Translate the module part of a from-import statement."""
|
||||
impf: ast.ImportFrom* = ast.ImportFrom*(node)
|
||||
if impf is None: return 0
|
||||
# 解析模块名:相对导入需补充包前缀
|
||||
# Resolve the module name: relative imports need the package prefix
|
||||
resolved: str = self._resolve_relative_module(trans.Pool, trans.CurrentPackage, impf.level, impf.module)
|
||||
if resolved is not None:
|
||||
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
|
||||
# from . import Y(impf.module is None):resolved 是包目录名
|
||||
# (lib.core.Gen.Call 等无 __init__.vp 的目录包也允许这样导入,
|
||||
# 见 CallGenCall.vp L12 from . import CallGenBase),目录本身不是
|
||||
# 模块文件、无 SHA1 → 跳过包级校验;真实目标子模块(pkg.Y)由
|
||||
# 下方 L759-762 逐个校验(LookupSha1ByModName / 本地兄弟文件)。
|
||||
# Validate the module SHA1 at import time (fast-fail, full translation only). For
|
||||
# `from . import Y` resolved is a package directory, not a file, so skip the check.
|
||||
if trans._declare_only == 0 and impf.module is not None: self._check_import_sha1(node, resolved)
|
||||
trans._imported_modules = self.add_imported_module(trans.Pool, trans._imported_modules, resolved)
|
||||
# from . import Y(impf.module is None):resolved 是包名(vpsdk),
|
||||
# 各 name 是包的子模块 vpsdk.Y。须把完整子模块名也加入 _imported_modules,
|
||||
# 否则别名 "syscall" 精确匹配不到包名 "vpsdk" → is_module_imported 返回 0
|
||||
# → syscall._syscall1 / syscall.LOAD_SO 跨模块解析失败。
|
||||
# For `from . import Y` resolved is the package name; each name is a submodule.
|
||||
# Add the full submodule names or cross-module resolution of syscall.* fails.
|
||||
if impf.module is None:
|
||||
names_m: list[ast.AST*]* = impf.names
|
||||
if names_m is not None:
|
||||
@@ -788,35 +617,19 @@ novirtual class ImportsGenMixin:
|
||||
string.strcpy(jb_m, resolved)
|
||||
jb_m[rl_m] = '.'
|
||||
string.strcpy(jb_m + rl_m + 1, alias_m.name)
|
||||
# from . import Y 的 Y 不一定是包子模块文件:可能是包
|
||||
# __init__.vp 中定义的符号(类/常量,如 JsonValue),无独立
|
||||
# 文件、不注册 SHA1。仅当完整名已注册 SHA1(真子模块)或是
|
||||
# 本地文件时才校验+登记;否则跳过,符号映射由
|
||||
# translate_import_from_names 的 from-import / re-export 处理。
|
||||
# Y may be a package __init__.vp symbol (class/constant) with no
|
||||
# file or SHA1; validate/register only for real submodules or local files.
|
||||
if (StubMergerStore.LookupSha1ByModName(jb_m) is not None or self._is_local_module_file(alias_m.name) != 0):
|
||||
if trans._declare_only == 0: self._check_import_sha1(node, jb_m)
|
||||
trans._imported_modules = self.add_imported_module(trans.Pool, trans._imported_modules, jb_m)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# translate_import_from_names - 处理 from X import Y 的名称部分
|
||||
# (迁移自 HandlesImports.ImportsHandle.HandleImportFromNames)
|
||||
#
|
||||
# 同样使用解析后的完整模块名,确保 from-import 映射
|
||||
# (如 LLVMType:mlirlite.__types)能正确查到 SHA1。
|
||||
# ============================================================
|
||||
# _CheckStaticFromImport - static 符号 from-import 拦截
|
||||
#
|
||||
# `from mod import name` 命中源模块登记的 static 符号 → fail-fast(与
|
||||
# ExprGenAttr 的 `mod.name` 取值拦截、CallGenMixin 的 `mod.fn()` 调用拦截同源,
|
||||
# 覆盖 from-import 形态)。用源码原名判表——定义侧登记用的也是裸名。
|
||||
# 独立成函数:内联进 translate_import_from_names 会干扰其类型推断(同款处理
|
||||
# 见 ExprGenAttr._CheckStaticCrossModule 与 CallGenMixin._CheckStaticCallCrossModule)。
|
||||
# ============================================================
|
||||
# translate_import_from_names handles the name part of `from X import Y` below.
|
||||
# _CheckStaticFromImport fail-fasts on `from mod import name` hitting a static symbol.
|
||||
def _CheckStaticFromImport(self, trans: HT.Translator*, fi_target: str, orig_name: str, node: ast.AST*) -> int:
|
||||
if trans is None or fi_target is None or orig_name is None: return 0
|
||||
if trans.ModuleSha1 is None: return 0
|
||||
# 快速短路:项目内无 static 符号时零开销(不查 sha1、不扫表)
|
||||
# Fast short-circuit: zero overhead when the project has no static symbols
|
||||
if self.Gen.CallGenBase.StaticCount <= 0: return 0
|
||||
sf_sha1: str = self.Gen.StructGen._resolve_module_sha1(fi_target, trans._from_imports)
|
||||
if sf_sha1 is None: return 0
|
||||
@@ -827,10 +640,10 @@ novirtual class ImportsGenMixin:
|
||||
|
||||
|
||||
def translate_import_from_names(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""处理 from X import Y 的名称部分,更新 trans._from_imports"""
|
||||
"""Translate the name part of a from-import statement."""
|
||||
impf: ast.ImportFrom* = ast.ImportFrom*(node)
|
||||
if impf is None: return 0
|
||||
# 解析模块名:相对导入需补充包前缀
|
||||
# Resolve the module name: relative imports need the package prefix
|
||||
resolved: str = self._resolve_relative_module(trans.Pool, trans.CurrentPackage, impf.level, impf.module)
|
||||
if resolved is not None:
|
||||
names: list[ast.AST*]* = impf.names
|
||||
@@ -844,10 +657,8 @@ novirtual class ImportsGenMixin:
|
||||
if alias.asname is not None:
|
||||
local_name = alias.asname
|
||||
orig_name = alias.name
|
||||
# from . import Y(impf.module is None):_resolve_relative_module
|
||||
# 返回的是包名(如 vpsdk),但 Y 是包的子模块 vpsdk.Y。须映射
|
||||
# local_name → 完整子模块名,否则 syscall.LOAD_SO 跨模块 CDefine /
|
||||
# syscall._syscall1 跨模块调用在包里找不到。
|
||||
# For `from . import Y`, _resolve_relative_module returns the package name
|
||||
# but Y is a submodule, so map local_name -> the full submodule name.
|
||||
fi_target: str = resolved
|
||||
if impf.module is None and resolved is not None:
|
||||
fl_t: sizet = string.strlen(resolved)
|
||||
@@ -859,27 +670,24 @@ novirtual class ImportsGenMixin:
|
||||
string.strcpy(jb_t + fl_t + 1, alias.name)
|
||||
fi_target = jb_t
|
||||
trans._from_imports = self.add_from_import(trans.Pool, trans._from_imports, local_name, fi_target, orig_name)
|
||||
# static 符号(禁止跨模块使用):from mod import name 命中源
|
||||
# 模块登记的 static 符号 → fail-fast。逻辑收敛在
|
||||
# _CheckStaticFromImport 内(不内联——内联新增分支会干扰本函数
|
||||
# 的类型推断,与 ExprGenAttr/CallGenMixin 同款处理)。
|
||||
# Static symbols (forbidden cross-module): `from mod import name` hitting one
|
||||
# fail-fasts. Logic stays in _CheckStaticFromImport (inlining breaks inference).
|
||||
self._CheckStaticFromImport(trans, fi_target, alias.name, node)
|
||||
# 命名空间隔离:from-import 的名称标记为可见结构体
|
||||
# Namespace isolation: mark the from-imported name as a visible struct
|
||||
self.Gen.StructGen.add_visible_struct(trans.Pool, local_name)
|
||||
# 注册 re-export 映射:当前模块 re-export 了 local_name,
|
||||
# 源模块为 fi_target(from . import Y 时是子模块名 vpsdk.Y,
|
||||
# 非包名)。使跨模块调用 module.func() 能解析到正确 SHA1
|
||||
# Register the re-export mapping (current module re-exports local_name from
|
||||
# fi_target) so cross-module module.func() resolves to the right SHA1.
|
||||
if trans.ModuleSha1 is not None: self.register_reexport(trans.ModuleSha1, local_name, fi_target)
|
||||
return 0
|
||||
|
||||
def InitImportsGen(self) -> int:
|
||||
"""初始化 ImportsGen 字段(构造时由 Generator 调用)"""
|
||||
"""Initialize the ImportsGen fields, called by Generator at construction."""
|
||||
if self is None: return 1
|
||||
self.ReexportSha1s = None
|
||||
self.ReexportFuncs = None
|
||||
self.ReexportSrcs = None
|
||||
self.ReexportCount = 0
|
||||
# 导入查找索引字段(索引表由 add_* 增量创建,PermPool 分配)
|
||||
# Import lookup index fields (created incrementally by add_*, PermPool-allocated)
|
||||
self.CurImportedModulesPtr = None
|
||||
self.CurImportedModulesNames = None
|
||||
self.CurFromImportsPtr = None
|
||||
|
||||
+219
-411
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -17,24 +17,21 @@ import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StmtGenDeclMixin - StmtGen 拆分 2/3:声明/枚举/nonlocal 访问
|
||||
# ============================================================
|
||||
# StmtGenDeclMixin - StmtGen split 2/3: declaration/enum/nonlocal access
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 常量
|
||||
# ============================================================
|
||||
# Constants
|
||||
define ENUM_MAX = 64
|
||||
define ENUM_MEMBER_MAX = 128
|
||||
|
||||
# ============================================================
|
||||
# StmtGenDeclMixin - 声明/枚举/nonlocal(中层级)
|
||||
# ============================================================
|
||||
# StmtGenDeclMixin - declaration/enum/nonlocal (mid layer)
|
||||
novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
"""声明/枚举/nonlocal 访问(StmtGen 拆分 2/3,继承 Cif)"""
|
||||
"""Declaration/enum/nonlocal access (StmtGen split 2/3, extends Cif)"""
|
||||
|
||||
def translate_global(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 global 语句:记录 global 变量名"""
|
||||
"""Translate a global statement and record the global variable names"""
|
||||
gn: rawptr[ast.Global] = rawptr[ast.Global](node)
|
||||
if gn is None: return 0
|
||||
names: list[ast.AST*]* = gn.names
|
||||
@@ -48,10 +45,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 0
|
||||
|
||||
def translate_nonlocal(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 nonlocal 语句:记录 nonlocal 变量名及其类型
|
||||
|
||||
类型信息通过查找父作用域的 alloca 获取(用于 env 指针数组方案的类型恢复)。
|
||||
"""
|
||||
"""Translate a nonlocal statement and record names with their types"""
|
||||
nl: rawptr[ast.Nonlocal] = rawptr[ast.Nonlocal](node)
|
||||
if nl is None: return 0
|
||||
names: list[ast.AST*]* = nl.names
|
||||
@@ -62,14 +56,13 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(nm_node)
|
||||
if nm.id is not None:
|
||||
# 查找父作用域中的变量 alloca,获取类型信息
|
||||
# Look up the variable alloca in the parent scope to get its type
|
||||
var_ty: mlirlite.LLVMType* = None
|
||||
var_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if var_alloca is not None and var_alloca.Ty is not None:
|
||||
var_ty = var_alloca.Ty.Pointee
|
||||
# Void 类型视为无效(Pointee 访问异常时可能返回 Void),
|
||||
# 类型无效直接报错终止(fail-fast),避免生成
|
||||
# "bitcast ... to void*" / "load void, void*" 非法 IR
|
||||
# Treat Void as invalid (Pointee access can return Void); report and abort
|
||||
# rather than emit illegal IR like "bitcast ... to void*" / "load void, void*".
|
||||
if var_ty is not None:
|
||||
match var_ty:
|
||||
case mlirlite.LLVMType.Void():
|
||||
@@ -80,18 +73,15 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 0
|
||||
|
||||
def translate_expr_stmt(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译表达式语句(printf 调用等)"""
|
||||
"""Translate an expression statement (e.g. a printf call)"""
|
||||
ex: rawptr[ast.Expr] = rawptr[ast.Expr](node)
|
||||
if ex is None: return 0
|
||||
call_node: ast.AST* = ex.value
|
||||
if call_node is None: return 0
|
||||
ck: int = call_node.kind()
|
||||
if ck != ast.ASTKind.Call:
|
||||
# REPL 模式(M2):片段函数本体内的裸表达式语句(如 1+2 / x / x + 1)
|
||||
# 求值后写入 ctx 结果区,由宿主回显。调用表达式仍走下方原路径
|
||||
# (执行 + 丢弃返回值,同 print/printf 语义)。
|
||||
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ "表达式语句除调用外一律忽略"
|
||||
# 的原行为逐字不变。
|
||||
# REPL mode (M2): bare expression statements in a fragment body (e.g. 1+2 / x / x + 1)
|
||||
# are written to the ctx result area. Guarded by trans.ReplCtx; non-REPL behavior unchanged.
|
||||
if trans is not None and trans.ReplCtx is not None and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0: self.Gen.VarGen.repl_expr_result(trans, call_node)
|
||||
return 0
|
||||
|
||||
@@ -101,21 +91,15 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
|
||||
func_name: str = self.Gen.ExprGenBase.get_func_name(func_node)
|
||||
if func_name is None:
|
||||
# 函数名解析失败:直接报错终止(fail-fast)
|
||||
# Function name resolution failed: report and abort (fail-fast)
|
||||
self.Gen.TypeGenErr.fatal_error(call_node, "表达式语句的函数名解析失败", "SyntaxError")
|
||||
|
||||
# printf / print 走特殊路径(print 是类型感知格式化,不映射到 printf)
|
||||
# printf / print take special paths (print is type-aware formatting, not mapped to printf)
|
||||
if string.strcmp(func_name, "printf") == 0: self.Gen.CallGenLeafKit.translate_printf_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
||||
elif string.strcmp(func_name, "print") == 0: self.Gen.CallGenLeafKit.translate_print_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
||||
else:
|
||||
# 通用函数调用(返回值丢弃)
|
||||
# M3:裸名调用**会话函数**(跨片段)时额外把返回值写进 ctx 结果区回显
|
||||
# (M2 的限制条目"调用表达式的结果不回显"对会话函数放开——用户在
|
||||
# `>>> f(41)` 后期望看到 `=> 42`)。结果区的类型分类直接用会话函数表里
|
||||
# 记录的返回类型 token(宿主从定义片段 IR 解析而来,是定义端 ABI 真相),
|
||||
# 不用影子条目推断 ⇒ 与调用点签名同源。
|
||||
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 原"执行 + 丢弃返回值"逐字不变;
|
||||
# 仅裸名(Name)且表中登记的会话函数走本分支(printf/print 等在上方已分流)。
|
||||
# Generic call (result discarded). M3: a bare-name call to a session function also
|
||||
# writes the result to ctx (classified by the session table's return token).
|
||||
if trans is not None and trans.ReplCtx is not None and func_node.kind() == ast.ASTKind.Name and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
|
||||
sfn_ent: rawptr[GenTypes.ReplFuncEntry] = self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name)
|
||||
if sfn_ent is not None and sfn_ent.RetTag != GenTypes.REPL_TY_VOID:
|
||||
@@ -129,16 +113,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 0
|
||||
|
||||
def pre_scan_allocas(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""预扫描语句中的 AnnAssign,提前创建 alloca
|
||||
|
||||
递归分支/循环/异常体:if/else/for/while/try 分支内的 AnnAssign 局部
|
||||
也必须提前注册到 entry alloca——否则闭包隐式捕获扫描(step 8c)
|
||||
在分支局部未注册时会误判为外层函数捕获(Terminal _build_path 的
|
||||
i/j/cwd_len 被当 nonlocal 捕获 → 循环计数器走 env/父函数存储 →
|
||||
跨函数 SSA 错乱,llc "instruction expected to be numbered")。
|
||||
|
||||
返回新增的变量数(调用方均忽略返回值)。
|
||||
"""
|
||||
"""Pre-scan AnnAssign statements and create their entry allocas early"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.AnnAssign: return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
|
||||
@@ -211,7 +186,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 0
|
||||
|
||||
def pre_scan_scope_decls(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""预扫描 Global/Nonlocal 声明,提前注册作用域名称"""
|
||||
"""Pre-scan Global/Nonlocal declarations and register scope names early"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Global: return self.translate_global(trans, node)
|
||||
@@ -219,20 +194,20 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 0
|
||||
|
||||
def translate_break(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 break 语句:跳转到 break 目标(循环 end / match end)"""
|
||||
"""Translate a break statement: branch to the break target"""
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
func: mlirlite.Function* = trans._cur_func
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
|
||||
if builder is None or func is None: return 0
|
||||
|
||||
# 发射 br 到 break 目标(_break_bb 由循环 / switch / match 的 case 生成侧设置)
|
||||
# Emit br to the break target (_break_bb set by the loop / switch / match case side)
|
||||
if trans._break_bb is None:
|
||||
# fail-fast:没有循环 / switch / match 时 break 无处可跳,禁止静默无效果
|
||||
# fail-fast: with no loop / switch / match there is nowhere to jump; never silently no-op
|
||||
self.Gen.TypeGenErr.fatal_error(node, "break 只能用在循环或 switch/match 的 case 体内(当前没有可跳出的目标)", "SyntaxError")
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, trans._break_bb)
|
||||
|
||||
# 创建死代码 BB,用于后续语句(break 后面的代码不可达)
|
||||
# Create a dead-code BB for following statements (code after break is unreachable)
|
||||
cnt: int = trans._label_counter
|
||||
trans._label_counter = cnt + 1
|
||||
name_buf: str = pool.alloc(32)
|
||||
@@ -242,33 +217,27 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 0
|
||||
|
||||
def translate_fallthrough(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 `fallthrough` 语句:本身不产出任何 IR
|
||||
|
||||
语义由 switch 生成侧消费——case 体含该标记时末尾不自动跳出 switch,直接
|
||||
落到下一个 case 体(对齐 C 的 [[fallthrough]])。合法位置**只有 switch 的
|
||||
case 语句体**;写进 match 的 case 体(match 完全禁止 fallthrough)或
|
||||
switch 之外一律 fail-fast,绝不静默无效果。
|
||||
"""
|
||||
"""Translate a fallthrough statement: emits no IR, consumed by the switch side"""
|
||||
if trans is None: return 0
|
||||
if trans._in_switch_case == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "fallthrough 只能用在 switch 的 case 体内(match 完全禁止 fallthrough," "语义是落到下一个 case)", "SyntaxError")
|
||||
return 0
|
||||
|
||||
def translate_continue(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 continue 语句:跳转到循环 cond/incr 块"""
|
||||
"""Translate a continue statement: branch to the loop cond/incr block"""
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
func: mlirlite.Function* = trans._cur_func
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
|
||||
if builder is None or func is None: return 0
|
||||
|
||||
# 发射 br 到 continue 目标
|
||||
# Emit br to the continue target
|
||||
if trans._continue_bb is None:
|
||||
# fail-fast:循环之外的 continue 无处可跳,禁止静默无效果
|
||||
# fail-fast: a continue outside a loop has nowhere to jump; never silently no-op
|
||||
self.Gen.TypeGenErr.fatal_error(node, "continue 只能用在循环体内(当前没有可继续的循环)", "SyntaxError")
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, trans._continue_bb)
|
||||
|
||||
# 创建死代码 BB,用于后续语句
|
||||
# Create a dead-code BB for following statements
|
||||
cnt: int = trans._label_counter
|
||||
trans._label_counter = cnt + 1
|
||||
name_buf: str = pool.alloc(32)
|
||||
@@ -278,16 +247,15 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 0
|
||||
|
||||
def get_stmt_kind_name(self, node: ast.AST*) -> str:
|
||||
"""获取语句类型名"""
|
||||
"""Return the statement type name"""
|
||||
if node is None: return None
|
||||
return node.type_name()
|
||||
|
||||
# ============================================================
|
||||
# 簇 D:nonlocal/闭包 env 访问(迁移自 HandlesNonlocal.vp)
|
||||
# ============================================================
|
||||
# Cluster D: nonlocal/closure env access (migrated from HandlesNonlocal.vp)
|
||||
|
||||
def _is_void_type(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""判断是否为 Void 类型,是则返回 1,否则 0"""
|
||||
"""Return 1 if the type is Void, else 0"""
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Void():
|
||||
@@ -296,10 +264,10 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 0
|
||||
|
||||
def get_env_ptr_var(self, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
"""获取或创建 _env_ptr 变量(存储闭包 env 指针)"""
|
||||
"""Get or create the _env_ptr variable holding the closure env pointer"""
|
||||
env_var: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "_env_ptr")
|
||||
if env_var is not None: return env_var
|
||||
# 创建 alloca 存储 env
|
||||
# Create an alloca to store env
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
@@ -310,10 +278,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return alloca
|
||||
|
||||
def _load_raw_var_ptr(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||
"""从闭包 env 加载 nonlocal 变量的原始指针(i8*)
|
||||
|
||||
返回 i8* 类型的 Value,调用方需 bitcast 为具体类型指针后 load/store。
|
||||
"""
|
||||
"""Load the raw pointer (i8*) of a nonlocal variable from the closure env"""
|
||||
idx: int = GenApi.get_nonlocal_index(trans, name)
|
||||
if idx < 0: return None
|
||||
|
||||
@@ -323,13 +288,13 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
|
||||
|
||||
# 1. 加载 env_ptr
|
||||
# 1. Load env_ptr
|
||||
env_alloca: mlirlite.Value* = self.get_env_ptr_var(trans)
|
||||
if env_alloca is None: return None
|
||||
env_ptr: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, env_alloca)
|
||||
if env_ptr is None: return None
|
||||
|
||||
# 2. GEP to offset (idx * 8) — env 存储指针数组,每槽 8 字节
|
||||
# 2. GEP to offset (idx * 8) - env is an array of pointers, 8 bytes per slot
|
||||
offset_val: mlirlite.Value* = mlirlite.const_int32(pool, idx * 8)
|
||||
addr: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, env_ptr, offset_val)
|
||||
if addr is None: return None
|
||||
@@ -340,20 +305,17 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return mlirlite.build_load(builder, i8_ptr_ty, ptr_addr)
|
||||
|
||||
def load_nonlocal_var(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||
"""从闭包 env 加载 nonlocal 变量值
|
||||
|
||||
通过 HT.get_nonlocal_type 获取变量类型,bitcast 原始指针后 load。
|
||||
"""
|
||||
"""Load the value of a nonlocal variable from the closure env"""
|
||||
raw_ptr: mlirlite.Value* = self._load_raw_var_ptr(trans, name)
|
||||
if raw_ptr is None: return None
|
||||
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
|
||||
# 获取变量类型(从注册时存储的类型信息)
|
||||
# Get the variable type (from the type info stored at registration)
|
||||
var_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
|
||||
if var_ty is None or self._is_void_type(var_ty) != 0:
|
||||
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "load void, void*" 非法 IR
|
||||
# fail-fast on a missing/invalid type, avoiding illegal IR like "load void, void*"
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
|
||||
|
||||
# bitcast raw i8* to var_ty*
|
||||
@@ -363,20 +325,17 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return mlirlite.build_load(builder, var_ty, var_ptr)
|
||||
|
||||
def get_nonlocal_var_ptr(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||
"""获取 nonlocal 变量的存储指针(var_ty*),用于 store 操作
|
||||
|
||||
返回的指针指向父函数的 alloca,store 操作对父函数可见。
|
||||
"""
|
||||
"""Get the storage pointer (var_ty*) of a nonlocal variable for store operations"""
|
||||
raw_ptr: mlirlite.Value* = self._load_raw_var_ptr(trans, name)
|
||||
if raw_ptr is None: return None
|
||||
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
|
||||
# 获取变量类型
|
||||
# Get the variable type
|
||||
var_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
|
||||
if var_ty is None or self._is_void_type(var_ty) != 0:
|
||||
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "bitcast ... to void*" 非法 IR
|
||||
# Missing/invalid type aborts (fail-fast) to avoid illegal "bitcast ... to void*" IR
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
|
||||
|
||||
# bitcast raw i8* to var_ty*
|
||||
@@ -384,16 +343,10 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return mlirlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
||||
|
||||
# ============================================================
|
||||
# 簇 E:枚举注册表(迁移自 HandlesEnum.vp)
|
||||
# ============================================================
|
||||
# Cluster E: enum registry (migrated from HandlesEnum.vp)
|
||||
|
||||
def init_enum_table(self, pool: memhub.MemBuddy*) -> int:
|
||||
"""初始化枚举注册表,返回 1 成功
|
||||
|
||||
表用 stdlib 分配(跨 Phase1 1b 的 mark/reset_to 存活——枚举表是
|
||||
跨文件持久表,pool 分配会被 reset 回收导致 Phase B 读垃圾,
|
||||
enum_count 出现 512 等垃圾值 / entry.Name 解引用崩溃)。
|
||||
"""
|
||||
"""Initialize the enum registry; returns 1 on success"""
|
||||
if self.EnumTable is not None: return 1
|
||||
|
||||
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
|
||||
@@ -404,24 +357,24 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return 1
|
||||
|
||||
def _get_enum_entry(self, i: int) -> rawptr[GenTypes.EnumEntry]:
|
||||
"""获取第 i 个枚举条目"""
|
||||
"""Get the i-th enum entry"""
|
||||
if self.EnumTable is None or i < 0 or i >= ENUM_MAX: return None
|
||||
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
|
||||
addr: uint64 = uint64(self.EnumTable) + i * entry_size
|
||||
return rawptr[GenTypes.EnumEntry](addr)
|
||||
|
||||
def _get_enum_member(self, enum_entry: rawptr[GenTypes.EnumEntry], i: int) -> rawptr[GenTypes.EnumMember]:
|
||||
"""获取枚举中第 i 个成员条目"""
|
||||
"""Get the i-th enum member entry"""
|
||||
if enum_entry is None or i < 0 or i >= ENUM_MEMBER_MAX: return None
|
||||
member_size: sizet = GenTypes.EnumMember.__sizeof__()
|
||||
addr: uint64 = uint64(enum_entry.Members) + i * member_size
|
||||
return rawptr[GenTypes.EnumMember](addr)
|
||||
|
||||
def register_enum(self, pool: memhub.MemBuddy*, name: str, base_ty: mlirlite.LLVMType*) -> rawptr[GenTypes.EnumEntry]:
|
||||
"""注册枚举类型,返回 EnumEntry 指针"""
|
||||
"""Register an enum type and return its EnumEntry pointer"""
|
||||
if self.init_enum_table(pool) == 0: return None
|
||||
|
||||
# 检查是否已注册
|
||||
# Check whether it is already registered
|
||||
existing: rawptr[GenTypes.EnumEntry] = self.find_enum(name)
|
||||
if existing is not None: return existing
|
||||
|
||||
@@ -432,24 +385,21 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
entry: rawptr[GenTypes.EnumEntry] = self._get_enum_entry(self.EnumCount)
|
||||
if entry is None: return None
|
||||
|
||||
# 分配成员数组(永久池:跨 mark/reset 存活,对齐 EnumTable 永久池化)
|
||||
# Allocate the member array (permanent pool: survives mark/reset like EnumTable)
|
||||
member_size: sizet = GenTypes.EnumMember.__sizeof__()
|
||||
entry.Members = CompilerFactory.factory.PermPool.alloc(member_size * ENUM_MEMBER_MAX)
|
||||
if entry.Members is None: return None
|
||||
string.memset(entry.Members, 0, member_size * ENUM_MEMBER_MAX)
|
||||
|
||||
# 复制类名(永久池深拷贝)
|
||||
# Copy the class name (permanent-pool deep copy)
|
||||
name_len: sizet = string.strlen(name)
|
||||
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
|
||||
if name_buf is not None:
|
||||
string.strcpy(name_buf, name)
|
||||
entry.Name = name_buf
|
||||
|
||||
# BaseTy 深拷贝:LLVMType 是 pool 分配,StdlibAlloc=1 时 reset 回收悬空 → Phase B
|
||||
# 枚举访问 ConstInt(悬空 BaseTy) 生成 void 值(llc 报错)。Phase1 1a(mark1 前)
|
||||
# 保持 pool 存活,无需拷贝。
|
||||
# 注意:StdlibAlloc 是 StructGenMixin 字段,本 Mixin 方法体不能直接 self.StdlibAlloc
|
||||
# (NoVTable 按本 Mixin 字段布局编译),走模块级委托。
|
||||
# Deep-copy BaseTy: LLVMType is pool-allocated and would dangle after reset under
|
||||
# StdlibAlloc=1. StdlibAlloc is a StructGenMixin field, so delegate via the module.
|
||||
if self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||
base_ty_copy: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, base_ty)
|
||||
if base_ty_copy is not None: entry.BaseTy = base_ty_copy
|
||||
@@ -461,11 +411,11 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return entry
|
||||
|
||||
def add_enum_member(self, pool: memhub.MemBuddy*, enum_entry: rawptr[GenTypes.EnumEntry], member_name: str, member_val: int64, member_ty: mlirlite.LLVMType*) -> int:
|
||||
"""向枚举添加成员,返回成员索引(-1 失败)"""
|
||||
"""Add a member to an enum; returns the member index or -1 on failure"""
|
||||
if enum_entry is None or member_name is None or member_ty is None: return -1
|
||||
|
||||
# 去重:同名成员已存在则跳过(跨模块同名枚举合并注册 / 同模块
|
||||
# 重复处理幂等——如 w32.fileio 与 fat32_types 都有 FRESULT)
|
||||
# Dedup: skip an existing member with the same name (cross-module enum merging, e.g.
|
||||
# w32.fileio and fat32_types both define FRESULT).
|
||||
for ei_d in range(enum_entry.MemberCount):
|
||||
ex_d: rawptr[GenTypes.EnumMember] = self._get_enum_member(enum_entry, ei_d)
|
||||
if ex_d is not None and ex_d.Name is not None:
|
||||
@@ -479,7 +429,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
me: rawptr[GenTypes.EnumMember] = self._get_enum_member(enum_entry, idx)
|
||||
if me is None: return -1
|
||||
|
||||
# 复制成员名(永久池深拷贝:成员表跨 mark/reset 存活)
|
||||
# Copy the member name (permanent-pool deep copy: the member table survives mark/reset)
|
||||
name_len: sizet = string.strlen(member_name)
|
||||
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
|
||||
if name_buf is not None:
|
||||
@@ -493,7 +443,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return idx
|
||||
|
||||
def find_enum(self, name: str) -> rawptr[GenTypes.EnumEntry]:
|
||||
"""按枚举类名查找,返回 EnumEntry 或 None"""
|
||||
"""Look up an enum by class name; returns EnumEntry or None"""
|
||||
if name is None or self.EnumTable is None: return None
|
||||
for i in range(self.EnumCount):
|
||||
entry: rawptr[GenTypes.EnumEntry] = self._get_enum_entry(i)
|
||||
@@ -502,7 +452,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return None
|
||||
|
||||
def lookup_enum_member(self, enum_name: str, member_name: str) -> rawptr[GenTypes.EnumMember]:
|
||||
"""按枚举类名和成员名查找,返回 EnumMember 或 None"""
|
||||
"""Look up a member by enum class name and member name; returns EnumMember or None"""
|
||||
if enum_name is None or member_name is None: return None
|
||||
entry: rawptr[GenTypes.EnumEntry] = self.find_enum(enum_name)
|
||||
if entry is None: return None
|
||||
@@ -513,13 +463,13 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return None
|
||||
|
||||
def is_enum_class(self, name: str) -> int:
|
||||
"""检查类名是否为已注册枚举,返回 1=是 / 0=否"""
|
||||
"""Return 1 if the class name is a registered enum, else 0"""
|
||||
if name is None: return 0
|
||||
if self.find_enum(name) is not None: return 1
|
||||
return 0
|
||||
|
||||
def get_enum_base_type(self, enum_name: str) -> mlirlite.LLVMType*:
|
||||
"""按枚举类名获取基准类型"""
|
||||
"""Get the base type for an enum class name"""
|
||||
entry: rawptr[GenTypes.EnumEntry] = self.find_enum(enum_name)
|
||||
if entry is not None: return entry.BaseTy
|
||||
return None
|
||||
@@ -18,23 +18,18 @@ import lib.core.Gen.Stmt.StmtGenDeclMixin as StmtGenDeclMixin
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StmtGenMixin - 语句/流程翻译 Mixin(P6 对象化重构 A 组,拆分后顶层)
|
||||
# ============================================================
|
||||
# StmtGenMixin - statement/flow translation Mixin (P6 object refactor group A, top-level)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StmtGenMixin - 语句分派中枢(顶层)
|
||||
# ============================================================
|
||||
# StmtGenMixin - statement dispatch hub (top level)
|
||||
novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
"""语句/流程翻译 Mixin(拆分后顶层,继承 Decl)"""
|
||||
"""Statement/flow translation Mixin (top level, extends Decl)"""
|
||||
|
||||
def translate_stmt(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译单条语句,返回新增的变量数(深度守卫包装器,原逻辑见 _translate_stmt_impl)"""
|
||||
"""Translate a single statement with a depth guard; returns the number of new variables"""
|
||||
if trans is not None:
|
||||
# --traceback: statement-level DWARF location (no-op outside a
|
||||
# DI-attached define or when DI is disabled). 此后本语句生成的每条
|
||||
# 指令自动带 ", !dbg !N",保证函数体内每个 call 都有合法 !dbg。
|
||||
# --traceback: statement-level DWARF location (no-op outside a DI-attached
|
||||
# define). Every instruction then carries ", !dbg !N".
|
||||
if node is not None and trans._cur_builder is not None:
|
||||
DebugInfoGen.SetStatement(trans.Pool, trans._cur_builder, node.lineno, node.col_offset + 1)
|
||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(node, "语句翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
|
||||
@@ -44,7 +39,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
return mret
|
||||
|
||||
def _translate_stmt_impl(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译单条语句,返回新增的变量数"""
|
||||
"""Translate a single statement; returns the number of new variables"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
|
||||
@@ -52,7 +47,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
elif k == ast.ASTKind.Assign: return self.Gen.AssignGen.translate_assign(trans, node)
|
||||
elif k == ast.ASTKind.AnnAssign: return self.Gen.AssignGen.translate_annassign(trans, node)
|
||||
elif k == ast.ASTKind.FunctionDef:
|
||||
# 嵌套函数定义:提升为顶层函数 + 创建闭包
|
||||
# nested function def: hoist to a top-level function and build a closure
|
||||
return self.Gen.FuncGen.translate_nested_function_def(trans, node)
|
||||
elif k == ast.ASTKind.Return: return self.Gen.StmtGenCif.translate_return(trans, node)
|
||||
elif k == ast.ASTKind.If: return self.Gen.StmtGenCif.translate_if(trans, node)
|
||||
@@ -75,12 +70,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
elif k == ast.ASTKind.Raise: return self.translate_raise(trans, node)
|
||||
elif k == ast.ASTKind.Try: return self.translate_try(trans, node)
|
||||
elif k == ast.ASTKind.With: return self.translate_with(trans, node)
|
||||
# 未知语句类型:直接报错终止(fail-fast),暴露上游解析问题
|
||||
# unknown statement type: fail-fast to expose an upstream parse problem
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"未知语句类型 kind={k}", "SyntaxError")
|
||||
return 0
|
||||
|
||||
def translate_delete(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 del 语句,返回 0"""
|
||||
"""Translate a del statement; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
dnode: rawptr[ast.Delete] = rawptr[ast.Delete](node)
|
||||
if dnode is None or dnode.targets is None: return 0
|
||||
@@ -92,8 +87,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
for ti in range(tn):
|
||||
tgt: ast.AST* = dnode.targets.get(ti)
|
||||
if tgt is None: continue
|
||||
# del obj → __delete__(对齐 TPC HandlesDelete.py:Name 目标调用 __delete__,
|
||||
# 类未定义 __delete__ 时静默无操作)
|
||||
# del obj -> __delete__ (matches TPC HandlesDelete.py; no-op if undefined)
|
||||
if tgt.kind() == ast.ASTKind.Name:
|
||||
del_obj: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, tgt, None, 0, trans)
|
||||
if del_obj is not None:
|
||||
@@ -101,7 +95,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if del_st_nm is not None:
|
||||
del_cls_nm: str = self.Gen.StructGen.get_class_name_by_type(pool, del_st_nm)
|
||||
if del_cls_nm is not None: self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, del_obj, "__delete__", trans)
|
||||
# del obj.attr → property deleter;无 deleter 时回退对象 __del__(对齐 TPC)
|
||||
# del obj.attr -> property deleter; falls back to __del__ (matches TPC)
|
||||
elif tgt.kind() == ast.ASTKind.Attribute:
|
||||
del_at: ast.Attribute* = ast.Attribute*(tgt)
|
||||
if del_at is not None:
|
||||
@@ -109,8 +103,8 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if del_prop == 0:
|
||||
del_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, del_at, None, 0, trans)
|
||||
if del_av is not None: self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, del_av, "__del__", trans)
|
||||
# del obj[key] → __delitem__(obj, key)(对齐 TPC HandlesDelete.py)。
|
||||
# 不能静默忽略 Subscript 目标——否则 del 不生效(len 不变)。
|
||||
# del obj[key] -> __delitem__(obj, key) (matches TPC); ignoring Subscript
|
||||
# targets would silently leave del ineffective.
|
||||
elif tgt.kind() == ast.ASTKind.Subscript:
|
||||
dl_sub: ast.Subscript* = ast.Subscript*(tgt)
|
||||
if dl_sub is not None and dl_sub.value is not None and dl_sub.slice is not None:
|
||||
@@ -129,36 +123,23 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
dl_ret: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(dl_fn)
|
||||
dl_fn_name: str = mlirlite.function_get_name(dl_fn)
|
||||
if dl_ret is not None and dl_fn_name is not None:
|
||||
# self 参数(容器)按 __delitem__ 首参类型对齐
|
||||
# self param (container) aligned to __delitem__'s first param
|
||||
dl_self: mlirlite.Value* = dl_obj
|
||||
dl_first: mlirlite.Param* = mlirlite.function_get_param_head(dl_fn)
|
||||
if dl_first is not None:
|
||||
dl_self_ty: mlirlite.LLVMType* = mlirlite.param_get_ty(dl_first)
|
||||
if dl_self_ty is not None: dl_self = mlirlite.build_bitcast(builder, dl_obj, dl_self_ty)
|
||||
if dl_self is not None:
|
||||
# 参数链表: self → key(独立 null 结尾)
|
||||
# param list: self -> key (separate null terminator)
|
||||
dl_self.Next = dl_idx
|
||||
dl_idx.Next = None
|
||||
mlirlite.build_call(builder, dl_fn_name, dl_self, 2, dl_ret, 0)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# translate_try - try/except 语句翻译
|
||||
#
|
||||
# 对齐 TPC HandlesTry.py L59-214(v1 迁移)。异常 = (i32 code, i8* msg)
|
||||
# 二元组;except X 编译期静态展开 X 及全部子孙异常类编码。布局:
|
||||
# pre → TryBB → DispatchBB(编码检查链)→ MatchBB → ExceptBB → AfterBB
|
||||
# try 体正常结束 → store 0 →(else 若有)→ AfterBB
|
||||
# DispatchBB 无匹配 → UnhandledBB:函数有 EH 输出参数则写入并返回
|
||||
# (向上传播),否则 store 0 → AfterBB(吞掉)
|
||||
# try 上下文(DispatchBB + 两个 alloca)写入 trans._eh_*,translate_raise
|
||||
# 与调用侧 EH 检测(CallGenMixin._emit_eh_guarded_call)据此重定向;
|
||||
# 退出时恢复外层上下文(嵌套 try 支持)。
|
||||
# v1 未实现 finalbody(TestFBProject 未使用;finally+return 交互复杂,
|
||||
# 后续按 TPC HandlesTry.py L188-214 的 pending_ret 方案扩展)。
|
||||
# ============================================================
|
||||
# translate_try - try/except translation. Exception = (i32 code, i8* msg);
|
||||
# except X statically expands X and all descendant exception codes.
|
||||
def translate_try(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 try/except 语句,返回 0"""
|
||||
"""Translate a try/except statement; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
tr: ast.Try* = ast.Try*(node)
|
||||
if tr is None: return 0
|
||||
@@ -168,7 +149,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if pool is None or builder is None or func is None: return 0
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) != 0: return 0
|
||||
|
||||
# ---- 1. 异常编码/消息 alloca(当前 try 上下文)----
|
||||
# ---- 1. exception code/message alloca (current try context) ----
|
||||
i32_ty_t: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ty_t: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty_t: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_t)
|
||||
@@ -181,7 +162,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if init_code_t is not None: mlirlite.build_store(builder, init_code_t, exception_code)
|
||||
if init_msg_t is not None: mlirlite.build_store(builder, init_msg_t, eh_message)
|
||||
|
||||
# ---- 2. 块布局(块名唯一化,create_block 不自动编号)----
|
||||
# ---- 2. block layout (unique names; create_block does not auto-number) ----
|
||||
lc_t: int = trans._label_counter
|
||||
trans._label_counter = lc_t + 1
|
||||
bb_buf_t: str = pool.alloc(64)
|
||||
@@ -201,9 +182,8 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
else_bb = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
n_handlers_t: sizet = 0
|
||||
if tr.handlers is not None: n_handlers_t = len(tr.handlers)
|
||||
# 注意:list[ptr](pool, N) 第二参数是 elem_size 不是容量!
|
||||
# 指针元素必须显式传 8(T.__sizeof__ 对联合类型不可靠),
|
||||
# 否则 append 按 N 字节步长错位存取 → 读出垃圾指针(崩溃)。
|
||||
# NOTE: list[ptr](pool, N)'s second arg is elem_size, not capacity; pointer
|
||||
# elements must pass 8, else append strides wrongly and reads garbage.
|
||||
except_bbs: list[rawptr[mlirlite.BasicBlock]]* = list[rawptr[mlirlite.BasicBlock]](pool, 8)
|
||||
match_bbs: list[rawptr[mlirlite.BasicBlock]]* = list[rawptr[mlirlite.BasicBlock]](pool, 8)
|
||||
if except_bbs is None or match_bbs is None: return 0
|
||||
@@ -215,7 +195,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
except_bbs.append(eb_t)
|
||||
match_bbs.append(mb_t)
|
||||
|
||||
# ---- 3. 保存外层 try 上下文,设置本 try 上下文 ----
|
||||
# ---- 3. save outer try context, set this try context ----
|
||||
old_dispatch_t: rawptr[mlirlite.BasicBlock] = trans._eh_dispatch_bb
|
||||
old_code_t: mlirlite.Value* = trans._eh_code_ptr
|
||||
old_msg_t: mlirlite.Value* = trans._eh_msg_ptr
|
||||
@@ -223,7 +203,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
trans._eh_code_ptr = exception_code
|
||||
trans._eh_msg_ptr = eh_message
|
||||
|
||||
# ---- 4. Try 体 ----
|
||||
# ---- 4. try body ----
|
||||
mlirlite.build_br(builder, try_bb)
|
||||
mlirlite.position_at_end(builder, try_bb)
|
||||
if tr.children is not None:
|
||||
@@ -231,16 +211,16 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
for tbi in range(tb_n):
|
||||
stmt_t: ast.AST* = tr.children.get(tbi)
|
||||
if stmt_t is not None: self.translate_stmt(trans, stmt_t)
|
||||
# 正常走完 try 体:清异常码,进 else(若有)或 After
|
||||
# try body completed: clear the code, go to else (if any) or After
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
if init_code_t is not None: mlirlite.build_store(builder, init_code_t, exception_code)
|
||||
if else_bb is not None: mlirlite.build_br(builder, else_bb)
|
||||
else: mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 5. DispatchBB:编码检查链 ----
|
||||
# ---- 5. DispatchBB: code check chain ----
|
||||
mlirlite.position_at_end(builder, dispatch_bb)
|
||||
if n_handlers_t == 0:
|
||||
# 无 except(非法 try,防御):直接吞掉
|
||||
# no except (invalid try; defensive): swallow
|
||||
mlirlite.build_br(builder, unhandled_bb)
|
||||
else:
|
||||
eh_code_val: mlirlite.Value* = mlirlite.build_load(builder, i32_ty_t, exception_code)
|
||||
@@ -252,17 +232,16 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
mlirlite.position_at_end(builder, next_check_t)
|
||||
eh_code_val = mlirlite.build_load(builder, i32_ty_t, exception_code)
|
||||
if hdr_t is None or hdr_t.type is None:
|
||||
# 裸 except:无条件匹配
|
||||
# bare except: unconditional match
|
||||
mlirlite.build_br(builder, match_bb_t)
|
||||
else:
|
||||
# 展开匹配编码集(handler.type 自身 + 全部子孙异常类)
|
||||
# expand the match code set (handler.type + all descendant classes)
|
||||
hname_t: str = None
|
||||
htype_t: ast.AST* = hdr_t.type
|
||||
if htype_t.kind() == ast.ASTKind.Name: hname_t = (ast.Name*(htype_t)).id
|
||||
elif htype_t.kind() == ast.ASTKind.Attribute: hname_t = (ast.Attribute*(htype_t)).attr
|
||||
# 匹配编码缓冲:必须是 int*(i32 步长)——collect_descendant_codes 按
|
||||
# out_codes[i](4 字节)写入;用 sizet*(8 字节)读会读出 64 位拼接垃圾
|
||||
# (多码时 code[i+1]<<32|code[i];单码时高位恰为 0 掩盖)。
|
||||
# match code buffer must be int* (i32 stride): collect_descendant_codes
|
||||
# writes 4-byte entries; reading as sizet* yields garbage.
|
||||
codes_buf_t: int* = pool.alloc(64 * 4)
|
||||
if codes_buf_t is None: return 0
|
||||
n_codes_t: int = 0
|
||||
@@ -285,13 +264,11 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
next_check_t = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
else: next_check_t = unhandled_bb
|
||||
mlirlite.build_cond_br(builder, match_cond_t, match_bb_t, next_check_t)
|
||||
# MatchBB:绑定 handler 变量(e = 消息指针),跳 ExceptBB
|
||||
# MatchBB: bind handler var (e = message ptr), jump to ExceptBB
|
||||
mlirlite.position_at_end(builder, match_bb_t)
|
||||
if hdr_t is not None and hdr_t.name is not None and hdr_t.name[0] != '\0':
|
||||
# 同名 handler 变量复用函数入口 alloca(find_var 首条命中、
|
||||
# 重复 define_var 不 shadow——多 try 共用 `as e` 时各 MatchBB
|
||||
# 各自 store 消息,各 ExceptBB 读同一入口 alloca,避免
|
||||
# 非支配 alloca 触发 "does not dominate all uses")。
|
||||
# Same-named handler vars reuse the entry alloca to avoid a non-
|
||||
# dominating alloca ("does not dominate all uses").
|
||||
h_ent_t: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, hdr_t.name)
|
||||
if h_ent_t is not None and h_ent_t.Alloca is not None: h_alloca_t: mlirlite.Value* = h_ent_t.Alloca
|
||||
else:
|
||||
@@ -303,7 +280,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
except_bb_t: rawptr[mlirlite.BasicBlock] = except_bbs.get(i_t)
|
||||
mlirlite.build_br(builder, except_bb_t)
|
||||
|
||||
# ---- 6. UnhandledBB:无 except 匹配 → 向上传播或吞掉 ----
|
||||
# ---- 6. UnhandledBB: no except matched -> propagate or swallow ----
|
||||
mlirlite.position_at_end(builder, unhandled_bb)
|
||||
if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None and trans._eh_code_out_param is not None:
|
||||
un_msg_t: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty_t, eh_message)
|
||||
@@ -315,7 +292,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if init_code_t is not None: mlirlite.build_store(builder, init_code_t, exception_code)
|
||||
mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 7. Except 体 ----
|
||||
# ---- 7. except bodies ----
|
||||
for i_t in range(n_handlers_t):
|
||||
hdr2_t: ast.ExceptHandler* = ast.ExceptHandler*(tr.handlers.get(i_t))
|
||||
except_bb2_t: rawptr[mlirlite.BasicBlock] = except_bbs.get(i_t)
|
||||
@@ -327,7 +304,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if stmt2_t is not None: self.translate_stmt(trans, stmt2_t)
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 8. Else 体 ----
|
||||
# ---- 8. else body ----
|
||||
if else_bb is not None:
|
||||
mlirlite.position_at_end(builder, else_bb)
|
||||
if tr.orelse is not None:
|
||||
@@ -337,7 +314,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if stmt3_t is not None: self.translate_stmt(trans, stmt3_t)
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 9. AfterBB + 恢复外层 try 上下文 ----
|
||||
# ---- 9. AfterBB + restore outer try context ----
|
||||
mlirlite.position_at_end(builder, after_bb)
|
||||
trans._eh_dispatch_bb = old_dispatch_t
|
||||
trans._eh_code_ptr = old_code_t
|
||||
@@ -345,17 +322,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
return 0
|
||||
|
||||
def translate_raise(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 raise 语句,返回 0
|
||||
|
||||
对齐 TPC HandlesRaise.py 的 try-except 语义(v1 迁移):
|
||||
- StopIteration:置 __stop_iter_flag__ = 1 后返回默认值(迭代结束协议)
|
||||
- 非 StopIteration:
|
||||
1. 处于 try(trans._eh_dispatch_bb 非空):异常编码/消息写入当前
|
||||
try 的 eh_code/eh_message alloca,分支到 DispatchBB 由 except 链裁决
|
||||
2. 函数有 EH 输出参数(_cur_func_has_eh):编码/消息写入
|
||||
__eh_code_out__/__eh_msg_out__,返回默认值(异常向调用方传播)
|
||||
3. 两者皆无:仅返回默认值(v1 语义:异常被吞掉)
|
||||
"""
|
||||
"""Translate a raise statement and dispatch the exception; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
@@ -363,12 +330,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if pool is None or builder is None or mod is None: return 0
|
||||
rn: rawptr[ast.Raise] = rawptr[ast.Raise](node)
|
||||
if rn is None or rn.exc is None:
|
||||
# bare raise(无 exc):v1 无活动异常可再抛,仅返回默认值
|
||||
# bare raise (no exc): v1 has no active exception; just return default
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# OOP iter 协议:raise StopIteration → 置 __stop_iter_flag__ = 1
|
||||
# (对齐 TPC HandlesRaise.py L76-100),随后按迭代结束返回默认值。
|
||||
# OOP iter protocol: raise StopIteration -> set __stop_iter_flag__ = 1, then
|
||||
# return the default value.
|
||||
ek: int = rn.exc.kind()
|
||||
is_stop_iter: int = 0
|
||||
if ek == ast.ASTKind.Name:
|
||||
@@ -388,7 +355,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# ---- 非 StopIteration:计算异常编码与消息 ----
|
||||
# ---- non-StopIteration: compute exception code and message ----
|
||||
exc_code: int = 1
|
||||
exc_msg: mlirlite.Value* = None
|
||||
if ek == ast.ASTKind.Constant:
|
||||
@@ -408,8 +375,8 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if exc_code == 0: exc_code = 1
|
||||
if ecl2.args is not None and len(ecl2.args) > 0: exc_msg = self.Gen.ExprGen.translate_value(builder, pool, mod, ecl2.args.get(0), None, 0, trans)
|
||||
|
||||
# ---- 异常分发 ----
|
||||
# 1. try 内:写入 try 的 eh_code/eh_message alloca,分支 DispatchBB
|
||||
# ---- exception dispatch ----
|
||||
# 1. inside try: write to the try's eh_code/eh_message alloca, branch to DispatchBB
|
||||
if trans._eh_dispatch_bb is not None and trans._eh_code_ptr is not None:
|
||||
code_val_r: mlirlite.Value* = mlirlite.const_int32(pool, exc_code)
|
||||
if code_val_r is not None: mlirlite.build_store(builder, code_val_r, trans._eh_code_ptr)
|
||||
@@ -417,7 +384,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if exc_msg is not None and trans._eh_msg_ptr is not None: mlirlite.build_store(builder, exc_msg, trans._eh_msg_ptr)
|
||||
mlirlite.build_br(builder, trans._eh_dispatch_bb)
|
||||
return 0
|
||||
# 2. 函数有 EH 输出参数:写入 out 参数(异常向调用方传播)
|
||||
# 2. function has EH out params: write them (propagate to caller)
|
||||
if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None and trans._eh_code_out_param is not None:
|
||||
code_val_r2: mlirlite.Value* = mlirlite.const_int32(pool, exc_code)
|
||||
if code_val_r2 is not None: mlirlite.build_store(builder, code_val_r2, trans._eh_code_out_param)
|
||||
@@ -425,19 +392,14 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if exc_msg is not None: mlirlite.build_store(builder, exc_msg, trans._eh_msg_out_param)
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
# 3. 无 try 无 EH 参数:仅返回默认值
|
||||
# 3. no try and no EH params: just return the default
|
||||
self._eh_ret_default(trans, pool, builder)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _eh_ret_default - 异常路径返回当前函数的默认零值
|
||||
#
|
||||
# 对齐 TPC HandlesTry.py UnhandledBB / HandlesRaise.py 的返回生成
|
||||
# (void→ret_void,ptr→null,float→0.0,其余→零值)。当前块已
|
||||
# 终结(前一条是 raise/return)时跳过,避免在终止块后插指令。
|
||||
# ============================================================
|
||||
# _eh_ret_default - return the function's default zero value on the exception
|
||||
# path (void->ret_void, ptr->null, float->0.0, else zero); skipped if terminated.
|
||||
def _eh_ret_default(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*) -> int:
|
||||
"""异常路径返回当前函数默认零值,返回 0"""
|
||||
"""Return the current function's default zero value on the exception path"""
|
||||
if trans is None or builder is None: return 0
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) != 0: return 0
|
||||
cur_func: mlirlite.Function* = trans._cur_func
|
||||
@@ -458,24 +420,10 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if zero_v is not None: mlirlite.build_ret(builder, zero_v)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# translate_with - with 语句翻译
|
||||
#
|
||||
# with expr() as x: body
|
||||
# → 1. 构造 context 对象(expr() 调用)
|
||||
# 2. 调用 __enter__()(沿继承链 MRO 解析,如 MemPool → MemManager)
|
||||
# 3. x 绑定 __enter__ 返回值(缺失时退回 context 对象)
|
||||
# 4. 翻译 body
|
||||
# 5. 逆序调用 __exit__()(无异常传播,__exit__ 仅做清理如 reset)
|
||||
#
|
||||
# 实现:context 对象存入临时变量 __with_ctx,方法调用复用
|
||||
# translate_call 的 Attribute 分派 + _translate_method_call 的 MRO 回退
|
||||
# (与普通 obj.method() 调用一致)。嵌套 with 时保存外层 __with_ctx,
|
||||
# 退出后恢复,避免内层覆盖导致外层 __exit__ 解析到错误对象。
|
||||
# 对齐 TPC HandlesWith 语义(不含 __provides__ provider 机制)。
|
||||
# ============================================================
|
||||
# translate_with - with statement translation: build context, call __enter__,
|
||||
# bind asname, translate body, then call __exit__ in reverse order.
|
||||
def translate_with(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""翻译 with 语句,返回 0"""
|
||||
"""Translate a with statement; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
wt: ast.With* = ast.With*(node)
|
||||
if wt is None: return 0
|
||||
@@ -487,27 +435,26 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if items is None: return 0
|
||||
n: sizet = len(items)
|
||||
|
||||
# 保存外层 __with_ctx(嵌套 with 时退出后恢复)
|
||||
# save the outer __with_ctx (restored after nested with exits)
|
||||
old_ctx_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "__with_ctx")
|
||||
|
||||
# 记录每个 item 的类名(__exit__ 调用时设置 __with_ctx 类型用)
|
||||
# record each item's class name (used to type __with_ctx on __exit__)
|
||||
ctx_class_buf: sizet* = pool.alloc(8 * n)
|
||||
if ctx_class_buf is None: return 0
|
||||
string.memset(ctx_class_buf, 0, 8 * n)
|
||||
|
||||
# 1. 全部 item:构造 context 对象 + 调用 __enter__ + asname 绑定
|
||||
# 1. all items: build context, call __enter__, bind asname
|
||||
for i in range(n):
|
||||
item_node: ast.AST* = items.get(i)
|
||||
if item_node is None: continue
|
||||
if item_node.kind() != ast.ASTKind.WithItem: continue
|
||||
item: rawptr[ast.WithItem] = rawptr[ast.WithItem](item_node)
|
||||
if item is None or item.context_expr is None: continue
|
||||
# 翻译 context_expr(对象构造,如 MemPool(arena, 4096))
|
||||
# translate context_expr (object construction, e.g. MemPool(arena, 4096))
|
||||
ctx_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, item.context_expr, trans._funcs, trans._func_count, trans)
|
||||
if ctx_val is None: continue
|
||||
# 提取类名(注解类名,Attribute 时带模块前缀,如 "memhub.MemPool"——
|
||||
# 与 AnnAssign 的 extract_class_name_from_annotation 一致,跨模块
|
||||
# 方法调用靠 AnnotClassName 回退按模块名查 SHA1)
|
||||
# Extract the class name (module-prefixed for Attribute, e.g. "memhub.MemPool");
|
||||
# cross-module calls fall back to AnnotClassName by module name.
|
||||
cls_name: str = None
|
||||
ce: ast.AST* = item.context_expr
|
||||
if ce.kind() == ast.ASTKind.Call:
|
||||
@@ -525,7 +472,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
cls_name = mbuf
|
||||
if cls_name is None: cls_name = ca.attr
|
||||
elif cfn.kind() == ast.ASTKind.Name: cls_name = (ast.Name*(cfn)).id
|
||||
# context 对象存入 __with_ctx(方法调用解析 self 用)
|
||||
# store the context object in __with_ctx (used to resolve self)
|
||||
ctx_alloca: mlirlite.Value* = mlirlite.build_alloca(builder, ctx_val.Ty)
|
||||
if ctx_alloca is None: continue
|
||||
mlirlite.build_store(builder, ctx_val, ctx_alloca)
|
||||
@@ -533,9 +480,9 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if cls_name is not None:
|
||||
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, "__with_ctx", cls_name)
|
||||
ctx_class_buf[i] = sizet(cls_name)
|
||||
# 调用 __enter__()
|
||||
# call __enter__()
|
||||
enter_val: mlirlite.Value* = self._with_ctx_call(trans, pool, builder, mod, "__enter__")
|
||||
# asname 绑定(__enter__ 返回值,缺失时退回 context 对象)
|
||||
# bind asname (__enter__ return value, or the context object on miss)
|
||||
if item.optional_vars is not None:
|
||||
ov: ast.AST* = item.optional_vars
|
||||
if ov.kind() == ast.ASTKind.Name:
|
||||
@@ -549,16 +496,15 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
mlirlite.build_store(builder, bind_val, asn_alloca)
|
||||
self.Gen.VarGen.define_var(trans.SymTab, asname, asn_alloca)
|
||||
if cls_name is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, asname, cls_name)
|
||||
# FakeDuck: __provides__ 元数据 → 生成 _push_provider 调用
|
||||
# (对齐 TPC HandlesWith.py L138-159;经 gen 单例委托 CallGenMixin。
|
||||
# 类名去模块前缀后查 __provides__ 表,如 memhub.MemBuddy → MemBuddy)
|
||||
# FakeDuck: __provides__ metadata -> emit a _push_provider call (matches TPC);
|
||||
# the class name is de-qualified before the __provides__ lookup.
|
||||
fd_cls_bare: str = cls_name
|
||||
if cls_name is not None:
|
||||
fd_dot: str = string.strrchr(cls_name, '.')
|
||||
if fd_dot is not None: fd_cls_bare = fd_dot + 1
|
||||
if fd_cls_bare is not None: self.Gen.CallGenFd._emit_provider_push(trans, pool, builder, mod, fd_cls_bare, ctx_val)
|
||||
|
||||
# 2. 翻译 body
|
||||
# 2. translate body
|
||||
body: list[ast.AST*]* = wt.children
|
||||
if body is not None:
|
||||
bn: sizet = len(body)
|
||||
@@ -566,8 +512,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
st: ast.AST* = body.get(bi)
|
||||
if st is not None: self.translate_stmt(trans, st)
|
||||
|
||||
# 2.5 FakeDuck: __provides__ 元数据 → 生成 _pop_provider 调用
|
||||
# (对齐 TPC HandlesWith.py L167-174;逆序弹栈,经 gen 单例委托)
|
||||
# 2.5 FakeDuck: __provides__ metadata -> emit _pop_provider calls in reverse.
|
||||
for pi_pop in range(n):
|
||||
if ctx_class_buf[pi_pop] != 0:
|
||||
cls_pop: str = rawptr(ctx_class_buf[pi_pop])
|
||||
@@ -577,7 +522,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if fd_dot_p is not None: fd_cls_bare_p = fd_dot_p + 1
|
||||
if fd_cls_bare_p is not None: self.Gen.CallGenFd._emit_provider_pop(trans, pool, builder, mod, fd_cls_bare_p)
|
||||
|
||||
# 3. 逆序调用 __exit__()(Python with 语义:后进先出)
|
||||
# 3. call __exit__() in reverse order (Python with semantics: LIFO)
|
||||
for i in range(n):
|
||||
ci: int = n - 1 - i
|
||||
if ctx_class_buf[ci] != 0:
|
||||
@@ -585,12 +530,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, "__with_ctx", cls_ci)
|
||||
self._with_ctx_call(trans, pool, builder, mod, "__exit__")
|
||||
|
||||
# 4. 恢复外层 __with_ctx
|
||||
# 4. restore the outer __with_ctx
|
||||
if old_ctx_alloca is not None: self.Gen.VarGen.define_var(trans.SymTab, "__with_ctx", old_ctx_alloca)
|
||||
return 0
|
||||
|
||||
def _with_ctx_call(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, method_name: str) -> mlirlite.Value*:
|
||||
"""构造并翻译 __with_ctx.<method_name>() 调用(__enter__/__exit__)"""
|
||||
"""Build and translate a __with_ctx.<method_name>() call (__enter__/__exit__)"""
|
||||
if pool is None or method_name is None: return None
|
||||
name_node: ast.AST* = ast.Name(pool, "__with_ctx", ast.ASTCtx.Load)
|
||||
if name_node is None: return None
|
||||
|
||||
+278
-711
File diff suppressed because it is too large.
Load diff
+185
-365
@@ -16,87 +16,49 @@ import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
# ============================================================
|
||||
# Gen/Translator.vp - 翻译器状态管理 + 主入口
|
||||
#
|
||||
# 负责: 1) Translator 类(状态管理:Module/变量表/函数表/导入)
|
||||
# 2) translate() 主翻译入口 3) dump_ir() IR 输出
|
||||
# global/nonlocal 名称管理委托函数在 lib.core.Gen.GenApi,
|
||||
# TranslatorGenMixin 侧另有同名一行委托。
|
||||
# ============================================================
|
||||
# Gen/Translator.vp - translator state management and main entry point:
|
||||
# Translator class (module/vars/funcs/imports), translate(), and dump_ir().
|
||||
|
||||
|
||||
|
||||
# ============================================================
|
||||
# HandlesTranslator - 翻译器状态管理 + 主入口
|
||||
#
|
||||
# 从 translator.py 拆分出来,负责:
|
||||
# 1. Translator 类 - 状态管理(Module, 变量表, 函数表, 导入)
|
||||
# 2. translate() - 主翻译入口
|
||||
# 3. dump_ir() - IR 输出
|
||||
#
|
||||
# 语句/表达式翻译全部委托给 Handles 模块:
|
||||
# HandlesMain - wrapper main 创建 + children 遍历
|
||||
# HandlesBody - 语句分派
|
||||
# HandlesExpr - 表达式翻译
|
||||
# HandlesFunctions - 函数定义处理
|
||||
# 注意:str = bytes = str = i8*
|
||||
#
|
||||
# P6-E 对象化重构:模块级 global/nonlocal 名称管理函数已迁移为
|
||||
# lib.core.Gen.TranslatorGenMixin 的方法,本模块保留同名模块级
|
||||
# 函数作为"一行委托"(同 P2-P5 模式)。Translator 类保留在本模块。
|
||||
# ============================================================
|
||||
# HandlesTranslator - translator state management and main entry point, split from translator.py.
|
||||
# Statement/expression translation is delegated to the Handles modules (Main/Body/Expr/Functions).
|
||||
|
||||
# 常量
|
||||
# 作用域变量表容量上限(唯一真源)。分配点 VarGenMixin.init_vars
|
||||
# (HT.MAX_VARS * VarEntry.__sizeof__())与守卫 VarGenMixin.add_var
|
||||
# (var_count >= HT.MAX_VARS)都取本常量,两者结构上不可能各自漂移。
|
||||
# Constants
|
||||
# Scoped variable-table capacity (single source of truth), shared by VarGenMixin init_vars and add_var.
|
||||
define MAX_VARS = 1024
|
||||
# 函数表容量上限(唯一真源)。分配点在本模块 _init_state
|
||||
# (GenApi.init_func_table(pool, MAX_FUNCS));注册侧守卫上限由
|
||||
# FuncGenDef/ClassGenVTable/AsyncGenMixin/VpiReader 传给
|
||||
# CallGenLeafKit.add_func_to_table 的 max_count 给出,必须 <= 本容量。
|
||||
# Function-table capacity (single source of truth). Allocation happens in _init_state;
|
||||
# the registration-side max_count passed to add_func_to_table must be <= this capacity.
|
||||
define MAX_FUNCS = 1024
|
||||
# 引用口径(同 CallGenBase.vp 常量段):跨模块 t.CDefine 只能在函数体内读,
|
||||
# 故引用方一律写限定名 HT.MAX_VARS / HT.MAX_FUNCS,且不得用于模块级
|
||||
# CDefine 初始化式。
|
||||
# global/nonlocal 名称表容量上限(唯一真源)。TranslatorGenMixin 的
|
||||
# add_global_name/add_nonlocal_name 越界守卫与 FuncGenMixin 的嵌套 nonlocal
|
||||
# 缓冲分配均经 HT.MAX_GLOBAL_NAMES 引用本常量;缓冲分配(Translator._init_state)
|
||||
# 与守卫必须同值,单改任一侧都会造成堆越界写或有效容量缩水。
|
||||
# Reference convention: cross-module t.CDefine is readable only inside functions, so write
|
||||
# qualified names there; the global/nonlocal name-table capacity must match on both guard sides.
|
||||
define MAX_GLOBAL_NAMES = 128
|
||||
# 协程表容量(TPV-M1):async 函数数 / 协程变量·局部名数
|
||||
# Coroutine table capacity (TPV-M1): async function count / coroutine var and local-name count
|
||||
define MAX_CORO_FUNCS = 256
|
||||
define MAX_CORO_VARS = 256
|
||||
define MAX_CORO_TRY = 16
|
||||
|
||||
# 翻译主环最大递归深度(translate_stmt/translate_value 嵌套)。AST 自引用环
|
||||
# 或翻译 bug 可致翻译主环(语句/表达式/调用)无限递归 → ASan stack-overflow。
|
||||
# match 守卫(MatchTranslateDepth)只保护 match 递归,本常量保护语句/表达式
|
||||
# 翻译环(fail-fast,对齐 TypeGenResolve 深度守卫模式)。
|
||||
# Max recursion depth of the translation main loop (translate_stmt/translate_value nesting).
|
||||
# Guards against infinite recursion from AST cycles or bugs (fail-fast, prevents ASan stack-overflow).
|
||||
define TRANSLATE_MAX_DEPTH = 256
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 全局 gen 单例(模块级镜像,同 HandlesType/HandlesStruct 模式)
|
||||
# ============================================================
|
||||
# Global gen singleton (module-level mirror, same pattern as HandlesType/HandlesStruct)
|
||||
|
||||
novirtual class Translator:
|
||||
"""翻译器:管理编译状态,委托 Handles 模块执行翻译"""
|
||||
"""Translator: manages compilation state and delegates translation to Handles modules."""
|
||||
|
||||
# LLVM 上下文(共享状态)
|
||||
# LLVM context (shared state)
|
||||
Module: mlirlite.LLVMModule*
|
||||
Pool: memhub.MemBuddy*
|
||||
|
||||
# 函数表
|
||||
# Function table
|
||||
_funcs: GenTypes.FuncEntry*
|
||||
_func_count: int
|
||||
|
||||
# 协程(TPV-M1 async def / await / coro.coro_run)
|
||||
# _async_funcs: 已注册 async 函数裸名表(char* 数组,8 字节/项)
|
||||
# _coro_var_async: 变量名→async 函数名 成对表(char* 数组,16 字节/项)
|
||||
# _coro_locals: 当前 async 函数局部变量名表(char* 数组,8 字节/项)
|
||||
# _coro_frames: async 名→帧类型 成对表(16 字节/项 = char* + LLVMType*)
|
||||
# Coroutines (TPV-M1 async def / await / coro.coro_run):
|
||||
# tables for async function names, var->async mapping, locals, and async->frame types.
|
||||
_async_funcs: str
|
||||
_async_func_count: int
|
||||
_coro_var_async: str
|
||||
@@ -105,7 +67,7 @@ novirtual class Translator:
|
||||
_coro_local_count: int
|
||||
_coro_frames: str
|
||||
_coro_frame_count: int
|
||||
# resume_<name> 翻译上下文(AsyncGenMixin 段翻译共享)
|
||||
# resume_<name> translation context (shared by AsyncGenMixin segment translation)
|
||||
_coro_state_ptr: mlirlite.Value*
|
||||
_coro_done_ptr: mlirlite.Value*
|
||||
_coro_ret_ptr: mlirlite.Value*
|
||||
@@ -114,180 +76,152 @@ novirtual class Translator:
|
||||
_coro_frame: mlirlite.Value*
|
||||
_coro_next_state: int
|
||||
_coro_nseg: int
|
||||
# M2:async 参数表(char* 数组,8 字节/项)+ 预扫描计数
|
||||
# M2: async parameter table (char* array, 8 bytes/item) + pre-scan counts
|
||||
_coro_params: str
|
||||
_coro_param_count: int
|
||||
_coro_hoist_count: int # __await_N 槽计数(预扫描与翻译共用,序一致)
|
||||
_coro_await_count: int # 总 await 数(= 恢复块数 = switch case 数)
|
||||
_coro_local_base: int # 局部槽起始 index(= 3 + nparams)
|
||||
_coro_extra_base: int # 额外槽(__await_N)起始 index
|
||||
_coro_break_bb: rawptr[mlirlite.BasicBlock] # While break 目标(M2 单层)
|
||||
_coro_continue_bb: rawptr[mlirlite.BasicBlock] # While continue 目标
|
||||
_coro_state_bbs: sizet* # 预建 state 块数组(await 恢复块定位)
|
||||
# M3-M7 协程扩展
|
||||
_coro_exc_ptr: mlirlite.Value* # 帧 exc 槽(i32,M4)
|
||||
_coro_msg_ptr: mlirlite.Value* # 帧 msg 槽(i8*,M7)
|
||||
_coro_wake_ptr: mlirlite.Value* # 帧 wake 槽(i64,M3 coro_sleep)
|
||||
_coro_try_stack: sizet* # try 栈:except/finally 块指针数组(8B/项)
|
||||
_coro_hoist_count: int # __await_N slot count (shared by pre-scan and translation, same order)
|
||||
_coro_await_count: int # Total await count (= resume blocks = switch cases)
|
||||
_coro_local_base: int # Local slot start index (= 3 + nparams)
|
||||
_coro_extra_base: int # Extra slot (__await_N) start index
|
||||
_coro_break_bb: rawptr[mlirlite.BasicBlock] # While break target (M2 single level)
|
||||
_coro_continue_bb: rawptr[mlirlite.BasicBlock] # While continue target
|
||||
_coro_state_bbs: sizet* # Pre-built state block array (locates await resume blocks)
|
||||
# M3-M7 coroutine extensions
|
||||
_coro_exc_ptr: mlirlite.Value* # Frame exc slot (i32, M4)
|
||||
_coro_msg_ptr: mlirlite.Value* # Frame msg slot (i8*, M7)
|
||||
_coro_wake_ptr: mlirlite.Value* # Frame wake slot (i64, M3 coro_sleep)
|
||||
_coro_try_stack: sizet* # try stack: except/finally block pointer array (8B/item)
|
||||
_coro_try_stack_count: int
|
||||
_coro_for_counter: int # range-For 编号(M3,翻译期递增)
|
||||
_coro_scan_for_count: int # 预扫描期 For 计数
|
||||
_coro_uses_sleep: int # 函数体是否用 coro_sleep(预扫描置位)
|
||||
_coro_extra_types: str # 额外槽类型表(i8 数组:1=i32 2=i64)
|
||||
_coro_extra_count: int # 额外槽总数
|
||||
_coro_await_slots: int* # __await_N 的帧相对槽 index(预扫描填)
|
||||
_coro_for_slots: int* # range-For N 的 __f_idx 帧相对槽 index(预扫描填)
|
||||
# per-async wake 槽 index 表(coro_run/gather 轮询反查用)
|
||||
_coro_wake_names: str # char* 数组(8B/项)
|
||||
_coro_wake_idxs: int* # i32 数组(4B/项,-1=无 wake 槽)
|
||||
_coro_for_counter: int # range-For id (M3, incremented during translation)
|
||||
_coro_scan_for_count: int # Pre-scan For count
|
||||
_coro_uses_sleep: int # Whether the body uses coro_sleep (set during pre-scan)
|
||||
_coro_extra_types: str # Extra slot type table (i8 array: 1=i32, 2=i64)
|
||||
_coro_extra_count: int # Total extra slots
|
||||
_coro_await_slots: int* # Frame-relative slot index of __await_N (filled by pre-scan)
|
||||
_coro_for_slots: int* # Frame-relative __f_idx slot index of range-For N (filled by pre-scan)
|
||||
# per-async wake slot index table (reverse lookup for coro_run/gather polling)
|
||||
_coro_wake_names: str # char* array (8B/item)
|
||||
_coro_wake_idxs: int* # i32 array (4B/item, -1 = no wake slot)
|
||||
_coro_wake_count: int
|
||||
|
||||
# 导入模块跟踪
|
||||
# Imported module tracking
|
||||
_imported_modules: str
|
||||
_from_imports: str
|
||||
|
||||
# 当前翻译上下文
|
||||
# Current translation context
|
||||
_cur_func: mlirlite.Function*
|
||||
_cur_builder: mlirlite.IRBuilder*
|
||||
|
||||
# 当前赋值语句的注解节点(AnnAssign 翻译 rhs 前设置,Assign 翻译前清空)。
|
||||
# 供裸名泛型构造从注解推断类型实参:`s: Stack[int] = Stack()` 用注解里的
|
||||
# int 特化(消费者见 CallGenCtor.vp 的 CurAssignNode/CurAssignAnnot 判定)。
|
||||
# Annotation node of the current assignment (set before translating the AnnAssign rhs, cleared for Assign).
|
||||
# Lets bare generic constructors infer type args from the annotation (see CallGenCtor.vp).
|
||||
CurAssignAnnot: ast.AST*
|
||||
|
||||
# 当前赋值语句的 RHS 节点(AnnAssign 翻译 rhs 前设置,rhs 翻译后清除)。
|
||||
# 供裸名泛型构造判定"构造调用是否为赋值直接 RHS":仅该 identity 命中时才用
|
||||
# CurAssignAnnot 推断类型实参(对齐 TPC 的直接 RHS 检查)。
|
||||
# RHS node of the current assignment (set before translating the AnnAssign rhs, cleared after).
|
||||
# Bare generic constructors use CurAssignAnnot only when this identity matches the direct RHS.
|
||||
CurAssignNode: ast.AST*
|
||||
|
||||
# 当前函数的返回注解节点(translate_function_def 进入函数体前设置,退出时
|
||||
# 恢复外层函数的值)。供裸名泛型构造回退推断类型实参:`-> Stack[int]` 里
|
||||
# `return Stack()` 用注解里的 int 特化(与 CurAssignAnnot 同类的上下文通道)。
|
||||
# Return annotation of the current function (set before translating its body, restored on exit).
|
||||
# Fallback for bare generic constructors: `return Stack()` under `-> Stack[int]` uses the int.
|
||||
CurFuncRetAnnot: ast.AST*
|
||||
|
||||
# 当前 Return 语句的直接值节点(translate_return 翻译返回值前设置,翻译后
|
||||
# 清除)。与 CurAssignNode 同款的 identity 判定:仅「裸名泛型构造就是 return
|
||||
# 的直接值」时才用 CurFuncRetAnnot(嵌套调用如 return f(Stack()) 不推断)。
|
||||
# Direct value node of the current Return statement (set before/cleared after translating it).
|
||||
# Bare generic constructors use CurFuncRetAnnot only for a direct return value (not return f(Stack())).
|
||||
CurRetNode: ast.AST*
|
||||
|
||||
# 循环控制流目标(break/continue)。注意 match-case 体内的 break 语义是
|
||||
# "跳出 match":MatchGenMixin 进出 case 体时暂存并把 _break_bb 覆盖为 match
|
||||
# 的 end_bb(出 case 恢复),故 case 内 break 不会误跳外层循环。
|
||||
# Loop control-flow targets (break/continue). A break inside a match case exits the match:
|
||||
# MatchGenMixin temporarily overrides _break_bb with the match end_bb while in a case body.
|
||||
_break_bb: rawptr[mlirlite.BasicBlock]
|
||||
_continue_bb: rawptr[mlirlite.BasicBlock]
|
||||
|
||||
# 当前是否处于 **switch** 的 case 语句体内(1=是)。供 `fallthrough` 语句判定
|
||||
# 合法性:只在 switch 的 case 体内合法;写进 match 的 case 体或 switch 之外
|
||||
# 一律 fail-fast(match 完全禁止 fallthrough)。由 MatchGenMixin 进出 case 时
|
||||
# 置为 `node.is_switch`(真 match=0,switch=1),出 case 恢复。
|
||||
# Whether we are inside a switch case body (1=yes). `fallthrough` is legal only there;
|
||||
# match case bodies and non-switch code fail-fast. Set from node.is_switch by MatchGenMixin.
|
||||
_in_switch_case: int
|
||||
|
||||
# 独立标签计数器(不与 builder.Counter 共享,避免 SSA 编号非单调)
|
||||
# Standalone label counter (not shared with builder.Counter, keeps SSA numbering monotonic)
|
||||
_label_counter: int
|
||||
|
||||
# 追加90:控制流 merge 块可达性(1=有 br 进入可达/0=全分支 return 结构性不可达)
|
||||
# Control-flow merge block reachability (1=reachable via br, 0=unreachable, all branches return)
|
||||
_ctrl_merge_reachable: int
|
||||
|
||||
# match 翻译递归深度(match → case body → translate_value → 嵌套 match)。
|
||||
# 守卫:AST 自引用环(子节点回指祖先)或翻译 bug 导致的无限递归在此
|
||||
# fail-fast(对齐 TypeGenResolve 深度守卫,防 ASan stack-overflow)。
|
||||
# match translation recursion depth (match -> case body -> translate_value -> nested match).
|
||||
# Guards against infinite recursion from AST self-cycles or bugs (fail-fast, prevents stack overflow).
|
||||
MatchTranslateDepth: int
|
||||
|
||||
# 翻译主环递归深度(translate_stmt/translate_value 入口计数)。match 守卫
|
||||
# 只覆盖 match 递归;语句/表达式/调用环(如 translate_value ↔ translate_call)
|
||||
# 由本字段守卫,超 TRANSLATE_MAX_DEPTH 即 fail-fast(防 ASan stack-overflow)。
|
||||
# Translation main-loop recursion depth (counted at translate_stmt/translate_value entry).
|
||||
# Guards statement/expression/call recursion; exceeding TRANSLATE_MAX_DEPTH fails fast.
|
||||
TranslateDepth: int
|
||||
|
||||
# global 声明的变量名集合(当前函数内)
|
||||
# Set of global-declared variable names (current function)
|
||||
_global_names: str
|
||||
_global_name_count: int
|
||||
|
||||
# nonlocal 声明的变量名集合(当前函数内)
|
||||
# Set of nonlocal-declared variable names (current function)
|
||||
_nonlocal_names: str
|
||||
_nonlocal_name_count: int
|
||||
# nonlocal 变量的类型信息(LLVMType 指针数组,与 _nonlocal_names 一一对应)
|
||||
# 用于 env 指针数组方案:子函数访问 nonlocal 变量时需要知道原始类型
|
||||
# Type info for nonlocal variables (LLVMType pointer array aligned with _nonlocal_names).
|
||||
# Used by the env pointer array scheme: inner functions need the original type.
|
||||
_nonlocal_types: str
|
||||
|
||||
# 闭包相关:当前函数名(用于嵌套函数提升命名)
|
||||
# Closure: current function name (used to name hoisted nested functions)
|
||||
_cur_func_name: str
|
||||
|
||||
# *args 变长参数支持:当前函数的 va_list 指针(i8* alloca)
|
||||
# 仅当函数声明了 *args 时设置,供 arg(type) 糖函数使用
|
||||
# *args support: va_list pointer of the current function (i8* alloca), set only when the
|
||||
# function declares *args; used by the arg(type) sugar.
|
||||
_va_list_ptr: mlirlite.Value*
|
||||
# *args 参数名(如 "args"),用于符号表注册
|
||||
# *args parameter name (e.g. "args"), used for symbol-table registration
|
||||
_vararg_name: str
|
||||
|
||||
# OOP iter 协议:当前 __next__ 方法的隐藏 stop-flag 参数(i1*)。
|
||||
# _translate_method 注入 __stop_iter_flag__ 参数时设置;translate_raise
|
||||
# 检测 raise StopIteration 时对该指针 store 1(对齐 TPC HandlesRaise.py)。
|
||||
# 非 __next__ 函数中为 None。
|
||||
# OOP iter protocol: hidden stop-flag parameter (i1*) of the current __next__ method.
|
||||
# translate_raise stores 1 there on raise StopIteration; None outside __next__.
|
||||
_stop_iter_flag_ptr: mlirlite.Value*
|
||||
|
||||
# try-except 机制(对齐 TPC HandlesTry.py):当前最内层 try 的
|
||||
# 分发块 + 异常编码/消息 alloca。translate_try 设置,translate_raise
|
||||
# 与调用侧 EH 检测写入/跳转。try 外为 None。
|
||||
# try-except state: dispatch block plus exception code/message allocas of the innermost try.
|
||||
# Set by translate_try; written by translate_raise and call-site EH checks. None outside try.
|
||||
_eh_dispatch_bb: rawptr[mlirlite.BasicBlock]
|
||||
_eh_code_ptr: mlirlite.Value*
|
||||
_eh_msg_ptr: mlirlite.Value*
|
||||
# 当前函数的 __eh_msg_out__(i8**)/__eh_code_out__(i32*)输出参数
|
||||
# (函数签名含 raise 时由 translate_function_def/_translate_method 注入)。
|
||||
# 无 EH 参数时 _cur_func_has_eh == 0。
|
||||
# Current function's __eh_msg_out__ (i8**) / __eh_code_out__ (i32*) output parameters,
|
||||
# injected when the signature involves raise. _cur_func_has_eh == 0 when there are none.
|
||||
_eh_msg_out_param: mlirlite.Value*
|
||||
_eh_code_out_param: mlirlite.Value*
|
||||
_cur_func_has_eh: int
|
||||
|
||||
# 模块 SHA1 前缀(16 字符),用于函数名混淆(SHA1 命名空间)
|
||||
# Module SHA1 prefix (16 chars) used for function-name mangling (SHA1 namespace)
|
||||
ModuleSha1: str
|
||||
# 当前文件所属包名(如 "mlirlite"),用于解析相对导入(from . import ...)
|
||||
# None 表示当前文件是顶级模块(无包),相对导入无法解析
|
||||
# Package name of the current file (e.g. "mlirlite"), used to resolve relative imports.
|
||||
# None means a top-level module (no package), so relative imports cannot be resolved.
|
||||
CurrentPackage: str
|
||||
# 闭包 env 中的 nonlocal 变量偏移映射(每个 nonlocal 变量在 env 中的字节偏移)
|
||||
# Byte offset of each nonlocal variable in the closure env
|
||||
_closure_env_offsets: int
|
||||
_closure_env_count: int
|
||||
# lambda 表达式全局唯一 ID 计数器(生成提升函数名 __lambda_{id})
|
||||
# 独立于 _closure_env_count:后者随函数进入被 clear_scope_names 重置,
|
||||
# lambda ID 需跨函数全局唯一(提升函数名避免冲突)
|
||||
# Globally unique lambda ID counter (generates hoisted names __lambda_{id}).
|
||||
# Kept independent of _closure_env_count, which resets per function, so IDs stay unique.
|
||||
_lambda_count: int
|
||||
# 函数链内 nonlocal 变量集合(预扫描收集,含嵌套函数声明的 nonlocal)。
|
||||
# 用于"不同层捕获不同变量"场景(如 level1→a, level2→b):b 定义在最外层
|
||||
# 但 level1 不捕获——level1 env 无 b 槽,level2 无法经 env 链获取 b 的堆。
|
||||
# 解决:在最外层函数中为链内所有 nonlocal 建堆,并把堆地址 store 到
|
||||
# 模块级映射变量 @nlheap.{sha1}.{name};内层闭包创建时从映射 load。
|
||||
# Set of nonlocal variables across the function chain (collected by pre-scan).
|
||||
# The outermost function allocates their heaps and stores addresses in @nlheap.{sha1}.{name}.
|
||||
_nl_chain_names: t.CPtr
|
||||
_nl_chain_count: int
|
||||
# 链内 nonlocal 堆预建标志(每函数只建一次):translate_function_def 进入
|
||||
# 新函数时重置为 0;_prepare_chain_nonlocals 首次调用置 1。多个嵌套函数
|
||||
# 定义共享同一堆(防重复 malloc + 二次重定向 VarEntry.Alloca)。
|
||||
# Chain-nonlocal heap built flag (built once per function): reset to 0 on entering a function,
|
||||
# set to 1 by _prepare_chain_nonlocals. Shared by nested definitions to avoid double malloc.
|
||||
_nl_chain_done: int
|
||||
|
||||
# 泛型特化上下文:当前正在特化的类型参数名/实参名列表
|
||||
# 由 _specialize_generic_class 设置,方法体翻译时用于将 T 替换为具体类型
|
||||
# None 表示不在泛型特化上下文中
|
||||
# Generic specialization context: type parameter and argument name lists of the class
|
||||
# currently being specialized. Set by _specialize_generic_class; None when not specializing.
|
||||
GenericTypeParamNames: list[str]*
|
||||
GenericTypeArgs: list[str]*
|
||||
|
||||
# Phase 1a 声明模式标志:1=只注册 struct/enum/union 不翻译代码体,0=全量翻译
|
||||
# Phase 1a declare mode: 1=register struct/enum/union only (no body), 0=full translation
|
||||
_declare_only: int
|
||||
|
||||
# App 预注册(Phase A-pre)专用开关:置 1 时,即使 _declare_only != 0 也执行
|
||||
# 导入语句处理与 LoadDepVpiIntoModule(依赖 stub 注入),使**类字段注解在预注册期
|
||||
# 就与阶段 A 处于同一上下文**。
|
||||
# 为什么需要:预注册期要注册类的字段类型,而包重导出类的票名前缀校正
|
||||
# (`_class_true_def_sha1`)依赖包 __init__ 注入时登记的别名 entry;预注册期不注入
|
||||
# ⇒ 只能保留**包前缀**,该票名被冻结进结构体布局,与阶段 A 构造函数侧解析出的
|
||||
# 真定义前缀分裂 ⇒「指针隐式转指针已禁止」(现场 ClassGenGeneric.vp:92,
|
||||
# `list[ast.ClassDef*]`:预注册得 1abcd….ClassDef*、阶段 A 得 e70bd….ClassDef*)。
|
||||
# 只对 **App 预注册**置位:includes 预注册(Phase A-pre-inc)必须保持不注入,
|
||||
# 否则依赖环自锁(见下方 `_declare_only == 0` 门处注释)。
|
||||
# App pre-registration (Phase A-pre) switch: when 1, imports and LoadDepVpiIntoModule run even
|
||||
# if _declare_only != 0, keeping class field annotations in phase A's context. App-only.
|
||||
_pre_allow_dep_inject: int
|
||||
|
||||
# 嵌套作用域符号表
|
||||
# Nested-scope symbol table
|
||||
SymTab: rawptr[GenTypes.SymbolTable]
|
||||
|
||||
# REPL 模式上下文(M2 会话变量):None ⇒ 非 REPL(普通编译路径逐字不变)。
|
||||
# 仅 Repl.vp 在翻译片段前置位、翻译后清空;Gen 侧全部会话槽拦截点以
|
||||
# `trans.ReplCtx is not None` 为唯一守卫。
|
||||
# REPL mode context (M2 session variables): None means non-REPL (normal path unchanged).
|
||||
# Only Repl.vp sets it; Gen-side session-slot interception checks `trans.ReplCtx is not None`.
|
||||
ReplCtx: rawptr[GenTypes.ReplCtx]
|
||||
|
||||
def __init__(self):
|
||||
@@ -331,7 +265,7 @@ novirtual class Translator:
|
||||
self._declare_only = 0
|
||||
self._pre_allow_dep_inject = 0
|
||||
self.SymTab = None
|
||||
# REPL 模式上下文:默认未设置(= 非 REPL,Gen 侧拦截全部旁路)
|
||||
# REPL mode context: unset by default (= non-REPL, all Gen-side interception bypassed)
|
||||
self.ReplCtx = None
|
||||
self._async_funcs = None
|
||||
self._async_func_count = 0
|
||||
@@ -375,15 +309,14 @@ novirtual class Translator:
|
||||
self._coro_wake_count = 0
|
||||
|
||||
# ============================================================
|
||||
# 状态初始化
|
||||
# ============================================================
|
||||
# State initialization
|
||||
def _init_state(self, pool: memhub.MemBuddy*):
|
||||
"""初始化变量表、函数表和子 Handle"""
|
||||
"""Initialize the variable table, function table, and sub-handles."""
|
||||
self.Pool = pool
|
||||
if self._funcs is None: self._funcs = GenApi.init_func_table(pool, MAX_FUNCS)
|
||||
# 嵌套作用域符号表(新版)
|
||||
# Nested-scope symbol table (new)
|
||||
if self.SymTab is None: self.SymTab = GenApi.init_symbol_table(pool)
|
||||
# global/nonlocal 名称集合缓冲区(MAX_GLOBAL_NAMES 个 char* 指针 = 128*8 字节)
|
||||
# global/nonlocal name buffers (MAX_GLOBAL_NAMES char* pointers = 128*8 bytes)
|
||||
if self._global_names is None:
|
||||
self._global_names = stdlib.malloc(MAX_GLOBAL_NAMES * 8)
|
||||
if self._global_names is not None: string.memset(self._global_names, 0, MAX_GLOBAL_NAMES * 8)
|
||||
@@ -392,12 +325,12 @@ novirtual class Translator:
|
||||
self._nonlocal_names = stdlib.malloc(MAX_GLOBAL_NAMES * 8)
|
||||
if self._nonlocal_names is not None: string.memset(self._nonlocal_names, 0, MAX_GLOBAL_NAMES * 8)
|
||||
self._nonlocal_name_count = 0
|
||||
# nonlocal 变量类型数组(LLVMType 指针数组,与 _nonlocal_names 一一对应)
|
||||
# nonlocal variable type array (LLVMType pointers, aligned with _nonlocal_names)
|
||||
if self._nonlocal_types is None:
|
||||
self._nonlocal_types = stdlib.malloc(MAX_GLOBAL_NAMES * 8)
|
||||
if self._nonlocal_types is not None: string.memset(self._nonlocal_types, 0, MAX_GLOBAL_NAMES * 8)
|
||||
|
||||
# 协程表(TPV-M1):async 函数名 / 变量→async 映射 / 局部名 / 帧类型
|
||||
# Coroutine tables (TPV-M1): async names / var->async map / locals / frame types
|
||||
if self._async_funcs is None:
|
||||
self._async_funcs = stdlib.malloc(MAX_CORO_FUNCS * 8)
|
||||
if self._async_funcs is not None: string.memset(self._async_funcs, 0, MAX_CORO_FUNCS * 8)
|
||||
@@ -437,24 +370,22 @@ novirtual class Translator:
|
||||
self._coro_frame_count = 0
|
||||
|
||||
# ============================================================
|
||||
# 主翻译入口
|
||||
# ============================================================
|
||||
# Main translation entry
|
||||
def translate(self, tree: ast.AST*) -> int:
|
||||
"""将 AST 翻译为 LLVM IR"""
|
||||
"""Translate the AST into LLVM IR."""
|
||||
if tree is None: return 1
|
||||
if _mbuddy is None: return 1
|
||||
|
||||
pool: memhub.MemBuddy* = _mbuddy
|
||||
|
||||
# OOM 归属:把「阶段/当前模块」写进 memhub 的分配上下文。池耗尽时 memhub 会打印
|
||||
# ctx=阶段/模块,从而知道是在翻译哪个模块时被吃满(SetAllocCtx 只存指针不拷贝,
|
||||
# ModuleSha1 由调用方在 translate 前设置且与本次翻译同寿命)。此前无人设置该上下文。
|
||||
# OOM attribution: record the phase/current module in memhub's allocation context so
|
||||
# exhaustion reports ctx=phase/module (SetAllocCtx stores the pointer only).
|
||||
memhub.SetAllocCtx("translate", self.ModuleSha1)
|
||||
|
||||
# 初始化状态
|
||||
# Initialize state
|
||||
self._init_state(pool)
|
||||
|
||||
# 创建 LLVM 模块
|
||||
# Create LLVM module
|
||||
mod: mlirlite.LLVMModule* = mlirlite.new_module(pool, "main")
|
||||
if mod is None:
|
||||
VLogger.error("NewModule returned NULL", "TR")
|
||||
@@ -464,42 +395,41 @@ novirtual class Translator:
|
||||
# --traceback: reset per-module DWARF state (no-op when DI is disabled).
|
||||
DebugInfoGen.BeginModule(mod, GenApi.get_current_file())
|
||||
|
||||
# 设置目标(优先使用 project.vpj / viperbuild 中的配置)
|
||||
# Set target (prefer project.vpj / viperbuild config)
|
||||
triple: str = Config.TargetTriple
|
||||
if triple is None: triple = "x86_64-pc-windows-msvc"
|
||||
mlirlite.module_set_target(mod, triple)
|
||||
# 数据布局:project.vpj / viperbuild 的 target.datalayout(None 时不输出该行)。
|
||||
# 写入 .ll 模块头,使生成的 IR 自带 LLVM 布局信息,与目标 triple 配套。
|
||||
# Data layout from project.vpj / viperbuild target.datalayout (omitted when None).
|
||||
# Written into the .ll module header so the IR carries layout info matching the target triple.
|
||||
if Config.TargetDataLayout is not None: mlirlite.module_set_datalayout(mod, Config.TargetDataLayout)
|
||||
|
||||
# 类型
|
||||
# Types
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||
|
||||
# 声明 printf
|
||||
# Declare printf
|
||||
printf_func: mlirlite.Function* = mlirlite.create_declare(pool, mod, "printf", i32_ty)
|
||||
if printf_func is None:
|
||||
VLogger.error("CreateDeclare printf returned NULL", "TR")
|
||||
return 1
|
||||
mlirlite.add_param(pool, printf_func, i8_ptr_ty, "fmt")
|
||||
printf_func.IsVarArg = 1
|
||||
# 声明 LLVM va_start/va_end intrinsic(供 *args 函数体内使用)
|
||||
# declare void @llvm.va_start(i8*)
|
||||
# declare void @llvm.va_end(i8*)
|
||||
# Declare LLVM va_start/va_end intrinsics (used inside *args function bodies):
|
||||
# declare void @llvm.va_start(i8*) / declare void @llvm.va_end(i8*)
|
||||
void_ty_decl: mlirlite.LLVMType* = mlirlite.Void(pool)
|
||||
va_start_func: mlirlite.Function* = mlirlite.create_declare(pool, mod, "llvm.va_start", void_ty_decl)
|
||||
if va_start_func is not None: mlirlite.add_param(pool, va_start_func, i8_ptr_ty, "ap")
|
||||
va_end_func: mlirlite.Function* = mlirlite.create_declare(pool, mod, "llvm.va_end", void_ty_decl)
|
||||
if va_end_func is not None: mlirlite.add_param(pool, va_end_func, i8_ptr_ty, "ap")
|
||||
|
||||
# 声明 malloc(闭包分配用)
|
||||
# Declare malloc (for closure allocation)
|
||||
malloc_func: mlirlite.Function* = mlirlite.create_declare(pool, mod, "malloc", i8_ptr_ty)
|
||||
if malloc_func is not None:
|
||||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
mlirlite.add_param(pool, malloc_func, i64_ty, "size")
|
||||
|
||||
# 检查用户是否定义了 main 函数
|
||||
# Check whether the user defined a main function
|
||||
has_user_main: int = 0
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
@@ -514,74 +444,45 @@ novirtual class Translator:
|
||||
break
|
||||
|
||||
if self._declare_only != 0:
|
||||
# Phase 1a-pre(2=import扫描) / Phase 1a(1=struct注册): 只处理模块级,不创建 main 函数和 builder
|
||||
# Phase 1a-pre(2=import scan) / Phase 1a(1=struct registration): module level only, no main/builder
|
||||
self._translate_module_level(pool, mod, tree)
|
||||
elif has_user_main == 0:
|
||||
# 无用户 main → 只翻译模块级语句,不创建 wrapper main
|
||||
# (避免每个文件都生成 define i32 @main() 导致链接冲突;只有 def main() 的入口文件才生成)
|
||||
# No user main -> translate module-level statements only, no wrapper main
|
||||
# (avoids define i32 @main() in every file; only the def main() entry file emits it).
|
||||
self._translate_module_level(pool, mod, tree)
|
||||
else:
|
||||
# 用户已定义 main → 委托 HandlesMain 翻译模块级
|
||||
# User main defined -> delegate module-level translation to HandlesMain
|
||||
self._translate_module_level(pool, mod, tree)
|
||||
|
||||
# --traceback: register module-level !llvm.dbg.cu / !llvm.module.flags
|
||||
# before the caller dumps the IR. Idempotent; no-op when no define got a
|
||||
# DISubprogram (stub / _declare_only) or when DI is disabled.
|
||||
# --traceback: register module-level !llvm.dbg.cu / !llvm.module.flags before the caller
|
||||
# dumps the IR. Idempotent; no-op with no DISubprogram or when DI is disabled.
|
||||
DebugInfoGen.Finalize(pool)
|
||||
|
||||
return 0
|
||||
# ============================================================
|
||||
# 包装 main 函数(无用户 main 时)→ 委托 HandlesMain
|
||||
# ============================================================
|
||||
# Wrapper main (when there is no user main) -> delegate to HandlesMain
|
||||
def _translate_wrapper_main(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, tree: ast.AST*, i32_ty: mlirlite.LLVMType*):
|
||||
"""委托 GenApi.create_wrapper_main() 创建包装 main(trans 单参)"""
|
||||
"""Delegate to GenApi.create_wrapper_main() to build the wrapper main (trans arg)."""
|
||||
added: int = GenApi.create_wrapper_main(self, tree, i32_ty)
|
||||
|
||||
# ============================================================
|
||||
# 模块级翻译(用户已定义 main)→ 委托 HandlesMain
|
||||
# ============================================================
|
||||
# Module-level translation (user main defined) -> delegate to HandlesMain
|
||||
def _translate_module_level(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, tree: ast.AST*):
|
||||
"""委托 GenApi.translate_children() 翻译模块级语句(trans 单参)"""
|
||||
# 【文件级类名预登记:三种模式都必须先做(2026-09-30)】
|
||||
# `_norm_type_arg_name` 规则 5.5 的第五类证据(is_file_class +
|
||||
# get_file_module_sha1)**只由本调用建立**,而下方 prologue 整块被
|
||||
# `_declare_only == 0` 门住 ⇒ Phase 1a(_declare_only == 1)的
|
||||
# `_prescan_funcdef_signatures` 解析**模块级函数签名**注解时,证据⑤ 恒落空:
|
||||
# 现场 includes/ast/base.vp —— 类型实参裸名 'AST' 的 ①②③④ 也全落空
|
||||
# (该类尚未 register_struct;"类定义窗口"还停在更早的 enum ASTFlag,
|
||||
# CurDefClassName 不会在类结束后复位)⇒ 实参特化名被冻结成**裸名**版本,
|
||||
# 与 1b 全量翻译期解析出的带 SHA1 形式分裂成两个特化结构体名,最终以
|
||||
# "非基本类型指针不可隐式转换"报出。
|
||||
# 登记只依赖 tree + 本文件模块 SHA1,与导入、类定义窗口、类定义顺序都无关,
|
||||
# 故提到门之前,三种模式(1a=1 / 1a-pre=2 / 全量=0)共用。
|
||||
"""Delegate to GenApi.translate_children() to translate module-level statements (trans arg)."""
|
||||
# File-level class-name pre-registration must run in all three modes: it is the only source of
|
||||
# evidence #5 for `_norm_type_arg_name` rule 5.5 and is hoisted above the _declare_only gate.
|
||||
GenApi.register_file_classes(pool, self.ModuleSha1, tree)
|
||||
# 本文件顶层函数名预登记(同址、同目的):依赖 vpi 注入的**裸名**声明
|
||||
# (extern/state/export 不带 SHA1 前缀)若与本文件自己的声明同名,会先占用
|
||||
# 该名字,使本文件声明在 forward_declare_functions 被静默丢弃(调用点改按
|
||||
# 依赖签名 coerce)。现场:includes/socket.vp 的 extern def connect 被
|
||||
# w32.winsock2 的 connect 顶掉 → socket.vp:236 报
|
||||
# 「指针隐式转指针已禁止:SocketAddr* → WSASocketAddr*」。
|
||||
# Pre-register this file's top-level function names: otherwise a bare-name vpi declaration
|
||||
# sharing a name shadows the local one in forward_declare_functions (calls coerce to the dep).
|
||||
GenApi.register_file_funcs(pool, tree)
|
||||
# viperbuild 宏语句擦除:把模块级 `import viperbuild` 配套的宏语句
|
||||
# proj = vb.Project(...) / proj.<字段> = ... / proj.config(...) / proj.build(...)
|
||||
# 从 tree.children 里摘掉(Pass 0 已在配置面消费;翻译期若留着重放会被当真实
|
||||
# 调用 codegen ⇒ `vb.Project` 未定义类)。位置必须在下面 import 处理/变量预注册/
|
||||
# translate_children 之前,使三者看到同一份已擦除的 children。
|
||||
# Erase viperbuild macro statements (proj = vb.Project(...), field assigns, config/build calls)
|
||||
# from tree.children so later passes see the same erased tree; Pass 0 already consumed them.
|
||||
self._erase_viperbuild_macros(pool, tree)
|
||||
# 先处理导入语句,再创建前向声明,解决前向引用问题。
|
||||
# 两道门分开:
|
||||
# ① 导入处理 + 自动容器可见性 + 依赖 vpi 注入 —— 全量翻译**与 App 预注册**
|
||||
# (_pre_allow_dep_inject,见 Translator 字段说明)都要做,使类字段注解
|
||||
# 在预注册期即与阶段 A 同上下文(否则包重导出类的票名前缀会分裂:
|
||||
# list[ast.ClassDef*] 现场);
|
||||
# ② 模块级变量预注册 + 函数前向声明 —— 仍只在全量翻译做(声明期跳过,
|
||||
# 保持 Phase A-pre 的"只注册 struct/enum/union"语义)。
|
||||
# includes 预注册(Phase A-pre-inc)两者都不做:_pre_allow_dep_inject 只由
|
||||
# App 预注册置位,避免依赖环自锁。
|
||||
# Two gates: (1) import handling + auto container visibility + vpi injection run in full
|
||||
# translation and App pre-registration; (2) module var pre-registration + forward declarations run only in full translation.
|
||||
if self._declare_only == 0 or self._pre_allow_dep_inject != 0:
|
||||
# 预处理导入语句,确保 _imported_modules 和 _from_imports 已填充
|
||||
# (前向声明需要解析类型注解,如 t.CArray[str] 依赖 t 模块已导入)
|
||||
# 记录最后处理的 import 行号,供 vpi 注入错误上下文帧定位
|
||||
# Pre-process imports so _imported_modules/_from_imports are filled (forward declarations need them).
|
||||
# Track the last import lineno for vpi injection error-context frames.
|
||||
cur_imp_lineno: int = 0
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
@@ -597,63 +498,35 @@ novirtual class Translator:
|
||||
cur_imp_lineno = child.lineno
|
||||
GenApi.translate_import_from_module(self, child)
|
||||
GenApi.translate_import_from_names(self, child)
|
||||
# 对齐 TPC _inject_auto_imports:检测源文件对 list/dict 容器的使用
|
||||
# (裸名注解 d: dict / 构造 dict(...) / 下标 list[T]),自动把容器
|
||||
# 类标记为可见结构体。背景:_list.vp/_dict.vp 定义内建容器类且已由
|
||||
# Phase1 注册,但源文件不显式 import 它们(TPC 在翻译前自动注入
|
||||
# import _list/_dict);TPV 用可见性机制等效替代——否则裸名
|
||||
# `d: dict` 注解 resolve_annotation_type 时 is_struct_visible=0
|
||||
# 解析失败(DictTest 的 "变量类型注解解析失败")。
|
||||
# Mirror TPC _inject_auto_imports: detect list/dict container usage (bare annotations,
|
||||
# constructors, subscripts) and mark the container classes visible so bare names resolve.
|
||||
self._inject_auto_container_visibility(pool, tree)
|
||||
# CTypedef 声明由 Phase1 1b 文本扫描注册(RegisterCTypedefFromSource)——
|
||||
# AST 字符串字段 token 零拷贝指向 src_buf,跨阶段内容不稳定,此处 AST 扫描
|
||||
# 读 attr 会 0xC0000005,故移除。
|
||||
# 跨模块 .vpi 注入:加载依赖模块的 .vpi,把函数 declare 注入当前模块。
|
||||
# 注入条件 = 全量翻译,或 **App 预注册**(_pre_allow_dep_inject):后者必须
|
||||
# 与阶段 A 同上下文,否则预注册期解析出的类字段票名(包重导出类的
|
||||
# 真定义前缀校正依赖包别名 entry)会与阶段 A 分裂。includes 预注册
|
||||
# (Phase A-pre-inc)不置位该开关 ⇒ 仍不注入,避免依赖环自锁。
|
||||
# CTypedef declarations are registered by Phase1 text scanning (AST string tokens are unstable).
|
||||
# Cross-module .vpi injection loads dep .vpi declares when full-translating or during App pre-registration.
|
||||
if self._imported_modules is not None:
|
||||
# 错误上下文帧:当前模块触发依赖 stub 加载的 import 位置
|
||||
# Error-context frame: import site that triggered dependency stub loading
|
||||
GenApi.push_trace(GenApi.get_current_file(), cur_imp_lineno)
|
||||
StubMergerBuild.LoadDepVpiIntoModule(pool, mod, self._imported_modules, self._from_imports, GenApi.get_temp_dir(), self)
|
||||
GenApi.pop_trace()
|
||||
# 文件级类名预登记已提前到本函数开头执行(覆盖三种 _declare_only 模式;
|
||||
# 为什么必须早于模块级变量/前向声明,见那里的说明)。
|
||||
# 模块级变量预注册 + 函数前向声明:仍**只在全量翻译**做(声明期跳过,
|
||||
# 保持 Phase A-pre"只注册 struct/enum/union"的语义)。
|
||||
# File-level class pre-registration already ran at the top of this function.
|
||||
# Module var pre-registration + forward declarations run only in full translation.
|
||||
if self._declare_only == 0:
|
||||
# 预注册模块级变量(AnnAssign/Assign)到 SymTab
|
||||
# 解决时序问题:class Logger 的 __init__ 引用 _mbuddy 时,
|
||||
# _mbuddy 的 AnnAssign 在 AST 中位于 class Logger 之后,
|
||||
# 按 AST 顺序翻译时 _mbuddy 尚未注册 → lookup_var 返回 None → rhs_val is None
|
||||
# 预处理先于 translate_children 注册所有模块级变量,
|
||||
# 后续 translate_children 遍历到同一 AnnAssign 时 handle_module_level_var
|
||||
# 通过 lookup_module_var 检测已存在并跳过(HandlesMain.py:206-209)
|
||||
# Pre-register module-level variables (AnnAssign/Assign) into SymTab to fix ordering: a class
|
||||
# may reference a module variable declared later in the AST. translate_children later skips existing ones.
|
||||
if ch is not None:
|
||||
for ci in range(cn_count):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
kd_mv: int = child.kind()
|
||||
if kd_mv == ast.ASTKind.AnnAssign or kd_mv == ast.ASTKind.Assign: GenApi.handle_module_level_var(self, child)
|
||||
# 创建前向声明
|
||||
# Create forward declarations
|
||||
GenApi.forward_declare_functions(self, tree)
|
||||
added: int = GenApi.translate_children(self, tree)
|
||||
|
||||
# ============================================================
|
||||
# _erase_viperbuild_macros - 擦除模块级 viperbuild 宏语句
|
||||
# ------------------------------------------------------------
|
||||
# viperbuild 是**编译器内建宏库**(编译期配置面),其用法形如:
|
||||
# import viperbuild as vb
|
||||
# proj = vb.Project("Test", "1.0.0")
|
||||
# proj.build(selfmachine=True)
|
||||
# 这些是**宏语句**:Pass 0(lib.core.builtin.ViperBuild)已从依赖闭包把它们消费成
|
||||
# Config 配置;翻译期若原样放行,`vb.Project`/`proj.build` 会被当真实调用
|
||||
# codegen(未定义类/函数)⇒ 报错。故此处把整组语句从 tree.children 摘除。
|
||||
# 只处理**模块级顶层语句**(配置不允许写在函数体里)。
|
||||
# ============================================================
|
||||
# _erase_viperbuild_macros - erase module-level viperbuild macro statements.
|
||||
# viperbuild is a compile-time macro library consumed by Pass 0; leaving its statements would codegen undefined calls.
|
||||
def _vb_in_proj_vars(self, name: str, proj_vars: list[str]*) -> int:
|
||||
"""name 是否为已登记的 Project 宏变量;1=是"""
|
||||
"""Return 1 if name is a registered Project macro variable."""
|
||||
if name is None or proj_vars is None: return 0
|
||||
n: sizet = len(proj_vars)
|
||||
i: sizet = 0
|
||||
@@ -664,7 +537,7 @@ novirtual class Translator:
|
||||
return 0
|
||||
|
||||
def _vb_is_project_ctor(self, value: ast.AST*, alias: str, ctor: str) -> int:
|
||||
"""value 是否为 `vb.Project(...)` / `Project(...)`;1=是"""
|
||||
"""Return 1 if value is `vb.Project(...)` or `Project(...)`."""
|
||||
if value is None or value.kind() != ast.ASTKind.Call: return 0
|
||||
cl: ast.Call* = ast.Call*(value)
|
||||
if cl is None or cl.func is None: return 0
|
||||
@@ -687,7 +560,7 @@ novirtual class Translator:
|
||||
return 0
|
||||
|
||||
def _vb_is_macro_assign(self, target: ast.AST*, value: ast.AST*, alias: str, ctor: str, proj_vars: list[str]*) -> int:
|
||||
"""赋值是否为 viperbuild 宏语句(Project 绑定 / 字段赋值);1=是且已登记"""
|
||||
"""Return 1 if the assignment is a viperbuild macro statement and register it."""
|
||||
if target is None or value is None: return 0
|
||||
tk: int = target.kind()
|
||||
if tk == ast.ASTKind.Name:
|
||||
@@ -707,7 +580,7 @@ novirtual class Translator:
|
||||
return 0
|
||||
|
||||
def _vb_is_macro_call(self, value: ast.AST*, proj_vars: list[str]*) -> int:
|
||||
"""表达式是否为 `proj.config(...)` / `proj.build(...)`;1=是"""
|
||||
"""Return 1 if the expression is `proj.config(...)` or `proj.build(...)`."""
|
||||
if value is None or value.kind() != ast.ASTKind.Call: return 0
|
||||
cl: ast.Call* = ast.Call*(value)
|
||||
if cl is None or cl.func is None: return 0
|
||||
@@ -723,7 +596,7 @@ novirtual class Translator:
|
||||
return 0
|
||||
|
||||
def _vb_is_macro_stmt(self, child: ast.AST*, alias: str, ctor: str, proj_vars: list[str]*) -> int:
|
||||
"""单条模块级语句是否为 viperbuild 宏语句;1=是(应擦除)"""
|
||||
"""Return 1 if a module-level statement is an erasable viperbuild macro statement."""
|
||||
if child is None: return 0
|
||||
kd: int = child.kind()
|
||||
if kd == ast.ASTKind.Assign:
|
||||
@@ -742,12 +615,12 @@ novirtual class Translator:
|
||||
return 0
|
||||
|
||||
def _erase_viperbuild_macros(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int:
|
||||
"""擦除模块级 viperbuild 宏语句(就地替换 tree.children);1=发生擦除"""
|
||||
"""Erase module-level viperbuild macro statements (replacing tree.children); 1 if erased."""
|
||||
if tree is None or tree.children is None: return 0
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
cn: sizet = len(ch)
|
||||
if cn == 0: return 0
|
||||
# ① 找 viperbuild 的本地名 / Project 构造名
|
||||
# (1) Find viperbuild's local name / Project constructor name
|
||||
alias: str = None
|
||||
ctor: str = None
|
||||
ci: sizet = 0
|
||||
@@ -780,7 +653,7 @@ novirtual class Translator:
|
||||
ni += 1
|
||||
ci += 1
|
||||
if alias is None and ctor is None: return 0
|
||||
# ② 过滤:先登记 Project 宏变量,再摘掉相关语句
|
||||
# (2) Filter: register Project macro variables, then drop the related statements
|
||||
proj_vars: list[str]* = list[str](pool, 8)
|
||||
kept: list[ast.AST*]* = list[ast.AST*](pool, 8)
|
||||
if proj_vars is None or kept is None: return 0
|
||||
@@ -795,18 +668,12 @@ novirtual class Translator:
|
||||
if erased != 0: tree.children = kept
|
||||
return erased
|
||||
|
||||
# 容器模块自动可见性注入(对齐 TPC _inject_auto_imports)
|
||||
# TPC 在翻译前自动注入 `import _list / import _dict (+json)`,使 list/dict 内建容器类
|
||||
# 在符号表中可见;TPV 用可见性机制等效替代:_list.vp/_dict.vp 已由 Phase1 注册,
|
||||
# 这里只需在当前文件翻译时把容器类名标记为可见结构体,裸名注解/构造即可解析。
|
||||
# 检测模式(与 TPC Phase2Translator._inject_auto_imports 一致):
|
||||
# - AnnAssign 注解裸名 Name(list/dict):d: dict
|
||||
# - 构造调用 func 裸名 Name(list/dict):dict(bd)
|
||||
# - Subscript 的 value 裸名 Name(list/dict):list[int]
|
||||
# Auto container visibility injection (mirrors TPC _inject_auto_imports): mark list/dict classes
|
||||
# visible so bare annotations/constructors resolve. Detects AnnAssign annotation, Call.func, Subscript.value.
|
||||
def _inject_auto_container_visibility(self, pool: memhub.MemBuddy*, tree: ast.AST*):
|
||||
"""检测 list/dict 容器使用,注入容器类可见性"""
|
||||
"""Detect list/dict container usage and inject container-class visibility."""
|
||||
if tree is None or tree.children is None: return
|
||||
# 用栈式缓冲区传递检测结果(跨函数参数用 4 字节 int 指针)
|
||||
# Use stack buffers to pass detection results (4-byte int pointers across helper calls)
|
||||
need_list_box: t.CPtr = stdlib.malloc(4)
|
||||
need_dict_box: t.CPtr = stdlib.malloc(4)
|
||||
if need_list_box is None or need_dict_box is None:
|
||||
@@ -817,13 +684,11 @@ novirtual class Translator:
|
||||
need_dict_box[0] = 0
|
||||
self._scan_container_usage(tree, need_list_box, need_dict_box)
|
||||
if need_dict_box[0] != 0:
|
||||
# dict 类可见 + _dict 模块导入(构造/方法调用的 SHA1 解析)
|
||||
# dict class visible + _dict module imported (SHA1 resolution for constructors/method calls)
|
||||
GenApi.add_visible_struct(pool, "dict")
|
||||
if self._imported_modules is None or GenApi.is_module_imported(self._imported_modules, "_dict") == 0:
|
||||
# [FIX 2026-08-31] 破环:_dict 自身依赖含当前模块则跳过注入。
|
||||
# 对齐 Phase1Main._InjectedCreatesCycle,覆盖所有 translate 路径
|
||||
# (Phase1 1b / Phase2 pre-inc / PhaseA)——否则 _list/_dict 显式
|
||||
# import memhub 且 memhub 用容器泛型时成 2-环,Phase A 永不收敛。
|
||||
# Break cycles: skip injection when _dict already depends on the current module (avoids a
|
||||
# memhub<->_list 2-cycle that stops Phase A from converging). Mirrors Phase1Main._InjectedCreatesCycle.
|
||||
if self._ContainerInjectionCreatesCycle("_dict") == 0: self._imported_modules = GenApi.add_imported_module(pool, self._imported_modules, "_dict")
|
||||
if need_list_box[0] != 0:
|
||||
GenApi.add_visible_struct(pool, "list")
|
||||
@@ -833,23 +698,13 @@ novirtual class Translator:
|
||||
stdlib.free(need_dict_box)
|
||||
|
||||
def _ContainerInjectionCreatesCycle(self, mod_name: str) -> int:
|
||||
"""注入 mod_name(_list/_dict)前检查是否成环,返回 1=成环需跳过注入。
|
||||
|
||||
判断被注入模块自身的 deps 是否已含当前模块名。若命中(如 _list 显式
|
||||
import memhub,而 memhub 因 `__provides__: list[str]` 注解要注入 _list)
|
||||
→ memhub↔_list 循环依赖 → Phase A 多遍收敛死锁(gfx.vp "无法完成翻译"
|
||||
根因)。对齐 Phase1Main._InjectedCreatesCycle,但本方法走 self.ModuleSha1
|
||||
查 RelPath,覆盖所有 translate 路径(Phase1 1b 全量翻译、Phase2 pre-inc/
|
||||
PhaseA 都会执行 _inject_auto_container_visibility)。命中则跳过注入:
|
||||
去掉环边后,_list 已因当前模块被其依赖而处于闭包,list 类仍由
|
||||
add_visible_struct 可见、泛型模板已由 Phase1 注册。
|
||||
"""
|
||||
"""Return 1 if injecting mod_name (_list/_dict) would create a dependency cycle."""
|
||||
if mod_name is None or self.ModuleSha1 is None: return 0
|
||||
inj_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
|
||||
if inj_sha1 is None: return 0
|
||||
inj_deps: str = StubMergerStore.LookupIncludesDeps(inj_sha1)
|
||||
if inj_deps is None: return 0
|
||||
# 当前模块名 = RelPath 基名(最后 '/' 或 '\\' 之后)去扩展名
|
||||
# Current module name = RelPath basename (after last '/' or '\\') without extension
|
||||
rel_path: str = StubMergerStore.LookupSha1RelPath(self.ModuleSha1)
|
||||
if rel_path is None: return 0
|
||||
base: str = string.strrchr(rel_path, '/')
|
||||
@@ -867,7 +722,7 @@ novirtual class Translator:
|
||||
else:
|
||||
string.strncpy(nm_buf, base, 127)
|
||||
nm_buf[127] = '\0'
|
||||
# 词边界精确匹配:inj_deps 中是否有 token 与当前模块名相同
|
||||
# Word-boundary match: whether inj_deps contains a token equal to the current module name
|
||||
nm_len2: sizet = string.strlen(nm_buf)
|
||||
d_len: sizet = string.strlen(inj_deps)
|
||||
dp: sizet = 0
|
||||
@@ -883,7 +738,7 @@ novirtual class Translator:
|
||||
return 0
|
||||
|
||||
def _scan_container_usage(self, node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr):
|
||||
"""递归扫描 AST 语句结构,检测 list/dict 容器使用"""
|
||||
"""Recursively scan AST statement structure for list/dict container usage."""
|
||||
if node is None: return
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.AnnAssign:
|
||||
@@ -909,20 +764,15 @@ novirtual class Translator:
|
||||
if arg_s is not None:
|
||||
ag_s: ast.Arg* = ast.Arg*(arg_s)
|
||||
if ag_s.annotation is not None: self._check_container_type_node(ag_s.annotation, need_list_box, need_dict_box)
|
||||
# `[...]`/`{}` 字面量不触发 _list/_dict 导入:字面量广泛用于编译期常量列表
|
||||
# (c.Asm 的 op/inp/out CLOBBER 参数、数组数据初始化),并非 list 容器使用。
|
||||
# list/dict 容器的真实使用由类型位置检测覆盖:`x: list` / `list[...]` 注解、
|
||||
# `list(...)`/`dict(...)` 构造(_check_container_type_node 的 AnnAssign.annotation /
|
||||
# Call.func / Subscript.value 分支)。无注解 `x = [...]` 若意图 list,会在
|
||||
# translate_list_literal 阶段 fail-fast 提示 _list 未注册(需显式注解)。
|
||||
# 递归语句块子节点
|
||||
# `[...]`/`{}` literals do not trigger _list/_dict imports (widely used for compile-time constants).
|
||||
# Real container usage is detected in type positions; then recurse into statement-block children.
|
||||
if node.children is not None:
|
||||
ch_s: list[ast.AST*]* = node.children
|
||||
n_s: sizet = len(ch_s)
|
||||
for ci_s in range(n_s): self._scan_container_usage(ch_s.get(ci_s), need_list_box, need_dict_box)
|
||||
|
||||
def _check_container_type_node(self, node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr):
|
||||
"""检测类型位置节点是否为 list/dict(支持 Name / Subscript / BinOp 递归)"""
|
||||
"""Check whether a type-position node is list/dict, recursing through Name/Subscript/BinOp."""
|
||||
if node is None: return
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
@@ -933,7 +783,7 @@ novirtual class Translator:
|
||||
elif k == ast.ASTKind.Subscript:
|
||||
sub_s: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub_s.value is not None: self._check_container_type_node(sub_s.value, need_list_box, need_dict_box)
|
||||
# rawptr[list[int]] / list[AST] 等:slice 里可能含 list,需下钻
|
||||
# rawptr[list[int]] / list[AST]: the slice may contain list, so recurse into it
|
||||
if sub_s.slice is not None: self._check_container_type_node(sub_s.slice, need_list_box, need_dict_box)
|
||||
elif k == ast.ASTKind.BinOp:
|
||||
bop_s: ast.BinOp* = ast.BinOp*(node)
|
||||
@@ -941,11 +791,7 @@ novirtual class Translator:
|
||||
self._check_container_type_node(ast.binop_get_right(bop_s), need_list_box, need_dict_box)
|
||||
|
||||
def _annotation_is_container(self, ann: ast.AST*) -> int:
|
||||
"""注解是否为 list/dict 容器(Name 裸名或 Subscript list[...]/dict[...])
|
||||
|
||||
数组初始化(t.CArray[...] / 裸数组注解)返回 0,避免 _scan_container_usage
|
||||
把数组字面量误判为 list 容器使用(FIX 2026-08-30)。
|
||||
"""
|
||||
"""Return 1 if the annotation is a list/dict container; array annotations return 0."""
|
||||
while ann is not None:
|
||||
ak: int = ann.kind()
|
||||
if ak == ast.ASTKind.Name:
|
||||
@@ -962,35 +808,17 @@ novirtual class Translator:
|
||||
|
||||
|
||||
# ============================================================
|
||||
# IR 输出
|
||||
# ============================================================
|
||||
# IR output
|
||||
def dump_ir(self, buf: bytes, size: sizet, mode: int):
|
||||
"""将 LLVM IR 输出到缓冲区
|
||||
|
||||
mode: 0=完整, 1=stub(仅声明), 2=text(仅代码)
|
||||
"""
|
||||
"""Write the LLVM IR into a buffer: mode 0=full, 1=stub (decls only), 2=text (code only)."""
|
||||
if self.Module is None or buf is None: return
|
||||
if _mbuddy is None: return
|
||||
buf[0] = '\0'
|
||||
mlirlite.LLVMModulePrint(buf, size, self.Module, _mbuddy, mode)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# --traceback: startup Install() ctor injection
|
||||
#
|
||||
# Lives here (not in BuildPipeline) because this module already imports
|
||||
# mlirlite. Calling mlirlite.* from a module without that import mangles the
|
||||
# calls with the package (__init__) SHA1, which no object defines, instead of
|
||||
# the defining submodule's SHA1 (link-time undefined symbol). BuildPipeline
|
||||
# only decides whether to trigger the injection.
|
||||
#
|
||||
# Injects into the traceback module's IR:
|
||||
# define void @__traceback_install_ctor() { entry: call @Install(); ret void }
|
||||
# plus an appending-linkage @llvm.global_ctors so the CRT runs Install() once at
|
||||
# process start (mirrors TPC BaseGen._RegisterTracebackInstallViaCtor).
|
||||
# fail-fast: a pre-existing @llvm.global_ctors, or a missing Install symbol,
|
||||
# aborts the build (silent degradation would leave the product without a map).
|
||||
# ============================================================
|
||||
# --traceback: inject an appending-linkage @llvm.global_ctors calling the traceback module's Install()
|
||||
# once at process start; fail fast if Install is missing or @llvm.global_ctors already exists.
|
||||
|
||||
# _tb_module_has_global_ctors - does the module already define @llvm.global_ctors
|
||||
def _tb_module_has_global_ctors(mod: mlirlite.LLVMModule*) -> int:
|
||||
@@ -1006,8 +834,7 @@ def _tb_module_has_global_ctors(mod: mlirlite.LLVMModule*) -> int:
|
||||
|
||||
|
||||
# _tb_find_install_function - locate this module's own Install()
|
||||
#
|
||||
# Accepts "<16hex>.Install" (the usual SHA1-mangled form) or the bare name.
|
||||
# Accepts "<16hex>.Install" (usual SHA1-mangled form) or the bare name.
|
||||
def _tb_find_install_function(mod: mlirlite.LLVMModule*) -> mlirlite.Function*:
|
||||
if mod is None: return None
|
||||
cur: mlirlite.Function* = mod.FuncHead
|
||||
@@ -1024,11 +851,8 @@ def _tb_find_install_function(mod: mlirlite.LLVMModule*) -> mlirlite.Function*:
|
||||
return None
|
||||
|
||||
|
||||
# InjectTracebackInstallCtor - inject startup Install() ctor into the module
|
||||
#
|
||||
# NOTE: FunctionPrint has no linkage field, so the ctor is emitted with default
|
||||
# (external) linkage instead of TPC's 'internal'; only traceback.vp defines it,
|
||||
# so there is no symbol clash.
|
||||
# InjectTracebackInstallCtor - inject the startup Install() ctor into the module.
|
||||
# NOTE: emitted with default (external) linkage since FunctionPrint has no linkage field; no clash.
|
||||
def InjectTracebackInstallCtor(tr: rawptr[Translator]) -> int:
|
||||
if tr is None or tr.Module is None: return 0
|
||||
if tr._declare_only != 0: return 0
|
||||
@@ -1097,24 +921,20 @@ def InjectTracebackInstallCtor(tr: rawptr[Translator]) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Mixin - Handler 非多态基类(自 HandlesBase 迁入)
|
||||
#
|
||||
# 持有 Translator 回指针。ExprCallHandle 等跨模块 handler 类经
|
||||
# `class X(Translator.Mixin)` 继承(TPV 支持模块限定基类继承)。
|
||||
# ============================================================
|
||||
# Mixin - non-polymorphic base class for handlers (moved from HandlesBase):
|
||||
# holds the Translator back-pointer; cross-module handler classes inherit `class X(Translator.Mixin)`.
|
||||
novirtual class Mixin:
|
||||
"""所有 Handle 的非多态基类:持有 Translator 回指针 + 共享委托方法"""
|
||||
"""Non-polymorphic base for all Handles: holds the Translator back-pointer and shared delegates."""
|
||||
Trans: Translator*
|
||||
|
||||
def InitMixin(self, trans: Translator*) -> int:
|
||||
"""初始化 Mixin 字段(子类 __init__ 中调用)"""
|
||||
"""Initialize the Mixin fields, called from a subclass __init__."""
|
||||
self.Trans = trans
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -14,116 +14,93 @@ import stdlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
# TypeSpec 模块导入别名用 TSpec(模块内类名也叫 TypeSpec,别名同名会被解析器
|
||||
# 当成"类 TypeSpec 的静态方法",导致跨模块调用被 mangled 成 <sha1>.TypeSpec.<fn>
|
||||
# 并隐式补 self 实参 → undefined symbol)。
|
||||
# Use alias TSpec for the TypeSpec module (its class is also named TypeSpec; a same-name alias
|
||||
# would be parsed as a static method -> mangled cross-module call with implicit self -> undefined symbol).
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
|
||||
|
||||
|
||||
# ============================================================
|
||||
# TypeGenBase - 类型表基础设施:字段 + InitTypeGen + trace/临时目录/generic 上下文
|
||||
# ============================================================
|
||||
# TypeGenBase - type-table infrastructure: fields + InitTypeGen + trace/temp-dir/generic context
|
||||
novirtual class TypeGenBase:
|
||||
"""字段 + InitTypeGen + trace/临时目录/generic 上下文"""
|
||||
"""Field storage, InitTypeGen, and trace/temp-dir/generic context helpers."""
|
||||
|
||||
# --- 仿 Python Traceback 调用栈(原 _g_trace_frames/_g_trace_count) ---
|
||||
# --- Python-Traceback-like call stack (was _g_trace_frames/_g_trace_count) ---
|
||||
Gen: Generator.Generator*
|
||||
TraceFrames: rawptr[GenTypes.TraceFrame] # malloc 数组(TypeGenMixin.TRACE_MAX 槽)
|
||||
TraceFrames: rawptr[GenTypes.TraceFrame] # malloc array (TypeGenMixin.TRACE_MAX slots)
|
||||
TraceCount: int
|
||||
|
||||
# --- 全局泛型特化上下文(原 _g_generic_tp_names/_g_generic_type_args) ---
|
||||
# --- Global generic specialization context (was _g_generic_tp_names/_g_generic_type_args) ---
|
||||
GenericTpNames: list[str]*
|
||||
GenericTypeArgs: list[str]*
|
||||
# 嵌套安全栈:set_generic_context 压入外层值,clear_generic_context 弹出
|
||||
# 恢复(原单槽赋值:嵌套特化/泛型函数翻译退出时把外层上下文清成 None →
|
||||
# 外层方法体后续的 T 替换丢失 → T.__sizeof__() NameError,VectorGenericTest
|
||||
# 特化 Vector[int].__init__ 回归根因)。
|
||||
GenericCtxStack: sizet* # i64* 固定数组:每层 2 个槽(tp_names, type_args 指针)
|
||||
# Nesting-safe stack: set_generic_context pushes the outer value, clear_generic_context pops it.
|
||||
# (A single slot would clear the outer context to None on exit, losing T substitution -> NameError.)
|
||||
GenericCtxStack: sizet* # fixed i64* array: 2 slots per level (tp_names, type_args pointers)
|
||||
GenericCtxDepth: int
|
||||
|
||||
# --- 全局 CDefine 编译期常量表(原 _g_cdefine_*) ---
|
||||
# --- Global CDefine compile-time constant table (was _g_cdefine_*) ---
|
||||
CDefineNames: list[str]*
|
||||
CDefineValues: list[str]*
|
||||
CDefineDoubleFlags: list[int]*
|
||||
CDefineStrFlags: list[int]* # 1=字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2")
|
||||
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2")
|
||||
CDefineIsDouble: int
|
||||
CDefineIsStr: int
|
||||
CDefineFound: int
|
||||
# --- CDefine 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) ---
|
||||
# 与 CDefineNames 同池分配、同 clear_cdefine_constants 复位;CDefineNames 只
|
||||
# 追加不重排 ⇒ 存槽下标稳定;Built == len(CDefineNames) 才可信,不可信/未命中
|
||||
# 一律回退线性扫描(正确性保证)。
|
||||
# --- CDefine name -> slot index (open addressing; slot stores index+1, 0=empty) ---
|
||||
# Same pool/reset as CDefineNames; valid only when Built == len(CDefineNames), else fall back to linear scan.
|
||||
CDefineNameIdx: sizet*
|
||||
CDefineNameIdxCap: int
|
||||
CDefineNameIdxBuilt: int
|
||||
|
||||
# --- 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用) ---
|
||||
# 缓存 reset_to(mark2)/clear_cdefine_constants 不清除 → 跨文件复用,
|
||||
# CONST_INT 递归查找只做一次。
|
||||
# --- Cross-module CDefine lookup cache (PermPool-allocated, reused across files) ---
|
||||
# Not cleared by reset_to(mark2)/clear_cdefine_constants, so CONST_INT lookup happens once.
|
||||
XmodCdefNames: list[str]*
|
||||
XmodCdefValues: list[str]*
|
||||
XmodCdefDoubleFlags: list[int]*
|
||||
XmodCdefStrFlags: list[int]*
|
||||
|
||||
# --- 当前模块 SHA1(原 _g_current_module_sha1) ---
|
||||
# --- Current module SHA1 (was _g_current_module_sha1) ---
|
||||
CurrentModuleSha1: str
|
||||
|
||||
# --- 全局 CTypedef 表(原 _g_ctypedef_*) ---
|
||||
# --- Global CTypedef table (was _g_ctypedef_*) ---
|
||||
CTypedefNames: list[str]*
|
||||
CTypedefTypeStrs: list[str]*
|
||||
CTypedefFound: int
|
||||
# CTypedef 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽)。
|
||||
# 与 CTypedefNames 同池分配、同 clear_ctypedefs 复位;CTypedefNames 只追加
|
||||
# 不重排(同名注册原地更新类型串)⇒ 存槽下标稳定;Built == len(CTypedefNames)
|
||||
# 才可信,不可信/未命中一律回退线性扫描(正确性保证)。
|
||||
# CTypedef name -> slot index (open addressing; slot stores index+1, 0=empty).
|
||||
# Same pool/reset as CTypedefNames; valid only when Built == len(CTypedefNames), else fall back to linear scan.
|
||||
CTypedefNameIdx: sizet*
|
||||
CTypedefNameIdxCap: int
|
||||
CTypedefNameIdxBuilt: int
|
||||
CTypedefResolveDepth: int
|
||||
CTypedefVisiting: list[str]*
|
||||
# builtins 源扫描标记:注解解析"最后匹配"兜底(_lookup_builtins_typedef)
|
||||
# 每文件最多读一次 includes/builtins.vp;随 clear_ctypedefs 复位
|
||||
# builtins source scan flag: "last match" fallback for annotation resolution (_lookup_builtins_typedef).
|
||||
# Reads includes/builtins.vp at most once per file; reset by clear_ctypedefs.
|
||||
BuiltinsTypedefScanned: int
|
||||
# builtins 最后匹配诊断("变量类型注解解析失败"详细报错用):
|
||||
# LastBuiltinsTypedefName = 最后一次请求的名称;LastBuiltinsTypedefResult =
|
||||
# 展开后的类型表达式(未命中 = None)。随 clear_ctypedefs 复位。
|
||||
# 两个被指串由以下自有缓冲承载(stdlib.malloc,clear_ctypedefs 释放):
|
||||
# 调用方常传临时缓冲(VpiReader._resolve_single_vpi_type_str 的段缓冲
|
||||
# malloc 后立刻 free),直接存指针会悬垂 → _build_ann_fail_msg 的
|
||||
# snprintf 读已释放内存(ASan heap-use-after-free)。
|
||||
# Builtins last-match diagnostics (for detailed "variable annotation resolve failed" errors):
|
||||
# the two strings are held by their own buffers (stdlib.malloc, freed by clear_ctypedefs) because callers pass temporaries that would dangle (ASan use-after-free).
|
||||
LastBuiltinsTypedefName: str
|
||||
LastBuiltinsTypedefResult: str
|
||||
LastBuiltinsTypedefNameBuf: bytes
|
||||
LastBuiltinsTypedefResultBuf: bytes
|
||||
# builtins import 别名缓冲(stdlib.malloc,格式 "alias\0target\0" 连续段):
|
||||
# _lookup_builtins_typedef 扫描 builtins.vp import 行填充;库 typedef 模块限定
|
||||
# 引用按别名还原真实模块名(__stdint.size_t → stdint.size_t)。
|
||||
# 用 malloc 而非 pool list——pool 每文件 reset_to 后 list 悬空;malloc 跨 pool
|
||||
# 生命周期存活,clear_ctypedefs 释放置空。
|
||||
# builtins import alias buffer (stdlib.malloc, format "alias\0target\0").
|
||||
# Filled by scanning builtins.vp import lines to restore real module names from aliases; malloc (not pool) so it survives per-file reset.
|
||||
ImportAliasBuf: bytes
|
||||
|
||||
# --- 库 typedef 按模块一次性门禁(_lookup_library_typedef 性能优化) ---
|
||||
# 库模块 typedef 缓存(PermPool 永久分配,跨文件/Phase 存活):
|
||||
# LibTypedefScannedMods: key=库模块名 → 已扫描标记(命中=该模块源已读并入缓存);
|
||||
# LibTypedefTypeMap: key="模块名|typedef 名" → 源中原始类型表达式。
|
||||
# 每次查询都读 64KB 库源全文扫描会使单文件 translate 耗时 7–12s;库源单次编译内
|
||||
# 不变,故缓存命中结果与逐次读源等价。
|
||||
# --- Per-module one-shot gate for library typedefs (perf optimization) ---
|
||||
# PermPool caches (across files/phases): LibTypedefScannedMods (key=module -> scanned) and LibTypedefTypeMap (key="module|typedef" -> raw type expr); scanning 64KB per query costs 7-12s.
|
||||
LibTypedefScannedMods: hashtable.HashTable*
|
||||
LibTypedefTypeMap: hashtable.HashTable*
|
||||
|
||||
# --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) ---
|
||||
# --- Const integer expression eval success flag (was _g_const_eval_ok) ---
|
||||
ConstEvalOk: int
|
||||
|
||||
# --- 全局当前文件名(原 _g_current_filename) ---
|
||||
# --- Global current filename (was _g_current_filename) ---
|
||||
CurrentFile: str
|
||||
|
||||
# --- TypeSpec 驻留表(统一类型描述符,Debugger/_Type.md 第 6 节) ---
|
||||
# 惰性创建(ensure_spec_arena);由类型子系统自身持有,避免模块级全局表。
|
||||
# 实例与内部数组均 PermPool 分配 ⇒ 跨文件/跨 mark 存活。
|
||||
# --- TypeSpec arena (unified type descriptors; Debugger/_Type.md section 6) ---
|
||||
# Lazily created (ensure_spec_arena); instance and arrays are PermPool-allocated, so they survive across files/marks.
|
||||
Specs: TSpec.TypeSpecArena*
|
||||
def InitTypeGen(self) -> int:
|
||||
"""初始化类型 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
|
||||
"""Initialize the type mixin fields (inlined from subclass constructors)."""
|
||||
if self is None: return 1
|
||||
self.TraceFrames = None
|
||||
self.TraceCount = 0
|
||||
@@ -160,7 +137,7 @@ novirtual class TypeGenBase:
|
||||
self.LastBuiltinsTypedefNameBuf = None
|
||||
self.LastBuiltinsTypedefResultBuf = None
|
||||
self.ImportAliasBuf = None
|
||||
# 库 typedef 持久缓存字段(不随 clear_ctypedefs 复位 → 跨文件/Phase 存活)
|
||||
# Persistent library typedef cache fields (not reset by clear_ctypedefs, so they survive across files/phases)
|
||||
self.LibTypedefScannedMods = None
|
||||
self.LibTypedefTypeMap = None
|
||||
self.ConstEvalOk = 0
|
||||
@@ -168,14 +145,10 @@ novirtual class TypeGenBase:
|
||||
self.Specs = None
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# ensure_spec_arena - 惰性创建 TypeSpec 驻留表(幂等)
|
||||
#
|
||||
# 用 PermPool 分配 arena 本体;表内部缓冲由 TSpec.SpecArenaInit 分配
|
||||
# (同样 PermPool)。失败返回 None(调用方按 fail-fast 处理,不静默退化)。
|
||||
# ============================================================
|
||||
# ensure_spec_arena - lazily create the TypeSpec arena (idempotent).
|
||||
# PermPool-allocated; returns None on failure (callers fail-fast, no silent degradation).
|
||||
def ensure_spec_arena(self) -> TSpec.TypeSpecArena*:
|
||||
"""确保 TypeSpec 驻留表已创建,返回 arena;失败返回 None"""
|
||||
"""Ensure the TypeSpec arena exists and return it; None on failure."""
|
||||
if self.Specs is not None: return self.Specs
|
||||
size: sizet = TSpec.TypeSpecArena.__sizeof__()
|
||||
arena: TSpec.TypeSpecArena* = TSpec.TypeSpecArena*(CompilerFactory.factory.PermPool.alloc(size))
|
||||
@@ -184,13 +157,11 @@ novirtual class TypeGenBase:
|
||||
if TSpec.SpecArenaInit(arena, 0) != 0: return None
|
||||
self.Specs = arena
|
||||
return arena
|
||||
# ============================================================
|
||||
# P2 迁移方法(函数体来自 HandlesType.vp,全局变量改 self.字段,
|
||||
# 同模块函数调用改 self.方法;签名与 HandlesType 原函数一致)
|
||||
# ============================================================
|
||||
# P2 migrated methods (bodies from HandlesType.vp; globals -> self fields, same-module calls -> self methods).
|
||||
# Signatures match the original HandlesType functions.
|
||||
|
||||
def _ensure_trace_init(self) -> int:
|
||||
"""确保调用栈已初始化(懒加载,用 malloc 不依赖 pool)"""
|
||||
"""Ensure the trace stack is initialized (lazy, malloc-based)."""
|
||||
if self.TraceFrames is not None: return 1
|
||||
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
|
||||
self.TraceFrames = CompilerFactory.factory.PermPool.alloc(entry_size * TypeGenMixin.TRACE_MAX)
|
||||
@@ -198,13 +169,13 @@ novirtual class TypeGenBase:
|
||||
string.memset(self.TraceFrames, 0, entry_size * TypeGenMixin.TRACE_MAX)
|
||||
return 1
|
||||
def _get_trace_frame(self, i: int) -> rawptr[GenTypes.TraceFrame]:
|
||||
"""获取第 i 个 TraceFrame 槽位"""
|
||||
"""Return the i-th TraceFrame slot."""
|
||||
if self.TraceFrames is None or i < 0 or i >= TypeGenMixin.TRACE_MAX: return None
|
||||
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
|
||||
addr: uint64 = uint64(self.TraceFrames) + i * entry_size
|
||||
return rawptr[GenTypes.TraceFrame](addr)
|
||||
def push_trace(self, file_name: str, lineno: int, col_start: int = 0, col_end: int = 0) -> None:
|
||||
"""压入一个翻译上下文帧(进入新的翻译作用域时调用)"""
|
||||
"""Push a translation context frame."""
|
||||
if self._ensure_trace_init() == 0: return
|
||||
if self.TraceCount >= TypeGenMixin.TRACE_MAX: return
|
||||
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(self.TraceCount)
|
||||
@@ -215,7 +186,7 @@ novirtual class TypeGenBase:
|
||||
frame.col_end = col_end
|
||||
self.TraceCount += 1
|
||||
def pop_trace(self) -> None:
|
||||
"""弹出栈顶帧(退出翻译作用域时调用)"""
|
||||
"""Pop the top trace frame."""
|
||||
if self.TraceCount <= 0: return
|
||||
self.TraceCount -= 1
|
||||
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(self.TraceCount)
|
||||
@@ -225,7 +196,7 @@ novirtual class TypeGenBase:
|
||||
frame.col_start = 0
|
||||
frame.col_end = 0
|
||||
def clear_trace(self) -> None:
|
||||
"""清空调用栈(每个文件翻译开始前调用)"""
|
||||
"""Clear the trace stack (called before translating each file)."""
|
||||
for i in range(self.TraceCount):
|
||||
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(i)
|
||||
if frame is not None:
|
||||
@@ -235,23 +206,19 @@ novirtual class TypeGenBase:
|
||||
frame.col_end = 0
|
||||
self.TraceCount = 0
|
||||
def set_temp_dir(self, temp_dir: str) -> None:
|
||||
"""设置全局 temp_dir(统一写入 CallGenBase.TempDir,跨子对象经 Gen 指针)"""
|
||||
"""Set the global temp_dir via CallGenBase.TempDir."""
|
||||
self.Gen.CallGenBase.SetTempDir(temp_dir)
|
||||
def get_temp_dir(self) -> str:
|
||||
"""获取全局 temp_dir(从 CallGenBase 读取)"""
|
||||
"""Get the global temp_dir from CallGenBase."""
|
||||
return self.Gen.CallGenBase.TempDir
|
||||
def set_current_module_sha1(self, sha1: str) -> None:
|
||||
"""设置当前正在编译的模块 SHA1(Phase1 在模块开始翻译前调用)"""
|
||||
"""Set the SHA1 of the module currently being compiled."""
|
||||
self.CurrentModuleSha1 = sha1
|
||||
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy*, tree: ast.AST*, tag: str):
|
||||
"""已移除调试插桩(占位保留签名,避免外部调用引用失败)"""
|
||||
"""Removed debug hook; signature kept as a placeholder for external callers."""
|
||||
pass
|
||||
def set_generic_context(self, tp_names: list[str]*, type_args: list[str]*):
|
||||
"""设置泛型特化上下文(进入泛型类方法体翻译时调用),嵌套安全
|
||||
|
||||
先压栈当前(外层)上下文,再设置新值。clear_generic_context 弹出恢复,
|
||||
避免嵌套特化/泛型函数翻译退出时清空外层上下文(T.__sizeof__() 回归根因)。
|
||||
"""
|
||||
"""Push and set the generic specialization context (nesting-safe)."""
|
||||
if self.GenericCtxStack is None:
|
||||
self.GenericCtxStack = stdlib.malloc(8 * 2 * 32)
|
||||
if self.GenericCtxStack is None:
|
||||
@@ -267,7 +234,7 @@ novirtual class TypeGenBase:
|
||||
self.GenericTypeArgs = type_args
|
||||
|
||||
def clear_generic_context(self):
|
||||
"""清除泛型特化上下文(离开泛型类方法体翻译时调用),恢复外层而非置 None"""
|
||||
"""Pop the generic specialization context, restoring the outer one."""
|
||||
if self.GenericCtxDepth > 0:
|
||||
self.GenericCtxDepth -= 1
|
||||
self.GenericTpNames = (list[str]*)(self.GenericCtxStack[self.GenericCtxDepth * 2])
|
||||
@@ -276,10 +243,7 @@ novirtual class TypeGenBase:
|
||||
self.GenericTpNames = None
|
||||
self.GenericTypeArgs = None
|
||||
def resolve_generic_type_name(self, name: str) -> str:
|
||||
"""如果 name 是泛型类型参数,返回对应的具体类型名;否则返回 name 本身
|
||||
|
||||
用于表达式中的 T 替换(如 T.__sizeof__() → int.__sizeof__())
|
||||
"""
|
||||
"""Return the concrete type name for a generic type parameter, else name itself."""
|
||||
if name is None: return name
|
||||
rv_d: str = name
|
||||
if self.GenericTpNames is not None and self.GenericTypeArgs is not None:
|
||||
|
||||
@@ -15,31 +15,22 @@ import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||
|
||||
# ============================================================
|
||||
# CDefine 名索引(名 → 槽下标,开放寻址 | 线性探测)
|
||||
#
|
||||
# 槽数 = 4096(2 的幂;负载 < 0.5 上限 2048 条,超限标记不可信回退线性扫描)。
|
||||
# 槽存槽下标 + 1(0=空槽)。CDefineNames 只追加不重排 ⇒ 槽下标稳定;
|
||||
# Built == len(CDefineNames) 才可信。
|
||||
# ============================================================
|
||||
# CDefine name index (name -> slot index; open addressing with linear probing).
|
||||
# 4096 power-of-two slots; slot stores index+1 (0=empty); valid only when Built == len(CDefineNames).
|
||||
define CDEFINE_IDX_CAP = 4096
|
||||
# 索引哈希常量(32 位 FNV-1a)
|
||||
# Index hash constants (32-bit FNV-1a)
|
||||
define CDEF_IDX_FNV_OFFSET = 2166136261
|
||||
define CDEF_IDX_FNV_PRIME = 16777619
|
||||
define CDEF_IDX_FNV_MASK = 4294967295
|
||||
|
||||
|
||||
# ============================================================
|
||||
# TypeGenCDef - TypeGenMixin 拆分(阶段 D):CDefine 编译期常量表(register/lookup/is/set/clear)
|
||||
# ============================================================
|
||||
# TypeGenCDef - split from TypeGenMixin (stage D): CDefine compile-time constant table (register/lookup/is/set/clear)
|
||||
novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
"""CDefine 编译期常量表(register/lookup/is/set/clear)"""
|
||||
"""CDefine compile-time constant table (register/lookup/is/set/clear)."""
|
||||
|
||||
# ============================================================
|
||||
# CDefine 名索引(开放寻址;槽存 index+1,0=空槽)
|
||||
# ============================================================
|
||||
# CDefine name index (open addressing; slot stores index+1, 0=empty)
|
||||
def _cdef_idx_hash(self, name: str) -> uint64:
|
||||
"""名 → 64 位哈希(32 位 FNV-1a,逐字节到 NUL 结束)"""
|
||||
"""64-bit FNV-1a hash of the name, computed byte by byte until NUL."""
|
||||
h: uint64 = uint64(CDEF_IDX_FNV_OFFSET)
|
||||
if name is None: return h
|
||||
i: sizet = 0
|
||||
@@ -50,11 +41,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
return h
|
||||
|
||||
def _cdef_idx_slot(self, si: sizet) -> sizet*:
|
||||
"""取索引第 si 个槽(8 字节,存槽下标+1;0=空槽)"""
|
||||
"""Return index slot si (slot stores index+1; 0 means empty)."""
|
||||
return (sizet*)(uint64(self.CDefineNameIdx) + uint64(si) * 8)
|
||||
|
||||
def _cdef_idx_init(self, pool: memhub.MemBuddy*) -> None:
|
||||
"""创建/清零名索引(与 CDefineNames 同池);失败则标记不可信"""
|
||||
"""Create and zero the name index in the same pool; mark it untrusted on failure."""
|
||||
idx: sizet* = pool.alloc(sizet(CDEFINE_IDX_CAP) * 8)
|
||||
if idx is None:
|
||||
self.CDefineNameIdx = None
|
||||
@@ -67,7 +58,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self.CDefineNameIdxBuilt = 0
|
||||
|
||||
def _cdef_idx_find(self, name: str) -> int:
|
||||
"""在名索引中探测 name,返回命中槽下标(>=0);未命中/不可用返回 -1"""
|
||||
"""Probe the name index for name; return the slot index, or -1 if missing/unusable."""
|
||||
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None: return -1
|
||||
if self.CDefineNameIdxBuilt != int(len(self.CDefineNames)): return -1
|
||||
h: uint64 = self._cdef_idx_hash(name)
|
||||
@@ -86,7 +77,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
return -1
|
||||
|
||||
def _cdef_idx_insert(self, slot_index: int, name: str) -> int:
|
||||
"""把 (name → slot_index) 记入索引;0=成功 / 1=失败"""
|
||||
"""Record name -> slot_index in the index; 0=success, 1=failure."""
|
||||
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None: return 1
|
||||
h: uint64 = self._cdef_idx_hash(name)
|
||||
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
|
||||
@@ -102,45 +93,20 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
return 1
|
||||
|
||||
def _cdef_idx_sync_append(self, name: str) -> None:
|
||||
"""CDefineNames.append 后同步索引(槽下标 = len-1);不可信则保持不可信"""
|
||||
"""Sync the index after CDefineNames.append (slot = len-1); keep it untrusted if so."""
|
||||
n: int = int(len(self.CDefineNames))
|
||||
if self.CDefineNameIdxBuilt == n - 1 and self._cdef_idx_insert(n - 1, name) == 0: self.CDefineNameIdxBuilt = n
|
||||
else: self.CDefineNameIdxBuilt = -1
|
||||
|
||||
# ============================================================
|
||||
# CDefine 表本体的池归一
|
||||
#
|
||||
# 表指针挂在**进程级单例** _g_gen.TypeGenCDef 上,不在任何 pool 的管辖
|
||||
# 范围内;而调用方传进来的 pool 在翻译期是「每文件 reset_to 回收」的瞬时池
|
||||
# (Phase1 1a/1b、Phase2 PhaseA、StubMerger 扫描、Repl 每片段都按 mark/reset
|
||||
# 轮转)。表本体若落在该池:reset 之后表指针**悬空但非 None** ⇒ `is None`
|
||||
# 守卫失效 ⇒ 下一次 register_cdefine_* 的 append 写进已回收内存(堆损坏;
|
||||
# 检测模式下该区被 free 填成 0xCD ⇒ append 的接收者 = 0xcdcdcdcdcdcdcdcd)。
|
||||
# 故表本体(四个 list + 名索引)一律落 PermPool(进程级永久池,从不 reset),
|
||||
# 与"文件级/片段级 reset"彻底解耦。
|
||||
# PermPool 不可用(无 CompilerFactory.factory 的极简单元程序)时退回入参
|
||||
# pool —— 与既有语义一致,只是不再有"表本体跨 reset 存活"的隐式假设。
|
||||
# ============================================================
|
||||
# Pool discipline for the CDefine table body.
|
||||
# Body (4 lists + name index) always lives in PermPool so it survives per-file reset_to(mark); falls back to the passed pool if unavailable.
|
||||
def _cdefine_table_pool(self, pool: memhub.MemBuddy*) -> memhub.MemBuddy*:
|
||||
"""CDefine 表本体的池:恒取 PermPool(不可用则退回入参 pool)"""
|
||||
"""Pool for the CDefine table body: PermPool, or the passed pool as a fallback."""
|
||||
if CompilerFactory.factory is not None and CompilerFactory.factory.PermPool is not None: return CompilerFactory.factory.PermPool
|
||||
return pool
|
||||
|
||||
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64) -> None:
|
||||
"""注册 CDefine 编译期常量到本地表
|
||||
|
||||
值以十进制字符串存储(64 位 %lld)。大整数 CDefine(如
|
||||
sys.maxsize = 9223372036854775807)必须全精度保存,否则 %d 32 位截断
|
||||
成 -1(0xFFFFFFFF)→ 读回 4294967295(SysLibTest maxsize FAIL 根因)。
|
||||
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 的根因)。
|
||||
"""
|
||||
"""Register an integer CDefine constant; store the value as a decimal string."""
|
||||
if name is None: return
|
||||
if self.CDefineNames is None:
|
||||
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
||||
@@ -148,8 +114,8 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
tv: list[str]* = list[str](tp, 64)
|
||||
td: list[int]* = list[int](tp, 64)
|
||||
tsf: list[int]* = list[int](tp, 64)
|
||||
# 建表失败(池不可用/耗尽)⇒ 明确报错并返回,绝不让下面的 append
|
||||
# 落到 None 或未初始化地址(原实现无检查 ⇒ append 写进 NULL/0xCD 区崩)
|
||||
# Table build failed (pool unusable/exhausted): report and return, never let the
|
||||
# append below hit None or an uninitialized address (would write NULL/0xCD).
|
||||
if tn is None or tv is None or td is None or tsf is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 表分配失败(表本体池不可用或耗尽)", "MemoryError")
|
||||
return
|
||||
@@ -158,8 +124,8 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self.CDefineDoubleFlags = td
|
||||
self.CDefineStrFlags = tsf
|
||||
self._cdef_idx_init(tp)
|
||||
# 表就绪守卫:表本体缺失/半初始化(建表失败或外部置空)⇒ fail-fast,
|
||||
# 不写垃圾(append 之前必有非空断言)
|
||||
# Table-ready guard: missing/half-initialized body -> fail-fast, no garbage writes
|
||||
# (assert non-null before append).
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
|
||||
return
|
||||
@@ -175,19 +141,13 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
viperlib.snprintf(val_buf, 32, "%lld", value)
|
||||
self.CDefineValues.append(val_buf)
|
||||
else: self.CDefineValues.append("0")
|
||||
# fail-fast:CDefine 值缓冲区分配失败必须终止编译
|
||||
# (与浮点/字符串分支口径一致,不再静默写 "0" 污染常量表)
|
||||
# fail-fast: a failed CDefine value buffer must abort compilation
|
||||
# (consistent with the double/string branches; do not silently write "0").
|
||||
if val_buf is None: self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 值缓冲区分配失败", "MemoryError")
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
|
||||
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double) -> None:
|
||||
"""注册浮点 CDefine 编译期常量(如 U_M_PI: t.CDefine = 3.14159...)
|
||||
|
||||
值以 %f 文本存储并标记浮点,供 translate_value 返回浮点常量。
|
||||
|
||||
名称同样深拷贝到 pool(理由见 register_cdefine_constant:注入 .vpi 时
|
||||
Name.id 指向的 vpi_buf 会被释放,只存指针会留下悬空名)。
|
||||
"""
|
||||
"""Register a float CDefine constant; store the value as %f text and flag it."""
|
||||
if name is None: return
|
||||
if self.CDefineNames is None:
|
||||
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
||||
@@ -203,7 +163,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self.CDefineDoubleFlags = td
|
||||
self.CDefineStrFlags = tsf
|
||||
self._cdef_idx_init(tp)
|
||||
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
|
||||
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
|
||||
return
|
||||
@@ -216,25 +176,19 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self._cdef_idx_sync_append(name_copy_d)
|
||||
val_buf: str = pool.alloc(32)
|
||||
if val_buf is not None:
|
||||
# 用 CRT stdio.snprintf 而非 viperlib.snprintf:viperlib 自实现格式化器的
|
||||
# %g 浮点路径有 bug(%.17g 输出错误值,如 pi 显示 3.1397985 而非 3.1415926,
|
||||
# 位模式 %a 正确),会导致 CDefine 表存错文本 → lookup 生成错 LLVM 常量。
|
||||
# Use CRT stdio.snprintf, not viperlib.snprintf: viperlib's %g float path is buggy
|
||||
# (wrong %.17g, e.g. pi prints 3.1397985 not 3.1415926), which would corrupt CDefine text.
|
||||
stdio.snprintf(val_buf, 32, "%.17g", value)
|
||||
self.CDefineValues.append(val_buf)
|
||||
else: self.CDefineValues.append("0")
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(1)
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
|
||||
# fail-fast:CDefine 值缓冲区分配失败必须终止编译,
|
||||
# fail-fast: a failed CDefine value buffer must abort compilation
|
||||
if val_buf is None:
|
||||
VLogger.error("CDefine 浮点值缓冲区分配失败", "CDEF-DBG")
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点值缓冲区分配失败", "MemoryError")
|
||||
def register_cdefine_string(self, pool: memhub.MemBuddy*, name: str, value: str) -> None:
|
||||
"""注册字符串 CDefine 编译期常量(如 ZLIB_VERSION: t.CDefine = "1.3.2")
|
||||
|
||||
字符串值深拷贝到 pool(AST 节点的 str_val 可能被 pool 操作覆写,
|
||||
与浮点 CDefine 的恢复同理)。名称同样深拷贝(理由见
|
||||
register_cdefine_constant:注入 .vpi 的 Name.id 指向会被释放的 vpi_buf)。
|
||||
"""
|
||||
"""Register a string CDefine constant, deep-copying name and value into pool."""
|
||||
if pool is None or name is None: return
|
||||
if value is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值缺失: {name}", "InternalCompilerError")
|
||||
@@ -253,7 +207,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self.CDefineDoubleFlags = td
|
||||
self.CDefineStrFlags = tsf
|
||||
self._cdef_idx_init(tp)
|
||||
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
|
||||
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 字符串表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
|
||||
return
|
||||
@@ -273,26 +227,15 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self.CDefineValues.append(vcopy)
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(1)
|
||||
# 同步设置"最近注册/查表结果为字符串"标志(is_cdefine_str() 读取)。
|
||||
# 跨模块字符串 CDefine 读回只调本函数 + 尾部 set_cdefine_found;不设标志会让
|
||||
# 属性路径 is_cdefine_str()==0 → 退化为整数分支返回 0 → IR 生成 inttoptr i32 0
|
||||
# → 运行期按 NULL 字符串处理崩溃。
|
||||
# Also set the "last result is a string" flag (read by is_cdefine_str()).
|
||||
# Cross-module string CDefine reads call this + set_cdefine_found; missing the flag degrades to int 0 -> NULL crash.
|
||||
self.CDefineIsStr = 1
|
||||
self.CDefineFound = 1
|
||||
# ============================================================
|
||||
# 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用)
|
||||
# ============================================================
|
||||
# Cross-module CDefine lookup cache (PermPool-allocated, reused across files)
|
||||
def register_xmod_cdefine(self, name: str, value_str: str, is_double: int, is_str: int) -> None:
|
||||
"""注册跨模块 CDefine 查找结果到 perm_pool 全局缓存(跨文件复用)
|
||||
|
||||
Args:
|
||||
name: CDefine 常量名(如 CONST_INT)
|
||||
value_str: 值文本(十进制/浮点/字符串)
|
||||
is_double: 1=浮点值
|
||||
is_str: 1=字符串值
|
||||
"""
|
||||
"""Cache a cross-module CDefine lookup result in the perm_pool global cache."""
|
||||
if name is None or value_str is None: return
|
||||
# 去重:同名已缓存则跳过(首次查找结果为准,CDefine 全局唯一约定)
|
||||
# Dedupe: skip if cached (first lookup wins; CDefines are globally unique)
|
||||
if self.XmodCdefNames is not None:
|
||||
x_n: sizet = len(self.XmodCdefNames)
|
||||
x_i: sizet
|
||||
@@ -305,8 +248,8 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self.XmodCdefValues = list[str](perm, 64)
|
||||
self.XmodCdefDoubleFlags = list[int](perm, 64)
|
||||
self.XmodCdefStrFlags = list[int](perm, 64)
|
||||
# key/value 深拷贝到 perm_pool:入参(pool.alloc 临时缓冲)在
|
||||
# reset_to(mark2) 后悬空,append 只存指针 → 必须深拷贝跨 reset 存活
|
||||
# Deep-copy key/value into perm_pool: the inputs (pool.alloc temporaries) dangle after
|
||||
# reset_to(mark2), so appending pointers alone would not survive the reset.
|
||||
klen: sizet = string.strlen(name)
|
||||
kcopy: str = perm.alloc(klen + 1)
|
||||
if kcopy is None: return
|
||||
@@ -320,11 +263,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if self.XmodCdefDoubleFlags is not None: self.XmodCdefDoubleFlags.append(is_double)
|
||||
if self.XmodCdefStrFlags is not None: self.XmodCdefStrFlags.append(is_str)
|
||||
def lookup_xmod_cdefine(self, name: str) -> str:
|
||||
"""查找跨模块 CDefine 缓存,命中设置 found/is_double/is_str 标志
|
||||
|
||||
Returns:
|
||||
值文本(命中),未命中返回 None
|
||||
"""
|
||||
"""Look up the cross-module CDefine cache and set the found/is_double/is_str flags."""
|
||||
self.CDefineFound = 0
|
||||
self.CDefineIsDouble = 0
|
||||
self.CDefineIsStr = 0
|
||||
@@ -342,24 +281,15 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
return val_str
|
||||
return None
|
||||
def lookup_cdefine_constant(self, name: str) -> int64:
|
||||
"""查找 CDefine 编译期常量
|
||||
|
||||
返回找到的值(64 位,大整数 CDefine 如 sys.maxsize 全精度)。
|
||||
由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1),
|
||||
用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。
|
||||
调用方应在调用后检查 _g_cdefine_found。
|
||||
浮点 CDefine 项返回整数部分(与 _g_cdefine_is_double 标志配合)。
|
||||
"""
|
||||
"""Look up an integer CDefine constant; 64-bit exact, found flag distinguishes -1."""
|
||||
self.CDefineFound = 0
|
||||
self.CDefineIsDouble = 0
|
||||
self.CDefineIsStr = 0
|
||||
if self.CDefineNames is None: return 0
|
||||
n: sizet = len(self.CDefineNames)
|
||||
i: sizet
|
||||
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
|
||||
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
|
||||
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 0(= 原全表扫描
|
||||
# 结果);索引不可用时回退下方原全表线性扫描。
|
||||
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
|
||||
# (== first scan hit); if unavailable fall back to the full linear scan below.
|
||||
cd_start: sizet = 0
|
||||
cd_end: sizet = n
|
||||
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
@@ -374,8 +304,8 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if val_str is not None:
|
||||
self.CDefineFound = 1
|
||||
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
|
||||
# 字符串 CDefine(如 ZLIB_VERSION="1.3.2"):整数查询路径
|
||||
# 标记 is_str,调用方改用 lookup_cdefine_string 取字符串。
|
||||
# String CDefine (e.g. ZLIB_VERSION="1.3.2"): mark is_str on the int path;
|
||||
# callers should use lookup_cdefine_string instead.
|
||||
self.CDefineIsStr = 1
|
||||
return 0
|
||||
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
|
||||
@@ -384,21 +314,15 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
return string.atoll(val_str)
|
||||
return 0
|
||||
def lookup_cdefine_double(self, name: str) -> double:
|
||||
"""查找 CDefine 编译期常量,返回 double 值(浮点/整数统一)
|
||||
|
||||
与 lookup_cdefine_constant 共享 _g_cdefine_found / _g_cdefine_is_double
|
||||
(found=1 且 is_double=1 → 浮点 CDefine,应返回浮点常量)。
|
||||
"""
|
||||
"""Look up a CDefine constant and return its double value (float or int)."""
|
||||
self.CDefineFound = 0
|
||||
self.CDefineIsDouble = 0
|
||||
self.CDefineIsStr = 0
|
||||
if self.CDefineNames is None: return 0.0
|
||||
n: sizet = len(self.CDefineNames)
|
||||
i: sizet
|
||||
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
|
||||
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
|
||||
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 0.0(= 原全表扫描
|
||||
# 结果);索引不可用时回退下方原全表线性扫描。
|
||||
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
|
||||
# (== first scan hit); if unavailable fall back to the full linear scan below.
|
||||
cd_start: sizet = 0
|
||||
cd_end: sizet = n
|
||||
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
@@ -408,7 +332,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
for i in range(cd_start, cd_end):
|
||||
nm: str = self.CDefineNames.get(i)
|
||||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。
|
||||
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs.
|
||||
if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0:
|
||||
val_str: str = self.CDefineValues.get(i)
|
||||
if val_str is not None:
|
||||
@@ -424,21 +348,15 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
return ret_i2
|
||||
return 0.0
|
||||
def lookup_cdefine_string(self, name: str) -> str:
|
||||
"""查找 CDefine 编译期字符串值(found=1 且 is_str=1 时有效)
|
||||
|
||||
返回的字符串指针位于 pool 内(当前模块生命周期内有效),
|
||||
调用方应尽快用于 create_global_string 拷贝到 LLVM 常量。
|
||||
"""
|
||||
"""Look up a string CDefine value (valid when found=1 and is_str=1)."""
|
||||
self.CDefineFound = 0
|
||||
self.CDefineIsDouble = 0
|
||||
self.CDefineIsStr = 0
|
||||
if self.CDefineNames is None: return None
|
||||
n: sizet = len(self.CDefineNames)
|
||||
i: sizet
|
||||
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
|
||||
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
|
||||
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 None(= 原全表扫描
|
||||
# 结果);索引不可用时回退下方原全表线性扫描。
|
||||
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
|
||||
# (== first scan hit); if unavailable fall back to the full linear scan below.
|
||||
cd_start: sizet = 0
|
||||
cd_end: sizet = n
|
||||
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
@@ -458,35 +376,25 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
return val_str
|
||||
return None
|
||||
def is_cdefine_str(self) -> int:
|
||||
"""检查最近一次 CDefine 查找命中的是否是字符串常量"""
|
||||
"""Whether the last CDefine hit was a string constant."""
|
||||
return self.CDefineIsStr
|
||||
def is_cdefine_double(self) -> int:
|
||||
"""检查最近一次 CDefine 查找命中的是否是浮点常量"""
|
||||
"""Whether the last CDefine hit was a float constant."""
|
||||
return self.CDefineIsDouble
|
||||
def is_cdefine_found(self) -> int:
|
||||
"""检查上次 lookup_cdefine_constant 是否找到值"""
|
||||
"""Whether the last CDefine lookup found a value."""
|
||||
return self.CDefineFound
|
||||
def set_cdefine_found(self, val: int) -> None:
|
||||
"""设置 cdefine_found 标志(供跨模块查找函数使用)"""
|
||||
"""Set the cdefine_found flag (used by cross-module lookup functions)."""
|
||||
self.CDefineFound = val
|
||||
def clear_cdefine_constants(self) -> None:
|
||||
"""清空 CDefine 常量表(模块切换时调用)
|
||||
|
||||
表本体自首次创建起常驻 PermPool(见 _cdefine_table_pool)⇒ 此处只
|
||||
**清空内容**、不丢弃表指针:
|
||||
- 丢指针会退回"下一次 register_cdefine_* 按**调用方 pool** 重建",
|
||||
表本体又落回会被 reset_to 回收的瞬时池(冷 temp 下翻译 includes
|
||||
时才走到注册路径 ⇒ 表本体跨 reset 悬空 ⇒ append 写进已回收/未
|
||||
初始化内存的根因);
|
||||
- 空表与既有语义完全等价:lookup 命中空表即"未命中",
|
||||
register 首次 append 即写回第 0 槽。
|
||||
"""
|
||||
"""Clear the CDefine table body in place, keeping the PermPool table pointers."""
|
||||
if self.CDefineNames is not None: self.CDefineNames.clear()
|
||||
if self.CDefineValues is not None: self.CDefineValues.clear()
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.clear()
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear()
|
||||
# 名索引清槽(槽存 index+1,0=空槽)+ Built 归零(空表与索引同步;
|
||||
# 与"新建表后首次 register"的索引状态逐字一致)
|
||||
# Clear index slots (store index+1; 0=empty) and reset Built to 0 (empty table and index
|
||||
# in sync, matching a freshly built table before its first register).
|
||||
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
string.memset(self.CDefineNameIdx, 0, sizet(self.CDefineNameIdxCap) * 8)
|
||||
self.CDefineNameIdxBuilt = 0
|
||||
|
||||
@@ -16,22 +16,17 @@ import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
|
||||
# ============================================================
|
||||
# CTypedef 名索引(名 → 槽下标,开放寻址 | 线性探测)
|
||||
#
|
||||
# 槽数 = 4096(2 的幂;负载 < 0.5 上限 2048 条,超限标记不可信回退线性扫描)。
|
||||
# 槽存槽下标 + 1(0=空槽)。CTypedefNames 只追加不重排(同名注册原地更新类型串)
|
||||
# ⇒ 槽下标稳定;Built == len(CTypedefNames) 才可信。
|
||||
# ============================================================
|
||||
# CTypedef name index (name -> slot, open addressing | linear probing; slot stores
|
||||
# index+1, 0=empty). CTypedefNames only appends, so slot indexes stay stable.
|
||||
define CTYPEDEF_IDX_CAP = 4096
|
||||
# 索引哈希常量(32 位 FNV-1a,与 StructGenMixin 范式一致)
|
||||
# Index hash constants (32-bit FNV-1a, same as StructGenMixin)
|
||||
define CTYPEDEF_IDX_FNV_OFFSET = 2166136261
|
||||
define CTYPEDEF_IDX_FNV_PRIME = 16777619
|
||||
define CTYPEDEF_IDX_FNV_MASK = 4294967295
|
||||
|
||||
|
||||
def _ctypedef_idx_hash(name: str) -> uint64:
|
||||
"""CTypedef 名 → 64 位哈希(32 位 FNV-1a,逐字节到 NUL 结束)"""
|
||||
"""Return the 64-bit FNV-1a hash of a CTypedef name, byte by byte until NUL."""
|
||||
h: uint64 = uint64(CTYPEDEF_IDX_FNV_OFFSET)
|
||||
if name is None: return h
|
||||
i: sizet = 0
|
||||
@@ -42,35 +37,22 @@ def _ctypedef_idx_hash(name: str) -> uint64:
|
||||
return h
|
||||
|
||||
|
||||
# ============================================================
|
||||
# TypeGenCType - CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr)
|
||||
#
|
||||
# 继承链仅 1 级(TPV self 调用仅支持 1 级父类);跨 Mixin 调用统一走 HandlesType 委托。
|
||||
# @t.NoVTable:静态绑定。
|
||||
# ============================================================
|
||||
# TypeGenCType - CTypedef table (register/lookup/ScanCTypedefDeclarations/
|
||||
# _eval_const_int_expr); inheritance chain is one level; @t.NoVTable static binding.
|
||||
@t.NoVTable
|
||||
class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
"""CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr)"""
|
||||
"""CTypedef table: register, lookup, scan declarations, eval const ints."""
|
||||
|
||||
# ============================================================
|
||||
# 本类**不**重复声明 CTypedef* 字段:它们由父类 TypeGenBase 的**类体**声明
|
||||
# (TypeGenBase.vp:73-84),跨模块可见性随父类声明一并提供(继承前缀)。
|
||||
# 判据:字段只在其**归属类**的类体声明一次;子类若再声明同名继承字段,该名字
|
||||
# 会变成子类"自有字段",自有表与继承前缀出现重名条目 ⇒ `base + 表内下标`
|
||||
# 的继承字段偏移整体错位(现场:TypeGenResolve.vp 的
|
||||
# `self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 落到 i32 槽 →
|
||||
# "指针隐式转整数已禁止" fail-fast;正确槽是同名继承字段的 i8* 槽)。
|
||||
# ============================================================
|
||||
# This class does NOT redeclare CTypedef* fields: the parent TypeGenBase class body
|
||||
# declares them once; redeclaring would misalign inherited field offsets.
|
||||
|
||||
# ============================================================
|
||||
# CTypedef 名索引(开放寻址;槽存 index+1,0=空槽)
|
||||
# ============================================================
|
||||
# CTypedef name index (open addressing; slot stores index+1, 0=empty)
|
||||
def _ctypedef_idx_slot(self, si: sizet) -> sizet*:
|
||||
"""取索引第 si 个槽(8 字节,存槽下标+1;0=空槽)"""
|
||||
"""Return index slot si (stores slot index+1; 0 means empty)."""
|
||||
return (sizet*)(uint64(self.CTypedefNameIdx) + uint64(si) * 8)
|
||||
|
||||
def _ctypedef_idx_init(self, pool: memhub.MemBuddy*) -> None:
|
||||
"""创建/清零名索引(与 CTypedefNames 同池);失败则标记不可信"""
|
||||
"""Create and zero the name index in the table pool; mark untrusted on failure."""
|
||||
idx: sizet* = pool.alloc(sizet(CTYPEDEF_IDX_CAP) * 8)
|
||||
if idx is None:
|
||||
self.CTypedefNameIdx = None
|
||||
@@ -83,7 +65,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
self.CTypedefNameIdxBuilt = 0
|
||||
|
||||
def _ctypedef_idx_find(self, name: str) -> int:
|
||||
"""在名索引中探测 name,返回命中槽下标(>=0);未命中/不可用返回 -1"""
|
||||
"""Probe the name index for name; return the slot index or -1 on miss."""
|
||||
if self.CTypedefNameIdx is None or self.CTypedefNameIdxCap <= 0 or name is None: return -1
|
||||
if self.CTypedefNameIdxBuilt != int(len(self.CTypedefNames)): return -1
|
||||
h: uint64 = _ctypedef_idx_hash(name)
|
||||
@@ -102,7 +84,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
return -1
|
||||
|
||||
def _ctypedef_idx_insert(self, slot_index: int, name: str) -> int:
|
||||
"""把 (name → slot_index) 记入索引;0=成功 / 1=失败"""
|
||||
"""Insert (name, slot_index) into the index; return 0 on success, 1 on failure."""
|
||||
if self.CTypedefNameIdx is None or self.CTypedefNameIdxCap <= 0 or name is None: return 1
|
||||
h: uint64 = _ctypedef_idx_hash(name)
|
||||
mask: sizet = sizet(self.CTypedefNameIdxCap - 1)
|
||||
@@ -118,47 +100,26 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
return 1
|
||||
|
||||
def _ctypedef_idx_sync_append(self, name: str) -> None:
|
||||
"""CTypedefNames.append 后同步索引(槽下标 = len-1);不可信则保持不可信"""
|
||||
"""Sync the index after append (slot = len-1); keep untrusted state otherwise."""
|
||||
n: int = int(len(self.CTypedefNames))
|
||||
if self.CTypedefNameIdxBuilt == n - 1 and self._ctypedef_idx_insert(n - 1, name) == 0: self.CTypedefNameIdxBuilt = n
|
||||
else: self.CTypedefNameIdxBuilt = -1
|
||||
|
||||
# ============================================================
|
||||
# CTypedef 表本体的池归一
|
||||
#
|
||||
# 表指针挂在**进程级单例** _g_gen.TypeGenCType 上,不在任何 pool 的管辖
|
||||
# 范围内;而调用方传进来的 pool 在翻译期是「每文件 reset_to 回收」的瞬时池
|
||||
# (Phase1 1a/1b、Phase2 PhaseA、Repl 每片段/每模块都按 mark/reset 轮转)。
|
||||
# 表本体若落在该池:reset 之后表指针**悬空但非 None** ⇒ `is None` 守卫失效
|
||||
# ⇒ 下一次 register_ctypedef 的 append 写进已回收内存(堆损坏;检测模式下
|
||||
# 该区被 free 填成 0xCD ⇒ append 收到 rdi=0xcdcdcdcdcdcdcdcd 后崩)。
|
||||
# 故表本体(表对象 + 名索引 + visiting 表)一律落 PermPool(进程级永久池,
|
||||
# 从不 reset),与"文件级/片段级 reset"彻底解耦。
|
||||
# PermPool 不可用(无 CompilerFactory.factory 的极简单元程序)时退回入参
|
||||
# pool —— 与既有语义一致,只是不再有"表本体跨 reset 存活"的隐式假设。
|
||||
# ============================================================
|
||||
# CTypedef table bodies live in the process-level PermPool (never reset), so a
|
||||
# file/fragment reset cannot dangle the table pointer; falls back to the pool arg.
|
||||
def _ctypedef_table_pool(self, pool: memhub.MemBuddy*) -> memhub.MemBuddy*:
|
||||
"""CTypedef 表本体的池:恒取 PermPool(不可用则退回入参 pool)"""
|
||||
"""Pool for the CTypedef table body: always PermPool, else the pool argument."""
|
||||
if CompilerFactory.factory is not None and CompilerFactory.factory.PermPool is not None: return CompilerFactory.factory.PermPool
|
||||
return pool
|
||||
|
||||
def register_ctypedef(self, pool: memhub.MemBuddy*, name: str, type_str: str) -> None:
|
||||
"""注册 CTypedef 到本地表
|
||||
|
||||
Args:
|
||||
pool: 内存池
|
||||
name: CTypedef 名称(如 "HANDLE")
|
||||
type_str: 类型表达式字符串(如 "VOIDPTR" 或 "t.CPtr")
|
||||
"""
|
||||
"""Register a CTypedef name and its type expression string into the local table."""
|
||||
if name is None or type_str is None or pool is None: return
|
||||
# rawptr 是编译器内置语法(TPC/TPV 双端),不可重定义:
|
||||
# 忽略任何对 rawptr 的 typedef 注册,防止覆盖内置裸指针语义
|
||||
# (rawptr[T] 固定 = 指向原始类型 T 的裸指针)。
|
||||
# rawptr is built-in syntax (TPC/TPV) and cannot be redefined: ignore any
|
||||
# typedef registration for rawptr (rawptr[T] = raw pointer to T).
|
||||
if string.strcmp(name, "rawptr") == 0: return
|
||||
# 复制 name/type_str 到 pool:调用方可能传入零拷贝 AST 字符串
|
||||
# (如 .vpi 读取缓冲区),缓冲区随后被 free 会导致表项悬空、
|
||||
# lookup_ctypedef strcmp 失配、typedef 退化为 i8*(testcheck
|
||||
# SetConsoleOutputCP 的 UINT 根因)。表须持有自己的字符串副本。
|
||||
# Copy name/type_str into the pool: callers may pass zero-copy AST strings
|
||||
# whose buffers are later freed; the table must own its own copies.
|
||||
name_len: sizet = string.strlen(name)
|
||||
name_copy: str = pool.alloc(name_len + 1)
|
||||
if name_copy is None: return
|
||||
@@ -171,45 +132,37 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
tp: memhub.MemBuddy* = self._ctypedef_table_pool(pool)
|
||||
tn: list[str]* = list[str](tp, 64)
|
||||
ts: list[str]* = list[str](tp, 64)
|
||||
# 建表失败(池不可用/耗尽)⇒ 明确报错并返回,绝不让下面的 append
|
||||
# 落到 None 或未初始化地址(原实现无检查 ⇒ 写进 0xCDCD 区崩)
|
||||
# Table creation failure: report and return; never let append write to
|
||||
# None or uninitialized memory (the old code crashed writing 0xCDCD).
|
||||
if tn is None or ts is None:
|
||||
VLogger.error("CTypedef 表分配失败(表本体池不可用或耗尽)", "TypeGen")
|
||||
return
|
||||
self.CTypedefNames = tn
|
||||
self.CTypedefTypeStrs = ts
|
||||
# 名索引随表创建(同池 = PermPool;分配失败则标记不可信,查找回退线性扫描)
|
||||
# name index is created with the table; on failure mark untrusted and fall back to linear scan
|
||||
self._ctypedef_idx_init(tp)
|
||||
# 表就绪守卫:表本体缺失(建表失败/被外部置空)⇒ fail-fast,不写垃圾
|
||||
# table-ready guard: fail-fast when the table is missing rather than write garbage
|
||||
if self.CTypedefNames is None or self.CTypedefTypeStrs is None:
|
||||
VLogger.error("CTypedef 表未就绪(CTypedefNames/CTypedefTypeStrs 为空)", "TypeGen")
|
||||
return
|
||||
# 去重:若已存在同名 CTypedef,更新其类型字符串
|
||||
# dedup: if a same-named CTypedef exists, update its type string
|
||||
exist_n: sizet = len(self.CTypedefNames)
|
||||
ei: sizet
|
||||
for ei in range(exist_n):
|
||||
en: str = self.CTypedefNames.get(ei)
|
||||
if en is not None and string.strcmp(en, name_copy) == 0:
|
||||
# 已存在,更新类型字符串
|
||||
# already exists; update the type string
|
||||
self.CTypedefTypeStrs.set(ei, type_copy)
|
||||
return
|
||||
self.CTypedefNames.append(name_copy)
|
||||
self.CTypedefTypeStrs.append(type_copy)
|
||||
# 追加后同步名索引(槽下标 = 追加前 len = 新 len-1)
|
||||
# sync the name index after append (slot = len before append = new len-1)
|
||||
self._ctypedef_idx_sync_append(name_copy)
|
||||
def store_builtins_diag(self, which: int, text: str) -> str:
|
||||
"""把 builtins 诊断串复制进自有缓冲并返回该缓冲(which: 0=Name, 1=Result)
|
||||
|
||||
调用方(VpiReader._resolve_single_vpi_type_str)传的往往是
|
||||
stdlib.malloc 的临时段缓冲,返回前即 free——直接存指针会让
|
||||
_build_ann_fail_msg 的 snprintf 读已释放内存(ASan
|
||||
heap-use-after-free)。诊断字段必须持有自己的副本。
|
||||
text 为 None 时返回 None("未命中"标记)。
|
||||
"""
|
||||
"""Copy a builtins diagnostic string into an owned buffer; which: 0=Name, 1=Result."""
|
||||
if text is None: return None
|
||||
# 诊断串按需 realloc 到精确长度:固定 TYPEDEF_DIAG_BUF(256) + strncpy(buf, text, 255)
|
||||
# 会静默截断超长诊断串 ⇒ 报错文本丢掉关键名字(归因信息失效)。
|
||||
# 缓冲仍为自有字段(生命周期不变:仍在 reset/析构时统一 stdlib.free)。
|
||||
# Realloc the diagnostic string to exact length: a fixed 256-byte buffer would
|
||||
# silently truncate long strings and lose key names in error text.
|
||||
need: sizet = string.strlen(text) + 1
|
||||
if which == 0:
|
||||
if self.LastBuiltinsTypedefNameBuf is None: self.LastBuiltinsTypedefNameBuf = stdlib.malloc(need)
|
||||
@@ -224,20 +177,14 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
return buf
|
||||
|
||||
def lookup_ctypedef(self, name: str) -> str:
|
||||
"""查找 CTypedef,返回类型表达式字符串
|
||||
|
||||
返回找到的类型表达式字符串,未找到返回 None。
|
||||
调用方应在调用后检查 _g_ctypedef_found 标志区分"未找到"和"空字符串"。
|
||||
"""
|
||||
"""Look up a CTypedef and return its type expression string, or None on miss."""
|
||||
self.CTypedefFound = 0
|
||||
if name is None: return None
|
||||
if self.CTypedefNames is None: return None
|
||||
n: sizet = len(self.CTypedefNames)
|
||||
i: sizet
|
||||
# 名索引快速路径:可信(Built==n)时直接定位唯一候选槽下标。CTypedef 表
|
||||
# 注册时按名去重(register_ctypedef 已存在则原地更新类型串、不追加)⇒ 名
|
||||
# 唯一且注册后不再改写,索引完整覆盖全表 ⇒ 未命中即"确无",直接返回 None
|
||||
# (= 原全表扫描结果);索引不可用/不可信 → 回退下方原全表扫描。
|
||||
# Fast path: when the index is trusted (Built==n) locate the unique slot; a miss
|
||||
# means truly absent; unavailable/untrusted index falls back to the linear scan.
|
||||
if self.CTypedefNameIdxBuilt == int(n) and self.CTypedefNameIdx is not None and self.CTypedefNameIdxCap > 0:
|
||||
ti: int = self._ctypedef_idx_find(name)
|
||||
if ti < 0: return None
|
||||
@@ -252,14 +199,10 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
return ts
|
||||
return None
|
||||
def is_ctypedef_found(self) -> int:
|
||||
"""检查上次 lookup_ctypedef 是否找到值"""
|
||||
"""Return whether the last lookup_ctypedef call found a value."""
|
||||
return self.CTypedefFound
|
||||
def lookup_ctypedef_span(self, text: str, start: sizet, end: sizet) -> str:
|
||||
"""按名字切片查 CTypedef(零分配,不复制子串)
|
||||
|
||||
与 lookup_ctypedef 同表;命中置 CTypedefFound=1 并返回类型表达式串,
|
||||
未命中返回 None(CTypedefFound=0)。用于符号性独立真源的别名链展开。
|
||||
"""
|
||||
"""Look up a CTypedef by a name span without copying the substring."""
|
||||
self.CTypedefFound = 0
|
||||
if text is None or end <= start: return None
|
||||
if self.CTypedefNames is None: return None
|
||||
@@ -269,8 +212,8 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
for i in range(n):
|
||||
nm: str = self.CTypedefNames.get(i)
|
||||
if nm is None: continue
|
||||
# 快速路径:命中要求 nm[0]==text[start](逐字节比较的 k=0 项),
|
||||
# 首字节不同者无需 strlen 即可判定不命中(不影响任何原命中项)。
|
||||
# Fast path: a hit requires nm[0]==text[start]; a differing first byte
|
||||
# rules out a match without strlen.
|
||||
if nm[0] != text[start] or string.strlen(nm) != want: continue
|
||||
hit: int = 1
|
||||
k: sizet = 0
|
||||
@@ -284,18 +227,14 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
self.CTypedefFound = 1
|
||||
return ts
|
||||
return None
|
||||
# 符号性真源(无符号判定的读入口)
|
||||
#
|
||||
# 实现:精确名白名单(TSpec.SpecSignFromTypeNameSpan)+ 别名链逐层展开;
|
||||
# 不表态时返回 TS_SIGN_UNKNOWN(由调用方决定是否回退旧路径)。
|
||||
# 不用 strstr 子串扫描 + 单层展开:会误判 BYTEPTR(含 BYTE)、sha1 前缀名(含
|
||||
# uint32),且多层别名(WCHAR→WORD→uint16)漏判。
|
||||
# Sign source (unsignedness read entry): exact-name whitelist + alias-chain
|
||||
# expansion; returns TS_SIGN_UNKNOWN when undecided.
|
||||
def spec_sign_of_annot(self, text: str, depth: int = 0) -> int:
|
||||
"""按注解文本判定其整数值符号性(独立真源),未覆盖返回 TS_SIGN_UNKNOWN"""
|
||||
"""Decide the integer signedness of an annotation text, else TS_SIGN_UNKNOWN."""
|
||||
if text is None or depth > 16: return TSpec.TS_SIGN_UNKNOWN
|
||||
return self._spec_sign_of_span(text, 0, string.strlen(text), depth)
|
||||
def _spec_sign_of_span(self, text: str, start: sizet, end: sizet, depth: int) -> int:
|
||||
"""顶层 '|' 分段,返回首个能确定的符号性(`X*` 取 X)"""
|
||||
"""Split on top-level '|' and return the first decidable signedness."""
|
||||
seg_start: sizet = start
|
||||
bracket_depth: int = 0
|
||||
i: sizet = start
|
||||
@@ -313,7 +252,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
i += 1
|
||||
return TSpec.TS_SIGN_UNKNOWN
|
||||
def _spec_sign_of_segment(self, text: str, start: sizet, end: sizet, depth: int) -> int:
|
||||
"""单个片段:去空白/括号 → 指针后缀 → 下标取元素 → 末段名 → 别名链展开"""
|
||||
"""Resolve one segment: trim brackets, handle pointer/subscript, expand aliases."""
|
||||
s: sizet = start
|
||||
e: sizet = end
|
||||
while s < e:
|
||||
@@ -322,7 +261,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
while e > s:
|
||||
if text[e - 1] != ' ' and text[e - 1] != '\t' and text[e - 1] != '\n': break
|
||||
e -= 1
|
||||
# 去外层成对括号(如 "(uint32)")
|
||||
# strip outer matched parentheses, e.g. "(uint32)"
|
||||
while e > s + 1:
|
||||
if text[s] != '(' or text[e - 1] != ')': break
|
||||
s += 1
|
||||
@@ -334,9 +273,9 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
if text[e - 1] != ' ' and text[e - 1] != '\t': break
|
||||
e -= 1
|
||||
if e <= s: return TSpec.TS_SIGN_UNKNOWN
|
||||
# C 指针后缀 '*' → 指针,不表态(指针语义由 PtrLevel 表达)
|
||||
# C pointer suffix '*' -> pointer, undecided (PtrLevel expresses pointer semantics)
|
||||
if text[e - 1] == '*': return TSpec.TS_SIGN_UNKNOWN
|
||||
# 泛型/数组/rawptr 下标:IDENT[INNER, ...] → 取 INNER(元素决定值符号性)
|
||||
# generic/array/rawptr subscript: IDENT[INNER, ...] -> INNER decides signedness
|
||||
bracket: sizet = 0
|
||||
has_bracket: int = 0
|
||||
k: sizet = s
|
||||
@@ -361,7 +300,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
if d == 0: break
|
||||
inner_e += 1
|
||||
return self._spec_sign_of_span(text, inner_s, inner_e, depth)
|
||||
# 去模块限定前缀:取最后一个 '.' 之后的名字
|
||||
# strip module qualifier: take the name after the last '.'
|
||||
name_s: sizet = s
|
||||
w: sizet = s
|
||||
while w < e:
|
||||
@@ -369,27 +308,16 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
w += 1
|
||||
exact: int = TSpec.SpecSignFromTypeNameSpan(text, name_s, e)
|
||||
if exact != TSpec.TS_SIGN_UNKNOWN: return exact
|
||||
# 精确名未覆盖 → 沿 CTypedef 链展开(depth 限制防环)
|
||||
# exact name uncovered -> expand along the CTypedef chain (depth guard)
|
||||
if depth >= 16: return TSpec.TS_SIGN_UNKNOWN
|
||||
ts: str = self.lookup_ctypedef_span(text, name_s, e)
|
||||
if ts is None or self.CTypedefFound == 0: return TSpec.TS_SIGN_UNKNOWN
|
||||
return self._spec_sign_of_span(ts, 0, string.strlen(ts), depth + 1)
|
||||
def clear_ctypedefs(self) -> None:
|
||||
"""清空 CTypedef 表(模块切换时调用)
|
||||
|
||||
表本体自首次创建起常驻 PermPool(见 _ctypedef_table_pool)⇒ 此处只
|
||||
**清空内容**、不丢弃表指针:
|
||||
- 丢指针会退回"下一次 register_ctypedef 按**调用方 pool** 重建",
|
||||
表本体又落回会被 reset_to 回收的瞬时池(冷 temp 下翻译 includes
|
||||
时才走到注册路径 ⇒ 表本体跨 reset 悬空 ⇒ append 写进已回收/未
|
||||
初始化内存的根因);
|
||||
- 空表与既有语义完全等价:lookup 命中空表即"未命中",
|
||||
register 首次 append 即写回第 0 槽。
|
||||
"""
|
||||
"""Clear the CTypedef table contents while keeping the resident table pointer."""
|
||||
if self.CTypedefNames is not None: self.CTypedefNames.clear()
|
||||
if self.CTypedefTypeStrs is not None: self.CTypedefTypeStrs.clear()
|
||||
# 名索引清槽(槽存 index+1,0=空槽)+ Built 归零(空表与索引同步;
|
||||
# 与"新建表后首次 register"的索引状态逐字一致)
|
||||
# Clear the index slots and reset Built (keeps an empty table and index in sync).
|
||||
if self.CTypedefNameIdx is not None and self.CTypedefNameIdxCap > 0:
|
||||
string.memset(self.CTypedefNameIdx, 0, sizet(self.CTypedefNameIdxCap) * 8)
|
||||
self.CTypedefNameIdxBuilt = 0
|
||||
@@ -399,13 +327,13 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
self.CTypedefNameIdxBuilt = -1
|
||||
self.CTypedefResolveDepth = 0
|
||||
if self.CTypedefVisiting is not None: self.CTypedefVisiting.clear()
|
||||
# builtins 源扫描标记随表清空复位:每文件最多读一次 includes/builtins.vp
|
||||
# (_lookup_builtins_typedef 的"最后匹配"兜底,见 TypeGenResolve)
|
||||
# Reset the builtins scan flag with the table: read includes/builtins.vp at most
|
||||
# once per file (see TypeGenResolve).
|
||||
self.BuiltinsTypedefScanned = 0
|
||||
self.LastBuiltinsTypedefName = None
|
||||
self.LastBuiltinsTypedefResult = None
|
||||
# 别名缓冲 / 诊断缓冲均为 stdlib.malloc 分配,跨 pool 生命周期;
|
||||
# 此处释放防泄漏(诊断缓冲按需再分配)
|
||||
# Alias/diagnostic buffers are stdlib.malloc'ed across pool lifetimes; free
|
||||
# them here to avoid leaks.
|
||||
if self.LastBuiltinsTypedefNameBuf is not None: stdlib.free(self.LastBuiltinsTypedefNameBuf)
|
||||
self.LastBuiltinsTypedefNameBuf = None
|
||||
if self.LastBuiltinsTypedefResultBuf is not None: stdlib.free(self.LastBuiltinsTypedefResultBuf)
|
||||
@@ -413,15 +341,11 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
if self.ImportAliasBuf is not None: stdlib.free(self.ImportAliasBuf)
|
||||
self.ImportAliasBuf = None
|
||||
def _ctypedef_push_visiting(self, pool: memhub.MemBuddy*, name: str) -> int:
|
||||
"""将 CTypedef 名加入正在解析列表(循环检测)
|
||||
|
||||
Returns:
|
||||
0=成功加入(无循环),1=检测到循环
|
||||
"""
|
||||
"""Push a CTypedef name onto the resolving list; return 1 on a detected cycle."""
|
||||
if name is None: return 0
|
||||
if self.CTypedefVisiting is None:
|
||||
# 与 CTypedef 表同池(PermPool):clear_ctypedefs 只清空、指针不丢,
|
||||
# 避免这张表也落在会被 reset_to 回收的调用方池上
|
||||
# Same pool as the table (PermPool): clear_ctypedefs empties but keeps the
|
||||
# pointer, avoiding a caller pool that reset_to would reclaim.
|
||||
vp: memhub.MemBuddy* = self._ctypedef_table_pool(pool)
|
||||
self.CTypedefVisiting = list[str](vp, 32)
|
||||
if self.CTypedefVisiting is None: return 0
|
||||
@@ -430,34 +354,26 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
for vi in range(vn):
|
||||
vs: str = self.CTypedefVisiting.get(vi)
|
||||
if vs is not None and string.strcmp(vs, name) == 0:
|
||||
return 1 # 循环检测
|
||||
return 1 # cycle detected
|
||||
self.CTypedefVisiting.append(name)
|
||||
return 0
|
||||
def _ctypedef_pop_visiting(self) -> None:
|
||||
"""从正在解析列表弹出最后一个 CTypedef 名"""
|
||||
"""Pop the last CTypedef name from the resolving list."""
|
||||
if self.CTypedefVisiting is None: return
|
||||
vn: sizet = len(self.CTypedefVisiting)
|
||||
if vn > 0: self.CTypedefVisiting.pop()
|
||||
def _is_ctypedef_annotation(self, annotation: ast.AST*) -> int:
|
||||
"""检测类型注解是否为 t.CTypedef
|
||||
|
||||
Args:
|
||||
annotation: 类型注解 AST 节点
|
||||
|
||||
Returns:
|
||||
1=是 t.CTypedef,0=否
|
||||
"""
|
||||
"""Return 1 if the annotation is t.CTypedef, otherwise 0."""
|
||||
if annotation is None: return 0
|
||||
k_v: int = annotation.kind()
|
||||
if k_v != ast.ASTKind.Attribute: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(annotation)
|
||||
# 字段访问改用 ast 模块访问器(直接访问 at.attr 可能按 stub 类型生成错误 GEP,
|
||||
# 读垃圾地址 → strcmp 崩溃)。
|
||||
# Use ast module accessors for field access: a direct at.attr read may emit a
|
||||
# wrong GEP from the stub type and crash strcmp.
|
||||
at_attr_v: str = ast.attribute_get_attr(at)
|
||||
if at_attr_v is None: return 0
|
||||
# 限长比较替代 strcmp:attr 是零拷贝引用 src_buf 的 token 字符串,可能无 NUL
|
||||
# 结尾("CTypedef" 后紧跟空格/其他字符),strcmp 会读穿到页边界 → 崩溃。
|
||||
# strncmp 只读 8 字节,再校验第 9 字节为终止/分隔符(防 "CTypedefXXX" 误匹配)。
|
||||
# Length-bounded compare instead of strcmp: the token may lack a NUL and strcmp
|
||||
# reads past the page; check byte 9 is a terminator/separator.
|
||||
if string.strncmp(at_attr_v, "CTypedef", 8) != 0: return 0
|
||||
sep_ch_d: char = at_attr_v[8]
|
||||
if sep_ch_d != '\0' and sep_ch_d != ' ' and sep_ch_d != '\n' and sep_ch_d != '\r' and sep_ch_d != '\t' and sep_ch_d != '|': return 0
|
||||
@@ -466,24 +382,13 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
mod_nm: ast.Name* = ast.Name*(at_val_v)
|
||||
mod_id_v: str = ast.name_get_id(mod_nm)
|
||||
if mod_id_v is None: return 0
|
||||
# 同样限长比较("t" 无 NUL 结尾时 strcmp 读穿)
|
||||
# same length-bounded compare ("t" may lack a NUL)
|
||||
if string.strncmp(mod_id_v, "t", 1) != 0: return 0
|
||||
sep_ch_m: char = mod_id_v[1]
|
||||
if sep_ch_m != '\0' and sep_ch_m != ' ' and sep_ch_m != '\n' and sep_ch_m != '\r' and sep_ch_m != '\t' and sep_ch_m != '|': return 0
|
||||
return 1
|
||||
def ScanCTypedefDeclarations(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int:
|
||||
"""扫描 AST 顶层节点,注册所有 NAME: t.CTypedef = Type 声明
|
||||
|
||||
在 _translate_module_level 中、模块级变量预注册之前调用,
|
||||
确保 CTypedef 名称在类型解析时可用。
|
||||
|
||||
Args:
|
||||
pool: 内存池
|
||||
tree: 模块 AST 根节点
|
||||
|
||||
Returns:
|
||||
注册的 CTypedef 数量
|
||||
"""
|
||||
"""Scan top-level AST nodes and register all NAME: t.CTypedef = Type declarations."""
|
||||
if pool is None or tree is None: return 0
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is None: return 0
|
||||
@@ -496,49 +401,39 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
if child.kind() != ast.ASTKind.AnnAssign: continue
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(child)
|
||||
if aa.annotation is None: continue
|
||||
# 检测注解是否为 t.CTypedef
|
||||
# check whether the annotation is t.CTypedef
|
||||
if self._is_ctypedef_annotation(aa.annotation) == 0: continue
|
||||
# 提取目标名称(必须是 Name 节点)
|
||||
# extract the target name (must be a Name node)
|
||||
if aa.target is None or aa.target.kind() != ast.ASTKind.Name: continue
|
||||
tgt: ast.Name* = ast.Name*(aa.target)
|
||||
tgt_id_v: str = ast.name_get_id(tgt)
|
||||
if tgt_id_v is None: continue
|
||||
# 提取类型表达式字符串(从 value AST 节点)
|
||||
# extract the type expression string (from the value AST node)
|
||||
if aa.value is None: continue
|
||||
type_str: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, aa.value)
|
||||
if type_str is None: continue
|
||||
# 注册 CTypedef
|
||||
# register the CTypedef
|
||||
self.register_ctypedef(pool, tgt_id_v, type_str)
|
||||
count += 1
|
||||
return count
|
||||
def register_ctypedef_from_source(self, pool: memhub.MemBuddy*, src: str) -> int:
|
||||
"""从源码文本逐行扫描注册 CTypedef(`typedef N = V` 或 `N: t.CTypedef = V`)
|
||||
|
||||
stdint.vp 崩溃根因修复:AST 字符串字段(attr/id)token 零拷贝指向
|
||||
src_buf,跨阶段内容不稳定(悬空),ScanCTypedefDeclarations 读
|
||||
attr 会 0xC0000005。本函数在 src_buf 有效期内(Phase1 1b translate
|
||||
前)用纯文本匹配注册,不依赖 AST 节点内容。
|
||||
"""
|
||||
"""Register CTypedefs by scanning raw source lines without reading AST fields."""
|
||||
if pool is None or src is None: return 0
|
||||
# 文件边界防护:clear_ctypedefs 只在 translate 前调用(Phase1Main
|
||||
# L800),文件级 reset_to(mark1) 回收本文件翻译期 pool 分配后,上一
|
||||
# 文件的 CTypedefNames 表指针悬空(非 None,list 本身也 pool 分配)。
|
||||
# 本函数去重遍历(register_ctypedef 内 strcmp)读已回收内存 →
|
||||
# 0xC0000005(1.log: stdint.vp 翻译时 strcmp 读地址 0x4)。先清表
|
||||
# 重建,杜绝悬空遍历。翻译期 Handle 按需注册不受影响(L800 会再清)。
|
||||
# File-boundary guard: clear and rebuild the table first, since a file-level
|
||||
# reset_to would dangle the previous file's table pointer and crash strcmp.
|
||||
self.clear_ctypedefs()
|
||||
src_len: sizet = string.strlen(src)
|
||||
count: int = 0
|
||||
line_start: sizet = 0
|
||||
i: sizet = 0
|
||||
# 行解析缓冲("NAME\0VALUE\0"),整文件复用(register_ctypedef 内部自会复制)
|
||||
# line parse buffer ("NAME\0VALUE\0"), reused per file (register_ctypedef copies)
|
||||
td_buf: str = pool.alloc(320)
|
||||
while i <= src_len:
|
||||
if i == src_len or src[i] == '\n':
|
||||
line_len: sizet = i - line_start
|
||||
if line_len > 0 and td_buf is not None:
|
||||
# 两种等价形态统一解析:语法糖 `typedef N = V`(builtins.vp 现用
|
||||
# 形态)与注解 `N: t.CTypedef = V`(stdint.vp 等库模块)。
|
||||
# Parse both equivalent forms: the sugar "typedef N = V" and the
|
||||
# annotation "N: t.CTypedef = V".
|
||||
if self.Gen.TypeGenResolve._parse_typedef_line(td_buf, src, line_start, line_len) != 0:
|
||||
name_buf: str = td_buf
|
||||
ty_buf: str = td_buf + string.strlen(td_buf) + 1
|
||||
@@ -548,24 +443,15 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
i += 1
|
||||
return count
|
||||
def _eval_const_int_expr(self, node: ast.AST*, pool: memhub.MemBuddy* = None, from_imports: str = None, imported_modules: str = None, depth: int = 0) -> int:
|
||||
"""递归计算常量整数表达式
|
||||
|
||||
pool/from_imports/imported_modules:跨模块 CDefine 查找上下文
|
||||
(t.CArray count 引用依赖模块的 CDefine,如 SHA512_BLOCK_LEN 定义在
|
||||
hashlib/__sha512.vp,本地 CDefine 表每模块清空,需跨模块回退查找)。
|
||||
depth:递归深度守卫(防御 AST 损坏成自引用环导致的无限递归——
|
||||
本函数被 resolve_annotation_type 的 t.CArray count 解析调用,其内部
|
||||
BinOp 自递归不经过 resolve_annotation_type 的 depth 守卫)。
|
||||
"""
|
||||
"""Recursively evaluate a constant integer expression with cross-module lookup."""
|
||||
if node is None:
|
||||
self.ConstEvalOk = 0
|
||||
return 0
|
||||
# 递归深度守卫:BinOp 左右子树成环时无限递归直至栈耗尽。
|
||||
# fail-fast 终止(fatal_type_error → sys.exit(1)),不静默回落。
|
||||
# Recursion depth guard: a cyclic BinOp would recurse forever; fail-fast abort.
|
||||
if depth > 64: self.Gen.TypeGenErr.fatal_type_error(node, "常量表达式递归过深(AST 环或嵌套超过 64 层)")
|
||||
k: int = node.kind()
|
||||
|
||||
# Constant: 整数字面量
|
||||
# Constant: integer literal
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
if cn.const_kind == ast.CONST_INT:
|
||||
@@ -574,7 +460,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
self.ConstEvalOk = 0
|
||||
return 0
|
||||
|
||||
# Name: CDefine 常量名(本地表优先,未命中走跨模块查找)
|
||||
# Name: CDefine constant (local table first, then cross-module lookup)
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is None:
|
||||
@@ -582,9 +468,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
return 0
|
||||
val: int = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||||
# 本地表未命中:跨模块 CDefine 查找
|
||||
# 用 HandlesExpr 模块级委托(self._lookup_cross_module_cdefine 是
|
||||
# ExprGenMixin 方法,TypeGenMixin 直接调用会生成未定义引用)
|
||||
# Local miss: cross-module CDefine lookup via the HandlesExpr delegate.
|
||||
if (from_imports is not None or imported_modules is not None) and pool is not None:
|
||||
x_val: int = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, nm.id, from_imports, imported_modules)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
@@ -595,10 +479,8 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
self.ConstEvalOk = 1
|
||||
return val
|
||||
|
||||
# Attribute: 模块限定常量名,如 hashlib.MD5_DIGEST_LEN
|
||||
# (t.CArray[uint8, hashlib.MD5_DIGEST_LEN] 的 count 是 Attribute,
|
||||
# 复用跨模块 CDefine 查找:*:模块 → 源文件提取;re-export 常量由
|
||||
# _lookup_cross_module_cdefine 的 from-import 递归子模块兜底)
|
||||
# Attribute: module-qualified constant name, e.g. hashlib.MD5_DIGEST_LEN;
|
||||
# reuse the cross-module CDefine lookup.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is None:
|
||||
@@ -622,16 +504,16 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
self.ConstEvalOk = 0
|
||||
return 0
|
||||
|
||||
# BinOp: 算术/位运算
|
||||
# BinOp: arithmetic / bitwise
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
# 递归计算左操作数
|
||||
# evaluate the left operand
|
||||
lv: int = self._eval_const_int_expr(bop.left, pool, from_imports, imported_modules, depth + 1)
|
||||
if self.ConstEvalOk == 0: return 0
|
||||
# 递归计算右操作数
|
||||
# evaluate the right operand
|
||||
rv: int = self._eval_const_int_expr(bop.right, pool, from_imports, imported_modules, depth + 1)
|
||||
if self.ConstEvalOk == 0: return 0
|
||||
# 根据运算符执行计算
|
||||
# apply the operator
|
||||
if bop.op == ast.OpKind.Add:
|
||||
self.ConstEvalOk = 1
|
||||
return lv + rv
|
||||
@@ -642,7 +524,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
self.ConstEvalOk = 1
|
||||
return lv * rv
|
||||
if bop.op == ast.OpKind.Div:
|
||||
# 整数常量除法:与 FloorDiv 一致(对齐 TPC ConstEvaluator L354-355)
|
||||
# integer constant division, same as FloorDiv (matches TPC ConstEvaluator)
|
||||
if rv == 0:
|
||||
self.ConstEvalOk = 0
|
||||
return 0
|
||||
@@ -660,11 +542,11 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
if bop.op == ast.OpKind.BitAnd:
|
||||
self.ConstEvalOk = 1
|
||||
return lv & rv
|
||||
# 不支持的运算符
|
||||
# unsupported operator
|
||||
self.ConstEvalOk = 0
|
||||
return 0
|
||||
|
||||
# 不支持的节点类型
|
||||
# unsupported node type
|
||||
self.ConstEvalOk = 0
|
||||
return 0
|
||||
|
||||
@@ -20,20 +20,18 @@ import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
|
||||
|
||||
# ============================================================
|
||||
# TypeGenErr - 错误上报/源行读取/类型推断(fatal_error/infer_* 等)
|
||||
# TypeGenErr - error reporting / source line reading / type inference (fatal_error, infer_*, ...)
|
||||
# ============================================================
|
||||
novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
"""错误上报/源行读取/类型推断(fatal_error/infer_* 等)"""
|
||||
"""Error reporting / source-line reading / type inference (fatal_error/infer_* etc.)"""
|
||||
|
||||
def _read_source_line(self, file_path: str, lineno: int) -> str:
|
||||
"""从源文件读取第 lineno 行内容"""
|
||||
"""Read line lineno from the source file"""
|
||||
if file_path is None or lineno <= 0: return None
|
||||
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
|
||||
if f.closed: return None
|
||||
# 按文件实际大小分配缓冲区:固定 256KB 会截断大源文件,末尾行号读不到 →
|
||||
# 误报"文件不存在或行号超出范围"(如 CallGenMixin.vp 555KB/9236 行的第
|
||||
# 8390 行)。64MB 上限防御异常超大文件整体读取。
|
||||
# Allocate by actual file size: a fixed 256KB buffer truncated large files so late line
|
||||
# numbers were unreadable. The 64MB cap guards against reading abnormally huge files.
|
||||
fsize: LONGLONG = f.size()
|
||||
if fsize <= 0 or fsize > 67108864:
|
||||
f.close()
|
||||
@@ -48,7 +46,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if bytes_read <= 0:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
# 逐行查找第 lineno 行
|
||||
# Scan line by line for the target lineno
|
||||
cur_line: int = 1
|
||||
line_start: sizet = 0
|
||||
for i in range(bytes_read):
|
||||
@@ -69,10 +67,10 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
def read_source_line(self, file_path: str, lineno: int) -> str:
|
||||
"""公开包装:从源文件读取第 lineno 行内容(供 traceback 展示)"""
|
||||
"""Public wrapper: read line lineno from the source file for traceback display"""
|
||||
return self._read_source_line(file_path, lineno)
|
||||
def dump_parse_traceback(self, file_path: str) -> None:
|
||||
"""解析失败时以 Python 风格输出 traceback(仿 fatal_error)"""
|
||||
"""On parse failure, print a Python-style traceback (like fatal_error)"""
|
||||
err_line: int = ast.get_parse_err_line()
|
||||
err_col: int = ast.get_parse_err_col()
|
||||
err_msg: str = ast.get_parse_err_msg()
|
||||
@@ -83,8 +81,8 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
src: str = self._read_source_line(file_path, err_line)
|
||||
if src is not None:
|
||||
stdio.printf(" %s\n", src)
|
||||
# 列标记:在出错 token 起始列下方标 ^(parser 的 col_offset 是 0-based,
|
||||
# 前缀 4 空格 + err_col 个空格后落点恰好是该 token 首字符)
|
||||
# Column marker: put ^ under the error token's start column (parser col_offset is
|
||||
# 0-based, so 4-space indent + err_col spaces lands on the token's first char)
|
||||
if err_col > 0:
|
||||
stdio.printf(" ")
|
||||
for _ in range(err_col): stdio.printf(" ")
|
||||
@@ -94,12 +92,9 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if err_msg is None or err_msg[0] == 0: err_msg = "语法错误"
|
||||
stdio.printf("SyntaxError: %s\n", err_msg)
|
||||
def fatal_error(self, node: ast.AST*, msg: str, err_type: str = "SyntaxError", col_start: int = 0, col_end: int = 0):
|
||||
"""以 Python 风格报错并退出(支持多帧、错误类型、列范围)
|
||||
|
||||
向后兼容:err_type/col_start/col_end 有默认值,旧调用 fatal_error(node, msg) 仍可用。
|
||||
"""
|
||||
"""Report an error in Python style and exit (supports multiple frames, error type, column range)."""
|
||||
stdio.printf("Traceback (most recent call last, compile-time error):\n")
|
||||
# 输出调用栈帧(从底到顶)
|
||||
# Print the call stack frames (bottom to top)
|
||||
for i in range(self.Gen.TypeGenBase.TraceCount):
|
||||
frame: rawptr[GenTypes.TraceFrame] = self.Gen.TypeGenBase._get_trace_frame(i)
|
||||
if frame is not None and frame.file_name is not None:
|
||||
@@ -110,14 +105,13 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
stdio.printf(" %s\n", src)
|
||||
stdlib.free(src)
|
||||
else: stdio.printf(' File "%s"\n', frame.file_name)
|
||||
# 输出当前帧(node 对应的位置)
|
||||
# Print the current frame (position corresponding to node)
|
||||
lineno: int = 0
|
||||
if node is not None: lineno = node.lineno
|
||||
file_name: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
if file_name is None: file_name = "<unknown>"
|
||||
# 行号兜底:克隆/构造节点 lineno 可能为 0(如泛型特化 _clone_arguments 的
|
||||
# ast.Arg 构造器硬编码 0,astaux.vp L139)。此时回退到 trace 栈最后一个
|
||||
# 有效帧(文件+行号)作为出错位置,避免输出误导性的 "line 0"。
|
||||
# Line-number fallback: cloned/synthesized nodes may have lineno 0 (e.g. ast.Arg built by
|
||||
# generic specialization hardcodes 0). Fall back to the trace stack's last valid frame.
|
||||
if lineno <= 0 and self.Gen.TypeGenBase.TraceCount > 0:
|
||||
ti_b: int = self.Gen.TypeGenBase.TraceCount - 1
|
||||
while ti_b >= 0:
|
||||
@@ -127,9 +121,8 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
lineno = tfb.lineno
|
||||
break
|
||||
ti_b -= 1
|
||||
# 行号合理性防御:先读取该行源码,验证 node.lineno 是否落在当前文件内。
|
||||
# 若读不到(文件不存在或行号超出范围),说明文件/行号归属不可信
|
||||
# (如 AST 树被内存复用/串扰后跨文件报错),打印警告而非误导性行号。
|
||||
# Sanity defense: read the line first to verify node.lineno belongs to the current file.
|
||||
# If unreadable the file/line is untrustworthy (e.g. reused AST); print a warning instead.
|
||||
src_line: str = None
|
||||
line_ok: int = 1
|
||||
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
|
||||
@@ -141,7 +134,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||||
if src_line is not None:
|
||||
stdio.printf(" %s\n", src_line)
|
||||
# 列范围标记:在源代码行下方用 ^ 标记出错范围
|
||||
# Column-range marker: use ^ under the source line to mark the error range
|
||||
if col_start > 0 and col_end >= col_start:
|
||||
stdio.printf(" ")
|
||||
j: int
|
||||
@@ -150,28 +143,27 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
else: stdio.printf(" ")
|
||||
stdio.printf("\n")
|
||||
stdlib.free(src_line)
|
||||
# 输出错误类型 + 消息
|
||||
# Output the error type + message
|
||||
if err_type is None or err_type[0] == 0: err_type = "SyntaxError"
|
||||
stdio.printf("%s: %s\n", err_type, msg)
|
||||
# 退出前强制刷出:stdout 重定向到文件时是全缓冲,直接终止进程会把整段
|
||||
# traceback 丢掉(实测 2.log 只剩一行 VLogger 输出)。
|
||||
# Force flush before exiting: stdout redirected to a file is fully buffered, so terminating
|
||||
# directly drops the whole traceback (observed: 2.log kept only one VLogger line).
|
||||
stdio.fflush(None)
|
||||
# 终止点收敛:REPL 会话内经 VLogger.repl_abort longjmp 回 REPL 循环
|
||||
# (Traceback 已打印,进程存活);批处理下该出口即 sys.exit(1),逐字不变。
|
||||
# Termination convergence: in REPL, longjmp back to the loop via VLogger.repl_abort (the
|
||||
# traceback is printed and the process survives); in batch this exit is sys.exit(1) unchanged.
|
||||
VLogger.repl_abort()
|
||||
|
||||
def fatal_type_error(self, node: ast.AST*, msg: str, col_start: int = 0, col_end: int = 0):
|
||||
"""以 Python 风格报错并退出(TypeError 版本)"""
|
||||
"""Report an error in Python style and exit (TypeError variant)"""
|
||||
self.fatal_error(node, msg, "TypeError", col_start, col_end)
|
||||
def try_extract_callable_ret(self, pool: memhub.MemBuddy*, annot: ast.AST*, imported_modules: str, from_imports: str, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||||
"""提取函数指针注解(t.Callable[[A,B], R] / callable[[A,B], R])的返回类型
|
||||
LLVMType;非 Callable 注解或无签名形态([]、[[]])返回 None"""
|
||||
"""Extract the return type of a function-pointer annotation (t.Callable[[A,B], R]); None for non-Callable or signature-less forms."""
|
||||
if annot is None or annot.kind() != ast.ASTKind.Subscript: return None
|
||||
sub_cr: ast.Subscript* = ast.Subscript*(annot)
|
||||
# 构造子判据走 TypeGenResolve 单一真源(Attribute 形态 t.Callable 与
|
||||
# 裸名形态 callable / Callable 等价)
|
||||
# The subscript test uses TypeGenResolve as the single source of truth (the t.Callable
|
||||
# Attribute form is equivalent to the bare-name callable / Callable forms)
|
||||
if self.Gen.TypeGenResolve._IsCallableSubscriptValue(sub_cr.value) == 0: return None
|
||||
# slice 必须是 Tuple(elts=[args_list, ret_node])
|
||||
# slice must be Tuple(elts=[args_list, ret_node])
|
||||
if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple: return None
|
||||
tp_cr: ast.Tuple* = ast.Tuple*(sub_cr.slice)
|
||||
if tp_cr.elts is None or len(tp_cr.elts) != 2: return None
|
||||
@@ -179,23 +171,22 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if ret_node_cr is None: return None
|
||||
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
|
||||
def has_decorator_marker(self, node: ast.AST*, marker_name: str, depth: int = 0) -> int:
|
||||
"""检测类型注解中是否包含指定的装饰器标记"""
|
||||
"""Check whether a type annotation contains the given decorator marker"""
|
||||
if node is None or marker_name is None: return 0
|
||||
# 递归深度守卫:vpi stub 注入时对每个函数 returns 调用本函数,
|
||||
# vpi tree 深层节点可能被覆盖损坏成回指环(VpiReader L810-813),
|
||||
# BinOp 递归无守卫会无限递归直至栈耗尽。fail-fast 终止。
|
||||
# Recursion depth guard: vpi stub injection calls this for every function's returns, and
|
||||
# deep vpi nodes may be corrupted into back-pointing cycles (VpiReader L810-813); fail fast.
|
||||
if depth > 128: self.fatal_type_error(node, "装饰器标记检测递归过深(AST 环或嵌套超过 128 层)")
|
||||
|
||||
k: int = node.kind()
|
||||
|
||||
# Attribute 节点: t.CExtern, t.State, t.CExport, t.CInline
|
||||
# Attribute node: t.CExtern, t.State, t.CExport, t.CInline
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is not None:
|
||||
if string.strcmp(at.attr, marker_name) == 0: return 1
|
||||
return 0
|
||||
|
||||
# BinOp 节点: int | t.CExtern(递归检测左右子节点)
|
||||
# BinOp node: int | t.CExtern (recurse into both children)
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0: return 1
|
||||
@@ -205,20 +196,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
return 0
|
||||
|
||||
def has_pointee_volatile_marker(self, node: ast.AST*) -> int:
|
||||
"""检测指针注解 rawptr[...] 的下标实参是否含 t.CVolatile 标记,1=是 / 0=否
|
||||
|
||||
语义:rawptr[t.CVolatile | T] 等价 C 的 volatile T*,即"指针所指对象按
|
||||
volatile 访问"——解引用(p[i] / p.field / *p)的 load/store 带
|
||||
LLVM volatile。与 has_decorator_marker 的区别:后者检测变量/字段**自身**
|
||||
的可见性限定(如 `locked: t.CVolatile | int`),对 Subscript 节点返回 0;
|
||||
本函数专检 rawptr 的 pointee 侧标记(下标实参子树),两者互不串味。
|
||||
标注形态(vast.parse 实证):
|
||||
rawptr[t.CVolatile | int]
|
||||
→ Subscript(value=Name('rawptr'),
|
||||
slice=BinOp(BitOr, Attribute(t,CVolatile), Attribute(t,CInt)))
|
||||
多级指针 rawptr[rawptr[t.CVolatile|T]] 的 slice 是 Subscript,
|
||||
has_decorator_marker 不递归 Subscript ⇒ 返回 0(明确不支持,见限制清单)。
|
||||
"""
|
||||
"""Whether a rawptr[...] subscript argument carries the t.CVolatile marker (pointee-side volatile), 1=yes / 0=no."""
|
||||
if node is None or node.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||||
@@ -228,23 +206,14 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
return self.has_decorator_marker(sub.slice, "CVolatile", 0)
|
||||
|
||||
def is_const_object_marker(self, node: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
|
||||
"""变量/字段本身是否 **const 对象**(1=是 / 0=否)。
|
||||
|
||||
判据与 TPC 的 BaseGen._IsConstObject **逐条一致**(见 Debugger/type-descriptors.md):
|
||||
① 注解子树含 t.CConst(has_decorator_marker,与 TPC 的 _AnnotationHasQualifier 同口径);
|
||||
② 声明类型的 LLVM 形态**不是指针**(TPC 用 PtrCount==0 表达同一件事)。
|
||||
`t.CConst` 在本语言里承载「指向的数据 const」(对齐 TPC 的 DataConst 语义),
|
||||
对非指针类型该限定退化为「该对象自身 const」⇒ 登记为 const 变量/字段;
|
||||
对指针类型(`p: t.CConst | T*`)限定只作用于 pointee ⇒ 指针变量自身
|
||||
仍可重新赋值,不登记。
|
||||
"""
|
||||
"""Whether a variable/field is itself a const object (1=yes / 0=no): annotation carries t.CConst and the declared LLVM type is not a pointer."""
|
||||
if self.has_decorator_marker(node, "CConst", 0) == 0: return 0
|
||||
if var_ty is None: return 0
|
||||
if var_ty.kind() == mlirlite.LLVMType.Ptr: return 0
|
||||
return 1
|
||||
|
||||
def is_pass_body(self, body: list[ast.AST*]*) -> int:
|
||||
"""检测函数体是否为 pass(空体或仅含 pass 语句)"""
|
||||
"""Test whether a function body is pass (empty or only a pass statement)"""
|
||||
if body is None: return 1
|
||||
bn: sizet = len(body)
|
||||
if bn == 0: return 1
|
||||
@@ -252,17 +221,13 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
stmt: ast.AST* = body.get(0)
|
||||
if stmt is not None and stmt.kind() == ast.ASTKind.Pass: return 1
|
||||
return 0
|
||||
# 获取 LLVM 整数类型的位宽
|
||||
# 实现已收拢到 TSpec.SpecTypeBits(该块在 TypeSpec 模块中的唯一实现),此处只做委托。
|
||||
# 私有调用点(self.get_llvm_type_bits)保持原样不受影响。
|
||||
# Get the LLVM integer type bit width. Delegates to TSpec.SpecTypeBits (the sole implementation
|
||||
# in TypeSpec); private call sites (self.get_llvm_type_bits) are unaffected.
|
||||
def get_llvm_type_bits(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托 SpecTypeBits)"""
|
||||
"""Get an LLVM integer type's bit width, 0 for non-integers (delegates to SpecTypeBits)"""
|
||||
return TSpec.SpecTypeBits(ty)
|
||||
def infer_expr_type(self, pool: memhub.MemBuddy*, node: ast.AST*, param_types: str) -> mlirlite.LLVMType*:
|
||||
"""推断表达式的 LLVM 类型
|
||||
|
||||
param_types 格式: "name:bits,name:bits,..."
|
||||
"""
|
||||
"""Infer an expression's LLVM type; param_types format is "name:bits,name:bits,..."."""
|
||||
if node is None: return mlirlite.Int32(pool)
|
||||
|
||||
k: int = node.kind()
|
||||
@@ -272,9 +237,8 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
elif k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and param_types is not None:
|
||||
# 在 param_types 中查找: "name:bits" 格式
|
||||
# 键按精确长度分配:固定 64 缓冲 + snprintf("%s:") 会静默截断超长参数名,
|
||||
# 截断后的键可能与**别的参数前缀**匹配成功 ⇒ 取到错误的位宽(类型误判)。
|
||||
# Look up "name:bits" in param_types. Allocate the key at exact length: a fixed 64
|
||||
# buffer truncated long names and could match another param's prefix, giving wrong bits.
|
||||
id_len: sizet = string.strlen(nm.id)
|
||||
search_key: str = pool.alloc(id_len + 2)
|
||||
if search_key is not None:
|
||||
@@ -306,7 +270,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
|
||||
return mlirlite.Int32(pool)
|
||||
def infer_return_type(self, pool: memhub.MemBuddy*, body: list[ast.AST*]*, param_types: str) -> mlirlite.LLVMType*:
|
||||
"""扫描函数体中的 return 语句,推断返回类型"""
|
||||
"""Scan a function body's return statements to infer the return type"""
|
||||
if body is None: return mlirlite.Int32(pool)
|
||||
|
||||
ret_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
@@ -323,15 +287,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if new_bits > cur_bits: ret_ty = expr_ty
|
||||
return ret_ty
|
||||
def body_has_value_return(self, pool: memhub.MemBuddy*, body: list[ast.AST*]*) -> int:
|
||||
"""扫描函数体,判断是否存在返回非 None 值的 return
|
||||
|
||||
递归检查控制流语句(if/while/for/try/with/match)内层 return,但跳过
|
||||
嵌套函数/类定义(FunctionDef/ClassDef 由自身翻译时独立检查,避免外层
|
||||
检查误伤嵌套作用域)。裸 return / return None(CONST_NONE)/ 无 return
|
||||
均不算返回非 None 值。
|
||||
供无返回类型注解函数的 fail-fast 检查使用:返回具体值必须有显式 ->
|
||||
注解,编译器不替用户推断返回类型(推断位宽会掩盖返回契约)。
|
||||
"""
|
||||
"""Whether the body has a return of a non-None value, recursively checking control-flow statements but skipping nested defs."""
|
||||
if body is None: return 0
|
||||
bn: sizet = len(body)
|
||||
for bi in range(bn):
|
||||
@@ -341,7 +297,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if k == ast.ASTKind.Return:
|
||||
rt: rawptr[ast.Return] = rawptr[ast.Return](stmt)
|
||||
if rt.value is not None:
|
||||
# return None(CONST_NONE)不算返回非 None 值
|
||||
# return None (CONST_NONE) does not count as returning a non-None value
|
||||
if rt.value.kind() == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(rt.value)
|
||||
if cn.const_kind == ast.CONST_NONE: continue
|
||||
@@ -353,16 +309,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if self.body_has_value_return(pool, sub) != 0: return 1
|
||||
return 0
|
||||
def build_param_types_str(self, pool: memhub.MemBuddy*, args_node: ast.AST*) -> str:
|
||||
"""构建参数类型编码字符串 "name:bits,name:bits,..."
|
||||
|
||||
[D5·截断纪律] 旧实现按 `an * 32 + 1` 预估缓冲,再用 `_strappend` 以
|
||||
`remain - 1` 截断且**不报错**:参数名偏长(或参数偏多)时编码串被静默
|
||||
截断 ⇒ `infer_expr_type` 的 `strstr(param_types, "name:")` 查找失配 /
|
||||
命中他人前缀 ⇒ 静默回落 Int32(类型误判)。现改为**上界预估 + 单遍就地
|
||||
拼接**:段一是只扫参数名的上界(name + ':' + 位宽 + ',' + NUL),段二是
|
||||
解析类型后逐条目就地格式化并立即拼接 —— 缓冲恒够,不存在截断;
|
||||
`_strappend` 一并删除(唯一调用点在本函数)。
|
||||
"""
|
||||
"""Build the param type encoding string "name:bits,name:bits,..." with upper-bound sizing and single-pass in-place concatenation."""
|
||||
if args_node is None: return None
|
||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if ags.args is None: return None
|
||||
@@ -370,11 +317,9 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
an: sizet = len(alist)
|
||||
if an == 0: return None
|
||||
|
||||
# 1) 上界估长(**只扫参数名**,不解析类型,也不暂存结果指针):
|
||||
# 每条 = name + ':' + 位宽(int32 十进制最长 11 字符含符号)+ ',';再加 NUL。
|
||||
# 必须"上界"而非"精确":把条目指针存进 sizet 数组再取出转回指针 strcat 会出错;
|
||||
# 上界缓冲不需要数组往返,每条目就地格式化、立即拼接。
|
||||
bound: sizet = 1 # 末尾 NUL
|
||||
# 1) Upper-bound sizing (scan only param names, no type resolution): each entry is
|
||||
# name + ':' + bits + ',', plus NUL. Use an upper bound, not an exact size.
|
||||
bound: sizet = 1 # trailing NUL
|
||||
pre_i: sizet
|
||||
for pre_i in range(an):
|
||||
pre_arg: ast.Arg* = ast.Arg*(alist.get(pre_i))
|
||||
@@ -384,7 +329,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if buf is None: return None
|
||||
buf[0] = '\0'
|
||||
|
||||
# 2) 单遍:解析类型 → 就地格式化条目 → 立即拼接(缓冲为上界,不会截断)
|
||||
# 2) Single pass: resolve type -> format entry in place -> append immediately (no truncation)
|
||||
cnt: sizet = 0
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
for ai in range(an):
|
||||
@@ -392,8 +337,8 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if arg is None or arg.arg is None: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
# 统一口径:CTypedef 动态表优先,miss 回退 map_t_type
|
||||
# (阶段 2:删除 map_t_type 硬编码别名后,BYTE 等靠库 typedef)
|
||||
# Unified: prefer the CTypedef dynamic table, fall back to map_t_type
|
||||
# (stage 2 removed hardcoded aliases; BYTE etc. now rely on library typedefs)
|
||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(arg.annotation)
|
||||
if at.attr is not None:
|
||||
@@ -402,7 +347,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
bits: int = self.get_llvm_type_bits(param_ty)
|
||||
if bits == 0: bits = 32
|
||||
nlen: sizet = string.strlen(arg.arg)
|
||||
# 条目缓冲:name + ':' + 位宽(预留 16 字节,足够 %d 任意取值)+ NUL
|
||||
# Entry buffer: name + ':' + bits (16 bytes reserved, enough for any %d) + NUL
|
||||
ent: str = pool.alloc(nlen + 1 + 16 + 1)
|
||||
if ent is None: continue
|
||||
string.strcpy(ent, arg.arg)
|
||||
|
||||
@@ -15,23 +15,15 @@ import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||
|
||||
|
||||
# ============================================================
|
||||
# TypeGenMixin - 类型映射与解析 Mixin(Generator 继承链入口)
|
||||
#
|
||||
# 方法已全部拆至 TypeGenBase/CDef/CType/Resolve/Err,本文件保留类声明。
|
||||
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
|
||||
# ============================================================
|
||||
# TypeGenMixin - type-mapping/parse mixin entry (@t.NoVTable: static binding, no vtable).
|
||||
# Methods live in TypeGenBase/CDef/CType/Resolve/Err; this file keeps only the class decl.
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 常量(与 HandlesType 保持一致)
|
||||
# ============================================================
|
||||
# Constants (kept in sync with HandlesType).
|
||||
define TRACE_MAX = 32
|
||||
define MAX_CTYPEDEF_DEPTH = 10
|
||||
|
||||
|
||||
# ============================================================
|
||||
# TypeGenMixin - 类型映射/注解解析/编译期常量表/错误上报
|
||||
# ============================================================
|
||||
# TypeGenMixin - type mapping, annotation parsing, compile-time constants, error reporting.
|
||||
novirtual class TypeGenMixin(TypeGenBase.TypeGenBase):
|
||||
"""类型映射与解析 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
|
||||
"""Type mapping and parsing mixin; all methods live in the sub-mixins."""
|
||||
+513
-1304
File diff suppressed because it is too large.
Load diff
+175
-377
File diff suppressed because it is too large.
Load diff
+177
-367
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,2 @@
|
||||
# ============================================================
|
||||
# Gen - 对象化生成器(P1 骨架 + P2 TypeGenMixin)
|
||||
# Generator: 翻译状态容器
|
||||
# ============================================================
|
||||
# Gen - objectified generator (P1 skeleton + P2 TypeGenMixin)
|
||||
# Generator: translation state container
|
||||
+69
-124
@@ -13,50 +13,42 @@ import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
# ============================================================
|
||||
# IncludesScanner - includes 目录递归扫描(Win32 FindFirstFileA/FindNextFileA)
|
||||
# 递归收集 .vp 文件路径与 SHA1,供 Phase1 stub 生成 / Phase2 stub 合并使用
|
||||
# ============================================================
|
||||
# IncludesScanner - recursive scan of the includes directory (Win32 FindFirstFileA/
|
||||
# FindNextFileA); collects .vp paths and SHA1s for Phase1 stub gen / Phase2 merge.
|
||||
|
||||
# 全局 mbuddy 指针
|
||||
# global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# 文件路径最大长度
|
||||
# max file path length
|
||||
define MAX_PATH_LEN = 512
|
||||
# 单次扫描最大文件数
|
||||
# max files per scan
|
||||
define MAX_FILES = 1024
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FileEntry - 文件条目(路径 + SHA1)
|
||||
# ============================================================
|
||||
# FileEntry - file entry (path + SHA1)
|
||||
novirtual class FileEntry:
|
||||
"""扫描到的文件条目"""
|
||||
Path: str # 文件绝对路径
|
||||
Sha1: str # SHA1 前16字符(16字节+null)
|
||||
RelPath: str # 相对于 includes 根目录的路径
|
||||
ModuleName: str # 模块名(如 "ast.parser")
|
||||
IncDir: str # 所属 includes 根目录(多 includes 目录支持)
|
||||
"""A scanned file entry."""
|
||||
Path: str # absolute file path
|
||||
Sha1: str # first 16 SHA1 chars (16 bytes + null)
|
||||
RelPath: str # path relative to the includes root
|
||||
ModuleName: str # module name (e.g. "ast.parser")
|
||||
IncDir: str # owning includes root dir (multi-root support)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ScanResult - 扫描结果
|
||||
# ============================================================
|
||||
# ScanResult - scan result
|
||||
novirtual class ScanResult:
|
||||
"""扫描结果集合"""
|
||||
Entries: FileEntry* # FileEntry 数组
|
||||
"""A collection of scan results."""
|
||||
Entries: FileEntry* # FileEntry array
|
||||
Count: int
|
||||
Capacity: int
|
||||
|
||||
|
||||
# ============================================================
|
||||
# create_scan_result - 创建扫描结果容器
|
||||
# ============================================================
|
||||
# create_scan_result - create the scan result container
|
||||
def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
|
||||
"""创建扫描结果容器,预分配 MAX_FILES 个槽位"""
|
||||
"""Create the scan result container with MAX_FILES preallocated slots."""
|
||||
if pool is None: return None
|
||||
size: sizet = MAX_FILES * FileEntry.__sizeof__()
|
||||
# 使用 stdlib.malloc 避免 mbuddy 池耗尽(与 StubMerger 保持一致)
|
||||
# use stdlib.malloc to avoid exhausting the mbuddy pool (consistent with StubMerger)
|
||||
entries: FileEntry* = stdlib.malloc(size)
|
||||
if entries is None: return None
|
||||
string.memset(entries, 0, size)
|
||||
@@ -70,47 +62,45 @@ def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================
|
||||
# add_file_entry - 向扫描结果添加文件条目
|
||||
# ============================================================
|
||||
# add_file_entry - append a file entry to the scan result
|
||||
def add_file_entry(result: ScanResult*, pool: memhub.MemBuddy*, abs_path: str, rel_path: str, sha1: str, inc_dir: str) -> int:
|
||||
"""添加文件条目到扫描结果,返回 0 成功(inc_dir 为所属 includes 根目录)"""
|
||||
"""Append a file entry to the scan result; inc_dir is the owning includes root."""
|
||||
if result is None or pool is None: return 1
|
||||
if result.Count >= result.Capacity: return 1
|
||||
|
||||
# 计算模块名:rel_path 中 / 替换为 .,去掉 .vp/.py 扩展名(__init__.vp 特判由 Utils 统一处理)
|
||||
# compute module name: '/' -> '.', strip .vp/.py (Utils handles __init__.vp)
|
||||
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
|
||||
if mod_buf is None: return 1
|
||||
|
||||
# 获取条目地址
|
||||
# get the entry address
|
||||
entry_size: sizet = FileEntry.__sizeof__()
|
||||
entry_addr: uint64 = uint64(result.Entries) + result.Count * entry_size
|
||||
entry: FileEntry* = (FileEntry*)(entry_addr)
|
||||
if entry is None: return 1
|
||||
|
||||
# 复制路径字符串
|
||||
# copy the path string
|
||||
abs_len: sizet = string.strlen(abs_path)
|
||||
abs_buf: str = stdlib.malloc(abs_len + 1)
|
||||
if abs_buf is None: return 1
|
||||
string.strcpy(abs_buf, abs_path)
|
||||
entry.Path = abs_buf
|
||||
|
||||
# 复制 SHA1
|
||||
# copy the SHA1
|
||||
sha1_buf: str = stdlib.malloc(17)
|
||||
if sha1_buf is None: return 1
|
||||
string.strcpy(sha1_buf, sha1)
|
||||
entry.Sha1 = sha1_buf
|
||||
|
||||
# 复制相对路径
|
||||
# copy the relative path
|
||||
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
|
||||
if rel_buf is None: return 1
|
||||
string.strcpy(rel_buf, rel_path)
|
||||
entry.RelPath = rel_buf
|
||||
|
||||
# 复制模块名
|
||||
# copy the module name
|
||||
entry.ModuleName = mod_buf
|
||||
|
||||
# 复制所属 includes 根目录(多 includes 目录支持,Phase2 拼路径用)
|
||||
# copy the owning includes root dir (used by Phase2 path joining)
|
||||
if inc_dir is not None:
|
||||
inc_len: sizet = string.strlen(inc_dir)
|
||||
inc_buf: str = stdlib.malloc(inc_len + 1)
|
||||
@@ -122,19 +112,17 @@ def add_file_entry(result: ScanResult*, pool: memhub.MemBuddy*, abs_path: str, r
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# compute_file_sha1 - 读取文件内容并计算 SHA1(CRLF→LF 后计算,与 Projectrans.py 一致)
|
||||
# ============================================================
|
||||
# compute_file_sha1 - read file content and compute SHA1 (CRLF->LF, matching Projectrans.py)
|
||||
def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
|
||||
"""读取文件内容并计算 SHA1 前16字符(CRLF→LF 后计算,与 Projectrans.py 一致)"""
|
||||
"""Read a file, normalize CRLF to LF, and return the first 16 SHA1 hex chars."""
|
||||
if pool is None or file_path is None: return None
|
||||
|
||||
# 读取文件内容(stdlib.malloc 分配,避免 mbuddy 池耗尽);BUF_SIZE 须与翻译侧 SRC_BUF_SIZE(1MB)一致,否则尾部变更不改 SHA1 导致缓存永不失效
|
||||
# read file content; BUF_SIZE must match SRC_BUF_SIZE (1MB) or trailing edits won't change SHA1
|
||||
BUF_SIZE: sizet = 1048576
|
||||
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
|
||||
if buf is None: return None
|
||||
|
||||
# 原地去除 \r(CRLF → LF),与 Projectrans.py 文本模式读取一致
|
||||
# strip \r in place (CRLF -> LF), matching Projectrans.py text-mode reads
|
||||
buf_len: sizet = string.strlen(buf)
|
||||
write_pos: sizet = 0
|
||||
for ch in buf:
|
||||
@@ -142,10 +130,10 @@ def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
|
||||
buf[write_pos] = ch
|
||||
write_pos += 1
|
||||
|
||||
# 添加 null 终止符
|
||||
# add a null terminator
|
||||
buf[write_pos] = 0
|
||||
|
||||
# 计算 SHA1
|
||||
# compute SHA1
|
||||
ctx: rawptr[hashlib.sha1] = hashlib.sha1()
|
||||
if ctx is None:
|
||||
stdlib.free(buf)
|
||||
@@ -158,29 +146,27 @@ def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
|
||||
return None
|
||||
ctx.final(digest)
|
||||
|
||||
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符,stdlib 分配保持原行为)
|
||||
# convert to hex (first 8 bytes = 16 hex chars; stdlib allocation unchanged)
|
||||
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
|
||||
stdlib.free(buf)
|
||||
stdlib.free(digest)
|
||||
return hex_buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# scan_directory_recursive - 递归扫描目录(FindFirstFileA/FindNextFileA 遍历目录树)
|
||||
# ============================================================
|
||||
# scan_directory_recursive - recursively scan a directory (FindFirstFileA/FindNextFileA)
|
||||
def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix: str, result: ScanResult*, inc_dir: str) -> int:
|
||||
"""递归扫描目录,收集 .vp/.py 文件(inc_dir 为所属 includes 根目录)"""
|
||||
"""Recursively collect .vp/.py files under a directory; inc_dir is the includes root."""
|
||||
if pool is None or root_dir is None or result is None: return 1
|
||||
# 子目录递归失败计数(不静默忽略,汇总处 fail-fast 终止)
|
||||
# subdir recursion failure count (not ignored; fail-fast at the summary)
|
||||
subdir_failed: int = 0
|
||||
|
||||
# 构造搜索模式: root_dir/*
|
||||
# build search pattern: root_dir/*
|
||||
root_len: sizet = string.strlen(root_dir)
|
||||
pattern: bytes = stdlib.malloc(root_len + 4)
|
||||
if pattern is None: return 1
|
||||
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
|
||||
|
||||
# find_data 缓冲 +16 余量:TPV 的 WIN32_FIND_DATAA.__sizeof__() 小于 Windows 实际写入,越界写会破坏相邻堆块
|
||||
# find_data buffer +16 slack: TPV's WIN32_FIND_DATAA.__sizeof__() undercounts Windows writes
|
||||
fd_size_s: sizet = win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||
find_data: rawptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(fd_size_s + 16)
|
||||
if find_data is None: return 1
|
||||
@@ -189,9 +175,9 @@ def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix:
|
||||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == win32base.INVALID_HANDLE_VALUE: return 1
|
||||
|
||||
# 遍历所有文件和子目录
|
||||
# iterate all files and subdirectories
|
||||
while True:
|
||||
# 跳过 . 和 ..
|
||||
# skip . and ..
|
||||
fname: str = find_data.cFileName
|
||||
if fname is not None:
|
||||
fname0: char = fname[0]
|
||||
@@ -208,15 +194,15 @@ def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix:
|
||||
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
|
||||
# 检查是否为目录
|
||||
# check whether it is a directory
|
||||
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||
|
||||
# 构造完整路径
|
||||
# build the full path
|
||||
fname_len: sizet = string.strlen(fname)
|
||||
full_path: bytes = Utils.JoinPath(root_dir, fname)
|
||||
if full_path is None: break
|
||||
|
||||
# 构造相对路径
|
||||
# build the relative path
|
||||
prefix_len: sizet = 0
|
||||
if rel_prefix is not None: prefix_len = string.strlen(rel_prefix)
|
||||
rel_path: bytes = None
|
||||
@@ -228,28 +214,28 @@ def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix:
|
||||
if rel_path is None: break
|
||||
|
||||
if is_dir != 0:
|
||||
# 递归扫描子目录
|
||||
# recurse into the subdirectory
|
||||
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0: subdir_failed += 1
|
||||
else:
|
||||
# 检查是否为 .vp 源文件(Viper 源仅 .vp,不再识别 .py)
|
||||
# check for a .vp source file (Viper uses .vp only; no longer .py)
|
||||
is_py: int = 0
|
||||
if Utils.is_vp_at(fname, 0, fname_len) != 0: is_py = 1
|
||||
|
||||
if is_py != 0:
|
||||
# 跳过 __pycache__ 目录下的文件
|
||||
# skip files under __pycache__
|
||||
is_pycache: int = 0
|
||||
if rel_prefix is not None:
|
||||
if string.strcmp(rel_prefix, "__pycache__") == 0: is_pycache = 1
|
||||
|
||||
if is_pycache == 0:
|
||||
# 计算 SHA1
|
||||
# compute SHA1
|
||||
sha1: str = compute_file_sha1(pool, full_path)
|
||||
if sha1 is not None:
|
||||
add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir)
|
||||
VLogger.debug(f"扫描: {rel_path} -> {sha1}", "scan")
|
||||
else: VLogger.warning(f"SHA1 计算失败: {full_path}", "scan")
|
||||
|
||||
# 继续搜索下一个文件
|
||||
# continue to the next file
|
||||
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
|
||||
win32file.FindClose(handle)
|
||||
@@ -258,17 +244,15 @@ def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# scan_includes - 扫描 includes 目录入口(返回所有 .vp 文件路径与 SHA1)
|
||||
# ============================================================
|
||||
# scan_includes - scan entry point: return all .vp file paths and SHA1s
|
||||
def scan_includes(pool: memhub.MemBuddy*, includes_dirs: str) -> ScanResult*:
|
||||
"""扫描 includes 目录(支持空格分隔的多目录),返回所有 .vp/.py 文件的扫描结果"""
|
||||
"""Scan space-separated includes dirs and return all scanned .vp/.py files."""
|
||||
if pool is None or includes_dirs is None: return None
|
||||
|
||||
result: ScanResult* = create_scan_result(pool)
|
||||
if result is None: return None
|
||||
|
||||
# 解析空格分隔的多 includes 目录(如 "../../includes ./includes"),逐目录递归扫描合并
|
||||
# parse space-separated includes dirs, recursively scanning and merging each
|
||||
dirs_total: sizet = string.strlen(includes_dirs)
|
||||
d_i: sizet = 0
|
||||
while d_i < dirs_total:
|
||||
@@ -288,11 +272,9 @@ def scan_includes(pool: memhub.MemBuddy*, includes_dirs: str) -> ScanResult*:
|
||||
return result
|
||||
|
||||
|
||||
# ============================================================
|
||||
# find_entry_by_module - 按模块名查找文件条目
|
||||
# ============================================================
|
||||
# find_entry_by_module - find a file entry by module name
|
||||
def find_entry_by_module(result: ScanResult*, module_name: str) -> FileEntry*:
|
||||
"""按模块名查找文件条目"""
|
||||
"""Find a file entry by module name."""
|
||||
if result is None or module_name is None: return None
|
||||
entry_size: sizet = FileEntry.__sizeof__()
|
||||
for i in range(result.Count):
|
||||
@@ -304,17 +286,9 @@ def find_entry_by_module(result: ScanResult*, module_name: str) -> FileEntry*:
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PathExistsA - 路径存在性检查(GetFileAttributesA)
|
||||
# ============================================================
|
||||
# _PathExistsA - path existence check (GetFileAttributesA)
|
||||
def _PathExistsA(path: str) -> int:
|
||||
"""路径是否存在(1=存在,0=不存在/参数错误)
|
||||
|
||||
用 GetFileAttributesA(无输出缓冲)替代 FindFirstFileA:后者写
|
||||
WIN32_FIND_DATAA 结构,而 TPV 该结构 sizeof 小于 Windows 实际写入
|
||||
(约 592 字节,差距 >270 字节),会越界写坏相邻堆块(BFS 输出槽
|
||||
0x2e 崩溃根因)。GetFileAttributesA 只返回属性值,无越界风险。
|
||||
"""
|
||||
"""Return 1 if the path exists, else 0, using GetFileAttributesA."""
|
||||
if path is None: return 0
|
||||
attr: ULONG = win32file.GetFileAttributesA(path)
|
||||
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
|
||||
@@ -322,24 +296,13 @@ def _PathExistsA(path: str) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _TryResolveModuleFile - 尝试单个候选路径并写输出
|
||||
#
|
||||
# Args:
|
||||
# inc_dir: includes 根目录
|
||||
# rel_seg: 模块名转出的相对路径段(ast/parser)
|
||||
# rel_len: rel_seg 长度
|
||||
# suffix: 候选后缀(".vp" 或 "/__init__.vp")
|
||||
# out_abs_path/out_rel_path/out_inc_dir: 输出槽(t.CPtr 指针)
|
||||
#
|
||||
# Returns:
|
||||
# 1 命中(已写输出),0 未命中
|
||||
# ============================================================
|
||||
# _TryResolveModuleFile - try one candidate path and write outputs (1=hit, 0=miss);
|
||||
# out_abs_path/out_rel_path/out_inc_dir are t.CPtr output slots.
|
||||
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix: str, out_abs_path: t.CPtr, out_rel_path: t.CPtr, out_inc_dir: t.CPtr) -> int:
|
||||
if inc_dir is None or rel_seg is None or suffix is None: return 0
|
||||
inc_len: sizet = string.strlen(inc_dir)
|
||||
suf_len: sizet = string.strlen(suffix)
|
||||
# 候选绝对路径: {inc}/{rel}{suffix}(手写拼接,避免 %. *s 精度修饰符)
|
||||
# candidate absolute path: {inc}/{rel}{suffix} (hand-built to avoid %. *s)
|
||||
cand_len: sizet = inc_len + 1 + rel_len + suf_len
|
||||
cand: bytes = stdlib.malloc(cand_len + 1)
|
||||
if cand is None: return 0
|
||||
@@ -350,7 +313,7 @@ def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix:
|
||||
if _PathExistsA(cand) == 0:
|
||||
stdlib.free(cand)
|
||||
return 0
|
||||
# 相对 includes 根目录的路径: {rel}{suffix}
|
||||
# path relative to the includes root: {rel}{suffix}
|
||||
rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1)
|
||||
if rel_path is None:
|
||||
stdlib.free(cand)
|
||||
@@ -358,8 +321,8 @@ def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix:
|
||||
string.strncpy(rel_path, rel_seg, rel_len)
|
||||
rel_path[rel_len] = '\0'
|
||||
string.strcat(rel_path, suffix)
|
||||
# 所属 includes 根目录深拷贝:先全部分配就绪再统一写槽,
|
||||
# 避免 inc 分配失败时 abs/rel 已转移所有权造成的半写状态
|
||||
# Deep-copy the owning includes root dir; allocate everything before writing
|
||||
# slots to avoid a half-written state when inc allocation fails.
|
||||
inc_buf: bytes = None
|
||||
if out_inc_dir is not None:
|
||||
inc_buf = stdlib.malloc(inc_len + 1)
|
||||
@@ -368,11 +331,8 @@ def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix:
|
||||
stdlib.free(rel_path)
|
||||
return 0
|
||||
string.strcpy(inc_buf, inc_dir)
|
||||
# 写输出槽(stdlib 深拷贝,调用方负责 free)
|
||||
# 槽联合必须用 (sizet*):t.CPtr* 会被折叠成 t.CPtr,
|
||||
# [0] 变成 1 字节访问 → 指针被截断(0x2e 崩溃根因)。CSizeT 成员为
|
||||
# 8 字节,写入用 sizet(ptr) 直接转换;读取用 t\.CVoid(u64, t.CPtr)
|
||||
# 转回指针(t\.CVoid 仅配合 t.CPtr 表示 void* 强转)。
|
||||
# Write output slots (stdlib deep copies; caller frees). Slots must be cast to
|
||||
# (sizet*): t.CPtr* folds to t.CPtr and [0] becomes a 1-byte access.
|
||||
if out_abs_path is not None:
|
||||
abs_slot: sizet* = (sizet*)(out_abs_path)
|
||||
abs_slot[0] = sizet(cand)
|
||||
@@ -387,28 +347,13 @@ def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix:
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ResolveModuleToPath - 模块名→文件路径(按文件系统按需解析,不做全量目录遍历)
|
||||
#
|
||||
# 解析规则(多 includes 目录逐个尝试,模块优先于包):
|
||||
# {inc_dir}/{mod.replace('.','/')}.vp (文件模块)
|
||||
# {inc_dir}/{mod.replace('.','/')}/__init__.vp (包模块)
|
||||
#
|
||||
# Args:
|
||||
# includes_dirs: 空格分隔的多个 includes 根目录
|
||||
# mod_name: 绝对模块名
|
||||
# out_abs_path: 输出绝对路径(stdlib.malloc,调用方负责 free)
|
||||
# out_rel_path: 输出相对 includes 根目录的路径(同上,如 ast/parser.vp)
|
||||
# out_inc_dir: 输出所属 includes 根目录(同上)
|
||||
#
|
||||
# Returns:
|
||||
# 0 找到;1 未找到/参数错误
|
||||
# ============================================================
|
||||
# ResolveModuleToPath - module name -> file path, resolved on demand (no full scan).
|
||||
# Tries each includes dir, preferring file modules over packages. Returns 0 found, 1 not.
|
||||
def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: t.CPtr, out_rel_path: t.CPtr, out_inc_dir: t.CPtr) -> int:
|
||||
if includes_dirs is None or mod_name is None: return 1
|
||||
mn_len: sizet = string.strlen(mod_name)
|
||||
if mn_len == 0: return 1
|
||||
# mod_name → 相对路径段(. → /):ast.parser → ast/parser
|
||||
# mod_name -> relative path segment ('.' -> '/'): ast.parser -> ast/parser
|
||||
rel_seg: bytes = stdlib.malloc(mn_len + 1)
|
||||
if rel_seg is None: return 1
|
||||
rel_len: sizet = 0
|
||||
@@ -433,12 +378,12 @@ def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: t.CPtr,
|
||||
if inc_dir_buf is None: continue
|
||||
string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len)
|
||||
inc_dir_buf[d_len] = '\0'
|
||||
# 候选 1: {inc}/{rel}.vp(文件模块)
|
||||
# candidate 1: {inc}/{rel}.vp (file module)
|
||||
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
|
||||
found = 1
|
||||
stdlib.free(inc_dir_buf)
|
||||
break
|
||||
# 候选 2: {inc}/{rel}/__init__.vp(包模块)
|
||||
# candidate 2: {inc}/{rel}/__init__.vp (package module)
|
||||
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
|
||||
found = 1
|
||||
stdlib.free(inc_dir_buf)
|
||||
|
||||
+388
-814
File diff suppressed because it is too large.
Load diff
+40
-63
@@ -9,38 +9,23 @@ import w32.win32file as w32file
|
||||
import w32.win32base as win32base
|
||||
|
||||
|
||||
# ============================================================
|
||||
# VLogger - 原生日志系统(Windows 控制台彩色输出)
|
||||
# 输出格式: ├ LEVEL: message / ├ LEVEL[category]: message(与 TPC lib/core/VLogger.py 对齐)
|
||||
# ============================================================
|
||||
# VLogger - native logger with colored Windows console output.
|
||||
# Format: ├ LEVEL: message or ├ LEVEL[category]: message (aligned with TPC lib/core/VLogger.py).
|
||||
|
||||
# 错误恢复(REPL/JIT 模式):error 打印后 longjmp 跳回 REPL 的 setjmp 点。
|
||||
# mingw-w64 x86_64:longjmp 原型为 void longjmp(jmp_buf, int);setjmp 宏展开为
|
||||
# _setjmp(buf, NULL)(jmp_buf 实为 _JBTYPE[16],见 Repl.vp 的 _REPL_JMPBUF_SIZE)。
|
||||
# Error recovery (REPL/JIT): after error, longjmp back to the REPL setjmp point.
|
||||
# mingw-w64 x86_64: longjmp is void longjmp(jmp_buf, int); setjmp expands to _setjmp(buf, NULL).
|
||||
extern def longjmp(buf: rawptr, val: int)
|
||||
|
||||
|
||||
# REPL 恢复点指针(由 Repl 在会话开始时注册、会话结束时清除)。
|
||||
# 生命周期 = 整个 REPL 会话:Repl 每轮片段都会重新武装(Repl.run 每轮 _setjmp +
|
||||
# Repl.ReplExecFragment 入口重注册),故它覆盖整条片段流水线(翻译 → 写 .ll →
|
||||
# llc → clang++ → LoadLibrary → GetProcAddress → 调用 → 回显),而不只是翻译期。
|
||||
# 批处理路径从不调用 set_repl_jmpbuf ⇒ 恒为 None ⇒ 所有出口退回 sys.exit(1)。
|
||||
# REPL recovery-point pointer: registered at session start, cleared at session end.
|
||||
# Covers the whole fragment pipeline; batch mode never sets it, so it stays None and exits via sys.exit(1).
|
||||
_repl_jmpbuf: rawptr = None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# repl_abort - TPV 内"诊断后终止"的唯一出口
|
||||
#
|
||||
# 约定:调用方**已打印**诊断(Traceback / VLogger.error / [OOM] 等),本函数只决定
|
||||
# "跳回 REPL 继续"还是"终止进程":
|
||||
# * REPL 会话(_repl_jmpbuf 已注册):fflush 后 longjmp 回 Repl.run 的恢复点,
|
||||
# 进程存活、提示符继续(宽容 Traceback);
|
||||
# * 批处理 / 未武装:sys.exit(1),与改动前的 fail-fast 行为逐字一致。
|
||||
# 收敛点:Logger.error、模块级 error、TypeGenErr.fatal_error、
|
||||
# ImportsGenMixin._report_import_error、CallGenBase/VpiReader 的链表哨兵。
|
||||
# ============================================================
|
||||
# repl_abort - the single "terminate after diagnosis" exit in TPV.
|
||||
# Caller already printed the diagnostic; returns to REPL via longjmp if a recovery point is set, else sys.exit(1).
|
||||
def repl_abort() -> int:
|
||||
"""诊断后的统一出口:已注册恢复点则 longjmp 回 REPL,否则终止进程。"""
|
||||
"""Unified exit after diagnosis: longjmp to the REPL if a recovery point is set, else exit."""
|
||||
if _repl_jmpbuf is not None:
|
||||
stdio.fflush(None)
|
||||
longjmp(_repl_jmpbuf, 1)
|
||||
@@ -49,24 +34,24 @@ def repl_abort() -> int:
|
||||
|
||||
|
||||
def set_repl_jmpbuf(buf: t.CPtr) -> int:
|
||||
"""注册 REPL 的 jmp_buf(会话开始;每片段入口可幂等重注册)。"""
|
||||
"""Register the REPL jmp_buf; idempotent at each fragment entry."""
|
||||
global _repl_jmpbuf
|
||||
_repl_jmpbuf = buf
|
||||
return 0
|
||||
|
||||
|
||||
def clear_repl_jmpbuf() -> int:
|
||||
"""清除已注册的 jmp_buf(REPL 会话结束时调用;中途不清)。"""
|
||||
"""Clear the registered jmp_buf at REPL session end."""
|
||||
global _repl_jmpbuf
|
||||
_repl_jmpbuf = None
|
||||
return 0
|
||||
|
||||
# 日志级别
|
||||
# Log levels
|
||||
enum LogLevel:
|
||||
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
|
||||
|
||||
|
||||
# Win32 控制台前景色属性
|
||||
# Win32 console foreground color attributes
|
||||
define FOREGROUND_BLUE = 0x0001
|
||||
define FOREGROUND_GREEN = 0x0002
|
||||
define FOREGROUND_RED = 0x0004
|
||||
@@ -76,7 +61,7 @@ define FOREGROUND_CYAN = FOREGROUND_BLUE | FOREGROUND_GREEN
|
||||
define FOREGROUND_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN
|
||||
define FOREGROUND_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE
|
||||
|
||||
# Win32 控制台背景色属性
|
||||
# Win32 console background color attributes
|
||||
define BACKGROUND_RED = 0x0040
|
||||
define BACKGROUND_GREEN = 0x0020
|
||||
define BACKGROUND_BLUE = 0x0010
|
||||
@@ -110,19 +95,17 @@ novirtual class Logger:
|
||||
if self._use_color: return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _log - 核心日志输出(分段着色:前缀符号 ├ / LEVEL 标签 / 消息内容)
|
||||
# 格式: ├ LEVEL: msg 或 ├ LEVEL[category]: msg
|
||||
# ============================================================
|
||||
# _log - core log output with segmented coloring (prefix symbol / level tag / message).
|
||||
# Format: ├ LEVEL: msg or ├ LEVEL[category]: msg.
|
||||
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str, sym_color: WORD, level_color: WORD, category: str = "") -> int:
|
||||
# 前缀符号着色
|
||||
# Color the prefix symbol
|
||||
self._set_color(sym_color)
|
||||
stdio.printf("%s ", prefix_sym)
|
||||
# LEVEL 标签着色
|
||||
# Color the level tag
|
||||
self._set_color(level_color)
|
||||
stdio.printf("%s", level_str)
|
||||
self._reset_color()
|
||||
# category 可选
|
||||
# Optional category
|
||||
if category is not None and category[0] != 0: stdio.printf(" [%s]", category)
|
||||
stdio.printf(": %s\n", msg)
|
||||
return 0
|
||||
@@ -139,11 +122,11 @@ novirtual class Logger:
|
||||
|
||||
def error(self, msg: str, category: str = "") -> int:
|
||||
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, FOREGROUND_RED | FOREGROUND_INTENSITY, category)
|
||||
# JIT/REPL 模式:诊断已打印,经统一出口跳回 REPL 循环(未武装时该出口仍终止进程)
|
||||
# JIT/REPL: diagnostic already printed; jump back to the REPL loop via the unified exit.
|
||||
if self.jit_flag:
|
||||
repl_abort()
|
||||
return ret
|
||||
# 批处理模式:错误立即终止进程(行为与改动前逐字一致)
|
||||
# Batch mode: terminate immediately on error.
|
||||
sys.exit(1)
|
||||
return ret
|
||||
|
||||
@@ -151,7 +134,7 @@ novirtual class Logger:
|
||||
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||||
|
||||
def critical(self, msg: str, category: str = "") -> int:
|
||||
# CRITICAL 使用 ╠ 前缀(红底白字标签,对齐 TPC critical 风格)
|
||||
# CRITICAL uses the ╠ prefix (red-background white label, aligned with TPC style).
|
||||
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY, category)
|
||||
|
||||
def set_level(self, level: int) -> int:
|
||||
@@ -159,23 +142,21 @@ novirtual class Logger:
|
||||
return 0
|
||||
|
||||
def set_jit_flag(self, v: bool) -> int:
|
||||
"""设置 JIT/REPL 模式标志:True 时 error 改为 longjmp 回 REPL 循环。"""
|
||||
"""Set the JIT/REPL flag: True makes error longjmp back to the REPL loop."""
|
||||
self.jit_flag = v
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 高级日志方法
|
||||
# ============================================================
|
||||
# Advanced log methods.
|
||||
|
||||
def banner(self, msg: str) -> int:
|
||||
"""输出分节标题(蓝色高亮,与 TPC banner 风格对齐)。"""
|
||||
"""Print a blue-highlighted section banner."""
|
||||
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n=== %s ===\n\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||
"""格式化编译错误输出(红底白字标题 + 位置 + 错误信息)。"""
|
||||
"""Format a compile error with a red-background header, location and message."""
|
||||
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
|
||||
stdio.printf(" 编译错误 ")
|
||||
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
|
||||
@@ -188,7 +169,7 @@ novirtual class Logger:
|
||||
return 0
|
||||
|
||||
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||
"""格式化编译警告输出(黄底黑字标题 + 位置 + 警告信息)。"""
|
||||
"""Format a compile warning with a yellow background, location and message."""
|
||||
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||
stdio.printf(" 编译警告 ")
|
||||
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||
@@ -201,18 +182,18 @@ novirtual class Logger:
|
||||
return 0
|
||||
|
||||
|
||||
# 全局 mbuddy 指针(由 lib.InitLib 注入)
|
||||
# Global mbuddy pointer (injected by lib.InitLib).
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# 全局 logger 指针
|
||||
# Global logger pointer
|
||||
logger: Logger*
|
||||
log: Logger*
|
||||
|
||||
def init_logger():
|
||||
"""初始化全局 logger 实例。"""
|
||||
"""Initialize the global logger instance."""
|
||||
global logger, log
|
||||
raw: t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
|
||||
# if raw is None: return None 存疑,如何去管理内存?
|
||||
# if raw is None: return None (memory management still unclear)
|
||||
logger = raw
|
||||
logger.__before_init__()
|
||||
logger.__init__(LogLevel.INFO)
|
||||
@@ -220,48 +201,44 @@ def init_logger():
|
||||
# if fmtbuffer is None: return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# --nowarn-register 开关:抑制 Phase A 依赖类 WARN 输出(仅抑制输出,不影响内部逻辑)
|
||||
# ============================================================
|
||||
# --nowarn-register switch: suppresses Phase A dependency WARN output (output only).
|
||||
_nowarn_register: int = 0
|
||||
|
||||
# ============================================================
|
||||
# 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐,无需先 get_logger())
|
||||
# ============================================================
|
||||
# Module-level convenience functions (aligned with TPC lib.core.VLogger; no get_logger() needed).
|
||||
|
||||
def info(msg: str, category: str = "") -> int:
|
||||
"""输出 INFO 级别日志。"""
|
||||
"""Log at INFO level."""
|
||||
if log is not None: return log.info(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def warning(msg: str, category: str = "") -> int:
|
||||
"""输出 WARN 级别日志。"""
|
||||
"""Log at WARN level."""
|
||||
if log is not None: return log.warning(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def error(msg: str, category: str = "") -> int:
|
||||
"""输出 ERROR 级别日志:批处理模式终止进程;JIT/REPL 模式 longjmp 回 REPL。"""
|
||||
"""Log at ERROR level: exit in batch mode, longjmp to REPL under JIT/REPL."""
|
||||
if log is not None: return log.error(msg, category)
|
||||
# logger 不可用时也走统一出口(REPL 已注册恢复点时仍可恢复)
|
||||
# When the logger is unavailable, still use the unified exit (REPL can recover if registered)
|
||||
repl_abort()
|
||||
return 0
|
||||
|
||||
|
||||
def success(msg: str, category: str = "") -> int:
|
||||
"""输出 SUCCESS 级别日志。"""
|
||||
"""Log at SUCCESS level."""
|
||||
if log is not None: return log.success(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def critical(msg: str, category: str = "") -> int:
|
||||
"""输出 CRITICAL 级别日志(致命错误,红底白字)。"""
|
||||
"""Log at CRITICAL level (fatal, red background)."""
|
||||
if log is not None: return log.critical(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def debug(msg: str, category: str = "") -> int:
|
||||
"""输出 DEBUG 级别日志。"""
|
||||
"""Log at DEBUG level."""
|
||||
if log is not None: return log.debug(msg, category)
|
||||
return 0
|
||||
@@ -1,3 +1 @@
|
||||
# ============================================================
|
||||
# core 包入口
|
||||
# ============================================================
|
||||
# core package entry
|
||||
+102
-187
@@ -1,35 +1,4 @@
|
||||
"""ViperBuild —— `viperbuild` 内建库的编译器侧实现(`--src` Pass 0 配置发现)
|
||||
|
||||
定位:
|
||||
`viperbuild` 是编译器内建的**编译期宏库**(与 `t`/`c`/`ctraits` 同级),
|
||||
不产生任何运行时代码。编译器在入口文件的**依赖闭包**内识别
|
||||
`import viperbuild as vb` / `from viperbuild import Project`,并据此提取
|
||||
构建配置(源目录 / 构建根目录 / 编译器 / 链接器 / 目标三元组等)。
|
||||
|
||||
本模块只负责「配置面」:
|
||||
① BFS 展开入口文件的依赖闭包(工程本地模块;解析不到的模块视为 stdlib 跳过);
|
||||
② 解析每个文件顶层语句,提取 `vb.Project(...)` / `proj.<字段>=...` /
|
||||
`proj.config(...)` / `proj.build(...)`;
|
||||
③ 常量求值:字符串字面量、名字引用(`define`/模块级常量)、`+` 拼接、
|
||||
`list[str]` 字面量与 `*展开`;
|
||||
④ 填充默认值并写回 `Config` 全局字段;
|
||||
⑤ 全程 fail-fast:没找到 / 找到但坏,一律报错终止,绝不静默兜底。
|
||||
|
||||
约束(与 v5 设计一致):
|
||||
· 一个依赖闭包内**只允许一个** Project 对象,出现第二个即 fatal。
|
||||
· Project 是宏类型,**禁止读取**(当右值用即 fatal)。
|
||||
· `source` 缺省 = 入口文件所在目录;显式指定时入口文件必须位于其下。
|
||||
· `buildroot` 缺省 = `source + "/.tpv_build"`。
|
||||
· `includes` 缺省 = 从 exe 所在目录向上 0..3 层探测 `includes` 目录,命中即用。
|
||||
· `build()` 显式 triple/datalayout 与 `selfmachine=True` 互斥(同时给 fatal);
|
||||
二者都未给时等价 `selfmachine=True`。
|
||||
|
||||
内存纪律:
|
||||
· AST / lexer / token 等瞬时数据从主 MemBuddy 池分配,**每文件 `reset_to(mark)`**;
|
||||
· BFS 工作集(worklist / visited / 模块名 / 常量表)从本模块私有 scratch 池分配,
|
||||
不受 `reset_to` 影响;
|
||||
· 最终写回 `Config` 的字符串一律 `stdlib.malloc` 深拷贝,跨池存活。
|
||||
"""
|
||||
"""Compiler-side implementation of the `viperbuild` built-in macro library (--src Pass 0)."""
|
||||
import t, c
|
||||
from stdint import *
|
||||
from w32.win32base import *
|
||||
@@ -45,46 +14,42 @@ import lib.Projectrans.Config as Config
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
|
||||
# kernel32!GetEnvironmentVariableA —— 本模块自声明(与 includes/traceback.vp 同范式;
|
||||
# win32base.vp 未声明它,kernel32 反正会链上),仅用于 selfmachine 运行时探测本机架构。
|
||||
# DWORD GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize);
|
||||
# kernel32!GetEnvironmentVariableA (DWORD(LPCSTR, LPSTR, DWORD)) - declared here, same as includes/traceback.vp;
|
||||
# win32base.vp omits it but kernel32 is linked anyway; only for selfmachine host detection.
|
||||
extern def GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: CHARPTR, nSize: DWORD) -> DWORD
|
||||
|
||||
# 路径/源码缓冲上限(与 main.vp 的 SRC_BUF_SIZE 保持一致口径)
|
||||
# Path/source buffer caps (same convention as main.vp's SRC_BUF_SIZE)
|
||||
define VB_PATH_CAP = 1024
|
||||
define VB_SRC_CAP = 1048576
|
||||
# scratch 池大小(BFS 工作集 + 常量表;不参与主池 reset)
|
||||
# Scratch pool size (BFS working set + constant tables; not part of main-pool reset)
|
||||
define VB_SCRATCH = 8388608
|
||||
# visited 上限(防御成环,闭包规模远超即 fatal)
|
||||
# Visited cap (cycle defense; fatal if the closure greatly exceeds it)
|
||||
define VB_MAX_VISIT = 4096
|
||||
# 列表常量的元素分隔符:换行符的**字符码**(源码路径 / 编译 flag 均不含换行)。
|
||||
# 注意必须存字符码再 char() 转换——Viper 中 'c' 字面量是**单字符字符串**(i8*),
|
||||
# 直接 define 成 '\n' 会得到 i8*,赋给 char 槽位触发"指针隐式转整数已禁止"。
|
||||
# Element separator for list constants: the char code of newline (source paths/flags never contain newlines).
|
||||
# Must store the code then char(): in Viper 'c' literals are single-char strings (i8*), not assignable to a char slot.
|
||||
define VB_SEP_CODE = 10
|
||||
|
||||
# ============================================================
|
||||
# 模块状态
|
||||
# ============================================================
|
||||
# Module state
|
||||
_scratch: memhub.MemBuddy* = None
|
||||
_entry_dir: str = None # 入口文件所在目录(source 缺省值;裸文件名时规整成 ".")
|
||||
_search_dirs: str = None # 闭包搜索根(空格分隔):入口目录 优先,其次当前目录 "."
|
||||
_work: list[str]* = None # BFS 待处理文件路径
|
||||
_visited: list[str]* = None # 已处理文件路径
|
||||
_pending: list[str]* = None # 单文件收集到的 import 模块名(BFS 待解析)
|
||||
_proj_vars: list[str]* = None # 绑定了 vb.Project(...) 实例的变量名
|
||||
_alias: str = None # viperbuild 的本地别名(import viperbuild as vb → "vb")
|
||||
_ctor_name: str = None # from viperbuild import Project as P → "P"
|
||||
_entry_dir: str = None # entry file dir (default source; normalized to "." for bare filenames)
|
||||
_search_dirs: str = None # closure search roots (space-separated): entry dir first, then "."
|
||||
_work: list[str]* = None # BFS pending file paths
|
||||
_visited: list[str]* = None # processed file paths
|
||||
_pending: list[str]* = None # import module names collected in one file (pending BFS)
|
||||
_proj_vars: list[str]* = None # variable names bound to a vb.Project(...) instance
|
||||
_alias: str = None # local alias of viperbuild (import viperbuild as vb -> "vb")
|
||||
_ctor_name: str = None # from viperbuild import Project as P -> "P"
|
||||
|
||||
# 单文件常量表(仅在该文件扫描期间有效,池分配)
|
||||
# Per-file constant tables (valid only while scanning that file; pool-allocated)
|
||||
_f_str_names: list[str]* = None
|
||||
_f_str_vals: list[str]* = None
|
||||
_f_list_names: list[str]* = None
|
||||
_f_list_vals: list[str]* = None
|
||||
|
||||
# 常量求值输出槽
|
||||
# Constant-eval output slot
|
||||
_g_out_str: str = None
|
||||
|
||||
# 提取出的配置(字符串一律 stdlib.malloc)
|
||||
# Extracted config (all strings stdlib.malloc)
|
||||
_c_name: str = None
|
||||
_c_version: str = None
|
||||
_c_source: str = None
|
||||
@@ -101,18 +66,16 @@ _c_selfmachine: int = 0
|
||||
_c_has_project: int = 0
|
||||
_c_has_build: int = 0
|
||||
|
||||
# 闭包内是否出现过 `import viperbuild` / `from viperbuild import ...`(仅用于诊断区分)
|
||||
# Whether `import viperbuild` / `from viperbuild import ...` appeared in the closure (diagnostics only)
|
||||
_saw_viperbuild: int = 0
|
||||
|
||||
# 是否已成功应用(供 main.vp 判断用 viperbuild 的 linker.output)
|
||||
# Whether applied successfully (for main.vp to decide viperbuild's linker.output)
|
||||
_applied: int = 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 基础工具
|
||||
# ============================================================
|
||||
# Basic helpers
|
||||
def _DupStd(s: str) -> str:
|
||||
"""stdlib.malloc 深拷贝(跨 reset_to 存活)"""
|
||||
"""Deep-copy a string with stdlib.malloc so it survives reset_to."""
|
||||
if s is None: return None
|
||||
n: sizet = string.strlen(s)
|
||||
buf: str = stdlib.malloc(n + 1)
|
||||
@@ -122,7 +85,7 @@ def _DupStd(s: str) -> str:
|
||||
|
||||
|
||||
def _DupScratch(s: str) -> str:
|
||||
"""scratch 池深拷贝(不受主池 reset_to 影响)"""
|
||||
"""Deep-copy a string into the scratch pool, unaffected by main-pool reset_to."""
|
||||
if s is None or _scratch is None: return None
|
||||
n: sizet = string.strlen(s)
|
||||
buf: str = _scratch.alloc(n + 1)
|
||||
@@ -132,7 +95,7 @@ def _DupScratch(s: str) -> str:
|
||||
|
||||
|
||||
def _Join2(a: str, b: str) -> str:
|
||||
"""a + "/" + b(a 为空返回 b)"""
|
||||
"""Join a and b with a slash; returns b when a is empty."""
|
||||
if a is None: return b
|
||||
if b is None: return a
|
||||
al: sizet = string.strlen(a)
|
||||
@@ -147,7 +110,7 @@ def _Join2(a: str, b: str) -> str:
|
||||
|
||||
|
||||
def _DirOf(path: str) -> str:
|
||||
"""返回 path 的目录(不含结尾分隔符);无分隔符返回 stdlib 分配的 """""
|
||||
"""Return the directory part of path (no trailing separator); empty string if none."""
|
||||
if path is None: return None
|
||||
n: sizet = string.strlen(path)
|
||||
i: sizet = n
|
||||
@@ -164,7 +127,7 @@ def _DirOf(path: str) -> str:
|
||||
|
||||
|
||||
def _BaseNameOf(path: str) -> str:
|
||||
"""返回 path 的文件名(含扩展名)"""
|
||||
"""Return the file name of path, including its extension."""
|
||||
if path is None: return None
|
||||
n: sizet = string.strlen(path)
|
||||
start: sizet = 0
|
||||
@@ -177,7 +140,7 @@ def _BaseNameOf(path: str) -> str:
|
||||
|
||||
|
||||
def _IsDir(path: str) -> int:
|
||||
"""路径是否为已存在目录(1=是)"""
|
||||
"""Whether path is an existing directory; 1=yes."""
|
||||
if path is None: return 0
|
||||
attr: ULONG = win32file.GetFileAttributesA(path)
|
||||
if attr == 0xFFFFFFFF: return 0
|
||||
@@ -186,12 +149,7 @@ def _IsDir(path: str) -> int:
|
||||
|
||||
|
||||
def _HasPrefix(s: str, prefix: str) -> int:
|
||||
"""s 是否以 prefix 开头(1=是;prefix 为 None/空/"." 视为真)
|
||||
|
||||
两侧各自剥掉一个前导 "./",避免 `--src App/main.vp` 与 `proj.source="./App"`
|
||||
因写法差异被判成"入口不在 source 之下";prefix 为 "." 时(入口是裸文件名,
|
||||
source 规整成当前目录)恒真。
|
||||
"""
|
||||
"""Whether s starts with prefix; 1=yes (None/empty/"." counts as true)."""
|
||||
if s is None: return 0
|
||||
if prefix is None: return 1
|
||||
if string.strcmp(prefix, ".") == 0: return 1
|
||||
@@ -205,11 +163,9 @@ def _HasPrefix(s: str, prefix: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 常量表
|
||||
# ============================================================
|
||||
# Constant tables
|
||||
def _FindInList(names: list[str]*, name: str) -> int:
|
||||
"""在 names 中线性查找 name,返回下标;未找到返回 -1"""
|
||||
"""Linear-search names for name; return its index, or -1 if not found."""
|
||||
if names is None or name is None: return -1
|
||||
n: sizet = names.__len__()
|
||||
i: sizet = 0
|
||||
@@ -222,7 +178,7 @@ def _FindInList(names: list[str]*, name: str) -> int:
|
||||
|
||||
|
||||
def _LookupStrConst(name: str) -> int:
|
||||
"""名字 → 字符串常量(写 _g_out_str);0=失败"""
|
||||
"""Resolve a name to a string constant in _g_out_str; 0=failure."""
|
||||
global _g_out_str
|
||||
idx: int = _FindInList(_f_str_names, name)
|
||||
if idx < 0: return 0
|
||||
@@ -233,7 +189,7 @@ def _LookupStrConst(name: str) -> int:
|
||||
|
||||
|
||||
def _LookupListConst(name: str, out: list[str]*) -> int:
|
||||
"""名字 → 列表常量(按 VB_SEP 拆分后写入 out);0=失败"""
|
||||
"""Resolve a name to a list constant, split into out; 0=failure."""
|
||||
idx: int = _FindInList(_f_list_names, name)
|
||||
if idx < 0: return 0
|
||||
v: str = _f_list_vals.get(sizet(idx))
|
||||
@@ -242,7 +198,7 @@ def _LookupListConst(name: str, out: list[str]*) -> int:
|
||||
|
||||
|
||||
def _SplitInto(out: list[str]*, joined: str) -> int:
|
||||
"""把 VB_SEP_CODE 连接的串拆成元素写入 out(空串表示空列表)"""
|
||||
"""Split a VB_SEP_CODE-joined string into out; empty string means empty list."""
|
||||
if out is None or joined is None: return 0
|
||||
n: sizet = string.strlen(joined)
|
||||
if n == 0: return 1
|
||||
@@ -262,11 +218,11 @@ def _SplitInto(out: list[str]*, joined: str) -> int:
|
||||
|
||||
|
||||
def _JoinSep(lst: list[str]*) -> str:
|
||||
"""list[str] → VB_SEP_CODE 连接串(池分配;空列表返回 None)"""
|
||||
"""Join a list[str] with VB_SEP_CODE (pool-allocated); None for an empty list."""
|
||||
if lst is None: return None
|
||||
n: sizet = lst.__len__()
|
||||
if n == 0: return None
|
||||
# 先测总长
|
||||
# Measure total length first
|
||||
total: sizet = 0
|
||||
i: sizet = 0
|
||||
while i < n:
|
||||
@@ -293,7 +249,7 @@ def _JoinSep(lst: list[str]*) -> str:
|
||||
|
||||
|
||||
def _JoinSpace(lst: list[str]*) -> str:
|
||||
"""list[str] → 空格连接串(stdlib 分配;空列表返回 None)"""
|
||||
"""Join a list[str] with spaces (stdlib-allocated); None for an empty list."""
|
||||
if lst is None: return None
|
||||
n: sizet = lst.__len__()
|
||||
if n == 0: return None
|
||||
@@ -323,7 +279,7 @@ def _JoinSpace(lst: list[str]*) -> str:
|
||||
|
||||
|
||||
def _FirstSpaceTok(joined: str) -> str:
|
||||
"""取空格分隔串的首个 token(stdlib 分配);空串/None 返回 None"""
|
||||
"""Return the first space-separated token (stdlib-allocated); None if empty."""
|
||||
if joined is None: return None
|
||||
n: sizet = string.strlen(joined)
|
||||
if n == 0: return None
|
||||
@@ -341,15 +297,9 @@ def _FirstSpaceTok(joined: str) -> str:
|
||||
return seg
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 常量表达式求值
|
||||
# ============================================================
|
||||
# Constant expression evaluation
|
||||
def _EvalStr(node: ast.AST*) -> int:
|
||||
"""字符串常量表达式求值(结果写 _g_out_str);0=失败
|
||||
|
||||
支持:字符串字面量、名字引用(同文件先定义的 str 常量 / define 常量)、
|
||||
`+` 拼接(左/右递归)。
|
||||
"""
|
||||
"""Evaluate a string constant expression into _g_out_str; 0=failure."""
|
||||
global _g_out_str
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
@@ -375,7 +325,7 @@ def _EvalStr(node: ast.AST*) -> int:
|
||||
|
||||
|
||||
def _ConcatStd(a: str, b: str) -> str:
|
||||
"""字符串拼接(stdlib 分配)"""
|
||||
"""Concatenate two strings with stdlib allocation."""
|
||||
if a is None: return b
|
||||
if b is None: return a
|
||||
al: sizet = string.strlen(a)
|
||||
@@ -388,11 +338,7 @@ def _ConcatStd(a: str, b: str) -> str:
|
||||
|
||||
|
||||
def _EvalList(node: ast.AST*, out: list[str]*) -> int:
|
||||
"""list[str] 常量表达式求值(元素写入 out);0=失败
|
||||
|
||||
支持:list 字面量(元素为 str 常量表达式)、`*名字` 展开(引用 list 常量)、
|
||||
`+` 拼接(左/右递归)。
|
||||
"""
|
||||
"""Evaluate a list[str] constant expression into out; 0=failure."""
|
||||
if node is None or out is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
@@ -426,7 +372,7 @@ def _EvalList(node: ast.AST*, out: list[str]*) -> int:
|
||||
|
||||
|
||||
def _EvalBool(node: ast.AST*) -> int:
|
||||
"""布尔常量求值;-1=非布尔常量"""
|
||||
"""Evaluate a bool constant; -1 means it is not a bool constant."""
|
||||
if node is None: return -1
|
||||
if node.kind() != ast.ASTKind.Constant: return -1
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
@@ -435,11 +381,9 @@ def _EvalBool(node: ast.AST*) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 宏引用检测(禁止读取)
|
||||
# ============================================================
|
||||
# Macro-reference detection (reading is forbidden)
|
||||
def _IsProjRef(node: ast.AST*) -> int:
|
||||
"""节点是否直接引用 Project 宏对象(Name 或 Attribute 的 base);1=是"""
|
||||
"""Whether a node directly references a Project macro object; 1=yes."""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
@@ -454,11 +398,9 @@ def _IsProjRef(node: ast.AST*) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 单文件扫描
|
||||
# ============================================================
|
||||
# Single-file scan
|
||||
def _ResetFileTables(pool: memhub.MemBuddy*) -> int:
|
||||
"""重建单文件常量表(池分配)"""
|
||||
"""Rebuild the per-file constant tables (pool-allocated)."""
|
||||
global _f_str_names, _f_str_vals, _f_list_names, _f_list_vals
|
||||
_f_str_names = list[str](pool, 8)
|
||||
_f_str_vals = list[str](pool, 8)
|
||||
@@ -470,14 +412,14 @@ def _ResetFileTables(pool: memhub.MemBuddy*) -> int:
|
||||
|
||||
|
||||
def _AddPending(mod_name: str) -> None:
|
||||
"""把待解析模块名记入 BFS 队列(scratch 池深拷贝)"""
|
||||
"""Append a module name to the BFS queue (deep-copied into the scratch pool)."""
|
||||
if mod_name is None or _pending is None: return
|
||||
if string.strlen(mod_name) == 0: return
|
||||
_pending.append(_DupScratch(mod_name))
|
||||
|
||||
|
||||
def _HandleImport(stmt: ast.Import*) -> int:
|
||||
"""处理 `import X [as Y]`:识别 viperbuild,其余模块登记入 BFS 队列"""
|
||||
"""Handle `import X [as Y]`: detect viperbuild, queue other modules for BFS."""
|
||||
global _alias, _saw_viperbuild
|
||||
if stmt is None or stmt.names is None: return 0
|
||||
n: sizet = stmt.names.__len__()
|
||||
@@ -495,11 +437,11 @@ def _HandleImport(stmt: ast.Import*) -> int:
|
||||
|
||||
|
||||
def _HandleImportFrom(stmt: ast.ImportFrom*) -> int:
|
||||
"""处理 `from X import Y`:识别 `from viperbuild import Project`,其余登记 BFS"""
|
||||
"""Handle `from X import Y`: detect viperbuild's Project, queue other modules."""
|
||||
global _ctor_name, _saw_viperbuild
|
||||
if stmt is None: return 0
|
||||
if stmt.level != 0:
|
||||
# 相对导入不参与工程本地闭包展开(无包上下文,跳过)
|
||||
# Relative imports are not part of the local closure (no package context: skip)
|
||||
return 0
|
||||
if stmt.module is None: return 0
|
||||
if string.strcmp(stmt.module, "viperbuild") != 0:
|
||||
@@ -520,7 +462,7 @@ def _HandleImportFrom(stmt: ast.ImportFrom*) -> int:
|
||||
|
||||
|
||||
def _IsProjectCtor(vnode: ast.AST*) -> int:
|
||||
"""判断表达式是否为 `vb.Project(...)` / `Project(...)`;1=是"""
|
||||
"""Whether an expression is a vb.Project(...) or Project(...) call; 1=yes."""
|
||||
if vnode is None: return 0
|
||||
if vnode.kind() != ast.ASTKind.Call: return 0
|
||||
cl: ast.Call* = ast.Call*(vnode)
|
||||
@@ -543,10 +485,7 @@ def _IsProjectCtor(vnode: ast.AST*) -> int:
|
||||
|
||||
|
||||
def _ApplyCtorArgs(cl: ast.Call*) -> int:
|
||||
"""`vb.Project("Test", "1.0.0")` / `vb.Project(name=, version=)` → _c_name / _c_version
|
||||
|
||||
位置参数与关键字参数都支持(位置在前:args[0]=name, args[1]=version)。
|
||||
"""
|
||||
"""Extract name/version from Project(...) args into _c_name / _c_version."""
|
||||
global _c_name, _c_version
|
||||
if cl is None: return 0
|
||||
if cl.args is not None:
|
||||
@@ -588,11 +527,11 @@ def _ApplyCtorArgs(cl: ast.Call*) -> int:
|
||||
|
||||
|
||||
def _SetField(fname: str, vnode: ast.AST*) -> int:
|
||||
"""`proj.<fname> = <vnode>` / `config(<fname>=<vnode>)`;0=成功"""
|
||||
"""Apply a proj.<fname> assignment or config(<fname>=...) keyword; 0=success."""
|
||||
global _c_source, _c_buildroot, _c_includes, _c_compiler, _c_linker
|
||||
global _c_static_libs, _c_dynamic_libs, _c_output, _c_name, _c_version
|
||||
if fname is None or vnode is None: return 1
|
||||
# ---- 字符串字段 ----
|
||||
# ---- String fields ----
|
||||
if string.strcmp(fname, "source") == 0:
|
||||
if _EvalStr(vnode) == 0:
|
||||
VLogger.error("viperbuild: source 必须是字符串常量表达式", "viperbuild")
|
||||
@@ -623,7 +562,7 @@ def _SetField(fname: str, vnode: ast.AST*) -> int:
|
||||
return 1
|
||||
_c_version = _DupStd(_g_out_str)
|
||||
return 0
|
||||
# ---- 列表字段 ----
|
||||
# ---- List fields ----
|
||||
if string.strcmp(fname, "includes") == 0: return _SetListField(0, vnode)
|
||||
if string.strcmp(fname, "compiler") == 0: return _SetListField(1, vnode)
|
||||
if string.strcmp(fname, "linker") == 0: return _SetListField(2, vnode)
|
||||
@@ -634,7 +573,7 @@ def _SetField(fname: str, vnode: ast.AST*) -> int:
|
||||
|
||||
|
||||
def _SetListField(slot: int, vnode: ast.AST*) -> int:
|
||||
"""列表字段赋值(slot: 0=includes 1=compiler 2=linker 3=static_libs 4=dynamic_libs)"""
|
||||
"""Assign a list field (0=includes, 1=compiler, 2=linker, 3=static_libs, 4=dynamic_libs)."""
|
||||
global _c_includes, _c_compiler, _c_linker, _c_static_libs, _c_dynamic_libs
|
||||
if _scratch is None: return 1
|
||||
tmp: list[str]* = list[str](_scratch, 8)
|
||||
@@ -652,7 +591,7 @@ def _SetListField(slot: int, vnode: ast.AST*) -> int:
|
||||
|
||||
|
||||
def _HandleAssign(stmt: ast.Assign*) -> int:
|
||||
"""处理赋值语句:常量定义 / `proj = vb.Project(...)` / `proj.<字段> = ...`"""
|
||||
"""Handle an assignment: constant, Project instance, or proj field."""
|
||||
if stmt is None or stmt.targets is None or stmt.value is None: return 0
|
||||
if stmt.targets.__len__() != 1: return 0
|
||||
tgt: ast.AST* = stmt.targets.get(0)
|
||||
@@ -664,21 +603,21 @@ def _HandleAssign(stmt: ast.Assign*) -> int:
|
||||
|
||||
|
||||
def _HandleAnnAssign(stmt: ast.AnnAssign*) -> int:
|
||||
"""处理注解赋值(含 `define NAME = <字面量>` 脱糖出的 AnnAssign)"""
|
||||
"""Handle an annotated assignment (including define-desugared AnnAssign)."""
|
||||
if stmt is None or stmt.target is None or stmt.value is None: return 0
|
||||
if stmt.target.kind() != ast.ASTKind.Name: return 0
|
||||
return _HandleNameBind(ast.Name*(stmt.target), stmt.value)
|
||||
|
||||
|
||||
def _HandleNameBind(tgt: ast.Name*, vnode: ast.AST*) -> int:
|
||||
"""`NAME = <value>`:Project 实例绑定 / 字符串常量 / 列表常量"""
|
||||
"""Handle NAME = value: Project binding, string constant, or list constant."""
|
||||
global _c_has_project
|
||||
if tgt is None or vnode is None or tgt.id is None: return 0
|
||||
# 右侧读取 Project 宏对象 ⇒ fatal(禁止读取)
|
||||
# Reading a Project macro object on the RHS is fatal (reading forbidden)
|
||||
if _IsProjRef(vnode) != 0:
|
||||
VLogger.error(f"viperbuild: 禁止读取 Project 宏对象({tgt.id} = proj ...)", "viperbuild")
|
||||
return 1
|
||||
# Project 实例绑定
|
||||
# Project instance binding
|
||||
if _IsProjectCtor(vnode) != 0:
|
||||
if _c_has_project != 0:
|
||||
VLogger.error("viperbuild: 一个依赖闭包内只允许一个 Project 对象", "viperbuild")
|
||||
@@ -687,12 +626,12 @@ def _HandleNameBind(tgt: ast.Name*, vnode: ast.AST*) -> int:
|
||||
_proj_vars.append(_DupScratch(tgt.id))
|
||||
cl: ast.Call* = ast.Call*(vnode)
|
||||
return _ApplyCtorArgs(cl)
|
||||
# 字符串常量
|
||||
# String constant
|
||||
if _EvalStr(vnode) != 0:
|
||||
_f_str_names.append(tgt.id)
|
||||
_f_str_vals.append(_DupScratch(_g_out_str))
|
||||
return 0
|
||||
# 列表常量
|
||||
# List constant
|
||||
if _scratch is not None:
|
||||
tmp: list[str]* = list[str](_scratch, 8)
|
||||
if tmp is not None:
|
||||
@@ -705,7 +644,7 @@ def _HandleNameBind(tgt: ast.Name*, vnode: ast.AST*) -> int:
|
||||
|
||||
|
||||
def _HandleAttrAssign(tgt: ast.Attribute*, vnode: ast.AST*) -> int:
|
||||
"""`proj.<字段> = <常量表达式>`"""
|
||||
"""Handle proj.<field> = <constant expression>."""
|
||||
if tgt is None or tgt.attr is None or tgt.value is None: return 0
|
||||
if tgt.value.kind() != ast.ASTKind.Name: return 0
|
||||
bn: ast.Name* = ast.Name*(tgt.value)
|
||||
@@ -718,7 +657,7 @@ def _HandleAttrAssign(tgt: ast.Attribute*, vnode: ast.AST*) -> int:
|
||||
|
||||
|
||||
def _HandleConfigCall(cl: ast.Call*) -> int:
|
||||
"""`proj.config(kw=...)`"""
|
||||
"""Handle a proj.config(kw=...) call."""
|
||||
if cl is None or cl.keywords is None: return 0
|
||||
n: sizet = cl.keywords.__len__()
|
||||
i: sizet = 0
|
||||
@@ -731,7 +670,7 @@ def _HandleConfigCall(cl: ast.Call*) -> int:
|
||||
|
||||
|
||||
def _HandleBuildCall(cl: ast.Call*) -> int:
|
||||
"""`proj.build(triple=, datalayout=, selfmachine=)`"""
|
||||
"""Handle a proj.build(triple=, datalayout=, selfmachine=) call."""
|
||||
global _c_triple, _c_datalayout, _c_selfmachine, _c_has_build
|
||||
if cl is None: return 1
|
||||
if _c_has_build != 0:
|
||||
@@ -768,7 +707,7 @@ def _HandleBuildCall(cl: ast.Call*) -> int:
|
||||
|
||||
|
||||
def _CallArgsHaveProjRef(cl: ast.Call*) -> int:
|
||||
"""调用的实参/关键字里是否引用了 Project 宏对象(读即 fatal);1=是"""
|
||||
"""Whether any call argument references a Project macro object; 1=yes."""
|
||||
if cl is None: return 0
|
||||
if cl.args is not None:
|
||||
n: sizet = cl.args.__len__()
|
||||
@@ -788,13 +727,13 @@ def _CallArgsHaveProjRef(cl: ast.Call*) -> int:
|
||||
|
||||
|
||||
def _HandleExprStmt(stmt: ast.Expr*) -> int:
|
||||
"""表达式语句:识别 `proj.config(...)` / `proj.build(...)`;其余检查读取"""
|
||||
"""Handle an expression statement: proj.config/build calls, else check reads."""
|
||||
if stmt is None or stmt.value is None: return 0
|
||||
v: ast.AST* = stmt.value
|
||||
if v.kind() != ast.ASTKind.Call: return 0
|
||||
cl: ast.Call* = ast.Call*(v)
|
||||
if cl is None or cl.func is None: return 0
|
||||
# 非 Project 方法调用:若实参里读了宏对象 ⇒ fatal(禁止读取)
|
||||
# Non-Project method call: if an argument reads the macro object -> fatal (reading forbidden)
|
||||
if cl.func.kind() != ast.ASTKind.Attribute:
|
||||
if _CallArgsHaveProjRef(cl) != 0:
|
||||
VLogger.error("viperbuild: 禁止读取 Project 宏对象", "viperbuild")
|
||||
@@ -821,12 +760,12 @@ def _HandleExprStmt(stmt: ast.Expr*) -> int:
|
||||
|
||||
|
||||
def _ScanFile(pool: memhub.MemBuddy*, path: str) -> int:
|
||||
"""解析并扫描单个 .vp 文件;0=成功,1=失败(已报错),2=可跳过(不可读/非 .vp)"""
|
||||
"""Parse and scan one .vp file; 0=ok, 1=failed, 2=skippable (unreadable/not .vp)."""
|
||||
if path is None: return 2
|
||||
sl: sizet = string.strlen(path)
|
||||
if sl == 0: return 2
|
||||
if Utils.is_vp_at(path, 0, sl) == 0:
|
||||
# 非 .vp:不参与闭包(.vpi/.vpnci 对编译器不可见)
|
||||
# Not .vp: excluded from the closure (.vpi/.vpnci invisible to the compiler)
|
||||
return 2
|
||||
|
||||
src_buf: bytes = Utils.read_file_to_buf(path, VB_SRC_CAP)
|
||||
@@ -882,15 +821,9 @@ def _ScanFile(pool: memhub.MemBuddy*, path: str) -> int:
|
||||
return rc
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 依赖闭包 BFS
|
||||
# ============================================================
|
||||
# Dependency closure BFS
|
||||
def _ResolveModuleToFile(module_name: str) -> str:
|
||||
"""把模块名解析为工程本地文件路径(搜索根 = 入口文件所在目录);未命中返回 None
|
||||
|
||||
输出槽契约与 StubMergerBuild 一致:槽为 8 字节缓冲,写侧 sizet(ptr)、
|
||||
读侧 rawptr(slot[0]);ResolveModuleToPath 返回的 abs/rel/inc 串由调用方 free。
|
||||
"""
|
||||
"""Resolve a module name to a local project file path; None if not found."""
|
||||
if module_name is None or _search_dirs is None: return None
|
||||
if string.strlen(module_name) == 0: return None
|
||||
out_abs: t.CPtr = stdlib.malloc(8)
|
||||
@@ -922,7 +855,7 @@ def _ResolveModuleToFile(module_name: str) -> str:
|
||||
|
||||
|
||||
def _Enqueue(path: str) -> None:
|
||||
"""把文件路径加入 BFS 队列(worklist/visited 去重)"""
|
||||
"""Add a file path to the BFS queue (deduped against worklist and visited)."""
|
||||
if path is None or _work is None: return
|
||||
if _FindInList(_visited, path) >= 0: return
|
||||
if _FindInList(_work, path) >= 0: return
|
||||
@@ -930,7 +863,7 @@ def _Enqueue(path: str) -> None:
|
||||
|
||||
|
||||
def _RunBfs(pool: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
"""从入口文件出发展开依赖闭包;0=成功"""
|
||||
"""Expand the dependency closure from the entry file; 0=success."""
|
||||
global _pending
|
||||
_work.append(_DupScratch(entry_path))
|
||||
while _work.__len__() > 0:
|
||||
@@ -948,7 +881,7 @@ def _RunBfs(pool: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
return 1
|
||||
rc: int = _ScanFile(pool, cur)
|
||||
if rc == 1: return 1
|
||||
# 解析本文件收集到的模块名
|
||||
# Resolve module names collected from this file
|
||||
pn: sizet = _pending.__len__()
|
||||
pi: sizet = 0
|
||||
while pi < pn:
|
||||
@@ -960,15 +893,9 @@ def _RunBfs(pool: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 默认值 / 校验 / 应用
|
||||
# ============================================================
|
||||
# Defaults / validation / apply
|
||||
def _DetectHost() -> int:
|
||||
"""运行时探测本机 triple / datalayout(写 _c_triple / _c_datalayout);0=成功
|
||||
|
||||
读 PROCESSOR_ARCHITECTURE 环境变量判定本机架构。当前仅支持 AMD64
|
||||
(编译器自身的目标平台 x86_64-pc-windows-gnu);其余架构 fail-fast。
|
||||
"""
|
||||
"""Detect the host triple/datalayout via PROCESSOR_ARCHITECTURE; 0=success."""
|
||||
global _c_triple, _c_datalayout
|
||||
buf: CHARPTR = stdlib.malloc(64)
|
||||
if buf is None:
|
||||
@@ -981,7 +908,7 @@ def _DetectHost() -> int:
|
||||
VLogger.error("viperbuild: selfmachine 探测失败(PROCESSOR_ARCHITECTURE 不可用)", "viperbuild")
|
||||
return 1
|
||||
buf[int(n)] = char(0)
|
||||
# "AMD64\0"(A=65 M=77 D=68 6=54 4=52)
|
||||
# "AMD64\0" (A=65 M=77 D=68 6=54 4=52)
|
||||
is_amd64: int = 0
|
||||
if buf[0] == char(65) and buf[1] == char(77) and buf[2] == char(68) and buf[3] == char(54) and buf[4] == char(52) and buf[5] == char(0): is_amd64 = 1
|
||||
if is_amd64 != 0:
|
||||
@@ -995,7 +922,7 @@ def _DetectHost() -> int:
|
||||
|
||||
|
||||
def _ProbeIncludes() -> str:
|
||||
"""按 exe 所在目录向上 0..3 层探测 includes 目录;命中返回绝对路径,否则 None"""
|
||||
"""Probe up to 3 levels above the exe dir for an includes directory; else None."""
|
||||
exe_buf: bytes = stdlib.malloc(VB_PATH_CAP)
|
||||
if exe_buf is None: return None
|
||||
exe_buf[0] = '\0'
|
||||
@@ -1023,7 +950,7 @@ def _ProbeIncludes() -> str:
|
||||
|
||||
|
||||
def _DeriveOutputFromEntry(entry_path: str) -> str:
|
||||
"""从入口文件名推导默认输出名(去扩展名 + ".exe")"""
|
||||
"""Derive the default output name from the entry file name."""
|
||||
base: str = _BaseNameOf(entry_path)
|
||||
if base is None: return "app.exe"
|
||||
n: sizet = string.strlen(base)
|
||||
@@ -1046,7 +973,7 @@ def _DeriveOutputFromEntry(entry_path: str) -> str:
|
||||
|
||||
|
||||
def _FinalizeCfg(entry_path: str) -> int:
|
||||
"""填充默认值 + 校验 + 写回 Config;0=成功"""
|
||||
"""Fill defaults, validate and write back into Config; 0=success."""
|
||||
global _c_source, _c_buildroot, _c_includes, _c_compiler, _c_linker
|
||||
global _c_static_libs, _c_dynamic_libs, _c_output, _c_triple, _c_datalayout
|
||||
global _c_selfmachine
|
||||
@@ -1058,7 +985,7 @@ def _FinalizeCfg(entry_path: str) -> int:
|
||||
VLogger.error("viperbuild: 未调用 proj.build(...),无法确定构建目标", "viperbuild")
|
||||
return 1
|
||||
|
||||
# ---- 默认值 ----
|
||||
# ---- Defaults ----
|
||||
if _c_source is None: _c_source = _DupStd(_entry_dir)
|
||||
if _c_buildroot is None: _c_buildroot = _Join2(_c_source, ".tpv_build")
|
||||
if _c_includes is None:
|
||||
@@ -1069,18 +996,17 @@ def _FinalizeCfg(entry_path: str) -> int:
|
||||
_c_includes = probed
|
||||
if _c_compiler is None: _c_compiler = _DupStd("llc -filetype=obj -relocation-model=pic")
|
||||
if _c_linker is None:
|
||||
# 内置默认 = 本仓 TransPyV/project.vpj 那份**已验证可用**的链接参数。
|
||||
# 注意:不要带 -lgcc —— msys64/mingw64 + lld 下没有 libgcc,
|
||||
# 会以 "lld: error: unable to find library -lgcc" 直接链接失败。
|
||||
# Built-in default = the verified link args from this repo's TransPyV/project.vpj.
|
||||
# Do NOT add -lgcc: msys64/mingw64 + lld has no libgcc and fails with "unable to find library -lgcc".
|
||||
_c_linker = _DupStd("clang++ -fuse-ld=lld -lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -Wl,--stack,0x1000000 -Wl,--no-insert-timestamp -Wl,--export-all-symbols -g")
|
||||
if _c_output is None: _c_output = _DeriveOutputFromEntry(entry_path)
|
||||
|
||||
# ---- 入口文件必须位于 source 之下 ----
|
||||
# ---- Entry file must be under source ----
|
||||
if _HasPrefix(entry_path, _c_source) == 0:
|
||||
VLogger.error(f"viperbuild: 入口文件不在 source 目录之下\n 入口: {entry_path}\n source: {_c_source}", "viperbuild")
|
||||
return 1
|
||||
|
||||
# ---- 目标平台 ----
|
||||
# ---- Target platform ----
|
||||
explicit_triple: int = 0
|
||||
if _c_triple is not None or _c_datalayout is not None: explicit_triple = 1
|
||||
if _c_selfmachine != 0 and explicit_triple != 0:
|
||||
@@ -1091,10 +1017,10 @@ def _FinalizeCfg(entry_path: str) -> int:
|
||||
VLogger.error("viperbuild: build() 的 triple 与 datalayout 必须成对给出", "viperbuild")
|
||||
return 1
|
||||
else:
|
||||
# 都未给 或 selfmachine=True ⇒ 运行时探测本机
|
||||
# Neither given, or selfmachine=True: detect the host at runtime
|
||||
if _DetectHost() != 0: return 1
|
||||
|
||||
# ---- 写回 Config ----
|
||||
# ---- Write back to Config ----
|
||||
Config.ProjectName = _c_name
|
||||
Config.ProjectVersion = _c_version
|
||||
Config.SourceDir = _c_source
|
||||
@@ -1117,7 +1043,7 @@ def _FinalizeCfg(entry_path: str) -> int:
|
||||
|
||||
|
||||
def _SplitToolCmd(joined: str, which: int) -> None:
|
||||
"""把 `["cmd", flag...]` 空格连接串拆成 Config 的 Cmd/Flags(which: 0=compiler 1=linker)"""
|
||||
"""Split a space-joined tool command into Config Cmd/Flags (0=compiler, 1=linker)."""
|
||||
if joined is None: return
|
||||
n: sizet = string.strlen(joined)
|
||||
sp: sizet = n
|
||||
@@ -1141,11 +1067,9 @@ def _SplitToolCmd(joined: str, which: int) -> None:
|
||||
Config.LinkerFlags = flags
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 入口
|
||||
# ============================================================
|
||||
# Entry point
|
||||
def _ResetAll() -> None:
|
||||
"""重置模块状态(每次 DiscoverAndApply 调用前)"""
|
||||
"""Reset module state before each DiscoverAndApply call."""
|
||||
global _alias, _ctor_name, _g_out_str, _applied, _entry_dir, _search_dirs
|
||||
global _c_name, _c_version, _c_source, _c_buildroot, _c_includes
|
||||
global _c_compiler, _c_linker, _c_static_libs, _c_dynamic_libs, _c_output
|
||||
@@ -1176,15 +1100,7 @@ def _ResetAll() -> None:
|
||||
|
||||
|
||||
def DiscoverAndApply(mb: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
"""`--src` Pass 0:发现并应用 viperbuild 配置;0=成功,非 0=失败(已报错)
|
||||
|
||||
Args:
|
||||
mb: 主 MemBuddy(AST 用;每文件 reset_to 回收)
|
||||
entry_path: 入口 .vp 文件路径(--src)
|
||||
|
||||
Returns:
|
||||
0 成功(Config 已填充);1 失败(fail-fast,调用方须终止编译)
|
||||
"""
|
||||
"""Pass 0 for --src: discover and apply viperbuild config; 0=success, else failed."""
|
||||
global _scratch, _work, _visited, _pending, _proj_vars, _entry_dir, _applied
|
||||
global _search_dirs
|
||||
|
||||
@@ -1194,19 +1110,18 @@ def DiscoverAndApply(mb: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
|
||||
_ResetAll()
|
||||
|
||||
# 入口目录(source 缺省值 + 闭包搜索根)
|
||||
# Entry dir (default source + closure search root)
|
||||
_entry_dir = _DirOf(entry_path)
|
||||
if _entry_dir is None:
|
||||
VLogger.error(f"viperbuild: 无法解析入口文件目录 {entry_path}", "viperbuild")
|
||||
return 1
|
||||
# 裸文件名(无目录分隔符)→ _DirOf 返回空串;必须规整成 ".",否则
|
||||
# ResolveModuleToPath("") 一个候选目录都不会尝试,入口同目录的依赖
|
||||
# (往往是 viperbuild 配置所在模块)永远解析不到 ⇒ 误报"未找到 import viperbuild"。
|
||||
# Bare filename (no separator) -> _DirOf returns ""; must normalize to ".", otherwise
|
||||
# ResolveModuleToPath("") tries no candidate and same-dir deps (often the viperbuild config) never resolve.
|
||||
if string.strlen(_entry_dir) == 0: _entry_dir = _DupStd(".")
|
||||
# 闭包搜索根:入口目录优先,其次当前目录 "."(配置模块可能不在入口目录内)
|
||||
# Closure search roots: entry dir first, then "." (the config module may live elsewhere)
|
||||
_search_dirs = _ConcatStd(_DupStd(_entry_dir), _DupStd(" ."))
|
||||
|
||||
# scratch 池(BFS 工作集,不参与主池 reset)
|
||||
# Scratch pool (BFS working set; not affected by main-pool reset)
|
||||
arena: bytes = stdlib.malloc(VB_SCRATCH)
|
||||
if arena is None:
|
||||
VLogger.error("viperbuild: scratch 内存分配失败", "viperbuild")
|
||||
@@ -1224,7 +1139,7 @@ def DiscoverAndApply(mb: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
VLogger.error("viperbuild: 工作集分配失败", "viperbuild")
|
||||
return 1
|
||||
|
||||
# 关键字/操作符表须在主池 reset 窗口之前建立(否则被回收 → 悬空崩溃)
|
||||
# Keyword/operator tables must be built before the main-pool reset window (else freed -> dangling crash)
|
||||
ast._init_tables(mb)
|
||||
|
||||
if _RunBfs(mb, entry_path) != 0: return 1
|
||||
@@ -1241,5 +1156,5 @@ def DiscoverAndApply(mb: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
|
||||
|
||||
def IsApplied() -> int:
|
||||
"""本次进程是否已成功应用 viperbuild 配置(供 main.vp 判断 linker.output 来源)"""
|
||||
"""Whether viperbuild config was applied in this process; 1=yes."""
|
||||
return _applied
|
||||
@@ -1,3 +1 @@
|
||||
# ============================================================
|
||||
# builtin 包入口 —— 编译器内建库(如 viperbuild)的编译器侧实现
|
||||
# ============================================================
|
||||
# builtin package entry - compiler-side implementation of builtin libs (e.g. viperbuild).
|
||||
+58
-79
@@ -50,14 +50,12 @@ define V_TPV_COMMIT = "ab0f37e1a8"
|
||||
define V_RUNTIME_COMMIT = "f087aa88e2"
|
||||
define V_TARGET = "x86_64-w64-windows-gnu"
|
||||
define V_LLVM = "22.1.8"
|
||||
# --repl 模板工程查找缓冲(repl.vpj 命中路径 / 未命中诊断文本)
|
||||
# --repl template-project lookup buffers (repl.vpj path / miss diagnostic text)
|
||||
define REPL_PROJ_PATH_CAP = 1024
|
||||
define REPL_PROJ_DIAG_CAP = 2048
|
||||
|
||||
static a: int
|
||||
# ============================================================
|
||||
# main: Command-line entry
|
||||
# ============================================================
|
||||
def main() -> int:
|
||||
w32cmd.SetConsoleOutputCP(CODE_PAGE)
|
||||
w32cmd.SetConsoleCP(CODE_PAGE)
|
||||
@@ -66,7 +64,7 @@ def main() -> int:
|
||||
cmd_line_d: str = w32base.GetCommandLineA()
|
||||
if cmd_line_d is not None and string.strstr(cmd_line_d, "--detection") is not None: detect_flag = 1
|
||||
|
||||
# --detection-deep:重诊断(每次 alloc 探测 + 周期全表扫描,极慢,默认关);须在 MemBuddy 构造前用原始命令行 strstr 取值
|
||||
# --detection-deep: heavy diagnostics (per-alloc probe + periodic full-table scan; extremely slow, off by default); read via raw command-line strstr before MemBuddy
|
||||
deep_detect_flag: int = 0
|
||||
if cmd_line_d is not None and string.strstr(cmd_line_d, "--detection-deep") is not None: deep_detect_flag = 1
|
||||
|
||||
@@ -94,7 +92,7 @@ def main() -> int:
|
||||
|
||||
CompilerFactory.factory = CompilerFactory.CompilerFactory(mb, perm_pool)
|
||||
|
||||
# 设置全局 mbuddy 指针(sys 和 argparse 都需要)
|
||||
# set the global mbuddy pointer (needed by sys and argparse)
|
||||
sys._mbuddy = mb
|
||||
argparse._mbuddy = mb
|
||||
ast._mbuddy = mb
|
||||
@@ -112,7 +110,7 @@ def main() -> int:
|
||||
VLogger._mbuddy = mb
|
||||
lib._mbuddy = mb
|
||||
|
||||
# P2: 创建项目级 Generator 单例(Phase1/Phase2/BuildPipeline 全程复用;
|
||||
# P2: create the project-level Generator singleton (reused across Phase1/Phase2/BuildPipeline;
|
||||
gen: rawptr[GenGenerator.Generator] = mb.alloc(GenGenerator.Generator.__sizeof__())
|
||||
if gen is None:
|
||||
stdio.printf("FAIL: Generator alloc failed\n")
|
||||
@@ -121,22 +119,22 @@ def main() -> int:
|
||||
gen.__init__(mb)
|
||||
|
||||
GenGenerator._g_gen = gen
|
||||
# P2-P5: 注入各 Handles 模块级单例(Phase1 阶段即调用其状态函数);模块级全局带 sha1 前缀(模块隔离),必须逐个模块赋值
|
||||
# P2-P5: inject module-level Handles singletons (Phase1 calls their state funcs); module globals carry sha1 prefixes, assign each
|
||||
GenApi._g_gen = gen
|
||||
# P6: 注入语句/赋值/变量/导入/翻译驱动模块的单例
|
||||
# P6: inject statement/assign/var/import/translator-driver module singletons
|
||||
|
||||
# 初始化 VLogger 并打印启动日志
|
||||
# initialize VLogger and print the startup log
|
||||
VLogger.init_logger()
|
||||
log: VLogger.Logger = VLogger.logger
|
||||
if log is None: log.error("TransPyV Failed to parse parameters")
|
||||
|
||||
# 初始化命令行参数(Windows: GetCommandLineA, POSIX: /proc/self/cmdline)
|
||||
# initialize command-line args (Windows: GetCommandLineA, POSIX: /proc/self/cmdline)
|
||||
sys._init_argv()
|
||||
|
||||
# 创建参数解析器
|
||||
# create the argument parser
|
||||
parser: argparse.ArgumentParser = argparse.ArgumentParser("TransPyV", "TransPyV command-line parameter parser", pool=mb)
|
||||
|
||||
# 注册参数(与 Projectrans.py main() 一致)
|
||||
# register arguments (matching Projectrans.py main())
|
||||
parser.add_argument("--project", None, argparse.STRING, 0, None, False, argparse.STORE, "Project VPJ path, by default it looks in the current directory")
|
||||
parser.add_argument("--src", None, argparse.STRING, 0, None, False, argparse.STORE, "Source directory")
|
||||
parser.add_argument("--temp", None, argparse.STRING, 0, None, False, argparse.STORE, "Declare a temporary directory for the interface")
|
||||
@@ -152,8 +150,8 @@ def main() -> int:
|
||||
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)")
|
||||
# 仅注册以便 argparse 接受该标志(取值不读这里:MemBuddy 在本函数 L85/L90 构造,
|
||||
# 早于 parse_args,值取自 L70-72 的原始命令行 strstr —— 与 --detection 同范式)
|
||||
# Registered only so argparse accepts the flag: the value is read via raw
|
||||
# command-line strstr before MemBuddy construction, same as --detection.
|
||||
parser.add_argument("--detection-deep", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Deep detection: probe every allocation and enable the periodic full-table scan (implies --detection; extremely slow, diagnostics only)")
|
||||
parser.add_argument("--show-args", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show all parsed arguments")
|
||||
parser.add_argument("--repl", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Start the interactive REPL (M2 session variables + M3 cross-fragment functions: fragment -> DLL -> call)")
|
||||
@@ -162,7 +160,7 @@ def main() -> int:
|
||||
parser.add_argument("--version", "-v", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show version and exit")
|
||||
parser.add_argument("--help", "-h", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show help and exit")
|
||||
|
||||
# 解析命令行参数
|
||||
# parse command-line arguments
|
||||
args: argparse.ParsedArgs = parser.parse_args(sys._argc, sys._argv)
|
||||
if args is None:
|
||||
log.error("参数解析失败", "argparse")
|
||||
@@ -188,16 +186,8 @@ Target: %s
|
||||
""", V_VIPER_VERSION, V_RUNTIME_VERSION, V_TPV_VERSION, V_TPV_COMMIT, V_TPC_COMMIT, V_RUNTIME_COMMIT, V_TARGET, V_LLVM)
|
||||
return 0
|
||||
|
||||
# === --repl 守卫:与 --phase2command / --asan 互斥 ===
|
||||
# 位置:在下面 --asan / --phase2command 的全局状态设置与清理副作用之前
|
||||
#(--asan 会切 includes 缓存后缀并清空 .binary_asan、--phase2command 会设置
|
||||
# BuildPipeline._phase2_cmd_path),故必须最先判定。
|
||||
# ① --phase2command:_phase2_cmd_path 非空时 compile_ll_to_obj 只把 opt/llc
|
||||
# 命令写进 bat 而**不执行**(BuildPipeline.vp:322-344)⇒ 片段 .obj 不会生成,
|
||||
# Repl 第 ⑩ 步的 clang++ -shared 必然失败(Repl.vp 的 ReplExecFragment)。
|
||||
# ② --asan:会给片段 .ll 先做 opt -passes=asan 插桩
|
||||
# (BuildPipeline.vp:346-361),而片段产物是运行期 LoadLibraryA 载入的 DLL,
|
||||
# 宿主进程并未挂 ASan 运行库,"片段 obj 被插桩"语义不明。
|
||||
# --repl guard: mutually exclusive with --phase2command / --asan, checked first
|
||||
# before their global-state/cleanup side effects; both would break fragment DLLs.
|
||||
repl_flag: bool = args.get_bool("repl")
|
||||
if repl_flag:
|
||||
if args.get_str("phase2command") is not None:
|
||||
@@ -224,14 +214,13 @@ Target: %s
|
||||
argparse.release(args)
|
||||
return 2
|
||||
|
||||
# === --repl: M1 交互式循环(表达式片段 → DLL → 调用回显)===
|
||||
# 分支位置:见下方"工程配置加载"之后(Repl 需要 Config 的 compiler/linker/target 通道),
|
||||
# 仍在 Phase1/Phase2 之前;该分支直接返回。
|
||||
# === --repl: M1 interactive loop (expression fragment -> DLL -> echo). Runs
|
||||
# after project-config load and before Phase1/Phase2, then returns.
|
||||
|
||||
# 打印解析结果
|
||||
# log.banner("TransPyV 参数解析结果")
|
||||
# print parse result
|
||||
# log.banner("TransPyV argument parse result")
|
||||
|
||||
# 字符串参数
|
||||
# string arguments
|
||||
project: str = args.get_str("project")
|
||||
src: str = args.get_str("src")
|
||||
temp: str = args.get_str("temp")
|
||||
@@ -240,9 +229,8 @@ Target: %s
|
||||
cc: str = args.get_str("cc")
|
||||
build_dir: str = args.get_str("build-dir")
|
||||
|
||||
# === --project 与 --src 互斥 ===
|
||||
# --project:传统 project.vpj 工程模式;--src:viperbuild 内建配置模式(入口文件)。
|
||||
# 两者的配置来源互斥,同时给无法确定以谁为准 ⇒ fail-fast。
|
||||
# === --project and --src are mutually exclusive: --project uses project.vpj,
|
||||
# --src uses viperbuild built-in config; giving both fails fast.
|
||||
if project is not None and src is not None:
|
||||
stdio.printf("FAIL: --project 与 --src 互斥\n")
|
||||
stdio.printf(" 原因: --project 走 project.vpj 工程模式,--src 走入口文件的 viperbuild 内建配置\n")
|
||||
@@ -275,10 +263,8 @@ Target: %s
|
||||
else: stdio.printf(" --clear-cache: False\n")
|
||||
stdio.printf("参数解析完成。\n\n")
|
||||
|
||||
# 模式开关:includes 缓存用独立后缀目录隔离,避免不同模式产物的 obj 混链
|
||||
# (ASan 插桩 obj / traceback 帧指针+ctor obj 与非对应模式不兼容)。
|
||||
# --asan 与 --traceback 可同开 ⇒ 后缀必须一次性按两个开关组合计算,
|
||||
# 后续 --clean/--rebuild-includes 经 Config.get_includes_binary_dir() 自动覆盖。
|
||||
# Mode switches: isolate includes caches by suffix so mode-specific objects do
|
||||
# not mix (ASan/traceback objects are incompatible); suffix combines both flags.
|
||||
gb_asan: bool = args.get_bool("asan")
|
||||
gb_traceback: bool = args.get_bool("traceback")
|
||||
if gb_asan: BuildPipeline._asan_mode = True
|
||||
@@ -291,33 +277,31 @@ Target: %s
|
||||
else: Config._inc_bin_suffix = ".binary"
|
||||
if gb_asan: log.info("ASan 模式已启用(opt -passes=asan 插桩; 运行库需 project.vpj 声明)", "asan")
|
||||
if gb_traceback: log.info("Traceback 模式已启用(llc 保留帧指针 + 链 dbghelp + Install 构造器 + <exe>.stackmap)", "traceback")
|
||||
# 开启任一模式都清空对应后缀的 includes 缓存,强制重建匹配当前模式的 obj
|
||||
# Enabling either mode clears that suffix's includes cache to force a rebuild
|
||||
if gb_asan or gb_traceback:
|
||||
mf_inc_bin_mode: str = Config.get_includes_binary_dir()
|
||||
if mf_inc_bin_mode is not None:
|
||||
n_mode: int = Utils.CleanDir(mf_inc_bin_mode)
|
||||
log.info(f"模式缓存清理: {mf_inc_bin_mode} 删除 {n_mode} 个文件(将按 {Config._inc_bin_suffix} 重建)", "mode")
|
||||
|
||||
# === --phase2command: 只生成命令写入 bat(Phase B/B+ 的 llc 与 Phase C 的 clang 链接命令),不执行 llc/链接 ===
|
||||
# === --phase2command: only write llc/link commands to a bat, do not execute them ===
|
||||
p2c: str = args.get_str("phase2command")
|
||||
if p2c is not None:
|
||||
BuildPipeline._phase2_cmd_path = p2c
|
||||
log.info(f"--phase2command: 不执行 llc/链接,命令写入 {p2c}", "phase2cmd")
|
||||
|
||||
# --nowarn-register:抑制 Phase A 依赖类 WARN 输出(跨模块 text.ll 未生成,下遍收敛)
|
||||
# --nowarn-register: suppress Phase A dependency WARNs (cross-module text.ll not yet generated)
|
||||
if args.get_bool("nowarn-register"):
|
||||
VLogger._nowarn_register = 1
|
||||
log.info("--nowarn-register 已启用:抑制注册类 WARN 输出", "vlogger")
|
||||
|
||||
# --no-llvmpeephole:关闭全部本地窥孔(T2/T3),FunctionPrint 直接序列化
|
||||
# --no-llvmpeephole: disable all local peepholes (T2/T3); FunctionPrint serializes directly
|
||||
if args.get_bool("no-llvmpeephole"):
|
||||
mlirlite.G_PeepEnable = 0
|
||||
log.info("--no-llvmpeephole 已启用:关闭 FunctionPrint 本地窥孔(RenumberSSA 保留)", "peep")
|
||||
|
||||
# === --repl 启动便利:未给 --project 时按 cwd → exe 同目录 查找 repl.vpj ===
|
||||
# 命中后按 --project 等价路径喂给下面既有的 Config.Load_project_config 流程
|
||||
#(不另写一套加载器);两处都没有则 fail-fast,诊断文本含"需要 --project"、
|
||||
# 两个已查位置与模板位置(由 Repl.ReplFindProject 写入 repl_diag_buf)。
|
||||
# === --repl convenience: without --project, search cwd then the exe dir for
|
||||
# repl.vpj and feed it to Config.Load_project_config; miss fails fast.
|
||||
proj_loaded: int = 0
|
||||
proj_path: str = project
|
||||
if repl_flag and proj_path is None:
|
||||
@@ -336,15 +320,15 @@ Target: %s
|
||||
stdio.printf(" 也可用 --project 显式指定工程文件(如本仓库的 TransPyV/project.vpj)\n")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
# 加载工程配置(显式 --project / --repl 查找到的 repl.vpj)
|
||||
# load project config (explicit --project / repl.vpj found by --repl)
|
||||
if proj_path is not None:
|
||||
log.banner("工程配置")
|
||||
cfg_ret: int = Config.Load_project_config(proj_path)
|
||||
if cfg_ret == 0: proj_loaded = 1
|
||||
else: log.warning(f"无法加载 project.vpj: {proj_path}", "config")
|
||||
else:
|
||||
# 尝试默认路径 project.vpj(当前目录和上级目录)
|
||||
# --src 模式走 viperbuild 内建配置,不得再隐式加载 project.vpj(否则污染配置来源)
|
||||
# try the default project.vpj (current and parent dir); --src mode must not
|
||||
# implicitly load project.vpj (it would pollute the config source).
|
||||
if src is None:
|
||||
proj_path = "project.vpj"
|
||||
if Config.Load_project_config(proj_path) == 0: proj_loaded = 1
|
||||
@@ -352,7 +336,7 @@ Target: %s
|
||||
proj_path = "../project.vpj"
|
||||
if Config.Load_project_config(proj_path) == 0: proj_loaded = 1
|
||||
if proj_loaded == 1:
|
||||
# 提取 project.vpj 所在目录,将相对路径解析为基于该目录的路径
|
||||
# extract project.vpj's dir so relative paths resolve against it
|
||||
proj_len: sizet = string.strlen(proj_path)
|
||||
slash_pos: sizet = proj_len
|
||||
for i in range(proj_len):
|
||||
@@ -367,15 +351,13 @@ Target: %s
|
||||
if proj_dir is not None:
|
||||
string.memcpy(proj_dir, proj_path, slash_pos)
|
||||
proj_dir[slash_pos] = '\0'
|
||||
# [--build-dir] 覆盖 project.vpj 的 build_dir:须在 resolve_paths 前设置,使相对路径仍基于 project_dir 解析
|
||||
# [--build-dir] overrides build_dir before resolve_paths so paths stay project_dir-based
|
||||
if build_dir is not None: Config.BuildDir = build_dir
|
||||
Config.resolve_paths(proj_dir)
|
||||
Config.print_config()
|
||||
|
||||
# === --src: viperbuild 内建配置发现与应用(Pass 0)===
|
||||
# 位置:project.vpj 加载之后、--asan/--clean/--phase 之前 —— 后续这些开关都要读
|
||||
# Config.TempDir/BuildDir/static_libs,必须先在 Pass 0 里从入口文件的依赖闭包
|
||||
# 提取 `import viperbuild` 配置并写回 Config。没找到 / 找到但坏一律 fail-fast。
|
||||
# === --src: viperbuild built-in config discovery/apply (Pass 0), before the
|
||||
# switches read Config; a miss or bad config fails fast.
|
||||
if src is not None:
|
||||
log.banner("viperbuild")
|
||||
if ViperBuild.DiscoverAndApply(mb, src) != 0:
|
||||
@@ -383,18 +365,17 @@ Target: %s
|
||||
return 1
|
||||
Config.print_config()
|
||||
|
||||
# === --repl: M1 交互式循环(表达式片段 → IR → llc → clang++ -shared → DLL → 调用回显)===
|
||||
# 位置:工程配置加载之后(Repl 经 Config 取 compiler/linker 命令与 target triple;
|
||||
# 未加载 project.vpj 时 Repl 退回内置默认 llc/clang++),仍在 Phase1/Phase2 之前。
|
||||
# === --repl: M1 interactive loop (fragment -> IR -> llc -> clang++ -shared ->
|
||||
# DLL -> echo), after config load and before Phase1/Phase2.
|
||||
if args.get_bool("repl"):
|
||||
Repl._perm_pool = perm_pool # jmp_buf/解析表/会话缓冲须跨行存活,必须用 PermPool
|
||||
Repl._perm_pool = perm_pool # jmp_buf/parse-table/session buffers must live across calls; must use PermPool
|
||||
if log is not None:
|
||||
log.set_jit_flag(True) # error 改为 longjmp 回 REPL(Repl.run 退出时复位 False)
|
||||
log.set_jit_flag(True) # errors longjmp back to REPL (reset to False when Repl.run exits)
|
||||
rc_repl: int = Repl.run(mb)
|
||||
argparse.release(args)
|
||||
return rc_repl
|
||||
|
||||
# --asan 要求工程在 project.vpj 的 linker.static_libs 声明 ASan 运行库(TPV 不硬编码路径),缺失则 fail-fast
|
||||
# --asan requires the project to declare the ASan runtime in project.vpj linker.static_libs (no hardcoded paths); missing fails fast
|
||||
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")
|
||||
@@ -402,7 +383,7 @@ Target: %s
|
||||
log.error("运行时还需要 PATH 里有 D:/msys64/clang64/bin(libclang_rt.asan_dynamic-x86_64.dll)", "asan")
|
||||
return 1
|
||||
|
||||
# === --clean: 清理 temp、output 和 includes.binary 目录 ===
|
||||
# === --clean: remove temp, output and includes.binary directories ===
|
||||
if _gb_clean:
|
||||
log.info("清理临时目录...", "clean")
|
||||
if Config.TempDir is not None:
|
||||
@@ -411,14 +392,14 @@ Target: %s
|
||||
if Config.OutputDir is not None:
|
||||
n2: int = Utils.CleanDir(Config.OutputDir)
|
||||
log.info(f"{Config.OutputDir}: 删除 {n2} 个文件", "clean")
|
||||
# 清理 includes.binary 目录(旧 .obj 文件会导致跳过重新编译)
|
||||
# Clean the includes.binary dir (stale .obj files would skip recompilation)
|
||||
mf_inc_bin_clean: str = Config.get_includes_binary_dir()
|
||||
if mf_inc_bin_clean is not None:
|
||||
n3: int = Utils.CleanDir(mf_inc_bin_clean)
|
||||
log.info(f"{mf_inc_bin_clean}: 删除 {n3} 个文件", "clean")
|
||||
|
||||
# === --rebuild-includes: 删除 includes.binary 预编译缓存 ===
|
||||
# 强制下次编译时重新编译所有 includes(不清 temp/output)
|
||||
# === --rebuild-includes: delete the includes.binary precompile cache ===
|
||||
# Forces all includes to recompile next time (temp/output untouched)
|
||||
if args.get_bool("rebuild-includes"):
|
||||
log.info("重建 includes 缓存...", "rebuild-includes")
|
||||
mf_inc_bin_rb: str = Config.get_includes_binary_dir()
|
||||
@@ -426,50 +407,48 @@ Target: %s
|
||||
n_rb: int = Utils.CleanDir(mf_inc_bin_rb)
|
||||
log.info(f"{mf_inc_bin_rb}: 删除 {n_rb} 个文件(includes 将重新编译)", "rebuild-includes")
|
||||
|
||||
# === --clear-cache: 清除 temp 目录缓存(.vpi/.stub.ll/.text.ll) ===
|
||||
# 强制下次 Phase1 重新生成 stub(不清 output/includes.binary)
|
||||
# === --clear-cache: clear the temp dir cache (.vpi/.stub.ll/.text.ll) ===
|
||||
# Forces Phase1 to regenerate stubs next time (output/includes.binary untouched)
|
||||
if args.get_bool("clear-cache"):
|
||||
log.info("清除 temp 缓存...", "clear-cache")
|
||||
if Config.TempDir is not None:
|
||||
n_cc: int = Utils.CleanDir(Config.TempDir)
|
||||
log.info(f"{Config.TempDir}: 删除 {n_cc} 个文件(.vpi/.stub.ll 缓存)", "clear-cache")
|
||||
|
||||
# === --phase 参数控制: 1=仅 stub 分离;2=仅 stub 合并+编译;all=两者都执行(默认) ===
|
||||
# === --phase: 1=stub split only; 2=stub merge+compile only; all=both (default) ===
|
||||
phase_mode: str = phase
|
||||
if phase_mode is None: phase_mode = "all"
|
||||
do_phase1: int = 0
|
||||
do_phase2: int = 0
|
||||
# 注意: 使用 string.strcmp 而非 ==,避免编译器 == 语义 bug
|
||||
# Note: use string.strcmp instead of == to avoid the compiler == semantics bug
|
||||
if string.strcmp(phase_mode, "1") == 0 or string.strcmp(phase_mode, "all") == 0: do_phase1 = 1
|
||||
if string.strcmp(phase_mode, "2") == 0 or string.strcmp(phase_mode, "all") == 0: do_phase2 = 1
|
||||
log.info(f"模式: {phase_mode} (phase1={do_phase1} phase2={do_phase2})", "phase")
|
||||
|
||||
# === Phase1: 扫描 includes(按需翻译)===
|
||||
# 对 includes 目录中每个 .vp/.py 文件,若 stub 不存在则翻译并分离 stub
|
||||
# === Phase1: scan includes (on-demand translation) ===
|
||||
# For each .vp/.py file under includes, translate and split the stub if absent
|
||||
if do_phase1 != 0 and Config.IncludesDirs is not None:
|
||||
ph1_temp: str = Config.TempDir
|
||||
if ph1_temp is None: ph1_temp = "."
|
||||
if Phase1Main.RunPhase1(mb, Config.IncludesDirs, ph1_temp, log) != 0:
|
||||
VLogger.error("Phase1 失败,终止编译", "Phase1")
|
||||
return 1
|
||||
# 如果仅 Phase1(不执行 Phase2),直接退出
|
||||
# Phase1-only (no Phase2): exit right away
|
||||
if do_phase2 == 0:
|
||||
log.info("Phase1 完成,退出", "phase")
|
||||
return 0
|
||||
|
||||
# AST 解析:如果指定了 --src 或 --project,读取文件并解析为 AST 树
|
||||
# AST parse: when --src or --project is given, read the file and parse it into an AST
|
||||
src_path: str = args.get_str("src")
|
||||
|
||||
# 无 project 配置且未指定 --src:fail-fast 退出(否则未初始化的 Config 字段为垃圾指针 → 崩溃)
|
||||
# No project config and no --src: fail fast (uninitialized Config fields are garbage pointers -> crash)
|
||||
if proj_loaded == 0 and src_path is None:
|
||||
log.error("未找到 project.vpj(当前目录/上级目录)且未指定 --src,无法构建", "config")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
|
||||
# === 工程模式(多文件) ===
|
||||
# --project:project.vpj 工程模式(扫 source_dir 下所有 .vp 编译);
|
||||
# --src + viperbuild:Pass 0 已把配置写回 Config,同样按工程模式构建(SourceDir 为根)。
|
||||
# 二者在参数解析处已判定互斥,不会同时成立。
|
||||
# Project mode (multi-file): --project scans source_dir for all .vp; --src + viperbuild
|
||||
# reuses it (Pass 0 writes config back, SourceDir as root); the two are mutually exclusive.
|
||||
if Config.SourceDir is not None and (project is not None or ViperBuild.IsApplied() != 0):
|
||||
mf_temp: str = Config.TempDir if Config.TempDir is not None else "."
|
||||
mf_output: str = Config.OutputDir if Config.OutputDir is not None else "."
|
||||
@@ -487,7 +466,7 @@ Target: %s
|
||||
log.success("TransPyV 完成")
|
||||
return mf_ret
|
||||
|
||||
# 走到这里说明没有任何可构建的工程根(SourceDir 缺失)—— fail-fast,不再静默"完成"
|
||||
# Reaching here means no buildable project root (SourceDir missing): fail fast, no silent "done"
|
||||
log.error("无可构建的工程根:project.vpj 缺 source_dir,或 viperbuild 未提供 source", "config")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
Reference in new issue
Block a user