feat(gen,static): static 符号禁止跨模块使用(定义侧登记 + 引用侧三处拦截)
语义 = C 的 internal linkage:static 符号(函数/变量)只在定义模块内可见,任何跨模块引用一律 fail-fast(NameError)。 - 登记表:CallGenBase 新增 StaticSha1Arr/StaticNameArr/StaticCount(PermPool 定长槽,与 CexportSha1Arr 同款)+ RegisterStaticSymbol/IsStaticSymbol(按 (符号名, 模块 SHA1) 精确配对,避免跨模块同名误判)。 - 定义侧登记:FuncGenDef 识别注解形态(-> t.CVoid | t.CStatic)与装饰器形态(@t.CStatic;裸 static def 已由解析层归一为装饰器)→ 登记函数名;TranslatorGenMixin.handle_module_level_var 识别注解形态(static x: int 已归一为 t.CStatic | int)→ 登记变量名。 - 引用侧拦截(三处,覆盖全部跨模块形态):ExprGenAttr.translate_attribute 入口统一拦截 mod.name(含 import 别名还原);CallGenMixin.translate_call 入口拦截 mod.fn(...) 调用(按 AST 形态一次覆盖 found_func/vpi/裸名兜底各分支);ImportsGenMixin.translate_import_from_names 拦截 from mod import name。 - 性能:三处检查均以 StaticCount > 0 快速短路——项目中无 static 符号时零开销(不触碰逐条线性扫描与 sha1 解析)。 - 同模块自引用(sha1 相同)与非模块基座(self.method / obj.field / Class.static_method)一律放行。
This commit is contained in:
1 parent
424dbd7379
commit
dd56580855
6 files changed
+178
-1
No files matched your search
@@ -63,6 +63,14 @@ novirtual class CallGenBase:
|
||||
CexportFuncArr: bytes
|
||||
CexportCount: int
|
||||
|
||||
# --- static 符号注册表(`static` 修饰符 / @t.CStatic / t.CStatic 标记的符号 →
|
||||
# 定义模块 SHA1)。语义 = 禁止跨模块使用(C 的 internal linkage):定义侧登记,
|
||||
# 跨模块引用侧(mod.name 取值 / mod.fn() 调用 / from mod import name)拦截报错。
|
||||
# 与 CexportSha1Arr 同款 PermPool 定长槽(跨 mark/reset 存活)。
|
||||
StaticSha1Arr: bytes
|
||||
StaticNameArr: bytes
|
||||
StaticCount: int
|
||||
|
||||
# --- LLVMIR 寄存器序号 ---
|
||||
LLVMIRRegSeq: int
|
||||
|
||||
@@ -418,6 +426,66 @@ novirtual class CallGenBase:
|
||||
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 头)
|
||||
|
||||
|
||||
@@ -53,6 +53,32 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||||
"""翻译函数调用,返回 call 结果 Value"""
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
# static 符号(禁止跨模块使用):模块限定**调用** `mod.fn(...)` 命中别的
|
||||
# 模块登记的 static 函数即 fail-fast。放在入口处统一拦截——后续分派有
|
||||
# 多条分支(found_func 命中 / 走 vpi 解析 / 走裸名兜底),只在某一分支
|
||||
# 检查会漏;此处按 AST 形态一次覆盖全部调用路径。基座不是已导入模块
|
||||
# (self.method / obj.method / Class.static_method)时放行。
|
||||
if cl is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute \
|
||||
and trans is not None and trans.ModuleSha1 is not None \
|
||||
and trans._imported_modules is not None \
|
||||
and self.Gen.CallGenBase.StaticCount > 0:
|
||||
st_at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if st_at.value is not None and st_at.attr is not None:
|
||||
st_mod: str = None
|
||||
if st_at.value.kind() == ast.ASTKind.Name:
|
||||
st_nm: ast.Name* = ast.Name*(st_at.value)
|
||||
st_mod = st_nm.id
|
||||
elif st_at.value.kind() == ast.ASTKind.Attribute:
|
||||
st_mod = self.Gen.ExprGenAttr._attr_module_path_str(pool, st_at.value)
|
||||
if st_mod is not None \
|
||||
and self.Gen.ImportsGen.is_module_imported(trans._imported_modules, st_mod) != 0:
|
||||
st_sha1: str = self.Gen.StructGen._resolve_module_sha1(st_mod, trans._from_imports)
|
||||
if st_sha1 is not None \
|
||||
and string.strncmp(st_sha1, trans.ModuleSha1, 16) != 0 \
|
||||
and self.Gen.CallGenBase.IsStaticSymbol(st_sha1, st_at.attr) != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(cl,
|
||||
f"'{st_mod}.{st_at.attr}' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)",
|
||||
"NameError")
|
||||
if cl is None: return None
|
||||
|
||||
func_name: str = self.Gen.ExprGenBase.get_func_name(cl.func)
|
||||
|
||||
@@ -202,6 +202,16 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
# 检查是否是已导入模块
|
||||
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, mod_path_w) == 0:
|
||||
return None
|
||||
# static 符号(`static x: int` / @t.CStatic / x: t.CStatic|T):语义为
|
||||
# 禁止跨模块使用(C 的 internal linkage),模块限定属性访问命中别的模块
|
||||
# 的 static 符号即 fail-fast。同模块自引用(sha1 相同)放行。
|
||||
mod_sha1_st: str = self._lookup_module_sha1(mod_path_w)
|
||||
if mod_sha1_st is not None and trans.ModuleSha1 is not None \
|
||||
and string.strncmp(mod_sha1_st, trans.ModuleSha1, 16) != 0 \
|
||||
and self.Gen.CallGenBase.IsStaticSymbol(mod_sha1_st, at.attr) != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(at,
|
||||
f"'{mod_path_w}.{at.attr}' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)",
|
||||
"NameError")
|
||||
# 在当前模块查找全局变量:优先按"目标模块 SHA1 前缀 + 属性名"精确匹配,
|
||||
# 避免多模块同名全局(@<A>._mbuddy / @<B>._mbuddy 同时注入)时裸名后缀
|
||||
# 匹配返回错误模块的全局(串扰)。目标 sha1 未知时退化裸名(后缀匹配兜底)
|
||||
@@ -343,6 +353,38 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
VLogger.error(diag_buf_an, "ATTR")
|
||||
return None
|
||||
|
||||
# static 符号(禁止跨模块使用)——统一入口拦截:
|
||||
# 属性基座是已导入模块名(含 import 别名还原)、且 attr 命中该模块登记的
|
||||
# static 符号(`static def f` / `static x: int` / @t.CStatic / x: t.CStatic|T)
|
||||
# 时 fail-fast。放在所有分支之前,一次覆盖 `mod.fn`(函数取址)、
|
||||
# `mod.var`(全局变量)等全部模块限定属性形态。同模块自引用(sha1 相同)
|
||||
# 与非模块基座(obj.field / Class.field)放行。
|
||||
if trans is not None and trans.ModuleSha1 is not None \
|
||||
and trans._imported_modules is not None \
|
||||
and self.Gen.CallGenBase.StaticCount > 0:
|
||||
st_path: str = self._attr_module_path_str(pool, at.value)
|
||||
if st_path is not None and trans._from_imports is not None:
|
||||
orig_st: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, st_path, 0)
|
||||
if orig_st is not None:
|
||||
ol_st: sizet = 0
|
||||
while orig_st[ol_st] != ' ' and orig_st[ol_st] != '\0' and orig_st[ol_st] != ':':
|
||||
ol_st += 1
|
||||
if ol_st > 0:
|
||||
ob_st: str = pool.alloc(ol_st + 1)
|
||||
if ob_st is not None:
|
||||
string.strncpy(ob_st, orig_st, ol_st)
|
||||
ob_st[ol_st] = '\0'
|
||||
st_path = ob_st
|
||||
if st_path is not None \
|
||||
and self.Gen.ImportsGen.is_module_imported(trans._imported_modules, st_path) != 0:
|
||||
st_sha1: str = self._lookup_module_sha1(st_path)
|
||||
if st_sha1 is not None \
|
||||
and string.strncmp(st_sha1, trans.ModuleSha1, 16) != 0 \
|
||||
and self.Gen.CallGenBase.IsStaticSymbol(st_sha1, at.attr) != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(at,
|
||||
f"'{st_path}.{at.attr}' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)",
|
||||
"NameError")
|
||||
|
||||
# 魔法属性 __doc__:查询 docstring 登记表(预注册阶段从 AST 提取),
|
||||
# 命中则当前模块内联生成字符串常量返回;未命中(无 docstring 的
|
||||
# 函数/类/方法)返回 null 指针(i8*)。支持本模块与跨模块符号:
|
||||
|
||||
@@ -147,7 +147,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
has_export = self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "CExport", 0)
|
||||
# 装饰器形式存储类标记(@t.CExport/@t.CExtern/@t.State/@t.CStatic)
|
||||
# 对齐 TPC HandlesFunctions._extract_decorator_storage_flags;
|
||||
# CExport/CExtern/State 影响函数名(导出裸名),CStatic 仅标记。
|
||||
# CExport/CExtern/State 影响函数名(导出裸名),CStatic 不影响函数名,
|
||||
# 但登记到 static 符号表(禁止跨模块使用,见下方 RegisterStaticSymbol)。
|
||||
# 注意: 用 HandlesClassDef._has_decorator 委托而非 self._has_decorator
|
||||
# (Mixin 是 @t.NoVTable 静态绑定,跨 Mixin 方法经 MRO 调用会生成
|
||||
# 不存在的 FuncGenMixin._has_decorator → 链接 undefined symbol)
|
||||
@@ -162,6 +163,17 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if has_extern != 0 or has_state != 0:
|
||||
is_extern_decl = 1
|
||||
|
||||
# static(禁止跨模块使用):注解形式(`def f() -> t.CVoid | t.CStatic:`)
|
||||
# 与装饰器形式(`@t.CStatic`)同源识别,命中即登记到 static 符号表。
|
||||
# 裸 `static def f(...)` 已在解析层归一为 @t.CStatic 装饰器
|
||||
# (parser.vp 的 _ParseStaticModified),此处无需额外判据。
|
||||
has_static: int = 0
|
||||
if fd.returns is not None:
|
||||
has_static = self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "CStatic", 0)
|
||||
if has_static == 0 and fd.decorator_list is not None:
|
||||
if self.Gen.ClassGenBase._has_decorator(fd.decorator_list, "CStatic") == 1:
|
||||
has_static = 1
|
||||
|
||||
# 计算 mangled name
|
||||
if has_extern != 0 or has_state != 0 or has_export != 0 or string.strcmp(fd.name, "main") == 0:
|
||||
mangled_name: str = fd.name
|
||||
@@ -211,6 +223,13 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if (has_export != 0 or has_state != 0) and trans.ModuleSha1 is not None:
|
||||
self.Gen.CallGenBase.register_cexport_func(trans.ModuleSha1, fd.name)
|
||||
|
||||
# 登记 static 符号(禁止跨模块使用):定义侧登记 (符号名, 模块 SHA1),
|
||||
# 跨模块引用侧(mod.name 取值 / mod.fn() 调用 / from mod import name)
|
||||
# 经 IsStaticSymbol 拦截并 fail-fast。函数名用裸名 fd.name 登记——
|
||||
# 跨模块引用侧的解析入口拿到的也是源码里的裸名(不含 sha1 前缀)。
|
||||
if has_static != 0 and trans.ModuleSha1 is not None:
|
||||
self.Gen.CallGenBase.RegisterStaticSymbol(trans.ModuleSha1, fd.name)
|
||||
|
||||
# 添加参数
|
||||
args_node: ast.Arguments* = fd.args
|
||||
if args_node is not None:
|
||||
|
||||
@@ -957,6 +957,19 @@ novirtual class ImportsGenMixin:
|
||||
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(与 `mod.name` 取值 /
|
||||
# `mod.fn()` 调用两处拦截同源,覆盖 from-import 形态)。
|
||||
# 用 alias.name(源码原名)判表——登记侧也用裸名。
|
||||
if trans.ModuleSha1 is not None and fi_target is not None \
|
||||
and self.Gen.CallGenBase.StaticCount > 0:
|
||||
fi_sha1_st: str = self.Gen.StructGen._resolve_module_sha1(fi_target, trans._from_imports)
|
||||
if fi_sha1_st is not None \
|
||||
and string.strncmp(fi_sha1_st, trans.ModuleSha1, 16) != 0 \
|
||||
and self.Gen.CallGenBase.IsStaticSymbol(fi_sha1_st, alias.name) != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node,
|
||||
f"'{fi_target}.{alias.name}' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)",
|
||||
"NameError")
|
||||
# 命名空间隔离:from-import 的名称标记为可见结构体
|
||||
self.Gen.StructGen.add_visible_struct(trans.Pool, local_name)
|
||||
# 注册 re-export 映射:当前模块 re-export 了 local_name,
|
||||
|
||||
@@ -687,6 +687,15 @@ novirtual class TranslatorGenMixin:
|
||||
nm: ast.Name* = ast.Name*(aa.target)
|
||||
var_name = nm.id
|
||||
annot_node = aa.annotation
|
||||
# static 符号(禁止跨模块使用):模块级变量带 t.CStatic 标记
|
||||
# (`static x: int = 0` 经解析层归一为 `x: t.CStatic | int`;亦覆盖
|
||||
# 手写 `x: t.CStatic | int`)→ 登记 (变量名, 定义模块 SHA1)。
|
||||
# 变量无装饰器通道(@t.CStatic 只作用于 def),故只看注解。
|
||||
# 时机:本处已在 `_declare_only` 提前返回(L662)之后,预声明遍不会
|
||||
# 走到,避免同一符号重复登记。
|
||||
if annot_node is not None and var_name is not None and trans.ModuleSha1 is not None:
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CStatic", 0) != 0:
|
||||
self.Gen.CallGenBase.RegisterStaticSymbol(trans.ModuleSha1, var_name)
|
||||
# t.Callable 模块级函数指针:登记 (变量名 → 返回类型) 签名。此路径原先
|
||||
# 只创建全局变量、不登记签名,模块级 t.Callable 变量被间接调用时在
|
||||
# CallGenLeafKit._translate_func_ptr_call 报"返回类型未注册(t.Callable
|
||||
|
||||
Reference in new issue
Block a user