diff --git a/App/lib/core/Gen/Call/CallGenBase.vp b/App/lib/core/Gen/Call/CallGenBase.vp index 7e5fa4f..b682c53 100644 --- a/App/lib/core/Gen/Call/CallGenBase.vp +++ b/App/lib/core/Gen/Call/CallGenBase.vp @@ -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 头) diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index e35fdaf..e6c28bd 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -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) diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 2626713..e33148e 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -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 前缀 + 属性名"精确匹配, # 避免多模块同名全局(@._mbuddy / @._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*)。支持本模块与跨模块符号: diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index 23d0428..e3950bf 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -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: diff --git a/App/lib/core/Gen/ImportsGenMixin.vp b/App/lib/core/Gen/ImportsGenMixin.vp index 077da36..13e3b45 100644 --- a/App/lib/core/Gen/ImportsGenMixin.vp +++ b/App/lib/core/Gen/ImportsGenMixin.vp @@ -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, diff --git a/App/lib/core/Gen/TranslatorGenMixin.vp b/App/lib/core/Gen/TranslatorGenMixin.vp index 9b94358..ae5cb44 100644 --- a/App/lib/core/Gen/TranslatorGenMixin.vp +++ b/App/lib/core/Gen/TranslatorGenMixin.vp @@ -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