diff --git a/App/lib/StubGen/VpiReader.vp b/App/lib/StubGen/VpiReader.vp index c3be7b9..17a2c6e 100644 --- a/App/lib/StubGen/VpiReader.vp +++ b/App/lib/StubGen/VpiReader.vp @@ -2,6 +2,7 @@ import t, c from stdint import * import ast import string +import stdio import stdlib import sys import memhub @@ -29,6 +30,13 @@ import lib.core.Handles.HandlesTranslator as HT # 按 sha1.类名.方法名 查表并用 _resolve_vpi_ret_annot_str 纯字符串 # 解析为 LLVMType,返回类型始终来自方法定义处,无白名单/无硬编码。 # ============================================================ +# ============================================================ +# VPI_DEBUG_ENABLE - vpi 返回注解缓存调试输出开关 +# 0=关闭(默认),1=开启(打印全部 [VPI-*] 调试插桩)。 +# 排查"方法返回类型无法精确获取"问题时临时改为 1,解决后改回 0。 +# ============================================================ +VPI_DEBUG_ENABLE: t.CDefine = 0 + VPI_RET_MAX: t.CDefine = 4096 VPI_KEY_LEN: t.CDefine = 224 VPI_RET_LEN: t.CDefine = 224 @@ -50,6 +58,8 @@ _g_vpi_ret_cap: int = 0 # ============================================================ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int: """从 vpi_buf 文本提取方法返回注解字符串(stdlib 持久)""" + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-CACHE] in sha1=%s\n", src_sha1) global _g_vpi_ret_keys, _g_vpi_ret_strs, _g_vpi_ret_count, _g_vpi_ret_cap if vpi_buf is None or src_sha1 is None: return 0 @@ -81,7 +91,9 @@ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int: if line_len >= 6 and string.strncmp(vpi_buf + i, "class ", 6) == 0: cstart: t.CSizeT = i + 6 cend: t.CSizeT = cstart - while cend < le and vpi_buf[cend] != ':' and vpi_buf[cend] != ' ': + # 类名提取:到 ':' / ' ' / '(' 前停止(继承基类括号不入类名, + # 如 "class MemBuddy(MemManager):" → "MemBuddy",与调用方短名一致) + while cend < le and vpi_buf[cend] != ':' and vpi_buf[cend] != ' ' and vpi_buf[cend] != '(': cend += 1 if cend > cstart: if cur_cls is not None: @@ -92,6 +104,11 @@ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int: cur_cls[cend - cstart] = '\0' # def name(...) -> Ret: elif line_len >= 4 and string.strncmp(vpi_buf + i, "def ", 4) == 0: + # 模块级函数(行首无缩进):清空类名上下文,避免类结束后残留 + # (如 json 的 null/int_val 等模块级函数被误存为 JsonValue.null) + if i == ls and cur_cls is not None: + stdlib.free(cur_cls) + cur_cls = None dstart: t.CSizeT = i + 4 dend: t.CSizeT = dstart while dend < le and vpi_buf[dend] != '(': @@ -105,18 +122,30 @@ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int: arrow_pos = rpos break rpos += 1 - if arrow_pos < le and dend > dstart: - # 返回类型 = arrow_pos+2 跳过空格 到 ':' 前 - rstart: t.CSizeT = arrow_pos + 2 - while rstart < le and vpi_buf[rstart] == ' ': - rstart += 1 - rend: t.CSizeT = rstart - while rend < le and vpi_buf[rend] != ':': - rend += 1 - if rend > rstart: + if dend > dstart: + # 返回类型:有 "-> Ret" 提取注解;无注解的非特殊方法默认 "i32" + # (与 HandlesType.infer_return_type 无 return 语句时的默认行为一致, + # 如 json._append_child / __setitem__ 无注解实际编译为 i32)。 + ret_src: str = None + rtype_len: t.CSizeT = 0 + if arrow_pos < le: + # 返回类型 = arrow_pos+2 跳过空格 到 ':' 前 + rstart2: t.CSizeT = arrow_pos + 2 + while rstart2 < le and vpi_buf[rstart2] == ' ': + rstart2 += 1 + rend2: t.CSizeT = rstart2 + while rend2 < le and vpi_buf[rend2] != ':': + rend2 += 1 + if rend2 > rstart2: + rtype_len = rend2 - rstart2 + ret_src = vpi_buf + rstart2 + else: + # 无返回注解:默认 i32(特殊方法除外,见下方过滤) + rtype_len = 3 + ret_src = "i32" + if ret_src is not None: # 构造 key 和 ret_str,存入缓存 mname_len: t.CSizeT = dend - dstart - rtype_len: t.CSizeT = rend - rstart cls_len2: t.CSizeT = 0 if cur_cls is not None: cls_len2 = string.strlen(cur_cls) @@ -127,30 +156,42 @@ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int: if mname_buf is not None: string.strncpy(mname_buf, vpi_buf + dstart, mname_len) mname_buf[mname_len] = '\0' - key_buf: bytes = stdlib.malloc(key_len + 1) - if key_buf is not None: - if cur_cls is not None: - viperlib.snprintf(key_buf, key_len + 1, "%s.%s.%s", src_sha1, cur_cls, mname_buf) + # 无注解的特殊方法(构造/析构)跳过:它们有专门调用路径 + skip_def: int = 0 + if arrow_pos >= le: + if string.strcmp(mname_buf, "__init__") == 0 or \ + string.strcmp(mname_buf, "__new__") == 0 or \ + string.strcmp(mname_buf, "__before_init__") == 0 or \ + string.strcmp(mname_buf, "__after_init__") == 0: + skip_def = 1 else: - viperlib.snprintf(key_buf, key_len + 1, "%s.%s", src_sha1, mname_buf) - # 去重:同一 key 已存在则跳过(同文件可能被多次加载) - dup_key: int = 0 - dki: int - for dki in range(_g_vpi_ret_count): - if string.strcmp(_g_vpi_ret_keys + dki * VPI_KEY_LEN, key_buf) == 0: - dup_key = 1 - break - if dup_key == 0: - grow_ok: int = 1 - if _g_vpi_ret_count >= _g_vpi_ret_cap: - grow_ok = _grow_vpi_ret_cache() - if grow_ok != 0: - string.strcpy(_g_vpi_ret_keys + _g_vpi_ret_count * VPI_KEY_LEN, key_buf) - string.strncpy(_g_vpi_ret_strs + _g_vpi_ret_count * VPI_RET_LEN, - vpi_buf + rstart, rtype_len) - (_g_vpi_ret_strs + _g_vpi_ret_count * VPI_RET_LEN)[rtype_len] = '\0' - _g_vpi_ret_count += 1 - stdlib.free(key_buf) + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-DEF] sha1=%s cls=%s m=%s\n", src_sha1, cur_cls, mname_buf) + if skip_def == 0: + key_buf: bytes = stdlib.malloc(key_len + 1) + if key_buf is not None: + if cur_cls is not None: + viperlib.snprintf(key_buf, key_len + 1, "%s.%s.%s", src_sha1, cur_cls, mname_buf) + else: + viperlib.snprintf(key_buf, key_len + 1, "%s.%s", src_sha1, mname_buf) + # 去重:同一 key 已存在则跳过(同文件可能被多次加载) + dup_key: int = 0 + dki: int + for dki in range(_g_vpi_ret_count): + if string.strcmp(_g_vpi_ret_keys + dki * VPI_KEY_LEN, key_buf) == 0: + dup_key = 1 + break + if dup_key == 0: + grow_ok: int = 1 + if _g_vpi_ret_count >= _g_vpi_ret_cap: + grow_ok = _grow_vpi_ret_cache() + if grow_ok != 0: + string.strcpy(_g_vpi_ret_keys + _g_vpi_ret_count * VPI_KEY_LEN, key_buf) + string.strncpy(_g_vpi_ret_strs + _g_vpi_ret_count * VPI_RET_LEN, + ret_src, rtype_len) + (_g_vpi_ret_strs + _g_vpi_ret_count * VPI_RET_LEN)[rtype_len] = '\0' + _g_vpi_ret_count += 1 + stdlib.free(key_buf) stdlib.free(mname_buf) pos = le + 1 line_no += 1 @@ -201,6 +242,8 @@ def _grow_vpi_ret_cache() -> int: # ============================================================ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: """按需加载模块 vpi stub 的返回注解缓存,返回 1=已就绪 / 0=失败""" + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-ENSURE] in sha1=%s temp=%s\n", src_sha1, temp_dir) if src_sha1 is None or temp_dir is None: return 0 sha1_len: t.CSizeT = string.strlen(src_sha1) @@ -212,6 +255,8 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: for pre_i in range(_g_vpi_ret_count): pre_key: bytes = _g_vpi_ret_keys + pre_i * VPI_KEY_LEN if string.strncmp(pre_key, src_sha1, sha1_len) == 0 and pre_key[sha1_len] == '.': + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-ENSURE] prefix-hit key=%s count=%d\n", pre_key, _g_vpi_ret_count) return 1 # 构造路径 {temp_dir}/{sha1[0:2]}/{sha1}.vpi(逐字符拼接,避免 %c 格式化问题) td_len: t.CSizeT = string.strlen(temp_dir) @@ -226,8 +271,12 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: vpi_path[td_len + 3] = '/' string.strcpy(vpi_path + td_len + 4, src_sha1) string.strcpy(vpi_path + td_len + 4 + sha1_len, ".vpi") + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-ENSURE] path=%s\n", vpi_path) f: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R) if f.closed: + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-ENSURE] open-fail\n") stdlib.free(vpi_path) return 0 vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) @@ -243,17 +292,74 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: else: vpi_buf[VPI_READ_BUF_SIZE - 1] = 0 _cache_vpi_return_annots_text(vpi_buf, src_sha1) + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-ENSURE] read=%d after-count=%d\n", bytes_read, _g_vpi_ret_count) stdlib.free(vpi_buf) stdlib.free(vpi_path) return 1 +# ============================================================ +# _subst_template_arg - 替换注解字符串中独立的模板参数 token +# +# 泛型模板返回注解(如 list[T].get -> T)中的类型参数名(T) +# 需替换为实际特化实参(AST|t.CPtr)后才能解析。仅替换前后 +# 非字母数字下划线边界的独立 token(避免误替换 t.CSizeT 等)。 +# 返回 stdlib 新分配字符串(调用者负责 free);失败返回 None。 +# ============================================================ +def _subst_template_arg(annot: str, tparam: str, targ: str) -> str: + """替换 annot 中独立的 tparam token 为 targ""" + if annot is None or tparam is None or targ is None: + return None + alen: t.CSizeT = string.strlen(annot) + tplen: t.CSizeT = string.strlen(tparam) + tglen: t.CSizeT = string.strlen(targ) + if tplen == 0 or tplen > alen: + return annot + out: bytes = stdlib.malloc(alen + tglen * 8 + 16) + if out is None: + return None + opos: t.CSizeT = 0 + p: t.CSizeT = 0 + while p < alen: + matched: int = 0 + if annot[p] == tparam[0] and p + tplen <= alen: + if string.strncmp(annot + p, tparam, tplen) == 0: + prev_ch: int = 0 + next_ch: int = 0 + if p > 0: + prev_ch = annot[p - 1] + if p + tplen < alen: + next_ch = annot[p + tplen] + prev_ok: int = 1 + if (prev_ch >= 'a' and prev_ch <= 'z') or (prev_ch >= 'A' and prev_ch <= 'Z') or \ + (prev_ch >= '0' and prev_ch <= '9') or prev_ch == '_': + prev_ok = 0 + next_ok: int = 1 + if (next_ch >= 'a' and next_ch <= 'z') or (next_ch >= 'A' and next_ch <= 'Z') or \ + (next_ch >= '0' and next_ch <= '9') or next_ch == '_': + next_ok = 0 + if prev_ok != 0 and next_ok != 0: + string.strncpy(out + opos, targ, tglen) + opos += tglen + p += tplen + matched = 1 + if matched == 0: + out[opos] = annot[p] + opos += 1 + p += 1 + out[opos] = '\0' + return out + + # ============================================================ # _lookup_vpi_ret_annot - 查找缓存的方法返回注解字符串 # ============================================================ def _lookup_vpi_ret_annot(src_sha1: str, cls_name: str, method_name: str) -> str: """查找缓存的方法返回注解字符串,未找到返回 None""" if _g_vpi_ret_keys is None or _g_vpi_ret_count == 0: + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-LOOKUP] empty count=%d\n", _g_vpi_ret_count) return None if src_sha1 is None or method_name is None: return None @@ -270,11 +376,74 @@ def _lookup_vpi_ret_annot(src_sha1: str, cls_name: str, method_name: str) -> str viperlib.snprintf(key_buf, key_len + 1, "%s.%s.%s", src_sha1, cls_name, method_name) else: viperlib.snprintf(key_buf, key_len + 1, "%s.%s", src_sha1, method_name) + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-LOOKUP] key=%s count=%d\n", key_buf, _g_vpi_ret_count) i: int for i in range(_g_vpi_ret_count): if string.strcmp(_g_vpi_ret_keys + i * VPI_KEY_LEN, key_buf) == 0: + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-LOOKUP] hit\n") stdlib.free(key_buf) return _g_vpi_ret_strs + i * VPI_RET_LEN + # 未命中:打印同 sha1 前缀的所有缓存条目(调试定位 key 形式差异) + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-MISS] key=%s\n", key_buf) + mi: int + for mi in range(_g_vpi_ret_count): + mk: bytes = _g_vpi_ret_keys + mi * VPI_KEY_LEN + if string.strncmp(mk, src_sha1, sha1_len) == 0: + if VPI_DEBUG_ENABLE != 0: + stdio.printf(" [VPI-ENTRY] %s\n", mk) + # 泛型特化归一化匹配:list[AST|t.CPtr].get → list[T].get 模板缓存。 + # 命中后把模板注解中的类型参数名(如 T)替换为实参(AST|t.CPtr)再返回。 + # 不依赖模板类已注册/特化已发生(base.vp 可能先于 list 模块翻译)。 + if cls_name is not None: + lbkt_v: str = string.strchr(cls_name, '[') + if lbkt_v is not None: + rbkt_v: str = string.strrchr(cls_name, ']') + if rbkt_v is not None and rbkt_v > lbkt_v: + base_len_v: t.CSizeT = t.CSizeT(t.CUInt64T(lbkt_v) - t.CUInt64T(cls_name)) + param_len_v: t.CSizeT = t.CSizeT(t.CUInt64T(rbkt_v) - t.CUInt64T(lbkt_v) - 1) + if base_len_v > 0 and param_len_v > 0: + base_v: str = stdlib.malloc(base_len_v + 1) + param_v: str = stdlib.malloc(param_len_v + 1) + if base_v is not None and param_v is not None: + string.strncpy(base_v, cls_name, base_len_v) + base_v[base_len_v] = '\0' + string.strncpy(param_v, lbkt_v + 1, param_len_v) + param_v[param_len_v] = '\0' + # 遍历缓存找 {sha1}.{base}[xxx].{method} + gi: int + for gi in range(_g_vpi_ret_count): + gk: bytes = _g_vpi_ret_keys + gi * VPI_KEY_LEN + if string.strncmp(gk, src_sha1, sha1_len) == 0 and \ + gk[sha1_len] == '.' and \ + string.strncmp(gk + sha1_len + 1, base_v, base_len_v) == 0 and \ + gk[sha1_len + 1 + base_len_v] == '[': + gklb: str = string.strchr(gk + sha1_len + 1 + base_len_v, ']') + if gklb is not None: + tparam_len_v: t.CSizeT = t.CSizeT( + t.CUInt64T(gklb) - t.CUInt64T(gk + sha1_len + 1 + base_len_v) - 1) + if tparam_len_v > 0: + meth_start_v: str = gklb + 2 # 跳过 ']' 和 '.' + if string.strcmp(meth_start_v, method_name) == 0: + tparam_v: str = stdlib.malloc(tparam_len_v + 1) + if tparam_v is not None: + string.strncpy(tparam_v, gk + sha1_len + 1 + base_len_v + 1, tparam_len_v) + tparam_v[tparam_len_v] = '\0' + new_annot_v: str = _subst_template_arg( + _g_vpi_ret_strs + gi * VPI_RET_LEN, tparam_v, param_v) + if VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-GEN] base=%s tparam=%s arg=%s annot=%s\n", + base_v, tparam_v, param_v, + _g_vpi_ret_strs + gi * VPI_RET_LEN) + stdlib.free(tparam_v) + stdlib.free(base_v) + stdlib.free(param_v) + stdlib.free(key_buf) + return new_annot_v + stdlib.free(base_v) + stdlib.free(param_v) stdlib.free(key_buf) return None diff --git a/App/lib/core/Handles/HandlesExprCall.vp b/App/lib/core/Handles/HandlesExprCall.vp index 8166d28..2d4d675 100644 --- a/App/lib/core/Handles/HandlesExprCall.vp +++ b/App/lib/core/Handles/HandlesExprCall.vp @@ -4,6 +4,7 @@ import ast import llvmlite import memhub import string +import stdio import stdlib import sys import viperlib @@ -3082,6 +3083,9 @@ def _lookup_method_ret_ty_via_vpi(pool: memhub.MemBuddy | t.CPtr, cls_name: str, method_name: str) -> llvmlite.LLVMType | t.CPtr: """从 vpi stub 返回注解缓存获取方法返回类型,未命中返回 None""" + if VpiReader.VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-CALL] lookup sha1=%s cls=%s m=%s tdlen=%d\n", + cls_sha1, cls_name, method_name, _g_temp_dir_len) if pool is None or cls_sha1 is None or method_name is None: return None # 函数内懒 import:VpiReader imports HandlesExprCall,顶层 import 会成环 @@ -3094,10 +3098,43 @@ def _lookup_method_ret_ty_via_vpi(pool: memhub.MemBuddy | t.CPtr, VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, _g_temp_dir) annot: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, cls_name, method_name) if annot is None: + if VpiReader.VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-CALL] annot=NONE\n") return None + if VpiReader.VPI_DEBUG_ENABLE != 0: + stdio.printf("[VPI-CALL] annot=%s\n", annot) return VpiReader._resolve_vpi_ret_annot_str(pool, annot, i32_ty, i8_ptr_ty) +# ============================================================ +# _special_method_ret_ty - 编译器内置特殊方法的固定返回类型 +# +# __before_init__ / __init__:编译器生成/约定返回 void +# __new__:返回 Ptr(StructTy)(存储空间指针) +# +# 这些方法不存在于源码(vpi stub 无条目),跨模块调用时 +# found_func/text.ll/vpi 缓存必然未命中,但返回类型是编译器 +# 确定性语义(与 HandlesClassDef._translate_method 3543-3548 一致), +# 在此直接固定,不触发 fail-fast 误报。非特殊方法返回 None。 +# ============================================================ +def _special_method_ret_ty(pool: memhub.MemBuddy | t.CPtr, + class_name: str, + method_name: str) -> llvmlite.LLVMType | t.CPtr: + """编译器内置特殊方法的固定返回类型;非特殊返回 None""" + if pool is None or method_name is None: + return None + if string.strcmp(method_name, "__before_init__") == 0 or \ + string.strcmp(method_name, "__init__") == 0: + return llvmlite.Void(pool) + if string.strcmp(method_name, "__new__") == 0: + if class_name is not None: + sp_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(class_name) + if sp_struct_ty is not None: + return llvmlite.Ptr(pool, sp_struct_ty) + return None + return None + + # ============================================================ # _fail_method_ret_ty - 方法返回类型无法精确获取时 fail-fast # @@ -3441,19 +3478,23 @@ def _translate_property_call(pool: memhub.MemBuddy | t.CPtr, if frt is not None: call_ret_ty = frt else: - cls_sha1_rt: str = HandlesStruct.get_struct_sha1(class_name) - looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL( - pool, cls_sha1_rt, class_name, method_name) - if looked_up_ty is not None: - call_ret_ty = looked_up_ty + sp_ty1: llvmlite.LLVMType | t.CPtr = _special_method_ret_ty(pool, class_name, method_name) + if sp_ty1 is not None: + call_ret_ty = sp_ty1 else: - # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) - vpi_ret_ty: llvmlite.LLVMType | t.CPtr = _lookup_method_ret_ty_via_vpi( + cls_sha1_rt: str = HandlesStruct.get_struct_sha1(class_name) + looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL( pool, cls_sha1_rt, class_name, method_name) - if vpi_ret_ty is not None: - call_ret_ty = vpi_ret_ty + if looked_up_ty is not None: + call_ret_ty = looked_up_ty else: - call_ret_ty = _fail_method_ret_ty(method_name) + # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) + vpi_ret_ty: llvmlite.LLVMType | t.CPtr = _lookup_method_ret_ty_via_vpi( + pool, cls_sha1_rt, class_name, method_name) + if vpi_ret_ty is not None: + call_ret_ty = vpi_ret_ty + else: + call_ret_ty = _fail_method_ret_ty(method_name) # 确保 declare 存在,然后调用 _ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count) @@ -3779,20 +3820,78 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr, if frt is not None: call_ret_ty = frt else: - # found_func 为 None(stub 未注入):尝试从 text.ll 查找实际返回类型 - # 避免指针返回值被默认 i32 导致 inttoptr 截断 - looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL( - pool, cls_sha1_mc, class_name, method_name) - if looked_up_ty is not None: - call_ret_ty = looked_up_ty + # found_func 为 None(stub 未注入):先处理编译器内置特殊方法 + sp_ty2: llvmlite.LLVMType | t.CPtr = _special_method_ret_ty(pool, class_name, method_name) + if sp_ty2 is not None: + call_ret_ty = sp_ty2 else: - # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) - vpi_ret_ty: llvmlite.LLVMType | t.CPtr = _lookup_method_ret_ty_via_vpi( - pool, cls_sha1_mc, class_name, method_name) - if vpi_ret_ty is not None: - call_ret_ty = vpi_ret_ty + # 泛型特化方法调用:特化函数可能尚未注册(如 ast.base 用 + # list[AST|t.CPtr].get)。主动触发特化(幂等,MethodsEmitted 防重入), + # 特化后重新查找即可命中,返回类型来自特化函数定义(天然精确)。 + gen_done: int = 0 + if class_name is not None: + lbkt_g: str = string.strchr(class_name, '[') + if lbkt_g is not None: + rbkt_g: str = string.strrchr(class_name, ']') + if rbkt_g is not None and rbkt_g > lbkt_g: + base_len_g: t.CSizeT = t.CSizeT(t.CUInt64T(lbkt_g) - t.CUInt64T(class_name)) + arg_len_g: t.CSizeT = t.CSizeT(t.CUInt64T(rbkt_g) - t.CUInt64T(lbkt_g) - 1) + if base_len_g > 0 and arg_len_g > 0: + base_buf_g: str = pool.alloc(base_len_g + 1) + arg_buf_g: str = pool.alloc(arg_len_g + 1) + if base_buf_g is not None and arg_buf_g is not None: + string.strncpy(base_buf_g, class_name, base_len_g) + base_buf_g[base_len_g] = '\0' + string.strncpy(arg_buf_g, lbkt_g + 1, arg_len_g) + arg_buf_g[arg_len_g] = '\0' + ta_list_g: list[str] | t.CPtr = list[str](pool, 4) + if ta_list_g is not None: + # 逗号分隔多类型实参(list[X] / dict[K, V]) + cur_arg_g: str = arg_buf_g + while cur_arg_g is not None: + comma_g: str = string.strchr(cur_arg_g, ',') + if comma_g is None: + ta_list_g.append(cur_arg_g) + break + seg_len_g: t.CSizeT = t.CSizeT(t.CUInt64T(comma_g) - t.CUInt64T(cur_arg_g)) + seg_buf_g: str = pool.alloc(seg_len_g + 1) + if seg_buf_g is not None: + string.strncpy(seg_buf_g, cur_arg_g, seg_len_g) + seg_buf_g[seg_len_g] = '\0' + ta_list_g.append(seg_buf_g) + cur_arg_g = comma_g + 1 + spec_name_g: str = HandlesClassDef._specialize_generic_class( + trans, base_buf_g, ta_list_g) + if spec_name_g is not None: + # 特化后重新查找特化函数(特化名 = 调用点类名) + spec_lk_g: str = pool.alloc( + string.strlen(class_name) + string.strlen(method_name) + 2) + if spec_lk_g is not None: + viperlib.snprintf(spec_lk_g, + string.strlen(class_name) + string.strlen(method_name) + 2, + "%s.%s", class_name, method_name) + spec_found_g: llvmlite.Function | t.CPtr = find_func_in_module(mod, spec_lk_g) + if spec_found_g is not None: + frt_g: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(spec_found_g) + if frt_g is not None: + call_ret_ty = frt_g + found_func = spec_found_g + gen_done = 1 + if gen_done == 0: + # 尝试从 text.ll 查找实际返回类型 + # 避免指针返回值被默认 i32 导致 inttoptr 截断 + looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL( + pool, cls_sha1_mc, class_name, method_name) + if looked_up_ty is not None: + call_ret_ty = looked_up_ty else: - call_ret_ty = _fail_method_ret_ty(method_name, cl) + # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) + vpi_ret_ty: llvmlite.LLVMType | t.CPtr = _lookup_method_ret_ty_via_vpi( + pool, cls_sha1_mc, class_name, method_name) + if vpi_ret_ty is not None: + call_ret_ty = vpi_ret_ty + else: + call_ret_ty = _fail_method_ret_ty(method_name, cl) # 翻译调用参数 cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args @@ -4993,8 +5092,11 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, llvmlite.value_set_next(tail3, arg_val3) tail3 = arg_val3 total3: int = 1 + can - ret_ty3: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL( - pool, sha1_fc, cls_nm2, func_name) + ret_ty3: llvmlite.LLVMType | t.CPtr = _special_method_ret_ty( + pool, cls_nm2, func_name) + if ret_ty3 is None: + ret_ty3 = _LookupMethodRetTyFromTextLL( + pool, sha1_fc, cls_nm2, func_name) if ret_ty3 is None: # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) ret_ty3 = _lookup_method_ret_ty_via_vpi( diff --git a/App/lib/core/Phase1.vp b/App/lib/core/Phase1.vp index c1ae0ed..1811c42 100644 --- a/App/lib/core/Phase1.vp +++ b/App/lib/core/Phase1.vp @@ -500,6 +500,11 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 设置全局 temp_dir(供跨模块 CDefine 查找使用) HandlesType.set_temp_dir(temp_dir) + # 同步设置 HandlesExprCall 的 temp_dir(供 vpi stub 返回注解缓存按需加载)。 + # 必须在 includes 翻译前设置:Phase1 翻译 json 等 includes 时,"同模块方法 + # 调用(定义在后)"(如 json.__setitem__ 调 _pool_alloc)需要读 + # {temp_dir}/{sha1[0:2]}/{sha1}.vpi 兜底,而 Phase2 的 SetTempDir 太晚。 + HandlesExprCall.SetTempDir(temp_dir) # 确保 temp 目录存在(build_dir/temp 可能尚未创建) BuildPipeline.ensure_dir(temp_dir)