只为定位"跨模块函数形参退化成 i32*"(现场 function_get_name 第 0 形参 i32*,
实参 %"46c7dc5230ea5a52.Function"*)。静态排查已穷尽:_ctype_to_llvm /
_type_str_to_llvm 产生 i32* 的出口只有「BaseType 退化成 i32 + PtrCount=1」
(含 CTypeInfo.ToString() 的 'CInt *' → i32*)与「IsEnum + Name + PtrCount=1」,
离线复现不出,需要运行时证据。
做法:把判定依据直接拼进 FailFastError 报文,使其随 Traceback 一起被看到:
[DIAG-decl] 实参类 / structs / _struct_sha1_map / class_sha1_map /
symtab 标志 / 声明函数类型 / 声明来源(创建者调用栈)
并在四个声明创建点记录首个创建者:
FuncGen._get_or_declare_func / get_or_declare_c_func、
BaseGen._get_or_declare_function、HandlesBase.GetOrCreateFuncDecl(会替换声明)。
定位完成后整体回滚(连同记录点)。
905 lines
59 KiB
Python
905 lines
59 KiB
Python
from __future__ import annotations
|
||
from typing import Any
|
||
import vast as ast
|
||
import os
|
||
import re
|
||
import sys
|
||
import llvmlite.ir as ir
|
||
from lib.core.Handles.HandlesBase import CTypeInfo
|
||
from lib.core.VLogger import get_logger as _vlog
|
||
from lib.constants.config import mode as _config_mode
|
||
|
||
|
||
class FailFastError(Exception):
|
||
"""专用 fail-fast 异常:编译器主动抛出的编译错误(不可隐式转换/不可恢复)。
|
||
|
||
刻意与 llvmlite 自身抛出的 TypeError/ValueError 区分开 —— 后者属于"此处可以尝试
|
||
简化回退"的类型调整失败,本异常则是必须上抛、禁止被兜底 `except` 吞掉的信号
|
||
(例如 `_adjust_args` 的 L120/L181 两条 fail-fast)。
|
||
"""
|
||
|
||
|
||
class MemberLookupFailFast(KeyError):
|
||
"""成员偏移查找失败的 fail-fast 异常(**继承 KeyError** 以保持既有兜底点可用)。
|
||
|
||
为什么继承 KeyError:本仓有两处 `except KeyError` 把 `_get_member_offset` 的
|
||
失败当作"该字段类型未知"继续回查(HandlesExprAttr 的字段类型三级回退),改成
|
||
其它异常类型会把这些兜底点击穿。
|
||
|
||
为什么重写 `__str__`:KeyError 的默认 `__str__` 是 `repr(消息)` —— 会在报文两端
|
||
加引号、并把换行/制表转义成字面 `\\n`,多行 fail-fast 报文(含"源代码 (文件, 行号):
|
||
行号 | 源码")因此彻底不可读。这里原样返回裸消息,与 FailFastError 的观感一致。
|
||
"""
|
||
|
||
def __str__(self) -> str:
|
||
if self.args and isinstance(self.args[0], str):
|
||
return self.args[0]
|
||
return super().__str__()
|
||
|
||
|
||
class FuncGenMixin:
|
||
|
||
def setup_from_symbol_table(self, SymbolTable: Any) -> None:
|
||
self.SymbolTable: Any = SymbolTable
|
||
for name, info in SymbolTable.items():
|
||
if isinstance(info, CTypeInfo):
|
||
# CTypeInfo 对象:struct/union 由 StructGen 处理,此处只处理 dict 格式
|
||
continue
|
||
if not isinstance(info, dict):
|
||
if hasattr(info, 'get'):
|
||
info = dict(info) if hasattr(info, '__iter__') else {}
|
||
else:
|
||
continue
|
||
TypeKind: str = info.get('type', '')
|
||
if TypeKind == 'struct':
|
||
ClassName: str = name
|
||
self.class_methods[ClassName] = []
|
||
self.class_members[ClassName] = []
|
||
members: dict[str, Any] = info.get('members', {})
|
||
if members:
|
||
for MemberName, MemberInfo in members.items():
|
||
if isinstance(MemberInfo, dict):
|
||
MemberType: ir.Type = self._type_str_to_llvm(MemberInfo.get('type', 'int'), MemberInfo.get('IsPtr', False))
|
||
self.class_members[ClassName].append((MemberName, MemberType))
|
||
elif isinstance(MemberInfo, CTypeInfo):
|
||
MemberType: ir.Type = self._ctype_to_llvm(MemberInfo)
|
||
if MemberType is None:
|
||
MemberType = self._type_str_to_llvm(MemberInfo.ToString(), MemberInfo.IsPtr)
|
||
self.class_members[ClassName].append((MemberName, MemberType))
|
||
self._generate_structs()
|
||
self._create_Vtable_globals()
|
||
|
||
def register_method(self, ClassName: str, MethodName: str, IsVtableSlot: bool = True) -> None:
|
||
"""登记类方法。
|
||
|
||
IsVtableSlot:该方法是否占用虚表槽(class_methods 即 vtable 槽序唯一真源)。
|
||
默认 True 与既有语义逐字一致;False 用于方法级 `novirtual def`(⇒ 方法装饰器
|
||
@t.NoVTable)——此时只保证类的方法容器存在,**不**把它排进槽序,使 TPC 与
|
||
TPV(ClassGenBase._should_method_be_virtual 排除该项)的虚表布局逐槽相同。
|
||
"""
|
||
if ClassName not in self.class_methods:
|
||
self.class_methods[ClassName] = []
|
||
if ClassName not in self.class_members:
|
||
self.class_members[ClassName] = []
|
||
self._generate_structs()
|
||
self._create_Vtable_globals()
|
||
# 构造函数(__new__/__init__/__before_init__)不放入 vtable,
|
||
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移
|
||
_short_name: str = MethodName.split('.')[-1] if '.' in MethodName else MethodName
|
||
if _short_name in ('__new__', '__init__', '__before_init__'):
|
||
return
|
||
if not IsVtableSlot:
|
||
return
|
||
if MethodName not in self.class_methods[ClassName]:
|
||
self.class_methods[ClassName].append(MethodName)
|
||
|
||
def _apply_auto_addr(self, func_name: str, args: list[ir.Value]) -> list[ir.Value]:
|
||
"""对函数参数应用自动取地址逻辑(t.CNeedPtr / t.CAutoPtr)。
|
||
|
||
在调用前对标记为 auto-addr 的参数自动取地址:
|
||
- 'need' (t.CNeedPtr): 仅对非指针值取地址(alloca + store + get pointer)
|
||
- 'always' (t.CAutoPtr): 无条件取地址
|
||
"""
|
||
modes: list[str | None] | None = self._auto_addr_params.get(func_name)
|
||
if not modes:
|
||
return args
|
||
new_args: list[ir.Value] = list(args)
|
||
for i, mode in enumerate(modes):
|
||
if i >= len(new_args) or mode is None:
|
||
continue
|
||
arg: ir.Value = new_args[i]
|
||
is_ptr: bool = isinstance(arg.type, ir.PointerType)
|
||
if mode == 'always' or (mode == 'need' and not is_ptr):
|
||
# alloca + store + get pointer
|
||
slot: ir.Value = self.builder.alloca(arg.type)
|
||
self.builder.store(arg, slot)
|
||
new_args[i] = slot
|
||
return new_args
|
||
|
||
def _RecordDeclFrom(self, name: str, func_type: Any) -> None:
|
||
"""[DIAG-decl] 临时诊断:记录函数声明的**首个创建来源**(调用栈 + 函数类型)。
|
||
|
||
用于定位"形参 i32* vs 实参 %sha1.CLS*"这类跨层不一致是谁创建的声明
|
||
(import 声明发射器 / stub 解析 / 符号表签名 / 继承包装 等)。
|
||
只记录一次(首个创建者即最终生效者)。定位完成后连同
|
||
_DescribeI32ParamDiag 与两处调用点一起删除。
|
||
"""
|
||
try:
|
||
_reg = getattr(self, '_decl_from', None)
|
||
if _reg is None:
|
||
_reg = {}
|
||
self._decl_from = _reg
|
||
if name in _reg:
|
||
return
|
||
import traceback as _tb
|
||
_reg[name] = ' <- '.join(
|
||
'%s:%d:%s' % (f.filename.replace('\\', '/').rsplit('/', 1)[-1], f.lineno, f.name)
|
||
for f in _tb.extract_stack()[-7:-1])
|
||
except Exception:
|
||
pass
|
||
|
||
def _DescribeI32ParamDiag(self, func: ir.Function, arg: ir.Value, param: ir.Value) -> str:
|
||
"""[DIAG-decl] 临时诊断:形参退化成"指向 i32 的指针"时,拼出判定依据。
|
||
|
||
只在"形参是 i32* 且实参是指向具体类的指针"时输出,随 fail-fast 报文一起
|
||
显示(避免探针输出被忽略)。定位完成后删除。
|
||
"""
|
||
try:
|
||
_p_poi: Any = getattr(param.type, 'pointee', None)
|
||
if not (isinstance(_p_poi, ir.IntType) and _p_poi.width == 32):
|
||
return ''
|
||
_a_poi: Any = getattr(arg.type, 'pointee', None)
|
||
_full: str | None = getattr(_a_poi, 'name', None)
|
||
if not _full:
|
||
return ''
|
||
_short: str = _full.split('.')[-1]
|
||
_sym: Any = getattr(self, 'SymbolTable', None)
|
||
_e: Any = None
|
||
try:
|
||
_e = _sym.lookup(_short) if _sym else None
|
||
except Exception:
|
||
_e = None
|
||
_reg: dict = getattr(self, '_decl_from', {}) or {}
|
||
_from: str = _reg.get(func.name) or _reg.get(_short) or '未知'
|
||
return (
|
||
f"\n [DIAG-decl] 实参类={_full}"
|
||
f" | structs={'有' if _short in self.structs else '无'}"
|
||
f" | _struct_sha1_map={getattr(self, '_struct_sha1_map', {}).get(_short)!r}"
|
||
f" | class_sha1_map={getattr(self, 'class_sha1_map', {}).get(_short)!r}"
|
||
f" | symtab[IsStruct={getattr(_e, 'IsStruct', None)},IsEnum={getattr(_e, 'IsEnum', None)},"
|
||
f"IsEnumMember={getattr(_e, 'IsEnumMember', None)},IsTypedef={getattr(_e, 'IsTypedef', None)},"
|
||
f"Base={type(getattr(_e, 'BaseType', None)).__name__}]"
|
||
f" | 声明函数类型={func.function_type}"
|
||
f" | 声明来源={_from}")
|
||
except Exception:
|
||
return ''
|
||
|
||
def _adjust_args(self, args: list[ir.Value], func: ir.Function) -> list[ir.Value]:
|
||
adjusted: list[ir.Value] = []
|
||
for i, (arg, param) in enumerate(zip(args, func.args)):
|
||
if arg.type != param.type:
|
||
if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name):
|
||
# C 语言中数组参数退化为指针,不做 load 而是退化为元素指针
|
||
if isinstance(param.type, ir.ArrayType):
|
||
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
|
||
elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}")
|
||
adjusted.append(elem_ptr)
|
||
else:
|
||
adjusted.append(self._load(arg, name=f"Load_arg_{i}"))
|
||
continue
|
||
try:
|
||
if isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.PointerType):
|
||
if isinstance(param.type.pointee, ir.IntType) and param.type.pointee.width == 8 and arg.type.width == 8:
|
||
# 当参数是 i8(字符值)而函数期望 i8*(字符串指针)时
|
||
# 为字符值分配内存,创建 null-terminated 字符串
|
||
tmp: ir.AllocaInstr = self._alloca(ir.ArrayType(ir.IntType(8), 2), name=f"char2str_arg_{i}")
|
||
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
|
||
char_ptr: ir.Value = self.builder.gep(tmp, [zero, zero], name=f"char2str_ptr_{i}")
|
||
self._store(arg, char_ptr)
|
||
null_ptr: ir.Value = self.builder.gep(tmp, [zero, ir.Constant(ir.IntType(32), 1)], name=f"char2str_null_{i}")
|
||
self._store(ir.Constant(ir.IntType(8), 0), null_ptr)
|
||
adjusted.append(char_ptr)
|
||
else:
|
||
if isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType):
|
||
# 当参数是指针而函数期望整数时,使用 ptrtoint
|
||
adjusted.append(self.builder.ptrtoint(arg, param.type, name=f"ptrtoint_arg_{i}"))
|
||
else:
|
||
if arg.type.width < 32:
|
||
arg = self.builder.zext(arg, ir.IntType(32), name=f"zext_arg_{i}")
|
||
adjusted.append(self.builder.inttoptr(arg, param.type, name=f"inttoptr_arg_{i}"))
|
||
elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType):
|
||
# fail-fast:指针→整数隐式转换(跨类型层级,有损)禁止,
|
||
# 须显式转换(用户隐式转换规则:同层指针可转,指针→整数不允许)。
|
||
# 诊断增强(只在即将终止时拼串,零行为影响):把 callee 名、
|
||
# 实参/形参个数与**逐项类型**一并报出——只给单个类型对无法判断
|
||
# 「形参表整体错位一位」(如 self/pool 约定不一致)这类根因。
|
||
_diag: str = "; ".join(
|
||
f"[{k}] arg={a.type} param={p.type}"
|
||
for k, (a, p) in enumerate(zip(args, func.args)))
|
||
raise FailFastError(
|
||
f"调用参数类型不匹配: 指针类型 {arg.type} 不能隐式转换为"
|
||
f"整数参数 {param.type};如需转换请显式 cast"
|
||
f"(callee={func.name} 实参{len(args)}个/形参{len(func.args)}个;"
|
||
f"逐项: {_diag})")
|
||
elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType):
|
||
if isinstance(arg.type.pointee, ir.PointerType) and isinstance(param.type.pointee, ir.IntType):
|
||
Loaded: ir.Value = self._load(arg, name=f"Load_arg_{i}")
|
||
adjusted.append(Loaded)
|
||
elif isinstance(arg.type.pointee, ir.ArrayType) and isinstance(param.type.pointee, (ir.IntType, ir.FloatType, ir.DoubleType)):
|
||
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
|
||
elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}")
|
||
if elem_ptr.type != param.type:
|
||
adjusted.append(self.builder.bitcast(elem_ptr, param.type, name=f"cast_arg_{i}"))
|
||
else:
|
||
adjusted.append(elem_ptr)
|
||
elif isinstance(arg.type.pointee, ir.ArrayType) and isinstance(param.type.pointee, ir.PointerType):
|
||
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
|
||
elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}")
|
||
if elem_ptr.type != param.type:
|
||
adjusted.append(self.builder.bitcast(elem_ptr, param.type, name=f"cast_arg_{i}"))
|
||
else:
|
||
adjusted.append(elem_ptr)
|
||
else:
|
||
# 新增指针隐式转换规则①/②:通用指针通配放行;非通用
|
||
# T*→U* 仅在无损时放行,非基本/有损/层数不同 fail-fast。
|
||
Allowed: bool
|
||
Reason: str
|
||
Allowed, Reason = self._ClassifyPtrImplicitConvert(arg.type, param.type)
|
||
if not Allowed:
|
||
# [DIAG-decl] 临时诊断(定位"形参 i32* vs 实参 %sha1.CLS*"):
|
||
# 把判定依据直接拼进报文,使其随 Traceback 一起被看到;
|
||
# 定位完成后连同 _RecordDeclFrom 调用点一起删除。
|
||
_diag_extra: str = self._DescribeI32ParamDiag(func, arg, param)
|
||
raise FailFastError(
|
||
f"指针隐式转换已禁止({Reason}):调用 {func.name} 第 {i} 个实参"
|
||
f" {arg.type} 不能隐式转为形参 {param.type};如需转换请显式 cast"
|
||
f"(如 t.CPtr(...)){self._DescribeValueNode()}{_diag_extra}")
|
||
adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}"))
|
||
else:
|
||
if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name):
|
||
adjusted.append(self._load(arg, name=f"Load_arg_{i}"))
|
||
elif isinstance(arg.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
tmp: ir.AllocaInstr = self._alloca(param.type, name=f"temp_arg_{i}")
|
||
Loaded: ir.Value = self.builder.bitcast(arg, ir.PointerType(param.type), name=f"cast_arg_{i}")
|
||
Loaded_val: ir.Value = self._load(Loaded, name=f"Load_arg_{i}")
|
||
self._store(Loaded_val, tmp)
|
||
adjusted.append(self._load(tmp, name=f"reLoad_arg_{i}"))
|
||
else:
|
||
tmp: ir.AllocaInstr = self._alloca(param.type, name=f"temp_arg_{i}")
|
||
cast_ptr: ir.Value = self.builder.bitcast(arg, ir.PointerType(param.type), name=f"cast_arg_{i}")
|
||
Loaded_val: ir.Value = self._load(cast_ptr, name=f"Load_arg_{i}")
|
||
self._store(Loaded_val, tmp)
|
||
adjusted.append(self._load(tmp, name=f"reLoad_arg_{i}"))
|
||
elif isinstance(arg.type, ir.PointerType) and arg.type.pointee == param.type:
|
||
adjusted.append(self._load(arg, name=f"Load_arg_{i}"))
|
||
elif isinstance(param.type, ir.PointerType) and param.type.pointee == arg.type:
|
||
tmp: ir.AllocaInstr = self._alloca(arg.type, name=f"temp_arg_{i}")
|
||
self._store(arg, tmp)
|
||
adjusted.append(tmp)
|
||
elif isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.IntType):
|
||
if arg.type.width < param.type.width:
|
||
# 整数扩展:使用 sext(符号扩展)以正确处理负数
|
||
adjusted.append(self.builder.sext(arg, param.type, name=f"sext_arg_{i}"))
|
||
else:
|
||
adjusted.append(self.builder.trunc(arg, param.type, name=f"trunc_arg_{i}"))
|
||
elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)) and isinstance(param.type, (ir.FloatType, ir.DoubleType)):
|
||
if isinstance(arg.type, ir.DoubleType) and isinstance(param.type, ir.FloatType):
|
||
adjusted.append(self.builder.fptrunc(arg, param.type, name=f"fptrunc_arg_{i}"))
|
||
elif isinstance(arg.type, ir.FloatType) and isinstance(param.type, ir.DoubleType):
|
||
adjusted.append(self.builder.fpext(arg, param.type, name=f"fpext_arg_{i}"))
|
||
else:
|
||
adjusted.append(arg)
|
||
elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)) and isinstance(param.type, ir.IntType):
|
||
# fail-fast:浮点→整数隐式转换(有损)禁止,须显式转换
|
||
# (用户隐式转换规则:浮点→整数不允许)。
|
||
raise FailFastError(
|
||
f"调用参数类型不匹配: 浮点类型 {arg.type} 不能隐式转换为"
|
||
f"整数参数 {param.type};如需转换请显式 cast")
|
||
elif isinstance(arg.type, ir.IntType) and isinstance(param.type, (ir.FloatType, ir.DoubleType)):
|
||
adjusted.append(self.builder.sitofp(arg, param.type, name=f"sitofp_arg_{i}"))
|
||
elif isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.PointerType):
|
||
# 整数转指针:inttoptr
|
||
if arg.type.width < 64:
|
||
i64_val: ir.Value = self.builder.sext(arg, ir.IntType(64), name=f"sext_arg_{i}")
|
||
adjusted.append(self.builder.inttoptr(i64_val, param.type, name=f"inttoptr_arg_{i}"))
|
||
else:
|
||
adjusted.append(self.builder.inttoptr(arg, param.type, name=f"inttoptr_arg_{i}"))
|
||
elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType):
|
||
# 指针转整数:ptrtoint
|
||
i64_val: ir.Value = self.builder.ptrtoint(arg, ir.IntType(64), name=f"ptrtoint_arg_{i}")
|
||
if i64_val.type.width > param.type.width:
|
||
adjusted.append(self.builder.trunc(i64_val, param.type, name=f"trunc_arg_{i}"))
|
||
elif i64_val.type.width < param.type.width:
|
||
adjusted.append(self.builder.sext(i64_val, param.type, name=f"sext_arg_{i}"))
|
||
else:
|
||
adjusted.append(i64_val)
|
||
else:
|
||
adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}"))
|
||
# 只收口 llvmlite 自身"可回退"的类型调整失败(builder/IR 构造抛出的
|
||
# TypeError/ValueError);FailFastError 与其余异常一律上抛,
|
||
# 避免编译器主动抛出的真因被伪装成 llvmlite 的下游报文。
|
||
except (TypeError, ValueError): # 参数类型调整失败时使用简化回退逻辑
|
||
if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name):
|
||
adjusted.append(self._load(arg, name=f"Load_arg_{i}"))
|
||
else:
|
||
adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}"))
|
||
elif isinstance(arg.type, ir.PointerType) and arg.type.pointee == param.type:
|
||
adjusted.append(self._load(arg, name=f"Load_arg_{i}"))
|
||
elif isinstance(param.type, ir.PointerType) and param.type.pointee == arg.type:
|
||
tmp: ir.AllocaInstr = self._alloca(arg.type, name=f"temp_arg_{i}")
|
||
self._store(arg, tmp)
|
||
adjusted.append(tmp)
|
||
elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType):
|
||
# 任意指针到指针的类型转换(如 struct* -> i8*)
|
||
adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}"))
|
||
else:
|
||
adjusted.append(arg)
|
||
else:
|
||
adjusted.append(arg)
|
||
while len(adjusted) < len(func.args):
|
||
missing_idx: int = len(adjusted)
|
||
param: ir.Argument = func.args[missing_idx]
|
||
param_type: ir.Type = param.type if hasattr(param, 'type') else param
|
||
if isinstance(param_type, ir.PointerType):
|
||
adjusted.append(ir.Constant(param_type, None))
|
||
elif isinstance(param_type, ir.IntType):
|
||
adjusted.append(ir.Constant(param_type, 0))
|
||
elif isinstance(param_type, (ir.FloatType, ir.DoubleType)):
|
||
adjusted.append(ir.Constant(param_type, 0.0))
|
||
else:
|
||
adjusted.append(ir.Constant(ir.IntType(32), 0))
|
||
if func.type.pointee.var_arg:
|
||
for i in range(len(func.args), len(args)):
|
||
arg: ir.Value = args[i]
|
||
adjusted.append(arg)
|
||
return adjusted
|
||
|
||
def _class_has_vtable(self, ClassName: str | None) -> bool:
|
||
"""类(或其任一祖先)是否带 vtable 槽。
|
||
|
||
与 StructGen.py:442-450 的父链传播同源——vtable 沿继承链携带,子类 struct 布局为
|
||
[vtable?] + [基类字段…] + [自身字段…]。只认 class_vtable /
|
||
_cross_module_vtable_classes(装饰器集合)会漏掉源码未写 @t.CVTable 的跨模块
|
||
AST 节点类(ast.Try / ast.Match / ast.ExceptHandler…):它们有方法、继承自
|
||
@t.CVTable 的 AST,struct 首元素确实是 vtable 指针,但 StructGen.py:415 在
|
||
"struct body 已从 stub 完整加载"时直接 continue,永不把它们登记进集合
|
||
(实测 TransPyV/temp/_shared_sym.json 的 class_vtable/cross_module_vtable 仅有
|
||
{AST, MemManager, Module})。此时 base 静默取 0 → 该类字段偏移整体前移 1
|
||
(ast.ExceptHandler.children 落到 end_col_offset,i32)。
|
||
"""
|
||
if not ClassName:
|
||
return False
|
||
_Seen: set[str] = set()
|
||
_Cur: str | None = ClassName
|
||
while _Cur and _Cur not in _Seen:
|
||
_Seen.add(_Cur)
|
||
if _Cur in self.class_vtable or _Cur in self._cross_module_vtable_classes:
|
||
return True
|
||
_Cur = self.class_parent.get(_Cur)
|
||
return False
|
||
|
||
def _NormalizeMemberClass(self, ClassName: str | None) -> str | None:
|
||
"""成员查询用的类键归一(短名 + 指针包装展开)。
|
||
|
||
- 统一短名解析(治本修复 2026-08-10):struct/class_members/class_vtable/
|
||
class_parent 均以短名注册(跨模块加载 HandlesImports L1604),而调用方
|
||
可能传入带 sha1 前缀的全名(如 "f47f88ae6545965a.Import")。
|
||
- rawptr[X] / t.CPtr[X] 指针包装归一:rawptr[X] 是内建裸指针类型
|
||
(X*),非真实类——接收者注解/类型名为 'rawptr[AST]' 时字段访问
|
||
(child.parent)应作用于内层 struct X,否则查 class_members 全 miss
|
||
→ fast-fail "属性 'parent' 在类 'rawptr[AST]' 中找不到(struct=opaque)"
|
||
(includes/ast/base.vp _set_parent_list bootstrap 根因)。
|
||
"""
|
||
if not ClassName:
|
||
return ClassName
|
||
_Cls: str = self._extract_short_name(ClassName)
|
||
if _Cls.startswith('rawptr[') or _Cls.startswith('t.CPtr['):
|
||
_lb: int = _Cls.find('[')
|
||
if _Cls.endswith(']') and _lb > 0:
|
||
_inner: str = _Cls[_lb + 1:-1].strip()
|
||
_short_inner: str = _inner.rsplit('.', 1)[-1] if '.' in _inner else _inner
|
||
_hit: str | None = None
|
||
if _short_inner in self.structs:
|
||
_hit = _short_inner
|
||
else:
|
||
for _k in self.structs:
|
||
if self._extract_short_name(_k) == _short_inner:
|
||
_hit = _k
|
||
break
|
||
if _hit:
|
||
_Cls = _hit
|
||
return _Cls
|
||
|
||
def _member_base(self, ClassName: str | None) -> int:
|
||
"""类 struct 的成员偏移基址(vtable 占位槽数)——**全仓唯一真源**。
|
||
|
||
`_get_member_offset` 与所有"自算 base 再 GEP 字段"的消费点(构造/初始化、
|
||
字段默认值、继承方法 this 调整等)都必须走本方法,禁止各自直查
|
||
`class_vtable`/`_cross_module_vtable_classes`——旧实现只认这两个装饰器集合,
|
||
跨模块 AST 节点类(ast.Try/ast.Match/ast.ExceptHandler…)源码未写
|
||
@t.CVTable 且 StructGen 在"struct body 已从 stub 加载"时提前 continue,
|
||
永不把它们登记进集合,于是 base 只能靠"成员表长度 == struct 元素数 - 1"
|
||
这个长度启发式救回;成员表未展平/尚未加载时启发式失效 ⇒ base 静默取 0
|
||
⇒ 该类所有字段偏移整体前移 1(ast.ExceptHandler.children 落到
|
||
end_col_offset(i32),len() 分派拿到 i32 ⇒ Type of #1 arg mismatch)。
|
||
|
||
判定规则(与 `StructGen._generate_structs` 生成 struct 的规则同源):
|
||
- vtable 归属沿 class_parent 链判定(见 `_class_has_vtable`);
|
||
- 再用 struct 实际元素数校准:
|
||
delta(=struct 元素数 - 成员表长度) == 0 ⇒ 该 struct 无 vtable 槽 → base=0;
|
||
delta == 1 ⇒ 恰多一个槽:首元素为指针 / 无父类 / 继承链归属成立 → base=1;
|
||
delta >= 2 ⇒ 长度启发式失效,保留"继承链 vtable 归属"结果(不再静默取 0)。
|
||
|
||
查询前会调用 `HandlesImports.EnsureClassMembersFlattened`(幂等)把"停在自有
|
||
字段"的未展平表补齐——未展平表会让下列 delta 校准失真,也会让按表内下标
|
||
取字段的消费点漏掉继承前缀。
|
||
"""
|
||
_Cls: str | None = self._NormalizeMemberClass(ClassName)
|
||
if not _Cls:
|
||
return 0
|
||
# 查询前确保成员表已按父类展平(幂等、顺序无关)。
|
||
# 未展平的表(停在自有字段)会让本方法的 delta 校准失真,也会让所有
|
||
# "按成员表下标 base+i 取字段"的消费点漏掉继承前缀 —— 见
|
||
# HandlesImports.EnsureClassMembersFlattened(内部有纯内存前置判断与
|
||
# 重入/重复尝试保护,对已展平类零额外开销)。
|
||
_EnsureIH: Any = getattr(self, '_import_handler_ref', None)
|
||
if _EnsureIH is not None and hasattr(_EnsureIH, 'EnsureClassMembersFlattened'):
|
||
try:
|
||
_EnsureIH.EnsureClassMembersFlattened(_Cls, self)
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"补齐类成员展平失败: {_e}", "Exception")
|
||
_ByInheritVtable: bool = self._class_has_vtable(_Cls)
|
||
base: int = 1 if _ByInheritVtable else 0
|
||
_StructBase: ir.IdentifiedStructType | None = self.structs.get(_Cls)
|
||
if (isinstance(_StructBase, ir.IdentifiedStructType)
|
||
and _StructBase.elements is not None and len(_StructBase.elements) > 0):
|
||
_NumElems: int = len(_StructBase.elements)
|
||
_NumMembers: int = len(self.class_members.get(_Cls, []))
|
||
_Delta: int = _NumElems - _NumMembers
|
||
_FirstElem: ir.Type = _StructBase.elements[0]
|
||
_IsVtablePtr: bool = (isinstance(_FirstElem, ir.PointerType)
|
||
and isinstance(_FirstElem.pointee, ir.IntType)
|
||
and _FirstElem.pointee.width == 8)
|
||
if _Delta == 0:
|
||
base = 0
|
||
elif _Delta == 1:
|
||
_HasParent: bool = self.class_parent.get(_Cls) is not None
|
||
base = 1 if (_IsVtablePtr or not _HasParent or _ByInheritVtable) else 0
|
||
# ---- 类型一致性校准(治本)----
|
||
# 上面的 delta 长度启发式只在"表与布局恰好差 vtable 一项"时成立:一旦成员表
|
||
# 比布局多/少**非 vtable** 项(跨模块类展平不全、同名类复用等),base 会被判错,
|
||
# 结果是**整个类所有字段偏移整体平移**。实测:`ast.Constant` 的
|
||
# struct = {i8*(vtable), AST*(parent), MemManager*(pool), i32(lineno), …}
|
||
# 而查询 `cnst.lineno` 得到的是 pool 那槽(`%MemManager*`)⇒ 调用点报
|
||
# "形参 lineno: i32 收到指针",掩盖了真因。
|
||
# 校准办法:对 b∈{0,1,2} 逐项比较"成员声明类型 vs elements[b+i] 类型",
|
||
# 取一致度最高者(完全相同 +2;同为指针但类型不同 +0)。正确对齐时
|
||
# 指针槽几乎全中,错位对齐只能靠 i32 撞运气,分数明显更低;平手保留启发式。
|
||
_MembersCal: list = self.class_members.get(_Cls, [])
|
||
if _MembersCal and _NumElems > 0:
|
||
_BestB: int = base
|
||
_BestScore: int = -1
|
||
_HeurScore: int = -1
|
||
_b: int
|
||
for _b in range(0, 3):
|
||
_Score: int = 0
|
||
_i2: int = 0
|
||
for _nm2, _mt2 in _MembersCal:
|
||
_j2: int = _b + _i2
|
||
if _j2 >= _NumElems:
|
||
break
|
||
_et2: ir.Type = _StructBase.elements[_j2]
|
||
if _mt2 is _et2 or str(_mt2) == str(_et2):
|
||
_Score += 2
|
||
_i2 += 1
|
||
if _b == base:
|
||
_HeurScore = _Score
|
||
if _Score > _BestScore:
|
||
_BestScore = _Score
|
||
_BestB = _b
|
||
# 仅当"明显更优"(至少多对齐 1 个成员)才覆盖启发式结果,避免抖动。
|
||
if _HeurScore >= 0 and _BestScore >= _HeurScore + 2:
|
||
base = _BestB
|
||
return base
|
||
|
||
def _get_member_offset(self, field_name: str, ClassName: str | None = None) -> int | None:
|
||
# base 判定用未归一的原始类名:_member_base 内部自行做同一套归一
|
||
# (_NormalizeMemberClass),二者结果逐字一致(避免二次归一在
|
||
# "rawptr[X] 展开命中 sha1 前缀 struct 键"这一角落改变取值)。
|
||
_MemberBaseKey: str | None = ClassName
|
||
# 治本修复:同名类 sha1 冲突检测。
|
||
# 当 class_sha1_map[ClassName] 的 sha1 与 Gen.structs[ClassName] 的 sha1 不一致时,
|
||
# class_members[ClassName] 可能存储了错误模块的同名类成员。
|
||
# 此处检测冲突,加载正确模块的 class_members 并以 sha1 前缀全名缓存。
|
||
_ResolvedClass: str = ClassName if ClassName else ''
|
||
if ClassName:
|
||
# 类键归一(短名 + rawptr[X]/t.CPtr[X] 展开)统一走 _NormalizeMemberClass,
|
||
# 与 _member_base 的 base 判定共用同一归一,禁止各写一份。
|
||
ClassName = self._NormalizeMemberClass(ClassName)
|
||
_ResolvedClass = ClassName
|
||
_ClassSha1Map: dict[str, str] = getattr(self, 'class_sha1_map', {})
|
||
_ExpectedSha1: str | None = _ClassSha1Map.get(ClassName)
|
||
if _ExpectedSha1:
|
||
_ExistingStruct = self.structs.get(ClassName)
|
||
if isinstance(_ExistingStruct, ir.IdentifiedStructType):
|
||
_ExistingSha1: str = self._extract_struct_sha1(_ExistingStruct)
|
||
if _ExistingSha1 and _ExistingSha1 != _ExpectedSha1:
|
||
# sha1 冲突:使用全名查找 class_members
|
||
_FullKey: str = f"{_ExpectedSha1}.{ClassName}"
|
||
if _FullKey in self.class_members and self.class_members[_FullKey]:
|
||
_ResolvedClass = _FullKey
|
||
else:
|
||
# 从正确 stub 加载 class_members
|
||
if self._Trans and hasattr(self._Trans, 'ImportHandler'):
|
||
_StubName: str = f"{_ExpectedSha1}.stub.ll"
|
||
# 临时保存旧值,清除短名条目以绕过 guard,加载后恢复
|
||
_OldMembers = self.class_members.get(ClassName)
|
||
self.class_members[ClassName] = []
|
||
try:
|
||
self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, _StubName, self)
|
||
except Exception:
|
||
pass
|
||
_NewMembers = self.class_members.get(ClassName)
|
||
if _NewMembers:
|
||
self.class_members[_FullKey] = _NewMembers
|
||
_ResolvedClass = _FullKey
|
||
# 恢复旧值(避免影响其他代码对短名的依赖)
|
||
if _OldMembers:
|
||
self.class_members[ClassName] = _OldMembers
|
||
else:
|
||
self.class_members.pop(ClassName, None)
|
||
# base(vtable 占位槽数):唯一真源 = _member_base(规则与文档见该方法)。
|
||
# 本函数在同一次调用内还会在下方多处重载成员表(懒加载 425-436、模块重载
|
||
# 482-508、字段预注册 558-578、force 重建 587-643),每次都可能改写
|
||
# _member_base 的三个输入(class_parent 父链、structs 布局、class_members
|
||
# 长度)。因此 base 与"即将返回的下标 i"必须取自**同一份最新已加载状态**:
|
||
# 所有 return 一律用 _BaseNow() 现算,禁止缓存早先算出的 base——缓存会让
|
||
# 偏移整体差 1(ast.ExceptHandler.children 落到 end_col_offset(i32),
|
||
# len 分派拿到 i32)。
|
||
def _BaseNow() -> int:
|
||
return self._member_base(_MemberBaseKey)
|
||
# 预热(与原行为逐字一致):先触发一次 EnsureClassMembersFlattened,
|
||
# 再决定下面的懒加载。
|
||
_BaseNow()
|
||
if ClassName and ClassName not in self.class_members and self._Trans and hasattr(self._Trans, 'ImportHandler'):
|
||
# 跨模块 class_members 懒加载:struct 已从 stub 加载但 class_members
|
||
# 可能尚未从 .vpi 填充(vpi/stub 就绪时序不一致),主动按类 sha1 加载。
|
||
_Sha1sForLoad: dict[str, str] = getattr(self, 'class_sha1_map', {})
|
||
for _mod_name_lz, _mod_sha1_lz in _Sha1sForLoad.items():
|
||
try:
|
||
self._Trans.ImportHandler._TryLoadClassMembersFromPyi(
|
||
ClassName, f"{_mod_sha1_lz}.stub.ll", self)
|
||
except Exception:
|
||
continue
|
||
if ClassName in self.class_members and len(self.class_members[ClassName]) > 0:
|
||
break
|
||
# 依赖补齐(上面的懒加载)后再确认一次展平(幂等);偏移基址由 _BaseNow()
|
||
# 在真正 return 处现算,不在此缓存。
|
||
_BaseNow()
|
||
if _ResolvedClass and _ResolvedClass in self.class_members:
|
||
for i, (name, _) in enumerate(self.class_members[_ResolvedClass]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
# 继承链回退:当字段不在当前类的 class_members 中时(常见于跨模块导入
|
||
# 父类未加载,字段被填充为 __inherited_N 占位名),沿 class_parent 链向上查找。
|
||
# 子类 struct 布局为 [vtable?] + [基类字段...] + [子类字段...],
|
||
# 基类字段在子类 struct 中的起始偏移即为 base(子类),
|
||
# 因此祖先 class_members 中的索引 i 对应子类 struct 中的偏移 base + i。
|
||
if ClassName and field_name:
|
||
_parent: str | None = self.class_parent.get(ClassName)
|
||
_visited: set = {ClassName} if ClassName else set()
|
||
_cls_short: str = ClassName.split('.')[-1] if ClassName else ''
|
||
while _parent and _parent not in _visited:
|
||
_visited.add(_parent)
|
||
# 父类候选键:class_parent 只登记**短名**(含泛型基类时是模板名,
|
||
# 如 'GSListNode'),而 class_members 里真实键可能是**实例化名**
|
||
# ('GSListNode[GlobalVariable]',见 includes/llvmlite/__module.vp
|
||
# `class GlobalVariable(GSListNode[GlobalVariable])`)或模板名。
|
||
# 少这一步 ⇒ 泛型基类字段(Next/Prev 等)沿链永远查不到 → fast-fail
|
||
# ("属性 'Next' 在类 'GlobalVariable' 中找不到")。
|
||
_p_tmpl: str = _parent.split('[')[0]
|
||
_cand_keys: list = []
|
||
for _k in (_parent, _p_tmpl, f"{_p_tmpl}[{_cls_short}]" if _cls_short else _p_tmpl):
|
||
if _k and _k not in _cand_keys:
|
||
_cand_keys.append(_k)
|
||
# 跳过 __inherited_N 占位条目,只在真实字段名中查找
|
||
_hit_idx: int = -1
|
||
for _k in _cand_keys:
|
||
if _k in self.class_members:
|
||
_members: list = self.class_members[_k]
|
||
_has_placeholder: bool = any(n.startswith('__inherited_') for n, _ in _members)
|
||
if not _has_placeholder:
|
||
for i, (name, _) in enumerate(_members):
|
||
if name == field_name:
|
||
_hit_idx = i
|
||
break
|
||
if _hit_idx >= 0:
|
||
break
|
||
if _hit_idx >= 0:
|
||
# 基类字段位于子类 struct 的 base 起始处,索引 i 即基类内偏移
|
||
return _BaseNow() + _hit_idx
|
||
# 父类 class_members 未加载或含占位条目,尝试从 stub 加载真实字段
|
||
if self._Trans and hasattr(self._Trans, 'ImportHandler'):
|
||
sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {})
|
||
for _k in _cand_keys:
|
||
for mod_name, mod_sha1 in sha1_map.items():
|
||
stub_name: str = f"{mod_sha1}.stub.ll"
|
||
self._Trans.ImportHandler._TryLoadClassMembersFromPyi(_k, stub_name, self)
|
||
if _k in self.class_members and not any(n.startswith('__inherited_') for n, _ in self.class_members[_k]):
|
||
for i, (name, _) in enumerate(self.class_members[_k]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
break
|
||
if _k in self.class_members and any(n == field_name for n, _ in self.class_members.get(_k, [])):
|
||
break
|
||
_parent = self.class_parent.get(_parent)
|
||
short_name: str | None = ClassName.split('.')[-1] if ClassName and '.' in ClassName else None
|
||
if short_name and short_name in self.class_members:
|
||
for i, (name, _) in enumerate(self.class_members[short_name]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
if ClassName and field_name:
|
||
if ClassName not in self.class_members or field_name not in [n for n, _ in self.class_members.get(ClassName, [])]:
|
||
if self._Trans and hasattr(self._Trans, 'ImportHandler'):
|
||
sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {})
|
||
for mod_name, mod_sha1 in sha1_map.items():
|
||
stub_name: str = f"{mod_sha1}.stub.ll"
|
||
self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, stub_name, self)
|
||
if ClassName in self.class_members and len(self.class_members[ClassName]) > 0:
|
||
for i, (name, _) in enumerate(self.class_members[ClassName]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
break
|
||
if short_name:
|
||
for mod_name, mod_sha1 in sha1_map.items():
|
||
stub_name: str = f"{mod_sha1}.stub.ll"
|
||
self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_name, stub_name, self)
|
||
if short_name in self.class_members and len(self.class_members[short_name]) > 0:
|
||
for i, (name, _) in enumerate(self.class_members[short_name]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
break
|
||
if ClassName and field_name:
|
||
for cn_key in self.class_members:
|
||
if cn_key == ClassName or cn_key.endswith(f'.{ClassName}') or (short_name and (cn_key == short_name or cn_key.endswith(f'.{short_name}'))):
|
||
for i, (name, _) in enumerate(self.class_members[cn_key]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
# REnum 变体搜索: ClassName 是 REnum(如 MetaType),成员注册在变体结构
|
||
# (MetaType_Var, MetaType_Concrete, MetaType_App)中。变体与 REnum 主结构
|
||
# 共享 max_variant_struct 布局,偏移一致(__tag 在偏移 0,字段从偏移 1 开始)
|
||
if ClassName and field_name:
|
||
variant_prefix: str = f"{ClassName}_"
|
||
for cn_key in self.class_members:
|
||
if cn_key.startswith(variant_prefix):
|
||
for i, (name, _) in enumerate(self.class_members[cn_key]):
|
||
if name == field_name:
|
||
return i
|
||
if short_name:
|
||
variant_prefix = f"{short_name}_"
|
||
for cn_key in self.class_members:
|
||
if cn_key.startswith(variant_prefix):
|
||
for i, (name, _) in enumerate(self.class_members[cn_key]):
|
||
if name == field_name:
|
||
return i
|
||
if ClassName and ClassName in self.structs:
|
||
struct_type: ir.IdentifiedStructType = self.structs[ClassName]
|
||
if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None and len(struct_type.elements) > 0:
|
||
for cn_key, members in self.class_members.items():
|
||
if cn_key == short_name or (short_name and cn_key.endswith(f'.{short_name}')):
|
||
for i, (name, _) in enumerate(members):
|
||
if name == field_name and i < len(struct_type.elements):
|
||
return i
|
||
break
|
||
else:
|
||
if short_name:
|
||
for cn_key, members in self.class_members.items():
|
||
sn: str = cn_key.split('.')[-1] if '.' in cn_key else cn_key
|
||
if sn == short_name:
|
||
for i, (name, _) in enumerate(members):
|
||
if name == field_name and i < len(struct_type.elements):
|
||
return i
|
||
break
|
||
if ClassName and field_name and ClassName not in self.class_members:
|
||
if self._Trans and hasattr(self._Trans, 'ImportHandler'):
|
||
for temp_dir_candidate in [getattr(self, '_temp_dir', None)]:
|
||
if temp_dir_candidate and os.path.isdir(temp_dir_candidate):
|
||
for pyi_file in os.listdir(temp_dir_candidate):
|
||
if pyi_file.endswith('.vpi') or pyi_file.endswith('.pyi'):
|
||
stub_name: str = pyi_file.replace('.vpi', '.stub.ll').replace('.pyi', '.stub.ll')
|
||
short_cn: str = short_name if short_name else ClassName
|
||
self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_cn, stub_name, self)
|
||
if short_cn in self.class_members and len(self.class_members[short_cn]) > 0:
|
||
for i, (name, _) in enumerate(self.class_members[short_cn]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
break
|
||
if ClassName and ClassName in self.structs and (ClassName not in self.class_members or len(self.class_members.get(ClassName, [])) == 0):
|
||
struct_type: ir.IdentifiedStructType = self.structs[ClassName]
|
||
if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None:
|
||
if self._Trans and hasattr(self._Trans, 'ClassHandler'):
|
||
class_handler: Any = self._Trans.ClassHandler
|
||
if self._current_tree:
|
||
for node in ast.iter_child_nodes(self._current_tree):
|
||
if isinstance(node, ast.ClassDef) and (node.name == ClassName or node.name == short_name):
|
||
if node.name not in self.class_members:
|
||
self.class_members[node.name] = []
|
||
if len(self.class_members[node.name]) == 0:
|
||
class_handler._PreRegisterClassMembers(node, self)
|
||
if ClassName in self.class_members:
|
||
for i, (name, _) in enumerate(self.class_members[ClassName]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
if short_name and short_name in self.class_members:
|
||
for i, (name, _) in enumerate(self.class_members[short_name]):
|
||
if name == field_name:
|
||
return _BaseNow() + i
|
||
break
|
||
# 最后回退:当继承链回退失败但 struct body 已加载时,
|
||
# 从 struct elements 数和 class_members 数推断继承字段偏移。
|
||
# 继承字段在 struct 开头(base 之后),此 fallback 仅适用于单继承字段的情况
|
||
# (如 GSListNode[T] 的 Next 字段)。子类 struct 布局:
|
||
# [vtable?] + [基类字段...] + [子类字段...]
|
||
# 当 class_members 只含子类自身字段、基类 class_members 未加载时,
|
||
# 继承字段数 = struct_elements - class_members_count,
|
||
# 若该值 == 1,则所查字段(如 Next)的偏移 = base + 0。
|
||
if ClassName and field_name:
|
||
_st_final = self.structs.get(ClassName)
|
||
if isinstance(_st_final, ir.IdentifiedStructType) and _st_final.elements is not None:
|
||
_num_elements_final: int = len(_st_final.elements)
|
||
_members_final: list = self.class_members.get(ClassName, [])
|
||
_num_members_final: int = len(_members_final)
|
||
if _num_elements_final > _num_members_final:
|
||
# base 是 vtable 占位(struct[0] = i8*),不是继承字段,必须扣除:
|
||
# 不扣 base 时,凡"struct 元素数恰好多 1"的类,差值其实只是 vtable
|
||
# 本身(class_members 不含 vtable),却会对任意字段名返回 base+0。
|
||
# 实例:ast.ImportFrom 的 level/module → 静默返回偏移 1(=parent,
|
||
# %"…AST"*)→ 调用点把 AST* 当 i32 传入 _resolve_relative_module
|
||
# → llvmlite "Type of #4 arg mismatch: i32 != %"…AST"*"。
|
||
# 成员表与 struct 不一致(少一个非 vtable 槽)时,先强制按定义模块
|
||
# 的 .vpi 重载一次成员表再按名查:_TryLoadClassMembersFromPyi 的 guard
|
||
# 会永久跳过"已存在但不完整"的表,此处即该冻结的解除点。重载后仍缺
|
||
# 才走下方推断/fast-fail。每类只重载一次(避免递归与重复 IO)。
|
||
_ReloadKey: tuple = (id(self), ClassName)
|
||
_ReloadTried: set = getattr(self, '_MemberReloadTried', None)
|
||
if _ReloadTried is None:
|
||
_ReloadTried = set()
|
||
self._MemberReloadTried = _ReloadTried
|
||
if _ReloadKey not in _ReloadTried:
|
||
_ReloadTried.add(_ReloadKey)
|
||
_Sha1Final: str | None = getattr(self, 'class_sha1_map', {}).get(ClassName)
|
||
if _Sha1Final and self._Trans and hasattr(self._Trans, 'ImportHandler'):
|
||
_OldMembers = _members_final
|
||
try:
|
||
self._Trans.ImportHandler._TryLoadClassMembersFromPyi(
|
||
ClassName, f"{_Sha1Final}.stub.ll", self, force=True)
|
||
except Exception:
|
||
pass
|
||
_ReloadedMembers: list = self.class_members.get(ClassName, [])
|
||
if len(_ReloadedMembers) < len(_OldMembers):
|
||
# 重载反而变少(.vpi 缺该类):回滚,避免丢字段
|
||
self.class_members[ClassName] = _OldMembers
|
||
_ReloadedMembers = _OldMembers
|
||
_members_final = _ReloadedMembers
|
||
_num_members_final = len(_members_final)
|
||
for _Ri, (_Rn, _Rt) in enumerate(_members_final):
|
||
if _Rn == field_name:
|
||
return _BaseNow() + _Ri
|
||
_num_inherited_final: int = _num_elements_final - _num_members_final - _BaseNow()
|
||
if _num_inherited_final == 1:
|
||
# 推断(缺失槽位 = 位于 base+0 的未展平继承字段)仅在成员表
|
||
# 尚未含展平继承字段时成立:若成员表首项本身就是任一已加载
|
||
# 祖先的字段名(如 ImportFrom 'parent' ← Stmt),说明继承字段
|
||
# 已展平、缺的是类自身字段,推断 base+0 必然读错槽位。
|
||
_ParentNamesFinal: set = set()
|
||
for _ParentF in (getattr(self, 'class_parents', {}).get(ClassName) or []):
|
||
for _Np, _Tp in self.class_members.get(_ParentF, []) or []:
|
||
_ParentNamesFinal.add(_Np)
|
||
if _members_final and _members_final[0][0] in _ParentNamesFinal:
|
||
pass # 已含展平继承字段 → 落入下方 fast-fail 暴露真实缺失
|
||
else:
|
||
# 单继承字段,偏移 = base(vtable 之后第一个位置)
|
||
return _BaseNow() + 0
|
||
# fast-fail(治本修复 2026-08-10):字段未找到不再静默返回 None。
|
||
# 静默 None 会让调用方(_gep_and_Load_member L26-27)直接 return None,
|
||
# AnnAssign InitValue 变 None → 变量 alloca 未初始化 → 运行期读垃圾指针
|
||
# (如 HandlesImports.HandleImport 的 imp.names → 0xffffffffffffffff 崩溃)。
|
||
# 抛出编译错误,暴露 class_members/struct 的真实状态便于定位根因。
|
||
_members_diag: list = self.class_members.get(ClassName, []) if ClassName else []
|
||
_struct_diag: str = 'not-registered'
|
||
if ClassName and ClassName in self.structs:
|
||
_st_diag = self.structs[ClassName]
|
||
if isinstance(_st_diag, ir.IdentifiedStructType):
|
||
_struct_diag = f"{len(_st_diag.elements)} elems" if _st_diag.elements is not None else 'opaque'
|
||
_member_names_diag: list = [n for n, _ in _members_diag][:16]
|
||
# 异常类型:MemberLookupFailFast(KeyError 子类 + 裸消息 __str__)——
|
||
# 既保留 `except KeyError` 兜底点,又让报文不再被 repr 成"带引号 + \n 转义"
|
||
# 的不可读形式(原有 traceback.print_stack 调试转储一并移除,保持输出干净)。
|
||
raise MemberLookupFailFast(
|
||
f"[fast-fail] 属性 '{field_name}' 在类 '{ClassName}' 中找不到"
|
||
f"(class_members={len(_members_diag)} 个: {_member_names_diag}, struct={_struct_diag})"
|
||
)
|
||
|
||
def _resolve_class_for_var(self, var_name: str) -> str | None:
|
||
for ClassName in self.class_methods:
|
||
if var_name == ClassName or var_name.startswith(f"__with_{ClassName}_"):
|
||
return ClassName
|
||
if var_name in self.variables:
|
||
var: ir.Value = self.variables[var_name]
|
||
if isinstance(var.type, ir.PointerType):
|
||
for ClassName, struct_type in self.structs.items():
|
||
if var.type.pointee == struct_type:
|
||
return ClassName
|
||
if isinstance(var.type.pointee, ir.PointerType) and var.type.pointee.pointee == struct_type:
|
||
return ClassName
|
||
return None
|
||
|
||
def _infer_return_type(self, FuncName: str) -> ir.Type:
|
||
if FuncName in self.functions:
|
||
return self.functions[FuncName].type.pointee.return_type
|
||
# 也尝试混淆后的名称
|
||
mangled: str = self._mangle_func_name(FuncName)
|
||
if mangled != FuncName and mangled in self.functions:
|
||
return self.functions[mangled].type.pointee.return_type
|
||
if FuncName in self.known_return_types:
|
||
return self.known_return_types[FuncName]
|
||
if mangled != FuncName and mangled in self.known_return_types:
|
||
return self.known_return_types[mangled]
|
||
for ClassName, methods in self.class_methods.items():
|
||
for method in methods:
|
||
if FuncName == method:
|
||
return ir.VoidType()
|
||
if self._import_handler_ref:
|
||
try:
|
||
# 先用原始名称查找,再用混淆名称查找
|
||
stub_func_type: Any = self._import_handler_ref._LookupStubFuncType(FuncName, self)
|
||
if stub_func_type is None and mangled != FuncName:
|
||
stub_func_type = self._import_handler_ref._LookupStubFuncType(mangled, self)
|
||
if stub_func_type and hasattr(stub_func_type, 'return_type'):
|
||
return stub_func_type.return_type
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"查找桩函数返回类型失败: {_e}", "Exception")
|
||
return ir.IntType(32)
|
||
|
||
def _get_or_declare_func(self, name: str, func_type: ir.FunctionType) -> ir.Function:
|
||
self._RecordDeclFrom(name, func_type) # [DIAG-decl] 临时诊断
|
||
for fname, fobj in self.functions.items():
|
||
if fname == name:
|
||
return fobj
|
||
if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname):
|
||
return fobj
|
||
func_decl: ir.Function = ir.Function(self.module, func_type, name=name)
|
||
self.functions[name] = func_decl
|
||
return func_decl
|
||
|
||
def get_or_declare_c_func(self, name: str, fallback_type: ir.FunctionType | None = None) -> ir.Function | None:
|
||
self._RecordDeclFrom(name, fallback_type) # [DIAG-decl] 临时诊断
|
||
if name in self.functions:
|
||
return self.functions[name]
|
||
for fname, fobj in self.functions.items():
|
||
if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname):
|
||
return fobj
|
||
stub_func_type: ir.FunctionType | None = None
|
||
if self._import_handler_ref:
|
||
try:
|
||
stub_func_type = self._import_handler_ref._LookupStubFuncType(name, self)
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"查找桩函数类型失败: {_e}", "Exception")
|
||
if stub_func_type:
|
||
func_decl: ir.Function = ir.Function(self.module, stub_func_type, name=name)
|
||
self.functions[name] = func_decl
|
||
return func_decl
|
||
if fallback_type is not None:
|
||
func_decl: ir.Function = ir.Function(self.module, fallback_type, name=name)
|
||
self.functions[name] = func_decl
|
||
return func_decl
|
||
return None |