diff --git a/App/lib/core/Gen/Call/CallGenBase.vp b/App/lib/core/Gen/Call/CallGenBase.vp index 5b8f8d1..821fc60 100644 --- a/App/lib/core/Gen/Call/CallGenBase.vp +++ b/App/lib/core/Gen/Call/CallGenBase.vp @@ -59,123 +59,89 @@ novirtual class CallGenBase: # --- c.cexport 注册表(原 _g_cexport_sha1_arr/_g_cexport_func_arr/_g_cexport_count) --- Gen: Generator.Generator* - CexportSha1Arr: bytes - CexportFuncArr: bytes - CexportCount: int + CexportSha1Arr: bytes = None + CexportFuncArr: bytes = None + CexportCount: int = 0 # --- static 符号注册表(`static` 修饰符 / @t.CStatic / t.CStatic 标记的符号 → # 定义模块 SHA1)。语义 = 禁止跨模块使用(C 的 internal linkage):定义侧登记, # 跨模块引用侧(mod.name 取值 / mod.fn() 调用 / from mod import name)拦截报错。 # 与 CexportSha1Arr 同款 PermPool 定长槽(跨 mark/reset 存活)。 - StaticSha1Arr: bytes - StaticNameArr: bytes - StaticCount: int + StaticSha1Arr: bytes = None + StaticNameArr: bytes = None + StaticCount: int = 0 # --- LLVMIR 寄存器序号 --- - LLVMIRRegSeq: int + LLVMIRRegSeq: int = 0 # --- temp 目录 --- - TempDir: str - TempDirLen: sizet + TempDir: str = None + TempDirLen: sizet = 0 # --- 闭包名表(原 _g_closure_names/_g_closure_count) --- - ClosureNames: str - ClosureCount: int + ClosureNames: str = None + ClosureCount: int = 0 # --- 函数指针签名表(原 _g_funcptr_names/_g_funcptr_rets/_g_funcptr_count) --- - FuncptrNames: str - FuncptrRets: uint64* - FuncptrCount: int + FuncptrNames: str = None + FuncptrRets: uint64* = None + FuncptrCount: int = 0 # --- 全局函数注册表(原 _global_funcs/_global_func_count) --- - GlobalFuncs: GenTypes.FuncEntry* - GlobalFuncCount: int + GlobalFuncs: GenTypes.FuncEntry* = None + GlobalFuncCount: int = 0 # --- 全局函数表"名 → 槽下标"索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) --- # GlobalFuncs 只追加不重排 ⇒ 槽下标稳定;Built == GlobalFuncCount 才可信,否则回退线性扫描。 - GlobalFuncNameIdx: sizet* - GlobalFuncNameIdxCap: int - GlobalFuncNameIdxBuilt: int + GlobalFuncNameIdx: sizet* = None + GlobalFuncNameIdxCap: int = 0 + GlobalFuncNameIdxBuilt: int = -1 # --- 全局默认参数表(原 _global_defaults/_global_defaults_count) --- - GlobalDefaults: GenTypes.FuncDefaultsEntry* - GlobalDefaultsCount: int + GlobalDefaults: GenTypes.FuncDefaultsEntry* = None + GlobalDefaultsCount: int = 0 # --- 依赖模块 text.ll「方法返回类型文本」缓存(_LookupMethodRetTyFromTextLL 性能优化) --- # key = define 的完整 mangled 函数名("{sha1}.{后缀}"),值只存文本(绝不存 LLVMType*,其分配在每文件 reset 失效的 pool 中);表与键/值均 PermPool 分配,命中后用调用方 pool 重建类型,建表失败即不缓存并回退原扫描。 - MethodRetTyMap: hashtable.HashTable* + MethodRetTyMap: hashtable.HashTable* = None def InitCallGen(self) -> int: """初始化调用 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)""" if self is None: return 1 - self.CexportSha1Arr = None - self.CexportFuncArr = None - self.CexportCount = 0 - self.LLVMIRRegSeq = 0 - self.TempDir = None - self.TempDirLen = 0 - self.ClosureNames = None - self.ClosureCount = 0 - self.FuncptrNames = None - self.FuncptrRets = None - self.FuncptrCount = 0 - self.GlobalFuncs = None - self.GlobalFuncCount = 0 - self.GlobalFuncNameIdx = None - self.GlobalFuncNameIdxCap = 0 - self.GlobalFuncNameIdxBuilt = -1 - self.GlobalDefaults = None - self.GlobalDefaultsCount = 0 - self.MethodRetTyMap = None return 0 - # P4 簇 1+2 迁移方法:模块 SHA1 查询 / CExport 注册表 / c.* 系列 / 内联汇编 / LLVMIR - - - def _lookup_module_sha1(self, mod_name: str) -> str: """查找模块名对应的 SHA1,找不到返回 None - 委托给 StubMerger.LookupSha1ByModName(精确匹配 + 包名回退)。 """ return StubMergerStore.LookupSha1ByModName(mod_name) - def _lookup_module_sha1_suffix(self, mod_suffix: str) -> str: """后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "llvmlite.__types" - 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点), 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。 委托给 StubMerger.LookupSha1ByModNameSuffix(精确 + 后缀 + 反向后缀)。 """ return StubMergerStore.LookupSha1ByModNameSuffix(mod_suffix) - - def _module_package_from_sha1(self, pool: memhub.MemBuddy*, - mod_sha1: str) -> str: + def _module_package_from_sha1(self, pool: memhub.MemBuddy*, mod_sha1: str) -> str: """从 SHA1 存储器的 rel_path 计算模块所属包名(目录部分,'/' → '.')""" - if pool is None or mod_sha1 is None: - return None + if pool is None or mod_sha1 is None: return None rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr() sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr() count: int = StubMergerStore.GetSha1StoreCount() - if rel_arr_ptr is None or sha1_arr_ptr is None or count <= 0: - return None + if rel_arr_ptr is None or sha1_arr_ptr is None or count <= 0: return None for i in range(count): sidx: sizet = sizet(i) * 17 - if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0: - continue + if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0: continue ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN rel: str = rel_arr_ptr + ridx last_slash: str = string.strrchr(rel, 47) # 47 = ord('/') - if last_slash is None: - return None + if last_slash is None: return None pkg_len: sizet = sizet(uint64(last_slash) - uint64(rel)) - if pkg_len == 0: - return None + if pkg_len == 0: return None pkg_buf: str = pool.alloc(pkg_len + 1) - if pkg_buf is None: - return None + if pkg_buf is None: return None for pi in range(pkg_len): if rel[pi] == '/': pkg_buf[pi] = '.' @@ -186,28 +152,23 @@ novirtual class CallGenBase: return None - def _lookup_reexport_from_vpi(self, pool: memhub.MemBuddy*, - mod_sha1: str, func_name: str) -> str: + def _lookup_reexport_from_vpi(self, pool: memhub.MemBuddy*, mod_sha1: str, func_name: str) -> str: """re-export 表未命中时,从模块 .vpi 按需解析 from-import 定位定义模块 - includes 模块(如 llvmlite/__init__)的 stub/text 已存在时不会被按需 全量翻译(Phase2 3.5 仅在 stub/text 缺失时翻译),而 re-export 表只在 declare_only=0 的全量翻译中注册(HandleImportFromNames),因此表可能为空。 本函数解析该模块 .vpi 的 "from .X import ... func_name ..." 语句, 返回定义模块名(如 llvmlite/__init__ 的 .__builder → "llvmlite.__builder")。 """ - if pool is None or mod_sha1 is None or func_name is None: - return None + if pool is None or mod_sha1 is None or func_name is None: return None # star import 候选模块累积(空格分隔):from X import * 无法确定归属,收集全部候选由调用方逐个验证 star_cands: str = None star_cands_len: sizet = 0 temp_dir: str = self.Gen.TypeGenBase.get_temp_dir() - if temp_dir is None: - return None + if temp_dir is None: return None td_len: sizet = string.strlen(temp_dir) vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, mod_sha1, "vpi") - if vpi_path is None: - return None + if vpi_path is None: return None READ_SZ: sizet = 1048576 buf: bytes = stdlib.malloc(READ_SZ) if buf is None: @@ -414,8 +375,7 @@ novirtual class CallGenBase: 误用 @1fdad1c76fb116bc.malloc 而非裸名 @malloc → ld.lld undefined symbol: 1fdad1c76fb116bc.malloc)。 """ - if self.Gen is None or sha1 is None or func_name is None: - return 0 + if self.Gen is None or sha1 is None or func_name is None: return 0 if self.Gen.CallGenBase.CexportSha1Arr is not None: for i in range(self.Gen.CallGenBase.CexportCount): idx: sizet = sizet(i) * 17 diff --git a/App/lib/core/Gen/Call/CallGenDispatch.vp b/App/lib/core/Gen/Call/CallGenDispatch.vp index 59a4944..d4e9c57 100644 --- a/App/lib/core/Gen/Call/CallGenDispatch.vp +++ b/App/lib/core/Gen/Call/CallGenDispatch.vp @@ -23,10 +23,6 @@ import lib.core.Gen.GenApi as GenApi # ============================================================ # CallGenDispatch - CallGenMixin 拆分(阶段 C):translate_call 检测段 -# -# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。 -# 每个 helper 返回 Value|CPtr,None = 未命中(translate_call 继续)。 -# @t.NoVTable:静态绑定。 # ============================================================ novirtual class CallGenDispatch(CallGenBase.CallGenBase): """translate_call 前置检测 helper(继承 Base)""" diff --git a/App/lib/core/Gen/Call/CallGenFd.vp b/App/lib/core/Gen/Call/CallGenFd.vp index 1866469..614b836 100644 --- a/App/lib/core/Gen/Call/CallGenFd.vp +++ b/App/lib/core/Gen/Call/CallGenFd.vp @@ -14,18 +14,9 @@ from . import CallGenBase # ============================================================ # CallGenFd - CallGenMixin 拆分(阶段 B):FakeDuck / requires 字段注入 / provider 栈 -# -# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。 -# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。 -# @t.NoVTable:静态绑定。 # ============================================================ -@t.NoVTable -class CallGenFd(CallGenBase.CallGenBase): +novirtual class CallGenFd(CallGenBase.CallGenBase): """FakeDuck / requires 字段注入 / provider 栈""" - - # _translate_fakeduck_call:FakeDuck str 变量方法调用 → 扩展类 _Type/_str(对齐 TPC HandlesExprCall.py L2841-2880)。对象是 Name 且有影子({var}__meta__)时 self=影子;否则在入口创建临时影子(__data__=obj 值、__mbuddy__=NULL)后作 self,避免 bitcast i8* → _str* 读垃圾指针 - - # ============================================================ def _translate_fakeduck_call(self, pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*, mod: llvmlite.LLVMModule*, @@ -34,21 +25,15 @@ class CallGenFd(CallGenBase.CallGenBase): cl: ast.Call*, trans: HT.Translator*) -> llvmlite.Value*: """FakeDuck: str 变量方法调用解析到扩展类 _Type/_str,返回 call 结果或 None""" - if pool is None or builder is None or cl is None or trans is None: - return None - if method_name is None: - return None + if pool is None or builder is None or cl is None or trans is None: return None + if method_name is None: return None # 仅对 str 类型触发(AnnotClassName == "str",对象是 char*); # cls_name 为 None(表达式接收者解析失败)时也尝试 - if cls_name is not None and string.strcmp(cls_name, "str") != 0: - return None - if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: - return None + if cls_name is not None and string.strcmp(cls_name, "str") != 0: return None + if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return None fd_at: ast.Attribute* = ast.Attribute*(cl.func) - if fd_at is None or fd_at.value is None or fd_at.attr is None: - return None - if string.strcmp(fd_at.attr, method_name) != 0: - return None + if fd_at is None or fd_at.value is None or fd_at.attr is None: return None + if string.strcmp(fd_at.attr, method_name) != 0: return None # 候选扩展类:先 _Type(a.type_id())后 _str(a.upper() 等) fd_func: llvmlite.Function* = None fd_cls: str = None @@ -63,12 +48,10 @@ class CallGenFd(CallGenBase.CallGenBase): if fd_func is not None: fd_cls = fd_cn break - if fd_func is None or fd_cls is None: - return None + if fd_func is None or fd_cls is None: return None # 扩展类结构体必须已注册(GEP/临时影子需要类型) fd_sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(fd_cls) - if fd_sty is None: - return None + if fd_sty is None: return None # 扩展类字段数:有字段(_str)时 self 用影子结构体指针; # 无字段(_Type)时 self 用对象值 bitcast 到扩展类指针(对齐 TPC L2856-2876) fd_entry_fd: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(fd_sty) @@ -93,8 +76,7 @@ class CallGenFd(CallGenBase.CallGenBase): # 先确保 _str 完整定义(跨模块结构体默认 opaque,alloca 报 unsized) self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty) fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty) - if fd_self is None: - return None + if fd_self is None: return None fd_obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans) if fd_obj_val is not None: fd_dfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__data__") @@ -102,8 +84,7 @@ class CallGenFd(CallGenBase.CallGenBase): fd_dp: llvmlite.Value* = llvmlite.build_gep_struct(builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index) if fd_dp is not None: llvmlite.build_store(builder, fd_obj_val, fd_dp) - fd_bfe: GenTypes.FieldEntry* = \ - self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__") + fd_bfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__") if fd_bfe is not None and fd_bfe.Ty is not None: fd_bp: llvmlite.Value* = llvmlite.build_gep_struct(builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index) fd_null: llvmlite.Value* = llvmlite.ConstNull(pool, fd_bfe.Ty, "null") @@ -149,8 +130,7 @@ class CallGenFd(CallGenBase.CallGenBase): if fd_ret_ty is None: fd_ret_ty = llvmlite.Int32(pool) fd_fname: str = llvmlite.function_get_name(fd_func) - if fd_fname is None: - return None + if fd_fname is None: return None # 返回类型精确化:跨模块 declare 的 list[T] 特化返回类型可能被简化为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标路径而非 list 内联 __getitem__(printf %s 崩溃)。从定义模块 text.ll 读取精确返回类型;仅当函数名确为 "{16hex}.{func}"(判定走唯一真源 GenApi.sha1_prefix_len)才提取模块 sha1 if GenApi.sha1_prefix_len(fd_fname) != 0: fd_sha1: str = pool.alloc(17) diff --git a/App/lib/core/Gen/Call/CallGenProp.vp b/App/lib/core/Gen/Call/CallGenProp.vp index 84880cf..d5d4058 100644 --- a/App/lib/core/Gen/Call/CallGenProp.vp +++ b/App/lib/core/Gen/Call/CallGenProp.vp @@ -431,8 +431,7 @@ novirtual class CallGenProp(CallGenBase.CallGenBase): method_name: str, cl: ast.Call*) -> int: """基于接收者实际类型判定 list 内建方法(实际类型不可得回退注解名)""" - sty: llvmlite.LLVMType* = \ - self.Gen.StructGen.get_struct_type_from_value(recv_val) + sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(recv_val) if sty is not None: ty_nm: str = llvmlite.get_struct_name(sty) if ty_nm is not None and ty_nm[0] != '\0': diff --git a/App/lib/core/Gen/Generator.vp b/App/lib/core/Gen/Generator.vp index 50cc473..b9c8bfe 100644 --- a/App/lib/core/Gen/Generator.vp +++ b/App/lib/core/Gen/Generator.vp @@ -459,4 +459,4 @@ novirtual class Generator: # ============================================================ # 全局生成器单例(组合架构兼容层) # ============================================================ -_g_gen: Generator | t.CPtr = None +_g_gen: Generator* = None diff --git a/App/lib/core/VLogger.vp b/App/lib/core/VLogger.vp index 791177e..f8ba6fc 100644 --- a/App/lib/core/VLogger.vp +++ b/App/lib/core/VLogger.vp @@ -17,7 +17,7 @@ import w32.win32base as win32base # 错误恢复(REPL/JIT 模式):error 打印后 longjmp 跳回 REPL 的 setjmp 点。 # mingw-w64 x86_64:longjmp 原型为 void longjmp(jmp_buf, int);setjmp 宏展开为 # _setjmp(buf, NULL)(jmp_buf 实为 _JBTYPE[16],见 Repl.vp 的 _REPL_JMPBUF_SIZE)。 -extern def longjmp(buf: t.CPtr, val: int) +extern def longjmp(buf: rawptr, val: int) # REPL 恢复点指针(由 Repl 在会话开始时注册、会话结束时清除)。 @@ -25,7 +25,7 @@ extern def longjmp(buf: t.CPtr, val: int) # Repl.ReplExecFragment 入口重注册),故它覆盖整条片段流水线(翻译 → 写 .ll → # llc → clang++ → LoadLibrary → GetProcAddress → 调用 → 回显),而不只是翻译期。 # 批处理路径从不调用 set_repl_jmpbuf ⇒ 恒为 None ⇒ 所有出口退回 sys.exit(1)。 -_repl_jmpbuf: t.CPtr = None +_repl_jmpbuf: rawptr = None # ============================================================ @@ -62,35 +62,29 @@ def clear_repl_jmpbuf() -> int: return 0 # 日志级别 -class LogLevel(t.CEnum): - DEBUG = 0 - INFO = 1 - WARNING = 2 - ERROR = 3 - SUCCESS = 4 - CRITICAL = 5 +enum LogLevel: + DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL # Win32 控制台前景色属性 -FOREGROUND_BLUE: t.CDefine = 0x0001 -FOREGROUND_GREEN: t.CDefine = 0x0002 -FOREGROUND_RED: t.CDefine = 0x0004 -FOREGROUND_INTENSITY: t.CDefine = 0x0008 -FOREGROUND_WHITE: t.CDefine = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE -FOREGROUND_CYAN: t.CDefine = FOREGROUND_BLUE | FOREGROUND_GREEN -FOREGROUND_YELLOW: t.CDefine = FOREGROUND_RED | FOREGROUND_GREEN -FOREGROUND_MAGENTA: t.CDefine = FOREGROUND_RED | FOREGROUND_BLUE +define FOREGROUND_BLUE = 0x0001 +define FOREGROUND_GREEN = 0x0002 +define FOREGROUND_RED = 0x0004 +define FOREGROUND_INTENSITY = 0x0008 +define FOREGROUND_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE +define FOREGROUND_CYAN = FOREGROUND_BLUE | FOREGROUND_GREEN +define FOREGROUND_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN +define FOREGROUND_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE # Win32 控制台背景色属性 -BACKGROUND_RED: t.CDefine = 0x0040 -BACKGROUND_GREEN: t.CDefine = 0x0020 -BACKGROUND_BLUE: t.CDefine = 0x0010 -BACKGROUND_INTENSITY: t.CDefine = 0x0080 -BACKGROUND_WHITE: t.CDefine = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE +define BACKGROUND_RED = 0x0040 +define BACKGROUND_GREEN = 0x0020 +define BACKGROUND_BLUE = 0x0010 +define BACKGROUND_INTENSITY = 0x0080 +define BACKGROUND_WHITE = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE -@t.NoVTable -class Logger: +novirtual class Logger: _level: int _console_handle: win32base.HANDLE _use_color: int @@ -138,25 +132,17 @@ class Logger: return 0 def debug(self, msg: str, category: str = "") -> int: - return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, - FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category) + return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category) def info(self, msg: str, category: str = "") -> int: - ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, - FOREGROUND_GREEN, - FOREGROUND_GREEN | FOREGROUND_INTENSITY, category) + ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category) return ret def warning(self, msg: str, category: str = "") -> int: - return self._log(LogLevel.WARNING, "├", "WARN", msg, - FOREGROUND_YELLOW, - FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, - category) + return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, category) def error(self, msg: str, category: str = "") -> int: - ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, - FOREGROUND_RED, - FOREGROUND_RED | FOREGROUND_INTENSITY, category) + ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, FOREGROUND_RED | FOREGROUND_INTENSITY, category) # JIT/REPL 模式:诊断已打印,经统一出口跳回 REPL 循环(未武装时该出口仍终止进程) if self.jit_flag: repl_abort() @@ -166,16 +152,11 @@ class Logger: return ret def success(self, msg: str, category: str = "") -> int: - return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, - FOREGROUND_GREEN, - FOREGROUND_GREEN | FOREGROUND_INTENSITY, category) + return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category) def critical(self, msg: str, category: str = "") -> int: # CRITICAL 使用 ╠ 前缀(红底白字标签,对齐 TPC critical 风格) - return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, - FOREGROUND_RED, - BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY, - category) + return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY, category) def set_level(self, level: int) -> int: self._level = level @@ -232,8 +213,8 @@ class Logger: _mbuddy: memhub.MemBuddy* # 全局 logger 指针 -logger: Logger | t.CPtr -log: Logger | t.CPtr +logger: Logger* +log: Logger* def init_logger(): """初始化全局 logger 实例。"""