- @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。
1285 lines
69 KiB
Plaintext
1285 lines
69 KiB
Plaintext
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 一个参数)
|