1193 lines
62 KiB
Plaintext
1193 lines
62 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import llvmlite
|
||
import memhub
|
||
import string
|
||
import stdio
|
||
import stdlib
|
||
import viperlib
|
||
import w32.fileio as fileio
|
||
import lib.Stub.StubMerger as StubMerger
|
||
import lib.core.VLogger as VLogger
|
||
import lib.Projectrans.Config as Config
|
||
|
||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||
|
||
|
||
# ============================================================
|
||
# ExprGenCdef - ExprGenMixin 拆分(阶段 D):跨模块 CDefine 查找
|
||
# ============================================================
|
||
novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||
"""跨模块 CDefine 查找"""
|
||
|
||
# _build_source_path_from_sha1:通过 SHA1 从 StubMerger 全局存储器构建源 .vp/.py 文件路径(.vpi 未生成时回退读源 .py);rel_path 以 "lib/" 开头 → {Config.SourceDir}/{rel_path},其他 → {Config.IncludesDir}/{rel_path}。返回 stdlib.malloc 分配的路径字符串(调用者负责 free),失败返回 None
|
||
def _build_source_path_from_sha1(self, sha1: str) -> str:
|
||
"""通过 SHA1 从全局存储器构建源 .py 文件路径"""
|
||
if sha1 is None: return None
|
||
if Config.IncludesDir is None: return None
|
||
# 从 StubMerger 全局存储器获取相对路径数组
|
||
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
|
||
count: int = StubMergerStore.GetSha1StoreCount()
|
||
if rel_arr_ptr is None or count <= 0: return None
|
||
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
|
||
if sha1_arr_ptr is None: return None
|
||
# 遍历查找匹配的 SHA1
|
||
idx_s: int = -1
|
||
for i in range(count):
|
||
sidx: sizet = sizet(i) * 17
|
||
cur_sha1: str = sha1_arr_ptr + sidx
|
||
if cur_sha1[0] == '\0': continue
|
||
if string.strcmp(cur_sha1, sha1) == 0:
|
||
idx_s = i
|
||
break
|
||
if idx_s < 0:
|
||
return None
|
||
# 获取相对路径(MAX_REL_PATH_LEN=256,避免跨模块 CDefine 解析问题)
|
||
MAX_RPL: sizet = 256
|
||
ridx: sizet = sizet(idx_s) * MAX_RPL
|
||
rel_path: str = rel_arr_ptr + ridx
|
||
if rel_path[0] == '\0':
|
||
return None
|
||
# 选择基础目录(优先级):1) 条目存储的 includes 根目录 IncDir(绝对路径,支持多 includes 目录,避免"首 includes 目录"启发式对非首目录文件失效);2) rel_path 前缀启发式——以 "lib/" 开头 → App 源文件(SourceDir);"includes/" 开头 → includes 文件(去掉前缀);其他无前缀路径先试 SourceDir,不存在再试 IncludesDir
|
||
base_dir: str = None
|
||
rel_used: str = rel_path
|
||
inc_arr_ptr: bytes = StubMergerStore.GetSha1StoreIncdirArrPtr()
|
||
if inc_arr_ptr is not None:
|
||
iidx: sizet = sizet(idx_s) * StubMerger.MAX_INC_DIR_LEN
|
||
inc_dir: str = inc_arr_ptr + iidx
|
||
if inc_dir[0] != '\0':
|
||
base_dir = inc_dir
|
||
if base_dir is None and string.strlen(rel_path) >= 4:
|
||
if (rel_path[0] == 'l' and rel_path[1] == 'i' and
|
||
rel_path[2] == 'b' and rel_path[3] == '/'):
|
||
base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
|
||
if base_dir is None and string.strlen(rel_path) >= 9:
|
||
if (rel_path[0] == 'i' and rel_path[1] == 'n' and
|
||
rel_path[2] == 'c' and rel_path[3] == 'l' and
|
||
rel_path[4] == 'u' and rel_path[5] == 'd' and
|
||
rel_path[6] == 'e' and rel_path[7] == 's' and
|
||
rel_path[8] == '/'):
|
||
rel_used = rel_path + 9
|
||
base_dir = Config.IncludesDir
|
||
if base_dir is None:
|
||
base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
|
||
# 构建完整路径: {base_dir}/{rel_used}
|
||
base_len: sizet = string.strlen(base_dir)
|
||
rel_len: sizet = string.strlen(rel_used)
|
||
path_len: sizet = base_len + rel_len + 2
|
||
buf: str = stdlib.malloc(path_len)
|
||
if buf is None:
|
||
return None
|
||
viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_used)
|
||
# 存在性检查:SourceDir 下不存在且 IncludesDir 不同 → 重试 IncludesDir
|
||
pf_chk: rawptr[fileio.File] = fileio.File(buf, fileio.MODE.R)
|
||
if pf_chk.closed:
|
||
pf_chk.close()
|
||
if Config.IncludesDir is not None and base_dir != Config.IncludesDir:
|
||
stdlib.free(buf)
|
||
base_len2: sizet = string.strlen(Config.IncludesDir)
|
||
path_len2: sizet = base_len2 + rel_len + 2
|
||
buf = stdlib.malloc(path_len2)
|
||
if buf is None:
|
||
return None
|
||
viperlib.snprintf(buf, path_len2, "%s/%s", Config.IncludesDir, rel_used)
|
||
pf_chk2: rawptr[fileio.File] = fileio.File(buf, fileio.MODE.R)
|
||
if pf_chk2.closed:
|
||
pf_chk2.close()
|
||
stdlib.free(buf)
|
||
return None
|
||
pf_chk2.close()
|
||
else:
|
||
stdlib.free(buf)
|
||
return None
|
||
else:
|
||
pf_chk.close()
|
||
return buf
|
||
# ============================================================
|
||
# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量
|
||
#
|
||
# 当当前模块的 CDefine 表中找不到某个 Name 时,从 from_imports
|
||
# 解析出源模块,再从该模块的 pyi 文件中解析 CDefine 常量值。
|
||
# 找到后缓存到当前模块的 CDefine 表中,避免重复查找。
|
||
#
|
||
# pyi 文件格式: "NAME: t.CDefine = value"
|
||
# ============================================================
|
||
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*,
|
||
name: str,
|
||
from_imports: str,
|
||
imported_modules: str = None,
|
||
depth: int = 0,
|
||
node: ast.AST* = None) -> int64:
|
||
"""跨模块查找 CDefine 常量,返回值(64 位)或 -1(未找到)
|
||
|
||
查找顺序:
|
||
1. from_imports 精确匹配(from X import NAME)
|
||
2. from_imports star 回退(from X import *)
|
||
3. imported_modules 遍历(import X):当通过 import X 导入模块时,
|
||
模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用)
|
||
|
||
depth: 递归深度(子模块递归/imported_modules 遍历的内部调用递增)。
|
||
node: 触发查找的 AST 节点(递归内传 None,line 0;顶层调用点传实际节点)。
|
||
"""
|
||
# 递归深度守卫:子模块递归(from .X import NAME re-export 链)与
|
||
# imported_modules 遍历组合可能形成循环(模块间互相 from import),
|
||
# 无上限时无限递归 → 每次递归 pool.alloc 缓冲(rc_sub_buf/rc_full_mod/
|
||
# rc_fi/im_buf/sub_fi)→ O(n²) 累积 → 主池 1.5GB OOM
|
||
# (NewAstTest CONST_INT 单次查找 14593 次调用,fi 恒为 CONST_INT:ast)。
|
||
# 正常跨模块 CDefine 查找深度 < 8(如 ast → ast.base),32 上限 fail-fast
|
||
# 暴露循环 import 链(而非静默 OOM)。报错带 node 行号 + 当前递归模块链。
|
||
if depth > 32:
|
||
err_buf: str = pool.alloc(256)
|
||
if err_buf is not None:
|
||
viperlib.snprintf(err_buf, 256,
|
||
"跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export): %s (depth=%d fi=%s)",
|
||
name, depth,
|
||
from_imports if from_imports is not None else "(nil)")
|
||
self.Gen.TypeGenErr.fatal_error(node, err_buf, "InternalCompilerError")
|
||
self.Gen.TypeGenErr.fatal_error(node,
|
||
"跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export)", "InternalCompilerError")
|
||
# 入口处清零标志,确保所有早退路径(return -1)都返回"未找到"状态
|
||
# 避免递归调用时前一次成功的标志残留导致误判
|
||
self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||
if name is None:
|
||
return -1
|
||
if from_imports is None:
|
||
from_imports = ""
|
||
|
||
# 1. 从 from_imports 查找名称对应的模块名
|
||
# 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导
|
||
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 0)
|
||
if mod_name_raw is None:
|
||
# 精确匹配失败:尝试 star import 回退
|
||
mod_name_raw = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 1)
|
||
|
||
# 截断模块名到 NUL 结尾:lookup_from_import 返回指向 from_imports 内部的
|
||
# 指针(非 NUL 结尾,后随空格分隔的其他条目,如 "ast CONST_STR:ast ...")。
|
||
# 不截断会污染 base_mod:strlen 读到整个 from_imports 末尾 → rc_full_mod
|
||
# 每层累积整个列表 + ".base"(dump:ast.base.base.base 无限递归根因),
|
||
# 且缓存 key 错乱("CONST_INT@ast CONST_STR:ast ...")无法命中。
|
||
# 与 _lookup_sha1_from_modname_raw 的截断逻辑一致(空格/冒号/NUL 分隔)。
|
||
base_mod: str = None
|
||
if mod_name_raw is not None:
|
||
mbl: sizet = 0
|
||
while mod_name_raw[mbl] != ' ' and mod_name_raw[mbl] != '\0' \
|
||
and mod_name_raw[mbl] != ':':
|
||
mbl += 1
|
||
mb_buf: str = pool.alloc(mbl + 1)
|
||
if mb_buf is not None:
|
||
mi2: sizet
|
||
for mi2 in range(mbl):
|
||
mb_buf[mi2] = mod_name_raw[mi2]
|
||
mb_buf[mbl] = '\0'
|
||
base_mod = mb_buf
|
||
while base_mod is not None and base_mod[0] == '.':
|
||
base_mod = base_mod + 1
|
||
|
||
# 跨模块 CDefine 查找缓存(perm_pool 永久,跨文件复用):
|
||
# 首次查找成功后结果缓存,后续文件(含 reset_to(mark2)/模块切换)直接命中,
|
||
# 避免 CONST_INT 跨模块递归查找反复触发(NewAstTest OOM 根因——
|
||
# 14593 次调用全部重复递归 ast → ast.base → ...)。
|
||
# key = "name@源模块"(源模块维度,用去点后的 base_mod 保证一致),
|
||
# 保持模块隔离:不同源模块的同名 CDefine 值不同时不串值。
|
||
# 仅 ALL_CAPS 全局唯一宏名才进入本函数(_is_cdefine_name_pattern 已过滤)。
|
||
xkey_buf: str = None
|
||
if base_mod is not None:
|
||
xk_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||
xkey_buf = pool.alloc(xk_len)
|
||
if xkey_buf is not None:
|
||
viperlib.snprintf(xkey_buf, xk_len, "%s@%s", name, base_mod)
|
||
xval: str = self.Gen.TypeGenCDef.lookup_xmod_cdefine(xkey_buf)
|
||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
|
||
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval)
|
||
elif self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||
xd_v: double = string.atof(xval)
|
||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v)
|
||
else:
|
||
xv_i: int64 = string.atoll(xval)
|
||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i)
|
||
return string.atoll(xval)
|
||
|
||
# 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6)
|
||
# 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中,
|
||
# 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules
|
||
if mod_name_raw is None:
|
||
# from_imports 完全没有匹配(精确和 star 都失败)
|
||
# 直接进入 imported_modules 遍历(步骤 9.6)
|
||
if imported_modules is None:
|
||
return -1
|
||
# 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历
|
||
# 设置 result_found=0 跳过步骤 3-9,直接到 9.6
|
||
self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||
# 直接遍历 imported_modules
|
||
im_len: sizet = string.strlen(imported_modules)
|
||
im_ci: sizet = 0
|
||
while im_ci < im_len:
|
||
while im_ci < im_len and imported_modules[im_ci] == ' ':
|
||
im_ci += 1
|
||
if im_ci >= im_len:
|
||
break
|
||
im_start: sizet = im_ci
|
||
while im_ci < im_len and imported_modules[im_ci] != ' ':
|
||
im_ci += 1
|
||
im_end: sizet = im_ci
|
||
im_nlen: sizet = im_end - im_start
|
||
if im_nlen > 0 and im_nlen < 256:
|
||
im_buf: str = pool.alloc(im_nlen + 1)
|
||
if im_buf is not None:
|
||
mk: sizet = 0
|
||
while mk < im_nlen:
|
||
im_buf[mk] = imported_modules[im_start + mk]
|
||
mk += 1
|
||
im_buf[im_nlen] = '\0'
|
||
sub_fi: str = pool.alloc(im_nlen + 3)
|
||
if sub_fi is not None:
|
||
sub_fi[0] = '*'
|
||
sub_fi[1] = ':'
|
||
mk2: sizet = 0
|
||
while mk2 < im_nlen:
|
||
sub_fi[2 + mk2] = im_buf[mk2]
|
||
mk2 += 1
|
||
sub_fi[2 + im_nlen] = '\0'
|
||
sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
|
||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||
return sub_val
|
||
return -1
|
||
|
||
# 2. 截断模块名 → 去前导点 → 精确+后缀查找 SHA1(复用 HandlesExprCall 公共逻辑)
|
||
sha1: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw)
|
||
if sha1 is None:
|
||
return -1
|
||
|
||
# 5. 获取 temp_dir
|
||
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
|
||
if temp_dir is None:
|
||
return -1
|
||
|
||
# 6. 构建 vpi 文件路径
|
||
td_len: sizet = string.strlen(temp_dir)
|
||
pyi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "vpi")
|
||
if pyi_path is None:
|
||
return -1
|
||
|
||
# 7. 读取 vpi 文件;若不存在或内容过短(空 stub)则回退读取源 .vp/.py 文件
|
||
PYI_READ_BUF_SIZE: sizet = 65536
|
||
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
|
||
if pyi_buf is None:
|
||
stdlib.free(pyi_path)
|
||
return -1
|
||
pf: rawptr[fileio.File] = fileio.File(pyi_path, fileio.MODE.R)
|
||
need_py_fallback: int = 0
|
||
bytes_read: LONG = 0
|
||
if pf.closed:
|
||
# .vpi 文件不存在:回退读取源 .vp/.py 文件
|
||
need_py_fallback = 1
|
||
else:
|
||
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
|
||
pf.close()
|
||
# vpi 内容过短(< 10 字节)视为空 stub,回退 .py
|
||
if bytes_read < 10:
|
||
need_py_fallback = 1
|
||
else:
|
||
if bytes_read < PYI_READ_BUF_SIZE:
|
||
pyi_buf[bytes_read] = 0
|
||
# vpi(存根签名文件)中的 CDefine 声明可能只带名字不带值
|
||
# (Converter 旧版只对 CONST_INT/BOOL 等输出 "= value",CONST_FLOAT
|
||
# 等不输出 → numpy 的 pi/e 在 stub 中无值),真正的值在源
|
||
# .vp/.py 文件中。旧启发式"vpi 中任一 CDefine 带值 → 不读源"是错的:
|
||
# 模块同时含 int CDefine(带值,如 numpy.MAX_NDIM=4)与 float
|
||
# CDefine(无值,如 pi)时,int 行让 cd_sig=1 而漏回退源 → pi
|
||
# 解析失败 → 退化 @pi 全局指针 → float<->ptr 报错(NumpyTest 根因)。
|
||
# 改为按目标名判定:仅当 vpi 中 name 的 CDefine 行带值时才信任
|
||
# vpi,否则(name 行无值 / name 不在 vpi / 整个 vpi 无带值 CDefine)
|
||
# 一律回退读源文件。
|
||
cd_sig: int = 0
|
||
name_has_val: int = 0
|
||
cs_name_len: sizet = string.strlen(name)
|
||
cs_total: sizet = string.strlen(pyi_buf)
|
||
cs_i: sizet = 0
|
||
while cs_i + 9 < cs_total:
|
||
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and
|
||
pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and
|
||
pyi_buf[cs_i + 4] == 'e' and pyi_buf[cs_i + 5] == 'f' and
|
||
pyi_buf[cs_i + 6] == 'i' and pyi_buf[cs_i + 7] == 'n' and
|
||
pyi_buf[cs_i + 8] == 'e'):
|
||
# 检查该行剩余部分是否有 '='(带值声明才在 vpi 中匹配)
|
||
cd_eq: int = 0
|
||
cs_j: sizet = cs_i + 9
|
||
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
|
||
if pyi_buf[cs_j] == '=':
|
||
cd_eq = 1
|
||
break
|
||
cs_j += 1
|
||
if cd_eq != 0:
|
||
cd_sig = 1
|
||
# 定位该行行首,检查是否以 "name:" 开头(目标名带值)
|
||
cs_line_start: sizet = cs_i
|
||
while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n':
|
||
cs_line_start -= 1
|
||
if cs_i - cs_line_start >= cs_name_len + 1:
|
||
nm_ok: int = 1
|
||
for ki2 in range(cs_name_len):
|
||
if pyi_buf[cs_line_start + ki2] != name[ki2]:
|
||
nm_ok = 0
|
||
break
|
||
if nm_ok == 1 and pyi_buf[cs_line_start + cs_name_len] == ':':
|
||
name_has_val = 1
|
||
cs_i += 1
|
||
if cd_sig == 0 or name_has_val == 0:
|
||
need_py_fallback = 1
|
||
stdlib.free(pyi_path)
|
||
|
||
if need_py_fallback != 0:
|
||
# 从 SHA1 存储器查找相对路径,构建 {base_dir}/{rel_path}
|
||
src_path: str = self._build_source_path_from_sha1(sha1)
|
||
if src_path is None:
|
||
# 存储器中找不到 SHA1 = 编译器 bug(Phase1/Phase2 未填充存储器)
|
||
# 报错终止,不静默返回 -1(避免后续生成错误的 @UNDEFINED 全局变量引用)
|
||
err_buf: str = pool.alloc(512)
|
||
if err_buf is not None:
|
||
viperlib.snprintf(err_buf, 512,
|
||
"跨模块 CDefine 查找失败:SHA1 %s 不在存储器中(base_mod=%s name=%s)。"
|
||
"Phase1/Phase2 未正确填充 PopulateSha1MapStore/AppendToSha1MapStore",
|
||
sha1, base_mod, name)
|
||
VLogger.error(err_buf, "XMOD-CD")
|
||
stdlib.free(pyi_buf)
|
||
return -1
|
||
pf = fileio.File(src_path, fileio.MODE.R)
|
||
stdlib.free(src_path)
|
||
if pf.closed:
|
||
stdlib.free(pyi_buf)
|
||
return -1
|
||
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
|
||
pf.close()
|
||
if bytes_read <= 0:
|
||
stdlib.free(pyi_buf)
|
||
return -1
|
||
if bytes_read < PYI_READ_BUF_SIZE:
|
||
pyi_buf[bytes_read] = 0
|
||
|
||
# 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
|
||
# 格式: NAME: t.CDefine = value
|
||
name_len: sizet = string.strlen(name)
|
||
total_len: sizet = string.strlen(pyi_buf)
|
||
pos: sizet = 0
|
||
result_val: int64 = 0
|
||
result_found: int = 0
|
||
res_is_double: int = 0
|
||
res_is_str: int = 0
|
||
while pos < total_len:
|
||
# 找到行首
|
||
line_start: sizet = pos
|
||
# 找到行尾
|
||
while pos < total_len and pyi_buf[pos] != '\n':
|
||
pos += 1
|
||
line_len: sizet = pos - line_start
|
||
if pos < total_len:
|
||
pos += 1 # 跳过 '\n'
|
||
|
||
# 检查行是否以 "name:" 开头
|
||
if line_len < name_len + 1:
|
||
continue
|
||
match_ok: int = 1
|
||
for ki in range(name_len):
|
||
if pyi_buf[line_start + ki] != name[ki]:
|
||
match_ok = 0
|
||
break
|
||
if match_ok == 0:
|
||
continue
|
||
if pyi_buf[line_start + name_len] != ':':
|
||
continue
|
||
|
||
# 检查行是否包含 "t.CDefine"
|
||
# 在行中查找 "t.CDefine"
|
||
has_cdefine: int = 0
|
||
ci: sizet = line_start + name_len + 1
|
||
while ci + 8 <= line_start + line_len:
|
||
if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and
|
||
pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and
|
||
pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and
|
||
pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and
|
||
pyi_buf[ci + 8] == 'e'):
|
||
has_cdefine = 1
|
||
break
|
||
ci += 1
|
||
if has_cdefine == 0:
|
||
continue
|
||
|
||
# 找到 "=" 后面的值
|
||
eq_pos: sizet = line_start + name_len + 1
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=':
|
||
eq_pos += 1
|
||
if eq_pos >= line_start + line_len:
|
||
continue
|
||
eq_pos += 1 # 跳过 '='
|
||
|
||
# 跳过空格
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
|
||
eq_pos += 1
|
||
|
||
# 检查是否是 t.CUnsignedLong(...) / t.CLong(...) / int(...) 等类型构造函数
|
||
# 格式: t.CUnsignedLong(-11) / t.CUnsignedLong(0xFFFFFFFF)
|
||
# 如果是,跳过 "t.CXxx(" 前缀,解析括号内的值,忽略结尾 ')'
|
||
is_type_ctor: int = 0
|
||
if eq_pos + 2 < line_start + line_len:
|
||
if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
|
||
# 找到 '(' 的位置
|
||
paren_pos: sizet = eq_pos + 2
|
||
while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(':
|
||
paren_pos += 1
|
||
if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
|
||
is_type_ctor = 1
|
||
eq_pos = paren_pos + 1 # 跳过 '('
|
||
# 跳过括号内可能的前导空格
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
|
||
eq_pos += 1
|
||
|
||
# 括号包裹标记:(1 << 23) / (512) / (A | B)
|
||
is_paren_expr: int = 0
|
||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '(':
|
||
is_paren_expr = 1
|
||
eq_pos += 1
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
|
||
eq_pos += 1
|
||
|
||
# === 解析左操作数 token(数字 / hex / 负号 / 名称) ===
|
||
# 负号标记
|
||
is_neg: int = 0
|
||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
|
||
is_neg = 1
|
||
eq_pos += 1
|
||
val_str_start: sizet = eq_pos
|
||
val_str_len: sizet = 0
|
||
is_hex: int = 0
|
||
is_float_val: int = 0
|
||
is_name_val: int = 0
|
||
is_str_val: int = 0
|
||
# 字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2"):读引号内内容。
|
||
# 跨模块读回此前不支持引号 → val_str_len=0 被跳过 → "跨模块 CDefine
|
||
# 未找到"(test_pyzlib.vp pyzlib.ZLIB_VERSION 根因)
|
||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
|
||
is_str_val = 1
|
||
eq_pos += 1 # 跳过开引号
|
||
val_str_start = eq_pos
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '"':
|
||
val_str_len += 1
|
||
eq_pos += 1
|
||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
|
||
eq_pos += 1 # 跳过闭引号
|
||
if eq_pos + 1 < line_start + line_len:
|
||
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
|
||
is_hex = 1
|
||
val_str_len = 2
|
||
eq_pos += 2
|
||
elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or \
|
||
(pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or \
|
||
pyi_buf[eq_pos] == '_':
|
||
is_name_val = 1
|
||
while eq_pos < line_start + line_len:
|
||
c_tk: char = pyi_buf[eq_pos]
|
||
if (c_tk >= 'A' and c_tk <= 'Z') or (c_tk >= 'a' and c_tk <= 'z') or \
|
||
(c_tk >= '0' and c_tk <= '9') or c_tk == '_':
|
||
val_str_len += 1
|
||
eq_pos += 1
|
||
else:
|
||
break
|
||
# 数字/hex 字符续读(字符串值已整体读取,跳过)
|
||
while eq_pos < line_start + line_len and is_name_val == 0 and is_str_val == 0:
|
||
ch: char = pyi_buf[eq_pos]
|
||
if is_hex == 1:
|
||
if ((ch >= '0' and ch <= '9') or
|
||
(ch >= 'a' and ch <= 'f') or
|
||
(ch >= 'A' and ch <= 'F')):
|
||
val_str_len += 1
|
||
eq_pos += 1
|
||
else:
|
||
break
|
||
else:
|
||
if ch >= '0' and ch <= '9':
|
||
val_str_len += 1
|
||
eq_pos += 1
|
||
elif ch == '.' and is_float_val == 0:
|
||
# 浮点值:'3.14159'('.' 只允许一次)
|
||
is_float_val = 1
|
||
val_str_len += 1
|
||
eq_pos += 1
|
||
else:
|
||
break
|
||
|
||
if val_str_len == 0 and is_str_val == 0:
|
||
continue
|
||
|
||
# 复制左 token 到缓冲区
|
||
val_buf: str = pool.alloc(val_str_len + 1)
|
||
if val_buf is None:
|
||
continue
|
||
for vi in range(val_str_len):
|
||
val_buf[vi] = pyi_buf[val_str_start + vi]
|
||
val_buf[val_str_len] = '\0'
|
||
|
||
# 左 token 求值
|
||
lval: int64 = 0
|
||
l_is_double: int = 0
|
||
ldbl: double = 0.0
|
||
if is_str_val != 0:
|
||
# 字符串 CDefine(如 ZLIB_VERSION = "1.3.2"):注册字符串值
|
||
# (含空串 ""),result_found 标记 + res_is_str 供尾部跳过整数
|
||
# 注册/缓存(否则 register_cdefine_constant 把字符串当 0 整数)
|
||
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, val_buf)
|
||
result_val = 0
|
||
result_found = 1
|
||
res_is_str = 1
|
||
elif is_name_val != 0:
|
||
# True/False 布尔字面量 → 1/0(对齐 TPC:bool 在 CDefine 语境按整数处理)
|
||
if string.strcmp(val_buf, "True") == 0:
|
||
lval = 1
|
||
elif string.strcmp(val_buf, "False") == 0:
|
||
lval = 0
|
||
else:
|
||
# 名称引用:查当前 CDefine 表(如 FOREGROUND_COMBO = FOREGROUND_RED)
|
||
lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf)
|
||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||
# 名称未注册:该行无法求值,跳过
|
||
continue
|
||
elif is_hex == 1:
|
||
# 手动解析十六进制(跳过 0x 前缀)——64 位累加,
|
||
# 0xFFFFFFFF=4294967295 保持全精度(常量生成端 i32 截断回 -1)
|
||
hex_result: int64 = 0
|
||
hex_buf: str = val_buf + 2 # 跳过 "0x"
|
||
for hi in range(val_str_len - 2):
|
||
hc: char = hex_buf[hi]
|
||
hd: int = 0
|
||
if hc >= '0' and hc <= '9':
|
||
hd = hc - '0'
|
||
elif hc >= 'a' and hc <= 'f':
|
||
hd = hc - 'a' + 10
|
||
elif hc >= 'A' and hc <= 'F':
|
||
hd = hc - 'A' + 10
|
||
else:
|
||
break
|
||
hex_result = hex_result * 16 + hd
|
||
lval = hex_result
|
||
elif is_float_val != 0:
|
||
l_is_double = 1
|
||
ldbl = string.atof(val_buf)
|
||
lval = string.atoll(val_buf)
|
||
else:
|
||
lval = string.atoll(val_buf)
|
||
if is_neg != 0:
|
||
lval = -lval
|
||
if l_is_double != 0:
|
||
ldbl = -ldbl
|
||
|
||
# 跳过空格,检查是否有二元运算符
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
|
||
eq_pos += 1
|
||
|
||
# 解析运算符(<< >> | & ^ + - * /)
|
||
cd_op: int = 0
|
||
if eq_pos + 1 < line_start + line_len:
|
||
if pyi_buf[eq_pos] == '<' and pyi_buf[eq_pos + 1] == '<':
|
||
cd_op = 1 # LShift
|
||
eq_pos += 2
|
||
elif pyi_buf[eq_pos] == '>' and pyi_buf[eq_pos + 1] == '>':
|
||
cd_op = 2 # RShift
|
||
eq_pos += 2
|
||
if cd_op == 0 and eq_pos < line_start + line_len:
|
||
if pyi_buf[eq_pos] == '|':
|
||
cd_op = 3 # BitOr
|
||
eq_pos += 1
|
||
elif pyi_buf[eq_pos] == '&':
|
||
cd_op = 4 # BitAnd
|
||
eq_pos += 1
|
||
elif pyi_buf[eq_pos] == '^':
|
||
cd_op = 5 # BitXor
|
||
eq_pos += 1
|
||
elif pyi_buf[eq_pos] == '+':
|
||
cd_op = 6 # Add
|
||
eq_pos += 1
|
||
elif pyi_buf[eq_pos] == '-':
|
||
cd_op = 7 # Sub
|
||
eq_pos += 1
|
||
elif pyi_buf[eq_pos] == '*':
|
||
cd_op = 8 # Mult
|
||
eq_pos += 1
|
||
elif pyi_buf[eq_pos] == '/':
|
||
cd_op = 9 # Div
|
||
eq_pos += 1
|
||
|
||
if cd_op != 0:
|
||
# === 解析右操作数 token ===
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
|
||
eq_pos += 1
|
||
r_neg: int = 0
|
||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
|
||
r_neg = 1
|
||
eq_pos += 1
|
||
r_start: sizet = eq_pos
|
||
r_len: sizet = 0
|
||
r_hex: int = 0
|
||
r_name: int = 0
|
||
if eq_pos + 1 < line_start + line_len:
|
||
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
|
||
r_hex = 1
|
||
r_len = 2
|
||
eq_pos += 2
|
||
elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or \
|
||
(pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or \
|
||
pyi_buf[eq_pos] == '_':
|
||
r_name = 1
|
||
while eq_pos < line_start + line_len:
|
||
c_r: char = pyi_buf[eq_pos]
|
||
if (c_r >= 'A' and c_r <= 'Z') or (c_r >= 'a' and c_r <= 'z') or \
|
||
(c_r >= '0' and c_r <= '9') or c_r == '_':
|
||
r_len += 1
|
||
eq_pos += 1
|
||
else:
|
||
break
|
||
while eq_pos < line_start + line_len and r_name == 0:
|
||
c_r2: char = pyi_buf[eq_pos]
|
||
if r_hex == 1:
|
||
if ((c_r2 >= '0' and c_r2 <= '9') or
|
||
(c_r2 >= 'a' and c_r2 <= 'f') or
|
||
(c_r2 >= 'A' and c_r2 <= 'F')):
|
||
r_len += 1
|
||
eq_pos += 1
|
||
else:
|
||
break
|
||
else:
|
||
if c_r2 >= '0' and c_r2 <= '9':
|
||
r_len += 1
|
||
eq_pos += 1
|
||
else:
|
||
break
|
||
if r_len == 0:
|
||
# 运算符后无右操作数:无法求值,跳过该行
|
||
continue
|
||
r_buf: str = pool.alloc(r_len + 1)
|
||
if r_buf is None:
|
||
continue
|
||
for r_i in range(r_len):
|
||
r_buf[r_i] = pyi_buf[r_start + r_i]
|
||
r_buf[r_len] = '\0'
|
||
rval: int64 = 0
|
||
if r_name != 0:
|
||
# True/False 布尔字面量 → 1/0
|
||
if string.strcmp(r_buf, "True") == 0:
|
||
rval = 1
|
||
elif string.strcmp(r_buf, "False") == 0:
|
||
rval = 0
|
||
else:
|
||
rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf)
|
||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||
# 名称未注册:无法求值,跳过该行
|
||
continue
|
||
elif r_hex != 0:
|
||
rhex_v: int64 = 0
|
||
rh_buf: str = r_buf + 2
|
||
for rh_i in range(r_len - 2):
|
||
rc: char = rh_buf[rh_i]
|
||
rd: int = 0
|
||
if rc >= '0' and rc <= '9':
|
||
rd = rc - '0'
|
||
elif rc >= 'a' and rc <= 'f':
|
||
rd = rc - 'a' + 10
|
||
elif rc >= 'A' and rc <= 'F':
|
||
rd = rc - 'A' + 10
|
||
else:
|
||
break
|
||
rhex_v = rhex_v * 16 + rd
|
||
rval = rhex_v
|
||
else:
|
||
rval = string.atoll(r_buf)
|
||
if r_neg != 0:
|
||
rval = -rval
|
||
# 计算二元表达式结果(对齐 TPC ConstEvaluator)
|
||
if cd_op == 1:
|
||
result_val = lval << rval
|
||
elif cd_op == 2:
|
||
result_val = lval >> rval
|
||
elif cd_op == 3:
|
||
result_val = lval | rval
|
||
elif cd_op == 4:
|
||
result_val = lval & rval
|
||
elif cd_op == 5:
|
||
result_val = lval ^ rval
|
||
elif cd_op == 6:
|
||
result_val = lval + rval
|
||
elif cd_op == 7:
|
||
result_val = lval - rval
|
||
elif cd_op == 8:
|
||
result_val = lval * rval
|
||
else:
|
||
if rval == 0:
|
||
continue
|
||
result_val = lval // rval
|
||
result_found = 1
|
||
break
|
||
else:
|
||
# 单值(无运算符):括号内单值如 (512) / (-11) 也走这里
|
||
if is_paren_expr != 0:
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
|
||
eq_pos += 1
|
||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == ')':
|
||
eq_pos += 1
|
||
if is_float_val != 0:
|
||
# 浮点 CDefine(如 U_M_PI = 3.14159...):
|
||
# 值缓存为 double(register_cdefine_double),供调用处生成
|
||
# double 常量;整数部分供 int 路径使用(is_cdefine_double 标志)。
|
||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl)
|
||
result_val = lval
|
||
result_found = 1
|
||
res_is_double = 1
|
||
else:
|
||
result_val = lval
|
||
result_found = 1
|
||
break
|
||
|
||
# 9.5 如果当前模块文件中没找到,递归查找子模块
|
||
# 处理 from .X import NAME 和 from .X import * 的情况
|
||
# 例如 ast.__init__.vp 中 from .base import CONST_INT, ...
|
||
# 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1
|
||
if result_found == 0:
|
||
# base_mod 已在函数开头计算(mod_name_raw 去前导点),
|
||
# 供子模块递归构建 "base_mod.X" 全名。
|
||
name_len_rc: sizet = string.strlen(name)
|
||
rc_pos: sizet = 0
|
||
while rc_pos < total_len and result_found == 0:
|
||
# 找到行首
|
||
rc_line_start: sizet = rc_pos
|
||
# 找到行尾
|
||
while rc_pos < total_len and pyi_buf[rc_pos] != '\n':
|
||
rc_pos += 1
|
||
rc_line_len: sizet = rc_pos - rc_line_start
|
||
if rc_pos < total_len:
|
||
rc_pos += 1
|
||
|
||
# 检查行是否以 "from " 开头(模块导入)
|
||
if rc_line_len < 12:
|
||
continue
|
||
if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \
|
||
pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \
|
||
pyi_buf[rc_line_start+4] != ' ':
|
||
continue
|
||
|
||
# 提取模块路径 token(从 rc_line_start+5 到空格/tab 为止,跳过前导点)
|
||
# 两种写法都要支持:
|
||
# 相对导入 `from .base import X` → 拼 base_mod.base
|
||
# 绝对导入 `from vqt6._types import *` → 直接用 token 自身
|
||
# 此前只认相对导入(硬要求 rc_line_start+5 是 '.'),绝对写法被
|
||
# 直接跳过 → vqt6 包(__init__.vp 用 `from vqt6._types import *`
|
||
# 重导出 QT_ALIGN_* 等)里的 CDefine 全部查不到 →
|
||
# "跨模块 CDefine 未找到: 'vqt6.QT_ALIGN_CENTER'"。
|
||
rc_is_rel: int = 0
|
||
rc_sub_start: sizet = rc_line_start + 5
|
||
while rc_sub_start < rc_line_start + rc_line_len and \
|
||
pyi_buf[rc_sub_start] == '.':
|
||
rc_is_rel = 1
|
||
rc_sub_start += 1
|
||
rc_sub_end: sizet = rc_sub_start
|
||
while rc_sub_end < rc_line_start + rc_line_len and \
|
||
pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t':
|
||
rc_sub_end += 1
|
||
rc_sub_len: sizet = rc_sub_end - rc_sub_start
|
||
if rc_sub_len == 0:
|
||
continue
|
||
|
||
# 检查行是否包含 "import"
|
||
rc_has_import: int = 0
|
||
rc_ipos: sizet = rc_sub_end
|
||
while rc_ipos + 6 <= rc_line_start + rc_line_len:
|
||
if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and \
|
||
pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and \
|
||
pyi_buf[rc_ipos+4] == 'r' and pyi_buf[rc_ipos+5] == 't':
|
||
rc_has_import = 1
|
||
break
|
||
rc_ipos += 1
|
||
if rc_has_import == 0:
|
||
continue
|
||
|
||
# 检查 NAME 是否在导入列表中,或导入是 *
|
||
# 处理多行导入:from .X import (... NAME, ...)
|
||
# 从 "import" 之后扫描,如果有 "(" 则扫描到 ")" 为止
|
||
rc_imp_start: sizet = rc_ipos + 6
|
||
rc_star: int = 0
|
||
rc_name_match: int = 0
|
||
|
||
# 确定扫描范围:单行或跨行(括号内)
|
||
rc_scan_end: sizet = rc_line_start + rc_line_len
|
||
rc_has_paren: int = 0
|
||
rc_pp: sizet = rc_imp_start
|
||
while rc_pp < rc_line_start + rc_line_len:
|
||
if pyi_buf[rc_pp] == '(':
|
||
rc_has_paren = 1
|
||
break
|
||
if pyi_buf[rc_pp] == '*':
|
||
rc_star = 1
|
||
rc_pp += 1
|
||
if rc_has_paren != 0:
|
||
# 多行导入:从 "(" 之后扫描到 ")" 为止
|
||
rc_scan_end = rc_pp + 1
|
||
rc_paren_closed: int = 0
|
||
rc_sp: sizet = rc_pp + 1
|
||
while rc_sp < total_len and rc_paren_closed == 0:
|
||
if pyi_buf[rc_sp] == ')':
|
||
rc_paren_closed = 1
|
||
rc_scan_end = rc_sp
|
||
break
|
||
rc_sp += 1
|
||
if rc_paren_closed == 0:
|
||
rc_scan_end = total_len
|
||
|
||
# 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 *
|
||
rc_cpos: sizet = rc_imp_start
|
||
while rc_cpos < rc_scan_end:
|
||
rc_ch: char = pyi_buf[rc_cpos]
|
||
if rc_ch == '*':
|
||
rc_star = 1
|
||
# 检查 NAME 是否匹配(作为完整单词)
|
||
if rc_cpos + name_len_rc <= rc_scan_end:
|
||
rc_match: int = 1
|
||
rc_ki: sizet
|
||
for rc_ki in range(name_len_rc):
|
||
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
|
||
rc_match = 0
|
||
break
|
||
if rc_match == 1:
|
||
# 检查前字符是否是非标识符字符
|
||
rc_before_ok: int = 0
|
||
if rc_cpos == rc_imp_start:
|
||
rc_before_ok = 1
|
||
else:
|
||
rc_before_ch: char = pyi_buf[rc_cpos - 1]
|
||
if rc_before_ch == ' ' or rc_before_ch == '\t' or \
|
||
rc_before_ch == '(' or rc_before_ch == ',' or \
|
||
rc_before_ch == '\n' or rc_before_ch == '\r':
|
||
rc_before_ok = 1
|
||
# 检查后字符是否是非标识符字符
|
||
rc_after_ok: int = 0
|
||
rc_after_pos: sizet = rc_cpos + name_len_rc
|
||
if rc_after_pos >= rc_scan_end:
|
||
rc_after_ok = 1
|
||
else:
|
||
rc_after_ch: char = pyi_buf[rc_after_pos]
|
||
if rc_after_ch == ' ' or rc_after_ch == '\t' or \
|
||
rc_after_ch == ')' or rc_after_ch == ',' or \
|
||
rc_after_ch == '\r' or rc_after_ch == '\n':
|
||
rc_after_ok = 1
|
||
if rc_before_ok == 1 and rc_after_ok == 1:
|
||
rc_name_match = 1
|
||
rc_cpos += 1
|
||
|
||
if rc_star == 0 and rc_name_match == 0:
|
||
continue
|
||
|
||
# 复制子模块名到新缓冲区(pyi_buf + rc_sub_start 不是 NUL 结尾)
|
||
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
|
||
if rc_sub_buf is None:
|
||
continue
|
||
rc_si: sizet
|
||
for rc_si in range(rc_sub_len):
|
||
rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
|
||
rc_sub_buf[rc_sub_len] = '\0'
|
||
|
||
# 构建完整模块名:
|
||
# 相对导入(rc_is_rel=1)→ <当前模块所属包> + "." + token
|
||
# 基准必须是"包"(rel_path 的目录部分)而不是模块名本身:
|
||
# pkg/sub/mod.vp 里的 `from .X import Y` 指向 pkg.sub.X,
|
||
# 用 base_mod("pkg.sub.mod")会拼成 pkg.sub.mod.X(错)。
|
||
# __init__.vp 时包名 == 模块名,此前只在这种情况碰巧正确
|
||
# (CallGenBase._lookup_reexport_from_vpi 用的是包名,此处对齐)。
|
||
# 绝对导入(rc_is_rel=0)→ 直接用 token(vqt6._types)
|
||
# 自导入(目标模块 == base_mod)跳过,避免无意义递归
|
||
rc_full_mod: str = None
|
||
if rc_is_rel != 0:
|
||
rc_base_mod: str = base_mod
|
||
rc_pkg: str = self.Gen.CallGenBase._module_package_from_sha1(pool, sha1)
|
||
if rc_pkg is not None:
|
||
rc_base_mod = rc_pkg
|
||
rc_full_len: sizet = string.strlen(rc_base_mod) + 1 + rc_sub_len + 1
|
||
rc_full_buf: str = pool.alloc(rc_full_len)
|
||
if rc_full_buf is None:
|
||
continue
|
||
viperlib.snprintf(rc_full_buf, rc_full_len, "%s.%s", rc_base_mod, rc_sub_buf)
|
||
rc_full_mod = rc_full_buf
|
||
else:
|
||
if string.strcmp(rc_sub_buf, base_mod) == 0:
|
||
continue
|
||
rc_full_mod = rc_sub_buf
|
||
|
||
# 防循环:若 base_mod 已以 "."+rc_sub_buf 结尾(如 ast.base 再拼
|
||
# .base → ast.base.base),说明子模块递归在重复追加同一模块段
|
||
# (模块间循环 re-export / 后缀匹配容错命中自身),跳过该次递归
|
||
# (NewAstTest CONST_INT 查找 dump:ast.base.base.base 无限增长根因)。
|
||
rc_blen: sizet = string.strlen(base_mod)
|
||
if rc_blen > rc_sub_len + 1:
|
||
rc_tail: str = base_mod + (rc_blen - rc_sub_len)
|
||
if rc_tail[0] == '.' and \
|
||
string.strncmp(rc_tail + 1, rc_sub_buf, rc_sub_len) == 0:
|
||
continue
|
||
|
||
# 构建临时 from_imports "name:full_mod"
|
||
rc_fi_len: sizet = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
|
||
rc_fi: str = pool.alloc(rc_fi_len)
|
||
if rc_fi is None:
|
||
continue
|
||
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
|
||
|
||
# 递归调用查找子模块
|
||
rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node)
|
||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||
result_val = rc_sub_val
|
||
result_found = 1
|
||
|
||
stdlib.free(pyi_buf)
|
||
|
||
# 9.6 from_imports star fallback 模块中未找到:回退到 imported_modules 遍历
|
||
# 当 `from stdint import *` 导致 star fallback 返回 stdint,但目标常量
|
||
# (如 SHARE_READ)实际在 `import w32.fileio` 导入的模块中时,
|
||
# 需要遍历 imported_modules 逐个查找
|
||
if result_found == 0 and imported_modules is not None:
|
||
im2_len: sizet = string.strlen(imported_modules)
|
||
im2_ci: sizet = 0
|
||
while im2_ci < im2_len and result_found == 0:
|
||
while im2_ci < im2_len and imported_modules[im2_ci] == ' ':
|
||
im2_ci += 1
|
||
if im2_ci >= im2_len:
|
||
break
|
||
im2_start: sizet = im2_ci
|
||
while im2_ci < im2_len and imported_modules[im2_ci] != ' ':
|
||
im2_ci += 1
|
||
im2_end: sizet = im2_ci
|
||
im2_nlen: sizet = im2_end - im2_start
|
||
if im2_nlen > 0 and im2_nlen < 256:
|
||
im2_buf: str = pool.alloc(im2_nlen + 1)
|
||
if im2_buf is not None:
|
||
mk3: sizet = 0
|
||
while mk3 < im2_nlen:
|
||
im2_buf[mk3] = imported_modules[im2_start + mk3]
|
||
mk3 += 1
|
||
im2_buf[im2_nlen] = '\0'
|
||
sub_fi2: str = pool.alloc(im2_nlen + 3)
|
||
if sub_fi2 is not None:
|
||
sub_fi2[0] = '*'
|
||
sub_fi2[1] = ':'
|
||
mk4: sizet = 0
|
||
while mk4 < im2_nlen:
|
||
sub_fi2[2 + mk4] = im2_buf[mk4]
|
||
mk4 += 1
|
||
sub_fi2[2 + im2_nlen] = '\0'
|
||
sub_val2: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
|
||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||
result_val = sub_val2
|
||
result_found = 1
|
||
|
||
# 10. 缓存到当前模块的 CDefine 表中,并写入跨模块 perm_pool 缓存
|
||
# 浮点 CDefine 已在匹配循环中 register_cdefine_double 缓存
|
||
# (res_is_double=1),此处只缓存整数 CDefine,避免覆盖 double。
|
||
if result_found != 0:
|
||
if res_is_str != 0:
|
||
# 字符串 CDefine:值已在匹配循环 register_cdefine_string(val_buf
|
||
# 是命中行的字符串内容,pool 分配跨函数有效),此处只写跨模块缓存
|
||
if base_mod is not None:
|
||
ck_len_s: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||
ckey_s: str = pool.alloc(ck_len_s)
|
||
if ckey_s is not None:
|
||
viperlib.snprintf(ckey_s, ck_len_s, "%s@%s", name, base_mod)
|
||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_s, val_buf, 0, 1)
|
||
elif res_is_double == 0:
|
||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, result_val)
|
||
# 跨模块查找缓存(perm_pool 永久,跨文件复用):整数
|
||
# key 与入口查询一致:"name@源模块"(用截断后的 base_mod,
|
||
# 与 mod_name_raw 一致——两者都是去点后的模块名,保证 key 匹配)
|
||
if base_mod is not None:
|
||
ck_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||
ckey_buf: str = pool.alloc(ck_len)
|
||
if ckey_buf is not None:
|
||
viperlib.snprintf(ckey_buf, ck_len, "%s@%s", name, base_mod)
|
||
cbuf_i: str = pool.alloc(32)
|
||
if cbuf_i is not None:
|
||
viperlib.snprintf(cbuf_i, 32, "%lld", result_val)
|
||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_buf, cbuf_i, 0, 0)
|
||
else:
|
||
# 浮点:从本地表取回 %.17g 文本,写入跨模块缓存(保精度)
|
||
if base_mod is not None:
|
||
ck_len2: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||
ckey2: str = pool.alloc(ck_len2)
|
||
if ckey2 is not None:
|
||
viperlib.snprintf(ckey2, ck_len2, "%s@%s", name, base_mod)
|
||
dval_cc: double = self.Gen.TypeGenCDef.lookup_cdefine_double(name)
|
||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||
cbuf_d: str = pool.alloc(64)
|
||
if cbuf_d is not None:
|
||
stdio.snprintf(cbuf_d, 64, "%.17g", dval_cc)
|
||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey2, cbuf_d, 1, 0)
|
||
self.Gen.TypeGenCDef.set_cdefine_found(1)
|
||
else:
|
||
self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||
|
||
return result_val
|
||
# ============================================================
|
||
# _is_cdefine_name_pattern - 检查名字是否符合 CDefine 命名约定
|
||
#
|
||
# CDefine 常量(如 FOREGROUND_GREEN, STD_OUTPUT_HANDLE, SCOPE_MODULE)
|
||
# 都遵循 ALL_CAPS 约定:仅含大写字母 A-Z、数字 0-9、下划线 _,
|
||
# 且至少含一个大写字母。
|
||
#
|
||
# Python 关键字(match, if, for)和变量名(self, pool, builder)
|
||
# 都含小写字母,不会匹配,从而避免 fail-fast 误报。
|
||
#
|
||
# 返回: 1=符合 CDefine 命名约定, 0=不符合
|
||
# ============================================================
|
||
def _is_cdefine_name_pattern(self, name: str) -> int:
|
||
"""检查名字是否符合 CDefine 命名约定(全大写+下划线+数字)"""
|
||
if name is None:
|
||
return 0
|
||
has_upper: int = 0
|
||
i: sizet = 0
|
||
while name[i] != '\0':
|
||
c: char = name[i]
|
||
if c >= 'a' and c <= 'z':
|
||
return 0
|
||
if c >= 'A' and c <= 'Z':
|
||
has_upper = 1
|
||
i += 1
|
||
return has_upper
|
||
# ============================================================
|
||
# 翻译变量引用(Name 节点)→ load
|
||
# ============================================================
|
||
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*,
|
||
value: int64) -> llvmlite.Value*:
|
||
"""CDefine 整数常量 → LLVM 常量(对齐 TPC TypeConvert 的 64 位升级)
|
||
|
||
**使用处宽度("W_min + 使用处宽度"规则)与物化宽度的关系**(与 TPC 同口径,
|
||
对照表见 Debugger/define-constants.md):
|
||
· 本函数是"取不到使用处宽度"的物化点(调用方 ExprGenName/ExprGenAttr 只有
|
||
常量名与值,没有目标类型)⇒ 按语言默认整型宽度物化:能落进 32 位 ⇒ i32,
|
||
否则 i64。**不**降到 W_min——降到 i8/i16 会把窄立即数喂进 i32 上下文与
|
||
C 默认实参提升(printf 变参),属静默降级/ABI 破坏。
|
||
· "使用处更宽 ⇒ 以该宽度物化"由**消费端**实现:translate_binop 按较宽侧
|
||
zext/sext/coerce_to_type 对齐,赋值/形参按目标存储类型 coerce。
|
||
· "使用处更窄 ⇒ fail-fast"在**注册处与声明处**实现:
|
||
AssignGenMixin.require_cdefine_storable(显式类型 T 的位宽)与
|
||
require_cdefine_use_width(`a: T = C`);超 64 位字面量由
|
||
_int_text_exceeds_64 以**源码文本**判定(lexer 的 atoll 会在字面量层
|
||
回绕,回绕后的 int64 不可信,故本函数不承担溢出判定)。
|
||
|
||
规则(与 translate_constant 的 i32 溢出升级一致,但保留 0xFFFFFFFF 语义):
|
||
值超 32 位无符号范围(> 0xFFFFFFFF)→ i64 全精度常量
|
||
(如 sys.maxsize = 9223372036854775807);
|
||
值低于 i32 下限(< -2147483648)→ i64 全精度常量。符号位为 1 的 64 位
|
||
模式(如 MEMBUDDY_CANARY_PATTERN = 0xA5A5A5A5A5A5A5A5)以 %lld 文本
|
||
存回即负数 -6510615555426900571,仅按 u32 截断回比较会漏判;
|
||
否则 → i32 常量,内部 int 截断(0xFFFFFFFF → -1,保持既有
|
||
INVALID_HANDLE_VALUE / WAIT_FAILED = -1 语义不变,避免 i32 4294967295
|
||
生成非法 LLVM IR 文本 "i32 4294967295")。
|
||
|
||
注意:不能用 `value > 4294967295` 直接比较——TPV 编译自身源码时大数字面量
|
||
(> 0x7FFFFFFF)在字面量层回绕成 i32(sext 后等于 -1),阈值恒成立导致所有
|
||
正数 CDefine 都被升 i64(INVALID_FILE_ATTRIBUTES 0xFFFFFFFF 从 i32 -1 变
|
||
i64 4294967295 → 与 _get_attrs 的 sext(-1) 比较失败,SysLibTest exists FAIL 根因)。
|
||
改用截断回比较:uint32(value) 回绕后还原,与 value 不等即超 u32 范围。
|
||
|
||
负数侧漏判的现场:`(0xA5A5A5A5 << 32) | 0xA5A5A5A5`(includes/memhub.vp:123)
|
||
经 CDefine 求值(ExprGenCdef 内 lval << rval 的 int64 回绕)得
|
||
-6510615555426900571,再经 TypeGenCDef.register_cdefine_constant 的
|
||
"%lld" 文本存回(TypeGenCDef.vp:156)仍为负数 → 原判据物化成 i32
|
||
-1515870811:写入路径 inttoptr i32(x86 movl 零扩展)实存
|
||
0x00000000A5A5A5A5,比较/打印路径 sext i32→i64 期望 0xFFFFFFFFA5A5A5A5
|
||
⇒ MemBuddy canary 校验 rc=6 假阳性。下限判据与既有
|
||
ExprGenConst.vp:108 / CallGenLeafKit.vp:1477 的写法一致。
|
||
"""
|
||
if (value >= 0 and uint64(uint32(value)) != value) or value < -2147483648:
|
||
return llvmlite.const_int64(pool, value)
|
||
return llvmlite.const_int32(pool, int(value))
|
||
|
||
# ============================================================
|
||
# cdefine_use_width_value - "使用处宽度"下的 CDefine 常量物化
|
||
# (与 TPC HandlesExpr._HandleNameLlvm + ConstWidthHint 同口径)
|
||
# ============================================================
|
||
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
|
||
"""把 CDefine 值归一到 bits 位的**有符号表示**(LLVM 整数字面量口径)。
|
||
|
||
前提:调用方已用 W_min 判据保证 value 落在
|
||
[-(2^(bits-1)), 2^bits-1](两解释口径)。非负但最高位为 1 的值
|
||
(如 0xFFFFFFFF)转成同一位模式的负数(i32 4294967295 → i32 -1),
|
||
与 _const_int_for_cdefine 既有的 i32 截断口径一致,避免生成非法 IR 文本。
|
||
"""
|
||
if bits == 8:
|
||
if value > 127:
|
||
return value - 256
|
||
return value
|
||
if bits == 16:
|
||
if value > 32767:
|
||
return value - 65536
|
||
return value
|
||
if bits == 32:
|
||
if value > 2147483647:
|
||
return value - 4294967296
|
||
return value
|
||
return value
|
||
|
||
def cdefine_use_width_value(self, pool: memhub.MemBuddy*,
|
||
node: ast.AST*, name: str,
|
||
value: int64,
|
||
target_ty: llvmlite.LLVMType*,
|
||
use_ctx: str = None) -> llvmlite.Value*:
|
||
"""CDefine 整数常量在**已知使用处类型**时的物化(与 TPC 同口径)。
|
||
|
||
返回 None ⇒ "取不到使用处宽度"(调用方沿用 _const_int_for_cdefine 的
|
||
语言默认整型宽度 32/64)。只当 target_ty 是 i8/i16/i32/i64 时启用
|
||
(i1 是比较/逻辑结果、指针不是整数使用处 ⇒ 落回默认,否则 `C == 0`
|
||
之类会拿到位宽 1 而误报"使用处宽度不足"):
|
||
使用处更宽(bits >= W_min)⇒ 按 bits 就地物化(立即数升宽无损);
|
||
使用处更窄(bits < W_min)⇒ fail-fast(替换静默截断)。
|
||
只被 CDefine 整数物化点调用 ⇒ 不改变非常量实参/返回值的既有 IR。
|
||
|
||
use_ctx:**真实站点种类**标签(如 "局部变量注解" / "模块级全局" / "形参" /
|
||
"返回注解"),由 `translate_value` 的调用方按站点下传;None ⇒ 报文不写
|
||
括注(**不得**再写死"形参 / 返回类型"——AnnAssign 的 `a: T = C` 现场也被
|
||
本函数拦到时,硬编码标签会给出错误站点)。
|
||
"""
|
||
bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(target_ty)
|
||
if bits != 8 and bits != 16 and bits != 32 and bits != 64:
|
||
return None
|
||
wmin: int = self.Gen.AssignGen.define_min_width(value)
|
||
if bits < wmin:
|
||
# "该使用处可表示的范围"与判据同一口径 = TPC `DefineWidthLoHi`
|
||
# = [-(2^(bits-1)), 2^bits - 1](有符号/无符号两种解释的并集;
|
||
# 判据本身只按**位宽**比较,故报文必须报"位宽 w 的可接受值域")。
|
||
# 字面量纪律见 AssignGenMixin.define_min_width:2147483648 /
|
||
# 4294967295 会被 TPC 压成 i32 并在提升时 sext,故用安全字面量
|
||
# 2147483647 的算术等价式构造。
|
||
m31: int64 = 2147483647
|
||
lo: int64 = -m31 - 1
|
||
hi: int64 = m31 * 2 + 1
|
||
if bits == 8:
|
||
lo = -128
|
||
hi = 255
|
||
elif bits == 16:
|
||
lo = -32768
|
||
hi = 65535
|
||
ctx: str = ""
|
||
if use_ctx is not None:
|
||
# 本语言的 str 是 i8*,**没有** `str + str` 的拼接语义(`+` 会退化
|
||
# 成裸指针 add → "invalid operand type for instruction")。
|
||
# 构建字符串一律用本仓库惯用法:f-string 插值 / string.strcat /
|
||
# viperlib.snprintf。
|
||
ctx = f"({use_ctx})"
|
||
self.Gen.TypeGenErr.fatal_error(
|
||
node,
|
||
f"[TPV] define 常量使用处宽度不足: 常量 '{name}' 的实际值 {value} "
|
||
f"需要至少 {wmin} 位(最小可容纳宽度 W_min),"
|
||
f"但使用处{ctx}只提供 {bits} 位"
|
||
f"(该位宽按有符号/无符号两种解释可接受的范围: {lo}..{hi})。"
|
||
f"禁止静默截断/降级,已终止编译。"
|
||
f"修法: 把使用处{ctx}的类型加宽到至少 {wmin} 位"
|
||
f"(如 t.CInt{wmin}T / CUInt{wmin}T 及更宽类型),或改用更小的常量值。",
|
||
"TypeError")
|
||
return None
|
||
return llvmlite.const_int(pool, bits, self._cdefine_bits_value(value, bits)) |