- StubMergerStore 新增共享导入上下文记账(AppendImportedModule / AppendFromImport / ImportCtx / AllocImportCtx / NotePlainImport),ImportsGenMixin 与 VpiReader 共用同一份 口径;顺带移除 add_from_import 的 256 缓冲静默截断,VpiReader 删除两份私有副本 - 注入窗口按「名字归属」判据解析(is_module_class + CurDefModuleSha1): _forward_declare_ptr_from_node 的 Name 分支、resolve_annotation_type 的 Name/Attribute 分支均补齐,使同一 stub 内先声明/先引用的类产出**具名占位**而不是 Ptr(i8),并新增 _is_tattr_marker 让 t.attr.* 属性标记不参与类型联合竞争 - VpiReader._inject_class_struct_declare:命中前向声明占位时不再早退,改为「占位 Ty 原地写入」(对齐 ClassGenDef.fill_fwd),修 StructTable 爆表(table full, cannot register Translator) - 清除全部一次性探针:[DBG-GEN](VpiReader / StructGenMixin / ClassGenDef)、 [DBG-ATTR](ExprGenAttr)、[MODSHA1](CallGenMixin) 根因记录:vpi 注入期漏记 `import X as Y -> from_imports "Y:X"`,使 GenTypes.VarEntry.Spec (TSpec.TypeSpec | t.CPtr)取不到模块 SHA1、退化为 i8* 并被永久冻结 (CallGenCtor.vp:897 .PtrLevel 属性访问失败)。
1005 lines
52 KiB
Plaintext
1005 lines
52 KiB
Plaintext
import lib.core.Gen.Generator as Generator
|
||
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import memhub
|
||
import hashtable
|
||
import string
|
||
import stdlib
|
||
import viperlib
|
||
import stdio
|
||
import sys
|
||
import w32.fileio as fileio
|
||
import lib.Stub.StubMerger as StubMerger
|
||
import lib.core.CompilerFactory as CompilerFactory
|
||
import lib.core.Gen.Translator as HT
|
||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||
import lib.core.VLogger as VLogger
|
||
|
||
|
||
# ============================================================
|
||
# ImportsGenMixin - 导入处理 Mixin
|
||
#
|
||
# 职责:模块导入表(imported_modules/from_imports)维护、re-export 登记、
|
||
# 相对导入解析与模块 SHA1 校验。@t.NoVTable:静态绑定(MRO 直接调用)。
|
||
# ============================================================
|
||
|
||
|
||
# ============================================================
|
||
# 常量(与 HandlesImports 保持一致)
|
||
# ============================================================
|
||
MAX_REEXPORT: t.CDefine = 512
|
||
|
||
|
||
# ============================================================
|
||
# ImportsGenMixin - 导入处理
|
||
# ============================================================
|
||
novirtual class ImportsGenMixin:
|
||
"""导入处理 Mixin(P6 方法化)"""
|
||
|
||
# --- 全局 re-export 映射表(原 _g_reexport_sha1s/_g_reexport_funcs/
|
||
# _g_reexport_srcs/_g_reexport_count) ---
|
||
Gen: Generator.Generator*
|
||
ReexportSha1s: bytes
|
||
ReexportFuncs: bytes
|
||
ReexportSrcs: bytes
|
||
ReexportCount: int
|
||
|
||
# --- 导入查找索引(把 O(串长) 的逐字符扫描降为 O(名字长) 哈希查找) ---
|
||
# imported_modules/_from_imports 是空格拼接的扁平串("a b c" / "n:m[:orig]"),
|
||
# 长度可达数千字符,而每个 Name 节点都会多次查询。索引与「当前串指针」绑定:
|
||
# 调用方查询的总是 add_* 刚生成的当前串,故命中索引快路径;其余来源(stub 期
|
||
# stdlib malloc 缓冲等)指针不匹配 → 回退逐字符扫描,语义完全不变。
|
||
# 索引表用 PermPool 分配(跨文件/Phase 存活),由对应 add_* 增量维护;key/value
|
||
# 均为指向该扁平串内部的指针,串在文件级 reset 前始终有效。
|
||
CurImportedModulesPtr: str
|
||
CurImportedModulesNames: hashtable.HashTable*
|
||
CurFromImportsPtr: str
|
||
CurFromImportsMods: hashtable.HashTable*
|
||
CurFromImportsOrigs: hashtable.HashTable*
|
||
|
||
# 注意:不声明 CurrentFile 字段——TypeGenMixin 已声明(Generator 展平块
|
||
# 只有一份,跨父类不去重规则下重复声明会导致字段错位)。本 Mixin 的
|
||
# set_current_file 与 CurrentFile 读取统一经 HandlesType 委托(延迟 import
|
||
# 规避 HandlesType↔HandlesImports 循环依赖)。
|
||
|
||
# _index_imported_module - 增量维护 imported_modules 查找索引
|
||
# old_ptr 为追加前的串指针(None = 首个模块),new_ptr 为追加后的新串,
|
||
# name_ptr 指向新串内的新模块名(key 直接引用串内部,无需复制)。
|
||
# 仅当 old_ptr 恰为当前索引绑定的串时复用其索引;否则放弃索引绑定并回退
|
||
# 逐字符扫描(语义不变)。
|
||
def _index_imported_module(self, old_ptr: str, new_ptr: str,
|
||
name_ptr: str) -> None:
|
||
"""增量更新 imported_modules 索引(复用旧索引或新建,失败则解除绑定)"""
|
||
if new_ptr is None or name_ptr is None:
|
||
return
|
||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||
if perm is None:
|
||
return
|
||
idx: hashtable.HashTable* = None
|
||
if old_ptr is None:
|
||
# 新文件/新链首:建全新索引(旧索引随旧串一起废弃)
|
||
idx = hashtable.HashTable(perm)
|
||
elif old_ptr == self.CurImportedModulesPtr and self.CurImportedModulesNames is not None:
|
||
# 正常路径:复用当前串的索引并追加(不重建,内存 O(导入数))
|
||
idx = self.CurImportedModulesNames
|
||
else:
|
||
# 旧串非当前索引串(异常来源):解除绑定,回退逐字符扫描
|
||
self.CurImportedModulesPtr = None
|
||
self.CurImportedModulesNames = None
|
||
return
|
||
if idx is None:
|
||
self.CurImportedModulesPtr = None
|
||
self.CurImportedModulesNames = None
|
||
return
|
||
# 同名已在(重复导入)则跳过;否则写入(key/value 均指向 new_ptr 内部)
|
||
if idx.__contains__(name_ptr) == 0:
|
||
idx.set_str(name_ptr, name_ptr)
|
||
self.CurImportedModulesPtr = new_ptr
|
||
self.CurImportedModulesNames = idx
|
||
|
||
# _index_from_import - 增量维护 from_imports 查找索引
|
||
# entry_off 为新增 entry("local:module[:orig]")在新串内相对首字节的偏移。
|
||
# 模块表 key=local_name → 指向模块名切片;orig 表 key=local_name → 指向原始名
|
||
# 切片(无别名时写入 None,以"名存在但无原始名"区别于未命中)。
|
||
# 语义对齐逐字符扫描:精确名首次命中为准(不覆盖),star 模块名以最后一条为准。
|
||
def _index_from_import(self, old_ptr: str, new_ptr: str, entry_off: sizet,
|
||
local_name: str, module_name: str,
|
||
original_name: str) -> None:
|
||
"""增量更新 from_imports 索引(复用旧索引或新建,失败则解除绑定)"""
|
||
if new_ptr is None or local_name is None or module_name is None:
|
||
return
|
||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||
if perm is None:
|
||
return
|
||
idx_m: hashtable.HashTable* = None
|
||
idx_o: hashtable.HashTable* = None
|
||
if old_ptr is None:
|
||
idx_m = hashtable.HashTable(perm)
|
||
idx_o = hashtable.HashTable(perm)
|
||
elif old_ptr == self.CurFromImportsPtr \
|
||
and self.CurFromImportsMods is not None \
|
||
and self.CurFromImportsOrigs is not None:
|
||
idx_m = self.CurFromImportsMods
|
||
idx_o = self.CurFromImportsOrigs
|
||
else:
|
||
self.CurFromImportsPtr = None
|
||
self.CurFromImportsMods = None
|
||
self.CurFromImportsOrigs = None
|
||
return
|
||
if idx_m is None or idx_o is None:
|
||
self.CurFromImportsPtr = None
|
||
self.CurFromImportsMods = None
|
||
self.CurFromImportsOrigs = None
|
||
return
|
||
local_len: sizet = string.strlen(local_name)
|
||
mod_len: sizet = string.strlen(module_name)
|
||
# 哈希键必须是 NUL 结尾串:扁平串内 local 段以 ':' 结尾(非 NUL),直接当键会让
|
||
# FNV 哈希到 "local:module..." 整段,与查询名 "local" 永不相等 → 索引恒 miss。
|
||
# 故用 PermPool 分配 NUL 结尾的稳定副本作键(与索引表同寿命),值仍指向扁平串
|
||
# 内模块名切片。
|
||
key_ptr: str = perm.alloc(local_len + 1)
|
||
if key_ptr is None:
|
||
self.CurFromImportsPtr = None
|
||
self.CurFromImportsMods = None
|
||
self.CurFromImportsOrigs = None
|
||
return
|
||
string.strncpy(key_ptr, local_name, local_len)
|
||
key_ptr[local_len] = '\0'
|
||
mod_ptr: str = new_ptr + entry_off + local_len + 1
|
||
if local_len == 1 and local_name[0] == '*':
|
||
# star import:模块名以最后一条为准(覆盖旧值,对齐 star_mod 语义)
|
||
idx_m.set_str(key_ptr, mod_ptr)
|
||
else:
|
||
if idx_m.__contains__(key_ptr) == 0:
|
||
idx_m.set_str(key_ptr, mod_ptr)
|
||
if idx_o.__contains__(key_ptr) == 0:
|
||
if original_name is not None and string.strcmp(original_name, local_name) != 0 \
|
||
and string.strlen(original_name) > 0:
|
||
idx_o.set_str(key_ptr, mod_ptr + mod_len + 1)
|
||
else:
|
||
# 首次出现即无别名(或别名为空)→ 该名查询返回 None(与逐字符扫描一致)
|
||
idx_o.set_str(key_ptr, None)
|
||
self.CurFromImportsPtr = new_ptr
|
||
self.CurFromImportsMods = idx_m
|
||
self.CurFromImportsOrigs = idx_o
|
||
|
||
# ============================================================
|
||
# 添加已导入模块名
|
||
# ============================================================
|
||
def add_imported_module(self, pool: memhub.MemBuddy*,
|
||
imported_modules: str,
|
||
name: str) -> str:
|
||
"""记录已导入的模块名,返回新的 imported_modules 字符串指针
|
||
|
||
字符串记账走 StubMergerStore.AppendImportedModule(单一真源,与 vpi
|
||
注入期同源);本方法只额外维护查询索引。
|
||
"""
|
||
if name is None:
|
||
return imported_modules
|
||
old_len: sizet = 0
|
||
if imported_modules is not None:
|
||
old_len = string.strlen(imported_modules)
|
||
buf: str = StubMergerStore.AppendImportedModule(pool, imported_modules, name)
|
||
if buf is None:
|
||
return imported_modules
|
||
if imported_modules is None:
|
||
# 索引:首个模块名(key 指向 buf 内部)
|
||
self._index_imported_module(None, buf, buf)
|
||
else:
|
||
# 索引:复用旧串索引并追加新名字(key 指向 buf 内的 name 段)
|
||
self._index_imported_module(imported_modules, buf, buf + old_len + 1)
|
||
return buf
|
||
|
||
# ============================================================
|
||
# 检查模块是否已导入
|
||
# ============================================================
|
||
def is_module_imported(self, imported_modules: str, name: str) -> int:
|
||
"""检查模块是否已导入(词边界精确匹配,避免子串误匹配)"""
|
||
if name is None or imported_modules is None:
|
||
return 0
|
||
# [索引快路径] 传入串即当前索引绑定的串(trans 字段的常态)→ O(名字长) 哈希查;
|
||
# 指针不匹配(stub 期 malloc 缓冲等)→ 回退下方逐字符扫描,语义不变。
|
||
if imported_modules == self.CurImportedModulesPtr \
|
||
and self.CurImportedModulesNames is not None:
|
||
if self.CurImportedModulesNames.__contains__(name) != 0:
|
||
return 1
|
||
# 索引未命中:绝不据此返回 0(索引不完整会误判"未导入"),
|
||
# 落到下方原逐字符扫描按真实串内容判定(正确性优先 | 性能次之)。
|
||
name_len: sizet = string.strlen(name)
|
||
cur: str = imported_modules
|
||
total_len: sizet = string.strlen(imported_modules)
|
||
ci: sizet = 0
|
||
while ci < total_len:
|
||
while ci < total_len and cur[ci] == ' ':
|
||
ci += 1
|
||
if ci >= total_len:
|
||
break
|
||
word_start: sizet = ci
|
||
while ci < total_len and cur[ci] != ' ':
|
||
ci += 1
|
||
word_len: sizet = ci - word_start
|
||
if word_len == name_len:
|
||
match: int = 1
|
||
for ei in range(name_len):
|
||
if cur[word_start + ei] != name[ei]:
|
||
match = 0
|
||
break
|
||
if match == 1:
|
||
return 1
|
||
return 0
|
||
|
||
# ============================================================
|
||
# 检查模块是否已导入或全局已登记(包子模块限定解析用)
|
||
# ============================================================
|
||
def is_module_imported_or_known(self, imported_modules: str, name: str) -> int:
|
||
"""已导入或可访问的模块(包子模块限定解析用)。
|
||
|
||
包子模块场景:`import intr` 只注册包名 "intr",但 intr.gdt / intr.idt
|
||
等子模块由包 __init__.vp 的 from . import X 在全局注册——模块限定
|
||
调用/属性访问(intr.gdt.init())的 is_module_imported 校验必须放行,
|
||
否则解析退化到函数名后缀匹配劫持其他模块同名函数
|
||
(serial.init 劫持 intr.gdt.init → GDT/IDT 未初始化根因)。
|
||
|
||
放行条件(保持单层名 fast-fail 语义不变):
|
||
1. 名字已在 _imported_modules(直接 import);
|
||
2. 名字在 Sha1Store 全局已登记,且其任一祖先包名已导入
|
||
(import intr → intr.gdt 可访问;`this.this()` 但 import this
|
||
被注释 → this 未导入 → 仍 fast-fail 报"模块未导入")。
|
||
"""
|
||
if self.is_module_imported(imported_modules, name) != 0:
|
||
return 1
|
||
if name is None:
|
||
return 0
|
||
if StubMergerStore.LookupSha1ByModName(name) is None:
|
||
return 0
|
||
nl: sizet = string.strlen(name)
|
||
dot_pos: sizet = 0
|
||
while dot_pos < nl:
|
||
if name[dot_pos] == '.':
|
||
pfx: str = stdlib.malloc(dot_pos + 1)
|
||
if pfx is not None:
|
||
string.strncpy(pfx, name, dot_pos)
|
||
pfx[dot_pos] = '\0'
|
||
hit: int = self.is_module_imported(imported_modules, pfx)
|
||
stdlib.free(pfx)
|
||
if hit != 0:
|
||
return 1
|
||
dot_pos += 1
|
||
return 0
|
||
|
||
# ============================================================
|
||
# 添加 from-import 名称映射
|
||
# ============================================================
|
||
def add_from_import(self, pool: memhub.MemBuddy*,
|
||
from_imports: str,
|
||
local_name: str,
|
||
module_name: str,
|
||
original_name: str = None) -> str:
|
||
"""添加 from-import 映射,返回新的 from_imports 字符串
|
||
|
||
格式:
|
||
无别名: "local_name:module_name"
|
||
有别名: "local_name:module_name:original_name"
|
||
(original_name 为源模块中的真实函数名,用于跨模块 SHA1 修饰)
|
||
|
||
字符串记账走 StubMergerStore.AppendFromImport(单一真源,与 vpi 注入期
|
||
同源;按精确长度分配,无旧的 256 缓冲静默截断);本方法只额外维护索引。
|
||
"""
|
||
if local_name is None or module_name is None:
|
||
return from_imports
|
||
old_len: sizet = 0
|
||
if from_imports is not None:
|
||
old_len = string.strlen(from_imports)
|
||
buf: str = StubMergerStore.AppendFromImport(
|
||
pool, from_imports, local_name, module_name, original_name)
|
||
if buf is None:
|
||
return from_imports
|
||
# 索引:首条 entry_off=0;追加时 entry 从 buf[old_len+1] 开始
|
||
entry_off: sizet = 0
|
||
if from_imports is not None:
|
||
entry_off = old_len + 1
|
||
self._index_from_import(from_imports, buf, entry_off,
|
||
local_name, module_name, original_name)
|
||
return buf
|
||
|
||
# ============================================================
|
||
# 查找 from-import 名称 → 返回原始函数名(别名场景)
|
||
#
|
||
# 对于 from X import Y as Z,返回 Y(源模块中的真实函数名)。
|
||
# 无别名时返回 None(local_name 即为原始名)。
|
||
# ============================================================
|
||
def lookup_from_import_original(self, from_imports: str,
|
||
local_name: str) -> str:
|
||
"""查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到"""
|
||
if local_name is None or from_imports is None:
|
||
return None
|
||
# [索引快路径] 传入串即当前索引绑定的串 → O(名字长) 哈希查;否则回退扫描。
|
||
# orig 表:名存在但存 None = 首次出现即无别名 → 返回 None(与扫描一致)。
|
||
if from_imports == self.CurFromImportsPtr and self.CurFromImportsOrigs is not None:
|
||
if self.CurFromImportsOrigs.__contains__(local_name) != 0:
|
||
v_o: t.CPtr = self.CurFromImportsOrigs.__getitem__(local_name)
|
||
if v_o is not None:
|
||
return rawptr(v_o)
|
||
# 索引未命中 | 值为 None(首个即无别名):绝不据此直接返回 None——
|
||
# 真实串可能仍含该名,落到下方原逐字符扫描按串内容判定(正确性优先)。
|
||
name_len: sizet = string.strlen(local_name)
|
||
cur: str = from_imports
|
||
ci: sizet = 0
|
||
total_len: sizet = string.strlen(from_imports)
|
||
while ci < total_len:
|
||
# 跳过前导空格
|
||
while ci < total_len and cur[ci] == ' ':
|
||
ci += 1
|
||
if ci >= total_len:
|
||
break
|
||
# 找到第一个 ':' 的位置
|
||
colon1: sizet = ci
|
||
while colon1 < total_len and cur[colon1] != ':' and cur[colon1] != ' ':
|
||
colon1 += 1
|
||
if colon1 >= total_len or cur[colon1] != ':':
|
||
break
|
||
entry_name_len: sizet = colon1 - ci
|
||
# 跳过 star import
|
||
if entry_name_len == 1 and cur[ci] == '*':
|
||
ci = colon1
|
||
while ci < total_len and cur[ci] != ' ':
|
||
ci += 1
|
||
continue
|
||
# 比较名称
|
||
if entry_name_len == name_len:
|
||
match: int = 1
|
||
ei: sizet = 0
|
||
while ei < name_len:
|
||
if cur[ci + ei] != local_name[ei]:
|
||
match = 0
|
||
break
|
||
ei += 1
|
||
if match == 1:
|
||
# 找到匹配,检查是否有第三个字段(原始名)
|
||
pos: sizet = colon1 + 1
|
||
# 跳过模块名
|
||
while pos < total_len and cur[pos] != ':' and cur[pos] != ' ' and cur[pos] != '\0':
|
||
pos += 1
|
||
if pos < total_len and cur[pos] == ':':
|
||
# 有第三个字段: original_name
|
||
orig_start: sizet = pos + 1
|
||
orig_end: sizet = orig_start
|
||
while orig_end < total_len and cur[orig_end] != ' ' and cur[orig_end] != '\0':
|
||
orig_end += 1
|
||
orig_len: sizet = orig_end - orig_start
|
||
if orig_len > 0:
|
||
orig_buf: str = cur + orig_start
|
||
# 返回指向内部的指针(调用方需在使用期内保持 from_imports 有效)
|
||
return orig_buf
|
||
return None
|
||
return None
|
||
# 跳到下一个条目
|
||
ci = colon1
|
||
while ci < total_len and cur[ci] != ' ':
|
||
ci += 1
|
||
return None
|
||
|
||
# ============================================================
|
||
# 查找 from-import 名称 → 返回模块名或 None
|
||
#
|
||
# allow_star_fallback: 是否允许 star import 回退(默认 1=允许)。
|
||
# 当查询明确模块名(如 "BuildPipeline")时,应传 0 禁用回退,
|
||
# 避免被 star import 模块名误导(如 from stdint import * 后
|
||
# 查询 "BuildPipeline" 错误回退到 "stdint")。
|
||
# ============================================================
|
||
def lookup_from_import(self, from_imports: str, name: str,
|
||
allow_star_fallback: int = 1) -> str:
|
||
"""查找 from-import 名称,返回模块名或 None
|
||
|
||
支持 star import: 如果 from_imports 中有 "*:module" 条目,
|
||
且未找到精确名称匹配,且 allow_star_fallback != 0,则返回 star import 的模块名。
|
||
"""
|
||
if name is None or from_imports is None:
|
||
return None
|
||
# [索引快路径] 传入串即当前索引绑定的串 → O(名字长) 哈希查;否则回退扫描。
|
||
# name 恰为 "*" 时与扫描一致:不做精确匹配,直接走 star 回退。
|
||
if from_imports == self.CurFromImportsPtr and self.CurFromImportsMods is not None:
|
||
name_is_star: int = 0
|
||
if string.strlen(name) == 1 and name[0] == '*':
|
||
name_is_star = 1
|
||
if name_is_star == 0:
|
||
v_f: t.CPtr = self.CurFromImportsMods.__getitem__(name)
|
||
if v_f is not None:
|
||
return rawptr(v_f)
|
||
if allow_star_fallback != 0:
|
||
sv_f: t.CPtr = self.CurFromImportsMods.__getitem__("*")
|
||
if sv_f is not None:
|
||
return rawptr(sv_f)
|
||
# 索引精确名 | star 均未命中:绝不据此直接返回 None(索引不完整会漏报
|
||
# "名已 import",_op_head NameError 即此类),落到下方原逐字符扫描按串内容判定。
|
||
name_len: sizet = string.strlen(name)
|
||
cur: str = from_imports
|
||
ci: sizet = 0
|
||
total_len: sizet = string.strlen(from_imports)
|
||
star_mod: str = None
|
||
while ci < total_len:
|
||
# 跳过前导空格
|
||
while ci < total_len and cur[ci] == ' ':
|
||
ci += 1
|
||
if ci >= total_len:
|
||
break
|
||
# 找到 ':' 的位置
|
||
colon_pos: sizet = ci
|
||
while colon_pos < total_len and cur[colon_pos] != ':' and cur[colon_pos] != ' ':
|
||
colon_pos += 1
|
||
if colon_pos >= total_len or cur[colon_pos] != ':':
|
||
break
|
||
entry_name_len: sizet = colon_pos - ci
|
||
# 检测 star import ("*:module")
|
||
if entry_name_len == 1 and cur[ci] == '*':
|
||
mod_start: sizet = colon_pos + 1
|
||
mod_end: sizet = mod_start
|
||
while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0' and cur[mod_end] != ':':
|
||
mod_end += 1
|
||
star_mod = cur + mod_start
|
||
# 比较名称
|
||
elif entry_name_len == name_len:
|
||
match: int = 1
|
||
ei: sizet = 0
|
||
while ei < name_len:
|
||
if cur[ci + ei] != name[ei]:
|
||
match = 0
|
||
break
|
||
ei += 1
|
||
if match == 1:
|
||
mod_start2: sizet = colon_pos + 1
|
||
mod_end2: sizet = mod_start2
|
||
# 模块名结束于: 空格、null、或第二个':'(别名格式的分隔符)
|
||
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':':
|
||
mod_end2 += 1
|
||
return cur + mod_start2
|
||
# 跳到下一个条目
|
||
ci = colon_pos
|
||
while ci < total_len and cur[ci] != ' ':
|
||
ci += 1
|
||
# 未找到精确匹配,仅在允许时回退到 star import
|
||
if allow_star_fallback == 0:
|
||
return None
|
||
return star_mod
|
||
|
||
# ============================================================
|
||
# register_reexport - 注册 re-export 映射
|
||
#
|
||
# 在 HandleImportFromNames 中调用:当处理 from .X import Y 时,
|
||
# 记录当前模块(通过 ModuleSha1)re-export 了函数 Y,源模块为 X。
|
||
#
|
||
# 幂等:重复注册相同映射不会增加条目。
|
||
# 内存用 stdlib.malloc 分配(全局存储器,跨 Phase 持久化)。
|
||
# ============================================================
|
||
def register_reexport(self, mod_sha1: str, func_name: str, source_module: str) -> int:
|
||
"""注册 re-export 映射(幂等),返回 0 成功,-1 失败"""
|
||
if mod_sha1 is None or func_name is None or source_module is None:
|
||
return -1
|
||
# 懒初始化
|
||
if self.ReexportSha1s is None:
|
||
self.ReexportSha1s = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 17)
|
||
self.ReexportFuncs = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 64)
|
||
self.ReexportSrcs = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 64)
|
||
if self.ReexportSha1s is None or self.ReexportFuncs is None or self.ReexportSrcs is None:
|
||
return -1
|
||
string.memset(self.ReexportSha1s, 0, MAX_REEXPORT * 17)
|
||
string.memset(self.ReexportFuncs, 0, MAX_REEXPORT * 64)
|
||
string.memset(self.ReexportSrcs, 0, MAX_REEXPORT * 64)
|
||
if self.ReexportCount >= MAX_REEXPORT:
|
||
return -1
|
||
# 幂等检查:查找是否已存在相同映射
|
||
for i in range(self.ReexportCount):
|
||
sidx: sizet = sizet(i) * 17
|
||
fidx: sizet = sizet(i) * 64
|
||
if string.strcmp(self.ReexportSha1s + sidx, mod_sha1) == 0:
|
||
if string.strcmp(self.ReexportFuncs + fidx, func_name) == 0:
|
||
# 已存在,更新 source_module(以防变化;64 字节槽,strncpy 钳制)
|
||
string.strncpy(self.ReexportSrcs + fidx, source_module, 63)
|
||
self.ReexportSrcs[fidx + 63] = '\0'
|
||
return 0
|
||
# 添加新条目(槽位定长 17/64 字节,strcpy 无界会越界写相邻槽,全部 strncpy 钳制)
|
||
idx2: sizet = sizet(self.ReexportCount) * 17
|
||
fidx2: sizet = sizet(self.ReexportCount) * 64
|
||
string.strncpy(self.ReexportSha1s + idx2, mod_sha1, 16)
|
||
self.ReexportSha1s[idx2 + 16] = '\0'
|
||
string.strncpy(self.ReexportFuncs + fidx2, func_name, 63)
|
||
self.ReexportFuncs[fidx2 + 63] = '\0'
|
||
string.strncpy(self.ReexportSrcs + fidx2, source_module, 63)
|
||
self.ReexportSrcs[fidx2 + 63] = '\0'
|
||
self.ReexportCount += 1
|
||
return 0
|
||
|
||
# ============================================================
|
||
# lookup_reexport - 查找 re-export 映射
|
||
#
|
||
# 给定模块 SHA1 和函数名,返回源模块名(如 "ast.lexer")或 None。
|
||
# 调用方通过 _lookup_module_sha1(source_module) 获取源模块的 SHA1。
|
||
# ============================================================
|
||
def lookup_reexport(self, mod_sha1: str, func_name: str) -> str:
|
||
"""查找 re-export 映射,返回源模块名或 None"""
|
||
if mod_sha1 is None or func_name is None:
|
||
return None
|
||
if self.ReexportSha1s is None or self.ReexportCount <= 0:
|
||
return None
|
||
for i in range(self.ReexportCount):
|
||
sidx: sizet = sizet(i) * 17
|
||
fidx: sizet = sizet(i) * 64
|
||
if string.strcmp(self.ReexportSha1s + sidx, mod_sha1) == 0:
|
||
if string.strcmp(self.ReexportFuncs + fidx, func_name) == 0:
|
||
return self.ReexportSrcs + fidx
|
||
return None
|
||
|
||
# ============================================================
|
||
# _resolve_relative_module - 解析相对导入为完整模块名
|
||
#
|
||
# 对于 from .X import Y(level=1, module="X")在包 pkg 中:
|
||
# 解析为 "pkg.X"
|
||
# 对于 from . import Y(level=1, module=None)在包 pkg 中:
|
||
# 解析为 "pkg"
|
||
# 对于 from ..X import Y(level=2, module="X")在包 pkg.sub 中:
|
||
# 解析为 "pkg.X"(先从 pkg.sub 上溯一级到 pkg,再追加 .X)
|
||
#
|
||
# Args:
|
||
# pool: 内存池
|
||
# current_package: 当前文件所属包名(如 "llvmlite"),None 表示无包
|
||
# level: 相对导入级别(0=绝对,1=., 2=..)
|
||
# module: ImportFrom 的 module 字段(可能为 None)
|
||
#
|
||
# Returns:
|
||
# 解析后的完整模块名;绝对导入(level<=0)直接返回 module;
|
||
# 无法解析时返回 module(回退到原始值)
|
||
# ============================================================
|
||
def _resolve_relative_module(self, pool: memhub.MemBuddy*,
|
||
current_package: str,
|
||
level: int,
|
||
module: str) -> str:
|
||
"""解析相对导入为完整模块名"""
|
||
if level <= 0:
|
||
return module
|
||
if current_package is None:
|
||
# 无包上下文(includes 顶级文件 / App 顶层文件):
|
||
# `from .X import Y` 的语义 = "同目录/根目录的 X 模块"
|
||
# (如 includes/viperstring.vp 的 `from .string import *`)。
|
||
# 直接返回 module 是正确解析,不是降级——禁止 fail-fast。
|
||
return module
|
||
|
||
# 从 current_package 开始,上溯 (level-1) 级
|
||
pkg: str = current_package
|
||
up: int = level - 1
|
||
while up > 0:
|
||
pkg_len: sizet = string.strlen(pkg)
|
||
last_dot: sizet = 0
|
||
found: int = 0
|
||
i: sizet = 0
|
||
while i < pkg_len:
|
||
if pkg[i] == '.':
|
||
last_dot = i
|
||
found = 1
|
||
i += 1
|
||
if found == 0:
|
||
# 无更多父级(相对导入级别超过包深度,上溯到根目录):
|
||
# 回退到 module 原始值(根目录模块语义),后续查找失败
|
||
# 由 import SHA1 检查明确报"模块未找到",此处不误杀。
|
||
pkg = None
|
||
break
|
||
# 截断到最后一个 '.' 处
|
||
new_pkg: str = pool.alloc(last_dot + 1)
|
||
if new_pkg is None:
|
||
self.Gen.TypeGenErr.fatal_error(None, f"相对导入解析失败:level={level} module='{module}'", "ImportError")
|
||
return module
|
||
string.strncpy(new_pkg, pkg, last_dot)
|
||
new_pkg[last_dot] = '\0'
|
||
pkg = new_pkg
|
||
up -= 1
|
||
|
||
if module is None:
|
||
# from . import Y → 模块就是包本身
|
||
return pkg
|
||
if pkg is None:
|
||
# 包已上溯到空(相对导入越界):回退到 module 原始值
|
||
return module
|
||
# 拼接 pkg + "." + module
|
||
pkg_len2: sizet = string.strlen(pkg)
|
||
mod_len: sizet = string.strlen(module)
|
||
buf: str = pool.alloc(pkg_len2 + 1 + mod_len + 1)
|
||
if buf is None:
|
||
self.Gen.TypeGenErr.fatal_error(None, f"相对导入解析失败:level={level} module='{module}'", "ImportError")
|
||
return module
|
||
string.strcpy(buf, pkg)
|
||
buf[pkg_len2] = '.'
|
||
string.strcpy(buf + pkg_len2 + 1, module)
|
||
return buf
|
||
|
||
# ============================================================
|
||
# compute_package_from_relpath - 从相对路径计算包名
|
||
#
|
||
# 包名 = 文件所在目录路径,将 / 和 \ 替换为 .
|
||
# 对于顶级文件(无目录分隔符),返回 None
|
||
#
|
||
# 示例:
|
||
# "llvmlite/__init__.vp" → "llvmlite"
|
||
# "llvmlite/__types.vp" → "llvmlite"
|
||
# "ast/parser.vp" → "ast"
|
||
# "ast.vp" → None(顶级文件)
|
||
# ============================================================
|
||
def compute_package_from_relpath(self, pool: memhub.MemBuddy*,
|
||
rel_path: str) -> str:
|
||
"""从相对路径计算包名(目录部分,分隔符替换为 .)"""
|
||
if rel_path is None:
|
||
return None
|
||
rlen: sizet = string.strlen(rel_path)
|
||
# 找最后一个 / 或 \
|
||
last_sep: sizet = 0
|
||
found: int = 0
|
||
i: sizet = 0
|
||
while i < rlen:
|
||
ch: char = rel_path[i]
|
||
if ch == '/' or ch == '\\':
|
||
last_sep = i
|
||
found = 1
|
||
i += 1
|
||
if found == 0:
|
||
# 无目录分隔符 → 顶级文件,无包
|
||
return None
|
||
# 复制目录部分,将 / 和 \ 替换为 .
|
||
dir_len: sizet = last_sep
|
||
buf: str = pool.alloc(dir_len + 1)
|
||
if buf is None:
|
||
return None
|
||
j: sizet = 0
|
||
while j < dir_len:
|
||
ch2: char = rel_path[j]
|
||
if ch2 == '/' or ch2 == '\\':
|
||
buf[j] = '.'
|
||
else:
|
||
buf[j] = ch2
|
||
j += 1
|
||
buf[dir_len] = '\0'
|
||
return buf
|
||
|
||
# ============================================================
|
||
# set_current_file - 设置当前编译的文件名
|
||
#
|
||
# 与 TypeGenMixin.set_current_file 行为一致(写 CurrentFile 字段),
|
||
# BuildPipeline/Phase1 通过 HandlesImports.set_current_file 委托调用。
|
||
# 经 HandlesType 委托(延迟 import 规避循环依赖),字段由 TypeGenMixin 持有。
|
||
# ============================================================
|
||
def set_current_file(self, file_path: str) -> None:
|
||
"""设置当前正在编译的文件名(与 HandlesType.set_current_file 同步调用)"""
|
||
# 文件级失效:索引绑定的是上一文件 arena 内的串指针,本文件 reset 后该
|
||
# 地址可能被新分配复用(如 _merge_template_imports 的合并串)→ 必须解绑,
|
||
# 否则可能命中陈旧索引返回错误结果。本文件的索引由后续 add_* 重新建立。
|
||
self.CurImportedModulesPtr = None
|
||
self.CurImportedModulesNames = None
|
||
self.CurFromImportsPtr = None
|
||
self.CurFromImportsMods = None
|
||
self.CurFromImportsOrigs = None
|
||
self.Gen.TypeGenResolve.set_current_file(file_path)
|
||
|
||
# ============================================================
|
||
# _report_import_error - 本地实现 Python 风格报错(避免循环依赖)
|
||
#
|
||
# 无法访问 HandlesType.fatal_error(HandlesType import 了本模块,
|
||
# 若本模块再 import HandlesType 会形成循环依赖),这里用简化格式:
|
||
# Traceback (most recent call last):
|
||
# File "<当前文件>", line <N>
|
||
# ImportError: <msg>
|
||
# ============================================================
|
||
def _report_import_error(self, node: ast.AST*, msg: str) -> None:
|
||
"""以 Python 风格报错并退出(本地实现,避免循环依赖)"""
|
||
stdio.printf("Traceback (most recent call last):\n")
|
||
lineno: int = 0
|
||
if node is not None:
|
||
lineno = node.lineno
|
||
file_name: str = self.Gen.TypeGenResolve.get_current_file()
|
||
if file_name is None:
|
||
file_name = "<unknown>"
|
||
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||
# 显示行内内容(读源文件第 lineno 行的实际代码,便于快速定位)
|
||
if lineno > 0 and file_name is not None:
|
||
f_err: rawptr[fileio.File] = fileio.File(file_name, fileio.MODE.R)
|
||
if not f_err.closed:
|
||
line_buf_err: bytes = stdlib.malloc(512)
|
||
if line_buf_err is not None:
|
||
cur_ln: int = 0
|
||
while cur_ln < lineno:
|
||
r_err: LONG = f_err.readline(line_buf_err, 511)
|
||
if r_err <= 0:
|
||
break
|
||
cur_ln += 1
|
||
if cur_ln == lineno:
|
||
ll_err: sizet = string.strlen(line_buf_err)
|
||
while ll_err > 0 and (line_buf_err[ll_err - 1] == '\n' or line_buf_err[ll_err - 1] == '\r'):
|
||
ll_err -= 1
|
||
line_buf_err[ll_err] = '\0'
|
||
stdio.printf(" %s\n", line_buf_err)
|
||
break
|
||
stdlib.free(line_buf_err)
|
||
f_err.close()
|
||
stdio.printf("ImportError: %s\n", msg)
|
||
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印),批处理下终止
|
||
VLogger.repl_abort()
|
||
|
||
# ============================================================
|
||
# _is_local_module_file - 当前文件同目录存在 {mod_name}.vp/.py
|
||
#
|
||
# 多文件项目(如 Test/G4/App 的 caller1.vp import g4node)的兄弟模块
|
||
# 不在 includes 扫描存储器中(SHA1 在项目源文件列表),检查应豁免。
|
||
# ============================================================
|
||
def _is_local_module_file(self, mod_name: str) -> int:
|
||
"""当前文件同目录存在 {mod_name}.vp/.py → 项目内兄弟模块"""
|
||
cur: str = self.Gen.TypeGenResolve.get_current_file()
|
||
if cur is None or mod_name is None:
|
||
return 0
|
||
cur_len: sizet = string.strlen(cur)
|
||
if cur_len == 0:
|
||
return 0
|
||
sep_pos: sizet = cur_len
|
||
while sep_pos > 0:
|
||
sep_pos -= 1
|
||
ch: char = cur[sep_pos]
|
||
if ch == '/' or ch == '\\':
|
||
sep_pos += 1
|
||
break
|
||
mod_len: sizet = string.strlen(mod_name)
|
||
path_len: sizet = sep_pos + mod_len + 5
|
||
path_buf: bytes = stdlib.malloc(path_len + 1)
|
||
if path_buf is None:
|
||
return 0
|
||
# .vp 文件(Viper 源仅 .vp,不再回退 .py)
|
||
string.strncpy(path_buf, cur, sep_pos)
|
||
path_buf[sep_pos] = '\0'
|
||
string.strcat(path_buf, mod_name)
|
||
string.strcat(path_buf, ".vp")
|
||
fex: rawptr[fileio.File] = fileio.File(path_buf, fileio.MODE.R)
|
||
if not fex.closed:
|
||
fex.close()
|
||
stdlib.free(path_buf)
|
||
return 1
|
||
stdlib.free(path_buf)
|
||
return 0
|
||
|
||
# _check_import_sha1 - 导入时校验模块 SHA1 是否已注册(fast-fail)
|
||
# includes 路径配置错误时扫描结果为 0、模块不注册 SHA1,若延迟到实际跨模块调用才报错
|
||
# 会误导("跨模块调用 'bd.alloc' 找不到模块 SHA1"),故在 import 语句处立即检测。
|
||
# t/c 是内部库,不参与 includes 扫描,豁免检查。
|
||
def _check_import_sha1(self, node: ast.AST*, mod_name: str) -> int:
|
||
"""导入时校验模块 SHA1 是否已注册,未注册则立即报错退出
|
||
|
||
Args:
|
||
node: Import/ImportFrom AST 节点(提供行号)
|
||
mod_name: 导入的模块名(如 "memhub"、"w32.fileio")
|
||
|
||
Returns:
|
||
1=检查通过;模块未注册时直接报错退出(不会返回 0)
|
||
"""
|
||
if mod_name is None:
|
||
return 1
|
||
# t/c 是内部库,不参与 includes 扫描,豁免检查
|
||
# ctraits 是纯编译期宏库(.vpi 后缀,IncludesScanner 不扫描 .vpi),豁免检查
|
||
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0 or string.strcmp(mod_name, "ctraits") == 0:
|
||
return 1
|
||
mod_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
|
||
if mod_sha1 is None:
|
||
# App 兄弟模块豁免:当前文件同目录存在 {mod_name}.vp/.py
|
||
# (如 Test/G4/App/caller1.vp import g4node——同项目源文件,
|
||
# SHA1 在项目文件列表而非 includes 扫描存储器,不应报错)
|
||
if self._is_local_module_file(mod_name) != 0:
|
||
return 1
|
||
err_buf: str = stdlib.malloc(256)
|
||
if err_buf is not None:
|
||
viperlib.snprintf(err_buf, 256,
|
||
"模块 '%s' 未注册 SHA1(includes 扫描未找到该模块,请检查 project.json 的 includes 路径配置)",
|
||
mod_name)
|
||
self._report_import_error(node, err_buf)
|
||
else:
|
||
self._report_import_error(node, "导入模块 SHA1 未注册(includes 扫描未找到该模块)")
|
||
return 1
|
||
|
||
# ============================================================
|
||
# translate_import - 处理 import 语句,更新 trans._imported_modules
|
||
# (迁移自 HandlesImports.ImportsHandle.HandleImport)
|
||
# ============================================================
|
||
def translate_import(self, trans: HT.Translator*,
|
||
node: ast.AST*) -> int:
|
||
"""处理 import 语句,返回 0
|
||
|
||
记账走 StubMergerStore.NotePlainImport(**该约定的唯一实现**):与 vpi
|
||
注入期(VpiReader 顶层遍历)同口径,包含
|
||
· `import X as Y` → from_imports 追加 "Y:X"(别名反查模块真名的唯一
|
||
入口,见 StubMergerStore.LookupModNameByAlias);
|
||
· `import a.b.c` → imported_modules 追加顶层包名 "a"。
|
||
两侧各自只保留特有副作用(此处:导入 SHA1 校验)。
|
||
注:NotePlainImport 为纯记账,不再续接 imported/from 两个查询索引 ⇒ 这两
|
||
个串上的查找走 is_module_imported / lookup_from_import 的逐字符扫描回退
|
||
(与 vpi 注入期上下文同款,正确性不受影响)。
|
||
"""
|
||
imp: ast.Import* = ast.Import*(node)
|
||
if imp is None:
|
||
return 0
|
||
names: list[ast.AST*]* = imp.names
|
||
if names is None:
|
||
return 0
|
||
nn: sizet = len(names)
|
||
pool_val: memhub.MemBuddy* = trans.Pool
|
||
ctx: StubMergerStore.ImportCtx* = StubMergerStore.AllocImportCtx(
|
||
pool_val, trans._imported_modules, trans._from_imports)
|
||
if ctx is None:
|
||
return 0
|
||
for ni in range(nn):
|
||
alias: ast.Alias* = ast.Alias*(names.get(ni))
|
||
if alias is not None and alias.name is not None:
|
||
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
|
||
# declare_only=1/2 是 struct 预注册/import 扫描阶段:此时 SHA1 表
|
||
# 尚未包含后续才追加的模块(如 App 源文件),检查会造成误报。
|
||
if trans._declare_only == 0:
|
||
self._check_import_sha1(node, alias.name)
|
||
StubMergerStore.NotePlainImport(pool_val, ctx, alias.name, alias.asname)
|
||
trans._imported_modules = ctx.ImportedModules
|
||
trans._from_imports = ctx.FromImports
|
||
return 0
|
||
|
||
# ============================================================
|
||
# translate_import_from_module - 处理 from X import Y 的模块部分
|
||
# (迁移自 HandlesImports.ImportsHandle.HandleImportFromModule)
|
||
#
|
||
# 对相对导入(level > 0),使用 trans.CurrentPackage 解析为
|
||
# 完整模块名(如 __types → llvmlite.__types),确保 .deps.txt
|
||
# 记录的模块名与 SHA1 映射表一致。
|
||
# ============================================================
|
||
def translate_import_from_module(self, trans: HT.Translator*,
|
||
node: ast.AST*) -> int:
|
||
"""处理 from X import Y 的模块部分,更新 trans._imported_modules"""
|
||
impf: ast.ImportFrom* = ast.ImportFrom*(node)
|
||
if impf is None:
|
||
return 0
|
||
# 解析模块名:相对导入需补充包前缀
|
||
resolved: str = self._resolve_relative_module(trans.Pool, trans.CurrentPackage,
|
||
impf.level, impf.module)
|
||
if resolved is not None:
|
||
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
|
||
# from . import Y(impf.module is None):resolved 是包目录名
|
||
# (lib.core.Gen.Call 等无 __init__.vp 的目录包也允许这样导入,
|
||
# 见 CallGenCall.vp L12 from . import CallGenBase),目录本身不是
|
||
# 模块文件、无 SHA1 → 跳过包级校验;真实目标子模块(pkg.Y)由
|
||
# 下方 L759-762 逐个校验(LookupSha1ByModName / 本地兄弟文件)。
|
||
if trans._declare_only == 0 and impf.module is not None:
|
||
self._check_import_sha1(node, resolved)
|
||
trans._imported_modules = self.add_imported_module(trans.Pool, trans._imported_modules, resolved)
|
||
# from . import Y(impf.module is None):resolved 是包名(vpsdk),
|
||
# 各 name 是包的子模块 vpsdk.Y。须把完整子模块名也加入 _imported_modules,
|
||
# 否则别名 "syscall" 精确匹配不到包名 "vpsdk" → is_module_imported 返回 0
|
||
# → syscall._syscall1 / syscall.LOAD_SO 跨模块解析失败。
|
||
if impf.module is None:
|
||
names_m: list[ast.AST*]* = impf.names
|
||
if names_m is not None:
|
||
nn_m: sizet = len(names_m)
|
||
for ni_m in range(nn_m):
|
||
alias_m: ast.Alias* = ast.Alias*(names_m.get(ni_m))
|
||
if alias_m is not None and alias_m.name is not None:
|
||
rl_m: sizet = string.strlen(resolved)
|
||
rn_m: sizet = string.strlen(alias_m.name)
|
||
jb_m: str = trans.Pool.alloc(rl_m + rn_m + 2)
|
||
if jb_m is not None:
|
||
string.strcpy(jb_m, resolved)
|
||
jb_m[rl_m] = '.'
|
||
string.strcpy(jb_m + rl_m + 1, alias_m.name)
|
||
# from . import Y 的 Y 不一定是包子模块文件:可能是包
|
||
# __init__.vp 中定义的符号(类/常量,如 JsonValue),无独立
|
||
# 文件、不注册 SHA1。仅当完整名已注册 SHA1(真子模块)或是
|
||
# 本地文件时才校验+登记;否则跳过,符号映射由
|
||
# translate_import_from_names 的 from-import / re-export 处理。
|
||
if (StubMergerStore.LookupSha1ByModName(jb_m) is not None
|
||
or self._is_local_module_file(alias_m.name) != 0):
|
||
if trans._declare_only == 0:
|
||
self._check_import_sha1(node, jb_m)
|
||
trans._imported_modules = self.add_imported_module(trans.Pool, trans._imported_modules, jb_m)
|
||
return 0
|
||
|
||
# ============================================================
|
||
# translate_import_from_names - 处理 from X import Y 的名称部分
|
||
# (迁移自 HandlesImports.ImportsHandle.HandleImportFromNames)
|
||
#
|
||
# 同样使用解析后的完整模块名,确保 from-import 映射
|
||
# (如 LLVMType:llvmlite.__types)能正确查到 SHA1。
|
||
# ============================================================
|
||
# _CheckStaticFromImport - static 符号 from-import 拦截
|
||
#
|
||
# `from mod import name` 命中源模块登记的 static 符号 → fail-fast(与
|
||
# ExprGenAttr 的 `mod.name` 取值拦截、CallGenMixin 的 `mod.fn()` 调用拦截同源,
|
||
# 覆盖 from-import 形态)。用源码原名判表——定义侧登记用的也是裸名。
|
||
# 独立成函数:内联进 translate_import_from_names 会干扰其类型推断(同款处理
|
||
# 见 ExprGenAttr._CheckStaticCrossModule 与 CallGenMixin._CheckStaticCallCrossModule)。
|
||
# ============================================================
|
||
def _CheckStaticFromImport(self, trans: HT.Translator*,
|
||
fi_target: str,
|
||
orig_name: str,
|
||
node: ast.AST*) -> int:
|
||
if trans is None or fi_target is None or orig_name is None:
|
||
return 0
|
||
if trans.ModuleSha1 is None:
|
||
return 0
|
||
# 快速短路:项目内无 static 符号时零开销(不查 sha1、不扫表)
|
||
if self.Gen.CallGenBase.StaticCount <= 0:
|
||
return 0
|
||
sf_sha1: str = self.Gen.StructGen._resolve_module_sha1(fi_target, trans._from_imports)
|
||
if sf_sha1 is None:
|
||
return 0
|
||
if string.strncmp(sf_sha1, trans.ModuleSha1, 16) == 0:
|
||
return 0
|
||
if self.Gen.CallGenBase.IsStaticSymbol(sf_sha1, orig_name) == 0:
|
||
return 0
|
||
self.Gen.TypeGenErr.fatal_error(node,
|
||
f"'{fi_target}.{orig_name}' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)",
|
||
"NameError")
|
||
return 0
|
||
|
||
|
||
def translate_import_from_names(self, trans: HT.Translator*,
|
||
node: ast.AST*) -> int:
|
||
"""处理 from X import Y 的名称部分,更新 trans._from_imports"""
|
||
impf: ast.ImportFrom* = ast.ImportFrom*(node)
|
||
if impf is None:
|
||
return 0
|
||
# 解析模块名:相对导入需补充包前缀
|
||
resolved: str = self._resolve_relative_module(trans.Pool, trans.CurrentPackage,
|
||
impf.level, impf.module)
|
||
if resolved is not None:
|
||
names: list[ast.AST*]* = impf.names
|
||
if names is not None:
|
||
nn: sizet = len(names)
|
||
for ni in range(nn):
|
||
alias: ast.Alias* = ast.Alias*(names.get(ni))
|
||
if alias is not None and alias.name is not None:
|
||
local_name: str = alias.name
|
||
orig_name: str = None
|
||
if alias.asname is not None:
|
||
local_name = alias.asname
|
||
orig_name = alias.name
|
||
# from . import Y(impf.module is None):_resolve_relative_module
|
||
# 返回的是包名(如 vpsdk),但 Y 是包的子模块 vpsdk.Y。须映射
|
||
# local_name → 完整子模块名,否则 syscall.LOAD_SO 跨模块 CDefine /
|
||
# syscall._syscall1 跨模块调用在包里找不到。
|
||
fi_target: str = resolved
|
||
if impf.module is None and resolved is not None:
|
||
fl_t: sizet = string.strlen(resolved)
|
||
fn_t: sizet = string.strlen(alias.name)
|
||
jb_t: str = trans.Pool.alloc(fl_t + fn_t + 2)
|
||
if jb_t is not None:
|
||
string.strcpy(jb_t, resolved)
|
||
jb_t[fl_t] = '.'
|
||
string.strcpy(jb_t + fl_t + 1, alias.name)
|
||
fi_target = jb_t
|
||
trans._from_imports = self.add_from_import(trans.Pool, trans._from_imports,
|
||
local_name, fi_target, orig_name)
|
||
# static 符号(禁止跨模块使用):from mod import name 命中源
|
||
# 模块登记的 static 符号 → fail-fast。逻辑收敛在
|
||
# _CheckStaticFromImport 内(不内联——内联新增分支会干扰本函数
|
||
# 的类型推断,与 ExprGenAttr/CallGenMixin 同款处理)。
|
||
self._CheckStaticFromImport(trans, fi_target, alias.name, node)
|
||
# 命名空间隔离:from-import 的名称标记为可见结构体
|
||
self.Gen.StructGen.add_visible_struct(trans.Pool, local_name)
|
||
# 注册 re-export 映射:当前模块 re-export 了 local_name,
|
||
# 源模块为 fi_target(from . import Y 时是子模块名 vpsdk.Y,
|
||
# 非包名)。使跨模块调用 module.func() 能解析到正确 SHA1
|
||
if trans.ModuleSha1 is not None:
|
||
self.register_reexport(trans.ModuleSha1, local_name, fi_target)
|
||
return 0
|
||
|
||
def InitImportsGen(self) -> int:
|
||
"""初始化 ImportsGen 字段(构造时由 Generator 调用)"""
|
||
if self is None:
|
||
return 1
|
||
self.ReexportSha1s = None
|
||
self.ReexportFuncs = None
|
||
self.ReexportSrcs = None
|
||
self.ReexportCount = 0
|
||
# 导入查找索引字段(索引表由 add_* 增量创建,PermPool 分配)
|
||
self.CurImportedModulesPtr = None
|
||
self.CurImportedModulesNames = None
|
||
self.CurFromImportsPtr = None
|
||
self.CurFromImportsMods = None
|
||
self.CurFromImportsOrigs = None
|
||
return 0
|