fix(HandlesExprCall): this_adjust 字段偏移计算剥离 sha1 前缀

register_reexport 等继承方法调用时 ClassName 带 sha1 前缀(如 e1ea7eaf63a9c3d1.Generator),而 class_members 的 key 是裸类名 → _compute_field_byte_offset 查不到字段 → this_adjust 被跳过 → 继承方法 self 不调整 → 方法内按 ImportsGenMixin 独立布局(0-3)访问 Generator 内存错位 → ReexportSrcs 读到 null 后写 [63] 越界(ASAN 写 0x3f 崩溃)。修复:查 class_members/structs 前剥离前缀(对齐 _EnsureClassParentsLoaded 做法)。
This commit is contained in:
TermiNexus committed 2026-08-18 13:32:01 +08:00
1 parent 7c6a497111
commit 97b714603d
1 file changed
+55 -5
+55 -5
View File
@@ -2432,10 +2432,20 @@ class ExprCallHandle(BaseHandle):
有 vtable 的类 struct 首元素为 vtable 指针(i8*),class_members 不含它。
返回 None 表示无法计算(struct 未定义 / 字段不存在)。
"""
# class_members 的 key 是裸类名,而 ClassName 可能带 sha1 前缀
# (跨模块 ObjVal 类型带前缀,如 "e1ea7eaf63a9c3d1.Generator")。
# 不剥离则 members 为空 → 找不到字段 → this_adjust 被跳过 → 继承方法
# self 不调整 → 运行时错位崩溃(register_reexport 写 0x3f 越界即此)。
lookup_cls: str = ClassName
if len(ClassName) > 17 and ClassName[16] == '.':
lookup_cls = ClassName[17:]
st = Gen.structs.get(ClassName)
if (not st or not isinstance(st, ir.IdentifiedStructType) or st.elements is None) \
and lookup_cls != ClassName:
st = Gen.structs.get(lookup_cls)
if not st or not isinstance(st, ir.IdentifiedStructType) or st.elements is None:
return None
members: list = Gen.class_members.get(ClassName, [])
members: list = Gen.class_members.get(lookup_cls, [])
has_vtable: bool = (ClassName in Gen.class_vtable
or ClassName in Gen._cross_module_vtable_classes)
base: int = 1 if has_vtable else 0
@@ -2493,7 +2503,10 @@ class ExprCallHandle(BaseHandle):
"""
if method_anc is None or method_anc == ClassName:
return ObjVal
anc_members: list = Gen.class_members.get(method_anc, [])
anc_lookup: str = method_anc
if len(method_anc) > 17 and method_anc[16] == '.':
anc_lookup = method_anc[17:]
anc_members: list = Gen.class_members.get(anc_lookup, [])
if not anc_members:
return ObjVal
first_field_name: str | None = None
@@ -3289,15 +3302,52 @@ class ExprCallHandle(BaseHandle):
attr_name = arg_node.attr
len_member = f'{attr_name}__len'
current_class = self.Trans._CurrentCpythonObjectClass
if current_class and current_class in Gen.class_members:
for m_name, m_type in Gen.class_members[current_class]:
# 关键:_CurrentCpythonObjectClass 可能带 sha1 前缀(如
# 'a351387236c6ce1c.TypeGenCType'),而 class_members /
# class_member_element_class 的 key 全部是裸类名。必须先把前缀剥离
# 再做 `in Gen.class_members` 检查——否则带前缀时 gate 直接 miss,
# 整个 self.attr 分支(含 list.__len__ 修复)被跳过,fall through
# 到 strlen(stdint.vp 0xC0000005 崩溃根因)。
bare_cls: str | None = current_class
if bare_cls and len(bare_cls) > 17 and bare_cls[16] == '.':
bare_cls = bare_cls[17:]
if bare_cls and bare_cls in Gen.class_members:
for m_name, m_type in Gen.class_members[bare_cls]:
if m_name == len_member:
self_val = Gen._get_var_ptr('self')
if self_val:
self_ptr = Gen._load(self_val, name="self_for_len")
offset = Gen._get_member_offset(len_member, current_class)
offset = Gen._get_member_offset(len_member, bare_cls)
len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"self_{len_member}_ptr")
return Gen._load(len_ptr, name=f"self_{len_member}")
# 修复 stdint.vp 崩溃(0xC0000005)根因:`list[X] | t.CPtr` 联合字段
# 的 LLVM 类型退化为 i8*,_HandleLenLlvm 无法识别为 list,fall through
# 到 strlen 分支 → 对 list 对象指针执行 strlen → 垃圾长度 → 越界 get
# → 垃圾指针 → strcmp 崩。此处按字段静态类型(class_member_element_class
# 记录的特化名,如 'list[str]',沿 MRO 链查找含父类)分派到 list.__len__。
field_cls: str | None = None
_elem_map: dict = getattr(Gen, 'class_member_element_class', {})
for _mro_cls in Gen._get_mro_chain(bare_cls):
# class_member_element_class 的 key 是裸类名(如 'TypeGenBase'),
# _get_mro_chain 在输入裸名时也返回裸名链;再保险剥一次前缀。
_mro_key: str = _mro_cls
if len(_mro_cls) > 17 and _mro_cls[16] == '.':
_mro_key = _mro_cls[17:]
_sub: dict = _elem_map.get(_mro_key, {})
if attr_name in _sub:
field_cls = _sub[attr_name]
break
if field_cls and (field_cls == 'list' or field_cls.startswith('list[')):
self_val = Gen._get_var_ptr('self')
if self_val:
self_ptr = Gen._load(self_val, name="self_for_len")
offset = Gen._get_member_offset(attr_name, bare_cls)
if offset is not None:
list_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"self_{attr_name}_ptr")
list_val = Gen._load(list_ptr, name=f"self_{attr_name}")
result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen)
if result is not None:
return result
val = self.HandleExprLlvm(arg_node)
if not val:
return None