import lib.core.Gen.GenTypes as GenTypes import lib.core.Gen.Generator as Generator import lib.core.Gen.GenApi as GenApi import lib.core.Gen.Translator as HT import t, c from stdint import * import ast import llvmlite import memhub import hashtable import string import stdio import stdlib import viperlib import w32.fileio as fileio import w32.win32file as win32file import lib.core.VLogger as VLogger import lib.Stub.StubMerger as StubMerger import lib.core.CompilerFactory as CompilerFactory import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect import lib.Stub.StubMerger.StubMergerStore as StubMergerStore import lib.Stub.StubGen.VpiReader as VpiReader # ============================================================ # CallGenBase - CallGenMixin 拆分 1/9(最底层父类):模块符号查找 + # 返回类型解析基础设施 # ============================================================ # ============================================================ # 常量(Gen 层各处容量上限) # ============================================================ # 全局函数表 / 默认参数表容量 define GLOBAL_FUNC_MAX = 8192 define GLOBAL_DEFAULTS_MAX = 4096 define _CEXPORT_MAX = 256 define ASM_MAX_OPERANDS = 16 define ASM_TEXT_BUF_SIZE = 2048 define ASM_CONST_BUF_SIZE = 512 define ASM_ARGS_BUF_SIZE = 512 define ASM_LINE_BUF_SIZE = 4096 define LLVMIR_MAX_OPS = 16 define LLVMIR_TEMPLATE_BUF = 512 # 闭包名表大小(消费方:CallGenLeafKit._ensure_closure_names/register_closure_name) define MAX_CLOSURE_NAMES = 128 # 函数指针签名表大小(消费方:CallGenLeafKit._ensure_funcptr_sigs/register_func_ptr_sig) define MAX_FUNC_PTR_SIGS = 64 # isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32。 # 消费方:CallGenLeafKit._collect_subclass_vtable_classes) define ISINSTANCE_CLASS_MAX = 64 # ============================================================ # CallGenBase - 最底层父类(字段 + 模块查找 + 返回类型解析) # ============================================================ novirtual class CallGenBase: """调用分派最底层父类(字段 + 模块符号查找 + 返回类型解析基础设施)""" # --- c.cexport 注册表(原 _g_cexport_sha1_arr/_g_cexport_func_arr/_g_cexport_count) --- Gen: Generator.Generator* CexportSha1Arr: bytes = None CexportFuncArr: bytes = None CexportCount: int = 0 # --- static 符号注册表(`static` 修饰符 / @t.CStatic / t.CStatic 标记的符号 → # 定义模块 SHA1)。语义 = 禁止跨模块使用(C 的 internal linkage):定义侧登记, # 跨模块引用侧(mod.name 取值 / mod.fn() 调用 / from mod import name)拦截报错。 # 与 CexportSha1Arr 同款 PermPool 定长槽(跨 mark/reset 存活)。 StaticSha1Arr: bytes = None StaticNameArr: bytes = None StaticCount: int = 0 # --- LLVMIR 寄存器序号 --- LLVMIRRegSeq: int = 0 # --- temp 目录 --- TempDir: str = None TempDirLen: sizet = 0 # --- 闭包名表(原 _g_closure_names/_g_closure_count) --- ClosureNames: str = None ClosureCount: int = 0 # --- 函数指针签名表(原 _g_funcptr_names/_g_funcptr_rets/_g_funcptr_count) --- FuncptrNames: str = None FuncptrRets: uint64* = None FuncptrCount: int = 0 # --- 全局函数注册表(原 _global_funcs/_global_func_count) --- GlobalFuncs: GenTypes.FuncEntry* = None GlobalFuncCount: int = 0 # --- 全局函数表"名 → 槽下标"索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) --- # GlobalFuncs 只追加不重排 ⇒ 槽下标稳定;Built == GlobalFuncCount 才可信,否则回退线性扫描。 GlobalFuncNameIdx: sizet* = None GlobalFuncNameIdxCap: int = 0 GlobalFuncNameIdxBuilt: int = -1 # --- 全局默认参数表(原 _global_defaults/_global_defaults_count) --- GlobalDefaults: GenTypes.FuncDefaultsEntry* = None GlobalDefaultsCount: int = 0 # --- 依赖模块 text.ll「方法返回类型文本」缓存(_LookupMethodRetTyFromTextLL 性能优化) --- # key = define 的完整 mangled 函数名("{sha1}.{后缀}"),值只存文本(绝不存 LLVMType*,其分配在每文件 reset 失效的 pool 中);表与键/值均 PermPool 分配,命中后用调用方 pool 重建类型,建表失败即不缓存并回退原扫描。 MethodRetTyMap: hashtable.HashTable* = None def InitCallGen(self) -> int: """初始化调用 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)""" if self is None: return 1 return 0 def _lookup_module_sha1(self, mod_name: str) -> str: """查找模块名对应的 SHA1,找不到返回 None 委托给 StubMerger.LookupSha1ByModName(精确匹配 + 包名回退)。 """ return StubMergerStore.LookupSha1ByModName(mod_name) def _lookup_module_sha1_suffix(self, mod_suffix: str) -> str: """后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "llvmlite.__types" 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点), 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。 委托给 StubMerger.LookupSha1ByModNameSuffix(精确 + 后缀 + 反向后缀)。 """ return StubMergerStore.LookupSha1ByModNameSuffix(mod_suffix) def _module_package_from_sha1(self, pool: memhub.MemBuddy*, mod_sha1: str) -> str: """从 SHA1 存储器的 rel_path 计算模块所属包名(目录部分,'/' → '.')""" if pool is None or mod_sha1 is None: return None rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr() sha1_arr_ptr: bytes = 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): sidx: sizet = sizet(i) * 17 if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0: continue ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN rel: str = rel_arr_ptr + ridx last_slash: str = string.strrchr(rel, 47) # 47 = ord('/') if last_slash is None: return None pkg_len: sizet = sizet(uint64(last_slash) - uint64(rel)) if pkg_len == 0: return None pkg_buf: str = pool.alloc(pkg_len + 1) if pkg_buf is None: return None for pi in range(pkg_len): if rel[pi] == '/': pkg_buf[pi] = '.' else: pkg_buf[pi] = rel[pi] pkg_buf[pkg_len] = '\0' return pkg_buf return None def _lookup_reexport_from_vpi(self, pool: memhub.MemBuddy*, mod_sha1: str, func_name: str) -> str: """re-export 表未命中时,从模块 .vpi 按需解析 from-import 定位定义模块 includes 模块(如 llvmlite/__init__)的 stub/text 已存在时不会被按需 全量翻译(Phase2 3.5 仅在 stub/text 缺失时翻译),而 re-export 表只在 declare_only=0 的全量翻译中注册(HandleImportFromNames),因此表可能为空。 本函数解析该模块 .vpi 的 "from .X import ... func_name ..." 语句, 返回定义模块名(如 llvmlite/__init__ 的 .__builder → "llvmlite.__builder")。 """ if pool is None or mod_sha1 is None or func_name is None: return None # star import 候选模块累积(空格分隔):from X import * 无法确定归属,收集全部候选由调用方逐个验证 star_cands: str = None star_cands_len: sizet = 0 temp_dir: str = self.Gen.TypeGenBase.get_temp_dir() if temp_dir is None: return None td_len: sizet = string.strlen(temp_dir) vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, mod_sha1, "vpi") if vpi_path is None: return None READ_SZ: sizet = 1048576 buf: bytes = stdlib.malloc(READ_SZ) if buf is None: stdlib.free(vpi_path) return None vf: rawptr[fileio.File] = fileio.File(vpi_path, fileio.MODE.R) stdlib.free(vpi_path) if vf.closed: stdlib.free(buf) return None nrd: LONG = vf.read_all(buf, READ_SZ) vf.close() if nrd <= 0: stdlib.free(buf) return None if nrd < READ_SZ: buf[nrd] = 0 name_len: sizet = string.strlen(func_name) # 模块自含定义标志:vpi 中行首 "def {func_name}(" 命中且未被同名 # from-import 覆盖 → 该模块自身定义 func_name,不是 re-export 源。 has_own_def: int = 0 total: sizet = string.strlen(buf) pos: sizet = 0 while pos < total: ls: sizet = pos while pos < total and buf[pos] != '\n': pos += 1 ll: sizet = pos - ls if pos < total: pos += 1 # 自含定义检测(须在 from-import 行匹配之前,对所有行生效):模块级 def 行首无缩进,类内方法 def 有缩进不会误命中 if ll > 4 and buf[ls] == 'd' and buf[ls + 1] == 'e' and buf[ls + 2] == 'f' and buf[ls + 3] == ' ': fstart_od: sizet = ls + 4 fend_od: sizet = fstart_od while fend_od < ls + ll and buf[fend_od] != '(' and buf[fend_od] != ' ': fend_od += 1 fl2_od: sizet = fend_od - fstart_od if fl2_od == name_len: fd_eq_od: int = 1 fdi_od: sizet = 0 while fdi_od < fl2_od: if buf[fstart_od + fdi_od] != func_name[fdi_od]: fd_eq_od = 0 break fdi_od += 1 if fd_eq_od != 0: has_own_def = 1 continue # 行必须以 "from " 开头 if ll < 6: continue if buf[ls] != 'f' or buf[ls + 1] != 'r' or buf[ls + 2] != 'o' or buf[ls + 3] != 'm' or buf[ls + 4] != ' ': continue # 找 " import " 分隔符 imp_pos: sizet = ls + 5 while imp_pos + 7 <= ls + ll: if buf[imp_pos] == ' ' and buf[imp_pos + 1] == 'i' and buf[imp_pos + 2] == 'm' and buf[imp_pos + 3] == 'p' and buf[imp_pos + 4] == 'o' and buf[imp_pos + 5] == 'r' and buf[imp_pos + 6] == 't' and buf[imp_pos + 7] == ' ': break imp_pos += 1 if imp_pos + 7 > ls + ll: continue mod_start: sizet = ls + 5 mod_end: sizet = imp_pos # 名称列表从 "import " 之后(逗号分隔,含 "A as B" 取原名 A) nm_start: sizet = imp_pos + 8 # star import 支持(from X import *):首个 star 行未必是定义模块,累积全部候选,扫描完由调用方按 vpi 返回注解逐个验证 if nm_start < ls + ll and buf[nm_start] == '*': star_dot: int = 0 if buf[mod_start] == '.': star_dot = 1 star_buf: str = pool.alloc(mod_end - mod_start + 1) if star_buf is not None: si2: sizet = 0 while mod_start + si2 < mod_end: star_buf[si2] = buf[mod_start + si2] si2 += 1 star_buf[si2] = '\0' while star_buf[0] == '.': star_buf = star_buf + 1 if string.strlen(star_buf) > 0: if star_dot != 0: spkg: str = self._module_package_from_sha1(pool, mod_sha1) if spkg is not None: srl: sizet = string.strlen(spkg) + string.strlen(star_buf) + 2 sres: str = pool.alloc(srl) if sres is not None: viperlib.snprintf(sres, srl, "%s.%s", spkg, star_buf) star_buf = sres # 累积候选(空格分隔) sbl: sizet = string.strlen(star_buf) new_cands: str = pool.alloc(star_cands_len + sbl + 2) if new_cands is not None: if star_cands is not None and star_cands_len > 0: string.strcpy(new_cands, star_cands) new_cands[star_cands_len] = ' ' string.strcpy(new_cands + star_cands_len + 1, star_buf) star_cands_len += sbl + 1 else: string.strcpy(new_cands, star_buf) star_cands_len = sbl star_cands = new_cands continue scan: sizet = nm_start while scan < ls + ll: while scan < ls + ll and (buf[scan] == ' ' or buf[scan] == ','): scan += 1 if scan >= ls + ll: break n_end: sizet = scan while n_end < ls + ll and buf[n_end] != ',' and buf[n_end] != ' ': n_end += 1 cand_start: sizet = scan cand_len: sizet = n_end - scan as_pos: sizet = scan while as_pos + 4 <= n_end: if buf[as_pos] == ' ' and buf[as_pos + 1] == 'a' and buf[as_pos + 2] == 's' and buf[as_pos + 3] == ' ': cand_start = as_pos + 4 cand_len = n_end - cand_start break as_pos += 1 if cand_len == name_len: m_ok: int = 1 for ki in range(name_len): if buf[cand_start + ki] != func_name[ki]: m_ok = 0 break if m_ok == 1: # 命中:解析模块名(前导点 → 相对导入,补包前缀) has_dot: int = 0 if buf[mod_start] == '.': has_dot = 1 mod_buf: str = pool.alloc(mod_end - mod_start + 1) if mod_buf is None: stdlib.free(buf) return None mi: sizet = 0 while mod_start + mi < mod_end: mod_buf[mi] = buf[mod_start + mi] mi += 1 mod_buf[mi] = '\0' while mod_buf[0] == '.': mod_buf = mod_buf + 1 if string.strlen(mod_buf) == 0: stdlib.free(buf) return None if has_dot != 0: pkg: str = self._module_package_from_sha1(pool, mod_sha1) if pkg is None: stdlib.free(buf) return None rl: sizet = string.strlen(pkg) + string.strlen(mod_buf) + 2 res: str = pool.alloc(rl) if res is None: stdlib.free(buf) return None viperlib.snprintf(res, rl, "%s.%s", pkg, mod_buf) stdlib.free(buf) return res stdlib.free(buf) return mod_buf scan = n_end # 全部行扫描完毕:仅有 star import 时返回累积候选(调用方逐个验证定义模块);模块自含 def {func_name} 时本模块即定义者,返回 None 保持归属 if star_cands is not None: stdlib.free(buf) if has_own_def == 0: return star_cands return None stdlib.free(buf) return None def register_cexport_func(self, sha1: str, func_name: str) -> int: """注册 CExport 函数到全局表(统一写入 Gen.CallGenBase 真源,跨子对象共享)""" if self.Gen is None or sha1 is None or func_name is None: return 0 if self.Gen.CallGenBase.CexportSha1Arr is None: self.Gen.CallGenBase.CexportSha1Arr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 17) self.Gen.CallGenBase.CexportFuncArr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 64) if self.Gen.CallGenBase.CexportSha1Arr is None or self.Gen.CallGenBase.CexportFuncArr is None: return 0 string.memset(self.Gen.CallGenBase.CexportSha1Arr, 0, _CEXPORT_MAX * 17) string.memset(self.Gen.CallGenBase.CexportFuncArr, 0, _CEXPORT_MAX * 64) if self.Gen.CallGenBase.CexportCount < _CEXPORT_MAX: idx: sizet = sizet(self.Gen.CallGenBase.CexportCount) * 17 # 槽位定长 17/64 字节,strcpy 无界会越界写相邻槽(stdlib 堆区) string.strncpy(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1, 16) self.Gen.CallGenBase.CexportSha1Arr[idx + 16] = '\0' idx2: sizet = sizet(self.Gen.CallGenBase.CexportCount) * 64 string.strncpy(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name, 63) self.Gen.CallGenBase.CexportFuncArr[idx2 + 63] = '\0' self.Gen.CallGenBase.CexportCount += 1 return 1 return 0 def is_cexport_func(self, sha1: str, func_name: str) -> int: """检查 (sha1, func_name) 是否是 CExport 函数(统一读 Gen.CallGenBase 真源) 注册表未命中时回退查 vpi 返回注解:跨模块编译存在时序差(如 apic.vp 先于 mm.vp 翻译时 mm 的 CExport 尚未 register_cexport_func → mm.malloc 误用 @1fdad1c76fb116bc.malloc 而非裸名 @malloc → ld.lld undefined symbol: 1fdad1c76fb116bc.malloc)。 """ if self.Gen is None or sha1 is None or func_name is None: return 0 if self.Gen.CallGenBase.CexportSha1Arr is not None: for i in range(self.Gen.CallGenBase.CexportCount): idx: sizet = sizet(i) * 17 if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0: idx2: sizet = sizet(i) * 64 if string.strcmp(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name) == 0: return 1 return self._is_cexport_func_from_vpi(sha1, func_name) # ============================================================ # RegisterStaticSymbol / IsStaticSymbol - static 符号注册表 # # `static def f` / `static x: int` / `@t.CStatic` / `x: t.CStatic | T` 四种写法 # 在解析层已归一为同一套标记(t.CStatic),语义 = 禁止跨模块使用(C 的 # internal linkage)。定义侧翻译时登记 (符号名, 定义模块 SHA1);跨模块引用侧 # (mod.name 取值 / mod.fn() 调用 / from mod import name)用 IsStaticSymbol # 判定并 fail-fast。 # ============================================================ def RegisterStaticSymbol(self, sha1: str, name: str) -> int: """登记 static 符号(定义模块 SHA1 + 符号名);1=已登记(含幂等重复)/ 0=失败""" if self.Gen is None or sha1 is None or name is None: return 0 if self.Gen.CallGenBase.StaticSha1Arr is None: self.Gen.CallGenBase.StaticSha1Arr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 17) self.Gen.CallGenBase.StaticNameArr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 64) if self.Gen.CallGenBase.StaticSha1Arr is None or self.Gen.CallGenBase.StaticNameArr is None: return 0 string.memset(self.Gen.CallGenBase.StaticSha1Arr, 0, _CEXPORT_MAX * 17) string.memset(self.Gen.CallGenBase.StaticNameArr, 0, _CEXPORT_MAX * 64) # 幂等:同 (sha1, name) 重复登记(跨遍重译 / 同文件多次进入)直接命中返回 for i in range(self.Gen.CallGenBase.StaticCount): si: sizet = sizet(i) * 17 if string.strcmp(self.Gen.CallGenBase.StaticSha1Arr + si, sha1) == 0: sn: sizet = sizet(i) * 64 if string.strcmp(self.Gen.CallGenBase.StaticNameArr + sn, name) == 0: return 1 if self.Gen.CallGenBase.StaticCount < _CEXPORT_MAX: idx: sizet = sizet(self.Gen.CallGenBase.StaticCount) * 17 # 槽位定长 17/64 字节,strcpy 无界会越界写相邻槽(stdlib 堆区) string.strncpy(self.Gen.CallGenBase.StaticSha1Arr + idx, sha1, 16) self.Gen.CallGenBase.StaticSha1Arr[idx + 16] = '\0' idx2: sizet = sizet(self.Gen.CallGenBase.StaticCount) * 64 string.strncpy(self.Gen.CallGenBase.StaticNameArr + idx2, name, 63) self.Gen.CallGenBase.StaticNameArr[idx2 + 63] = '\0' self.Gen.CallGenBase.StaticCount += 1 return 1 return 0 def IsStaticSymbol(self, sha1: str, name: str) -> int: """(sha1, name) 是否为已登记的 static 符号(跨模块引用拦截判据) 与 is_cexport_func 的区别:cexport 是"导出裸名"(跨模块**允许**), static 是"禁止跨模块"(跨模块**必须报错**);两者判据方向相反, 但同用 (sha1, 符号名) 精确配对,避免同名符号跨模块误判。 """ if self.Gen is None or sha1 is None or name is None: return 0 if self.Gen.CallGenBase.StaticSha1Arr is None: return 0 for i in range(self.Gen.CallGenBase.StaticCount): si: sizet = sizet(i) * 17 if string.strcmp(self.Gen.CallGenBase.StaticSha1Arr + si, sha1) == 0: sn: sizet = sizet(i) * 64 if string.strcmp(self.Gen.CallGenBase.StaticNameArr + sn, name) == 0: return 1 return 0 def _is_cexport_func_from_vpi(self, sha1: str, func_name: str) -> int: """vpi 返回注解含 t.CExport → 定义端用裸名(跨模块调用无 sha1 头) 对齐 _is_state_func_from_vpi(State 标记)的 vpi 按需解析路径。 """ if sha1 is None or func_name is None: return 0 if self.Gen is None or self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen <= 0: return 0 VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir) cexp_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name) if cexp_annot is None: return 0 if string.strstr(cexp_annot, "CExport") is not None: return 1 return 0 def _is_state_func_from_vpi(self, pool: memhub.MemBuddy*, sha1: str, func_name: str) -> int: """vpi 返回注解含 t.State → 外部 C 函数(定义端用裸名,跨模块调用无 sha1 头) t.State(同 t.CExtern/t.CExport)是外部函数标记:定义端生成裸名 @PyList_Size(链接外部库 libpython),跨模块调用若生成 @{sha1}.PyList_Size → ld.lld undefined symbol(CPythonTest PyList_Size 根因)。CExport 走 is_cexport_func 注册表,State 函数未注册 → 按需解析 vpi 返回注解判断。 """ if pool is None or sha1 is None or func_name is None: return 0 if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir) state_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name) if state_annot is None: return 0 # 返回注解含 "State"(如 "sizet | t.State")→ 外部 C 函数 if string.strstr(state_annot, "State") is not None: return 1 return 0 def _lookup_mod_sha1(self, pool: memhub.MemBuddy*, from_imports: str, class_name: str, allow_star_fallback: int = 1) -> str: """从 from_imports 查找类所属模块的 SHA1,返回 None=未找到 allow_star_fallback: 传 0 禁用 star import 回退(用于明确模块名/函数名查找) """ mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, class_name, allow_star_fallback) if mod_name_raw is None: return None return self._lookup_sha1_from_modname_raw(pool, mod_name_raw) def _lookup_sha1_from_modname_raw(self, pool: memhub.MemBuddy*, mod_name_raw: str) -> str: """从原始模块名查找 SHA1,返回 None=未找到""" # 手动找到模块名结束位置(空格、null、或 ':' 别名分隔符),复制到新缓冲区 mod_len: sizet = 0 while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':': mod_len += 1 mod_name_buf: str = pool.alloc(mod_len + 1) if mod_name_buf is None: return None for mi in range(mod_len): mod_name_buf[mi] = mod_name_raw[mi] mod_name_buf[mod_len] = '\0' # 去掉相对导入的前导点 base_mod: str = mod_name_buf while base_mod[0] == '.': base_mod = base_mod + 1 sha1: str = self._lookup_module_sha1(base_mod) if sha1 is None: sha1 = self._lookup_module_sha1_suffix(base_mod) return sha1 def _FuncTyFromFunction(self, pool: memhub.MemBuddy*, f_ref: llvmlite.Function*) -> llvmlite.LLVMType*: """由 llvmlite.Function 的形参链表(Param 链)构造 LLVMType.Func 类型。 为什么不能直接把 Param 链交给 llvmlite.Func:`Function.Params` 是 **Param** 链 (__function.vp,`Param.Ty: rawptr[LLVMType]`),而 `LLVMType.Func` 的类型构造链表是 **ParamNode** 链(__types.vp,由 new_param_node 建、paramnode_set_next 串)。两者是 互不相关的 class —— 严格指针规则下不可互传(旧的无条件 bitcast 掩盖了这点,且两者 字段顺序不同,bitcast 后读 Ty/Name 都是错位数据)。 这里逐个 new_param_node 重建 ParamNode 链再交给 Func:语义等价(两侧都只取各参数的 类型),类型也对得上 Func(params: rawptr[ParamNode]) 的形参。 """ pn_head: llvmlite.ParamNode* = None pn_prev: llvmlite.ParamNode* = None p_cnt: sizet = 0 p_cur: llvmlite.Param* = llvmlite.function_get_param_head(f_ref) while p_cur is not None: p_ty: llvmlite.LLVMType* = llvmlite.param_get_ty(p_cur) if p_ty is None: return None pn: llvmlite.ParamNode* = llvmlite.new_param_node(pool, p_ty) if pn is None: return None if pn_head is None: pn_head = pn if pn_prev is not None: llvmlite.paramnode_set_next(pn_prev, pn) pn_prev = pn p_cnt += 1 p_cur = llvmlite.param_get_next(p_cur) return llvmlite.Func(pool, f_ref.RetTy, pn_head, p_cnt) def find_func_in_module(self, mod: llvmlite.LLVMModule*, name: str, owner_sha1: str = None) -> llvmlite.Function*: """在模块的函数链表中按名称查找函数 查找策略: 1. 精确匹配(如 "strlen" 匹配 @strlen,有 t.CExport 的函数) 2. 后缀匹配(如 "atoi" 匹配 @"83210a72e829a086.atoi",无 t.CExport 的函数) owner_sha1: 当前模块 SHA1。非 None 时在本模块 define 判定之前先做 "本模块同名函数"优先扫描(mangled 名以 "{owner_sha1}." 开头且恰为 "{owner_sha1}.{name}")。裸名调用应优先解析为当前模块自身函数—— 其前向 declare 在函数体翻译时可能尚未激活(IsDeclared 仍为 1), pass2 的 IsDeclared==0 判定会跳过它,pass3 又会按链表顺序误绑排在 FuncHead 前面的跨模块 stub declare(lexer.vp _advance 根因)。 使用 llvmlite.function_get_name/next 访问器绕过 stub 类型限制 (stub 类型字段不足且字段类型可能错误,直接字段访问会被静默跳过)。 """ if mod is None or name is None: return None name_len: sizet = string.strlen(name) # 热点优化:原「趟 1 精确匹配+本模块同名、趟 2 declare/任意后缀」两趟完整链表扫描 # 合并为一趟——同一节点只取一次 function_get_name/NameLen/next(原两趟各取一次, # 该链表平均数百节点且本函数每次调用都全链遍历,PROF 中本函数及其访问器约占 10%)。 # 优先级与逐趟结果完全一致:精确匹配命中即返回 > 本模块同名 > declare 后缀 > 任意后缀; # 同类候选均取链表首个命中。 cur: llvmlite.Function* = mod.FuncHead mangle_hit: llvmlite.Function* = None declare_hit: llvmlite.Function* = None any_hit: llvmlite.Function* = None while cur is not None: cur_name: str = llvmlite.function_get_name(cur) if cur_name is not None: # 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp(mangled 名以 sha1 十六进制打头,裸名多为字母/下划线) if cur_name[0] == name[0] and string.strcmp(cur_name, name) == 0: return cur # 名字长度取 Function.NameLen(创建时一次算好),免去对热链每个节点重复 strlen cur_len: sizet = llvmlite.function_get_name_len(cur) # 本模块同名候选:仅接受恰为 "{owner_sha1}.{name}" 的形态(不匹配 "{sha1}.Class.method");declare 与 define 同名同符号,是否 IsDeclared==0 均可返回 if owner_sha1 is not None and mangle_hit is None and cur_len == 17 + name_len: if string.strncmp(cur_name, owner_sha1, 16) == 0 and cur_name[16] == '.': suffix_start_o: sizet = cur_len - name_len if cur_name[suffix_start_o - 1] == '.': match_o: int = 1 for i in range(name_len): if cur_name[suffix_start_o + i] != name[i]: match_o = 0 break if match_o == 1: mangle_hit = cur # 后缀匹配(.name):不跳过 declare(方法 stub 也是 declare,由调用处判断 IsDeclared 决定用裸名或 sha1 前缀名) if cur_len > name_len + 1: suffix_start: sizet = cur_len - name_len if cur_name[suffix_start - 1] == '.': match: int = 1 for i in range(name_len): if cur_name[suffix_start + i] != name[i]: match = 0 break if match == 1: # 首个任意后缀命中(原第三遍兜底) if any_hit is None: any_hit = cur # 首个 IsDeclared==0 命中(原第二遍优先) if cur.IsDeclared == 0 and declare_hit is None: declare_hit = cur cur = llvmlite.function_get_next(cur) if mangle_hit is not None: return mangle_hit if declare_hit is not None: return declare_hit return any_hit # _ensure_method_declare:found_func 为 None(stub 未注入或跨模块调用)时在当前模块创建 declare,避免 llc 报 undefined value def _ensure_method_declare(self, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, call_name: str, ret_ty: llvmlite.LLVMType*, param_head: llvmlite.Value*, param_count: int): """为跨模块方法调用创建 declare 声明 若模块中已存在同名 declare 且参数数量较少(例如 File.__init__ 的 stub 仅 3 个参数,而实际调用传入 4 个),则向现有 declare 追加缺失 参数,避免 llc 报 `argument invalid for parameter type` 之类的错误。 已存在的 define 不修改(其参数由定义决定)。 """ if pool is None or mod is None or call_name is None: return # 链遍历哨兵(fail-fast):FuncHead/Params 的 Next 被越界写破坏成环时 # 无上限遍历会死循环(builtins.vp dump 卡死根因排查),超限即报错终止。 DECLARE_CHAIN_LIMIT: int = 100000 # 检查模块中是否已有同名函数(declare 或 define) existing: llvmlite.Function* = mod.FuncHead existing_steps: int = 0 while existing is not None: existing_steps += 1 if existing_steps > DECLARE_CHAIN_LIMIT: stdio.printf("[CALLGEN] 链表异常: module funcs(call_name=%s,遍历超限,终止)\n", call_name) # 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印) VLogger.repl_abort() if existing.Name is not None: # 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp if existing.Name[0] == call_name[0] and string.strcmp(existing.Name, call_name) == 0: # 已存在:仅当是 declare 且参数数量不足时追加缺失参数 if existing.IsDeclared == 1: # 变长函数(*args,IsVarArg=1)不追加参数:多余实参由 # 变参机制(...)承接,追加有类型参数会导致调用侧把变参 # 强转为追加的类型(如 i8 → i64),%d/%x 等格式读错。 if existing.IsVarArg == 1: return # 遍历现有参数链表计数(避免依赖 GSList.Count 字段的类型推断) cur_count: int = 0 cur_p: llvmlite.Param* = llvmlite.function_get_param_head(existing) while cur_p is not None: cur_count += 1 if cur_count > DECLARE_CHAIN_LIMIT: stdio.printf("[CALLGEN] 链表异常: func params(call_name=%s,参数链遍历超限,终止)\n", call_name) # 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印) VLogger.repl_abort() cur_p = cur_p.Next # 只追加缺失的参数(cur_count < param_count 时) if cur_count < param_count: # 定位到 param_head 的第 cur_count 个节点开始追加 pnode2: llvmlite.Value* = param_head pi2: int = 0 # 跳过已有的 cur_count 个参数 while pnode2 is not None and pi2 < cur_count: pnode2 = pnode2.Next pi2 += 1 # 追加剩余参数 while pnode2 is not None and pi2 < param_count: if pnode2.Ty is not None: param2: llvmlite.Param* = llvmlite.new_param(pool, pnode2.Ty, None) if param2 is not None: llvmlite.function_add_param(existing, param2) pnode2 = pnode2.Next pi2 += 1 return existing = existing.Next # 裸名 C 库函数(如 string.memset → @memset):call_name 无 sha1 前缀,无法提取定义侧模块查 vpi 参数注解,故按标准签名建 declare(与 CallGenLib._ensure_c_lib_declare 白名单一致) if call_name is not None and string.strchr(call_name, 46) is None: std_lib_fn: llvmlite.Function* = self.Gen.CallGenLib._ensure_c_lib_declare(pool, mod, call_name) if std_lib_fn is not None: return # 创建函数声明 func: llvmlite.Function* = llvmlite.new_function(pool, call_name, ret_ty) if func is None: return func.IsDeclared = 1 # 定义侧参数类型优先:从 call_name 提取 {sha1}.{cls}.{method} 查 vpi 参数注解取定义侧类型。 # · 整数:仅当定义宽度更宽时采用定义宽度(反方向由 CallGenLib._coerce_args_to_def_params 在调用点先窄化实参) # · 指针:**一律采用定义侧类型**——declare 被同模块所有调用点共用,若沿用"首个调用点的实参类型", # 后续调用传入别的具体指针类型就会被这条被冻住的形参类型 coerce,报"非基本类型指针间隐式转换" # (现场 VarGenMixin.repl_ensure_entry 的 node: ast.AST*:先被 translate_annassign 以 AnnAssign* # 建 declare,后 translate_assign 传 AST* 即报"源类型 …AST* → 目标类型 …AnnAssign*") sha1_d: str = None cls_d: str = None meth_d: str = None if call_name is not None: # SHA1 前缀判定走唯一真源 GenApi.sha1_prefix_len,前缀长度取其返回值(不硬编码 17/16) sha1_pfx_d: int = GenApi.sha1_prefix_len(call_name) if sha1_pfx_d != 0: pfx_len_d: sizet = sizet(sha1_pfx_d) sha1_buf_d: bytes = stdlib.malloc(pfx_len_d) if sha1_buf_d is not None: string.strncpy(sha1_buf_d, call_name, pfx_len_d - 1) sha1_buf_d[pfx_len_d - 1] = '\0' sha1_d = sha1_buf_d rest_d: str = call_name + pfx_len_d # 取末个 '.':method 不含 '.',首个 '.' 会把含模块限定名的类型实参截断 dot2_d: str = string.strrchr(rest_d, '.') if dot2_d is not None: cls_len_d: sizet = uint64(dot2_d) - uint64(rest_d) if cls_len_d > 0: cls_buf_d: bytes = stdlib.malloc(cls_len_d + 1) if cls_buf_d is not None: string.strncpy(cls_buf_d, rest_d, cls_len_d) cls_buf_d[cls_len_d] = '\0' cls_d = cls_buf_d meth_d = dot2_d + 1 else: meth_d = rest_d prm_annots_d: str = None if sha1_d is not None and meth_d is not None: prm_annots_d = self._vpi_method_param_annot(pool, sha1_d, cls_d, meth_d) i32_ty_d: llvmlite.LLVMType* = llvmlite.Int32(pool) i8_ptr_d: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool)) # 添加参数(declare 中参数名无意义,设为 None 只输出类型) pnode: llvmlite.Value* = param_head pi: int = 0 while pnode is not None and pi < param_count: param_ty_d: llvmlite.LLVMType* = pnode.Ty if prm_annots_d is not None and prm_annots_d[0] != '\0' and pnode.Ty is not None: seg_ty_d: llvmlite.LLVMType* = VpiReader._resolve_vpi_param_annot_seg(pool, sha1_d, cls_d, meth_d, pi, i32_ty_d, i8_ptr_d) if seg_ty_d is not None: match pnode.Ty: case llvmlite.LLVMType.Int(bits_a): match seg_ty_d: case llvmlite.LLVMType.Int(bits_b): if bits_b > bits_a: param_ty_d = seg_ty_d case _: pass case llvmlite.LLVMType.Ptr(_): # 指针形参:定义侧为准(见上方注释)。定义侧解析不出指针时保留实参类型 match seg_ty_d: case llvmlite.LLVMType.Ptr(_): param_ty_d = seg_ty_d case _: pass case _: pass if param_ty_d is not None: param: llvmlite.Param* = llvmlite.new_param(pool, param_ty_d, None) if param is not None: llvmlite.function_add_param(func, param) pnode = pnode.Next pi += 1 if sha1_d is not None: stdlib.free(sha1_d) if cls_d is not None: stdlib.free(cls_d) llvmlite.module_add_function(mod, func) # _lookup_method_ret_ty_via_vpi:按 模块SHA1 + 类名 + 方法名 查 vpi 返回注解缓存并解析为 LLVMType,未命中返回 None(不硬编码白名单,类型始终来自定义处) # ============================================================ def _lookup_method_ret_ty_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> llvmlite.LLVMType*: """从 vpi stub 返回注解缓存获取方法返回类型,未命中返回 None""" if pool is None or cls_sha1 is None or method_name is None: return None # 函数内懒 import:VpiReader imports HandlesExprCall,顶层 import 会成环 i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool) i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool)) # 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi # 覆盖"模块自身翻译时 stub 尚未注入"(同模块方法调用,定义在后)的场景 if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir) annot: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, cls_name, method_name) if annot is None: return None return VpiReader._resolve_vpi_ret_annot_str(pool, annot, i32_ty, i8_ptr_ty) # _vpi_method_param_annot:按需加载并查询 vpi 参数注解串('@' 分隔,含首参 self 空串占位),供调用方补全 declare/call 缺失参数;未命中返回 None def _vpi_method_param_annot(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> str: """按需加载并查询 vpi 参数注解串,未命中返回 None""" if pool is None or cls_sha1 is None or cls_name is None or method_name is None: return None # 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir) return VpiReader._lookup_vpi_param_annots(cls_sha1, cls_name, method_name) # _vpi_resolve_param_annot_ty:解析 vpi 参数注解段为 LLVMType,失败返回 None(调用方以 i8* 兜底) def _vpi_resolve_param_annot_ty(self, pool: memhub.MemBuddy*, seg: str) -> llvmlite.LLVMType*: """解析单个 vpi 参数注解段为 LLVMType,失败返回 None""" if pool is None or seg is None: return None i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool) i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool)) return VpiReader._resolve_vpi_ret_annot_str(pool, seg, i32_ty, i8_ptr_ty) # _special_method_ret_ty:编译器内置特殊方法的固定返回类型——__before_init__/__init__ → void,__new__ → Ptr(StructTy),非特殊返回 None(vpi 无条目,固定即可避免 fail-fast 误报) # ============================================================ def _special_method_ret_ty(self, pool: memhub.MemBuddy*, class_name: str, method_name: str) -> llvmlite.LLVMType*: """编译器内置特殊方法的固定返回类型;非特殊返回 None""" if pool is None or method_name is None: return None if string.strcmp(method_name, "__before_init__") == 0 or string.strcmp(method_name, "__init__") == 0: return llvmlite.Void(pool) if string.strcmp(method_name, "__new__") == 0: if class_name is not None: sp_struct_ty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name) if sp_struct_ty is not None: return llvmlite.Ptr(pool, sp_struct_ty) return None return None # _fail_method_ret_ty:方法返回类型无法精确获取时 ERROR + 错误堆栈终止(node 为调用点 AST,用于定位),绝不静默默认 i32 # ============================================================ def _fail_method_ret_ty(self, method_name: str, node: ast.AST* = None) -> llvmlite.LLVMType*: """方法返回类型无法精确获取时 fail-fast(ERROR + 错误堆栈) 返回类型来源(按序):当前模块函数 → text.ll define → vpi stub 返回注解缓存。 全部未命中说明方法定义缺失或 stub 损坏,立即终止暴露,绝不静默默认 i32。 """ VLogger.error(f"方法返回类型无法精确获取(函数/text.ll/vpi stub 均未命中): {method_name}", "HandlesExprCall") self.Gen.TypeGenErr.fatal_error(node, f"方法返回类型无法精确获取,默认 i32 会截断指针/i64: {method_name}", "TypeError") return None # SetTempDir:设置 temp 目录路径(Phase A 翻译前调用,供读取依赖模块 text.ll) # ============================================================ def SetTempDir(self, temp_dir: str) -> None: """设置 temp 目录路径(统一写入 Gen.CallGenBase 真源,跨子对象共享)""" if self.Gen is None: return self.Gen.CallGenBase.TempDir = temp_dir if temp_dir is not None: self.Gen.CallGenBase.TempDirLen = string.strlen(temp_dir) else: self.Gen.CallGenBase.TempDirLen = 0 # _ParseLLVMRetTyStr:把 LLVM IR 返回类型字符串(define 与 @ 之间)转为 LLVMType——void / iN / double / float,含 * 者返回通用 Ptr(Int8);无法解析返回 None # ============================================================ def _ParseLLVMRetTyStr(self, pool: memhub.MemBuddy*, buf: bytes, start: sizet, end: sizet) -> llvmlite.LLVMType*: """将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType""" if pool is None or buf is None or start >= end: return None # 去除前导空白 s: sizet = start while s < end: c: int = buf[s] if c != ' ' and c != '\t': break s += 1 # 去除尾部空白 e: sizet = end while e > s: c2: int = buf[e - 1] if c2 != ' ' and c2 != '\t': break e -= 1 ty_len: sizet = e - s if ty_len == 0: return None # struct 字面量 {T1, T2, ...}(tuple 打包返回类型)必须在 * 检查之前,否则内含 * 会被误判为 i8*(破坏 tuple 解包) if s < e and buf[s] == '{': # 字段链表节点必须是 llvmlite.ParamNode(new_param_node 返回 rawptr[ParamNode], # paramnode_set_next / Struct 的形参也都是 rawptr[ParamNode])。原先误注解为 # Function*(与 ParamNode 是不相关类),严格指针规则下这两处的实参传递会被判 # 「struct↔struct 隐式转换已禁止」;改注解即根治,不需要也不应该加强转。 th_st: llvmlite.ParamNode* = None tt_st: llvmlite.ParamNode* = None tc_st: int = 0 ps_st: sizet = s + 1 while ps_st < e and buf[ps_st] != '}': while ps_st < e and (buf[ps_st] == ' ' or buf[ps_st] == ','): ps_st += 1 if ps_st >= e or buf[ps_st] == '}': break es_st: sizet = ps_st while ps_st < e and buf[ps_st] != ',' and buf[ps_st] != '}': ps_st += 1 ee_st: sizet = ps_st if ee_st > es_st: et_st: llvmlite.LLVMType* = self._ParseLLVMRetTyStr(pool, buf, es_st, ee_st) if et_st is None: return None en_st: llvmlite.ParamNode* = llvmlite.new_param_node(pool, et_st) if en_st is None: return None if th_st is None: th_st = en_st else: llvmlite.paramnode_set_next(tt_st, en_st) tt_st = en_st tc_st += 1 if th_st is None or tc_st == 0: return None st_ty_st: llvmlite.LLVMType* = llvmlite.Struct(pool, th_st, tc_st, None) # 指针到 struct({i32, i8}*):返回 Ptr(Struct);纯 tuple 打包 # ({i8*, i16})无尾部 *,返回 Struct 本身。 if ps_st < e and buf[ps_st] == '*': return llvmlite.Ptr(pool, st_ty_st) return st_ty_st # 命名类型(%"name" / %name)带指针:先解析到真实结构体/枚举类型再包 Ptr。 # 为什么不许坍缩成通用 i8*:返回类型是「指向类对象」时,坍缩后调用方变量 # 的 alloca 变成 i8*,下标语义随之从「类的 __getitem__」掉到「字节下标」。 # 现场(Test/FakeDuckTest/App/main.vp:115 `p0: str = parts[0]`): # includes/_fakeduck.vp `def split(self, delimiter: str) -> list[str]*`, # 定义侧 text.ll 为 # define %".list[str]"* @"._str.split"(...) # 本函数若把 `%".list[str]"*` 当通用指针,parts 的 alloca 即 # 退化为 i8* ⇒ parts[0] 取到整数 ⇒ 赋给 str 报 # 「整数隐式转指针已禁止」(报文指不到真因)。 # 解析不到登记项时保持原「通用指针」行为(名字可能来自尚未登记的模块)。 if s < e and buf[s] == '%': nm_s: sizet = s + 1 nm_e: sizet = e nm_star: int = 0 while nm_e > nm_s and buf[nm_e - 1] == '*': nm_star += 1 nm_e -= 1 while nm_e > nm_s and (buf[nm_e - 1] == ' ' or buf[nm_e - 1] == '\t'): nm_e -= 1 if nm_star > 0 and nm_e > nm_s: if buf[nm_s] == '"' and buf[nm_e - 1] == '"': nm_s += 1 nm_e -= 1 if nm_e > nm_s: nm_len: sizet = nm_e - nm_s nm_buf: bytes = stdlib.malloc(nm_len + 1) if nm_buf is not None: string.strncpy(nm_buf, buf + nm_s, nm_len) nm_buf[nm_len] = '\0' nm_ty: llvmlite.LLVMType* = GenApi.get_struct_type(nm_buf) if nm_ty is None: nm_ty = GenApi.get_enum_base_type(nm_buf) stdlib.free(nm_buf) if nm_ty is not None: nm_wrap: llvmlite.LLVMType* = nm_ty nm_i: int = 0 while nm_i < nm_star: nm_wrap = llvmlite.Ptr(pool, nm_wrap) nm_i += 1 return nm_wrap # 检查是否含 * (指针类型) → 返回 i8* (通用指针) # 包括 i8*, i32*, { i32, i8 }*,以及上面未登记命名的 %name* ci: sizet = s while ci < e: if buf[ci] == '*': return llvmlite.Ptr(pool, llvmlite.Int8(pool)) ci += 1 # void if ty_len == 4 and string.strncmp(buf + s, "void", 4) == 0: return llvmlite.Void(pool) # double if ty_len == 6 and string.strncmp(buf + s, "double", 6) == 0: return llvmlite.Double(pool) # float if ty_len == 5 and string.strncmp(buf + s, "float", 5) == 0: return llvmlite.Float(pool) # iN (i8, i16, i32, i64) if ty_len >= 2 and buf[s] == 'i': bits: int = 0 di: sizet = s + 1 valid: int = 1 while di < e: d: int = buf[di] if d >= '0' and d <= '9': bits = bits * 10 + (d - '0') else: valid = 0 break di += 1 if valid != 0 and bits > 0: if bits == 8: return llvmlite.Int8(pool) if bits == 16: return llvmlite.Int16(pool) if bits == 32: return llvmlite.Int32(pool) if bits == 64: return llvmlite.Int64(pool) # 无法解析 return None # _LookupCachedRetTyText:查「依赖模块 text.ll 方法返回类型文本」持久缓存——键为 define 完整 mangled 名("{sha1}.{class}.{method}",bare 模式 "{sha1}.{method}"),命中返回文本,未命中/表未建返回 None def _LookupCachedRetTyText(self, class_sha1: str, class_name: str, method_name: str, use_bare: int) -> str: """查返回类型文本缓存:命中返回文本,未命中返回 None""" if self.Gen is None or class_sha1 is None or method_name is None: return None ht: hashtable.HashTable* = self.Gen.CallGenBase.MethodRetTyMap if ht is None: return None sha1_len: sizet = string.strlen(class_sha1) cls_len: sizet = 0 if class_name is not None: cls_len = string.strlen(class_name) meth_len: sizet = string.strlen(method_name) # 长度公式与 _LookupMethodRetTyFromTextLL 构造 search_buf 时完全一致 key_len: sizet = sha1_len + meth_len + 2 if use_bare == 0: key_len = sha1_len + cls_len + meth_len + 4 key_buf: bytes = stdlib.malloc(key_len) if key_buf is None: return None if use_bare != 0: viperlib.snprintf(key_buf, key_len, "%s.%s", class_sha1, method_name) else: viperlib.snprintf(key_buf, key_len, "%s.%s.%s", class_sha1, class_name, method_name) v: t.CPtr = ht.__getitem__(key_buf) stdlib.free(key_buf) if v is None: return None return rawptr(v) # _CacheModuleRetTyTexts:单趟扫描某模块 text.ll——把全部 define 行的返回类型写入持久缓存(键/值深拷贝进 PermPool,同名只记首次,只登记 "{sha1}." 开头的本模块 mangled 名),并在同一趟里返回首个匹配 search_buf 的 define 的返回类型(未匹配返回 None);仅在成功读到 text.ll 后调用,负结果绝不缓存 def _CacheModuleRetTyTexts(self, class_sha1: str, text_buf: bytes, text_len: sizet, pool: memhub.MemBuddy*, search_buf: str, search_str_len: sizet) -> llvmlite.LLVMType*: """单趟「登记 + 查找」text.ll 全部 define 行(建表/分配失败时只查找不登记)""" if text_buf is None or text_len == 0: return None # 合并原「先全量登记一遍、再二次扫描查找」的两趟全文件遍历(PROF:两次扫描合计约 14%)。 # 建表失败(PermPool 未就绪等)不影响查找:cache_on=0 时只走同趟查找。 cache_on: int = 0 ht: hashtable.HashTable* = None perm: memhub.MemBuddy* = None sha1_len: sizet = 0 if self.Gen is not None and class_sha1 is not None: perm = CompilerFactory.factory.PermPool if perm is not None: ht = self.Gen.CallGenBase.MethodRetTyMap if ht is None: ht = hashtable.HashTable(perm) if ht is not None: self.Gen.CallGenBase.MethodRetTyMap = ht if ht is not None: cache_on = 1 sha1_len = string.strlen(class_sha1) pos: sizet = 0 result_ty: llvmlite.LLVMType* = None while pos < text_len: ls: sizet = pos le: sizet = pos while le < text_len: if text_buf[le] == '\n': break le += 1 ll: sizet = le - ls # define 行判定与 _LookupMethodRetTyFromTextLL 逐字一致 if ll >= 7 and text_buf[ls] == 'd' and string.strncmp(text_buf + ls, "define ", 7) == 0: at_pos: sizet = ls + 7 while at_pos < ls + ll: if text_buf[at_pos] == '@': break at_pos += 1 if at_pos < ls + ll: name_start: sizet = at_pos + 1 quoted: int = 0 if name_start < ls + ll and text_buf[name_start] == '"': name_start += 1 quoted = 1 # 定界:带引号名到 '"',裸名到 '('(与查找侧边界判定等价) name_end: sizet = name_start if quoted != 0: while name_end < ls + ll: if text_buf[name_end] == '"': break name_end += 1 else: while name_end < ls + ll: if text_buf[name_end] == '(': break name_end += 1 name_len: sizet = name_end - name_start # 只登记本模块 mangled 名 "{sha1}.{后缀}"(查询键恒为此前缀);表不可用时跳过登记 if cache_on != 0 and name_len > sha1_len + 1: if string.strncmp(text_buf + name_start, class_sha1, sha1_len) == 0 and text_buf[name_start + sha1_len] == '.': key_buf: bytes = stdlib.malloc(name_len + 1) if key_buf is not None: string.strncpy(key_buf, text_buf + name_start, name_len) key_buf[name_len] = '\0' # 同名首次为准(不被后续同名覆盖,与首个命中一致) if ht.__contains__(key_buf) == 0: rt_len: sizet = at_pos - (ls + 7) kcopy: str = perm.alloc(name_len + 1) tcopy: str = perm.alloc(rt_len + 1) if kcopy is not None and tcopy is not None: string.strncpy(kcopy, text_buf + name_start, name_len) kcopy[name_len] = '\0' string.strncpy(tcopy, text_buf + ls + 7, rt_len) tcopy[rt_len] = '\0' ht.set_str(kcopy, tcopy) stdlib.free(key_buf) # 同趟查找:首个与 search_buf 匹配、且后随 '"' / '(' / 行尾的 define(判定与原二次扫描逐字一致),先命中者为准 if result_ty is None and name_start + search_str_len <= ls + ll: if string.strncmp(text_buf + name_start, search_buf, search_str_len) == 0: end_ch: int = 0 if name_start + search_str_len < ls + ll: end_ch = text_buf[name_start + search_str_len] if end_ch == 34 or end_ch == 40 or end_ch == 0: result_ty = self._ParseLLVMRetTyStr(pool, text_buf, ls + 7, at_pos) pos = le + 1 return result_ty # _LookupMethodRetTyFromTextLL:读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),按 "{sha1}.{class_name}.{method_name}" 搜索 define 行并解析返回类型;未找到/文件不存在返回 None # ============================================================ def _LookupMethodRetTyFromTextLL(self, pool: memhub.MemBuddy*, class_sha1: str, class_name: str, method_name: str, use_bare: int = 0) -> llvmlite.LLVMType*: """从依赖模块的 text.ll 中查找方法返回类型 use_bare: 1=按模块级函数查找(搜索 "{sha1}.{method_name}" define, 用于 new_lexer/tokenize 等模块函数;class_name 忽略)。 """ if pool is None or class_sha1 is None or method_name is None: return None if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return None sha1_len: sizet = string.strlen(class_sha1) if sha1_len < 2: return None # 缓存命中:O(1) 取回返回类型文本并用调用方 pool 重建类型直接返回;未命中走下方原实现 cached_text: str = self._LookupCachedRetTyText(class_sha1, class_name, method_name, use_bare) if cached_text is not None: return self._ParseLLVMRetTyStr(pool, cached_text, 0, string.strlen(cached_text)) # 构造 text.ll 路径 (level=1): {temp_dir}/{sha1[0:2]}/{sha1}.text.ll # 使用 subdir 缓冲区避免 %c 格式化问题(与 StubMerger._sliced_path 一致) path_len: sizet = self.Gen.CallGenBase.TempDirLen + sha1_len + 14 path_buf: bytes = stdlib.malloc(path_len) if path_buf is None: return None subdir_buf: bytes = stdlib.malloc(3) if subdir_buf is None: stdlib.free(path_buf) return None subdir_buf[0] = class_sha1[0] subdir_buf[1] = class_sha1[1] subdir_buf[2] = '\0' viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll", self.Gen.CallGenBase.TempDir, subdir_buf, class_sha1) stdlib.free(subdir_buf) # 按文件实际大小读取 text.ll(固定缓冲区会截断大模块导致末尾定义查不到);64MB 上限防御异常超大文件 tf: rawptr[fileio.File] = fileio.File(path_buf, fileio.MODE.R) if tf.closed: stdlib.free(path_buf) return None tfsize: LONGLONG = tf.size() if tfsize <= 0 or tfsize > 67108864: tf.close() stdlib.free(path_buf) return None read_size: sizet = sizet(tfsize) read_buf: bytes = stdlib.malloc(read_size + 1) if read_buf is None: tf.close() stdlib.free(path_buf) return None text_len: int64 = tf.read_all(read_buf, ULONG(read_size + 1)) tf.close() stdlib.free(path_buf) if text_len <= 0: stdlib.free(read_buf) return None read_buf[text_len] = '\0' # 构造搜索模式: "{sha1}.{class_name}.{method_name}" 或 bare 模式 "{sha1}.{method_name}" cls_len: sizet = 0 if class_name is not None: cls_len = string.strlen(class_name) meth_len: sizet = string.strlen(method_name) search_len: sizet = sha1_len + meth_len + 2 if use_bare == 0: search_len = sha1_len + cls_len + meth_len + 4 search_buf: bytes = stdlib.malloc(search_len) if search_buf is None: stdlib.free(read_buf) return None if use_bare != 0: viperlib.snprintf(search_buf, search_len, "%s.%s", class_sha1, method_name) else: viperlib.snprintf(search_buf, search_len, "%s.%s.%s", class_sha1, class_name, method_name) search_str_len: sizet = string.strlen(search_buf) # 单趟扫描 text.ll:登记其全部 define 行供后续 O(1) 命中,并同趟取出首个匹配项。 # 文件长度直接用 read_all 的返回值,省掉一次整文件 strlen。 result_ty: llvmlite.LLVMType* = self._CacheModuleRetTyTexts( class_sha1, read_buf, sizet(text_len), pool, search_buf, search_str_len) stdlib.free(search_buf) stdlib.free(read_buf) return result_ty # _dep_text_ll_exists:探测 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll 是否存在(1=存在),用于区分「依赖已编译但函数缺失」与「依赖模块尚未翻译」 # ============================================================ def _dep_text_ll_exists(self, pool: memhub.MemBuddy*, sha1: str) -> int: """探测 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll 是否存在,1=存在""" if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0 or sha1 is None: return 0 sha1_len: sizet = string.strlen(sha1) if sha1_len < 2: return 0 path_len: sizet = self.Gen.CallGenBase.TempDirLen + sha1_len + 14 path_buf: bytes = stdlib.malloc(path_len) subdir_buf: bytes = stdlib.malloc(3) if path_buf is None or subdir_buf is None: if path_buf is not None: stdlib.free(path_buf) if subdir_buf is not None: stdlib.free(subdir_buf) return 0 subdir_buf[0] = sha1[0] subdir_buf[1] = sha1[1] subdir_buf[2] = '\0' viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll", self.Gen.CallGenBase.TempDir, subdir_buf, sha1) stdlib.free(subdir_buf) # 存在性判定用 GetFileAttributesA(属性查询,不开文件句柄) attr: ULONG = win32file.GetFileAttributesA(path_buf) exists: int = 1 # INVALID_FILE_ATTRIBUTES = 不存在/不可访问 → 0。 if attr == 0xFFFFFFFF: exists = 0 # 原实现以只读 CreateFileA 打开:目录会被拒绝(未带 FILE_FLAG_BACKUP_SEMANTICS) # → 0,故此处显式排除目录以保持「是可读常规文件」的等价判定。 if (attr & win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: exists = 0 stdlib.free(path_buf) return exists # 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助:把 obj.prop / obj.prop=v / del obj.prop / Class.method() 翻译为 {sha1}.ClassName.prop 系列函数调用(元数据表由 HandlesClassDef 填充) # _translate_property_call:翻译属性方法调用——优先用模块中已有函数(精确/后缀匹配),否则用类所属模块 SHA1 构造 "{sha1}.ClassName.method";call_head/call_count 为已构建好的参数链表(如 getter 只有 obj 一个参数)