Files
TransPyV/App/lib/core/Handles/HandlesExpr.py
2026-07-26 20:33:17 +08:00

2922 lines
141 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import ast
import memhub
import string
import llvmlite
import stdlib
import stdio
import viperlib
import w32.fileio as fileio
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExprOps as HandlesExprOps
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesEnum as HandlesEnum
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
# ============================================================
# HandlesExpr - 表达式处理Mixin 继承模式)
#
# 工具函数保留为模块级供外部调用ExprHandle.HandleValue 提供 trans 接口
# ============================================================
# ============================================================
# 从 AST 节点提取函数名
# ============================================================
def get_func_name(func_node: ast.AST | t.CPtr) -> str:
"""从函数节点提取函数名"""
if func_node is None:
return None
k: int = func_node.kind()
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_node)
return nm.id
elif k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(func_node)
return at.attr
return None
# ============================================================
# 转义 LLVM IR 字符串字面量
# ============================================================
def escape_llvm_string(pool: memhub.MemBuddy | t.CPtr,
src: t.CChar | t.CPtr) -> t.CChar | t.CPtr:
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
if src is None:
return None
slen: t.CSizeT = string.strlen(src)
alloc_size: t.CSizeT = 4 * slen + 8
buf: t.CChar | t.CPtr = pool.alloc(alloc_size)
if buf is None:
return None
pos: t.CSizeT = 0
buf[pos] = 'c'
pos += 1
buf[pos] = '"'
pos += 1
for i in range(slen):
ch: t.CChar = src[i]
if ch == '\0':
break
if ch == '\"' or ch == '\\' or ch < 0x20:
buf[pos] = '\\'
pos += 1
hi: int = (ch >> 4) & 0xF
lo: int = ch & 0xF
if hi < 10:
buf[pos] = '0' + hi
else:
buf[pos] = 'A' + (hi - 10)
pos += 1
if lo < 10:
buf[pos] = '0' + lo
else:
buf[pos] = 'A' + (lo - 10)
pos += 1
else:
buf[pos] = ch
pos += 1
buf[pos] = '\\'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = '"'
pos += 1
buf[pos] = '\0'
return buf
# ============================================================
# 获取 LLVM 整数类型的位宽
# ============================================================
def get_llvm_type_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Int(bits):
return bits
case _:
return 0
# ============================================================
# 获取 LLVM 浮点类型的位宽
# ============================================================
def get_llvm_float_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Float(bits):
return bits
case _:
return 0
# ============================================================
# 检查类型是否为 Ptr独立函数避免嵌套 match 的编译器 BUG
#
# 嵌套 match 的 REnum 检测在宿主编译器中有 BUG当 match 嵌套在
# 另一个 match 的 case 块中时,内层 match 走非 REnum 路径,
# 导致 case 列表为空。提取为独立函数可规避此问题。
# ============================================================
def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Ptr(pointee):
return 1
case _:
return 0
# ============================================================
# 检查类型是否为 Array独立函数避免嵌套 match 的编译器 BUG
# ============================================================
def is_array_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Array(_, _):
return 1
case _:
return 0
# ============================================================
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
#
# 处理两种情况:
# - ty 直接是 Struct → 检查是否注册
# - ty 是 Ptr(Struct) → 检查 pointee 是否注册
#
# 返回类名字符串或 None。用于 subscript 操作中判断是否需要
# 转发到 __getitem__/__setitem__ 而非生成 GEP。
# ============================================================
def _get_custom_struct_cls_nm(pool: memhub.MemBuddy | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> str:
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None"""
if ty is None:
return None
# 直接检查 ty 是否是注册的结构体
cls_nm_direct: str = HandlesStruct.get_class_name_by_type(pool, ty)
if cls_nm_direct is not None:
return cls_nm_direct
# 如果 ty 是 Ptr检查 pointee
if is_ptr_type(ty) != 0:
pointee_cs: llvmlite.LLVMType | t.CPtr = ty.Pointee
if pointee_cs is not None:
return HandlesStruct.get_class_name_by_type(pool, pointee_cs)
return None
# ============================================================
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...))load 解引用
#
# 用于支持 X|t.CPtr 类型变量的属性访问:
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
# - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
#
# 用 is_ptr_type 避免嵌套 match 的编译器 BUG
# ============================================================
def _deref_if_ptr_ptr(builder: llvmlite.IRBuilder | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""如果 obj_ptr 是 Ptr(Ptr(...))load 解引用获取内层指针"""
if obj_ptr is None or obj_ptr.Ty is None:
return obj_ptr
if is_ptr_type(obj_ptr.Ty) == 0:
return obj_ptr
pointee: llvmlite.LLVMType | t.CPtr = obj_ptr.Ty.Pointee
if pointee is None:
return obj_ptr
if is_ptr_type(pointee) == 0:
return obj_ptr
# obj_ptr 是 Ptr(Ptr(...))load 解引用
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, pointee, obj_ptr)
if loaded is None:
return obj_ptr
return loaded
# ============================================================
# 类型强制转换:整数 sext/trunc浮点 fpext/fptruncint↔float si2fp/fp2si
# ============================================================
def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr,
val: llvmlite.Value | t.CPtr,
target_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""将 val 强制转换为 target_ty
整数: sext扩展/ trunc截断
浮点: fpext扩展/ fptrunc截断
int→float: sitofp
float→int: fptosi
"""
if val is None or target_ty is None:
return val
# 整数转换
val_bits: int = get_llvm_type_bits(val.Ty)
target_bits: int = get_llvm_type_bits(target_ty)
# 整数 → 指针: inttoptr必须在纯整数转换之前检查因为 ptr 的 bits 非零会误入 sext/trunc 路径)
# 适用于: 跨模块方法返回 i64/i32默认推断目标变量是指针类型
if val_bits != 0 and is_ptr_type(target_ty) != 0:
return llvmlite.build_inttoptr(builder, val, target_ty)
# 指针 → 整数: ptrtoint当目标明确是整数而非指针时
if is_ptr_type(val.Ty) != 0 and target_bits != 0 and is_ptr_type(target_ty) == 0:
return llvmlite.build_ptrtoint(builder, val, target_ty)
if val_bits != 0 and target_bits != 0:
if val_bits == target_bits:
return val
if val_bits < target_bits:
return llvmlite.build_sext(builder, val, target_ty)
return llvmlite.build_trunc(builder, val, target_ty)
# 浮点转换
val_fbits: int = get_llvm_float_bits(val.Ty)
target_fbits: int = get_llvm_float_bits(target_ty)
if val_fbits != 0 and target_fbits != 0:
if val_fbits == target_fbits:
return val
if val_fbits < target_fbits:
return llvmlite.build_fpext(builder, val, target_ty)
return llvmlite.build_fptrunc(builder, val, target_ty)
# int → float
if val_bits != 0 and target_fbits != 0:
return llvmlite.build_si2fp(builder, val, target_ty)
# float → int
if val_fbits != 0 and target_bits != 0:
return llvmlite.build_fp2si(builder, val, target_ty)
# 指针 → 非指针值: build_load 解引用
# 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
# 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load
# 注意:使用独立函数 is_ptr_type 检查,避免嵌套 match 的编译器 BUG
if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) == 0:
return llvmlite.build_load(builder, target_ty, val)
# 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) != 0:
return llvmlite.build_bitcast(builder, val, target_ty)
return val
# ============================================================
# 创建全局字符串常量
# ============================================================
def create_global_string(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
str_val: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""创建全局字符串常量并返回 i8* bitcast"""
escaped: t.CChar | t.CPtr = escape_llvm_string(pool, str_val)
if escaped is None:
stdio.printf("[CGS] escape_llvm_string None\n")
stdio.fflush(0)
return None
slen: t.CSizeT = string.strlen(str_val)
count: int = slen + 1
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, count)
# 使用模块级计数器trans._label_counter避免跨函数命名冲突
str_idx: int = trans._label_counter
trans._label_counter += 1
# 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名
gv_name: t.CChar | t.CPtr = pool.alloc(48)
if gv_name is None:
stdio.printf("[CGS] gv_name alloc None\n")
stdio.fflush(0)
return None
if trans.ModuleSha1 is not None:
viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
else:
viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name, arr_ty)
if gv is None:
stdio.printf("[CGS] new_global_variable None name=%s\n", gv_name)
stdio.fflush(0)
return None
llvmlite.module_add_global(mod, gv)
# 去掉 private 链接和函数一致stub 中 declaretext 中 define
llvmlite.global_set_unnamed_addr(gv, 1)
llvmlite.global_set_constant(gv, 1)
gv.Initializer = escaped
arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty)
gv_ref_name: t.CChar | t.CPtr = pool.alloc(64)
if gv_ref_name is None:
stdio.printf("[CGS] gv_ref_name alloc None\n")
stdio.fflush(0)
return None
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
gv_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
bc: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
if bc is None:
stdio.printf("[CGS] build_bitcast None\n")
stdio.fflush(0)
return bc
# ============================================================
# 翻译常量表达式
# ============================================================
def translate_constant(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译常量int/str/bool"""
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn is None:
stdio.printf("[TC] cn is None\n")
stdio.fflush(0)
return None
ck: int = cn.const_kind
stdio.printf("[TC] const_kind=%d\n", ck)
stdio.fflush(0)
if cn.const_kind == ast.CONST_INT:
# 超出 i32 范围则用 i64避免常量创建时被截断
iv: t.CInt64T = cn.int_val
if iv > 2147483647 or iv < -2147483648:
return llvmlite.const_int64(pool, iv)
return llvmlite.const_int32(pool, iv)
elif cn.const_kind == ast.CONST_FLOAT:
# 浮点常量默认创建为 double64 位),赋值时由 coerce_to_type 自动 fptrunc
double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
elif cn.const_kind == ast.CONST_STR:
sv: str = cn.str_val
if sv is None:
stdio.printf("[TC] CONST_STR but str_val is None\n")
stdio.fflush(0)
return None
stdio.printf("[TC] CONST_STR sv[0]=%d\n", sv[0])
stdio.fflush(0)
r: llvmlite.Value | t.CPtr = create_global_string(builder, pool, mod, sv, trans)
if r is None:
stdio.printf("[TC] create_global_string returned None\n")
stdio.fflush(0)
return r
elif cn.const_kind == ast.CONST_BOOL:
if cn.int_val != 0:
return llvmlite.const_int32(pool, 1)
return llvmlite.const_int32(pool, 0)
elif cn.const_kind == ast.CONST_NONE:
# None → i8* null空指针常量用于 `p is None` / `p is not None` 比较
stdio.printf("[TC] NONE step1 Int8\n")
stdio.fflush(0)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if i8_ty is None:
stdio.printf("[TC] NONE Int8 alloc None\n")
stdio.fflush(0)
return None
stdio.printf("[TC] NONE step2 Ptr\n")
stdio.fflush(0)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
if i8_ptr_ty is None:
stdio.printf("[TC] NONE Ptr alloc None\n")
stdio.fflush(0)
return None
stdio.printf("[TC] NONE step3 ConstNull\n")
stdio.fflush(0)
rv_none: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
if rv_none is None:
stdio.printf("[TC] NONE ConstNull None\n")
stdio.fflush(0)
else:
stdio.printf("[TC] NONE ok\n")
stdio.fflush(0)
return rv_none
stdio.printf("[TC] unknown const_kind=%d\n", ck)
stdio.fflush(0)
return None
# ============================================================
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
#
# 用于 list 字面量 [a, b, c] 的元素类型推断
# ============================================================
def _infer_elem_type_from_value(pool: memhub.MemBuddy | t.CPtr,
value: llvmlite.Value | t.CPtr) -> str:
"""从 LLVM Value 的类型推断 list 元素类型名"""
if value is None or value.Ty is None:
return "str"
ty: llvmlite.LLVMType | t.CPtr = value.Ty
bits: int = get_llvm_type_bits(ty)
if bits != 0:
if bits == 32:
return "int"
if bits == 64:
return "CSizeT"
if bits == 8:
return "CInt8T"
if bits == 16:
return "CInt16T"
return "int"
if is_ptr_type(ty) != 0:
return "str"
fbits: int = get_llvm_float_bits(ty)
if fbits != 0:
if fbits == 64:
return "CDouble"
if fbits == 32:
return "CFloat"
return "str"
# ============================================================
# _find_pool_var — 查找上下文中的 pool 变量
#
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================
def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找上下文中的 pool 变量,返回 alloca 或 None"""
if trans is None:
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, "pool")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "_mbuddy")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "mbuddy")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "mb")
if alloca is not None:
return alloca
return None
# ============================================================
# translate_list_literal — 翻译 list 字面量 [a, b, c]
#
# 将 list 字面量翻译为 list[T](pool) 构造 + append 调用序列
#
# Args:
# builder: IRBuilder
# pool: 编译器内存池MemBuddy
# mod: LLVMModule
# node: ast.List 节点
# elem_type_name: 元素类型名(如 "str", "int"None 表示从元素推断
# trans: Translator 对象
#
# Returns:
# list 对象指针Ptr(list[T] struct)None 失败
# ============================================================
def translate_list_literal(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
elem_type_name: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
if node is None or trans is None:
return None
list_node: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
if list_node is None:
return None
elts: list[ast.AST | t.CPtr] | t.CPtr = list_node.elts
elts_count: t.CSizeT = 0
if elts is not None:
elts_count = elts.__len__()
# 推断元素类型
inferred_type_name: str = elem_type_name
first_elem_val: llvmlite.Value | t.CPtr = None
if inferred_type_name is None:
inferred_type_name = "str"
if elts_count > 0:
first_elem_node: ast.AST | t.CPtr = elts.get(0)
first_elem_val = translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
if first_elem_val is not None:
inferred_type_name = _infer_elem_type_from_value(pool, first_elem_val)
# 查找 pool 变量
pool_alloca: llvmlite.Value | t.CPtr = _find_pool_var(trans)
if pool_alloca is None:
HandlesType.fatal_error(node, "list literal requires pool variable in context")
return None
# load pool 变量值i8*
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
pool_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
if pool_val is None:
return None
# 特化 list[T] 类
type_args: list[str] | t.CPtr = list[str](pool, 4)
type_args.append(inferred_type_name)
spec_name: str = HandlesClassDef._specialize_generic_class(trans, "list", type_args)
if spec_name is None:
HandlesType.fatal_error(node, "list literal generic specialization failed")
return None
# 查找 struct entry
struct_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(spec_name)
if struct_entry is None:
HandlesType.fatal_error(node, "list literal specialized class not registered")
return None
struct_ty: llvmlite.LLVMType | t.CPtr = struct_entry.Ty
if struct_ty is None:
return None
# alloca 临时 list 空间
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
if tmp is None:
return None
# 存储指针:默认用 alloca如果有 __new__ 则用 __new__ 返回的指针
# __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用
# (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致,
# 避免为无 __new__ 的类生成过度声明)
storage_ptr: llvmlite.Value | t.CPtr = tmp
has_new_flag: int = 0
if struct_entry is not None:
has_new_flag = struct_entry.HasNew
if has_new_flag != 0:
new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if new_args is None:
return None
string.memset(new_args, 0, 8 * 32)
new_args[0] = t.CSizeT(pool_val)
new_ptr: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
if new_ptr is not None:
storage_ptr = new_ptr
# 调用 __before_init__(storage_ptr)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
init_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if init_args is None:
return None
string.memset(init_args, 0, 8 * 32)
init_args[0] = t.CSizeT(pool_val)
elem_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
init_args[1] = t.CSizeT(elem_size_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
# 对每个元素调用 append(storage_ptr, elem_val)
if elts_count > 0:
# 第一个元素可能已经翻译过
if first_elem_val is not None:
append_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if append_args is not None:
string.memset(append_args, 0, 8 * 32)
append_args[0] = t.CSizeT(first_elem_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
# 翻译剩余元素
i: t.CSizeT = 1
while i < elts_count:
elem_node_i: ast.AST | t.CPtr = elts.get(i)
elem_val_i: llvmlite.Value | t.CPtr = translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
if elem_val_i is not None:
ap_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if ap_args is not None:
string.memset(ap_args, 0, 8 * 32)
ap_args[0] = t.CSizeT(elem_val_i)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
i += 1
return storage_ptr
# ============================================================
# 翻译值表达式RHS 分派)— 模块级版本
# ============================================================
def translate_value(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译值表达式为 LLVM Value"""
if node is None:
return None
k: int = node.kind()
if k == ast.ASTKind.Constant:
rv: llvmlite.Value | t.CPtr = translate_constant(builder, pool, mod, node, trans)
if rv is None:
stdio.printf("[TV] Constant returned None\n")
stdio.fflush(0)
else:
stdio.printf("[TV] Constant ok\n")
stdio.fflush(0)
return rv
elif k == ast.ASTKind.Name:
return translate_name_value(builder, pool, node, trans)
elif k == ast.ASTKind.BinOp:
return HandlesExprOps.translate_binop(pool, builder, mod, node, trans)
elif k == ast.ASTKind.Call:
return HandlesExprCall.translate_call(pool, builder, mod, node,
funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Compare:
return translate_compare(builder, pool, mod, node, trans)
elif k == ast.ASTKind.UnaryOp:
return translate_unaryop(builder, pool, mod, node, trans)
elif k == ast.ASTKind.BoolOp:
return translate_boolop(builder, pool, mod, node,
funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Subscript:
return translate_subscript(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Attribute:
return translate_attribute(builder, pool, mod, node, trans)
elif k == ast.ASTKind.List:
return translate_list_literal(builder, pool, mod, node, None, trans)
elif k == ast.ASTKind.IfExp:
return translate_ifexp(builder, pool, mod, node, trans)
return None
# ============================================================
# 翻译三元表达式 IfExp(test, body, orelse)
#
# 生成: cond ? body : orelse
# then_blk: 翻译 body → br merge
# else_blk: 翻译 orelse → br merge
# merge_blk: phi [body_val, then_blk], [orelse_val, else_blk]
# ============================================================
def translate_ifexp(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译三元表达式 (body if test else orelse),返回 phi 结果 Value"""
if node is None or builder is None or trans is None:
return None
ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node)
if ie is None:
return None
func: llvmlite.Function | t.CPtr = trans._cur_func
if func is None:
stdio.printf("[IFEXP] func=None\n")
stdio.fflush(0)
return None
# 1. 求值条件
cond_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, ie.test, None, 0, trans)
if cond_val is None:
stdio.printf("[IFEXP] cond_val=None\n")
stdio.fflush(0)
return None
# 转换为 i1
cond_bits: int = get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1:
cond_i1: llvmlite.Value | t.CPtr = cond_val
else:
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
# 2. 创建三个基本块
cnt: int = trans._label_counter
trans._label_counter = cnt + 1
name_buf: t.CChar | t.CPtr = pool.alloc(32)
if name_buf is None:
return None
viperlib.snprintf(name_buf, 32, "ifexp.then.%d", cnt)
then_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "ifexp.else.%d", cnt)
else_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "ifexp.merge.%d", cnt)
merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
if then_bb is None or else_bb is None or merge_bb is None:
return None
# 3. 条件跳转
llvmlite.build_cond_br(builder, cond_i1, then_bb, else_bb)
# 4. then 分支
llvmlite.position_at_end(builder, then_bb)
body_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, ie.body, None, 0, trans)
then_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock
if body_val is None:
body_val = llvmlite.const_int32(pool, 0)
# 暂不 br等类型对齐后再 br
# 5. else 分支
llvmlite.position_at_end(builder, else_bb)
orelse_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, ie.orelse, None, 0, trans)
else_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock
if orelse_val is None:
orelse_val = llvmlite.const_int32(pool, 0)
# 暂不 br等类型对齐后再 br
# 类型对齐 (两分支类型可能不同)
# 必须在各自分支中执行 coerce_to_type确保 merge 块中 PHI 是第一条指令
# (否则 coerce_to_type 生成的 sext/zext/ptrtoint/inttoptr 指令在 PHI 之前,违反 LLVM 规则)
body_bits: int = get_llvm_type_bits(body_val.Ty)
orelse_bits: int = get_llvm_type_bits(orelse_val.Ty)
body_is_ptr: int = is_ptr_type(body_val.Ty)
orelse_is_ptr: int = is_ptr_type(orelse_val.Ty)
# 情况0: 一侧是指针,另一侧是整数(如 i64 vs i8* null
# 必须优先处理,因为 Ptr 的 bits=0 会导致下面三个分支全部跳过
# 选择 body_val.Ty 作为 phi_tythen 分支优先orelse_val 对齐到它
# coerce_to_type 内部会调用 ptrtoint指针→整数或 inttoptr整数→指针
if body_is_ptr != orelse_is_ptr:
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty)
elif body_bits > orelse_bits and orelse_bits > 0:
# 在 else 块中转换 orelse_val
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty)
elif orelse_bits > body_bits and body_bits > 0:
# 回到 then 块转换 body_val
llvmlite.position_at_end(builder, then_blk_for_phi)
body_val = coerce_to_type(builder, body_val, orelse_val.Ty)
elif body_bits == orelse_bits and body_bits > 0:
# bits 相同但类型不同(如 i64 vs Ptrnull 不能用于 i64 PHI
# 此分支理论上不会触发已被情况0覆盖保留作为防御
if is_ptr_type(body_val.Ty) != is_ptr_type(orelse_val.Ty):
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty)
# 现在分别在 then/else 块中 br 到 merge在类型转换之后
llvmlite.position_at_end(builder, then_blk_for_phi)
llvmlite.build_br(builder, merge_bb)
llvmlite.position_at_end(builder, else_blk_for_phi)
llvmlite.build_br(builder, merge_bb)
# 6. merge 块: phiPHI 是 merge 块的第一条指令)
llvmlite.position_at_end(builder, merge_bb)
phi_ty: llvmlite.LLVMType | t.CPtr = body_val.Ty
if phi_ty is None:
phi_ty = llvmlite.Int32(pool)
inc1: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(
pool, body_val, then_blk_for_phi)
inc2: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(
pool, orelse_val, else_blk_for_phi)
if inc1 is None or inc2 is None:
return None
# 链表顺序: inc1 (head) -> inc2 (tail)
# 之前错误写成 inc2.Next = inc1导致 build_phi 从 inc1 遍历时
# inc1.Next 为 None只输出一个 phi 条目,引发 LLVM 验证错误:
# "PHINode should have one entry for each predecessor"
inc1.Next = inc2
return llvmlite.build_phi(builder, phi_ty, inc1, 2)
# ============================================================
# 翻译比较表达式 Compare(left, ops, comparators)
# ============================================================
def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译比较表达式,返回 i1 结果"""
cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
if cmp is None:
return None
ops_list: list[t.CInt] | t.CPtr = (list[t.CInt] | t.CPtr)(cmp.ops)
if ops_list is None or ops_list.__len__() == 0:
return None
op: int = ops_list.get(0)
comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
if comparators is None or comparators.__len__() == 0:
stdio.printf("[CMP] comparators empty\n")
stdio.fflush(0)
return None
rhs: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, comparators.get(0), None, 0, trans)
if rhs is None:
stdio.printf("[CMP] rhs=None\n")
stdio.fflush(0)
return None
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
# 对于值类型变量(如 cnt: Counter用 alloca 指针尝试重载
lhs_node: ast.AST | t.CPtr = cmp.left
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
if nm is not None and nm.id is not None:
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, nm.id)
if lhs_alloca is not None:
ovl_result: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
pool, builder, mod, lhs_alloca, rhs, op, trans, 1)
if ovl_result is not None:
return ovl_result
# 正常翻译 lhs
lhs: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, cmp.left, None, 0, trans)
if lhs is None:
stdio.printf("[CMP] lhs=None\n")
stdio.fflush(0)
return None
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
pool, builder, mod, lhs, rhs, op, trans, 1)
if ovl_result2 is not None:
return ovl_result2
predicate: int = llvmlite.ICMP_SLT
if op == ast.OpKind.Lt:
predicate = llvmlite.ICMP_SLT
elif op == ast.OpKind.Le:
predicate = llvmlite.ICMP_SLE
elif op == ast.OpKind.Gt:
predicate = llvmlite.ICMP_SGT
elif op == ast.OpKind.Ge:
predicate = llvmlite.ICMP_SGE
elif op == ast.OpKind.Eq:
predicate = llvmlite.ICMP_EQ
elif op == ast.OpKind.Ne:
predicate = llvmlite.ICMP_NE
elif op == ast.OpKind.Is:
# `x is y` → 指针/值相等比较
predicate = llvmlite.ICMP_EQ
elif op == ast.OpKind.IsNot:
# `x is not y` → 指针/值不等比较
predicate = llvmlite.ICMP_NE
# 隐式类型转换:当一侧是指针(i8*),另一侧是整数时,
# 从指针加载第一个字节,使两侧类型一致
lhs_is_ptr: int = is_ptr_type(lhs.Ty)
rhs_is_ptr: int = is_ptr_type(rhs.Ty)
if lhs_is_ptr != 0 and rhs_is_ptr == 0:
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
lhs = llvmlite.build_load(builder, i8_ty, lhs)
elif rhs_is_ptr != 0 and lhs_is_ptr == 0:
i8_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
rhs = llvmlite.build_load(builder, i8_ty2, rhs)
# 类型对齐icmp 要求两边类型相同,将较窄的整数 sext 到较宽的类型
lhs_bits: int = get_llvm_type_bits(lhs.Ty)
rhs_bits: int = get_llvm_type_bits(rhs.Ty)
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
if lhs_bits < rhs_bits:
lhs = llvmlite.build_sext(builder, lhs, rhs.Ty)
else:
rhs = llvmlite.build_sext(builder, rhs, lhs.Ty)
return llvmlite.build_icmp(builder, predicate, lhs, rhs)
# ============================================================
# 翻译一元运算表达式 UnaryOp(op, operand)
# ============================================================
def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译一元运算(-x, +x, not x, ~x"""
uo: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uo is None:
return None
operand: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, uo.operand, None, 0, trans)
if operand is None:
return None
if uo.op == ast.OpKind.USub:
# -x = 0 - x
# 根据 operand 类型选择零值常量(避免 i32 与 i64 类型不匹配)
operand_bits: int = get_llvm_type_bits(operand.Ty)
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
if operand_bits == 64:
zero = llvmlite.const_int64(pool, 0)
elif operand_bits > 0 and operand_bits != 32:
# 其他位宽i8/i16 等):用 i32 零值operand 会被 coerce
zero = llvmlite.const_int32(pool, 0)
return llvmlite.build_sub(builder, zero, operand)
elif uo.op == ast.OpKind.UAdd:
# +x = x
return operand
elif uo.op == ast.OpKind.Not:
# not x = (x == 0),返回 i1
operand_bits_not: int = get_llvm_type_bits(operand.Ty)
zero = llvmlite.const_int32(pool, 0)
if operand_bits_not == 64:
zero = llvmlite.const_int64(pool, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero)
elif uo.op == ast.OpKind.Invert:
# ~x = x ^ -1
operand_bits_inv: int = get_llvm_type_bits(operand.Ty)
neg1: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, -1)
if operand_bits_inv == 64:
neg1 = llvmlite.const_int64(pool, -1)
return llvmlite.build_xor(builder, operand, neg1)
return operand
# ============================================================
# 翻译布尔运算 BoolOp(op, values) — 短路求值,返回 i1
#
# and: a and b and c → 若 a 为假短路到 merge(返回 a 的 i1),否则求 b
# 最后一个值直接求值并跳 merge
# or: a or b or c → 若 a 为真短路到 merge(返回 a 的 i1),否则求 b
# 最后一个值直接求值并跳 merge
# merge 块用 phi 合并所有入边的 i1
# ============================================================
def translate_boolop(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译布尔运算and/or的短路求值返回 i1"""
bo: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
if bo is None:
return None
op: int = bo.op
values: list[ast.AST | t.CPtr] | t.CPtr = bo.values
if values is None:
return None
count: t.CSizeT = values.__len__()
if count == 0:
return None
if count == 1:
return translate_value(builder, pool, mod, values.get(0),
funcs_ptr, func_count, trans)
func: llvmlite.Function | t.CPtr = builder.Func
if func is None:
return None
# 创建 merge BB用 builder.Counter 生成唯一标签名position_at_end 的
# move_to_end 保证 BB 文本顺序与控制流顺序一致,标签名用 counter 安全)
cnt: int = builder.Counter
builder.Counter = cnt + 1
name_buf: t.CChar | t.CPtr = pool.alloc(32)
if name_buf is None:
return None
viperlib.snprintf(name_buf, 32, "bool.merge.%d", cnt)
merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
# phi 入边链表头/尾
phi_head: llvmlite.PhiIncoming | t.CPtr = None
phi_tail: llvmlite.PhiIncoming | t.CPtr = None
phi_count: int = count - 1 # 循环次数
# 对每个值(除最后一个)求值并短路
for i in range(count - 1):
val_node: ast.AST | t.CPtr = values.get(i)
val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, val_node,
funcs_ptr, func_count, trans)
if val is None:
return None
# 转为 i1已经是 i1 的直接用,否则与 0 比较)
val_bits: int = get_llvm_type_bits(val.Ty)
val_i1: llvmlite.Value | t.CPtr = val
if val_bits != 1:
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero)
# 创建 next BB求值下一个值
cnt = builder.Counter
builder.Counter = cnt + 1
viperlib.snprintf(name_buf, 32, "bool.next.%d", cnt)
next_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
# and: 短路假→merge真→nextor: 短路真→merge假→next
if op == ast.OpKind.And:
llvmlite.build_cond_br(builder, val_i1, next_bb, merge_bb)
else:
llvmlite.build_cond_br(builder, val_i1, merge_bb, next_bb)
# 添加 phi 入边:(val_i1, cond_br 所在 BB)
cur_bb: llvmlite.BasicBlock | t.CPtr = builder.CurBlock
inc: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(pool, val_i1, cur_bb)
if phi_head is None:
phi_head = inc
phi_tail = inc
else:
phi_tail.Next = inc
phi_tail = inc
# 定位到 next BB求值下一个值
llvmlite.position_at_end(builder, next_bb)
# 最后一个值:求值后直接跳 merge
last_node: ast.AST | t.CPtr = values.get(count - 1)
last_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, last_node,
funcs_ptr, func_count, trans)
if last_val is None:
return None
last_bits: int = get_llvm_type_bits(last_val.Ty)
last_i1: llvmlite.Value | t.CPtr = last_val
if last_bits != 1:
zero = llvmlite.const_int32(pool, 0)
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, zero)
llvmlite.build_br(builder, merge_bb)
cur_bb = builder.CurBlock
inc = llvmlite.new_phi_incoming(pool, last_i1, cur_bb)
if phi_head is None:
phi_head = inc
else:
phi_tail.Next = inc
phi_count += 1
# 定位到 merge创建 phi 合并所有入边
llvmlite.position_at_end(builder, merge_bb)
i1_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count)
# ============================================================
# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .py 文件路径
#
# .pyi 文件可能未生成Phase1 未输出 pyi此时回退读取源 .py 文件。
# 从 StubMerger 全局存储器查找 SHA1 对应的相对路径,
# 根据 rel_path 前缀选择基础目录:
# - rel_path 以 "lib/" 开头 → App 源文件,用 {Config.SourceDir}/{rel_path}
# - 其他 → includes 文件,用 {Config.IncludesDir}/{rel_path}
#
# Returns:
# stdlib.malloc 分配的路径字符串(调用者负责 freeNone 失败
# ============================================================
def _build_source_path_from_sha1(sha1: str) -> str:
"""通过 SHA1 从全局存储器构建源 .py 文件路径"""
if sha1 is None:
return None
if Config.IncludesDir is None:
return None
# 从 StubMerger 全局存储器获取相对路径数组
rel_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
count: int = StubMerger.GetSha1StoreCount()
if rel_arr_ptr is None or count <= 0:
return None
sha1_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
if sha1_arr_ptr is None:
return None
# 遍历查找匹配的 SHA1
idx_s: int = -1
for i in range(count):
sidx: t.CSizeT = t.CSizeT(i) * 17
cur_sha1: str = sha1_arr_ptr + sidx
if cur_sha1[0] == '\0':
continue
if string.strcmp(cur_sha1, sha1) == 0:
idx_s = i
break
if idx_s < 0:
return None
# 获取相对路径MAX_REL_PATH_LEN=256避免跨模块 CDefine 解析问题)
MAX_RPL: t.CSizeT = 256
ridx: t.CSizeT = t.CSizeT(idx_s) * MAX_RPL
rel_path: str = rel_arr_ptr + ridx
if rel_path[0] == '\0':
return None
# 选择基础目录rel_path 以 "lib/" 开头 → App 源文件SourceDir否则 includes 文件
base_dir: str = Config.IncludesDir
is_app: int = 0
if string.strlen(rel_path) >= 4:
if (rel_path[0] == 'l' and rel_path[1] == 'i' and
rel_path[2] == 'b' and rel_path[3] == '/'):
is_app = 1
if is_app != 0 and Config.SourceDir is not None:
base_dir = Config.SourceDir
# 构建完整路径: {base_dir}/{rel_path}
base_len: t.CSizeT = string.strlen(base_dir)
rel_len: t.CSizeT = string.strlen(rel_path)
path_len: t.CSizeT = base_len + rel_len + 2
buf: str = stdlib.malloc(path_len)
if buf is None:
return None
viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_path)
return buf
# ============================================================
# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量
#
# 当当前模块的 CDefine 表中找不到某个 Name 时,从 from_imports
# 解析出源模块,再从该模块的 pyi 文件中解析 CDefine 常量值。
# 找到后缓存到当前模块的 CDefine 表中,避免重复查找。
#
# pyi 文件格式: "NAME: t.CDefine = value"
# ============================================================
def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
name: str,
from_imports: str) -> int:
"""跨模块查找 CDefine 常量,返回值或 -1未找到"""
# [XMOD-CD] 诊断:记录跨模块 CDefine 查找入口
_xmod_log_buf: str = pool.alloc(512)
if _xmod_log_buf is not None:
viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] enter name=%s from_imports=%s\n", name, from_imports)
_xmod_lf: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf.closed:
_xmod_lf.write_str(_xmod_log_buf)
_xmod_lf.close()
if name is None or from_imports is None:
return -1
# 1. 从 from_imports 查找名称对应的模块名
# allow_star_fallback=1: CDefine 常量(如 INVALID_HANDLE_VALUE通过
# from w32.win32base import * 导入,不会作为精确条目出现在 from_imports 中,
# 而是作为 *:w32.win32base 条目。必须启用 star import 回退才能找到源模块。
mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 1)
if mod_name_raw is None:
if _xmod_log_buf is not None:
viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] FAIL step1 mod=None name=%s\n", name)
_xmod_lf2: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf2.closed:
_xmod_lf2.write_str(_xmod_log_buf)
_xmod_lf2.close()
return -1
# 2. 复制模块名到新缓冲区lookup_from_import 返回的是内部指针)
# 截断于空格、null、或 ':'(别名格式的分隔符)
mod_len: t.CSizeT = 0
while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':':
mod_len += 1
mod_name_buf: str = pool.alloc(mod_len + 1)
if mod_name_buf is None:
return -1
for mi in range(mod_len):
mod_name_buf[mi] = mod_name_raw[mi]
mod_name_buf[mod_len] = '\0'
# 3. 去掉相对导入的前导点
base_mod: str = mod_name_buf
while base_mod[0] == '.':
base_mod = base_mod + 1
# 4. 查找模块的 SHA1
sha1: str = HandlesExprCall._lookup_module_sha1(base_mod)
if sha1 is None:
sha1 = HandlesExprCall._lookup_module_sha1_suffix(base_mod)
if sha1 is None:
if _xmod_log_buf is not None:
viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] FAIL step4 sha1=None base_mod=%s\n", base_mod)
_xmod_lf3: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf3.closed:
_xmod_lf3.write_str(_xmod_log_buf)
_xmod_lf3.close()
return -1
# [XMOD-CD] 诊断:记录 SHA1 查找成功
if _xmod_log_buf is not None:
viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] ok step4 base_mod=%s sha1=%s\n", base_mod, sha1)
_xmod_lf4: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf4.closed:
_xmod_lf4.write_str(_xmod_log_buf)
_xmod_lf4.close()
# 4.5 优先查全局跨模块 CDefine 表(编译期注册,无需文件 I/O
gcdef_val: int = HandlesType.lookup_global_cdefine(sha1, name)
if HandlesType.is_cdefine_found() != 0:
return gcdef_val
# 5. 获取 temp_dir
temp_dir: str = HandlesType.get_temp_dir()
if temp_dir is None:
return -1
# 6. 构建 pyi 文件路径
td_len: t.CSizeT = string.strlen(temp_dir)
pyi_path: str = StubMerger._sliced_path(temp_dir, td_len, sha1, "pyi")
if pyi_path is None:
return -1
# 7. 读取 pyi 文件;若不存在或内容过短(空 stub则回退读取源 .py 文件
PYI_READ_BUF_SIZE: t.CSizeT = 65536
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
if pyi_buf is None:
stdlib.free(pyi_path)
return -1
pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.R)
need_py_fallback: int = 0
bytes_read: LONG = 0
if pf.closed:
# .pyi 文件不存在:回退读取源 .py 文件
need_py_fallback = 1
else:
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
pf.close()
# pyi 内容过短(< 10 字节)视为空 stub回退 .py
if bytes_read < 10:
need_py_fallback = 1
else:
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
stdlib.free(pyi_path)
if need_py_fallback != 0:
# 从 SHA1 存储器查找相对路径,构建 {base_dir}/{rel_path}
src_path: str = _build_source_path_from_sha1(sha1)
if src_path is None:
# 存储器中找不到 SHA1 = 编译器 bugPhase1/Phase2 未填充存储器)
# 报错终止,不静默返回 -1避免后续生成错误的 @UNDEFINED 全局变量引用)
err_buf: str = pool.alloc(512)
if err_buf is not None:
viperlib.snprintf(err_buf, 512,
"跨模块 CDefine 查找失败SHA1 %s 不在存储器中base_mod=%s name=%s)。"
"Phase1/Phase2 未正确填充 PopulateSha1MapStore/AppendToSha1MapStore",
sha1, base_mod, name)
VLogger.error(err_buf, "XMOD-CD")
stdlib.free(pyi_buf)
return -1
# [XMOD-CD] 诊断:记录 src_path
_xmod_dbg_sp2: str = pool.alloc(512)
if _xmod_dbg_sp2 is not None:
viperlib.snprintf(_xmod_dbg_sp2, 512,
"[XMOD-CD] py-fallback src_path=%s sha1=%s\n", src_path, sha1)
_xmod_lf_sp2: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf_sp2.closed:
_xmod_lf_sp2.write_str(_xmod_dbg_sp2)
_xmod_lf_sp2.close()
pf = fileio.File(src_path, fileio.MODE.R)
stdlib.free(src_path)
if pf.closed:
# [XMOD-CD] 诊断:文件打开失败
_xmod_dbg_fc: str = pool.alloc(512)
if _xmod_dbg_fc is not None:
viperlib.snprintf(_xmod_dbg_fc, 512,
"[XMOD-CD] FAIL file-closed sha1=%s\n", sha1)
_xmod_lf_fc: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf_fc.closed:
_xmod_lf_fc.write_str(_xmod_dbg_fc)
_xmod_lf_fc.close()
stdlib.free(pyi_buf)
return -1
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
pf.close()
if bytes_read <= 0:
# [XMOD-CD] 诊断:文件读取失败
_xmod_dbg_br: str = pool.alloc(512)
if _xmod_dbg_br is not None:
viperlib.snprintf(_xmod_dbg_br, 512,
"[XMOD-CD] FAIL bytes_read=%d sha1=%s\n", bytes_read, sha1)
_xmod_lf_br: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf_br.closed:
_xmod_lf_br.write_str(_xmod_dbg_br)
_xmod_lf_br.close()
stdlib.free(pyi_buf)
return -1
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
# 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
# 格式: NAME: t.CDefine = value
name_len: t.CSizeT = string.strlen(name)
total_len: t.CSizeT = string.strlen(pyi_buf)
# [XMOD-CD] 诊断:记录文件读取结果和 total_len
_xmod_dbg1: str = pool.alloc(512)
if _xmod_dbg1 is not None:
_dbg_first80: str = pool.alloc(81)
if _dbg_first80 is not None:
_dbg_n: int = 0
while _dbg_n < 80 and _dbg_n < total_len:
_dbg_first80[_dbg_n] = pyi_buf[_dbg_n]
_dbg_n += 1
_dbg_first80[_dbg_n] = '\0'
else:
_dbg_first80 = "<alloc-fail>"
viperlib.snprintf(_xmod_dbg1, 512, "[XMOD-CD] file-read name=%s bytes_read=%d total_len=%d first80=%.80s\n",
name, bytes_read, total_len, _dbg_first80)
_xmod_lf_d1: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf_d1.closed:
_xmod_lf_d1.write_str(_xmod_dbg1)
_xmod_lf_d1.close()
pos: t.CSizeT = 0
result_val: int = 0
result_found: int = 0
while pos < total_len:
# 找到行首
line_start: t.CSizeT = pos
# 找到行尾
while pos < total_len and pyi_buf[pos] != '\n':
pos += 1
line_len: t.CSizeT = pos - line_start
if pos < total_len:
pos += 1 # 跳过 '\n'
# 检查行是否以 "name:" 开头
if line_len < name_len + 1:
continue
match_ok: int = 1
for ki in range(name_len):
if pyi_buf[line_start + ki] != name[ki]:
match_ok = 0
break
if match_ok == 0:
continue
if pyi_buf[line_start + name_len] != ':':
continue
# 检查行是否包含 "t.CDefine"
# 在行中查找 "t.CDefine"
has_cdefine: int = 0
ci: t.CSizeT = line_start + name_len + 1
while ci + 8 <= line_start + line_len:
if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and
pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and
pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and
pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and
pyi_buf[ci + 8] == 'e'):
has_cdefine = 1
break
ci += 1
if has_cdefine == 0:
continue
# 找到 "=" 后面的值
eq_pos: t.CSizeT = line_start + name_len + 1
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=':
eq_pos += 1
if eq_pos >= line_start + line_len:
continue
eq_pos += 1 # 跳过 '='
# 跳过空格
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 解析整数值(支持十六进制 0x 前缀)
val_str_start: t.CSizeT = eq_pos
val_str_len: t.CSizeT = 0
is_hex: int = 0
if eq_pos + 1 < line_start + line_len:
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
is_hex = 1
val_str_len = 2
eq_pos += 2
while eq_pos < line_start + line_len:
ch: t.CChar = pyi_buf[eq_pos]
if is_hex == 1:
if ((ch >= '0' and ch <= '9') or
(ch >= 'a' and ch <= 'f') or
(ch >= 'A' and ch <= 'F')):
val_str_len += 1
eq_pos += 1
else:
break
else:
if ch >= '0' and ch <= '9':
val_str_len += 1
eq_pos += 1
else:
break
if val_str_len == 0:
continue
# 复制值字符串到新缓冲区并解析
val_buf: str = pool.alloc(val_str_len + 1)
if val_buf is None:
continue
for vi in range(val_str_len):
val_buf[vi] = pyi_buf[val_str_start + vi]
val_buf[val_str_len] = '\0'
if is_hex == 1:
# 手动解析十六进制(跳过 0x 前缀)
hex_result: int = 0
hex_buf: str = val_buf + 2 # 跳过 "0x"
for hi in range(val_str_len - 2):
hc: t.CChar = hex_buf[hi]
hd: int = 0
if hc >= '0' and hc <= '9':
hd = hc - '0'
elif hc >= 'a' and hc <= 'f':
hd = hc - 'a' + 10
elif hc >= 'A' and hc <= 'F':
hd = hc - 'A' + 10
else:
break
hex_result = hex_result * 16 + hd
result_val = hex_result
result_found = 1
else:
result_val = string.atoi(val_buf)
result_found = 1
break
# 9.5 如果当前模块文件中没找到,递归查找子模块
# 处理 from .X import NAME 和 from .X import * 的情况
# 例如 ast.__init__.py 中 from .base import CONST_INT, ...
# 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1
if result_found == 0:
name_len_rc: t.CSizeT = string.strlen(name)
rc_pos: t.CSizeT = 0
while rc_pos < total_len and result_found == 0:
# 找到行首
rc_line_start: t.CSizeT = rc_pos
# 找到行尾
while rc_pos < total_len and pyi_buf[rc_pos] != '\n':
rc_pos += 1
rc_line_len: t.CSizeT = rc_pos - rc_line_start
if rc_pos < total_len:
rc_pos += 1
# 检查行是否以 "from ." 开头(相对导入子模块)
if rc_line_len < 12:
continue
if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \
pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \
pyi_buf[rc_line_start+4] != ' ' or pyi_buf[rc_line_start+5] != '.':
continue
# 提取子模块名(从 rc_line_start+6 开始,到空格/tab 为止)
rc_sub_start: t.CSizeT = rc_line_start + 6
rc_sub_end: t.CSizeT = rc_sub_start
while rc_sub_end < rc_line_start + rc_line_len and \
pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t':
rc_sub_end += 1
rc_sub_len: t.CSizeT = rc_sub_end - rc_sub_start
if rc_sub_len == 0:
continue
# [XMOD-CD] 诊断:记录找到的 from . 行和子模块名
_xmod_dbg_rc: str = pool.alloc(512)
if _xmod_dbg_rc is not None:
_rc_sub_buf_dbg: str = pool.alloc(rc_sub_len + 1)
if _rc_sub_buf_dbg is not None:
_rc_si2: t.CSizeT
for _rc_si2 in range(rc_sub_len):
_rc_sub_buf_dbg[_rc_si2] = pyi_buf[rc_sub_start + _rc_si2]
_rc_sub_buf_dbg[rc_sub_len] = '\0'
else:
_rc_sub_buf_dbg = "<alloc-fail>"
viperlib.snprintf(_xmod_dbg_rc, 512,
"[XMOD-CD] rc-from-line name=%s sub=%s rc_line_start=%d rc_line_len=%d\n",
name, _rc_sub_buf_dbg, rc_line_start, rc_line_len)
_xmod_lf_rc: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf_rc.closed:
_xmod_lf_rc.write_str(_xmod_dbg_rc)
_xmod_lf_rc.close()
# 检查行是否包含 "import"
rc_has_import: int = 0
rc_ipos: t.CSizeT = rc_sub_end
while rc_ipos + 6 <= rc_line_start + rc_line_len:
if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and \
pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and \
pyi_buf[rc_ipos+4] == 'r' and pyi_buf[rc_ipos+5] == 't':
rc_has_import = 1
break
rc_ipos += 1
if rc_has_import == 0:
continue
# 检查 NAME 是否在导入列表中,或导入是 *
# 处理多行导入from .X import (... NAME, ...)
# 从 "import" 之后扫描,如果有 "(" 则扫描到 ")" 为止
rc_imp_start: t.CSizeT = rc_ipos + 6
rc_star: int = 0
rc_name_match: int = 0
# 确定扫描范围:单行或跨行(括号内)
rc_scan_end: t.CSizeT = rc_line_start + rc_line_len
rc_has_paren: int = 0
rc_pp: t.CSizeT = rc_imp_start
while rc_pp < rc_line_start + rc_line_len:
if pyi_buf[rc_pp] == '(':
rc_has_paren = 1
break
if pyi_buf[rc_pp] == '*':
rc_star = 1
rc_pp += 1
if rc_has_paren != 0:
# 多行导入:从 "(" 之后扫描到 ")" 为止
rc_scan_end = rc_pp + 1
rc_paren_closed: int = 0
rc_sp: t.CSizeT = rc_pp + 1
while rc_sp < total_len and rc_paren_closed == 0:
if pyi_buf[rc_sp] == ')':
rc_paren_closed = 1
rc_scan_end = rc_sp
break
rc_sp += 1
if rc_paren_closed == 0:
rc_scan_end = total_len
# 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 *
rc_cpos: t.CSizeT = rc_imp_start
while rc_cpos < rc_scan_end:
rc_ch: t.CChar = pyi_buf[rc_cpos]
if rc_ch == '*':
rc_star = 1
# 检查 NAME 是否匹配(作为完整单词)
if rc_cpos + name_len_rc <= rc_scan_end:
rc_match: int = 1
rc_ki: t.CSizeT
for rc_ki in range(name_len_rc):
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
rc_match = 0
break
if rc_match == 1:
# 检查前字符是否是非标识符字符
rc_before_ok: int = 0
if rc_cpos == rc_imp_start:
rc_before_ok = 1
else:
rc_before_ch: t.CChar = pyi_buf[rc_cpos - 1]
if rc_before_ch == ' ' or rc_before_ch == '\t' or \
rc_before_ch == '(' or rc_before_ch == ',' or \
rc_before_ch == '\n' or rc_before_ch == '\r':
rc_before_ok = 1
# 检查后字符是否是非标识符字符
rc_after_ok: int = 0
rc_after_pos: t.CSizeT = rc_cpos + name_len_rc
if rc_after_pos >= rc_scan_end:
rc_after_ok = 1
else:
rc_after_ch: t.CChar = pyi_buf[rc_after_pos]
if rc_after_ch == ' ' or rc_after_ch == '\t' or \
rc_after_ch == ')' or rc_after_ch == ',' or \
rc_after_ch == '\r' or rc_after_ch == '\n':
rc_after_ok = 1
if rc_before_ok == 1 and rc_after_ok == 1:
rc_name_match = 1
rc_cpos += 1
# [XMOD-CD] 诊断:记录 NAME 匹配结果
_xmod_dbg_match: str = pool.alloc(512)
if _xmod_dbg_match is not None:
viperlib.snprintf(_xmod_dbg_match, 512,
"[XMOD-CD] rc-match name=%s star=%d name_match=%d has_paren=%d rc_scan_end=%d rc_imp_start=%d\n",
name, rc_star, rc_name_match, rc_has_paren, rc_scan_end, rc_imp_start)
_xmod_lf_m: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf_m.closed:
_xmod_lf_m.write_str(_xmod_dbg_match)
_xmod_lf_m.close()
if rc_star == 0 and rc_name_match == 0:
continue
# 复制子模块名到新缓冲区pyi_buf + rc_sub_start 不是 NUL 结尾)
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
if rc_sub_buf is None:
continue
rc_si: t.CSizeT
for rc_si in range(rc_sub_len):
rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
rc_sub_buf[rc_sub_len] = '\0'
# 构建完整子模块名 base_mod.X
rc_full_len: t.CSizeT = string.strlen(base_mod) + 1 + rc_sub_len + 1
rc_full_mod: str = pool.alloc(rc_full_len)
if rc_full_mod is None:
continue
viperlib.snprintf(rc_full_mod, rc_full_len, "%s.%s", base_mod, rc_sub_buf)
# 构建临时 from_imports "name:full_mod"
rc_fi_len: t.CSizeT = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
rc_fi: str = pool.alloc(rc_fi_len)
if rc_fi is None:
continue
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
# [XMOD-CD] 诊断:记录递归调用前的状态
_xmod_dbg_recurse: str = pool.alloc(512)
if _xmod_dbg_recurse is not None:
viperlib.snprintf(_xmod_dbg_recurse, 512,
"[XMOD-CD] rc-recurse name=%s full_mod=%s fi=%s star=%d name_match=%d has_paren=%d scan_end=%d\n",
name, rc_full_mod, rc_fi, rc_star, rc_name_match, rc_has_paren, rc_scan_end)
_xmod_lf_rec: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf_rec.closed:
_xmod_lf_rec.write_str(_xmod_dbg_recurse)
_xmod_lf_rec.close()
# 递归调用查找子模块
rc_sub_val: int = _lookup_cross_module_cdefine(pool, name, rc_fi)
if HandlesType.is_cdefine_found() != 0:
result_val = rc_sub_val
result_found = 1
stdlib.free(pyi_buf)
# 10. 缓存到当前模块的 CDefine 表中
if result_found != 0:
HandlesType.register_cdefine_constant(pool, name, result_val)
HandlesType.set_cdefine_found(1)
else:
HandlesType.set_cdefine_found(0)
# [XMOD-CD] 诊断:记录查找结果
if _xmod_log_buf is not None:
if result_found != 0:
viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] FOUND name=%s val=%d base_mod=%s\n", name, result_val, base_mod)
else:
viperlib.snprintf(_xmod_log_buf, 512, "[XMOD-CD] NOTFOUND name=%s base_mod=%s sha1=%s\n", name, base_mod, sha1)
_xmod_lf5: fileio.File | t.CPtr = fileio.File(
"d:/Users/TermiNexus/Desktop/TransPyC/_xmod_cdefine.log", fileio.MODE.A)
if not _xmod_lf5.closed:
_xmod_lf5.write_str(_xmod_log_buf)
_xmod_lf5.close()
return result_val
# ============================================================
# 翻译变量引用Name 节点)→ load
# ============================================================
def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译变量引用Name 节点)→ load"""
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm is None:
return None
nm_id: str = nm.id
if nm_id is None:
return None
# CDefine 编译期常量: 直接返回整数常量值(不生成运行时代码)
# NAME: t.CDefine = value 形式定义的常量在编译期已注册到全局表
cdef_val: int = HandlesType.lookup_cdefine_constant(nm_id)
if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_val)
# 本地 CDefine 表未找到:尝试跨模块查找
# 对于 from w32.win32base import * 导入的 INVALID_HANDLE_VALUE 等常量,
# CDefine 表在模块切换时被清空,需要从 from_imports 查找来源模块并读取 .pyi
if trans is not None:
if trans._from_imports is not None:
cdef_val = _lookup_cross_module_cdefine(pool, nm_id, trans._from_imports)
if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_val)
# 模块别名检查:如果 nm_id 是已导入模块名(如 win32file, fileio
# 不应被当作普通变量或全局变量,返回 None 让上层处理
if trans is not None and trans._imported_modules is not None:
if HandlesImports.is_module_imported(trans._imported_modules, nm_id) != 0:
return None
# global 变量:从模块作用域查找
if trans is not None:
if HT.is_global_name(trans, nm_id) != 0:
mod_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
trans.SymTab, nm_id)
if mod_alloca is not None:
load_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
load_ty = mod_alloca.Ty.Pointee
if load_ty is None:
load_ty = llvmlite.Int32(pool)
return llvmlite.build_load(builder, load_ty, mod_alloca)
# nonlocal 变量:通过闭包 env 访问
if HT.is_nonlocal_name(trans, nm_id) != 0:
return HandlesNonlocal.load_nonlocal_var(trans, nm_id)
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm_id)
if alloca is None:
# 跨模块 CDefine 查找(仅当 Name 不是任何变量时才尝试):
# 从 from_imports 解析源模块,再从该模块的 pyi 文件中解析 CDefine 常量值
# (如 FLAG_IS_ASYNC 从 base.py 导入)
if trans is not None and trans._from_imports is not None:
cross_val: int = _lookup_cross_module_cdefine(pool, nm_id, trans._from_imports)
if cross_val >= 0:
return llvmlite.const_int32(pool, cross_val)
return None
load_ty: llvmlite.LLVMType | t.CPtr = None
if alloca.Ty is not None:
load_ty = alloca.Ty.Pointee
if load_ty is None:
load_ty = llvmlite.Int32(pool)
return llvmlite.build_load(builder, load_ty, alloca)
# ============================================================
# ExprHandle - 表达式处理器Mixin 继承模式)
#
# HandleValue 提供 trans 接口,委托到模块级 translate_value
# ============================================================
@t.NoVTable
class ExprHandle(HandlesBase.Mixin):
"""表达式处理器:继承 Mixin 获得 Trans 回指针"""
def __init__(self, trans: HT.Translator | t.CPtr):
self.Trans = trans
# ============================================================
# HandleValue - 翻译值表达式为 LLVM Value
# ============================================================
def HandleValue(self, node: ast.AST | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译值表达式,从 self.Trans 获取共享状态"""
return translate_value(
self.Trans._cur_builder, self.Trans.Pool, self.Trans.Module,
node,
self.Trans._funcs, self.Trans._func_count, self.Trans)
# ============================================================
# NewExprHandle - 工厂函数
# ============================================================
def NewExprHandle(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr) -> ExprHandle | t.CPtr:
h: ExprHandle | t.CPtr = pool.alloc(ExprHandle.__sizeof__())
if h is None:
return None
string.memset(h, 0, ExprHandle.__sizeof__())
h.Trans = trans
return h
# ============================================================
# list_getitem_inline — 内联生成 list[T] __getitem__ 逻辑
#
# list 结构布局 (6 字段 × 8 字节):
# __data__(0) __count__(8) __capacity__(16) __pool__(24) __elem_size__(32) __iter_index__(40)
# 返回元素地址 (i8*),调用方根据元素类型 load 正确的值
# ============================================================
def list_getitem_inline(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr,
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""内联生成 list __getitem__ 逻辑,返回元素地址 (i8*)"""
if builder is None or pool is None or lm_obj is None or idx_val is None:
return None
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 加载 __data__ (偏移 0, index 0)
g_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
g_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx0)
if g_dpp is None:
return None
g_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, g_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
g_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
g_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx4)
if g_epp is None:
return None
g_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, g_epp)
if g_data is None or g_esize is None:
return None
g_idx_i64: llvmlite.Value | t.CPtr = coerce_to_type(builder, idx_val, i64_ty)
if g_idx_i64 is None:
return None
g_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, g_idx_i64, g_esize)
if g_offset is None:
return None
# 返回元素地址 (i8*),不 load
return llvmlite.build_gep(builder, i8_ty, g_data, g_offset)
# ============================================================
# _list_elem_type_from_name — 从 list 类型名提取元素 LLVM 类型
#
# 类型名格式: <sha1>.list[<elem_type>]
# 用 strstr 检查完整模式,避免子串提取
# 支持: list[int]→i32, list[str]/list[bytes]→i8*, list[CInt]→i32 等
# 默认: i32
# ============================================================
def _list_elem_type_from_name(pool: memhub.MemBuddy | t.CPtr,
struct_name: str) -> llvmlite.LLVMType | t.CPtr:
"""从 list 类型名提取元素 LLVM 类型"""
if pool is None or struct_name is None:
return None
# 用 strstr 检查完整模式
if string.strstr(struct_name, "list[int]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt8T]") is not None:
return llvmlite.Int8(pool)
if string.strstr(struct_name, "list[CInt16T]") is not None:
return llvmlite.Int16(pool)
if string.strstr(struct_name, "list[CInt32T]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt64T]") is not None:
return llvmlite.Int64(pool)
if string.strstr(struct_name, "list[CSizeT]") is not None:
return llvmlite.Int64(pool)
if string.strstr(struct_name, "list[CDouble]") is not None:
return llvmlite.Double(pool)
if string.strstr(struct_name, "list[CFloat]") is not None:
return llvmlite.Float(pool)
if string.strstr(struct_name, "list[CChar]") is not None:
return llvmlite.Int8(pool)
# str/bytes/CPtr/其他指针类型 → i8*
i8_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
return llvmlite.Ptr(pool, i8_ty_d)
# ============================================================
# list_setitem_inline — 内联生成 list[T] __setitem__ 逻辑
#
# 直接存储 rhs_val 到元素地址 (coerce 为 i8* 后 store)
# ============================================================
def list_setitem_inline(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr,
idx_val: llvmlite.Value | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr) -> int:
"""内联生成 list __setitem__ 逻辑,返回 0"""
if builder is None or pool is None or lm_obj is None or idx_val is None or rhs_val is None:
return 0
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 加载 __data__ (偏移 0, index 0)
s_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
s_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx0)
if s_dpp is None:
return 0
s_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, s_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
s_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
s_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx4)
if s_epp is None:
return 0
s_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, s_epp)
if s_data is None or s_esize is None:
return 0
s_idx_i64: llvmlite.Value | t.CPtr = coerce_to_type(builder, idx_val, i64_ty)
if s_idx_i64 is None:
return 0
s_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, s_idx_i64, s_esize)
if s_offset is None:
return 0
s_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s_data, s_offset)
if s_addr is None:
return 0
s_val_ptr: llvmlite.Value | t.CPtr = coerce_to_type(builder, rhs_val, i8_ptr_ty)
if s_val_ptr is None:
return 0
llvmlite.build_store(builder, s_val_ptr, s_addr)
return 0
# ============================================================
# is_list_subscript — 检查 Subscript 节点是否是 list[T] 类型
#
# 返回 list 对象指针 (Ptr(list[T])) 或 None
# ============================================================
def is_list_subscript(node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""检查 Subscript 节点是否是 list[T] 类型,返回 list 对象指针或 None"""
if node is None or trans is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None or sub.value is None:
return None
if sub.value.kind() != ast.ASTKind.Name:
return None
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm is None or nm.id is None:
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is None or alloca.Ty is None:
return None
if is_ptr_type(alloca.Ty) == 0:
return None
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
return None
# 检查 pointee 是否是 Ptr(Struct)
inner_ty: llvmlite.LLVMType | t.CPtr = None
match pointee:
case llvmlite.LLVMType.Ptr(it):
inner_ty = it
case _:
return None
if inner_ty is None:
return None
# 检查 inner_ty 是否是 Struct 且名称包含 "list["
struct_name: str = None
match inner_ty:
case llvmlite.LLVMType.Struct(_, _, sname):
struct_name = sname
case _:
return None
if struct_name is None:
return None
if string.strstr(struct_name, "list[") is None:
return None
# 是 list 类型: load 出 list 对象指针
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
return llvmlite.build_load(builder, pointee, alloca)
# ============================================================
# 翻译下标表达式 Subscript(value, slice, ctx) — ptr[i] / arr[i]
#
# 指针遍历: ptr 是指针变量load 出指针值后 GEP + load
# 数组遍历: arr 是数组变量,用 alloca 指针做双索引 GEP [0, i] + load
# list[T]: 泛型类不注册 structsubscript 走 __getitem__ 内联路径
# ============================================================
def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译下标表达式,返回加载的元素值"""
if node is None or builder is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None:
return None
# 翻译索引
idx_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, sub.slice, None, 0, trans)
if idx_val is None:
return None
# 如果 value 是 Name尝试从 alloca 类型推断
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is not None and trans is not None:
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is None:
stdio.printf("[TS] alloca=None var=%s\n", nm.id)
stdio.fflush(0)
elif alloca.Ty is None:
stdio.printf("[TS] alloca.Ty=None var=%s\n", nm.id)
stdio.fflush(0)
if alloca is not None and alloca.Ty is not None:
if is_ptr_type(alloca.Ty) != 0:
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
stdio.printf("[TS] pointee=None var=%s\n", nm.id)
stdio.fflush(0)
if pointee is not None:
# 不调用 pointee.kind() 避免跨模块引用 LLVMType.kind 符号
stdio.printf("[TS] var=%s pointee_not_null\n", nm.id)
stdio.fflush(0)
# 单层 match避免嵌套 match 的编译器 bug
match pointee:
case llvmlite.LLVMType.Array(elem_ty, count):
# 数组遍历: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, pointee, elem_ty, alloca, idx_val)
if elem_ptr is not None:
return llvmlite.build_load(builder, elem_ty, elem_ptr)
return None
case llvmlite.LLVMType.Ptr(inner_ty):
stdio.printf("[TS] matched Ptr var=%s\n", nm.id)
stdio.fflush(0)
# 检查 inner_ty 是否是 list[T] 类型(泛型类不注册 struct
# list 的 subscript 应该走 __getitem__ 内联路径,而非指针遍历
list_struct_name: str = None
match inner_ty:
case llvmlite.LLVMType.Struct(_, _, lsn):
list_struct_name = lsn
case _:
pass
if list_struct_name is not None:
if string.strstr(list_struct_name, "list[") is not None:
# list[T] 类型: 内联生成 __getitem__ 逻辑
ptr_val_list: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_list is not None:
elem_addr_list: llvmlite.Value | t.CPtr = list_getitem_inline(
builder, pool, ptr_val_list, idx_val)
if elem_addr_list is not None:
# 根据类型名提取元素类型bitcast 后 load
elem_ty_list: llvmlite.LLVMType | t.CPtr = _list_elem_type_from_name(
pool, list_struct_name)
if elem_ty_list is not None:
elem_ptr_ty_list: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, elem_ty_list)
casted_addr_list: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, elem_addr_list, elem_ptr_ty_list)
if casted_addr_list is not None:
return llvmlite.build_load(
builder, elem_ty_list, casted_addr_list)
return None
# inner_ty 是 Ptr 说明 alloca.Ty 是三重指针 Ptr(Ptr(Ptr(...))),
# 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。
# 此时 [i] 应该是指针解引用 (步长 sizeof(ptr)=8), 而非调用
# 结构体的 __getitem__ 方法 (会错误生成 AST.__getitem__ 调用)。
# 对比 HashTable|t.CPtr: alloca.Ty=HashTable**, inner_ty=Struct(HashTable)
# (非 Ptr), 仍走下面的方法调用转发路径。
if is_ptr_type(inner_ty) != 0:
ptr_val_pp: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_pp is not None:
elem_ptr_pp: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, inner_ty, ptr_val_pp, idx_val)
if elem_ptr_pp is not None:
return llvmlite.build_load(builder, inner_ty, elem_ptr_pp)
return None
# 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
# 优先转发到 __getitem__避免 IsPtrElement 误判为指针遍历
cls_nm_rd_ptr: str = _get_custom_struct_cls_nm(pool, inner_ty)
if cls_nm_rd_ptr is None:
stdio.printf("[TS] cls_nm=None var=%s\n", nm.id)
stdio.fflush(0)
if not (cls_nm_rd_ptr is None):
stdio.printf("[TS] cls_nm=%s var=%s\n", cls_nm_rd_ptr, nm.id)
stdio.fflush(0)
stdio.printf("[TS] pre_getitem cls=%s\n", cls_nm_rd_ptr)
stdio.fflush(0)
obj_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if obj_ptr_rd is None:
stdio.printf("[TS] __getitem__ build_load=None\n")
stdio.fflush(0)
if not (obj_ptr_rd is None):
arg_vals_rd_p: t.CSizeT | t.CPtr = pool.alloc(8)
if arg_vals_rd_p is None:
stdio.printf("[TS] __getitem__ alloc=None\n")
stdio.fflush(0)
if not (arg_vals_rd_p is None):
arg_vals_rd_p[0] = t.CSizeT(idx_val)
ret_rd_p: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_rd_ptr, "__getitem__",
obj_ptr_rd, arg_vals_rd_p, 1, trans)
if ret_rd_p is None:
stdio.printf("[TS] __getitem__ call=None cls=%s\n", cls_nm_rd_ptr)
stdio.fflush(0)
if not (ret_rd_p is None):
return ret_rd_p
return None
# bytes|t.CPtr / str|t.CPtr: alloca 是 i8**
# 直接 GEP 按 i8* 步长8 字节load i8*
# 仅当 inner_ty 不是自定义结构体时才走指针遍历
if trans is not None:
ve_pe_ts: HandlesVar.VarEntry | t.CPtr = \
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
if ve_pe_ts is not None and ve_pe_ts.IsPtrElement == 1:
elem_ptr_ts: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, pointee, alloca, idx_val)
if elem_ptr_ts is not None:
return llvmlite.build_load(builder, pointee, elem_ptr_ts)
return None
# 普通指针遍历: 先 load 指针值,再 GEP
ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val is not None:
elem_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, inner_ty, ptr_val, idx_val)
if elem_ptr2 is not None:
return llvmlite.build_load(builder, inner_ty, elem_ptr2)
return None
case _:
# 自定义类(结构体类型): 转发到 __getitem__ 方法调用
# 如 hashtable[key] → hashtable.__getitem__(key)
cls_nm_sub: str = HandlesStruct.get_class_name_by_type(pool, pointee)
if cls_nm_sub is None:
pass
if not (cls_nm_sub is None):
obj_val_sub: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if not (obj_val_sub is None):
arg_vals_sub: t.CSizeT | t.CPtr = pool.alloc(8)
if not (arg_vals_sub is None):
arg_vals_sub[0] = t.CSizeT(idx_val)
ret_sub: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_sub, "__getitem__",
obj_val_sub, arg_vals_sub, 1, trans)
if not (ret_sub is None):
return ret_sub
pass
# 通用路径:翻译 value 获取指针
ptr_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, sub.value, None, 0, trans)
if ptr_val is None or ptr_val.Ty is None:
return None
# 检查是否是指针类型
if is_ptr_type(ptr_val.Ty) != 0:
elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
if elem_ty2 is not None:
# 数组类型: 双索引 GEP + load 元素
match elem_ty2:
case llvmlite.LLVMType.Array(arr_elem_ty2, arr_count2):
elem_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, elem_ty2, arr_elem_ty2, ptr_val, idx_val)
if elem_ptr3 is not None:
return llvmlite.build_load(builder, arr_elem_ty2, elem_ptr3)
return None
case _:
pass
# 自定义结构体 (如 self._ht[key]): 转发到 __getitem__ 方法调用
cls_nm_rd_gen: str = _get_custom_struct_cls_nm(pool, elem_ty2)
if cls_nm_rd_gen is None:
pass
if not (cls_nm_rd_gen is None):
arg_vals_rd_g: t.CSizeT | t.CPtr = pool.alloc(8)
if not (arg_vals_rd_g is None):
arg_vals_rd_g[0] = t.CSizeT(idx_val)
ret_rd_g: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_rd_gen, "__getitem__",
ptr_val, arg_vals_rd_g, 1, trans)
if not (ret_rd_g is None):
return ret_rd_g
return None
# 指针类型: 单索引 GEP + load
elem_ptr4: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, elem_ty2, ptr_val, idx_val)
if elem_ptr4 is not None:
return llvmlite.build_load(builder, elem_ty2, elem_ptr4)
return None
# ============================================================
# find_global_in_module - 在模块全局变量链表中按名称查找
#
# 查找策略:
# 1. 精确匹配 (如 "_mbuddy" 匹配 @_mbuddy)
# 2. 后缀匹配 (如 "_mbuddy" 匹配 @"sha1._mbuddy")
# ============================================================
def find_global_in_module(mod: llvmlite.LLVMModule | t.CPtr,
name: str) -> llvmlite.GlobalVariable | t.CPtr:
"""在模块全局变量链表中按名称查找全局变量"""
if mod is None or name is None:
return None
name_len: t.CSizeT = string.strlen(name)
cur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
# 第一遍: 精确匹配
while cur is not None:
if cur.Name is not None:
if string.strcmp(cur.Name, name) == 0:
return cur
cur = cur.Next
# 第二遍: 后缀匹配 (.name)
cur = mod.GlobalHead
while cur is not None:
if cur.Name is not None:
cur_len: t.CSizeT = string.strlen(cur.Name)
if cur_len > name_len + 1:
suffix_start: t.CSizeT = cur_len - name_len
if cur.Name[suffix_start - 1] == '.':
match: int = 1
for i in range(name_len):
if cur.Name[suffix_start + i] != name[i]:
match = 0
break
if match == 1:
return cur
cur = cur.Next
return None
# ============================================================
# make_global_ref - 创建全局变量引用 (@name类型为 ty*)
#
# 用于跨模块全局变量访问: stub 未注入时,翻译阶段生成 @name 引用,
# 链接时由 stub 提供 external global 声明。
# ============================================================
def make_global_ref(pool: memhub.MemBuddy | t.CPtr,
name: str,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""创建全局变量引用 (类型为 ty*,名称为 @name)"""
if name is None or ty is None:
return None
ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, ty)
ref_name: t.CChar | t.CPtr = pool.alloc(64)
if ref_name is None:
return None
viperlib.snprintf(ref_name, 64, "@%s", name)
return llvmlite.SSAValue(pool, ptr_ty, ref_name)
# ============================================================
# _resolve_module_attribute_global - 解析模块属性访问的全局变量引用
#
# 当 obj 是已导入模块名 (如 sys._mbuddy) 时:
# 1. 在当前模块全局变量链表中查找 (stub 已注入的情况)
# 2. 未找到则创建前向引用 (@attr_name, 类型为 ty 参数)
#
# 参数:
# ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退)
# 返回: Value 指针 (类型为 ty*),未识别为模块属性返回 None
# ============================================================
def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
at: ast.Attribute | t.CPtr,
ty_hint: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""解析模块属性访问,返回全局变量引用 (用于跨模块 global 访问)"""
if at is None or at.value is None or at.attr is None:
return None
if at.value.kind() != ast.ASTKind.Name:
return None
if trans is None or trans._imported_modules is None:
return None
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mod_nm.id is None:
return None
# 检查是否是已导入模块
if HandlesImports.is_module_imported(trans._imported_modules, mod_nm.id) == 0:
return None
# 在当前模块查找全局变量 (stub 可能已注入)
gv: llvmlite.GlobalVariable | t.CPtr = find_global_in_module(mod, at.attr)
if gv is not None and gv.Ty is not None:
return make_global_ref(pool, at.attr, gv.Ty)
# 未找到: 创建 external global 声明并添加到模块
# 否则只生成 @_argc 引用而无声明LLVM 报 "use of undefined value '@_argc'"
use_ty: llvmlite.LLVMType | t.CPtr = ty_hint
if use_ty is None:
use_ty = llvmlite.Int8(pool)
# 确保 use_ty 中的跨模块结构体类型有 opaque 声明
llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty)
ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty)
if ext_gv is not None:
ext_gv.Linkage = "external"
ext_gv.Initializer = None
llvmlite.module_add_global(mod, ext_gv)
return make_global_ref(pool, at.attr, use_ty)
# ============================================================
# 翻译属性访问 Attribute(value, attr, ctx) — obj.field
#
# 优先级:
# 1. 枚举成员访问 (State.Idle → 常量值)
# 2. 模块属性访问 (sys._mbuddy → 加载全局变量)
# 3. 结构体字段访问 (obj.field → GEP + load)
#
# 对于 Name 类型的 obj直接使用 alloca 指针(不 load 结构体)
# ============================================================
def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译属性访问,返回加载的字段值"""
if node is None or builder is None:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at is None or at.value is None or at.attr is None:
return None
# 枚举成员访问: EnumName.MemberName → 常量值
# 当 value 是 Name 且 Name.id 是已注册枚举时,查找成员并返回常量
if at.value.kind() == ast.ASTKind.Name:
enum_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if enum_nm.id is not None:
if HandlesEnum.is_enum_class(enum_nm.id) == 1:
member: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member(
enum_nm.id, at.attr)
if member is not None:
base_ty: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type(
enum_nm.id)
if base_ty is not None:
# 生成数字字符串作为常量名LLVM IR 要求常量输出数字值)
name_buf: t.CChar | t.CPtr = pool.alloc(32)
if name_buf is not None:
viperlib.snprintf(name_buf, 32, "%lld", member.Value)
return llvmlite.ConstInt(pool, base_ty, member.Value, name_buf)
return llvmlite.const_int32(pool, member.Value)
return None
# 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值
# 当 value 是 Attribute如 fileio.MODE且 attr 部分是已注册枚举时
if at.value.kind() == ast.ASTKind.Attribute:
inner_at_e: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value)
if inner_at_e.value is not None and inner_at_e.value.kind() == ast.ASTKind.Name:
inner_nm_e: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at_e.value)
if inner_nm_e.id is not None and inner_at_e.attr is not None:
if HandlesEnum.is_enum_class(inner_at_e.attr) == 1:
member_e: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member(
inner_at_e.attr, at.attr)
if member_e is not None:
base_ty_e: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type(
inner_at_e.attr)
if base_ty_e is not None:
name_buf_e: t.CChar | t.CPtr = pool.alloc(32)
if name_buf_e is not None:
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.const_int32(pool, member_e.Value)
return None
# 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用并加载
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_ma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_ma.id is not None:
# 先检查是否是普通变量 (优先级高于模块属性)
ma_is_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, nm_ma.id)
if ma_is_var is None:
# 先检查是否是 CDefine 编译期常量 (如 ast.CONST_INT / win32base.INVALID_HANDLE_VALUE)
# 模块属性访问的 CDefine 常量需要跨模块查找源 .py / .pyi 文件
cdef_ma_val: int = HandlesType.lookup_cdefine_constant(at.attr)
cd_local: int = HandlesType.is_cdefine_found()
if cd_local == 0:
# 确定真实模块名:优先从 _from_imports 解析别名
# from_imports 中条目格式 "local_name:real_module"
# 如 win32base:w32.win32base → 别名 win32base 真实模块 w32.win32base
real_mod_name: str = nm_ma.id
if trans._from_imports is not None:
resolved_mod: str = HandlesImports.lookup_from_import(
trans._from_imports, nm_ma.id, 0)
if resolved_mod is not None:
# 复制模块名到新缓冲区截断于空格、null、或 ':'
rm_len: t.CSizeT = 0
while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':':
rm_len += 1
rm_buf: str = pool.alloc(rm_len + 1)
if rm_buf is not None:
for rm_i in range(rm_len):
rm_buf[rm_i] = resolved_mod[rm_i]
rm_buf[rm_len] = '\0'
real_mod_name = rm_buf
# 即使 _from_imports 为 None如纯 `import ast` 无 from-import
# 也必须尝试跨模块 CDefine 查找,否则 ast.CONST_INT 会被
# 误解析为全局变量引用 @CONST_INT未定义符号
# 构建临时 from_imports "{attr}:{real_module}" 用于跨模块查找
if real_mod_name is not None:
ma_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(real_mod_name) + 2
ma_fi: str = pool.alloc(ma_fi_len)
if ma_fi is not None:
viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name)
cdef_ma_val = _lookup_cross_module_cdefine(pool, at.attr, ma_fi)
if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_ma_val)
# 检查 nm_ma.id.at.attr 是否是已导入子模块 (如 w32.win32file)
# 如果是子模块,不应创建全局变量,让嵌套属性访问路径处理
is_submod: int = 0
if trans._imported_modules is not None:
submod_len: t.CSizeT = string.strlen(nm_ma.id) + string.strlen(at.attr) + 2
submod_buf: t.CChar | t.CPtr = pool.alloc(submod_len)
if submod_buf is not None:
viperlib.snprintf(submod_buf, submod_len, "%s.%s", nm_ma.id, at.attr)
if HandlesImports.is_module_imported(trans._imported_modules, submod_buf) == 1:
is_submod = 1
# also check from_imports: at.attr 别名对应真实模块 w32.win32file
if is_submod == 0 and trans._from_imports is not None:
fi_mod_raw: str = HandlesImports.lookup_from_import(
trans._from_imports, at.attr, 0)
if fi_mod_raw is not None:
# 复制模块名到新缓冲区截断于空格、null、或 ':'
fm_len: t.CSizeT = 0
while fi_mod_raw[fm_len] != ' ' and fi_mod_raw[fm_len] != '\0' and fi_mod_raw[fm_len] != ':':
fm_len += 1
fi_mod: str = pool.alloc(fm_len + 1)
if fi_mod is not None:
for fm_i in range(fm_len):
fi_mod[fm_i] = fi_mod_raw[fm_i]
fi_mod[fm_len] = '\0'
# 检查 fi_mod 是否以 "nm_ma.id." 开头 (如 w32.win32file 以 w32. 开头)
prefix_len: t.CSizeT = string.strlen(nm_ma.id)
fi_mod_len: t.CSizeT = string.strlen(fi_mod)
if fi_mod_len > prefix_len + 1:
match_prefix: int = 1
for pi in range(prefix_len):
if fi_mod[pi] != nm_ma.id[pi]:
match_prefix = 0
break
if match_prefix == 1 and fi_mod[prefix_len] == '.':
is_submod = 1
if is_submod != 0:
return None # 是子模块,交给嵌套属性访问路径
# 非 CDefine: 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
mod_gv_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global(
pool, mod, trans, at, i8_ptr_hint)
if mod_gv_ref is not None and mod_gv_ref.Ty is not None:
load_ty_mod: llvmlite.LLVMType | t.CPtr = mod_gv_ref.Ty.Pointee
if load_ty_mod is not None:
return llvmlite.build_load(builder, load_ty_mod, mod_gv_ref)
# 嵌套模块属性访问 (w32.win32file.GENERIC_READ → 查找全局 @GENERIC_READ)
# 当 at.value 是 Attribute 且解析为已导入子模块时,查找 at.attr 作为全局常量
if at.value.kind() == ast.ASTKind.Attribute and trans is not None:
inner_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value)
if inner_at.value is not None and inner_at.value.kind() == ast.ASTKind.Name:
inner_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at.value)
if inner_nm.id is not None and inner_at.attr is not None:
if HandlesVar.lookup_var(trans.SymTab, inner_nm.id) is None:
# 构建限定模块名: "w32.win32file"
qual_len: t.CSizeT = string.strlen(inner_nm.id) + string.strlen(inner_at.attr) + 2
qual_buf: t.CChar | t.CPtr = pool.alloc(qual_len)
if qual_buf is not None:
viperlib.snprintf(qual_buf, qual_len, "%s.%s", inner_nm.id, inner_at.attr)
if HandlesImports.is_module_imported(trans._imported_modules, qual_buf) == 1:
# 查找全局变量 at.attr (stub 已注入的情况)
gv_nested: llvmlite.GlobalVariable | t.CPtr = find_global_in_module(mod, at.attr)
if gv_nested is not None and gv_nested.Ty is not None:
gv_ref_nested: llvmlite.Value | t.CPtr = make_global_ref(
pool, at.attr, gv_nested.Ty)
if gv_ref_nested is not None and gv_ref_nested.Ty is not None:
load_ty_nested: llvmlite.LLVMType | t.CPtr = gv_ref_nested.Ty.Pointee
if load_ty_nested is not None:
return llvmlite.build_load(builder, load_ty_nested, gv_ref_nested)
# 未找到全局: 尝试 CDefine 常量查找
# 对于 w32.win32base.INVALID_HANDLE_VALUE 等嵌套模块属性访问,
# 先从限定模块的 .pyi 文件中查找 CDefine 常量值并内联
cdef_val_nested: int = HandlesType.lookup_cdefine_constant(at.attr)
if HandlesType.is_cdefine_found() == 0:
# 构建临时 from_imports "attr:module" 用于跨模块查找
tmp_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(qual_buf) + 2
tmp_fi: str = pool.alloc(tmp_fi_len)
if tmp_fi is not None:
viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf)
cdef_val_nested = _lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_val_nested)
# CDefine 未找到: 创建前向引用 (CDefine 通常是 i32)
i32_ty_fwd: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
gv_ref_fwd: llvmlite.Value | t.CPtr = make_global_ref(
pool, at.attr, i32_ty_fwd)
if gv_ref_fwd is not None and gv_ref_fwd.Ty is not None:
load_ty_fwd: llvmlite.LLVMType | t.CPtr = gv_ref_fwd.Ty.Pointee
if load_ty_fwd is not None:
return llvmlite.build_load(builder, load_ty_fwd, gv_ref_fwd)
# 对于 Name 类型的 obj直接查找 alloca不 load 结构体)
obj_ptr: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is not None:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
# 非 Name 路径:翻译对象值(会 load
if obj_ptr is None:
obj_ptr = translate_value(builder, pool, mod, at.value, None, 0, trans)
if obj_ptr is None or obj_ptr.Ty is None:
if at.value.kind() == ast.ASTKind.Name:
nm_d: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_d.id is not None:
stdio.printf("[TA-DIAG] obj_ptr=None attr=%s name=%s\n",
at.attr, nm_d.id)
return None
# 如果 obj_ptr 是 Ptr(Ptr(Struct))X|t.CPtr 变量的 alloca
# load 解引用获取 Ptr(Struct)
obj_ptr = _deref_if_ptr_ptr(builder, obj_ptr)
# 查找结构体类型信息
# obj_ptr 类型应该是 Ptr(Struct(...))
match obj_ptr.Ty:
case llvmlite.LLVMType.Ptr(struct_ty):
# 确保跨模块结构体的完整定义在当前模块中可用(供 getelementptr 访问字段)
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
# 查找字段索引和类型
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
struct_ty, at.attr)
# REnum 变体字段查找: lookup_field 失败时,尝试 REnum 别名
# REnum 结构体字段名是 _p1/_p2/...,但源码用变体字段名访问(如 ty.Pointee
if field_info is None:
renum_se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(
struct_ty)
if renum_se is not None and renum_se.Name is not None:
is_rn: int = HandlesClassDef._is_renum_class_name(renum_se.Name)
if is_rn == 1:
alias_idx: int = HandlesClassDef._lookup_renum_field_alias(
renum_se.Name, at.attr)
if alias_idx >= 1:
alias_fe: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct._get_field_entry(renum_se, alias_idx)
if alias_fe is not None and alias_fe.Ty is not None:
alias_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
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)
# 回退: 类型指针比较失败时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
if field_info is None:
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_fb.id is not None:
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm_fb.id)
if var_entry is not None and var_entry.AnnotClassName is not None:
stdio.printf("[TA-DIAG] fallback annot=%s attr=%s\n",
var_entry.AnnotClassName, at.attr)
cur_sha1: str = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class(
var_entry.AnnotClassName, at.attr, cur_sha1)
if field_info is None:
stdio.printf("[TA-DIAG] lfbc_failed annot=%s attr=%s\n",
var_entry.AnnotClassName, at.attr)
stdio.fflush(0)
else:
stdio.printf("[TA-DIAG] lfbc_ok annot=%s attr=%s idx=%d\n",
var_entry.AnnotClassName, at.attr, field_info.Index)
stdio.fflush(0)
# 回退 1 成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 原始 struct_ty 可能是 i8X|t.CPtr 简化为 Ptr(i8)
# 需用实际结构体类型做 GEP否则 GEP i8 失败
if field_info is not None:
annot_se: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_struct(var_entry.AnnotClassName)
if annot_se is None:
stdio.printf("[TA-DIAG] fb1 find_struct None annot=%s\n",
var_entry.AnnotClassName)
stdio.fflush(0)
if annot_se is not None and annot_se.Ty is not None:
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, annot_se.Ty)
casted_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(builder, obj_ptr, annot_ptr_ty)
if casted_ptr is not None:
obj_ptr = casted_ptr
struct_ty = annot_se.Ty
# 回退 1 更新 struct_ty 后,需重新确保
# 注解类型结构体定义在当前模块可用
# (原始 struct_ty 可能是 i8ensure 无效)
HandlesStruct.ensure_struct_def_in_module(
pool, mod, annot_se.Ty)
stdio.printf("[TA-DIAG] fb1 struct_ty updated\n")
stdio.fflush(0)
else:
stdio.printf("[TA-DIAG] fb1 bitcast None\n")
stdio.fflush(0)
# 回退 2: 子类搜索 — 注解类型是基类但实际值是派生类
# 如 node: AST | t.CPtr = If(...),访问 node.orelse
if field_info is None:
sub_entry: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_subclass_with_field(
var_entry.AnnotClassName, at.attr)
if sub_entry is not None and sub_entry.Ty is not None:
sub_field: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct.lookup_field(sub_entry.Ty, at.attr)
if sub_field is not None:
sub_ptr_ty: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, sub_entry.Ty)
casted_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(builder, obj_ptr, sub_ptr_ty)
if casted_ptr is not None:
sub_field_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_gep_struct(
builder, sub_entry.Ty, sub_field.Ty,
casted_ptr, sub_field.Index)
if sub_field_ptr is not None:
return llvmlite.build_load(
builder, sub_field.Ty, sub_field_ptr)
if field_info is not None:
field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty
# 联合体bitcast obj_ptr 到 field_ty* 后 load
if HandlesStruct.is_union_by_type(struct_ty) == 1:
field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_ty)
casted: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, obj_ptr, field_ptr_ty)
if casted is not None:
return llvmlite.build_load(builder, field_ty, casted)
return None
# 普通结构体GEP + load
field_idx: int = field_info.Index
stdio.printf("[TA-DIAG] gep_try idx=%d struct_is_ptr=%d\n",
field_idx, is_ptr_type(struct_ty))
stdio.fflush(0)
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, field_ty, obj_ptr, field_idx)
if field_ptr is None:
stdio.printf("[TA-DIAG] gep_failed idx=%d\n", field_idx)
stdio.fflush(0)
if field_ptr is not None:
stdio.printf("[TA-DIAG] gep_ok idx=%d is_array=%d\n",
field_idx, is_array_type(field_ty))
stdio.fflush(0)
# 数组类型字段不能 load 为 SSA value直接返回字段指针
# 用于后续下标访问: self.state[0] → GEP state 字段 → GEP 数组元素
if is_array_type(field_ty) != 0:
return field_ptr
stdio.printf("[TA-DIAG] pre_load idx=%d field_ty_not_null=%d\n",
field_idx, 1 if field_ty is not None else 0)
stdio.fflush(0)
loaded_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, field_ty, field_ptr)
stdio.printf("[TA-DIAG] post_load idx=%d loaded=%d\n",
field_idx, 1 if loaded_val is not None else 0)
stdio.fflush(0)
# 联合类型字段(如 Token | t.CPtr被编译为 i8*
# 若 AnnotClassName 指示了具体结构体类型bitcast 为正确的结构体指针
if loaded_val is not None and field_info.AnnotClassName is not None:
annot_struct: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(
field_info.AnnotClassName)
if annot_struct is not None and annot_struct.Ty is not None:
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, annot_struct.Ty)
return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty)
return loaded_val
# 诊断:所有字段查找路径失败
sn_diag: str = HandlesStruct._extract_struct_name(struct_ty)
if sn_diag is not None:
stdio.printf("[TA-DIAG] field=None attr=%s sname=%s\n",
at.attr, sn_diag)
else:
stdio.printf("[TA-DIAG] field=None attr=%s sname=(null)\n",
at.attr)
return None
case _:
stdio.printf("[TA-DIAG] type_mismatch attr=%s\n", at.attr)
return None
# ============================================================
# 获取下标表达式的元素指针(不加载值,用于赋值)
# ============================================================
def get_subscript_ptr(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""获取下标表达式的元素指针(用于赋值 lhs"""
if node is None or builder is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None:
return None
# 翻译索引
idx_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, sub.slice, None, 0, trans)
if idx_val is None:
return None
# 如果 value 是 Name尝试从 alloca 类型推断
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is not None and trans is not None:
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is None:
stdio.printf("[GSP] alloca=None var=%s\n", nm.id)
stdio.fflush(0)
elif alloca.Ty is None:
stdio.printf("[GSP] alloca.Ty=None var=%s\n", nm.id)
stdio.fflush(0)
if alloca is not None and alloca.Ty is not None:
if is_ptr_type(alloca.Ty) != 0:
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
stdio.printf("[GSP] pointee=None var=%s\n", nm.id)
stdio.fflush(0)
if pointee is not None:
pe_arr: int = is_array_type(pointee)
pe_ptr: int = is_ptr_type(pointee)
stdio.printf("[GSP] var=%s pe_arr=%d pe_ptr=%d\n", nm.id, pe_arr, pe_ptr)
stdio.fflush(0)
# 单层 match避免嵌套 match 的编译器 bug
match pointee:
case llvmlite.LLVMType.Array(elem_ty, count):
stdio.printf("[GSP] matched Array\n")
stdio.fflush(0)
# 数组遍历
return llvmlite.build_gep_array(
builder, pointee, elem_ty, alloca, idx_val)
case llvmlite.LLVMType.Ptr(inner_ty):
stdio.printf("[GSP] matched Ptr\n")
stdio.fflush(0)
# inner_ty 是 Ptr 说明 alloca.Ty 是三重指针,
# 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。
# 此时 [i]=val 应该是指针解引用赋值, 而非调用 __setitem__。
if is_ptr_type(inner_ty) != 0:
ptr_val_gsp: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_gsp is not None:
return llvmlite.build_gep(
builder, inner_ty, ptr_val_gsp, idx_val)
return None
# 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
# 优先返回 None 触发 __setitem__ 重载,
# 避免 IsPtrElement 误判为指针遍历
cls_nm_ptr_chk: str = _get_custom_struct_cls_nm(pool, inner_ty)
if cls_nm_ptr_chk is None:
pass
if not (cls_nm_ptr_chk is None):
stdio.printf("[GSP] custom struct %s, defer to __setitem__\n",
cls_nm_ptr_chk)
stdio.fflush(0)
return None
# bytes|t.CPtr / str|t.CPtr: alloca 是 i8**
# 直接 GEP 按 i8* 步长8 字节),不 load
# 仅当 inner_ty 不是自定义结构体时才走指针遍历
if trans is not None:
ve_pe: HandlesVar.VarEntry | t.CPtr = \
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
if ve_pe is not None and ve_pe.IsPtrElement == 1:
stdio.printf("[GSP] IsPtrElement=1, gep direct\n")
stdio.fflush(0)
return llvmlite.build_gep(
builder, pointee, alloca, idx_val)
# 普通指针遍历: 先 load 指针值,再 GEP
ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val is None:
stdio.printf("[GSP] build_load=None\n")
stdio.fflush(0)
return None
gep_r: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, inner_ty, ptr_val, idx_val)
if gep_r is None:
stdio.printf("[GSP] build_gep=None\n")
stdio.fflush(0)
return gep_r
case _:
# pointee 是普通标量类型 (如 i64, i32, i8):
# alloca 是 Ptr(标量), 直接 GEP 获取第 idx 个元素指针
# 支持 arg_vals[i] = val 这类参数数组下标赋值
stdio.printf("[GSP] matched scalar, gep direct\n")
stdio.fflush(0)
return llvmlite.build_gep(builder, pointee, alloca, idx_val)
# 通用路径
stdio.printf("[GSP] fallback to generic path\n")
stdio.fflush(0)
ptr_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, sub.value, None, 0, trans)
if ptr_val is None or ptr_val.Ty is None:
stdio.printf("[GSP] generic: ptr_val=None\n")
stdio.fflush(0)
return None
if is_ptr_type(ptr_val.Ty) != 0:
elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
if elem_ty2 is not None:
# 数组类型: 双索引 GEP (getelementptr [N x ty], ptr, i32 0, i32 idx)
match elem_ty2:
case llvmlite.LLVMType.Array(arr_elem_ty, arr_count):
return llvmlite.build_gep_array(
builder, elem_ty2, arr_elem_ty, ptr_val, idx_val)
case _:
pass
# 自定义结构体 (如 self._ht[key]): 返回 None,
# 触发 HandlesAssign 的 __setitem__ 重载
cls_nm_gen: str = _get_custom_struct_cls_nm(pool, elem_ty2)
if cls_nm_gen is None:
pass
if not (cls_nm_gen is None):
stdio.printf("[GSP] generic custom struct %s, defer to __setitem__\n",
cls_nm_gen)
stdio.fflush(0)
return None
# 指针类型: 单索引 GEP (getelementptr ty, ptr, idx)
return llvmlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
return None
# ============================================================
# 获取属性访问的字段指针(不加载值,用于赋值)
# 对于 Name 类型的 obj直接使用 alloca 指针(不 load 结构体)
# ============================================================
def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""获取属性访问的字段指针(用于赋值 lhs"""
if node is None or builder is None:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at is None or at.value is None or at.attr is None:
return None
# 对于 Name 类型的 obj直接查找 alloca不 load 结构体)
obj_ptr: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is not None:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
# 模块属性访问 (写路径): sys._mbuddy = mb → store 到全局变量 @_mbuddy
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用
if obj_ptr is None and at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_wma.id is not None:
# 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
wma_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global(
pool, mod, trans, at, i8_ptr_w)
if wma_ref is not None:
return wma_ref
# 非 Name 路径:翻译对象值(会 load
if obj_ptr is None:
obj_ptr = translate_value(builder, pool, mod, at.value, None, 0, trans)
if obj_ptr is None or obj_ptr.Ty is None:
return None
# 如果 obj_ptr 是 Ptr(Ptr(Struct))X|t.CPtr 变量的 alloca
# load 解引用获取 Ptr(Struct)
obj_ptr = _deref_if_ptr_ptr(builder, obj_ptr)
match obj_ptr.Ty:
case llvmlite.LLVMType.Ptr(struct_ty):
# 确保跨模块结构体的完整定义在当前模块中可用(供 getelementptr 访问字段)
# 与 translate_attribute 一致,避免 opaque 类型导致 GEP 报错
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
struct_ty, at.attr)
# 回退 1: 类型指针比较失败时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
if field_info is None:
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_fb.id is not None:
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm_fb.id)
if var_entry is not None and var_entry.AnnotClassName is not None:
cur_sha1: str = None
if trans is not None:
cur_sha1 = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class(
var_entry.AnnotClassName, at.attr, cur_sha1)
# 回退 1 成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 原始 struct_ty 可能是 i8X|t.CPtr 简化为 Ptr(i8)
# 需用实际结构体类型做 GEP否则 GEP i8 失败
if field_info is not None:
annot_se: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_struct(var_entry.AnnotClassName)
if annot_se is not None and annot_se.Ty is not None:
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, annot_se.Ty)
casted_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(builder, obj_ptr, annot_ptr_ty)
if casted_ptr is not None:
obj_ptr = casted_ptr
struct_ty = annot_se.Ty
# 回退 1 更新 struct_ty 后,需重新确保
# 注解类型结构体定义在当前模块可用
HandlesStruct.ensure_struct_def_in_module(
pool, mod, annot_se.Ty)
# 回退 2: 子类搜索 — 注解类型是基类但实际值是派生类
# 如 node: AST | t.CPtr = If(...),访问 node.orelse
# orelse 在 If 上不在 AST 上,需搜索 AST 的子类
if field_info is None:
sub_entry: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_subclass_with_field(
var_entry.AnnotClassName, at.attr)
if sub_entry is not None and sub_entry.Ty is not None:
sub_field: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct.lookup_field(sub_entry.Ty, at.attr)
if sub_field is not None:
sub_ptr_ty: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, sub_entry.Ty)
casted_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(builder, obj_ptr, sub_ptr_ty)
if casted_ptr is not None:
# 确保子类结构体定义在当前模块可用
HandlesStruct.ensure_struct_def_in_module(
pool, mod, sub_entry.Ty)
return llvmlite.build_gep_struct(
builder, sub_entry.Ty, sub_field.Ty,
casted_ptr, sub_field.Index)
if field_info is not None:
# 联合体bitcast obj_ptr 到 field_ty*(字段指针用于 store
if HandlesStruct.is_union_by_type(struct_ty) == 1:
field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_info.Ty)
return llvmlite.build_bitcast(builder, obj_ptr, field_ptr_ty)
# 普通结构体GEP 获取字段指针
return llvmlite.build_gep_struct(
builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index)
return None
case _:
return None