fix(stub): 包 __init__ 判定改按模块名,修 ast.ClassDef 票名前缀分裂
存量缺口:VpiReader 判定"本 stub 是否包 __init__"依赖 sha1→rel_path 反查 (strstr(rel_path, "__init__.vp"))。该反查在部分运行形态下取不到条目 ⇒ 判定恒 false ⇒ 包重导出类的别名 entry 静默不登记 ⇒ `ast.ClassDef` 这类限定名在泛型实参 里保留包前缀(幽灵名 `list[1abcd5277fa442de.ClassDef*]` 全产物恒 opaque),与构造 函数侧的真定义模块前缀(`list[e70bd9b4487d921d.ClassDef*]`)分裂成两个类型,报 「指针隐式转指针已禁止」(现场 ClassGenGeneric.vp:92 的 CastError)。 改法(不再依赖 rel_path 字段,均为模块名表的既有事实): - StubMergerStore 新增 LookupModNameBySha1:按 SHA1 反查模块名(只读指针)。 - VpiReader 的 ImportFrom 分支:取 level>0 的兄弟模块 S,用其模块名去掉末段得到包 前缀 P;又因 Utils.PathToModuleName 把 pkg/__init__.vp 记成 "pkg",故 「本 stub 的模块名 == P」即证明本 stub 就是包 P 的 __init__ ⇒ is_pkg_init_rt=1, 同时直接用 P 拼出子模块全名(不再从 rel_path 反推目录名)。 - 模块名证据不足时仍回退原 rel_path 路线,行为不变。
This commit is contained in:
1 parent
b91dfef777
commit
9994d2e983
2 files changed
+111
-24
No files matched your search
@@ -2155,23 +2155,69 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*,
|
||||
elif sk == ast.ASTKind.ImportFrom:
|
||||
sifr: ast.ImportFrom* = ast.ImportFrom*(schild)
|
||||
sifr_mod: str = sifr.module
|
||||
# 相对导入补包前缀:vpi 里 `from .base import X` 只存 module="base"(前导点记在 level),若按字面 "base" 精确查找则查不到 "ast.base" ⇒ 裸类名(AST 等)解析失败 ⇒ rawptr[AST] 等注解 fail-fast。
|
||||
# 故按 stub 自身相对路径还原包名(ast/parser.vp → "ast"),level==1 时拼成完整模块名 "ast.base" 再登记(level>1 保持原样,避免未确认路径形态时做过深上溯)。
|
||||
# 相对导入补包前缀 + 包 __init__ 判定(两者**都不再依赖 rel_path 表**)。
|
||||
#
|
||||
# vpi 里 `from .base import X` 只存 module="base"(前导点记在 level),按字面
|
||||
# "base" 精确查找查不到 "ast.base" ⇒ 裸类名(AST 等)解析失败。包前缀的
|
||||
# 权威来源是**模块名表**:兄弟模块 S 的全名 = "<包前缀>.S"(如 "ast.stmts")
|
||||
# ⇒ 包前缀 = S 的模块名去掉最后一段;而 Utils.PathToModuleName 把
|
||||
# `pkg/__init__.vp` 记成 "pkg"(**不带** .__init__,见其 :150 文档)
|
||||
# ⇒ "本 stub 的模块名 == 该包前缀"即证明**本 stub 就是该包的 __init__**,
|
||||
# 这正是重导出别名 entry 的登记条件。
|
||||
# 为什么要脱离 rel_path:rel_path 反查在部分运行形态下取不到条目 ⇒
|
||||
# 原 `strstr(rel_path,"__init__.vp")` 判定恒 false ⇒ 别名静默不登记 ⇒
|
||||
# `ast.ClassDef` 这类**限定名在泛型实参里保留包前缀**,与构造函数侧(真
|
||||
# 定义模块前缀)分裂成两个类型(现场 ClassGenGeneric.vp:92 的 CastError;
|
||||
# 幽灵名 `list[1abcd5277fa442de.ClassDef*]` 在全部产物里恒为 opaque)。
|
||||
# 取不到模块名证据时回退旧的 rel_path 路线,行为不变。
|
||||
pkg_prefix_rt: str = None
|
||||
if sifr.level > 0 and sifr_mod is not None and sifr_mod[0] != '\0':
|
||||
_sib_sha_rt: str = StubMergerStore.LookupSha1ByModNameSuffix(sifr_mod)
|
||||
if _sib_sha_rt is not None:
|
||||
_sub_mod_rt: str = StubMergerStore.LookupModNameBySha1(_sib_sha_rt)
|
||||
if _sub_mod_rt is not None:
|
||||
_dot_rt: str = string.strrchr(_sub_mod_rt, '.')
|
||||
if _dot_rt is not None:
|
||||
_pfx_len_rt: sizet = sizet(uint64(_dot_rt) - uint64(_sub_mod_rt))
|
||||
if _pfx_len_rt > 0:
|
||||
_my_mod_rt: str = StubMergerStore.LookupModNameBySha1(src_sha1)
|
||||
# 确认:本 stub 的模块名必须**恰好等于**该前缀(= 本 stub
|
||||
# 就是包 __init__)。后缀命中可能落到同后缀的别的模块,
|
||||
# 故必须用本 stub 自己的模块名确认,不确认就让位给 rel_path。
|
||||
_conf_rt: int = 0
|
||||
if _my_mod_rt is not None and string.strlen(_my_mod_rt) == _pfx_len_rt:
|
||||
if string.strncmp(_my_mod_rt, _sub_mod_rt, _pfx_len_rt) == 0:
|
||||
_conf_rt = 1
|
||||
if _conf_rt != 0:
|
||||
pkg_prefix_rt = pool.alloc(_pfx_len_rt + 1)
|
||||
if pkg_prefix_rt is not None:
|
||||
string.strncpy(pkg_prefix_rt, _sub_mod_rt, _pfx_len_rt)
|
||||
pkg_prefix_rt[_pfx_len_rt] = '\0'
|
||||
sifr_mod_q: str = sifr_mod
|
||||
if sifr_mod is not None and sifr_mod[0] != '\0' and sifr.level == 1:
|
||||
rel_p_r: str = StubMergerStore.LookupSha1RelPath(src_sha1)
|
||||
if rel_p_r is not None:
|
||||
slash_r: str = string.strrchr(rel_p_r, '/')
|
||||
if slash_r is not None:
|
||||
pkg_len_r: sizet = string.strlen(rel_p_r) - string.strlen(slash_r)
|
||||
if pkg_len_r > 0:
|
||||
qtotal_r: sizet = pkg_len_r + 1 + string.strlen(sifr_mod) + 1
|
||||
qbuf_r: str = pool.alloc(qtotal_r)
|
||||
if qbuf_r is not None:
|
||||
string.strncpy(qbuf_r, rel_p_r, pkg_len_r)
|
||||
qbuf_r[pkg_len_r] = '.'
|
||||
string.strcpy(qbuf_r + pkg_len_r + 1, sifr_mod)
|
||||
sifr_mod_q = qbuf_r
|
||||
if pkg_prefix_rt is not None:
|
||||
qtotal_p: sizet = string.strlen(pkg_prefix_rt) + 1 + string.strlen(sifr_mod) + 1
|
||||
qbuf_p: str = pool.alloc(qtotal_p)
|
||||
if qbuf_p is not None:
|
||||
qbuf_p[0] = '\0'
|
||||
string.strcat(qbuf_p, pkg_prefix_rt)
|
||||
string.strcat(qbuf_p, ".")
|
||||
string.strcat(qbuf_p, sifr_mod)
|
||||
sifr_mod_q = qbuf_p
|
||||
else:
|
||||
rel_p_r: str = StubMergerStore.LookupSha1RelPath(src_sha1)
|
||||
if rel_p_r is not None:
|
||||
slash_r: str = string.strrchr(rel_p_r, '/')
|
||||
if slash_r is not None:
|
||||
pkg_len_r: sizet = string.strlen(rel_p_r) - string.strlen(slash_r)
|
||||
if pkg_len_r > 0:
|
||||
qtotal_r: sizet = pkg_len_r + 1 + string.strlen(sifr_mod) + 1
|
||||
qbuf_r: str = pool.alloc(qtotal_r)
|
||||
if qbuf_r is not None:
|
||||
string.strncpy(qbuf_r, rel_p_r, pkg_len_r)
|
||||
qbuf_r[pkg_len_r] = '.'
|
||||
string.strcpy(qbuf_r + pkg_len_r + 1, sifr_mod)
|
||||
sifr_mod_q = qbuf_r
|
||||
if sifr_mod is not None:
|
||||
stub_imported_modules = _stub_add_imported(pool, stub_imported_modules, sifr_mod_q)
|
||||
# 方案A:包 __init__.vpi 的重导出类以包 sha1 注册别名 entry
|
||||
@@ -2179,16 +2225,26 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*,
|
||||
# sha1 匹配,注册端按定义文件 sha1 → name+sha1 错位 fail-fast。
|
||||
# 仅 __init__ 触发,普通模块 vpi 的跨模块 from-import 不注册,
|
||||
# 避免把类错误归属到当前模块 sha1)
|
||||
# 包 __init__ 判定(**首选不依赖 rel_path 表**):pkg_prefix_rt 非空
|
||||
# 即上面的模块名证据成立——"本 stub 的模块名 == 兄弟模块名去掉末段"
|
||||
# ⇒ 本 stub 就是该包的 __init__(同一事实,不重复计算)。
|
||||
# rel_path 反查取不到条目时原判定恒 false ⇒ 重导出别名静默不登记 ⇒
|
||||
# `ast.ClassDef` 这类限定名在泛型实参里保留**包前缀**,与定义模块
|
||||
# 前缀分裂(现场 ClassGenGeneric.vp:92 的 CastError)。
|
||||
# 仅当模块名证据不足时才回退旧 rel_path 判定(行为不变)。
|
||||
is_pkg_init_rt: int = 0
|
||||
rel_path_rt: str = StubMergerStore.LookupSha1RelPath(src_sha1)
|
||||
if rel_path_rt is not None:
|
||||
# [FIX] 原判定 strcmp(rel_path + strlen(rel_path) - 9, "__init__.vp")
|
||||
# 假设 "__init__.vp" 从 len-9 开始;实际包路径 "ast/__init__.vp"
|
||||
# 长 15,该后缀从索引 4 起(11 字符),len-9=6 指向 "init__.vp"
|
||||
# → 判定恒 false → 包重导出永不触发(ast.Attribute 未注册根因之一)。
|
||||
# 改用 strstr 判定 rel_path 包含 "__init__.vp"(仅包 __init__ 文件命中)。
|
||||
if string.strstr(rel_path_rt, "__init__.vp") is not None:
|
||||
is_pkg_init_rt = 1
|
||||
if pkg_prefix_rt is not None:
|
||||
is_pkg_init_rt = 1
|
||||
if is_pkg_init_rt == 0:
|
||||
rel_path_rt: str = StubMergerStore.LookupSha1RelPath(src_sha1)
|
||||
if rel_path_rt is not None:
|
||||
# [FIX] 原判定 strcmp(rel_path + strlen(rel_path) - 9, "__init__.vp")
|
||||
# 假设 "__init__.vp" 从 len-9 开始;实际包路径 "ast/__init__.vp"
|
||||
# 长 15,该后缀从索引 4 起(11 字符),len-9=6 指向 "init__.vp"
|
||||
# → 判定恒 false → 包重导出永不触发(ast.Attribute 未注册根因之一)。
|
||||
# 改用 strstr 判定 rel_path 包含 "__init__.vp"(仅包 __init__ 文件命中)。
|
||||
if string.strstr(rel_path_rt, "__init__.vp") is not None:
|
||||
is_pkg_init_rt = 1
|
||||
# 解析被 from-import 的模块 sha1:相对导入按 parser 表示判定——
|
||||
# 前导点存于 level(from .exprs import → level=1, module="exprs",
|
||||
# 见 includes/ast/stmts.vp ImportFrom.level / parser.vp _parse_from_import),
|
||||
|
||||
@@ -620,3 +620,34 @@ def LookupSha1RelPath(sha1: str) -> str:
|
||||
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
||||
return StubMerger._sha1_store_rel + ridx
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 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=未找到
|
||||
# ============================================================
|
||||
def LookupModNameBySha1(sha1: str) -> str:
|
||||
"""通过 SHA1 查找模块名,返回只读指针,None=未找到"""
|
||||
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):
|
||||
sidx_mb: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_mb, sha1) == 0:
|
||||
midx_mb: sizet = sizet(i) * 64
|
||||
return StubMerger._sha1_store_mod + midx_mb
|
||||
return None
|
||||
Reference in new issue
Block a user