提交新的建议忽略
This commit is contained in:
1 parent
8ad500c4da
commit
f8436c74d2
14 files changed
+1004
-271
No files matched your search
@@ -23,3 +23,8 @@ __pycache__/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# 编译
|
||||
**/output/
|
||||
**/temp/
|
||||
**/.tpv_build/
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 常量正确隔离
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 条目重复)
|
||||
|
||||
@@ -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 输出
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user