diff --git a/App/lib/core/Gen/Type/TypeGenErr.vp b/App/lib/core/Gen/Type/TypeGenErr.vp index 52ca620..9e2d7d4 100644 --- a/App/lib/core/Gen/Type/TypeGenErr.vp +++ b/App/lib/core/Gen/Type/TypeGenErr.vp @@ -388,9 +388,10 @@ class TypeGenErr(TypeGenBase.TypeGenBase): [D5·截断纪律] 旧实现按 `an * 32 + 1` 预估缓冲,再用 `_strappend` 以 `remain - 1` 截断且**不报错**:参数名偏长(或参数偏多)时编码串被静默 截断 ⇒ `infer_expr_type` 的 `strstr(param_types, "name:")` 查找失配 / - 命中他人前缀 ⇒ 静默回落 Int32(类型误判)。现改为**两遍精确分配**: - 先逐参数生成条目(指针暂存数组)并累计长度,再按精确长度分配后拼接—— - 不存在长度上限,故无需截断;`_strappend` 一并删除(唯一调用点在本函数)。 + 命中他人前缀 ⇒ 静默回落 Int32(类型误判)。现改为**上界预估 + 单遍就地 + 拼接**:段一是只扫参数名的上界(name + ':' + 位宽 + ',' + NUL),段二是 + 解析类型后逐条目就地格式化并立即拼接 —— 缓冲恒够,不存在截断; + `_strappend` 一并删除(唯一调用点在本函数)。 """ if args_node is None: return None @@ -402,11 +403,25 @@ class TypeGenErr(TypeGenBase.TypeGenBase): if an == 0: return None - # 1) 逐参数生成条目 "name:bits"(精确分配),指针暂存 - ents: sizet | t.CPtr = pool.alloc(an * 8) - if ents is None: + # 1) 上界估长(**只扫参数名**,不解析类型也不需要暂存结果指针): + # 每条 = name + ':' + 位宽(int32 十进制最长 11 字符含符号)+ ',';再加 NUL。 + # ⚠ 这里必须"上界"而非"精确":此前版本把每条目指针存进 sizet 数组、 + # 再取出转回指针 strcat,自举出的二进制上取回了 -1(0xffffffffffffffff) + # ⇒ ASan: access-violation in strcat(build_param_types_str)。 + # 改为上界缓冲后**不需要数组往返**,每条目就地格式化、立即拼接。 + bound: sizet = 1 # 末尾 NUL + pre_i: sizet + for pre_i in range(an): + pre_arg: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist.get(pre_i)) + if pre_arg is None or pre_arg.arg is None: + continue + bound += string.strlen(pre_arg.arg) + 1 + 12 + 1 + buf: str = pool.alloc(bound) + if buf is None: return None - total: sizet = 1 # 末尾 NUL + buf[0] = '\0' + + # 2) 单遍:解析类型 → 就地格式化条目 → 立即拼接(缓冲为上界,不会截断) cnt: sizet = 0 i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) for ai in range(an): @@ -427,28 +442,17 @@ class TypeGenErr(TypeGenBase.TypeGenBase): if bits == 0: bits = 32 nlen: sizet = string.strlen(arg.arg) - # name + ':' + 十进制位宽(int32 最长 11 字符)+ NUL - ent: str = pool.alloc(nlen + 1 + 12 + 1) + # 条目缓冲:name + ':' + 位宽(预留 16 字节,足够 %d 任意取值)+ NUL + ent: str = pool.alloc(nlen + 1 + 16 + 1) if ent is None: continue string.strcpy(ent, arg.arg) ent[nlen] = ':' - viperlib.snprintf(ent + nlen + 1, 12, "%d", bits) - ents[cnt] = sizet(ent) + viperlib.snprintf(ent + nlen + 1, 16, "%d", bits) + if cnt > 0: + string.strcat(buf, ",") + string.strcat(buf, ent) cnt += 1 - total += nlen + 1 + string.strlen(ent + nlen + 1) if cnt == 0: return None - if cnt > 1: - total += cnt - 1 # 分隔符 "," - - # 2) 按精确长度分配 + 拼接(不会截断) - buf: str = pool.alloc(total) - if buf is None: - return None - buf[0] = '\0' - for i2 in range(cnt): - if i2 > 0: - string.strcat(buf, ",") - string.strcat(buf, (str | t.CPtr)(t.CVoid(ents[i2], t.CPtr))) return buf \ No newline at end of file diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 192602c..ac3977e 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -163,13 +163,11 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if elts is None or pool is None: return None n3: sizet = len(elts) - # 1) 解析全部元素(结果指针暂存,避免二次解析) - es_arr: sizet | t.CPtr = None - if n3 > 0: - es_arr = pool.alloc(n3 * 8) - if es_arr is None: - return None - total3: sizet = 2 # open + close + # 1) 量长(解析结果用完即弃):**不把元素指针暂存进数组再取回**—— + # 该「sizet 数组往返再转回指针」的写法在自举出的二进制上会取回 -1 + # (0xffffffffffffffff)⇒ ASan access-violation in strcat(同类现场见 + # TypeGenErr.build_param_types_str)。此处改为二次解析 + 直写。 + total3: sizet = 2 # open + close(各按 1 字节算;实参为空串时多算无害) cnt3: sizet = 0 for i3 in range(n3): e3: ast.AST | t.CPtr = elts.get(i3) @@ -181,21 +179,28 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): self.Gen.TypeGenErr.fatal_error( e3, "类型注解的元素无法解析为类型名(列表/元组注解)", "NameError") return None - es_arr[cnt3] = sizet(es3) cnt3 += 1 total3 += string.strlen(es3) if cnt3 > 1: total3 += (cnt3 - 1) * 2 # 分隔符 ", " - # 2) 按精确长度分配 + 拼接(无上限,不会截断) + # 2) 按精确长度分配 + 二次解析直写(无上限,不会截断) buf4: str = pool.alloc(total3 + 1) if buf4 is None: return None buf4[0] = '\0' string.strcat(buf4, open_ch) - for i4 in range(cnt3): - if i4 > 0: + wrote3: sizet = 0 + for i4 in range(n3): + e4: ast.AST | t.CPtr = elts.get(i4) + if e4 is None: + continue + es4: str = self._subscript_slice_to_str(pool, e4) + if es4 is None: + continue # 第一遍已校验过,此处不会发生 + if wrote3 > 0: string.strcat(buf4, ", ") - string.strcat(buf4, (str | t.CPtr)(t.CVoid(es_arr[i4], t.CPtr))) + string.strcat(buf4, es4) + wrote3 += 1 string.strcat(buf4, close_ch) return buf4 def _is_sha1_prefixed(self, s: str) -> int: