refactor(tpv): 删无符号判定的旧字符串兜底,真源唯一(S4 去字符串化收尾)
删 _type_name_unsigned_legacy / _annot_str_unsigned_scan / _annot_str_unsigned_legacy / _last_dot_tail / _is_simple_ident;_type_name_unsigned 与 _annot_str_unsigned 变为真源唯一(spec_sign_of_annot)。 依据:真源已含顶层 | 分段、去括号/空白、IDENT[INNER] 取元素、去模块前缀、精确名白名单、CTypedef 链展开,旧路径能力是它的真子集(S2 已把旧路径修到与真源一致),差异面就是假阳性区 —— strstr 子串扫描正是 A4/A5 根因(BYTEPTR 含 BYTE、任何含 uint32 的 mangled 名都被判无符号)。现语义:真源 UNKNOWN=不表态 => 返回 0;若日后发现漏判,补白名单/补 CTypedef,不恢复子串扫描。
This commit is contained in:
1 parent
cc17a8f8db
commit
679758c05b
1 file changed
+29
-172
@@ -402,164 +402,22 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
return 1
|
||||
return 0
|
||||
return 0
|
||||
def _last_dot_tail(self, name: str) -> str:
|
||||
"""取最后一个 '.' 之后的部分(无 '.' 返回 None);用于模块限定名退查"""
|
||||
if name is None:
|
||||
return None
|
||||
dot: str = string.strrchr(name, 46)
|
||||
if dot is None:
|
||||
return None
|
||||
return dot + 1
|
||||
def _type_name_unsigned_legacy(self, type_name: str) -> int:
|
||||
"""[旧路径] 类型名是否为无符号整数类型(精确匹配 + CTypedef 展开一层)"""
|
||||
if type_name is None:
|
||||
return 0
|
||||
# t.CUIntXXT 形式
|
||||
if string.strcmp(type_name, "CUInt8T") == 0: return 1
|
||||
if string.strcmp(type_name, "CUInt16T") == 0: return 1
|
||||
if string.strcmp(type_name, "CUInt32T") == 0: return 1
|
||||
if string.strcmp(type_name, "CUInt64T") == 0: return 1
|
||||
# 内建无符号别名(非 CTypedef,动态兜底查不到 → 必须显式登记)。
|
||||
# CSizeT 此前缺失 → viperio.Buf.write 的 capacity/length 比较生成
|
||||
# icmp sgt/sle/slt(应 ult/ule/ult),A5 同族残留根因。
|
||||
if string.strcmp(type_name, "CSizeT") == 0: return 1
|
||||
if string.strcmp(type_name, "CUIntPtrT") == 0: return 1
|
||||
# bool / t.CBool:i1 属无符号语义(should_zext_widen 对 narrow_bits==1
|
||||
# 已强制 zext,此处登记仅为与独立真源口径一致)。
|
||||
# 原表把 SSizeT 列为无符号(A3 缺陷)——已删:SSizeT 实为有符号。
|
||||
if string.strcmp(type_name, "CBool") == 0: return 1
|
||||
if string.strcmp(type_name, "bool") == 0: return 1
|
||||
# stdint 裸别名(from stdint import *)
|
||||
if string.strcmp(type_name, "UInt8T") == 0: return 1
|
||||
if string.strcmp(type_name, "UInt16T") == 0: return 1
|
||||
if string.strcmp(type_name, "UInt32T") == 0: return 1
|
||||
if string.strcmp(type_name, "UInt64T") == 0: return 1
|
||||
# CUnsigned* 形式
|
||||
if string.strcmp(type_name, "CUnsignedChar") == 0: return 1
|
||||
if string.strcmp(type_name, "CUnsignedShort") == 0: return 1
|
||||
if string.strcmp(type_name, "CUnsignedInt") == 0: return 1
|
||||
if string.strcmp(type_name, "CUnsignedLong") == 0: return 1
|
||||
if string.strcmp(type_name, "CUnsignedLongLong") == 0: return 1
|
||||
# 常见无符号别名
|
||||
if string.strcmp(type_name, "BYTE") == 0: return 1
|
||||
if string.strcmp(type_name, "UCHAR") == 0: return 1
|
||||
if string.strcmp(type_name, "USHORT") == 0: return 1
|
||||
if string.strcmp(type_name, "UINT") == 0: return 1
|
||||
if string.strcmp(type_name, "ULONG") == 0: return 1
|
||||
# [阶段3] CTypedef 动态表兜底:stdint 别名(DWORD/uint32_t 等)走库定义
|
||||
# 展开,按 _annot_str_unsigned_legacy 子串规则判断符号性。
|
||||
# S2 修正:原实现**只展开一层** ⇒ 多层别名漏判(如 WCHAR→WORD→
|
||||
# t.CUInt16T、FSIZE_t→DWORD→t.CUInt32T);改为沿链展开至不动点。
|
||||
cur_u: str = type_name
|
||||
depth_u: int = 0
|
||||
while depth_u < 16:
|
||||
ts_u: str = self.Gen.TypeGenCType.lookup_ctypedef(cur_u)
|
||||
if ts_u is None or self.Gen.TypeGenCType.CTypedefFound == 0:
|
||||
# 模块限定名(如 stdint.u32 / builtins.DWORD)退到最后一段重查
|
||||
tail_u: str = self._last_dot_tail(cur_u)
|
||||
if tail_u is not None:
|
||||
ts_u = self.Gen.TypeGenCType.lookup_ctypedef(tail_u)
|
||||
if ts_u is None or self.Gen.TypeGenCType.CTypedefFound == 0:
|
||||
break
|
||||
if self._annot_str_unsigned_legacy(ts_u) != 0:
|
||||
return 1
|
||||
if string.strcmp(ts_u, cur_u) == 0:
|
||||
break
|
||||
cur_u = ts_u
|
||||
depth_u += 1
|
||||
return 0
|
||||
def _annot_str_unsigned_scan(self, annot: str) -> int:
|
||||
"""[旧路径·子串表] 纯子串扫描(不做别名链展开)
|
||||
|
||||
用于复合注解(t.CArray[uint8, 16] / uint8 | t.CPtr),
|
||||
_type_name_unsigned_legacy 的精确匹配对这类串不适用。
|
||||
"""
|
||||
if annot is None:
|
||||
return 0
|
||||
if string.strstr(annot, "CUInt8T") is not None: return 1
|
||||
if string.strstr(annot, "CUInt16T") is not None: return 1
|
||||
if string.strstr(annot, "CUInt32T") is not None: return 1
|
||||
if string.strstr(annot, "CUInt64T") is not None: return 1
|
||||
# 内建无符号别名(同 _type_name_unsigned)
|
||||
if string.strstr(annot, "CSizeT") is not None: return 1
|
||||
if string.strstr(annot, "CUIntPtrT") is not None: return 1
|
||||
# 小写 stdint 别名(uint16/uint32/uint64/uint8):复合注解如
|
||||
# t.CArray[uint16, 256] 的注解串用大写规则扫不到,需显式识别小写
|
||||
# (ide.vp identify_data: t.CArray[uint16,256] 的 word60 误 sext 根因)。
|
||||
# 注意不含 "size_t":ssize_t(有符号)包含该子串会误判。
|
||||
if string.strstr(annot, "uint8") is not None: return 1
|
||||
if string.strstr(annot, "uint16") is not None: return 1
|
||||
if string.strstr(annot, "uint32") is not None: return 1
|
||||
if string.strstr(annot, "uint64") is not None: return 1
|
||||
if string.strstr(annot, "CUnsigned") is not None: return 1
|
||||
# bool / CBool:i1 属无符号语义(与 _type_name_unsigned_legacy 同口径)
|
||||
if string.strstr(annot, "CBool") is not None: return 1
|
||||
if string.strstr(annot, "bool") is not None: return 1
|
||||
if string.strstr(annot, "BYTE") is not None: return 1
|
||||
if string.strstr(annot, "UCHAR") is not None: return 1
|
||||
if string.strstr(annot, "USHORT") is not None: return 1
|
||||
if string.strstr(annot, "UINT") is not None: return 1
|
||||
if string.strstr(annot, "ULONG") is not None: return 1
|
||||
# WORD 系:WORD/DWORD/QWORD/WCHAR/FSIZE_t/LBA_t 均为无符号(WORD=t.CUInt16T)。
|
||||
# 原表漏 WORD ⇒ 复合别名 `WCHARPTR = WORD | t.CPtr` 被旧判为 signed
|
||||
# (S2 闸门实测暴露:旧判 signed / 新真源 unsigned,旧错)。
|
||||
if string.strstr(annot, "WORD") is not None: return 1
|
||||
return 0
|
||||
def _is_simple_ident(self, s: str) -> int:
|
||||
"""是否简单标识符(仅标识符字符与 '.',不含空白/运算符/括号/逗号)"""
|
||||
if s is None:
|
||||
return 0
|
||||
n: sizet = string.strlen(s)
|
||||
if n == 0:
|
||||
return 0
|
||||
i: sizet = 0
|
||||
while i < n:
|
||||
if s[i] == ' ' or s[i] == '\t' or s[i] == '\n' or s[i] == '\r':
|
||||
return 0
|
||||
if s[i] == '|' or s[i] == '[' or s[i] == ']' or s[i] == '(' or s[i] == ')':
|
||||
return 0
|
||||
if s[i] == '*' or s[i] == ',' or s[i] == '+':
|
||||
return 0
|
||||
i += 1
|
||||
return 1
|
||||
def _annot_str_unsigned_legacy(self, annot: str) -> int:
|
||||
"""[旧路径] 注解串无符号判定 = 子串扫描 + 裸别名沿 CTypedef 链展开后再扫
|
||||
|
||||
为何需要链展开:`_node_type_unsigned` 的 Subscript/Attribute 分支会把
|
||||
**裸别名**(`sizet` / `u64` / `WORD` …)当作注解串传进来,而子串表只认
|
||||
`CSizeT` / `uint64` 等长名 ⇒ 这些别名此前被漏判(A5 同族)。链展开后
|
||||
其目标(`t.CSizeT` / `t.CUInt64T`)能被子串表命中。
|
||||
复合串(含 '|' / '[' 等)不做链展开,交给子串表。
|
||||
"""
|
||||
if self._annot_str_unsigned_scan(annot) != 0:
|
||||
return 1
|
||||
if annot is None or self._is_simple_ident(annot) == 0:
|
||||
return 0
|
||||
cur: str = annot
|
||||
d: int = 0
|
||||
while d < 16:
|
||||
ts: str = self.Gen.TypeGenCType.lookup_ctypedef(cur)
|
||||
if ts is None or self.Gen.TypeGenCType.CTypedefFound == 0:
|
||||
tail: str = self._last_dot_tail(cur)
|
||||
if tail is not None:
|
||||
ts = self.Gen.TypeGenCType.lookup_ctypedef(tail)
|
||||
if ts is None or self.Gen.TypeGenCType.CTypedefFound == 0:
|
||||
break
|
||||
if self._annot_str_unsigned_scan(ts) != 0:
|
||||
return 1
|
||||
if string.strcmp(ts, cur) == 0:
|
||||
break
|
||||
cur = ts
|
||||
d += 1
|
||||
return 0
|
||||
# ============================================================
|
||||
# S3 切读(无符号判定块)——**新真源优先**,旧路径降级为兜底
|
||||
# S4 去字符串化(无符号判定块)——**真源唯一**,旧字符串路径已删
|
||||
#
|
||||
# 真源:TypeGenCType.spec_sign_of_annot(精确名白名单 + CTypedef 链逐层展开)。
|
||||
# 切读策略(D5「旧路径留兜底」):
|
||||
# - 新真源**明确表态**(UNSIGNED/SIGNED)→ 直接采用(不再看旧子串表);
|
||||
# - 新真源返回 UNKNOWN(类名 / sha1 前缀名 / 未登记别名等尚未覆盖)→ 回退旧路径。
|
||||
# S2 的「双写一致性闸门」在切读后已无意义(读就取新真源,比对恒等),故撤除。
|
||||
# 真源:TypeGenCType.spec_sign_of_annot(顶层 '|' 分段 → 去空白/外层括号 →
|
||||
# '*' 后缀 → IDENT[INNER, ...] 取元素 → 去模块限定前缀 → 精确名白名单 →
|
||||
# CTypedef 链逐层展开,限深 16 防环)。
|
||||
# 它已完整覆盖旧路径的能力(含复合注解与别名链),故 S4 直接删掉旧字符串兜底:
|
||||
# `_type_name_unsigned_legacy` / `_annot_str_unsigned_scan` /
|
||||
# `_annot_str_unsigned_legacy` / `_last_dot_tail` / `_is_simple_ident`
|
||||
# —— 那套 strstr 子串扫描正是 A4/A5 假阳性的源头(`BYTEPTR` 含 BYTE 被判
|
||||
# 无符号;任何含 `uint32` 的串(含 sha1 前缀 mangled 名)都会命中)。
|
||||
#
|
||||
# 现语义:真源 UNKNOWN = **不表态** ⇒ 两个入口一律返回 0(保持编译器默认的
|
||||
# zext/sext 取向)。若日后发现某名字确实是无符号却没被判出,正确处理是
|
||||
# **补进 SpecSignFromTypeNameSpan 白名单 / 补 CTypedef 声明**,
|
||||
# 而不是恢复子串扫描(那会把假阳性一并放回来)。
|
||||
# ============================================================
|
||||
def _spec_sign_from_annot(self, annot: str) -> int:
|
||||
"""真源入口:注解 → TS_SIGN_*(UNKNOWN 表示不表态)"""
|
||||
@@ -569,11 +427,11 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
#
|
||||
# S4 切读:条目的 Spec 由 SpecFromLLVMType 反推(恒 TS_INT)+ SpecWithSign
|
||||
# 按注解切 TS_UINT。因此 `Kind == TS_UINT` 是**注解明确表态无符号**的强信号,
|
||||
# 可优先于 AnnotClassName 字符串判定直接采用。
|
||||
# 可优先于注解名字符串判定直接采用。
|
||||
#
|
||||
# ⚠️ 反向不成立:`Kind == TS_INT` 既可能是"注解有符号"也可能是"未表态"
|
||||
# (SpecFromLLVMType 默认),**不可**据此断言 SIGNED —— 故只切无符号方向,
|
||||
# 有符号仍走 AnnotClassName 字符串真源(避免把未表态误判成 signed)。
|
||||
# 有符号仍走注解名(Spec.SourceName)真源(避免把未表态误判成 signed)。
|
||||
# ============================================================
|
||||
def _entry_spec_unsigned(self, sp: TSpec.TypeSpec | t.CPtr) -> int:
|
||||
"""Spec 是否明确无符号整数(TS_UINT);其它一律 0(未表态/不适用)"""
|
||||
@@ -583,27 +441,26 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
return 1
|
||||
return 0
|
||||
def _type_name_unsigned(self, type_name: str) -> int:
|
||||
"""[S3 切读] 类型名无符号判定:新真源优先,未表态时回退旧路径
|
||||
"""[S4] 类型名无符号判定:**真源唯一**(旧字符串兜底已删)
|
||||
|
||||
注:旧路径在此**按需**调用(仅新真源不表态时),避免热路径上无谓的
|
||||
子串扫描 + CTypedef 链查表双份开销。
|
||||
真源 UNKNOWN = 不表态 ⇒ 返回 0(非无符号),保持编译器默认的 zext/sext 取向。
|
||||
"""
|
||||
if type_name is not None:
|
||||
s: int = self._spec_sign_from_annot(type_name)
|
||||
if s == TSpec.TS_SIGN_UNSIGNED:
|
||||
if self._spec_sign_from_annot(type_name) == TSpec.TS_SIGN_UNSIGNED:
|
||||
return 1
|
||||
if s == TSpec.TS_SIGN_SIGNED:
|
||||
return 0
|
||||
return self._type_name_unsigned_legacy(type_name)
|
||||
return 0
|
||||
def _annot_str_unsigned(self, annot: str) -> int:
|
||||
"""[S3 切读] 注解串无符号判定:新真源优先,未表态时回退旧路径"""
|
||||
"""[S4] 注解串无符号判定:**真源唯一**(旧子串扫描已删)
|
||||
|
||||
真源按顶层 '|' 分段、去括号/空白、取 `IDENT[INNER]` 的元素、去模块限定前缀,
|
||||
再走精确名白名单 + CTypedef 链展开 —— 已覆盖原 `_annot_str_unsigned_scan`
|
||||
对复合注解(`t.CArray[uint8, 16]` / `uint8 | t.CPtr`)的全部能力,
|
||||
且无子串假阳性。UNKNOWN = 不表态 ⇒ 返回 0。
|
||||
"""
|
||||
if annot is not None:
|
||||
s: int = self._spec_sign_from_annot(annot)
|
||||
if s == TSpec.TS_SIGN_UNSIGNED:
|
||||
if self._spec_sign_from_annot(annot) == TSpec.TS_SIGN_UNSIGNED:
|
||||
return 1
|
||||
if s == TSpec.TS_SIGN_SIGNED:
|
||||
return 0
|
||||
return self._annot_str_unsigned_legacy(annot)
|
||||
return 0
|
||||
def translate_compare(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
|
||||
Reference in new issue
Block a user