提交新的建议忽略

This commit is contained in:
TermiNexus committed 2026-08-06 18:08:09 +08:00
1 parent 8ad500c4da
commit f8436c74d2
14 files changed
+1004 -271

No files matched your search

+5
View File
@@ -23,3 +23,8 @@ __pycache__/
*.swp
*.swo
*~
# 编译
**/output/
**/temp/
**/.tpv_build/
+93 -2
View File
@@ -220,6 +220,9 @@ def _join_path(rel: str, project_dir: str) -> str:
- ../includes + Test → Test/../includes(OS 解析为 includes)
- 绝对路径保持不变
- None 返回 None
拼接结果经 _normalize_path 规范化:./Test/../includes → ./includes,
消除 .. 段,保证路径显示干净且相对寻址正确。
"""
if rel is None:
return None
@@ -240,7 +243,7 @@ def _join_path(rel: str, project_dir: str) -> str:
if buf is None:
return rel
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel)
return buf
return _normalize_path(buf)
# 处理 ./ 或 .\\ 前缀:拼接 project_dir + rel[2:]
rel_off: t.CSizeT = 0
@@ -261,7 +264,95 @@ def _join_path(rel: str, project_dir: str) -> str:
if buf is None:
return rel # 分配失败,返回原路径
viperlib.snprintf(buf, total_len, "%s/%s", project_dir, rel + rel_off)
return buf
return _normalize_path(buf)
def _normalize_path(path: str) -> str:
"""规范化路径:解析 . 和 .. 段,合并多余分隔符
例: ./test/BuddyTest/../../includes → ./includes
./test/BuddyTest/App → ./test/BuddyTest/App
绝对路径(/ 或 \\ 开头,含盘符 C:)保持不变。
返回 _mbuddy 分配的字符串;失败时返回原路径。
"""
if path is None:
return None
plen: t.CSizeT = string.strlen(path)
if plen == 0:
return path
# 绝对路径(/ 或 \ 开头,或盘符如 C:)不规范化
if path[0] == '/' or path[0] == '\\':
return path
if plen >= 2 and path[1] == ':':
return path
# 输出缓冲(最坏情况 = 输入长度 + 可能的前缀 ./)
out: str = _mbuddy.alloc(plen + 3)
if out is None:
return path
seg_start: t.CSizeT = 0
out_len: t.CSizeT = 0
has_seg: int = 0
i: t.CSizeT = 0
while i <= plen:
ch: t.CChar = '\0'
if i < plen:
ch = path[i]
if ch == '/' or ch == '\\' or ch == '\0':
seg_len: t.CSizeT = i - seg_start
if seg_len > 0:
# 单段 ".":跳过
if seg_len == 1 and path[seg_start] == '.':
pass
# 单段 "..":弹出上一段(无段可弹时保留,如 ../includes)
elif seg_len == 2 and path[seg_start] == '.' and path[seg_start + 1] == '.':
if out_len > 0:
# 回退到上一个分隔符
k: t.CSizeT = out_len
while k > 0 and out[k - 1] != '/' and out[k - 1] != '\\':
k -= 1
if k > 0:
out_len = k - 1
else:
out_len = 0
# 仍有剩余段时保持分隔符逻辑
if out_len > 0:
has_seg = 1
else:
has_seg = 0
else:
# 无段可弹:保留 ..
out[0] = '.'
out[1] = '.'
out_len = 2
has_seg = 1
else:
# 普通段:追加分隔符 + 段
if has_seg > 0:
out[out_len] = '/'
out_len += 1
jj: t.CSizeT = 0
while jj < seg_len:
out[out_len] = path[seg_start + jj]
out_len += 1
jj += 1
has_seg = 1
seg_start = i + 1
i += 1
if has_seg == 0:
# 全部段被消掉(如 ./..):返回当前目录
return "./"
# 相对路径补回 ./ 前缀(与原路径风格一致;以 .. 开头的除外)
if out[0] != '.' and out[0] != '/':
# 右移 2 位留出 "./"(从后往前拷贝,避免覆盖源字符)
mv: t.CSizeT = out_len
while mv > 0:
mv -= 1
out[mv + 2] = out[mv]
out[0] = '.'
out[1] = '/'
out_len += 2
out[out_len] = '\0'
return out
def resolve_paths(project_dir: str) -> int:
+5
View File
@@ -348,6 +348,11 @@ def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr,
if resolved_ty is not None:
param_ty = resolved_ty
llvmlite.add_param(pool, func, param_ty, pname)
# *args 变长参数:标记 IsVarArg=1,使调用侧走 C ABI 参数提升
# (i8/i16 → i32,float → double),避免 %d/%x 等格式因参数按 i64
# 传递而读错(第二个 %x 读到第一个 i64 槽的高 4 字节)。
if args_node.vararg is not None:
func.IsVarArg = 1
# 注册到函数表(用裸名 fd.name,不是 mangled_name)
if funcs_ptr is not None:
+2
View File
@@ -119,6 +119,8 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
tr._declare_only = declare_only
# 设置当前文件名(供报错使用)
HandlesType.set_current_file(file_path)
# 同步到 HandlesImports(import 时 SHA1 检查报错需要当前文件名,循环依赖无法直接读 HandlesType)
HandlesImports.set_current_file(file_path)
# 清空调用栈(每个文件翻译开始前重置)
HandlesType.clear_trace()
# 模块切换:清空 CDefine 常量表,确保每个模块的 CDefine 常量正确隔离
+5
View File
@@ -292,6 +292,11 @@ class AssignHandle(HandlesBase.Mixin):
# Attribute 赋值: obj.field = val
if tk == ast.ASTKind.Attribute:
# property setter 拦截: obj.attr = v 且 attr 是 @x.setter 方法时,
# 生成 call Class.attr$set(obj, v),跳过字段存储
if HandlesExprCall._try_property_setter(
builder, pool, mod, target, rhs_val, self.Trans) == 1:
continue
field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
builder, pool, mod, target, self.Trans)
if field_ptr is not None:
+35
View File
@@ -70,6 +70,41 @@ def translate_stmt(trans: HT.Translator | t.CPtr,
return translate_continue(trans)
elif k == ast.ASTKind.Match:
return trans.MatchH.Handle(node)
elif k == ast.ASTKind.Delete:
return translate_delete(trans, node)
return 0
# ============================================================
# 翻译 Delete 语句: del obj.attr
#
# 支持 property deleter: 当目标 attr 是 @x.deleter 方法时,
# 生成 call Class.attr$del(obj)。其余目标暂不生成代码。
# ============================================================
def translate_delete(trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译 del 语句,返回 0"""
if trans is None or node is None:
return 0
dnode: ast.Delete | t.CPtr = (ast.Delete | t.CPtr)(node)
if dnode is None or dnode.targets is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
if pool is None or mod is None or builder is None:
return 0
tn: t.CSizeT = dnode.targets.__len__()
for ti in range(tn):
tgt: ast.AST | t.CPtr = dnode.targets.get(ti)
if tgt is None:
continue
# del obj.attr → property deleter
if tgt.kind() == ast.ASTKind.Attribute:
del_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(tgt)
if del_at is not None:
HandlesExprCall._try_property_deleter(
pool, builder, mod, del_at, trans)
return 0
+318 -160
View File
@@ -224,6 +224,157 @@ def _has_decorator(decorator_list: list[ast.AST | t.CPtr] | t.CPtr,
return 0
# ============================================================
# 方法元数据(装饰器)支持
#
# 与 TPC 的 FuncMeta 对齐,按位掩码存储:
# META_STATIC @staticmethod 无 self 参数
# META_CLASS @classmethod cls 为首参(Ptr(struct_ty))
# META_PROP_GETTER @property / @x.getter obj.prop → call Class.prop(obj)
# META_PROP_SETTER @x.setter obj.prop = v → call Class.prop$set(obj, v)
# META_PROP_DELETER @x.deleter del obj.prop → call Class.prop$del(obj)
#
# setter/deleter 函数名带 $set/$del 后缀,避免与 getter 冲突(与 TPC 一致)。
# 元数据表用 stdlib 全局分配,跨模块持久(属性访问可能发生在其他模块)。
# 注册时机:_translate_oop_methods(含 mark_only=1 的 struct 预注册阶段),
# 确保在 App 源文件翻译前元数据已就绪(不依赖 includes 是否已全量翻译)。
# ============================================================
META_STATIC: t.CDefine = 1
META_CLASS: t.CDefine = 2
META_PROP_GETTER: t.CDefine = 4
META_PROP_SETTER: t.CDefine = 8
META_PROP_DELETER: t.CDefine = 16
META_MAX: t.CDefine = 512
META_KEY_LEN: t.CDefine = 128
_g_method_meta_keys: bytes = None
_g_method_meta_flags: bytes = None
_g_method_meta_count: int = 0
# ============================================================
# _extract_func_meta — 从 decorator_list 提取方法元数据位掩码
#
# 支持(与 TPC HandlesFunctions._ExtractFuncMeta 对齐):
# @staticmethod / @property / @classmethod(Name 形式)
# @property.setter / @x.setter / @x.getter / @x.deleter(Attribute 形式)
# ============================================================
def _extract_func_meta(decorator_list: list[ast.AST | t.CPtr] | t.CPtr) -> int:
"""从装饰器列表提取元数据位掩码,返回 0=无装饰器元数据"""
meta: int = 0
if decorator_list is None:
return meta
dn: t.CSizeT = decorator_list.__len__()
if dn == 0:
return meta
for di in range(dn):
deco: ast.AST | t.CPtr = decorator_list.get(di)
if deco is None:
continue
k: int = deco.kind()
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(deco)
if nm.id is not None:
if string.strcmp(nm.id, "staticmethod") == 0:
meta |= META_STATIC
elif string.strcmp(nm.id, "property") == 0:
meta |= META_PROP_GETTER
elif string.strcmp(nm.id, "classmethod") == 0:
meta |= META_CLASS
elif k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(deco)
if at.attr is not None and at.value is not None:
if at.value.kind() == ast.ASTKind.Name:
vn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
is_prop_name: int = 0
if vn.id is not None and string.strcmp(vn.id, "property") == 0:
is_prop_name = 1
# @property.setter 或 @x.setter/getter/deleter 都识别
if is_prop_name != 0 or string.strcmp(at.attr, "setter") == 0 \
or string.strcmp(at.attr, "getter") == 0 or string.strcmp(at.attr, "deleter") == 0:
if string.strcmp(at.attr, "setter") == 0:
meta |= META_PROP_SETTER
elif string.strcmp(at.attr, "getter") == 0:
meta |= META_PROP_GETTER
elif string.strcmp(at.attr, "deleter") == 0:
meta |= META_PROP_DELETER
return meta
# ============================================================
# register_method_meta — 注册方法元数据(ClassName.method → meta)
#
# 幂等:重复注册相同 key 时更新标志位。内存用 stdlib 全局分配。
# ============================================================
def register_method_meta(class_name: str, method_name: str,
meta: int) -> None:
"""注册方法元数据(幂等)"""
global _g_method_meta_keys, _g_method_meta_flags, _g_method_meta_count
if class_name is None or method_name is None or meta == 0:
return
# 懒初始化
if _g_method_meta_keys is None:
_g_method_meta_keys = stdlib.malloc(META_MAX * META_KEY_LEN)
_g_method_meta_flags = stdlib.malloc(META_MAX * 4)
if _g_method_meta_keys is None or _g_method_meta_flags is None:
return
string.memset(_g_method_meta_keys, 0, META_MAX * META_KEY_LEN)
string.memset(_g_method_meta_flags, 0, META_MAX * 4)
# 构造 key "ClassName.method"
cl_len: t.CSizeT = string.strlen(class_name)
mn_len: t.CSizeT = string.strlen(method_name)
key_buf: bytes = stdlib.malloc(cl_len + mn_len + 2)
if key_buf is None:
return
viperlib.snprintf(key_buf, cl_len + mn_len + 2, "%s.%s", class_name, method_name)
# 去重更新
i: int
for i in range(_g_method_meta_count):
if string.strcmp(_g_method_meta_keys + i * META_KEY_LEN, key_buf) == 0:
flags_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(
t.CUInt64T(_g_method_meta_flags) + i * 4, t.CPtr))
flags_ptr[0] = meta
stdlib.free(key_buf)
return
if _g_method_meta_count < META_MAX:
string.strcpy(_g_method_meta_keys + _g_method_meta_count * META_KEY_LEN, key_buf)
flags_ptr2: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(
t.CUInt64T(_g_method_meta_flags) + _g_method_meta_count * 4, t.CPtr))
flags_ptr2[0] = meta
_g_method_meta_count += 1
stdlib.free(key_buf)
# ============================================================
# lookup_method_meta — 查找方法元数据(ClassName.method → meta)
#
# 返回位掩码;未注册返回 0。跨模块持久,供属性访问/赋值/删除识别。
# ============================================================
def lookup_method_meta(class_name: str, method_name: str) -> int:
"""查找方法元数据位掩码,返回 0=未注册或非装饰器方法"""
if class_name is None or method_name is None:
return 0
if _g_method_meta_keys is None or _g_method_meta_count <= 0:
return 0
cl_len: t.CSizeT = string.strlen(class_name)
mn_len: t.CSizeT = string.strlen(method_name)
key_buf: bytes = stdlib.malloc(cl_len + mn_len + 2)
if key_buf is None:
return 0
viperlib.snprintf(key_buf, cl_len + mn_len + 2, "%s.%s", class_name, method_name)
found: int = 0
i: int
for i in range(_g_method_meta_count):
if string.strcmp(_g_method_meta_keys + i * META_KEY_LEN, key_buf) == 0:
flags_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(
t.CUInt64T(_g_method_meta_flags) + i * 4, t.CPtr))
found = flags_ptr[0]
break
stdlib.free(key_buf)
return found
# ============================================================
# _is_marker_base — 检查基类名是否为标记基类(非真实父类)
#
@@ -2261,9 +2412,6 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
if cd is None or cd.name is None:
return 0
stdio.printf("[VTDBG-STEP] class=%s translate_class_def enter (declare_only=%d)\n",
cd.name, (trans._declare_only if trans is not None else -1))
pool: memhub.MemBuddy | t.CPtr = trans.Pool
class_name: str = cd.name
@@ -2293,9 +2441,6 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
# 检查是否已注册(用 SHA1 区分跨模块同名类)
existing: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module(
class_name, trans.ModuleSha1)
stdio.printf("[VTDBG-STEP] class=%s find_struct_by_module returned\n", class_name)
stdio.printf("[VTDBG-STEP] class=%s existing_check done, entry=%p\n",
class_name, existing)
if existing is not None:
# BUG 5 方案1修改点2:existing 时字段完整性检查
# 结构体已注册,但需验证字段信息是否完整(防止 i32 回退残留或注册不完整)
@@ -2340,14 +2485,11 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
# 否则 find_struct_by_module("GSListNode[Value]") 返回 None
# ============================================================
_trigger_base_specialization(trans, cd)
stdio.printf("[VTDBG-STEP] class=%s trigger_base_spec done\n", class_name)
# ============================================================
# VTable 检测:判断是否启用虚表
# ============================================================
has_vtable: int = _detect_vtable_status(cd, trans)
stdio.printf("[VTDBG-STEP] class=%s vtable_status done, has_vtable=%d\n",
class_name, has_vtable)
parent_name: str = _get_parent_class(cd, trans)
is_novtable_deco: int = 0
if cd.decorator_list is not None:
@@ -2373,11 +2515,7 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
if parent_check is None:
parent_check = HandlesStruct.find_struct(parent_name)
if parent_check is None:
stdio.printf("[VTDBG-STEP] class=%s parent %s not registered, defer\n",
class_name, parent_name)
return 0
stdio.printf("[VTDBG-STEP] class=%s parent_check done, parent=%s\n",
class_name, parent_name)
# ============================================================
# 1. 遍历 class body,收集字段信息
@@ -2756,9 +2894,6 @@ def _format_func_type_str(pool: memhub.MemBuddy | t.CPtr,
if buf is None or func is None or buf_size == 0:
return 0
buf[0] = '\0'
stdio.printf("[VTDBG-FMT] enter func=%p\n", func)
stdio.fflush(None)
# 打印返回类型(使用访问器函数避免跨模块直接字段访问)
ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(func)
ret_buf: t.CChar | t.CPtr = pool.alloc(128)
@@ -2767,8 +2902,6 @@ def _format_func_type_str(pool: memhub.MemBuddy | t.CPtr,
llvmlite.TypePrint(ret_buf, 128, ret_ty, pool)
_vtable_strcat(buf, buf_size, ret_buf)
_vtable_strcat(buf, buf_size, " (")
stdio.printf("[VTDBG-FMT] ret done\n")
stdio.fflush(None)
# 遍历参数(使用访问器函数避免跨模块直接字段访问)
cur: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func)
@@ -2777,58 +2910,60 @@ def _format_func_type_str(pool: memhub.MemBuddy | t.CPtr,
_pc: int = 0
while cur is not None:
_pc += 1
stdio.printf("[VTDBG-FMT] p%d=%p\n", _pc, cur)
stdio.fflush(None)
if _pc > 100:
stdio.printf("[VTDBG-FUNC-LOOP] func=%p param_chain>100 stop\n", func)
break
if first == 0:
_vtable_strcat(buf, buf_size, ", ")
if param_buf is not None:
param_buf[0] = '\0'
stdio.printf("[VTDBG-FMT] p%d get_ty...\n", _pc)
stdio.fflush(None)
param_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur)
stdio.printf("[VTDBG-FMT] p%d ty=%p\n", _pc, param_ty)
stdio.fflush(None)
if param_ty is not None:
llvmlite.TypePrint(param_buf, 128, param_ty, pool)
stdio.printf("[VTDBG-FMT] p%d str=%s\n", _pc, param_buf)
stdio.fflush(None)
_vtable_strcat(buf, buf_size, param_buf)
stdio.printf("[VTDBG-FMT] p%d next...\n", _pc)
stdio.fflush(None)
cur = llvmlite.param_get_next(cur)
first = 0
_vtable_strcat(buf, buf_size, ")")
stdio.printf("[VTDBG-FMT] done\n")
stdio.fflush(None)
return 1
# _generate_vtable — 生成 VTable 全局变量
# ============================================================
# vtable 收集暂存区(模块级)
#
# 1. 收集 class body 中所有 FunctionDef 作为虚方法
# 2. 构造 VTable 类型 { i8*, i8*, ... }
# 3. 构造初始化字符串 { i8* bitcast (...), ... }
# 4. 创建全局变量 @"SHA1.ClassName.vtable"
# 5. 存储虚方法名列表到 StructEntry
def _generate_vtable(trans: HT.Translator | t.CPtr,
cd: ast.ClassDef | t.CPtr,
class_name: str) -> int:
"""生成 VTable 全局变量"""
# _collect_vtable_methods 的收集结果,跨函数传递:
# - _translate_oop_methods 在翻译方法体之前先收集一次,
# 使方法体内的 self.虚方法 调用能查到 VTableMethods(虚分派依赖)
# - _generate_vtable 再收集一次,生成 vtable 全局变量
# method_names_buf 是 stdlib 分配,存入 StructEntry.VTableMethods
# 后跨模块持久(不得迁入 mb 池 reset 区间)。
# ============================================================
_g_vt_full_names: t.CSizeT | t.CPtr = None
_g_vt_method_names: t.CSizeT | t.CPtr = None
_g_vt_parent_sha1s: t.CSizeT | t.CPtr = None
_g_vt_method_count: int = 0
# ============================================================
# _collect_vtable_methods — 收集类的虚方法信息
#
# 1. 继承父类虚方法(父类方法排在子类方法之前,保持槽位顺序一致)
# 2. 收集当前类虚方法(跳过已继承的)
#
# 结果存入模块级暂存区(_g_vt_*),并设置 entry.VTableMethodCount /
# VTableMethods(stdlib 分配,供虚分派跨模块查询)。
#
# 返回 1=有虚方法 / 0=无方法或失败
# ============================================================
def _collect_vtable_methods(trans: HT.Translator | t.CPtr,
cd: ast.ClassDef | t.CPtr,
class_name: str) -> int:
"""收集虚方法信息,设置 entry.VTableMethods/VTableMethodCount"""
global _g_vt_full_names, _g_vt_method_names, _g_vt_parent_sha1s, _g_vt_method_count
if trans is None or cd is None or class_name is None:
return 0
# 懒导入
import lib.core.Handles.HandlesFunctions as HandlesFunctions
import lib.core.Handles.HandlesExprCall as HandlesExprCall
pool: memhub.MemBuddy | t.CPtr = trans.Pool
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
stdio.printf("[VTDBG-ENTER] class=%s\n", class_name)
children: list[ast.AST | t.CPtr] | t.CPtr = cd.children
if children is None:
return 0
@@ -2871,10 +3006,6 @@ def _generate_vtable(trans: HT.Translator | t.CPtr,
parent_vt_count: int = 0
if vt_parent_entry is not None:
parent_vt_count = vt_parent_entry.VTableMethodCount
stdio.printf("[VTDBG-PARENT] class=%s parent=%s parent_entry=%p vt_count=%d\n",
class_name, parent_name, vt_parent_entry, parent_vt_count)
stdio.printf("[VTDBG-STEP] class=%s collect-parent done, method_count=%d\n",
class_name, method_count)
if parent_vt_count > 0:
for pvi in range(parent_vt_count):
if method_count >= VTBL_METHOD_MAX:
@@ -2978,13 +3109,57 @@ def _generate_vtable(trans: HT.Translator | t.CPtr,
method_count += 1
# 暂存结果(_generate_vtable 复用)
_g_vt_full_names = full_names_buf
_g_vt_method_names = method_names_buf
_g_vt_parent_sha1s = method_parent_sha1s
_g_vt_method_count = method_count
# 设置 entry 元数据(虚分派查询用;必须在本类方法体翻译前就绪,
# 否则方法体内 self.虚方法 调用无法虚分派)
if vt_self_entry is not None and method_count > 0:
vt_self_entry.VTableMethodCount = method_count
vt_self_entry.VTableMethods = method_names_buf
if method_count == 0:
stdio.printf("[VTDBG-STEP] class=%s collect done, method_count=0 -> return\n", class_name)
return 0
return 1
# _generate_vtable — 生成 VTable 全局变量
#
# 1. 收集 class body 中所有 FunctionDef 作为虚方法
# 2. 构造 VTable 类型 { i8*, i8*, ... }
# 3. 构造初始化字符串 { i8* bitcast (...), ... }
# 4. 创建全局变量 @"SHA1.ClassName.vtable"
# 5. 存储虚方法名列表到 StructEntry
def _generate_vtable(trans: HT.Translator | t.CPtr,
cd: ast.ClassDef | t.CPtr,
class_name: str) -> int:
"""生成 VTable 全局变量"""
if trans is None or cd is None or class_name is None:
return 0
# 懒导入
import lib.core.Handles.HandlesFunctions as HandlesFunctions
import lib.core.Handles.HandlesExprCall as HandlesExprCall
pool: memhub.MemBuddy | t.CPtr = trans.Pool
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
# 收集虚方法信息(继承父类 + 自身,结果存模块级暂存区 _g_vt_*)
# 同时设置 entry.VTableMethodCount/VTableMethods(虚分派查询用)
if _collect_vtable_methods(trans, cd, class_name) == 0:
return 0
full_names_buf: t.CSizeT | t.CPtr = _g_vt_full_names
method_names_buf: t.CSizeT | t.CPtr = _g_vt_method_names
method_parent_sha1s: t.CSizeT | t.CPtr = _g_vt_parent_sha1s
method_count: int = _g_vt_method_count
# 重新获取 entry(后续存储 VTableMethods 用)
vt_self_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module(
class_name, trans.ModuleSha1)
# 2. 构造 VTable 类型: { i8*, i8*, ..., i8* }
stdio.printf("[VTDBG-STEP] class=%s collect done, method_count=%d -> build vtable\n",
class_name, method_count)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
@@ -3009,8 +3184,6 @@ def _generate_vtable(trans: HT.Translator | t.CPtr,
if init_buf is None:
return 0
init_buf[0] = '\0'
stdio.printf("[VTDBG-STEP] class=%s build vtable-ty done, method_count=%d -> init str\n",
class_name, method_count)
_vtable_strcat(init_buf, 16384, "{ ")
for i in range(method_count):
@@ -3058,8 +3231,6 @@ def _generate_vtable(trans: HT.Translator | t.CPtr,
if func_valid == 0:
# 回退到全局表完整 entry 的 TypeStr(stdlib 深拷贝,跨文件永久存活)。
ge: HandlesExprCall.FuncEntry | t.CPtr = HandlesExprCall.find_func_entry_global(full_name)
stdio.printf("[VTDBG] vtable '%s' method '%s' func=%p valid=0 fallback_ge=%p\n",
class_name, full_name, func, ge)
if ge is not None and ge.TypeStr is not None:
_vtable_strcat(init_buf, 16384, "i8* bitcast (")
_vtable_strcat(init_buf, 16384, ge.TypeStr)
@@ -3080,14 +3251,9 @@ def _generate_vtable(trans: HT.Translator | t.CPtr,
if func_ty_buf is None:
_vtable_strcat(init_buf, 16384, "i8* null")
continue
stdio.printf("[VTDBG-STEP] class=%s method[%d] %s valid func=%p -> format_func_type\n",
class_name, i, full_name, func)
_format_func_type_str(pool, func_ty_buf, 512, func)
# 构造 bitcast 字符串: i8* bitcast (func_ty* @mangled to i8*)
stdio.printf("[VTDBG-STEP] class=%s method[%d] bitcast start, ftb=%p mn=%p\n",
class_name, i, func_ty_buf, mangled_name)
stdio.fflush(None)
_vtable_strcat(init_buf, 16384, "i8* bitcast (")
_vtable_strcat(init_buf, 16384, func_ty_buf)
_vtable_strcat(init_buf, 16384, "* @")
@@ -3099,13 +3265,8 @@ def _generate_vtable(trans: HT.Translator | t.CPtr,
else:
_vtable_strcat(init_buf, 16384, mangled_name)
_vtable_strcat(init_buf, 16384, " to i8*)")
stdio.printf("[VTDBG-STEP] class=%s method[%d] bitcast done\n", class_name, i)
stdio.fflush(None)
_vtable_strcat(init_buf, 16384, " }")
stdio.printf("[VTDBG-STEP] class=%s vtable init str done, len=%d\n",
class_name, string.strlen(init_buf))
stdio.fflush(None)
# 4. 构造 VTable 全局变量名
vtable_name_buf: t.CChar | t.CPtr = pool.alloc(128)
@@ -3113,16 +3274,10 @@ def _generate_vtable(trans: HT.Translator | t.CPtr,
return 0
viperlib.snprintf(vtable_name_buf, 128, "%s.vtable", class_name)
vtable_mangled: str = HandlesFunctions._mangle_name(trans, vtable_name_buf)
stdio.printf("[VTDBG-STEP] class=%s vtable name done, mn=%p\n",
class_name, vtable_mangled)
stdio.fflush(None)
# 5. 创建全局变量
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(
pool, vtable_mangled, vtable_ty)
stdio.printf("[VTDBG-STEP] class=%s new_global_variable done, gv=%p\n",
class_name, gv)
stdio.fflush(None)
if gv is None:
return 0
gv.Initializer = init_buf
@@ -3131,18 +3286,11 @@ def _generate_vtable(trans: HT.Translator | t.CPtr,
# 添加到模块
llvmlite.module_add_global(mod, gv)
stdio.printf("[VTDBG-STEP] class=%s module_add_global done\n", class_name)
stdio.fflush(None)
# 6. 存储虚方法名列表到 StructEntry(直接用 entry,规避跨模块同名 find_struct 找错)
stdio.printf("[VTDBG-STEP] class=%s store VTableMethods, entry=%p mnb=%p\n",
class_name, vt_self_entry, method_names_buf)
stdio.fflush(None)
if vt_self_entry is not None:
vt_self_entry.VTableMethods = method_names_buf
vt_self_entry.VTableMethodCount = method_count
stdio.printf("[VTDBG-STEP] class=%s VTableMethods stored\n", class_name)
stdio.fflush(None)
return 0
@@ -3189,7 +3337,7 @@ def _translate_oop_methods(trans: HT.Translator | t.CPtr,
# 提前用类型指针定位 entry(规避跨模块同名类 find_struct 找错)
oop_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty)
# 第一遍:检测是否有方法,标记 OOP
# 第一遍:检测是否有方法,标记 OOP,并注册装饰器元数据
for ci in range(cn):
stmt: ast.AST | t.CPtr = children.get(ci)
if stmt is None:
@@ -3202,6 +3350,12 @@ def _translate_oop_methods(trans: HT.Translator | t.CPtr,
has_init = 1
if string.strcmp(fd_check.name, "__new__") == 0:
has_new = 1
# 注册装饰器元数据(@property/@staticmethod/@classmethod/@x.setter 等)。
# 此处(含 mark_only=1 的 struct 预注册阶段)注册,
# 确保其他模块在 App 文件翻译时能识别属性访问。
fd_meta: int = _extract_func_meta(fd_check.decorator_list)
if fd_meta != 0:
register_method_meta(class_name, fd_check.name, fd_meta)
if has_method == 0:
# 子类没有自己的方法,但可能继承了父类的虚方法
@@ -3229,6 +3383,16 @@ def _translate_oop_methods(trans: HT.Translator | t.CPtr,
return 0
# 第二遍:翻译每个方法
# 翻译方法体前先预注册 vtable 元数据(VTableMethods/VTableMethodCount)。
# 根因:方法体翻译先于 _generate_vtable,若不预注册,方法体内的
# self.虚方法 调用(如 MemManager.calloc 里的 self.alloc)读不到
# VTableMethods,虚分派失败,被静态绑定到本类实现(基类 alloc 返回
# None),导致 calloc 永远返回 NULL。
oop_has_vt_pre: int = 0
if oop_entry is not None:
oop_has_vt_pre = oop_entry.HasVTable
if oop_has_vt_pre == 1:
_collect_vtable_methods(trans, cd, class_name)
for ci in range(cn):
stmt: ast.AST | t.CPtr = children.get(ci)
if stmt is None:
@@ -3236,8 +3400,6 @@ def _translate_oop_methods(trans: HT.Translator | t.CPtr,
if stmt.kind() == ast.ASTKind.FunctionDef:
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(stmt)
if fd is not None and fd.name is not None:
stdio.printf("[VTDBG-STEP] class=%s oop loop method: %s\n",
class_name, fd.name)
_translate_method(trans, fd, struct_ty, class_name)
# 如果有虚表,先生成 VTable 全局变量(设置 VTableMethodCount)
@@ -3257,7 +3419,6 @@ def _translate_oop_methods(trans: HT.Translator | t.CPtr,
if oop_entry is not None:
oop_entry.MethodsEmitted = 1
stdio.printf("[VTDBG-STEP] class=%s oop_methods done\n", class_name)
return 0
@@ -3296,8 +3457,26 @@ def _translate_method(trans: HT.Translator | t.CPtr,
self_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, struct_ty)
method_name: str = fd.name
stdio.printf("[VTDBG-STEP] class=%s translate_method enter: %s\n",
class_name, method_name)
# 提取装饰器元数据(@property/@staticmethod/@classmethod/@x.setter/@x.deleter)
func_meta: int = _extract_func_meta(fd.decorator_list)
# property setter/deleter 函数名带 $set/$del 后缀,避免与 getter 冲突
method_name_buf: t.CChar | t.CPtr = pool.alloc(128)
if method_name_buf is None:
return 0
viperlib.snprintf(method_name_buf, 128, "%s", fd.name)
if func_meta & META_PROP_SETTER != 0:
string.strcat(method_name_buf, "$set")
elif func_meta & META_PROP_DELETER != 0:
string.strcat(method_name_buf, "$del")
method_name = method_name_buf
is_static_m: int = 0
is_class_m: int = 0
if func_meta & META_STATIC != 0:
is_static_m = 1
if func_meta & META_CLASS != 0:
is_class_m = 1
# 构建方法全名: ClassName.method_name
full_name_buf: t.CChar | t.CPtr = pool.alloc(128)
@@ -3335,7 +3514,8 @@ def _translate_method(trans: HT.Translator | t.CPtr,
if func is None:
return 0
# 提取默认参数信息(方法的 args[0] 是 self,不含在 param_count 中)
# 提取默认参数信息(普通方法 args[0] 是 self,不含在 param_count 中;
# @staticmethod 无 self,param_count 为完整参数数)
# 注意: 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
md_defaults: t.CVoid | t.CPtr = None
@@ -3346,7 +3526,10 @@ def _translate_method(trans: HT.Translator | t.CPtr,
md_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(md_args_node)
if md_ags.args is not None:
md_alist: list[ast.AST | t.CPtr] | t.CPtr = md_ags.args
md_param_count = md_alist.__len__() - 1
if is_static_m != 0:
md_param_count = md_alist.__len__()
else:
md_param_count = md_alist.__len__() - 1
if md_ags.defaults is not None:
md_dlist: list[ast.AST | t.CPtr] | t.CPtr = md_ags.defaults
md_defaults = md_dlist
@@ -3359,17 +3542,32 @@ def _translate_method(trans: HT.Translator | t.CPtr,
pool) == 0:
trans._func_count = func_count + 1
# 添加 self 参数(Ptr(struct_ty))
llvmlite.add_param(pool, func, self_ptr_ty, "%self")
# 添加 self/cls 参数(Ptr(struct_ty));@staticmethod 无 self,跳过
# @classmethod 的 cls 也用 Ptr(struct_ty),首参名取 args[0](如 cls)
if is_static_m == 0:
first_pname: str = "self"
if is_class_m != 0 and fd.args is not None:
fd_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args)
if fd_ags.args is not None and fd_ags.args.__len__() > 0:
fd_first_arg: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(fd_ags.args.get(0))
if fd_first_arg is not None and fd_first_arg.arg is not None:
first_pname = fd_first_arg.arg
first_pname_buf: t.CChar | t.CPtr = pool.alloc(32)
if first_pname_buf is not None:
viperlib.snprintf(first_pname_buf, 32, "%%%s", first_pname)
llvmlite.add_param(pool, func, self_ptr_ty, first_pname_buf)
# 添加其他参数(支持类型注解,跳过索引 0 的 self 参数)
# 添加其他参数(支持类型注解;@staticmethod 从索引 0 开始,其余跳过索引 0 的 self/cls)
args_node: ast.Arguments | t.CPtr = fd.args
if args_node is not None:
ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
if ags.args is not None:
alist: list[ast.AST | t.CPtr] | t.CPtr = ags.args
an: t.CSizeT = alist.__len__()
for ai in range(1, an):
start_idx: int = 0
if is_static_m == 0:
start_idx = 1
for ai in range(start_idx, an):
arg: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist.get(ai))
if arg is not None and arg.arg is not None:
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
@@ -3400,24 +3598,41 @@ def _translate_method(trans: HT.Translator | t.CPtr,
# 进入函数作用域
HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION)
# 为 self 创建 alloca 并 store(与其他参数一致)
# 为 self/cls 创建 alloca 并 store(与其他参数一致);@staticmethod 无 self,跳过
# 修复: 之前 self 注册为 SSA 值导致 translate_name_value 错误地 load 一次
# 将 Ptr(struct_ty) 变成 struct 值,引发变参函数 ABI 不匹配崩溃
self_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, self_ptr_ty)
if self_alloca is not None:
HandlesVar.define_var(trans.SymTab, "self", self_alloca)
# 设置 self 的类型注解类名(属性访问 lookup_field 回退查找用)
HandlesVar.set_var_annot_class_name(trans.SymTab, "self", class_name)
self_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, self_ptr_ty, "%self")
llvmlite.build_store(func_builder, self_val, self_alloca)
if is_static_m == 0:
self_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, self_ptr_ty)
if self_alloca is not None:
self_var_name: str = "self"
self_ssa_name: str = "%self"
if is_class_m != 0 and args_node is not None:
cags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
if cags.args is not None and cags.args.__len__() > 0:
cfirst: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(cags.args.get(0))
if cfirst is not None and cfirst.arg is not None:
self_var_name = cfirst.arg
self_ssa_name_buf: t.CChar | t.CPtr = pool.alloc(32)
if self_ssa_name_buf is not None:
viperlib.snprintf(self_ssa_name_buf, 32, "%%%s", cfirst.arg)
self_ssa_name = self_ssa_name_buf
HandlesVar.define_var(trans.SymTab, self_var_name, self_alloca)
# 设置 self/cls 的类型注解类名(属性访问 lookup_field 回退查找用)
HandlesVar.set_var_annot_class_name(trans.SymTab, self_var_name, class_name)
self_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, self_ptr_ty, self_ssa_name)
llvmlite.build_store(func_builder, self_val, self_alloca)
# 为其他参数创建 alloca 并 store(与普通函数一致,跳过索引 0 的 self 参数)
# 为其他参数创建 alloca 并 store(与普通函数一致;
# @staticmethod 从索引 0 开始,其余跳过索引 0 的 self/cls 参数)
if args_node is not None:
ags2: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
if ags2.args is not None:
alist2: list[ast.AST | t.CPtr] | t.CPtr = ags2.args
an2: t.CSizeT = alist2.__len__()
for ai2 in range(1, an2):
start_idx2: int = 0
if is_static_m == 0:
start_idx2 = 1
for ai2 in range(start_idx2, an2):
arg2: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist2.get(ai2))
if arg2 is not None and arg2.arg is not None:
param_ty2: llvmlite.LLVMType | t.CPtr = i32_ty
@@ -3537,27 +3752,18 @@ def _generate_before_init(trans: HT.Translator | t.CPtr,
if trans is None or struct_ty is None or class_name is None:
return 0
stdio.printf("[VTDBG-STEP] class=%s -> before_init\n", class_name)
stdio.fflush(None)
import lib.core.Handles.HandlesFunctions as HandlesFunctions
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
stdio.printf("[BI-DBG] step1 pool/mod/funcs ok, func_count=%d\n", trans._func_count)
stdio.fflush(None)
pool: memhub.MemBuddy | t.CPtr = trans.Pool
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
funcs_ptr: HandlesExprCall.FuncEntry | t.CPtr = trans._funcs
func_count: int = trans._func_count
stdio.printf("[BI-DBG] step2 pool=%p mod=%p funcs=%p ok\n", pool, mod, funcs_ptr)
stdio.fflush(None)
self_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, struct_ty)
void_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool)
stdio.printf("[BI-DBG] step3 Ptr/Void ty ok self_ptr=%p\n", self_ptr_ty)
stdio.fflush(None)
# 构建函数名: ClassName.__before_init__
full_name_buf: t.CChar | t.CPtr = pool.alloc(128)
@@ -3565,82 +3771,50 @@ def _generate_before_init(trans: HT.Translator | t.CPtr,
return 0
viperlib.snprintf(full_name_buf, 128, "%s.__before_init__", class_name)
full_name: str = full_name_buf
stdio.printf("[BI-DBG] step4 full_name=%s\n", full_name)
stdio.fflush(None)
mangled_name: str = HandlesFunctions._mangle_name(trans, full_name)
stdio.printf("[BI-DBG] step5 mangled=%s\n", mangled_name)
stdio.fflush(None)
# 创建函数
func: llvmlite.Function | t.CPtr = llvmlite.create_function(
pool, mod, mangled_name, void_ty)
stdio.printf("[BI-DBG] step6 create_function done func=%p\n", func)
stdio.fflush(None)
if func is None:
return 0
# 注册到函数表
max_funcs: int = 256
rc_add: int = HandlesExprCall.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs, None, 0, 0, pool)
stdio.printf("[BI-DBG] step7 add_func_to_table rc=%d\n", rc_add)
stdio.fflush(None)
if rc_add == 0:
trans._func_count = func_count + 1
# 添加 self 参数
llvmlite.add_param(pool, func, self_ptr_ty, "%self")
stdio.printf("[BI-DBG] step8 add_param done\n")
stdio.fflush(None)
# 参数添加完成,补全全局函数表的 TypeStr
rc_ut: int = HandlesExprCall.update_global_func_typestr(pool, full_name, func)
stdio.printf("[BI-DBG] step9 update_global_func_typestr rc=%d\n", rc_ut)
stdio.fflush(None)
# 创建 entry 块 + builder
entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, "entry")
stdio.printf("[BI-DBG] step10 create_block entry=%p\n", entry_blk)
stdio.fflush(None)
if entry_blk is None:
return 0
func_builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, func)
stdio.printf("[BI-DBG] step11 new_builder builder=%p\n", func_builder)
stdio.fflush(None)
if func_builder is None:
return 0
llvmlite.position_at_end(func_builder, entry_blk)
# 注册 self 为 SSA 值
self_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, self_ptr_ty, "%self")
stdio.printf("[BI-DBG] step12 SSAValue self_val=%p\n", self_val)
stdio.fflush(None)
# 1. 零值填充: store zeroinitializer, Ptr(struct_ty)* %self
zero: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, struct_ty)
stdio.printf("[BI-DBG] step13 ConstZero zero=%p\n", zero)
stdio.fflush(None)
if zero is not None:
llvmlite.build_store(func_builder, zero, self_val)
stdio.printf("[BI-DBG] step14 build_store zero done\n")
stdio.fflush(None)
# 1.5 如果有虚表,store vtable 全局地址到 __vtable__ 字段(索引 0)
# 用 find_struct_by_type 定位 entry,规避跨模块同名 find_struct 找错
stdio.printf("[BI-DBG] step15 before find_struct_by_type, struct_ty=%p\n", struct_ty)
stdio.fflush(None)
bi_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty)
stdio.printf("[BI-DBG] step16 find_struct_by_type done, bi_entry=%p\n", bi_entry)
stdio.fflush(None)
bi_has_vt: int = 0
if bi_entry is not None:
stdio.printf("[BI-DBG] step17 read bi_entry->HasVTable\n", None)
stdio.fflush(None)
bi_has_vt = bi_entry.HasVTable
stdio.printf("[VTDBG-STEP] class=%s before_init: has_vt=%d vt_count=%d field_count=%d\n",
class_name, bi_has_vt,
(bi_entry.VTableMethodCount if bi_entry is not None else -1),
(bi_entry.FieldCount if bi_entry is not None else -1))
if bi_has_vt == 1:
vt_method_count: int = 0
if bi_entry is not None:
@@ -3699,31 +3873,16 @@ def _generate_before_init(trans: HT.Translator | t.CPtr,
# 2. 逐字段 store 默认值(用 bi_entry 直接访问,规避跨模块同名 find_struct 找错)
if bi_entry is not None:
stdio.printf("[VTDBG-STEP] class=%s before_init: store-defaults loop start, field_count=%d\n",
class_name, bi_entry.FieldCount)
for fi in range(bi_entry.FieldCount):
fe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry(
bi_entry, fi)
stdio.printf("[VTDBG-FIELD] class=%s fi=%d fe=%p index=%d default=%p ty=%p\n",
class_name, fi, fe,
(fe.Index if fe is not None else -1),
(fe.DefaultVal if fe is not None else None),
(fe.Ty if fe is not None else None))
if fe is None or fe.DefaultVal is None:
continue
# 打印 DefaultVal 的 AST kind(定位悬空/垃圾节点)
dk: int = fe.DefaultVal.kind()
stdio.printf("[VTDBG-FIELD] class=%s fi=%d translate default kind=%d\n",
class_name, fi, dk)
# 翻译默认值表达式
default_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
func_builder, pool, mod, fe.DefaultVal, None, 0, trans)
if default_val is None:
stdio.printf("[VTDBG-FIELD] class=%s fi=%d translate default -> None\n",
class_name, fi)
continue
stdio.printf("[VTDBG-STEP] class=%s before_init: field[%d] default translated\n",
class_name, fi)
# GEP 到字段
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
func_builder, struct_ty, fe.Ty, self_val, fe.Index)
@@ -3734,7 +3893,6 @@ def _generate_before_init(trans: HT.Translator | t.CPtr,
llvmlite.build_store(func_builder, default_val, field_ptr)
# 3. ret void
stdio.printf("[VTDBG-STEP] class=%s before_init: done, ret void\n", class_name)
llvmlite.build_ret_void(func_builder)
return 0
+33
View File
@@ -262,6 +262,15 @@ def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr,
"""
if val is None or target_ty is None:
return val
# 函数 → 指针: bitcast(如 CreateThread(..., _thread_alloc_free, ...) 传函数指针)
# 函数 Value 的 Ty 是 Func,既不是整数也不是指针,需在整数/浮点转换之前处理
match val.Ty:
case llvmlite.LLVMType.Func(_, _, _):
if is_ptr_type(target_ty) != 0:
return llvmlite.build_bitcast(builder, val, target_ty)
return val
case _:
pass
# 整数转换
val_bits: int = get_llvm_type_bits(val.Ty)
target_bits: int = get_llvm_type_bits(target_ty)
@@ -1903,6 +1912,13 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
load_ty = alloca.Ty.Pointee
if load_ty is None:
load_ty = llvmlite.Int32(pool)
# 结构体值变量(alloca 类型为 Struct,如 `x: Cls | t.CPtr`):
# 按"对象即指针"语义返回其地址(alloca 指针),而非加载结构体值。
# 结构体值无法参与 ptrtoint/方法调用等地址相关操作,加载值会导致
# `ptrtoint %Struct ... to i64` 无效指令(llc invalid cast opcode)。
match load_ty:
case llvmlite.LLVMType.Struct(_, _, _):
return alloca
return llvmlite.build_load(builder, load_ty, alloca)
# 模块别名检查:SymTab 查找失败后才检查
@@ -1912,6 +1928,15 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
if HandlesImports.is_module_imported(trans._imported_modules, nm_id) != 0:
return None
# 模块级函数引用:函数名作为值传递(如 CreateThread(..., _thread_alloc_free, ...))。
# 之前缺失此查找导致函数名参数翻译返回 None 被跳过,后续参数整体前移
# (lpStartAddress 变成对象地址,线程从数据区执行直接崩溃)。
if trans is not None and trans.Module is not None:
fn_ref: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(
trans.Module, nm_id)
if fn_ref is not None:
return fn_ref
# 所有查找路径均失败
# fail-fast: 仅对符合 CDefine 命名约定(ALL_CAPS)的名字报错
# 避免对 match/self/pool 等关键字和变量名误报
@@ -2839,6 +2864,14 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
builder, struct_ty, alias_fe.Ty, obj_ptr, alias_idx)
if alias_ptr is not None:
return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr)
# property getter 回退: obj.attr 是 @property/@x.getter 方法时生成调用
# (字段未找到时先尝试,避免报误导性的"字段未找到"错误)
if field_info is None and at.value is not None and trans is not None:
prop_cls_fb: str = HandlesStruct.get_class_name_by_type(pool, struct_ty)
prop_val: llvmlite.Value | t.CPtr = HandlesExprCall._try_property_getter_attr(
pool, builder, mod, at, obj_ptr, trans, prop_cls_fb)
if prop_val is not None:
return prop_val
# 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
if field_info is None:
+342
View File
@@ -2894,6 +2894,35 @@ def _call_method_on_ptr(pool: memhub.MemBuddy | t.CPtr,
total_count += 1
param_node = llvmlite.param_get_next(param_node)
# 参数类型强制转换:按 found_func 的形参类型 coerce 每个实参(跳过 self/cls 参数 0)
# 修复: 构造器调用 __init__(ptr, base, 4096) 时 size 形参是 i64 (t.CSizeT),
# 但 4096 字面量按 i32 传递,未提升导致运行时 size 高 32 位垃圾,
# 破坏伙伴分配器的池大小元数据(如 total=2048)引发 calloc 失败/OOM。
if found_func is not None and head is not None:
pc_param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
# 跳过 self 参数(索引 0)
if pc_param is not None:
pc_param = llvmlite.param_get_next(pc_param)
pc_node: llvmlite.Value | t.CPtr = head.Next
new_tail: llvmlite.Value | t.CPtr = head
if head is not None:
head.Next = None
while pc_node is not None:
pc_next_save: llvmlite.Value | t.CPtr = pc_node.Next
coerced_pc: llvmlite.Value | t.CPtr = pc_node
if pc_param is not None:
pty_pc: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(pc_param)
if pty_pc is not None:
c_pc: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(
builder, pc_node, pty_pc, None)
if c_pc is not None:
coerced_pc = c_pc
pc_param = llvmlite.param_get_next(pc_param)
coerced_pc.Next = None
new_tail.Next = coerced_pc
new_tail = coerced_pc
pc_node = pc_next_save
# 虚方法分发:如果类有虚表且该方法在虚表中,走间接调用
# 复用前面已获取的 cmop_entry(find_struct_by_type 规避跨模块同名找错)
if found_func is not None:
@@ -2950,6 +2979,11 @@ def _ensure_method_declare(pool: memhub.MemBuddy | t.CPtr,
if string.strcmp(existing.Name, call_name) == 0:
# 已存在:仅当是 declare 且参数数量不足时追加缺失参数
if existing.IsDeclared == 1:
# 变长函数(*args,IsVarArg=1)不追加参数:多余实参由
# 变参机制(...)承接,追加有类型参数会导致调用侧把变参
# 强转为追加的类型(如 i8 → i64),%d/%x 等格式读错。
if existing.IsVarArg == 1:
return
# 遍历现有参数链表计数(避免依赖 GSList.Count 字段的类型推断)
cur_count: int = 0
cur_p: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(existing)
@@ -3329,6 +3363,296 @@ def _LookupMethodRetTyFromTextLL(pool: memhub.MemBuddy | t.CPtr,
return result_ty
# ============================================================
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
#
# 元数据表由 HandlesClassDef 在类翻译/预注册阶段填充,
# 这里负责把 obj.prop / obj.prop=v / del obj.prop / Class.method()
# 翻译为对应的 {sha1}.ClassName.prop 系列函数调用。
# ============================================================
# ============================================================
# _translate_property_call — 翻译属性方法调用
#
# 与 _translate_method_call 的调用名/返回类型解析逻辑一致:
# 1. 优先用模块中已有的函数(精确/后缀匹配,来自 stub 注入)
# 2. 否则用类所属模块的 SHA1 构造 "{sha1}.ClassName.method" 调用名
# 3. 返回类型:函数实类型 → text.ll 查找 → 按方法名推断(默认 i32)
#
# call_head/call_count: 已构建好的参数链表(如 getter 只有 obj 一个参数)
# ============================================================
def _translate_property_call(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
class_name: str,
method_name: str,
call_head: llvmlite.Value | t.CPtr,
call_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译属性/静态方法调用,返回 call 结果 Value"""
if pool is None or builder is None or class_name is None or method_name is None:
return None
# 构造查找名: ClassName.method_name
lookup_name: t.CChar | t.CPtr = pool.alloc(128)
if lookup_name is None:
return None
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
# 查找函数(精确/后缀匹配)
found_func: llvmlite.Function | t.CPtr = find_func_in_module(mod, lookup_name)
# 确定调用名
call_name: str = lookup_name
if found_func is not None:
found_name: t.CChar | t.CPtr = llvmlite.function_get_name(found_func)
if found_name is not None:
call_name = found_name
else:
# 未找到:用类所属模块的 SHA1 构造调用名
cls_sha1_pc: str = HandlesStruct.get_struct_sha1(class_name)
if cls_sha1_pc is not None:
mlen_pc: t.CSizeT = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
mangled_pc: str = pool.alloc(mlen_pc)
if mangled_pc is not None:
viperlib.snprintf(mangled_pc, mlen_pc, "%s.%s", cls_sha1_pc, lookup_name)
call_name = mangled_pc
# 确定返回类型
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
call_ret_ty: llvmlite.LLVMType | t.CPtr = i32_ty
if found_func is not None:
frt: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func)
if frt is not None:
call_ret_ty = frt
else:
cls_sha1_rt: str = HandlesStruct.get_struct_sha1(class_name)
looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL(
pool, cls_sha1_rt, class_name, method_name)
if looked_up_ty is not None:
call_ret_ty = looked_up_ty
else:
call_ret_ty = _infer_method_ret_ty(pool, method_name)
# 确保 declare 存在,然后调用
_ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count)
return llvmlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
# ============================================================
# _resolve_obj_class_name — 解析对象表达式的类名(短名)
#
# 优先从变量注解(AnnotClassName)解析(如 "memhub.MemBuddy" → "MemBuddy"),
# 失败时从 LLVM 类型解析(Ptr(Struct) → 类名)。
# 供属性访问/赋值/删除识别 property 时使用。
# ============================================================
def _resolve_obj_class_name(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
obj_node: ast.AST | t.CPtr) -> str:
"""解析对象表达式的类名(短名),失败返回 None"""
if pool is None or trans is None or obj_node is None:
return None
if obj_node.kind() == ast.ASTKind.Name:
obj_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(obj_node)
if obj_nm.id is not None:
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, obj_nm.id)
if var_entry is not None and var_entry.AnnotClassName is not None:
annot_val: str = var_entry.AnnotClassName
dot_ptr: t.CChar | t.CPtr = string.strrchr(annot_val, '.')
if dot_ptr is not None:
return dot_ptr + 1
return annot_val
return None
# ============================================================
# _try_property_getter_attr — 尝试将 obj.attr 翻译为 property getter 调用
#
# 检查 obj 的类是否有 @property/@x.getter 的 attr 方法;
# 若有则生成 call Class.attr(obj) 并返回调用结果 Value;否则返回 None。
# ============================================================
def _try_property_getter_attr(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
at_node: ast.Attribute | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr,
trans: HT.Translator | t.CPtr,
fallback_class_name: str = None) -> llvmlite.Value | t.CPtr:
"""尝试 property getter 属性访问,返回调用结果 Value;非 getter 返回 None
fallback_class_name: 对象表达式非 Name 时从 LLVM 类型解析出的类名兜底。
"""
if at_node is None or at_node.attr is None or obj_ptr is None or trans is None:
return None
class_name: str = _resolve_obj_class_name(pool, trans, at_node.value)
if class_name is None:
class_name = fallback_class_name
if class_name is None:
return None
meta: int = HandlesClassDef.lookup_method_meta(class_name, at_node.attr)
if meta & HandlesClassDef.META_PROP_GETTER == 0:
return None
# 单参数调用: obj → getter
llvmlite.value_set_next(obj_ptr, None)
return _translate_property_call(
pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans)
# ============================================================
# _try_property_setter — 尝试将 obj.attr = v 翻译为 property setter 调用
#
# 检查 obj 的类是否有 @x.setter 的 attr 方法;
# 若有则生成 call Class.attr$set(obj, v),返回 1;否则返回 0。
# ============================================================
def _try_property_setter(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
target_attr: ast.Attribute | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
"""尝试 property setter 赋值,返回 1=已处理(生成调用)"""
if pool is None or builder is None or mod is None or trans is None:
return 0
if target_attr is None or target_attr.attr is None or rhs_val is None:
return 0
class_name: str = _resolve_obj_class_name(pool, trans, target_attr.value)
if class_name is None:
return 0
meta: int = HandlesClassDef.lookup_method_meta(class_name, target_attr.attr)
if meta & HandlesClassDef.META_PROP_SETTER == 0:
return 0
# 翻译对象值(self 参数)
obj_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, target_attr.value, None, 0, trans)
if obj_val is None:
return 0
# 构建参数链表: obj → rhs
llvmlite.value_set_next(obj_val, None)
llvmlite.value_set_next(rhs_val, None)
llvmlite.value_set_next(obj_val, rhs_val)
# setter 函数名带 $set 后缀
setter_name_buf: t.CChar | t.CPtr = pool.alloc(128)
if setter_name_buf is None:
return 0
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
_translate_property_call(pool, builder, mod, class_name,
setter_name_buf, obj_val, 2, trans)
return 1
# ============================================================
# _try_property_deleter — 尝试将 del obj.attr 翻译为 property deleter 调用
#
# 检查 obj 的类是否有 @x.deleter 的 attr 方法;
# 若有则生成 call Class.attr$del(obj),返回 1;否则返回 0。
# ============================================================
def _try_property_deleter(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
target_attr: ast.Attribute | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
"""尝试 property deleter 删除,返回 1=已处理(生成调用)"""
if pool is None or builder is None or mod is None or trans is None:
return 0
if target_attr is None or target_attr.attr is None:
return 0
class_name: str = _resolve_obj_class_name(pool, trans, target_attr.value)
if class_name is None:
return 0
meta: int = HandlesClassDef.lookup_method_meta(class_name, target_attr.attr)
if meta & HandlesClassDef.META_PROP_DELETER == 0:
return 0
# 翻译对象值(self 参数)
obj_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, target_attr.value, None, 0, trans)
if obj_val is None:
return 0
llvmlite.value_set_next(obj_val, None)
# deleter 函数名带 $del 后缀
del_name_buf: t.CChar | t.CPtr = pool.alloc(128)
if del_name_buf is None:
return 0
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
_translate_property_call(pool, builder, mod, class_name,
del_name_buf, obj_val, 1, trans)
return 1
# ============================================================
# _translate_static_class_call — 翻译 ClassName.method(args) 静态/类方法调用
#
# @staticmethod: 直接 call Class.method(args...),无 self
# @classmethod: 在参数前插入栈上分配的类实例指针(与 TPC 一致)
# ============================================================
def _translate_static_class_call(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
class_name: str,
method_name: str,
cl: ast.Call | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译类名静态/类方法调用,返回 call 结果 Value;非静态/类方法返回 None"""
if pool is None or builder is None or mod is None or trans is None:
return None
if class_name is None or method_name is None or cl is None:
return None
meta: int = HandlesClassDef.lookup_method_meta(class_name, method_name)
is_static: int = 0
is_class: int = 0
if meta & HandlesClassDef.META_STATIC != 0:
is_static = 1
if meta & HandlesClassDef.META_CLASS != 0:
is_class = 1
if is_static == 0 and is_class == 0:
return None
# 构建参数链表
head: llvmlite.Value | t.CPtr = None
total_count: int = 0
# @classmethod: 插入栈上分配的类实例指针作为 cls 参数
if is_class != 0:
struct_ty_cc: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(class_name)
if struct_ty_cc is None:
return None
cls_ptr: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty_cc)
if cls_ptr is None:
return None
llvmlite.value_set_next(cls_ptr, None)
head = cls_ptr
total_count = 1
# 翻译调用参数
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
can: t.CSizeT = 0
if cargs is not None:
can = cargs.__len__()
tail: llvmlite.Value | t.CPtr = head
for ai in range(can):
arg_node: ast.AST | t.CPtr = cargs.get(ai)
if arg_node is None:
continue
arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, arg_node, None, 0, trans)
if arg_val is None:
continue
llvmlite.value_set_next(arg_val, None)
if head is None:
head = arg_val
tail = arg_val
else:
llvmlite.value_set_next(tail, arg_val)
tail = arg_val
total_count += 1
if head is None:
return None
return _translate_property_call(pool, builder, mod, class_name,
method_name, head, total_count, trans)
# ============================================================
# _translate_method_call - 翻译方法调用 obj.method(args)
#
@@ -4445,6 +4769,24 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
i64_ty_ln2: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
return llvmlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0)
# 检测类名静态/类方法调用: ClassName.method(args)
# 当 cl.func 是 Attribute、value 是已注册结构体类名(非普通变量)时,
# 且方法带 @staticmethod/@classmethod 元数据,走静态调用路径
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
st_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if st_at is not None and st_at.value is not None and trans is not None:
if st_at.value.kind() == ast.ASTKind.Name:
st_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(st_at.value)
if st_nm.id is not None:
# 仅当该名称不是普通变量且是已注册结构体类名时尝试静态/类方法
st_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, st_nm.id)
if st_var is None and HandlesStruct.get_struct_type(st_nm.id) is not None:
st_res: llvmlite.Value | t.CPtr = _translate_static_class_call(
pool, builder, mod, st_nm.id, func_name, cl, trans)
if st_res is not None:
return st_res
# 检测方法调用: obj.method(args)
# 当 cl.func 是 Attribute 且 obj 是已注册的结构体变量时,翻译为方法调用
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
+90
View File
@@ -6,9 +6,12 @@ import string
import stdlib
import viperlib
import stdio
import sys
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.StubMerger as StubMerger
# ============================================================
@@ -468,6 +471,85 @@ def compute_package_from_relpath(pool: memhub.MemBuddy | t.CPtr,
return buf
# ============================================================
# 全局当前文件名(镜像 HandlesType._g_current_file)
#
# HandlesImports 因循环依赖(HandlesType import 了本模块)不能
# import HandlesType,无法读取其 _g_current_file。因此由
# BuildPipeline/Phase1 在调用 HandlesType.set_current_file 时
# 同步调用本模块的 set_current_file,供导入时 SHA1 检查报错使用。
# ============================================================
_g_current_file: str = None
def set_current_file(file_path: str) -> None:
"""设置当前正在编译的文件名(与 HandlesType.set_current_file 同步调用)"""
global _g_current_file
_g_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(node: ast.AST | t.CPtr, 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 = _g_current_file
if file_name is None:
file_name = "<unknown>"
stdio.printf(' File "%s", line %d\n', file_name, lineno)
stdio.printf("ImportError: %s\n", msg)
sys.exit(1)
# ============================================================
# _check_import_sha1 - 导入时校验模块 SHA1 是否已注册(fast-fail)
#
# 背景:若 includes 路径配置错误(如 project.json 指向不存在的
# ./includes),includes 扫描结果为 0,模块不会注册 SHA1。此前
# 错误延迟到实际调用 module.func() 时才报误导性的
# "跨模块调用 'bd.alloc' 找不到模块 SHA1"。
# 现在在 import 语句处立即检测并报错,快速定位配置问题。
#
# t/c 是内部库,不参与 includes 扫描,豁免检查。
# ============================================================
def _check_import_sha1(node: ast.AST | t.CPtr, 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 扫描,豁免检查
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0:
return 1
mod_sha1: str = StubMerger.LookupSha1ByModName(mod_name)
if mod_sha1 is None:
err_buf: t.CChar | t.CPtr = stdlib.malloc(256)
if err_buf is not None:
viperlib.snprintf(err_buf, 256,
"模块 '%s' 未注册 SHA1(includes 扫描未找到该模块,请检查 project.json 的 includes 路径配置)",
mod_name)
_report_import_error(node, err_buf)
else:
_report_import_error(node, "导入模块 SHA1 未注册(includes 扫描未找到该模块)")
return 1
# ============================================================
# ImportsHandle - 导入语句处理器(Mixin 继承模式)
#
@@ -495,6 +577,11 @@ class ImportsHandle(HandlesBase.Mixin):
for ni in range(nn):
alias: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(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 self.Trans._declare_only == 0:
_check_import_sha1(node, alias.name)
pool_val: memhub.MemBuddy | t.CPtr = self.Trans.Pool
im_val: str = self.Trans._imported_modules
self.Trans._imported_modules = add_imported_module(
@@ -540,6 +627,9 @@ class ImportsHandle(HandlesBase.Mixin):
self.Trans.Pool, self.Trans.CurrentPackage,
impf.level, impf.module)
if resolved is not None:
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
if self.Trans._declare_only == 0:
_check_import_sha1(node, resolved)
self.Trans._imported_modules = add_imported_module(
self.Trans.Pool, self.Trans._imported_modules, resolved)
return 0
-6
View File
@@ -87,12 +87,6 @@ def translate_children(trans: HT.Translator | t.CPtr,
child: ast.AST | t.CPtr = ch.get(ci)
if child is None: continue
kd: int = child.kind()
if kd == ast.ASTKind.ClassDef:
stdio.printf("[MAIN-DBG] module child[%d] kind=%d ClassDef\n", ci, kd)
elif kd == ast.ASTKind.FunctionDef:
stdio.printf("[MAIN-DBG] module child[%d] kind=%d FunctionDef\n", ci, kd)
elif kd == ast.ASTKind.AnnAssign:
stdio.printf("[MAIN-DBG] module child[%d] kind=%d AnnAssign\n", ci, kd)
# _declare_only == 0 时,_translate_module_level 已预处理导入语句,
# 此处跳过避免重复处理(from_imports 条目重复)
-34
View File
@@ -232,16 +232,10 @@ class Translator:
pool: memhub.MemBuddy | t.CPtr = _mbuddy
# 初始化状态
stdio.printf("[TRDBG] translate enter, declare_only=%d\n", self._declare_only)
stdio.fflush(None)
self._init_state(pool)
stdio.printf("[TRDBG] after _init_state, funcs=%p\n", self._funcs)
stdio.fflush(None)
# 创建 LLVM 模块
mod: llvmlite.LLVMModule | t.CPtr = llvmlite.new_module(pool, "main")
stdio.printf("[TRDBG] after new_module, mod=%p\n", mod)
stdio.fflush(None)
if mod is None:
VLogger.error("NewModule returned NULL", "TR")
return 1
@@ -252,21 +246,15 @@ class Translator:
if triple is None:
triple = "x86_64-pc-windows-msvc"
llvmlite.module_set_target(mod, triple)
stdio.printf("[TRDBG] after module_set_target\n")
stdio.fflush(None)
# 类型
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
stdio.printf("[TRDBG] after types i32=%p i8=%p i8ptr=%p\n", i32_ty, i8_ty, i8_ptr_ty)
stdio.fflush(None)
# 声明 printf
printf_func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, "printf", i32_ty)
stdio.printf("[TRDBG] after create_declare printf=%p\n", printf_func)
stdio.fflush(None)
if printf_func is None:
VLogger.error("CreateDeclare printf returned NULL", "TR")
return 1
@@ -285,8 +273,6 @@ class Translator:
pool, mod, "llvm.va_end", void_ty_decl)
if va_end_func is not None:
llvmlite.add_param(pool, va_end_func, i8_ptr_ty, "ap")
stdio.printf("[TRDBG] after va_start/va_end decl\n")
stdio.fflush(None)
# 声明 malloc(闭包分配用)
malloc_func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
@@ -294,18 +280,12 @@ class Translator:
if malloc_func is not None:
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
llvmlite.add_param(pool, malloc_func, i64_ty, "size")
stdio.printf("[TRDBG] after malloc decl\n")
stdio.fflush(None)
# 检查用户是否定义了 main 函数
has_user_main: int = 0
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
stdio.printf("[TRDBG] tree.children=%p\n", ch)
stdio.fflush(None)
if ch is not None:
cn_count: t.CSizeT = ch.__len__()
stdio.printf("[TRDBG] children count=%d\n", cn_count)
stdio.fflush(None)
for ci in range(cn_count):
child: ast.AST | t.CPtr = ch.get(ci)
if child is not None and child.kind() == ast.ASTKind.FunctionDef:
@@ -314,8 +294,6 @@ class Translator:
if string.strcmp(fd.name, "main") == 0:
has_user_main = 1
break
stdio.printf("[TRDBG] main check done, has_user_main=%d\n", has_user_main)
stdio.fflush(None)
if self._declare_only != 0:
# Phase 1a-pre(2=import扫描) / Phase 1a(1=struct注册): 只处理模块级,不创建 main 函数和 builder
@@ -365,13 +343,9 @@ class Translator:
elif kd == ast.ASTKind.ImportFrom:
self.ImportsH.HandleImportFromModule(child)
self.ImportsH.HandleImportFromNames(child)
stdio.printf("[MLDBG] after import preprocess\n")
stdio.fflush(None)
# 扫描当前模块的 CTypedef 声明(NAME: t.CTypedef = Type),
# 注册到动态 CTypedef 表,供 resolve_annotation_type 查询
HandlesType.ScanCTypedefDeclarations(pool, tree)
stdio.printf("[MLDBG] after ScanCTypedefDeclarations\n")
stdio.fflush(None)
# 跨模块 .vpi 注入:加载依赖模块的 .vpi 文件,
# 将函数 declare 声明注入到当前模块,解决跨模块函数调用找不到函数的问题
# 严格保证 declare_only == 0 时才注入(避免 Phase A-pre-inc 死锁)
@@ -380,8 +354,6 @@ class Translator:
StubMerger.LoadDepVpiIntoModule(
pool, mod, self._imported_modules, self._from_imports,
HandlesExprCall._g_temp_dir, self)
stdio.printf("[MLDBG] after LoadDepVpiIntoModule\n")
stdio.fflush(None)
# 预注册模块级变量(AnnAssign/Assign)到 SymTab
# 解决时序问题:class Logger 的 __init__ 引用 _mbuddy 时,
# _mbuddy 的 AnnAssign 在 AST 中位于 class Logger 之后,
@@ -397,15 +369,9 @@ class Translator:
kd_mv: int = child.kind()
if kd_mv == ast.ASTKind.AnnAssign or kd_mv == ast.ASTKind.Assign:
HandlesMain.handle_module_level_var(self, child)
stdio.printf("[MLDBG] after module-level var pre-reg\n")
stdio.fflush(None)
# 创建前向声明
HandlesFunctions.forward_declare_functions(self, tree)
stdio.printf("[MLDBG] after forward_declare_functions\n")
stdio.fflush(None)
added: int = HandlesMain.translate_children(self, tree)
stdio.printf("[MLDBG] after translate_children\n")
stdio.fflush(None)
# ============================================================
# IR 输出
+72 -21
View File
@@ -1078,28 +1078,79 @@ def map_t_type(pool: memhub.MemBuddy | t.CPtr,
def _is_t_attr(node: ast.AST | t.CPtr,
attr_name: str,
imported_modules: str) -> int:
"""检测节点是否是 t.attr_name 形式"""
"""检测节点是否是 t.attr_name 形式(含 from t import attr_name 的裸名)
支持两种写法(与 TPC 的 IsTModule 检查对齐):
- t.CPtr / t.CVoid(Attribute 形式)
- 裸名 CPtr / CVoid(from t import CPtr 形式)
裸名识别是必要功能:测试/库代码大量用 `from t import CPtr` 后直接写
`X | CPtr`、`CVoid` 参数注解等,若只认 t.XXX 会漏判。
"""
if node is None or attr_name is None:
return 0
if node.kind() != ast.ASTKind.Attribute:
if node.kind() == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at.attr is None:
return 0
if string.strcmp(at.attr, attr_name) != 0:
return 0
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return 0
val_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if val_nm.id is None:
return 0
if HandlesImports.is_module_imported(imported_modules, val_nm.id) == 0:
return 0
if string.strcmp(val_nm.id, "t") == 0:
return 1
return 0
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at.attr is None:
return 0
if string.strcmp(at.attr, attr_name) != 0:
return 0
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return 0
val_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if val_nm.id is None:
return 0
if HandlesImports.is_module_imported(imported_modules, val_nm.id) == 0:
return 0
if string.strcmp(val_nm.id, "t") == 0:
# 裸名形式: from t import attr_name → Name(id == attr_name)
if node.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is None:
return 0
if string.strcmp(nm.id, attr_name) != 0:
return 0
# 裸名需确认 t 模块已导入(from t import X 会注册 t 到 _imported_modules)
if HandlesImports.is_module_imported(imported_modules, "t") == 0:
return 0
return 1
return 0
# ============================================================
# _is_cptr_marker / _is_cvoid_marker — 检测联合类型中的指针标记
#
# 支持两种形式(与 TPC 的 IsTModule 检查对齐):
# - t.CPtr / t.CVoid(Attribute 形式)
# - 裸名 CPtr / CVoid(from t import CPtr 形式)
# 裸名识别避免了 `X | CPtr` 落入"无 CPtr 标记"分支导致联合被解析为
# 非指针一边(如 CUInt8T | CPtr 被错误解析为 i8 而非 i8*)。
# ============================================================
def _is_cptr_marker(node: ast.AST | t.CPtr,
imported_modules: str) -> int:
"""检测节点是否为 CPtr 指针标记,返回 1=是 / 0=否"""
if _is_t_attr(node, "CPtr", imported_modules) != 0:
return 1
if node is not None and node.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None and string.strcmp(nm.id, "CPtr") == 0:
return 1
return 0
def _is_cvoid_marker(node: ast.AST | t.CPtr,
imported_modules: str) -> int:
"""检测节点是否为 CVoid 指针标记,返回 1=是 / 0=否"""
if _is_t_attr(node, "CVoid", imported_modules) != 0:
return 1
if node is not None and node.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None and string.strcmp(nm.id, "CVoid") == 0:
return 1
return 0
# ============================================================
# extract_class_name_from_annotation - 从类型注解 AST 中提取类名
#
@@ -1268,8 +1319,8 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr,
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop.op == ast.OpKind.BitOr:
left_is_cptr: int = _is_t_attr(bop.left, "CPtr", imported_modules)
right_is_cptr: int = _is_t_attr(bop.right, "CPtr", imported_modules)
left_is_cptr: int = _is_cptr_marker(bop.left, imported_modules)
right_is_cptr: int = _is_cptr_marker(bop.right, imported_modules)
# 提取非 CPtr 一边的类名
if left_is_cptr != 0 and right_is_cptr == 0:
return extract_class_name_from_annotation(bop.right, imported_modules, pool)
@@ -1488,9 +1539,9 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop.op == ast.OpKind.BitOr:
# 检测 t.CPtr 标记(指针包装标记)
left_is_cptr: int = _is_t_attr(bop.left, "CPtr", imported_modules)
right_is_cptr: int = _is_t_attr(bop.right, "CPtr", imported_modules)
# 检测 t.CPtr 标记(指针包装标记,支持 t.CPtr 与 from t import CPtr 裸名)
left_is_cptr: int = _is_cptr_marker(bop.left, imported_modules)
right_is_cptr: int = _is_cptr_marker(bop.right, imported_modules)
if left_is_cptr != 0 or right_is_cptr != 0:
# 有 CPtr 标记:解析非 CPtr 一边的实际类型,包装为 Ptr
@@ -1499,7 +1550,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
if left_is_cptr != 0:
other_node = bop.right
# 检测另一边是否是 t.CVoid (void* = i8*)
other_is_cvoid: int = _is_t_attr(other_node, "CVoid", imported_modules)
other_is_cvoid: int = _is_cvoid_marker(other_node, imported_modules)
if other_is_cvoid != 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
other_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
+4 -48
View File
@@ -546,8 +546,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# 初始化 AST 表(只需一次)
ast._init_tables(mb)
stdio.printf("[MEM-DBG] 扫描完成后: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
# FileEntry 结构体大小(Phase 1a 和 1b 共用)
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
@@ -563,9 +561,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_skipped: int = 0
p1a_failed: int = 0
stdio.printf("[MEM-DBG] Phase 1a-pre 扫描 import 前: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
for i in range(result.Count):
entry_addr_a: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry_a: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_a, t.CPtr))
@@ -582,9 +577,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_skipped += 1
continue
stdio.printf("[MEM-DBG] Phase 1a-pre 翻译前: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
# 读取文件内容
file_path_a: str = entry_a.Path
f_a: fileio.File | t.CPtr = fileio.File(file_path_a, fileio.MODE.R)
@@ -633,6 +625,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
tr_a._declare_only = 2
tr_a.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry_a.RelPath)
HandlesType.set_current_file(file_path_a)
HandlesImports.set_current_file(file_path_a)
HandlesType.set_current_module_sha1(sha1_a)
HandlesType.clear_cdefine_constants()
HandlesType.clear_ctypedefs()
@@ -668,9 +661,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(src_buf_a)
stdio.printf("[MEM-DBG] Phase 1a-pre 扫描 import 完成: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
# ============================================================
# 依赖图按需翻译:构建可达 SHA1 集合
#
@@ -787,9 +777,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
viperlib.snprintf(fb_gpre, 1024, "预注册 %d 个泛型类模板文件", generic_file_count)
VLogger.info(fb_gpre, "Phase1")
stdio.printf("[MEM-DBG] 泛型模板预注册前: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
for gi in range(generic_file_count):
gi_addr_p: t.CUInt64T = t.CUInt64T(generic_file_indices) + gi * 4
gi_ptr_p: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(gi_addr_p, t.CPtr))
@@ -841,6 +828,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
gen_tr._declare_only = 1
gen_tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, gen_entry.RelPath)
HandlesType.set_current_file(gen_path)
HandlesImports.set_current_file(gen_path)
HandlesType.set_current_module_sha1(gen_entry.Sha1)
HandlesType.clear_cdefine_constants()
HandlesType.clear_ctypedefs()
@@ -853,9 +841,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(gen_tr._nonlocal_names)
stdlib.free(gen_src)
stdio.printf("[MEM-DBG] 泛型模板预注册完成: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
# 遍历文件:优先使用拓扑顺序,回退到字母序
iter_count: int = topo_count if topo_count > 0 else result.Count
for i in range(iter_count):
@@ -937,12 +922,11 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
tr_r._declare_only = 1
tr_r.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry_r.RelPath)
HandlesType.set_current_file(file_path_r)
HandlesImports.set_current_file(file_path_r)
HandlesType.set_current_module_sha1(sha1_r)
HandlesType.clear_cdefine_constants()
HandlesType.clear_ctypedefs()
HandlesStruct.reset_visible_structs(mb, 0)
stdio.printf("[MEM-DBG] Phase 1a struct 注册前: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
ret_r: int = tr_r.translate(tree_r)
if ret_r != 0:
p1a_fl += 1
@@ -981,8 +965,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# 1b 每文件翻译完后 reset_to(mark1) 回收该文件的 AST/IR/Handler/dump 临时,
# 避免 90 文件累积导致 1GB 池 OOM。
mark1: t.CSizeT = mb.mark()
stdio.printf("[MEM-DBG] Phase 1b 打点 mark1=%zu: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mark1, mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
# 1b 阶段新注册的 struct/enum 条目改 stdlib 分配(跨文件永久存活,不被 reset 回收)
HandlesStruct.set_stdlib_alloc(1)
@@ -1051,14 +1033,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
viperlib.snprintf(fb, 1024, "翻译: %s (sha1=%s)", rp, sha1)
VLogger.info(fb, "Phase1")
stdio.printf("[MEM-DBG] Phase 1b 翻译前: total=%zu used=%zu free=%zu free_blocks=%zu\n",
mb.usable_size, mb.used_bytes(), mb.free_bytes(), mb.free_count())
stdio.printf("[STDBG] stepA before free(stub_path)\n")
stdio.fflush(None)
stdlib.free(stub_path)
stdio.printf("[STDBG] stepB after free(stub_path)\n")
stdio.fflush(None)
# 读取文件内容
file_path: str = entry.Path
@@ -1098,8 +1073,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
src_buf[bytes_read] = 0
else:
src_buf[SRC_BUF_SIZE - 1] = 0
stdio.printf("[STDBG] stepC after file read, bytes_read=%d\n", bytes_read)
stdio.fflush(None)
# 解析 AST
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
@@ -1108,14 +1081,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
failed += 1
continue
ast._lexer_init(lx, src_buf, mb)
stdio.printf("[STDBG] stepD before tokenize\n")
stdio.fflush(None)
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
stdio.printf("[STDBG] stepE after tokenize\n")
stdio.fflush(None)
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
stdio.printf("[STDBG] stepF after parse_tokens, tree=%p\n", tree)
stdio.fflush(None)
if tree is None:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
@@ -1129,8 +1096,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
pyi_buf: bytes = stdlib.malloc(PYI_BUF_SIZE)
if pyi_buf is not None:
pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst(mb, tree, entry.RelPath, pyi_buf, PYI_BUF_SIZE)
stdio.printf("[STDBG] stepG after _GeneratePyiFromAst, pyi_pos=%d\n", pyi_pos)
stdio.fflush(None)
if pyi_pos > 0:
pyi_path: str = StubMerger._sliced_path(temp_dir, dir_len, sha1, "vpi")
if pyi_path is not None:
@@ -1150,15 +1115,12 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
tr.ModuleSha1 = sha1
tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry.RelPath)
HandlesType.set_current_file(file_path)
HandlesImports.set_current_file(file_path)
HandlesType.set_current_module_sha1(sha1)
HandlesType.clear_cdefine_constants()
HandlesType.clear_ctypedefs()
HandlesStruct.reset_visible_structs(mb, 0)
stdio.printf("[STDBG] stepH before tr.translate\n")
stdio.fflush(None)
ret: int = tr.translate(tree)
stdio.printf("[STDBG] stepI after tr.translate, ret=%d\n", ret)
stdio.fflush(None)
if ret != 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
@@ -1185,8 +1147,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
continue
tr.dump_ir(stub_buf, PHASE1_IR_SIZE, llvmlite.OUTPUT_STUB)
stub_len: t.CSizeT = string.strlen(stub_buf)
stdio.printf("[MEM-DBG] Phase 1b dump_ir(stub) done, len=%d used=%zu\n",
stub_len, mb.used_bytes())
# save stub.ll
dir_len_p1: t.CSizeT = string.strlen(temp_dir)
@@ -1211,8 +1171,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
continue
tr.dump_ir(text_buf, PHASE1_IR_SIZE, llvmlite.OUTPUT_TEXT)
text_len: t.CSizeT = string.strlen(text_buf)
stdio.printf("[MEM-DBG] Phase 1b dump_ir(text) done, len=%d used=%zu\n",
text_len, mb.used_bytes())
# save text.ll
text_path_p1: str = StubMerger._sliced_path(temp_dir, dir_len_p1, sha1, "text.ll")
@@ -1249,9 +1207,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# OOM 回收:回收本文件翻译产生的瞬时数据(AST/tokens/lexer/IR/Handler/dump 临时)
# 持久表(struct/enum/REnum/spec 缓存/全局函数名等)已迁出 reset 区间(stdlib),
# reset_to(mark1) 只回收 mark1 之后分配、未被单独 free 的块。
stdio.printf("[MEM-DBG] Phase 1b 翻译后 reset 前: used=%zu\n", mb.used_bytes())
mb.reset_to(mark1)
stdio.printf("[MEM-DBG] Phase 1b reset 完成: used=%zu\n", mb.used_bytes())
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None: