Files
ViperC/App/lib/core/Gen/Call/CallGenBase.vp
T
TermiNexus 1ad7abf33d refactor(TransPyV): 统一自举写法(novirtual / define / rawptr→* / 去续行反斜杠)
- @t.NoVTable class X → novirtual class X
- t.CDefine = N → define N
- rawptr[X]、X | t.CPtr → X*
- t.CPtr → rawptr(裸指针标记)
- t.Callable[...] → callable[...]
- 去掉 if/elif 条件的行续反斜杠(改单行写法)
- w32.win32file.X → win32file.X(已 import as)
- core/__init__.vp 去掉未使用的 import t, c / from stdint import *

涉及:CompilerFactory、IncludesScanner、Repl、core/__init__、
Gen/Call/{CallGenBase,CallGenCBuiltin,CallGenCall,CallGenDispatch,CallGenLeafKit}、
Gen/Class/ClassGenGeneric。
2026-10-02 02:16:37 +08:00

1285 lines
69 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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_sha1>.list[str]"* @"<sha1>._str.split"(...)
# 本函数若把 `%"<list_sha1>.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 一个参数)