升级了仿 Python 错误堆栈提示,修复了 bool 不存在的问题,将 bool 改为 i1 而非 i8,实现了 bool 的归一化

This commit is contained in:
TermiNexus committed 2026-07-31 04:12:12 +08:00
1 parent bfb55d68f4
commit 9dcf34599d
13 files changed
+938 -213

No files matched your search

+2
View File
@@ -91,6 +91,8 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
tr._declare_only = declare_only tr._declare_only = declare_only
# 设置当前文件名(供报错使用) # 设置当前文件名(供报错使用)
HandlesType.set_current_file(file_path) HandlesType.set_current_file(file_path)
# 清空调用栈(每个文件翻译开始前重置)
HandlesType.clear_trace()
# 模块切换:清空 CDefine 常量表,确保每个模块的 CDefine 常量正确隔离 # 模块切换:清空 CDefine 常量表,确保每个模块的 CDefine 常量正确隔离
HandlesType.clear_cdefine_constants() HandlesType.clear_cdefine_constants()
# 命名空间隔离:includes 文件宽松模式(全部可见),用户文件严格模式(仅本地+import) # 命名空间隔离:includes 文件宽松模式(全部可见),用户文件严格模式(仅本地+import)
+39 -4
View File
@@ -14,6 +14,7 @@ import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.VLogger as VLogger
# ============================================================ # ============================================================
@@ -310,7 +311,7 @@ class AnnAssignHandle(HandlesBase.Mixin):
builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder
# 从 annotation 推断类型(修复:原硬编码 i32 导致 str 等类型错误) # 从 annotation 推断类型(修复:原硬编码 i32 导致 str 等类型错误)
var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) var_ty: llvmlite.LLVMType | t.CPtr = None
annot: ast.AST | t.CPtr = aa.annotation annot: ast.AST | t.CPtr = aa.annotation
if annot is not None: if annot is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
@@ -334,6 +335,18 @@ class AnnAssignHandle(HandlesBase.Mixin):
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans) pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is not None: if resolved is not None:
var_ty = resolved var_ty = resolved
else:
# fast-fail: 注解类型解析失败,不再静默回退 i32(会导致变量类型错误)
VLogger.error(
f"变量类型注解解析失败,变量名: {nm.id}",
"HandlesAnnAssign")
var_ty = llvmlite.Int32(pool)
else:
# fast-fail: AnnAssign 必须有 annotation
VLogger.error(
f"AnnAssign 缺少类型注解,变量名: {nm.id}",
"HandlesAnnAssign")
var_ty = llvmlite.Int32(pool)
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
HandlesStruct.ensure_struct_def_in_module(pool, self.Trans.Module, var_ty) HandlesStruct.ensure_struct_def_in_module(pool, self.Trans.Module, var_ty)
@@ -388,13 +401,25 @@ class AnnAssignHandle(HandlesBase.Mixin):
mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module
# 确定类型(从 annotation 推断) # 确定类型(从 annotation 推断)
var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) var_ty: llvmlite.LLVMType | t.CPtr = None
annot: ast.AST | t.CPtr = aa.annotation annot: ast.AST | t.CPtr = aa.annotation
if annot is not None: if annot is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans) pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is not None: if resolved is not None:
var_ty = resolved var_ty = resolved
else:
# fast-fail: 注解类型解析失败,不再静默回退 i32
VLogger.error(
f"变量类型注解解析失败,变量名: {nm.id}",
"HandlesAnnAssign")
var_ty = llvmlite.Int32(pool)
else:
# fast-fail: AnnAssign 必须有 annotation
VLogger.error(
f"AnnAssign 缺少类型注解,变量名: {nm.id}",
"HandlesAnnAssign")
var_ty = llvmlite.Int32(pool)
# global 变量:写入模块作用域中的全局变量 # global 变量:写入模块作用域中的全局变量
if HT.is_global_name(self.Trans, nm.id) != 0: if HT.is_global_name(self.Trans, nm.id) != 0:
@@ -423,9 +448,19 @@ class AnnAssignHandle(HandlesBase.Mixin):
nl_ptr: llvmlite.Value | t.CPtr = HandlesNonlocal.get_nonlocal_var_ptr( nl_ptr: llvmlite.Value | t.CPtr = HandlesNonlocal.get_nonlocal_var_ptr(
self.Trans, nm.id) self.Trans, nm.id)
if nl_ptr is not None: if nl_ptr is not None:
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) # 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
nl_target_ty: llvmlite.LLVMType | t.CPtr = None
if nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
if nl_target_ty is None:
# fast-fail: nl_ptr 类型信息丢失,无法安全 store
VLogger.error(
f"nonlocal 变量类型信息丢失,无法写入: {nm.id}",
"HandlesAnnAssign")
return 0
rhs_coerced: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type( rhs_coerced: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(
builder, rhs_val, i32_ty) builder, rhs_val, nl_target_ty)
llvmlite.build_store(builder, rhs_coerced, nl_ptr) llvmlite.build_store(builder, rhs_coerced, nl_ptr)
return 0 return 0
+12 -2
View File
@@ -345,9 +345,19 @@ class AssignHandle(HandlesBase.Mixin):
nl_ptr: llvmlite.Value | t.CPtr = HandlesNonlocal.get_nonlocal_var_ptr( nl_ptr: llvmlite.Value | t.CPtr = HandlesNonlocal.get_nonlocal_var_ptr(
self.Trans, nm.id) self.Trans, nm.id)
if nl_ptr is not None: if nl_ptr is not None:
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) # 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
nl_target_ty: llvmlite.LLVMType | t.CPtr = None
if nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
if nl_target_ty is None:
# fast-fail: nl_ptr 类型信息丢失,无法安全 store
VLogger.error(
f"nonlocal 变量类型信息丢失,无法写入: {nm.id}",
"HandlesAssign")
continue
rhs_coerced: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type( rhs_coerced: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(
builder, rhs_val, i32_ty) builder, rhs_val, nl_target_ty)
llvmlite.build_store(builder, rhs_coerced, nl_ptr) llvmlite.build_store(builder, rhs_coerced, nl_ptr)
continue continue
+40 -3
View File
@@ -985,6 +985,12 @@ def _translate_union_def(trans: HT.Translator | t.CPtr,
field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
if field_ty is None: if field_ty is None:
# fast-fail: 字段类型解析失败,用 fatal_error 输出 Python 风格 traceback
type_name: str = HandlesType.annotation_to_str(aa.annotation)
HandlesType.fatal_error(
aa,
f"'{type_name}' is not defined",
"NameError")
field_ty = llvmlite.Int32(pool) field_ty = llvmlite.Int32(pool)
# 计算字段大小,更新 max_size # 计算字段大小,更新 max_size
@@ -1132,7 +1138,8 @@ def _get_annotation_field_size(annotation: ast.AST | t.CPtr) -> int:
if string.strcmp(type_name, "bytes") == 0: if string.strcmp(type_name, "bytes") == 0:
return 8 return 8
if string.strcmp(type_name, "bool") == 0: if string.strcmp(type_name, "bool") == 0:
return 4 # bool = t.CBool = i8(1 字节)
return 1
# 已注册的结构体类型 # 已注册的结构体类型
struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(type_name) struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(type_name)
if struct_ty is not None: if struct_ty is not None:
@@ -2209,6 +2216,14 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
existing: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( existing: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module(
class_name, trans.ModuleSha1) class_name, trans.ModuleSha1)
if existing is not None: if existing is not None:
# BUG 5 方案1修改点2:existing 时字段完整性检查
# 结构体已注册,但需验证字段信息是否完整(防止 i32 回退残留或注册不完整)
if existing.FieldCount == 0 and trans._declare_only == 0:
# Phase B 时字段数为 0 说明注册不完整(Phase A-pre 可能漏注册)
HandlesType.fatal_error(
node,
f"结构体已注册但字段数为0,类: {class_name},可能注册不完整",
"InternalCompilerError")
# 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法 # 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法
if existing.Ty is not None: if existing.Ty is not None:
# Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行) # Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行)
@@ -2364,7 +2379,28 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
if field_ty is None: if field_ty is None:
field_ty = llvmlite.Int32(pool) # BUG 5 方案1修改点1:消除 i32 静默回退
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,延迟到下一遍
# Phase B(declare_only=0):真正的类型解析错误,fast-fail 终止该类翻译
# 不再用 i32 回退(会导致结构体布局错误 + GEP 索引错乱)
# 例外:泛型 Subscript(如 list[AST|t.CPtr])未特化时回退为 Ptr(i8),
# 因为泛型特化可能延迟到构造调用时。AnnotClassName 会携带 mangled name
# 供 translate_attribute 做后续 bitcast。
is_generic_sub: int = 0
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript:
is_generic_sub = 1
if is_generic_sub != 0:
# 泛型未特化:回退为 Ptr(i8),依赖 AnnotClassName 做后续类型恢复
field_ty = llvmlite.Ptr(pool, llvmlite.Int8(pool))
else:
type_name2: str = HandlesType.annotation_to_str(aa.annotation)
HandlesType.fatal_error(
aa,
f"'{type_name2}' is not defined",
"NameError")
if trans._declare_only == 1:
return 0
return 0
# 存储到数组(指针转 CSizeT) # 存储到数组(指针转 CSizeT)
if field_count < 48: if field_count < 48:
@@ -2377,9 +2413,10 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
else: else:
field_defaults_buf[field_count] = 0 field_defaults_buf[field_count] = 0
# 提取原始类型注解的类名(联合类型简化为 Ptr(i8) 时回退查找结构体) # 提取原始类型注解的类名(联合类型简化为 Ptr(i8) 时回退查找结构体)
# BUG 1 方案3:传 pool 使泛型 Subscript 返回 mangled name(如 "list[ast.AST|t.CPtr]")
if aa.annotation is not None: if aa.annotation is not None:
annot_cn: str = HandlesType.extract_class_name_from_annotation( annot_cn: str = HandlesType.extract_class_name_from_annotation(
aa.annotation, trans._imported_modules) aa.annotation, trans._imported_modules, pool)
if annot_cn is not None: if annot_cn is not None:
diag_cn_val: t.CSizeT = t.CSizeT(annot_cn) diag_cn_val: t.CSizeT = t.CSizeT(annot_cn)
field_annot_buf[field_count] = diag_cn_val field_annot_buf[field_count] = diag_cn_val
+56 -10
View File
@@ -129,11 +129,9 @@ def get_llvm_float_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
# ============================================================ # ============================================================
# 检查类型是否为 Ptr(独立函数,避免嵌套 match 的编译器 BUG) # is_ptr_type - 检查类型是否为 Ptr
# #
# 嵌套 match 的 REnum 检测在宿主编译器中有 BUG:当 match 嵌套在 # 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。
# 另一个 match 的 case 块中时,内层 match 走非 REnum 路径,
# 导致 case 列表为空。提取为独立函数可规避此问题。
# ============================================================ # ============================================================
def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int: def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否""" """检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
@@ -178,7 +176,9 @@ def get_ptr_level(ty: llvmlite.LLVMType | t.CPtr) -> int:
# ============================================================ # ============================================================
# 检查类型是否为 Array(独立函数,避免嵌套 match 的编译器 BUG) # is_array_type - 检查类型是否为 Array
#
# 返回 1=是, 0=否。类型检查工具函数。
# ============================================================ # ============================================================
def is_array_type(ty: llvmlite.LLVMType | t.CPtr) -> int: def is_array_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否""" """检查 ty 是否是 Array 类型,返回 1=是, 0=否"""
@@ -224,8 +224,6 @@ def _get_custom_struct_cls_nm(pool: memhub.MemBuddy | t.CPtr,
# 用于支持 X|t.CPtr 类型变量的属性访问: # 用于支持 X|t.CPtr 类型变量的属性访问:
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用 # - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
# - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用 # - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
#
# 用 is_ptr_type 避免嵌套 match 的编译器 BUG
# ============================================================ # ============================================================
def _deref_if_ptr_ptr(builder: llvmlite.IRBuilder | t.CPtr, def _deref_if_ptr_ptr(builder: llvmlite.IRBuilder | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr: obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
@@ -303,7 +301,6 @@ def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr,
# 指针 → 非指针值: build_load 解引用 # 指针 → 非指针值: build_load 解引用
# 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值 # 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
# 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load # 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load
# 注意:使用独立函数 is_ptr_type 检查,避免嵌套 match 的编译器 BUG
if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) == 0: if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) == 0:
return llvmlite.build_load(builder, target_ty, val) return llvmlite.build_load(builder, target_ty, val)
# 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针) # 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
@@ -2147,7 +2144,7 @@ def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr,
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is not None: if pointee is not None:
# 不调用 pointee.kind() 避免跨模块引用 LLVMType.kind 符号 # 不调用 pointee.kind() 避免跨模块引用 LLVMType.kind 符号
# 单层 match(避免嵌套 match 的编译器 bug) # 用 match 检查 pointee 类型
match pointee: match pointee:
case llvmlite.LLVMType.Array(elem_ty, count): case llvmlite.LLVMType.Array(elem_ty, count):
# 数组遍历: getelementptr [N x elem_ty], ... , i32 0, i32 %idx # 数组遍历: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
@@ -2536,6 +2533,21 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
return None return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at is None or at.value is None or at.attr is None: if at is None or at.value is None or at.attr is None:
# fast-fail: AST 节点字段缺失属解析器 bug,应报告具体缺失字段
diag_buf_an: bytes = stdlib.malloc(256)
if diag_buf_an is not None:
missing_an: str = "?"
if at is not None:
if at.value is None:
missing_an = "value"
elif at.attr is None:
missing_an = "attr"
viperlib.snprintf(diag_buf_an, 256,
"属性访问失败: AST 节点字段缺失 (%s sha1=%s:%d)",
missing_an,
trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?",
at.lineno if at is not None and at.lineno is not None else 0)
VLogger.error(diag_buf_an, "ATTR")
return None return None
# 枚举成员访问: EnumName.MemberName → 常量值 # 枚举成员访问: EnumName.MemberName → 常量值
@@ -2556,6 +2568,10 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
viperlib.snprintf(name_buf, 32, "%lld", member.Value) viperlib.snprintf(name_buf, 32, "%lld", member.Value)
return llvmlite.ConstInt(pool, base_ty, member.Value, name_buf) return llvmlite.ConstInt(pool, base_ty, member.Value, name_buf)
return llvmlite.const_int32(pool, member.Value) return llvmlite.const_int32(pool, member.Value)
# fast-fail: 枚举成员未找到
VLogger.error(
f"枚举成员未找到: {enum_nm.id}.{at.attr}",
"ATTR")
return None return None
# 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值 # 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值
@@ -2577,6 +2593,10 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
viperlib.snprintf(name_buf_e, 32, "%lld", member_e.Value) viperlib.snprintf(name_buf_e, 32, "%lld", member_e.Value)
return llvmlite.ConstInt(pool, base_ty_e, member_e.Value, name_buf_e) return llvmlite.ConstInt(pool, base_ty_e, member_e.Value, name_buf_e)
return llvmlite.const_int32(pool, member_e.Value) return llvmlite.const_int32(pool, member_e.Value)
# fast-fail: 嵌套枚举成员未找到
VLogger.error(
f"嵌套枚举成员未找到: {inner_at_e.attr}.{at.attr}",
"ATTR")
return None return None
# 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy # 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy
@@ -2868,6 +2888,19 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
VLogger.error(fb_attr, "ATTR") VLogger.error(fb_attr, "ATTR")
return None return None
case _: case _:
# fast-fail: 对非指针类型做属性访问(如 i32.x),
# 静默返回 None 会导致下游 "store to None" 崩溃,丢失错误现场
diag_buf_ap: bytes = stdlib.malloc(256)
if diag_buf_ap is not None:
attr_nm_ap: str = "?"
if at is not None and at.attr is not None:
attr_nm_ap = at.attr
viperlib.snprintf(diag_buf_ap, 256,
"属性访问失败: .%s (base=非指针类型,无法做属性访问 sha1=%s:%d)",
attr_nm_ap,
trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?",
at.lineno if at is not None and at.lineno is not None else 0)
VLogger.error(diag_buf_ap, "ATTR")
return None return None
# ============================================================ # ============================================================
@@ -2900,7 +2933,7 @@ def get_subscript_ptr(builder: llvmlite.IRBuilder | t.CPtr,
if is_ptr_type(alloca.Ty) != 0: if is_ptr_type(alloca.Ty) != 0:
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is not None: if pointee is not None:
# 单层 match(避免嵌套 match 的编译器 bug) # 用 match 检查 pointee 类型
match pointee: match pointee:
case llvmlite.LLVMType.Array(elem_ty, count): case llvmlite.LLVMType.Array(elem_ty, count):
# 数组遍历 # 数组遍历
@@ -3101,4 +3134,17 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
VLogger.error(fb_attr_g, "ATTR") VLogger.error(fb_attr_g, "ATTR")
return None return None
case _: case _:
# fast-fail: 对非指针类型做属性写入(如 i32.x = ...),
# 静默返回 None 会导致下游 "store to None" 崩溃
diag_buf_gp: bytes = stdlib.malloc(256)
if diag_buf_gp is not None:
attr_nm_gp: str = "?"
if at is not None and at.attr is not None:
attr_nm_gp = at.attr
viperlib.snprintf(diag_buf_gp, 256,
"属性写入失败: .%s (base=非指针类型,无法做属性写入 sha1=%s:%d)",
attr_nm_gp,
trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?",
at.lineno if at is not None and at.lineno is not None else 0)
VLogger.error(diag_buf_gp, "ATTR")
return None return None
+95 -68
View File
@@ -18,89 +18,33 @@ import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesImports as HandlesImports import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.StubMerger as StubMerger
# ============================================================ # ============================================================
# 全局模块 SHA1 映射(module_name → SHA1) # 模块 SHA1 查询(委托给 StubMerger 全局存储器)
# #
# 翻译时用于跨模块函数调用名混淆: # 翻译时用于跨模块函数调用名混淆:
# string.strchr(...) → call @{sha1_of_string}.strchr(...) # string.strchr(...) → call @{sha1_of_string}.strchr(...)
# #
# 通过 set_module_sha1_map 设置,_lookup_module_sha1 查询 # 数据来源是 StubMerger.PopulateSha1MapStore/AppendToSha1MapStore
# 填充的全局存储器,不再维护独立的 SHA1 映射表副本。
# ============================================================ # ============================================================
_g_sha1_arr: bytes = None
_g_mod_arr: bytes = None
_g_sha1_count: int = 0
def set_module_sha1_map(sha1_arr: bytes, mod_arr: bytes, count: int) -> int:
"""设置全局模块 SHA1 映射"""
global _g_sha1_arr
global _g_mod_arr
global _g_sha1_count
_g_sha1_arr = sha1_arr
_g_mod_arr = mod_arr
_g_sha1_count = count
return 0
def _lookup_module_sha1(mod_name: str) -> str: def _lookup_module_sha1(mod_name: str) -> str:
"""查找模块名对应的 SHA1,找不到返回 None""" """查找模块名对应的 SHA1,找不到返回 None
if mod_name is None:
return None 委托给 StubMerger.LookupSha1ByModName(精确匹配 + 包名回退)。
if _g_sha1_arr is None or _g_mod_arr is None: """
return None return StubMerger.LookupSha1ByModName(mod_name)
if _g_sha1_count <= 0:
return None
for i in range(_g_sha1_count):
idx: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(_g_mod_arr + idx, mod_name) == 0:
sidx: t.CSizeT = t.CSizeT(i) * 17
return _g_sha1_arr + sidx
return None
def _lookup_module_sha1_suffix(mod_suffix: str) -> str: def _lookup_module_sha1_suffix(mod_suffix: str) -> str:
"""后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "llvmlite.__types" """后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "llvmlite.__types"
用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点), 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点),
去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。
委托给 StubMerger.LookupSha1ByModNameSuffix(精确 + 后缀 + 反向后缀)。
""" """
if mod_suffix is None: return StubMerger.LookupSha1ByModNameSuffix(mod_suffix)
return None
if _g_sha1_arr is None or _g_mod_arr is None:
return None
if _g_sha1_count <= 0:
return None
suffix_len: t.CSizeT = string.strlen(mod_suffix)
if suffix_len == 0:
return None
for i in range(_g_sha1_count):
idx: t.CSizeT = t.CSizeT(i) * 64
mod_name: str = _g_mod_arr + idx
mod_len: t.CSizeT = string.strlen(mod_name)
if mod_len == 0:
continue
# 精确匹配
if mod_len == suffix_len:
if string.strcmp(mod_name, mod_suffix) == 0:
sidx: t.CSizeT = t.CSizeT(i) * 17
return _g_sha1_arr + sidx
# 后缀匹配:mod_name 以 "." + mod_suffix 结尾
if mod_len > suffix_len:
if mod_name[mod_len - suffix_len - 1] == '.':
cmp_start: str = mod_name + mod_len - suffix_len
if string.strcmp(cmp_start, mod_suffix) == 0:
sidx2: t.CSizeT = t.CSizeT(i) * 17
return _g_sha1_arr + sidx2
# 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾
# 用于 from_imports 存完整路径(如 lib.core.Handles.HandlesTranslator)
# 而 mod_arr 只存文件名(如 HandlesTranslator)的情况:
# import X as Y 的别名 Y 经 from_imports 查到完整路径,
# 需反向匹配 mod_arr 中的文件名才能找到 SHA1
if suffix_len > mod_len:
if mod_suffix[suffix_len - mod_len - 1] == '.':
cmp_start_rev: str = mod_suffix + suffix_len - mod_len
if string.strcmp(cmp_start_rev, mod_name) == 0:
sidx_rev: t.CSizeT = t.CSizeT(i) * 17
return _g_sha1_arr + sidx_rev
return None
# ============================================================ # ============================================================
# 全局 CExport 函数表(module_sha1 + func_name) # 全局 CExport 函数表(module_sha1 + func_name)
@@ -534,6 +478,10 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr,
# 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配 # 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配
# x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致 # x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致
# 与 _infer_method_ret_ty 的默认策略保持一致 # 与 _infer_method_ret_ty 的默认策略保持一致
# WARNING: 未登记的外部函数返回类型默认推断为 i32,若实际返回指针/i64 会被截断
VLogger.warning(
f"外部函数返回类型未登记,默认推断为 i32(可能截断指针/i64): {func_name}",
"HandlesExprCall")
return llvmlite.Int32(pool) return llvmlite.Int32(pool)
# ============================================================ # ============================================================
@@ -2146,19 +2094,37 @@ def _apply_struct_defaults(pool: memhub.MemBuddy | t.CPtr,
# 用类型指针定位 entry,规避跨模块同名 find_struct 找错 # 用类型指针定位 entry,规避跨模块同名 find_struct 找错
entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty) entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty)
if entry is None: if entry is None:
# fast-fail: 结构体类型未注册,静默 return 会导致字段未初始化
VLogger.error(
f"结构体类型未注册,无法应用默认值: {class_name}",
"HandlesExprCall")
return return
for fi in range(entry.FieldCount): for fi in range(entry.FieldCount):
fe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry( fe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry(
entry, fi) entry, fi)
if fe is None or fe.DefaultVal is None: if fe is None:
# fast-fail: 字段索引越界(entry.FieldCount 与实际不一致)
VLogger.error(
f"字段索引越界,无法获取字段 entry: {class_name}",
"HandlesExprCall")
continue continue
if fe.DefaultVal is None:
continue # 合理跳过:无默认值的字段
default_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( default_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, fe.DefaultVal, None, 0, trans) builder, pool, mod, fe.DefaultVal, None, 0, trans)
if default_val is None: if default_val is None:
# fast-fail: 默认值翻译失败(如引用了未定义符号)
VLogger.error(
f"字段默认值翻译失败: {class_name}",
"HandlesExprCall")
continue continue
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, fe.Ty, tmp, fe.Index) builder, struct_ty, fe.Ty, tmp, fe.Index)
if field_ptr is None: if field_ptr is None:
# fast-fail: GEP 失败(struct_ty 与 fe.Ty 不匹配)
VLogger.error(
f"字段 GEP 失败,类型不匹配: {class_name}",
"HandlesExprCall")
continue continue
default_val = HandlesExpr.coerce_to_type(builder, default_val, fe.Ty) default_val = HandlesExpr.coerce_to_type(builder, default_val, fe.Ty)
llvmlite.build_store(builder, default_val, field_ptr) llvmlite.build_store(builder, default_val, field_ptr)
@@ -2295,6 +2261,10 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, arg, None, 0, trans) builder, pool, mod, arg, None, 0, trans)
if arg_val is None: if arg_val is None:
# fast-fail: 参数翻译失败
VLogger.error(
f"构造函数参数翻译失败: {class_name}",
"HandlesExprCall")
continue continue
fe: HandlesStruct.FieldEntry | t.CPtr = None fe: HandlesStruct.FieldEntry | t.CPtr = None
if ctor_entry is not None: if ctor_entry is not None:
@@ -2302,10 +2272,18 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
else: else:
fe = HandlesStruct.get_field_by_index(class_name, ai) fe = HandlesStruct.get_field_by_index(class_name, ai)
if fe is None: if fe is None:
# fast-fail: 字段索引越界(用户传了多余参数)
VLogger.error(
f"构造函数参数过多,字段索引越界: {class_name}",
"HandlesExprCall")
continue continue
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, fe.Ty, storage_ptr, fe.Index) builder, struct_ty, fe.Ty, storage_ptr, fe.Index)
if field_ptr is None: if field_ptr is None:
# fast-fail: GEP 失败(struct_ty 与 fe.Ty 不匹配)
VLogger.error(
f"构造函数字段 GEP 失败: {class_name}",
"HandlesExprCall")
continue continue
arg_val = HandlesExpr.coerce_to_type(builder, arg_val, fe.Ty) arg_val = HandlesExpr.coerce_to_type(builder, arg_val, fe.Ty)
llvmlite.build_store(builder, arg_val, field_ptr) llvmlite.build_store(builder, arg_val, field_ptr)
@@ -2328,6 +2306,10 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, arg, None, 0, trans) builder, pool, mod, arg, None, 0, trans)
if arg_val is None: if arg_val is None:
# fast-fail: 参数翻译失败
VLogger.error(
f"构造函数参数翻译失败: {class_name}",
"HandlesExprCall")
continue continue
# 获取字段信息(用 ctor_entry 规避跨模块同名 find_struct 找错) # 获取字段信息(用 ctor_entry 规避跨模块同名 find_struct 找错)
fe: HandlesStruct.FieldEntry | t.CPtr = None fe: HandlesStruct.FieldEntry | t.CPtr = None
@@ -2336,11 +2318,19 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
else: else:
fe = HandlesStruct.get_field_by_index(class_name, ai) fe = HandlesStruct.get_field_by_index(class_name, ai)
if fe is None: if fe is None:
# fast-fail: 字段索引越界(用户传了多余参数)
VLogger.error(
f"构造函数参数过多,字段索引越界: {class_name}",
"HandlesExprCall")
continue continue
# GEP 到字段 ai # GEP 到字段 ai
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, fe.Ty, tmp, fe.Index) builder, struct_ty, fe.Ty, tmp, fe.Index)
if field_ptr is None: if field_ptr is None:
# fast-fail: GEP 失败(struct_ty 与 fe.Ty 不匹配)
VLogger.error(
f"构造函数字段 GEP 失败: {class_name}",
"HandlesExprCall")
continue continue
# 类型转换并 store # 类型转换并 store
arg_val = HandlesExpr.coerce_to_type(builder, arg_val, fe.Ty) arg_val = HandlesExpr.coerce_to_type(builder, arg_val, fe.Ty)
@@ -2975,6 +2965,10 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr,
# 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配 # 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配
# x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致 # x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致
# 与 _infer_external_func_ret_ty 的默认策略保持一致 # 与 _infer_external_func_ret_ty 的默认策略保持一致
# WARNING: 未登记的方法返回类型默认推断为 i32,若实际返回指针/i64 会被截断
VLogger.warning(
f"方法返回类型未登记,默认推断为 i32(可能截断指针/i64): {method_name}",
"HandlesExprCall")
return llvmlite.Int32(pool) return llvmlite.Int32(pool)
@@ -4071,6 +4065,35 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
i32_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) i32_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
return _translate_t_type_cast(pool, builder, cast_arg_int, i32_ty_builtin) return _translate_t_type_cast(pool, builder, cast_arg_int, i32_ty_builtin)
return None return None
elif string.strcmp(func_name, "bool") == 0:
# bool(x) → i1(严格 != 0 归一化)
# 整数: icmp ne x, 0 → i1
# 浮点: fcmp one x, 0.0 → i1
# 指针: icmp ne x, null → i1
cast_arg_bool: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cast_arg_bool is not None:
arg_bits: int = HandlesExpr.get_llvm_type_bits(cast_arg_bool.Ty)
arg_fbits: int = HandlesExpr.get_llvm_float_bits(cast_arg_bool.Ty)
# 整数: icmp ne x, 0
if arg_bits != 0:
zero_int: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, arg_bits, 0)
if zero_int is not None:
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cast_arg_bool, zero_int)
return None
# 浮点: fcmp one x, 0.0
if arg_fbits != 0:
zero_ty: llvmlite.LLVMType | t.CPtr = cast_arg_bool.Ty
zero_flt: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(pool, zero_ty, 0.0)
if zero_flt is not None:
return llvmlite.build_fcmp(builder, llvmlite.FCMP_ONE, cast_arg_bool, zero_flt)
return None
# 指针: icmp ne x, null
ptr_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cast_arg_bool.Ty, "null")
if ptr_null is not None:
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cast_arg_bool, ptr_null)
return None
return None
# 检测 t.XXX 类型转换: t.CUInt64T(ptr), t.CPtr(val), t.CInt(val) 等 # 检测 t.XXX 类型转换: t.CUInt64T(ptr), t.CPtr(val), t.CInt(val) 等
# 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用) # 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用)
@@ -4184,6 +4207,10 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
sz_builtin = 8 sz_builtin = 8
elif string.strcmp(sz_cls_name, "CSizeT") == 0: elif string.strcmp(sz_cls_name, "CSizeT") == 0:
sz_builtin = 8 sz_builtin = 8
elif string.strcmp(sz_cls_name, "CBool") == 0:
sz_builtin = 1
elif string.strcmp(sz_cls_name, "bool") == 0:
sz_builtin = 1
if sz_builtin >= 0: if sz_builtin >= 0:
# 注意: 必须用 const_int64 而非 ConstInt(...,"0") # 注意: 必须用 const_int64 而非 ConstInt(...,"0")
# ConstInt 的 name 参数是 IR 文本表示,ValuePrint 输出时使用 Name 而非 IntVal # ConstInt 的 name 参数是 IR 文本表示,ValuePrint 输出时使用 Name 而非 IntVal
+22 -48
View File
@@ -604,48 +604,18 @@ def struct_has_defaults(class_name: str) -> int:
# #
# 如果 value 是 Ptr(Struct(...)),返回 Struct 类型 # 如果 value 是 Ptr(Struct(...)),返回 Struct 类型
# ============================================================ # ============================================================
def _is_struct_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Struct 类型(独立函数,规避嵌套 match 编译器 BUG)"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Struct(fields, fcount, name):
return 1
case _:
return 0
def _is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Ptr 类型(独立函数,规避嵌套 match 编译器 BUG)"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Ptr(pointee):
return 1
case _:
return 0
def _get_ptr_pointee(ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""获取 Ptr 类型的 pointee(独立函数,规避嵌套 match 编译器 BUG)"""
if ty is None:
return None
match ty:
case llvmlite.LLVMType.Ptr(pointee):
return pointee
case _:
return None
def get_struct_type_from_value(val: llvmlite.Value | t.CPtr) -> llvmlite.LLVMType | t.CPtr: def get_struct_type_from_value(val: llvmlite.Value | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""从 Value 的类型推断结构体类型""" """从 Value 的类型推断结构体类型"""
if val is None or val.Ty is None: if val is None or val.Ty is None:
return None return None
# 嵌套 match:先匹配 Ptr,再在 case 块内匹配 pointee 是否为 Struct
match val.Ty: match val.Ty:
case llvmlite.LLVMType.Ptr(pointee): case llvmlite.LLVMType.Ptr(pointee):
if _is_struct_type(pointee) != 0: match pointee:
return pointee case llvmlite.LLVMType.Struct(fields, fcount, name):
return None return pointee
case _:
return None
case _: case _:
return None return None
@@ -1051,15 +1021,16 @@ def ensure_struct_def_in_module(pool: memhub.MemBuddy | t.CPtr,
if ty is None or mod is None or pool is None: if ty is None or mod is None or pool is None:
return return
# 指针类型:确保 pointee 至少有 opaque 声明 # 指针类型:确保 pointee 至少有 opaque 声明
# 用 _is_ptr_type 检查(独立函数,规避嵌套 match BUG) match ty:
if _is_ptr_type(ty) != 0: case llvmlite.LLVMType.Ptr(pointee):
llvmlite.module_ensure_opaque_for_type(mod, pool, ty) llvmlite.module_ensure_opaque_for_type(mod, pool, ty)
# 如果 pointee 是 Struct,递归复制完整定义 # 如果 pointee 是 Struct,递归复制完整定义
# (GEP 访问字段需要 sized 类型,仅 opaque 会导致 llc 报错) # (GEP 访问字段需要 sized 类型,仅 opaque 会导致 llc 报错)
pointee: llvmlite.LLVMType | t.CPtr = _get_ptr_pointee(ty) if pointee is not None:
if pointee is not None: ensure_struct_def_in_module(pool, mod, pointee)
ensure_struct_def_in_module(pool, mod, pointee) return
return case _:
pass
# 获取结构体名称(非 Struct 类型返回 None) # 获取结构体名称(非 Struct 类型返回 None)
sname: t.CChar | t.CPtr = llvmlite.get_struct_name(ty) sname: t.CChar | t.CPtr = llvmlite.get_struct_name(ty)
if sname is None: if sname is None:
@@ -1113,7 +1084,7 @@ def _ensure_field_types_in_module(pool: memhub.MemBuddy | t.CPtr,
"""递归扫描类型树,确保值类型子结构体有完整定义""" """递归扫描类型树,确保值类型子结构体有完整定义"""
if ty is None or mod is None or pool is None: if ty is None or mod is None or pool is None:
return return
# 用 match 遍历类型(独立函数,规避嵌套 match BUG) # 嵌套 match 遍历类型树
match ty: match ty:
case llvmlite.LLVMType.Struct(fields, fcount, sname): case llvmlite.LLVMType.Struct(fields, fcount, sname):
# 遍历字段链表 # 遍历字段链表
@@ -1122,8 +1093,11 @@ def _ensure_field_types_in_module(pool: memhub.MemBuddy | t.CPtr,
while fld is not None: while fld is not None:
if fld.Ty is not None: if fld.Ty is not None:
# 值类型子结构体(如 FILETIME)需要完整定义 # 值类型子结构体(如 FILETIME)需要完整定义
if _is_struct_type(fld.Ty) != 0: match fld.Ty:
ensure_struct_def_in_module(pool, mod, fld.Ty) case llvmlite.LLVMType.Struct(f2, fc2, n2):
ensure_struct_def_in_module(pool, mod, fld.Ty)
case _:
pass
_ensure_field_types_in_module(pool, mod, fld.Ty) _ensure_field_types_in_module(pool, mod, fld.Ty)
fld = fld.Next fld = fld.Next
case llvmlite.LLVMType.Ptr(pointee): case llvmlite.LLVMType.Ptr(pointee):
+306 -23
View File
@@ -28,6 +28,92 @@ import lib.core.Handles.HandlesStruct as HandlesStruct
_g_current_file: str = None _g_current_file: str = None
# ============================================================
# 仿 Python Traceback 调用栈
#
# 在编译过程中通过 push_trace/pop_trace 维护翻译上下文栈,
# fatal_error 输出时从底到顶展示所有帧,形成多帧 traceback。
# ============================================================
TRACE_MAX: t.CDefine = 32
@t.NoVTable
class TraceFrame:
file_name: str # 文件名
lineno: int # 行号
col_start: int # 列起始(1-based,0=不指定)
col_end: int # 列结束(1-based,0=不指定)
_g_trace_frames: TraceFrame | t.CPtr = None
_g_trace_count: int = 0
def _ensure_trace_init() -> int:
"""确保调用栈已初始化(懒加载,用 malloc 不依赖 pool)"""
global _g_trace_frames
if _g_trace_frames is not None:
return 1
entry_size: t.CSizeT = TraceFrame.__sizeof__()
_g_trace_frames = stdlib.malloc(entry_size * TRACE_MAX)
if _g_trace_frames is None:
return 0
string.memset(_g_trace_frames, 0, entry_size * TRACE_MAX)
return 1
def _get_trace_frame(i: int) -> TraceFrame | t.CPtr:
"""获取第 i 个 TraceFrame 槽位"""
if _g_trace_frames is None or i < 0 or i >= TRACE_MAX:
return None
entry_size: t.CSizeT = TraceFrame.__sizeof__()
addr: t.CUInt64T = t.CUInt64T(_g_trace_frames) + i * entry_size
return (TraceFrame | t.CPtr)(t.CVoid(addr, t.CPtr))
def push_trace(file_name: str, lineno: int, col_start: int = 0, col_end: int = 0):
"""压入一个翻译上下文帧(进入新的翻译作用域时调用)"""
if _ensure_trace_init() == 0:
return
if _g_trace_count >= TRACE_MAX:
return
frame: TraceFrame | t.CPtr = _get_trace_frame(_g_trace_count)
if frame is None:
return
frame.file_name = file_name
frame.lineno = lineno
frame.col_start = col_start
frame.col_end = col_end
_g_trace_count += 1
def pop_trace():
"""弹出栈顶帧(退出翻译作用域时调用)"""
if _g_trace_count <= 0:
return
_g_trace_count -= 1
frame: TraceFrame | t.CPtr = _get_trace_frame(_g_trace_count)
if frame is not None:
frame.file_name = None
frame.lineno = 0
frame.col_start = 0
frame.col_end = 0
def clear_trace():
"""清空调用栈(每个文件翻译开始前调用)"""
global _g_trace_count
i: int
for i in range(_g_trace_count):
frame: TraceFrame | t.CPtr = _get_trace_frame(i)
if frame is not None:
frame.file_name = None
frame.lineno = 0
frame.col_start = 0
frame.col_end = 0
_g_trace_count = 0
# ============================================================ # ============================================================
# 全局 temp_dir(pyi 文件所在目录,由 BuildPipeline/Phase1 设置) # 全局 temp_dir(pyi 文件所在目录,由 BuildPipeline/Phase1 设置)
# #
@@ -444,39 +530,80 @@ def _read_source_line(file_path: str, lineno: int) -> str:
# ============================================================ # ============================================================
# fatal_error - 以 Python 风格报错并退出(带源代码行) # fatal_error - 以 Python 风格报错并退出(支持多帧、错误类型、列范围)
# #
# 格式: # 格式:
# Traceback (most recent call last): # Traceback (most recent call last):
# File "path", line N # File "path1", line N1
# source code line # source code line 1
# SyntaxError: msg # File "path2", line N2
# source code line 2
# ^^^^ ← 列范围标记(可选)
# TypeError: msg
#
# 参数:
# node: AST 节点(提供行号),可为 None
# msg: 错误消息
# err_type: 错误类型字符串(默认 "SyntaxError")
# col_start: 列起始(1-based,0=不标记列范围)
# col_end: 列结束(1-based,0=不标记列范围)
# ============================================================ # ============================================================
def fatal_error(node: ast.AST | t.CPtr, msg: str): def fatal_error(node: ast.AST | t.CPtr, msg: str,
"""以 Python 风格报错并退出(带源代码行)""" err_type: str = "SyntaxError",
col_start: int = 0, col_end: int = 0):
"""以 Python 风格报错并退出(支持多帧、错误类型、列范围)
向后兼容:err_type/col_start/col_end 有默认值,旧调用 fatal_error(node, msg) 仍可用。
"""
stdio.printf("Traceback (most recent call last):\n")
# 输出调用栈帧(从底到顶)
i: int
for i in range(_g_trace_count):
frame: TraceFrame | t.CPtr = _get_trace_frame(i)
if frame is not None and frame.file_name is not None:
stdio.printf(' File "%s", line %d\n', frame.file_name, frame.lineno)
if frame.lineno > 0:
src: str = _read_source_line(frame.file_name, frame.lineno)
if src is not None:
stdio.printf(" %s\n", src)
stdlib.free(src)
# 输出当前帧(node 对应的位置)
lineno: int = 0 lineno: int = 0
if node is not None: if node is not None:
lineno = node.lineno lineno = node.lineno
file_name: str = _g_current_file file_name: str = _g_current_file
if file_name is None: if file_name is None:
file_name = "<unknown>" file_name = "<unknown>"
stdio.printf("Traceback (most recent call last):\n")
stdio.printf(' File "%s", line %d\n', file_name, lineno) stdio.printf(' File "%s", line %d\n', file_name, lineno)
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0: if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
src_line: str = _read_source_line(file_name, lineno) src_line: str = _read_source_line(file_name, lineno)
if src_line is not None: if src_line is not None:
stdio.printf(" %s\n", src_line) stdio.printf(" %s\n", src_line)
# 列范围标记:在源代码行下方用 ^ 标记出错范围
if col_start > 0 and col_end >= col_start:
stdio.printf(" ")
j: int
for j in range(col_end):
if j >= col_start - 1 and j < col_end:
stdio.printf("^")
else:
stdio.printf(" ")
stdio.printf("\n")
stdlib.free(src_line) stdlib.free(src_line)
stdio.printf("SyntaxError: %s\n", msg) # 输出错误类型 + 消息
if err_type is None or err_type[0] == 0:
err_type = "SyntaxError"
stdio.printf("%s: %s\n", err_type, msg)
sys.exit(1) sys.exit(1)
# ============================================================ # ============================================================
# fatal_type_error - 兼容别名(指向 fatal_error) # fatal_type_error - 类型错误别名(err_type="TypeError")
# ============================================================ # ============================================================
def fatal_type_error(node: ast.AST | t.CPtr, msg: str): def fatal_type_error(node: ast.AST | t.CPtr, msg: str,
"""以 Python 风格报错并退出(fatal_error 的别名)""" col_start: int = 0, col_end: int = 0):
fatal_error(node, msg) """以 Python 风格报错并退出(TypeError 版本)"""
fatal_error(node, msg, "TypeError", col_start, col_end)
# ============================================================ # ============================================================
# 将 t.CXxx 类型名映射到 LLVMType(纯函数) # 将 t.CXxx 类型名映射到 LLVMType(纯函数)
@@ -551,7 +678,8 @@ def map_t_type(pool: memhub.MemBuddy | t.CPtr,
return llvmlite.Int64(pool) return llvmlite.Int64(pool)
# bool # bool
elif string.strcmp(type_name, "CBool") == 0: elif string.strcmp(type_name, "CBool") == 0:
return llvmlite.Int8(pool) # bool = t.CBool = i1(LLVM 1 位整数,语义与 C _Bool 一致)
return llvmlite.Int1(pool)
# ptrdiff_t # ptrdiff_t
elif string.strcmp(type_name, "CPtrDiffT") == 0: elif string.strcmp(type_name, "CPtrDiffT") == 0:
return llvmlite.Int64(pool) return llvmlite.Int64(pool)
@@ -777,9 +905,104 @@ def _is_t_attr(node: ast.AST | t.CPtr,
# Attribute 节点 (memhub.MemBuddy) → "MemBuddy" # Attribute 节点 (memhub.MemBuddy) → "MemBuddy"
# BinOp 联合类型 (MemBuddy | t.CPtr) → 非 CPtr 一边的类名 # BinOp 联合类型 (MemBuddy | t.CPtr) → 非 CPtr 一边的类名
# ============================================================ # ============================================================
# ============================================================
# annotation_to_str - 将类型注解 AST 转换为可读字符串(用于错误消息)
#
# 与 extract_class_name_from_annotation 不同,此函数不跳过任何类型名,
# 纯粹用于错误消息显示。支持递归处理嵌套类型。
#
# 支持的节点:
# Name → "bool", "int", "MemBuddy"
# Attribute → "t.CBool", "memhub.MemBuddy"
# Subscript → "list[...]", "t.CArray[...]"
# Constant (字符串注解) → 字符串值
# BinOp (A | B) → "A | B"
# ============================================================
def annotation_to_str(annot: ast.AST | t.CPtr) -> str:
"""将类型注解 AST 转换为可读字符串,返回 None=无法提取
注意:返回的字符串可能由 malloc 分配,调用方无需释放(错误路径用)。
"""
if annot is None:
return "<none>"
k: int = annot.kind()
# Constant 节点: 字符串注解
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(annot)
if cn.const_kind == ast.CONST_STR and cn.str_val is not None:
return cn.str_val
return "<constant>"
# Name 节点: bool, int, MemBuddy
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot)
if nm.id is not None:
return nm.id
return "<name>"
# Attribute 节点: t.CBool, memhub.MemBuddy
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot)
if at.attr is None:
return "<attribute>"
if at.value is not None:
prefix: str = annotation_to_str(at.value)
if prefix is not None:
buf: t.CChar | t.CPtr = stdlib.malloc(
string.strlen(prefix) + string.strlen(at.attr) + 2)
if buf is not None:
viperlib.snprintf(buf,
string.strlen(prefix) + string.strlen(at.attr) + 2,
"%s.%s", prefix, at.attr)
return buf
return at.attr
# Subscript 节点: list[...], t.CArray[...]
if k == ast.ASTKind.Subscript:
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
base: str = annotation_to_str(sub.value)
if base is not None:
buf2: t.CChar | t.CPtr = stdlib.malloc(string.strlen(base) + 5)
if buf2 is not None:
viperlib.snprintf(buf2, string.strlen(base) + 5,
"%s[...]", base)
return buf2
return "<subscript>"
# BinOp 节点: A | B (联合类型)
if k == ast.ASTKind.BinOp:
bo: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
left_s: str = annotation_to_str(bo.left)
right_s: str = annotation_to_str(bo.right)
if left_s is not None and right_s is not None:
buf3: t.CChar | t.CPtr = stdlib.malloc(
string.strlen(left_s) + string.strlen(right_s) + 5)
if buf3 is not None:
viperlib.snprintf(buf3,
string.strlen(left_s) + string.strlen(right_s) + 5,
"%s | %s", left_s, right_s)
return buf3
return "<binop>"
return "<unknown>"
# ============================================================
# extract_class_name_from_annotation
# ============================================================
def extract_class_name_from_annotation(node: ast.AST | t.CPtr, def extract_class_name_from_annotation(node: ast.AST | t.CPtr,
imported_modules: str) -> str: imported_modules: str,
"""从类型注解 AST 中提取类名,返回 None=无法提取""" pool: memhub.MemBuddy | t.CPtr = None) -> str:
"""从类型注解 AST 中提取类名,返回 None=无法提取
pool 可用时,对泛型 Subscript(如 list[AST | t.CPtr])返回 mangled name
(如 "list[ast.AST|t.CPtr]"),与 resolve_annotation_type 的 mangled name 格式一致,
使 find_struct 能找到特化的泛型结构体。
pool 为 None 时,对 Subscript 只返回裸类名(如 "list"),保持向后兼容。
"""
if node is None: if node is None:
return None return None
k: int = node.kind() k: int = node.kind()
@@ -796,6 +1019,8 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr,
return None return None
if string.strcmp(cn.str_val, "bytes") == 0: if string.strcmp(cn.str_val, "bytes") == 0:
return None return None
if string.strcmp(cn.str_val, "bool") == 0:
return None
return cn.str_val return cn.str_val
return None return None
@@ -811,6 +1036,8 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr,
return None return None
if string.strcmp(nm.id, "bytes") == 0: if string.strcmp(nm.id, "bytes") == 0:
return None return None
if string.strcmp(nm.id, "bool") == 0:
return None
return nm.id return nm.id
# Attribute 节点: memhub.MemBuddy → "memhub.MemBuddy"(模块限定名) # Attribute 节点: memhub.MemBuddy → "memhub.MemBuddy"(模块限定名)
@@ -841,23 +1068,76 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr,
right_is_cptr: int = _is_t_attr(bop.right, "CPtr", imported_modules) right_is_cptr: int = _is_t_attr(bop.right, "CPtr", imported_modules)
# 提取非 CPtr 一边的类名 # 提取非 CPtr 一边的类名
if left_is_cptr != 0 and right_is_cptr == 0: if left_is_cptr != 0 and right_is_cptr == 0:
return extract_class_name_from_annotation(bop.right, imported_modules) return extract_class_name_from_annotation(bop.right, imported_modules, pool)
if right_is_cptr != 0 and left_is_cptr == 0: if right_is_cptr != 0 and left_is_cptr == 0:
return extract_class_name_from_annotation(bop.left, imported_modules) return extract_class_name_from_annotation(bop.left, imported_modules, pool)
# 无 CPtr 标记:尝试两边 # 无 CPtr 标记:尝试两边
if left_is_cptr == 0 and right_is_cptr == 0: if left_is_cptr == 0 and right_is_cptr == 0:
left_name: str = extract_class_name_from_annotation(bop.left, imported_modules) left_name: str = extract_class_name_from_annotation(bop.left, imported_modules, pool)
if left_name is not None: if left_name is not None:
return left_name return left_name
return extract_class_name_from_annotation(bop.right, imported_modules) return extract_class_name_from_annotation(bop.right, imported_modules, pool)
return None return None
# Subscript 节点: list[AST | t.CPtr] → "list" # BUG 1 方案3:Subscript 节点拼接 mangled name
# list[AST | t.CPtr] → "list[ast.AST|t.CPtr]"(pool 可用时)
# 与 resolve_annotation_type 的 mangled name 格式一致,使 find_struct 能找到特化结构体
if k == ast.ASTKind.Subscript: if k == ast.ASTKind.Subscript:
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub.value is not None: if sub.value is None:
return extract_class_name_from_annotation(sub.value, imported_modules) return None
return None base_name: str = extract_class_name_from_annotation(sub.value, imported_modules, pool)
if base_name is None:
return None
# pool 不可用时,只返回裸类名(向后兼容)
if pool is None or sub.slice is None:
return base_name
# pool 可用:拼接 mangled name "base_name[slice_name]"
# slice 是 Tuple 时用逗号分隔多个参数,否则单个参数
slice_name: str = None
if sub.slice.kind() == ast.ASTKind.Tuple:
# Tuple slice: dict[str, int] → "str,int"
sub_tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(sub.slice)
if sub_tup.elts is not None:
tup_elts_sn: list[ast.AST | t.CPtr] | t.CPtr = sub_tup.elts
tn_sn: t.CSizeT = tup_elts_sn.__len__()
# 计算总长度
tup_total: t.CSizeT = 1
ti_sn: t.CSizeT
for ti_sn in range(tn_sn):
if ti_sn > 0:
tup_total += 1
elt_node: ast.AST | t.CPtr = tup_elts_sn.get(ti_sn)
elt_name: str = _get_type_name_from_annotation(pool, elt_node)
if elt_name is not None:
tup_total += string.strlen(elt_name)
tup_buf: str = pool.alloc(tup_total)
if tup_buf is not None:
tup_buf[0] = '\0'
for ti_sn in range(tn_sn):
if ti_sn > 0:
string.strcat(tup_buf, ",")
elt_node2: ast.AST | t.CPtr = tup_elts_sn.get(ti_sn)
elt_name2: str = _get_type_name_from_annotation(pool, elt_node2)
if elt_name2 is not None:
string.strcat(tup_buf, elt_name2)
slice_name = tup_buf
else:
# 非 Tuple slice: list[AST | t.CPtr] → "ast.AST|t.CPtr"
slice_name = _get_type_name_from_annotation(pool, sub.slice)
if slice_name is None:
return base_name
# 拼接 "base_name[slice_name]"
mangled_total: t.CSizeT = string.strlen(base_name) + 1 + string.strlen(slice_name) + 1 + 1
mangled_buf: str = pool.alloc(mangled_total)
if mangled_buf is None:
return base_name
mangled_buf[0] = '\0'
string.strcat(mangled_buf, base_name)
string.strcat(mangled_buf, "[")
string.strcat(mangled_buf, slice_name)
string.strcat(mangled_buf, "]")
return mangled_buf
return None return None
@@ -925,6 +1205,9 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
return llvmlite.Int32(pool) return llvmlite.Int32(pool)
elif string.strcmp(nm.id, "bytes") == 0: elif string.strcmp(nm.id, "bytes") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool)) return llvmlite.Ptr(pool, llvmlite.Int8(pool))
elif string.strcmp(nm.id, "bool") == 0:
# bool = t.CBool = i1(与 map_t_type 中 CBool 映射一致)
return llvmlite.Int1(pool)
# class 名称: 查找已注册的 StructType,返回 StructType 本身(不包装 Ptr) # class 名称: 查找已注册的 StructType,返回 StructType 本身(不包装 Ptr)
# 这样 build_alloca 会分配结构体大小的空间,alloca 返回 struct_ty* # 这样 build_alloca 会分配结构体大小的空间,alloca 返回 struct_ty*
# 命名空间隔离:严格模式下仅解析可见的结构体 # 命名空间隔离:严格模式下仅解析可见的结构体
+2 -8
View File
@@ -541,14 +541,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
VLogger.warning("扫描结果无有效 SHA1,跳过 Phase1(项目可能无 includes 依赖)", "Phase1") VLogger.warning("扫描结果无有效 SHA1,跳过 Phase1(项目可能无 includes 依赖)", "Phase1")
stdlib.free(sha1_set) stdlib.free(sha1_set)
return 0 return 0
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用) # 模块 SHA1 映射已由 PopulateSha1MapStore 填充到 StubMerger 全局存储器,
td_len_p1map: t.CSizeT = string.strlen(temp_dir) # HandlesExprCall._lookup_module_sha1 直接查全局存储器,不再需要独立副本。
p1_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
p1_mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
p1_inc_count: int = 0
if p1_sha1_arr is not None and p1_mod_arr is not None:
p1_inc_count = StubMerger._BuildIncludesSha1Map(temp_dir, td_len_p1map, p1_sha1_arr, p1_mod_arr)
HandlesExprCall.set_module_sha1_map(p1_sha1_arr, p1_mod_arr, p1_inc_count)
# 初始化 AST 表(只需一次) # 初始化 AST 表(只需一次)
ast._init_tables(mb) ast._init_tables(mb)
+3 -43
View File
@@ -264,49 +264,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
VLogger.info(fb, "project") VLogger.info(fb, "project")
stdlib.free(map_path_am) stdlib.free(map_path_am)
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用) # 模块 SHA1 映射已由 PopulateSha1MapStore + AppendToSha1MapStore 统一填充到
td_len_pb: t.CSizeT = string.strlen(temp_dir) # StubMerger 全局存储器,HandlesExprCall._lookup_module_sha1 直接查全局存储器,
pb_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) # 不再需要构建独立的 SHA1 映射表副本。
pb_mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
pb_inc_count: int = 0
if pb_sha1_arr is not None and pb_mod_arr is not None:
pb_inc_count = StubMerger._BuildIncludesSha1Map(temp_dir, td_len_pb, pb_sha1_arr, pb_mod_arr)
# 追加用户源文件的 (模块名, SHA1) 到全局映射
# 使跨模块方法调用能通过 from_imports + _lookup_module_sha1 找到正确的 SHA1
if pb_sha1_arr is not None and pb_mod_arr is not None:
src_dir_len_us: t.CSizeT = string.strlen(source_dir)
for i in range(file_count):
ea_us: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent_us: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_us, t.CPtr))
if ent_us is None or ent_us.Path is None or ent_us.Sha1 is None:
continue
if pb_inc_count >= StubMerger.MAX_INCLUDES:
break
# 计算相对路径并转换为点分模块名(保留完整包路径)
path_str: str = ent_us.Path
path_len_us: t.CSizeT = string.strlen(path_str)
rel_path_us: str = path_str
if path_len_us > src_dir_len_us + 1:
if string.strncmp(path_str, source_dir, src_dir_len_us) == 0:
rel_path_us = path_str + src_dir_len_us + 1
# 使用 _PathToModuleName 转换为点分模块名(处理 __init__.vp → 包名)
mod_name_us: str = StubMerger._PathToModuleName(rel_path_us)
if mod_name_us is None:
continue
# 写入模块名
mod_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 64
mn_len_us: t.CSizeT = string.strlen(mod_name_us)
if mn_len_us >= 64:
string.strncpy(pb_mod_arr + mod_idx, mod_name_us, 63)
pb_mod_arr[mod_idx + 63] = '\0'
else:
string.strcpy(pb_mod_arr + mod_idx, mod_name_us)
stdlib.free(mod_name_us)
# 写入 SHA1
sha1_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 17
string.strcpy(pb_sha1_arr + sha1_idx, ent_us.Sha1)
pb_inc_count += 1
HandlesExprCall.set_module_sha1_map(pb_sha1_arr, pb_mod_arr, pb_inc_count)
# === 2. Phase A: 为每个文件生成 stub + text === # === 2. Phase A: 为每个文件生成 stub + text ===
+90 -4
View File
@@ -503,6 +503,96 @@ def GetSha1StoreCount() -> int:
return _sha1_store_count return _sha1_store_count
# ============================================================
# LookupSha1ByModName - 从模块名查找 SHA1(直接查全局存储器)
#
# 精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
# 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
#
# Returns:
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
# ============================================================
def LookupSha1ByModName(mod_name: str) -> str:
"""从模块名查找 SHA1(精确匹配 + 包名回退)"""
if mod_name is None:
return None
if _sha1_store_arr is None or _sha1_store_mod is None:
return None
if _sha1_store_count <= 0:
return None
# 第一遍:精确匹配
i_em: int
for i_em in range(_sha1_store_count):
idx_em: t.CSizeT = t.CSizeT(i_em) * 64
if string.strcmp(_sha1_store_mod + idx_em, mod_name) == 0:
sidx_em: t.CSizeT = t.CSizeT(i_em) * 17
return _sha1_store_arr + sidx_em
# 第二遍:包名回退("ast" → "ast.__init__")
nm_len_em: t.CSizeT = string.strlen(mod_name)
init_buf_em: bytes = stdlib.malloc(nm_len_em + 12)
if init_buf_em is not None:
viperlib.snprintf(init_buf_em, nm_len_em + 12, "%s.__init__", mod_name)
i_im: int
for i_im in range(_sha1_store_count):
idx2_im: t.CSizeT = t.CSizeT(i_im) * 64
if string.strcmp(_sha1_store_mod + idx2_im, init_buf_em) == 0:
sidx2_im: t.CSizeT = t.CSizeT(i_im) * 17
stdlib.free(init_buf_em)
return _sha1_store_arr + sidx2_im
stdlib.free(init_buf_em)
return None
# ============================================================
# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1
#
# 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点),
# 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。
# 包含三种匹配方式:精确、后缀、反向后缀。
#
# Returns:
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
# ============================================================
def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
"""后缀匹配查找模块 SHA1"""
if mod_suffix is None:
return None
if _sha1_store_arr is None or _sha1_store_mod is None:
return None
if _sha1_store_count <= 0:
return None
suffix_len_sf: t.CSizeT = string.strlen(mod_suffix)
if suffix_len_sf == 0:
return None
i_sf: int
for i_sf in range(_sha1_store_count):
idx_sf: t.CSizeT = t.CSizeT(i_sf) * 64
mod_name_sf: str = _sha1_store_mod + idx_sf
mod_len_sf: t.CSizeT = string.strlen(mod_name_sf)
if mod_len_sf == 0:
continue
# 精确匹配
if mod_len_sf == suffix_len_sf:
if string.strcmp(mod_name_sf, mod_suffix) == 0:
sidx_sf: t.CSizeT = t.CSizeT(i_sf) * 17
return _sha1_store_arr + sidx_sf
# 后缀匹配:mod_name 以 "." + mod_suffix 结尾
if mod_len_sf > suffix_len_sf:
if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.':
cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf
if string.strcmp(cmp_start_sf, mod_suffix) == 0:
sidx2_sf: t.CSizeT = t.CSizeT(i_sf) * 17
return _sha1_store_arr + sidx2_sf
# 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾
if suffix_len_sf > mod_len_sf:
if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.':
cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf
if string.strcmp(cmp_start_rev_sf, mod_name_sf) == 0:
sidx_rev_sf: t.CSizeT = t.CSizeT(i_sf) * 17
return _sha1_store_arr + sidx_rev_sf
return None
# ============================================================ # ============================================================
# PopulateIncludesDeps - 将 includes 文件的依赖存入内存 # PopulateIncludesDeps - 将 includes 文件的依赖存入内存
# #
@@ -1382,13 +1472,9 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤 app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤
""" """
global _g_reachable_incomplete
if source_dir is None or temp_dir is None or reachable_set is None: if source_dir is None or temp_dir is None or reachable_set is None:
return -1 return -1
# 重置不完整标志
_g_reachable_incomplete = 0
SRC_BUF_SIZE_R: t.CSizeT = 1048576 SRC_BUF_SIZE_R: t.CSizeT = 1048576
# 1. 构建 SHA1→module_name 映射 # 1. 构建 SHA1→module_name 映射
+268
View File
@@ -0,0 +1,268 @@
import stdio
import t, c
import memhub
import stdlib
import llvmlite
import string
# ============================================================
# nested_match_test - 嵌套 match REnum 检测测试
#
# 验证 BUG 2 修复:当 match 嵌套在另一个 match 的 case 块中时,
# 内层 match 能正确识别 REnum 变体并生成正确的 case 列表。
#
# 修复前:内层 match 走非 REnum 路径,case 列表为空。
# 修复后:内层 match 能正确识别 REnum 变体。
# ============================================================
# 内存池(测试内部使用)
_mbuddy: memhub.MemBuddy | t.CPtr
# 测试用内存大小(64KB,足够构造 LLVMType 值)
_NMATCH_POOL_SIZE: t.CDefine = 65536
def _init_mbuddy() -> int:
"""初始化内存池,返回 0=成功, 1=失败"""
arena: bytes = stdlib.malloc(_NMATCH_POOL_SIZE)
if arena is None:
stdio.printf("FAIL: malloc for arena failed\n")
return 1
mb: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(arena, _NMATCH_POOL_SIZE)
if mb is None:
stdio.printf("FAIL: MemBuddy init failed\n")
return 1
_mbuddy = mb
return 0
def test_single_match() -> int:
"""Test 1: 单层 match(基线测试)"""
stdio.printf("--- Test 1: 单层 match LLVMType ---\n")
ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy)
if ty is None:
stdio.printf("FAIL: Int32 returned None\n")
return 1
match ty:
case llvmlite.LLVMType.Int(bits):
stdio.printf("Int(%d) OK\n", bits)
if bits == 32:
stdio.printf("bits==32 OK\n")
return 0
stdio.printf("bits==%d FAIL\n", bits)
return 1
case _:
stdio.printf("single match default FAIL\n")
return 1
def test_nested_match_other() -> int:
"""Test 2: 嵌套 match(case 块内 match 另一个 REnum 值)
BUG 2 核心测试:在 match 的 case 块内嵌套 match 另一个 REnum 值。
"""
stdio.printf("--- Test 2: 嵌套 match(case 块内 match 另一值)---\n")
ty_outer: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy)
ty_inner: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(_mbuddy)
match ty_outer:
case llvmlite.LLVMType.Int(bits):
# 在 Int 的 case 块内嵌套 match 另一个 REnum 值
match ty_inner:
case llvmlite.LLVMType.Int(inner_bits):
stdio.printf("outer Int(%d), inner Int(%d) OK\n", bits, inner_bits)
if bits == 32 and inner_bits == 8:
stdio.printf("values OK\n")
return 0
stdio.printf("values FAIL\n")
return 1
case _:
stdio.printf("inner match default FAIL\n")
return 1
case _:
stdio.printf("outer match default FAIL\n")
return 1
def test_nested_match_pointee() -> int:
"""Test 3: 嵌套 match 同一值的子结构(Ptr 的 pointee)
模拟 HandlesStruct.vp 中 get_struct_type_from_value 的嵌套 match 模式。
"""
stdio.printf("--- Test 3: 嵌套 match pointee ---\n")
# 构造 Ptr(Int32) 类型
inner_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy)
ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(_mbuddy, inner_ty)
match ptr_ty:
case llvmlite.LLVMType.Ptr(pointee):
# 在 Ptr 的 case 块内嵌套 match pointee
match pointee:
case llvmlite.LLVMType.Int(bits):
stdio.printf("Ptr(Int(%d)) OK\n", bits)
if bits == 32:
stdio.printf("pointee bits==32 OK\n")
return 0
stdio.printf("pointee bits==%d FAIL\n", bits)
return 1
case _:
stdio.printf("pointee match default FAIL\n")
return 1
case _:
stdio.printf("outer Ptr match FAIL\n")
return 1
def test_three_level_nested() -> int:
"""Test 4: 3 层嵌套 match
模拟 HandlesStruct.vp 中 _ensure_field_types_in_module 的嵌套模式:
外层 match → case 块内 match pointee → 再嵌套 match 另一值。
"""
stdio.printf("--- Test 4: 3 层嵌套 match ---\n")
# 构造 Ptr(Ptr(Int32)) 类型
innermost: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy)
mid_ptr: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(_mbuddy, innermost)
outer_ptr: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(_mbuddy, mid_ptr)
# 另一个值用于第 3 层 match
extra_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int16(_mbuddy)
match outer_ptr:
case llvmlite.LLVMType.Ptr(p1):
# 第 2 层 match p1
match p1:
case llvmlite.LLVMType.Ptr(p2):
# 第 3 层 match p2
match p2:
case llvmlite.LLVMType.Int(bits):
stdio.printf("Ptr(Ptr(Int(%d))) OK\n", bits)
case _:
stdio.printf("level 3 default FAIL\n")
return 1
# 第 3 层 match 另一个值(验证 case 块内多次 match)
match extra_ty:
case llvmlite.LLVMType.Int(ebits):
stdio.printf("extra Int(%d) in nested OK\n", ebits)
if ebits == 16:
stdio.printf("3-level values OK\n")
return 0
stdio.printf("3-level values FAIL\n")
return 1
case _:
stdio.printf("extra match FAIL\n")
return 1
case _:
stdio.printf("level 2 default FAIL\n")
return 1
case _:
stdio.printf("level 1 default FAIL\n")
return 1
def test_nested_match_in_if() -> int:
"""Test 5: 在 if 块内嵌套 match
另一种常见的嵌套场景:if 条件块内使用 match。
"""
stdio.printf("--- Test 5: if 块内嵌套 match ---\n")
ty: llvmlite.LLVMType | t.CPtr = llvmlite.Float(_mbuddy)
if ty is not None:
match ty:
case llvmlite.LLVMType.Float(bits):
# 在 Float 的 case 块内再 match
ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Double(_mbuddy)
match ty2:
case llvmlite.LLVMType.Float(bits2):
stdio.printf("Float(%d) + Double(%d) OK\n", bits, bits2)
if bits == 32 and bits2 == 64:
stdio.printf("float values OK\n")
return 0
stdio.printf("float values FAIL\n")
return 1
case _:
stdio.printf("inner float match FAIL\n")
return 1
case _:
stdio.printf("outer float match FAIL\n")
return 1
stdio.printf("ty is None FAIL\n")
return 1
def test_nested_match_mismatch() -> int:
"""Test 6: 嵌套 match 不同变体(验证 default 分支)
确保嵌套 match 的 default 分支(case _)能正确触发。
"""
stdio.printf("--- Test 6: 嵌套 match 不同变体(default 分支)---\n")
# 外层是 Int,内层是 Float - 验证内层 match 不会误匹配 Int
ty_outer: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy)
ty_inner: llvmlite.LLVMType | t.CPtr = llvmlite.Float(_mbuddy)
match ty_outer:
case llvmlite.LLVMType.Int(bits):
match ty_inner:
case llvmlite.LLVMType.Int(wrong_bits):
stdio.printf("FAIL: Float matched as Int(%d)\n", wrong_bits)
return 1
case llvmlite.LLVMType.Float(fbits):
stdio.printf("outer Int(%d), inner Float(%d) OK\n", bits, fbits)
if bits == 32 and fbits == 32:
stdio.printf("mismatch values OK\n")
return 0
stdio.printf("mismatch values FAIL\n")
return 1
case _:
stdio.printf("inner default FAIL (should be Float)\n")
return 1
case _:
stdio.printf("outer default FAIL\n")
return 1
def nested_match_test() -> int:
stdio.printf("=== nested_match_test: 嵌套 match REnum 检测测试 ===\n\n")
# 初始化内存池
if _init_mbuddy() != 0:
stdio.printf("FAIL: 内存池初始化失败\n")
return 1
r: int = 0
stdio.fflush(None)
if test_single_match() != 0:
r = 1
stdio.fflush(None)
if test_nested_match_other() != 0:
r = 1
stdio.fflush(None)
if test_nested_match_pointee() != 0:
r = 1
stdio.fflush(None)
if test_three_level_nested() != 0:
r = 1
stdio.fflush(None)
if test_nested_match_in_if() != 0:
r = 1
stdio.fflush(None)
if test_nested_match_mismatch() != 0:
r = 1
stdio.fflush(None)
if r == 0:
stdio.printf("\n=== nested_match_test: ALL PASS ===\n")
else:
stdio.printf("\n=== nested_match_test: SOME FAIL ===\n")
return r
+3
View File
@@ -32,6 +32,7 @@ from namespace_test import namespace_test
from testcheck_test import testcheck_test from testcheck_test import testcheck_test
from opovl_test import opovl_test from opovl_test import opovl_test
from circ_test import circ_test from circ_test import circ_test
from nested_match_test import nested_match_test
# from generic_test import generic_test # 屏蔽泛型模块(GenericTest 运行时崩溃,待修复) # from generic_test import generic_test # 屏蔽泛型模块(GenericTest 运行时崩溃,待修复)
@@ -101,6 +102,8 @@ def main() -> int:
stdio.fflush(None) stdio.fflush(None)
r = circ_test() r = circ_test()
stdio.fflush(None) stdio.fflush(None)
r = nested_match_test()
stdio.fflush(None)
# 屏蔽泛型模块(GenericTest 运行时崩溃,待修复) # 屏蔽泛型模块(GenericTest 运行时崩溃,待修复)
# stdio.printf("[TM] before generic_test\n") # stdio.printf("[TM] before generic_test\n")