feat(gen): P2 生命期标注 @life(willFree(...)) 驱动释放后使用与二次释放检查
标注面:@life(willFree(<形参名|0起序号>)),兼容叠加式 @willFree(...),可混用累加;序号按形参表下标、self/cls 计入。GenApi.compute_willfree_mask 解析标注并对非法标注 fail-fast。本文件表 StructGenMixin.WfTable + 跨模块 .vpi 行尾 wf=<mask>(Converter 写 / VpiReader 读 / CallGenBase 查)。调用点在实参全部翻译之后把对应实参变量槽置 VarEntry.Freed;其后的读取路径报 LifetimeError。重新赋值清除失效态。
This commit is contained in:
1 parent
9ff5a3a03e
commit
97313e9e55
10 files changed
+379
-8
No files matched your search
@@ -517,15 +517,25 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*,
|
||||
# v3 跨模块逃逸摘要:把形参逃逸掩码与返回借用位写成**同一行**的行尾注释,供 VpiReader
|
||||
# 解析后在别的模块的调用点查询(跨模块摘要表按文件重置,只能靠 vpi 传递)。
|
||||
# 只在真有逃逸/返回借用时写,保持 vpi 干净:无标记 = 无摘要 = 调用点保守放行。
|
||||
# P2 willFree:同行走 `# ... wf=<mask>`(第 k 位=第 k 个形参在本调用后失效),
|
||||
# 纯声明式、不依赖逃逸扫描的 scratch,所以单独算、独立决定是否落盘。
|
||||
wf_mask_c: int = GenApi.compute_willfree_mask(fn)
|
||||
esc_v_c: int = 0
|
||||
ret_v_c: int = 0
|
||||
if _PyiEnsureEscScratch() != 0:
|
||||
esc_box_c: sizet* = sizet*(uint64(_g_pyi_esc_scratch) + 520)
|
||||
if GenApi.compute_escape_summary(None, fn, _g_pyi_esc_scratch, esc_box_c) == 0:
|
||||
if esc_box_c[0] != 0 or esc_box_c[1] != 0:
|
||||
tmp_c: bytes = stdlib.malloc(64)
|
||||
if tmp_c is not None:
|
||||
viperlib.snprintf(tmp_c, 64, " # esc=%d ret=%d", int(esc_box_c[0]), int(esc_box_c[1]))
|
||||
pos = _PyiEmit(buf, buf_size, pos, tmp_c)
|
||||
stdlib.free(tmp_c)
|
||||
esc_v_c = int(esc_box_c[0])
|
||||
ret_v_c = int(esc_box_c[1])
|
||||
if esc_v_c != 0 or ret_v_c != 0 or wf_mask_c != 0:
|
||||
tmp_c: bytes = stdlib.malloc(96)
|
||||
if tmp_c is not None:
|
||||
if esc_v_c != 0 or ret_v_c != 0:
|
||||
if wf_mask_c != 0: viperlib.snprintf(tmp_c, 96, " # esc=%d ret=%d wf=%d", esc_v_c, ret_v_c, wf_mask_c)
|
||||
else: viperlib.snprintf(tmp_c, 96, " # esc=%d ret=%d", esc_v_c, ret_v_c)
|
||||
else: viperlib.snprintf(tmp_c, 96, " # wf=%d", wf_mask_c)
|
||||
pos = _PyiEmit(buf, buf_size, pos, tmp_c)
|
||||
stdlib.free(tmp_c)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
return pos
|
||||
|
||||
|
||||
@@ -116,6 +116,15 @@ define VPI_IDX_FNV_MASK = 4294967295
|
||||
define VPI_ESC_MAX = 8192
|
||||
define VPI_ESC_KEY_LEN = 224
|
||||
|
||||
# P2 跨模块 willFree 标注缓存(.vpi 行尾 `# ... wf=<mask>`,由 StubGen.Converter._PyiFunction 写出)。
|
||||
# 键格式与逃逸摘要完全一致("{sha1}.{Class}.{method}" / "{sha1}.{func}");值 = 形参失效掩码
|
||||
# (第 k 位 = 第 k 个形参在本调用后失效)。无标注的 def 不写条目,"查不到" 即 "无标注"。
|
||||
define VPI_WF_MAX = 8192
|
||||
define VPI_WF_KEY_LEN = 224
|
||||
_g_vpi_wf_keys: bytes = None
|
||||
_g_vpi_wf_vals: sizet* = None
|
||||
_g_vpi_wf_count: int = 0
|
||||
|
||||
_g_vpi_esc_keys: bytes = None
|
||||
_g_vpi_esc_vals: sizet* = None
|
||||
_g_vpi_esc_count: int = 0
|
||||
@@ -882,6 +891,7 @@ def _cache_vpi_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
|
||||
_cache_vpi_return_annots_text(vpi_buf, src_sha1)
|
||||
_cache_vpi_param_annots_text(vpi_buf, src_sha1)
|
||||
_cache_vpi_escape_annots_text(vpi_buf, src_sha1)
|
||||
_cache_vpi_willfree_annots_text(vpi_buf, src_sha1)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -1000,6 +1010,122 @@ def _lookup_vpi_escape_packed(src_sha1: str, cls_name: str, method_name: str) ->
|
||||
return res
|
||||
|
||||
|
||||
# _cache_vpi_willfree_annots_text - extract the trailing `wf=<mask>` marker (P2 willFree).
|
||||
# 与逃逸摘要同构:不依赖 AST,按行扫描;只在有 wf 标记的 def 上建条目。
|
||||
def _cache_vpi_willfree_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
|
||||
"""抽取 vpi 行尾 P2 willFree 标记(`# ... wf=<mask>`)进缓存;1=处理完成 / 0=缓冲区或分配失败"""
|
||||
global _g_vpi_wf_keys, _g_vpi_wf_vals, _g_vpi_wf_count
|
||||
if vpi_buf is None or src_sha1 is None: return 0
|
||||
if _g_vpi_wf_keys is None:
|
||||
_g_vpi_wf_keys = stdlib.malloc(VPI_WF_MAX * VPI_WF_KEY_LEN)
|
||||
_g_vpi_wf_vals = sizet*(stdlib.malloc(VPI_WF_MAX * 8))
|
||||
if _g_vpi_wf_keys is None or _g_vpi_wf_vals is None: return 0
|
||||
string.memset(_g_vpi_wf_keys, 0, VPI_WF_MAX * VPI_WF_KEY_LEN)
|
||||
string.memset(_g_vpi_wf_vals, 0, VPI_WF_MAX * 8)
|
||||
sha1_len: sizet = string.strlen(src_sha1)
|
||||
buf_len: sizet = string.strlen(vpi_buf)
|
||||
cur_cls: bytes = None
|
||||
pos: sizet = 0
|
||||
while pos < buf_len:
|
||||
ls: sizet = pos
|
||||
le: sizet = pos
|
||||
while le < buf_len and vpi_buf[le] != '\n': le += 1
|
||||
i: sizet = ls
|
||||
while i < le and vpi_buf[i] == ' ': i += 1
|
||||
if i < le:
|
||||
# class X: -> 记录当前类上下文(与方法键一致)
|
||||
if le - i >= 6 and string.strncmp(vpi_buf + i, "class ", 6) == 0:
|
||||
cstart: sizet = i + 6
|
||||
cend: sizet = cstart
|
||||
while cend < le and vpi_buf[cend] != ':' and vpi_buf[cend] != ' ' and vpi_buf[cend] != '(': cend += 1
|
||||
if cend > cstart:
|
||||
if cur_cls is not None: stdlib.free(cur_cls)
|
||||
cur_cls = stdlib.malloc(cend - cstart + 1)
|
||||
if cur_cls is not None:
|
||||
string.strncpy(cur_cls, vpi_buf + cstart, cend - cstart)
|
||||
cur_cls[cend - cstart] = '\0'
|
||||
# def name(...) ... # [esc=<mask> ret=<ret>] wf=<mask>
|
||||
elif le - i >= 4 and string.strncmp(vpi_buf + i, "def ", 4) == 0:
|
||||
# 模块级函数(无缩进)清掉类上下文,避免上一类泄漏
|
||||
if i == ls and cur_cls is not None:
|
||||
stdlib.free(cur_cls)
|
||||
cur_cls = None
|
||||
# 只在注释段里找 "wf=":避开参数名(如 def f(wf: int))
|
||||
mark: sizet = le
|
||||
hm: sizet = le
|
||||
j: sizet = i
|
||||
while j < le:
|
||||
if vpi_buf[j] == '#':
|
||||
hm = j
|
||||
break
|
||||
j += 1
|
||||
if hm < le:
|
||||
j2: sizet = hm + 1
|
||||
while j2 + 3 <= le:
|
||||
if vpi_buf[j2] == 'w' and vpi_buf[j2 + 1] == 'f' and vpi_buf[j2 + 2] == '=':
|
||||
mark = j2
|
||||
break
|
||||
j2 += 1
|
||||
if mark < le:
|
||||
kp: sizet = mark + 3
|
||||
wf_v: int = 0
|
||||
while kp < le and vpi_buf[kp] >= '0' and vpi_buf[kp] <= '9':
|
||||
wf_v = wf_v * 10 + (int(vpi_buf[kp]) - 48)
|
||||
kp += 1
|
||||
if wf_v != 0 and _g_vpi_wf_count < VPI_WF_MAX:
|
||||
dstart: sizet = i + 4
|
||||
dend: sizet = dstart
|
||||
while dend < le and vpi_buf[dend] != '(': dend += 1
|
||||
if dend > dstart:
|
||||
mname_len: sizet = dend - dstart
|
||||
cls_len_w: sizet = 0
|
||||
if cur_cls is not None: cls_len_w = string.strlen(cur_cls)
|
||||
key_len_w: sizet = sha1_len + 1 + cls_len_w + 1 + mname_len
|
||||
if key_len_w < VPI_WF_KEY_LEN:
|
||||
mname_buf_w: bytes = stdlib.malloc(mname_len + 1)
|
||||
key_buf_w: bytes = stdlib.malloc(key_len_w + 1)
|
||||
if mname_buf_w is not None and key_buf_w is not None:
|
||||
string.strncpy(mname_buf_w, vpi_buf + dstart, mname_len)
|
||||
mname_buf_w[mname_len] = '\0'
|
||||
if cur_cls is not None: viperlib.snprintf(key_buf_w, key_len_w + 1, "%s.%s.%s", src_sha1, cur_cls, mname_buf_w)
|
||||
else: viperlib.snprintf(key_buf_w, key_len_w + 1, "%s.%s", src_sha1, mname_buf_w)
|
||||
string.strcpy(_g_vpi_wf_keys + sizet(_g_vpi_wf_count) * VPI_WF_KEY_LEN, key_buf_w)
|
||||
_g_vpi_wf_vals[_g_vpi_wf_count] = sizet(wf_v)
|
||||
_g_vpi_wf_count += 1
|
||||
if mname_buf_w is not None: stdlib.free(mname_buf_w)
|
||||
if key_buf_w is not None: stdlib.free(key_buf_w)
|
||||
pos = le + 1
|
||||
if cur_cls is not None: stdlib.free(cur_cls)
|
||||
return 1
|
||||
|
||||
|
||||
# _lookup_vpi_willfree - query the cross-module P2 willFree cache (key shape = escape summary).
|
||||
def _lookup_vpi_willfree(src_sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""查跨模块 willFree 形参掩码;0 = 无标注"""
|
||||
if _g_vpi_wf_keys is None or _g_vpi_wf_count == 0: return 0
|
||||
if src_sha1 is None or method_name is None: return 0
|
||||
sha1_len: sizet = string.strlen(src_sha1)
|
||||
cl_len: sizet = 0
|
||||
if cls_name is not None: cl_len = string.strlen(cls_name)
|
||||
me_len: sizet = string.strlen(method_name)
|
||||
key_len: sizet = sha1_len + 1 + cl_len + 1 + me_len
|
||||
kb: bytes = stdlib.malloc(key_len + 1)
|
||||
if kb is None: return 0
|
||||
if cls_name is not None: viperlib.snprintf(kb, key_len + 1, "%s.%s.%s", src_sha1, cls_name, method_name)
|
||||
else: viperlib.snprintf(kb, key_len + 1, "%s.%s", src_sha1, method_name)
|
||||
res: int = 0
|
||||
idx: int = 0
|
||||
while idx < _g_vpi_wf_count:
|
||||
ent: bytes = _g_vpi_wf_keys + sizet(idx) * VPI_WF_KEY_LEN
|
||||
# 键以模块 sha1 打头:先比前 2 个十六进制字符,避免逐项 strcmp
|
||||
if ent[0] == kb[0] and ent[1] == kb[1] and string.strcmp(ent, kb) == 0:
|
||||
res = int(_g_vpi_wf_vals[idx])
|
||||
break
|
||||
idx += 1
|
||||
stdlib.free(kb)
|
||||
return res
|
||||
|
||||
|
||||
# _vpi_param_annot_seg_is - equality check for a param-annotation segment (for the "str/bytes is not a generic pointer" rule).
|
||||
def _vpi_param_annot_seg_is(src_sha1: str, cls_name: str, method_name: str, seg_idx: int, want: str) -> int:
|
||||
"""Check whether the definition-side param-annotation segment equals want exactly; 1=yes / 0=no."""
|
||||
|
||||
Reference in new issue
Block a user