diff --git a/App/lib/StubGen/VpiReader.vp b/App/lib/StubGen/VpiReader.vp index 864402b..19750f4 100644 --- a/App/lib/StubGen/VpiReader.vp +++ b/App/lib/StubGen/VpiReader.vp @@ -1,1333 +1,1334 @@ -import t, c -from stdint import * -import ast -import string -import stdio -import stdlib -import sys -import memhub -import llvmlite -import viperlib -import w32.fileio as fileio -import lib.core.VLogger as VLogger -import lib.core.Handles.HandlesType as HandlesType -import lib.core.Handles.HandlesStruct as HandlesStruct -import lib.core.Handles.HandlesExprCall as HandlesExprCall -import lib.core.Handles.HandlesFunctions as HandlesFunctions -import lib.core.Handles.HandlesTranslator as HT - - -# ============================================================ -# 方法返回注解缓存(stdlib 持久) -# -# 背景:vpi tree 在"解析后立即"有效,但遍历处理到后续类的方法时, -# 深层 AST 节点(如 parse_args 的 returns 注解)可能已被覆盖损坏 -# (kind() 读取异常),直接 resolve 得到垃圾类型(如 i8*)。 -# 因此采用文本提取方案:ParseVpiIntoModule 读 .vpi stub 时,从 -# vpi_buf 原始文本(在函数末尾才释放,始终有效)直接按行格式 -# 提取 "def m(...) -> Ret:" 的返回注解字符串缓存到 stdlib(持久, -# 不受 pool 状态影响)。HandlesExprCall._lookup_method_ret_ty_via_vpi -# 按 sha1.类名.方法名 查表并用 _resolve_vpi_ret_annot_str 纯字符串 -# 解析为 LLVMType,返回类型始终来自方法定义处,无白名单/无硬编码。 -# ============================================================ -VPI_RET_MAX: t.CDefine = 4096 -VPI_KEY_LEN: t.CDefine = 224 -VPI_RET_LEN: t.CDefine = 224 - -_g_vpi_ret_keys: bytes = None -_g_vpi_ret_strs: bytes = None -_g_vpi_ret_count: int = 0 -_g_vpi_ret_cap: int = 0 - - -# ============================================================ -# _cache_vpi_return_annots_text - 从 vpi_buf 文本直接提取方法返回注解 -# -# 完全不依赖 AST tree(tree 深层节点在后续处理中可能损坏), -# 直接按 vpi 生成文件的行格式提取: -# " def method(...) -> Ret: pass"(类内方法,缩进) -# "def method(...) -> Ret: pass"(模块级函数) -# "class X:"(跟踪当前类名) -# ============================================================ -def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int: - """从 vpi_buf 文本提取方法返回注解字符串(stdlib 持久)""" - 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 - if _g_vpi_ret_keys is None: - _g_vpi_ret_keys = stdlib.malloc(VPI_RET_MAX * VPI_KEY_LEN) - _g_vpi_ret_strs = stdlib.malloc(VPI_RET_MAX * VPI_RET_LEN) - if _g_vpi_ret_keys is None or _g_vpi_ret_strs is None: - return 0 - string.memset(_g_vpi_ret_keys, 0, VPI_RET_MAX * VPI_KEY_LEN) - string.memset(_g_vpi_ret_strs, 0, VPI_RET_MAX * VPI_RET_LEN) - _g_vpi_ret_cap = VPI_RET_MAX - sha1_len: t.CSizeT = string.strlen(src_sha1) - buf_len: t.CSizeT = string.strlen(vpi_buf) - cur_cls: bytes = None - pos: t.CSizeT = 0 - line_no: int = 0 - while pos < buf_len: - ls: t.CSizeT = pos - le: t.CSizeT = pos - while le < buf_len and vpi_buf[le] != '\n': - le += 1 - line_len: t.CSizeT = le - ls - # 跳过前导空格,检查 class/def - i: t.CSizeT = ls - while i < le and vpi_buf[i] == ' ': - i += 1 - if i < le: - # class X: - if line_len >= 6 and string.strncmp(vpi_buf + i, "class ", 6) == 0: - cstart: t.CSizeT = i + 6 - cend: t.CSizeT = cstart - # 类名提取:到 ':' / ' ' / '(' 前停止(继承基类括号不入类名, - # 如 "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: - stdlib.free(cur_cls) - cur_cls = stdlib.malloc(cend - cstart + 1) - if cur_cls is not None: - string.strncpy(cur_cls, vpi_buf + cstart, cend - cstart) - 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] != '(': - dend += 1 - # 方法名 = [dstart, dend) - # 找 "->" - rpos: t.CSizeT = dstart - arrow_pos: t.CSizeT = le - while rpos < le - 1: - if vpi_buf[rpos] == '-' and vpi_buf[rpos + 1] == '>': - arrow_pos = rpos - break - rpos += 1 - 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 - cls_len2: t.CSizeT = 0 - if cur_cls is not None: - cls_len2 = string.strlen(cur_cls) - key_len: t.CSizeT = sha1_len + 1 + cls_len2 + 1 + mname_len - if key_len < VPI_KEY_LEN and rtype_len < VPI_RET_LEN: - # 方法名复制到临时缓冲(vpi_buf+dstart 非 null 结尾,不能直接 snprintf) - mname_buf: bytes = stdlib.malloc(mname_len + 1) - if mname_buf is not None: - string.strncpy(mname_buf, vpi_buf + dstart, mname_len) - mname_buf[mname_len] = '\0' - # 无注解的特殊方法(构造/析构)跳过:它们有专门调用路径 - skip_def: int = 0 - if arrow_pos >= le and ( - 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 - 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 - grow_ok: int = 1 - if dup_key == 0 and _g_vpi_ret_count >= _g_vpi_ret_cap: - grow_ok = _grow_vpi_ret_cache() - if dup_key == 0 and 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 - if cur_cls is not None: - stdlib.free(cur_cls) - return 1 - - -# ============================================================ -# _grow_vpi_ret_cache - 扩容方法返回注解缓存(容量翻倍) -# -# 背景:VPI_RET_MAX 初始 4096,但 includes 模块量大(~90 个 stub、 -# 每个几十个方法),缓存写满后条目被静默丢弃,导致已缓存模块的 -# 方法(如 JsonValue._pool_alloc)查不到而 fail-fast 误报。 -# 用 stdlib.realloc 翻倍扩容(上限 1M 条目,防御异常增长)。 -# ============================================================ -def _grow_vpi_ret_cache() -> int: - """扩容返回注解缓存(容量翻倍),返回 1=成功 / 0=失败""" - global _g_vpi_ret_keys, _g_vpi_ret_strs, _g_vpi_ret_cap - if _g_vpi_ret_keys is None or _g_vpi_ret_cap <= 0: - return 0 - new_cap: int = _g_vpi_ret_cap * 2 - if new_cap > 1048576: - return 0 - new_keys: bytes = stdlib.realloc(_g_vpi_ret_keys, new_cap * VPI_KEY_LEN) - if new_keys is None: - return 0 - new_strs: bytes = stdlib.realloc(_g_vpi_ret_strs, new_cap * VPI_RET_LEN) - if new_strs is None: - return 0 - _g_vpi_ret_keys = new_keys - _g_vpi_ret_strs = new_strs - _g_vpi_ret_cap = new_cap - return 1 - - -# ============================================================ -# _ensure_vpi_ret_annot_loaded - 按需加载模块的 vpi stub 返回注解缓存 -# -# 背景:_cache_vpi_return_annots_text 仅在 stub 注入(LoadDepVpiIntoModule, -# 即"当前模块 import 了该模块")时填充缓存。但模块自身翻译时,自己的 -# stub 尚未被任何依赖方读取,且自己的 text.ll 要翻译完成才落盘,导致 -# "同模块方法调用(定义在后)"(如 json.__setitem__ 调用后定义的 -# _pool_alloc)在 found_func/text.ll/vpi 缓存三类来源上全部未命中。 -# 此函数在缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi -# (Converter 在 deps 阶段已生成全部 stub 文件),填充缓存后即可命中。 -# 幂等:模块已缓存过则跳过,不重复读文件。 -# ============================================================ -def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: - """按需加载模块 vpi stub 的返回注解缓存,返回 1=已就绪 / 0=失败""" - if src_sha1 is None or temp_dir is None: - return 0 - sha1_len: t.CSizeT = string.strlen(src_sha1) - if sha1_len < 2: - return 0 - # 该模块已有缓存条目 → 跳过(幂等) - if _g_vpi_ret_keys is not None and _g_vpi_ret_count > 0: - pre_i: 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] == '.': - return 1 - # 构造路径 {temp_dir}/{sha1[0:2]}/{sha1}.vpi(逐字符拼接,避免 %c 格式化问题) - td_len: t.CSizeT = string.strlen(temp_dir) - path_len: t.CSizeT = td_len + sha1_len + 12 - vpi_path: bytes = stdlib.malloc(path_len) - if vpi_path is None: - return 0 - string.strcpy(vpi_path, temp_dir) - vpi_path[td_len] = '/' - vpi_path[td_len + 1] = src_sha1[0] - vpi_path[td_len + 2] = src_sha1[1] - vpi_path[td_len + 3] = '/' - string.strcpy(vpi_path + td_len + 4, src_sha1) - string.strcpy(vpi_path + td_len + 4 + sha1_len, ".vpi") - f: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R) - if f.closed: - stdlib.free(vpi_path) - return 0 - vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) - if vpi_buf is None: - f.close() - stdlib.free(vpi_path) - return 0 - bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE) - f.close() - if bytes_read > 0: - if bytes_read < VPI_READ_BUF_SIZE: - vpi_buf[bytes_read] = 0 - else: - vpi_buf[VPI_READ_BUF_SIZE - 1] = 0 - _cache_vpi_return_annots_text(vpi_buf, src_sha1) - 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 - # 容量 = alen + tglen*8 + 16 只覆盖 ≤8 次替换;替换次数无上限时 - # opos 会越界写穿 stdlib 堆(哨兵盲区 → CRT 堆损坏后任意 malloc/free - # 静默段错误、崩溃点漂移)。按最坏上界分配:每替换消耗 tplen 写 tglen, - # 最坏全部被替换 → alen + (alen/tplen)*tglen + 1。 - max_sub: t.CSizeT = alen / tplen - if max_sub == 0: - max_sub = 1 - out_size: t.CSizeT = alen + max_sub * tglen + 1 - out: bytes = stdlib.malloc(out_size) - if out is None: - return None - opos: t.CSizeT = 0 - p: t.CSizeT = 0 - while p < alen: - # 防御:opos 超分配即 fail-fast 返回 None(绝不越界写) - if opos >= out_size: - stdlib.free(out) - return None - 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: - return None - if src_sha1 is None or method_name is None: - return None - sha1_len: t.CSizeT = string.strlen(src_sha1) - cl_len: t.CSizeT = 0 - if cls_name is not None: - cl_len = string.strlen(cls_name) - me_len: t.CSizeT = string.strlen(method_name) - key_len: t.CSizeT = sha1_len + 1 + cl_len + 1 + me_len - key_buf: bytes = stdlib.malloc(key_len + 1) - if key_buf is None: - return None - if cls_name is not None: - 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) - i: int - for i in range(_g_vpi_ret_count): - if string.strcmp(_g_vpi_ret_keys + i * VPI_KEY_LEN, key_buf) == 0: - stdlib.free(key_buf) - return _g_vpi_ret_strs + i * VPI_RET_LEN - # 泛型特化归一化匹配: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) - meth_start_v: str = gklb + 2 # 跳过 ']' 和 '.' - if tparam_len_v > 0 and 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) - 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 - - -# ============================================================ -# _resolve_single_vpi_type_str - 解析单个类型段(无 CPtr/CVoid)为 LLVMType -# -# 纯字符串解析,不构造 AST 节点。 -# ============================================================ -def _resolve_single_vpi_type_str(pool: memhub.MemBuddy | t.CPtr, - seg: str) -> llvmlite.LLVMType | t.CPtr: - """解析单个类型段为 LLVMType,无法识别返回 None""" - if seg is None or pool is None: - return None - i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) - i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) - # 内置别名 - if string.strcmp(seg, "str") == 0 or string.strcmp(seg, "bytes") == 0: - return i8_ptr_ty - if string.strcmp(seg, "int") == 0: - return llvmlite.Int32(pool) - if string.strcmp(seg, "bool") == 0: - return llvmlite.Int1(pool) - # LLVM 类型串:vpi 缓存对无返回注解方法默认存 "i32" - # (见 _cache_vpi_return_annots_text;push 等无注解方法返回 i32)。 - # 之前缺分支导致解析返回 None → 泛型模板方法(Vector[int].push)调用 - # 走 fail-fast"方法返回类型无法精确获取",实际是约定不一致。 - if string.strcmp(seg, "i32") == 0: - return llvmlite.Int32(pool) - if string.strcmp(seg, "i64") == 0: - return llvmlite.Int64(pool) - if string.strcmp(seg, "i16") == 0: - return llvmlite.Int16(pool) - if string.strcmp(seg, "i8") == 0: - return i8_ty - if string.strcmp(seg, "i1") == 0: - return llvmlite.Int1(pool) - if string.strcmp(seg, "f32") == 0: - return llvmlite.Float(pool) - if string.strcmp(seg, "f64") == 0: - return llvmlite.Double(pool) - if string.strcmp(seg, "void") == 0: - return llvmlite.Void(pool) - # vpi 源文本中 `-> None` 被原样缓存为 "None"(_cache_vpi_return_annots_text - # 提取 arrow_pos 后到 ':' 前的内容)。此前无 "None" 分支 → 解析返回 None → - # `-> None` 的跨模块方法(如 AssignGenMixin._register_cdefine_from_value)vpi - # 缓存命中也解析不出 void → 方法返回类型 fail-fast 误报(追加80e 断点 B)。 - # None 与 void 同义,归一到 Void。 - if string.strcmp(seg, "None") == 0: - return llvmlite.Void(pool) - # t.XXX / c.XXX 内置类型映射 - if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0: - attr_nm: str = seg + 2 - mapped_ty: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type(pool, attr_nm) - if mapped_ty is not None: - return mapped_ty - return None - # 模块限定类名 "memhub.MemBuddy" → 取最后一段查 struct - last_dot: str = string.strrchr(seg, '.') - if last_dot is not None: - cls_nm2: str = last_dot + 1 - mod_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(cls_nm2) - if mod_struct_ty is not None: - return mod_struct_ty - return None - # 裸类名 → 全局 struct 注册表 - bare_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(seg) - if bare_struct_ty is not None: - return bare_struct_ty - # 裸 typedef 名(如 VOIDPTR) - mapped_bare: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type(pool, seg) - if mapped_bare is not None: - return mapped_bare - return None - - -# ============================================================ -# _resolve_vpi_ret_annot_str - 直接解析 vpi 返回注解字符串为 LLVMType -# -# 背景:_parse_type_string 跨模块构造 ast.Name/ast.BinOp 在 TPV 中可能 -# 退化为裸函数调用(__init__/vtable 未执行),节点 kind() 异常导致 -# resolve 得到垃圾类型(如 Ptr(i8))。此处纯字符串解析避免该问题。 -# -# 支持: "str"/"bytes" → i8*; "int" → i32; "bool" → i1; -# "t.CChar" 等 → map_t_type 映射; -# "X | t.CPtr" → Ptr(Struct X); "t.CVoid | t.CPtr" → i8*; -# "memhub.MemBuddy | t.CPtr" → Ptr(Struct MemBuddy)。 -# ============================================================ -def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy | t.CPtr, - ret_annot: str, - i32_ty: llvmlite.LLVMType | t.CPtr, - i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr: - """直接解析 vpi 返回注解字符串为 LLVMType(不构造 AST 节点)""" - if ret_annot is None or pool is None: - return None - - # tuple[T1, T2, ...] → 匿名结构体 {T1, T2, ...}(多返回值打包, - # 与 TypeGenMixin._resolve_generic_subscript_ty 的 tuple 分支一致)。 - # b2a_hqx -> tuple[str, t.CUnsignedShort] 之前按普通段解析失败 → 调用侧 - # 返回类型退化 i8* → tuple 解包失效(hqx 段崩溃根因)。 - if string.strncmp(ret_annot, "tuple[", 6) == 0: - inner_len_t: t.CSizeT = 0 - while ret_annot[6 + inner_len_t] != ']' and ret_annot[6 + inner_len_t] != '\0': - inner_len_t += 1 - if ret_annot[6 + inner_len_t] != ']' or inner_len_t == 0: - return None - tup_head_t: llvmlite.ParamNode | t.CPtr = None - tup_tail_t: llvmlite.ParamNode | t.CPtr = None - tup_count_t: int = 0 - p_t: t.CSizeT = 0 - while p_t < inner_len_t: - # 跳过前导空格/逗号 - while p_t < inner_len_t and (ret_annot[6 + p_t] == ' ' or ret_annot[6 + p_t] == ','): - p_t += 1 - seg_s_t: t.CSizeT = p_t - while p_t < inner_len_t and ret_annot[6 + p_t] != ',': - p_t += 1 - seg_e_t: t.CSizeT = p_t - while seg_e_t > seg_s_t and ret_annot[6 + seg_e_t - 1] == ' ': - seg_e_t -= 1 - if seg_e_t > seg_s_t: - seg_len_t: t.CSizeT = seg_e_t - seg_s_t - seg_buf_t: bytes = stdlib.malloc(seg_len_t + 1) - if seg_buf_t is None: - return None - string.strncpy(seg_buf_t, ret_annot + 6 + seg_s_t, seg_len_t) - seg_buf_t[seg_len_t] = '\0' - seg_ty_t: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(pool, seg_buf_t) - stdlib.free(seg_buf_t) - if seg_ty_t is None: - return None - tup_node_t: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, seg_ty_t) - if tup_node_t is None: - return None - if tup_head_t is None: - tup_head_t = tup_node_t - else: - llvmlite.paramnode_set_next(tup_tail_t, tup_node_t) - tup_tail_t = tup_node_t - tup_count_t += 1 - if tup_head_t is None: - return None - return llvmlite.Struct(pool, tup_head_t, tup_count_t, None) - - has_cptr: int = 0 - has_cvoid: int = 0 - first_ty: llvmlite.LLVMType | t.CPtr = None - - buf_len: t.CSizeT = string.strlen(ret_annot) - pos: t.CSizeT = 0 - while pos <= buf_len: - # 跳过前导空格 - while pos < buf_len and ret_annot[pos] == ' ': - pos += 1 - seg_start: t.CSizeT = pos - while pos < buf_len and ret_annot[pos] != '|': - pos += 1 - seg_end: t.CSizeT = pos - # trim 尾随空格 - while seg_end > seg_start and ret_annot[seg_end - 1] == ' ': - seg_end -= 1 - if seg_end > seg_start: - seg_len2: t.CSizeT = seg_end - seg_start - seg_buf: bytes = stdlib.malloc(seg_len2 + 1) - if seg_buf is None: - break - string.strncpy(seg_buf, ret_annot + seg_start, seg_len2) - seg_buf[seg_len2] = '\0' - if string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0: - has_cptr = 1 - elif string.strcmp(seg_buf, "t.CVoid") == 0 or string.strcmp(seg_buf, "CVoid") == 0: - has_cvoid = 1 - else: - seg_ty: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(pool, seg_buf) - if seg_ty is not None and first_ty is None: - first_ty = seg_ty - stdlib.free(seg_buf) - pos += 1 # 跳过 '|' - - if has_cptr != 0: - if has_cvoid != 0: - return i8_ptr_ty - if first_ty is not None: - return llvmlite.Ptr(pool, first_ty) - return i8_ptr_ty - if has_cvoid != 0: - # t.CVoid | t.State:State 是装饰器标记(编译侧忽略),t.CVoid → void。 - # 此前缺此分支 → first_ty=None → 返回 None → 跨模块函数返回类型 - # fail-fast(CPythonTest Py_Initialize() -> t.CVoid | t.State 根因)。 - return llvmlite.Void(pool) - if first_ty is not None: - return first_ty - return None - - -# ============================================================ -# VpiReader - .vpi AST 解析器 -# -# 从 .vpi 存根文件读取函数签名和 CTypedef 定义,注入 declare -# 到当前模块。用于跨模块函数调用时补充缺失的函数声明。 -# -# .vpi 文件由 StubGen.Converter._GeneratePyiFromAst 生成, -# 包含函数签名(无函数体)、类定义、变量声明、import 语句。 -# VpiReader 解析 .vpi 为 AST,提取 FunctionDef 和 CTypedef -# AnnAssign,在当前模块创建对应的 declare 声明。 -# ============================================================ - -# .vpi 读取缓冲区大小(256KB,与 PYI_BUF_SIZE 一致) -VPI_READ_BUF_SIZE: t.CSizeT = 262144 - -# SHA1 字符串长度(16 hex 字符) -SHA1_LEN: t.CSizeT = 16 - - -# ============================================================ -# _extract_sha1_from_path - 从 .vpi 文件路径提取 SHA1 -# -# .vpi 文件名格式为 {sha1}.vpi,sha1 是 16 位十六进制字符。 -# 从路径中找到最后一个路径分隔符(/ 或 \),取后续文件名的前 -# 16 个字符作为 SHA1。分配新缓冲区存储 SHA1(调用者负责 free)。 -# -# Args: -# vpi_path: .vpi 文件路径 -# -# Returns: -# SHA1 字符串(stdlib.malloc 分配,需 stdlib.free),None 失败 -# ============================================================ -def _extract_sha1_from_path(vpi_path: str) -> str: - """从 .vpi 文件路径提取 SHA1(文件名前16字符,malloc 分配)""" - if vpi_path is None: - return None - # 查找最后一个路径分隔符 / 或 \ - total_len: t.CSizeT = string.strlen(vpi_path) - if total_len < SHA1_LEN + 4: # 至少需要 "xxxx.vpi" 的长度 - return None - # 从后向前查找最后一个 / 或 \ - sep_pos: t.CSizeT = total_len - while sep_pos > 0: - sep_pos -= 1 - ch: t.CChar = vpi_path[sep_pos] - if ch == '/' or ch == '\\': - sep_pos += 1 - break - if sep_pos + SHA1_LEN > total_len: - return None - # 分配缓冲区并复制 SHA1(16 字符 + null 终止符) - sha1_buf: str = stdlib.malloc(SHA1_LEN + 1) - if sha1_buf is None: - return None - string.strncpy(sha1_buf, vpi_path + sep_pos, SHA1_LEN) - sha1_buf[SHA1_LEN] = '\0' - return sha1_buf - - -# ============================================================ -# _stub_add_imported - 追加模块名到空格分隔列表(stub 上下文收集用) -# -# 与 HandlesImports.add_imported_module 逻辑一致,但内联在本模块, -# 避免 VpiReader → HandlesImports → StubMerger → VpiReader 循环依赖。 -# ============================================================ -def _stub_add_imported(pool: memhub.MemBuddy | t.CPtr, - cur: str, name: str) -> str: - """追加模块名到空格分隔列表(pool 分配),返回新列表""" - if name is None: - return cur - if cur is None: - nlen: t.CSizeT = string.strlen(name) - buf: str = pool.alloc(nlen + 1) - if buf is not None: - string.strcpy(buf, name) - return buf - old_len: t.CSizeT = string.strlen(cur) - name_len: t.CSizeT = string.strlen(name) - buf2: str = pool.alloc(old_len + 1 + name_len + 1) - if buf2 is not None: - string.strcpy(buf2, cur) - buf2[old_len] = ' ' - string.strcpy(buf2 + old_len + 1, name) - return buf2 - - -# ============================================================ -# _stub_add_from - 追加 from-import 映射("local:mod[:orig]" 空格分隔) -# ============================================================ -def _stub_add_from(pool: memhub.MemBuddy | t.CPtr, - cur: str, local: str, mod: str, orig: str) -> str: - """追加 from-import 映射(pool 分配),返回新列表""" - if local is None or mod is None: - return cur - entry: str = pool.alloc(256) - if entry is None: - return cur - if orig is not None and string.strcmp(orig, local) != 0: - viperlib.snprintf(entry, 256, "%s:%s:%s", local, mod, orig) - else: - viperlib.snprintf(entry, 256, "%s:%s", local, mod) - if cur is None: - return entry - old_len: t.CSizeT = string.strlen(cur) - entry_len: t.CSizeT = string.strlen(entry) - buf: str = pool.alloc(old_len + 1 + entry_len + 1) - if buf is not None: - string.strcpy(buf, cur) - buf[old_len] = ' ' - string.strcpy(buf + old_len + 1, entry) - return buf - - -# ============================================================ -# ParseVpiIntoModule - 解析 .vpi 文件,将函数声明注入到当前模块 -# -# 读取 .vpi 文件 → AST 解析 → 遍历顶层节点 → 创建 declare 声明。 -# 对每个 FunctionDef: -# 1. 提取函数名、参数、返回类型 -# 2. 检测 CExport/CExtern/State 标记(决定是否用裸名) -# 3. 用 find_func_in_module 去重(已存在则跳过) -# 4. 用 create_declare + add_param 创建 declare 声明 -# 5. 注册到函数表(用裸名,非 mangled_name) -# 对 AnnAssign with t.CTypedef:调用 register_ctypedef 注册 -# ClassDef/Import/ImportFrom:跳过 -# -# Args: -# pool: 内存池 -# mod: 当前 LLVM 模块 -# vpi_path: .vpi 文件路径 -# trans: 翻译器对象 -# -# Returns: -# 1=成功,0=失败 -# ============================================================ -def ParseVpiIntoModule(pool: memhub.MemBuddy | t.CPtr, - mod: llvmlite.LLVMModule | t.CPtr, - vpi_path: str, - trans: HT.Translator | t.CPtr) -> int: - """解析 .vpi 文件,将函数声明注入到当前模块 - - 解析 stub 期间临时将错误归因切换到被读 stub 文件(.vpi): - 注解解析失败时 fatal_error 应指向 stub 及其行号(真实出错位置), - 而非当前编译模块(错误归因修复)。 - """ - if pool is None or mod is None or vpi_path is None or trans is None: - return 0 - saved_cur: str = HandlesType.get_current_file() - HandlesType.set_current_file(vpi_path) - ret: int = _ParseVpiIntoModuleImpl(pool, mod, vpi_path, trans) - HandlesType.set_current_file(saved_cur) - return ret - - -def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr, - mod: llvmlite.LLVMModule | t.CPtr, - vpi_path: str, - trans: HT.Translator | t.CPtr) -> int: - """解析 .vpi 文件,将函数声明注入到当前模块(ParseVpiIntoModule 实际实现)""" - if pool is None or mod is None or vpi_path is None or trans is None: - return 0 - - # 1. 读取 .vpi 文件 - f: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R) - if f.closed: - return 0 - vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) - if vpi_buf is None: - f.close() - return 0 - bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE) - f.close() - if bytes_read <= 0: - stdlib.free(vpi_buf) - return 0 - if bytes_read < VPI_READ_BUF_SIZE: - vpi_buf[bytes_read] = 0 - else: - vpi_buf[VPI_READ_BUF_SIZE - 1] = 0 - - # 2. 解析为 AST - lx: ast.Lexer | t.CPtr = ast.new_lexer(pool) - if lx is None: - stdlib.free(vpi_buf) - return 0 - ast._lexer_init(lx, vpi_buf, pool) - tokens: ast.Token | t.CPtr = ast.tokenize(lx) - tree: ast.AST | t.CPtr = ast.parse_tokens(pool, tokens) - # vpi_buf 延迟释放:AST 字符串字段可能引用 vpi_buf 内部(token 零拷贝), - # 提前 free 会导致 Name.id/Attribute.attr 等悬空 → 后续解析返回错误类型。 - # 在 ParseVpiIntoModule 末尾统一释放。 - if tree is None: - stdlib.free(vpi_buf) - return 0 - - # 提取源模块 SHA1(用于函数名混淆,在 AST 解析成功后提取避免提前返回时泄漏) - src_sha1: str = _extract_sha1_from_path(vpi_path) - - # 从 vpi_buf 文本直接提取方法返回注解字符串缓存到 stdlib - # (不依赖 AST tree:tree 深层节点在后续遍历处理时可能被覆盖损坏, - # 但 vpi_buf 原始文本在 ParseVpiIntoModule 末尾才释放,始终有效) - _cache_vpi_return_annots_text(vpi_buf, src_sha1) - - # 3. 遍历 AST 顶层节点 - ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children - if ch is None: - if src_sha1 is not None: - stdlib.free(src_sha1) - return 1 - n: t.CSizeT = ch.__len__() - - # 收集 stub 内被读模块自己的 import 语句,构建独立的注解解析上下文。 - # .vpi stub 顶部含被读模块的 import(Converter 已写入,如 string.vp 的 - # "import t, c"),用它替代当前模块的 imported_modules/from_imports 去 - # 解析注解——根治跨上下文误报(如 t.CArray[str] 需要 't' 已导入)。 - stub_imported_modules: str = None - stub_from_imports: str = None - si: t.CSizeT - for si in range(n): - schild: ast.AST | t.CPtr = ch.get(si) - if schild is None: - continue - sk: int = schild.kind() - if sk == ast.ASTKind.Import: - simp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(schild) - if simp.names is not None: - sn_n: t.CSizeT = simp.names.__len__() - sni: t.CSizeT - for sni in range(sn_n): - sal: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(simp.names.get(sni)) - if sal is not None and sal.name is not None: - stub_imported_modules = _stub_add_imported( - pool, stub_imported_modules, sal.name) - if sal.asname is not None: - stub_imported_modules = _stub_add_imported( - pool, stub_imported_modules, sal.asname) - elif sk == ast.ASTKind.ImportFrom: - sifr: ast.ImportFrom | t.CPtr = (ast.ImportFrom | t.CPtr)(schild) - sifr_mod: str = sifr.module - if sifr_mod is not None: - stub_imported_modules = _stub_add_imported( - pool, stub_imported_modules, sifr_mod) - if sifr.names is not None: - sifr_nn: t.CSizeT = sifr.names.__len__() - sifri: t.CSizeT - for sifri in range(sifr_nn): - sial: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(sifr.names.get(sifri)) - if sial is not None and sial.name is not None: - # '*' star import 不建 from 映射(lookup 有 star 回退) - if string.strcmp(sial.name, "*") != 0: - slocal: str = sial.name - sorig: str = None - if sial.asname is not None: - slocal = sial.asname - sorig = sial.name - stub_from_imports = _stub_add_from( - pool, stub_from_imports, slocal, sifr_mod, sorig) - - # 用 stub 自己的 imports 替代当前模块的(stub 注解解析上下文正确性) - imported_modules: str = trans._imported_modules - from_imports: str = trans._from_imports - if stub_imported_modules is not None: - imported_modules = stub_imported_modules - if stub_from_imports is not None: - from_imports = stub_from_imports - # 把源模块自己加入 stub 注解上下文:stub 注解(如 __sha512 的 transform - # 参数 t.CArray[t.CUInt8T, SHA512_BLOCK_LEN])引用源模块自身的 CDefine, - # 但 stub 的 import 只含被 import 的模块(t/c/string),不含自己。 - # 不加则 _eval_const_int_expr 跨模块 CDefine 查找遍历不到源模块 - # (imported_modules 无 __sha512)→ t.CArray count 报"必须是整数常量"。 - # 示例:RelPath "hashlib/__sha512.vp" → 模块名 "hashlib.__sha512"。 - if src_sha1 is not None: - import lib.core.StubMerger as StubMerger - stub_rel_src: str = StubMerger.LookupSha1RelPath(src_sha1) - if stub_rel_src is not None: - sr_len_src: t.CSizeT = string.strlen(stub_rel_src) - if sr_len_src > 3 and string.strncmp(stub_rel_src + sr_len_src - 3, ".vp", 3) == 0: - sr_len_src -= 3 - elif sr_len_src > 3 and string.strncmp(stub_rel_src + sr_len_src - 3, ".py", 3) == 0: - sr_len_src -= 3 - if sr_len_src > 0: - src_mod_buf: str = stdlib.malloc(sr_len_src + 1) - if src_mod_buf is not None: - sri_src: t.CSizeT - for sri_src in range(sr_len_src): - ch_src: t.CChar = stub_rel_src[sri_src] - if ch_src == '/' or ch_src == '\\': - src_mod_buf[sri_src] = '.' - else: - src_mod_buf[sri_src] = ch_src - src_mod_buf[sr_len_src] = '\0' - imported_modules = _stub_add_imported(pool, imported_modules, src_mod_buf) - stdlib.free(src_mod_buf) - - i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) - i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) - i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) - - # 函数表信息 - funcs_ptr: HandlesExprCall.FuncEntry | t.CPtr = trans._funcs - max_funcs: int = 1024 - - i: t.CSizeT - for i in range(n): - child: ast.AST | t.CPtr = ch.get(i) - if child is None: - continue - kd: int = child.kind() - - # FunctionDef → 提取函数名、参数、返回类型 → 注入 declare - if kd == ast.ASTKind.FunctionDef: - fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child) - if fd is None or fd.name is None: - continue - _inject_function_declare(pool, mod, fd, trans, src_sha1, - imported_modules, from_imports, - i32_ty, i8_ptr_ty, - funcs_ptr, max_funcs) - continue - - # AnnAssign with t.CTypedef → 注册 CTypedef; - # 普通模块级全局变量(如 tokens._op_head)→ external global declare - if kd == ast.ASTKind.AnnAssign: - aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(child) - if aa is None: - continue - if HandlesType._is_ctypedef_annotation(aa.annotation) != 0: - _register_ctypedef_from_annassign(pool, aa) - else: - _inject_global_variable_declare(pool, mod, aa, trans, - stub_imported_modules, stub_from_imports, - i32_ty, i8_ptr_ty) - continue - - # ClassDef → 递归注入类内方法(如 MemBuddy.__init__/alloc) - # 方法 declare 使用 {sha1}.{ClassName}.{method} 混淆名, - # 参数类型从 vpi 注解解析(避免调用时 _ensure_method_declare 用实参类型 - # 创建错误 declare,如 size: t.CSizeT 被写成 i32 而非 i64) - if kd == ast.ASTKind.ClassDef: - class_cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child) - if class_cd is None or class_cd.name is None: - continue - cls_name_v: str = class_cd.name - # 泛型类(class X[T])跳过方法 declare 注入:stub 模板签名含类级 - # 类型参数 T(如 ndarray[T].__add__ 的返回 ndarray[T] | t.CPtr), - # 注入时 T 无法确定具体类型,直接解析会触发 - # _specialize_generic_class("ndarray", ["T"]) 错误特化 - # (Phase2 翻译 App 文件注入 numpy stub 时崩溃)。 - # 具体特化的方法 define 已在 Phase1 生成,跨模块调用按特化类名 - # (ndarray[t.CDouble].__add__)查找命中,无需 stub 模板 declare。 - tp_stub: list[str] | t.CPtr = class_cd.type_params - if tp_stub is not None and tp_stub.__len__() > 0: - continue - cls_children: list[ast.AST | t.CPtr] | t.CPtr = class_cd.children - if cls_children is not None: - cn2: t.CSizeT = cls_children.__len__() - for ci2 in range(cn2): - m_child: ast.AST | t.CPtr = cls_children.get(ci2) - if m_child is None: - continue - if m_child.kind() != ast.ASTKind.FunctionDef: - continue - m_fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m_child) - if m_fd is None or m_fd.name is None: - continue - _inject_method_declare(pool, mod, m_fd, cls_name_v, trans, - src_sha1, imported_modules, from_imports, - i32_ty, i8_ptr_ty, funcs_ptr, max_funcs) - continue - - # Import/ImportFrom → 跳过 - # (import 不需要处理) - - # 释放 SHA1 缓冲区(_extract_sha1_from_path 用 stdlib.malloc 分配) - if src_sha1 is not None: - stdlib.free(src_sha1) - # vpi_buf 延迟到全部 tree 处理完成后释放(见解析处注释) - stdlib.free(vpi_buf) - return 1 - - -# ============================================================ -# _inject_global_variable_declare - 从 vpi AnnAssign 创建模块级全局变量 declare -# -# 背景:vpi 里模块级全局变量(如 tokens.vp 的 _op_head: OpEntry | t.CPtr = None) -# 需在当前模块创建 external global 声明(短名 @name,COFF 按名链接),否则 -# 跨模块 from-import 读取全局变量时 find_global_in_module 找不到 → -# "跨模块全局变量类型未知(stub 未注入)" 误杀(lexer.vp 读 tokens._op_head)。 -# ============================================================ -def _inject_global_variable_declare(pool: memhub.MemBuddy | t.CPtr, - mod: llvmlite.LLVMModule | t.CPtr, - aa: ast.AnnAssign | t.CPtr, - trans: HT.Translator | t.CPtr, - imported_modules: str, - from_imports: str, - i32_ty: llvmlite.LLVMType | t.CPtr, - i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> None: - """从 vpi AnnAssign 创建模块级全局变量 external declare(短名 @name)""" - if pool is None or mod is None or aa is None or trans is None: - return - tgt_v: ast.AST | t.CPtr = aa.target - if tgt_v is None or tgt_v.kind() != ast.ASTKind.Name: - return - nm_v: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tgt_v) - if nm_v is None or nm_v.id is None: - return - gname_v: str = nm_v.id - if gname_v[0] == '\0': - return - # 去重:遍历模块全局变量链表(短名匹配) - cur_v: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead - while cur_v is not None: - if cur_v.Name is not None and string.strcmp(cur_v.Name, gname_v) == 0: - return - cur_v = cur_v.Next - # 解析注解类型(裸类 → Ptr(struct),CArray → 元素指针) - resolved_v: llvmlite.LLVMType | t.CPtr = None - if aa.annotation is not None: - resolved_v = HandlesType.resolve_annotation_type( - pool, aa.annotation, imported_modules, from_imports, trans) - if resolved_v is None: - return # 类型解析失败跳过:调用侧有 fail-fast 定位 - gv_ty_v: llvmlite.LLVMType | t.CPtr = resolved_v - match resolved_v: - case llvmlite.LLVMType.Struct(_, _, _): - gv_ty_v = llvmlite.Ptr(pool, resolved_v) - case llvmlite.LLVMType.Array(aelem_v, acount_v): - # 定长数组(CArray[T, N]):保留完整数组类型 [N x T],与定义端 - # handle_module_level_var 生成的 global [N x T] 一致——此前一律 - # 衰减为元素指针 i32*,链接后引用端 load 把数组前 8 字节当指针 - # 读 → 垃圾地址 → zdeflate_len_to_symbol 读 NULL/垃圾崩溃 - # (zdeflate_len_base 跨模块数组根因)。 - if acount_v > 0: - gv_ty_v = resolved_v - else: - # 不定长数组(CArray[T, None]):元素指针(C 数组衰减) - gv_ty_v = llvmlite.Ptr(pool, aelem_v) - case _: - gv_ty_v = resolved_v - gv_v: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gname_v, gv_ty_v) - if gv_v is None: - return - llvmlite.module_add_global(mod, gv_v) - gv_v.Linkage = "external" - - -# ============================================================ -# _inject_function_declare - 为单个 FunctionDef 创建 declare 声明 -# -# 检测 CExport/CExtern/State 标记,计算 mangled name, -# 用 find_func_in_module 去重,创建 declare 并添加参数, -# 注册到函数表。 -# ============================================================ -def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr, - mod: llvmlite.LLVMModule | t.CPtr, - fd: ast.FunctionDef | t.CPtr, - trans: HT.Translator | t.CPtr, - src_sha1: str, - imported_modules: str, - from_imports: str, - i32_ty: llvmlite.LLVMType | t.CPtr, - i8_ptr_ty: llvmlite.LLVMType | t.CPtr, - funcs_ptr: HandlesExprCall.FuncEntry | t.CPtr, - max_funcs: int) -> None: - """为 FunctionDef 创建 declare 声明并注入到模块""" - if fd.name is None: - return - - # 检测 CExtern/State/CExport 标记(从返回类型注解) - has_extern: int = 0 - has_state: int = 0 - has_export: int = 0 - if fd.returns is not None: - has_extern = HandlesType.has_decorator_marker(fd.returns, "CExtern") - has_state = HandlesType.has_decorator_marker(fd.returns, "State") - has_export = HandlesType.has_decorator_marker(fd.returns, "CExport") - - # 计算 mangled name - # CExport/CExtern/State/main 函数用裸名,其他函数用源模块 SHA1 前缀 - use_bare_name: int = 0 - if has_extern != 0 or has_state != 0 or has_export != 0 or string.strcmp(fd.name, "main") == 0: - mangled_name: str = fd.name - use_bare_name = 1 - else: - mangled_name = HandlesFunctions._mangle_name_with_sha1(src_sha1, fd.name) - - # 去重:如果函数已存在则跳过 - existing: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, mangled_name) - if existing is not None: - return - # 也检查裸名(非裸名模式时,CExport 函数可能已通过其他途径注册) - if use_bare_name == 0: - existing_bare: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, fd.name) - if existing_bare is not None: - return - - # 推断返回类型 - # 优先用缓存的返回注解字符串(tree 解析后立即提取,节点未损坏) - # 直接字符串解析(绕开 _parse_type_string 跨模块 AST 构造在 TPV 中的 - # __init__/vtable 未执行问题,节点 kind() 异常导致 resolve 得垃圾类型) - ret_ty: llvmlite.LLVMType | t.CPtr = None - ret_annot_fd: str = _lookup_vpi_ret_annot(src_sha1, None, fd.name) - if ret_annot_fd is not None: - ret_ty = _resolve_vpi_ret_annot_str(pool, ret_annot_fd, i32_ty, i8_ptr_ty) - if ret_ty is None and fd.returns is not None: - ret_ty = HandlesType.resolve_annotation_type( - pool, fd.returns, imported_modules, from_imports, trans) - # 如果返回类型是纯 t.State(无实际类型),用 Void - if ret_ty is None and fd.returns is not None: - if has_state != 0: - ret_ty = llvmlite.Void(pool) - # 默认返回 i32 - if ret_ty is None: - ret_ty = i32_ty - - # 创建 declare - func: llvmlite.Function | t.CPtr = llvmlite.create_declare( - pool, mod, mangled_name, ret_ty) - if func is None: - return - - # 提取参数并添加到 declare - param_count: int = 0 - if fd.args is not None: - args_node: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args) - if args_node.args is not None: - args_list: list[ast.AST | t.CPtr] | t.CPtr = args_node.args - param_count = args_list.__len__() - pi: t.CSizeT - for pi in range(param_count): - ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(args_list.get(pi)) - if ag is None: - continue - pname: str = ag.arg - if pname is None: - pname = "arg" - # 解析参数类型 - param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty - if ag.annotation is not None: - resolved_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, ag.annotation, imported_modules, from_imports, trans) - if resolved_ty is not None: - # 结构体按值参数 → i64:跨模块结构体在本模块是 opaque - # (未定义字段),LLVM 不允许 opaque struct 作为函数参数 - # (llc: invalid type for function argument)。Windows ABI - # 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。 - # CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。 - match resolved_ty: - case llvmlite.LLVMType.Struct(_, _, _): - param_ty = llvmlite.Int64(pool) - case llvmlite.LLVMType.Array(aelem_ty, _): - param_ty = llvmlite.Ptr(pool, aelem_ty) - case _: - param_ty = resolved_ty - llvmlite.add_param(pool, func, param_ty, pname) - # *args 变长参数:标记 IsVarArg=1,使调用侧走 C ABI 参数提升 - # (i8/i16 → i32,float → double),避免 %d/%x 等格式因参数按 i64 - # 传递而读错(第二个 %x 读到第一个 i64 槽的高 4 字节)。 - if args_node.vararg is not None: - func.IsVarArg = 1 - - # 注册到函数表(用裸名 fd.name,不是 mangled_name) - if funcs_ptr is not None: - cur_count: int = trans._func_count - if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs, - None, 0, param_count) == 0: - trans._func_count = cur_count + 1 - - -# ============================================================ -# _inject_method_declare - 为 ClassDef 内的方法创建 declare -# -# 方法混淆名: {src_sha1}.{ClassName}.{method} -# 参数类型从 vpi 注解解析(self 无注解时默认为 i8*)。 -# 注入正确签名避免调用时 _ensure_method_declare 用实参类型 -# 创建错误 declare(如 size: t.CSizeT 被写成 i32 而非 i64)。 -# ============================================================ -def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr, - mod: llvmlite.LLVMModule | t.CPtr, - m_fd: ast.FunctionDef | t.CPtr, - cls_name: str, - trans: HT.Translator | t.CPtr, - src_sha1: str, - imported_modules: str, - from_imports: str, - i32_ty: llvmlite.LLVMType | t.CPtr, - i8_ptr_ty: llvmlite.LLVMType | t.CPtr, - funcs_ptr: HandlesExprCall.FuncEntry | t.CPtr, - max_funcs: int) -> None: - """为 ClassDef 内方法创建 declare 声明并注入到模块""" - if m_fd is None or m_fd.name is None or cls_name is None: - return - if src_sha1 is None: - return - - # 构造方法查找名: ClassName.method - lookup_name_len: t.CSizeT = string.strlen(cls_name) + 1 + string.strlen(m_fd.name) + 1 - lookup_name: str = pool.alloc(lookup_name_len) - if lookup_name is None: - return - viperlib.snprintf(lookup_name, lookup_name_len, "%s.%s", cls_name, m_fd.name) - - # mangled name: {src_sha1}.{ClassName}.{method} - mangled_len: t.CSizeT = string.strlen(src_sha1) + 1 + lookup_name_len - mangled_name: str = pool.alloc(mangled_len) - if mangled_name is None: - return - viperlib.snprintf(mangled_name, mangled_len, "%s.%s", src_sha1, lookup_name) - - # 去重:如果方法已存在则跳过 - existing: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, mangled_name) - if existing is not None: - return - - # 推断返回类型 - # 优先用缓存的返回注解字符串(tree 解析后立即提取,节点未损坏), - # 用 _parse_type_string 重新解析成新 AST(立即使用不被覆盖)。 - # 直接解析 m_fd.returns 可能因 vpi tree 深层节点被覆盖而得到垃圾类型。 - ret_ty: llvmlite.LLVMType | t.CPtr = None - ret_annot: str = _lookup_vpi_ret_annot(src_sha1, cls_name, m_fd.name) - if ret_annot is not None: - ret_ty = _resolve_vpi_ret_annot_str(pool, ret_annot, i32_ty, i8_ptr_ty) - if ret_ty is None and m_fd.returns is not None: - ret_ty = HandlesType.resolve_annotation_type( - pool, m_fd.returns, imported_modules, from_imports, trans) - # __new__ 无返回注解时推断为类指针(__new__ 返回 self 指针,默认 i32 会截断指针) - if ret_ty is None and string.strcmp(m_fd.name, "__new__") == 0: - cls_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(cls_name) - if cls_ty is not None: - ret_ty = llvmlite.Ptr(pool, cls_ty) - if ret_ty is None: - ret_ty = i32_ty - - # 创建 declare - func: llvmlite.Function | t.CPtr = llvmlite.create_declare( - pool, mod, mangled_name, ret_ty) - if func is None: - return - - # 提取参数并添加到 declare - param_count: int = 0 - if m_fd.args is not None: - args_node: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(m_fd.args) - if args_node.args is not None: - args_list: list[ast.AST | t.CPtr] | t.CPtr = args_node.args - param_count = args_list.__len__() - pi: t.CSizeT - for pi in range(param_count): - ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(args_list.get(pi)) - if ag is None: - continue - pname: str = ag.arg - if pname is None: - pname = "arg" - # 解析参数类型 - param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty - if ag.annotation is not None: - resolved_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( - pool, ag.annotation, imported_modules, from_imports, trans) - if resolved_ty is not None: - # 结构体按值参数 → i64:跨模块结构体在本模块是 opaque - # (未定义字段),LLVM 不允许 opaque struct 作为函数参数 - # (llc: invalid type for function argument)。Windows ABI - # 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。 - # CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。 - match resolved_ty: - case llvmlite.LLVMType.Struct(_, _, _): - param_ty = llvmlite.Int64(pool) - case llvmlite.LLVMType.Array(aelem_ty, _): - param_ty = llvmlite.Ptr(pool, aelem_ty) - case _: - param_ty = resolved_ty - llvmlite.add_param(pool, func, param_ty, pname) - - # OOP iter 协议:__next__ 方法 stub 声明带隐藏 __stop_iter_flag__(i1*), - # 与 _translate_method 的 define 签名一致(对齐 TPC HandlesFunctions.py - # L703-705)。for x in obj 循环会以 2 个实参调用 __next__(self, flag)。 - if m_fd.name is not None and string.strcmp(m_fd.name, "__next__") == 0: - i1_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) - if i1_ty_si is not None: - i1_ptr_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i1_ty_si) - if i1_ptr_ty_si is not None: - llvmlite.add_param(pool, func, i1_ptr_ty_si, "__stop_iter_flag__") - - # 注册到函数表(用裸名 ClassName.method,与调用方 lookup_name 一致) - # ParamCount 不含 self(方法首参 self/cls 不计入,与 _translate_method 的 - # md_param_count = args.__len__() - 1 保持一致)——默认参数填充按非 self - # 参数计数,若含 self 会导致 mdf_param_count 虚高、填充逻辑错位/越界。 - if funcs_ptr is not None: - cur_count: int = trans._func_count - param_count_no_self: int = param_count - if param_count_no_self > 0: - param_count_no_self -= 1 - if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, lookup_name, func, max_funcs, - None, 0, param_count_no_self) == 0: - trans._func_count = cur_count + 1 - - -# ============================================================ -# _register_ctypedef_from_annassign - 从 AnnAssign 节点注册 CTypedef -# -# 提取 NAME: t.CTypedef = Type 中的名称和类型表达式字符串, -# 调用 register_ctypedef 注册到动态 CTypedef 表。 -# ============================================================ -def _register_ctypedef_from_annassign(pool: memhub.MemBuddy | t.CPtr, - aa: ast.AnnAssign | t.CPtr) -> None: - """从 AnnAssign 节点提取并注册 CTypedef""" - if aa is None or pool is None: - return - # 提取目标名称(必须是 Name 节点) - if aa.target is None or aa.target.kind() != ast.ASTKind.Name: - return - tgt: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.target) - if tgt.id is None: - return - # 提取类型表达式字符串(从 value AST 节点) - if aa.value is None: - return - type_str: str = HandlesType._get_type_name_from_annotation(pool, aa.value) - if type_str is None: - return - # 注册 CTypedef - HandlesType.register_ctypedef(pool, tgt.id, type_str) +import t, c +from stdint import * +import ast +import string +import stdio +import stdlib +import sys +import memhub +import llvmlite +import viperlib +import w32.fileio as fileio +import lib.core.VLogger as VLogger +import lib.core.Handles.HandlesType as HandlesType +import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesExprCall as HandlesExprCall +import lib.core.Handles.HandlesFunctions as HandlesFunctions +import lib.core.Handles.HandlesTranslator as HT +import lib.core.StubMergerStore as StubMergerStore + + +# ============================================================ +# 方法返回注解缓存(stdlib 持久) +# +# 背景:vpi tree 在"解析后立即"有效,但遍历处理到后续类的方法时, +# 深层 AST 节点(如 parse_args 的 returns 注解)可能已被覆盖损坏 +# (kind() 读取异常),直接 resolve 得到垃圾类型(如 i8*)。 +# 因此采用文本提取方案:ParseVpiIntoModule 读 .vpi stub 时,从 +# vpi_buf 原始文本(在函数末尾才释放,始终有效)直接按行格式 +# 提取 "def m(...) -> Ret:" 的返回注解字符串缓存到 stdlib(持久, +# 不受 pool 状态影响)。HandlesExprCall._lookup_method_ret_ty_via_vpi +# 按 sha1.类名.方法名 查表并用 _resolve_vpi_ret_annot_str 纯字符串 +# 解析为 LLVMType,返回类型始终来自方法定义处,无白名单/无硬编码。 +# ============================================================ +VPI_RET_MAX: t.CDefine = 4096 +VPI_KEY_LEN: t.CDefine = 224 +VPI_RET_LEN: t.CDefine = 224 + +_g_vpi_ret_keys: bytes = None +_g_vpi_ret_strs: bytes = None +_g_vpi_ret_count: int = 0 +_g_vpi_ret_cap: int = 0 + + +# ============================================================ +# _cache_vpi_return_annots_text - 从 vpi_buf 文本直接提取方法返回注解 +# +# 完全不依赖 AST tree(tree 深层节点在后续处理中可能损坏), +# 直接按 vpi 生成文件的行格式提取: +# " def method(...) -> Ret: pass"(类内方法,缩进) +# "def method(...) -> Ret: pass"(模块级函数) +# "class X:"(跟踪当前类名) +# ============================================================ +def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int: + """从 vpi_buf 文本提取方法返回注解字符串(stdlib 持久)""" + 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 + if _g_vpi_ret_keys is None: + _g_vpi_ret_keys = stdlib.malloc(VPI_RET_MAX * VPI_KEY_LEN) + _g_vpi_ret_strs = stdlib.malloc(VPI_RET_MAX * VPI_RET_LEN) + if _g_vpi_ret_keys is None or _g_vpi_ret_strs is None: + return 0 + string.memset(_g_vpi_ret_keys, 0, VPI_RET_MAX * VPI_KEY_LEN) + string.memset(_g_vpi_ret_strs, 0, VPI_RET_MAX * VPI_RET_LEN) + _g_vpi_ret_cap = VPI_RET_MAX + sha1_len: t.CSizeT = string.strlen(src_sha1) + buf_len: t.CSizeT = string.strlen(vpi_buf) + cur_cls: bytes = None + pos: t.CSizeT = 0 + line_no: int = 0 + while pos < buf_len: + ls: t.CSizeT = pos + le: t.CSizeT = pos + while le < buf_len and vpi_buf[le] != '\n': + le += 1 + line_len: t.CSizeT = le - ls + # 跳过前导空格,检查 class/def + i: t.CSizeT = ls + while i < le and vpi_buf[i] == ' ': + i += 1 + if i < le: + # class X: + if line_len >= 6 and string.strncmp(vpi_buf + i, "class ", 6) == 0: + cstart: t.CSizeT = i + 6 + cend: t.CSizeT = cstart + # 类名提取:到 ':' / ' ' / '(' 前停止(继承基类括号不入类名, + # 如 "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: + stdlib.free(cur_cls) + cur_cls = stdlib.malloc(cend - cstart + 1) + if cur_cls is not None: + string.strncpy(cur_cls, vpi_buf + cstart, cend - cstart) + 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] != '(': + dend += 1 + # 方法名 = [dstart, dend) + # 找 "->" + rpos: t.CSizeT = dstart + arrow_pos: t.CSizeT = le + while rpos < le - 1: + if vpi_buf[rpos] == '-' and vpi_buf[rpos + 1] == '>': + arrow_pos = rpos + break + rpos += 1 + 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 + cls_len2: t.CSizeT = 0 + if cur_cls is not None: + cls_len2 = string.strlen(cur_cls) + key_len: t.CSizeT = sha1_len + 1 + cls_len2 + 1 + mname_len + if key_len < VPI_KEY_LEN and rtype_len < VPI_RET_LEN: + # 方法名复制到临时缓冲(vpi_buf+dstart 非 null 结尾,不能直接 snprintf) + mname_buf: bytes = stdlib.malloc(mname_len + 1) + if mname_buf is not None: + string.strncpy(mname_buf, vpi_buf + dstart, mname_len) + mname_buf[mname_len] = '\0' + # 无注解的特殊方法(构造/析构)跳过:它们有专门调用路径 + skip_def: int = 0 + if arrow_pos >= le and ( + 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 + 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 + grow_ok: int = 1 + if dup_key == 0 and _g_vpi_ret_count >= _g_vpi_ret_cap: + grow_ok = _grow_vpi_ret_cache() + if dup_key == 0 and 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 + if cur_cls is not None: + stdlib.free(cur_cls) + return 1 + + +# ============================================================ +# _grow_vpi_ret_cache - 扩容方法返回注解缓存(容量翻倍) +# +# 背景:VPI_RET_MAX 初始 4096,但 includes 模块量大(~90 个 stub、 +# 每个几十个方法),缓存写满后条目被静默丢弃,导致已缓存模块的 +# 方法(如 JsonValue._pool_alloc)查不到而 fail-fast 误报。 +# 用 stdlib.realloc 翻倍扩容(上限 1M 条目,防御异常增长)。 +# ============================================================ +def _grow_vpi_ret_cache() -> int: + """扩容返回注解缓存(容量翻倍),返回 1=成功 / 0=失败""" + global _g_vpi_ret_keys, _g_vpi_ret_strs, _g_vpi_ret_cap + if _g_vpi_ret_keys is None or _g_vpi_ret_cap <= 0: + return 0 + new_cap: int = _g_vpi_ret_cap * 2 + if new_cap > 1048576: + return 0 + new_keys: bytes = stdlib.realloc(_g_vpi_ret_keys, new_cap * VPI_KEY_LEN) + if new_keys is None: + return 0 + new_strs: bytes = stdlib.realloc(_g_vpi_ret_strs, new_cap * VPI_RET_LEN) + if new_strs is None: + return 0 + _g_vpi_ret_keys = new_keys + _g_vpi_ret_strs = new_strs + _g_vpi_ret_cap = new_cap + return 1 + + +# ============================================================ +# _ensure_vpi_ret_annot_loaded - 按需加载模块的 vpi stub 返回注解缓存 +# +# 背景:_cache_vpi_return_annots_text 仅在 stub 注入(LoadDepVpiIntoModule, +# 即"当前模块 import 了该模块")时填充缓存。但模块自身翻译时,自己的 +# stub 尚未被任何依赖方读取,且自己的 text.ll 要翻译完成才落盘,导致 +# "同模块方法调用(定义在后)"(如 json.__setitem__ 调用后定义的 +# _pool_alloc)在 found_func/text.ll/vpi 缓存三类来源上全部未命中。 +# 此函数在缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi +# (Converter 在 deps 阶段已生成全部 stub 文件),填充缓存后即可命中。 +# 幂等:模块已缓存过则跳过,不重复读文件。 +# ============================================================ +def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: + """按需加载模块 vpi stub 的返回注解缓存,返回 1=已就绪 / 0=失败""" + if src_sha1 is None or temp_dir is None: + return 0 + sha1_len: t.CSizeT = string.strlen(src_sha1) + if sha1_len < 2: + return 0 + # 该模块已有缓存条目 → 跳过(幂等) + if _g_vpi_ret_keys is not None and _g_vpi_ret_count > 0: + pre_i: 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] == '.': + return 1 + # 构造路径 {temp_dir}/{sha1[0:2]}/{sha1}.vpi(逐字符拼接,避免 %c 格式化问题) + td_len: t.CSizeT = string.strlen(temp_dir) + path_len: t.CSizeT = td_len + sha1_len + 12 + vpi_path: bytes = stdlib.malloc(path_len) + if vpi_path is None: + return 0 + string.strcpy(vpi_path, temp_dir) + vpi_path[td_len] = '/' + vpi_path[td_len + 1] = src_sha1[0] + vpi_path[td_len + 2] = src_sha1[1] + vpi_path[td_len + 3] = '/' + string.strcpy(vpi_path + td_len + 4, src_sha1) + string.strcpy(vpi_path + td_len + 4 + sha1_len, ".vpi") + f: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R) + if f.closed: + stdlib.free(vpi_path) + return 0 + vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) + if vpi_buf is None: + f.close() + stdlib.free(vpi_path) + return 0 + bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE) + f.close() + if bytes_read > 0: + if bytes_read < VPI_READ_BUF_SIZE: + vpi_buf[bytes_read] = 0 + else: + vpi_buf[VPI_READ_BUF_SIZE - 1] = 0 + _cache_vpi_return_annots_text(vpi_buf, src_sha1) + 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 + # 容量 = alen + tglen*8 + 16 只覆盖 ≤8 次替换;替换次数无上限时 + # opos 会越界写穿 stdlib 堆(哨兵盲区 → CRT 堆损坏后任意 malloc/free + # 静默段错误、崩溃点漂移)。按最坏上界分配:每替换消耗 tplen 写 tglen, + # 最坏全部被替换 → alen + (alen/tplen)*tglen + 1。 + max_sub: t.CSizeT = alen / tplen + if max_sub == 0: + max_sub = 1 + out_size: t.CSizeT = alen + max_sub * tglen + 1 + out: bytes = stdlib.malloc(out_size) + if out is None: + return None + opos: t.CSizeT = 0 + p: t.CSizeT = 0 + while p < alen: + # 防御:opos 超分配即 fail-fast 返回 None(绝不越界写) + if opos >= out_size: + stdlib.free(out) + return None + 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: + return None + if src_sha1 is None or method_name is None: + return None + sha1_len: t.CSizeT = string.strlen(src_sha1) + cl_len: t.CSizeT = 0 + if cls_name is not None: + cl_len = string.strlen(cls_name) + me_len: t.CSizeT = string.strlen(method_name) + key_len: t.CSizeT = sha1_len + 1 + cl_len + 1 + me_len + key_buf: bytes = stdlib.malloc(key_len + 1) + if key_buf is None: + return None + if cls_name is not None: + 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) + i: int + for i in range(_g_vpi_ret_count): + if string.strcmp(_g_vpi_ret_keys + i * VPI_KEY_LEN, key_buf) == 0: + stdlib.free(key_buf) + return _g_vpi_ret_strs + i * VPI_RET_LEN + # 泛型特化归一化匹配: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) + meth_start_v: str = gklb + 2 # 跳过 ']' 和 '.' + if tparam_len_v > 0 and 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) + 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 + + +# ============================================================ +# _resolve_single_vpi_type_str - 解析单个类型段(无 CPtr/CVoid)为 LLVMType +# +# 纯字符串解析,不构造 AST 节点。 +# ============================================================ +def _resolve_single_vpi_type_str(pool: memhub.MemBuddy | t.CPtr, + seg: str) -> llvmlite.LLVMType | t.CPtr: + """解析单个类型段为 LLVMType,无法识别返回 None""" + if seg is None or pool is None: + return None + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + # 内置别名 + if string.strcmp(seg, "str") == 0 or string.strcmp(seg, "bytes") == 0: + return i8_ptr_ty + if string.strcmp(seg, "int") == 0: + return llvmlite.Int32(pool) + if string.strcmp(seg, "bool") == 0: + return llvmlite.Int1(pool) + # LLVM 类型串:vpi 缓存对无返回注解方法默认存 "i32" + # (见 _cache_vpi_return_annots_text;push 等无注解方法返回 i32)。 + # 之前缺分支导致解析返回 None → 泛型模板方法(Vector[int].push)调用 + # 走 fail-fast"方法返回类型无法精确获取",实际是约定不一致。 + if string.strcmp(seg, "i32") == 0: + return llvmlite.Int32(pool) + if string.strcmp(seg, "i64") == 0: + return llvmlite.Int64(pool) + if string.strcmp(seg, "i16") == 0: + return llvmlite.Int16(pool) + if string.strcmp(seg, "i8") == 0: + return i8_ty + if string.strcmp(seg, "i1") == 0: + return llvmlite.Int1(pool) + if string.strcmp(seg, "f32") == 0: + return llvmlite.Float(pool) + if string.strcmp(seg, "f64") == 0: + return llvmlite.Double(pool) + if string.strcmp(seg, "void") == 0: + return llvmlite.Void(pool) + # vpi 源文本中 `-> None` 被原样缓存为 "None"(_cache_vpi_return_annots_text + # 提取 arrow_pos 后到 ':' 前的内容)。此前无 "None" 分支 → 解析返回 None → + # `-> None` 的跨模块方法(如 AssignGenMixin._register_cdefine_from_value)vpi + # 缓存命中也解析不出 void → 方法返回类型 fail-fast 误报(追加80e 断点 B)。 + # None 与 void 同义,归一到 Void。 + if string.strcmp(seg, "None") == 0: + return llvmlite.Void(pool) + # t.XXX / c.XXX 内置类型映射 + if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0: + attr_nm: str = seg + 2 + mapped_ty: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type(pool, attr_nm) + if mapped_ty is not None: + return mapped_ty + return None + # 模块限定类名 "memhub.MemBuddy" → 取最后一段查 struct + last_dot: str = string.strrchr(seg, '.') + if last_dot is not None: + cls_nm2: str = last_dot + 1 + mod_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(cls_nm2) + if mod_struct_ty is not None: + return mod_struct_ty + return None + # 裸类名 → 全局 struct 注册表 + bare_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(seg) + if bare_struct_ty is not None: + return bare_struct_ty + # 裸 typedef 名(如 VOIDPTR) + mapped_bare: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type(pool, seg) + if mapped_bare is not None: + return mapped_bare + return None + + +# ============================================================ +# _resolve_vpi_ret_annot_str - 直接解析 vpi 返回注解字符串为 LLVMType +# +# 背景:_parse_type_string 跨模块构造 ast.Name/ast.BinOp 在 TPV 中可能 +# 退化为裸函数调用(__init__/vtable 未执行),节点 kind() 异常导致 +# resolve 得到垃圾类型(如 Ptr(i8))。此处纯字符串解析避免该问题。 +# +# 支持: "str"/"bytes" → i8*; "int" → i32; "bool" → i1; +# "t.CChar" 等 → map_t_type 映射; +# "X | t.CPtr" → Ptr(Struct X); "t.CVoid | t.CPtr" → i8*; +# "memhub.MemBuddy | t.CPtr" → Ptr(Struct MemBuddy)。 +# ============================================================ +def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy | t.CPtr, + ret_annot: str, + i32_ty: llvmlite.LLVMType | t.CPtr, + i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr: + """直接解析 vpi 返回注解字符串为 LLVMType(不构造 AST 节点)""" + if ret_annot is None or pool is None: + return None + + # tuple[T1, T2, ...] → 匿名结构体 {T1, T2, ...}(多返回值打包, + # 与 TypeGenMixin._resolve_generic_subscript_ty 的 tuple 分支一致)。 + # b2a_hqx -> tuple[str, t.CUnsignedShort] 之前按普通段解析失败 → 调用侧 + # 返回类型退化 i8* → tuple 解包失效(hqx 段崩溃根因)。 + if string.strncmp(ret_annot, "tuple[", 6) == 0: + inner_len_t: t.CSizeT = 0 + while ret_annot[6 + inner_len_t] != ']' and ret_annot[6 + inner_len_t] != '\0': + inner_len_t += 1 + if ret_annot[6 + inner_len_t] != ']' or inner_len_t == 0: + return None + tup_head_t: llvmlite.ParamNode | t.CPtr = None + tup_tail_t: llvmlite.ParamNode | t.CPtr = None + tup_count_t: int = 0 + p_t: t.CSizeT = 0 + while p_t < inner_len_t: + # 跳过前导空格/逗号 + while p_t < inner_len_t and (ret_annot[6 + p_t] == ' ' or ret_annot[6 + p_t] == ','): + p_t += 1 + seg_s_t: t.CSizeT = p_t + while p_t < inner_len_t and ret_annot[6 + p_t] != ',': + p_t += 1 + seg_e_t: t.CSizeT = p_t + while seg_e_t > seg_s_t and ret_annot[6 + seg_e_t - 1] == ' ': + seg_e_t -= 1 + if seg_e_t > seg_s_t: + seg_len_t: t.CSizeT = seg_e_t - seg_s_t + seg_buf_t: bytes = stdlib.malloc(seg_len_t + 1) + if seg_buf_t is None: + return None + string.strncpy(seg_buf_t, ret_annot + 6 + seg_s_t, seg_len_t) + seg_buf_t[seg_len_t] = '\0' + seg_ty_t: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(pool, seg_buf_t) + stdlib.free(seg_buf_t) + if seg_ty_t is None: + return None + tup_node_t: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, seg_ty_t) + if tup_node_t is None: + return None + if tup_head_t is None: + tup_head_t = tup_node_t + else: + llvmlite.paramnode_set_next(tup_tail_t, tup_node_t) + tup_tail_t = tup_node_t + tup_count_t += 1 + if tup_head_t is None: + return None + return llvmlite.Struct(pool, tup_head_t, tup_count_t, None) + + has_cptr: int = 0 + has_cvoid: int = 0 + first_ty: llvmlite.LLVMType | t.CPtr = None + + buf_len: t.CSizeT = string.strlen(ret_annot) + pos: t.CSizeT = 0 + while pos <= buf_len: + # 跳过前导空格 + while pos < buf_len and ret_annot[pos] == ' ': + pos += 1 + seg_start: t.CSizeT = pos + while pos < buf_len and ret_annot[pos] != '|': + pos += 1 + seg_end: t.CSizeT = pos + # trim 尾随空格 + while seg_end > seg_start and ret_annot[seg_end - 1] == ' ': + seg_end -= 1 + if seg_end > seg_start: + seg_len2: t.CSizeT = seg_end - seg_start + seg_buf: bytes = stdlib.malloc(seg_len2 + 1) + if seg_buf is None: + break + string.strncpy(seg_buf, ret_annot + seg_start, seg_len2) + seg_buf[seg_len2] = '\0' + if string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0: + has_cptr = 1 + elif string.strcmp(seg_buf, "t.CVoid") == 0 or string.strcmp(seg_buf, "CVoid") == 0: + has_cvoid = 1 + else: + seg_ty: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(pool, seg_buf) + if seg_ty is not None and first_ty is None: + first_ty = seg_ty + stdlib.free(seg_buf) + pos += 1 # 跳过 '|' + + if has_cptr != 0: + if has_cvoid != 0: + return i8_ptr_ty + if first_ty is not None: + return llvmlite.Ptr(pool, first_ty) + return i8_ptr_ty + if has_cvoid != 0: + # t.CVoid | t.State:State 是装饰器标记(编译侧忽略),t.CVoid → void。 + # 此前缺此分支 → first_ty=None → 返回 None → 跨模块函数返回类型 + # fail-fast(CPythonTest Py_Initialize() -> t.CVoid | t.State 根因)。 + return llvmlite.Void(pool) + if first_ty is not None: + return first_ty + return None + + +# ============================================================ +# VpiReader - .vpi AST 解析器 +# +# 从 .vpi 存根文件读取函数签名和 CTypedef 定义,注入 declare +# 到当前模块。用于跨模块函数调用时补充缺失的函数声明。 +# +# .vpi 文件由 StubGen.Converter._GeneratePyiFromAst 生成, +# 包含函数签名(无函数体)、类定义、变量声明、import 语句。 +# VpiReader 解析 .vpi 为 AST,提取 FunctionDef 和 CTypedef +# AnnAssign,在当前模块创建对应的 declare 声明。 +# ============================================================ + +# .vpi 读取缓冲区大小(256KB,与 PYI_BUF_SIZE 一致) +VPI_READ_BUF_SIZE: t.CSizeT = 262144 + +# SHA1 字符串长度(16 hex 字符) +SHA1_LEN: t.CSizeT = 16 + + +# ============================================================ +# _extract_sha1_from_path - 从 .vpi 文件路径提取 SHA1 +# +# .vpi 文件名格式为 {sha1}.vpi,sha1 是 16 位十六进制字符。 +# 从路径中找到最后一个路径分隔符(/ 或 \),取后续文件名的前 +# 16 个字符作为 SHA1。分配新缓冲区存储 SHA1(调用者负责 free)。 +# +# Args: +# vpi_path: .vpi 文件路径 +# +# Returns: +# SHA1 字符串(stdlib.malloc 分配,需 stdlib.free),None 失败 +# ============================================================ +def _extract_sha1_from_path(vpi_path: str) -> str: + """从 .vpi 文件路径提取 SHA1(文件名前16字符,malloc 分配)""" + if vpi_path is None: + return None + # 查找最后一个路径分隔符 / 或 \ + total_len: t.CSizeT = string.strlen(vpi_path) + if total_len < SHA1_LEN + 4: # 至少需要 "xxxx.vpi" 的长度 + return None + # 从后向前查找最后一个 / 或 \ + sep_pos: t.CSizeT = total_len + while sep_pos > 0: + sep_pos -= 1 + ch: t.CChar = vpi_path[sep_pos] + if ch == '/' or ch == '\\': + sep_pos += 1 + break + if sep_pos + SHA1_LEN > total_len: + return None + # 分配缓冲区并复制 SHA1(16 字符 + null 终止符) + sha1_buf: str = stdlib.malloc(SHA1_LEN + 1) + if sha1_buf is None: + return None + string.strncpy(sha1_buf, vpi_path + sep_pos, SHA1_LEN) + sha1_buf[SHA1_LEN] = '\0' + return sha1_buf + + +# ============================================================ +# _stub_add_imported - 追加模块名到空格分隔列表(stub 上下文收集用) +# +# 与 HandlesImports.add_imported_module 逻辑一致,但内联在本模块, +# 避免 VpiReader → HandlesImports → StubMerger → VpiReader 循环依赖。 +# ============================================================ +def _stub_add_imported(pool: memhub.MemBuddy | t.CPtr, + cur: str, name: str) -> str: + """追加模块名到空格分隔列表(pool 分配),返回新列表""" + if name is None: + return cur + if cur is None: + nlen: t.CSizeT = string.strlen(name) + buf: str = pool.alloc(nlen + 1) + if buf is not None: + string.strcpy(buf, name) + return buf + old_len: t.CSizeT = string.strlen(cur) + name_len: t.CSizeT = string.strlen(name) + buf2: str = pool.alloc(old_len + 1 + name_len + 1) + if buf2 is not None: + string.strcpy(buf2, cur) + buf2[old_len] = ' ' + string.strcpy(buf2 + old_len + 1, name) + return buf2 + + +# ============================================================ +# _stub_add_from - 追加 from-import 映射("local:mod[:orig]" 空格分隔) +# ============================================================ +def _stub_add_from(pool: memhub.MemBuddy | t.CPtr, + cur: str, local: str, mod: str, orig: str) -> str: + """追加 from-import 映射(pool 分配),返回新列表""" + if local is None or mod is None: + return cur + entry: str = pool.alloc(256) + if entry is None: + return cur + if orig is not None and string.strcmp(orig, local) != 0: + viperlib.snprintf(entry, 256, "%s:%s:%s", local, mod, orig) + else: + viperlib.snprintf(entry, 256, "%s:%s", local, mod) + if cur is None: + return entry + old_len: t.CSizeT = string.strlen(cur) + entry_len: t.CSizeT = string.strlen(entry) + buf: str = pool.alloc(old_len + 1 + entry_len + 1) + if buf is not None: + string.strcpy(buf, cur) + buf[old_len] = ' ' + string.strcpy(buf + old_len + 1, entry) + return buf + + +# ============================================================ +# ParseVpiIntoModule - 解析 .vpi 文件,将函数声明注入到当前模块 +# +# 读取 .vpi 文件 → AST 解析 → 遍历顶层节点 → 创建 declare 声明。 +# 对每个 FunctionDef: +# 1. 提取函数名、参数、返回类型 +# 2. 检测 CExport/CExtern/State 标记(决定是否用裸名) +# 3. 用 find_func_in_module 去重(已存在则跳过) +# 4. 用 create_declare + add_param 创建 declare 声明 +# 5. 注册到函数表(用裸名,非 mangled_name) +# 对 AnnAssign with t.CTypedef:调用 register_ctypedef 注册 +# ClassDef/Import/ImportFrom:跳过 +# +# Args: +# pool: 内存池 +# mod: 当前 LLVM 模块 +# vpi_path: .vpi 文件路径 +# trans: 翻译器对象 +# +# Returns: +# 1=成功,0=失败 +# ============================================================ +def ParseVpiIntoModule(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + vpi_path: str, + trans: HT.Translator | t.CPtr) -> int: + """解析 .vpi 文件,将函数声明注入到当前模块 + + 解析 stub 期间临时将错误归因切换到被读 stub 文件(.vpi): + 注解解析失败时 fatal_error 应指向 stub 及其行号(真实出错位置), + 而非当前编译模块(错误归因修复)。 + """ + if pool is None or mod is None or vpi_path is None or trans is None: + return 0 + saved_cur: str = HandlesType.get_current_file() + HandlesType.set_current_file(vpi_path) + ret: int = _ParseVpiIntoModuleImpl(pool, mod, vpi_path, trans) + HandlesType.set_current_file(saved_cur) + return ret + + +def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + vpi_path: str, + trans: HT.Translator | t.CPtr) -> int: + """解析 .vpi 文件,将函数声明注入到当前模块(ParseVpiIntoModule 实际实现)""" + if pool is None or mod is None or vpi_path is None or trans is None: + return 0 + + # 1. 读取 .vpi 文件 + f: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R) + if f.closed: + return 0 + vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) + if vpi_buf is None: + f.close() + return 0 + bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE) + f.close() + if bytes_read <= 0: + stdlib.free(vpi_buf) + return 0 + if bytes_read < VPI_READ_BUF_SIZE: + vpi_buf[bytes_read] = 0 + else: + vpi_buf[VPI_READ_BUF_SIZE - 1] = 0 + + # 2. 解析为 AST + lx: ast.Lexer | t.CPtr = ast.new_lexer(pool) + if lx is None: + stdlib.free(vpi_buf) + return 0 + ast._lexer_init(lx, vpi_buf, pool) + tokens: ast.Token | t.CPtr = ast.tokenize(lx) + tree: ast.AST | t.CPtr = ast.parse_tokens(pool, tokens) + # vpi_buf 延迟释放:AST 字符串字段可能引用 vpi_buf 内部(token 零拷贝), + # 提前 free 会导致 Name.id/Attribute.attr 等悬空 → 后续解析返回错误类型。 + # 在 ParseVpiIntoModule 末尾统一释放。 + if tree is None: + stdlib.free(vpi_buf) + return 0 + + # 提取源模块 SHA1(用于函数名混淆,在 AST 解析成功后提取避免提前返回时泄漏) + src_sha1: str = _extract_sha1_from_path(vpi_path) + + # 从 vpi_buf 文本直接提取方法返回注解字符串缓存到 stdlib + # (不依赖 AST tree:tree 深层节点在后续遍历处理时可能被覆盖损坏, + # 但 vpi_buf 原始文本在 ParseVpiIntoModule 末尾才释放,始终有效) + _cache_vpi_return_annots_text(vpi_buf, src_sha1) + + # 3. 遍历 AST 顶层节点 + ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children + if ch is None: + if src_sha1 is not None: + stdlib.free(src_sha1) + return 1 + n: t.CSizeT = ch.__len__() + + # 收集 stub 内被读模块自己的 import 语句,构建独立的注解解析上下文。 + # .vpi stub 顶部含被读模块的 import(Converter 已写入,如 string.vp 的 + # "import t, c"),用它替代当前模块的 imported_modules/from_imports 去 + # 解析注解——根治跨上下文误报(如 t.CArray[str] 需要 't' 已导入)。 + stub_imported_modules: str = None + stub_from_imports: str = None + si: t.CSizeT + for si in range(n): + schild: ast.AST | t.CPtr = ch.get(si) + if schild is None: + continue + sk: int = schild.kind() + if sk == ast.ASTKind.Import: + simp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(schild) + if simp.names is not None: + sn_n: t.CSizeT = simp.names.__len__() + sni: t.CSizeT + for sni in range(sn_n): + sal: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(simp.names.get(sni)) + if sal is not None and sal.name is not None: + stub_imported_modules = _stub_add_imported( + pool, stub_imported_modules, sal.name) + if sal.asname is not None: + stub_imported_modules = _stub_add_imported( + pool, stub_imported_modules, sal.asname) + elif sk == ast.ASTKind.ImportFrom: + sifr: ast.ImportFrom | t.CPtr = (ast.ImportFrom | t.CPtr)(schild) + sifr_mod: str = sifr.module + if sifr_mod is not None: + stub_imported_modules = _stub_add_imported( + pool, stub_imported_modules, sifr_mod) + if sifr.names is not None: + sifr_nn: t.CSizeT = sifr.names.__len__() + sifri: t.CSizeT + for sifri in range(sifr_nn): + sial: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(sifr.names.get(sifri)) + if sial is not None and sial.name is not None: + # '*' star import 不建 from 映射(lookup 有 star 回退) + if string.strcmp(sial.name, "*") != 0: + slocal: str = sial.name + sorig: str = None + if sial.asname is not None: + slocal = sial.asname + sorig = sial.name + stub_from_imports = _stub_add_from( + pool, stub_from_imports, slocal, sifr_mod, sorig) + + # 用 stub 自己的 imports 替代当前模块的(stub 注解解析上下文正确性) + imported_modules: str = trans._imported_modules + from_imports: str = trans._from_imports + if stub_imported_modules is not None: + imported_modules = stub_imported_modules + if stub_from_imports is not None: + from_imports = stub_from_imports + # 把源模块自己加入 stub 注解上下文:stub 注解(如 __sha512 的 transform + # 参数 t.CArray[t.CUInt8T, SHA512_BLOCK_LEN])引用源模块自身的 CDefine, + # 但 stub 的 import 只含被 import 的模块(t/c/string),不含自己。 + # 不加则 _eval_const_int_expr 跨模块 CDefine 查找遍历不到源模块 + # (imported_modules 无 __sha512)→ t.CArray count 报"必须是整数常量"。 + # 示例:RelPath "hashlib/__sha512.vp" → 模块名 "hashlib.__sha512"。 + if src_sha1 is not None: + import lib.core.StubMerger as StubMerger + stub_rel_src: str = StubMergerStore.LookupSha1RelPath(src_sha1) + if stub_rel_src is not None: + sr_len_src: t.CSizeT = string.strlen(stub_rel_src) + if sr_len_src > 3 and string.strncmp(stub_rel_src + sr_len_src - 3, ".vp", 3) == 0: + sr_len_src -= 3 + elif sr_len_src > 3 and string.strncmp(stub_rel_src + sr_len_src - 3, ".py", 3) == 0: + sr_len_src -= 3 + if sr_len_src > 0: + src_mod_buf: str = stdlib.malloc(sr_len_src + 1) + if src_mod_buf is not None: + sri_src: t.CSizeT + for sri_src in range(sr_len_src): + ch_src: t.CChar = stub_rel_src[sri_src] + if ch_src == '/' or ch_src == '\\': + src_mod_buf[sri_src] = '.' + else: + src_mod_buf[sri_src] = ch_src + src_mod_buf[sr_len_src] = '\0' + imported_modules = _stub_add_imported(pool, imported_modules, src_mod_buf) + stdlib.free(src_mod_buf) + + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + + # 函数表信息 + funcs_ptr: HandlesExprCall.FuncEntry | t.CPtr = trans._funcs + max_funcs: int = 1024 + + i: t.CSizeT + for i in range(n): + child: ast.AST | t.CPtr = ch.get(i) + if child is None: + continue + kd: int = child.kind() + + # FunctionDef → 提取函数名、参数、返回类型 → 注入 declare + if kd == ast.ASTKind.FunctionDef: + fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child) + if fd is None or fd.name is None: + continue + _inject_function_declare(pool, mod, fd, trans, src_sha1, + imported_modules, from_imports, + i32_ty, i8_ptr_ty, + funcs_ptr, max_funcs) + continue + + # AnnAssign with t.CTypedef → 注册 CTypedef; + # 普通模块级全局变量(如 tokens._op_head)→ external global declare + if kd == ast.ASTKind.AnnAssign: + aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(child) + if aa is None: + continue + if HandlesType._is_ctypedef_annotation(aa.annotation) != 0: + _register_ctypedef_from_annassign(pool, aa) + else: + _inject_global_variable_declare(pool, mod, aa, trans, + stub_imported_modules, stub_from_imports, + i32_ty, i8_ptr_ty) + continue + + # ClassDef → 递归注入类内方法(如 MemBuddy.__init__/alloc) + # 方法 declare 使用 {sha1}.{ClassName}.{method} 混淆名, + # 参数类型从 vpi 注解解析(避免调用时 _ensure_method_declare 用实参类型 + # 创建错误 declare,如 size: t.CSizeT 被写成 i32 而非 i64) + if kd == ast.ASTKind.ClassDef: + class_cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child) + if class_cd is None or class_cd.name is None: + continue + cls_name_v: str = class_cd.name + # 泛型类(class X[T])跳过方法 declare 注入:stub 模板签名含类级 + # 类型参数 T(如 ndarray[T].__add__ 的返回 ndarray[T] | t.CPtr), + # 注入时 T 无法确定具体类型,直接解析会触发 + # _specialize_generic_class("ndarray", ["T"]) 错误特化 + # (Phase2 翻译 App 文件注入 numpy stub 时崩溃)。 + # 具体特化的方法 define 已在 Phase1 生成,跨模块调用按特化类名 + # (ndarray[t.CDouble].__add__)查找命中,无需 stub 模板 declare。 + tp_stub: list[str] | t.CPtr = class_cd.type_params + if tp_stub is not None and tp_stub.__len__() > 0: + continue + cls_children: list[ast.AST | t.CPtr] | t.CPtr = class_cd.children + if cls_children is not None: + cn2: t.CSizeT = cls_children.__len__() + for ci2 in range(cn2): + m_child: ast.AST | t.CPtr = cls_children.get(ci2) + if m_child is None: + continue + if m_child.kind() != ast.ASTKind.FunctionDef: + continue + m_fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m_child) + if m_fd is None or m_fd.name is None: + continue + _inject_method_declare(pool, mod, m_fd, cls_name_v, trans, + src_sha1, imported_modules, from_imports, + i32_ty, i8_ptr_ty, funcs_ptr, max_funcs) + continue + + # Import/ImportFrom → 跳过 + # (import 不需要处理) + + # 释放 SHA1 缓冲区(_extract_sha1_from_path 用 stdlib.malloc 分配) + if src_sha1 is not None: + stdlib.free(src_sha1) + # vpi_buf 延迟到全部 tree 处理完成后释放(见解析处注释) + stdlib.free(vpi_buf) + return 1 + + +# ============================================================ +# _inject_global_variable_declare - 从 vpi AnnAssign 创建模块级全局变量 declare +# +# 背景:vpi 里模块级全局变量(如 tokens.vp 的 _op_head: OpEntry | t.CPtr = None) +# 需在当前模块创建 external global 声明(短名 @name,COFF 按名链接),否则 +# 跨模块 from-import 读取全局变量时 find_global_in_module 找不到 → +# "跨模块全局变量类型未知(stub 未注入)" 误杀(lexer.vp 读 tokens._op_head)。 +# ============================================================ +def _inject_global_variable_declare(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + aa: ast.AnnAssign | t.CPtr, + trans: HT.Translator | t.CPtr, + imported_modules: str, + from_imports: str, + i32_ty: llvmlite.LLVMType | t.CPtr, + i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> None: + """从 vpi AnnAssign 创建模块级全局变量 external declare(短名 @name)""" + if pool is None or mod is None or aa is None or trans is None: + return + tgt_v: ast.AST | t.CPtr = aa.target + if tgt_v is None or tgt_v.kind() != ast.ASTKind.Name: + return + nm_v: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tgt_v) + if nm_v is None or nm_v.id is None: + return + gname_v: str = nm_v.id + if gname_v[0] == '\0': + return + # 去重:遍历模块全局变量链表(短名匹配) + cur_v: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead + while cur_v is not None: + if cur_v.Name is not None and string.strcmp(cur_v.Name, gname_v) == 0: + return + cur_v = cur_v.Next + # 解析注解类型(裸类 → Ptr(struct),CArray → 元素指针) + resolved_v: llvmlite.LLVMType | t.CPtr = None + if aa.annotation is not None: + resolved_v = HandlesType.resolve_annotation_type( + pool, aa.annotation, imported_modules, from_imports, trans) + if resolved_v is None: + return # 类型解析失败跳过:调用侧有 fail-fast 定位 + gv_ty_v: llvmlite.LLVMType | t.CPtr = resolved_v + match resolved_v: + case llvmlite.LLVMType.Struct(_, _, _): + gv_ty_v = llvmlite.Ptr(pool, resolved_v) + case llvmlite.LLVMType.Array(aelem_v, acount_v): + # 定长数组(CArray[T, N]):保留完整数组类型 [N x T],与定义端 + # handle_module_level_var 生成的 global [N x T] 一致——此前一律 + # 衰减为元素指针 i32*,链接后引用端 load 把数组前 8 字节当指针 + # 读 → 垃圾地址 → zdeflate_len_to_symbol 读 NULL/垃圾崩溃 + # (zdeflate_len_base 跨模块数组根因)。 + if acount_v > 0: + gv_ty_v = resolved_v + else: + # 不定长数组(CArray[T, None]):元素指针(C 数组衰减) + gv_ty_v = llvmlite.Ptr(pool, aelem_v) + case _: + gv_ty_v = resolved_v + gv_v: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gname_v, gv_ty_v) + if gv_v is None: + return + llvmlite.module_add_global(mod, gv_v) + gv_v.Linkage = "external" + + +# ============================================================ +# _inject_function_declare - 为单个 FunctionDef 创建 declare 声明 +# +# 检测 CExport/CExtern/State 标记,计算 mangled name, +# 用 find_func_in_module 去重,创建 declare 并添加参数, +# 注册到函数表。 +# ============================================================ +def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + fd: ast.FunctionDef | t.CPtr, + trans: HT.Translator | t.CPtr, + src_sha1: str, + imported_modules: str, + from_imports: str, + i32_ty: llvmlite.LLVMType | t.CPtr, + i8_ptr_ty: llvmlite.LLVMType | t.CPtr, + funcs_ptr: HandlesExprCall.FuncEntry | t.CPtr, + max_funcs: int) -> None: + """为 FunctionDef 创建 declare 声明并注入到模块""" + if fd.name is None: + return + + # 检测 CExtern/State/CExport 标记(从返回类型注解) + has_extern: int = 0 + has_state: int = 0 + has_export: int = 0 + if fd.returns is not None: + has_extern = HandlesType.has_decorator_marker(fd.returns, "CExtern") + has_state = HandlesType.has_decorator_marker(fd.returns, "State") + has_export = HandlesType.has_decorator_marker(fd.returns, "CExport") + + # 计算 mangled name + # CExport/CExtern/State/main 函数用裸名,其他函数用源模块 SHA1 前缀 + use_bare_name: int = 0 + if has_extern != 0 or has_state != 0 or has_export != 0 or string.strcmp(fd.name, "main") == 0: + mangled_name: str = fd.name + use_bare_name = 1 + else: + mangled_name = HandlesFunctions._mangle_name_with_sha1(src_sha1, fd.name) + + # 去重:如果函数已存在则跳过 + existing: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, mangled_name) + if existing is not None: + return + # 也检查裸名(非裸名模式时,CExport 函数可能已通过其他途径注册) + if use_bare_name == 0: + existing_bare: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, fd.name) + if existing_bare is not None: + return + + # 推断返回类型 + # 优先用缓存的返回注解字符串(tree 解析后立即提取,节点未损坏) + # 直接字符串解析(绕开 _parse_type_string 跨模块 AST 构造在 TPV 中的 + # __init__/vtable 未执行问题,节点 kind() 异常导致 resolve 得垃圾类型) + ret_ty: llvmlite.LLVMType | t.CPtr = None + ret_annot_fd: str = _lookup_vpi_ret_annot(src_sha1, None, fd.name) + if ret_annot_fd is not None: + ret_ty = _resolve_vpi_ret_annot_str(pool, ret_annot_fd, i32_ty, i8_ptr_ty) + if ret_ty is None and fd.returns is not None: + ret_ty = HandlesType.resolve_annotation_type( + pool, fd.returns, imported_modules, from_imports, trans) + # 如果返回类型是纯 t.State(无实际类型),用 Void + if ret_ty is None and fd.returns is not None: + if has_state != 0: + ret_ty = llvmlite.Void(pool) + # 默认返回 i32 + if ret_ty is None: + ret_ty = i32_ty + + # 创建 declare + func: llvmlite.Function | t.CPtr = llvmlite.create_declare( + pool, mod, mangled_name, ret_ty) + if func is None: + return + + # 提取参数并添加到 declare + param_count: int = 0 + if fd.args is not None: + args_node: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args) + if args_node.args is not None: + args_list: list[ast.AST | t.CPtr] | t.CPtr = args_node.args + param_count = args_list.__len__() + pi: t.CSizeT + for pi in range(param_count): + ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(args_list.get(pi)) + if ag is None: + continue + pname: str = ag.arg + if pname is None: + pname = "arg" + # 解析参数类型 + param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty + if ag.annotation is not None: + resolved_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( + pool, ag.annotation, imported_modules, from_imports, trans) + if resolved_ty is not None: + # 结构体按值参数 → i64:跨模块结构体在本模块是 opaque + # (未定义字段),LLVM 不允许 opaque struct 作为函数参数 + # (llc: invalid type for function argument)。Windows ABI + # 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。 + # CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。 + match resolved_ty: + case llvmlite.LLVMType.Struct(_, _, _): + param_ty = llvmlite.Int64(pool) + case llvmlite.LLVMType.Array(aelem_ty, _): + param_ty = llvmlite.Ptr(pool, aelem_ty) + case _: + param_ty = resolved_ty + llvmlite.add_param(pool, func, param_ty, pname) + # *args 变长参数:标记 IsVarArg=1,使调用侧走 C ABI 参数提升 + # (i8/i16 → i32,float → double),避免 %d/%x 等格式因参数按 i64 + # 传递而读错(第二个 %x 读到第一个 i64 槽的高 4 字节)。 + if args_node.vararg is not None: + func.IsVarArg = 1 + + # 注册到函数表(用裸名 fd.name,不是 mangled_name) + if funcs_ptr is not None: + cur_count: int = trans._func_count + if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs, + None, 0, param_count) == 0: + trans._func_count = cur_count + 1 + + +# ============================================================ +# _inject_method_declare - 为 ClassDef 内的方法创建 declare +# +# 方法混淆名: {src_sha1}.{ClassName}.{method} +# 参数类型从 vpi 注解解析(self 无注解时默认为 i8*)。 +# 注入正确签名避免调用时 _ensure_method_declare 用实参类型 +# 创建错误 declare(如 size: t.CSizeT 被写成 i32 而非 i64)。 +# ============================================================ +def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + m_fd: ast.FunctionDef | t.CPtr, + cls_name: str, + trans: HT.Translator | t.CPtr, + src_sha1: str, + imported_modules: str, + from_imports: str, + i32_ty: llvmlite.LLVMType | t.CPtr, + i8_ptr_ty: llvmlite.LLVMType | t.CPtr, + funcs_ptr: HandlesExprCall.FuncEntry | t.CPtr, + max_funcs: int) -> None: + """为 ClassDef 内方法创建 declare 声明并注入到模块""" + if m_fd is None or m_fd.name is None or cls_name is None: + return + if src_sha1 is None: + return + + # 构造方法查找名: ClassName.method + lookup_name_len: t.CSizeT = string.strlen(cls_name) + 1 + string.strlen(m_fd.name) + 1 + lookup_name: str = pool.alloc(lookup_name_len) + if lookup_name is None: + return + viperlib.snprintf(lookup_name, lookup_name_len, "%s.%s", cls_name, m_fd.name) + + # mangled name: {src_sha1}.{ClassName}.{method} + mangled_len: t.CSizeT = string.strlen(src_sha1) + 1 + lookup_name_len + mangled_name: str = pool.alloc(mangled_len) + if mangled_name is None: + return + viperlib.snprintf(mangled_name, mangled_len, "%s.%s", src_sha1, lookup_name) + + # 去重:如果方法已存在则跳过 + existing: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, mangled_name) + if existing is not None: + return + + # 推断返回类型 + # 优先用缓存的返回注解字符串(tree 解析后立即提取,节点未损坏), + # 用 _parse_type_string 重新解析成新 AST(立即使用不被覆盖)。 + # 直接解析 m_fd.returns 可能因 vpi tree 深层节点被覆盖而得到垃圾类型。 + ret_ty: llvmlite.LLVMType | t.CPtr = None + ret_annot: str = _lookup_vpi_ret_annot(src_sha1, cls_name, m_fd.name) + if ret_annot is not None: + ret_ty = _resolve_vpi_ret_annot_str(pool, ret_annot, i32_ty, i8_ptr_ty) + if ret_ty is None and m_fd.returns is not None: + ret_ty = HandlesType.resolve_annotation_type( + pool, m_fd.returns, imported_modules, from_imports, trans) + # __new__ 无返回注解时推断为类指针(__new__ 返回 self 指针,默认 i32 会截断指针) + if ret_ty is None and string.strcmp(m_fd.name, "__new__") == 0: + cls_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(cls_name) + if cls_ty is not None: + ret_ty = llvmlite.Ptr(pool, cls_ty) + if ret_ty is None: + ret_ty = i32_ty + + # 创建 declare + func: llvmlite.Function | t.CPtr = llvmlite.create_declare( + pool, mod, mangled_name, ret_ty) + if func is None: + return + + # 提取参数并添加到 declare + param_count: int = 0 + if m_fd.args is not None: + args_node: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(m_fd.args) + if args_node.args is not None: + args_list: list[ast.AST | t.CPtr] | t.CPtr = args_node.args + param_count = args_list.__len__() + pi: t.CSizeT + for pi in range(param_count): + ag: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(args_list.get(pi)) + if ag is None: + continue + pname: str = ag.arg + if pname is None: + pname = "arg" + # 解析参数类型 + param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty + if ag.annotation is not None: + resolved_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( + pool, ag.annotation, imported_modules, from_imports, trans) + if resolved_ty is not None: + # 结构体按值参数 → i64:跨模块结构体在本模块是 opaque + # (未定义字段),LLVM 不允许 opaque struct 作为函数参数 + # (llc: invalid type for function argument)。Windows ABI + # 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。 + # CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。 + match resolved_ty: + case llvmlite.LLVMType.Struct(_, _, _): + param_ty = llvmlite.Int64(pool) + case llvmlite.LLVMType.Array(aelem_ty, _): + param_ty = llvmlite.Ptr(pool, aelem_ty) + case _: + param_ty = resolved_ty + llvmlite.add_param(pool, func, param_ty, pname) + + # OOP iter 协议:__next__ 方法 stub 声明带隐藏 __stop_iter_flag__(i1*), + # 与 _translate_method 的 define 签名一致(对齐 TPC HandlesFunctions.py + # L703-705)。for x in obj 循环会以 2 个实参调用 __next__(self, flag)。 + if m_fd.name is not None and string.strcmp(m_fd.name, "__next__") == 0: + i1_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_si is not None: + i1_ptr_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i1_ty_si) + if i1_ptr_ty_si is not None: + llvmlite.add_param(pool, func, i1_ptr_ty_si, "__stop_iter_flag__") + + # 注册到函数表(用裸名 ClassName.method,与调用方 lookup_name 一致) + # ParamCount 不含 self(方法首参 self/cls 不计入,与 _translate_method 的 + # md_param_count = args.__len__() - 1 保持一致)——默认参数填充按非 self + # 参数计数,若含 self 会导致 mdf_param_count 虚高、填充逻辑错位/越界。 + if funcs_ptr is not None: + cur_count: int = trans._func_count + param_count_no_self: int = param_count + if param_count_no_self > 0: + param_count_no_self -= 1 + if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, lookup_name, func, max_funcs, + None, 0, param_count_no_self) == 0: + trans._func_count = cur_count + 1 + + +# ============================================================ +# _register_ctypedef_from_annassign - 从 AnnAssign 节点注册 CTypedef +# +# 提取 NAME: t.CTypedef = Type 中的名称和类型表达式字符串, +# 调用 register_ctypedef 注册到动态 CTypedef 表。 +# ============================================================ +def _register_ctypedef_from_annassign(pool: memhub.MemBuddy | t.CPtr, + aa: ast.AnnAssign | t.CPtr) -> None: + """从 AnnAssign 节点提取并注册 CTypedef""" + if aa is None or pool is None: + return + # 提取目标名称(必须是 Name 节点) + if aa.target is None or aa.target.kind() != ast.ASTKind.Name: + return + tgt: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.target) + if tgt.id is None: + return + # 提取类型表达式字符串(从 value AST 节点) + if aa.value is None: + return + type_str: str = HandlesType._get_type_name_from_annotation(pool, aa.value) + if type_str is None: + return + # 注册 CTypedef + HandlesType.register_ctypedef(pool, tgt.id, type_str) diff --git a/App/lib/core/BuildPipeline.vp b/App/lib/core/BuildPipeline.vp index 31c9f76..e7d5cb8 100644 --- a/App/lib/core/BuildPipeline.vp +++ b/App/lib/core/BuildPipeline.vp @@ -36,6 +36,7 @@ import lib.core.StubMerger as StubMerger import lib.StubGen.Converter as StubConverter import lib.Projectrans.Config as Config import lib.core.Gen.Generator as GenGenerator +import lib.core.StubMergerCollect as StubMergerCollect # ============================================================ # BuildPipeline - 编译管线 @@ -143,7 +144,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str, if pyi_pos > 0: # 写入 {sha1}.vpi 到 temp_dir(切片路径) td_len_vpi: t.CSizeT = string.strlen(temp_dir) - pyi_path: str = StubMerger._sliced_path(temp_dir, td_len_vpi, sha1_val, "vpi") + pyi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_vpi, sha1_val, "vpi") if pyi_path is not None: pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.W) if not pf.closed: diff --git a/App/lib/core/Gen/CallGenBase.vp b/App/lib/core/Gen/CallGenBase.vp index e2e1603..4ce776d 100644 --- a/App/lib/core/Gen/CallGenBase.vp +++ b/App/lib/core/Gen/CallGenBase.vp @@ -24,6 +24,8 @@ import lib.core.StubMerger as StubMerger import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.core.Handles.HandlesAnnAssign as HandlesAnnAssign import lib.core.CompilerFactory as CompilerFactory +import lib.core.StubMergerCollect as StubMergerCollect +import lib.core.StubMergerStore as StubMergerStore @@ -135,7 +137,7 @@ class CallGenBase: 委托给 StubMerger.LookupSha1ByModName(精确匹配 + 包名回退)。 """ - return StubMerger.LookupSha1ByModName(mod_name) + return StubMergerStore.LookupSha1ByModName(mod_name) def _lookup_module_sha1_suffix(self, mod_suffix: str) -> str: @@ -145,7 +147,7 @@ class CallGenBase: 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。 委托给 StubMerger.LookupSha1ByModNameSuffix(精确 + 后缀 + 反向后缀)。 """ - return StubMerger.LookupSha1ByModNameSuffix(mod_suffix) + return StubMergerStore.LookupSha1ByModNameSuffix(mod_suffix) def _module_package_from_sha1(self, pool: memhub.MemBuddy | t.CPtr, @@ -153,9 +155,9 @@ class CallGenBase: """从 SHA1 存储器的 rel_path 计算模块所属包名(目录部分,'/' → '.')""" if pool is None or mod_sha1 is None: return None - rel_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() - sha1_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr() - count: int = StubMerger.GetSha1StoreCount() + rel_arr_ptr: bytes | t.CPtr = StubMergerStore.GetSha1StoreRelArrPtr() + sha1_arr_ptr: bytes | t.CPtr = StubMergerStore.GetSha1StoreArrPtr() + count: int = StubMergerStore.GetSha1StoreCount() if rel_arr_ptr is None or sha1_arr_ptr is None or count <= 0: return None for i in range(count): @@ -199,7 +201,7 @@ class CallGenBase: if temp_dir is None: return None td_len: t.CSizeT = string.strlen(temp_dir) - vpi_path: str = StubMerger._sliced_path(temp_dir, td_len, mod_sha1, "vpi") + vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, mod_sha1, "vpi") if vpi_path is None: return None READ_SZ: t.CSizeT = 1048576 diff --git a/App/lib/core/Gen/ClassGenGeneric.vp b/App/lib/core/Gen/ClassGenGeneric.vp index b2e66fd..880f092 100644 --- a/App/lib/core/Gen/ClassGenGeneric.vp +++ b/App/lib/core/Gen/ClassGenGeneric.vp @@ -21,6 +21,7 @@ import lib.core.CompilerFactory as CompilerFactory import lib.core.Gen.ClassGenBase as ClassGenBase +import lib.core.StubMergerStore as StubMergerStore # 泛型特化递归深度上限(泛型循环套用防护) @@ -456,7 +457,7 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase): """将模板模块的 imports 并入 trans._imported_modules(空格分隔)""" if trans is None or template_sha1 is None: return - tdeps: str = StubMerger.LookupIncludesDeps(template_sha1) + tdeps: str = StubMergerStore.LookupIncludesDeps(template_sha1) if tdeps is None or tdeps[0] == '\0': return cur: str = trans._imported_modules diff --git a/App/lib/core/Gen/ExprGenCdef.vp b/App/lib/core/Gen/ExprGenCdef.vp index 2476e10..62cfbf4 100644 --- a/App/lib/core/Gen/ExprGenCdef.vp +++ b/App/lib/core/Gen/ExprGenCdef.vp @@ -28,6 +28,8 @@ import lib.Projectrans.Utils as Utils import lib.core.Gen.ExprGenBase as ExprGenBase +import lib.core.StubMergerCollect as StubMergerCollect +import lib.core.StubMergerStore as StubMergerStore # ============================================================ # ExprGenCdef - ExprGenMixin 拆分(阶段 D):跨模块 CDefine 查找 @@ -59,11 +61,11 @@ class ExprGenCdef(ExprGenBase.ExprGenBase): if Config.IncludesDir is None: return None # 从 StubMerger 全局存储器获取相对路径数组 - rel_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() - count: int = StubMerger.GetSha1StoreCount() + rel_arr_ptr: bytes | t.CPtr = StubMergerStore.GetSha1StoreRelArrPtr() + count: int = StubMergerStore.GetSha1StoreCount() if rel_arr_ptr is None or count <= 0: return None - sha1_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr() + sha1_arr_ptr: bytes | t.CPtr = StubMergerStore.GetSha1StoreArrPtr() if sha1_arr_ptr is None: return None # 遍历查找匹配的 SHA1 @@ -301,7 +303,7 @@ class ExprGenCdef(ExprGenBase.ExprGenBase): # 6. 构建 vpi 文件路径 td_len: t.CSizeT = string.strlen(temp_dir) - pyi_path: str = StubMerger._sliced_path(temp_dir, td_len, sha1, "vpi") + pyi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "vpi") if pyi_path is None: return -1 diff --git a/App/lib/core/Gen/ExprGenCls.vp b/App/lib/core/Gen/ExprGenCls.vp index 9c0b843..b315c16 100644 --- a/App/lib/core/Gen/ExprGenCls.vp +++ b/App/lib/core/Gen/ExprGenCls.vp @@ -28,6 +28,7 @@ import lib.Projectrans.Utils as Utils import lib.core.Gen.ExprGenBase as ExprGenBase +import lib.core.StubMergerStore as StubMergerStore # ============================================================ # ExprGenCls - ExprGenMixin 拆分(阶段 D):类名推断/docstring 登记 @@ -185,9 +186,9 @@ class ExprGenCls(ExprGenBase.ExprGenBase): return 1 # 3. 模块名(模块本身,少见);用 StubMerger 模块级函数(避免 # 跨 Mixin self 调用生成不存在符号) - mod_sha1: str = StubMerger.LookupSha1ByModName(name_id) + mod_sha1: str = StubMergerStore.LookupSha1ByModName(name_id) if mod_sha1 is None: - mod_sha1 = StubMerger.LookupSha1ByModNameSuffix(name_id) + mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(name_id) if mod_sha1 is not None: string.strcpy(out_mod, mod_sha1) string.strcpy(out_sym, name_id) @@ -235,9 +236,9 @@ class ExprGenCls(ExprGenBase.ExprGenBase): is_mod: int = 0 mod_sha1: str = None if HandlesVar.lookup_var(trans.SymTab, head_id) is None: - mod_sha1 = StubMerger.LookupSha1ByModName(head_id) + mod_sha1 = StubMergerStore.LookupSha1ByModName(head_id) if mod_sha1 is None: - mod_sha1 = StubMerger.LookupSha1ByModNameSuffix(head_id) + mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(head_id) if mod_sha1 is not None: is_mod = 1 if is_mod != 0: diff --git a/App/lib/core/Gen/ImportsGenMixin.vp b/App/lib/core/Gen/ImportsGenMixin.vp index 1c86939..6a64e6b 100644 --- a/App/lib/core/Gen/ImportsGenMixin.vp +++ b/App/lib/core/Gen/ImportsGenMixin.vp @@ -11,6 +11,7 @@ import w32.fileio as fileio import lib.core.StubMerger as StubMerger import lib.core.CompilerFactory as CompilerFactory import lib.core.Handles.HandlesType as HandlesType +import lib.core.StubMergerStore as StubMergerStore # ============================================================ @@ -615,7 +616,7 @@ class ImportsGenMixin: # ctraits 是纯编译期宏库(.vpi 后缀,IncludesScanner 不扫描 .vpi),豁免检查 if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0 or string.strcmp(mod_name, "ctraits") == 0: return 1 - mod_sha1: str = StubMerger.LookupSha1ByModName(mod_name) + mod_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name) if mod_sha1 is None: # App 兄弟模块豁免:当前文件同目录存在 {mod_name}.vp/.py # (如 Test/G4/App/caller1.vp import g4node——同项目源文件, diff --git a/App/lib/core/Handles/HandlesAssign.vp b/App/lib/core/Handles/HandlesAssign.vp index 6d262b4..9131d8b 100644 --- a/App/lib/core/Handles/HandlesAssign.vp +++ b/App/lib/core/Handles/HandlesAssign.vp @@ -16,6 +16,7 @@ import lib.core.Handles.HandlesNonlocal as HandlesNonlocal import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.StubMerger as StubMerger +import lib.core.StubMergerStore as StubMergerStore # ============================================================ @@ -85,7 +86,7 @@ class AssignHandle(HandlesBase.Mixin): # 通过 sha1 查找文件名(人类可读) rel_path: str = None if sha1 is not None: - rel_path = StubMerger.LookupSha1RelPath(sha1) + rel_path = StubMergerStore.LookupSha1RelPath(sha1) # 根据 AST 节点类型提取详情 if val_kind == ast.ASTKind.Attribute: at_nv: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(asgn.value) diff --git a/App/lib/core/Handles/HandlesTranslator.vp b/App/lib/core/Handles/HandlesTranslator.vp index c998ae5..8baed28 100644 --- a/App/lib/core/Handles/HandlesTranslator.vp +++ b/App/lib/core/Handles/HandlesTranslator.vp @@ -25,6 +25,7 @@ import lib.core.Handles.HandlesFunctions as HandlesFunctions import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesStruct as HandlesStruct import lib.Projectrans.Config as Config +import lib.core.StubMergerBuild as StubMergerBuild # ============================================================ # HandlesTranslator - 翻译器状态管理 + 主入口 @@ -454,7 +455,7 @@ class Translator: import lib.core.StubMerger as StubMerger # 错误上下文帧:当前模块触发依赖 stub 加载的 import 位置 HandlesType.push_trace(HandlesType.get_current_file(), cur_imp_lineno) - StubMerger.LoadDepVpiIntoModule( + StubMergerBuild.LoadDepVpiIntoModule( pool, mod, self._imported_modules, self._from_imports, HandlesType.get_temp_dir(), self) HandlesType.pop_trace() diff --git a/App/lib/core/Phase1.vp b/App/lib/core/Phase1.vp index 4f4adc9..521da7c 100644 --- a/App/lib/core/Phase1.vp +++ b/App/lib/core/Phase1.vp @@ -23,6 +23,10 @@ import lib.core.CompilerFactory as CompilerFactory import lib.core.BuildPipeline as BuildPipeline import lib.Projectrans.Config as Config import lib.StubGen.Converter as StubConverter +import lib.core.StubMergerBuild as StubMergerBuild +import lib.core.StubMergerCollect as StubMergerCollect +import lib.core.StubMergerIR as StubMergerIR +import lib.core.StubMergerStore as StubMergerStore # 全局 mbuddy 指针 _mbuddy: memhub.MemBuddy | t.CPtr @@ -101,9 +105,9 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr, # 按需翻译过滤 in_set: int = 0 if use_reachable != 0: - in_set = StubMerger._is_in_sha1_set(sha1_e, reachable_set, reachable_count) + in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count) else: - in_set = StubMerger._is_in_sha1_set(sha1_e, sha1_set, set_count) + in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count) if in_set == 0: continue @@ -596,9 +600,9 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr, sha1_e: str = entry.Sha1 in_set: int = 0 if use_reachable != 0: - in_set = StubMerger._is_in_sha1_set(sha1_e, reachable_set, reachable_count) + in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count) else: - in_set = StubMerger._is_in_sha1_set(sha1_e, sha1_set, set_count) + in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count) if in_set == 0: continue @@ -675,13 +679,13 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr, mod_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) map_count_ts: int = 0 if sha1_arr_ts is not None and mod_arr_ts is not None: - map_count_ts = StubMerger._BuildIncludesSha1Map( + map_count_ts = StubMergerIR._BuildIncludesSha1Map( temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts) for fi_imp in range(file_count): if map_count_ts <= 0: break fsha1_imp: str = file_sha1s_buf + fi_imp * SHA1_LEN - deps_imp: str = StubMerger.LookupIncludesDeps(fsha1_imp) + deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp) if deps_imp is None: continue dl_imp: t.CSizeT = string.strlen(deps_imp) @@ -700,7 +704,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr, continue string.strncpy(dname_imp, deps_imp + dstart_imp, dlen_imp) dname_imp[dlen_imp] = '\0' - dsha1_imp: str = StubMerger._FindSha1ByModName( + dsha1_imp: str = StubMergerIR._FindSha1ByModName( sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp) stdlib.free(dname_imp) if dsha1_imp is None: @@ -950,9 +954,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, set_count += 1 # 先填充全局 SHA1 映射存储器(即使 set_count==0 也填充,确保 Phase2 能获取 includes 条目) # 注意:PopulateSha1MapStore 会先 free 旧存储器再重新分配 - StubMerger.PopulateSha1MapStore(result) + StubMergerStore.PopulateSha1MapStore(result) # 写入 _sha1_map.txt(人类可读输出,程序内部不读取;机器分析使用 PopulateSha1MapStore 内存存储器) - StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0) + StubMergerCollect.WriteIncludesSha1Map(mb, temp_dir, result, None, 0) if set_count <= 0: VLogger.warning("扫描结果无有效 SHA1,跳过 Phase1(项目可能无 includes 依赖)", "Phase1") stdlib.free(sha1_set) @@ -993,7 +997,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, p1a_failed += 1 continue - if StubMerger._is_in_sha1_set(sha1_a, sha1_set, set_count) == 0: + if StubMergerCollect._is_in_sha1_set(sha1_a, sha1_set, set_count) == 0: p1a_skipped += 1 continue @@ -1086,7 +1090,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 同时填充内存依赖存储器(PopulateIncludesDeps),供 _BuildReachableSha1Set # 直接从内存读取,不依赖 .deps.txt 过程文件 td_len_a: t.CSizeT = string.strlen(temp_dir) - deps_path_a: str = StubMerger._sliced_path(temp_dir, td_len_a, sha1_a, "deps.txt") + deps_path_a: str = StubMergerCollect._sliced_path(temp_dir, td_len_a, sha1_a, "deps.txt") if deps_path_a is not None: df_a: fileio.File | t.CPtr = fileio.File(deps_path_a, fileio.MODE.W) if not df_a.closed: @@ -1097,7 +1101,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(deps_path_a) # 填充内存依赖存储器(供 _BuildReachableSha1Set 使用,不读 .deps.txt) if tr_a._imported_modules is not None: - StubMerger.PopulateIncludesDeps(sha1_a, tr_a._imported_modules) + StubMergerStore.PopulateIncludesDeps(sha1_a, tr_a._imported_modules) # 释放 Translator 的 C malloc 资源 if tr_a._global_names is not None: @@ -1131,7 +1135,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, reachable_set = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17) if reachable_set is not None: string.memset(reachable_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17) - reachable_count = StubMerger._BuildReachableSha1Set(mb, Config.SourceDir, temp_dir, reachable_set) + reachable_count = StubMergerBuild._BuildReachableSha1Set(mb, Config.SourceDir, temp_dir, reachable_set) if reachable_count < 0: VLogger.error("可达集合构建失败,终止", "Phase1") return 1 @@ -1139,10 +1143,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, use_reachable = 1 # 用可达集合重新生成 _sha1_map.txt(按图求索的最终产物) # Phase B+ 只遍历这些条目,避免编译不需要的 includes(如 asm.vp) - StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, reachable_set, reachable_count) + StubMergerCollect.WriteIncludesSha1Map(mb, temp_dir, result, reachable_set, reachable_count) # 重新加载 sha1_set,使后续 Phase 1a-pre/1a/1b 的过滤也使用可达集合 string.memset(sha1_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17) - set_count = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set) + set_count = StubMergerCollect._load_includes_sha1_set(mb, temp_dir, sha1_set) else: VLogger.warning("可达集合为空,回退全量扫描", "Phase1") stdlib.free(reachable_set) @@ -1272,11 +1276,11 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 按需翻译过滤(拓扑排序已过滤,但字母序回退需要检查) if topo_count == 0: if use_reachable != 0: - if StubMerger._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0: + if StubMergerCollect._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0: p1a_skp += 1 continue else: - if StubMerger._is_in_sha1_set(sha1_r, sha1_set, set_count) == 0: + if StubMergerCollect._is_in_sha1_set(sha1_r, sha1_set, set_count) == 0: p1a_skp += 1 continue @@ -1403,13 +1407,13 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, sha1_pv: str = entry_pv.Sha1 in_set_pv: int = 0 if use_reachable != 0: - in_set_pv = StubMerger._is_in_sha1_set(sha1_pv, reachable_set, reachable_count) + in_set_pv = StubMergerCollect._is_in_sha1_set(sha1_pv, reachable_set, reachable_count) else: - in_set_pv = StubMerger._is_in_sha1_set(sha1_pv, sha1_set, set_count) + in_set_pv = StubMergerCollect._is_in_sha1_set(sha1_pv, sha1_set, set_count) if in_set_pv == 0: continue # vpi 已存在则跳过 - vpi_path_pv: str = StubMerger._sliced_path(temp_dir, dir_len_pv, sha1_pv, "vpi") + vpi_path_pv: str = StubMergerCollect._sliced_path(temp_dir, dir_len_pv, sha1_pv, "vpi") if vpi_path_pv is None: p1bpre_failed += 1 continue @@ -1456,7 +1460,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, if pyi_buf_pv is not None: pyi_pos_pv: t.CSizeT = StubConverter._GeneratePyiFromAst(mb, tree_pv, entry_pv.RelPath, pyi_buf_pv, PYI_BUF_SIZE) if pyi_pos_pv > 0: - pyi_path_pv: str = StubMerger._sliced_path(temp_dir, dir_len_pv, sha1_pv, "vpi") + pyi_path_pv: str = StubMergerCollect._sliced_path(temp_dir, dir_len_pv, sha1_pv, "vpi") if pyi_path_pv is not None: pf_pv: fileio.File | t.CPtr = fileio.File(pyi_path_pv, fileio.MODE.W) if not pf_pv.closed: @@ -1510,9 +1514,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, if topo_count == 0: in_set = 0 if use_reachable != 0: - in_set = StubMerger._is_in_sha1_set(sha1, reachable_set, reachable_count) + in_set = StubMergerCollect._is_in_sha1_set(sha1, reachable_set, reachable_count) else: - in_set = StubMerger._is_in_sha1_set(sha1, sha1_set, set_count) + in_set = StubMergerCollect._is_in_sha1_set(sha1, sha1_set, set_count) if in_set == 0: skipped += 1 continue @@ -1520,7 +1524,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 构造 stub 路径: {temp_dir}/{sha1}.stub.ll dir_len: t.CSizeT = string.strlen(temp_dir) sha1_len: t.CSizeT = string.strlen(sha1) - stub_path: str = StubMerger._sliced_path(temp_dir, dir_len, sha1, "stub.ll") + stub_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "stub.ll") if stub_path is None: failed += 1 continue @@ -1530,7 +1534,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, if not sf.closed: sf.close() # 检查 text.ll 是否也存在(虚表扫描需要 text.ll) - text_path: str = StubMerger._sliced_path(temp_dir, dir_len, sha1, "text.ll") + text_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "text.ll") if text_path is not None: tf: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R) if not tf.closed: @@ -1617,7 +1621,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, if pyi_buf is not None: pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst(mb, tree, entry.RelPath, pyi_buf, PYI_BUF_SIZE) if pyi_pos > 0: - pyi_path: str = StubMerger._sliced_path(temp_dir, dir_len, sha1, "vpi") + pyi_path: str = StubMergerCollect._sliced_path(temp_dir, dir_len, sha1, "vpi") if pyi_path is not None: pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.W) if not pf.closed: @@ -1690,7 +1694,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # save stub.ll dir_len_p1: t.CSizeT = string.strlen(temp_dir) - stub_path_p1: str = StubMerger._sliced_path(temp_dir, dir_len_p1, sha1, "stub.ll") + stub_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "stub.ll") if stub_path_p1 is not None: sf_p1: fileio.File | t.CPtr = fileio.File(stub_path_p1, fileio.MODE.W) if not sf_p1.closed: @@ -1713,7 +1717,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, text_len: t.CSizeT = string.strlen(text_buf) # save text.ll - text_path_p1: str = StubMerger._sliced_path(temp_dir, dir_len_p1, sha1, "text.ll") + text_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "text.ll") if text_path_p1 is not None: tf_p1: fileio.File | t.CPtr = fileio.File(text_path_p1, fileio.MODE.W) if not tf_p1.closed: @@ -1723,7 +1727,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.free(text_buf) # save dependencies (_imported_modules) for Phase B - deps_path_p1: str = StubMerger._sliced_path(temp_dir, dir_len_p1, sha1, "deps.txt") + deps_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "deps.txt") if deps_path_p1 is not None: df_p1: fileio.File | t.CPtr = fileio.File(deps_path_p1, fileio.MODE.W) if not df_p1.closed: diff --git a/App/lib/core/Phase2.vp b/App/lib/core/Phase2.vp index 7395259..dab1532 100644 --- a/App/lib/core/Phase2.vp +++ b/App/lib/core/Phase2.vp @@ -26,6 +26,11 @@ import lib.core.CompilerFactory as CompilerFactory import lib.core.StubMerger as StubMerger import lib.Projectrans.Utils as Utils import lib.Projectrans.Config as Config +import lib.core.StubMergerBuild as StubMergerBuild +import lib.core.StubMergerCollect as StubMergerCollect +import lib.core.StubMergerIR as StubMergerIR +import lib.core.StubMergerStore as StubMergerStore +import lib.core.StubMergerTopo as StubMergerTopo # 全局 mbuddy 指针 _mbuddy: memhub.MemBuddy | t.CPtr @@ -321,12 +326,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # Phase2 不依赖 Phase1 的副作用:如果 Sha1Store 为空(Phase1 未运行或提前返回), # 自行扫描 includes 目录并填充 Sha1Store,确保后续"编译缺失 includes"逻辑能正常工作 if includes_dir is not None: - if StubMerger.GetSha1StoreCount() == 0: + if StubMergerStore.GetSha1StoreCount() == 0: inc_scan_result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir) if inc_scan_result is None: VLogger.error("includes 扫描失败,无法填充 Sha1Store", "project") if inc_scan_result is not None: - inc_filled: int = StubMerger.PopulateSha1MapStore(inc_scan_result) + inc_filled: int = StubMergerStore.PopulateSha1MapStore(inc_scan_result) fb_inc: str = VLogger.fmt_buf() if fb_inc is not None: viperlib.snprintf(fb_inc, 1024, "Phase2 自主填充 includes Sha1Store: %d 个", inc_filled) @@ -358,7 +363,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, rel_path_am = p_str + src_dir_len_am + 1 # 同步追加到内存存储器(供跨模块 CDefine 查找) # rel_path_am 格式如 "lib/core/StubMerger.vp",与 _sha1_map.txt 一致 - if StubMerger.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) != 0: + if StubMergerStore.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) != 0: fb_apf: str = VLogger.fmt_buf() if fb_apf is not None: viperlib.snprintf(fb_apf, 1024, "AppendToSha1MapStore 失败: %s", rel_path_am) @@ -404,11 +409,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # 同时填充 PopulateIncludesDeps,供 Phase B+ 的 _BuildReachableSha1Set 使用。 # 多遍扫描:解决跨文件继承问题(如 Constant(AST) 在 AST 未注册时被跳过)。 if includes_dir is not None and includes_binary_dir is not None: - store_count_inc_pre: int = StubMerger.GetSha1StoreCount() + store_count_inc_pre: int = StubMergerStore.GetSha1StoreCount() if store_count_inc_pre > 0: - store_sha1_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr() - store_rel_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() - store_incdir_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreIncdirArrPtr() + store_sha1_inc_pre: bytes | t.CPtr = StubMergerStore.GetSha1StoreArrPtr() + store_rel_inc_pre: bytes | t.CPtr = StubMergerStore.GetSha1StoreRelArrPtr() + store_incdir_inc_pre: bytes | t.CPtr = StubMergerStore.GetSha1StoreIncdirArrPtr() deps_filled_pre: int = 0 PHASE_A_INC_MAX_PASSES: t.CInt = 3 # [修复] 记录预注册失败的 includes:多遍后仍失败 → 报错终止编译。 @@ -467,7 +472,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # 依赖填充只在第一遍执行(PopulateIncludesDeps 内部有去重) # 注意:必须在 reset_to(mark2) 之前调用——内部深拷贝到 stdlib if pass_inc_i == 0 and tr_inc_pre._imported_modules is not None \ - and StubMerger.PopulateIncludesDeps( + and StubMergerStore.PopulateIncludesDeps( inc_sha1_pre, tr_inc_pre._imported_modules) == 0: deps_filled_pre = deps_filled_pre + 1 # OOM 清理:回收本 includes 文件预注册翻译的瞬时数据 @@ -618,7 +623,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, stub_len_a: t.CSizeT = string.strlen(stub_buf_a) # save stub.ll (切片路径: temp_dir/{sha1前缀}/{sha1}.stub.ll) - stub_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "stub.ll") + stub_path_a: str = StubMergerCollect._sliced_path(temp_dir, td_len_pa, ent.Sha1, "stub.ll") if stub_path_a is not None: sf_a: fileio.File | t.CPtr = fileio.File(stub_path_a, fileio.MODE.W) if not sf_a.closed: @@ -635,7 +640,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, text_len_a: t.CSizeT = string.strlen(text_buf_a) # save text.ll (切片路径) - text_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "text.ll") + text_path_a: str = StubMergerCollect._sliced_path(temp_dir, td_len_pa, ent.Sha1, "text.ll") if text_path_a is not None: tf_a: fileio.File | t.CPtr = fileio.File(text_path_a, fileio.MODE.W) if not tf_a.closed: @@ -645,7 +650,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, stdlib.free(text_buf_a) # save dependencies (_imported_modules) for Phase B (切片路径) - deps_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "deps.txt") + deps_path_a: str = StubMergerCollect._sliced_path(temp_dir, td_len_pa, ent.Sha1, "deps.txt") if deps_path_a is not None: df_a: fileio.File | t.CPtr = fileio.File(deps_path_a, fileio.MODE.W) if not df_a.closed: @@ -732,7 +737,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, viperlib.snprintf(fb, 1024, "combined_ir 分配失败: %s", ent.Path) VLogger.error(fb, "PhaseB") continue - combined_len: t.CSizeT = StubMerger.BuildCombinedIR(temp_dir, ent.Sha1, combined_ir, COMBINED_IR_SIZE) + combined_len: t.CSizeT = StubMergerBuild.BuildCombinedIR(temp_dir, ent.Sha1, combined_ir, COMBINED_IR_SIZE) if combined_len == 0: fb: str = VLogger.fmt_buf() if fb is not None: @@ -768,7 +773,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, is_main_mod = 1 # 切片路径: output_dir/{sha1前缀}/{sha1}.obj - obj_path_sliced: str = StubMerger._sliced_path(output_dir, od_len, ent.Sha1, "obj") + obj_path_sliced: str = StubMergerCollect._sliced_path(output_dir, od_len, ent.Sha1, "obj") if obj_path_sliced is not None: op_sliced_len: t.CSizeT = string.strlen(obj_path_sliced) if is_main_mod != 0: @@ -811,7 +816,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if fb_pbp_enter is not None: viperlib.snprintf(fb_pbp_enter, 1024, "PhaseB+ 入口: store_count=%d includes_binary_dir=%s", - StubMerger.GetSha1StoreCount(), includes_binary_dir) + StubMergerStore.GetSha1StoreCount(), includes_binary_dir) VLogger.info(fb_pbp_enter, "PhaseB+") # 创建 includes_binary_dir 目录(确保 .obj 能写入和 collect_obj_files 能扫描) @@ -828,7 +833,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, reachable_count: int = 0 if reachable_set is not None: string.memset(reachable_set, 0, REACHABLE_SET_SIZE) - reachable_count = StubMerger._BuildReachableSha1Set( + reachable_count = StubMergerBuild._BuildReachableSha1Set( mb, source_dir, temp_dir, reachable_set, app_deps_buf) fb_reach: str = VLogger.fmt_buf() if fb_reach is not None: @@ -856,10 +861,10 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, store_mod_arr_mi: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) store_count_mi: int = 0 if store_sha1_arr_mi is not None and store_mod_arr_mi is not None: - store_count_mi = StubMerger._BuildIncludesSha1Map( + store_count_mi = StubMergerIR._BuildIncludesSha1Map( temp_dir, td_len_pb, store_sha1_arr_mi, store_mod_arr_mi) - store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() - store_incdir_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreIncdirArrPtr() + store_rel_arr_mi: bytes | t.CPtr = StubMergerStore.GetSha1StoreRelArrPtr() + store_incdir_arr_mi: bytes | t.CPtr = StubMergerStore.GetSha1StoreIncdirArrPtr() if store_count_mi > 0: # 拓扑排序可达 includes:被依赖模块先完整翻译。跨模块 vtable 生成 @@ -870,7 +875,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, VLogger.error("拓扑排序索引分配失败,终止编译", "PhaseB+") topo_count = 0 else: - topo_count = StubMerger.BuildTopoSortedIndices( + topo_count = StubMergerTopo.BuildTopoSortedIndices( store_count_mi, store_sha1_arr_mi, store_mod_arr_mi, reachable_set, reachable_count, topo_idx) @@ -882,7 +887,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(ti) * StubMerger.MAX_REL_PATH_LEN # 检查 .obj 是否已存在于 includes.binary(切片子目录) ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir) - check_pat_mi: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj") + check_pat_mi: str = StubMergerCollect._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj") check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__()) obj_exists_mi: int = 0 if check_pat_mi is not None and check_fd_mi is not None: @@ -916,7 +921,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # 检查是否为声明文件(只有 declare 没有实质 define) # 判断方法: text.ll 中若有混淆函数 define(含 @")则为实现文件 is_decl_mi: int = -1 - tpath_mi: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") + tpath_mi: str = StubMergerCollect._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") if tpath_mi is not None: tf_mi: fileio.File | t.CPtr = fileio.File(tpath_mi, fileio.MODE.R) if not tf_mi.closed: @@ -964,7 +969,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE) inc_combined_len_mi: t.CSizeT = 0 if inc_combined_mi is not None: - inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) + inc_combined_len_mi = StubMergerBuild.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) # 如果 stub/text 不存在,翻译源文件并保存 stub + text,然后重试 if inc_combined_len_mi == 0 and inc_combined_mi is not None: @@ -981,7 +986,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if inc_stub_buf is not None: tr_mi.dump_ir(inc_stub_buf, PBP_IR_SIZE, llvmlite.OUTPUT_STUB) inc_stub_len: t.CSizeT = string.strlen(inc_stub_buf) - inc_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll") + inc_stub_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll") if inc_stub_path is not None: isf: fileio.File | t.CPtr = fileio.File(inc_stub_path, fileio.MODE.W) if not isf.closed: @@ -994,7 +999,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if inc_text_buf is not None: tr_mi.dump_ir(inc_text_buf, PBP_IR_SIZE, llvmlite.OUTPUT_TEXT) inc_text_len: t.CSizeT = string.strlen(inc_text_buf) - inc_text_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") + inc_text_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") if inc_text_path is not None: itf: fileio.File | t.CPtr = fileio.File(inc_text_path, fileio.MODE.W) if not itf.closed: @@ -1003,15 +1008,15 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, stdlib.free(inc_text_path) stdlib.free(inc_text_buf) # 重试 BuildCombinedIR - inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) + inc_combined_len_mi = StubMergerBuild.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) # OOM 清理:stub/text 已写盘,回收本 includes 文件翻译瞬时数据 # (AST/tokens/lexer/IR/Handler);BuildCombinedIR 只读文件,不受影响 mb.reset_to(mark2) if inc_combined_len_mi == 0: # 诊断:检查 stub.ll/text.ll 是否存在 - diag_stub: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll") - diag_text: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") + diag_stub: str = StubMergerCollect._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll") + diag_text: str = StubMergerCollect._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll") diag_stub_ex: int = 0 diag_text_ex: int = 0 if diag_stub is not None: @@ -1062,7 +1067,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # 添加到 obj_paths (切片路径,从 includes_binary_dir 取) ibd_len_mi2: t.CSizeT = string.strlen(includes_binary_dir) - inc_obj_sliced: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi2, inc_sha1_mi, "obj") + inc_obj_sliced: str = StubMergerCollect._sliced_path(includes_binary_dir, ibd_len_mi2, inc_sha1_mi, "obj") if inc_obj_sliced is not None: inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced) @@ -1156,7 +1161,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, rsha1_pc: str = reachable_set + t.CSizeT(ri_pc) * 17 if rsha1_pc[0] == '\0': continue - obj_path_pc: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_pc, rsha1_pc, "obj") + obj_path_pc: str = StubMergerCollect._sliced_path(includes_binary_dir, ibd_len_pc, rsha1_pc, "obj") if obj_path_pc is not None: # 检查 .obj 文件是否存在 cf_pc: fileio.File | t.CPtr = fileio.File(obj_path_pc, fileio.MODE.R) @@ -1174,7 +1179,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, else: # .obj 缺失:纯声明模块(text.ll 无 define 行,如 stdio.vp 全 pass) # 无需 .obj,静默跳过;实现文件缺失才是编译产物异常(fail-fast) - if StubMerger.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1: + if StubMergerIR.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1: fb_pc: str = VLogger.fmt_buf() if fb_pc is not None: viperlib.snprintf(fb_pc, 1024, "可达模块 .obj 缺失: %s", obj_path_pc) diff --git a/App/lib/core/StubMerger.vp b/App/lib/core/StubMerger.vp index e6c06fd..14a84ad 100644 --- a/App/lib/core/StubMerger.vp +++ b/App/lib/core/StubMerger.vp @@ -70,2322 +70,3 @@ MAX_DEPS_STR_LEN: t.CDefine = 512 MAX_REL_PATH_LEN: t.CDefine = 256 # includes 根目录最大长度(绝对路径,多 includes 支持) MAX_INC_DIR_LEN: t.CDefine = 512 - - -# ============================================================ -# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free) -# -# 根据 Config.Sha1SliceLevel 将文件分散到 SHA1 前缀子目录: -# level=0 → {temp_dir}/{sha1}.{ext} -# level=1 → {temp_dir}/b7/{sha1}.{ext} -# level=2 → {temp_dir}/b7/90/{sha1}.{ext} -# -# 自动调用 BuildPipeline.ensure_dir 创建子目录(level>0 时)。 -# -# Args: -# temp_dir: 基础目录 -# td_len: temp_dir 长度(避免重复 strlen) -# sha1: SHA1 字符串 -# ext: 文件扩展名(如 "stub.ll") -# -# Returns: -# 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败 -# ============================================================ -def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str: - """构建切片路径(stdlib.malloc 分配,调用者负责 free) - - level>0 时自动调用 ensure_dir 创建子目录。 - """ - if temp_dir is None or sha1 is None or ext is None: - return None - sha1_len: t.CSizeT = string.strlen(sha1) - ext_len: t.CSizeT = string.strlen(ext) - - # 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free) - subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel) - sub_len: t.CSizeT = 0 - if subdir_buf is not None: - sub_len = string.strlen(subdir_buf) - - # 确保子目录存在: {temp_dir}/{subdir} - # 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True), - # 进而 BuildCombinedIR 读不到文件返回 0("BuildCombinedIR 失败")。 - dir_path_len: t.CSizeT = td_len + sub_len + 2 - dir_path: bytes = stdlib.malloc(dir_path_len) - if dir_path is not None: - viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf) - BuildPipeline.ensure_dir(dir_path) - stdlib.free(dir_path) - - # 构建完整路径 - path: str = None - path_len: t.CSizeT = 0 - if sub_len > 0: - path_len = td_len + sub_len + sha1_len + ext_len + 4 - path = stdlib.malloc(path_len) - if path is not None: - viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext) - else: - path_len = td_len + sha1_len + ext_len + 3 - path = stdlib.malloc(path_len) - if path is not None: - viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext) - - return path - - -# ============================================================ -# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1 -# -# 根据 level 递归扫描子目录: -# level=0 → 扫描 {base_dir}/*.stub.ll,从文件名提取 SHA1 -# level>0 → 扫描 {base_dir}/* 的子目录,对每个子目录递归调用 -# -# Args: -# base_dir: 当前扫描目录 -# bd_len: base_dir 长度 -# level: 剩余递归层数 -# out_arr: 输出数组(每个 SHA1 17 字节 = 16字符 + null) -# out_count: 当前已收集数量 -# max_count: 最大数量 -# -# Returns: -# 收集后的总数量 -# ============================================================ -def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int, - out_arr: str, out_count: int, max_count: int) -> int: - """递归扫描子目录,收集 .stub.ll 文件的 SHA1""" - if base_dir is None or out_arr is None: - return out_count - if out_count >= max_count: - return out_count - - find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__()) - if find_data is None: - return out_count - string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__()) - - if level <= 0: - # 扫描 {base_dir}/*.stub.ll - pattern: bytes = stdlib.malloc(bd_len + 16) - if pattern is None: - stdlib.free(find_data) - return out_count - viperlib.snprintf(pattern, bd_len + 16, "%s/*.stub.ll", base_dir) - handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) - stdlib.free(pattern) - if handle != win32base.INVALID_HANDLE_VALUE: - while 1: - if out_count >= max_count: - break - fname: str = find_data.cFileName - if fname is not None: - if fname[0] != '\0': - # 从文件名提取 SHA1(前 16 字符) - string.strncpy(out_arr + out_count * 17, fname, 16) - out_arr[out_count * 17 + 16] = '\0' - out_count += 1 - if win32file.FindNextFileA(handle, find_data) == 0: - break - win32file.FindClose(handle) - else: - # 扫描 {base_dir}/* 的子目录 - pattern = stdlib.malloc(bd_len + 4) - if pattern is None: - stdlib.free(find_data) - return out_count - viperlib.snprintf(pattern, bd_len + 4, "%s/*", base_dir) - handle = win32file.FindFirstFileA(pattern, find_data) - stdlib.free(pattern) - if handle != win32base.INVALID_HANDLE_VALUE: - while 1: - # 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16) - attrs: t.CUInt32T = find_data.dwFileAttributes - if (attrs & 16) != 0: - fname = find_data.cFileName - if fname is not None: - # 跳过 "." 和 ".." - is_dot: int = 0 - if fname[0] == '.': - if fname[1] == '\0': - is_dot = 1 - elif fname[1] == '.' and fname[2] == '\0': - is_dot = 1 - if is_dot == 0: - # 构建子目录路径并递归扫描 - sub_dir: str = Utils.JoinPath(base_dir, fname) - if sub_dir is not None: - sub_dir_len: t.CSizeT = string.strlen(sub_dir) - out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count) - stdlib.free(sub_dir) - if win32file.FindNextFileA(handle, find_data) == 0: - break - win32file.FindClose(handle) - - stdlib.free(find_data) - return out_count - - -# ============================================================ -# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1 -# -# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。 -# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。 -# ============================================================ -def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr, - temp_dir: str, - sha1_set: str) -> int: - """从全局内存存储器将 includes 的 SHA1 写入 sha1_set - - pool/temp_dir 参数保留以兼容现有调用者,但不再使用。 - 数据来源是 PopulateSha1MapStore 填充的全局存储器。 - sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null) - 返回找到的 includes SHA1 数量,-1 表示错误""" - if sha1_set is None: - return -1 - if _sha1_store_arr is None: - return -1 - count: int = 0 - for i in range(_sha1_store_count): - if count >= MAX_INCLUDES_SHA1: - break - idx: t.CSizeT = t.CSizeT(i) * 17 - string.strcpy(sha1_set + t.CSizeT(count) * 17, _sha1_store_arr + idx) - count += 1 - return count - - -# ============================================================ -# _is_in_sha1_set - 检查 SHA1 是否在集合中 -# ============================================================ -def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int: - """检查 sha1 是否在集合中""" - if sha1 is None or sha1_set is None: - return 0 - for i in range(set_count): - if string.strcmp(sha1_set + i * 17, sha1) == 0: - return 1 - return 0 - - -# ============================================================ -# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出) -# -# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。 -# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器。 -# -# Args: -# mb: 内存池 -# temp_dir: 临时目录(_sha1_map.txt 写入位置) -# scan_result: IncludesScanner 扫描结果 -# -# Returns: -# 0 成功,非 0 失败 -# ============================================================ -def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str, - scan_result: IncludesScanner.ScanResult | t.CPtr, - filter_set: str, - filter_count: int) -> int: - """将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器) - - 若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。 - """ - if temp_dir is None or scan_result is None: - return 1 - - # 构造路径 temp_dir/_sha1_map.txt - dir_len: t.CSizeT = string.strlen(temp_dir) - map_path: bytes = stdlib.malloc(dir_len + 32) - if map_path is None: - return 1 - viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir) - - # 打开文件写入(CREATE_ALWAYS) - f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.W) - if f.closed: - return 1 - - # 写入每个 include 条目: {sha1}:includes/{rel_path}\n - entry_size_w: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() - line_buf: bytes = stdlib.malloc(512) - if line_buf is None: - f.close() - return 1 - - written_count: int = 0 - for i in range(scan_result.Count): - ea_w: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_w - ent_w: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_w, t.CPtr)) - if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: - continue - # 若提供过滤集合,只写入集合中的条目 - if filter_set is not None and filter_count > 0: - if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: - continue - viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath) - line_len_w: t.CSizeT = string.strlen(line_buf) - f.write(line_buf, line_len_w) - written_count += 1 - - f.close() - return 0 - - -# ============================================================ -# PopulateSha1MapStore - 从扫描结果填充全局 SHA1 映射存储器 -# -# 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。 -# 模块名通过 _PathToModuleName(includes/rel_path) 计算。 -# 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。 -# -# Args: -# scan_result: IncludesScanner 扫描结果 -# -# Returns: -# 填充的条目数,-1 表示失败 -# ============================================================ -def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> int: - """从扫描结果填充全局 SHA1 映射存储器 - - 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。 - 模块名通过 _PathToModuleName(includes/rel_path) 计算。 - 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。 - """ - global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_incdir, _sha1_store_count - if scan_result is None: - return -1 - # 释放旧存储器 - if _sha1_store_arr is not None: - stdlib.free(_sha1_store_arr) - if _sha1_store_mod is not None: - stdlib.free(_sha1_store_mod) - if _sha1_store_rel is not None: - stdlib.free(_sha1_store_rel) - if _sha1_store_incdir is not None: - stdlib.free(_sha1_store_incdir) - _sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17) - _sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64) - _sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN) - _sha1_store_incdir = stdlib.malloc(MAX_INCLUDES * MAX_INC_DIR_LEN) - if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None or _sha1_store_incdir is None: - return -1 - string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17) - string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64) - string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN) - string.memset(_sha1_store_incdir, 0, MAX_INCLUDES * MAX_INC_DIR_LEN) - _sha1_store_count = 0 - p_bad: int = 0 - entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() - for i in range(scan_result.Count): - if _sha1_store_count >= MAX_INCLUDES: - VLogger.error("includes SHA1 存储超上限,终止", "StubMerger") - ea: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_p - ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) - if ent is None or ent.Sha1 is None or ent.RelPath is None: - p_bad += 1 - continue - # 构建完整路径 includes/{RelPath}(_PathToModuleName 需要) - rp_len: t.CSizeT = string.strlen(ent.RelPath) - full_path_buf: bytes = stdlib.malloc(rp_len + 10) - if full_path_buf is None: - p_bad += 1 - continue - viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath) - mod_name: str = Utils.PathToModuleName(full_path_buf, 1) - stdlib.free(full_path_buf) - if mod_name is not None: - idx: t.CSizeT = t.CSizeT(_sha1_store_count) * 17 - string.strcpy(_sha1_store_arr + idx, ent.Sha1) - idx2: t.CSizeT = t.CSizeT(_sha1_store_count) * 64 - mn_len: t.CSizeT = string.strlen(mod_name) - if mn_len < 64: - string.strcpy(_sha1_store_mod + idx2, mod_name) - else: - string.strncpy(_sha1_store_mod + idx2, mod_name, 63) - _sha1_store_mod[idx2 + 63] = '\0' - stdlib.free(mod_name) - # 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用) - idx3: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN - if rp_len < MAX_REL_PATH_LEN: - string.strcpy(_sha1_store_rel + idx3, ent.RelPath) - else: - string.strncpy(_sha1_store_rel + idx3, ent.RelPath, MAX_REL_PATH_LEN - 1) - _sha1_store_rel[idx3 + MAX_REL_PATH_LEN - 1] = '\0' - # 保存所属 includes 根目录(Phase2 预注册/按需编译拼路径用) - idx_inc: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_INC_DIR_LEN - if ent.IncDir is not None: - icd_len: t.CSizeT = string.strlen(ent.IncDir) - if icd_len < MAX_INC_DIR_LEN: - string.strcpy(_sha1_store_incdir + idx_inc, ent.IncDir) - else: - string.strncpy(_sha1_store_incdir + idx_inc, ent.IncDir, MAX_INC_DIR_LEN - 1) - _sha1_store_incdir[idx_inc + MAX_INC_DIR_LEN - 1] = '\0' - _sha1_store_count += 1 - if p_bad > 0: - fb_pbad: str = VLogger.fmt_buf() - if fb_pbad is not None: - viperlib.snprintf(fb_pbad, 1024, "includes SHA1 存储有 %d 个条目失败", p_bad) - VLogger.error(fb_pbad, "StubMerger") - return _sha1_store_count - - -# ============================================================ -# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器 -# -# Phase1 调用 PopulateSha1MapStore 填充 includes 文件, -# Phase2 扫描 App 源文件后调用此函数追加 App 模块条目, -# 确保编译 App 自身时跨模块 CDefine 查找能命中 App 模块 -# (如 StubMerger.MAX_INCLUDES 在编译 Phase2 时通过 -# _build_source_path_from_sha1 查找 StubMerger 的源路径)。 -# -# 若存储器未初始化(PopulateSha1MapStore 未调用),自动初始化。 -# 若 SHA1 已存在(去重),跳过。 -# -# Args: -# sha1: SHA1 字符串(16 字符 + null) -# rel_path: 相对路径(如 "lib/core/StubMerger.vp", -# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致) -# -# Returns: -# 0 成功(含去重跳过),1 失败 -# ============================================================ -def AppendToSha1MapStore(sha1: str, rel_path: str) -> int: - """追加条目到全局 SHA1 映射存储器(Phase2 用)""" - global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_incdir, _sha1_store_count - if sha1 is None or rel_path is None: - return 1 - # 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用) - if _sha1_store_arr is None: - _sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17) - _sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64) - _sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN) - _sha1_store_incdir = stdlib.malloc(MAX_INCLUDES * MAX_INC_DIR_LEN) - if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None or _sha1_store_incdir is None: - return 1 - string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17) - string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64) - string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN) - string.memset(_sha1_store_incdir, 0, MAX_INCLUDES * MAX_INC_DIR_LEN) - _sha1_store_count = 0 - if _sha1_store_count >= MAX_INCLUDES: - return 1 - # 去重:若 SHA1 已存在则跳过 - for i in range(_sha1_store_count): - sidx_d: t.CSizeT = t.CSizeT(i) * 17 - if string.strcmp(_sha1_store_arr + sidx_d, sha1) == 0: - return 0 - # 模块名:rel_path "lib/core/StubMerger.vp" → "lib.core.StubMerger" - full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10) - if full_path_buf is None: - return 1 - viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path) - mod_name: str = Utils.PathToModuleName(full_path_buf, 1) - stdlib.free(full_path_buf) - if mod_name is None: - return 1 - # 写入 SHA1 - idx_a: t.CSizeT = t.CSizeT(_sha1_store_count) * 17 - string.strcpy(_sha1_store_arr + idx_a, sha1) - # 写入模块名 - idx_m: t.CSizeT = t.CSizeT(_sha1_store_count) * 64 - mn_len: t.CSizeT = string.strlen(mod_name) - if mn_len < 64: - string.strcpy(_sha1_store_mod + idx_m, mod_name) - else: - string.strncpy(_sha1_store_mod + idx_m, mod_name, 63) - _sha1_store_mod[idx_m + 63] = '\0' - stdlib.free(mod_name) - # 写入相对路径 - idx_r: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN - rp_len: t.CSizeT = string.strlen(rel_path) - if rp_len < MAX_REL_PATH_LEN: - string.strcpy(_sha1_store_rel + idx_r, rel_path) - else: - string.strncpy(_sha1_store_rel + idx_r, rel_path, MAX_REL_PATH_LEN - 1) - _sha1_store_rel[idx_r + MAX_REL_PATH_LEN - 1] = '\0' - # App 源文件不属于任何 includes 目录:IncDir 留空(Phase2 回退 includes_dir 参数) - idx_inc_a: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_INC_DIR_LEN - _sha1_store_incdir[idx_inc_a] = '\0' - _sha1_store_count += 1 - return 0 - - -# ============================================================ -# GetSha1StoreCount - 获取全局存储器条目数 -# ============================================================ -def GetSha1StoreCount() -> int: - """返回全局 SHA1 存储器的条目数""" - return _sha1_store_count - - -# ============================================================ -# LookupSha1ByModName - 从模块名查找 SHA1(直接查全局存储器) -# -# 精确匹配失败时,尝试 "{mod_name}.__init__" 回退, -# 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。 -# -# Returns: -# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到 -# ============================================================ -def LookupSha1ByModName(mod_name: str) -> str: - """从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName""" - return _FindSha1ByModName(_sha1_store_arr, _sha1_store_mod, _sha1_store_count, mod_name) - - -# ============================================================ -# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1 -# -# 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点), -# 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。 -# 包含三种匹配方式:精确、后缀、反向后缀。 -# -# Returns: -# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到 -# ============================================================ -def LookupSha1ByModNameSuffix(mod_suffix: str) -> str: - """后缀匹配查找模块 SHA1""" - if mod_suffix is None: - return None - if _sha1_store_arr is None or _sha1_store_mod is None: - return None - if _sha1_store_count <= 0: - return None - suffix_len_sf: t.CSizeT = string.strlen(mod_suffix) - if suffix_len_sf == 0: - return None - i_sf: int - for i_sf in range(_sha1_store_count): - idx_sf: t.CSizeT = t.CSizeT(i_sf) * 64 - mod_name_sf: str = _sha1_store_mod + idx_sf - mod_len_sf: t.CSizeT = string.strlen(mod_name_sf) - if mod_len_sf == 0: - continue - # 精确匹配 - if mod_len_sf == suffix_len_sf: - if string.strcmp(mod_name_sf, mod_suffix) == 0: - sidx_sf: t.CSizeT = t.CSizeT(i_sf) * 17 - return _sha1_store_arr + sidx_sf - # 后缀匹配:mod_name 以 "." + mod_suffix 结尾 - if mod_len_sf > suffix_len_sf: - if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.': - cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf - if string.strcmp(cmp_start_sf, mod_suffix) == 0: - sidx2_sf: t.CSizeT = t.CSizeT(i_sf) * 17 - return _sha1_store_arr + sidx2_sf - # 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾 - if suffix_len_sf > mod_len_sf: - if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.': - cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf - if string.strcmp(cmp_start_rev_sf, mod_name_sf) == 0: - sidx_rev_sf: t.CSizeT = t.CSizeT(i_sf) * 17 - return _sha1_store_arr + sidx_rev_sf - return None - - -# ============================================================ -# PopulateIncludesDeps - 将 includes 文件的依赖存入内存 -# -# Phase B+ 前用 declare_only 翻译 includes 文件,提取 _imported_modules -# 后调用此函数存入内存。_BuildReachableSha1Set 从内存读取,不读 deps.txt。 -# -# Args: -# sha1: includes 文件的 SHA1(16字符) -# deps_str: 依赖模块名字符串(空格分隔,如 "t c stdint string") -# -# Returns: -# 0 成功,非 0 失败 -# ============================================================ -def PopulateIncludesDeps(sha1: str, deps_str: str) -> int: - """将 includes 文件的依赖存入内存""" - global _includes_deps_sha1, _includes_deps_str, _includes_deps_count - if sha1 is None: - return 1 - # 延迟初始化 - if _includes_deps_sha1 is None: - _includes_deps_sha1 = stdlib.malloc(MAX_INCLUDES * 17) - _includes_deps_str = stdlib.malloc(MAX_INCLUDES * MAX_DEPS_STR_LEN) - if _includes_deps_sha1 is None or _includes_deps_str is None: - return 1 - string.memset(_includes_deps_sha1, 0, MAX_INCLUDES * 17) - string.memset(_includes_deps_str, 0, MAX_INCLUDES * MAX_DEPS_STR_LEN) - _includes_deps_count = 0 - if _includes_deps_count >= MAX_INCLUDES: - return 1 - # 去重:若 SHA1 已存在则跳过 - for i in range(_includes_deps_count): - sidx: t.CSizeT = t.CSizeT(i) * 17 - if string.strcmp(_includes_deps_sha1 + sidx, sha1) == 0: - return 0 - # 写入 SHA1 - idx: t.CSizeT = t.CSizeT(_includes_deps_count) * 17 - string.strcpy(_includes_deps_sha1 + idx, sha1) - # 写入依赖字符串 - idx2: t.CSizeT = t.CSizeT(_includes_deps_count) * MAX_DEPS_STR_LEN - if deps_str is not None: - dl: t.CSizeT = string.strlen(deps_str) - if dl < MAX_DEPS_STR_LEN: - string.strcpy(_includes_deps_str + idx2, deps_str) - else: - string.strncpy(_includes_deps_str + idx2, deps_str, MAX_DEPS_STR_LEN - 1) - else: - _includes_deps_str[idx2] = '\0' - _includes_deps_count = _includes_deps_count + 1 - return 0 - - -# ============================================================ -# LookupIncludesDeps - 从内存查找 includes 文件的依赖 -# -# Args: -# sha1: includes 文件的 SHA1(16字符) -# -# Returns: -# 依赖模块名字符串指针(空格分隔),None 表示未找到 -# ============================================================ -def LookupIncludesDeps(sha1: str) -> str: - """从内存查找 includes 文件的依赖""" - if sha1 is None or _includes_deps_sha1 is None: - return None - if _includes_deps_count <= 0: - return None - for i in range(_includes_deps_count): - sidx: t.CSizeT = t.CSizeT(i) * 17 - if string.strcmp(_includes_deps_sha1 + sidx, sha1) == 0: - idx2: t.CSizeT = t.CSizeT(i) * MAX_DEPS_STR_LEN - return _includes_deps_str + idx2 - return None - - -# ============================================================ -# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先) -# -# 背景:跨模块 vtable 生成要求被依赖模块先完整翻译(如 astaux.vp 继承 -# base.vp 的 AST,vtable 槽位需 AST.accept 的 TypeStr 已注册到全局表)。 -# Phase B+ 原先按 store 扫描顺序编译,依赖顺序不保证 → fail-fast 暴露。 -# -# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中)。 -# indegree[m] = m 在可达集合内的直接依赖数; -# 入度为 0(不依赖任何可达模块)者先输出,输出后对依赖它的模块 -# 减入度,入度归 0 者入队。结果即"被依赖者先编译"。 -# reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。 -# -# Args: -# store_count / store_sha1_arr / store_mod_arr: store 元数据 -# reachable_set / reachable_count: 可达集合(_BuildReachableSha1Set 输出) -# sorted_idx: 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引 -# -# Returns: -# 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译) -# ============================================================ -def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, - store_mod_arr: bytes, reachable_set: bytes, - reachable_count: int, - sorted_idx: t.CSizeT | t.CPtr) -> int: - """对可达 includes 逆拓扑排序(被依赖者先),store 索引写入 sorted_idx""" - if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None: - return 0 - indegree: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1))) - queue: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1))) - rel_idx: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1))) - if indegree is None or queue is None or rel_idx is None: - if indegree is not None: - stdlib.free(indegree) - if queue is not None: - stdlib.free(queue) - if rel_idx is not None: - stdlib.free(rel_idx) - return 0 - # 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为) - rel_count: int = 0 - i_t: int - if reachable_set is None or reachable_count <= 0: - for i_t in range(store_count): - rel_idx[rel_count] = i_t - rel_count += 1 - else: - for i_t in range(store_count): - if _is_in_sha1_set(store_sha1_arr + t.CSizeT(i_t) * 17, reachable_set, reachable_count) != 0: - rel_idx[rel_count] = i_t - rel_count += 1 - # 计算入度:m 在可达集合内的直接依赖数 - i_r: int - for i_r in range(rel_count): - si: int = rel_idx[i_r] - deps_si: str = LookupIncludesDeps(store_sha1_arr + t.CSizeT(si) * 17) - if deps_si is None: - continue - dl_si: t.CSizeT = string.strlen(deps_si) - dpos: t.CSizeT = 0 - while dpos < dl_si: - dstart: t.CSizeT = dpos - while dpos < dl_si and deps_si[dpos] != ' ': - dpos += 1 - dlen: t.CSizeT = dpos - dstart - if dpos < dl_si: - dpos += 1 - if dlen == 0: - continue - dname_buf: bytes = stdlib.malloc(dlen + 1) - if dname_buf is None: - continue - string.strncpy(dname_buf, deps_si + dstart, dlen) - dname_buf[dlen] = '\0' - dsha1: str = _FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf) - stdlib.free(dname_buf) - if dsha1 is not None and _is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0: - indegree[si] = indegree[si] + 1 - # Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译 - q_head: int = 0 - q_tail: int = 0 - for i_r in range(rel_count): - si2: int = rel_idx[i_r] - if indegree[si2] == 0: - queue[q_tail] = si2 - q_tail += 1 - out_count: int = 0 - while q_head < q_tail: - m: int = queue[q_head] - q_head += 1 - sorted_idx[out_count] = m - out_count += 1 - # 对依赖 m 的可达模块减入度 - mod_m: str = store_mod_arr + t.CSizeT(m) * 64 - mod_m_len: t.CSizeT = string.strlen(mod_m) - for i_r in range(rel_count): - si3: int = rel_idx[i_r] - if indegree[si3] == 0: - continue - deps_si3: str = LookupIncludesDeps(store_sha1_arr + t.CSizeT(si3) * 17) - if deps_si3 is None: - continue - dl_si3: t.CSizeT = string.strlen(deps_si3) - dpos3: t.CSizeT = 0 - found_dep: int = 0 - while dpos3 < dl_si3 and found_dep == 0: - dstart3: t.CSizeT = dpos3 - while dpos3 < dl_si3 and deps_si3[dpos3] != ' ': - dpos3 += 1 - dlen3: t.CSizeT = dpos3 - dstart3 - if dpos3 < dl_si3: - dpos3 += 1 - if dlen3 == 0: - continue - if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0: - found_dep = 1 - if found_dep == 1: - indegree[si3] = indegree[si3] - 1 - if indegree[si3] == 0: - queue[q_tail] = si3 - q_tail += 1 - # 有环时剩余条目按 store 顺序补齐(避免漏编译) - for i_r in range(rel_count): - si4: int = rel_idx[i_r] - if indegree[si4] > 0: - sorted_idx[out_count] = si4 - out_count += 1 - stdlib.free(indegree) - stdlib.free(queue) - stdlib.free(rel_idx) - return out_count - - -def GetSha1StoreArrPtr() -> bytes | t.CPtr: - """返回 SHA1 数组指针(每个条目 17 字节)""" - return _sha1_store_arr - - -def GetSha1StoreModArrPtr() -> bytes | t.CPtr: - """返回模块名数组指针(每个条目 64 字节)""" - return _sha1_store_mod - - -def GetSha1StoreRelArrPtr() -> bytes | t.CPtr: - """返回相对路径数组指针(每个条目 MAX_REL_PATH_LEN 字节)""" - return _sha1_store_rel - - -def GetSha1StoreIncdirArrPtr() -> bytes | t.CPtr: - """返回所属 includes 根目录数组指针(每个条目 MAX_INC_DIR_LEN 字节,空串表示不属于任何 includes 目录)""" - return _sha1_store_incdir - - -# ============================================================ -# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用) -# -# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。 -# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*MAX_REL_PATH_LEN] -# 内存归全局存储器所有,调用者不要 free -# ============================================================ -def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int: - """获取全局存储器数组指针 - - out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针, - 函数将存储器数组指针写入这些位置。 - 返回条目数,-1 失败 - """ - if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: - return -1 - if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None: - return -1 - sp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_sha1_arr, t.CPtr)) - if sp is not None: - sp[0] = _sha1_store_arr - mp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_mod_arr, t.CPtr)) - if mp is not None: - mp[0] = _sha1_store_mod - rp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_rel_arr, t.CPtr)) - if rp is not None: - rp[0] = _sha1_store_rel - return _sha1_store_count - - -# ============================================================ -# LookupSha1RelPath - 通过 SHA1 查找相对路径 -# -# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free)。 -# 用于错误提示:将 sha1 转换为人类可读的文件路径。 -# -# Args: -# sha1: SHA1 字符串(16 字符) -# -# Returns: -# 相对路径字符串指针(只读),None=未找到 -# ============================================================ -def LookupSha1RelPath(sha1: str) -> str: - """通过 SHA1 查找相对路径,返回只读指针,None=未找到""" - if sha1 is None: - return None - if _sha1_store_arr is None or _sha1_store_rel is None: - return None - for i in range(_sha1_store_count): - sidx: t.CSizeT = t.CSizeT(i) * 17 - if string.strcmp(_sha1_store_arr + sidx, sha1) == 0: - ridx: t.CSizeT = t.CSizeT(i) * MAX_REL_PATH_LEN - return _sha1_store_rel + ridx - return None - - -# ============================================================ -# IsDeclOnlyModule - 判断模块是否为纯声明文件 -# -# 读取 {temp_dir}/{sha1}.text.ll,扫描是否含 "define " 行: -# 返回 1 = 纯声明(无函数体,如 stdio.vp 全 pass),无需 .obj -# 返回 0 = 实现文件(含 define 函数体) -# 返回 -1 = text.ll 不存在/无法判断 -# -# 与 Phase B+(Phase2.vp 编译缺失 includes 时)的内联判断逻辑一致, -# 供 Phase C 链接时区分"声明文件无需 .obj"与"实现文件缺失"。 -# ============================================================ -def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int: - """判断模块是否为纯声明文件(text.ll 无 define 行)""" - if temp_dir is None or sha1 is None: - return -1 - tpath: str = _sliced_path(temp_dir, td_len, sha1, "text.ll") - if tpath is None: - return -1 - tf_d: fileio.File | t.CPtr = fileio.File(tpath, fileio.MODE.R) - stdlib.free(tpath) - if tf_d.closed: - return -1 - tbuf_d: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) - tbr_d: t.CInt64T = 0 - if tbuf_d is not None: - tbr_d = tf_d.read_all(tbuf_d, STUB_READ_BUF_SIZE) - tf_d.close() - if tbuf_d is None: - return -1 - if tbr_d <= 0: - # 空 text.ll:无函数体,纯声明 - stdlib.free(tbuf_d) - return 1 - if tbr_d < STUB_READ_BUF_SIZE: - tbuf_d[tbr_d] = '\0' - else: - tbuf_d[STUB_READ_BUF_SIZE - 1] = '\0' - # 逐行扫描:找 "define " 行即为实现文件 - tpos_d: t.CSizeT = 0 - while tpos_d < tbr_d: - tls_d: t.CSizeT = tpos_d - while tpos_d < tbr_d: - if tbuf_d[tpos_d] == '\n': - break - tpos_d += 1 - tll_d: t.CSizeT = tpos_d - tls_d - if tpos_d < tbr_d: - tpos_d += 1 - if tll_d >= 7 and string.strncmp(tbuf_d + tls_d, "define ", 7) == 0: - stdlib.free(tbuf_d) - return 0 - stdlib.free(tbuf_d) - return 1 - - -# ============================================================ -# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define -# -# 逐字节strncmp搜索 func_sig(如 @"sha1.func"(/ @printf( ),每次 -# 找到匹配后检查行首是否以 "declare " 或 "define " 开头,跳过 call 语句。 -# 避免 strstr 子串匹配误匹配 call ... @"func"(...) 中的 @"func"( 子串。 -# -# Args: -# out_buf: 已有的 IR 文本缓冲区 -# func_sig: 函数签名片段,如 @"sha1.func"(/ @printf( -# -# Returns: -# 1=已存在 declare/define, 0=不存在 -# ============================================================ -def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int: - """检查 out_buf 中是否已有函数的 declare 或 define 行""" - if out_buf is None or func_sig is None: - return 0 - sig_len: t.CSizeT = string.strlen(func_sig) - out_len: t.CSizeT = string.strlen(out_buf) - if sig_len == 0 or out_len == 0 or sig_len > out_len: - return 0 - pos: t.CSizeT = 0 - while pos + sig_len <= out_len: - if string.strncmp(out_buf + pos, func_sig, sig_len) == 0: - ls: t.CSizeT = pos - while ls > 0 and out_buf[ls - 1] != '\n': - ls -= 1 - if string.strncmp(out_buf + ls, "declare ", 8) == 0: - return 1 - if string.strncmp(out_buf + ls, "define ", 7) == 0: - return 1 - pos += 1 - return 0 - - -# ============================================================ -# _CommentOutDeclareInBuf - 将 out_buf 中匹配 func_sig 的 declare 行注释掉 -# -# 用于解决 declare/define 冲突:当本地 text.ll 包含 define 而依赖 -# text.ll 已生成了同函数的 declare 时,将 declare 行首字符改为 ';' -# 使其成为注释,然后追加 define 行。 -# ============================================================ -def _CommentOutDeclareInBuf(out_buf: bytes, func_sig: str) -> int: - """在 out_buf 中搜索 declare 行(包含 func_sig),将其注释掉""" - if out_buf is None or func_sig is None: - return 0 - sig_len: t.CSizeT = string.strlen(func_sig) - out_len: t.CSizeT = string.strlen(out_buf) - if sig_len == 0 or out_len == 0 or sig_len > out_len: - return 0 - pos: t.CSizeT = 0 - while pos + sig_len <= out_len: - if string.strncmp(out_buf + pos, func_sig, sig_len) == 0: - ls: t.CSizeT = pos - while ls > 0 and out_buf[ls - 1] != '\n': - ls -= 1 - if string.strncmp(out_buf + ls, "declare ", 8) == 0: - out_buf[ls] = ';' - return 1 - pos += 1 - return 0 - - -# ============================================================ -# _CommentOutGlobalInBuf - 将 out_buf 中匹配全局变量名的行注释掉 -# -# 解决 stub/text 全局变量重复定义问题:stub 中可能是 external 声明 -# 或完整定义,以 text 中的定义为准,将 stub 中的同名行注释掉。 -# ============================================================ -def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int: - """在 out_buf 中搜索全局变量定义行(以 global_name 开头),将行首改为 ';' - - 注释掉**所有**匹配行(而非仅第一个):跨模块同名裸全局(如 main.vp 与 - argparse.vp 都声明 _mbuddy)时,本地 stub external + 依赖 stub external + - 依赖 text 定义会同时存在。只注释第一个会导致依赖 text 的定义残留 → - 合成 IR 重复定义 → llc "redefinition of global '@_mbuddy'"(ArgparseTest - 回归根因)。"以 text 中定义为准"的语义:后追加者注释掉全部旧行,最终 - 只保留最后一个定义。 - """ - if out_buf is None or global_name is None: - return 0 - name_len: t.CSizeT = string.strlen(global_name) - out_len: t.CSizeT = string.strlen(out_buf) - if name_len == 0 or out_len == 0 or name_len > out_len: - return 0 - pos: t.CSizeT = 0 - commented: int = 0 - while pos + name_len <= out_len: - if string.strncmp(out_buf + pos, global_name, name_len) == 0: - # 确认是行首(前一个是换行或 pos==0) - if pos == 0 or out_buf[pos - 1] == '\n': - out_buf[pos] = ';' - commented = 1 - pos += 1 - return commented - - -def _HasGlobalInBuf(out_buf: bytes, global_name: str) -> int: - """检测 out_buf 中是否已有同名全局变量行(行首匹配,未注释) - - 用于跳过重复追加依赖全局强定义:stub 注入已提供 external 声明, - 依赖 text.ll 的强定义只需在定义模块存在一份,复制进每个依赖方 - 会导致 ld.lld duplicate symbol(如 _op_head、MemBuddy.vtable)。 - """ - if out_buf is None or global_name is None: - return 0 - name_len: t.CSizeT = string.strlen(global_name) - out_len: t.CSizeT = string.strlen(out_buf) - if name_len == 0 or out_len == 0 or name_len > out_len: - return 0 - pos: t.CSizeT = 0 - while pos + name_len <= out_len: - if string.strncmp(out_buf + pos, global_name, name_len) == 0: - # 确认是行首(前一个是换行或 pos==0) - if pos == 0 or out_buf[pos - 1] == '\n': - return 1 - pos += 1 - return 0 - - -# ============================================================ -# _CommentOutOpaqueTypeInBuf - 将 out_buf 中匹配类型名的 opaque 声明行注释掉 -# -# 解决跨模块类型定义冲突:本地 stub 可能包含自动生成的 opaque 声明 -#(如 %"sha1.Widget" = type opaque),而依赖 stub 包含完整定义 -#(如 %"sha1.Widget" = type { i8*, i32 })。去重时需要将 opaque 声明 -# 注释掉,让完整定义被追加,否则 LLC 报错 "Cannot allocate unsized type"。 -# ============================================================ -def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int: - """在 out_buf 中搜索 type_name_prefix 对应的 opaque 声明行,将行首改为 ';' - - type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=' 和后续内容) - 返回 1=已注释, 0=未找到 - """ - if out_buf is None or type_name_prefix is None: - return 0 - prefix_len: t.CSizeT = string.strlen(type_name_prefix) - out_len: t.CSizeT = string.strlen(out_buf) - if prefix_len == 0 or out_len == 0 or prefix_len > out_len: - return 0 - pos: t.CSizeT = 0 - while pos + prefix_len <= out_len: - if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0: - # 确认是行首(前一个是换行或 pos==0) - if pos == 0 or out_buf[pos - 1] == '\n': - # 检查后面是否是 " = type opaque" - p: t.CSizeT = pos + prefix_len - # 跳过空格 - while p < out_len and out_buf[p] == ' ': - p += 1 - if p < out_len and out_buf[p] == '=': - p += 1 - while p < out_len and out_buf[p] == ' ': - p += 1 - if p + 11 <= out_len: - if string.strncmp(out_buf + p, "type opaque", 11) == 0: - # 找到 opaque 声明,将行首改为 ';' - out_buf[pos] = ';' - return 1 - pos += 1 - return 0 - - -# ============================================================ -# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块 -# -# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp -# 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer 子模块类型。 -# 当模块名包含 '.' 时,检查其顶层包名是否在 deps_buf 中。 -# -# 例如: -# deps_buf = "ast stdio string" -# mod_name = "ast.lexer" → 顶层包 "ast" 在 deps_buf 中 → 返回 1 -# mod_name = "ast.token" → 顶层包 "ast" 在 deps_buf 中 → 返回 1 -# mod_name = "foo.bar" → 顶层包 "foo" 不在 deps_buf 中 → 返回 0 -# ============================================================ -def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int: - """检查 mod_name 是否是 deps_buf 中已导入包的子模块""" - if deps_buf is None or mod_name is None: - return 0 - # 查找第一个 '.',提取顶层包名 - dot_pos: str = string.strchr(mod_name, 46) # '.' = 46 - if dot_pos is None: - return 0 - pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(mod_name)) - if pkg_len == 0 or pkg_len >= 256: - return 0 - # 构造顶层包名(截断到 '.' 之前) - # 直接在 mod_name 上临时截断检查,避免分配 - saved_ch: t.CChar = mod_name[pkg_len] - mod_name[pkg_len] = '\0' - result: int = HandlesImports.is_module_imported(deps_buf, mod_name) - mod_name[pkg_len] = saved_ch - return result - - -# ============================================================ -# _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque) -# -# type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=') -# 完整定义: %"name" = type { ... } 或 %"name" = type < { ... } > -# 返回 1=已有完整定义, 0=无完整定义 -# ============================================================ -def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int: - """检查 out_buf 中是否已有 type_name_prefix 的完整定义""" - if out_buf is None or type_name_prefix is None: - return 0 - prefix_len: t.CSizeT = string.strlen(type_name_prefix) - out_len: t.CSizeT = string.strlen(out_buf) - if prefix_len == 0 or out_len == 0 or prefix_len > out_len: - return 0 - pos: t.CSizeT = 0 - while pos + prefix_len <= out_len: - if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0: - # 确认是行首(前一个是换行或 pos==0),且未被注释(行首不是 ';') - is_line_start: int = 0 - if pos == 0: - is_line_start = 1 - elif out_buf[pos - 1] == '\n': - is_line_start = 1 - if is_line_start == 1: - # 检查后面是否是 " = type {" 或 " = type <" - p: t.CSizeT = pos + prefix_len - while p < out_len and out_buf[p] == ' ': - p += 1 - if p < out_len and out_buf[p] == '=': - p += 1 - while p < out_len and out_buf[p] == ' ': - p += 1 - if p + 5 < out_len: - if string.strncmp(out_buf + p, "type ", 5) == 0: - p += 5 - if p < out_len: - if out_buf[p] == '{' or out_buf[p] == '<': - return 1 - pos += 1 - return 0 - - -# ============================================================ -# _LoadAndAppendStub - 加载依赖 stub 并追加到 out_buf -# -# 读取 {temp_dir}/{sha1}.stub.ll,跳过 header(;, target, source_filename), -# 去重 declare/global,追加到 out_buf。返回新 out_pos。 -# ============================================================ -def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, - dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT, - out_pos: t.CSizeT) -> t.CSizeT: - """加载依赖 stub 并追加(跳过 header,去重)""" - if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None: - return out_pos - - dep_path: str = _sliced_path(temp_dir, td_len, dep_sha1, "stub.ll") - if dep_path is None: - return out_pos - df_ls: fileio.File | t.CPtr = fileio.File(dep_path, fileio.MODE.R) - if df_ls.closed: - stdlib.free(dep_path) - # 依赖 stub 缺失:includes 映射内的模块缺失属编译错误(fail-fast), - # 非映射模块(如 t/c 内部库、用户文件)按设计跳过 - if LookupSha1RelPath(dep_sha1) is not None: - fb_ls: str = VLogger.fmt_buf() - if fb_ls is not None: - viperlib.snprintf(fb_ls, 1024, "依赖 stub 缺失: %s", dep_sha1) - VLogger.error(fb_ls, "StubMerger") - return out_pos - dep_br_ls: t.CInt64T = df_ls.read_all(dep_buf, STUB_READ_BUF_SIZE) - df_ls.close() - stdlib.free(dep_path) - if dep_br_ls <= 0: - # 依赖 stub 为空:纯声明/纯常量模块(如 stdio.vp 全 pass、CDefine 模块) - # 无可声明符号,stub.ll 为空属合法形态,静默跳过 - return out_pos - if dep_br_ls < STUB_READ_BUF_SIZE: - dep_buf[dep_br_ls] = '\0' - else: - dep_buf[STUB_READ_BUF_SIZE - 1] = '\0' - - # 注释掉依赖 stub 中的字符串常量行(模块内部的,不需要) - # 简单方案:搜索 "= external unnamed_addr constant" 并将行首的 @ 改为 ;@ - cfs_pos: t.CSizeT = 0 - while cfs_pos + 30 < t.CSizeT(dep_br_ls): - ext_p: t.CPtr = string.strstr(dep_buf + cfs_pos, "= external unnamed_addr constant") - if ext_p is None: - break - ext_off2: t.CSizeT = t.CSizeT(t.CUInt64T(ext_p) - t.CUInt64T(dep_buf)) - # 向前查找行首的 @ - ls: t.CSizeT = ext_off2 - while ls > 0: - if dep_buf[ls] == '\n': - ls += 1 - break - ls -= 1 - if ls < ext_off2 and dep_buf[ls] == '@': - dep_buf[ls] = ';' - cfs_pos = ext_off2 + 30 - - # 跳过 header,追加内容(去重 declare/global) - dep_pos: t.CSizeT = 0 - skipping: int = 1 - while dep_pos < dep_br_ls: - line_start: t.CSizeT = dep_pos - while dep_pos < dep_br_ls: - if dep_buf[dep_pos] == '\n': - break - dep_pos += 1 - line_len: t.CSizeT = dep_pos - line_start - if dep_pos < dep_br_ls: - dep_pos += 1 - if skipping != 0: - if line_len == 0: - continue - ch0: int = dep_buf[line_start] - if ch0 == ';': - continue - if ch0 == 't' and line_len >= 6: - if string.strncmp(dep_buf + line_start, "target", 6) == 0: - continue - if ch0 == 's' and line_len >= 15: - if string.strncmp(dep_buf + line_start, "source_filename", 15) == 0: - continue - skipping = 0 - # 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句) - if line_len >= 8 and string.strncmp(dep_buf + line_start, "declare ", 8) == 0: - at: t.CSizeT = line_start + 8 - while at < line_start + line_len: - if dep_buf[at] == '@': - break - at += 1 - if at < line_start + line_len: - lp: t.CSizeT = at + 1 - while lp < line_start + line_len: - if dep_buf[lp] == '(': - break - lp += 1 - if lp < line_start + line_len: - saved: t.CChar = dep_buf[lp + 1] - dep_buf[lp + 1] = '\0' - if _IsFuncDeclaredOrDefined(out_buf, dep_buf + at) != 0: - dep_buf[lp + 1] = saved - continue - dep_buf[lp + 1] = saved - # 去重:external global - if line_len > 0 and dep_buf[line_start] == '@': - eq: t.CSizeT = line_start + 1 - while eq < line_start + line_len: - if dep_buf[eq] == '=': - break - eq += 1 - if eq < line_start + line_len: - saved: t.CChar = dep_buf[eq + 1] - dep_buf[eq + 1] = '\0' - if string.strstr(out_buf, dep_buf + line_start) is not None: - dep_buf[eq + 1] = saved - continue - dep_buf[eq + 1] = saved - # 去重:type 定义(如 %"sha1.Point" = type {i32, i32}) - # 区分完整定义(= type { ... } / = type < ... >)和 opaque 声明(= type opaque) - # 当本地 stub 有 opaque 声明而依赖 stub 有完整定义时,注释掉 opaque 行, - # 让完整定义被追加,避免 LLC "Cannot allocate unsized type" 错误。 - if line_len > 0 and dep_buf[line_start] == '%': - eq2: t.CSizeT = line_start + 1 - while eq2 < line_start + line_len: - if dep_buf[eq2] == '=': - break - eq2 += 1 - if eq2 < line_start + line_len: - saved2: t.CChar = dep_buf[eq2 + 1] - dep_buf[eq2 + 1] = '\0' - # 检查依赖行是否为完整定义(= type { 或 = type <) - dep_is_full_def: int = 0 - if eq2 + 8 <= line_start + line_len: - if dep_buf[eq2 + 2] == 't' and dep_buf[eq2 + 3] == 'y' and dep_buf[eq2 + 4] == 'p' and dep_buf[eq2 + 5] == 'e' and dep_buf[eq2 + 6] == ' ': - if dep_buf[eq2 + 7] == '{' or dep_buf[eq2 + 7] == '<': - dep_is_full_def = 1 - if dep_is_full_def == 1: - # 依赖行是完整定义 - # 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀 - saved_eq: t.CChar = dep_buf[eq2] - dep_buf[eq2] = '\0' - # 1. 注释掉 out_buf 中的 opaque 声明(如果有) - commented: int = _CommentOutOpaqueTypeInBuf(out_buf, dep_buf + line_start) - # 2. 检查 out_buf 中是否已有完整定义(避免重复) - has_full: int = _HasFullTypeDefinition(out_buf, dep_buf + line_start) - dep_buf[eq2] = saved_eq - if has_full == 1: - dep_buf[eq2 + 1] = saved2 - continue - # 3. 追加完整定义 - else: - # 依赖行是 opaque 声明,用原有子串匹配去重 - found_opaque: t.CPtr = string.strstr(out_buf, dep_buf + line_start) - if found_opaque is not None: - dep_buf[eq2 + 1] = saved2 - continue - dep_buf[eq2 + 1] = saved2 - # 追加 - if out_pos + line_len + 2 < out_size: - string.strncpy(out_buf + out_pos, dep_buf + line_start, line_len) - out_pos += line_len - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - return out_pos - - -# ============================================================ -# _LoadAndAppendTextDeclares - 从依赖 text.ll 提取 declare 追加到 out_buf -# -# 读取 {temp_dir}/{sha1}.text.ll,将 define 行转为 declare(提取函数签名), -# 跳过函数体,去重后追加到 out_buf。返回新 out_pos。 -# ============================================================ -def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, - dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT, - out_pos: t.CSizeT) -> t.CSizeT: - """从依赖 text.ll 提取 declare(define 转 declare),追加到 out_buf""" - if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None: - return out_pos - - text_path: str = _sliced_path(temp_dir, td_len, dep_sha1, "text.ll") - if text_path is None: - return out_pos - tf_lt: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R) - if tf_lt.closed: - stdlib.free(text_path) - # 依赖 text 缺失:includes 映射内的模块缺失属编译错误(fail-fast), - # 非映射模块(如 t/c 内部库、用户文件)按设计跳过 - if LookupSha1RelPath(dep_sha1) is not None: - fb_lt: str = VLogger.fmt_buf() - if fb_lt is not None: - viperlib.snprintf(fb_lt, 1024, "依赖 text 缺失: %s", dep_sha1) - VLogger.error(fb_lt, "StubMerger") - return out_pos - text_br: t.CInt64T = tf_lt.read_all(dep_buf, STUB_READ_BUF_SIZE) - tf_lt.close() - stdlib.free(text_path) - if text_br <= 0: - # 依赖 text 为空:纯 stub 模块(如 stdio.vp 全 pass 声明)无函数体, - # text.ll 为空属合法形态,无可提取 declare,静默跳过 - return out_pos - if text_br < STUB_READ_BUF_SIZE: - dep_buf[text_br] = '\0' - else: - dep_buf[STUB_READ_BUF_SIZE - 1] = '\0' - - dep_pos_lt: t.CSizeT = 0 - in_body: int = 0 - while dep_pos_lt < text_br: - ls: t.CSizeT = dep_pos_lt - while dep_pos_lt < text_br: - if dep_buf[dep_pos_lt] == '\n': - break - dep_pos_lt += 1 - ll: t.CSizeT = dep_pos_lt - ls - if dep_pos_lt < text_br: - dep_pos_lt += 1 - - # 函数体跳过:直到单独的 } 行 - if in_body != 0: - if ll == 1 and dep_buf[ls] == '}': - in_body = 0 - continue - - # 跳过空行和 header - if ll == 0: - continue - c0: int = dep_buf[ls] - if c0 == ';': - continue - if c0 == 't' and ll >= 6 and string.strncmp(dep_buf + ls, "target", 6) == 0: - continue - if c0 == 's' and ll >= 15 and string.strncmp(dep_buf + ls, "source_filename", 15) == 0: - continue - - # 处理 define 行:转为 declare - if ll >= 7 and string.strncmp(dep_buf + ls, "define ", 7) == 0: - # 提取 @funcname( 用于去重 - at_lt: t.CSizeT = ls + 7 - while at_lt < ls + ll: - if dep_buf[at_lt] == '@': - break - at_lt += 1 - if at_lt >= ls + ll: - in_body = 1 - continue - lp_lt: t.CSizeT = at_lt + 1 - while lp_lt < ls + ll: - if dep_buf[lp_lt] == '(': - break - lp_lt += 1 - if lp_lt >= ls + ll: - in_body = 1 - continue - # 去重检查:用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句 - saved_lt: t.CChar = dep_buf[lp_lt + 1] - dep_buf[lp_lt + 1] = '\0' - if _IsFuncDeclaredOrDefined(out_buf, dep_buf + at_lt) != 0: - dep_buf[lp_lt + 1] = saved_lt - in_body = 1 - continue - dep_buf[lp_lt + 1] = saved_lt - - # 找行尾的 ' {' 并截断 - decl_end: t.CSizeT = ls + ll - if decl_end > ls + 1: - if dep_buf[decl_end - 1] == '{' and dep_buf[decl_end - 2] == ' ': - decl_end -= 2 - - # 输出: declare + content (skip "define " 7 chars, up to decl_end) - # 泛型特化方法 define 是 "define linkonce_odr ..."(FunctionPrint - # 对名含 '[' 的函数输出 linkonce_odr):若只跳过 "define " 会拼出 - # "declare linkonce_odr ..." → llc 报 "invalid linkage for function - # declaration"(LLVM 只允许 define 用 linkonce_odr,declare 必须 - # external)。对齐 TPC Phase2Translator 的 decl_line 剥 linkage 逻辑, - # 循环跳过所有 linkage 词。 - c_start_lt: t.CSizeT = ls + 7 - skipped_lt: int = 1 - while skipped_lt != 0: - skipped_lt = 0 - if c_start_lt + 13 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "linkonce_odr ", 13) == 0: - c_start_lt += 13 - skipped_lt = 1 - elif c_start_lt + 10 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "weak_odr ", 9) == 0: - c_start_lt += 9 - skipped_lt = 1 - elif c_start_lt + 10 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "linkonce ", 9) == 0: - c_start_lt += 9 - skipped_lt = 1 - elif c_start_lt + 6 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "weak ", 5) == 0: - c_start_lt += 5 - skipped_lt = 1 - elif c_start_lt + 10 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "internal ", 9) == 0: - c_start_lt += 9 - skipped_lt = 1 - elif c_start_lt + 9 <= decl_end and \ - string.strncmp(dep_buf + c_start_lt, "private ", 8) == 0: - c_start_lt += 8 - skipped_lt = 1 - copy_len_lt: t.CSizeT = decl_end - c_start_lt - if out_pos + 8 + copy_len_lt + 2 < out_size: - string.strncpy(out_buf + out_pos, "declare ", 8) - out_pos += 8 - string.strncpy(out_buf + out_pos, dep_buf + c_start_lt, copy_len_lt) - out_pos += copy_len_lt - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - in_body = 1 - continue - - # 提取类型定义(%... = type { ... } 或 %... = type < ... >) - # stub.ll 中只有 opaque 声明,需要从 text.ll 中提取完整定义 - # 用于 alloca 和 getelementptr 等需要完整类型的指令 - if ll > 0 and dep_buf[ls] == '%': - eq_td: t.CSizeT = ls + 1 - while eq_td < ls + ll: - if dep_buf[eq_td] == '=': - break - eq_td += 1 - if eq_td < ls + ll: - # 检查是否为完整定义(= type { 或 = type <) - dep_is_full_td: int = 0 - if eq_td + 8 <= ls + ll: - if dep_buf[eq_td + 2] == 't' and dep_buf[eq_td + 3] == 'y' \ - and dep_buf[eq_td + 4] == 'p' and dep_buf[eq_td + 5] == 'e' \ - and dep_buf[eq_td + 6] == ' ': - if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<': - dep_is_full_td = 1 - if dep_is_full_td == 1: - # 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀 - saved_eq_td: t.CChar = dep_buf[eq_td] - dep_buf[eq_td] = '\0' - # 1. 注释掉 out_buf 中的 opaque 声明(如果有) - _CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls) - # 2. 检查 out_buf 中是否已有完整定义(避免重复) - has_full_td: int = _HasFullTypeDefinition(out_buf, dep_buf + ls) - dep_buf[eq_td] = saved_eq_td - if has_full_td == 0: - # 追加完整类型定义 - if out_pos + ll + 2 < out_size: - string.strncpy(out_buf + out_pos, dep_buf + ls, ll) - out_pos += ll - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - continue - - # 提取全局变量定义(@... = ...) - # 依赖 text.ll 中的全局变量带初值定义(如 @_mbuddy = global ...) - # stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义 - # 注释掉 out_buf 中的同名行(external 声明或旧定义),追加 text.ll 中的完整定义 - if ll > 0 and dep_buf[ls] == '@': - eq_gv: t.CSizeT = ls + 1 - while eq_gv < ls + ll: - if dep_buf[eq_gv] == '=': - break - eq_gv += 1 - if eq_gv < ls + ll: - # 截断到 '=' 之前(不含 '='),得到全局变量名 @name - saved_gv: t.CChar = dep_buf[eq_gv] - dep_buf[eq_gv] = '\0' - # 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义) - # 时跳过:跨模块全局(如 _op_head、MemBuddy.vtable)只需一份 - # 强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致 - # ld.lld duplicate symbol(NewAstTest _op_head/MemBuddy.vtable 根因)。 - # 仅当依赖方完全没有该全局(既无 external 也无定义)时才需要补充。 - if _HasGlobalInBuf(out_buf, dep_buf + ls) == 0: - # 注释掉 out_buf 中的同名行(external 声明或旧定义) - _CommentOutGlobalInBuf(out_buf, dep_buf + ls) - dep_buf[eq_gv] = saved_gv - # 追加完整定义 - if out_pos + ll + 2 < out_size: - string.strncpy(out_buf + out_pos, dep_buf + ls, ll) - out_pos += ll - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - else: - dep_buf[eq_gv] = saved_gv - continue - - # 跳过其他行 - return out_pos - - -# ============================================================ -# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射 -# -# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。 -# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。 -# 所有内存由 stdlib.malloc 分配,调用者负责释放。 -# ============================================================ -def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT, - sha1_list: str, - mod_list: str) -> int: - """从全局内存存储器复制 sha1→module_name 映射到调用者数组 - - temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。 - 数据来源是 PopulateSha1MapStore 填充的全局存储器。 - """ - if sha1_list is None or mod_list is None: - return 0 - if _sha1_store_arr is None or _sha1_store_mod is None: - return 0 - count: int = 0 - for i in range(_sha1_store_count): - if count >= MAX_INCLUDES: - break - idx: t.CSizeT = t.CSizeT(i) * 17 - idx2: t.CSizeT = t.CSizeT(i) * 64 - string.strcpy(sha1_list + t.CSizeT(count) * 17, _sha1_store_arr + idx) - string.strcpy(mod_list + t.CSizeT(count) * 64, _sha1_store_mod + idx2) - count += 1 - return count - - -# ============================================================ -# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配) -# -# 在 sha1_arr/mod_arr 映射中查找模块名对应的 SHA1。 -# 用于依赖图分析:模块名 → SHA1。 -# -# Args: -# sha1_arr: SHA1 数组(每个 17 字节) -# mod_arr: 模块名数组(每个 64 字节) -# count: 映射条目数 -# mod_name: 要查找的模块名 -# -# Returns: -# SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None -# ============================================================ -def _FindSha1ByModName(sha1_arr: str, mod_arr: str, - count: int, mod_name: str) -> str: - """从模块名查找 SHA1(精确匹配 + 包名回退) - - 精确匹配失败时,尝试 "{mod_name}.__init__" 回退, - 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。 - """ - if sha1_arr is None or mod_arr is None or mod_name is None: - return None - if count <= 0: - return None - # 第一遍:精确匹配 - for i in range(count): - idx: t.CSizeT = t.CSizeT(i) * 64 - if string.strcmp(mod_arr + idx, mod_name) == 0: - sidx: t.CSizeT = t.CSizeT(i) * 17 - return sha1_arr + sidx - # 第二遍:包名回退("ast" → "ast.__init__") - nm_len: t.CSizeT = string.strlen(mod_name) - init_buf: bytes = stdlib.malloc(nm_len + 12) - if init_buf is not None: - viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name) - for i in range(count): - idx2: t.CSizeT = t.CSizeT(i) * 64 - if string.strcmp(mod_arr + idx2, init_buf) == 0: - sidx2: t.CSizeT = t.CSizeT(i) * 17 - stdlib.free(init_buf) - return sha1_arr + sidx2 - stdlib.free(init_buf) - return None - - -# ============================================================ -# _AddSha1ToSet - 将 SHA1 加入集合(去重) -# -# 检查 SHA1 是否已在集合中,若不在则加入。返回新的 count。 -# -# Args: -# set_buf: SHA1 集合缓冲区(每个 17 字节) -# count: 当前集合中的 SHA1 数量 -# sha1: 要加入的 SHA1 -# -# Returns: -# 新的 count(如果已存在则不变) -# ============================================================ -def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int: - """将 SHA1 加入集合,返回新 count""" - if set_buf is None or sha1 is None: - return count - for i in range(count): - idx: t.CSizeT = t.CSizeT(i) * 17 - if string.strcmp(set_buf + idx, sha1) == 0: - return count - if count < MAX_INCLUDES_SHA1: - idx: t.CSizeT = t.CSizeT(count) * 17 - string.strcpy(set_buf + idx, sha1) - return count + 1 - return count - - -# ============================================================ -# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需翻译) -# -# 从 source_dir 的源文件开始,解析 import 语句,递归收集可达的 -# includes 文件 SHA1。Phase 1b 只翻译可达集合中的文件,避免 -# 翻译不需要的 includes(如 Test 不依赖 llvmlite,则不翻译)。 -# -# 算法(工作列表): -# 1. 扫描 source_dir 下的 .vp/.py 文件,解析 AST 获取 _imported_modules -# 2. 构建 SHA1→module_name 映射(从全局内存存储器) -# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名 -# -# Args: -# mb: 内存池 -# source_dir: 源文件目录(Config.SourceDir) -# temp_dir: 临时目录(.deps.txt 所在位置;_sha1_map.txt 仅人类可读输出,不读取) -# reachable_set: 输出参数,可达 SHA1 集合缓冲区(MAX_INCLUDES_SHA1 * 17 字节) -# -# Returns: -# 可达 SHA1 数量(>0 成功,<=0 失败) -# ============================================================ -def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, - temp_dir: str, - reachable_set: str, - app_deps: str = None) -> int: - """构建可达 SHA1 集合(依赖图按需翻译) - - app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤 - """ - if source_dir is None or temp_dir is None or reachable_set is None: - return -1 - - SRC_BUF_SIZE_R: t.CSizeT = 1048576 - - # 1. 构建 SHA1→module_name 映射 - td_len_r: t.CSizeT = string.strlen(temp_dir) - sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17) - mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64) - if sha1_arr is None or mod_arr is None: - if sha1_arr is not None: - stdlib.free(sha1_arr) - if mod_arr is not None: - stdlib.free(mod_arr) - return -1 - map_count: int = _BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr) - if map_count <= 0: - VLogger.error("无法构建 SHA1 映射", "Reachable") - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - return -1 - fb: str = VLogger.fmt_buf() - if fb is not None: - viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count) - VLogger.debug(fb, "Reachable") - - # 工作列表(空格分隔的模块名) - worklist: bytes = stdlib.malloc(REACH_WORKLIST_BUF) - if worklist is None: - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - return -1 - worklist[0] = '\0' - wl_len: t.CSizeT = 0 - - reachable_count: int = 0 - - # 2. 如果 app_deps 非空,直接使用它作为初始 worklist(跳过自己的非递归扫描) - # 否则,回退到自己的非递归扫描(source_dir/*.py,只扫描顶层) - if app_deps is not None and app_deps[0] != '\0': - ad_len: t.CSizeT = string.strlen(app_deps) - if ad_len > 0 and ad_len < REACH_WORKLIST_BUF: - string.strcpy(worklist, app_deps) - wl_len = ad_len - else: - VLogger.error("app_deps 过长,无法构建可达集合", "StubMerger") - else: - # app_deps 缺失:Phase1 场景(未翻译 App 源文件)为设计性回退, - # 显式 warning 暴露回退,不再静默。 - # 原实现仅非递归扫描 source_dir 顶层("%s/*"),漏掉 lib/ 等子目录的 - # App 文件 → 其依赖(如 lib/Projectrans/Config.vp 的 json)不进入 - # worklist → json 不可达 → Phase1 不 stub 不翻译 → Phase2 Phase A - # 翻译 Config.vp 时 root.is_object() 跨模块调用无 stub 可查 → 静默 - # translate_value 返回 None → "if 条件表达式翻译失败"(追加82 根因)。 - # 改为递归扫描(复用 IncludesScanner.scan_includes),覆盖全部子目录。 - VLogger.warning("app_deps 缺失,递归扫描 source_dir", "Reachable") - scan_res_r: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, source_dir) - if scan_res_r is not None: - es_r: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() - for e_i_r in range(scan_res_r.Count): - e_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)( - t.CVoid(t.CUInt64T(scan_res_r.Entries) + t.CUInt64T(e_i_r) * es_r, t.CPtr)) - if e_r is None or e_r.Path is None: - continue - sbuf: bytes = Utils.read_file_to_buf(e_r.Path, SRC_BUF_SIZE_R) - if sbuf is None: - continue - # 解析 AST 获取 _imported_modules(与原顶层扫描一致) - lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) - if lx is not None: - ast._lexer_init(lx, sbuf, mb) - tokens: ast.Token | t.CPtr = ast.tokenize(lx) - tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) - if tree is not None: - tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() - if tr is not None: - tr._declare_only = 2 - # 源文件包名:用 RelPath(相对 source_dir,支持子目录), - # 保证 from .xxx import 相对导入解析正确 - tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, e_r.RelPath) - HandlesStruct.reset_visible_structs(mb, 0) - tr.translate(tree) - # 容器使用检测:App 源文件用 list/dict(裸名注解/构造/[] - # /{} 字面量)时注入 _list/_dict 依赖(与原实现一致) - tr._inject_auto_container_visibility(mb, tree) - # 获取 _imported_modules - if tr._imported_modules is not None: - im_len: t.CSizeT = string.strlen(tr._imported_modules) - if im_len > 0 and wl_len + im_len + 1 < REACH_WORKLIST_BUF: - string.strcpy(worklist + wl_len, tr._imported_modules) - wl_len += im_len - worklist[wl_len] = ' ' - wl_len += 1 - worklist[wl_len] = '\0' - # 计算 App 源文件 SHA1,填充依赖到内存存储器 - # worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖 - if e_r.Sha1 is not None: - PopulateIncludesDeps(e_r.Sha1, tr._imported_modules) - # 释放 Translator 资源 - if tr._global_names is not None: - stdlib.free(tr._global_names) - if tr._nonlocal_names is not None: - stdlib.free(tr._nonlocal_names) - stdlib.free(sbuf) - - # 去掉末尾多余空格 - if wl_len > 0 and worklist[wl_len - 1] == ' ': - worklist[wl_len - 1] = '\0' - wl_len -= 1 - - fb: str = VLogger.fmt_buf() - if fb is not None: - viperlib.snprintf(fb, 1024, "源文件直接依赖: '%s'", worklist) - VLogger.debug(fb, "Reachable") - - # 2.5. 预加入基础模块(builtins) - # - # builtins(内置函数模块:ord/chr 等)始终纳入编译链,调用时自动关联。 - # _list/_dict 不再无条件预加入:Phase A 翻译 App 源文件时 - # _inject_auto_container_visibility 已按需检测 list/dict 使用并注入 - # _imported_modules(含 []/{} 字面量),json 由 _dict 的真实 import 链带入。 - if HandlesImports.is_module_imported(worklist, "builtins") == 0: - bsha1_b: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, "builtins") - if bsha1_b is not None: - if wl_len + 9 < REACH_WORKLIST_BUF: - if wl_len > 0: - worklist[wl_len] = ' ' - wl_len += 1 - string.strcpy(worklist + wl_len, "builtins") - wl_len += 8 - worklist[wl_len] = ' ' - wl_len += 1 - worklist[wl_len] = '\0' - fb: str = VLogger.fmt_buf() - if fb is not None: - viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist) - VLogger.debug(fb, "Reachable") - - # 3. 工作列表算法:递归收集可达 SHA1 - processed: bytes = stdlib.malloc(REACH_WORKLIST_BUF) - if processed is None: - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - stdlib.free(worklist) - return -1 - processed[0] = '\0' - proc_len: t.CSizeT = 0 - - wl_pos: t.CSizeT = 0 - wl_iter: int = 0 - while wl_pos < wl_len: - wl_iter += 1 - # 提取一个模块名(到空格或末尾) - name_start: t.CSizeT = wl_pos - while wl_pos < wl_len and worklist[wl_pos] != ' ': - wl_pos += 1 - name_len: t.CSizeT = wl_pos - name_start - if wl_pos < wl_len: - wl_pos += 1 - if name_len == 0: - continue - - # 复制模块名 - mod_buf: bytes = stdlib.malloc(name_len + 1) - if mod_buf is None: - continue - string.strncpy(mod_buf, worklist + name_start, name_len) - mod_buf[name_len] = '\0' - - # 检查是否已处理 - already: int = 0 - if HandlesImports.is_module_imported(processed, mod_buf) != 0: - already = 1 - if already == 0: - # 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug) - if processed is not None: - if proc_len + name_len + 1 < REACH_WORKLIST_BUF: - string.strcpy(processed + proc_len, mod_buf) - proc_len = proc_len + name_len - processed[proc_len] = ' ' - proc_len = proc_len + 1 - processed[proc_len] = '\0' - else: - VLogger.error("依赖 worklist 溢出", "StubMerger") - - # 查找 SHA1 - found_sha1: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf) - if found_sha1 is not None: - # App 源文件(Sha1Store 中 IncDir 为空,AppendToSha1MapStore 规则) - # 不加入 reachable_set:Phase B 已编译其 .obj,若加入会导致 Phase B+ - # 把 App 文件重复编译进 includes.binary,Phase C 链接时与 Phase B 的 - # App obj 重复符号(SysLibTest ld.lld duplicate symbol: _mbuddy 根因)。 - # 依赖仍继续展开(App 文件是依赖图中间节点,其 includes 依赖必须可达)。 - is_app_r: int = 0 - if _sha1_store_incdir is not None: - for ai_r in range(_sha1_store_count): - asi_r: t.CSizeT = t.CSizeT(ai_r) * 17 - if string.strcmp(_sha1_store_arr + asi_r, found_sha1) == 0: - aii_r: t.CSizeT = t.CSizeT(ai_r) * MAX_INC_DIR_LEN - if _sha1_store_incdir[aii_r] == '\0': - is_app_r = 1 - break - if is_app_r == 0: - reachable_count = _AddSha1ToSet(reachable_set, reachable_count, found_sha1) - - # 从内存查找依赖(不读 deps.txt 过程文件) - # Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存 - deps_str: str = LookupIncludesDeps(found_sha1) - if deps_str is not None: - dl: t.CSizeT = string.strlen(deps_str) - if dl > 0: - if wl_len + dl + 2 < REACH_WORKLIST_BUF: - if wl_len > 0 and worklist[wl_len - 1] != ' ': - worklist[wl_len] = ' ' - wl_len = wl_len + 1 - string.strcpy(worklist + wl_len, deps_str) - wl_len = wl_len + dl - worklist[wl_len] = ' ' - wl_len = wl_len + 1 - worklist[wl_len] = '\0' - else: - VLogger.error("依赖 worklist 溢出", "StubMerger") - # deps_str 为空字符串视为无依赖(合法情况,如叶子模块) - else: - # 依赖未在内存中找到 - # 按需过滤: 检查是否是 App 源文件(source_dir 下的 .vp/.py 文件) - # App 源文件不需要加入 reachable_set(只用于 includes 过滤),跳过 - sd_len_ff: t.CSizeT = string.strlen(source_dir) - mb_len_ff: t.CSizeT = string.strlen(mod_buf) - app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6) - is_app_src: int = 0 - if app_mod_path is not None: - # 优先尝试 .vp,向后兼容 .py - viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.vp", source_dir, mod_buf) - af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R) - if not af.closed: - is_app_src = 1 - af.close() - else: - # .vp 不存在,回退 .py - viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.py", source_dir, mod_buf) - af = fileio.File(app_mod_path, fileio.MODE.R) - if not af.closed: - is_app_src = 1 - af.close() - stdlib.free(app_mod_path) - if is_app_src == 0: - # fast-fail: includes 文件依赖未在内存中找到,Phase 1a-pre 未正确填充 - fb_ff: str = VLogger.fmt_buf() - if fb_ff is not None: - viperlib.snprintf(fb_ff, 1024, "依赖未找到 fast-fail: 模块 %s (sha1=%s) 的依赖未在内存中注册", mod_buf, found_sha1) - VLogger.error(fb_ff, "Reachable") - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - stdlib.free(worklist) - stdlib.free(processed) - stdlib.free(mod_buf) - return -1 - stdlib.free(mod_buf) - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - stdlib.free(worklist) - stdlib.free(processed) - - fb: str = VLogger.fmt_buf() - if fb is not None: - viperlib.snprintf(fb, 1024, "可达 SHA1: %d 个", reachable_count) - VLogger.debug(fb, "Reachable") - return reachable_count - - -# ============================================================ -# BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR -# -# 按需加载:读取 deps.txt 获取导入模块名,通过内存存储器查找 SHA1, -# 仅加载实际使用的依赖 stub(而非全部扫描)。 -# _sha1_map.txt 仅作为人类可读输出,程序内部不读取。 -# ============================================================ -def BuildCombinedIR(temp_dir: str, local_sha1: str, - out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT: - """组合本地 stub + 依赖 stubs + 本地 text → out_buf(按需加载)""" - if temp_dir is None or local_sha1 is None or out_buf is None or out_size == 0: - return 0 - - td_len: t.CSizeT = string.strlen(temp_dir) - out_buf[0] = '\0' - out_pos: t.CSizeT = 0 - - # 1. 读取并追加本地 stub.ll(含 header) - stub_path: str = _sliced_path(temp_dir, td_len, local_sha1, "stub.ll") - if stub_path is None: - return 0 - sf: fileio.File | t.CPtr = fileio.File(stub_path, fileio.MODE.R) - if sf.closed: - stdlib.free(stub_path) - return 0 - stub_content: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) - if stub_content is None: - sf.close() - stdlib.free(stub_path) - return 0 - stub_br: t.CInt64T = sf.read_all(stub_content, STUB_READ_BUF_SIZE) - sf.close() - stdlib.free(stub_path) - if stub_br <= 0: - stdlib.free(stub_content) - return 0 - if stub_br < STUB_READ_BUF_SIZE: - stub_content[stub_br] = '\0' - else: - stub_content[STUB_READ_BUF_SIZE - 1] = '\0' - slen: t.CSizeT = stub_br - # 修复本地 stub.ll 中字符串常量的非法 IR: - # llvmlite OUTPUT_STUB 模式输出 "= external unnamed_addr constant" 的非法形式。 - # 简单方案:搜索 "= external unnamed_addr constant" 并将 external 替换为 8 个空格。 - fix_pos: t.CSizeT = 0 - while fix_pos + 30 < slen: - ext_ptr: t.CPtr = string.strstr(stub_content + fix_pos, "= external unnamed_addr constant") - if ext_ptr is None: - break - # ext_ptr 指向 "= external ..." 中的 '=' - # 'external' 开始于 ext_ptr + 2 - ext_off: t.CSizeT = t.CSizeT(t.CUInt64T(ext_ptr) - t.CUInt64T(stub_content)) + 2 - if ext_off + 8 <= slen: - stub_content[ext_off] = ' ' - stub_content[ext_off + 1] = ' ' - stub_content[ext_off + 2] = ' ' - stub_content[ext_off + 3] = ' ' - stub_content[ext_off + 4] = ' ' - stub_content[ext_off + 5] = ' ' - stub_content[ext_off + 6] = ' ' - stub_content[ext_off + 7] = ' ' - fix_pos = ext_off + 8 - # 直接复制修复后的 stub 内容 - if out_pos + slen + 2 < out_size: - string.strcpy(out_buf + out_pos, stub_content) - out_pos += slen - if out_buf[out_pos - 1] != '\n': - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - stdlib.free(stub_content) - - # 2. 按需加载依赖 stubs(根据 deps.txt 过滤,而非扫描全部) - # 先加载依赖 stub(含 type 定义),确保 type 定义在本地 text.ll 的 define 块之前 - # 2a. 构建 includes SHA1 → module_name 映射(从全局内存存储器) - sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17) - mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64) - inc_count: int = 0 - if sha1_arr is not None and mod_arr is not None: - inc_count = _BuildIncludesSha1Map(temp_dir, td_len, sha1_arr, mod_arr) - - # 2b. 读取 deps.txt 获取导入模块名集合 - deps_buf: str = None - deps_loaded: int = 0 - deps_path: str = _sliced_path(temp_dir, td_len, local_sha1, "deps.txt") - if deps_path is not None: - df_deps: fileio.File | t.CPtr = fileio.File(deps_path, fileio.MODE.R) - if not df_deps.closed: - deps_buf = stdlib.malloc(4096) - if deps_buf is not None: - deps_br: t.CInt64T = df_deps.read_all(deps_buf, 4096) - if deps_br > 0: - if deps_br < 4096: - deps_buf[deps_br] = '\0' - else: - deps_buf[4095] = '\0' - deps_loaded = 1 - elif deps_br == 0: - # 空 deps.txt:无 import 的包标记文件(如 lib/core/Gen/__init__.vp、 - # lib/StubGen/__init__.vp 纯注释)合法——deps_loaded 置 1 视为 - # "已加载且无依赖",避免下方"deps.txt 缺失"误报(追加85 根因)。 - # deps_buf 是 malloc 未初始化内存,必须显式置空串, - # 否则后续 strlen(deps_buf) 读到垃圾长度遍历越界。 - deps_buf[0] = '\0' - deps_loaded = 1 - df_deps.close() - stdlib.free(deps_path) - - # deps.txt 缺失但存在 includes 映射:依赖无法按需过滤,fail-fast 终止 - if deps_loaded == 0 and inc_count > 0: - VLogger.error("依赖文件 deps.txt 缺失,无法合并 includes stub", "StubMerger") - - # 2c. 递归扫描 temp_dir 中所有 .stub.ll,按需加载 - all_sha1_arr: str = stdlib.malloc(MAX_INCLUDES_SHA1 * 17) - all_count: int = 0 - if all_sha1_arr is not None: - string.memset(all_sha1_arr, 0, MAX_INCLUDES_SHA1 * 17) - all_count = _collect_stub_sha1s(temp_dir, td_len, Config.Sha1SliceLevel, all_sha1_arr, 0, MAX_INCLUDES_SHA1) - if all_count > 0: - dep_buf: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) - if dep_buf is not None: - si: int = 0 - while si < all_count: - dep_sha1: str = all_sha1_arr + t.CSizeT(si) * 17 - if string.strcmp(dep_sha1, local_sha1) == 0: - si += 1 - continue - should_load: int = 0 - # 检查是否在 includes 映射中 - is_include: int = 0 - if inc_count > 0: - for ii in range(inc_count): - idx_ii: t.CSizeT = t.CSizeT(ii) * 17 - if string.strcmp(sha1_arr + idx_ii, dep_sha1) != 0: - continue - is_include = 1 - if deps_loaded != 0: - idx_mi: t.CSizeT = t.CSizeT(ii) * 64 - dep_mod_name: str = mod_arr + idx_mi - if HandlesImports.is_module_imported(deps_buf, dep_mod_name) != 0: - should_load = 1 - elif _is_submodule_of_imported(deps_buf, dep_mod_name) != 0: - # 传递性依赖:deps.txt 只记录直接导入(如 ast), - # 但包的 __init__.vp 内部 from .lexer import Lexer - # 会让 IR 引用 ast.lexer 子模块类型。 - # 检查 dep_mod_name 是否是已导入包的子模块 - # (如 ast.lexer 是 ast 的子模块) - should_load = 1 - break - if is_include == 0: - # 非 includes stub(用户文件),总是加载 - should_load = 1 - if should_load != 0: - out_pos = _LoadAndAppendStub(temp_dir, td_len, dep_sha1, dep_buf, out_buf, out_size, out_pos) - out_pos = _LoadAndAppendTextDeclares(temp_dir, td_len, dep_sha1, dep_buf, out_buf, out_size, out_pos) - si += 1 - stdlib.free(dep_buf) - stdlib.free(all_sha1_arr) - - # 2d. 验证 deps.txt 中所有依赖模块的 stub 文件都存在(fail-fast) - # 避免到 llc 才报 undefined value 错误。 - # 仅验证在 includes 映射中找到的模块(t/c 等内部库无 SHA1 映射,自动跳过)。 - if deps_loaded != 0 and inc_count > 0: - dep_total: t.CSizeT = string.strlen(deps_buf) - dp: t.CSizeT = 0 - while dp < dep_total: - # 跳过前导空格 - while dp < dep_total: - if deps_buf[dp] != ' ': - break - dp += 1 - if dp >= dep_total: - break - # 提取模块名(到下一个空格或末尾) - ms: t.CSizeT = dp - while dp < dep_total: - if deps_buf[dp] == ' ': - break - dp += 1 - ml: t.CSizeT = dp - ms - if ml == 0: - continue - # 复制模块名到临时缓冲区并 null 终止 - mod_nm: str = stdlib.malloc(ml + 1) - if mod_nm is None: - continue - string.strncpy(mod_nm, deps_buf + ms, ml) - mod_nm[ml] = '\0' - # 在 includes 映射中查找模块对应的 SHA1 - found_s: str = None - for mi2 in range(inc_count): - idx_m2: t.CSizeT = t.CSizeT(mi2) * 64 - if string.strcmp(mod_arr + idx_m2, mod_nm) == 0: - idx_s2: t.CSizeT = t.CSizeT(mi2) * 17 - found_s = sha1_arr + idx_s2 - break - if found_s is not None: - # 检查 stub 文件是否存在 - chk_path: str = _sliced_path(temp_dir, td_len, found_s, "stub.ll") - if chk_path is not None: - chk_f: fileio.File | t.CPtr = fileio.File(chk_path, fileio.MODE.R) - if chk_f.closed: - fb: str = VLogger.fmt_buf() - if fb is not None: - viperlib.snprintf(fb, 1024, "依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译", mod_nm, found_s, chk_path) - VLogger.error(fb, "BuildCombinedIR") - chk_f.close() - stdlib.free(chk_path) - stdlib.free(mod_nm) - - # 3. 读取并追加本地 text.ll(行级去重,跳过 stub.ll 中已存在的定义) - text_path: str = _sliced_path(temp_dir, td_len, local_sha1, "text.ll") - if text_path is None: - return out_pos - tf: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R) - if tf.closed: - # 本地 text.ll 缺失:合并 IR 无法生成定义块,fail-fast 终止 - fb_tlp: str = VLogger.fmt_buf() - if fb_tlp is not None: - viperlib.snprintf(fb_tlp, 1024, "本地 text.ll 缺失: %s", text_path) - VLogger.error(fb_tlp, "StubMerger") - stdlib.free(text_path) - else: - text_content: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) - if text_content is None: - tf.close() - stdlib.free(text_path) - else: - text_br: t.CInt64T = tf.read_all(text_content, STUB_READ_BUF_SIZE) - tf.close() - stdlib.free(text_path) - if text_br > 0: - if text_br < STUB_READ_BUF_SIZE: - text_content[text_br] = '\0' - else: - text_content[STUB_READ_BUF_SIZE - 1] = '\0' - # 行级去重追加(与 _LoadAndAppendStub 相同逻辑) - tx_pos: t.CSizeT = 0 - tx_skip: int = 1 - while tx_pos < text_br: - tx_ls: t.CSizeT = tx_pos - while tx_pos < text_br: - if text_content[tx_pos] == '\n': - break - tx_pos += 1 - tx_ll: t.CSizeT = tx_pos - tx_ls - if tx_pos < text_br: - tx_pos += 1 - if tx_skip != 0: - if tx_ll == 0: - continue - tx_c0: int = text_content[tx_ls] - if tx_c0 == ';': - continue - if tx_c0 == 't' and tx_ll >= 6: - if string.strncmp(text_content + tx_ls, "target", 6) == 0: - continue - if tx_c0 == 's' and tx_ll >= 15: - if string.strncmp(text_content + tx_ls, "source_filename", 15) == 0: - continue - tx_skip = 0 - # 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句) - if tx_ll >= 8 and string.strncmp(text_content + tx_ls, "declare ", 8) == 0: - tx_at: t.CSizeT = tx_ls + 8 - while tx_at < tx_ls + tx_ll: - if text_content[tx_at] == '@': - break - tx_at += 1 - if tx_at < tx_ls + tx_ll: - tx_lp: t.CSizeT = tx_at + 1 - while tx_lp < tx_ls + tx_ll: - if text_content[tx_lp] == '(': - break - tx_lp += 1 - if tx_lp < tx_ls + tx_ll: - tx_sv: t.CChar = text_content[tx_lp + 1] - text_content[tx_lp + 1] = '\0' - if _IsFuncDeclaredOrDefined(out_buf, text_content + tx_at) != 0: - text_content[tx_lp + 1] = tx_sv - continue - text_content[tx_lp + 1] = tx_sv - # 去重:define(注释掉 out_buf 中已有的 declare,避免 declare/define 冲突) - if tx_ll >= 7 and string.strncmp(text_content + tx_ls, "define ", 7) == 0: - tx_at_d: t.CSizeT = tx_ls + 7 - while tx_at_d < tx_ls + tx_ll: - if text_content[tx_at_d] == '@': - break - tx_at_d += 1 - if tx_at_d < tx_ls + tx_ll: - tx_lp_d: t.CSizeT = tx_at_d + 1 - while tx_lp_d < tx_ls + tx_ll: - if text_content[tx_lp_d] == '(': - break - tx_lp_d += 1 - if tx_lp_d < tx_ls + tx_ll: - tx_sv_d: t.CChar = text_content[tx_lp_d + 1] - text_content[tx_lp_d + 1] = '\0' - _CommentOutDeclareInBuf(out_buf, text_content + tx_at_d) - text_content[tx_lp_d + 1] = tx_sv_d - # 处理:@global = ... - # stub 中可能是 external 声明或完整定义,以 text 中的定义为准 - # 找到 stub 中同名全局变量行并注释掉,然后追加 text 中的 - if tx_ll > 0 and text_content[tx_ls] == '@': - tx_eq: t.CSizeT = tx_ls + 1 - while tx_eq < tx_ls + tx_ll: - if text_content[tx_eq] == '=': - break - tx_eq += 1 - if tx_eq < tx_ls + tx_ll: - tx_sv2: t.CChar = text_content[tx_eq + 1] - text_content[tx_eq + 1] = '\0' - _CommentOutGlobalInBuf(out_buf, text_content + tx_ls) - text_content[tx_eq + 1] = tx_sv2 - # 去重:%type = ... - if tx_ll > 0 and text_content[tx_ls] == '%': - tx_eq2: t.CSizeT = tx_ls + 1 - while tx_eq2 < tx_ls + tx_ll: - if text_content[tx_eq2] == '=': - break - tx_eq2 += 1 - if tx_eq2 < tx_ls + tx_ll: - tx_sv3: t.CChar = text_content[tx_eq2 + 1] - text_content[tx_eq2 + 1] = '\0' - if string.strstr(out_buf, text_content + tx_ls) is not None: - text_content[tx_eq2 + 1] = tx_sv3 - continue - text_content[tx_eq2 + 1] = tx_sv3 - # 追加 - if out_pos + tx_ll + 2 < out_size: - string.strncpy(out_buf + out_pos, text_content + tx_ls, tx_ll) - out_pos += tx_ll - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' - stdlib.free(text_content) - - if deps_buf is not None: - stdlib.free(deps_buf) - if sha1_arr is not None: - stdlib.free(sha1_arr) - if mod_arr is not None: - stdlib.free(mod_arr) - - return out_pos - - -# ============================================================ -# LoadDepVpiIntoModule - 加载依赖模块的 .vpi 文件,注入 declare 到当前模块 -# -# 遍历 imported_modules(空格分隔的模块名),对每个模块: -# 1. 用 LookupSha1ByModName 获取 SHA1 -# 2. 用 _sliced_path 构造 .vpi 文件路径 -# 3. 调用 VpiReader.ParseVpiIntoModule 解析 .vpi 并注入 declare -# -# 在 _translate_module_level 中、Import 处理完成后、translate_children -# 之前调用,确保跨模块函数调用时 declare 已存在。 -# -# Args: -# pool: 内存池 -# mod: 当前 LLVM 模块 -# imported_modules: 已导入模块名字符串(空格分隔) -# from_imports: from-import 映射字符串("name:module name:module" 格式) -# temp_dir: temp 目录路径 -# trans: 翻译器对象 -# -# Returns: -# 成功加载的 .vpi 文件数 -# ============================================================ -def LoadDepVpiIntoModule(pool: memhub.MemBuddy | t.CPtr, - mod: llvmlite.LLVMModule | t.CPtr, - imported_modules: str, - from_imports: str, - temp_dir: str, - trans: HandlesTranslator.Translator | t.CPtr) -> int: - """加载依赖模块的 .vpi 文件,将函数声明注入到当前模块""" - if pool is None or mod is None or temp_dir is None or trans is None: - return 0 - if imported_modules is None: - return 0 - - td_len: t.CSizeT = string.strlen(temp_dir) - total_len: t.CSizeT = string.strlen(imported_modules) - success_count: int = 0 - - # 遍历 imported_modules 中的每个模块名(空格分隔) - ci: t.CSizeT = 0 - while ci < total_len: - # 跳过前导空格 - while ci < total_len and imported_modules[ci] == ' ': - ci += 1 - if ci >= total_len: - break - # 提取模块名(到下一个空格或字符串末尾) - word_start: t.CSizeT = ci - while ci < total_len and imported_modules[ci] != ' ': - ci += 1 - word_len: t.CSizeT = ci - word_start - if word_len == 0: - continue - - # 复制模块名到临时缓冲区并 null 终止 - mod_nm_buf: str = stdlib.malloc(word_len + 1) - if mod_nm_buf is None: - continue - string.strncpy(mod_nm_buf, imported_modules + word_start, word_len) - mod_nm_buf[word_len] = '\0' - - # 用 LookupSha1ByModName 查找模块的 SHA1 - sha1_val: str = LookupSha1ByModName(mod_nm_buf) - - # 如果精确查找失败,尝试包名回退(如 "ast" → "ast.__init__") - # LookupSha1ByModName 内部已处理包名回退,此处无需重复 - - if sha1_val is not None: - # 构造 .vpi 文件路径 - vpi_path: str = _sliced_path(temp_dir, td_len, sha1_val, "vpi") - if vpi_path is not None: - # 检查 .vpi 文件是否存在(可读) - vf: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R) - if not vf.closed: - vf.close() - # 调用 VpiReader 解析 .vpi 并注入 declare - # (错误上下文帧由调用方 HandlesTranslator 维护) - ret: int = VpiReader.ParseVpiIntoModule(pool, mod, vpi_path, trans) - if ret == 1: - success_count += 1 - stdlib.free(vpi_path) - # sha1_val 指向内部存储,无需 free - - stdlib.free(mod_nm_buf) - - return success_count diff --git a/App/lib/core/StubMergerBuild.vp b/App/lib/core/StubMergerBuild.vp new file mode 100644 index 0000000..6eeca01 --- /dev/null +++ b/App/lib/core/StubMergerBuild.vp @@ -0,0 +1,1152 @@ +import t, c +from stdint import * +import memhub +import string +import stdlib +import llvmlite +import w32.win32file as win32file +import w32.win32base as win32base +import w32.fileio as fileio +import viperlib +import sys +import ast +import lib.core.VLogger as VLogger +import lib.core.IncludesScanner as IncludesScanner +import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesImports as HandlesImports +import lib.core.Handles.HandlesTranslator as HandlesTranslator +import lib.core.BuildPipeline as BuildPipeline +import lib.StubGen.VpiReader as VpiReader +import lib.Projectrans.Utils as Utils +import lib.Projectrans.Config as Config + + +import lib.core.StubMergerCollect as StubMergerCollect +import lib.core.StubMergerStore as StubMergerStore +import lib.core.StubMergerTopo as StubMergerTopo +import lib.core.StubMergerIR as StubMergerIR +import lib.core.StubMerger as StubMerger + +# ============================================================ +# StubMergerBuild - StubMerger 拆分(阶段 E):StubMergerBuild +# +# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留 +# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经 +# StubMerger._xxx 访问(main.vp 先例)。 +# ============================================================ + +# ============================================================ +# _LoadAndAppendStub - 加载依赖 stub 并追加到 out_buf +# +# 读取 {temp_dir}/{sha1}.stub.ll,跳过 header(;, target, source_filename), +# 去重 declare/global,追加到 out_buf。返回新 out_pos。 +# ============================================================ +def _LoadAndAppendStub(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, + dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT, + out_pos: t.CSizeT) -> t.CSizeT: + """加载依赖 stub 并追加(跳过 header,去重)""" + if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None: + return out_pos + + dep_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, dep_sha1, "stub.ll") + if dep_path is None: + return out_pos + df_ls: fileio.File | t.CPtr = fileio.File(dep_path, fileio.MODE.R) + if df_ls.closed: + stdlib.free(dep_path) + # 依赖 stub 缺失:includes 映射内的模块缺失属编译错误(fail-fast), + # 非映射模块(如 t/c 内部库、用户文件)按设计跳过 + if StubMergerStore.LookupSha1RelPath(dep_sha1) is not None: + fb_ls: str = VLogger.fmt_buf() + if fb_ls is not None: + viperlib.snprintf(fb_ls, 1024, "依赖 stub 缺失: %s", dep_sha1) + VLogger.error(fb_ls, "StubMerger") + return out_pos + dep_br_ls: t.CInt64T = df_ls.read_all(dep_buf, STUB_READ_BUF_SIZE) + df_ls.close() + stdlib.free(dep_path) + if dep_br_ls <= 0: + # 依赖 stub 为空:纯声明/纯常量模块(如 stdio.vp 全 pass、CDefine 模块) + # 无可声明符号,stub.ll 为空属合法形态,静默跳过 + return out_pos + if dep_br_ls < STUB_READ_BUF_SIZE: + dep_buf[dep_br_ls] = '\0' + else: + dep_buf[STUB_READ_BUF_SIZE - 1] = '\0' + + # 注释掉依赖 stub 中的字符串常量行(模块内部的,不需要) + # 简单方案:搜索 "= external unnamed_addr constant" 并将行首的 @ 改为 ;@ + cfs_pos: t.CSizeT = 0 + while cfs_pos + 30 < t.CSizeT(dep_br_ls): + ext_p: t.CPtr = string.strstr(dep_buf + cfs_pos, "= external unnamed_addr constant") + if ext_p is None: + break + ext_off2: t.CSizeT = t.CSizeT(t.CUInt64T(ext_p) - t.CUInt64T(dep_buf)) + # 向前查找行首的 @ + ls: t.CSizeT = ext_off2 + while ls > 0: + if dep_buf[ls] == '\n': + ls += 1 + break + ls -= 1 + if ls < ext_off2 and dep_buf[ls] == '@': + dep_buf[ls] = ';' + cfs_pos = ext_off2 + 30 + + # 跳过 header,追加内容(去重 declare/global) + dep_pos: t.CSizeT = 0 + skipping: int = 1 + while dep_pos < dep_br_ls: + line_start: t.CSizeT = dep_pos + while dep_pos < dep_br_ls: + if dep_buf[dep_pos] == '\n': + break + dep_pos += 1 + line_len: t.CSizeT = dep_pos - line_start + if dep_pos < dep_br_ls: + dep_pos += 1 + if skipping != 0: + if line_len == 0: + continue + ch0: int = dep_buf[line_start] + if ch0 == ';': + continue + if ch0 == 't' and line_len >= 6: + if string.strncmp(dep_buf + line_start, "target", 6) == 0: + continue + if ch0 == 's' and line_len >= 15: + if string.strncmp(dep_buf + line_start, "source_filename", 15) == 0: + continue + skipping = 0 + # 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句) + if line_len >= 8 and string.strncmp(dep_buf + line_start, "declare ", 8) == 0: + at: t.CSizeT = line_start + 8 + while at < line_start + line_len: + if dep_buf[at] == '@': + break + at += 1 + if at < line_start + line_len: + lp: t.CSizeT = at + 1 + while lp < line_start + line_len: + if dep_buf[lp] == '(': + break + lp += 1 + if lp < line_start + line_len: + saved: t.CChar = dep_buf[lp + 1] + dep_buf[lp + 1] = '\0' + if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, dep_buf + at) != 0: + dep_buf[lp + 1] = saved + continue + dep_buf[lp + 1] = saved + # 去重:external global + if line_len > 0 and dep_buf[line_start] == '@': + eq: t.CSizeT = line_start + 1 + while eq < line_start + line_len: + if dep_buf[eq] == '=': + break + eq += 1 + if eq < line_start + line_len: + saved: t.CChar = dep_buf[eq + 1] + dep_buf[eq + 1] = '\0' + if string.strstr(out_buf, dep_buf + line_start) is not None: + dep_buf[eq + 1] = saved + continue + dep_buf[eq + 1] = saved + # 去重:type 定义(如 %"sha1.Point" = type {i32, i32}) + # 区分完整定义(= type { ... } / = type < ... >)和 opaque 声明(= type opaque) + # 当本地 stub 有 opaque 声明而依赖 stub 有完整定义时,注释掉 opaque 行, + # 让完整定义被追加,避免 LLC "Cannot allocate unsized type" 错误。 + if line_len > 0 and dep_buf[line_start] == '%': + eq2: t.CSizeT = line_start + 1 + while eq2 < line_start + line_len: + if dep_buf[eq2] == '=': + break + eq2 += 1 + if eq2 < line_start + line_len: + saved2: t.CChar = dep_buf[eq2 + 1] + dep_buf[eq2 + 1] = '\0' + # 检查依赖行是否为完整定义(= type { 或 = type <) + dep_is_full_def: int = 0 + if eq2 + 8 <= line_start + line_len: + if dep_buf[eq2 + 2] == 't' and dep_buf[eq2 + 3] == 'y' and dep_buf[eq2 + 4] == 'p' and dep_buf[eq2 + 5] == 'e' and dep_buf[eq2 + 6] == ' ': + if dep_buf[eq2 + 7] == '{' or dep_buf[eq2 + 7] == '<': + dep_is_full_def = 1 + if dep_is_full_def == 1: + # 依赖行是完整定义 + # 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀 + saved_eq: t.CChar = dep_buf[eq2] + dep_buf[eq2] = '\0' + # 1. 注释掉 out_buf 中的 opaque 声明(如果有) + commented: int = StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + line_start) + # 2. 检查 out_buf 中是否已有完整定义(避免重复) + has_full: int = StubMergerIR._HasFullTypeDefinition(out_buf, dep_buf + line_start) + dep_buf[eq2] = saved_eq + if has_full == 1: + dep_buf[eq2 + 1] = saved2 + continue + # 3. 追加完整定义 + else: + # 依赖行是 opaque 声明,用原有子串匹配去重 + found_opaque: t.CPtr = string.strstr(out_buf, dep_buf + line_start) + if found_opaque is not None: + dep_buf[eq2 + 1] = saved2 + continue + dep_buf[eq2 + 1] = saved2 + # 追加 + if out_pos + line_len + 2 < out_size: + string.strncpy(out_buf + out_pos, dep_buf + line_start, line_len) + out_pos += line_len + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + return out_pos +# ============================================================ +# _LoadAndAppendTextDeclares - 从依赖 text.ll 提取 declare 追加到 out_buf +# +# 读取 {temp_dir}/{sha1}.text.ll,将 define 行转为 declare(提取函数签名), +# 跳过函数体,去重后追加到 out_buf。返回新 out_pos。 +# ============================================================ +def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, + dep_buf: bytes, out_buf: bytes, out_size: t.CSizeT, + out_pos: t.CSizeT) -> t.CSizeT: + """从依赖 text.ll 提取 declare(define 转 declare),追加到 out_buf""" + if temp_dir is None or dep_sha1 is None or dep_buf is None or out_buf is None: + return out_pos + + text_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, dep_sha1, "text.ll") + if text_path is None: + return out_pos + tf_lt: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R) + if tf_lt.closed: + stdlib.free(text_path) + # 依赖 text 缺失:includes 映射内的模块缺失属编译错误(fail-fast), + # 非映射模块(如 t/c 内部库、用户文件)按设计跳过 + if StubMergerStore.LookupSha1RelPath(dep_sha1) is not None: + fb_lt: str = VLogger.fmt_buf() + if fb_lt is not None: + viperlib.snprintf(fb_lt, 1024, "依赖 text 缺失: %s", dep_sha1) + VLogger.error(fb_lt, "StubMerger") + return out_pos + text_br: t.CInt64T = tf_lt.read_all(dep_buf, STUB_READ_BUF_SIZE) + tf_lt.close() + stdlib.free(text_path) + if text_br <= 0: + # 依赖 text 为空:纯 stub 模块(如 stdio.vp 全 pass 声明)无函数体, + # text.ll 为空属合法形态,无可提取 declare,静默跳过 + return out_pos + if text_br < STUB_READ_BUF_SIZE: + dep_buf[text_br] = '\0' + else: + dep_buf[STUB_READ_BUF_SIZE - 1] = '\0' + + dep_pos_lt: t.CSizeT = 0 + in_body: int = 0 + while dep_pos_lt < text_br: + ls: t.CSizeT = dep_pos_lt + while dep_pos_lt < text_br: + if dep_buf[dep_pos_lt] == '\n': + break + dep_pos_lt += 1 + ll: t.CSizeT = dep_pos_lt - ls + if dep_pos_lt < text_br: + dep_pos_lt += 1 + + # 函数体跳过:直到单独的 } 行 + if in_body != 0: + if ll == 1 and dep_buf[ls] == '}': + in_body = 0 + continue + + # 跳过空行和 header + if ll == 0: + continue + c0: int = dep_buf[ls] + if c0 == ';': + continue + if c0 == 't' and ll >= 6 and string.strncmp(dep_buf + ls, "target", 6) == 0: + continue + if c0 == 's' and ll >= 15 and string.strncmp(dep_buf + ls, "source_filename", 15) == 0: + continue + + # 处理 define 行:转为 declare + if ll >= 7 and string.strncmp(dep_buf + ls, "define ", 7) == 0: + # 提取 @funcname( 用于去重 + at_lt: t.CSizeT = ls + 7 + while at_lt < ls + ll: + if dep_buf[at_lt] == '@': + break + at_lt += 1 + if at_lt >= ls + ll: + in_body = 1 + continue + lp_lt: t.CSizeT = at_lt + 1 + while lp_lt < ls + ll: + if dep_buf[lp_lt] == '(': + break + lp_lt += 1 + if lp_lt >= ls + ll: + in_body = 1 + continue + # 去重检查:用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句 + saved_lt: t.CChar = dep_buf[lp_lt + 1] + dep_buf[lp_lt + 1] = '\0' + if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, dep_buf + at_lt) != 0: + dep_buf[lp_lt + 1] = saved_lt + in_body = 1 + continue + dep_buf[lp_lt + 1] = saved_lt + + # 找行尾的 ' {' 并截断 + decl_end: t.CSizeT = ls + ll + if decl_end > ls + 1: + if dep_buf[decl_end - 1] == '{' and dep_buf[decl_end - 2] == ' ': + decl_end -= 2 + + # 输出: declare + content(skip "define " 7 chars, up to decl_end) + # 泛型特化方法 define 是 "define linkonce_odr ..."(FunctionPrint + # 对名含 '[' 的函数输出 linkonce_odr):若只跳过 "define " 会拼出 + # "declare linkonce_odr ..." → llc 报 "invalid linkage for function + # declaration"(LLVM 只允许 define 用 linkonce_odr,declare 必须 + # external)。对齐 TPC Phase2Translator 的 decl_line 剥 linkage 逻辑, + # 循环跳过所有 linkage 词。 + c_start_lt: t.CSizeT = ls + 7 + skipped_lt: int = 1 + while skipped_lt != 0: + skipped_lt = 0 + if c_start_lt + 13 <= decl_end and \ + string.strncmp(dep_buf + c_start_lt, "linkonce_odr ", 13) == 0: + c_start_lt += 13 + skipped_lt = 1 + elif c_start_lt + 10 <= decl_end and \ + string.strncmp(dep_buf + c_start_lt, "weak_odr ", 9) == 0: + c_start_lt += 9 + skipped_lt = 1 + elif c_start_lt + 10 <= decl_end and \ + string.strncmp(dep_buf + c_start_lt, "linkonce ", 9) == 0: + c_start_lt += 9 + skipped_lt = 1 + elif c_start_lt + 6 <= decl_end and \ + string.strncmp(dep_buf + c_start_lt, "weak ", 5) == 0: + c_start_lt += 5 + skipped_lt = 1 + elif c_start_lt + 10 <= decl_end and \ + string.strncmp(dep_buf + c_start_lt, "internal ", 9) == 0: + c_start_lt += 9 + skipped_lt = 1 + elif c_start_lt + 9 <= decl_end and \ + string.strncmp(dep_buf + c_start_lt, "private ", 8) == 0: + c_start_lt += 8 + skipped_lt = 1 + copy_len_lt: t.CSizeT = decl_end - c_start_lt + if out_pos + 8 + copy_len_lt + 2 < out_size: + string.strncpy(out_buf + out_pos, "declare ", 8) + out_pos += 8 + string.strncpy(out_buf + out_pos, dep_buf + c_start_lt, copy_len_lt) + out_pos += copy_len_lt + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + in_body = 1 + continue + + # 提取类型定义(%... = type { ... } 或 %... = type < ... >) + # stub.ll 中只有 opaque 声明,需要从 text.ll 中提取完整定义 + # 用于 alloca 和 getelementptr 等需要完整类型的指令 + if ll > 0 and dep_buf[ls] == '%': + eq_td: t.CSizeT = ls + 1 + while eq_td < ls + ll: + if dep_buf[eq_td] == '=': + break + eq_td += 1 + if eq_td < ls + ll: + # 检查是否为完整定义(= type { 或 = type <) + dep_is_full_td: int = 0 + if eq_td + 8 <= ls + ll: + if dep_buf[eq_td + 2] == 't' and dep_buf[eq_td + 3] == 'y' \ + and dep_buf[eq_td + 4] == 'p' and dep_buf[eq_td + 5] == 'e' \ + and dep_buf[eq_td + 6] == ' ': + if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<': + dep_is_full_td = 1 + if dep_is_full_td == 1: + # 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀 + saved_eq_td: t.CChar = dep_buf[eq_td] + dep_buf[eq_td] = '\0' + # 1. 注释掉 out_buf 中的 opaque 声明(如果有) + StubMergerIR._CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls) + # 2. 检查 out_buf 中是否已有完整定义(避免重复) + has_full_td: int = StubMergerIR._HasFullTypeDefinition(out_buf, dep_buf + ls) + dep_buf[eq_td] = saved_eq_td + if has_full_td == 0: + # 追加完整类型定义 + if out_pos + ll + 2 < out_size: + string.strncpy(out_buf + out_pos, dep_buf + ls, ll) + out_pos += ll + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + continue + + # 提取全局变量定义(@... = ...) + # 依赖 text.ll 中的全局变量带初值定义(如 @StubMerger._mbuddy = global ...) + # stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义 + # 注释掉 out_buf 中的同名行(external 声明或旧定义),追加 text.ll 中的完整定义 + if ll > 0 and dep_buf[ls] == '@': + eq_gv: t.CSizeT = ls + 1 + while eq_gv < ls + ll: + if dep_buf[eq_gv] == '=': + break + eq_gv += 1 + if eq_gv < ls + ll: + # 截断到 '=' 之前(不含 '='),得到全局变量名 @name + saved_gv: t.CChar = dep_buf[eq_gv] + dep_buf[eq_gv] = '\0' + # 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义) + # 时跳过:跨模块全局(如 _op_head、MemBuddy.vtable)只需一份 + # 强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致 + # ld.lld duplicate symbol(NewAstTest _op_head/MemBuddy.vtable 根因)。 + # 仅当依赖方完全没有该全局(既无 external 也无定义)时才需要补充。 + if StubMergerIR._HasGlobalInBuf(out_buf, dep_buf + ls) == 0: + # 注释掉 out_buf 中的同名行(external 声明或旧定义) + StubMergerIR._CommentOutGlobalInBuf(out_buf, dep_buf + ls) + dep_buf[eq_gv] = saved_gv + # 追加完整定义 + if out_pos + ll + 2 < out_size: + string.strncpy(out_buf + out_pos, dep_buf + ls, ll) + out_pos += ll + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + else: + dep_buf[eq_gv] = saved_gv + continue + + # 跳过其他行 + return out_pos +# ============================================================ +# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需翻译) +# +# 从 source_dir 的源文件开始,解析 import 语句,递归收集可达的 +# includes 文件 SHA1。Phase 1b 只翻译可达集合中的文件,避免 +# 翻译不需要的 includes(如 Test 不依赖 llvmlite,则不翻译)。 +# +# 算法(工作列表): +# 1. 扫描 source_dir 下的 .vp/.py 文件,解析 AST 获取 _imported_modules +# 2. 构建 SHA1→module_name 映射(从全局内存存储器) +# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名 +# +# Args: +# mb: 内存池 +# source_dir: 源文件目录(Config.SourceDir) +# temp_dir: 临时目录(.deps.txt 所在位置;_sha1_map.txt 仅人类可读输出,不读取) +# reachable_set: 输出参数,可达 SHA1 集合缓冲区(MAX_INCLUDES_SHA1 * 17 字节) +# +# Returns: +# 可达 SHA1 数量(>0 成功,<=0 失败) +# ============================================================ +def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, + temp_dir: str, + reachable_set: str, + app_deps: str = None) -> int: + """构建可达 SHA1 集合(依赖图按需翻译) + + app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤 + """ + if source_dir is None or temp_dir is None or reachable_set is None: + return -1 + + SRC_BUF_SIZE_R: t.CSizeT = 1048576 + + # 1. 构建 SHA1→module_name 映射 + td_len_r: t.CSizeT = string.strlen(temp_dir) + sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17) + mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64) + if sha1_arr is None or mod_arr is None: + if sha1_arr is not None: + stdlib.free(sha1_arr) + if mod_arr is not None: + stdlib.free(mod_arr) + return -1 + map_count: int = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr) + if map_count <= 0: + VLogger.error("无法构建 SHA1 映射", "Reachable") + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + return -1 + fb: str = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count) + VLogger.debug(fb, "Reachable") + + # 工作列表(空格分隔的模块名) + worklist: bytes = stdlib.malloc(REACH_WORKLIST_BUF) + if worklist is None: + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + return -1 + worklist[0] = '\0' + wl_len: t.CSizeT = 0 + + reachable_count: int = 0 + + # 2. 如果 app_deps 非空,直接使用它作为初始 worklist(跳过自己的非递归扫描) + # 否则,回退到自己的非递归扫描(source_dir/*.py,只扫描顶层) + if app_deps is not None and app_deps[0] != '\0': + ad_len: t.CSizeT = string.strlen(app_deps) + if ad_len > 0 and ad_len < REACH_WORKLIST_BUF: + string.strcpy(worklist, app_deps) + wl_len = ad_len + else: + VLogger.error("app_deps 过长,无法构建可达集合", "StubMerger") + else: + # app_deps 缺失:Phase1 场景(未翻译 App 源文件)为设计性回退, + # 显式 warning 暴露回退,不再静默。 + # 原实现仅非递归扫描 source_dir 顶层("%s/*"),漏掉 lib/ 等子目录的 + # App 文件 → 其依赖(如 lib/Projectrans/Config.vp 的 json)不进入 + # worklist → json 不可达 → Phase1 不 stub 不翻译 → Phase2 Phase A + # 翻译 Config.vp 时 root.is_object() 跨模块调用无 stub 可查 → 静默 + # translate_value 返回 None → "if 条件表达式翻译失败"(追加82 根因)。 + # 改为递归扫描(复用 IncludesScanner.scan_includes),覆盖全部子目录。 + VLogger.warning("app_deps 缺失,递归扫描 source_dir", "Reachable") + scan_res_r: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, source_dir) + if scan_res_r is not None: + es_r: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + for e_i_r in range(scan_res_r.Count): + e_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)( + t.CVoid(t.CUInt64T(scan_res_r.Entries) + t.CUInt64T(e_i_r) * es_r, t.CPtr)) + if e_r is None or e_r.Path is None: + continue + sbuf: bytes = Utils.read_file_to_buf(e_r.Path, SRC_BUF_SIZE_R) + if sbuf is None: + continue + # 解析 AST 获取 _imported_modules(与原顶层扫描一致) + lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) + if lx is not None: + ast._lexer_init(lx, sbuf, mb) + tokens: ast.Token | t.CPtr = ast.tokenize(lx) + tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) + if tree is not None: + tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() + if tr is not None: + tr._declare_only = 2 + # 源文件包名:用 RelPath(相对 source_dir,支持子目录), + # 保证 from .xxx import 相对导入解析正确 + tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, e_r.RelPath) + HandlesStruct.reset_visible_structs(mb, 0) + tr.translate(tree) + # 容器使用检测:App 源文件用 list/dict(裸名注解/构造/[] + # /{} 字面量)时注入 _list/_dict 依赖(与原实现一致) + tr._inject_auto_container_visibility(mb, tree) + # 获取 _imported_modules + if tr._imported_modules is not None: + im_len: t.CSizeT = string.strlen(tr._imported_modules) + if im_len > 0 and wl_len + im_len + 1 < REACH_WORKLIST_BUF: + string.strcpy(worklist + wl_len, tr._imported_modules) + wl_len += im_len + worklist[wl_len] = ' ' + wl_len += 1 + worklist[wl_len] = '\0' + # 计算 App 源文件 SHA1,填充依赖到内存存储器 + # worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖 + if e_r.Sha1 is not None: + StubMergerStore.PopulateIncludesDeps(e_r.Sha1, tr._imported_modules) + # 释放 Translator 资源 + if tr._global_names is not None: + stdlib.free(tr._global_names) + if tr._nonlocal_names is not None: + stdlib.free(tr._nonlocal_names) + stdlib.free(sbuf) + + # 去掉末尾多余空格 + if wl_len > 0 and worklist[wl_len - 1] == ' ': + worklist[wl_len - 1] = '\0' + wl_len -= 1 + + fb: str = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "源文件直接依赖: '%s'", worklist) + VLogger.debug(fb, "Reachable") + + # 2.5. 预加入基础模块(builtins) + # + # builtins(内置函数模块:ord/chr 等)始终纳入编译链,调用时自动关联。 + # _list/_dict 不再无条件预加入:Phase A 翻译 App 源文件时 + # _inject_auto_container_visibility 已按需检测 list/dict 使用并注入 + # _imported_modules(含 []/{} 字面量),json 由 _dict 的真实 import 链带入。 + if HandlesImports.is_module_imported(worklist, "builtins") == 0: + bsha1_b: str = StubMergerIR._FindSha1ByModName(sha1_arr, mod_arr, map_count, "builtins") + if bsha1_b is not None: + if wl_len + 9 < REACH_WORKLIST_BUF: + if wl_len > 0: + worklist[wl_len] = ' ' + wl_len += 1 + string.strcpy(worklist + wl_len, "builtins") + wl_len += 8 + worklist[wl_len] = ' ' + wl_len += 1 + worklist[wl_len] = '\0' + fb: str = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist) + VLogger.debug(fb, "Reachable") + + # 3. 工作列表算法:递归收集可达 SHA1 + processed: bytes = stdlib.malloc(REACH_WORKLIST_BUF) + if processed is None: + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + stdlib.free(worklist) + return -1 + processed[0] = '\0' + proc_len: t.CSizeT = 0 + + wl_pos: t.CSizeT = 0 + wl_iter: int = 0 + while wl_pos < wl_len: + wl_iter += 1 + # 提取一个模块名(到空格或末尾) + name_start: t.CSizeT = wl_pos + while wl_pos < wl_len and worklist[wl_pos] != ' ': + wl_pos += 1 + name_len: t.CSizeT = wl_pos - name_start + if wl_pos < wl_len: + wl_pos += 1 + if name_len == 0: + continue + + # 复制模块名 + mod_buf: bytes = stdlib.malloc(name_len + 1) + if mod_buf is None: + continue + string.strncpy(mod_buf, worklist + name_start, name_len) + mod_buf[name_len] = '\0' + + # 检查是否已处理 + already: int = 0 + if HandlesImports.is_module_imported(processed, mod_buf) != 0: + already = 1 + if already == 0: + # 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug) + if processed is not None: + if proc_len + name_len + 1 < REACH_WORKLIST_BUF: + string.strcpy(processed + proc_len, mod_buf) + proc_len = proc_len + name_len + processed[proc_len] = ' ' + proc_len = proc_len + 1 + processed[proc_len] = '\0' + else: + VLogger.error("依赖 worklist 溢出", "StubMerger") + + # 查找 SHA1 + found_sha1: str = StubMergerIR._FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf) + if found_sha1 is not None: + # App 源文件(Sha1Store 中 IncDir 为空,AppendToSha1MapStore 规则) + # 不加入 reachable_set:Phase B 已编译其 .obj,若加入会导致 Phase B+ + # 把 App 文件重复编译进 includes.binary,Phase C 链接时与 Phase B 的 + # App obj 重复符号(SysLibTest ld.lld duplicate symbol: StubMerger._mbuddy 根因)。 + # 依赖仍继续展开(App 文件是依赖图中间节点,其 includes 依赖必须可达)。 + is_app_r: int = 0 + if StubMerger._sha1_store_incdir is not None: + for ai_r in range(StubMerger._sha1_store_count): + asi_r: t.CSizeT = t.CSizeT(ai_r) * 17 + if string.strcmp(StubMerger._sha1_store_arr + asi_r, found_sha1) == 0: + aii_r: t.CSizeT = t.CSizeT(ai_r) * MAX_INC_DIR_LEN + if StubMerger._sha1_store_incdir[aii_r] == '\0': + is_app_r = 1 + break + if is_app_r == 0: + reachable_count = StubMergerIR._AddSha1ToSet(reachable_set, reachable_count, found_sha1) + + # 从内存查找依赖(不读 deps.txt 过程文件) + # Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存 + deps_str: str = StubMergerStore.LookupIncludesDeps(found_sha1) + if deps_str is not None: + dl: t.CSizeT = string.strlen(deps_str) + if dl > 0: + if wl_len + dl + 2 < REACH_WORKLIST_BUF: + if wl_len > 0 and worklist[wl_len - 1] != ' ': + worklist[wl_len] = ' ' + wl_len = wl_len + 1 + string.strcpy(worklist + wl_len, deps_str) + wl_len = wl_len + dl + worklist[wl_len] = ' ' + wl_len = wl_len + 1 + worklist[wl_len] = '\0' + else: + VLogger.error("依赖 worklist 溢出", "StubMerger") + # deps_str 为空字符串视为无依赖(合法情况,如叶子模块) + else: + # 依赖未在内存中找到 + # 按需过滤: 检查是否是 App 源文件(source_dir 下的 .vp/.py 文件) + # App 源文件不需要加入 reachable_set(只用于 includes 过滤),跳过 + sd_len_ff: t.CSizeT = string.strlen(source_dir) + mb_len_ff: t.CSizeT = string.strlen(mod_buf) + app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6) + is_app_src: int = 0 + if app_mod_path is not None: + # 优先尝试 .vp,向后兼容 .py + viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.vp", source_dir, mod_buf) + af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R) + if not af.closed: + is_app_src = 1 + af.close() + else: + # .vp 不存在,回退 .py + viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.py", source_dir, mod_buf) + af = fileio.File(app_mod_path, fileio.MODE.R) + if not af.closed: + is_app_src = 1 + af.close() + stdlib.free(app_mod_path) + if is_app_src == 0: + # fast-fail: includes 文件依赖未在内存中找到,Phase 1a-pre 未正确填充 + fb_ff: str = VLogger.fmt_buf() + if fb_ff is not None: + viperlib.snprintf(fb_ff, 1024, "依赖未找到 fast-fail: 模块 %s(sha1=%s) 的依赖未在内存中注册", mod_buf, found_sha1) + VLogger.error(fb_ff, "Reachable") + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + stdlib.free(worklist) + stdlib.free(processed) + stdlib.free(mod_buf) + return -1 + stdlib.free(mod_buf) + stdlib.free(sha1_arr) + stdlib.free(mod_arr) + stdlib.free(worklist) + stdlib.free(processed) + + fb: str = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "可达 SHA1: %d 个", reachable_count) + VLogger.debug(fb, "Reachable") + return reachable_count +# ============================================================ +# BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR +# +# 按需加载:读取 deps.txt 获取导入模块名,通过内存存储器查找 SHA1, +# 仅加载实际使用的依赖 stub(而非全部扫描)。 +# _sha1_map.txt 仅作为人类可读输出,程序内部不读取。 +# ============================================================ +def BuildCombinedIR(temp_dir: str, local_sha1: str, + out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT: + """组合本地 stub + 依赖 stubs + 本地 text → out_buf(按需加载)""" + if temp_dir is None or local_sha1 is None or out_buf is None or out_size == 0: + return 0 + + td_len: t.CSizeT = string.strlen(temp_dir) + out_buf[0] = '\0' + out_pos: t.CSizeT = 0 + + # 1. 读取并追加本地 stub.ll(含 header) + stub_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, local_sha1, "stub.ll") + if stub_path is None: + return 0 + sf: fileio.File | t.CPtr = fileio.File(stub_path, fileio.MODE.R) + if sf.closed: + stdlib.free(stub_path) + return 0 + stub_content: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) + if stub_content is None: + sf.close() + stdlib.free(stub_path) + return 0 + stub_br: t.CInt64T = sf.read_all(stub_content, STUB_READ_BUF_SIZE) + sf.close() + stdlib.free(stub_path) + if stub_br <= 0: + stdlib.free(stub_content) + return 0 + if stub_br < STUB_READ_BUF_SIZE: + stub_content[stub_br] = '\0' + else: + stub_content[STUB_READ_BUF_SIZE - 1] = '\0' + slen: t.CSizeT = stub_br + # 修复本地 stub.ll 中字符串常量的非法 IR: + # llvmlite OUTPUT_STUB 模式输出 "= external unnamed_addr constant" 的非法形式。 + # 简单方案:搜索 "= external unnamed_addr constant" 并将 external 替换为 8 个空格。 + fix_pos: t.CSizeT = 0 + while fix_pos + 30 < slen: + ext_ptr: t.CPtr = string.strstr(stub_content + fix_pos, "= external unnamed_addr constant") + if ext_ptr is None: + break + # ext_ptr 指向 "= external ..." 中的 '=' + # 'external' 开始于 ext_ptr + 2 + ext_off: t.CSizeT = t.CSizeT(t.CUInt64T(ext_ptr) - t.CUInt64T(stub_content)) + 2 + if ext_off + 8 <= slen: + stub_content[ext_off] = ' ' + stub_content[ext_off + 1] = ' ' + stub_content[ext_off + 2] = ' ' + stub_content[ext_off + 3] = ' ' + stub_content[ext_off + 4] = ' ' + stub_content[ext_off + 5] = ' ' + stub_content[ext_off + 6] = ' ' + stub_content[ext_off + 7] = ' ' + fix_pos = ext_off + 8 + # 直接复制修复后的 stub 内容 + if out_pos + slen + 2 < out_size: + string.strcpy(out_buf + out_pos, stub_content) + out_pos += slen + if out_buf[out_pos - 1] != '\n': + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + stdlib.free(stub_content) + + # 2. 按需加载依赖 stubs(根据 deps.txt 过滤,而非扫描全部) + # 先加载依赖 stub(含 type 定义),确保 type 定义在本地 text.ll 的 define 块之前 + # 2a. 构建 includes SHA1 → module_name 映射(从全局内存存储器) + sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17) + mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64) + inc_count: int = 0 + if sha1_arr is not None and mod_arr is not None: + inc_count = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len, sha1_arr, mod_arr) + + # 2b. 读取 deps.txt 获取导入模块名集合 + deps_buf: str = None + deps_loaded: int = 0 + deps_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, local_sha1, "deps.txt") + if deps_path is not None: + df_deps: fileio.File | t.CPtr = fileio.File(deps_path, fileio.MODE.R) + if not df_deps.closed: + deps_buf = stdlib.malloc(4096) + if deps_buf is not None: + deps_br: t.CInt64T = df_deps.read_all(deps_buf, 4096) + if deps_br > 0: + if deps_br < 4096: + deps_buf[deps_br] = '\0' + else: + deps_buf[4095] = '\0' + deps_loaded = 1 + elif deps_br == 0: + # 空 deps.txt:无 import 的包标记文件(如 lib/core/Gen/__init__.vp、 + # lib/StubGen/__init__.vp 纯注释)合法——deps_loaded 置 1 视为 + # "已加载且无依赖",避免下方"deps.txt 缺失"误报(追加85 根因)。 + # deps_buf 是 malloc 未初始化内存,必须显式置空串, + # 否则后续 strlen(deps_buf) 读到垃圾长度遍历越界。 + deps_buf[0] = '\0' + deps_loaded = 1 + df_deps.close() + stdlib.free(deps_path) + + # deps.txt 缺失但存在 includes 映射:依赖无法按需过滤,fail-fast 终止 + if deps_loaded == 0 and inc_count > 0: + VLogger.error("依赖文件 deps.txt 缺失,无法合并 includes stub", "StubMerger") + + # 2c. 递归扫描 temp_dir 中所有 .stub.ll,按需加载 + all_sha1_arr: str = stdlib.malloc(MAX_INCLUDES_SHA1 * 17) + all_count: int = 0 + if all_sha1_arr is not None: + string.memset(all_sha1_arr, 0, MAX_INCLUDES_SHA1 * 17) + all_count = StubMergerCollect._collect_stub_sha1s(temp_dir, td_len, Config.Sha1SliceLevel, all_sha1_arr, 0, MAX_INCLUDES_SHA1) + if all_count > 0: + dep_buf: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) + if dep_buf is not None: + si: int = 0 + while si < all_count: + dep_sha1: str = all_sha1_arr + t.CSizeT(si) * 17 + if string.strcmp(dep_sha1, local_sha1) == 0: + si += 1 + continue + should_load: int = 0 + # 检查是否在 includes 映射中 + is_include: int = 0 + if inc_count > 0: + for ii in range(inc_count): + idx_ii: t.CSizeT = t.CSizeT(ii) * 17 + if string.strcmp(sha1_arr + idx_ii, dep_sha1) != 0: + continue + is_include = 1 + if deps_loaded != 0: + idx_mi: t.CSizeT = t.CSizeT(ii) * 64 + dep_mod_name: str = mod_arr + idx_mi + if HandlesImports.is_module_imported(deps_buf, dep_mod_name) != 0: + should_load = 1 + elif StubMergerIR._is_submodule_of_imported(deps_buf, dep_mod_name) != 0: + # 传递性依赖:deps.txt 只记录直接导入(如 ast), + # 但包的 __init__.vp 内部 from .lexer import Lexer + # 会让 IR 引用 ast.lexer 子模块类型。 + # 检查 dep_mod_name 是否是已导入包的子模块 + # (如 ast.lexer 是 ast 的子模块) + should_load = 1 + break + if is_include == 0: + # 非 includes stub(用户文件),总是加载 + should_load = 1 + if should_load != 0: + out_pos = _LoadAndAppendStub(temp_dir, td_len, dep_sha1, dep_buf, out_buf, out_size, out_pos) + out_pos = _LoadAndAppendTextDeclares(temp_dir, td_len, dep_sha1, dep_buf, out_buf, out_size, out_pos) + si += 1 + stdlib.free(dep_buf) + stdlib.free(all_sha1_arr) + + # 2d. 验证 deps.txt 中所有依赖模块的 stub 文件都存在(fail-fast) + # 避免到 llc 才报 undefined value 错误。 + # 仅验证在 includes 映射中找到的模块(t/c 等内部库无 SHA1 映射,自动跳过)。 + if deps_loaded != 0 and inc_count > 0: + dep_total: t.CSizeT = string.strlen(deps_buf) + dp: t.CSizeT = 0 + while dp < dep_total: + # 跳过前导空格 + while dp < dep_total: + if deps_buf[dp] != ' ': + break + dp += 1 + if dp >= dep_total: + break + # 提取模块名(到下一个空格或末尾) + ms: t.CSizeT = dp + while dp < dep_total: + if deps_buf[dp] == ' ': + break + dp += 1 + ml: t.CSizeT = dp - ms + if ml == 0: + continue + # 复制模块名到临时缓冲区并 null 终止 + mod_nm: str = stdlib.malloc(ml + 1) + if mod_nm is None: + continue + string.strncpy(mod_nm, deps_buf + ms, ml) + mod_nm[ml] = '\0' + # 在 includes 映射中查找模块对应的 SHA1 + found_s: str = None + for mi2 in range(inc_count): + idx_m2: t.CSizeT = t.CSizeT(mi2) * 64 + if string.strcmp(mod_arr + idx_m2, mod_nm) == 0: + idx_s2: t.CSizeT = t.CSizeT(mi2) * 17 + found_s = sha1_arr + idx_s2 + break + if found_s is not None: + # 检查 stub 文件是否存在 + chk_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, found_s, "stub.ll") + if chk_path is not None: + chk_f: fileio.File | t.CPtr = fileio.File(chk_path, fileio.MODE.R) + if chk_f.closed: + fb: str = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译", mod_nm, found_s, chk_path) + VLogger.error(fb, "BuildCombinedIR") + chk_f.close() + stdlib.free(chk_path) + stdlib.free(mod_nm) + + # 3. 读取并追加本地 text.ll(行级去重,跳过 stub.ll 中已存在的定义) + text_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, local_sha1, "text.ll") + if text_path is None: + return out_pos + tf: fileio.File | t.CPtr = fileio.File(text_path, fileio.MODE.R) + if tf.closed: + # 本地 text.ll 缺失:合并 IR 无法生成定义块,fail-fast 终止 + fb_tlp: str = VLogger.fmt_buf() + if fb_tlp is not None: + viperlib.snprintf(fb_tlp, 1024, "本地 text.ll 缺失: %s", text_path) + VLogger.error(fb_tlp, "StubMerger") + stdlib.free(text_path) + else: + text_content: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) + if text_content is None: + tf.close() + stdlib.free(text_path) + else: + text_br: t.CInt64T = tf.read_all(text_content, STUB_READ_BUF_SIZE) + tf.close() + stdlib.free(text_path) + if text_br > 0: + if text_br < STUB_READ_BUF_SIZE: + text_content[text_br] = '\0' + else: + text_content[STUB_READ_BUF_SIZE - 1] = '\0' + # 行级去重追加(与 _LoadAndAppendStub 相同逻辑) + tx_pos: t.CSizeT = 0 + tx_skip: int = 1 + while tx_pos < text_br: + tx_ls: t.CSizeT = tx_pos + while tx_pos < text_br: + if text_content[tx_pos] == '\n': + break + tx_pos += 1 + tx_ll: t.CSizeT = tx_pos - tx_ls + if tx_pos < text_br: + tx_pos += 1 + if tx_skip != 0: + if tx_ll == 0: + continue + tx_c0: int = text_content[tx_ls] + if tx_c0 == ';': + continue + if tx_c0 == 't' and tx_ll >= 6: + if string.strncmp(text_content + tx_ls, "target", 6) == 0: + continue + if tx_c0 == 's' and tx_ll >= 15: + if string.strncmp(text_content + tx_ls, "source_filename", 15) == 0: + continue + tx_skip = 0 + # 去重:declare(用 _IsFuncDeclaredOrDefined 避免误匹配 call 语句) + if tx_ll >= 8 and string.strncmp(text_content + tx_ls, "declare ", 8) == 0: + tx_at: t.CSizeT = tx_ls + 8 + while tx_at < tx_ls + tx_ll: + if text_content[tx_at] == '@': + break + tx_at += 1 + if tx_at < tx_ls + tx_ll: + tx_lp: t.CSizeT = tx_at + 1 + while tx_lp < tx_ls + tx_ll: + if text_content[tx_lp] == '(': + break + tx_lp += 1 + if tx_lp < tx_ls + tx_ll: + tx_sv: t.CChar = text_content[tx_lp + 1] + text_content[tx_lp + 1] = '\0' + if StubMergerIR._IsFuncDeclaredOrDefined(out_buf, text_content + tx_at) != 0: + text_content[tx_lp + 1] = tx_sv + continue + text_content[tx_lp + 1] = tx_sv + # 去重:define(注释掉 out_buf 中已有的 declare,避免 declare/define 冲突) + if tx_ll >= 7 and string.strncmp(text_content + tx_ls, "define ", 7) == 0: + tx_at_d: t.CSizeT = tx_ls + 7 + while tx_at_d < tx_ls + tx_ll: + if text_content[tx_at_d] == '@': + break + tx_at_d += 1 + if tx_at_d < tx_ls + tx_ll: + tx_lp_d: t.CSizeT = tx_at_d + 1 + while tx_lp_d < tx_ls + tx_ll: + if text_content[tx_lp_d] == '(': + break + tx_lp_d += 1 + if tx_lp_d < tx_ls + tx_ll: + tx_sv_d: t.CChar = text_content[tx_lp_d + 1] + text_content[tx_lp_d + 1] = '\0' + StubMergerIR._CommentOutDeclareInBuf(out_buf, text_content + tx_at_d) + text_content[tx_lp_d + 1] = tx_sv_d + # 处理:@global = ... + # stub 中可能是 external 声明或完整定义,以 text 中的定义为准 + # 找到 stub 中同名全局变量行并注释掉,然后追加 text 中的 + if tx_ll > 0 and text_content[tx_ls] == '@': + tx_eq: t.CSizeT = tx_ls + 1 + while tx_eq < tx_ls + tx_ll: + if text_content[tx_eq] == '=': + break + tx_eq += 1 + if tx_eq < tx_ls + tx_ll: + tx_sv2: t.CChar = text_content[tx_eq + 1] + text_content[tx_eq + 1] = '\0' + StubMergerIR._CommentOutGlobalInBuf(out_buf, text_content + tx_ls) + text_content[tx_eq + 1] = tx_sv2 + # 去重:%type = ... + if tx_ll > 0 and text_content[tx_ls] == '%': + tx_eq2: t.CSizeT = tx_ls + 1 + while tx_eq2 < tx_ls + tx_ll: + if text_content[tx_eq2] == '=': + break + tx_eq2 += 1 + if tx_eq2 < tx_ls + tx_ll: + tx_sv3: t.CChar = text_content[tx_eq2 + 1] + text_content[tx_eq2 + 1] = '\0' + if string.strstr(out_buf, text_content + tx_ls) is not None: + text_content[tx_eq2 + 1] = tx_sv3 + continue + text_content[tx_eq2 + 1] = tx_sv3 + # 追加 + if out_pos + tx_ll + 2 < out_size: + string.strncpy(out_buf + out_pos, text_content + tx_ls, tx_ll) + out_pos += tx_ll + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + stdlib.free(text_content) + + if deps_buf is not None: + stdlib.free(deps_buf) + if sha1_arr is not None: + stdlib.free(sha1_arr) + if mod_arr is not None: + stdlib.free(mod_arr) + + return out_pos +# ============================================================ +# LoadDepVpiIntoModule - 加载依赖模块的 .vpi 文件,注入 declare 到当前模块 +# +# 遍历 imported_modules(空格分隔的模块名),对每个模块: +# 1. 用 LookupSha1ByModName 获取 SHA1 +# 2. 用 _sliced_path 构造 .vpi 文件路径 +# 3. 调用 VpiReader.ParseVpiIntoModule 解析 .vpi 并注入 declare +# +# 在 _translate_module_level 中、Import 处理完成后、translate_children +# 之前调用,确保跨模块函数调用时 declare 已存在。 +# +# Args: +# pool: 内存池 +# mod: 当前 LLVM 模块 +# imported_modules: 已导入模块名字符串(空格分隔) +# from_imports: from-import 映射字符串("name:module name:module" 格式) +# temp_dir: temp 目录路径 +# trans: 翻译器对象 +# +# Returns: +# 成功加载的 .vpi 文件数 +# ============================================================ +def LoadDepVpiIntoModule(pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + imported_modules: str, + from_imports: str, + temp_dir: str, + trans: HandlesTranslator.Translator | t.CPtr) -> int: + """加载依赖模块的 .vpi 文件,将函数声明注入到当前模块""" + if pool is None or mod is None or temp_dir is None or trans is None: + return 0 + if imported_modules is None: + return 0 + + td_len: t.CSizeT = string.strlen(temp_dir) + total_len: t.CSizeT = string.strlen(imported_modules) + success_count: int = 0 + + # 遍历 imported_modules 中的每个模块名(空格分隔) + ci: t.CSizeT = 0 + while ci < total_len: + # 跳过前导空格 + while ci < total_len and imported_modules[ci] == ' ': + ci += 1 + if ci >= total_len: + break + # 提取模块名(到下一个空格或字符串末尾) + word_start: t.CSizeT = ci + while ci < total_len and imported_modules[ci] != ' ': + ci += 1 + word_len: t.CSizeT = ci - word_start + if word_len == 0: + continue + + # 复制模块名到临时缓冲区并 null 终止 + mod_nm_buf: str = stdlib.malloc(word_len + 1) + if mod_nm_buf is None: + continue + string.strncpy(mod_nm_buf, imported_modules + word_start, word_len) + mod_nm_buf[word_len] = '\0' + + # 用 LookupSha1ByModName 查找模块的 SHA1 + sha1_val: str = StubMergerStore.LookupSha1ByModName(mod_nm_buf) + + # 如果精确查找失败,尝试包名回退(如 "ast" → "ast.__init__") + # LookupSha1ByModName 内部已处理包名回退,此处无需重复 + + if sha1_val is not None: + # 构造 .vpi 文件路径 + vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1_val, "vpi") + if vpi_path is not None: + # 检查 .vpi 文件是否存在(可读) + vf: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R) + if not vf.closed: + vf.close() + # 调用 VpiReader 解析 .vpi 并注入 declare + # (错误上下文帧由调用方 HandlesTranslator 维护) + ret: int = VpiReader.ParseVpiIntoModule(pool, mod, vpi_path, trans) + if ret == 1: + success_count += 1 + stdlib.free(vpi_path) + # sha1_val 指向内部存储,无需 free + + stdlib.free(mod_nm_buf) + + return success_count diff --git a/App/lib/core/StubMergerCollect.vp b/App/lib/core/StubMergerCollect.vp new file mode 100644 index 0000000..8dcb2ec --- /dev/null +++ b/App/lib/core/StubMergerCollect.vp @@ -0,0 +1,286 @@ +import t, c +from stdint import * +import memhub +import string +import stdlib +import llvmlite +import w32.win32file as win32file +import w32.win32base as win32base +import w32.fileio as fileio +import viperlib +import sys +import ast +import lib.core.VLogger as VLogger +import lib.core.IncludesScanner as IncludesScanner +import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesImports as HandlesImports +import lib.core.Handles.HandlesTranslator as HandlesTranslator +import lib.core.BuildPipeline as BuildPipeline +import lib.StubGen.VpiReader as VpiReader +import lib.Projectrans.Utils as Utils +import lib.Projectrans.Config as Config + + +import lib.core.StubMergerStore as StubMergerStore +import lib.core.StubMergerTopo as StubMergerTopo +import lib.core.StubMergerIR as StubMergerIR +import lib.core.StubMergerBuild as StubMergerBuild +import lib.core.StubMerger as StubMerger + +# ============================================================ +# StubMergerCollect - StubMerger 拆分(阶段 E):StubMergerCollect +# +# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留 +# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经 +# StubMerger._xxx 访问(main.vp 先例)。 +# ============================================================ + +# ============================================================ +# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free) +# +# 根据 Config.Sha1SliceLevel 将文件分散到 SHA1 前缀子目录: +# level=0 → {temp_dir}/{sha1}.{ext} +# level=1 → {temp_dir}/b7/{sha1}.{ext} +# level=2 → {temp_dir}/b7/90/{sha1}.{ext} +# +# 自动调用 BuildPipeline.ensure_dir 创建子目录(level>0 时)。 +# +# Args: +# temp_dir: 基础目录 +# td_len: temp_dir 长度(避免重复 strlen) +# sha1: SHA1 字符串 +# ext: 文件扩展名(如 "stub.ll") +# +# Returns: +# 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败 +# ============================================================ +def _sliced_path(temp_dir: str, td_len: t.CSizeT, sha1: str, ext: str) -> str: + """构建切片路径(stdlib.malloc 分配,调用者负责 free) + + level>0 时自动调用 ensure_dir 创建子目录。 + """ + if temp_dir is None or sha1 is None or ext is None: + return None + sha1_len: t.CSizeT = string.strlen(sha1) + ext_len: t.CSizeT = string.strlen(ext) + + # 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free) + subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel) + sub_len: t.CSizeT = 0 + if subdir_buf is not None: + sub_len = string.strlen(subdir_buf) + + # 确保子目录存在: {temp_dir}/{subdir} + # 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True), + # 进而 BuildCombinedIR 读不到文件返回 0("BuildCombinedIR 失败")。 + dir_path_len: t.CSizeT = td_len + sub_len + 2 + dir_path: bytes = stdlib.malloc(dir_path_len) + if dir_path is not None: + viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf) + BuildPipeline.ensure_dir(dir_path) + stdlib.free(dir_path) + + # 构建完整路径 + path: str = None + path_len: t.CSizeT = 0 + if sub_len > 0: + path_len = td_len + sub_len + sha1_len + ext_len + 4 + path = stdlib.malloc(path_len) + if path is not None: + viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext) + else: + path_len = td_len + sha1_len + ext_len + 3 + path = stdlib.malloc(path_len) + if path is not None: + viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext) + + return path +# ============================================================ +# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1 +# +# 根据 level 递归扫描子目录: +# level=0 → 扫描 {base_dir}/*.stub.ll,从文件名提取 SHA1 +# level>0 → 扫描 {base_dir}/* 的子目录,对每个子目录递归调用 +# +# Args: +# base_dir: 当前扫描目录 +# bd_len: base_dir 长度 +# level: 剩余递归层数 +# out_arr: 输出数组(每个 SHA1 17 字节 = 16字符 + null) +# out_count: 当前已收集数量 +# max_count: 最大数量 +# +# Returns: +# 收集后的总数量 +# ============================================================ +def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int, + out_arr: str, out_count: int, max_count: int) -> int: + """递归扫描子目录,收集 .stub.ll 文件的 SHA1""" + if base_dir is None or out_arr is None: + return out_count + if out_count >= max_count: + return out_count + + find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__()) + if find_data is None: + return out_count + string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__()) + + if level <= 0: + # 扫描 {base_dir}/*.stub.ll + pattern: bytes = stdlib.malloc(bd_len + 16) + if pattern is None: + stdlib.free(find_data) + return out_count + viperlib.snprintf(pattern, bd_len + 16, "%s/*.stub.ll", base_dir) + handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) + stdlib.free(pattern) + if handle != win32base.INVALID_HANDLE_VALUE: + while 1: + if out_count >= max_count: + break + fname: str = find_data.cFileName + if fname is not None: + if fname[0] != '\0': + # 从文件名提取 SHA1(前 16 字符) + string.strncpy(out_arr + out_count * 17, fname, 16) + out_arr[out_count * 17 + 16] = '\0' + out_count += 1 + if win32file.FindNextFileA(handle, find_data) == 0: + break + win32file.FindClose(handle) + else: + # 扫描 {base_dir}/* 的子目录 + pattern = stdlib.malloc(bd_len + 4) + if pattern is None: + stdlib.free(find_data) + return out_count + viperlib.snprintf(pattern, bd_len + 4, "%s/*", base_dir) + handle = win32file.FindFirstFileA(pattern, find_data) + stdlib.free(pattern) + if handle != win32base.INVALID_HANDLE_VALUE: + while 1: + # 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16) + attrs: t.CUInt32T = find_data.dwFileAttributes + if(attrs & 16) != 0: + fname = find_data.cFileName + if fname is not None: + # 跳过 "." 和 ".." + is_dot: int = 0 + if fname[0] == '.': + if fname[1] == '\0': + is_dot = 1 + elif fname[1] == '.' and fname[2] == '\0': + is_dot = 1 + if is_dot == 0: + # 构建子目录路径并递归扫描 + sub_dir: str = Utils.JoinPath(base_dir, fname) + if sub_dir is not None: + sub_dir_len: t.CSizeT = string.strlen(sub_dir) + out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count) + stdlib.free(sub_dir) + if win32file.FindNextFileA(handle, find_data) == 0: + break + win32file.FindClose(handle) + + stdlib.free(find_data) + return out_count +# ============================================================ +# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1 +# +# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。 +# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。 +# ============================================================ +def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr, + temp_dir: str, + sha1_set: str) -> int: + """从全局内存存储器将 includes 的 SHA1 写入 sha1_set + + pool/temp_dir 参数保留以兼容现有调用者,但不再使用。 + 数据来源是 PopulateSha1MapStore 填充的全局存储器。 + sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null) + 返回找到的 includes SHA1 数量,-1 表示错误""" + if sha1_set is None: + return -1 + if StubMerger._sha1_store_arr is None: + return -1 + count: int = 0 + for i in range(StubMerger._sha1_store_count): + if count >= MAX_INCLUDES_SHA1: + break + idx: t.CSizeT = t.CSizeT(i) * 17 + string.strcpy(sha1_set + t.CSizeT(count) * 17, StubMerger._sha1_store_arr + idx) + count += 1 + return count +# ============================================================ +# _is_in_sha1_set - 检查 SHA1 是否在集合中 +# ============================================================ +def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int: + """检查 sha1 是否在集合中""" + if sha1 is None or sha1_set is None: + return 0 + for i in range(set_count): + if string.strcmp(sha1_set + i * 17, sha1) == 0: + return 1 + return 0 +# ============================================================ +# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出) +# +# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。 +# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器。 +# +# Args: +# mb: 内存池 +# temp_dir: 临时目录(_sha1_map.txt 写入位置) +# scan_result: IncludesScanner 扫描结果 +# +# Returns: +# 0 成功,非 0 失败 +# ============================================================ +def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str, + scan_result: IncludesScanner.ScanResult | t.CPtr, + filter_set: str, + filter_count: int) -> int: + """将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器) + + 若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。 + """ + if temp_dir is None or scan_result is None: + return 1 + + # 构造路径 temp_dir/_sha1_map.txt + dir_len: t.CSizeT = string.strlen(temp_dir) + map_path: bytes = stdlib.malloc(dir_len + 32) + if map_path is None: + return 1 + viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir) + + # 打开文件写入(CREATE_ALWAYS) + f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.W) + if f.closed: + return 1 + + # 写入每个 include 条目: {sha1}:includes/{rel_path}\n + entry_size_w: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + line_buf: bytes = stdlib.malloc(512) + if line_buf is None: + f.close() + return 1 + + written_count: int = 0 + for i in range(scan_result.Count): + ea_w: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_w + ent_w: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_w, t.CPtr)) + if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: + continue + # 若提供过滤集合,只写入集合中的条目 + if filter_set is not None and filter_count > 0: + if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: + continue + viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath) + line_len_w: t.CSizeT = string.strlen(line_buf) + f.write(line_buf, line_len_w) + written_count += 1 + + f.close() + return 0 diff --git a/App/lib/core/StubMergerIR.vp b/App/lib/core/StubMergerIR.vp new file mode 100644 index 0000000..de7322c --- /dev/null +++ b/App/lib/core/StubMergerIR.vp @@ -0,0 +1,416 @@ +import t, c +from stdint import * +import memhub +import string +import stdlib +import llvmlite +import w32.win32file as win32file +import w32.win32base as win32base +import w32.fileio as fileio +import viperlib +import sys +import ast +import lib.core.VLogger as VLogger +import lib.core.IncludesScanner as IncludesScanner +import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesImports as HandlesImports +import lib.core.Handles.HandlesTranslator as HandlesTranslator +import lib.core.BuildPipeline as BuildPipeline +import lib.StubGen.VpiReader as VpiReader +import lib.Projectrans.Utils as Utils +import lib.Projectrans.Config as Config + + +import lib.core.StubMergerCollect as StubMergerCollect +import lib.core.StubMergerStore as StubMergerStore +import lib.core.StubMergerTopo as StubMergerTopo +import lib.core.StubMergerBuild as StubMergerBuild +import lib.core.StubMerger as StubMerger + +# ============================================================ +# StubMergerIR - StubMerger 拆分(阶段 E):StubMergerIR +# +# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留 +# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经 +# StubMerger._xxx 访问(main.vp 先例)。 +# ============================================================ + +# ============================================================ +# IsDeclOnlyModule - 判断模块是否为纯声明文件 +# +# 读取 {temp_dir}/{sha1}.text.ll,扫描是否含 "define " 行: +# 返回 1 = 纯声明(无函数体,如 stdio.vp 全 pass),无需 .obj +# 返回 0 = 实现文件(含 define 函数体) +# 返回 -1 = text.ll 不存在/无法判断 +# +# 与 Phase B+(Phase2.vp 编译缺失 includes 时)的内联判断逻辑一致, +# 供 Phase C 链接时区分"声明文件无需 .obj"与"实现文件缺失"。 +# ============================================================ +def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int: + """判断模块是否为纯声明文件(text.ll 无 define 行)""" + if temp_dir is None or sha1 is None: + return -1 + tpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "text.ll") + if tpath is None: + return -1 + tf_d: fileio.File | t.CPtr = fileio.File(tpath, fileio.MODE.R) + stdlib.free(tpath) + if tf_d.closed: + return -1 + tbuf_d: bytes = stdlib.malloc(STUB_READ_BUF_SIZE) + tbr_d: t.CInt64T = 0 + if tbuf_d is not None: + tbr_d = tf_d.read_all(tbuf_d, STUB_READ_BUF_SIZE) + tf_d.close() + if tbuf_d is None: + return -1 + if tbr_d <= 0: + # 空 text.ll:无函数体,纯声明 + stdlib.free(tbuf_d) + return 1 + if tbr_d < STUB_READ_BUF_SIZE: + tbuf_d[tbr_d] = '\0' + else: + tbuf_d[STUB_READ_BUF_SIZE - 1] = '\0' + # 逐行扫描:找 "define " 行即为实现文件 + tpos_d: t.CSizeT = 0 + while tpos_d < tbr_d: + tls_d: t.CSizeT = tpos_d + while tpos_d < tbr_d: + if tbuf_d[tpos_d] == '\n': + break + tpos_d += 1 + tll_d: t.CSizeT = tpos_d - tls_d + if tpos_d < tbr_d: + tpos_d += 1 + if tll_d >= 7 and string.strncmp(tbuf_d + tls_d, "define ", 7) == 0: + stdlib.free(tbuf_d) + return 0 + stdlib.free(tbuf_d) + return 1 +# ============================================================ +# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define +# +# 逐字节strncmp搜索 func_sig(如 @"sha1.func"(/ @printf( ),每次 +# 找到匹配后检查行首是否以 "declare " 或 "define " 开头,跳过 call 语句。 +# 避免 strstr 子串匹配误匹配 call ... @"func"(...) 中的 @"func"( 子串。 +# +# Args: +# out_buf: 已有的 IR 文本缓冲区 +# func_sig: 函数签名片段,如 @"sha1.func"(/ @printf( +# +# Returns: +# 1=已存在 declare/define, 0=不存在 +# ============================================================ +def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int: + """检查 out_buf 中是否已有函数的 declare 或 define 行""" + if out_buf is None or func_sig is None: + return 0 + sig_len: t.CSizeT = string.strlen(func_sig) + out_len: t.CSizeT = string.strlen(out_buf) + if sig_len == 0 or out_len == 0 or sig_len > out_len: + return 0 + pos: t.CSizeT = 0 + while pos + sig_len <= out_len: + if string.strncmp(out_buf + pos, func_sig, sig_len) == 0: + ls: t.CSizeT = pos + while ls > 0 and out_buf[ls - 1] != '\n': + ls -= 1 + if string.strncmp(out_buf + ls, "declare ", 8) == 0: + return 1 + if string.strncmp(out_buf + ls, "define ", 7) == 0: + return 1 + pos += 1 + return 0 +# ============================================================ +# _CommentOutDeclareInBuf - 将 out_buf 中匹配 func_sig 的 declare 行注释掉 +# +# 用于解决 declare/define 冲突:当本地 text.ll 包含 define 而依赖 +# text.ll 已生成了同函数的 declare 时,将 declare 行首字符改为 ';' +# 使其成为注释,然后追加 define 行。 +# ============================================================ +def _CommentOutDeclareInBuf(out_buf: bytes, func_sig: str) -> int: + """在 out_buf 中搜索 declare 行(包含 func_sig),将其注释掉""" + if out_buf is None or func_sig is None: + return 0 + sig_len: t.CSizeT = string.strlen(func_sig) + out_len: t.CSizeT = string.strlen(out_buf) + if sig_len == 0 or out_len == 0 or sig_len > out_len: + return 0 + pos: t.CSizeT = 0 + while pos + sig_len <= out_len: + if string.strncmp(out_buf + pos, func_sig, sig_len) == 0: + ls: t.CSizeT = pos + while ls > 0 and out_buf[ls - 1] != '\n': + ls -= 1 + if string.strncmp(out_buf + ls, "declare ", 8) == 0: + out_buf[ls] = ';' + return 1 + pos += 1 + return 0 +# ============================================================ +# _CommentOutGlobalInBuf - 将 out_buf 中匹配全局变量名的行注释掉 +# +# 解决 stub/text 全局变量重复定义问题:stub 中可能是 external 声明 +# 或完整定义,以 text 中的定义为准,将 stub 中的同名行注释掉。 +# ============================================================ +def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int: + """在 out_buf 中搜索全局变量定义行(以 global_name 开头),将行首改为 ';' + + 注释掉**所有**匹配行(而非仅第一个):跨模块同名裸全局(如 main.vp 与 + argparse.vp 都声明 StubMerger._mbuddy)时,本地 stub external + 依赖 stub external + + 依赖 text 定义会同时存在。只注释第一个会导致依赖 text 的定义残留 → + 合成 IR 重复定义 → llc "redefinition of global '@StubMerger._mbuddy'"(ArgparseTest + 回归根因)。"以 text 中定义为准"的语义:后追加者注释掉全部旧行,最终 + 只保留最后一个定义。 + """ + if out_buf is None or global_name is None: + return 0 + name_len: t.CSizeT = string.strlen(global_name) + out_len: t.CSizeT = string.strlen(out_buf) + if name_len == 0 or out_len == 0 or name_len > out_len: + return 0 + pos: t.CSizeT = 0 + commented: int = 0 + while pos + name_len <= out_len: + if string.strncmp(out_buf + pos, global_name, name_len) == 0: + # 确认是行首(前一个是换行或 pos==0) + if pos == 0 or out_buf[pos - 1] == '\n': + out_buf[pos] = ';' + commented = 1 + pos += 1 + return commented +def _HasGlobalInBuf(out_buf: bytes, global_name: str) -> int: + """检测 out_buf 中是否已有同名全局变量行(行首匹配,未注释) + + 用于跳过重复追加依赖全局强定义:stub 注入已提供 external 声明, + 依赖 text.ll 的强定义只需在定义模块存在一份,复制进每个依赖方 + 会导致 ld.lld duplicate symbol(如 _op_head、MemBuddy.vtable)。 + """ + if out_buf is None or global_name is None: + return 0 + name_len: t.CSizeT = string.strlen(global_name) + out_len: t.CSizeT = string.strlen(out_buf) + if name_len == 0 or out_len == 0 or name_len > out_len: + return 0 + pos: t.CSizeT = 0 + while pos + name_len <= out_len: + if string.strncmp(out_buf + pos, global_name, name_len) == 0: + # 确认是行首(前一个是换行或 pos==0) + if pos == 0 or out_buf[pos - 1] == '\n': + return 1 + pos += 1 + return 0 +# ============================================================ +# _CommentOutOpaqueTypeInBuf - 将 out_buf 中匹配类型名的 opaque 声明行注释掉 +# +# 解决跨模块类型定义冲突:本地 stub 可能包含自动生成的 opaque 声明 +#(如 %"sha1.Widget" = type opaque),而依赖 stub 包含完整定义 +#(如 %"sha1.Widget" = type { i8*, i32 })。去重时需要将 opaque 声明 +# 注释掉,让完整定义被追加,否则 LLC 报错 "Cannot allocate unsized type"。 +# ============================================================ +def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int: + """在 out_buf 中搜索 type_name_prefix 对应的 opaque 声明行,将行首改为 ';' + + type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=' 和后续内容) + 返回 1=已注释, 0=未找到 + """ + if out_buf is None or type_name_prefix is None: + return 0 + prefix_len: t.CSizeT = string.strlen(type_name_prefix) + out_len: t.CSizeT = string.strlen(out_buf) + if prefix_len == 0 or out_len == 0 or prefix_len > out_len: + return 0 + pos: t.CSizeT = 0 + while pos + prefix_len <= out_len: + if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0: + # 确认是行首(前一个是换行或 pos==0) + if pos == 0 or out_buf[pos - 1] == '\n': + # 检查后面是否是 " = type opaque" + p: t.CSizeT = pos + prefix_len + # 跳过空格 + while p < out_len and out_buf[p] == ' ': + p += 1 + if p < out_len and out_buf[p] == '=': + p += 1 + while p < out_len and out_buf[p] == ' ': + p += 1 + if p + 11 <= out_len: + if string.strncmp(out_buf + p, "type opaque", 11) == 0: + # 找到 opaque 声明,将行首改为 ';' + out_buf[pos] = ';' + return 1 + pos += 1 + return 0 +# ============================================================ +# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块 +# +# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp +# 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer 子模块类型。 +# 当模块名包含 '.' 时,检查其顶层包名是否在 deps_buf 中。 +# +# 例如: +# deps_buf = "ast stdio string" +# mod_name = "ast.lexer" → 顶层包 "ast" 在 deps_buf 中 → 返回 1 +# mod_name = "ast.token" → 顶层包 "ast" 在 deps_buf 中 → 返回 1 +# mod_name = "foo.bar" → 顶层包 "foo" 不在 deps_buf 中 → 返回 0 +# ============================================================ +def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int: + """检查 mod_name 是否是 deps_buf 中已导入包的子模块""" + if deps_buf is None or mod_name is None: + return 0 + # 查找第一个 '.',提取顶层包名 + dot_pos: str = string.strchr(mod_name, 46) # '.' = 46 + if dot_pos is None: + return 0 + pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(mod_name)) + if pkg_len == 0 or pkg_len >= 256: + return 0 + # 构造顶层包名(截断到 '.' 之前) + # 直接在 mod_name 上临时截断检查,避免分配 + saved_ch: t.CChar = mod_name[pkg_len] + mod_name[pkg_len] = '\0' + result: int = HandlesImports.is_module_imported(deps_buf, mod_name) + mod_name[pkg_len] = saved_ch + return result +# ============================================================ +# _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque) +# +# type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=') +# 完整定义: %"name" = type { ... } 或 %"name" = type < { ... } > +# 返回 1=已有完整定义, 0=无完整定义 +# ============================================================ +def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int: + """检查 out_buf 中是否已有 type_name_prefix 的完整定义""" + if out_buf is None or type_name_prefix is None: + return 0 + prefix_len: t.CSizeT = string.strlen(type_name_prefix) + out_len: t.CSizeT = string.strlen(out_buf) + if prefix_len == 0 or out_len == 0 or prefix_len > out_len: + return 0 + pos: t.CSizeT = 0 + while pos + prefix_len <= out_len: + if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0: + # 确认是行首(前一个是换行或 pos==0),且未被注释(行首不是 ';') + is_line_start: int = 0 + if pos == 0: + is_line_start = 1 + elif out_buf[pos - 1] == '\n': + is_line_start = 1 + if is_line_start == 1: + # 检查后面是否是 " = type {" 或 " = type <" + p: t.CSizeT = pos + prefix_len + while p < out_len and out_buf[p] == ' ': + p += 1 + if p < out_len and out_buf[p] == '=': + p += 1 + while p < out_len and out_buf[p] == ' ': + p += 1 + if p + 5 < out_len: + if string.strncmp(out_buf + p, "type ", 5) == 0: + p += 5 + if p < out_len: + if out_buf[p] == '{' or out_buf[p] == '<': + return 1 + pos += 1 + return 0 +# ============================================================ +# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射 +# +# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。 +# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。 +# 所有内存由 stdlib.malloc 分配,调用者负责释放。 +# ============================================================ +def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT, + sha1_list: str, + mod_list: str) -> int: + """从全局内存存储器复制 sha1→module_name 映射到调用者数组 + + temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。 + 数据来源是 PopulateSha1MapStore 填充的全局存储器。 + """ + if sha1_list is None or mod_list is None: + return 0 + if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: + return 0 + count: int = 0 + for i in range(StubMerger._sha1_store_count): + if count >= MAX_INCLUDES: + break + idx: t.CSizeT = t.CSizeT(i) * 17 + idx2: t.CSizeT = t.CSizeT(i) * 64 + string.strcpy(sha1_list + t.CSizeT(count) * 17, StubMerger._sha1_store_arr + idx) + string.strcpy(mod_list + t.CSizeT(count) * 64, StubMerger._sha1_store_mod + idx2) + count += 1 + return count +# ============================================================ +# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配) +# +# 在 sha1_arr/mod_arr 映射中查找模块名对应的 SHA1。 +# 用于依赖图分析:模块名 → SHA1。 +# +# Args: +# sha1_arr: SHA1 数组(每个 17 字节) +# mod_arr: 模块名数组(每个 64 字节) +# count: 映射条目数 +# mod_name: 要查找的模块名 +# +# Returns: +# SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None +# ============================================================ +def _FindSha1ByModName(sha1_arr: str, mod_arr: str, + count: int, mod_name: str) -> str: + """从模块名查找 SHA1(精确匹配 + 包名回退) + + 精确匹配失败时,尝试 "{mod_name}.__init__" 回退, + 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。 + """ + if sha1_arr is None or mod_arr is None or mod_name is None: + return None + if count <= 0: + return None + # 第一遍:精确匹配 + for i in range(count): + idx: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(mod_arr + idx, mod_name) == 0: + sidx: t.CSizeT = t.CSizeT(i) * 17 + return sha1_arr + sidx + # 第二遍:包名回退("ast" → "ast.__init__") + nm_len: t.CSizeT = string.strlen(mod_name) + init_buf: bytes = stdlib.malloc(nm_len + 12) + if init_buf is not None: + viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name) + for i in range(count): + idx2: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(mod_arr + idx2, init_buf) == 0: + sidx2: t.CSizeT = t.CSizeT(i) * 17 + stdlib.free(init_buf) + return sha1_arr + sidx2 + stdlib.free(init_buf) + return None +# ============================================================ +# _AddSha1ToSet - 将 SHA1 加入集合(去重) +# +# 检查 SHA1 是否已在集合中,若不在则加入。返回新的 count。 +# +# Args: +# set_buf: SHA1 集合缓冲区(每个 17 字节) +# count: 当前集合中的 SHA1 数量 +# sha1: 要加入的 SHA1 +# +# Returns: +# 新的 count(如果已存在则不变) +# ============================================================ +def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int: + """将 SHA1 加入集合,返回新 count""" + if set_buf is None or sha1 is None: + return count + for i in range(count): + idx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(set_buf + idx, sha1) == 0: + return count + if count < MAX_INCLUDES_SHA1: + idx: t.CSizeT = t.CSizeT(count) * 17 + string.strcpy(set_buf + idx, sha1) + return count + 1 + return count diff --git a/App/lib/core/StubMergerStore.vp b/App/lib/core/StubMergerStore.vp new file mode 100644 index 0000000..9319c2c --- /dev/null +++ b/App/lib/core/StubMergerStore.vp @@ -0,0 +1,409 @@ +import t, c +from stdint import * +import memhub +import string +import stdlib +import llvmlite +import w32.win32file as win32file +import w32.win32base as win32base +import w32.fileio as fileio +import viperlib +import sys +import ast +import lib.core.VLogger as VLogger +import lib.core.IncludesScanner as IncludesScanner +import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesImports as HandlesImports +import lib.core.Handles.HandlesTranslator as HandlesTranslator +import lib.core.BuildPipeline as BuildPipeline +import lib.StubGen.VpiReader as VpiReader +import lib.Projectrans.Utils as Utils +import lib.Projectrans.Config as Config + + +import lib.core.StubMergerCollect as StubMergerCollect +import lib.core.StubMergerTopo as StubMergerTopo +import lib.core.StubMergerIR as StubMergerIR +import lib.core.StubMergerBuild as StubMergerBuild +import lib.core.StubMerger as StubMerger + +# ============================================================ +# StubMergerStore - StubMerger 拆分(阶段 E):StubMergerStore +# +# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留 +# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经 +# StubMerger._xxx 访问(main.vp 先例)。 +# ============================================================ + +# ============================================================ +# PopulateSha1MapStore - 从扫描结果填充全局 SHA1 映射存储器 +# +# 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。 +# 模块名通过 _PathToModuleName(includes/rel_path) 计算。 +# 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。 +# +# Args: +# scan_result: IncludesScanner 扫描结果 +# +# Returns: +# 填充的条目数,-1 表示失败 +# ============================================================ +def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> int: + """从扫描结果填充全局 SHA1 映射存储器 + + 遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。 + 模块名通过 _PathToModuleName(includes/rel_path) 计算。 + 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。 + """ + if scan_result is None: + return -1 + # 释放旧存储器 + if StubMerger._sha1_store_arr is not None: + stdlib.free(StubMerger._sha1_store_arr) + if StubMerger._sha1_store_mod is not None: + stdlib.free(StubMerger._sha1_store_mod) + if StubMerger._sha1_store_rel is not None: + stdlib.free(StubMerger._sha1_store_rel) + if StubMerger._sha1_store_incdir is not None: + stdlib.free(StubMerger._sha1_store_incdir) + StubMerger._sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17) + StubMerger._sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64) + StubMerger._sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN) + StubMerger._sha1_store_incdir = stdlib.malloc(MAX_INCLUDES * MAX_INC_DIR_LEN) + if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: + return -1 + string.memset(StubMerger._sha1_store_arr, 0, MAX_INCLUDES * 17) + string.memset(StubMerger._sha1_store_mod, 0, MAX_INCLUDES * 64) + string.memset(StubMerger._sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN) + string.memset(StubMerger._sha1_store_incdir, 0, MAX_INCLUDES * MAX_INC_DIR_LEN) + StubMerger._sha1_store_count = 0 + p_bad: int = 0 + entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + for i in range(scan_result.Count): + if StubMerger._sha1_store_count >= MAX_INCLUDES: + VLogger.error("includes SHA1 存储超上限,终止", "StubMerger") + ea: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_p + ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) + if ent is None or ent.Sha1 is None or ent.RelPath is None: + p_bad += 1 + continue + # 构建完整路径 includes/{RelPath}(_PathToModuleName 需要) + rp_len: t.CSizeT = string.strlen(ent.RelPath) + full_path_buf: bytes = stdlib.malloc(rp_len + 10) + if full_path_buf is None: + p_bad += 1 + continue + viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath) + mod_name: str = Utils.PathToModuleName(full_path_buf, 1) + stdlib.free(full_path_buf) + if mod_name is not None: + idx: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 17 + string.strcpy(StubMerger._sha1_store_arr + idx, ent.Sha1) + idx2: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 64 + mn_len: t.CSizeT = string.strlen(mod_name) + if mn_len < 64: + string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name) + else: + string.strncpy(StubMerger._sha1_store_mod + idx2, mod_name, 63) + StubMerger._sha1_store_mod[idx2 + 63] = '\0' + stdlib.free(mod_name) + # 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用) + idx3: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * MAX_REL_PATH_LEN + if rp_len < MAX_REL_PATH_LEN: + string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath) + else: + string.strncpy(StubMerger._sha1_store_rel + idx3, ent.RelPath, MAX_REL_PATH_LEN - 1) + StubMerger._sha1_store_rel[idx3 + MAX_REL_PATH_LEN - 1] = '\0' + # 保存所属 includes 根目录(Phase2 预注册/按需编译拼路径用) + idx_inc: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * MAX_INC_DIR_LEN + if ent.IncDir is not None: + icd_len: t.CSizeT = string.strlen(ent.IncDir) + if icd_len < MAX_INC_DIR_LEN: + string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir) + else: + string.strncpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir, MAX_INC_DIR_LEN - 1) + StubMerger._sha1_store_incdir[idx_inc + MAX_INC_DIR_LEN - 1] = '\0' + StubMerger._sha1_store_count += 1 + if p_bad > 0: + fb_pbad: str = VLogger.fmt_buf() + if fb_pbad is not None: + viperlib.snprintf(fb_pbad, 1024, "includes SHA1 存储有 %d 个条目失败", p_bad) + VLogger.error(fb_pbad, "StubMerger") + return StubMerger._sha1_store_count +# ============================================================ +# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器 +# +# Phase1 调用 PopulateSha1MapStore 填充 includes 文件, +# Phase2 扫描 App 源文件后调用此函数追加 App 模块条目, +# 确保编译 App 自身时跨模块 CDefine 查找能命中 App 模块 +# (如 StubMerger.MAX_INCLUDES 在编译 Phase2 时通过 +# _build_source_path_from_sha1 查找 StubMerger 的源路径)。 +# +# 若存储器未初始化(PopulateSha1MapStore 未调用),自动初始化。 +# 若 SHA1 已存在(去重),跳过。 +# +# Args: +# sha1: SHA1 字符串(16 字符 + null) +# rel_path: 相对路径(如 "lib/core/StubMerger.vp", +# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致) +# +# Returns: +# 0 成功(含去重跳过),1 失败 +# ============================================================ +def AppendToSha1MapStore(sha1: str, rel_path: str) -> int: + """追加条目到全局 SHA1 映射存储器(Phase2 用)""" + if sha1 is None or rel_path is None: + return 1 + # 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用) + if StubMerger._sha1_store_arr is None: + StubMerger._sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17) + StubMerger._sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64) + StubMerger._sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN) + StubMerger._sha1_store_incdir = stdlib.malloc(MAX_INCLUDES * MAX_INC_DIR_LEN) + if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: + return 1 + string.memset(StubMerger._sha1_store_arr, 0, MAX_INCLUDES * 17) + string.memset(StubMerger._sha1_store_mod, 0, MAX_INCLUDES * 64) + string.memset(StubMerger._sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN) + string.memset(StubMerger._sha1_store_incdir, 0, MAX_INCLUDES * MAX_INC_DIR_LEN) + StubMerger._sha1_store_count = 0 + if StubMerger._sha1_store_count >= MAX_INCLUDES: + return 1 + # 去重:若 SHA1 已存在则跳过 + for i in range(StubMerger._sha1_store_count): + sidx_d: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0: + return 0 + # 模块名:rel_path "lib/core/StubMerger.vp" → "lib.core.StubMerger" + full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10) + if full_path_buf is None: + return 1 + viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path) + mod_name: str = Utils.PathToModuleName(full_path_buf, 1) + stdlib.free(full_path_buf) + if mod_name is None: + return 1 + # 写入 SHA1 + idx_a: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 17 + string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1) + # 写入模块名 + idx_m: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 64 + mn_len: t.CSizeT = string.strlen(mod_name) + if mn_len < 64: + string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name) + else: + string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63) + StubMerger._sha1_store_mod[idx_m + 63] = '\0' + stdlib.free(mod_name) + # 写入相对路径 + idx_r: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * MAX_REL_PATH_LEN + rp_len: t.CSizeT = string.strlen(rel_path) + if rp_len < MAX_REL_PATH_LEN: + string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path) + else: + string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, MAX_REL_PATH_LEN - 1) + StubMerger._sha1_store_rel[idx_r + MAX_REL_PATH_LEN - 1] = '\0' + # App 源文件不属于任何 includes 目录:IncDir 留空(Phase2 回退 includes_dir 参数) + idx_inc_a: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * MAX_INC_DIR_LEN + StubMerger._sha1_store_incdir[idx_inc_a] = '\0' + StubMerger._sha1_store_count += 1 + return 0 +# ============================================================ +# GetSha1StoreCount - 获取全局存储器条目数 +# ============================================================ +def GetSha1StoreCount() -> int: + """返回全局 SHA1 存储器的条目数""" + return StubMerger._sha1_store_count +# ============================================================ +# LookupSha1ByModName - 从模块名查找 SHA1(直接查全局存储器) +# +# 精确匹配失败时,尝试 "{mod_name}.__init__" 回退, +# 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。 +# +# Returns: +# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到 +# ============================================================ +def LookupSha1ByModName(mod_name: str) -> str: + """从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName""" + return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name) +# ============================================================ +# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1 +# +# 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点), +# 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。 +# 包含三种匹配方式:精确、后缀、反向后缀。 +# +# Returns: +# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到 +# ============================================================ +def LookupSha1ByModNameSuffix(mod_suffix: str) -> str: + """后缀匹配查找模块 SHA1""" + if mod_suffix is None: + return None + if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: + return None + if StubMerger._sha1_store_count <= 0: + return None + suffix_len_sf: t.CSizeT = string.strlen(mod_suffix) + if suffix_len_sf == 0: + return None + i_sf: int + for i_sf in range(StubMerger._sha1_store_count): + idx_sf: t.CSizeT = t.CSizeT(i_sf) * 64 + mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf + mod_len_sf: t.CSizeT = string.strlen(mod_name_sf) + if mod_len_sf == 0: + continue + # 精确匹配 + if mod_len_sf == suffix_len_sf: + if string.strcmp(mod_name_sf, mod_suffix) == 0: + sidx_sf: t.CSizeT = t.CSizeT(i_sf) * 17 + return StubMerger._sha1_store_arr + sidx_sf + # 后缀匹配:mod_name 以 "." + mod_suffix 结尾 + if mod_len_sf > suffix_len_sf: + if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.': + cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf + if string.strcmp(cmp_start_sf, mod_suffix) == 0: + sidx2_sf: t.CSizeT = t.CSizeT(i_sf) * 17 + return StubMerger._sha1_store_arr + sidx2_sf + # 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾 + if suffix_len_sf > mod_len_sf: + if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.': + cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf + if string.strcmp(cmp_start_rev_sf, mod_name_sf) == 0: + sidx_rev_sf: t.CSizeT = t.CSizeT(i_sf) * 17 + return StubMerger._sha1_store_arr + sidx_rev_sf + return None +# ============================================================ +# PopulateIncludesDeps - 将 includes 文件的依赖存入内存 +# +# Phase B+ 前用 declare_only 翻译 includes 文件,提取 _imported_modules +# 后调用此函数存入内存。_BuildReachableSha1Set 从内存读取,不读 deps.txt。 +# +# Args: +# sha1: includes 文件的 SHA1(16字符) +# deps_str: 依赖模块名字符串(空格分隔,如 "t c stdint string") +# +# Returns: +# 0 成功,非 0 失败 +# ============================================================ +def PopulateIncludesDeps(sha1: str, deps_str: str) -> int: + """将 includes 文件的依赖存入内存""" + if sha1 is None: + return 1 + # 延迟初始化 + if StubMerger._includes_deps_sha1 is None: + StubMerger._includes_deps_sha1 = stdlib.malloc(MAX_INCLUDES * 17) + StubMerger._includes_deps_str = stdlib.malloc(MAX_INCLUDES * MAX_DEPS_STR_LEN) + if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: + return 1 + string.memset(StubMerger._includes_deps_sha1, 0, MAX_INCLUDES * 17) + string.memset(StubMerger._includes_deps_str, 0, MAX_INCLUDES * MAX_DEPS_STR_LEN) + StubMerger._includes_deps_count = 0 + if StubMerger._includes_deps_count >= MAX_INCLUDES: + return 1 + # 去重:若 SHA1 已存在则跳过 + for i in range(StubMerger._includes_deps_count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0: + return 0 + # 写入 SHA1 + idx: t.CSizeT = t.CSizeT(StubMerger._includes_deps_count) * 17 + string.strcpy(StubMerger._includes_deps_sha1 + idx, sha1) + # 写入依赖字符串 + idx2: t.CSizeT = t.CSizeT(StubMerger._includes_deps_count) * MAX_DEPS_STR_LEN + if deps_str is not None: + dl: t.CSizeT = string.strlen(deps_str) + if dl < MAX_DEPS_STR_LEN: + string.strcpy(StubMerger._includes_deps_str + idx2, deps_str) + else: + string.strncpy(StubMerger._includes_deps_str + idx2, deps_str, MAX_DEPS_STR_LEN - 1) + else: + StubMerger._includes_deps_str[idx2] = '\0' + StubMerger._includes_deps_count = StubMerger._includes_deps_count + 1 + return 0 +# ============================================================ +# LookupIncludesDeps - 从内存查找 includes 文件的依赖 +# +# Args: +# sha1: includes 文件的 SHA1(16字符) +# +# Returns: +# 依赖模块名字符串指针(空格分隔),None 表示未找到 +# ============================================================ +def LookupIncludesDeps(sha1: str) -> str: + """从内存查找 includes 文件的依赖""" + if sha1 is None or StubMerger._includes_deps_sha1 is None: + return None + if StubMerger._includes_deps_count <= 0: + return None + for i in range(StubMerger._includes_deps_count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0: + idx2: t.CSizeT = t.CSizeT(i) * MAX_DEPS_STR_LEN + return StubMerger._includes_deps_str + idx2 + return None +def GetSha1StoreArrPtr() -> bytes | t.CPtr: + """返回 SHA1 数组指针(每个条目 17 字节)""" + return StubMerger._sha1_store_arr +def GetSha1StoreModArrPtr() -> bytes | t.CPtr: + """返回模块名数组指针(每个条目 64 字节)""" + return StubMerger._sha1_store_mod +def GetSha1StoreRelArrPtr() -> bytes | t.CPtr: + """返回相对路径数组指针(每个条目 MAX_REL_PATH_LEN 字节)""" + return StubMerger._sha1_store_rel +def GetSha1StoreIncdirArrPtr() -> bytes | t.CPtr: + """返回所属 includes 根目录数组指针(每个条目 MAX_INC_DIR_LEN 字节,空串表示不属于任何 includes 目录)""" + return StubMerger._sha1_store_incdir +# ============================================================ +# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用) +# +# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。 +# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*MAX_REL_PATH_LEN] +# 内存归全局存储器所有,调用者不要 free +# ============================================================ +def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int: + """获取全局存储器数组指针 + + out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针, + 函数将存储器数组指针写入这些位置。 + 返回条目数,-1 失败 + """ + if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: + return -1 + if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None: + return -1 + sp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_sha1_arr, t.CPtr)) + if sp is not None: + sp[0] = StubMerger._sha1_store_arr + mp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_mod_arr, t.CPtr)) + if mp is not None: + mp[0] = StubMerger._sha1_store_mod + rp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_rel_arr, t.CPtr)) + if rp is not None: + rp[0] = StubMerger._sha1_store_rel + return StubMerger._sha1_store_count +# ============================================================ +# LookupSha1RelPath - 通过 SHA1 查找相对路径 +# +# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free)。 +# 用于错误提示:将 sha1 转换为人类可读的文件路径。 +# +# Args: +# sha1: SHA1 字符串(16 字符) +# +# Returns: +# 相对路径字符串指针(只读),None=未找到 +# ============================================================ +def LookupSha1RelPath(sha1: str) -> str: + """通过 SHA1 查找相对路径,返回只读指针,None=未找到""" + if sha1 is None: + return None + if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None: + return None + for i in range(StubMerger._sha1_store_count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(StubMerger._sha1_store_arr + sidx, sha1) == 0: + ridx: t.CSizeT = t.CSizeT(i) * MAX_REL_PATH_LEN + return StubMerger._sha1_store_rel + ridx + return None diff --git a/App/lib/core/StubMergerTopo.vp b/App/lib/core/StubMergerTopo.vp new file mode 100644 index 0000000..711604a --- /dev/null +++ b/App/lib/core/StubMergerTopo.vp @@ -0,0 +1,168 @@ +import t, c +from stdint import * +import memhub +import string +import stdlib +import llvmlite +import w32.win32file as win32file +import w32.win32base as win32base +import w32.fileio as fileio +import viperlib +import sys +import ast +import lib.core.VLogger as VLogger +import lib.core.IncludesScanner as IncludesScanner +import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesImports as HandlesImports +import lib.core.Handles.HandlesTranslator as HandlesTranslator +import lib.core.BuildPipeline as BuildPipeline +import lib.StubGen.VpiReader as VpiReader +import lib.Projectrans.Utils as Utils +import lib.Projectrans.Config as Config + + +import lib.core.StubMergerCollect as StubMergerCollect +import lib.core.StubMergerStore as StubMergerStore +import lib.core.StubMergerIR as StubMergerIR +import lib.core.StubMergerBuild as StubMergerBuild +import lib.core.StubMerger as StubMerger + +# ============================================================ +# StubMergerTopo - StubMerger 拆分(阶段 E):StubMergerTopo +# +# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留 +# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经 +# StubMerger._xxx 访问(main.vp 先例)。 +# ============================================================ + +# ============================================================ +# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先) +# +# 背景:跨模块 vtable 生成要求被依赖模块先完整翻译(如 astaux.vp 继承 +# base.vp 的 AST,vtable 槽位需 AST.accept 的 TypeStr 已注册到全局表)。 +# Phase B+ 原先按 store 扫描顺序编译,依赖顺序不保证 → fail-fast 暴露。 +# +# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中)。 +# indegree[m] = m 在可达集合内的直接依赖数; +# 入度为 0(不依赖任何可达模块)者先输出,输出后对依赖它的模块 +# 减入度,入度归 0 者入队。结果即"被依赖者先编译"。 +# reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。 +# +# Args: +# store_count / store_sha1_arr / store_mod_arr: store 元数据 +# reachable_set / reachable_count: 可达集合(_BuildReachableSha1Set 输出) +# sorted_idx: 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引 +# +# Returns: +# 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译) +# ============================================================ +def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, + store_mod_arr: bytes, reachable_set: bytes, + reachable_count: int, + sorted_idx: t.CSizeT | t.CPtr) -> int: + """对可达 includes 逆拓扑排序(被依赖者先),store 索引写入 sorted_idx""" + if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None: + return 0 + indegree: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1))) + queue: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1))) + rel_idx: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1))) + if indegree is None or queue is None or rel_idx is None: + if indegree is not None: + stdlib.free(indegree) + if queue is not None: + stdlib.free(queue) + if rel_idx is not None: + stdlib.free(rel_idx) + return 0 + # 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为) + rel_count: int = 0 + i_t: int + if reachable_set is None or reachable_count <= 0: + for i_t in range(store_count): + rel_idx[rel_count] = i_t + rel_count += 1 + else: + for i_t in range(store_count): + if StubMergerCollect._is_in_sha1_set(store_sha1_arr + t.CSizeT(i_t) * 17, reachable_set, reachable_count) != 0: + rel_idx[rel_count] = i_t + rel_count += 1 + # 计算入度:m 在可达集合内的直接依赖数 + i_r: int + for i_r in range(rel_count): + si: int = rel_idx[i_r] + deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + t.CSizeT(si) * 17) + if deps_si is None: + continue + dl_si: t.CSizeT = string.strlen(deps_si) + dpos: t.CSizeT = 0 + while dpos < dl_si: + dstart: t.CSizeT = dpos + while dpos < dl_si and deps_si[dpos] != ' ': + dpos += 1 + dlen: t.CSizeT = dpos - dstart + if dpos < dl_si: + dpos += 1 + if dlen == 0: + continue + dname_buf: bytes = stdlib.malloc(dlen + 1) + if dname_buf is None: + continue + string.strncpy(dname_buf, deps_si + dstart, dlen) + dname_buf[dlen] = '\0' + dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf) + stdlib.free(dname_buf) + if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0: + indegree[si] = indegree[si] + 1 + # Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译 + q_head: int = 0 + q_tail: int = 0 + for i_r in range(rel_count): + si2: int = rel_idx[i_r] + if indegree[si2] == 0: + queue[q_tail] = si2 + q_tail += 1 + out_count: int = 0 + while q_head < q_tail: + m: int = queue[q_head] + q_head += 1 + sorted_idx[out_count] = m + out_count += 1 + # 对依赖 m 的可达模块减入度 + mod_m: str = store_mod_arr + t.CSizeT(m) * 64 + mod_m_len: t.CSizeT = string.strlen(mod_m) + for i_r in range(rel_count): + si3: int = rel_idx[i_r] + if indegree[si3] == 0: + continue + deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + t.CSizeT(si3) * 17) + if deps_si3 is None: + continue + dl_si3: t.CSizeT = string.strlen(deps_si3) + dpos3: t.CSizeT = 0 + found_dep: int = 0 + while dpos3 < dl_si3 and found_dep == 0: + dstart3: t.CSizeT = dpos3 + while dpos3 < dl_si3 and deps_si3[dpos3] != ' ': + dpos3 += 1 + dlen3: t.CSizeT = dpos3 - dstart3 + if dpos3 < dl_si3: + dpos3 += 1 + if dlen3 == 0: + continue + if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0: + found_dep = 1 + if found_dep == 1: + indegree[si3] = indegree[si3] - 1 + if indegree[si3] == 0: + queue[q_tail] = si3 + q_tail += 1 + # 有环时剩余条目按 store 顺序补齐(避免漏编译) + for i_r in range(rel_count): + si4: int = rel_idx[i_r] + if indegree[si4] > 0: + sorted_idx[out_count] = si4 + out_count += 1 + stdlib.free(indegree) + stdlib.free(queue) + stdlib.free(rel_idx) + return out_count diff --git a/App/main.vp b/App/main.vp index 9a598bd..db2e504 100644 --- a/App/main.vp +++ b/App/main.vp @@ -47,6 +47,8 @@ import subprocess import viperlib import hashlib import t, c +import lib.core.StubMergerBuild as StubMergerBuild +import lib.core.StubMergerCollect as StubMergerCollect # Main Pool 1 GiB @@ -599,7 +601,7 @@ TermiNexus Age %sY\n\n""", VIPER_VERSION, RUNTIME_VERSION, TPV_VERSION, TERMINEX if sf_stub_buf is not None: tr.dump_ir(sf_stub_buf, SF_IR_SIZE, llvmlite.OUTPUT_STUB) sf_stub_len: t.CSizeT = string.strlen(sf_stub_buf) - sf_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_sf, module_name, "stub.ll") + sf_stub_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_sf, module_name, "stub.ll") if sf_stub_path is not None: sf_f: fileio.File | t.CPtr = fileio.File(sf_stub_path, fileio.MODE.W) if not sf_f.closed: @@ -612,7 +614,7 @@ TermiNexus Age %sY\n\n""", VIPER_VERSION, RUNTIME_VERSION, TPV_VERSION, TERMINEX if sf_text_buf is not None: tr.dump_ir(sf_text_buf, SF_IR_SIZE, llvmlite.OUTPUT_TEXT) sf_text_len: t.CSizeT = string.strlen(sf_text_buf) - sf_text_path: str = StubMerger._sliced_path(temp_dir, td_len_sf, module_name, "text.ll") + sf_text_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_sf, module_name, "text.ll") if sf_text_path is not None: sf_tf: fileio.File | t.CPtr = fileio.File(sf_text_path, fileio.MODE.W) if not sf_tf.closed: @@ -637,7 +639,7 @@ TermiNexus Age %sY\n\n""", VIPER_VERSION, RUNTIME_VERSION, TPV_VERSION, TERMINEX SF_COMBINED_SIZE: t.CSizeT = 4194304 # 4MB combined_buf: bytes = stdlib.malloc(SF_COMBINED_SIZE) if combined_buf is not None: - combined_len: t.CSizeT = StubMerger.BuildCombinedIR(temp_dir, module_name, combined_buf, SF_COMBINED_SIZE) + combined_len: t.CSizeT = StubMergerBuild.BuildCombinedIR(temp_dir, module_name, combined_buf, SF_COMBINED_SIZE) if combined_len > 0: final_ir = combined_buf final_ir_len = combined_len