fix(transpyv): 修正 mark_module_class 的表生命周期(ASan 悬空键崩溃)
类别:a(编译器缺陷 —— 上一提交自引入的跨文件悬空 HashTable) 现场:ASan access-violation(READ @0x1),rcx=0x1 string.strcmp (includes/string.vp:86) → HashTable._find_slot (_resize) → __setitem__ → set_int → StructGenMixin.mark_module_class → ClassGenDef.translate_class_def → TranslatorGenMixin.translate_children → Translator.translate → RunPhase1 → main 根因:`ModuleClasses` 原先在 `mark_module_class` 里**懒创建**,并长期挂在 Gen 上; 表体与键都取自当次文件的 pool,而 pool 在文件/遍切换时会被 reset ⇒ 下一个文件里 `_resize` 重新散列旧键时 strcmp 读到已回收内存(0x1)⇒ 崩溃。 改动(与既有 `VisibleStructs` 同口径:表与键同池、同文件生命周期): 1. `ModuleClasses` 改由 `reset_visible_structs`(每文件翻译前调用)**无条件新建** (不再区分 strict 分支),与键同池 ⇒ 无悬空。 2. `CurDefModuleSha1` 指向 `trans.ModuleSha1`(同一 pool 的串),同样在该函数里 清空,避免下一次 strcat/strcmp 读已回收内存。 3. `mark_module_class` 不再懒建表:池或表缺失时只记 `CurDefModuleSha1`、跳过登记 (宁可退化为改前行为,也不冒悬空风险)。 语义不变:规则 5.5 的证据链仍是 ① 结构体表按名(跨文件/跨遍)→ ② 正在定义类所属 模块 + find_struct_by_module → ③ 本文件已声明类归属表(自引用字段窗口 —— 登记与 查询在同一文件同一遍内,正是该表的作用域)。
This commit is contained in:
1 parent
2b8cc2a69b
commit
1b2324edab
1 file changed
+23
-9
@@ -153,8 +153,18 @@ novirtual class StructGenMixin:
|
||||
|
||||
pool: 内存分配器(严格模式下创建 HashTable)
|
||||
strict: 1=严格模式(用户文件),0=宽松模式(includes 文件)
|
||||
|
||||
`ModuleClasses` 也在此**每文件重建**:它的键是 pool 字符串,而 pool 在
|
||||
文件/遍切换时会被 reset ⇒ 若表跨文件长期存活,`_find_slot` 会 strcmp 到
|
||||
已回收的键(现场:ASan access-violation,rcx=0x1,栈
|
||||
mark_module_class → HashTable.set_int → _resize → _find_slot → strcmp)。
|
||||
与 `VisibleStructs` 同口径(同样每文件新建、键同池),故无悬空。
|
||||
`CurDefModuleSha1` 指向 `trans.ModuleSha1`(同一 pool 的串),同样必须
|
||||
每文件清空,否则下一次 strcat/strcmp 读的是已回收内存。
|
||||
"""
|
||||
self.StrictVisibility = strict
|
||||
self.CurDefModuleSha1 = None
|
||||
self.ModuleClasses = hashtable.HashTable(pool)
|
||||
if strict != 0:
|
||||
# 严格模式:创建新的 HashTable 记录可见结构体
|
||||
self.VisibleStructs = hashtable.HashTable(pool)
|
||||
@@ -195,18 +205,22 @@ novirtual class StructGenMixin:
|
||||
def mark_module_class(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> None:
|
||||
"""登记"{sha1}.{类名}":**该模块声明了这个类**(类定义入口调用一次)。
|
||||
|
||||
键里带 SHA1 ⇒ 天然按"声明模块"区分,且与翻译顺序/模块切换无关(不需要
|
||||
每文件重置):即使表里残留别的模块的登记,查询方只会命中
|
||||
(它自己那一对 sha1+类名),不会把别的模块的类错认成自己的。
|
||||
键里带 SHA1 ⇒ 天然按"声明模块"区分。表由 `reset_visible_structs` **每文件
|
||||
重建**(与键同池 ⇒ 无悬空),因此这里的登记只在本文件本遍内有效 ——
|
||||
恰好覆盖使用者(`_norm_type_arg_name` 规则 5.5)的窗口:判"类定义中"的
|
||||
自引用字段时,登记与查询在同一文件同一遍内。
|
||||
|
||||
`CurDefModuleSha1` 同步记录("正在定义类所属模块"的权威来源,任何翻译
|
||||
路径上都是 trans.ModuleSha1)。
|
||||
"""
|
||||
if pool is None or sha1 is None or name is None:
|
||||
if sha1 is None or name is None:
|
||||
return
|
||||
# 同步记录"正在定义类所属模块"(任何翻译路径上唯一的权威来源)
|
||||
self.CurDefModuleSha1 = sha1
|
||||
if self.ModuleClasses is None:
|
||||
self.ModuleClasses = hashtable.HashTable(pool)
|
||||
if self.ModuleClasses is None:
|
||||
return
|
||||
if pool is None or self.ModuleClasses is None:
|
||||
# 未按文件初始化(该路径未调用 reset_visible_structs):不登记。
|
||||
# 不在此懒建表 —— 表必须与键同池、且随文件重置,否则会 strcmp 到
|
||||
# 已回收的键(ASan access-violation 现场)。
|
||||
return
|
||||
k_buf: str = self._module_class_key(pool, sha1, name)
|
||||
if k_buf is None:
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user