From bdc5ec3e8810420da07eaf3e5976eabf8b02a245 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 18 Aug 2026 14:38:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(HandlesImports):=20=E8=B7=A8=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=B1=95=E5=B9=B3=E7=88=B6=E7=B1=BB=E6=97=A0=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=20force=20=E9=87=8D=E8=BD=BD=EF=BC=8C=E6=B6=88?= =?UTF-8?q?=E9=99=A4=E5=B9=B6=E8=A1=8C=20worker=20=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E4=B8=8D=E7=A1=AE=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 并行 worker 中 class_members[Generator] 曾出现 83/85/88/89/105/106 vs 114 的不确定变体:父类在某次展平时祖先未加载完成即被冻结为部分成员,guard (成员非空即跳过)又阻止重载 → this_adjust 按不完整 members 计算字节偏移 → 继承方法 self 错位 → 运行时越界崩溃(FuncTest is_exception_name strcmp, 错位 68 字节)。 修复: - 父类加载循环无条件 force 重载:_TryLoadClassMembersFromPyi(force=True) 先清空该类 class_members/defaults/signeds/bitfields/byteorders/bitoffsets 再重建,字段顺序与定义模块 stub 一致;对其祖先递归同样强制重载, 最终所有 worker 展平结果确定性一致。 - 已加载父类经 class_sha1_map 直连定义模块 sha1 强制重载(O(1)), 避免对每个父类全量扫描 ModuleSha1Map。 - _TryLoadClassMembersFromPyi 返回 bool(找到即停),移除 [DIAG-EXT] 诊断。 --- lib/core/Handles/HandlesImports.py | 111 ++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index d9e7688..7c293c6 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -1931,26 +1931,39 @@ class ImportHandle(BaseHandle): _parent_all_placeholders: bool = bool(_parent_members_check) and all( n.startswith('__inherited_') for n, _ in _parent_members_check ) - if not _parent_members_check or _parent_all_placeholders: - # 清除占位条目以便 _TryLoadClassMembersFromPyi 的 guard 通过 - _old_parent_members: list = _parent_members_check - if _parent_all_placeholders: - Gen.class_members[_ParentClass] = [] - _sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) - _parent_loaded: bool = False + # 2026-08-18 关键修复:无条件 force 重载父类成员(此前仅当成员为空/ + # 全占位时才加载)。并行 worker 中父类可能已存在"不完整"的成员表 + # (此前某次展平时祖先未加载完成即冻结部分字段,guard 又阻止重载), + # 导致 class_members[Generator] 83~106 vs 114 不确定 → this_adjust + # 错位 → 继承方法 self 偏移错误 → 运行时越界崩溃。force=True 会先 + # 清空再重建(缺啥补啥、顺序与定义模块一致),且 _EmitExternalClassDeclLlvm + # 对其祖先递归同样强制重载,最终所有 worker 展平结果确定性一致。 + _old_parent_members: list = _parent_members_check + if _parent_all_placeholders: + Gen.class_members[_ParentClass] = [] + _sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) or {} + _parent_found: bool = False + # 已加载过的父类直接用其定义模块 sha1 强制重载(O(1)), + # 避免对每个父类线性扫描全部 ModuleSha1Map。 + _parent_sha1: str | None = getattr(Gen, 'class_sha1_map', {}).get(_ParentClass) + if _parent_sha1: + try: + _parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, f"{_parent_sha1}.stub.ll", Gen, force=True) + except Exception: + _parent_found = False + else: for _mod_name, _mod_sha1 in _sha1_map.items(): _stub_name: str = f"{_mod_sha1}.stub.ll" try: - self._TryLoadClassMembersFromPyi(_ParentClass, _stub_name, Gen) + _parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, _stub_name, Gen, force=True) except Exception: - pass - _parent_members_check = Gen.class_members.get(_ParentClass, []) - if _parent_members_check and not all(n.startswith('__inherited_') for n, _ in _parent_members_check): - _parent_loaded = True + _parent_found = False + if _parent_found: break - if not _parent_loaded and _old_parent_members: - # 加载失败,回滚到旧占位条目(有总比没有好) - Gen.class_members[_ParentClass] = _old_parent_members + _parent_members_check = Gen.class_members.get(_ParentClass, []) + if not _parent_found and _old_parent_members: + # 加载失败(父类不在任何 pyi 中,如泛型特化基类),回滚旧条目(有总比没有好) + Gen.class_members[_ParentClass] = _old_parent_members if _ParentClass in Gen.class_members: # 按继承源去重(与 HandlesClassDef._FlattenInherited 一致,2026-08-18): # 菱形继承(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase, @@ -2257,11 +2270,48 @@ class ImportHandle(BaseHandle): except KeyError: pass - def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin) -> None: + def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin, + force: bool = False) -> bool: + """从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。 + + force=True 时(跨模块展平父类的强制重载):先清空该类的成员数据再重建, + 保证并行 worker 间展平结果确定性一致(缺啥补啥、字段顺序与定义模块一致), + 避免此前"部分成员被冻结、guard 阻止重载"导致的 class_members[Generator] + 83~106 vs 114 不确定变体(this_adjust 错位崩溃)。 + """ ProjectRoot = self._find_project_root() temp_dir = os.path.join(ProjectRoot, 'temp') - if class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0: - return + if (not force) and class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0: + # guard 命中:class_members 已存在(可能从别处预置、不完整),但 + # class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names + # 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114) + # → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载 + # class_members,避免覆盖可能更完整的已有数据)。 + try: + _pyi_b0: str = stub_filename.replace('.stub.ll', '.vpi') + _path0: str = os.path.join(temp_dir, _pyi_b0) + if os.path.isfile(_path0): + _tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0) + if _tree0 is None: + with open(_path0, 'r', encoding='utf-8') as _fh0: + _tree0 = ast.parse(_fh0.read()) + getattr(self, '_pyi_cache', {})[_path0] = _tree0 + if _tree0 is not None: + for _n0 in _tree0.body: + if isinstance(_n0, ast.ClassDef) and _n0.name == class_name: + _b0: list[str] = [] + for _bb in _n0.bases: + _bn0: str | None = getattr(_bb, 'id', None) or getattr(_bb, 'attr', None) + if _bn0 and _bn0 not in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): + _b0.append(_bn0) + if _b0: + if not hasattr(Gen, 'class_parents'): + Gen.class_parents = {} + Gen.class_parents[class_name] = _b0 + break + except Exception: + pass + return False pyi_basename = stub_filename.replace('.stub.ll', '.vpi') pyi_path = os.path.join(temp_dir, pyi_basename) if not os.path.isfile(pyi_path): @@ -2276,18 +2326,18 @@ class ImportHandle(BaseHandle): if pyi_path not in self._pyi_cache: if not os.path.isfile(pyi_path): self._pyi_cache[pyi_path] = None - return + return False try: with open(pyi_path, 'r', encoding='utf-8') as f: self._pyi_cache[pyi_path] = ast.parse(f.read()) except Exception as e: _vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e) self._pyi_cache[pyi_path] = None - return + return False pyi_tree = self._pyi_cache[pyi_path] if pyi_tree is None: - return + return False source_sha1 = stub_filename.replace('.stub.ll', '') module_name = None if hasattr(Gen, 'ModuleSha1Map'): @@ -2319,11 +2369,22 @@ class ImportHandle(BaseHandle): found = True break if not found: - return + return False + if force: + # 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。 + # 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。 + Gen.class_members[class_name] = [] + for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields', + 'class_member_byteorders', 'class_member_bitoffsets'): + _gd: dict | None = getattr(Gen, _gd_attr, None) + if _gd is not None: + _gd.pop(class_name, None) + if hasattr(Gen, 'class_parents'): + Gen.class_parents.pop(class_name, None) + if hasattr(Gen, 'class_parent'): + Gen.class_parent.pop(class_name, None) self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name, source_sha1=source_sha1) - if class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0: - return - return + return True def _BuildScalarConstant(self, value_node: ast.AST, target_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Constant | None: """Build a scalar constant value from AST node, delegated to unified method."""