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
+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
|
||||
Reference in new issue
Block a user