chore: 改名同步 TransPyV -> ViperC(源码/配置内的纯替换)
This commit is contained in:
1 parent
f6447ad594
commit
15f77da9d3
29 files changed
+32737
-32737
No files matched your search
@@ -20,9 +20,9 @@
|
||||
SUSPECT 其余 ⇒ **需人工判定**;`--strict` 下 exit 1(门禁失败)
|
||||
|
||||
覆盖范围:仓库内全部 `*.py`(含根目录脚本、`MCP/**`、`TransPyC/Tools/**`),
|
||||
跳过 `.git`/`__pycache__`/`output`/`temp`/`.tpv_build`/`dlls` 与解析器自身
|
||||
跳过 `.git`/`__pycache__`/`output`/`temp`/`__vpcbuild__`/`dlls` 与解析器自身
|
||||
(`lib/vast`、`lib/vast_c` 是前端**实现**,不是前端**消费者**)。
|
||||
TPV 侧源码(`TransPyV/**` 的 `.vp`)跑自己的前端,不在本脚本范围内。
|
||||
VPC 侧源码(`ViperC/**` 的 `.vp`)跑自己的前端,不在本脚本范围内。
|
||||
|
||||
用法(**只读,不写任何文件**)
|
||||
python TransPyC/Tools/AuditViperParseEntries.py # 打印审计表
|
||||
@@ -35,7 +35,7 @@ import sys
|
||||
ScriptDir = os.path.dirname(os.path.abspath(__file__))
|
||||
RepoRoot = os.path.dirname(os.path.dirname(ScriptDir))
|
||||
|
||||
SkipDirs = {'__pycache__', '.git', '.tpv_build', 'dlls', 'output', 'temp', 'node_modules'}
|
||||
SkipDirs = {'__pycache__', '.git', '__vpcbuild__', 'dlls', 'output', 'temp', 'node_modules'}
|
||||
# 前端实现(vast 解析器本体;它自己就是"唯一许可入口"的实现层)
|
||||
SkipRelPrefixes = ('TransPyC/lib/vast/', 'TransPyC/lib/vast_c/')
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
5) 类成员表按族隔离: 基类先在**本族**找, 再找**支撑族**(''), 最后全局唯一名;
|
||||
同一基类名跨族重复时报 AMBIG(宁可 UNRESOLVED 也不猜)。
|
||||
6) `t.REnum`(tagged union)派生类: 变体字段合并进父类成员集
|
||||
(TPV 允许在父类指针上访问变体字段, 不合并会产生大量误报)。
|
||||
(VPC 允许在父类指针上访问变体字段, 不合并会产生大量误报)。
|
||||
|
||||
前端 = **TPC 自研 vast**(`lib/vast`,与 StubGen/Converter.py 用的 `import vast as ast`
|
||||
是同一源文件; 不可用 CPython `ast`, 否则含 `list[X*]` 等 vast 专有写法的文件会整体
|
||||
@@ -44,7 +44,7 @@ SyntaxError 被跳过)。vast 与 CPython ast 的差异及本脚本的适配:
|
||||
python TransPyC/Tools/ScanAstFieldMiss.py --selftest [--includes DIR] [--out FILE]
|
||||
|
||||
默认值:
|
||||
--app-root <RepoRoot>/TransPyV/App
|
||||
--app-root <RepoRoot>/ViperC/App
|
||||
--includes <RepoRoot>/includes
|
||||
--out <RepoRoot>/Debugger/ScanAstFieldMiss.txt
|
||||
|
||||
@@ -81,7 +81,7 @@ ScriptDir = os.path.dirname(os.path.abspath(__file__))
|
||||
TpcRoot = os.path.dirname(ScriptDir)
|
||||
RepoRoot = os.path.dirname(TpcRoot)
|
||||
|
||||
DefaultAppRoot = os.path.join(RepoRoot, 'TransPyV', 'App')
|
||||
DefaultAppRoot = os.path.join(RepoRoot, 'ViperC', 'App')
|
||||
DefaultIncludesDir = os.path.join(RepoRoot, 'includes')
|
||||
DefaultOutFile = os.path.join(RepoRoot, 'Debugger', 'ScanAstFieldMiss.txt')
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ def resolve_paths(config: dict[str, Any], project_file: str) -> dict[str, Any]:
|
||||
resolved_sources.append(resolve(src))
|
||||
result['sources'] = [s for s in resolved_sources if os.path.isfile(s)]
|
||||
|
||||
# 直接使用 output_dir / temp_dir(与新版本 TPV 一致,不再支持 build_dir)
|
||||
# 直接使用 output_dir / temp_dir(与新版本 VPC 一致,不再支持 build_dir)
|
||||
result['temp_dir'] = resolve(config.get('temp_dir', './temp'))
|
||||
result['output_dir'] = resolve(config.get('output_dir', './output'))
|
||||
|
||||
@@ -150,7 +150,7 @@ def main() -> None:
|
||||
parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)')
|
||||
parser.add_argument('--dwarf', action='store_true', help='函数级 DWARF 调试元数据:每模块一份 DICompileUnit/DIFile,每个 define 一份 DISubprogram 并挂 !dbg,函数入口第一条指令挂 DILocation。使 ASan/llvm-symbolizer 能把崩溃地址反查为 "<文件>.vp:函数起始行"(默认关闭;--asan 时自动开启)。开启时强制重译 App 模块(不复用不含 !llvm.dbg.cu 的旧 .ll/.obj),.ll/.obj 体积与编译时间略增')
|
||||
parser.add_argument('--traceback', action='store_true', help='Ctrl+C 调用栈打印(仅 Windows/PE):链接完成后用 llvm-dwarfdump 把最终 exe 的 .debug_line/.debug_info 导出为 "<exe>.stackmap" 地址→源行旁文件(小端定长格式),供 exe 内运行时在 Ctrl+C/Ctrl+Break 时把 CRT 到当前的返回地址逐帧翻译为 "<文件>.vp:行/函数"。会自动开启 DWARF(同 --asan,否则行表为空);找不到 llvm-dwarfdump / 调用失败 / 解析不到任何行记录 / 目标不是 Windows PE 时直接报错终止编译')
|
||||
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)')
|
||||
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 viperc.exe)')
|
||||
parser.add_argument('--hotspot-sites', nargs='?', const='', default=None, help='调用点热点统计:为每个匹配白名单的直接 call 点插入计数器,运行时 Ctrl+C 各模块按真实次数降序写入 <path>(默认为 hotspot_sites.txt);同时为每个函数定义加计时器,按自身耗时(self)降序写入同目录 hotspot_time.txt,行为 `<函数名> <self> <inc> <进入次数>`(单位 tick,x86_64 上为序列化 lfence; rdtsc);self 已扣除被插桩子调用的 inc,但不含未插桩被调方(间接调用、strcmp/memcpy 等无定义外部函数),它们计入调用者的 self')
|
||||
parser.add_argument('--hotspot-callees', default=None, help='--hotspot-sites 的白名单被调函数基名(逗号分隔),默认内存/字符串族 strcmp,strncmp,strchr,strlen,strstr,memcpy,memset,memmove,strcpy,strcat')
|
||||
parser.add_argument('--hotspot-sites-all', action='store_true', help='--hotspot-sites 对全部直接 call 点插桩(忽略白名单;体积/开销大,用于深度排查)')
|
||||
|
||||
+1342
-1342
File diff suppressed because it is too large.
Load diff
+5048
-5048
File diff suppressed because it is too large.
Load diff
+1132
-1132
File diff suppressed because it is too large.
Load diff
+684
-684
File diff suppressed because it is too large.
Load diff
+1207
-1207
File diff suppressed because it is too large.
Load diff
@@ -1,300 +1,300 @@
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
import vast as ast
|
||||
import sys
|
||||
import llvmlite.ir as ir
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.translator import Translator
|
||||
from lib.core.VLogger import get_logger as _vlog
|
||||
from lib.core.Handles.HandlesBase import BaseHandle
|
||||
|
||||
|
||||
class AugAssignHandle(BaseHandle):
|
||||
_AUGOP_MAP: dict[type, str] = {
|
||||
ast.Add: '__iadd__', ast.Sub: '__isub__', ast.Mult: '__imul__',
|
||||
ast.Div: '__itruediv__', ast.FloorDiv: '__ifloordiv__', ast.Mod: '__imod__', ast.Pow: '__ipow__',
|
||||
ast.BitAnd: '__iand__', ast.BitOr: '__ior__', ast.BitXor: '__ixor__',
|
||||
ast.LShift: '__ilshift__', ast.RShift: '__irshift__',
|
||||
}
|
||||
_AUGOP_FALLBACK: dict[type, str] = {
|
||||
ast.Add: '__add__', ast.Sub: '__sub__', ast.Mult: '__mul__',
|
||||
ast.Div: '__truediv__', ast.FloorDiv: '__floordiv__', ast.Mod: '__mod__', ast.Pow: '__pow__',
|
||||
ast.BitAnd: '__and__', ast.BitOr: '__or__', ast.BitXor: '__xor__',
|
||||
ast.LShift: '__lshift__', ast.RShift: '__rshift__',
|
||||
}
|
||||
|
||||
def _resolve_global_var_type(self, Gen: "Translator.LlvmGen", VarName: str, fallback: ir.Type) -> ir.Type:
|
||||
"""解析 global 变量的 LLVM 类型:优先用模块级 AnnAssign 的注解类型。
|
||||
|
||||
问题场景:函数内 `_g_alloc_count += 1`(global 声明),模块级注解为
|
||||
`_g_alloc_count: t.CSizeT`(i64)。若不查模块级注解类型,此处会用 RHS 字面量
|
||||
类型(i32)自动建 i32 全局,与模块内其他读取/写入(i64)类型不匹配,
|
||||
产生 "Operands must be the same type, got (i64, i32)" 类错误。
|
||||
模块级 AnnAssign 已通过 _EmitGlobalAnnAssignLlvm 创建 LLVM 全局,
|
||||
但注解无初始值(如 `_g_alloc_max: t.CSizeT = 0`)时 initializer 为空、
|
||||
Gen.module.globals 中无该符号,故需在此兜底解析注解类型。
|
||||
"""
|
||||
module_ann: dict = getattr(Gen, '_module_ann_types', None)
|
||||
if module_ann and VarName in module_ann:
|
||||
ann_type: ir.Type = module_ann[VarName]
|
||||
if ann_type is not None:
|
||||
return ann_type
|
||||
if isinstance(fallback, ir.IntType):
|
||||
return fallback
|
||||
return ir.IntType(64)
|
||||
|
||||
def _is_aug_unsigned(self, Node: ast.AugAssign, OldVal: ir.Value | None, Value: ir.Value) -> bool:
|
||||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||
if isinstance(Node.target, ast.Name):
|
||||
if Gen._check_node_unsigned(Node.target):
|
||||
return True
|
||||
elif isinstance(Node.target, ast.Attribute):
|
||||
if Gen._check_node_unsigned(Node.target):
|
||||
return True
|
||||
elif isinstance(Node.target, ast.Subscript):
|
||||
if Gen._check_node_unsigned(Node.target):
|
||||
return True
|
||||
if Node.value is not None and Gen._check_node_unsigned(Node.value):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _HandleAugAssignLlvm(self, Node: ast.AugAssign) -> None:
|
||||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||
if isinstance(Node.target, ast.Name):
|
||||
VarName: str = Node.target.id
|
||||
if (VarName not in Gen.variables
|
||||
and VarName not in Gen.global_vars
|
||||
and VarName not in Gen._reg_values
|
||||
and VarName not in Gen._direct_values):
|
||||
src_info: str = Gen._get_node_info()
|
||||
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
|
||||
sys.exit(1)
|
||||
if Gen._IsVarConst(VarName):
|
||||
_vlog().error(Gen._ConstAssignMessage("增量赋值", VarName))
|
||||
sys.exit(1)
|
||||
ClassName: str | None = Gen.var_struct_class.get(VarName)
|
||||
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
|
||||
if ClassName and iop_name:
|
||||
iFuncName: str = f'{ClassName}.{iop_name}'
|
||||
FuncName: str = f'{ClassName}.{self._AUGOP_FALLBACK.get(type(Node.op), "")}'
|
||||
if iFuncName in Gen.functions or FuncName in Gen.functions:
|
||||
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load()))
|
||||
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
|
||||
if OldVal and Value:
|
||||
result: ir.Value | None
|
||||
if iFuncName in Gen.functions:
|
||||
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, iop_name, OldVal, Gen, Value)
|
||||
else:
|
||||
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, self._AUGOP_FALLBACK[type(Node.op)], OldVal, Gen, Value)
|
||||
if result is not None:
|
||||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||||
VarPtr: ir.Value = Gen.variables[VarName]
|
||||
if VarPtr.type.pointee == result.type:
|
||||
Gen._store(result, VarPtr)
|
||||
elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(result.type, ir.PointerType):
|
||||
CastedVal: ir.Value = Gen.builder.bitcast(result, VarPtr.type.pointee, name=f"cast_{VarName}")
|
||||
Gen._store(CastedVal, VarPtr)
|
||||
else:
|
||||
NewVar: ir.AllocaInstr = Gen._allocaEntry(result.type, name=VarName)
|
||||
Gen._store(result, NewVar)
|
||||
Gen.variables[VarName] = NewVar
|
||||
else:
|
||||
Gen._reg_values[VarName] = result
|
||||
Gen.variables[VarName] = None
|
||||
return
|
||||
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
|
||||
if not Value:
|
||||
return
|
||||
is_unsigned: bool = self._is_aug_unsigned(Node, None, Value)
|
||||
if isinstance(Node.target, ast.Name):
|
||||
VarName: str = Node.target.id
|
||||
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
|
||||
if VarName in Gen.global_vars:
|
||||
# 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局
|
||||
# (注解无初始值的全局变量),此处自动创建零初始化全局变量
|
||||
GVar: ir.GlobalVariable
|
||||
if VarName not in Gen.module.globals:
|
||||
_gv_type: ir.Type = self._resolve_global_var_type(Gen, VarName, Value.type)
|
||||
GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName)
|
||||
GVar.initializer = ir.Constant(_gv_type, 0)
|
||||
else:
|
||||
GVar = Gen.module.globals[VarName]
|
||||
Gen.variables[VarName] = GVar
|
||||
OldVal: ir.Value = Gen._load(GVar, name=VarName)
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
Gen._store(NewVal, GVar)
|
||||
return
|
||||
if VarName in Gen._reg_values:
|
||||
OldVal: ir.Value = Gen._reg_values[VarName]
|
||||
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
|
||||
Gen._store(OldVal, var)
|
||||
Gen.variables[VarName] = var
|
||||
del Gen._reg_values[VarName]
|
||||
if VarName in Gen._direct_values:
|
||||
OldVal: ir.Value = Gen._direct_values.pop(VarName)
|
||||
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
|
||||
Gen._store(OldVal, var)
|
||||
Gen.variables[VarName] = var
|
||||
if Gen.builder.block.is_terminated:
|
||||
return
|
||||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||||
OldVal: ir.Value = Gen._load(Gen.variables[VarName], name=VarName)
|
||||
store_ptr: ir.Value = Gen.variables[VarName]
|
||||
if isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||
if Value.type.width != 64:
|
||||
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset")
|
||||
NewVal: ir.Value
|
||||
if Op == '-' or Op == '+':
|
||||
if Op == '-':
|
||||
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset")
|
||||
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub")
|
||||
else:
|
||||
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add")
|
||||
else:
|
||||
ptr_val: ir.Value = OldVal
|
||||
OldVal = Gen._load(OldVal, name="deref_ptr_for_op")
|
||||
orig_type: ir.Type = OldVal.type
|
||||
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if OldVal.type.width < Value.type.width:
|
||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
|
||||
elif OldVal.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
|
||||
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
|
||||
if NewVal.type.width > orig_type.width:
|
||||
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
|
||||
store_ptr = ptr_val
|
||||
else:
|
||||
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if OldVal.type.width < Value.type.width:
|
||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
|
||||
elif OldVal.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
|
||||
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||
if Op in ('+', '-'):
|
||||
if Value.type.width < 64:
|
||||
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_aug")
|
||||
NewVal: ir.Value
|
||||
if Op == '-':
|
||||
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_aug")
|
||||
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_aug")
|
||||
else:
|
||||
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
|
||||
Gen._store(NewVal, store_ptr)
|
||||
return
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
if isinstance(store_ptr.type, ir.PointerType):
|
||||
TargetType: ir.Type = store_ptr.type.pointee
|
||||
if NewVal.type != TargetType:
|
||||
Coerced: ir.Value | None = Gen._coerce_value(NewVal, TargetType)
|
||||
if Coerced is not None:
|
||||
NewVal = Coerced
|
||||
Gen._store(NewVal, store_ptr)
|
||||
elif VarName == 'pos':
|
||||
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
|
||||
Gen.variables[VarName] = var
|
||||
OldVal: ir.Value = Gen._load(var, name=VarName)
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
Gen._store(NewVal, var)
|
||||
elif isinstance(Node.target, ast.Attribute):
|
||||
AttrName: str = Node.target.attr
|
||||
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.target)
|
||||
if OldVal:
|
||||
def _deref_if_ptr(val: ir.Value) -> ir.Value:
|
||||
if isinstance(val.type, ir.PointerType):
|
||||
pointee: ir.Type = val.type.pointee
|
||||
if isinstance(pointee, (ir.IntType, ir.FloatType, ir.DoubleType)):
|
||||
return Gen._load(val, name="deref_aug")
|
||||
return val
|
||||
OldVal = _deref_if_ptr(OldVal)
|
||||
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
|
||||
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if OldVal.type.width < Value.type.width:
|
||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
|
||||
elif OldVal.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
|
||||
elif isinstance(Node.target, ast.Subscript):
|
||||
SubTarget: ast.Subscript = Node.target
|
||||
ElemPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(SubTarget)
|
||||
if ElemPtr:
|
||||
if isinstance(ElemPtr.type, ir.PointerType):
|
||||
OldVal: ir.Value = Gen._load(ElemPtr, name="old_subscript_val")
|
||||
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
|
||||
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if OldVal.type.width < Value.type.width:
|
||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_sub")
|
||||
elif OldVal.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_sub")
|
||||
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||
if Op in ('+', '-'):
|
||||
if Value.type.width < 64:
|
||||
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_sub")
|
||||
NewVal: ir.Value
|
||||
if Op == '-':
|
||||
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_sub")
|
||||
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_sub")
|
||||
else:
|
||||
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
|
||||
Gen._store(NewVal, ElemPtr)
|
||||
return
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
|
||||
if NewVal.type.width > ElemPtr.type.pointee.width:
|
||||
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
|
||||
Gen._store(NewVal, ElemPtr)
|
||||
elif isinstance(Node.target, ast.UnaryOp):
|
||||
# 解引用增强赋值 `*p += v`:目标节点是 UnaryOp(Deref)
|
||||
# (SetStoreContext 不改 UnaryOp 的 ctx),与 `*p = v` 同一形态。
|
||||
# 流程:操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。
|
||||
if not isinstance(Node.target.op, ast.Deref):
|
||||
BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
|
||||
raise Exception(
|
||||
f"AugAssign 目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)"
|
||||
f"{Gen._get_node_info()}"
|
||||
)
|
||||
DerefPtr: ir.Value | None = self.HandleExprLlvm(Node.target.operand)
|
||||
if DerefPtr is None:
|
||||
raise Exception(f"解引用增量赋值 '*' 的操作数无法求值{Gen._get_node_info()}")
|
||||
if not isinstance(DerefPtr.type, ir.PointerType):
|
||||
raise Exception(
|
||||
f"解引用增量赋值 '*' 的操作数必须是指针类型(实际 {DerefPtr.type})"
|
||||
f"{Gen._get_node_info()}"
|
||||
)
|
||||
DerefOp: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
|
||||
DerefOld: ir.Value = Gen._load(DerefPtr, name="old_deref_val")
|
||||
if isinstance(DerefOld.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if DerefOld.type.width < Value.type.width:
|
||||
DerefOld = Gen.builder.zext(DerefOld, Value.type, name="zext_left_deref")
|
||||
elif DerefOld.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, DerefOld.type, name="zext_right_deref")
|
||||
elif isinstance(DerefOld.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||
if DerefOp in ('+', '-'):
|
||||
if Value.type.width < 64:
|
||||
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_deref")
|
||||
OffVal: ir.Value
|
||||
if DerefOp == '-':
|
||||
OffVal = Gen.builder.gep(DerefOld, [Gen.builder.neg(Value, name="neg_ptr_offset_deref")], name="ptr_sub_deref")
|
||||
else:
|
||||
OffVal = Gen.builder.gep(DerefOld, [Value], name="ptr_add_deref")
|
||||
Gen._store(OffVal, DerefPtr)
|
||||
return
|
||||
DerefNew: ir.Value = Gen.emit_binary_op(DerefOp, DerefOld, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
if isinstance(DerefNew.type, ir.IntType) and isinstance(DerefPtr.type.pointee, ir.IntType):
|
||||
if DerefNew.type.width > DerefPtr.type.pointee.width:
|
||||
DerefNew = Gen.builder.trunc(DerefNew, DerefPtr.type.pointee, name="trunc_deref_result")
|
||||
Gen._store(DerefNew, DerefPtr)
|
||||
else:
|
||||
# fail-fast:目标形态未覆盖时绝不静默丢弃(对齐 TransPyV
|
||||
# translate_augassign 的同名 fail-fast),避免"语句被整条丢掉、
|
||||
# 变量静默不变"。
|
||||
TargetOp: str | None = None
|
||||
if isinstance(Node.target, ast.UnaryOp):
|
||||
TargetOp = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
|
||||
raise Exception(
|
||||
f"AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用"
|
||||
f"(当前 {type(Node.target).__name__}"
|
||||
+ (f" op='{TargetOp}'" if TargetOp else "")
|
||||
+ f"){Gen._get_node_info()}"
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
import vast as ast
|
||||
import sys
|
||||
import llvmlite.ir as ir
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.translator import Translator
|
||||
from lib.core.VLogger import get_logger as _vlog
|
||||
from lib.core.Handles.HandlesBase import BaseHandle
|
||||
|
||||
|
||||
class AugAssignHandle(BaseHandle):
|
||||
_AUGOP_MAP: dict[type, str] = {
|
||||
ast.Add: '__iadd__', ast.Sub: '__isub__', ast.Mult: '__imul__',
|
||||
ast.Div: '__itruediv__', ast.FloorDiv: '__ifloordiv__', ast.Mod: '__imod__', ast.Pow: '__ipow__',
|
||||
ast.BitAnd: '__iand__', ast.BitOr: '__ior__', ast.BitXor: '__ixor__',
|
||||
ast.LShift: '__ilshift__', ast.RShift: '__irshift__',
|
||||
}
|
||||
_AUGOP_FALLBACK: dict[type, str] = {
|
||||
ast.Add: '__add__', ast.Sub: '__sub__', ast.Mult: '__mul__',
|
||||
ast.Div: '__truediv__', ast.FloorDiv: '__floordiv__', ast.Mod: '__mod__', ast.Pow: '__pow__',
|
||||
ast.BitAnd: '__and__', ast.BitOr: '__or__', ast.BitXor: '__xor__',
|
||||
ast.LShift: '__lshift__', ast.RShift: '__rshift__',
|
||||
}
|
||||
|
||||
def _resolve_global_var_type(self, Gen: "Translator.LlvmGen", VarName: str, fallback: ir.Type) -> ir.Type:
|
||||
"""解析 global 变量的 LLVM 类型:优先用模块级 AnnAssign 的注解类型。
|
||||
|
||||
问题场景:函数内 `_g_alloc_count += 1`(global 声明),模块级注解为
|
||||
`_g_alloc_count: t.CSizeT`(i64)。若不查模块级注解类型,此处会用 RHS 字面量
|
||||
类型(i32)自动建 i32 全局,与模块内其他读取/写入(i64)类型不匹配,
|
||||
产生 "Operands must be the same type, got (i64, i32)" 类错误。
|
||||
模块级 AnnAssign 已通过 _EmitGlobalAnnAssignLlvm 创建 LLVM 全局,
|
||||
但注解无初始值(如 `_g_alloc_max: t.CSizeT = 0`)时 initializer 为空、
|
||||
Gen.module.globals 中无该符号,故需在此兜底解析注解类型。
|
||||
"""
|
||||
module_ann: dict = getattr(Gen, '_module_ann_types', None)
|
||||
if module_ann and VarName in module_ann:
|
||||
ann_type: ir.Type = module_ann[VarName]
|
||||
if ann_type is not None:
|
||||
return ann_type
|
||||
if isinstance(fallback, ir.IntType):
|
||||
return fallback
|
||||
return ir.IntType(64)
|
||||
|
||||
def _is_aug_unsigned(self, Node: ast.AugAssign, OldVal: ir.Value | None, Value: ir.Value) -> bool:
|
||||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||
if isinstance(Node.target, ast.Name):
|
||||
if Gen._check_node_unsigned(Node.target):
|
||||
return True
|
||||
elif isinstance(Node.target, ast.Attribute):
|
||||
if Gen._check_node_unsigned(Node.target):
|
||||
return True
|
||||
elif isinstance(Node.target, ast.Subscript):
|
||||
if Gen._check_node_unsigned(Node.target):
|
||||
return True
|
||||
if Node.value is not None and Gen._check_node_unsigned(Node.value):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _HandleAugAssignLlvm(self, Node: ast.AugAssign) -> None:
|
||||
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
|
||||
if isinstance(Node.target, ast.Name):
|
||||
VarName: str = Node.target.id
|
||||
if (VarName not in Gen.variables
|
||||
and VarName not in Gen.global_vars
|
||||
and VarName not in Gen._reg_values
|
||||
and VarName not in Gen._direct_values):
|
||||
src_info: str = Gen._get_node_info()
|
||||
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
|
||||
sys.exit(1)
|
||||
if Gen._IsVarConst(VarName):
|
||||
_vlog().error(Gen._ConstAssignMessage("增量赋值", VarName))
|
||||
sys.exit(1)
|
||||
ClassName: str | None = Gen.var_struct_class.get(VarName)
|
||||
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
|
||||
if ClassName and iop_name:
|
||||
iFuncName: str = f'{ClassName}.{iop_name}'
|
||||
FuncName: str = f'{ClassName}.{self._AUGOP_FALLBACK.get(type(Node.op), "")}'
|
||||
if iFuncName in Gen.functions or FuncName in Gen.functions:
|
||||
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load()))
|
||||
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
|
||||
if OldVal and Value:
|
||||
result: ir.Value | None
|
||||
if iFuncName in Gen.functions:
|
||||
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, iop_name, OldVal, Gen, Value)
|
||||
else:
|
||||
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, self._AUGOP_FALLBACK[type(Node.op)], OldVal, Gen, Value)
|
||||
if result is not None:
|
||||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||||
VarPtr: ir.Value = Gen.variables[VarName]
|
||||
if VarPtr.type.pointee == result.type:
|
||||
Gen._store(result, VarPtr)
|
||||
elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(result.type, ir.PointerType):
|
||||
CastedVal: ir.Value = Gen.builder.bitcast(result, VarPtr.type.pointee, name=f"cast_{VarName}")
|
||||
Gen._store(CastedVal, VarPtr)
|
||||
else:
|
||||
NewVar: ir.AllocaInstr = Gen._allocaEntry(result.type, name=VarName)
|
||||
Gen._store(result, NewVar)
|
||||
Gen.variables[VarName] = NewVar
|
||||
else:
|
||||
Gen._reg_values[VarName] = result
|
||||
Gen.variables[VarName] = None
|
||||
return
|
||||
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
|
||||
if not Value:
|
||||
return
|
||||
is_unsigned: bool = self._is_aug_unsigned(Node, None, Value)
|
||||
if isinstance(Node.target, ast.Name):
|
||||
VarName: str = Node.target.id
|
||||
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
|
||||
if VarName in Gen.global_vars:
|
||||
# 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局
|
||||
# (注解无初始值的全局变量),此处自动创建零初始化全局变量
|
||||
GVar: ir.GlobalVariable
|
||||
if VarName not in Gen.module.globals:
|
||||
_gv_type: ir.Type = self._resolve_global_var_type(Gen, VarName, Value.type)
|
||||
GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName)
|
||||
GVar.initializer = ir.Constant(_gv_type, 0)
|
||||
else:
|
||||
GVar = Gen.module.globals[VarName]
|
||||
Gen.variables[VarName] = GVar
|
||||
OldVal: ir.Value = Gen._load(GVar, name=VarName)
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
Gen._store(NewVal, GVar)
|
||||
return
|
||||
if VarName in Gen._reg_values:
|
||||
OldVal: ir.Value = Gen._reg_values[VarName]
|
||||
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
|
||||
Gen._store(OldVal, var)
|
||||
Gen.variables[VarName] = var
|
||||
del Gen._reg_values[VarName]
|
||||
if VarName in Gen._direct_values:
|
||||
OldVal: ir.Value = Gen._direct_values.pop(VarName)
|
||||
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
|
||||
Gen._store(OldVal, var)
|
||||
Gen.variables[VarName] = var
|
||||
if Gen.builder.block.is_terminated:
|
||||
return
|
||||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||||
OldVal: ir.Value = Gen._load(Gen.variables[VarName], name=VarName)
|
||||
store_ptr: ir.Value = Gen.variables[VarName]
|
||||
if isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||
if Value.type.width != 64:
|
||||
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset")
|
||||
NewVal: ir.Value
|
||||
if Op == '-' or Op == '+':
|
||||
if Op == '-':
|
||||
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset")
|
||||
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub")
|
||||
else:
|
||||
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add")
|
||||
else:
|
||||
ptr_val: ir.Value = OldVal
|
||||
OldVal = Gen._load(OldVal, name="deref_ptr_for_op")
|
||||
orig_type: ir.Type = OldVal.type
|
||||
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if OldVal.type.width < Value.type.width:
|
||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
|
||||
elif OldVal.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
|
||||
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
|
||||
if NewVal.type.width > orig_type.width:
|
||||
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
|
||||
store_ptr = ptr_val
|
||||
else:
|
||||
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if OldVal.type.width < Value.type.width:
|
||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
|
||||
elif OldVal.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
|
||||
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||
if Op in ('+', '-'):
|
||||
if Value.type.width < 64:
|
||||
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_aug")
|
||||
NewVal: ir.Value
|
||||
if Op == '-':
|
||||
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_aug")
|
||||
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_aug")
|
||||
else:
|
||||
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
|
||||
Gen._store(NewVal, store_ptr)
|
||||
return
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
if isinstance(store_ptr.type, ir.PointerType):
|
||||
TargetType: ir.Type = store_ptr.type.pointee
|
||||
if NewVal.type != TargetType:
|
||||
Coerced: ir.Value | None = Gen._coerce_value(NewVal, TargetType)
|
||||
if Coerced is not None:
|
||||
NewVal = Coerced
|
||||
Gen._store(NewVal, store_ptr)
|
||||
elif VarName == 'pos':
|
||||
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
|
||||
Gen.variables[VarName] = var
|
||||
OldVal: ir.Value = Gen._load(var, name=VarName)
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
Gen._store(NewVal, var)
|
||||
elif isinstance(Node.target, ast.Attribute):
|
||||
AttrName: str = Node.target.attr
|
||||
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.target)
|
||||
if OldVal:
|
||||
def _deref_if_ptr(val: ir.Value) -> ir.Value:
|
||||
if isinstance(val.type, ir.PointerType):
|
||||
pointee: ir.Type = val.type.pointee
|
||||
if isinstance(pointee, (ir.IntType, ir.FloatType, ir.DoubleType)):
|
||||
return Gen._load(val, name="deref_aug")
|
||||
return val
|
||||
OldVal = _deref_if_ptr(OldVal)
|
||||
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
|
||||
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if OldVal.type.width < Value.type.width:
|
||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
|
||||
elif OldVal.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
|
||||
elif isinstance(Node.target, ast.Subscript):
|
||||
SubTarget: ast.Subscript = Node.target
|
||||
ElemPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(SubTarget)
|
||||
if ElemPtr:
|
||||
if isinstance(ElemPtr.type, ir.PointerType):
|
||||
OldVal: ir.Value = Gen._load(ElemPtr, name="old_subscript_val")
|
||||
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
|
||||
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if OldVal.type.width < Value.type.width:
|
||||
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_sub")
|
||||
elif OldVal.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_sub")
|
||||
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||
if Op in ('+', '-'):
|
||||
if Value.type.width < 64:
|
||||
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_sub")
|
||||
NewVal: ir.Value
|
||||
if Op == '-':
|
||||
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_sub")
|
||||
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_sub")
|
||||
else:
|
||||
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
|
||||
Gen._store(NewVal, ElemPtr)
|
||||
return
|
||||
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
|
||||
if NewVal.type.width > ElemPtr.type.pointee.width:
|
||||
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
|
||||
Gen._store(NewVal, ElemPtr)
|
||||
elif isinstance(Node.target, ast.UnaryOp):
|
||||
# 解引用增强赋值 `*p += v`:目标节点是 UnaryOp(Deref)
|
||||
# (SetStoreContext 不改 UnaryOp 的 ctx),与 `*p = v` 同一形态。
|
||||
# 流程:操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。
|
||||
if not isinstance(Node.target.op, ast.Deref):
|
||||
BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
|
||||
raise Exception(
|
||||
f"AugAssign 目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)"
|
||||
f"{Gen._get_node_info()}"
|
||||
)
|
||||
DerefPtr: ir.Value | None = self.HandleExprLlvm(Node.target.operand)
|
||||
if DerefPtr is None:
|
||||
raise Exception(f"解引用增量赋值 '*' 的操作数无法求值{Gen._get_node_info()}")
|
||||
if not isinstance(DerefPtr.type, ir.PointerType):
|
||||
raise Exception(
|
||||
f"解引用增量赋值 '*' 的操作数必须是指针类型(实际 {DerefPtr.type})"
|
||||
f"{Gen._get_node_info()}"
|
||||
)
|
||||
DerefOp: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
|
||||
DerefOld: ir.Value = Gen._load(DerefPtr, name="old_deref_val")
|
||||
if isinstance(DerefOld.type, ir.IntType) and isinstance(Value.type, ir.IntType):
|
||||
if DerefOld.type.width < Value.type.width:
|
||||
DerefOld = Gen.builder.zext(DerefOld, Value.type, name="zext_left_deref")
|
||||
elif DerefOld.type.width > Value.type.width:
|
||||
Value = Gen.builder.zext(Value, DerefOld.type, name="zext_right_deref")
|
||||
elif isinstance(DerefOld.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
|
||||
if DerefOp in ('+', '-'):
|
||||
if Value.type.width < 64:
|
||||
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_deref")
|
||||
OffVal: ir.Value
|
||||
if DerefOp == '-':
|
||||
OffVal = Gen.builder.gep(DerefOld, [Gen.builder.neg(Value, name="neg_ptr_offset_deref")], name="ptr_sub_deref")
|
||||
else:
|
||||
OffVal = Gen.builder.gep(DerefOld, [Value], name="ptr_add_deref")
|
||||
Gen._store(OffVal, DerefPtr)
|
||||
return
|
||||
DerefNew: ir.Value = Gen.emit_binary_op(DerefOp, DerefOld, Value, is_unsigned=is_unsigned, is_aug=True)
|
||||
if isinstance(DerefNew.type, ir.IntType) and isinstance(DerefPtr.type.pointee, ir.IntType):
|
||||
if DerefNew.type.width > DerefPtr.type.pointee.width:
|
||||
DerefNew = Gen.builder.trunc(DerefNew, DerefPtr.type.pointee, name="trunc_deref_result")
|
||||
Gen._store(DerefNew, DerefPtr)
|
||||
else:
|
||||
# fail-fast:目标形态未覆盖时绝不静默丢弃(对齐 ViperC
|
||||
# translate_augassign 的同名 fail-fast),避免"语句被整条丢掉、
|
||||
# 变量静默不变"。
|
||||
TargetOp: str | None = None
|
||||
if isinstance(Node.target, ast.UnaryOp):
|
||||
TargetOp = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
|
||||
raise Exception(
|
||||
f"AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用"
|
||||
f"(当前 {type(Node.target).__name__}"
|
||||
+ (f" op='{TargetOp}'" if TargetOp else "")
|
||||
+ f"){Gen._get_node_info()}"
|
||||
)
|
||||
+1490
-1490
File diff suppressed because it is too large.
Load diff
+2033
-2033
File diff suppressed because it is too large.
Load diff
+2367
-2367
File diff suppressed because it is too large.
Load diff
+1267
-1267
File diff suppressed because it is too large.
Load diff
@@ -1,280 +1,280 @@
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Any
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.translator import Translator
|
||||
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
|
||||
from lib.core.Handles.HandlesBase import BaseHandle
|
||||
import vast as ast
|
||||
import re
|
||||
import llvmlite.ir as ir
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# `_parse_format_spec` 的格式说明符解析正则**预编译**(性能:避免每次
|
||||
# `re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。
|
||||
# 语义与逐字写法完全一致,模式串一字未改)。
|
||||
# ---------------------------------------------------------------
|
||||
# Python 格式说明符 `[[fill]align][sign][#][0][width][,][.precision][type]`
|
||||
_RE_FORMAT_SPEC: re.Pattern[str] = re.compile(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$')
|
||||
|
||||
|
||||
class ExprFormatHandle(BaseHandle):
|
||||
def _HandleJoinedStrLlvm(self, Node: ast.JoinedStr) -> ir.Value:
|
||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||
if not Gen or not Gen.builder:
|
||||
return ir.Constant(ir.IntType(8).as_pointer(), None)
|
||||
format_str: str = ""
|
||||
cast_args: list[Any] = []
|
||||
for part in Node.values:
|
||||
if isinstance(part, ast.Constant) and isinstance(part.value, str):
|
||||
format_str += part.value.replace('\\', '\\\\').replace('%', '%%').replace('\n', '\\n').replace('\t', '\\t').replace('\0', '\\0')
|
||||
elif isinstance(part, ast.FormattedValue):
|
||||
fmt_class: str | None = self.Trans.ExprHandler._get_var_class(part.value, Gen)
|
||||
if fmt_class and Gen._has_function(f'{fmt_class}.__str__'):
|
||||
obj_val: Any = self.HandleExprLlvm(part.value)
|
||||
if obj_val:
|
||||
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
|
||||
if fmt_class in Gen.structs:
|
||||
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[fmt_class]), name=f"cast_{fmt_class}")
|
||||
val: Any = Gen.builder.call(Gen._get_function(f'{fmt_class}.__str__'), [obj_val], name=f"call_{fmt_class}.__str__")
|
||||
Gen._RegisterTempPtr(val)
|
||||
format_str += "%s"
|
||||
cast_args.append(val)
|
||||
else:
|
||||
format_str += "%s"
|
||||
cast_args.append(ir.Constant(ir.PointerType(ir.IntType(8)), None))
|
||||
continue
|
||||
val = self.HandleExprLlvm(part.value)
|
||||
if not val:
|
||||
format_str += "%d"
|
||||
cast_args.append(ir.Constant(ir.IntType(32), 0))
|
||||
continue
|
||||
# 检查 format_spec(Python f-string 格式说明符)
|
||||
if part.format_spec is not None:
|
||||
c_fmt: str | None = self._parse_format_spec(part.format_spec, val)
|
||||
if c_fmt is not None:
|
||||
cast_val: Any = self._cast_value_for_format(val, c_fmt, Gen, part.value)
|
||||
format_str += c_fmt
|
||||
cast_args.append(cast_val)
|
||||
continue
|
||||
if isinstance(val.type, ir.PointerType):
|
||||
pointee: ir.Type = val.type.pointee
|
||||
if isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||||
format_str += "%s"
|
||||
cast_args.append(val)
|
||||
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||||
int_val: Any = Gen.builder.ptrtoint(val, ir.IntType(64), name="fstr_ptr2int")
|
||||
trunc_val: Any = Gen.builder.trunc(int_val, ir.IntType(32), name="fstr_trunc")
|
||||
format_str += "%d"
|
||||
cast_args.append(trunc_val)
|
||||
else:
|
||||
Loaded: Any = Gen._load(val, name="fstr_deref")
|
||||
if isinstance(Loaded.type, ir.IntType):
|
||||
if Loaded.type.width == 8:
|
||||
format_str += "%c"
|
||||
ext: Any = Gen.builder.zext(Loaded, ir.IntType(32), name="fstr_char2int")
|
||||
cast_args.append(ext)
|
||||
else:
|
||||
format_str += "%d"
|
||||
cast_args.append(Loaded)
|
||||
elif isinstance(Loaded.type, (ir.FloatType, ir.DoubleType)):
|
||||
format_str += "%f"
|
||||
cast_args.append(Loaded)
|
||||
elif isinstance(Loaded.type, ir.PointerType):
|
||||
format_str += "%s"
|
||||
cast_args.append(Loaded)
|
||||
else:
|
||||
format_str += "%d"
|
||||
cast_args.append(Loaded)
|
||||
elif isinstance(val.type, ir.IntType):
|
||||
if val.type.width == 8:
|
||||
format_str += "%c"
|
||||
ext = Gen.builder.zext(val, ir.IntType(32), name="fstr_char2int")
|
||||
cast_args.append(ext)
|
||||
elif val.type.width == 1:
|
||||
zext: Any = Gen.builder.zext(val, ir.IntType(32), name="fstr_bool2int")
|
||||
format_str += "%d"
|
||||
cast_args.append(zext)
|
||||
else:
|
||||
is_u: bool = Gen._check_node_unsigned(part.value)
|
||||
format_str += "%u" if is_u else "%d"
|
||||
cast_args.append(val)
|
||||
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
|
||||
format_str += "%f"
|
||||
cast_args.append(val)
|
||||
else:
|
||||
format_str += "%d"
|
||||
cast_args.append(val)
|
||||
if not format_str:
|
||||
return Gen.emit_constant("", 'string')
|
||||
format_ptr: Any = Gen._create_string_global(format_str)
|
||||
# 统一使用 snprintf 生成字符串,返回 i8*
|
||||
# __str__ 方法内使用堆分配(返回值需持久化),其他场景使用栈分配(自动释放)
|
||||
in_str_method: bool = bool(getattr(Gen, 'func', None) and getattr(Gen.func, 'name', '').endswith('.__str__'))
|
||||
snprintf_type: ir.FunctionType = ir.FunctionType(ir.IntType(32), [
|
||||
ir.PointerType(ir.IntType(8)),
|
||||
ir.IntType(64),
|
||||
ir.PointerType(ir.IntType(8))
|
||||
], var_arg=True)
|
||||
snprintf_func: Any = Gen.get_or_declare_c_func('snprintf', snprintf_type)
|
||||
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
|
||||
# 第一遍:获取所需长度(NULL 缓冲,size=0)
|
||||
len_val: ir.Value = Gen.builder.call(snprintf_func, [null_ptr, ir.Constant(ir.IntType(64), 0), format_ptr] + cast_args, name="fstr_len")
|
||||
# 加 1 用于 null 终止符
|
||||
size_val: ir.Value = Gen.builder.add(len_val, ir.Constant(ir.IntType(32), 1), name="fstr_size")
|
||||
size_ext: ir.Value = Gen.builder.zext(size_val, ir.IntType(64), name="fstr_size_ext")
|
||||
if in_str_method:
|
||||
# 堆分配(__str__ 返回值需跨函数生命周期)
|
||||
malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)])
|
||||
malloc_func: Any = Gen._get_or_declare_func('malloc', malloc_type)
|
||||
buf_ptr: ir.Value = Gen.builder.call(malloc_func, [size_ext], name="fstr_heap_buf")
|
||||
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
|
||||
Gen._RegisterTempPtr(buf_ptr)
|
||||
return buf_ptr
|
||||
# 栈分配(局部使用,自动释放)
|
||||
buf_ptr: ir.Value = Gen._alloca(ir.IntType(8), name="fstr_stack_buf", size=size_val)
|
||||
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
|
||||
return buf_ptr
|
||||
|
||||
def _parse_format_spec(self, format_spec_node: ast.AST, val: ir.Value) -> str | None:
|
||||
"""将 Python f-string format_spec 转换为 C printf 格式说明符。
|
||||
|
||||
支持: d/x/X/o/c/f/s 及 [[fill]align][sign][#][0][width][.precision][type] 语法。
|
||||
返回 None 表示无法解析,调用方应回退到类型推断。
|
||||
"""
|
||||
if isinstance(format_spec_node, ast.JoinedStr):
|
||||
parts: list[str] = []
|
||||
for v in format_spec_node.values:
|
||||
if isinstance(v, ast.Constant) and isinstance(v.value, str):
|
||||
parts.append(v.value)
|
||||
else:
|
||||
return None
|
||||
spec_str: str = ''.join(parts)
|
||||
elif isinstance(format_spec_node, ast.Constant) and isinstance(format_spec_node.value, str):
|
||||
spec_str = format_spec_node.value
|
||||
else:
|
||||
return None
|
||||
|
||||
if not spec_str:
|
||||
return None
|
||||
|
||||
m: Any = _RE_FORMAT_SPEC.match(spec_str)
|
||||
if not m:
|
||||
return None
|
||||
|
||||
fill_align, sign, alt, zero, width, comma, precision, fmt_type = m.groups()
|
||||
|
||||
c_flags: str = ''
|
||||
if sign == '+':
|
||||
c_flags += '+'
|
||||
elif sign == ' ':
|
||||
c_flags += ' '
|
||||
if alt:
|
||||
c_flags += '#'
|
||||
|
||||
if fill_align:
|
||||
align_char: str = fill_align[-1] if len(fill_align) > 1 else fill_align[0]
|
||||
if align_char in ('<', '^'):
|
||||
c_flags += '-'
|
||||
if zero:
|
||||
c_flags += '0'
|
||||
|
||||
c_width: str = width or ''
|
||||
c_precision: str = precision or ''
|
||||
|
||||
length_mod: str = ''
|
||||
if isinstance(val.type, ir.IntType):
|
||||
# 32 位**不写**长度修饰符(N14):与 TPV 侧 ExprGenFstr 保持同步。
|
||||
# 写 'l' 会在 LP64(long=64 位)下让 printf 按 64 位读取 i32 实参。
|
||||
if val.type.width == 64:
|
||||
length_mod = 'll'
|
||||
elif val.type.width == 16:
|
||||
length_mod = 'h'
|
||||
elif val.type.width == 8:
|
||||
length_mod = 'hh'
|
||||
|
||||
c_specifier: str = ''
|
||||
if fmt_type in ('d', 'n'):
|
||||
c_specifier = f'{length_mod}d'
|
||||
elif fmt_type == 'x':
|
||||
c_specifier = f'{length_mod}x'
|
||||
elif fmt_type == 'X':
|
||||
c_specifier = f'{length_mod}X'
|
||||
elif fmt_type == 'o':
|
||||
c_specifier = f'{length_mod}o'
|
||||
elif fmt_type == 'b':
|
||||
return None
|
||||
elif fmt_type == 'c':
|
||||
c_specifier = 'c'
|
||||
elif fmt_type in ('f', 'F'):
|
||||
c_specifier = 'f'
|
||||
elif fmt_type in ('e', 'E', 'g', 'G'):
|
||||
c_specifier = fmt_type
|
||||
elif fmt_type == 's':
|
||||
c_specifier = 's'
|
||||
elif fmt_type == '%':
|
||||
c_specifier = 'f%%'
|
||||
else:
|
||||
if isinstance(val.type, ir.IntType):
|
||||
if val.type.width == 8:
|
||||
c_specifier = 'c'
|
||||
elif val.type.width == 1:
|
||||
c_specifier = 'd'
|
||||
else:
|
||||
c_specifier = f'{length_mod}d'
|
||||
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
|
||||
c_specifier = 'f'
|
||||
elif isinstance(val.type, ir.PointerType):
|
||||
c_specifier = 's'
|
||||
else:
|
||||
c_specifier = 'd'
|
||||
|
||||
return f'%{c_flags}{c_width}{c_precision}{c_specifier}'
|
||||
|
||||
def _cast_value_for_format(self, val: ir.Value, c_fmt: str, Gen: Any, arg_node: ast.AST) -> ir.Value:
|
||||
"""根据 C printf 格式说明符转换值类型。"""
|
||||
fmt_type_char: str = ''
|
||||
for ch in reversed(c_fmt):
|
||||
if ch.isalpha() and ch not in ('l', 'h'):
|
||||
fmt_type_char = ch
|
||||
break
|
||||
elif ch == '%':
|
||||
break
|
||||
|
||||
if fmt_type_char == 's':
|
||||
if isinstance(val.type, ir.PointerType):
|
||||
pointee: ir.Type = val.type.pointee
|
||||
if isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||||
return val
|
||||
if isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8:
|
||||
return Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="fstr_arr2ptr")
|
||||
return val
|
||||
elif fmt_type_char == 'c':
|
||||
if isinstance(val.type, ir.IntType) and val.type.width < 32:
|
||||
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_c")
|
||||
return val
|
||||
elif fmt_type_char in ('d', 'x', 'X', 'o', 'i'):
|
||||
if isinstance(val.type, ir.IntType):
|
||||
if val.type.width == 8:
|
||||
is_u: bool = Gen._check_node_unsigned(arg_node)
|
||||
if is_u:
|
||||
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i8")
|
||||
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i8")
|
||||
elif val.type.width == 16:
|
||||
is_u = Gen._check_node_unsigned(arg_node)
|
||||
if is_u:
|
||||
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i16")
|
||||
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i16")
|
||||
elif val.type.width == 1:
|
||||
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_bool")
|
||||
return val
|
||||
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
|
||||
return Gen.builder.fptosi(val, ir.IntType(32), name="fstr_f2i")
|
||||
return val
|
||||
elif fmt_type_char in ('f', 'e', 'E', 'g', 'G'):
|
||||
if isinstance(val.type, ir.FloatType):
|
||||
return Gen.builder.fpext(val, ir.DoubleType(), name="fstr_fpext")
|
||||
elif isinstance(val.type, ir.IntType):
|
||||
return Gen.builder.sitofp(val, ir.DoubleType(), name="fstr_i2f")
|
||||
return val
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Any
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.translator import Translator
|
||||
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
|
||||
from lib.core.Handles.HandlesBase import BaseHandle
|
||||
import vast as ast
|
||||
import re
|
||||
import llvmlite.ir as ir
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# `_parse_format_spec` 的格式说明符解析正则**预编译**(性能:避免每次
|
||||
# `re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。
|
||||
# 语义与逐字写法完全一致,模式串一字未改)。
|
||||
# ---------------------------------------------------------------
|
||||
# Python 格式说明符 `[[fill]align][sign][#][0][width][,][.precision][type]`
|
||||
_RE_FORMAT_SPEC: re.Pattern[str] = re.compile(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$')
|
||||
|
||||
|
||||
class ExprFormatHandle(BaseHandle):
|
||||
def _HandleJoinedStrLlvm(self, Node: ast.JoinedStr) -> ir.Value:
|
||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||
if not Gen or not Gen.builder:
|
||||
return ir.Constant(ir.IntType(8).as_pointer(), None)
|
||||
format_str: str = ""
|
||||
cast_args: list[Any] = []
|
||||
for part in Node.values:
|
||||
if isinstance(part, ast.Constant) and isinstance(part.value, str):
|
||||
format_str += part.value.replace('\\', '\\\\').replace('%', '%%').replace('\n', '\\n').replace('\t', '\\t').replace('\0', '\\0')
|
||||
elif isinstance(part, ast.FormattedValue):
|
||||
fmt_class: str | None = self.Trans.ExprHandler._get_var_class(part.value, Gen)
|
||||
if fmt_class and Gen._has_function(f'{fmt_class}.__str__'):
|
||||
obj_val: Any = self.HandleExprLlvm(part.value)
|
||||
if obj_val:
|
||||
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
|
||||
if fmt_class in Gen.structs:
|
||||
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[fmt_class]), name=f"cast_{fmt_class}")
|
||||
val: Any = Gen.builder.call(Gen._get_function(f'{fmt_class}.__str__'), [obj_val], name=f"call_{fmt_class}.__str__")
|
||||
Gen._RegisterTempPtr(val)
|
||||
format_str += "%s"
|
||||
cast_args.append(val)
|
||||
else:
|
||||
format_str += "%s"
|
||||
cast_args.append(ir.Constant(ir.PointerType(ir.IntType(8)), None))
|
||||
continue
|
||||
val = self.HandleExprLlvm(part.value)
|
||||
if not val:
|
||||
format_str += "%d"
|
||||
cast_args.append(ir.Constant(ir.IntType(32), 0))
|
||||
continue
|
||||
# 检查 format_spec(Python f-string 格式说明符)
|
||||
if part.format_spec is not None:
|
||||
c_fmt: str | None = self._parse_format_spec(part.format_spec, val)
|
||||
if c_fmt is not None:
|
||||
cast_val: Any = self._cast_value_for_format(val, c_fmt, Gen, part.value)
|
||||
format_str += c_fmt
|
||||
cast_args.append(cast_val)
|
||||
continue
|
||||
if isinstance(val.type, ir.PointerType):
|
||||
pointee: ir.Type = val.type.pointee
|
||||
if isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||||
format_str += "%s"
|
||||
cast_args.append(val)
|
||||
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||||
int_val: Any = Gen.builder.ptrtoint(val, ir.IntType(64), name="fstr_ptr2int")
|
||||
trunc_val: Any = Gen.builder.trunc(int_val, ir.IntType(32), name="fstr_trunc")
|
||||
format_str += "%d"
|
||||
cast_args.append(trunc_val)
|
||||
else:
|
||||
Loaded: Any = Gen._load(val, name="fstr_deref")
|
||||
if isinstance(Loaded.type, ir.IntType):
|
||||
if Loaded.type.width == 8:
|
||||
format_str += "%c"
|
||||
ext: Any = Gen.builder.zext(Loaded, ir.IntType(32), name="fstr_char2int")
|
||||
cast_args.append(ext)
|
||||
else:
|
||||
format_str += "%d"
|
||||
cast_args.append(Loaded)
|
||||
elif isinstance(Loaded.type, (ir.FloatType, ir.DoubleType)):
|
||||
format_str += "%f"
|
||||
cast_args.append(Loaded)
|
||||
elif isinstance(Loaded.type, ir.PointerType):
|
||||
format_str += "%s"
|
||||
cast_args.append(Loaded)
|
||||
else:
|
||||
format_str += "%d"
|
||||
cast_args.append(Loaded)
|
||||
elif isinstance(val.type, ir.IntType):
|
||||
if val.type.width == 8:
|
||||
format_str += "%c"
|
||||
ext = Gen.builder.zext(val, ir.IntType(32), name="fstr_char2int")
|
||||
cast_args.append(ext)
|
||||
elif val.type.width == 1:
|
||||
zext: Any = Gen.builder.zext(val, ir.IntType(32), name="fstr_bool2int")
|
||||
format_str += "%d"
|
||||
cast_args.append(zext)
|
||||
else:
|
||||
is_u: bool = Gen._check_node_unsigned(part.value)
|
||||
format_str += "%u" if is_u else "%d"
|
||||
cast_args.append(val)
|
||||
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
|
||||
format_str += "%f"
|
||||
cast_args.append(val)
|
||||
else:
|
||||
format_str += "%d"
|
||||
cast_args.append(val)
|
||||
if not format_str:
|
||||
return Gen.emit_constant("", 'string')
|
||||
format_ptr: Any = Gen._create_string_global(format_str)
|
||||
# 统一使用 snprintf 生成字符串,返回 i8*
|
||||
# __str__ 方法内使用堆分配(返回值需持久化),其他场景使用栈分配(自动释放)
|
||||
in_str_method: bool = bool(getattr(Gen, 'func', None) and getattr(Gen.func, 'name', '').endswith('.__str__'))
|
||||
snprintf_type: ir.FunctionType = ir.FunctionType(ir.IntType(32), [
|
||||
ir.PointerType(ir.IntType(8)),
|
||||
ir.IntType(64),
|
||||
ir.PointerType(ir.IntType(8))
|
||||
], var_arg=True)
|
||||
snprintf_func: Any = Gen.get_or_declare_c_func('snprintf', snprintf_type)
|
||||
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
|
||||
# 第一遍:获取所需长度(NULL 缓冲,size=0)
|
||||
len_val: ir.Value = Gen.builder.call(snprintf_func, [null_ptr, ir.Constant(ir.IntType(64), 0), format_ptr] + cast_args, name="fstr_len")
|
||||
# 加 1 用于 null 终止符
|
||||
size_val: ir.Value = Gen.builder.add(len_val, ir.Constant(ir.IntType(32), 1), name="fstr_size")
|
||||
size_ext: ir.Value = Gen.builder.zext(size_val, ir.IntType(64), name="fstr_size_ext")
|
||||
if in_str_method:
|
||||
# 堆分配(__str__ 返回值需跨函数生命周期)
|
||||
malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)])
|
||||
malloc_func: Any = Gen._get_or_declare_func('malloc', malloc_type)
|
||||
buf_ptr: ir.Value = Gen.builder.call(malloc_func, [size_ext], name="fstr_heap_buf")
|
||||
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
|
||||
Gen._RegisterTempPtr(buf_ptr)
|
||||
return buf_ptr
|
||||
# 栈分配(局部使用,自动释放)
|
||||
buf_ptr: ir.Value = Gen._alloca(ir.IntType(8), name="fstr_stack_buf", size=size_val)
|
||||
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
|
||||
return buf_ptr
|
||||
|
||||
def _parse_format_spec(self, format_spec_node: ast.AST, val: ir.Value) -> str | None:
|
||||
"""将 Python f-string format_spec 转换为 C printf 格式说明符。
|
||||
|
||||
支持: d/x/X/o/c/f/s 及 [[fill]align][sign][#][0][width][.precision][type] 语法。
|
||||
返回 None 表示无法解析,调用方应回退到类型推断。
|
||||
"""
|
||||
if isinstance(format_spec_node, ast.JoinedStr):
|
||||
parts: list[str] = []
|
||||
for v in format_spec_node.values:
|
||||
if isinstance(v, ast.Constant) and isinstance(v.value, str):
|
||||
parts.append(v.value)
|
||||
else:
|
||||
return None
|
||||
spec_str: str = ''.join(parts)
|
||||
elif isinstance(format_spec_node, ast.Constant) and isinstance(format_spec_node.value, str):
|
||||
spec_str = format_spec_node.value
|
||||
else:
|
||||
return None
|
||||
|
||||
if not spec_str:
|
||||
return None
|
||||
|
||||
m: Any = _RE_FORMAT_SPEC.match(spec_str)
|
||||
if not m:
|
||||
return None
|
||||
|
||||
fill_align, sign, alt, zero, width, comma, precision, fmt_type = m.groups()
|
||||
|
||||
c_flags: str = ''
|
||||
if sign == '+':
|
||||
c_flags += '+'
|
||||
elif sign == ' ':
|
||||
c_flags += ' '
|
||||
if alt:
|
||||
c_flags += '#'
|
||||
|
||||
if fill_align:
|
||||
align_char: str = fill_align[-1] if len(fill_align) > 1 else fill_align[0]
|
||||
if align_char in ('<', '^'):
|
||||
c_flags += '-'
|
||||
if zero:
|
||||
c_flags += '0'
|
||||
|
||||
c_width: str = width or ''
|
||||
c_precision: str = precision or ''
|
||||
|
||||
length_mod: str = ''
|
||||
if isinstance(val.type, ir.IntType):
|
||||
# 32 位**不写**长度修饰符(N14):与 VPC 侧 ExprGenFstr 保持同步。
|
||||
# 写 'l' 会在 LP64(long=64 位)下让 printf 按 64 位读取 i32 实参。
|
||||
if val.type.width == 64:
|
||||
length_mod = 'll'
|
||||
elif val.type.width == 16:
|
||||
length_mod = 'h'
|
||||
elif val.type.width == 8:
|
||||
length_mod = 'hh'
|
||||
|
||||
c_specifier: str = ''
|
||||
if fmt_type in ('d', 'n'):
|
||||
c_specifier = f'{length_mod}d'
|
||||
elif fmt_type == 'x':
|
||||
c_specifier = f'{length_mod}x'
|
||||
elif fmt_type == 'X':
|
||||
c_specifier = f'{length_mod}X'
|
||||
elif fmt_type == 'o':
|
||||
c_specifier = f'{length_mod}o'
|
||||
elif fmt_type == 'b':
|
||||
return None
|
||||
elif fmt_type == 'c':
|
||||
c_specifier = 'c'
|
||||
elif fmt_type in ('f', 'F'):
|
||||
c_specifier = 'f'
|
||||
elif fmt_type in ('e', 'E', 'g', 'G'):
|
||||
c_specifier = fmt_type
|
||||
elif fmt_type == 's':
|
||||
c_specifier = 's'
|
||||
elif fmt_type == '%':
|
||||
c_specifier = 'f%%'
|
||||
else:
|
||||
if isinstance(val.type, ir.IntType):
|
||||
if val.type.width == 8:
|
||||
c_specifier = 'c'
|
||||
elif val.type.width == 1:
|
||||
c_specifier = 'd'
|
||||
else:
|
||||
c_specifier = f'{length_mod}d'
|
||||
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
|
||||
c_specifier = 'f'
|
||||
elif isinstance(val.type, ir.PointerType):
|
||||
c_specifier = 's'
|
||||
else:
|
||||
c_specifier = 'd'
|
||||
|
||||
return f'%{c_flags}{c_width}{c_precision}{c_specifier}'
|
||||
|
||||
def _cast_value_for_format(self, val: ir.Value, c_fmt: str, Gen: Any, arg_node: ast.AST) -> ir.Value:
|
||||
"""根据 C printf 格式说明符转换值类型。"""
|
||||
fmt_type_char: str = ''
|
||||
for ch in reversed(c_fmt):
|
||||
if ch.isalpha() and ch not in ('l', 'h'):
|
||||
fmt_type_char = ch
|
||||
break
|
||||
elif ch == '%':
|
||||
break
|
||||
|
||||
if fmt_type_char == 's':
|
||||
if isinstance(val.type, ir.PointerType):
|
||||
pointee: ir.Type = val.type.pointee
|
||||
if isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||||
return val
|
||||
if isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8:
|
||||
return Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="fstr_arr2ptr")
|
||||
return val
|
||||
elif fmt_type_char == 'c':
|
||||
if isinstance(val.type, ir.IntType) and val.type.width < 32:
|
||||
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_c")
|
||||
return val
|
||||
elif fmt_type_char in ('d', 'x', 'X', 'o', 'i'):
|
||||
if isinstance(val.type, ir.IntType):
|
||||
if val.type.width == 8:
|
||||
is_u: bool = Gen._check_node_unsigned(arg_node)
|
||||
if is_u:
|
||||
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i8")
|
||||
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i8")
|
||||
elif val.type.width == 16:
|
||||
is_u = Gen._check_node_unsigned(arg_node)
|
||||
if is_u:
|
||||
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i16")
|
||||
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i16")
|
||||
elif val.type.width == 1:
|
||||
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_bool")
|
||||
return val
|
||||
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
|
||||
return Gen.builder.fptosi(val, ir.IntType(32), name="fstr_f2i")
|
||||
return val
|
||||
elif fmt_type_char in ('f', 'e', 'E', 'g', 'G'):
|
||||
if isinstance(val.type, ir.FloatType):
|
||||
return Gen.builder.fpext(val, ir.DoubleType(), name="fstr_fpext")
|
||||
elif isinstance(val.type, ir.IntType):
|
||||
return Gen.builder.sitofp(val, ir.DoubleType(), name="fstr_i2f")
|
||||
return val
|
||||
return val
|
||||
+584
-584
File diff suppressed because it is too large.
Load diff
@@ -1,443 +1,443 @@
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Any
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.translator import Translator
|
||||
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
|
||||
import vast as ast
|
||||
import llvmlite.ir as ir
|
||||
from lib.core.SymbolUtils import IsTModule
|
||||
|
||||
|
||||
class ExprUtils:
|
||||
def __init__(self, translator: "Translator") -> None:
|
||||
self.Trans: Translator = translator
|
||||
|
||||
def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None:
|
||||
OpMap: dict[type, str] = {
|
||||
ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/',
|
||||
ast.FloorDiv: '//', ast.Mod: '%', ast.Pow: '**', ast.BitAnd: '&',
|
||||
ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>',
|
||||
ast.MatMult: '@',
|
||||
}
|
||||
return OpMap.get(type(Op), None)
|
||||
|
||||
def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None:
|
||||
OpMap: dict[type, str] = {
|
||||
ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-',
|
||||
ast.Deref: '*', ast.AddrOf: '&',
|
||||
}
|
||||
return OpMap.get(type(Op), None)
|
||||
|
||||
def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None:
|
||||
OpMap: dict[type, str] = {
|
||||
ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=',
|
||||
ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not',
|
||||
ast.In: 'in', ast.NotIn: 'not in',
|
||||
}
|
||||
return OpMap.get(type(Op), None)
|
||||
|
||||
def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None:
|
||||
if isinstance(node, ast.Name):
|
||||
VarName: str = node.id
|
||||
return Gen.var_struct_class.get(VarName)
|
||||
if isinstance(node, ast.Attribute):
|
||||
AttrName: str = node.attr
|
||||
# 从父对象的类成员类型推导(如 self._ht → ParsedArgs._ht → HashTable)
|
||||
# 必须先查 BaseClass,避免字段名和变量名冲突导致错误分派
|
||||
# (如 old_args.args 匹配到函数参数 args 的 var_struct_class='AST')
|
||||
BaseClass: str | None = self._get_var_class(node.value, Gen)
|
||||
if not BaseClass and isinstance(node.value, ast.Name) and node.value.id == 'self':
|
||||
BaseClass = getattr(self.Trans, '_CurrentCpythonObjectClass', None)
|
||||
if not BaseClass:
|
||||
# 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory):
|
||||
# var_struct_class 只登记**当前模块**变量,跨模块量不在其中 → 递归到此
|
||||
# 返回 None → 整条链 `Mod.Var.Field` 解析为空 → 赋值/调用 fail-fast
|
||||
# ("属性访问未能解析出目标类型/成员",StructGenMixin.vp:139 根因)。
|
||||
BaseClass = self._resolve_module_var_class(Gen, node.value, AttrName)
|
||||
if BaseClass:
|
||||
# 对齐 _HandleLenLlvm L3445-3454:class_member_element_class /
|
||||
# class_members 的键是裸类名,而 var_struct_class 可能带 sha1 前缀
|
||||
# (如 'd81b22c853de40a2.Call')。直查会 miss → 容器字段
|
||||
# (list[...] / rawptr[list[...]],如 ast.Call.args)方法分派失败
|
||||
# → "Undefined method 'get' in class 'rawptr'"。剥前缀 + MRO 链查
|
||||
# element_class(list 特化名,如 list[...ASTptr])优先。
|
||||
_bare: str = BaseClass
|
||||
if len(_bare) > 17 and _bare[16] == '.':
|
||||
_bare = _bare[17:]
|
||||
element_class_map = getattr(Gen, 'class_member_element_class', {})
|
||||
try:
|
||||
_mro_chain: list = Gen._get_mro_chain(_bare)
|
||||
except Exception:
|
||||
_mro_chain = [_bare]
|
||||
for _mro_cls in _mro_chain:
|
||||
_mk: str = _mro_cls
|
||||
if len(_mk) > 17 and _mk[16] == '.':
|
||||
_mk = _mk[17:]
|
||||
_sub = element_class_map.get(_mk)
|
||||
if _sub and AttrName in _sub:
|
||||
return _sub[AttrName]
|
||||
if _bare in Gen.class_members:
|
||||
for m_name, m_type in Gen.class_members[_bare]:
|
||||
if m_name == AttrName:
|
||||
if isinstance(m_type, ir.PointerType) and isinstance(m_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||
found = Gen.find_struct_by_pointee(m_type.pointee)
|
||||
if found:
|
||||
return found[0]
|
||||
break
|
||||
# Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后,
|
||||
# 避免字段名和变量名冲突导致错误分派)
|
||||
cls: str | None = Gen.var_struct_class.get(AttrName)
|
||||
if cls:
|
||||
return cls
|
||||
return None
|
||||
return None
|
||||
|
||||
def _resolve_module_var_class(self, Gen: LlvmCodeGenerator, ModNode: ast.AST,
|
||||
AttrName: str) -> str | None:
|
||||
"""`模块别名.模块级变量` 的类名解析(如 CompilerFactory.factory)
|
||||
|
||||
背景:`CompilerFactory.factory.PermPool` 这类链的第一跳既不在
|
||||
`var_struct_class`(只含当前模块变量)也不在 `global_struct_class`
|
||||
(只含按变量名登记的项),于是 `_get_var_class` 递归到底返回 None →
|
||||
整条链解析为空 → 赋值/调用 fail-fast("属性访问未能解析出目标类型/成员")。
|
||||
这里按模块别名把 `Mod.Var` 落到其 LLVM 全局量,再从全局量类型反推结构体
|
||||
类名;候选键形态对齐 `_lookup_module_global`(裸名 / {mod}.{var} /
|
||||
{sha1}.{var})。仅当 ModNode 确为 import 模块(含别名)时生效。
|
||||
"""
|
||||
if not isinstance(ModNode, ast.Name):
|
||||
return None
|
||||
Mod: str = ModNode.id
|
||||
imported: Any = getattr(self.Trans, '_ImportedModules', None) or []
|
||||
sha1_map: Any = getattr(Gen, 'ModuleSha1Map', {}) or {}
|
||||
try:
|
||||
resolved_mod: str = self.Trans.SymbolTable.resolve_alias(Mod)
|
||||
except Exception:
|
||||
resolved_mod = Mod
|
||||
aliases: Any = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {}
|
||||
_is_mod: bool = (Mod in imported or resolved_mod in imported
|
||||
or Mod in sha1_map or resolved_mod in sha1_map
|
||||
or Mod in aliases or resolved_mod in aliases)
|
||||
if not _is_mod:
|
||||
# 兜底:ModuleSha1Map 的键多为全路径(lib.core.CompilerFactory),
|
||||
# 用后缀扫描判定「Mod 是否确实是某个已导入模块(含别名)」。
|
||||
for _k in sha1_map:
|
||||
if _k == Mod or _k.endswith(f'.{Mod}'):
|
||||
_is_mod = True
|
||||
break
|
||||
if not _is_mod:
|
||||
return None
|
||||
# 1) 变量名侧已有登记(stub 解析时跨模块量常以**裸变量名**登记)
|
||||
gsc: Any = getattr(Gen, 'global_struct_class', {}) or {}
|
||||
vsc: Any = getattr(Gen, 'var_struct_class', {}) or {}
|
||||
for key in (f"{Mod}.{AttrName}", f"{resolved_mod}.{AttrName}", AttrName):
|
||||
if key in gsc:
|
||||
return gsc[key]
|
||||
if key in vsc:
|
||||
return vsc[key]
|
||||
# 2) 借属性侧既有解析器(覆盖 module.globals/variables 与前缀形态)
|
||||
try:
|
||||
_via_attr: str | None = self.Trans.ExprAttrHandle._resolve_var_classname(Gen, AttrName)
|
||||
except Exception:
|
||||
_via_attr = None
|
||||
if _via_attr:
|
||||
return _via_attr
|
||||
# 3) 从 LLVM 全局量类型反推结构体类名
|
||||
cands: list[str] = [f"{Mod}.{AttrName}", AttrName]
|
||||
for m in imported:
|
||||
if m == Mod or m.endswith(f".{Mod}"):
|
||||
k: str = f"{m}.{AttrName}"
|
||||
if k not in cands:
|
||||
cands.append(k)
|
||||
for cand in (Mod, resolved_mod):
|
||||
sha1: str | None = sha1_map.get(cand)
|
||||
if sha1:
|
||||
k2: str = f"{sha1}.{AttrName}"
|
||||
if k2 not in cands:
|
||||
cands.append(k2)
|
||||
globals_map: Any = getattr(getattr(Gen, 'module', None), 'globals', {}) or {}
|
||||
# 3b) 后缀扫描:全局量键常为 "{sha1}.{var}",sha1 不一定能从 Mod 反查到
|
||||
for _gk in globals_map:
|
||||
if _gk.endswith(f".{AttrName}") and _gk not in cands:
|
||||
cands.append(_gk)
|
||||
for key in cands:
|
||||
gv: Any = globals_map.get(key)
|
||||
if gv is None:
|
||||
continue
|
||||
ty: Any = getattr(gv, 'type', None)
|
||||
if not isinstance(ty, ir.PointerType):
|
||||
continue
|
||||
pointee: Any = ty.pointee
|
||||
# 全局量本身是指针变量(值类型 X*,LLVM 全局类型 X**)→ 再剥一层
|
||||
if isinstance(pointee, ir.PointerType):
|
||||
pointee = pointee.pointee
|
||||
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||
found: Any = Gen.find_struct_by_pointee(pointee)
|
||||
if found:
|
||||
return found[0]
|
||||
return None
|
||||
|
||||
def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None:
|
||||
FullMethodName: str = f'{ClassName}.{op_name}'
|
||||
if not Gen._has_function(FullMethodName):
|
||||
FullMethodName = f'{ClassName}.{op_name}__'
|
||||
if not Gen._has_function(FullMethodName):
|
||||
# 多继承:沿 MRO 祖先链查找 dunder 方法(先声明父类优先,与 TPV 一致)
|
||||
for anc in Gen._get_mro_chain(ClassName)[1:]:
|
||||
anc_method: str = f'{anc}.{op_name}'
|
||||
if Gen._has_function(anc_method):
|
||||
FullMethodName = anc_method
|
||||
break
|
||||
anc_method_ss = f'{anc}.{op_name}__'
|
||||
if Gen._has_function(anc_method_ss):
|
||||
FullMethodName = anc_method_ss
|
||||
break
|
||||
if not Gen._has_function(FullMethodName):
|
||||
# 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且
|
||||
# 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本
|
||||
# (如 'ndarray[double]')。注解 'numpy.ndarray | t.CPtr' 未指定类型参数时,
|
||||
# var_struct_class 仅记录裸模板名,但方法只在特化版本中定义。
|
||||
# 选择策略:优先选 obj_val.type.pointee 实际匹配的特化;否则选第一个有方法的特化。
|
||||
if '[' not in ClassName:
|
||||
SpecClass: str | None = None
|
||||
# 先收集所有候选特化(有方法 declare 或 define 的)
|
||||
best_candidates: list[str] = []
|
||||
for CN in Gen.structs.keys():
|
||||
if CN.startswith(f'{ClassName}[') and (Gen._has_function(f'{CN}.{op_name}') or Gen._has_function(f'{CN}.{op_name}__')):
|
||||
best_candidates.append(CN)
|
||||
# 优先选已编译方法体(define)的特化:dict 插入序选第一个会命中
|
||||
# 只有 declare 无 define 的错误特化(如 list[AST]),生成 undefined
|
||||
# symbol(ld.lld undefined symbol: list[AST].__len__ 根因)。
|
||||
for CN in best_candidates:
|
||||
_fn: str = f'{CN}.{op_name}' if Gen._has_function(f'{CN}.{op_name}') else f'{CN}.{op_name}__'
|
||||
_f: Any = Gen._get_function(_fn)
|
||||
if _f is not None and getattr(_f, 'blocks', []):
|
||||
SpecClass = CN
|
||||
break
|
||||
if SpecClass is None and best_candidates:
|
||||
# 全部只有 declare:按需补发方法体(幂等,_specialize_generic_class
|
||||
# 缓存机制会从 declare_only 升级为完整特化),补发后重新检查
|
||||
try:
|
||||
self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen)
|
||||
except Exception:
|
||||
pass
|
||||
_fn2: str = f'{best_candidates[0]}.{op_name}' if Gen._has_function(f'{best_candidates[0]}.{op_name}') else f'{best_candidates[0]}.{op_name}__'
|
||||
_f2: Any = Gen._get_function(_fn2)
|
||||
if _f2 is not None and getattr(_f2, 'blocks', []):
|
||||
SpecClass = best_candidates[0]
|
||||
if SpecClass:
|
||||
FullMethodName = f'{SpecClass}.{op_name}'
|
||||
if not Gen._has_function(FullMethodName):
|
||||
FullMethodName = f'{SpecClass}.{op_name}__'
|
||||
if Gen._has_function(FullMethodName):
|
||||
func: Any = Gen._get_function(FullMethodName)
|
||||
# 多继承 this 调整:运算符重载方法定义在"无 vtable 父类"且字段区偏移 > 0 时,
|
||||
# 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。
|
||||
method_anc: str | None = None
|
||||
_fm_short: str = FullMethodName.split('.')[-1] if '.' in FullMethodName else FullMethodName
|
||||
if '.' in FullMethodName:
|
||||
_fm_cls: str = FullMethodName.split('.')[0]
|
||||
if _fm_cls != ClassName:
|
||||
method_anc = _fm_cls
|
||||
_call_self: Any = self.Trans.ExprCallHandle._adjust_this_for_inherited_method(Gen, obj_val, ClassName, method_anc)
|
||||
call_args: list[Any] = [_call_self]
|
||||
if other_val is not None:
|
||||
call_args.append(other_val)
|
||||
if len(func.ftype.args) == len(call_args):
|
||||
for i, (expected_type, actual_val) in enumerate(zip(func.ftype.args, call_args)):
|
||||
if isinstance(expected_type, ir.PointerType) and isinstance(actual_val.type, ir.PointerType):
|
||||
if expected_type != actual_val.type:
|
||||
if isinstance(expected_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||
call_args[i] = Gen.builder.bitcast(actual_val, expected_type, name=f"bitcast_arg_{i}")
|
||||
result: Any = Gen.builder.call(func, call_args, name=f"call_{FullMethodName}")
|
||||
return result
|
||||
return None
|
||||
|
||||
def _llvm_type_to_detailed_string(self, llvm_type: ir.Type, var_name: str = "") -> dict[str, Any]:
|
||||
info: dict[str, Any] = {
|
||||
'name': "unknown",
|
||||
'size': 0,
|
||||
'signed': False,
|
||||
'ptr': False
|
||||
}
|
||||
if isinstance(llvm_type, ir.IntType):
|
||||
info['size'] = llvm_type.width // 8
|
||||
info['signed'] = True
|
||||
if llvm_type.width == 8:
|
||||
info['name'] = "char"
|
||||
elif llvm_type.width == 16:
|
||||
info['name'] = "short"
|
||||
elif llvm_type.width == 32:
|
||||
info['name'] = "int"
|
||||
elif llvm_type.width == 64:
|
||||
info['name'] = "long long"
|
||||
else:
|
||||
info['name'] = f"int{llvm_type.width}"
|
||||
elif isinstance(llvm_type, ir.DoubleType):
|
||||
info['size'] = 8
|
||||
info['signed'] = True
|
||||
info['name'] = "double"
|
||||
elif isinstance(llvm_type, ir.FloatType):
|
||||
info['size'] = 4
|
||||
info['signed'] = True
|
||||
info['name'] = "float"
|
||||
elif isinstance(llvm_type, ir.PointerType):
|
||||
info['size'] = 8
|
||||
info['signed'] = False
|
||||
info['ptr'] = True
|
||||
pointee_info: dict[str, Any] = self._llvm_type_to_detailed_string(llvm_type.pointee)
|
||||
info['name'] = f"{pointee_info['name']}*"
|
||||
elif isinstance(llvm_type, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||||
size: int = 0
|
||||
for elem in llvm_type.elements:
|
||||
elem_info: dict[str, Any] = self._llvm_type_to_detailed_string(elem)
|
||||
size += elem_info['size']
|
||||
info['size'] = size
|
||||
info['signed'] = False
|
||||
if isinstance(llvm_type, ir.IdentifiedStructType):
|
||||
info['name'] = llvm_type.name
|
||||
else:
|
||||
info['name'] = "struct"
|
||||
elif isinstance(llvm_type, ir.ArrayType):
|
||||
elem_info = self._llvm_type_to_detailed_string(llvm_type.element)
|
||||
info['size'] = elem_info['size'] * llvm_type.count
|
||||
info['signed'] = elem_info['signed']
|
||||
info['name'] = f"{elem_info['name']}[]"
|
||||
elif isinstance(llvm_type, ir.VoidType):
|
||||
info['size'] = 0
|
||||
info['signed'] = False
|
||||
info['name'] = "void"
|
||||
else:
|
||||
info['size'] = 0
|
||||
info['signed'] = False
|
||||
info['name'] = str(llvm_type)
|
||||
return info
|
||||
|
||||
def _infer_expr_llvm_type_full(self, node: ast.AST) -> ir.Type:
|
||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||
if isinstance(node, ast.Constant):
|
||||
if isinstance(node.value, bool):
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node.value, int):
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node.value, float):
|
||||
return ir.DoubleType()
|
||||
elif isinstance(node.value, str):
|
||||
if getattr(node, 'kind', None) == 'u':
|
||||
return ir.PointerType(ir.IntType(16))
|
||||
return ir.PointerType(ir.IntType(8))
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.Name):
|
||||
if node.id in Gen.variables:
|
||||
var_ptr: Any = Gen.variables[node.id]
|
||||
if isinstance(var_ptr.type, ir.PointerType):
|
||||
return var_ptr.type.pointee
|
||||
return var_ptr.type
|
||||
if node.id in Gen._reg_values:
|
||||
return Gen._reg_values[node.id].type
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.Attribute):
|
||||
attr_name: str = node.attr
|
||||
AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
|
||||
if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None):
|
||||
# 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型)
|
||||
return self.Trans.LlvmGen._enum_int_type(
|
||||
getattr(AttrInfo, 'EnumName', None), AttrInfo)
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.BinOp):
|
||||
left_type: ir.Type = self._infer_expr_llvm_type_full(node.left)
|
||||
right_type: ir.Type = self._infer_expr_llvm_type_full(node.right)
|
||||
if isinstance(left_type, (ir.FloatType, ir.DoubleType)) or isinstance(right_type, (ir.FloatType, ir.DoubleType)):
|
||||
return ir.DoubleType()
|
||||
if isinstance(left_type, ir.PointerType) or isinstance(right_type, ir.PointerType):
|
||||
return ir.PointerType(ir.IntType(8))
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.UnaryOp):
|
||||
op_inner: ir.Type = self._infer_expr_llvm_type_full(node.operand)
|
||||
if isinstance(node.op, ast.Deref):
|
||||
# *p → pointee 类型
|
||||
if isinstance(op_inner, ir.PointerType):
|
||||
return op_inner.pointee
|
||||
return op_inner
|
||||
if isinstance(node.op, ast.AddrOf):
|
||||
# &x → T*(取地址即指针)
|
||||
return ir.PointerType(op_inner)
|
||||
return op_inner
|
||||
elif isinstance(node, ast.Subscript):
|
||||
val_type: ir.Type = self._infer_expr_llvm_type_full(node.value)
|
||||
if isinstance(val_type, ir.PointerType):
|
||||
if isinstance(val_type.pointee, ir.IntType) and val_type.pointee.width == 8:
|
||||
return ir.IntType(8)
|
||||
if isinstance(val_type.pointee, ir.ArrayType):
|
||||
return ir.PointerType(val_type.pointee.element)
|
||||
return val_type.pointee
|
||||
if isinstance(val_type, ir.ArrayType):
|
||||
return val_type.element
|
||||
if isinstance(val_type, ir.IntType):
|
||||
return ir.IntType(8)
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.Call):
|
||||
if isinstance(node.func, ast.Name):
|
||||
func_name: str = node.func.id
|
||||
if Gen._has_function(func_name):
|
||||
fn: Any = Gen._get_function(func_name)
|
||||
return fn.function_type.return_type
|
||||
if func_name == 'len':
|
||||
return ir.IntType(32)
|
||||
if func_name == 'sizeof':
|
||||
return ir.IntType(32)
|
||||
if func_name == 'ord':
|
||||
return ir.IntType(32)
|
||||
if func_name == 'chr':
|
||||
return ir.IntType(8)
|
||||
if func_name == 'int':
|
||||
return ir.IntType(32)
|
||||
if func_name == 'float' or func_name == 'double':
|
||||
return ir.DoubleType()
|
||||
elif isinstance(node.func, ast.Attribute):
|
||||
if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c':
|
||||
if node.func.attr == 'Deref':
|
||||
return ir.IntType(64)
|
||||
if isinstance(node.func.value, ast.Name) and IsTModule(node.func.value.id, self.Trans.SymbolTable):
|
||||
if node.func.attr == 'CChar':
|
||||
return ir.PointerType(ir.IntType(8))
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.Compare):
|
||||
return ir.IntType(1)
|
||||
elif isinstance(node, ast.BoolOp):
|
||||
return ir.IntType(1)
|
||||
elif isinstance(node, ast.IfExp):
|
||||
return self._infer_expr_llvm_type_full(node.body)
|
||||
elif isinstance(node, ast.Attribute):
|
||||
return ir.PointerType(ir.IntType(8))
|
||||
return ir.IntType(32)
|
||||
|
||||
def _collect_names(self, node: ast.AST, bound: set[str], free: set[str] | None = None) -> None:
|
||||
"""收集 AST 节点中的绑定变量和自由变量
|
||||
|
||||
Args:
|
||||
node: AST 节点
|
||||
bound: 集合,收集绑定变量(ast.Store 上下文)
|
||||
free: 集合,收集自由变量(ast.Load 上下文),可为 None
|
||||
"""
|
||||
if free is None:
|
||||
free = set()
|
||||
if isinstance(node, ast.Name):
|
||||
if isinstance(node.ctx, ast.Store):
|
||||
bound.add(node.id)
|
||||
elif isinstance(node.ctx, ast.Load):
|
||||
free.add(node.id)
|
||||
elif isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
# 这些节点有自己的作用域,不递归进入
|
||||
# 但需要收集参数名作为 bound
|
||||
if hasattr(node, 'args') and node.args:
|
||||
for arg in node.args.args:
|
||||
bound.add(arg.arg)
|
||||
elif getattr(node, '_fields', None):
|
||||
for field in node._fields:
|
||||
value: Any = getattr(node, field, None)
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, (ast.stmt, ast.expr)):
|
||||
self._collect_names(item, bound, free)
|
||||
elif isinstance(value, (ast.stmt, ast.expr)):
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Any
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.translator import Translator
|
||||
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
|
||||
import vast as ast
|
||||
import llvmlite.ir as ir
|
||||
from lib.core.SymbolUtils import IsTModule
|
||||
|
||||
|
||||
class ExprUtils:
|
||||
def __init__(self, translator: "Translator") -> None:
|
||||
self.Trans: Translator = translator
|
||||
|
||||
def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None:
|
||||
OpMap: dict[type, str] = {
|
||||
ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/',
|
||||
ast.FloorDiv: '//', ast.Mod: '%', ast.Pow: '**', ast.BitAnd: '&',
|
||||
ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>',
|
||||
ast.MatMult: '@',
|
||||
}
|
||||
return OpMap.get(type(Op), None)
|
||||
|
||||
def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None:
|
||||
OpMap: dict[type, str] = {
|
||||
ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-',
|
||||
ast.Deref: '*', ast.AddrOf: '&',
|
||||
}
|
||||
return OpMap.get(type(Op), None)
|
||||
|
||||
def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None:
|
||||
OpMap: dict[type, str] = {
|
||||
ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=',
|
||||
ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not',
|
||||
ast.In: 'in', ast.NotIn: 'not in',
|
||||
}
|
||||
return OpMap.get(type(Op), None)
|
||||
|
||||
def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None:
|
||||
if isinstance(node, ast.Name):
|
||||
VarName: str = node.id
|
||||
return Gen.var_struct_class.get(VarName)
|
||||
if isinstance(node, ast.Attribute):
|
||||
AttrName: str = node.attr
|
||||
# 从父对象的类成员类型推导(如 self._ht → ParsedArgs._ht → HashTable)
|
||||
# 必须先查 BaseClass,避免字段名和变量名冲突导致错误分派
|
||||
# (如 old_args.args 匹配到函数参数 args 的 var_struct_class='AST')
|
||||
BaseClass: str | None = self._get_var_class(node.value, Gen)
|
||||
if not BaseClass and isinstance(node.value, ast.Name) and node.value.id == 'self':
|
||||
BaseClass = getattr(self.Trans, '_CurrentCpythonObjectClass', None)
|
||||
if not BaseClass:
|
||||
# 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory):
|
||||
# var_struct_class 只登记**当前模块**变量,跨模块量不在其中 → 递归到此
|
||||
# 返回 None → 整条链 `Mod.Var.Field` 解析为空 → 赋值/调用 fail-fast
|
||||
# ("属性访问未能解析出目标类型/成员",StructGenMixin.vp:139 根因)。
|
||||
BaseClass = self._resolve_module_var_class(Gen, node.value, AttrName)
|
||||
if BaseClass:
|
||||
# 对齐 _HandleLenLlvm L3445-3454:class_member_element_class /
|
||||
# class_members 的键是裸类名,而 var_struct_class 可能带 sha1 前缀
|
||||
# (如 'd81b22c853de40a2.Call')。直查会 miss → 容器字段
|
||||
# (list[...] / rawptr[list[...]],如 ast.Call.args)方法分派失败
|
||||
# → "Undefined method 'get' in class 'rawptr'"。剥前缀 + MRO 链查
|
||||
# element_class(list 特化名,如 list[...ASTptr])优先。
|
||||
_bare: str = BaseClass
|
||||
if len(_bare) > 17 and _bare[16] == '.':
|
||||
_bare = _bare[17:]
|
||||
element_class_map = getattr(Gen, 'class_member_element_class', {})
|
||||
try:
|
||||
_mro_chain: list = Gen._get_mro_chain(_bare)
|
||||
except Exception:
|
||||
_mro_chain = [_bare]
|
||||
for _mro_cls in _mro_chain:
|
||||
_mk: str = _mro_cls
|
||||
if len(_mk) > 17 and _mk[16] == '.':
|
||||
_mk = _mk[17:]
|
||||
_sub = element_class_map.get(_mk)
|
||||
if _sub and AttrName in _sub:
|
||||
return _sub[AttrName]
|
||||
if _bare in Gen.class_members:
|
||||
for m_name, m_type in Gen.class_members[_bare]:
|
||||
if m_name == AttrName:
|
||||
if isinstance(m_type, ir.PointerType) and isinstance(m_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||
found = Gen.find_struct_by_pointee(m_type.pointee)
|
||||
if found:
|
||||
return found[0]
|
||||
break
|
||||
# Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后,
|
||||
# 避免字段名和变量名冲突导致错误分派)
|
||||
cls: str | None = Gen.var_struct_class.get(AttrName)
|
||||
if cls:
|
||||
return cls
|
||||
return None
|
||||
return None
|
||||
|
||||
def _resolve_module_var_class(self, Gen: LlvmCodeGenerator, ModNode: ast.AST,
|
||||
AttrName: str) -> str | None:
|
||||
"""`模块别名.模块级变量` 的类名解析(如 CompilerFactory.factory)
|
||||
|
||||
背景:`CompilerFactory.factory.PermPool` 这类链的第一跳既不在
|
||||
`var_struct_class`(只含当前模块变量)也不在 `global_struct_class`
|
||||
(只含按变量名登记的项),于是 `_get_var_class` 递归到底返回 None →
|
||||
整条链解析为空 → 赋值/调用 fail-fast("属性访问未能解析出目标类型/成员")。
|
||||
这里按模块别名把 `Mod.Var` 落到其 LLVM 全局量,再从全局量类型反推结构体
|
||||
类名;候选键形态对齐 `_lookup_module_global`(裸名 / {mod}.{var} /
|
||||
{sha1}.{var})。仅当 ModNode 确为 import 模块(含别名)时生效。
|
||||
"""
|
||||
if not isinstance(ModNode, ast.Name):
|
||||
return None
|
||||
Mod: str = ModNode.id
|
||||
imported: Any = getattr(self.Trans, '_ImportedModules', None) or []
|
||||
sha1_map: Any = getattr(Gen, 'ModuleSha1Map', {}) or {}
|
||||
try:
|
||||
resolved_mod: str = self.Trans.SymbolTable.resolve_alias(Mod)
|
||||
except Exception:
|
||||
resolved_mod = Mod
|
||||
aliases: Any = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {}
|
||||
_is_mod: bool = (Mod in imported or resolved_mod in imported
|
||||
or Mod in sha1_map or resolved_mod in sha1_map
|
||||
or Mod in aliases or resolved_mod in aliases)
|
||||
if not _is_mod:
|
||||
# 兜底:ModuleSha1Map 的键多为全路径(lib.core.CompilerFactory),
|
||||
# 用后缀扫描判定「Mod 是否确实是某个已导入模块(含别名)」。
|
||||
for _k in sha1_map:
|
||||
if _k == Mod or _k.endswith(f'.{Mod}'):
|
||||
_is_mod = True
|
||||
break
|
||||
if not _is_mod:
|
||||
return None
|
||||
# 1) 变量名侧已有登记(stub 解析时跨模块量常以**裸变量名**登记)
|
||||
gsc: Any = getattr(Gen, 'global_struct_class', {}) or {}
|
||||
vsc: Any = getattr(Gen, 'var_struct_class', {}) or {}
|
||||
for key in (f"{Mod}.{AttrName}", f"{resolved_mod}.{AttrName}", AttrName):
|
||||
if key in gsc:
|
||||
return gsc[key]
|
||||
if key in vsc:
|
||||
return vsc[key]
|
||||
# 2) 借属性侧既有解析器(覆盖 module.globals/variables 与前缀形态)
|
||||
try:
|
||||
_via_attr: str | None = self.Trans.ExprAttrHandle._resolve_var_classname(Gen, AttrName)
|
||||
except Exception:
|
||||
_via_attr = None
|
||||
if _via_attr:
|
||||
return _via_attr
|
||||
# 3) 从 LLVM 全局量类型反推结构体类名
|
||||
cands: list[str] = [f"{Mod}.{AttrName}", AttrName]
|
||||
for m in imported:
|
||||
if m == Mod or m.endswith(f".{Mod}"):
|
||||
k: str = f"{m}.{AttrName}"
|
||||
if k not in cands:
|
||||
cands.append(k)
|
||||
for cand in (Mod, resolved_mod):
|
||||
sha1: str | None = sha1_map.get(cand)
|
||||
if sha1:
|
||||
k2: str = f"{sha1}.{AttrName}"
|
||||
if k2 not in cands:
|
||||
cands.append(k2)
|
||||
globals_map: Any = getattr(getattr(Gen, 'module', None), 'globals', {}) or {}
|
||||
# 3b) 后缀扫描:全局量键常为 "{sha1}.{var}",sha1 不一定能从 Mod 反查到
|
||||
for _gk in globals_map:
|
||||
if _gk.endswith(f".{AttrName}") and _gk not in cands:
|
||||
cands.append(_gk)
|
||||
for key in cands:
|
||||
gv: Any = globals_map.get(key)
|
||||
if gv is None:
|
||||
continue
|
||||
ty: Any = getattr(gv, 'type', None)
|
||||
if not isinstance(ty, ir.PointerType):
|
||||
continue
|
||||
pointee: Any = ty.pointee
|
||||
# 全局量本身是指针变量(值类型 X*,LLVM 全局类型 X**)→ 再剥一层
|
||||
if isinstance(pointee, ir.PointerType):
|
||||
pointee = pointee.pointee
|
||||
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||
found: Any = Gen.find_struct_by_pointee(pointee)
|
||||
if found:
|
||||
return found[0]
|
||||
return None
|
||||
|
||||
def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None:
|
||||
FullMethodName: str = f'{ClassName}.{op_name}'
|
||||
if not Gen._has_function(FullMethodName):
|
||||
FullMethodName = f'{ClassName}.{op_name}__'
|
||||
if not Gen._has_function(FullMethodName):
|
||||
# 多继承:沿 MRO 祖先链查找 dunder 方法(先声明父类优先,与 VPC 一致)
|
||||
for anc in Gen._get_mro_chain(ClassName)[1:]:
|
||||
anc_method: str = f'{anc}.{op_name}'
|
||||
if Gen._has_function(anc_method):
|
||||
FullMethodName = anc_method
|
||||
break
|
||||
anc_method_ss = f'{anc}.{op_name}__'
|
||||
if Gen._has_function(anc_method_ss):
|
||||
FullMethodName = anc_method_ss
|
||||
break
|
||||
if not Gen._has_function(FullMethodName):
|
||||
# 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且
|
||||
# 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本
|
||||
# (如 'ndarray[double]')。注解 'numpy.ndarray | t.CPtr' 未指定类型参数时,
|
||||
# var_struct_class 仅记录裸模板名,但方法只在特化版本中定义。
|
||||
# 选择策略:优先选 obj_val.type.pointee 实际匹配的特化;否则选第一个有方法的特化。
|
||||
if '[' not in ClassName:
|
||||
SpecClass: str | None = None
|
||||
# 先收集所有候选特化(有方法 declare 或 define 的)
|
||||
best_candidates: list[str] = []
|
||||
for CN in Gen.structs.keys():
|
||||
if CN.startswith(f'{ClassName}[') and (Gen._has_function(f'{CN}.{op_name}') or Gen._has_function(f'{CN}.{op_name}__')):
|
||||
best_candidates.append(CN)
|
||||
# 优先选已编译方法体(define)的特化:dict 插入序选第一个会命中
|
||||
# 只有 declare 无 define 的错误特化(如 list[AST]),生成 undefined
|
||||
# symbol(ld.lld undefined symbol: list[AST].__len__ 根因)。
|
||||
for CN in best_candidates:
|
||||
_fn: str = f'{CN}.{op_name}' if Gen._has_function(f'{CN}.{op_name}') else f'{CN}.{op_name}__'
|
||||
_f: Any = Gen._get_function(_fn)
|
||||
if _f is not None and getattr(_f, 'blocks', []):
|
||||
SpecClass = CN
|
||||
break
|
||||
if SpecClass is None and best_candidates:
|
||||
# 全部只有 declare:按需补发方法体(幂等,_specialize_generic_class
|
||||
# 缓存机制会从 declare_only 升级为完整特化),补发后重新检查
|
||||
try:
|
||||
self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen)
|
||||
except Exception:
|
||||
pass
|
||||
_fn2: str = f'{best_candidates[0]}.{op_name}' if Gen._has_function(f'{best_candidates[0]}.{op_name}') else f'{best_candidates[0]}.{op_name}__'
|
||||
_f2: Any = Gen._get_function(_fn2)
|
||||
if _f2 is not None and getattr(_f2, 'blocks', []):
|
||||
SpecClass = best_candidates[0]
|
||||
if SpecClass:
|
||||
FullMethodName = f'{SpecClass}.{op_name}'
|
||||
if not Gen._has_function(FullMethodName):
|
||||
FullMethodName = f'{SpecClass}.{op_name}__'
|
||||
if Gen._has_function(FullMethodName):
|
||||
func: Any = Gen._get_function(FullMethodName)
|
||||
# 多继承 this 调整:运算符重载方法定义在"无 vtable 父类"且字段区偏移 > 0 时,
|
||||
# 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。
|
||||
method_anc: str | None = None
|
||||
_fm_short: str = FullMethodName.split('.')[-1] if '.' in FullMethodName else FullMethodName
|
||||
if '.' in FullMethodName:
|
||||
_fm_cls: str = FullMethodName.split('.')[0]
|
||||
if _fm_cls != ClassName:
|
||||
method_anc = _fm_cls
|
||||
_call_self: Any = self.Trans.ExprCallHandle._adjust_this_for_inherited_method(Gen, obj_val, ClassName, method_anc)
|
||||
call_args: list[Any] = [_call_self]
|
||||
if other_val is not None:
|
||||
call_args.append(other_val)
|
||||
if len(func.ftype.args) == len(call_args):
|
||||
for i, (expected_type, actual_val) in enumerate(zip(func.ftype.args, call_args)):
|
||||
if isinstance(expected_type, ir.PointerType) and isinstance(actual_val.type, ir.PointerType):
|
||||
if expected_type != actual_val.type:
|
||||
if isinstance(expected_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||
call_args[i] = Gen.builder.bitcast(actual_val, expected_type, name=f"bitcast_arg_{i}")
|
||||
result: Any = Gen.builder.call(func, call_args, name=f"call_{FullMethodName}")
|
||||
return result
|
||||
return None
|
||||
|
||||
def _llvm_type_to_detailed_string(self, llvm_type: ir.Type, var_name: str = "") -> dict[str, Any]:
|
||||
info: dict[str, Any] = {
|
||||
'name': "unknown",
|
||||
'size': 0,
|
||||
'signed': False,
|
||||
'ptr': False
|
||||
}
|
||||
if isinstance(llvm_type, ir.IntType):
|
||||
info['size'] = llvm_type.width // 8
|
||||
info['signed'] = True
|
||||
if llvm_type.width == 8:
|
||||
info['name'] = "char"
|
||||
elif llvm_type.width == 16:
|
||||
info['name'] = "short"
|
||||
elif llvm_type.width == 32:
|
||||
info['name'] = "int"
|
||||
elif llvm_type.width == 64:
|
||||
info['name'] = "long long"
|
||||
else:
|
||||
info['name'] = f"int{llvm_type.width}"
|
||||
elif isinstance(llvm_type, ir.DoubleType):
|
||||
info['size'] = 8
|
||||
info['signed'] = True
|
||||
info['name'] = "double"
|
||||
elif isinstance(llvm_type, ir.FloatType):
|
||||
info['size'] = 4
|
||||
info['signed'] = True
|
||||
info['name'] = "float"
|
||||
elif isinstance(llvm_type, ir.PointerType):
|
||||
info['size'] = 8
|
||||
info['signed'] = False
|
||||
info['ptr'] = True
|
||||
pointee_info: dict[str, Any] = self._llvm_type_to_detailed_string(llvm_type.pointee)
|
||||
info['name'] = f"{pointee_info['name']}*"
|
||||
elif isinstance(llvm_type, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||||
size: int = 0
|
||||
for elem in llvm_type.elements:
|
||||
elem_info: dict[str, Any] = self._llvm_type_to_detailed_string(elem)
|
||||
size += elem_info['size']
|
||||
info['size'] = size
|
||||
info['signed'] = False
|
||||
if isinstance(llvm_type, ir.IdentifiedStructType):
|
||||
info['name'] = llvm_type.name
|
||||
else:
|
||||
info['name'] = "struct"
|
||||
elif isinstance(llvm_type, ir.ArrayType):
|
||||
elem_info = self._llvm_type_to_detailed_string(llvm_type.element)
|
||||
info['size'] = elem_info['size'] * llvm_type.count
|
||||
info['signed'] = elem_info['signed']
|
||||
info['name'] = f"{elem_info['name']}[]"
|
||||
elif isinstance(llvm_type, ir.VoidType):
|
||||
info['size'] = 0
|
||||
info['signed'] = False
|
||||
info['name'] = "void"
|
||||
else:
|
||||
info['size'] = 0
|
||||
info['signed'] = False
|
||||
info['name'] = str(llvm_type)
|
||||
return info
|
||||
|
||||
def _infer_expr_llvm_type_full(self, node: ast.AST) -> ir.Type:
|
||||
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
|
||||
if isinstance(node, ast.Constant):
|
||||
if isinstance(node.value, bool):
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node.value, int):
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node.value, float):
|
||||
return ir.DoubleType()
|
||||
elif isinstance(node.value, str):
|
||||
if getattr(node, 'kind', None) == 'u':
|
||||
return ir.PointerType(ir.IntType(16))
|
||||
return ir.PointerType(ir.IntType(8))
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.Name):
|
||||
if node.id in Gen.variables:
|
||||
var_ptr: Any = Gen.variables[node.id]
|
||||
if isinstance(var_ptr.type, ir.PointerType):
|
||||
return var_ptr.type.pointee
|
||||
return var_ptr.type
|
||||
if node.id in Gen._reg_values:
|
||||
return Gen._reg_values[node.id].type
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.Attribute):
|
||||
attr_name: str = node.attr
|
||||
AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
|
||||
if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None):
|
||||
# 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型)
|
||||
return self.Trans.LlvmGen._enum_int_type(
|
||||
getattr(AttrInfo, 'EnumName', None), AttrInfo)
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.BinOp):
|
||||
left_type: ir.Type = self._infer_expr_llvm_type_full(node.left)
|
||||
right_type: ir.Type = self._infer_expr_llvm_type_full(node.right)
|
||||
if isinstance(left_type, (ir.FloatType, ir.DoubleType)) or isinstance(right_type, (ir.FloatType, ir.DoubleType)):
|
||||
return ir.DoubleType()
|
||||
if isinstance(left_type, ir.PointerType) or isinstance(right_type, ir.PointerType):
|
||||
return ir.PointerType(ir.IntType(8))
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.UnaryOp):
|
||||
op_inner: ir.Type = self._infer_expr_llvm_type_full(node.operand)
|
||||
if isinstance(node.op, ast.Deref):
|
||||
# *p → pointee 类型
|
||||
if isinstance(op_inner, ir.PointerType):
|
||||
return op_inner.pointee
|
||||
return op_inner
|
||||
if isinstance(node.op, ast.AddrOf):
|
||||
# &x → T*(取地址即指针)
|
||||
return ir.PointerType(op_inner)
|
||||
return op_inner
|
||||
elif isinstance(node, ast.Subscript):
|
||||
val_type: ir.Type = self._infer_expr_llvm_type_full(node.value)
|
||||
if isinstance(val_type, ir.PointerType):
|
||||
if isinstance(val_type.pointee, ir.IntType) and val_type.pointee.width == 8:
|
||||
return ir.IntType(8)
|
||||
if isinstance(val_type.pointee, ir.ArrayType):
|
||||
return ir.PointerType(val_type.pointee.element)
|
||||
return val_type.pointee
|
||||
if isinstance(val_type, ir.ArrayType):
|
||||
return val_type.element
|
||||
if isinstance(val_type, ir.IntType):
|
||||
return ir.IntType(8)
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.Call):
|
||||
if isinstance(node.func, ast.Name):
|
||||
func_name: str = node.func.id
|
||||
if Gen._has_function(func_name):
|
||||
fn: Any = Gen._get_function(func_name)
|
||||
return fn.function_type.return_type
|
||||
if func_name == 'len':
|
||||
return ir.IntType(32)
|
||||
if func_name == 'sizeof':
|
||||
return ir.IntType(32)
|
||||
if func_name == 'ord':
|
||||
return ir.IntType(32)
|
||||
if func_name == 'chr':
|
||||
return ir.IntType(8)
|
||||
if func_name == 'int':
|
||||
return ir.IntType(32)
|
||||
if func_name == 'float' or func_name == 'double':
|
||||
return ir.DoubleType()
|
||||
elif isinstance(node.func, ast.Attribute):
|
||||
if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c':
|
||||
if node.func.attr == 'Deref':
|
||||
return ir.IntType(64)
|
||||
if isinstance(node.func.value, ast.Name) and IsTModule(node.func.value.id, self.Trans.SymbolTable):
|
||||
if node.func.attr == 'CChar':
|
||||
return ir.PointerType(ir.IntType(8))
|
||||
return ir.IntType(32)
|
||||
elif isinstance(node, ast.Compare):
|
||||
return ir.IntType(1)
|
||||
elif isinstance(node, ast.BoolOp):
|
||||
return ir.IntType(1)
|
||||
elif isinstance(node, ast.IfExp):
|
||||
return self._infer_expr_llvm_type_full(node.body)
|
||||
elif isinstance(node, ast.Attribute):
|
||||
return ir.PointerType(ir.IntType(8))
|
||||
return ir.IntType(32)
|
||||
|
||||
def _collect_names(self, node: ast.AST, bound: set[str], free: set[str] | None = None) -> None:
|
||||
"""收集 AST 节点中的绑定变量和自由变量
|
||||
|
||||
Args:
|
||||
node: AST 节点
|
||||
bound: 集合,收集绑定变量(ast.Store 上下文)
|
||||
free: 集合,收集自由变量(ast.Load 上下文),可为 None
|
||||
"""
|
||||
if free is None:
|
||||
free = set()
|
||||
if isinstance(node, ast.Name):
|
||||
if isinstance(node.ctx, ast.Store):
|
||||
bound.add(node.id)
|
||||
elif isinstance(node.ctx, ast.Load):
|
||||
free.add(node.id)
|
||||
elif isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
# 这些节点有自己的作用域,不递归进入
|
||||
# 但需要收集参数名作为 bound
|
||||
if hasattr(node, 'args') and node.args:
|
||||
for arg in node.args.args:
|
||||
bound.add(arg.arg)
|
||||
elif getattr(node, '_fields', None):
|
||||
for field in node._fields:
|
||||
value: Any = getattr(node, field, None)
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, (ast.stmt, ast.expr)):
|
||||
self._collect_names(item, bound, free)
|
||||
elif isinstance(value, (ast.stmt, ast.expr)):
|
||||
self._collect_names(value, bound, free)
|
||||
+2291
-2291
File diff suppressed because it is too large.
Load diff
+4157
-4157
File diff suppressed because it is too large.
Load diff
+1142
-1142
File diff suppressed because it is too large.
Load diff
+2493
-2493
File diff suppressed because it is too large.
Load diff
@@ -76,7 +76,7 @@ class ExprGenMixin:
|
||||
# (GEP 只接受「一指针一整数」,已在上方两个 ptr/int 分支处理并返回)。
|
||||
# 落到下方 ops/shift_ops/bit_ops 会直出非法 IR(`add i8*, i8*` ⇒ opt 报
|
||||
# invalid operand type for instruction),故在 IR 生成前 fail-fast。
|
||||
# 判据与报文逐句对齐 TPV ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。
|
||||
# 判据与报文逐句对齐 VPC ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。
|
||||
# ============================================================
|
||||
if op in ('+', 'Add', '-', 'Sub', '*', 'Mult', '/', 'Div', '//', 'FloorDiv',
|
||||
'%', 'Mod', '**', 'Pow', '&', 'BitAnd', '|', 'BitOr', '^', 'BitXor',
|
||||
@@ -346,7 +346,7 @@ class ExprGenMixin:
|
||||
value = self.builder.bitcast(value, ret_type, name="ret_cast")
|
||||
elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType):
|
||||
if isinstance(ret_type, ir.IntType):
|
||||
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
|
||||
# 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径)。
|
||||
# 原先"同宽 load 所指物 / 其余 ptrtoint"都是静默转换。
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||
raise FailFastError(
|
||||
|
||||
+1258
-1258
File diff suppressed because it is too large.
Load diff
@@ -16,7 +16,7 @@ class _VolatileLoadInstr(ir.LoadInstr):
|
||||
`descr` 也不输出该关键字),故按本仓库既有做法(BaseGen.VaArgInstruction
|
||||
自定义 `descr`)派生子类,只把 `load` 关键字扩成 `load volatile`。
|
||||
输出格式与 llvmlite 原始实现逐字段一致(类型、指针、align、元数据),
|
||||
仅多一个 `volatile` 关键字;语义与 TransPyV 侧 `build_load(..., vol)` 对齐。
|
||||
仅多一个 `volatile` 关键字;语义与 ViperC 侧 `build_load(..., vol)` 对齐。
|
||||
"""
|
||||
|
||||
def descr(self, buf: list[str]) -> None:
|
||||
@@ -147,7 +147,7 @@ class MemoryOpsMixin:
|
||||
# ============================================================
|
||||
# 隐式转换公共判据:None(空值常量)/ 指针层级 / 基本类型 / 无损
|
||||
#
|
||||
# 语义唯一来源与 TransPyV 对齐(ExprGenBase.IsNoneValue、coerce_to_type)。
|
||||
# 语义唯一来源与 ViperC 对齐(ExprGenBase.IsNoneValue、coerce_to_type)。
|
||||
# 收敛于此,供赋值 / 返回 / 实参 / 数组元素等所有 coerce 入口复用,避免
|
||||
# 各处各自演化出相反结论。
|
||||
# ============================================================
|
||||
@@ -197,7 +197,7 @@ class MemoryOpsMixin:
|
||||
|
||||
@staticmethod
|
||||
def _ScalarConvertLossless(Src: Any, Dst: Any) -> bool:
|
||||
"""基本标量「无损」判定(与 TransPyV `_scalar_convert_reason` 同口径,逐条照搬):
|
||||
"""基本标量「无损」判定(与 ViperC `_scalar_convert_reason` 同口径,逐条照搬):
|
||||
- 整数 ↔ 整数:允许 ⇔ **目标位宽 ≥ 源位宽**(低→高无损;高→低是**有损窄化**,禁);
|
||||
- 浮点 ↔ 浮点:**所有方向都允许**;
|
||||
- 整数 → 浮点:允许 ⇔ **目标浮点位宽 ≥ 源整数位宽**(否则有损 → 禁);
|
||||
@@ -306,7 +306,7 @@ class MemoryOpsMixin:
|
||||
Node: Any = None) -> tuple[bool, str]:
|
||||
"""隐式转换判据:返回 (Allowed, Reason)。
|
||||
|
||||
规则(用户决定,与 TransPyV 同口径):
|
||||
规则(用户决定,与 ViperC 同口径):
|
||||
① 通用指针(t.CPtr/i8*)为通配:与任何 T* **双向**互转放行(须同级)。
|
||||
② T* → U*(皆非通用):允许 ⇔ 被指类型 T → U 无损;非基本类型
|
||||
(结构体/类/联合)一律不允许 ⇒ Foo*→Bar* 禁止;**例外**有二:
|
||||
@@ -368,7 +368,7 @@ class MemoryOpsMixin:
|
||||
|
||||
@staticmethod
|
||||
def _ValueNodeOf(Node: Any = None) -> Any:
|
||||
"""把「外层语句节点」下沉到其「值子表达式节点」(与 TransPyV _value_node_of 同口径)。
|
||||
"""把「外层语句节点」下沉到其「值子表达式节点」(与 ViperC _value_node_of 同口径)。
|
||||
|
||||
Assign/AnnAssign/AugAssign/Expr/Return 取 .value;非语句节点原样返回。
|
||||
规则③的「构造物化」豁免须看到**真正被转换的那个右值表达式**(如 `p: Point = Point()`
|
||||
@@ -392,7 +392,7 @@ class MemoryOpsMixin:
|
||||
def _IsFrameStackAddr(Node: Any, Gen: Any, TargetType: Any = None) -> bool:
|
||||
"""AST 判据:Node 是否交出「当前帧栈上存储的地址」。
|
||||
|
||||
两种形态(与 TransPyV fail_if_stack_addr_escape 同口径):
|
||||
两种形态(与 ViperC fail_if_stack_addr_escape 同口径):
|
||||
(a) `&local`:UnaryOp(AddrOf, Name),且该 Name 是本帧局部 alloca(含形参);
|
||||
(b) 数组退让:裸本帧**数组**名出现在**指针**目标位置(如 `return arr` with `-> char*`),
|
||||
交出的是首元素地址,同属栈地址。
|
||||
@@ -695,7 +695,7 @@ class MemoryOpsMixin:
|
||||
val = self.builder.zext(val, ir.IntType(64), name="zext_ptr_store")
|
||||
return self.builder.inttoptr(val, target_type, name="int_to_ptr_store")
|
||||
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.PointerType):
|
||||
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
|
||||
# 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径)。
|
||||
# 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load
|
||||
# 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。
|
||||
# None(空指针常量)已在上方短路为目标零值,不会落到这里。
|
||||
|
||||
+472
-472
@@ -1,23 +1,23 @@
|
||||
"""LLVMTypeMapper — AST 注解节点到 LLVM IR 类型字符串的映射器
|
||||
|
||||
从 SymbolTable 中提取的类型解析逻辑,职责单一:
|
||||
将 Python AST 注解节点翻译为 LLVM IR 类型字符串(如 'i32', 'i8*', '{ i32, i8* }')。
|
||||
|
||||
原 SymbolTable 上的 4 个方法:
|
||||
- _GetLLVMTypeStr → LLVMTypeMapper.get_llvm_type_str
|
||||
- _GetFuncRetTypeStr → LLVMTypeMapper.get_func_ret_type_str
|
||||
- _GetFuncParamTypeStr → LLVMTypeMapper.get_func_param_type_str
|
||||
- _ResolveTypedefValueType → LLVMTypeMapper.resolve_typedef_value_type
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import vast as ast
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.SymbolTable import SymbolTable
|
||||
|
||||
from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap
|
||||
"""LLVMTypeMapper — AST 注解节点到 LLVM IR 类型字符串的映射器
|
||||
|
||||
从 SymbolTable 中提取的类型解析逻辑,职责单一:
|
||||
将 Python AST 注解节点翻译为 LLVM IR 类型字符串(如 'i32', 'i8*', '{ i32, i8* }')。
|
||||
|
||||
原 SymbolTable 上的 4 个方法:
|
||||
- _GetLLVMTypeStr → LLVMTypeMapper.get_llvm_type_str
|
||||
- _GetFuncRetTypeStr → LLVMTypeMapper.get_func_ret_type_str
|
||||
- _GetFuncParamTypeStr → LLVMTypeMapper.get_func_param_type_str
|
||||
- _ResolveTypedefValueType → LLVMTypeMapper.resolve_typedef_value_type
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import vast as ast
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.SymbolTable import SymbolTable
|
||||
|
||||
from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap
|
||||
from lib.core.SymbolUtils import (
|
||||
IsTModule, IsListAnnotation, IsCPtrNode, IsCPtrSubscriptNode,
|
||||
IsRawPtrSubscriptNode, EnumIntWidth,
|
||||
@@ -110,347 +110,347 @@ class LLVMTypeMapper:
|
||||
resolved_b: str = self._ctype_to_llvm_str(entry)
|
||||
if resolved_b:
|
||||
return resolved_b
|
||||
return ''
|
||||
|
||||
# ==================================================================
|
||||
# 公共 API
|
||||
# ==================================================================
|
||||
|
||||
def get_llvm_type_str(self, node: ast.AST | None) -> str:
|
||||
"""将 AST 注解节点转换为 LLVM IR 类型字符串"""
|
||||
if node is None:
|
||||
self._symtab.diagnostics.warn('', 0, "类型注解为 None,回退到 i32")
|
||||
return 'i32'
|
||||
if isinstance(node, ast.Name):
|
||||
return self._resolve_name(node)
|
||||
elif isinstance(node, ast.Attribute):
|
||||
return self._resolve_attribute(node)
|
||||
elif isinstance(node, ast.Subscript):
|
||||
return self._resolve_subscript(node)
|
||||
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
|
||||
return self._resolve_bitor(node)
|
||||
elif isinstance(node, ast.Constant):
|
||||
if isinstance(node.value, bool):
|
||||
return 'i8'
|
||||
return 'i32'
|
||||
elif isinstance(node, ast.Call):
|
||||
return self._resolve_call(node)
|
||||
self._symtab.diagnostics.warn('', 0, f"无法解析类型节点 {type(node).__name__},回退到 i32")
|
||||
return 'i32'
|
||||
|
||||
def get_func_ret_type_str(self, returns_node: ast.AST | None) -> str:
|
||||
"""提取函数返回类型的 LLVM 字符串"""
|
||||
if returns_node is None:
|
||||
return 'i32'
|
||||
if isinstance(returns_node, ast.BinOp) and isinstance(returns_node.op, ast.BitOr):
|
||||
# 统一走 CPtr 感知的联合解析(int | t.CPtr → i32*、str | t.CPtr → i8**)
|
||||
return self._resolve_bitor(returns_node)
|
||||
result: str = self.get_llvm_type_str(returns_node)
|
||||
return result if result else 'i32'
|
||||
|
||||
def get_func_param_type_str(self, annotation_node: ast.AST | None) -> str:
|
||||
"""提取函数参数类型的 LLVM 字符串。
|
||||
|
||||
末尾的「不许静默变 i32」守卫:`Subscript` / 联合(`|`)形态的形参注解若解析结果
|
||||
是 `'i32'`,只可能是**解析失败后的兜底** —— 合法的下标注解是容器 / 泛型 / 定长数组
|
||||
(解析出指针、数组或结构体指针),合法的联合写法是 `T | t.CPtr` 这类指针叠加,都
|
||||
不会是 i32。该串会被 `_InsertFuncSymbol` 原样存进符号表(`FuncPtrParams`),再由
|
||||
`BuildLLVMFuncTypeFromSig` 物化成形参类型 —— 一旦是 i32,调用点就在 `_adjust_args`
|
||||
报「实参 %X* 不能隐式转为 i32」,把真因(**注解没解析出来**)掩盖成"调用点类型
|
||||
不匹配"(现场:CallGenMixin.vp:149 调用 `_specialize_generic_class` 的
|
||||
`type_args: list[str]*`)。故此处响亮失败,并带上注解原文。
|
||||
"""
|
||||
if annotation_node is None:
|
||||
return 'i8*'
|
||||
result: str
|
||||
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
|
||||
# 统一走 CPtr 感知的联合解析
|
||||
result = self._resolve_bitor(annotation_node)
|
||||
else:
|
||||
result = self.get_llvm_type_str(annotation_node)
|
||||
if not result:
|
||||
result = 'i8*'
|
||||
if result == 'i32' and isinstance(annotation_node, (ast.Subscript, ast.BinOp)):
|
||||
_text: str
|
||||
try:
|
||||
_text = ast.unparse(annotation_node)
|
||||
except Exception:
|
||||
_text = f'<{type(annotation_node).__name__}>'
|
||||
raise TypeNameUnresolvedError(
|
||||
f"形参注解 '{_text}' 解析失败并落到 i32(下标 / 联合形态的合法结果应为"
|
||||
f"指针、数组或结构体指针,不会是 i32)—— 拒绝静默降级为 i32")
|
||||
return result
|
||||
|
||||
def resolve_typedef_value_type(self, node: ast.AST | None) -> str:
|
||||
"""解析 typedef 值表达式的 C 类型字符串"""
|
||||
if node is None:
|
||||
return ''
|
||||
if isinstance(node, ast.Name):
|
||||
if node.id in self._symtab:
|
||||
Entry: CTypeInfo = self._symtab[node.id]
|
||||
if Entry.IsTypedef and Entry.OriginalType:
|
||||
return Entry.OriginalType
|
||||
# 短别名 / 内建速记(char/int/int64/uchar/str…):与注解位置
|
||||
# `_resolve_name` 同源。缺此分支时 typedef 值里的裸名解析为空,
|
||||
# 整条 typedef 随之解析失败(现场:w32/win32base.vp 的
|
||||
# `LPCSTR: t.CTypedef = t.CConst | char | t.CPtr` ⇒ LPCSTR 不登记,
|
||||
# 用到处报「裸名 LPCSTR 无法解析为类型」)。
|
||||
_binfo: CTypeInfo | None = self._lookup_builtins_typedef(node.id)
|
||||
if _binfo is None:
|
||||
_e: tuple[type, int] | None = BuiltinTypeMap.Get(node.id)
|
||||
if _e:
|
||||
_binfo = CTypeInfo()
|
||||
_binfo.BaseType = _e[0]
|
||||
_binfo.PtrCount = _e[1]
|
||||
if _binfo is not None and _binfo.BaseType:
|
||||
_bt: object = _binfo.BaseType
|
||||
_nm: str = (_bt if isinstance(_bt, str)
|
||||
else CTypeHelper.GetCName(_bt) if isinstance(_bt, type)
|
||||
else type(_bt).__name__)
|
||||
if _nm and _nm != 'CType':
|
||||
return _nm + (' *' * (_binfo.PtrCount or 0))
|
||||
return ''
|
||||
if isinstance(node, ast.Attribute):
|
||||
if isinstance(node.value, ast.Name) and IsTModule(node.value.id):
|
||||
# 直接判断属性名是否为 CPtr(消除对 CName=='*' 的依赖)
|
||||
if node.attr == 'CPtr':
|
||||
return 'ptr'
|
||||
if node.attr in _TYPEDEF_VALUE_QUALIFIERS:
|
||||
# 类型限定符标记(const/volatile)本身不是类型:它只修饰 `|` 另一侧。
|
||||
# 旧写法 `t.CConst | char | t.CPtr` 靠 char 一侧掩盖了这一点;一旦写成
|
||||
# `t.CConst | str`(`const str`),把 'CConst' 当基类型会得到 'CConst *',
|
||||
# 于是别名登记失败、所有使用处报「裸名 … 无法解析为类型」。
|
||||
return ''
|
||||
CName: str | None = CTypeHelper.GetCName(node.attr)
|
||||
if CName == 'CPtr':
|
||||
return 'ptr'
|
||||
if CName:
|
||||
return CName
|
||||
return ''
|
||||
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
|
||||
left_str: str = self.resolve_typedef_value_type(node.left)
|
||||
right_str: str = self.resolve_typedef_value_type(node.right)
|
||||
# 限定符侧解析为空串 ⇒ 另一侧就是完整类型(见 _TYPEDEF_VALUE_QUALIFIERS)。
|
||||
# 只在两侧都有类型信息时才按"基类型 + 指针标记"合并。
|
||||
if not left_str:
|
||||
return 'void *' if right_str == 'ptr' else right_str
|
||||
if not right_str:
|
||||
return 'void *' if left_str == 'ptr' else left_str
|
||||
if right_str == 'ptr' or right_str.endswith('*'):
|
||||
if left_str != 'ptr':
|
||||
return left_str + ' *'
|
||||
return right_str if right_str.endswith('*') else 'void *'
|
||||
if left_str == 'ptr' or left_str.endswith('*'):
|
||||
if right_str != 'ptr':
|
||||
return right_str + ' *'
|
||||
return left_str if left_str.endswith('*') else 'void *'
|
||||
if right_str not in ('ptr', 'void', 'i32', 'i64'):
|
||||
return right_str
|
||||
if left_str not in ('ptr', 'void', 'i32', 'i64'):
|
||||
return left_str
|
||||
return left_str if left_str else right_str
|
||||
return ''
|
||||
|
||||
# ==================================================================
|
||||
# 内部解析方法
|
||||
# ==================================================================
|
||||
|
||||
def _ctype_to_llvm_str(self, info: CTypeInfo) -> str:
|
||||
"""从 CTypeInfo 的 BaseType+PtrCount 生成归一化的 LLVM 类型字符串。
|
||||
|
||||
void* 在 LLVM IR 中归一化为 i8*;struct/enum/union 等非基础类型返回空字符串。
|
||||
"""
|
||||
if not info or not info.BaseType:
|
||||
return ''
|
||||
base_type = info.BaseType
|
||||
base_llvm: str = ''
|
||||
if isinstance(base_type, type) and issubclass(base_type, _t.CType):
|
||||
base_llvm = CTypeRegistry.CTypeToLLVM(base_type)
|
||||
elif isinstance(base_type, _t.CType):
|
||||
base_llvm = CTypeRegistry.CTypeToLLVM(type(base_type))
|
||||
if not base_llvm:
|
||||
return ''
|
||||
if info.PtrCount > 0:
|
||||
if base_llvm == 'void':
|
||||
return 'i8*'
|
||||
return base_llvm + '*' * info.PtrCount
|
||||
return base_llvm
|
||||
|
||||
def _ctype_str_to_llvm(self, type_str: str) -> str:
|
||||
"""将 C 类型名字符串(如 'CUnsignedInt *')转换为 LLVM 类型字符串(如 'i32*')
|
||||
|
||||
支持:
|
||||
- C 类名:'CUnsignedInt' → 'i32','CChar' → 'i8'
|
||||
- 指针:'CUnsignedInt *' → 'i32*','void *' → 'i8*'
|
||||
- 已是 LLVM 类型:'i32' → 'i32','i8*' → 'i8*'
|
||||
无法识别返回空字符串。
|
||||
"""
|
||||
s: str = type_str.strip()
|
||||
ptr_count: int = 0
|
||||
while s.endswith('*'):
|
||||
ptr_count += 1
|
||||
s = s[:-1].strip()
|
||||
base_llvm: str = ''
|
||||
if s in ('void', 'CVoid'):
|
||||
base_llvm = 'void'
|
||||
else:
|
||||
base_llvm = CTypeRegistry.NameToLLVM(s)
|
||||
if not base_llvm and s in ('i8', 'i16', 'i32', 'i64', 'float', 'double', 'half', 'fp128', 'void'):
|
||||
base_llvm = s
|
||||
if not base_llvm:
|
||||
return ''
|
||||
if ptr_count > 0:
|
||||
if base_llvm == 'void':
|
||||
return 'i8*'
|
||||
return base_llvm + '*' * ptr_count
|
||||
return base_llvm
|
||||
|
||||
def _resolve_name(self, node: ast.Name) -> str:
|
||||
"""解析 ast.Name 节点"""
|
||||
id_map: dict[str, str] = {
|
||||
'str': 'i8*', 'int': 'i32', 'bool': 'i8',
|
||||
'float': 'double', 'None': 'void',
|
||||
'UINT8PTR': 'i8*', 'INT8PTR': 'i8*', 'BYTEPTR': 'i8*',
|
||||
'UINT16PTR': 'i16*', 'INT16PTR': 'i16*',
|
||||
'UINT32PTR': 'i32*', 'INT32PTR': 'i32*',
|
||||
'UINT64PTR': 'i64*', 'INT64PTR': 'i64*',
|
||||
}
|
||||
if node.id in id_map:
|
||||
return id_map[node.id]
|
||||
# 通过 BuiltinTypeMap 解析 VOIDPTR、INTPTR 等指针别名
|
||||
builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(node.id)
|
||||
if builtin_entry:
|
||||
TypeClass: type
|
||||
base_ptr: int
|
||||
TypeClass, base_ptr = builtin_entry
|
||||
base_llvm: str = CTypeRegistry.CTypeToLLVM(TypeClass)
|
||||
if base_ptr > 0:
|
||||
if base_llvm == 'void':
|
||||
return 'i8*'
|
||||
return base_llvm + '*' * base_ptr
|
||||
return base_llvm if base_llvm else 'i32'
|
||||
# 查找 SymbolTable 中的 typedef
|
||||
if node.id in self._symtab:
|
||||
entry: CTypeInfo = self._symtab[node.id]
|
||||
if entry and entry.IsTypedef:
|
||||
resolved_td: str = self._typedef_entry_to_llvm(entry)
|
||||
if resolved_td:
|
||||
return resolved_td
|
||||
# --- 本模块声明的类型名 -------------------------------------------
|
||||
# 类名:裸类引用自身即指针 —— 与 _resolve_attribute 的 '%struct.X*' 同口径
|
||||
# (下游 TypeConvert._type_str_to_llvm 按短名命中已注册 struct ⇒ %"sha1.X"*)。
|
||||
if node.id in self._ClassNames:
|
||||
return f'%struct.{node.id}*'
|
||||
# 枚举名:按登记的存储位宽物化(登记缺失时 32,与改前恒 i32 同值)
|
||||
if node.id in self._EnumWidths:
|
||||
return f'i{self._EnumWidths[node.id]}'
|
||||
# --- 已加载模块(import / from-import)声明的类型名 ----------------
|
||||
if node.id in self._symtab:
|
||||
entry_loaded: CTypeInfo = self._symtab[node.id]
|
||||
if entry_loaded:
|
||||
# IsStruct 覆盖 struct / renum / exception 三种类符号(见 _InsertClassSymbol)
|
||||
if entry_loaded.IsStruct:
|
||||
return f'%struct.{node.id}*'
|
||||
if entry_loaded.IsEnum and not entry_loaded.IsRenum:
|
||||
return f'i{EnumIntWidth(entry_loaded)}'
|
||||
# --- 同名冲突:平坦条目被 define / 枚举成员 / 函数占位时找回类型 ----
|
||||
# 平坦表(无前缀)一个名字只能留一个条目,而不同模块可能用**不同类型种类**
|
||||
# 声明同名符号(谁后插入谁占住裸名),两个真实例子:
|
||||
# · stdint.vp:9 `INT: t.CTypedef = t.CInt` vs argparse.vp:33
|
||||
# `define INT = 1`(sig 里是 `INT: t.CDefine = 1`)—— 注解 `oflag: INT`
|
||||
# · llvmlite/__function.vp:77 `class Function` vs ast/tokens.vp:95
|
||||
# 枚举成员 `Function: t.State` —— 注解 `func: rawptr[Function]`
|
||||
# 注解位置只可能是 typedef / 类 / 枚举 ⇒ 平坦条目不是这三种时,跨命名空间
|
||||
# (各模块的声明登记在 _namespaces[模块名] 下)再找同名条目。
|
||||
_cand_sym: CTypeInfo | None
|
||||
for _ns_syms in getattr(self._symtab, '_namespaces', {}).values():
|
||||
_cand_sym = _ns_syms.get(node.id)
|
||||
if _cand_sym is None:
|
||||
continue
|
||||
if getattr(_cand_sym, 'IsTypedef', False):
|
||||
_resolved_ns: str = self._typedef_entry_to_llvm(_cand_sym)
|
||||
if _resolved_ns:
|
||||
return _resolved_ns
|
||||
elif getattr(_cand_sym, 'IsStruct', False):
|
||||
return f'%struct.{node.id}*'
|
||||
elif getattr(_cand_sym, 'IsEnum', False) and not getattr(_cand_sym, 'IsRenum', False):
|
||||
return f'i{EnumIntWidth(_cand_sym)}'
|
||||
# --- builtins.vp 隐式 CTypedef ----------------------------------
|
||||
# int64/uint64/size_t 等:用户文件无需 import 即用,符号表必定 miss,
|
||||
# 必须在此按权威定义展开(与 TypeConvert.L454-468 同一口径),
|
||||
# 否则会误伤成下一条 fail-fast。
|
||||
builtin_info: CTypeInfo | None = self._lookup_builtins_typedef(node.id)
|
||||
if builtin_info is not None:
|
||||
builtin_llvm: str = self._ctype_to_llvm_str(builtin_info)
|
||||
if builtin_llvm:
|
||||
return builtin_llvm
|
||||
# 存储类 / 类型限定符标记(CExport/CExtern/CStatic/CInline/CConst/
|
||||
# CVolatile/State 等,均以 `from t import ...` 裸名出现):自身不是类型,
|
||||
# 只作标记 ⇒ 贡献空串,由 _resolve_bitor 取另一侧真实类型
|
||||
# (`CInt | CExport` → i32)。与 _resolve_attribute 的 marker 分支、
|
||||
# TypeAnnotationResolver._ResolveNameNode 的 STORAGE_CLASS/TYPE_QUALIFIER
|
||||
# 判定同口径。缺此分支时裸名标记会落到下方 fail-fast —— 而带前缀写作
|
||||
# `t.CExport` 时却能通过(走 Attribute 路径),同一语义两种写法结果不同。
|
||||
# 现场:Test/AstTest `def main() -> CInt | CExport`(第 108 行)。
|
||||
_marker_cls: object = getattr(_t, node.id, None)
|
||||
if (isinstance(_marker_cls, type) and _marker_cls is not _t.CType
|
||||
and issubclass(_marker_cls, _t.CType)):
|
||||
_pos_tags: object = getattr(_marker_cls, 'position', None) or ()
|
||||
if _t.CType.BASE not in _pos_tags and (
|
||||
_t.CType.STORAGE_CLASS in _pos_tags or _t.CType.TYPE_QUALIFIER in _pos_tags):
|
||||
return ''
|
||||
# fail-fast:类型信息缺失不再静默退化成 i32
|
||||
_node_line: object = getattr(node, 'lineno', None)
|
||||
_node_col: object = getattr(node, 'col_offset', None)
|
||||
_pos: str = ""
|
||||
if _node_line:
|
||||
_pos = f"(注解位置 第 {_node_line} 行"
|
||||
_pos += f", 列 {_node_col})" if _node_col is not None else ")"
|
||||
_loaded_cnt: int = len(getattr(self._symtab, '_symbols', None) or {})
|
||||
raise TypeNameUnresolvedError(
|
||||
f"裸名 '{node.id}' 无法解析为类型{_pos}\n"
|
||||
f" · 已尝试的解析来源(全部 miss):内置类型速记 / BuiltinTypeMap / "
|
||||
f"builtins.vp 的 CTypedef / 符号表 typedef / "
|
||||
f"本模块类名({len(self._ClassNames)} 个)/ 本模块枚举名({len(self._EnumWidths)} 个)/ "
|
||||
f"符号表已加载条目({_loaded_cnt} 个)中的类与枚举\n"
|
||||
f" · 若该名字确实是 typedef / 类:先确认声明它的模块已在本模块之前加载"
|
||||
f"(未加载 ⇒ 符号表里没有它,与注解拼写无关);否则按「注解拼写错误或"
|
||||
f"该类型不存在」处理")
|
||||
|
||||
def _resolve_attribute(self, node: ast.Attribute) -> str:
|
||||
"""解析 ast.Attribute 节点"""
|
||||
attr_name: str = node.attr if hasattr(node, 'attr') else ''
|
||||
llvm_str: str | None = CTypeRegistry.NameToLLVM(attr_name)
|
||||
if llvm_str is not None:
|
||||
return llvm_str
|
||||
if attr_name in ('CState', 'CDefine', 'CTypedef', 'CExtern', 'CStatic', 'CConst', 'State'):
|
||||
return ''
|
||||
if attr_name in ('CCharPtr', 'CIntPtr', 'CVoidPtr', 'CArrayPtr'):
|
||||
return 'i8*'
|
||||
if attr_name == 'CVoidPtr':
|
||||
return 'i8*'
|
||||
if attr_name and attr_name[0].isupper() and attr_name not in CTypeRegistry._name_to_class:
|
||||
return f'%struct.{attr_name}*'
|
||||
if attr_name and attr_name not in CTypeRegistry._name_to_class:
|
||||
return f'%struct.{attr_name}*'
|
||||
self._symtab.diagnostics.warn('', 0, f"无法解析 Attribute 类型 '{attr_name}',回退到 i32")
|
||||
return 'i32'
|
||||
|
||||
def _resolve_subscript(self, node: ast.Subscript) -> str:
|
||||
"""解析 ast.Subscript 节点"""
|
||||
if isinstance(node.value, ast.Name) and node.value.id == 'tuple':
|
||||
slice_node: ast.AST = node.slice
|
||||
elem_types: list[str] = []
|
||||
if isinstance(slice_node, ast.Tuple):
|
||||
elt: ast.AST
|
||||
for elt in slice_node.elts:
|
||||
elem_types.append(self.get_llvm_type_str(elt))
|
||||
else:
|
||||
elem_types.append(self.get_llvm_type_str(slice_node))
|
||||
if elem_types:
|
||||
return '{ ' + ', '.join(elem_types) + ' }'
|
||||
return ''
|
||||
|
||||
# ==================================================================
|
||||
# 公共 API
|
||||
# ==================================================================
|
||||
|
||||
def get_llvm_type_str(self, node: ast.AST | None) -> str:
|
||||
"""将 AST 注解节点转换为 LLVM IR 类型字符串"""
|
||||
if node is None:
|
||||
self._symtab.diagnostics.warn('', 0, "类型注解为 None,回退到 i32")
|
||||
return 'i32'
|
||||
if isinstance(node, ast.Name):
|
||||
return self._resolve_name(node)
|
||||
elif isinstance(node, ast.Attribute):
|
||||
return self._resolve_attribute(node)
|
||||
elif isinstance(node, ast.Subscript):
|
||||
return self._resolve_subscript(node)
|
||||
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
|
||||
return self._resolve_bitor(node)
|
||||
elif isinstance(node, ast.Constant):
|
||||
if isinstance(node.value, bool):
|
||||
return 'i8'
|
||||
return 'i32'
|
||||
elif isinstance(node, ast.Call):
|
||||
return self._resolve_call(node)
|
||||
self._symtab.diagnostics.warn('', 0, f"无法解析类型节点 {type(node).__name__},回退到 i32")
|
||||
return 'i32'
|
||||
|
||||
def get_func_ret_type_str(self, returns_node: ast.AST | None) -> str:
|
||||
"""提取函数返回类型的 LLVM 字符串"""
|
||||
if returns_node is None:
|
||||
return 'i32'
|
||||
if isinstance(returns_node, ast.BinOp) and isinstance(returns_node.op, ast.BitOr):
|
||||
# 统一走 CPtr 感知的联合解析(int | t.CPtr → i32*、str | t.CPtr → i8**)
|
||||
return self._resolve_bitor(returns_node)
|
||||
result: str = self.get_llvm_type_str(returns_node)
|
||||
return result if result else 'i32'
|
||||
|
||||
def get_func_param_type_str(self, annotation_node: ast.AST | None) -> str:
|
||||
"""提取函数参数类型的 LLVM 字符串。
|
||||
|
||||
末尾的「不许静默变 i32」守卫:`Subscript` / 联合(`|`)形态的形参注解若解析结果
|
||||
是 `'i32'`,只可能是**解析失败后的兜底** —— 合法的下标注解是容器 / 泛型 / 定长数组
|
||||
(解析出指针、数组或结构体指针),合法的联合写法是 `T | t.CPtr` 这类指针叠加,都
|
||||
不会是 i32。该串会被 `_InsertFuncSymbol` 原样存进符号表(`FuncPtrParams`),再由
|
||||
`BuildLLVMFuncTypeFromSig` 物化成形参类型 —— 一旦是 i32,调用点就在 `_adjust_args`
|
||||
报「实参 %X* 不能隐式转为 i32」,把真因(**注解没解析出来**)掩盖成"调用点类型
|
||||
不匹配"(现场:CallGenMixin.vp:149 调用 `_specialize_generic_class` 的
|
||||
`type_args: list[str]*`)。故此处响亮失败,并带上注解原文。
|
||||
"""
|
||||
if annotation_node is None:
|
||||
return 'i8*'
|
||||
result: str
|
||||
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
|
||||
# 统一走 CPtr 感知的联合解析
|
||||
result = self._resolve_bitor(annotation_node)
|
||||
else:
|
||||
result = self.get_llvm_type_str(annotation_node)
|
||||
if not result:
|
||||
result = 'i8*'
|
||||
if result == 'i32' and isinstance(annotation_node, (ast.Subscript, ast.BinOp)):
|
||||
_text: str
|
||||
try:
|
||||
_text = ast.unparse(annotation_node)
|
||||
except Exception:
|
||||
_text = f'<{type(annotation_node).__name__}>'
|
||||
raise TypeNameUnresolvedError(
|
||||
f"形参注解 '{_text}' 解析失败并落到 i32(下标 / 联合形态的合法结果应为"
|
||||
f"指针、数组或结构体指针,不会是 i32)—— 拒绝静默降级为 i32")
|
||||
return result
|
||||
|
||||
def resolve_typedef_value_type(self, node: ast.AST | None) -> str:
|
||||
"""解析 typedef 值表达式的 C 类型字符串"""
|
||||
if node is None:
|
||||
return ''
|
||||
if isinstance(node, ast.Name):
|
||||
if node.id in self._symtab:
|
||||
Entry: CTypeInfo = self._symtab[node.id]
|
||||
if Entry.IsTypedef and Entry.OriginalType:
|
||||
return Entry.OriginalType
|
||||
# 短别名 / 内建速记(char/int/int64/uchar/str…):与注解位置
|
||||
# `_resolve_name` 同源。缺此分支时 typedef 值里的裸名解析为空,
|
||||
# 整条 typedef 随之解析失败(现场:w32/win32base.vp 的
|
||||
# `LPCSTR: t.CTypedef = t.CConst | char | t.CPtr` ⇒ LPCSTR 不登记,
|
||||
# 用到处报「裸名 LPCSTR 无法解析为类型」)。
|
||||
_binfo: CTypeInfo | None = self._lookup_builtins_typedef(node.id)
|
||||
if _binfo is None:
|
||||
_e: tuple[type, int] | None = BuiltinTypeMap.Get(node.id)
|
||||
if _e:
|
||||
_binfo = CTypeInfo()
|
||||
_binfo.BaseType = _e[0]
|
||||
_binfo.PtrCount = _e[1]
|
||||
if _binfo is not None and _binfo.BaseType:
|
||||
_bt: object = _binfo.BaseType
|
||||
_nm: str = (_bt if isinstance(_bt, str)
|
||||
else CTypeHelper.GetCName(_bt) if isinstance(_bt, type)
|
||||
else type(_bt).__name__)
|
||||
if _nm and _nm != 'CType':
|
||||
return _nm + (' *' * (_binfo.PtrCount or 0))
|
||||
return ''
|
||||
if isinstance(node, ast.Attribute):
|
||||
if isinstance(node.value, ast.Name) and IsTModule(node.value.id):
|
||||
# 直接判断属性名是否为 CPtr(消除对 CName=='*' 的依赖)
|
||||
if node.attr == 'CPtr':
|
||||
return 'ptr'
|
||||
if node.attr in _TYPEDEF_VALUE_QUALIFIERS:
|
||||
# 类型限定符标记(const/volatile)本身不是类型:它只修饰 `|` 另一侧。
|
||||
# 旧写法 `t.CConst | char | t.CPtr` 靠 char 一侧掩盖了这一点;一旦写成
|
||||
# `t.CConst | str`(`const str`),把 'CConst' 当基类型会得到 'CConst *',
|
||||
# 于是别名登记失败、所有使用处报「裸名 … 无法解析为类型」。
|
||||
return ''
|
||||
CName: str | None = CTypeHelper.GetCName(node.attr)
|
||||
if CName == 'CPtr':
|
||||
return 'ptr'
|
||||
if CName:
|
||||
return CName
|
||||
return ''
|
||||
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
|
||||
left_str: str = self.resolve_typedef_value_type(node.left)
|
||||
right_str: str = self.resolve_typedef_value_type(node.right)
|
||||
# 限定符侧解析为空串 ⇒ 另一侧就是完整类型(见 _TYPEDEF_VALUE_QUALIFIERS)。
|
||||
# 只在两侧都有类型信息时才按"基类型 + 指针标记"合并。
|
||||
if not left_str:
|
||||
return 'void *' if right_str == 'ptr' else right_str
|
||||
if not right_str:
|
||||
return 'void *' if left_str == 'ptr' else left_str
|
||||
if right_str == 'ptr' or right_str.endswith('*'):
|
||||
if left_str != 'ptr':
|
||||
return left_str + ' *'
|
||||
return right_str if right_str.endswith('*') else 'void *'
|
||||
if left_str == 'ptr' or left_str.endswith('*'):
|
||||
if right_str != 'ptr':
|
||||
return right_str + ' *'
|
||||
return left_str if left_str.endswith('*') else 'void *'
|
||||
if right_str not in ('ptr', 'void', 'i32', 'i64'):
|
||||
return right_str
|
||||
if left_str not in ('ptr', 'void', 'i32', 'i64'):
|
||||
return left_str
|
||||
return left_str if left_str else right_str
|
||||
return ''
|
||||
|
||||
# ==================================================================
|
||||
# 内部解析方法
|
||||
# ==================================================================
|
||||
|
||||
def _ctype_to_llvm_str(self, info: CTypeInfo) -> str:
|
||||
"""从 CTypeInfo 的 BaseType+PtrCount 生成归一化的 LLVM 类型字符串。
|
||||
|
||||
void* 在 LLVM IR 中归一化为 i8*;struct/enum/union 等非基础类型返回空字符串。
|
||||
"""
|
||||
if not info or not info.BaseType:
|
||||
return ''
|
||||
base_type = info.BaseType
|
||||
base_llvm: str = ''
|
||||
if isinstance(base_type, type) and issubclass(base_type, _t.CType):
|
||||
base_llvm = CTypeRegistry.CTypeToLLVM(base_type)
|
||||
elif isinstance(base_type, _t.CType):
|
||||
base_llvm = CTypeRegistry.CTypeToLLVM(type(base_type))
|
||||
if not base_llvm:
|
||||
return ''
|
||||
if info.PtrCount > 0:
|
||||
if base_llvm == 'void':
|
||||
return 'i8*'
|
||||
return base_llvm + '*' * info.PtrCount
|
||||
return base_llvm
|
||||
|
||||
def _ctype_str_to_llvm(self, type_str: str) -> str:
|
||||
"""将 C 类型名字符串(如 'CUnsignedInt *')转换为 LLVM 类型字符串(如 'i32*')
|
||||
|
||||
支持:
|
||||
- C 类名:'CUnsignedInt' → 'i32','CChar' → 'i8'
|
||||
- 指针:'CUnsignedInt *' → 'i32*','void *' → 'i8*'
|
||||
- 已是 LLVM 类型:'i32' → 'i32','i8*' → 'i8*'
|
||||
无法识别返回空字符串。
|
||||
"""
|
||||
s: str = type_str.strip()
|
||||
ptr_count: int = 0
|
||||
while s.endswith('*'):
|
||||
ptr_count += 1
|
||||
s = s[:-1].strip()
|
||||
base_llvm: str = ''
|
||||
if s in ('void', 'CVoid'):
|
||||
base_llvm = 'void'
|
||||
else:
|
||||
base_llvm = CTypeRegistry.NameToLLVM(s)
|
||||
if not base_llvm and s in ('i8', 'i16', 'i32', 'i64', 'float', 'double', 'half', 'fp128', 'void'):
|
||||
base_llvm = s
|
||||
if not base_llvm:
|
||||
return ''
|
||||
if ptr_count > 0:
|
||||
if base_llvm == 'void':
|
||||
return 'i8*'
|
||||
return base_llvm + '*' * ptr_count
|
||||
return base_llvm
|
||||
|
||||
def _resolve_name(self, node: ast.Name) -> str:
|
||||
"""解析 ast.Name 节点"""
|
||||
id_map: dict[str, str] = {
|
||||
'str': 'i8*', 'int': 'i32', 'bool': 'i8',
|
||||
'float': 'double', 'None': 'void',
|
||||
'UINT8PTR': 'i8*', 'INT8PTR': 'i8*', 'BYTEPTR': 'i8*',
|
||||
'UINT16PTR': 'i16*', 'INT16PTR': 'i16*',
|
||||
'UINT32PTR': 'i32*', 'INT32PTR': 'i32*',
|
||||
'UINT64PTR': 'i64*', 'INT64PTR': 'i64*',
|
||||
}
|
||||
if node.id in id_map:
|
||||
return id_map[node.id]
|
||||
# 通过 BuiltinTypeMap 解析 VOIDPTR、INTPTR 等指针别名
|
||||
builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(node.id)
|
||||
if builtin_entry:
|
||||
TypeClass: type
|
||||
base_ptr: int
|
||||
TypeClass, base_ptr = builtin_entry
|
||||
base_llvm: str = CTypeRegistry.CTypeToLLVM(TypeClass)
|
||||
if base_ptr > 0:
|
||||
if base_llvm == 'void':
|
||||
return 'i8*'
|
||||
return base_llvm + '*' * base_ptr
|
||||
return base_llvm if base_llvm else 'i32'
|
||||
# 查找 SymbolTable 中的 typedef
|
||||
if node.id in self._symtab:
|
||||
entry: CTypeInfo = self._symtab[node.id]
|
||||
if entry and entry.IsTypedef:
|
||||
resolved_td: str = self._typedef_entry_to_llvm(entry)
|
||||
if resolved_td:
|
||||
return resolved_td
|
||||
# --- 本模块声明的类型名 -------------------------------------------
|
||||
# 类名:裸类引用自身即指针 —— 与 _resolve_attribute 的 '%struct.X*' 同口径
|
||||
# (下游 TypeConvert._type_str_to_llvm 按短名命中已注册 struct ⇒ %"sha1.X"*)。
|
||||
if node.id in self._ClassNames:
|
||||
return f'%struct.{node.id}*'
|
||||
# 枚举名:按登记的存储位宽物化(登记缺失时 32,与改前恒 i32 同值)
|
||||
if node.id in self._EnumWidths:
|
||||
return f'i{self._EnumWidths[node.id]}'
|
||||
# --- 已加载模块(import / from-import)声明的类型名 ----------------
|
||||
if node.id in self._symtab:
|
||||
entry_loaded: CTypeInfo = self._symtab[node.id]
|
||||
if entry_loaded:
|
||||
# IsStruct 覆盖 struct / renum / exception 三种类符号(见 _InsertClassSymbol)
|
||||
if entry_loaded.IsStruct:
|
||||
return f'%struct.{node.id}*'
|
||||
if entry_loaded.IsEnum and not entry_loaded.IsRenum:
|
||||
return f'i{EnumIntWidth(entry_loaded)}'
|
||||
# --- 同名冲突:平坦条目被 define / 枚举成员 / 函数占位时找回类型 ----
|
||||
# 平坦表(无前缀)一个名字只能留一个条目,而不同模块可能用**不同类型种类**
|
||||
# 声明同名符号(谁后插入谁占住裸名),两个真实例子:
|
||||
# · stdint.vp:9 `INT: t.CTypedef = t.CInt` vs argparse.vp:33
|
||||
# `define INT = 1`(sig 里是 `INT: t.CDefine = 1`)—— 注解 `oflag: INT`
|
||||
# · llvmlite/__function.vp:77 `class Function` vs ast/tokens.vp:95
|
||||
# 枚举成员 `Function: t.State` —— 注解 `func: rawptr[Function]`
|
||||
# 注解位置只可能是 typedef / 类 / 枚举 ⇒ 平坦条目不是这三种时,跨命名空间
|
||||
# (各模块的声明登记在 _namespaces[模块名] 下)再找同名条目。
|
||||
_cand_sym: CTypeInfo | None
|
||||
for _ns_syms in getattr(self._symtab, '_namespaces', {}).values():
|
||||
_cand_sym = _ns_syms.get(node.id)
|
||||
if _cand_sym is None:
|
||||
continue
|
||||
if getattr(_cand_sym, 'IsTypedef', False):
|
||||
_resolved_ns: str = self._typedef_entry_to_llvm(_cand_sym)
|
||||
if _resolved_ns:
|
||||
return _resolved_ns
|
||||
elif getattr(_cand_sym, 'IsStruct', False):
|
||||
return f'%struct.{node.id}*'
|
||||
elif getattr(_cand_sym, 'IsEnum', False) and not getattr(_cand_sym, 'IsRenum', False):
|
||||
return f'i{EnumIntWidth(_cand_sym)}'
|
||||
# --- builtins.vp 隐式 CTypedef ----------------------------------
|
||||
# int64/uint64/size_t 等:用户文件无需 import 即用,符号表必定 miss,
|
||||
# 必须在此按权威定义展开(与 TypeConvert.L454-468 同一口径),
|
||||
# 否则会误伤成下一条 fail-fast。
|
||||
builtin_info: CTypeInfo | None = self._lookup_builtins_typedef(node.id)
|
||||
if builtin_info is not None:
|
||||
builtin_llvm: str = self._ctype_to_llvm_str(builtin_info)
|
||||
if builtin_llvm:
|
||||
return builtin_llvm
|
||||
# 存储类 / 类型限定符标记(CExport/CExtern/CStatic/CInline/CConst/
|
||||
# CVolatile/State 等,均以 `from t import ...` 裸名出现):自身不是类型,
|
||||
# 只作标记 ⇒ 贡献空串,由 _resolve_bitor 取另一侧真实类型
|
||||
# (`CInt | CExport` → i32)。与 _resolve_attribute 的 marker 分支、
|
||||
# TypeAnnotationResolver._ResolveNameNode 的 STORAGE_CLASS/TYPE_QUALIFIER
|
||||
# 判定同口径。缺此分支时裸名标记会落到下方 fail-fast —— 而带前缀写作
|
||||
# `t.CExport` 时却能通过(走 Attribute 路径),同一语义两种写法结果不同。
|
||||
# 现场:Test/AstTest `def main() -> CInt | CExport`(第 108 行)。
|
||||
_marker_cls: object = getattr(_t, node.id, None)
|
||||
if (isinstance(_marker_cls, type) and _marker_cls is not _t.CType
|
||||
and issubclass(_marker_cls, _t.CType)):
|
||||
_pos_tags: object = getattr(_marker_cls, 'position', None) or ()
|
||||
if _t.CType.BASE not in _pos_tags and (
|
||||
_t.CType.STORAGE_CLASS in _pos_tags or _t.CType.TYPE_QUALIFIER in _pos_tags):
|
||||
return ''
|
||||
# fail-fast:类型信息缺失不再静默退化成 i32
|
||||
_node_line: object = getattr(node, 'lineno', None)
|
||||
_node_col: object = getattr(node, 'col_offset', None)
|
||||
_pos: str = ""
|
||||
if _node_line:
|
||||
_pos = f"(注解位置 第 {_node_line} 行"
|
||||
_pos += f", 列 {_node_col})" if _node_col is not None else ")"
|
||||
_loaded_cnt: int = len(getattr(self._symtab, '_symbols', None) or {})
|
||||
raise TypeNameUnresolvedError(
|
||||
f"裸名 '{node.id}' 无法解析为类型{_pos}\n"
|
||||
f" · 已尝试的解析来源(全部 miss):内置类型速记 / BuiltinTypeMap / "
|
||||
f"builtins.vp 的 CTypedef / 符号表 typedef / "
|
||||
f"本模块类名({len(self._ClassNames)} 个)/ 本模块枚举名({len(self._EnumWidths)} 个)/ "
|
||||
f"符号表已加载条目({_loaded_cnt} 个)中的类与枚举\n"
|
||||
f" · 若该名字确实是 typedef / 类:先确认声明它的模块已在本模块之前加载"
|
||||
f"(未加载 ⇒ 符号表里没有它,与注解拼写无关);否则按「注解拼写错误或"
|
||||
f"该类型不存在」处理")
|
||||
|
||||
def _resolve_attribute(self, node: ast.Attribute) -> str:
|
||||
"""解析 ast.Attribute 节点"""
|
||||
attr_name: str = node.attr if hasattr(node, 'attr') else ''
|
||||
llvm_str: str | None = CTypeRegistry.NameToLLVM(attr_name)
|
||||
if llvm_str is not None:
|
||||
return llvm_str
|
||||
if attr_name in ('CState', 'CDefine', 'CTypedef', 'CExtern', 'CStatic', 'CConst', 'State'):
|
||||
return ''
|
||||
if attr_name in ('CCharPtr', 'CIntPtr', 'CVoidPtr', 'CArrayPtr'):
|
||||
return 'i8*'
|
||||
if attr_name == 'CVoidPtr':
|
||||
return 'i8*'
|
||||
if attr_name and attr_name[0].isupper() and attr_name not in CTypeRegistry._name_to_class:
|
||||
return f'%struct.{attr_name}*'
|
||||
if attr_name and attr_name not in CTypeRegistry._name_to_class:
|
||||
return f'%struct.{attr_name}*'
|
||||
self._symtab.diagnostics.warn('', 0, f"无法解析 Attribute 类型 '{attr_name}',回退到 i32")
|
||||
return 'i32'
|
||||
|
||||
def _resolve_subscript(self, node: ast.Subscript) -> str:
|
||||
"""解析 ast.Subscript 节点"""
|
||||
if isinstance(node.value, ast.Name) and node.value.id == 'tuple':
|
||||
slice_node: ast.AST = node.slice
|
||||
elem_types: list[str] = []
|
||||
if isinstance(slice_node, ast.Tuple):
|
||||
elt: ast.AST
|
||||
for elt in slice_node.elts:
|
||||
elem_types.append(self.get_llvm_type_str(elt))
|
||||
else:
|
||||
elem_types.append(self.get_llvm_type_str(slice_node))
|
||||
if elem_types:
|
||||
return '{ ' + ', '.join(elem_types) + ' }'
|
||||
if IsCPtrSubscriptNode(node):
|
||||
# t.CPtr[T] → Ptr(resolve(T))(对齐 TPV Subscript 分支
|
||||
# t.CPtr[T] → Ptr(resolve(T))(对齐 VPC Subscript 分支
|
||||
# TypeGenResolve.vp:2769-2790;void 首层归一化为 i8*)
|
||||
return self._add_pointer_level(self.get_llvm_type_str(node.slice))
|
||||
if IsRawPtrSubscriptNode(node):
|
||||
@@ -458,115 +458,115 @@ class LLVMTypeMapper:
|
||||
# 合并语义同口径(裸类引用自身即指针,吸收这一层;其余逐层外包)。
|
||||
# 此前只认 IsCPtrSubscriptNode → rawptr[T] 落到下方 warn 并退化成 i32。
|
||||
return self._rawptr_subscript_type_str(node)
|
||||
if IsListAnnotation(node):
|
||||
slice_node: ast.AST = node.slice
|
||||
elem_type: str
|
||||
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1:
|
||||
elem_type = self.get_llvm_type_str(slice_node.elts[0])
|
||||
else:
|
||||
elem_type = self.get_llvm_type_str(slice_node)
|
||||
return elem_type + '*'
|
||||
# 泛型类下标(单/多参):list[str] / dict[str][int] / 用户泛型 Box[Node]。
|
||||
# 特化名与声明端(DeclarationGenerator 泛型分支)同口径,见
|
||||
# SymbolUtils.ResolveGenericClassSpecName;输出与 _resolve_name /
|
||||
# _resolve_attribute 的类引用**同一表示**(%struct.<名>*,短名交给
|
||||
# TypeConvert._type_str_to_llvm_base 按 _struct_sha1_map 物化到
|
||||
# %"{模板sha1}.<特化名>"*)。此前单参泛型下标落到下方 warn + 'i32' ⇒
|
||||
# 形参类型串成 i32(现场:ClassGenGeneric._specialize_generic_class 的
|
||||
# 形参 `type_args: list[str]*`)。
|
||||
_Spec: str | None = ResolveGenericClassSpecName(
|
||||
node, self._symtab,
|
||||
GetStructSha1MapFromSymtab(self._symtab),
|
||||
GetTCAliasMapFromSymtab(self._symtab))
|
||||
if _Spec:
|
||||
return f'%struct.{_Spec}*'
|
||||
self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32")
|
||||
return 'i32'
|
||||
|
||||
def _resolve_bitor(self, node: ast.BinOp) -> str:
|
||||
"""解析 ast.BinOp(BitOr) 节点(CPtr 感知的指针层级叠加)。
|
||||
|
||||
T | t.CPtr 一侧为 t.CPtr 标记时,另一侧追加一层 '*':
|
||||
int | t.CPtr → i32* (原逻辑误降级为 i8*)
|
||||
str | t.CPtr → i8** (原逻辑误降级为 i8*)
|
||||
(int | t.CPtr) | t.CPtr → i32**(嵌套递归叠加)
|
||||
T | t.CPtr[...] 一侧为下标指针类型时,另一侧叠加该下标的指针层级 N
|
||||
(N = 1 + 切片自身层数,不含 struct 吸收,对齐 TPV BinOp 下标分支
|
||||
TypeGenResolve.vp:2596-2639):
|
||||
t.CUInt8T | t.CPtr[t.CPtr] → i8**(原逻辑误降级为首段 i8)
|
||||
"""
|
||||
left: str = self.get_llvm_type_str(node.left)
|
||||
right: str = self.get_llvm_type_str(node.right)
|
||||
left_is_cptr: bool = IsCPtrNode(node.left)
|
||||
right_is_cptr: bool = IsCPtrNode(node.right)
|
||||
if right_is_cptr:
|
||||
return self._add_pointer_level(left)
|
||||
if left_is_cptr:
|
||||
return self._add_pointer_level(right)
|
||||
# t.CPtr[...] 下标指针标记:X | t.CPtr[...] → Ptr^N(X)
|
||||
if IsCPtrSubscriptNode(node.right):
|
||||
return self._add_pointer_levels(left, self._cptr_subscript_ptr_level(node.right))
|
||||
if IsCPtrSubscriptNode(node.left):
|
||||
return self._add_pointer_levels(right, self._cptr_subscript_ptr_level(node.left))
|
||||
if right and right.endswith('*'):
|
||||
return right
|
||||
if left and left.endswith('*'):
|
||||
return left
|
||||
if left and left not in ('i32', 'void', 'i64'):
|
||||
return left
|
||||
if right and right not in ('i32', 'void', 'i64'):
|
||||
return right
|
||||
return left if left else right
|
||||
|
||||
def _add_pointer_level(self, ty: str) -> str:
|
||||
"""给类型字符串追加一层指针 '*'(void/void* → i8*)。"""
|
||||
if not ty:
|
||||
return 'i8*'
|
||||
if ty == 'void' or ty == 'void*':
|
||||
return 'i8*'
|
||||
if ty.endswith('*'):
|
||||
return ty + '*'
|
||||
return ty + '*'
|
||||
|
||||
def _rawptr_subscript_type_str(self, node: ast.Subscript) -> str:
|
||||
"""rawptr[T](内置裸指针下标)→ LLVM 类型串。
|
||||
|
||||
与定义端同口径(`T | t.CPtr` 的合并):裸类引用('%struct.X*' /
|
||||
'%"sha1.X"*' / 泛型特化等以 '%' 开头的结构指针)自身即指针,吸收这一层;
|
||||
其余(标量、已是指针的 typedef、联合写法、嵌套 rawptr / t.CPtr 下标)
|
||||
逐层外包(void → i8*)。
|
||||
"""
|
||||
inner: ast.AST = node.slice
|
||||
inner_type: str = self.get_llvm_type_str(inner)
|
||||
if (not isinstance(inner, ast.BinOp)
|
||||
and not IsRawPtrSubscriptNode(inner)
|
||||
and not IsCPtrSubscriptNode(inner)
|
||||
and inner_type.startswith('%') and inner_type.endswith('*')):
|
||||
return inner_type
|
||||
return self._add_pointer_level(inner_type)
|
||||
|
||||
def _add_pointer_levels(self, ty: str, count: int) -> str:
|
||||
"""给类型字符串叠加 count 层指针(void/void* 首层归一化为 i8*)。"""
|
||||
result: str = ty
|
||||
for _ in range(count):
|
||||
result = self._add_pointer_level(result)
|
||||
return result
|
||||
|
||||
def _cptr_subscript_ptr_level(self, node: ast.AST) -> int:
|
||||
"""t.CPtr[T] 下标指针标记贡献的指针层级 N(= 1 + 切片自身层数)。
|
||||
|
||||
复用定义侧解析器 TypeAnnotationResolver.from_node(其 _from_node_subscript
|
||||
已实现 t.CPtr[T] → Ptr(T) 的层级叠加,见 TypeAnnotationResolver.py:756-783),
|
||||
与 HandlesTypeMerge._HandleSubscript(HandlesTypeMerge.py:512-531)同口径。
|
||||
调用前须经 IsCPtrSubscriptNode 判据过滤,故 PtrCount 至少为 1。
|
||||
"""
|
||||
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR
|
||||
return _TAR.from_node(node, self._symtab).PtrCount
|
||||
|
||||
def _resolve_call(self, node: ast.Call) -> str:
|
||||
"""解析 ast.Call 节点"""
|
||||
if isinstance(node.func, ast.Attribute):
|
||||
if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id):
|
||||
return self.get_llvm_type_str(ast.Attribute(value=ast.Name(id='t'), attr=node.func.attr))
|
||||
self._symtab.diagnostics.warn('', 0, "无法解析 Call 类型,回退到 i32")
|
||||
return 'i32'
|
||||
if IsListAnnotation(node):
|
||||
slice_node: ast.AST = node.slice
|
||||
elem_type: str
|
||||
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1:
|
||||
elem_type = self.get_llvm_type_str(slice_node.elts[0])
|
||||
else:
|
||||
elem_type = self.get_llvm_type_str(slice_node)
|
||||
return elem_type + '*'
|
||||
# 泛型类下标(单/多参):list[str] / dict[str][int] / 用户泛型 Box[Node]。
|
||||
# 特化名与声明端(DeclarationGenerator 泛型分支)同口径,见
|
||||
# SymbolUtils.ResolveGenericClassSpecName;输出与 _resolve_name /
|
||||
# _resolve_attribute 的类引用**同一表示**(%struct.<名>*,短名交给
|
||||
# TypeConvert._type_str_to_llvm_base 按 _struct_sha1_map 物化到
|
||||
# %"{模板sha1}.<特化名>"*)。此前单参泛型下标落到下方 warn + 'i32' ⇒
|
||||
# 形参类型串成 i32(现场:ClassGenGeneric._specialize_generic_class 的
|
||||
# 形参 `type_args: list[str]*`)。
|
||||
_Spec: str | None = ResolveGenericClassSpecName(
|
||||
node, self._symtab,
|
||||
GetStructSha1MapFromSymtab(self._symtab),
|
||||
GetTCAliasMapFromSymtab(self._symtab))
|
||||
if _Spec:
|
||||
return f'%struct.{_Spec}*'
|
||||
self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32")
|
||||
return 'i32'
|
||||
|
||||
def _resolve_bitor(self, node: ast.BinOp) -> str:
|
||||
"""解析 ast.BinOp(BitOr) 节点(CPtr 感知的指针层级叠加)。
|
||||
|
||||
T | t.CPtr 一侧为 t.CPtr 标记时,另一侧追加一层 '*':
|
||||
int | t.CPtr → i32* (原逻辑误降级为 i8*)
|
||||
str | t.CPtr → i8** (原逻辑误降级为 i8*)
|
||||
(int | t.CPtr) | t.CPtr → i32**(嵌套递归叠加)
|
||||
T | t.CPtr[...] 一侧为下标指针类型时,另一侧叠加该下标的指针层级 N
|
||||
(N = 1 + 切片自身层数,不含 struct 吸收,对齐 VPC BinOp 下标分支
|
||||
TypeGenResolve.vp:2596-2639):
|
||||
t.CUInt8T | t.CPtr[t.CPtr] → i8**(原逻辑误降级为首段 i8)
|
||||
"""
|
||||
left: str = self.get_llvm_type_str(node.left)
|
||||
right: str = self.get_llvm_type_str(node.right)
|
||||
left_is_cptr: bool = IsCPtrNode(node.left)
|
||||
right_is_cptr: bool = IsCPtrNode(node.right)
|
||||
if right_is_cptr:
|
||||
return self._add_pointer_level(left)
|
||||
if left_is_cptr:
|
||||
return self._add_pointer_level(right)
|
||||
# t.CPtr[...] 下标指针标记:X | t.CPtr[...] → Ptr^N(X)
|
||||
if IsCPtrSubscriptNode(node.right):
|
||||
return self._add_pointer_levels(left, self._cptr_subscript_ptr_level(node.right))
|
||||
if IsCPtrSubscriptNode(node.left):
|
||||
return self._add_pointer_levels(right, self._cptr_subscript_ptr_level(node.left))
|
||||
if right and right.endswith('*'):
|
||||
return right
|
||||
if left and left.endswith('*'):
|
||||
return left
|
||||
if left and left not in ('i32', 'void', 'i64'):
|
||||
return left
|
||||
if right and right not in ('i32', 'void', 'i64'):
|
||||
return right
|
||||
return left if left else right
|
||||
|
||||
def _add_pointer_level(self, ty: str) -> str:
|
||||
"""给类型字符串追加一层指针 '*'(void/void* → i8*)。"""
|
||||
if not ty:
|
||||
return 'i8*'
|
||||
if ty == 'void' or ty == 'void*':
|
||||
return 'i8*'
|
||||
if ty.endswith('*'):
|
||||
return ty + '*'
|
||||
return ty + '*'
|
||||
|
||||
def _rawptr_subscript_type_str(self, node: ast.Subscript) -> str:
|
||||
"""rawptr[T](内置裸指针下标)→ LLVM 类型串。
|
||||
|
||||
与定义端同口径(`T | t.CPtr` 的合并):裸类引用('%struct.X*' /
|
||||
'%"sha1.X"*' / 泛型特化等以 '%' 开头的结构指针)自身即指针,吸收这一层;
|
||||
其余(标量、已是指针的 typedef、联合写法、嵌套 rawptr / t.CPtr 下标)
|
||||
逐层外包(void → i8*)。
|
||||
"""
|
||||
inner: ast.AST = node.slice
|
||||
inner_type: str = self.get_llvm_type_str(inner)
|
||||
if (not isinstance(inner, ast.BinOp)
|
||||
and not IsRawPtrSubscriptNode(inner)
|
||||
and not IsCPtrSubscriptNode(inner)
|
||||
and inner_type.startswith('%') and inner_type.endswith('*')):
|
||||
return inner_type
|
||||
return self._add_pointer_level(inner_type)
|
||||
|
||||
def _add_pointer_levels(self, ty: str, count: int) -> str:
|
||||
"""给类型字符串叠加 count 层指针(void/void* 首层归一化为 i8*)。"""
|
||||
result: str = ty
|
||||
for _ in range(count):
|
||||
result = self._add_pointer_level(result)
|
||||
return result
|
||||
|
||||
def _cptr_subscript_ptr_level(self, node: ast.AST) -> int:
|
||||
"""t.CPtr[T] 下标指针标记贡献的指针层级 N(= 1 + 切片自身层数)。
|
||||
|
||||
复用定义侧解析器 TypeAnnotationResolver.from_node(其 _from_node_subscript
|
||||
已实现 t.CPtr[T] → Ptr(T) 的层级叠加,见 TypeAnnotationResolver.py:756-783),
|
||||
与 HandlesTypeMerge._HandleSubscript(HandlesTypeMerge.py:512-531)同口径。
|
||||
调用前须经 IsCPtrSubscriptNode 判据过滤,故 PtrCount 至少为 1。
|
||||
"""
|
||||
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR
|
||||
return _TAR.from_node(node, self._symtab).PtrCount
|
||||
|
||||
def _resolve_call(self, node: ast.Call) -> str:
|
||||
"""解析 ast.Call 节点"""
|
||||
if isinstance(node.func, ast.Attribute):
|
||||
if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id):
|
||||
return self.get_llvm_type_str(ast.Attribute(value=ast.Name(id='t'), attr=node.func.attr))
|
||||
self._symtab.diagnostics.warn('', 0, "无法解析 Call 类型,回退到 i32")
|
||||
return 'i32'
|
||||
+319
-319
@@ -1,320 +1,320 @@
|
||||
"""Pass 1: ASTSymbolExtractor — 从 AST 提取原始符号信息"""
|
||||
from __future__ import annotations
|
||||
import vast as ast
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.SymbolTable import SymbolTable
|
||||
|
||||
from lib.core.SymbolData import (
|
||||
ModuleSymbols, ClassSymbolData, TypedefSymbolData,
|
||||
FuncSymbolData, DefineSymbolData, AnonymousTypeData
|
||||
)
|
||||
from lib.core.Handles.HandlesBase import CTypeInfo
|
||||
"""Pass 1: ASTSymbolExtractor — 从 AST 提取原始符号信息"""
|
||||
from __future__ import annotations
|
||||
import vast as ast
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.SymbolTable import SymbolTable
|
||||
|
||||
from lib.core.SymbolData import (
|
||||
ModuleSymbols, ClassSymbolData, TypedefSymbolData,
|
||||
FuncSymbolData, DefineSymbolData, AnonymousTypeData
|
||||
)
|
||||
from lib.core.Handles.HandlesBase import CTypeInfo
|
||||
from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName
|
||||
from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH
|
||||
from lib.includes import t
|
||||
|
||||
|
||||
class ASTSymbolExtractor:
|
||||
"""从 Python 源文件的 AST 中提取所有符号信息"""
|
||||
|
||||
def __init__(self, symbol_table: SymbolTable) -> None:
|
||||
self._symtab: SymbolTable = symbol_table
|
||||
|
||||
def extract(self, file_path: str) -> ModuleSymbols:
|
||||
"""提取文件中的所有符号信息"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
code: str = f.read()
|
||||
tree: ast.Module = ast.parse(code, file_path)
|
||||
|
||||
result: ModuleSymbols = ModuleSymbols(file_path=file_path)
|
||||
|
||||
# 第一步:收集所有类的成员和匿名类型
|
||||
class_members: dict[str, dict[str, CTypeInfo]] = {}
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.ClassDef):
|
||||
members: dict[str, CTypeInfo] = {}
|
||||
self._collect_members(node, members, result.anonymous_types)
|
||||
class_members[node.name] = members
|
||||
|
||||
# 第二步:从每个顶层节点提取符号信息
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.ClassDef):
|
||||
members_info: dict[str, CTypeInfo] = class_members.get(node.name, {})
|
||||
result.classes.append(self._extract_class(node, members_info))
|
||||
elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
|
||||
self._extract_ann_assign(node, result)
|
||||
elif isinstance(node, ast.FunctionDef):
|
||||
result.functions.append(self._extract_function(node))
|
||||
|
||||
return result
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 成员收集(递归处理嵌套匿名类)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _collect_members(self, class_node: ast.ClassDef, members: dict[str, CTypeInfo], anonymous_types: dict[str, AnonymousTypeData], prefix: str = '') -> None:
|
||||
"""递归收集类成员,包括嵌套匿名类型"""
|
||||
for item in class_node.body:
|
||||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||||
member_name: str = item.target.id
|
||||
full_name: str = f'{prefix}{member_name}' if prefix else member_name
|
||||
member_type: CTypeInfo | None = CTypeInfo.FromNode(item.annotation, self._symtab)
|
||||
if member_type is None:
|
||||
member_type = CTypeInfo()
|
||||
member_type.BaseType = t.CInt()
|
||||
is_ptr: bool = member_type.IsPtr
|
||||
if isinstance(item.annotation, ast.BinOp):
|
||||
if AnnotationContainsName(item.annotation, 'CPtr'):
|
||||
is_ptr = True
|
||||
members[full_name] = member_type.Copy()
|
||||
|
||||
elif isinstance(item, ast.ClassDef):
|
||||
is_anonymous: bool = any(
|
||||
any(AnnotationContainsTType(base, 'Anonymous') for base in item.bases)
|
||||
) if item.bases else False
|
||||
|
||||
if is_anonymous:
|
||||
is_union: bool = any(
|
||||
any(AnnotationContainsTType(base, 'CUnion') for base in item.bases)
|
||||
)
|
||||
# 也检查装饰器形式 @t.CUnion
|
||||
if not is_union and hasattr(item, 'decorator_list') and item.decorator_list:
|
||||
for deco in item.decorator_list:
|
||||
if isinstance(deco, ast.Attribute) and hasattr(deco.value, 'id') and IsTModule(deco.value.id, self._symtab) and deco.attr == 'CUnion':
|
||||
is_union = True
|
||||
break
|
||||
elif isinstance(deco, ast.Name):
|
||||
cls2: type | None = getattr(t, deco.id, None)
|
||||
if cls2 is None and hasattr(self._symtab, '_t_type_symbols'):
|
||||
cls2 = self._symtab._t_type_symbols.get(deco.id)
|
||||
if cls2 is not None and isinstance(cls2, type) and issubclass(cls2, t.CUnion):
|
||||
is_union = True
|
||||
break
|
||||
|
||||
nested_name: str = f'{prefix}{item.name}' if prefix else item.name
|
||||
members[nested_name] = CTypeInfo()
|
||||
members[nested_name].BaseType = f'union {item.name}' if is_union else f'struct {item.name}'
|
||||
members[nested_name].PtrCount = 0
|
||||
members[nested_name].ArrayDims = []
|
||||
|
||||
new_prefix: str = f'{prefix}{item.name}.' if prefix else f'{item.name}.'
|
||||
self._collect_members(item, members, anonymous_types, new_prefix)
|
||||
|
||||
anonymous_members: dict[str, CTypeInfo] = {}
|
||||
for full_name, member_info in members.items():
|
||||
if full_name.startswith(f"{nested_name}."):
|
||||
member_name: str = full_name[len(nested_name) + 1:]
|
||||
anonymous_members[member_name] = member_info
|
||||
|
||||
if item.name not in anonymous_types:
|
||||
anonymous_types[item.name] = AnonymousTypeData(
|
||||
name=item.name,
|
||||
is_union=is_union,
|
||||
members=anonymous_members,
|
||||
lineno=item.lineno
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 类符号提取
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _extract_class(self, node: ast.ClassDef, members_info: dict[str, CTypeInfo]) -> ClassSymbolData:
|
||||
"""从 ClassDef 节点提取类符号数据"""
|
||||
type_kind: str = 'struct'
|
||||
is_cpython_object: bool = False
|
||||
is_packed: bool = False
|
||||
|
||||
# 检查装饰器
|
||||
if hasattr(node, 'decorator_list') and node.decorator_list:
|
||||
for decorator in node.decorator_list:
|
||||
if isinstance(decorator, ast.Attribute):
|
||||
if (hasattr(decorator.value, 'id') and
|
||||
IsTModule(decorator.value.id, self._symtab)):
|
||||
if decorator.attr == 'Object':
|
||||
is_cpython_object = True
|
||||
elif decorator.attr == 'CUnion':
|
||||
type_kind = 'union'
|
||||
elif decorator.attr == 'CEnum':
|
||||
type_kind = 'enum'
|
||||
elif decorator.attr == 'REnum':
|
||||
type_kind = 'renum'
|
||||
elif decorator.attr == 'CStruct':
|
||||
type_kind = 'struct'
|
||||
elif isinstance(decorator, ast.Name):
|
||||
# from t import CStruct / CUnion / CEnum / REnum
|
||||
cls: type | None = getattr(t, decorator.id, None)
|
||||
if cls is None and hasattr(self._symtab, '_t_type_symbols'):
|
||||
cls = self._symtab._t_type_symbols.get(decorator.id)
|
||||
if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType):
|
||||
if issubclass(cls, t.CUnion):
|
||||
type_kind = 'union'
|
||||
elif issubclass(cls, t.REnum):
|
||||
type_kind = 'renum'
|
||||
elif issubclass(cls, t.CEnum):
|
||||
type_kind = 'enum'
|
||||
elif issubclass(cls, t.CStruct):
|
||||
type_kind = 'struct'
|
||||
elif decorator.id == 'Object':
|
||||
is_cpython_object = True
|
||||
elif isinstance(decorator, ast.Call):
|
||||
if isinstance(decorator.func, ast.Attribute):
|
||||
if hasattr(decorator.func.value, 'id') and decorator.func.value.id == 'c' and decorator.func.attr == 'Attribute':
|
||||
for arg in decorator.args:
|
||||
if isinstance(arg, ast.Attribute):
|
||||
if isinstance(arg.value, ast.Attribute):
|
||||
if hasattr(arg.value.value, 'id') and IsTModule(arg.value.value.id, self._symtab) and arg.value.attr == 'attr' and arg.attr == 'packed':
|
||||
is_packed = True
|
||||
|
||||
# 检查基类
|
||||
for base in node.bases:
|
||||
if AnnotationContainsTType(base, 'CUnion'):
|
||||
type_kind = 'union'
|
||||
break
|
||||
elif AnnotationContainsTType(base, 'CEnum'):
|
||||
type_kind = 'enum'
|
||||
break
|
||||
elif AnnotationContainsTType(base, 'REnum'):
|
||||
type_kind = 'renum'
|
||||
break
|
||||
elif AnnotationContainsTType(base, 'CStruct'):
|
||||
type_kind = 'struct'
|
||||
break
|
||||
elif AnnotationContainsTType(base, 'Object'):
|
||||
is_cpython_object = True
|
||||
type_kind = 'struct'
|
||||
break
|
||||
elif isinstance(base, ast.Name) and base.id == 'Exception':
|
||||
type_kind = 'exception'
|
||||
break
|
||||
elif isinstance(base, ast.Name):
|
||||
base_entry: CTypeInfo | None = self._symtab.get(base.id)
|
||||
if base_entry and base_entry.IsExceptionClass:
|
||||
type_kind = 'exception'
|
||||
break
|
||||
|
||||
# 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端
|
||||
# HandlesClassDef._RegisterEnumMembers / TPV ClassGenEnum 同一规则源)。
|
||||
# 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源
|
||||
# (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle),
|
||||
# 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读
|
||||
# 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。
|
||||
enum_members: list[tuple[str, int, int]] = []
|
||||
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
|
||||
if type_kind == 'enum':
|
||||
enum_width = EnumClassIntWidth(node.body, self._symtab)
|
||||
next_enum_value: int = 0
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.Assign):
|
||||
if len(item.targets) == 1 and isinstance(item.targets[0], ast.Name):
|
||||
member_name: str = item.targets[0].id
|
||||
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||||
next_enum_value = item.value.value + 1
|
||||
else:
|
||||
next_enum_value += 1
|
||||
enum_members.append((member_name, next_enum_value - 1, item.lineno))
|
||||
elif isinstance(item, ast.AnnAssign):
|
||||
if isinstance(item.target, ast.Name):
|
||||
member_name: str = item.target.id
|
||||
if item.value:
|
||||
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||||
next_enum_value = item.value.value + 1
|
||||
else:
|
||||
next_enum_value += 1
|
||||
else:
|
||||
next_enum_value += 1
|
||||
enum_members.append((member_name, next_enum_value - 1, item.lineno))
|
||||
|
||||
# REnum 变体提取(嵌套 ClassDef + Assign 显式值)
|
||||
# 与 HandlesClassDef._EmitREnumLlvm 的 tag 分配逻辑对齐:
|
||||
# - 先收集 Assign 显式值
|
||||
# - ClassDef 变体从 max(explicit)+1 开始顺序编号
|
||||
# - Assign 无显式值时用独立计数器 variant_index
|
||||
if type_kind == 'renum':
|
||||
explicit_values: dict[str, int] = {}
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.Assign):
|
||||
for target in item.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||||
explicit_values[target.id] = item.value.value
|
||||
next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0
|
||||
variant_index: int = 0
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.ClassDef):
|
||||
variant_name_r: str = item.name
|
||||
tag: int = next_class_tag
|
||||
next_class_tag += 1
|
||||
enum_members.append((variant_name_r, tag, item.lineno))
|
||||
elif isinstance(item, ast.Assign):
|
||||
for target in item.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
var_name: str = target.id
|
||||
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||||
value: int = item.value.value
|
||||
else:
|
||||
value = variant_index
|
||||
variant_index += 1
|
||||
enum_members.append((var_name, value, item.lineno))
|
||||
|
||||
return ClassSymbolData(
|
||||
name=node.name,
|
||||
type_kind=type_kind,
|
||||
lineno=node.lineno,
|
||||
members=members_info,
|
||||
is_cpython_object=is_cpython_object,
|
||||
is_packed=is_packed,
|
||||
enum_members=enum_members,
|
||||
enum_width=enum_width,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# AnnAssign 提取(typedef / define)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _extract_ann_assign(self, node: ast.AnnAssign, result: ModuleSymbols) -> None:
|
||||
"""从 AnnAssign 节点提取 typedef 或 define 数据"""
|
||||
var_name: str = node.target.id
|
||||
has_cdefine: bool = AnnotationContainsTType(node.annotation, 'CDefine') if node.annotation else False
|
||||
has_postdef: bool = AnnotationContainsTType(node.annotation, 'Postdefinition') if node.annotation else False
|
||||
has_ctypedef: bool = AnnotationContainsTType(node.annotation, 'CTypedef') if node.annotation else False
|
||||
|
||||
if has_cdefine:
|
||||
result.defines.append(DefineSymbolData(
|
||||
name=var_name,
|
||||
lineno=node.lineno,
|
||||
value_node=node.value,
|
||||
))
|
||||
else:
|
||||
# rawptr 是编译器内置语法(TPC/TPV 双端),不可重定义:
|
||||
# 忽略任何对 rawptr 的 typedef 声明,防止覆盖内置裸指针语义
|
||||
if var_name == 'rawptr':
|
||||
return
|
||||
result.typedefs.append(TypedefSymbolData(
|
||||
name=var_name,
|
||||
lineno=node.lineno,
|
||||
annotation=node.annotation,
|
||||
value=node.value,
|
||||
has_cdefine=has_cdefine,
|
||||
has_postdef=has_postdef,
|
||||
has_ctypedef=has_ctypedef,
|
||||
))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 函数提取
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _extract_function(self, node: ast.FunctionDef) -> FuncSymbolData:
|
||||
"""从 FunctionDef 节点提取函数符号数据"""
|
||||
params: list[tuple[str, ast.AST | None]] = [(arg.arg, arg.annotation) for arg in node.args.args]
|
||||
is_variadic: bool = node.args.vararg is not None
|
||||
is_inline: bool = CheckAnnotationHasCInline(node.returns)
|
||||
|
||||
return FuncSymbolData(
|
||||
name=node.name,
|
||||
lineno=node.lineno,
|
||||
returns_node=node.returns,
|
||||
params=params,
|
||||
is_variadic=is_variadic,
|
||||
is_inline=is_inline,
|
||||
)
|
||||
from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH
|
||||
from lib.includes import t
|
||||
|
||||
|
||||
class ASTSymbolExtractor:
|
||||
"""从 Python 源文件的 AST 中提取所有符号信息"""
|
||||
|
||||
def __init__(self, symbol_table: SymbolTable) -> None:
|
||||
self._symtab: SymbolTable = symbol_table
|
||||
|
||||
def extract(self, file_path: str) -> ModuleSymbols:
|
||||
"""提取文件中的所有符号信息"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
code: str = f.read()
|
||||
tree: ast.Module = ast.parse(code, file_path)
|
||||
|
||||
result: ModuleSymbols = ModuleSymbols(file_path=file_path)
|
||||
|
||||
# 第一步:收集所有类的成员和匿名类型
|
||||
class_members: dict[str, dict[str, CTypeInfo]] = {}
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.ClassDef):
|
||||
members: dict[str, CTypeInfo] = {}
|
||||
self._collect_members(node, members, result.anonymous_types)
|
||||
class_members[node.name] = members
|
||||
|
||||
# 第二步:从每个顶层节点提取符号信息
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.ClassDef):
|
||||
members_info: dict[str, CTypeInfo] = class_members.get(node.name, {})
|
||||
result.classes.append(self._extract_class(node, members_info))
|
||||
elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
|
||||
self._extract_ann_assign(node, result)
|
||||
elif isinstance(node, ast.FunctionDef):
|
||||
result.functions.append(self._extract_function(node))
|
||||
|
||||
return result
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 成员收集(递归处理嵌套匿名类)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _collect_members(self, class_node: ast.ClassDef, members: dict[str, CTypeInfo], anonymous_types: dict[str, AnonymousTypeData], prefix: str = '') -> None:
|
||||
"""递归收集类成员,包括嵌套匿名类型"""
|
||||
for item in class_node.body:
|
||||
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
||||
member_name: str = item.target.id
|
||||
full_name: str = f'{prefix}{member_name}' if prefix else member_name
|
||||
member_type: CTypeInfo | None = CTypeInfo.FromNode(item.annotation, self._symtab)
|
||||
if member_type is None:
|
||||
member_type = CTypeInfo()
|
||||
member_type.BaseType = t.CInt()
|
||||
is_ptr: bool = member_type.IsPtr
|
||||
if isinstance(item.annotation, ast.BinOp):
|
||||
if AnnotationContainsName(item.annotation, 'CPtr'):
|
||||
is_ptr = True
|
||||
members[full_name] = member_type.Copy()
|
||||
|
||||
elif isinstance(item, ast.ClassDef):
|
||||
is_anonymous: bool = any(
|
||||
any(AnnotationContainsTType(base, 'Anonymous') for base in item.bases)
|
||||
) if item.bases else False
|
||||
|
||||
if is_anonymous:
|
||||
is_union: bool = any(
|
||||
any(AnnotationContainsTType(base, 'CUnion') for base in item.bases)
|
||||
)
|
||||
# 也检查装饰器形式 @t.CUnion
|
||||
if not is_union and hasattr(item, 'decorator_list') and item.decorator_list:
|
||||
for deco in item.decorator_list:
|
||||
if isinstance(deco, ast.Attribute) and hasattr(deco.value, 'id') and IsTModule(deco.value.id, self._symtab) and deco.attr == 'CUnion':
|
||||
is_union = True
|
||||
break
|
||||
elif isinstance(deco, ast.Name):
|
||||
cls2: type | None = getattr(t, deco.id, None)
|
||||
if cls2 is None and hasattr(self._symtab, '_t_type_symbols'):
|
||||
cls2 = self._symtab._t_type_symbols.get(deco.id)
|
||||
if cls2 is not None and isinstance(cls2, type) and issubclass(cls2, t.CUnion):
|
||||
is_union = True
|
||||
break
|
||||
|
||||
nested_name: str = f'{prefix}{item.name}' if prefix else item.name
|
||||
members[nested_name] = CTypeInfo()
|
||||
members[nested_name].BaseType = f'union {item.name}' if is_union else f'struct {item.name}'
|
||||
members[nested_name].PtrCount = 0
|
||||
members[nested_name].ArrayDims = []
|
||||
|
||||
new_prefix: str = f'{prefix}{item.name}.' if prefix else f'{item.name}.'
|
||||
self._collect_members(item, members, anonymous_types, new_prefix)
|
||||
|
||||
anonymous_members: dict[str, CTypeInfo] = {}
|
||||
for full_name, member_info in members.items():
|
||||
if full_name.startswith(f"{nested_name}."):
|
||||
member_name: str = full_name[len(nested_name) + 1:]
|
||||
anonymous_members[member_name] = member_info
|
||||
|
||||
if item.name not in anonymous_types:
|
||||
anonymous_types[item.name] = AnonymousTypeData(
|
||||
name=item.name,
|
||||
is_union=is_union,
|
||||
members=anonymous_members,
|
||||
lineno=item.lineno
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 类符号提取
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _extract_class(self, node: ast.ClassDef, members_info: dict[str, CTypeInfo]) -> ClassSymbolData:
|
||||
"""从 ClassDef 节点提取类符号数据"""
|
||||
type_kind: str = 'struct'
|
||||
is_cpython_object: bool = False
|
||||
is_packed: bool = False
|
||||
|
||||
# 检查装饰器
|
||||
if hasattr(node, 'decorator_list') and node.decorator_list:
|
||||
for decorator in node.decorator_list:
|
||||
if isinstance(decorator, ast.Attribute):
|
||||
if (hasattr(decorator.value, 'id') and
|
||||
IsTModule(decorator.value.id, self._symtab)):
|
||||
if decorator.attr == 'Object':
|
||||
is_cpython_object = True
|
||||
elif decorator.attr == 'CUnion':
|
||||
type_kind = 'union'
|
||||
elif decorator.attr == 'CEnum':
|
||||
type_kind = 'enum'
|
||||
elif decorator.attr == 'REnum':
|
||||
type_kind = 'renum'
|
||||
elif decorator.attr == 'CStruct':
|
||||
type_kind = 'struct'
|
||||
elif isinstance(decorator, ast.Name):
|
||||
# from t import CStruct / CUnion / CEnum / REnum
|
||||
cls: type | None = getattr(t, decorator.id, None)
|
||||
if cls is None and hasattr(self._symtab, '_t_type_symbols'):
|
||||
cls = self._symtab._t_type_symbols.get(decorator.id)
|
||||
if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType):
|
||||
if issubclass(cls, t.CUnion):
|
||||
type_kind = 'union'
|
||||
elif issubclass(cls, t.REnum):
|
||||
type_kind = 'renum'
|
||||
elif issubclass(cls, t.CEnum):
|
||||
type_kind = 'enum'
|
||||
elif issubclass(cls, t.CStruct):
|
||||
type_kind = 'struct'
|
||||
elif decorator.id == 'Object':
|
||||
is_cpython_object = True
|
||||
elif isinstance(decorator, ast.Call):
|
||||
if isinstance(decorator.func, ast.Attribute):
|
||||
if hasattr(decorator.func.value, 'id') and decorator.func.value.id == 'c' and decorator.func.attr == 'Attribute':
|
||||
for arg in decorator.args:
|
||||
if isinstance(arg, ast.Attribute):
|
||||
if isinstance(arg.value, ast.Attribute):
|
||||
if hasattr(arg.value.value, 'id') and IsTModule(arg.value.value.id, self._symtab) and arg.value.attr == 'attr' and arg.attr == 'packed':
|
||||
is_packed = True
|
||||
|
||||
# 检查基类
|
||||
for base in node.bases:
|
||||
if AnnotationContainsTType(base, 'CUnion'):
|
||||
type_kind = 'union'
|
||||
break
|
||||
elif AnnotationContainsTType(base, 'CEnum'):
|
||||
type_kind = 'enum'
|
||||
break
|
||||
elif AnnotationContainsTType(base, 'REnum'):
|
||||
type_kind = 'renum'
|
||||
break
|
||||
elif AnnotationContainsTType(base, 'CStruct'):
|
||||
type_kind = 'struct'
|
||||
break
|
||||
elif AnnotationContainsTType(base, 'Object'):
|
||||
is_cpython_object = True
|
||||
type_kind = 'struct'
|
||||
break
|
||||
elif isinstance(base, ast.Name) and base.id == 'Exception':
|
||||
type_kind = 'exception'
|
||||
break
|
||||
elif isinstance(base, ast.Name):
|
||||
base_entry: CTypeInfo | None = self._symtab.get(base.id)
|
||||
if base_entry and base_entry.IsExceptionClass:
|
||||
type_kind = 'exception'
|
||||
break
|
||||
|
||||
# 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端
|
||||
# HandlesClassDef._RegisterEnumMembers / VPC ClassGenEnum 同一规则源)。
|
||||
# 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源
|
||||
# (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle),
|
||||
# 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读
|
||||
# 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。
|
||||
enum_members: list[tuple[str, int, int]] = []
|
||||
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
|
||||
if type_kind == 'enum':
|
||||
enum_width = EnumClassIntWidth(node.body, self._symtab)
|
||||
next_enum_value: int = 0
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.Assign):
|
||||
if len(item.targets) == 1 and isinstance(item.targets[0], ast.Name):
|
||||
member_name: str = item.targets[0].id
|
||||
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||||
next_enum_value = item.value.value + 1
|
||||
else:
|
||||
next_enum_value += 1
|
||||
enum_members.append((member_name, next_enum_value - 1, item.lineno))
|
||||
elif isinstance(item, ast.AnnAssign):
|
||||
if isinstance(item.target, ast.Name):
|
||||
member_name: str = item.target.id
|
||||
if item.value:
|
||||
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||||
next_enum_value = item.value.value + 1
|
||||
else:
|
||||
next_enum_value += 1
|
||||
else:
|
||||
next_enum_value += 1
|
||||
enum_members.append((member_name, next_enum_value - 1, item.lineno))
|
||||
|
||||
# REnum 变体提取(嵌套 ClassDef + Assign 显式值)
|
||||
# 与 HandlesClassDef._EmitREnumLlvm 的 tag 分配逻辑对齐:
|
||||
# - 先收集 Assign 显式值
|
||||
# - ClassDef 变体从 max(explicit)+1 开始顺序编号
|
||||
# - Assign 无显式值时用独立计数器 variant_index
|
||||
if type_kind == 'renum':
|
||||
explicit_values: dict[str, int] = {}
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.Assign):
|
||||
for target in item.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||||
explicit_values[target.id] = item.value.value
|
||||
next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0
|
||||
variant_index: int = 0
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.ClassDef):
|
||||
variant_name_r: str = item.name
|
||||
tag: int = next_class_tag
|
||||
next_class_tag += 1
|
||||
enum_members.append((variant_name_r, tag, item.lineno))
|
||||
elif isinstance(item, ast.Assign):
|
||||
for target in item.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
var_name: str = target.id
|
||||
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
|
||||
value: int = item.value.value
|
||||
else:
|
||||
value = variant_index
|
||||
variant_index += 1
|
||||
enum_members.append((var_name, value, item.lineno))
|
||||
|
||||
return ClassSymbolData(
|
||||
name=node.name,
|
||||
type_kind=type_kind,
|
||||
lineno=node.lineno,
|
||||
members=members_info,
|
||||
is_cpython_object=is_cpython_object,
|
||||
is_packed=is_packed,
|
||||
enum_members=enum_members,
|
||||
enum_width=enum_width,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# AnnAssign 提取(typedef / define)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _extract_ann_assign(self, node: ast.AnnAssign, result: ModuleSymbols) -> None:
|
||||
"""从 AnnAssign 节点提取 typedef 或 define 数据"""
|
||||
var_name: str = node.target.id
|
||||
has_cdefine: bool = AnnotationContainsTType(node.annotation, 'CDefine') if node.annotation else False
|
||||
has_postdef: bool = AnnotationContainsTType(node.annotation, 'Postdefinition') if node.annotation else False
|
||||
has_ctypedef: bool = AnnotationContainsTType(node.annotation, 'CTypedef') if node.annotation else False
|
||||
|
||||
if has_cdefine:
|
||||
result.defines.append(DefineSymbolData(
|
||||
name=var_name,
|
||||
lineno=node.lineno,
|
||||
value_node=node.value,
|
||||
))
|
||||
else:
|
||||
# rawptr 是编译器内置语法(TPC/VPC 双端),不可重定义:
|
||||
# 忽略任何对 rawptr 的 typedef 声明,防止覆盖内置裸指针语义
|
||||
if var_name == 'rawptr':
|
||||
return
|
||||
result.typedefs.append(TypedefSymbolData(
|
||||
name=var_name,
|
||||
lineno=node.lineno,
|
||||
annotation=node.annotation,
|
||||
value=node.value,
|
||||
has_cdefine=has_cdefine,
|
||||
has_postdef=has_postdef,
|
||||
has_ctypedef=has_ctypedef,
|
||||
))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 函数提取
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _extract_function(self, node: ast.FunctionDef) -> FuncSymbolData:
|
||||
"""从 FunctionDef 节点提取函数符号数据"""
|
||||
params: list[tuple[str, ast.AST | None]] = [(arg.arg, arg.annotation) for arg in node.args.args]
|
||||
is_variadic: bool = node.args.vararg is not None
|
||||
is_inline: bool = CheckAnnotationHasCInline(node.returns)
|
||||
|
||||
return FuncSymbolData(
|
||||
name=node.name,
|
||||
lineno=node.lineno,
|
||||
returns_node=node.returns,
|
||||
params=params,
|
||||
is_variadic=is_variadic,
|
||||
is_inline=is_inline,
|
||||
)
|
||||
+318
-318
@@ -1,318 +1,318 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import vast as ast
|
||||
from typing import TYPE_CHECKING
|
||||
import llvmlite.ir as ir
|
||||
|
||||
from lib.core.ConstEvaluator import ConstEvaluator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
|
||||
|
||||
|
||||
class ConstEvalMixin:
|
||||
"""常量表达式求值 Mixin
|
||||
|
||||
提供数组初始化常量构建与常量表达式求值能力。
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _bswap_constant_value(val: int, width: int) -> int:
|
||||
"""编译期字节序交换"""
|
||||
if width == 16:
|
||||
return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF)
|
||||
elif width == 32:
|
||||
return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF)
|
||||
elif width == 64:
|
||||
return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF)
|
||||
return val
|
||||
|
||||
def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None:
|
||||
_zv: ir.Constant | None = Gen._zero_value_for_type(ElemType)
|
||||
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
|
||||
if not isinstance(value_node, (ast.List, ast.Set)):
|
||||
return None
|
||||
# 显式空元素判据(与 TPV AssignGenMixin._init_global_array_from_list 的裸空指针
|
||||
# 判据两侧一致):Python 侧的空 AST 元素就是 None(TPV 侧是裸 0)。列表元素计数
|
||||
# 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值;
|
||||
# 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。
|
||||
for _gi, _elt in enumerate(value_node.elts):
|
||||
if _elt is None:
|
||||
raise TypeError(
|
||||
f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。'
|
||||
f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);'
|
||||
f'拒绝静默补零生成残缺初始化,已终止编译。'
|
||||
f'位置 line {getattr(value_node, "lineno", 0)}'
|
||||
f':{getattr(value_node, "col_offset", 0)}'
|
||||
)
|
||||
if isinstance(ElemType, ir.ArrayType):
|
||||
constants: list[ir.Constant] = []
|
||||
for elt in value_node.elts:
|
||||
if isinstance(elt, (ast.List, ast.Set)):
|
||||
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen)
|
||||
if inner_consts:
|
||||
try:
|
||||
constants.append(ir.Constant(ElemType, inner_consts))
|
||||
except Exception: # fallback to _fallback on constant construction failure
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
while len(constants) < ArrayCount:
|
||||
constants.append(_fallback)
|
||||
return constants[:ArrayCount]
|
||||
StructName: str | None = None
|
||||
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
||||
StructName = elem_type_node.id
|
||||
constants = []
|
||||
for elt in value_node.elts:
|
||||
if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName:
|
||||
const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.Constant):
|
||||
# 字符串常量:根据引号类型决定 i8 或 i8*
|
||||
if isinstance(elt.value, str):
|
||||
const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.UnaryOp):
|
||||
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr':
|
||||
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int):
|
||||
try:
|
||||
constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF))
|
||||
except Exception: # fallback to _fallback on constant construction failure
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord':
|
||||
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1:
|
||||
try:
|
||||
constants.append(ir.Constant(ElemType, ord(elt.args[0].value)))
|
||||
except Exception: # fallback to _fallback on constant construction failure
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
const_val: int | float | str | None = self._try_eval_const_call(elt, Gen)
|
||||
if const_val is not None:
|
||||
try:
|
||||
constants.append(ir.Constant(ElemType, const_val))
|
||||
except Exception: # fallback to _fallback on constant construction failure
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
while len(constants) < ArrayCount:
|
||||
constants.append(_fallback)
|
||||
if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64):
|
||||
swapped: list[ir.Constant] = []
|
||||
for c in constants:
|
||||
if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int):
|
||||
swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width)
|
||||
swapped.append(ir.Constant(ElemType, swapped_val))
|
||||
else:
|
||||
swapped.append(c)
|
||||
constants = swapped
|
||||
return constants[:ArrayCount]
|
||||
|
||||
def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
|
||||
"""根据引号类型决定字符串常量的类型:
|
||||
|
||||
- 单引号单字符 → i8 (char 值)
|
||||
- 双引号/单引号多字符/三重引号 → i8* (C 字符串指针)
|
||||
- 空字符串 → 0 (None),传入 ptr 目标自动隐式转换
|
||||
"""
|
||||
if not isinstance(node.value, str):
|
||||
return None
|
||||
s: str = node.value
|
||||
# 空字符串 → 0 (None)
|
||||
if len(s) == 0:
|
||||
try:
|
||||
return ir.Constant(target_type, 0)
|
||||
except Exception:
|
||||
return None
|
||||
# 获取源代码以判断引号类型
|
||||
quote_char: str | None = None
|
||||
is_triple: bool = False
|
||||
col_offset: int = getattr(node, 'col_offset', 0)
|
||||
node_lineno: int = getattr(node, 'lineno', 0)
|
||||
# AST 的 lineno 可能不准确(常量节点可能指向下一行),
|
||||
# 因此在 lineno 和 lineno-1 两行中查找引号
|
||||
for try_lineno in (node_lineno, node_lineno - 1):
|
||||
source_line: str | None = Gen._get_source_line(try_lineno)
|
||||
if source_line and col_offset < len(source_line):
|
||||
ch: str = source_line[col_offset]
|
||||
if ch in ("'", '"'):
|
||||
quote_char = ch
|
||||
if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'):
|
||||
is_triple = True
|
||||
break
|
||||
# 判断是否为 char (i8):单引号、非三重、单字符
|
||||
is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1)
|
||||
if is_char:
|
||||
# 单引号单字符 → i8
|
||||
if isinstance(target_type, ir.IntType):
|
||||
try:
|
||||
return ir.Constant(target_type, ord(s))
|
||||
except Exception:
|
||||
return None
|
||||
# 目标类型不是整数 → 类型不匹配
|
||||
return None
|
||||
else:
|
||||
# 双引号/多字符/三重 → i8* (C 字符串)
|
||||
if isinstance(target_type, ir.PointerType):
|
||||
str_val: str = s + '\x00'
|
||||
str_bytes: bytes = str_val.encode('utf-8')
|
||||
arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes))
|
||||
gv_name: str = f"str_const_{id(node)}"
|
||||
gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name)
|
||||
gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
|
||||
gv.linkage = 'internal'
|
||||
return ir.Constant(target_type, gv.get_reference())
|
||||
# 目标类型不是指针 → 类型不匹配
|
||||
return None
|
||||
|
||||
def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
|
||||
"""统一的标量常量构建方法。
|
||||
|
||||
合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性:
|
||||
- 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False)
|
||||
- 字符串处理委托给 _BuildStringLiteralConstant
|
||||
- 显式拒绝 BaseStructType
|
||||
- 全部 try/except 保护
|
||||
"""
|
||||
# 显式拒绝结构体类型
|
||||
if isinstance(target_type, ir.BaseStructType):
|
||||
return None
|
||||
# 处理常量节点
|
||||
if isinstance(node, ast.Constant):
|
||||
# bool 必须在 int 之前判断(bool 是 int 的子类)
|
||||
if isinstance(node.value, bool):
|
||||
try:
|
||||
return ir.Constant(target_type, 1 if node.value else 0)
|
||||
except Exception:
|
||||
return None
|
||||
elif isinstance(node.value, int):
|
||||
try:
|
||||
return ir.Constant(target_type, node.value)
|
||||
except Exception:
|
||||
return None
|
||||
elif isinstance(node.value, float):
|
||||
try:
|
||||
return ir.Constant(target_type, node.value)
|
||||
except Exception:
|
||||
return None
|
||||
elif isinstance(node.value, str):
|
||||
# 字符串处理委托给 _BuildStringLiteralConstant
|
||||
const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen)
|
||||
if const is not None:
|
||||
return const
|
||||
# 回退:直接创建字符串全局变量(i8*)
|
||||
try:
|
||||
return Gen._create_string_global(node.value)
|
||||
except Exception:
|
||||
return None
|
||||
# 处理一元负号
|
||||
elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
|
||||
if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int):
|
||||
try:
|
||||
return ir.Constant(target_type, -node.operand.value)
|
||||
except Exception:
|
||||
return None
|
||||
# 处理 True/False 名称节点
|
||||
elif isinstance(node, ast.Name):
|
||||
if node.id == 'True':
|
||||
try:
|
||||
return ir.Constant(target_type, 1)
|
||||
except Exception:
|
||||
return None
|
||||
elif node.id == 'False':
|
||||
try:
|
||||
if isinstance(target_type, ir.PointerType):
|
||||
return ir.Constant(target_type, None)
|
||||
return ir.Constant(target_type, 0)
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
|
||||
def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None:
|
||||
if isinstance(node, ast.Call):
|
||||
func_name: str | None = None
|
||||
if isinstance(node.func, ast.Name):
|
||||
func_name = node.func.id
|
||||
elif isinstance(node.func, ast.Attribute):
|
||||
func_name = node.func.attr
|
||||
if func_name:
|
||||
arg_vals: list[int | float | str | None] = []
|
||||
for arg in node.args:
|
||||
v: int | float | str | None = self._try_eval_const_expr(arg, Gen)
|
||||
if v is None:
|
||||
return None
|
||||
arg_vals.append(v)
|
||||
if func_name == 'COLOR_RGB' and len(arg_vals) == 3:
|
||||
r: int | float | str | None
|
||||
g: int | float | str | None
|
||||
b: int | float | str | None
|
||||
r, g, b = arg_vals
|
||||
return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r)
|
||||
if func_name in Gen._DefineConstants:
|
||||
return Gen._DefineConstants[func_name]
|
||||
for key in Gen.functions:
|
||||
if key.endswith(f'__{func_name}'):
|
||||
return None
|
||||
return None
|
||||
return self._try_eval_const_expr(node, Gen)
|
||||
|
||||
def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None:
|
||||
return ConstEvaluator.eval_with_symtab(node, self.SymbolTable)
|
||||
|
||||
def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None:
|
||||
inner_type: ir.Type = ArrayType.element
|
||||
count: int = ArrayType.count
|
||||
elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else []
|
||||
_zv: ir.Constant | None = Gen._zero_value_for_type(inner_type)
|
||||
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined)
|
||||
constants: list[ir.Constant] = []
|
||||
for elt in elts:
|
||||
if isinstance(inner_type, ir.ArrayType):
|
||||
if isinstance(elt, (ast.List, ast.Set)):
|
||||
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen)
|
||||
if inner_consts:
|
||||
constants.append(ir.Constant(inner_type, inner_consts))
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.Constant):
|
||||
const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.UnaryOp):
|
||||
const = self._BuildScalarConstantUnified(elt, inner_type, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
while len(constants) < count:
|
||||
constants.append(_fallback)
|
||||
return constants[:count]
|
||||
from __future__ import annotations
|
||||
|
||||
import vast as ast
|
||||
from typing import TYPE_CHECKING
|
||||
import llvmlite.ir as ir
|
||||
|
||||
from lib.core.ConstEvaluator import ConstEvaluator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
|
||||
|
||||
|
||||
class ConstEvalMixin:
|
||||
"""常量表达式求值 Mixin
|
||||
|
||||
提供数组初始化常量构建与常量表达式求值能力。
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _bswap_constant_value(val: int, width: int) -> int:
|
||||
"""编译期字节序交换"""
|
||||
if width == 16:
|
||||
return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF)
|
||||
elif width == 32:
|
||||
return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF)
|
||||
elif width == 64:
|
||||
return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF)
|
||||
return val
|
||||
|
||||
def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None:
|
||||
_zv: ir.Constant | None = Gen._zero_value_for_type(ElemType)
|
||||
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
|
||||
if not isinstance(value_node, (ast.List, ast.Set)):
|
||||
return None
|
||||
# 显式空元素判据(与 VPC AssignGenMixin._init_global_array_from_list 的裸空指针
|
||||
# 判据两侧一致):Python 侧的空 AST 元素就是 None(VPC 侧是裸 0)。列表元素计数
|
||||
# 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值;
|
||||
# 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。
|
||||
for _gi, _elt in enumerate(value_node.elts):
|
||||
if _elt is None:
|
||||
raise TypeError(
|
||||
f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。'
|
||||
f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);'
|
||||
f'拒绝静默补零生成残缺初始化,已终止编译。'
|
||||
f'位置 line {getattr(value_node, "lineno", 0)}'
|
||||
f':{getattr(value_node, "col_offset", 0)}'
|
||||
)
|
||||
if isinstance(ElemType, ir.ArrayType):
|
||||
constants: list[ir.Constant] = []
|
||||
for elt in value_node.elts:
|
||||
if isinstance(elt, (ast.List, ast.Set)):
|
||||
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen)
|
||||
if inner_consts:
|
||||
try:
|
||||
constants.append(ir.Constant(ElemType, inner_consts))
|
||||
except Exception: # fallback to _fallback on constant construction failure
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
while len(constants) < ArrayCount:
|
||||
constants.append(_fallback)
|
||||
return constants[:ArrayCount]
|
||||
StructName: str | None = None
|
||||
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
||||
StructName = elem_type_node.id
|
||||
constants = []
|
||||
for elt in value_node.elts:
|
||||
if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName:
|
||||
const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.Constant):
|
||||
# 字符串常量:根据引号类型决定 i8 或 i8*
|
||||
if isinstance(elt.value, str):
|
||||
const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.UnaryOp):
|
||||
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr':
|
||||
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int):
|
||||
try:
|
||||
constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF))
|
||||
except Exception: # fallback to _fallback on constant construction failure
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord':
|
||||
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1:
|
||||
try:
|
||||
constants.append(ir.Constant(ElemType, ord(elt.args[0].value)))
|
||||
except Exception: # fallback to _fallback on constant construction failure
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
const_val: int | float | str | None = self._try_eval_const_call(elt, Gen)
|
||||
if const_val is not None:
|
||||
try:
|
||||
constants.append(ir.Constant(ElemType, const_val))
|
||||
except Exception: # fallback to _fallback on constant construction failure
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
while len(constants) < ArrayCount:
|
||||
constants.append(_fallback)
|
||||
if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64):
|
||||
swapped: list[ir.Constant] = []
|
||||
for c in constants:
|
||||
if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int):
|
||||
swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width)
|
||||
swapped.append(ir.Constant(ElemType, swapped_val))
|
||||
else:
|
||||
swapped.append(c)
|
||||
constants = swapped
|
||||
return constants[:ArrayCount]
|
||||
|
||||
def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
|
||||
"""根据引号类型决定字符串常量的类型:
|
||||
|
||||
- 单引号单字符 → i8 (char 值)
|
||||
- 双引号/单引号多字符/三重引号 → i8* (C 字符串指针)
|
||||
- 空字符串 → 0 (None),传入 ptr 目标自动隐式转换
|
||||
"""
|
||||
if not isinstance(node.value, str):
|
||||
return None
|
||||
s: str = node.value
|
||||
# 空字符串 → 0 (None)
|
||||
if len(s) == 0:
|
||||
try:
|
||||
return ir.Constant(target_type, 0)
|
||||
except Exception:
|
||||
return None
|
||||
# 获取源代码以判断引号类型
|
||||
quote_char: str | None = None
|
||||
is_triple: bool = False
|
||||
col_offset: int = getattr(node, 'col_offset', 0)
|
||||
node_lineno: int = getattr(node, 'lineno', 0)
|
||||
# AST 的 lineno 可能不准确(常量节点可能指向下一行),
|
||||
# 因此在 lineno 和 lineno-1 两行中查找引号
|
||||
for try_lineno in (node_lineno, node_lineno - 1):
|
||||
source_line: str | None = Gen._get_source_line(try_lineno)
|
||||
if source_line and col_offset < len(source_line):
|
||||
ch: str = source_line[col_offset]
|
||||
if ch in ("'", '"'):
|
||||
quote_char = ch
|
||||
if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'):
|
||||
is_triple = True
|
||||
break
|
||||
# 判断是否为 char (i8):单引号、非三重、单字符
|
||||
is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1)
|
||||
if is_char:
|
||||
# 单引号单字符 → i8
|
||||
if isinstance(target_type, ir.IntType):
|
||||
try:
|
||||
return ir.Constant(target_type, ord(s))
|
||||
except Exception:
|
||||
return None
|
||||
# 目标类型不是整数 → 类型不匹配
|
||||
return None
|
||||
else:
|
||||
# 双引号/多字符/三重 → i8* (C 字符串)
|
||||
if isinstance(target_type, ir.PointerType):
|
||||
str_val: str = s + '\x00'
|
||||
str_bytes: bytes = str_val.encode('utf-8')
|
||||
arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes))
|
||||
gv_name: str = f"str_const_{id(node)}"
|
||||
gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name)
|
||||
gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
|
||||
gv.linkage = 'internal'
|
||||
return ir.Constant(target_type, gv.get_reference())
|
||||
# 目标类型不是指针 → 类型不匹配
|
||||
return None
|
||||
|
||||
def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
|
||||
"""统一的标量常量构建方法。
|
||||
|
||||
合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性:
|
||||
- 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False)
|
||||
- 字符串处理委托给 _BuildStringLiteralConstant
|
||||
- 显式拒绝 BaseStructType
|
||||
- 全部 try/except 保护
|
||||
"""
|
||||
# 显式拒绝结构体类型
|
||||
if isinstance(target_type, ir.BaseStructType):
|
||||
return None
|
||||
# 处理常量节点
|
||||
if isinstance(node, ast.Constant):
|
||||
# bool 必须在 int 之前判断(bool 是 int 的子类)
|
||||
if isinstance(node.value, bool):
|
||||
try:
|
||||
return ir.Constant(target_type, 1 if node.value else 0)
|
||||
except Exception:
|
||||
return None
|
||||
elif isinstance(node.value, int):
|
||||
try:
|
||||
return ir.Constant(target_type, node.value)
|
||||
except Exception:
|
||||
return None
|
||||
elif isinstance(node.value, float):
|
||||
try:
|
||||
return ir.Constant(target_type, node.value)
|
||||
except Exception:
|
||||
return None
|
||||
elif isinstance(node.value, str):
|
||||
# 字符串处理委托给 _BuildStringLiteralConstant
|
||||
const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen)
|
||||
if const is not None:
|
||||
return const
|
||||
# 回退:直接创建字符串全局变量(i8*)
|
||||
try:
|
||||
return Gen._create_string_global(node.value)
|
||||
except Exception:
|
||||
return None
|
||||
# 处理一元负号
|
||||
elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
|
||||
if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int):
|
||||
try:
|
||||
return ir.Constant(target_type, -node.operand.value)
|
||||
except Exception:
|
||||
return None
|
||||
# 处理 True/False 名称节点
|
||||
elif isinstance(node, ast.Name):
|
||||
if node.id == 'True':
|
||||
try:
|
||||
return ir.Constant(target_type, 1)
|
||||
except Exception:
|
||||
return None
|
||||
elif node.id == 'False':
|
||||
try:
|
||||
if isinstance(target_type, ir.PointerType):
|
||||
return ir.Constant(target_type, None)
|
||||
return ir.Constant(target_type, 0)
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
|
||||
def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None:
|
||||
if isinstance(node, ast.Call):
|
||||
func_name: str | None = None
|
||||
if isinstance(node.func, ast.Name):
|
||||
func_name = node.func.id
|
||||
elif isinstance(node.func, ast.Attribute):
|
||||
func_name = node.func.attr
|
||||
if func_name:
|
||||
arg_vals: list[int | float | str | None] = []
|
||||
for arg in node.args:
|
||||
v: int | float | str | None = self._try_eval_const_expr(arg, Gen)
|
||||
if v is None:
|
||||
return None
|
||||
arg_vals.append(v)
|
||||
if func_name == 'COLOR_RGB' and len(arg_vals) == 3:
|
||||
r: int | float | str | None
|
||||
g: int | float | str | None
|
||||
b: int | float | str | None
|
||||
r, g, b = arg_vals
|
||||
return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r)
|
||||
if func_name in Gen._DefineConstants:
|
||||
return Gen._DefineConstants[func_name]
|
||||
for key in Gen.functions:
|
||||
if key.endswith(f'__{func_name}'):
|
||||
return None
|
||||
return None
|
||||
return self._try_eval_const_expr(node, Gen)
|
||||
|
||||
def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None:
|
||||
return ConstEvaluator.eval_with_symtab(node, self.SymbolTable)
|
||||
|
||||
def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None:
|
||||
inner_type: ir.Type = ArrayType.element
|
||||
count: int = ArrayType.count
|
||||
elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else []
|
||||
_zv: ir.Constant | None = Gen._zero_value_for_type(inner_type)
|
||||
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined)
|
||||
constants: list[ir.Constant] = []
|
||||
for elt in elts:
|
||||
if isinstance(inner_type, ir.ArrayType):
|
||||
if isinstance(elt, (ast.List, ast.Set)):
|
||||
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen)
|
||||
if inner_consts:
|
||||
constants.append(ir.Constant(inner_type, inner_consts))
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.Constant):
|
||||
const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
elif isinstance(elt, ast.UnaryOp):
|
||||
const = self._BuildScalarConstantUnified(elt, inner_type, Gen)
|
||||
if const:
|
||||
constants.append(const)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
else:
|
||||
constants.append(_fallback)
|
||||
while len(constants) < count:
|
||||
constants.append(_fallback)
|
||||
return constants[:count]
|
||||
File diff suppressed because it is too large.
Load diff
+1317
-1317
File diff suppressed because it is too large.
Load diff
+7
-7
@@ -32,9 +32,9 @@ from lib.vast.Tokenizer import (
|
||||
# novirtual → `@t.NoVTable`。本层只"合成装饰器节点",不新增第二套表示:
|
||||
# - 类级:CVTable 强制有 vtable / NoVTable 强制无 vtable
|
||||
# (TPC:HandlesClassDef._resolve_class_flags 读 ClassDef.decorator_list
|
||||
# 的 CVTable/NoVTable;TPV:ClassGenBase._detect_vtable_status)
|
||||
# 的 CVTable/NoVTable;VPC:ClassGenBase._detect_vtable_status)
|
||||
# - 方法级:CVTable 进虚表槽 / NoVTable 不进虚表槽
|
||||
# (TPV:ClassGenBase._should_method_be_virtual;TPC:同判据见
|
||||
# (VPC:ClassGenBase._should_method_be_virtual;TPC:同判据见
|
||||
# SymbolUtils.ClassVtableSlotMethods / 方法收集处)
|
||||
#
|
||||
# 行号守恒:合成节点的位置取"修饰符 token"的位置,ClassDef/FunctionDef 自身的
|
||||
@@ -419,7 +419,7 @@ class Parser(ExprParserMixin):
|
||||
用于把 typedef 的右值按**类型位置**解析(`X: t.CTypedef = const uint16*`
|
||||
里的 `*` 后缀由 _ParseTypeAnnotation 消费,与注解位同一规则;描述符
|
||||
`const`/`volatile` 已由 SymbolUtils._ExpandTypeDescriptors 在文本层展开)。
|
||||
仅按标记名判定,不依赖 import 别名表(与 TPV `_IsCTypedefAnnotation` 同口径)。
|
||||
仅按标记名判定,不依赖 import 别名表(与 VPC `_IsCTypedefAnnotation` 同口径)。
|
||||
"""
|
||||
if annotation is None:
|
||||
return False
|
||||
@@ -761,7 +761,7 @@ class Parser(ExprParserMixin):
|
||||
nxt: Token = self._Peek(1)
|
||||
marker: str = _VIRTUAL_MARKERS[mod.Value]
|
||||
# 后随必须是 class / def;def 的别名 fn/fun/func/function 同样接受
|
||||
# (与 TPV 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的
|
||||
# (与 VPC 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的
|
||||
# fn→def 展开只命中**行首**的 fn,`virtual fn m()` 不会被先展开,
|
||||
# 故此处必须显式接受别名(否则两端对同一源码一端报错一端通过)。
|
||||
if nxt.Type != NAME or (nxt.Value != 'class'
|
||||
@@ -820,7 +820,7 @@ class Parser(ExprParserMixin):
|
||||
|
||||
def _ParseStructDef(self, decorators: list[N.expr] | None = None) -> N.ClassDef:
|
||||
"""`struct X:` / `struct X(Base):` → 与 `@t.NoVTable class X(t.CStruct):`
|
||||
逐字段同形的 ClassDef(与 TPV 的 _parse_structdef 同口径)。
|
||||
逐字段同形的 ClassDef(与 VPC 的 _parse_structdef 同口径)。
|
||||
|
||||
实现:复用 _ParseClassDef 的完整解析路径(类型参数 / 基类 / 关键字实参 /
|
||||
类体套件),只在**解析前后**补三件事:
|
||||
@@ -974,7 +974,7 @@ class Parser(ExprParserMixin):
|
||||
return node
|
||||
|
||||
def _ParseStaticDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef:
|
||||
"""解析 static 函数定义(等价 ``@t.CStatic``,与 TransPyV 的
|
||||
"""解析 static 函数定义(等价 ``@t.CStatic``,与 ViperC 的
|
||||
`_ParseStaticModified` 同源)。
|
||||
|
||||
允许语法(与 export/extern 对称,配不配 def 都一样):
|
||||
@@ -1038,7 +1038,7 @@ class Parser(ExprParserMixin):
|
||||
define A(x: int): ...
|
||||
|
||||
产出标准 FunctionDef 节点,返回注解改写为 ``<原类型> | t.CDefine``
|
||||
(无显式返回注解时单独 ``t.CDefine``),使 TPC/TPV 复用既有的
|
||||
(无显式返回注解时单独 ``t.CDefine``),使 TPC/VPC 复用既有的
|
||||
CDefine 宏函数识别路径(StubGen 原样保留生成 C #define、
|
||||
alwaysinline 内联);并设自定义属性 ``Define = True`` 供
|
||||
Unparser 还原 define 语法。
|
||||
|
||||
Reference in new issue
Block a user