1571 lines
102 KiB
Python
1571 lines
102 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
|
||
from lib.includes import t
|
||
import ast
|
||
import llvmlite.ir as ir
|
||
|
||
class AssignHandle:
|
||
def __init__(self, translator: "Translator"):
|
||
self.Trans = translator
|
||
self._CurrentCpythonObjectClass: str = None
|
||
|
||
@staticmethod
|
||
def _zero_value(var_type):
|
||
if isinstance(var_type, ir.ArrayType):
|
||
elem_zv = AssignHandle._zero_value(var_type.element)
|
||
if elem_zv is None:
|
||
return None
|
||
return ir.Constant(var_type, [elem_zv] * var_type.count)
|
||
elif isinstance(var_type, ir.IntType):
|
||
return ir.Constant(var_type, 0)
|
||
elif isinstance(var_type, (ir.FloatType, ir.DoubleType)):
|
||
return ir.Constant(var_type, 0.0)
|
||
elif isinstance(var_type, ir.PointerType):
|
||
return ir.Constant(var_type, None)
|
||
elif isinstance(var_type, ir.BaseStructType):
|
||
if var_type.elements is not None and len(var_type.elements) > 0:
|
||
elem_zvs = [AssignHandle._zero_value(m) for m in var_type.elements]
|
||
if any(zv is None for zv in elem_zvs):
|
||
return None
|
||
return ir.Constant(var_type, elem_zvs)
|
||
return None
|
||
return None
|
||
|
||
@staticmethod
|
||
def _contains_identified_struct(var_type):
|
||
if isinstance(var_type, ir.IdentifiedStructType):
|
||
return True
|
||
if isinstance(var_type, ir.ArrayType):
|
||
return AssignHandle._contains_identified_struct(var_type.element)
|
||
if isinstance(var_type, ir.PointerType):
|
||
return AssignHandle._contains_identified_struct(var_type.pointee)
|
||
if isinstance(var_type, ir.BaseStructType):
|
||
if var_type.elements:
|
||
return any(AssignHandle._contains_identified_struct(e) for e in var_type.elements)
|
||
return False
|
||
|
||
def HandleExprLlvm(self, Node, VarType=None):
|
||
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
|
||
|
||
def _HandleAssignLlvm(self, Node):
|
||
Gen = self.Trans.LlvmGen
|
||
Gen._set_node_info(Node, "Assign")
|
||
if len(Node.targets) == 1:
|
||
Target = Node.targets[0]
|
||
if isinstance(Target, ast.Name):
|
||
VarName = Target.id
|
||
if VarName in Gen.var_const_flags:
|
||
src_info = Gen._get_node_info()
|
||
print(f"[错误] 不能对 const 变量 '{VarName}' 赋值{src_info}")
|
||
import sys
|
||
sys.exit(1)
|
||
if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) and Node.value.value.id == 't':
|
||
AttrName = Node.value.attr
|
||
Gen.var_type_info[VarName] = {'type': 't_type', 'name': AttrName}
|
||
Gen.var_type_assignments.setdefault(VarName, []).append({'type': 't_type', 'name': AttrName})
|
||
from lib.includes.t import CTypeRegistry
|
||
c_type_name = ''
|
||
resolved = CTypeRegistry.ResolveName(AttrName)
|
||
if resolved is not None:
|
||
ctype_cls, ptr_level = resolved
|
||
c_type_name = CTypeRegistry.CTypeToCName(ctype_cls)
|
||
if ptr_level > 0 and c_type_name:
|
||
c_type_name = c_type_name + ' *'
|
||
if not c_type_name:
|
||
if AttrName == 'CPtr':
|
||
c_type_name = 'void *'
|
||
is_unsigned = Gen._is_type_unsigned(c_type_name) if c_type_name else False
|
||
fmt_str = "%u\n" if is_unsigned else "%d\n"
|
||
fmt_bytes = fmt_str.encode('utf-8') + b'\x00'
|
||
fmt_type = ir.ArrayType(ir.IntType(8), len(fmt_bytes))
|
||
fmt_const = ir.Constant(fmt_type, bytearray(fmt_bytes))
|
||
fmt_gvar = ir.GlobalVariable(Gen.module, fmt_type, name=f"str_const_{Gen.string_const_counter}")
|
||
Gen.string_const_counter += 1
|
||
fmt_gvar.initializer = fmt_const
|
||
fmt_gvar.linkage = 'internal'
|
||
fmt_ptr = Gen.builder.bitcast(fmt_gvar, ir.PointerType(ir.IntType(8)), name=f"{VarName}_fmt")
|
||
fmt_var_name = f"__{VarName}_fmt"
|
||
if fmt_var_name not in Gen.variables:
|
||
fmt_alloca = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=fmt_var_name)
|
||
Gen.variables[fmt_var_name] = fmt_alloca
|
||
Gen.builder.store(fmt_ptr, Gen.variables[fmt_var_name])
|
||
kind_var_name = f"__{VarName}_kind"
|
||
if kind_var_name not in Gen.variables:
|
||
kind_alloca = Gen._allocaEntry(ir.IntType(32), name=kind_var_name)
|
||
Gen.variables[kind_var_name] = kind_alloca
|
||
Gen.builder.store(ir.Constant(ir.IntType(32), 0), Gen.variables[kind_var_name])
|
||
if VarName not in Gen.variables:
|
||
dummy_var = Gen._alloca(ir.IntType(8), name=VarName)
|
||
Gen.variables[VarName] = dummy_var
|
||
return
|
||
if isinstance(Node.value, ast.Name) and Node.value.id in Gen.structs:
|
||
ClassName = Node.value.id
|
||
Gen.var_type_info[VarName] = {'type': 'class_cast', 'name': ClassName}
|
||
Gen.var_type_assignments.setdefault(VarName, []).append({'type': 'class_cast', 'name': ClassName})
|
||
fmt_str = "%p\n"
|
||
fmt_bytes = fmt_str.encode('utf-8') + b'\x00'
|
||
fmt_type = ir.ArrayType(ir.IntType(8), len(fmt_bytes))
|
||
fmt_const = ir.Constant(fmt_type, bytearray(fmt_bytes))
|
||
fmt_gvar = ir.GlobalVariable(Gen.module, fmt_type, name=f"str_const_{Gen.string_const_counter}")
|
||
Gen.string_const_counter += 1
|
||
fmt_gvar.initializer = fmt_const
|
||
fmt_gvar.linkage = 'internal'
|
||
fmt_ptr = Gen.builder.bitcast(fmt_gvar, ir.PointerType(ir.IntType(8)), name=f"{VarName}_fmt")
|
||
fmt_var_name = f"__{VarName}_fmt"
|
||
if fmt_var_name not in Gen.variables:
|
||
fmt_alloca = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=fmt_var_name)
|
||
Gen.variables[fmt_var_name] = fmt_alloca
|
||
Gen.builder.store(fmt_ptr, Gen.variables[fmt_var_name])
|
||
kind_var_name = f"__{VarName}_kind"
|
||
if kind_var_name not in Gen.variables:
|
||
kind_alloca = Gen._allocaEntry(ir.IntType(32), name=kind_var_name)
|
||
Gen.variables[kind_var_name] = kind_alloca
|
||
Gen.builder.store(ir.Constant(ir.IntType(32), 1), Gen.variables[kind_var_name])
|
||
if VarName not in Gen.variables:
|
||
dummy_var = Gen._alloca(ir.IntType(8), name=VarName)
|
||
Gen.variables[VarName] = dummy_var
|
||
return
|
||
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == 'va_list':
|
||
va_list_ptr = Gen._allocaEntry(ir.IntType(8).as_pointer(), name="va_list")
|
||
Gen.variables[VarName] = va_list_ptr
|
||
return
|
||
IsStructCtor = (isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name)
|
||
and Node.value.func.id in Gen.structs)
|
||
if IsStructCtor:
|
||
ClassName = Node.value.func.id
|
||
StructType = Gen.structs[ClassName]
|
||
|
||
IsUnion = False
|
||
TypeInfo = self.Trans.SymbolTable.lookup(ClassName)
|
||
if TypeInfo and TypeInfo.IsUnion:
|
||
IsUnion = True
|
||
|
||
if IsUnion:
|
||
Value = self.HandleExprLlvm(Node.value)
|
||
if Value:
|
||
var = Gen._allocaEntry(Value.type, name=VarName)
|
||
Gen._store(Value, var)
|
||
Gen.variables[VarName] = var
|
||
return
|
||
|
||
ExistingPtr = None
|
||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||
VarPtr = Gen.variables[VarName]
|
||
if isinstance(VarPtr.type.pointee, ir.PointerType):
|
||
ExistingPtr = VarPtr
|
||
elif VarName in Gen._reg_values:
|
||
OldVal = Gen._reg_values[VarName]
|
||
if isinstance(OldVal.type, ir.PointerType):
|
||
var = Gen._allocaEntry(OldVal.type, name=VarName)
|
||
Gen._store(OldVal, var)
|
||
Gen.variables[VarName] = var
|
||
del Gen._reg_values[VarName]
|
||
ExistingPtr = var
|
||
elif VarName in Gen._direct_values:
|
||
OldVal = Gen._direct_values.pop(VarName)
|
||
if isinstance(OldVal.type, ir.PointerType):
|
||
var = Gen._allocaEntry(OldVal.type, name=VarName)
|
||
Gen._store(OldVal, var)
|
||
Gen.variables[VarName] = var
|
||
ExistingPtr = var
|
||
if ExistingPtr:
|
||
pointee = ExistingPtr.type.pointee
|
||
if isinstance(pointee, ir.PointerType) and pointee.pointee == StructType:
|
||
self._InitStructAtPtr(Node.value, ClassName, ExistingPtr)
|
||
return
|
||
elif isinstance(pointee, ir.PointerType) and isinstance(pointee.pointee, ir.IntType) and pointee.pointee.width == 8:
|
||
self._InitStructAtPtr(Node.value, ClassName, ExistingPtr)
|
||
Gen.var_struct_class[VarName] = ClassName
|
||
return
|
||
if isinstance(Node.value, ast.Attribute):
|
||
enum_type_name = self._CheckEnumMemberAssignment(Node.value)
|
||
if enum_type_name:
|
||
Gen.var_type_info[VarName] = {'type': 'enum', 'name': enum_type_name}
|
||
var_type_for_list = None
|
||
if isinstance(Node.value, ast.List) and VarName in Gen.var_type_info:
|
||
type_info = Gen.var_type_info[VarName]
|
||
if type_info.get('type') == 'list':
|
||
var_type_for_list = type_info.get('full_type')
|
||
Value = self.HandleExprLlvm(Node.value, VarType=var_type_for_list)
|
||
if not Value:
|
||
return
|
||
if isinstance(Value.type, ir.VoidType):
|
||
return
|
||
Gen._UnregisterTempPtr(Value)
|
||
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
found_class = None
|
||
found = Gen.find_struct_by_pointee(Value.type.pointee)
|
||
if found:
|
||
CN, ST = found
|
||
found_class = CN
|
||
Gen.var_struct_class[VarName] = CN
|
||
Gen._var_to_heap_ptr[VarName] = Value
|
||
if found_class is None and isinstance(Value.type.pointee, ir.IdentifiedStructType):
|
||
pointee_name = Value.type.pointee.name
|
||
for CN, ST in Gen.structs.items():
|
||
if isinstance(ST, ir.IdentifiedStructType) and ST.name == pointee_name:
|
||
found_class = CN
|
||
Gen.var_struct_class[VarName] = CN
|
||
Gen._var_to_heap_ptr[VarName] = Value
|
||
break
|
||
else:
|
||
if VarName in ('mctx', 's1ctx', 's2ctx', 's5ctx'):
|
||
pass
|
||
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||
Gen._store(Value, Gen.variables[VarName])
|
||
else:
|
||
var = Gen._allocaEntry(Value.type, name=VarName)
|
||
Gen._store(Value, var)
|
||
Gen.variables[VarName] = var
|
||
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
|
||
call_func_name = Node.value.func.id
|
||
if call_func_name in Gen.var_type_info:
|
||
type_info = Gen.var_type_info[call_func_name]
|
||
if type_info['type'] in ('t_type', 'class_cast'):
|
||
src_fmt_var = f"__{call_func_name}_fmt"
|
||
dst_fmt_var = f"__{VarName}_fmt"
|
||
if src_fmt_var in Gen.variables:
|
||
if dst_fmt_var not in Gen.variables:
|
||
dst_alloca = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=dst_fmt_var)
|
||
Gen.variables[dst_fmt_var] = dst_alloca
|
||
src_fmt_ptr = Gen._load(Gen.variables[src_fmt_var], name=f"Load_{call_func_name}_fmt_for_{VarName}")
|
||
Gen.builder.store(src_fmt_ptr, Gen.variables[dst_fmt_var])
|
||
src_kind_var = f"__{call_func_name}_kind"
|
||
dst_kind_var = f"__{VarName}_kind"
|
||
if src_kind_var in Gen.variables:
|
||
if dst_kind_var not in Gen.variables:
|
||
dst_kind_alloca = Gen._allocaEntry(ir.IntType(32), name=dst_kind_var)
|
||
Gen.variables[dst_kind_var] = dst_kind_alloca
|
||
src_kind_val = Gen._load(Gen.variables[src_kind_var], name=f"Load_{call_func_name}_kind_for_{VarName}")
|
||
Gen.builder.store(src_kind_val, Gen.variables[dst_kind_var])
|
||
Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name}
|
||
return
|
||
if VarName in Gen.global_vars:
|
||
if VarName in Gen.module.globals:
|
||
GVar = Gen.module.globals[VarName]
|
||
Gen._store(Value, GVar)
|
||
Gen.variables[VarName] = GVar
|
||
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
found = Gen.find_struct_by_pointee(Value.type.pointee)
|
||
if found:
|
||
CN, ST = found
|
||
Gen.var_struct_class[VarName] = CN
|
||
elif VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||
Gen._store(Value, Gen.variables[VarName])
|
||
return
|
||
if VarName in Gen._reg_values:
|
||
OldVal = Gen._reg_values[VarName]
|
||
var = 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 = Gen._direct_values.pop(VarName)
|
||
var = Gen._allocaEntry(OldVal.type, name=VarName)
|
||
Gen._store(OldVal, var)
|
||
Gen.variables[VarName] = var
|
||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||
VarPtr = Gen.variables[VarName]
|
||
TargetType = VarPtr.type.pointee if isinstance(VarPtr.type, ir.PointerType) else None
|
||
try:
|
||
types_differ = TargetType and Value.type != TargetType
|
||
except AssertionError:
|
||
types_differ = False
|
||
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType):
|
||
LoadedValue = Gen._load(Value, name=f"Load_{VarName}")
|
||
try:
|
||
Loaded_differ = TargetType and LoadedValue.type != TargetType
|
||
except AssertionError:
|
||
Loaded_differ = False
|
||
if Loaded_differ:
|
||
try:
|
||
Coerced = Gen._coerce_value(LoadedValue, TargetType)
|
||
if Coerced is not None:
|
||
LoadedValue = Coerced
|
||
except AssertionError:
|
||
pass
|
||
Gen._store(LoadedValue, VarPtr)
|
||
elif types_differ:
|
||
try:
|
||
Coerced = Gen._coerce_value(Value, TargetType)
|
||
if Coerced is not None:
|
||
Gen._store(Coerced, VarPtr)
|
||
else:
|
||
Gen._store(Value, VarPtr)
|
||
except AssertionError:
|
||
Gen._store(Value, VarPtr)
|
||
else:
|
||
Gen._store(Value, VarPtr)
|
||
if VarName not in Gen.variables:
|
||
# 检查是否为模块级全局变量,避免创建局部变量遮蔽全局
|
||
if VarName in Gen.module.globals:
|
||
GVar = Gen.module.globals[VarName]
|
||
Gen._store(Value, GVar)
|
||
Gen.variables[VarName] = GVar
|
||
elif isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType):
|
||
LoadedValue = Gen._load(Value, name=f"Load_{VarName}")
|
||
var = Gen._allocaEntry(LoadedValue.type, name=VarName)
|
||
Gen._store(LoadedValue, var)
|
||
Gen.variables[VarName] = var
|
||
else:
|
||
var = Gen._allocaEntry(Value.type, name=VarName)
|
||
Gen._store(Value, var)
|
||
Gen.variables[VarName] = var
|
||
is_u = Gen._check_node_unsigned(Node.value)
|
||
Gen._record_var_signedness(VarName, 'unsigned int' if is_u else 'int')
|
||
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
|
||
call_func_name = Node.value.func.id
|
||
if call_func_name in Gen.var_type_info:
|
||
type_info = Gen.var_type_info[call_func_name]
|
||
if type_info['type'] in ('t_type', 'class_cast'):
|
||
src_fmt_var = f"__{call_func_name}_fmt"
|
||
dst_fmt_var = f"__{VarName}_fmt"
|
||
if src_fmt_var in Gen.variables:
|
||
if dst_fmt_var not in Gen.variables:
|
||
dst_alloca = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=dst_fmt_var)
|
||
Gen.variables[dst_fmt_var] = dst_alloca
|
||
src_fmt_ptr = Gen._load(Gen.variables[src_fmt_var], name=f"Load_{call_func_name}_fmt_for_{VarName}")
|
||
Gen.builder.store(src_fmt_ptr, Gen.variables[dst_fmt_var])
|
||
src_kind_var = f"__{call_func_name}_kind"
|
||
dst_kind_var = f"__{VarName}_kind"
|
||
if src_kind_var in Gen.variables:
|
||
if dst_kind_var not in Gen.variables:
|
||
dst_kind_alloca = Gen._allocaEntry(ir.IntType(32), name=dst_kind_var)
|
||
Gen.variables[dst_kind_var] = dst_kind_alloca
|
||
src_kind_val = Gen._load(Gen.variables[src_kind_var], name=f"Load_{call_func_name}_kind_for_{VarName}")
|
||
Gen.builder.store(src_kind_val, Gen.variables[dst_kind_var])
|
||
Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name}
|
||
else:
|
||
Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name}
|
||
elif isinstance(Target, ast.Attribute):
|
||
TargetType = None
|
||
AttrName = Target.attr
|
||
VarName = None
|
||
if isinstance(Target.value, ast.Name):
|
||
VarName = Target.value.id
|
||
if VarName:
|
||
ClassName = Gen.var_struct_class.get(VarName)
|
||
if not ClassName and getattr(Gen, 'global_struct_class', None):
|
||
ClassName = Gen.global_struct_class.get(VarName)
|
||
if ClassName:
|
||
Gen.var_struct_class[VarName] = ClassName
|
||
if not ClassName:
|
||
for CN in Gen.structs:
|
||
if VarName == CN.lower() or VarName.startswith(CN):
|
||
ClassName = CN
|
||
break
|
||
if ClassName and ClassName in Gen.class_members:
|
||
for m_name, m_type in Gen.class_members[ClassName]:
|
||
if m_name == AttrName:
|
||
TargetType = m_type
|
||
break
|
||
Value = self.HandleExprLlvm(Node.value, VarType=TargetType)
|
||
if Value:
|
||
self._HandleAttributeStoreLlvm(Target, Value)
|
||
elif isinstance(Target, ast.Subscript):
|
||
TargetType = None
|
||
SubVarName = None
|
||
if isinstance(Target.value, ast.Name):
|
||
SubVarName = Target.value.id
|
||
if SubVarName in Gen.var_struct_class:
|
||
ClassName = Gen.var_struct_class[SubVarName]
|
||
if ClassName in Gen.class_members:
|
||
for m_name, m_type in Gen.class_members[ClassName]:
|
||
if m_name == SubVarName:
|
||
if isinstance(m_type, ir.PointerType):
|
||
pointee = m_type.pointee
|
||
if isinstance(pointee, ir.ArrayType):
|
||
TargetType = pointee.element
|
||
elif isinstance(m_type, ir.ArrayType):
|
||
TargetType = m_type.element
|
||
break
|
||
if not TargetType and isinstance(Target.value, ast.Name):
|
||
SubVarName = Target.value.id
|
||
var_val = Gen.variables.get(SubVarName)
|
||
if var_val is None and SubVarName in Gen._direct_values:
|
||
var_val = Gen._direct_values[SubVarName]
|
||
if var_val is not None:
|
||
var_type = var_val.type
|
||
if isinstance(var_type, ir.PointerType):
|
||
pointee = var_type.pointee
|
||
if isinstance(pointee, ir.ArrayType):
|
||
TargetType = pointee.element
|
||
elif isinstance(var_type, ir.ArrayType):
|
||
TargetType = var_type.element
|
||
Value = self.HandleExprLlvm(Node.value, VarType=TargetType)
|
||
if Value:
|
||
self._HandleSubscriptStoreLlvm(Target, Value)
|
||
elif isinstance(Target, ast.Tuple):
|
||
if isinstance(Node.value, ast.Call):
|
||
FuncName = None
|
||
ModulePath = None
|
||
if isinstance(Node.value.func, ast.Name):
|
||
FuncName = Node.value.func.id
|
||
elif isinstance(Node.value.func, ast.Attribute):
|
||
FuncName = Node.value.func.attr
|
||
ModulePath = self.Trans.ExprCallHandle._get_ModulePath(Node.value.func.value)
|
||
CReturnTypes = []
|
||
FuncDef = self.Trans.FunctionDefCache.get(FuncName) if FuncName else None
|
||
if FuncDef:
|
||
if FuncDef.returns and isinstance(FuncDef.returns, ast.Subscript):
|
||
if isinstance(FuncDef.returns.value, ast.Name) and FuncDef.returns.value.id == 'tuple':
|
||
slice_node = FuncDef.returns.slice
|
||
if isinstance(slice_node, ast.Tuple):
|
||
CReturnTypes = slice_node.elts
|
||
else:
|
||
CReturnTypes = [slice_node]
|
||
if FuncDef.decorator_list:
|
||
for decorator in FuncDef.decorator_list:
|
||
if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute):
|
||
if decorator.func.attr == 'CReturn':
|
||
for arg in decorator.args:
|
||
CReturnTypes.append(arg)
|
||
if not CReturnTypes and FuncName:
|
||
func = Gen.functions.get(FuncName)
|
||
if func:
|
||
func_ft = getattr(func, 'ftype', None)
|
||
if func_ft:
|
||
ret_type = func_ft.return_type
|
||
if isinstance(ret_type, ir.LiteralStructType):
|
||
CReturnTypes = [None] * len(ret_type.elements)
|
||
if not CReturnTypes:
|
||
sym_key = FuncName
|
||
if not self.Trans.SymbolTable.has(sym_key):
|
||
if ModulePath:
|
||
sym_key = f"{ModulePath}.{FuncName}"
|
||
sym_info = self.Trans.SymbolTable.lookup(sym_key)
|
||
if sym_info and sym_info.IsFunction:
|
||
ret_type_info = sym_info.FuncPtrReturn
|
||
param_type_infos = [pt for _, pt in (sym_info.FuncPtrParams or [])]
|
||
if ret_type_info:
|
||
if isinstance(ret_type_info, CTypeInfo) and ret_type_info.BaseType:
|
||
ret_type = ret_type_info.ToLLVM(Gen)
|
||
else:
|
||
ret_type = Gen._type_str_to_llvm(str(ret_type_info) if ret_type_info else 'i32', False)
|
||
if isinstance(ret_type, ir.LiteralStructType):
|
||
CReturnTypes = [None] * len(ret_type.elements)
|
||
if FuncName not in Gen.functions:
|
||
if isinstance(ret_type, ir.VoidType):
|
||
ret_type = ir.IntType(32)
|
||
llvm_param_types = []
|
||
for pt in param_type_infos:
|
||
if isinstance(pt, CTypeInfo) and pt.BaseType:
|
||
lp = pt.ToLLVM(Gen)
|
||
else:
|
||
lp = Gen._type_str_to_llvm(str(pt) if pt else 'i32', '*' in str(pt) if isinstance(pt, str) else False)
|
||
if isinstance(lp, ir.VoidType):
|
||
lp = ir.IntType(8).as_pointer()
|
||
llvm_param_types.append(lp)
|
||
func_type = ir.FunctionType(ret_type, llvm_param_types)
|
||
MangledName = Gen._mangle_func_name(FuncName)
|
||
func_decl = ir.Function(Gen.module, func_type, name=MangledName)
|
||
Gen.functions[MangledName] = func_decl
|
||
Gen.functions[FuncName] = func_decl
|
||
if not CReturnTypes and FuncName:
|
||
func = Gen.functions.get(FuncName)
|
||
if func:
|
||
func_ft = getattr(func, 'ftype', None)
|
||
if func_ft:
|
||
ret_type = func_ft.return_type
|
||
if isinstance(ret_type, ir.LiteralStructType):
|
||
CReturnTypes = [None] * len(ret_type.elements)
|
||
if CReturnTypes and len(Target.elts) == len(CReturnTypes):
|
||
CallArgs = []
|
||
for arg in Node.value.args:
|
||
ArgVal = self.HandleExprLlvm(arg)
|
||
if ArgVal:
|
||
CallArgs.append(ArgVal)
|
||
for kw in Node.value.keywords:
|
||
pass
|
||
func = Gen.functions.get(FuncName)
|
||
func_ft = getattr(func, 'ftype', None) if func else None
|
||
func_params = list(getattr(func_ft, 'args', []) or []) if func_ft else []
|
||
while len(CallArgs) < len(func_params):
|
||
FuncDef = self.Trans.FunctionDefCache.get(FuncName)
|
||
if FuncDef and len(CallArgs) < len(FuncDef.args.args):
|
||
arg_def = FuncDef.args.defaults
|
||
arg_idx = len(CallArgs)
|
||
total_args = len(FuncDef.args.args)
|
||
default_start = total_args - len(arg_def)
|
||
if arg_idx >= default_start:
|
||
def_idx = arg_idx - default_start
|
||
if def_idx < len(arg_def):
|
||
DefVal = self.HandleExprLlvm(arg_def[def_idx])
|
||
if DefVal:
|
||
expected_type = func_params[arg_idx]
|
||
if DefVal.type != expected_type:
|
||
if isinstance(expected_type, ir.IntType) and isinstance(DefVal.type, ir.IntType):
|
||
if DefVal.type.width < expected_type.width:
|
||
DefVal = Gen.builder.zext(DefVal, expected_type, name=f"zext_default_{arg_idx}")
|
||
elif DefVal.type.width > expected_type.width:
|
||
DefVal = Gen.builder.trunc(DefVal, expected_type, name=f"trunc_default_{arg_idx}")
|
||
CallArgs.append(DefVal)
|
||
continue
|
||
if len(CallArgs) < len(func_params):
|
||
CallArgs.append(ir.Constant(func_params[len(CallArgs)], 0))
|
||
else:
|
||
break
|
||
adjusted = Gen._adjust_args(CallArgs, func)
|
||
call_result = Gen.builder.call(func, adjusted, name=f"call_{FuncName}")
|
||
func_ft = getattr(func, 'ftype', None)
|
||
ret_type = getattr(func_ft, 'return_type', None) if func_ft else None
|
||
for j, elt in enumerate(Target.elts):
|
||
if isinstance(elt, ast.Name):
|
||
VarName = elt.id
|
||
field_val = Gen.builder.extract_value(call_result, j, name=f"extract_{FuncName}_{j}")
|
||
if CReturnTypes[j] is not None:
|
||
ReturnTypeInfo = CTypeInfo.FromNode(CReturnTypes[j], self.Trans.SymbolTable)
|
||
if ReturnTypeInfo is None:
|
||
ReturnTypeInfo = CTypeInfo()
|
||
ReturnTypeInfo.BaseType = t.CInt()
|
||
VarType = Gen._ctype_to_llvm(ReturnTypeInfo)
|
||
elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements):
|
||
VarType = ret_type.elements[j]
|
||
else:
|
||
VarType = field_val.type
|
||
if isinstance(VarType, ir.VoidType):
|
||
VarType = ir.IntType(32)
|
||
var = Gen._alloca(VarType, name=VarName)
|
||
Gen.variables[VarName] = var
|
||
if CReturnTypes[j] is not None:
|
||
Gen._record_var_signedness(VarName, ReturnTypeStr)
|
||
elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements):
|
||
elem_type = ret_type.elements[j]
|
||
if isinstance(elem_type, ir.IntType):
|
||
if elem_type.width == 16:
|
||
Gen._record_var_signedness(VarName, 'unsigned short')
|
||
elif elem_type.width == 32:
|
||
Gen._record_var_signedness(VarName, 'unsigned int')
|
||
elif elem_type.width == 64:
|
||
Gen._record_var_signedness(VarName, 'unsigned long long')
|
||
Gen._store(field_val, var)
|
||
return
|
||
|
||
def _InitStructAtPtr(self, call_node, ClassName, VarPtr):
|
||
Gen = self.Trans.LlvmGen
|
||
if ClassName not in Gen.structs:
|
||
return
|
||
StructType = Gen.structs[ClassName]
|
||
StructPtrType = ir.PointerType(StructType)
|
||
RawPtr = Gen._load(VarPtr, name=getattr(VarPtr, 'name', 'ptr'))
|
||
StructPtr = Gen.builder.bitcast(RawPtr, StructPtrType, name=ClassName)
|
||
members = Gen.class_members.get(ClassName, [])
|
||
defaults = Gen.class_member_defaults.get(ClassName, {})
|
||
member_values = {}
|
||
for member_name, member_type in members:
|
||
if member_name in defaults:
|
||
member_values[member_name] = defaults[member_name]
|
||
if call_node.args:
|
||
for i, arg in enumerate(call_node.args):
|
||
if i < len(members):
|
||
member_name, _ = members[i]
|
||
val = self.HandleExprLlvm(arg)
|
||
if val:
|
||
member_values[member_name] = val
|
||
if call_node.keywords:
|
||
for kw in call_node.keywords:
|
||
val = self.HandleExprLlvm(kw.value)
|
||
if val:
|
||
member_values[kw.arg] = val
|
||
has_vtable = ClassName in Gen.class_vtable
|
||
base = 1 if has_vtable else 0
|
||
for i, (member_name, member_type) in enumerate(members):
|
||
if isinstance(member_type, ir.VoidType):
|
||
continue
|
||
if member_name in member_values:
|
||
ElemPtr = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + i)], name=f"{ClassName}_{member_name}")
|
||
try:
|
||
Gen._store(member_values[member_name], ElemPtr)
|
||
except Exception as _e:
|
||
if __import__('lib.constants.config', fromlist=['mode']).mode == "strict":
|
||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||
InitFuncName = f'{ClassName}.__init__'
|
||
if Gen._has_function(InitFuncName):
|
||
Args = [self.HandleExprLlvm(arg) for arg in call_node.args]
|
||
Args = [a for a in Args if a]
|
||
Gen.builder.call(Gen._get_function(InitFuncName), [StructPtr] + Args, name=f"call_{InitFuncName}")
|
||
|
||
def _StoreOrStrCopy(self, VarName, VarPtr, Value):
|
||
Gen = self.Trans.LlvmGen
|
||
DestPtr = Gen._load(VarPtr, name=VarName)
|
||
if BaseHandle._is_char_pointer(DestPtr):
|
||
if BaseHandle._is_char_pointer(Value):
|
||
if 'strcpy' not in Gen.functions:
|
||
strcpy_type = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))])
|
||
Gen.functions['strcpy'] = ir.Function(Gen.module, strcpy_type, name='strcpy')
|
||
Gen.builder.call(Gen.functions['strcpy'], [DestPtr, Value])
|
||
return
|
||
try:
|
||
CastedVal = Gen.builder.bitcast(Value, VarPtr.type.pointee, name=f"cast_{VarName}")
|
||
Gen._store(CastedVal, VarPtr)
|
||
except Exception: # 回退:bitcast 失败时 alloca 新变量
|
||
NewVar = Gen._allocaEntry(Value.type, name=VarName)
|
||
Gen._store(Value, NewVar)
|
||
Gen.variables[VarName] = NewVar
|
||
|
||
def _HandleAttributeStoreLlvm(self, Target, Value):
|
||
Gen = self.Trans.LlvmGen
|
||
AttrName = Target.attr
|
||
if isinstance(Target.value, ast.Name) and Target.value.id == 'self':
|
||
ClassName = self.Trans._CurrentCpythonObjectClass
|
||
if ClassName and ClassName in Gen.structs:
|
||
PropKey = f'{ClassName}.{AttrName}'
|
||
PropInfo = self.Trans.SymbolTable.lookup(PropKey)
|
||
if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_SETTER in PropInfo.MetaList:
|
||
SelfVar = Gen._get_var_ptr('self')
|
||
if SelfVar:
|
||
SelfPtr = Gen._load(SelfVar, name="self")
|
||
SetterFunc = Gen._get_function(PropKey + '$set')
|
||
if SetterFunc and SelfPtr:
|
||
Gen.builder.call(SetterFunc, [SelfPtr, Value], name=f"prop_set_{PropKey}")
|
||
return
|
||
SelfVar = Gen._get_var_ptr('self')
|
||
if not SelfVar:
|
||
return
|
||
if SelfVar:
|
||
SelfPtr = Gen._load(SelfVar, name="self")
|
||
is_vtable_method = False
|
||
if ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes:
|
||
if ClassName in Gen.class_methods:
|
||
for mi, mn in enumerate(Gen.class_methods[ClassName]):
|
||
method_short = mn.split('.')[-1] if '.' in mn else mn
|
||
if method_short == AttrName or mn == AttrName:
|
||
is_vtable_method = True
|
||
method_idx = mi
|
||
break
|
||
if is_vtable_method:
|
||
VtableSlotPtr = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}")
|
||
VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}")
|
||
methods = Gen.class_methods[ClassName]
|
||
VtableArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods))
|
||
ClassVtable = Gen.Vtables.get(ClassName)
|
||
if ClassVtable:
|
||
ClassVtableAddr = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"class_vtable_addr_{ClassName}")
|
||
IsSameVtable = Gen.builder.icmp_unsigned('==', VtablePtr, ClassVtableAddr, name=f"is_same_vtable_{ClassName}")
|
||
CopyBlock = Gen.func.append_basic_block(name=f"vtable_copy_self_{AttrName}")
|
||
SkipBlock = Gen.func.append_basic_block(name=f"vtable_skip_self_{AttrName}")
|
||
Gen.builder.cbranch(IsSameVtable, CopyBlock, SkipBlock)
|
||
Gen.builder.position_at_end(CopyBlock)
|
||
if not hasattr(Gen, '_vtable_copy_counter'):
|
||
Gen._vtable_copy_counter = 0
|
||
Gen._vtable_copy_counter += 1
|
||
CopyName = f"{Gen._mangle_name(ClassName)}_vtable_copy_self_{Gen._vtable_copy_counter}"
|
||
VtableCopyGlobal = ir.GlobalVariable(Gen.module, VtableArrayType, name=CopyName)
|
||
VtableCopyGlobal.linkage = 'internal'
|
||
VtableCopyGlobal.initializer = ir.Constant(VtableArrayType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods))
|
||
VtableCopyTyped = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_typed")
|
||
SrcTyped = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"src_vtable_typed")
|
||
VtableSize = len(methods) * Gen.ptr_size
|
||
MemcpyFunc = Gen._find_function('llvm.memcpy.p0i8.p0i8.i64')
|
||
if not MemcpyFunc:
|
||
MemcpyType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)])
|
||
MemcpyFunc = ir.Function(Gen.module, MemcpyType, name='llvm.memcpy.p0i8.p0i8.i64')
|
||
Gen.functions['llvm.memcpy.p0i8.p0i8.i64'] = MemcpyFunc
|
||
Gen.builder.call(MemcpyFunc, [VtableCopyTyped, SrcTyped, ir.Constant(ir.IntType(64), VtableSize), ir.Constant(ir.IntType(1), 0)])
|
||
VtableCopyGlobalAddr = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_addr")
|
||
Gen._store(VtableCopyGlobalAddr, VtableSlotPtr)
|
||
Gen.builder.branch(SkipBlock)
|
||
Gen.builder.position_at_end(SkipBlock)
|
||
VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_after_copy")
|
||
VtableTyped = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}")
|
||
MethodPtrAddr = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_slot_{AttrName}")
|
||
FuncPtrI8 = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}")
|
||
Gen._store(FuncPtrI8, MethodPtrAddr)
|
||
return
|
||
offset = Gen._get_member_offset(AttrName, ClassName)
|
||
struct_type = Gen.structs[ClassName]
|
||
max_offset = len(struct_type.elements) if isinstance(struct_type, ir.IdentifiedStructType) else 0
|
||
if offset >= max_offset:
|
||
VarName = f"self.{AttrName}"
|
||
if VarName not in Gen.variables:
|
||
NewVar = Gen._allocaEntry(Value.type, name=AttrName)
|
||
Gen.variables[VarName] = NewVar
|
||
Gen._store(Value, Gen.variables[VarName])
|
||
return
|
||
MemberPtr = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
elif isinstance(Target.value, ast.Name):
|
||
VarName = Target.value.id
|
||
ClassName = Gen.var_struct_class.get(VarName)
|
||
|
||
if not ClassName and getattr(Gen, 'global_struct_class', None):
|
||
ClassName = Gen.global_struct_class.get(VarName)
|
||
if ClassName:
|
||
Gen.var_struct_class[VarName] = ClassName
|
||
|
||
if not ClassName:
|
||
for CN in Gen.structs:
|
||
if VarName == CN.lower() or VarName.startswith(CN):
|
||
ClassName = CN
|
||
break
|
||
|
||
if not ClassName and VarName in Gen.module.globals:
|
||
GVar = Gen.module.globals[VarName]
|
||
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||
found = Gen.find_struct_by_pointee(GVar.type.pointee)
|
||
if found:
|
||
ClassName = found[0]
|
||
Gen.var_struct_class[VarName] = ClassName
|
||
if getattr(Gen, 'global_struct_class', None):
|
||
Gen.global_struct_class[VarName] = ClassName
|
||
|
||
if not ClassName and VarName in Gen.variables:
|
||
VarPtr = Gen.variables[VarName]
|
||
if isinstance(VarPtr.type, ir.PointerType):
|
||
pointee = VarPtr.type.pointee
|
||
if isinstance(pointee, ir.PointerType):
|
||
pointee = pointee.pointee
|
||
found = Gen.find_struct_by_pointee(pointee)
|
||
if found:
|
||
CN, ST = found
|
||
ClassName = CN
|
||
|
||
IsUnion = False
|
||
TypeInfo = self.Trans.SymbolTable.lookup(ClassName)
|
||
if TypeInfo and TypeInfo.IsUnion:
|
||
IsUnion = True
|
||
|
||
if ClassName and ClassName in Gen.structs:
|
||
PropKey = f'{ClassName}.{AttrName}'
|
||
PropInfo = self.Trans.SymbolTable.lookup(PropKey)
|
||
if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_SETTER in PropInfo.MetaList:
|
||
ObjVal = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
|
||
SetterFunc = Gen._get_function(PropKey + '$set')
|
||
if SetterFunc and ObjVal:
|
||
if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType):
|
||
ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}")
|
||
Gen.builder.call(SetterFunc, [ObjVal, Value], name=f"prop_set_{PropKey}")
|
||
return
|
||
ObjVal = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
|
||
if ObjVal:
|
||
if BaseHandle._is_char_pointer(ObjVal):
|
||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
|
||
if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType):
|
||
ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}")
|
||
if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]:
|
||
if IsUnion:
|
||
NestedStructName = f"{ClassName}_{AttrName}"
|
||
if NestedStructName in Gen.structs:
|
||
NestedStructType = Gen.structs[NestedStructName]
|
||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}")
|
||
offset = Gen._get_member_offset(AttrName, NestedStructName)
|
||
if offset is not None and offset < len(NestedStructType.elements):
|
||
MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
return
|
||
bitfield_offsets = Gen.class_member_bitoffsets.get(ClassName, {})
|
||
if AttrName in bitfield_offsets:
|
||
self._store_bitfield_member(ObjVal, ClassName, AttrName, Value)
|
||
return
|
||
bitfields = Gen.class_member_bitfields.get(ClassName, {})
|
||
if AttrName in bitfields and bitfields[AttrName] > 0:
|
||
if ClassName not in Gen.class_member_bitoffsets:
|
||
Gen.class_member_bitoffsets[ClassName] = {}
|
||
if AttrName not in Gen.class_member_bitoffsets[ClassName]:
|
||
bo = 0
|
||
for bf_name, bf_width in bitfields.items():
|
||
if bf_name == AttrName:
|
||
break
|
||
bo += bf_width
|
||
Gen.class_member_bitoffsets[ClassName][AttrName] = bo
|
||
self._store_bitfield_member(ObjVal, ClassName, AttrName, Value)
|
||
return
|
||
is_vtable_method = False
|
||
if ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes:
|
||
if ClassName in Gen.class_methods:
|
||
for mi, mn in enumerate(Gen.class_methods[ClassName]):
|
||
method_short = mn.split('.')[-1] if '.' in mn else mn
|
||
if method_short == AttrName or mn == AttrName:
|
||
is_vtable_method = True
|
||
method_idx = mi
|
||
break
|
||
if is_vtable_method:
|
||
VtableSlotPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}")
|
||
VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}")
|
||
methods = Gen.class_methods[ClassName]
|
||
VtableArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods))
|
||
ClassVtable = Gen.Vtables.get(ClassName)
|
||
if ClassVtable:
|
||
ClassVtableAddr = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"class_vtable_addr_{ClassName}")
|
||
IsSameVtable = Gen.builder.icmp_unsigned('==', VtablePtr, ClassVtableAddr, name=f"is_same_vtable_{ClassName}")
|
||
CopyBlock = Gen.func.append_basic_block(name=f"vtable_copy_{AttrName}")
|
||
SkipBlock = Gen.func.append_basic_block(name=f"vtable_skip_{AttrName}")
|
||
Gen.builder.cbranch(IsSameVtable, CopyBlock, SkipBlock)
|
||
Gen.builder.position_at_end(CopyBlock)
|
||
if not hasattr(Gen, '_vtable_copy_counter'):
|
||
Gen._vtable_copy_counter = 0
|
||
Gen._vtable_copy_counter += 1
|
||
CopyName = f"{Gen._mangle_name(ClassName)}_vtable_copy_{Gen._vtable_copy_counter}"
|
||
VtableCopyGlobal = ir.GlobalVariable(Gen.module, VtableArrayType, name=CopyName)
|
||
VtableCopyGlobal.linkage = 'internal'
|
||
VtableCopyGlobal.initializer = ir.Constant(VtableArrayType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods))
|
||
VtableCopyTyped = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_typed")
|
||
SrcTyped = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"src_vtable_typed")
|
||
VtableSize = len(methods) * Gen.ptr_size
|
||
MemcpyFunc = Gen._find_function('llvm.memcpy.p0i8.p0i8.i64')
|
||
if not MemcpyFunc:
|
||
MemcpyType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)])
|
||
MemcpyFunc = ir.Function(Gen.module, MemcpyType, name='llvm.memcpy.p0i8.p0i8.i64')
|
||
Gen.functions['llvm.memcpy.p0i8.p0i8.i64'] = MemcpyFunc
|
||
Gen.builder.call(MemcpyFunc, [VtableCopyTyped, SrcTyped, ir.Constant(ir.IntType(64), VtableSize), ir.Constant(ir.IntType(1), 0)])
|
||
VtableCopyGlobalAddr = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_addr")
|
||
Gen._store(VtableCopyGlobalAddr, VtableSlotPtr)
|
||
Gen.builder.branch(SkipBlock)
|
||
Gen.builder.position_at_end(SkipBlock)
|
||
VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_after_copy")
|
||
VtableTyped = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}")
|
||
MethodPtrAddr = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_slot_{AttrName}")
|
||
FuncPtrI8 = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}")
|
||
Gen._store(FuncPtrI8, MethodPtrAddr)
|
||
return
|
||
offset = Gen._get_member_offset(AttrName, ClassName)
|
||
StructType = Gen.structs.get(ClassName)
|
||
if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0):
|
||
self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen)
|
||
StructType = Gen.structs.get(ClassName)
|
||
if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0):
|
||
return
|
||
if StructType is not None and StructType.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None:
|
||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(StructType), name=f"cast_{ClassName}")
|
||
if offset is not None and StructType is not None and StructType.elements is not None and offset < len(StructType.elements):
|
||
MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
else:
|
||
return
|
||
|
||
byte_order = getattr(Gen, 'class_member_byteorders', {}).get(ClassName, {}).get(AttrName, "")
|
||
|
||
if byte_order == 'big':
|
||
st = Gen.structs.get(ClassName)
|
||
if st is None or st.elements is None or len(st.elements) == 0:
|
||
return
|
||
member_type = st.elements[offset]
|
||
if isinstance(member_type, ir.IntType):
|
||
if member_type.width == 16:
|
||
Value = Gen.builder.bswap(Value, name=f"bswap_store_{AttrName}")
|
||
elif member_type.width == 32:
|
||
Value = Gen.builder.bswap(Value, name=f"bswap_store_{AttrName}")
|
||
elif member_type.width == 64:
|
||
Value = Gen.builder.bswap(Value, name=f"bswap_store_{AttrName}")
|
||
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
if not ClassName:
|
||
imported_modules = getattr(self.Trans, '_ImportedModules', None)
|
||
resolved_mod = self.Trans.SymbolTable.resolve_alias(VarName)
|
||
if imported_modules and (VarName in imported_modules or resolved_mod in imported_modules):
|
||
PossibleKeys = [f"{VarName}.{AttrName}", AttrName]
|
||
for mod_name in imported_modules:
|
||
if mod_name.endswith(f".{VarName}") or mod_name == VarName:
|
||
key = f"{mod_name}.{AttrName}"
|
||
if key not in PossibleKeys:
|
||
PossibleKeys.append(key)
|
||
for key in PossibleKeys:
|
||
if key in Gen.module.globals:
|
||
GVar = Gen.module.globals[key]
|
||
if isinstance(GVar, ir.Function):
|
||
return
|
||
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
return
|
||
self._StoreWithCoerce(Value, GVar)
|
||
return
|
||
ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {})
|
||
sha1_candidates = [VarName, resolved_mod]
|
||
for mod_name in imported_modules:
|
||
if mod_name.endswith(f".{VarName}") or mod_name == VarName:
|
||
if mod_name not in sha1_candidates:
|
||
sha1_candidates.append(mod_name)
|
||
for cand in sha1_candidates:
|
||
sha1 = ModuleSha1Map.get(cand)
|
||
if sha1:
|
||
prefixed = f"{sha1}.{AttrName}"
|
||
if prefixed in Gen.module.globals:
|
||
GVar2 = Gen.module.globals[prefixed]
|
||
if isinstance(GVar2, ir.Function):
|
||
return
|
||
if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
|
||
return
|
||
self._StoreWithCoerce(Value, GVar2)
|
||
return
|
||
elif isinstance(Target.value, ast.Attribute):
|
||
ObjVal = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
|
||
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
|
||
pointee = ObjVal.type.pointee
|
||
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None:
|
||
for CN, ST in Gen.structs.items():
|
||
if isinstance(ST, ir.IdentifiedStructType) and ST.name == pointee.name and ST.elements is not None:
|
||
pointee = ST
|
||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}")
|
||
break
|
||
found_match = False
|
||
for CN, ST in Gen.structs.items():
|
||
if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name):
|
||
found_match = True
|
||
if ST.elements is None:
|
||
self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen)
|
||
ST = Gen.structs.get(CN, ST)
|
||
if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None:
|
||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}")
|
||
bitfield_offsets = Gen.class_member_bitoffsets.get(CN, {})
|
||
if AttrName in bitfield_offsets:
|
||
self._store_bitfield_member(ObjVal, CN, AttrName, Value)
|
||
return
|
||
offset = Gen._get_member_offset(AttrName, CN)
|
||
if offset is not None and ST.elements is not None and offset < len(ST.elements):
|
||
MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
break
|
||
if not found_match:
|
||
if isinstance(pointee, ir.IdentifiedStructType):
|
||
matched_cn = None
|
||
for CN in Gen.class_members:
|
||
members = Gen.class_members[CN]
|
||
for mname, mtype in members:
|
||
if mname == AttrName:
|
||
matched_cn = CN
|
||
break
|
||
if matched_cn:
|
||
break
|
||
if matched_cn and matched_cn in Gen.structs:
|
||
ST = Gen.structs[matched_cn]
|
||
if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0):
|
||
self.Trans.ImportHandler._TryLoadStructFromStub(matched_cn, Gen)
|
||
ST = Gen.structs.get(matched_cn, ST)
|
||
if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None:
|
||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{matched_cn}")
|
||
offset = Gen._get_member_offset(AttrName, matched_cn)
|
||
if offset is not None and ST.elements is not None and offset < len(ST.elements):
|
||
MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
return
|
||
elif isinstance(Target.value, ast.Call):
|
||
# Check if this is c.Deref(...) — we need the pointer, not the Loaded value
|
||
ObjVal = None
|
||
is_cderef = (isinstance(Target.value.func, ast.Attribute) and
|
||
isinstance(Target.value.func.value, ast.Name) and
|
||
Target.value.func.value.id == 'c' and
|
||
Target.value.func.attr == 'Deref' and
|
||
Target.value.args)
|
||
if is_cderef:
|
||
# Get the pointer from c.Deref argument, don't Load the value
|
||
deref_arg = Target.value.args[0]
|
||
inner_val = self.HandleExprLlvm(deref_arg)
|
||
if inner_val and isinstance(inner_val.type, ir.PointerType):
|
||
if isinstance(inner_val.type.pointee, ir.PointerType):
|
||
ObjVal = Gen._load(inner_val, name="deref_load_ptr")
|
||
else:
|
||
ObjVal = inner_val
|
||
if ObjVal is None:
|
||
ObjVal = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
|
||
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
|
||
pointee = ObjVal.type.pointee
|
||
if isinstance(pointee, ir.PointerType):
|
||
ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr")
|
||
pointee = ObjVal.type.pointee
|
||
for CN, ST in Gen.structs.items():
|
||
if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name):
|
||
if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0):
|
||
self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen)
|
||
ST = Gen.structs.get(CN, ST)
|
||
if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None:
|
||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}")
|
||
bitfield_offsets = Gen.class_member_bitoffsets.get(CN, {})
|
||
if AttrName in bitfield_offsets:
|
||
self._store_bitfield_member(ObjVal, CN, AttrName, Value)
|
||
return
|
||
offset = Gen._get_member_offset(AttrName, CN)
|
||
if offset is not None and ST.elements is not None and offset < len(ST.elements):
|
||
MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
break
|
||
elif isinstance(Target.value, ast.Subscript):
|
||
SubPtr = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value)
|
||
if SubPtr and isinstance(SubPtr.type, ir.PointerType):
|
||
pointee = SubPtr.type.pointee
|
||
|
||
if isinstance(Target.value.value, ast.Attribute):
|
||
AttrNameSub = Target.value.value.attr
|
||
if AttrNameSub == 'items' and 'Item' in Gen.structs:
|
||
ObjValCast = Gen.builder.bitcast(SubPtr, ir.PointerType(Gen.structs['Item']), name=f"cast_Item")
|
||
offset = Gen._get_member_offset(AttrName, 'Item')
|
||
MemberPtr = Gen.builder.gep(ObjValCast, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
return
|
||
|
||
if BaseHandle._is_char_pointer(SubPtr):
|
||
for CN, ST in Gen.structs.items():
|
||
ObjValCast = Gen.builder.bitcast(SubPtr, ir.PointerType(ST), name=f"cast_{CN}")
|
||
offset = Gen._get_member_offset(AttrName, CN)
|
||
MemberPtr = Gen.builder.gep(ObjValCast, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
break
|
||
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||
found = Gen.find_struct_by_pointee(pointee)
|
||
if found:
|
||
CN, ST = found
|
||
if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0):
|
||
self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen)
|
||
ST = Gen.structs.get(CN, ST)
|
||
if ST.elements is not None and isinstance(SubPtr.type, ir.PointerType) and isinstance(SubPtr.type.pointee, ir.IdentifiedStructType) and SubPtr.type.pointee.elements is None:
|
||
SubPtr = Gen.builder.bitcast(SubPtr, ir.PointerType(ST), name=f"cast_{CN}")
|
||
offset = Gen._get_member_offset(AttrName, CN)
|
||
if offset is not None and ST.elements is not None and offset < len(ST.elements):
|
||
MemberPtr = Gen.builder.gep(SubPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
elif isinstance(pointee, ir.PointerType):
|
||
LoadedPtr = Gen._load(SubPtr, name=f"Load_{AttrName}_ptr")
|
||
if isinstance(LoadedPtr.type, ir.PointerType) and isinstance(LoadedPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||
found = Gen.find_struct_by_pointee(LoadedPtr.type.pointee)
|
||
if found:
|
||
CN, ST = found
|
||
offset = Gen._get_member_offset(AttrName, CN)
|
||
MemberPtr = Gen.builder.gep(LoadedPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||
self._StoreWithCoerce(Value, MemberPtr)
|
||
|
||
def _HandleSubscriptStoreLlvm(self, Target, Value):
|
||
Gen = self.Trans.LlvmGen
|
||
if isinstance(Target.value, ast.Name):
|
||
SubVarName = Target.value.id
|
||
if SubVarName in Gen.var_const_flags:
|
||
src_info = Gen._get_node_info()
|
||
self.Trans.LogError(f"不能对 const 变量 '{SubVarName}' 的元素赋值{src_info}")
|
||
ClassName = self.Trans.ExprHandler._get_var_class(Target.value, Gen)
|
||
if ClassName and Gen._has_function(f'{ClassName}.__setitem__'):
|
||
obj_val = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
|
||
idx_val = self.Trans.ExprHandler.HandleExprLlvm(Target.slice)
|
||
if obj_val and idx_val:
|
||
if BaseHandle._is_char_pointer(obj_val):
|
||
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
|
||
Gen.builder.call(Gen._get_function(f'{ClassName}.__setitem__'), [obj_val, idx_val, Value], name=f"call_{ClassName}.__setitem__")
|
||
return
|
||
ValueVal = self.HandleExprLlvm(Target.value)
|
||
IndexVal = self.HandleExprLlvm(Target.slice)
|
||
if not ValueVal or not IndexVal:
|
||
return
|
||
if BaseHandle._is_char_pointer(IndexVal):
|
||
Loaded_byte = Gen._load(IndexVal, name="Load_char_idx_store")
|
||
IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx_store")
|
||
if isinstance(ValueVal.type, ir.IntType):
|
||
var_ptr = self._get_int_ptr(Target.value)
|
||
if var_ptr:
|
||
i8_ptr = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr_store")
|
||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index_store")
|
||
ptr = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr_store")
|
||
self._StoreWithCoerce(Value, ptr)
|
||
return
|
||
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
|
||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
|
||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store")
|
||
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
|
||
self._StoreWithCoerce(Value, ElemPtr)
|
||
return
|
||
if isinstance(ValueVal.type, ir.PointerType):
|
||
pointee = ValueVal.type.pointee
|
||
if isinstance(pointee, ir.ArrayType):
|
||
zero = ir.Constant(ir.IntType(32), 0)
|
||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32:
|
||
if IndexVal.type.width < 32:
|
||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||
else:
|
||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript")
|
||
else:
|
||
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
|
||
self._StoreWithCoerce(Value, ElemPtr)
|
||
elif isinstance(ValueVal.type, ir.ArrayType):
|
||
zero = ir.Constant(ir.IntType(32), 0)
|
||
if isinstance(Target.value, ast.Name):
|
||
VarName = Target.value.id
|
||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||
VarPtr = Gen.variables[VarName]
|
||
if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType):
|
||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32:
|
||
if IndexVal.type.width < 32:
|
||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||
else:
|
||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript")
|
||
self._StoreWithCoerce(Value, ElemPtr)
|
||
return
|
||
if isinstance(Target.value, ast.Subscript):
|
||
InnerPtr = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value)
|
||
if InnerPtr:
|
||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32:
|
||
if IndexVal.type.width < 32:
|
||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||
else:
|
||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript")
|
||
self._StoreWithCoerce(Value, ElemPtr)
|
||
return
|
||
if isinstance(Target.value, ast.Attribute):
|
||
AttrPtr = self.Trans.ExprAttrHandle._get_attr_ptr(Target.value)
|
||
if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType):
|
||
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32:
|
||
if IndexVal.type.width < 32:
|
||
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
|
||
else:
|
||
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
|
||
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript")
|
||
self._StoreWithCoerce(Value, ElemPtr)
|
||
return
|
||
arr_alloc = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
|
||
Gen._store(ValueVal, arr_alloc)
|
||
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript")
|
||
self._StoreWithCoerce(Value, ElemPtr)
|
||
ModifiedArr = Gen._load(arr_alloc, name="modified_arr")
|
||
if isinstance(Target.value, ast.Subscript):
|
||
OuterPtr = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value)
|
||
if OuterPtr:
|
||
Gen._store(ModifiedArr, OuterPtr)
|
||
elif isinstance(Target.value, ast.Attribute):
|
||
OuterPtr = self.Trans.ExprAttrHandle._get_attr_ptr(Target.value)
|
||
if OuterPtr:
|
||
Gen._store(ModifiedArr, OuterPtr)
|
||
|
||
def _StoreWithCoerce(self, Value, Ptr):
|
||
Gen = self.Trans.LlvmGen
|
||
if isinstance(Ptr.type, ir.PointerType):
|
||
TargetType = Ptr.type.pointee
|
||
if Value.type != TargetType:
|
||
if isinstance(TargetType, ir.ArrayType) and isinstance(Value.type, ir.PointerType):
|
||
ElemType = TargetType.element
|
||
ElemSize = Gen._get_struct_size(ElemType)
|
||
for i in range(TargetType.count):
|
||
DstElem = Gen.builder.gep(Ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)], name=f"arr_dst_{i}")
|
||
if ElemSize == 1:
|
||
SrcPtr = Gen.builder.gep(Value, [ir.Constant(ir.IntType(32), i)], name=f"arr_src_{i}")
|
||
else:
|
||
PtrInt = Gen.builder.ptrtoint(Value, ir.IntType(64))
|
||
Offset = ir.Constant(ir.IntType(64), i * ElemSize)
|
||
NewPtrInt = Gen.builder.add(PtrInt, Offset)
|
||
SrcPtr = Gen.builder.inttoptr(NewPtrInt, ir.PointerType(ElemType), name=f"arr_src_{i}")
|
||
SrcVal = Gen._load(SrcPtr, name=f"arr_elem_{i}")
|
||
if SrcVal.type != ElemType:
|
||
try:
|
||
SrcVal = Gen.builder.bitcast(SrcVal, ElemType, name=f"arr_cast_{i}")
|
||
except Exception: # 回退:bitcast 失败时跳过该元素
|
||
continue
|
||
Gen._store(SrcVal, DstElem)
|
||
return
|
||
if isinstance(TargetType, ir.PointerType) and isinstance(Value, ir.Constant) and Value.constant is None:
|
||
Value = ir.Constant(TargetType, None)
|
||
elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.PointerType):
|
||
try:
|
||
Value = Gen.builder.bitcast(Value, TargetType, name="null_ptr_bitcast")
|
||
except Exception: # 回退:bitcast 失败时设为 null 常量
|
||
Value = ir.Constant(TargetType, None)
|
||
else:
|
||
Coerced = Gen._coerce_value(Value, TargetType)
|
||
if Coerced is not None:
|
||
Value = Coerced
|
||
Gen._store(Value, Ptr)
|
||
|
||
|
||
def _get_int_ptr(self, target):
|
||
Gen = self.Trans.LlvmGen
|
||
if isinstance(target, ast.Name):
|
||
VarName = target.id
|
||
if VarName in Gen.variables and Gen.variables[VarName] is not None:
|
||
VarPtr = Gen.variables[VarName]
|
||
if isinstance(VarPtr.type, ir.PointerType):
|
||
pointee = VarPtr.type.pointee
|
||
if isinstance(pointee, ir.ArrayType):
|
||
return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}")
|
||
return None
|
||
|
||
def _EmitGlobalAnnAssignLlvm(self, Node, Gen):
|
||
if isinstance(Node.target, ast.Name):
|
||
VarName = Node.target.id
|
||
EffectiveAnnotation = Node.annotation
|
||
if self.Trans.SymbolTable.has(VarName):
|
||
Info = self.Trans.SymbolTable[VarName]
|
||
if Info.get('type') == 'typedef':
|
||
return
|
||
if isinstance(EffectiveAnnotation, ast.BinOp) and isinstance(EffectiveAnnotation.op, ast.BitOr):
|
||
for side in [EffectiveAnnotation.left, EffectiveAnnotation.right]:
|
||
if isinstance(side, ast.Subscript) and isinstance(side.value, ast.Name) and side.value.id == 'list':
|
||
EffectiveAnnotation = side
|
||
break
|
||
if isinstance(EffectiveAnnotation, ast.Subscript) and isinstance(EffectiveAnnotation.value, ast.Name) and EffectiveAnnotation.value.id == 'list':
|
||
slice_node = EffectiveAnnotation.slice
|
||
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
|
||
elem_type_node = slice_node.elts[0]
|
||
count_node = slice_node.elts[1]
|
||
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
||
SymEntry = self.Trans.SymbolTable.lookup(elem_type_node.id)
|
||
if SymEntry and isinstance(SymEntry, CTypeInfo) and SymEntry.IsFuncPtr:
|
||
ElemType = ir.IntType(8).as_pointer()
|
||
else:
|
||
ElemType = Gen.structs[elem_type_node.id]
|
||
else:
|
||
ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable)
|
||
if ElemTypeInfo and ElemTypeInfo.IsFuncPtr:
|
||
ElemType = ir.IntType(8).as_pointer()
|
||
else:
|
||
if ElemTypeInfo is None:
|
||
ElemTypeInfo = CTypeInfo()
|
||
ElemTypeInfo.BaseType = t.CInt()
|
||
ElemType = Gen._ctype_to_llvm(ElemTypeInfo)
|
||
if isinstance(ElemType, ir.VoidType):
|
||
ElemType = ir.IntType(32)
|
||
ArrayCount = 1
|
||
if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int):
|
||
ArrayCount = count_node.value
|
||
elif isinstance(count_node, ast.Name):
|
||
if self.Trans.SymbolTable.has(count_node.id):
|
||
SymInfo = self.Trans.SymbolTable[count_node.id]
|
||
if isinstance(SymInfo.value, int):
|
||
ArrayCount = SymInfo.value
|
||
if ArrayCount == 1 and count_node.id in getattr(Gen, '_define_constants', {}):
|
||
DefVal = Gen._define_constants[count_node.id]
|
||
if isinstance(DefVal, int):
|
||
ArrayCount = DefVal
|
||
elif isinstance(count_node, ast.Attribute):
|
||
ev = self._eval_global_count(count_node, Gen)
|
||
if ev is not None:
|
||
ArrayCount = ev
|
||
elif isinstance(count_node, ast.BinOp):
|
||
ev = self._eval_global_count(count_node, Gen)
|
||
if ev is not None:
|
||
ArrayCount = ev
|
||
elif count_node is None or (isinstance(count_node, ast.Constant) and count_node.value is None):
|
||
if Node.value and isinstance(Node.value, ast.List):
|
||
ArrayCount = len(Node.value.elts)
|
||
elif Node.value and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str):
|
||
ArrayCount = len(Node.value.value) + 1
|
||
VarType = ir.ArrayType(ElemType, ArrayCount)
|
||
if VarName not in Gen.variables and VarName not in Gen.module.globals:
|
||
GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
||
if isinstance(ElemType, ir.IdentifiedStructType):
|
||
GlobalVar.linkage = 'common'
|
||
elif Node.value and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str):
|
||
str_val = Node.value.value + '\x00'
|
||
str_bytes = bytearray(str_val, 'utf-8')
|
||
if len(str_bytes) < ArrayCount:
|
||
str_bytes.extend(b'\x00' * (ArrayCount - len(str_bytes)))
|
||
try:
|
||
GlobalVar.initializer = ir.Constant(VarType, str_bytes[:ArrayCount])
|
||
except Exception: # 回退:初始化常量失败时用零值
|
||
GlobalVar.initializer = self._zero_value(VarType)
|
||
else:
|
||
InitConstants = self.Trans._BuildArrayInitConstants(Node.value, ElemType, elem_type_node, ArrayCount, Gen)
|
||
if InitConstants:
|
||
GlobalVar.initializer = ir.Constant(VarType, InitConstants)
|
||
elif ArrayCount > 256:
|
||
GlobalVar.linkage = 'common'
|
||
else:
|
||
if self._contains_identified_struct(VarType):
|
||
GlobalVar.linkage = 'common'
|
||
else:
|
||
GlobalVar.initializer = ir.Constant(VarType, [ir.Constant(ElemType, ir.Undefined)] * ArrayCount)
|
||
Gen.variables[VarName] = GlobalVar
|
||
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
||
Gen.global_struct_class[VarName] = elem_type_node.id
|
||
return
|
||
if isinstance(EffectiveAnnotation, ast.Subscript) and isinstance(EffectiveAnnotation.value, ast.Name) and EffectiveAnnotation.value.id == 'list':
|
||
slice_node = EffectiveAnnotation.slice
|
||
elem_type_node = slice_node
|
||
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
||
ElemType = Gen.structs[elem_type_node.id]
|
||
else:
|
||
ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable)
|
||
if ElemTypeInfo is None:
|
||
ElemTypeInfo = CTypeInfo()
|
||
ElemTypeInfo.BaseType = t.CInt()
|
||
ElemType = Gen._ctype_to_llvm(ElemTypeInfo)
|
||
if isinstance(ElemType, ir.VoidType):
|
||
ElemType = ir.IntType(32)
|
||
VarType = ir.PointerType(ElemType)
|
||
if VarName not in Gen.variables and VarName not in Gen.module.globals:
|
||
GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
||
GlobalVar.initializer = ir.Constant(VarType, None)
|
||
Gen.variables[VarName] = GlobalVar
|
||
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
|
||
Gen.global_struct_class[VarName] = elem_type_node.id
|
||
return
|
||
if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs:
|
||
StructName = Node.annotation.id
|
||
VarType = Gen.structs[StructName]
|
||
if VarName not in Gen.variables and VarName not in Gen.module.globals:
|
||
GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
||
if Node.value and isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == StructName:
|
||
const = self.Trans.ClassHandler._BuildStructConstant(Node.value, StructName, Gen)
|
||
if const:
|
||
GlobalVar.initializer = const
|
||
else:
|
||
GlobalVar.initializer = ir.Constant(VarType, None)
|
||
else:
|
||
GlobalVar.initializer = ir.Constant(VarType, None)
|
||
Gen.variables[VarName] = GlobalVar
|
||
Gen.global_struct_class[VarName] = StructName
|
||
return
|
||
TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
|
||
if not TypeInfo or not TypeInfo.BaseType:
|
||
ArrayTypeInfo = CTypeInfo._HandleArraySubscript(Node.annotation, self.Trans.SymbolTable)
|
||
if ArrayTypeInfo:
|
||
TypeInfo = ArrayTypeInfo
|
||
IsCDefine = False
|
||
if TypeInfo and TypeInfo.BaseType:
|
||
if isinstance(TypeInfo.BaseType, t._CTypedef):
|
||
return
|
||
if isinstance(TypeInfo.BaseType, t.CDefine):
|
||
IsCDefine = True
|
||
if not IsCDefine and self._IsTypedefAnnotation(Node.annotation):
|
||
IsCDefine = True
|
||
TTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation)
|
||
if TTypeInfo:
|
||
TTypeInfo.IsTypedef = True
|
||
TTypeInfo.Name = VarName
|
||
self.Trans.SymbolTable.insert(VarName, TTypeInfo)
|
||
if IsCDefine:
|
||
if Node.value:
|
||
define_constants = vars(Gen).setdefault('_define_constants', {})
|
||
try:
|
||
const_value = self._eval_const_expr(Node.value, Gen)
|
||
if const_value is not None:
|
||
define_constants[VarName] = const_value
|
||
except Exception as _e:
|
||
if __import__('lib.constants.config', fromlist=['mode']).mode == "strict":
|
||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||
return
|
||
if TypeInfo is None:
|
||
TypeInfo = CTypeInfo()
|
||
TypeInfo.BaseType = t.CInt()
|
||
VarType = Gen._ctype_to_llvm(TypeInfo)
|
||
if isinstance(VarType, ir.VoidType):
|
||
VarType = ir.IntType(32)
|
||
if VarName in Gen.variables or VarName in Gen.module.globals:
|
||
return
|
||
if isinstance(VarType, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||
GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
||
GlobalVar.initializer = ir.Constant(VarType, None)
|
||
Gen.variables[VarName] = GlobalVar
|
||
if TypeInfo and TypeInfo.IsStruct and TypeInfo.Name:
|
||
Gen.global_struct_class[VarName] = TypeInfo.Name
|
||
return
|
||
GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
||
if Node.value:
|
||
if isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str) and isinstance(VarType, ir.PointerType):
|
||
str_val = Node.value.value + '\x00'
|
||
str_bytes = str_val.encode('utf-8')
|
||
arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes))
|
||
str_gv_name = VarName + '_str'
|
||
if str_gv_name not in Gen.module.globals:
|
||
str_gv = ir.GlobalVariable(Gen.module, arr_type, name=str_gv_name)
|
||
str_gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
|
||
str_gv.linkage = 'internal'
|
||
else:
|
||
str_gv = Gen.module.globals[str_gv_name]
|
||
ptr_type = ir.PointerType(ir.IntType(8))
|
||
if VarName not in Gen.module.globals:
|
||
ptr_gv = ir.GlobalVariable(Gen.module, ptr_type, name=VarName)
|
||
ptr_gv.initializer = ir.Constant(ptr_type, str_gv)
|
||
ptr_gv.linkage = 'internal'
|
||
else:
|
||
ptr_gv = Gen.module.globals[VarName]
|
||
Gen.variables[VarName] = ptr_gv
|
||
else:
|
||
if isinstance(VarType, ir.ArrayType) and isinstance(Node.value, (ast.List, ast.Set)):
|
||
InitConstants = self.Trans._BuildMultiDimArrayInitConstants(Node.value, VarType, Gen)
|
||
if InitConstants:
|
||
GlobalVar.initializer = ir.Constant(VarType, InitConstants)
|
||
else:
|
||
if self._contains_identified_struct(VarType):
|
||
GlobalVar.linkage = 'common'
|
||
else:
|
||
zv = self._zero_value(VarType)
|
||
if zv:
|
||
GlobalVar.initializer = zv
|
||
else:
|
||
GlobalVar.linkage = 'common'
|
||
else:
|
||
init_val = self._eval_global_const(Node.value, VarType, Gen)
|
||
if init_val:
|
||
GlobalVar.initializer = init_val
|
||
elif isinstance(VarType, ir.BaseStructType):
|
||
GlobalVar.linkage = 'common'
|
||
else:
|
||
GlobalVar.initializer = self._zero_value(VarType)
|
||
else:
|
||
if isinstance(VarType, ir.BaseStructType) or self._contains_identified_struct(VarType):
|
||
GlobalVar.linkage = 'common'
|
||
else:
|
||
GlobalVar.initializer = self._zero_value(VarType)
|
||
Gen.variables[VarName] = GlobalVar
|
||
|
||
def _eval_global_const(self, value_node, var_type, Gen):
|
||
if isinstance(value_node, ast.Constant):
|
||
if isinstance(value_node.value, bool):
|
||
if isinstance(var_type, ir.IntType):
|
||
return ir.Constant(var_type, int(value_node.value))
|
||
elif isinstance(value_node.value, int):
|
||
if isinstance(var_type, ir.IntType):
|
||
return ir.Constant(var_type, value_node.value)
|
||
elif isinstance(var_type, (ir.FloatType, ir.DoubleType)):
|
||
return ir.Constant(var_type, float(value_node.value))
|
||
elif isinstance(value_node.value, float):
|
||
if isinstance(var_type, (ir.FloatType, ir.DoubleType)):
|
||
return ir.Constant(var_type, value_node.value)
|
||
elif isinstance(var_type, ir.IntType):
|
||
return ir.Constant(var_type, int(value_node.value))
|
||
elif isinstance(value_node.value, str):
|
||
return None
|
||
# Handle type constructor calls like t.CDouble(1e-9), t.CFloat(3.14), t.CInt(42)
|
||
if isinstance(value_node, ast.Call):
|
||
inner_val = self._eval_type_ctor_const(value_node, var_type, Gen)
|
||
if inner_val is not None:
|
||
return inner_val
|
||
if isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.USub):
|
||
inner = self._eval_global_const(value_node.operand, var_type, Gen)
|
||
if inner:
|
||
return ir.Constant(var_type, -inner.constant)
|
||
return None
|
||
|
||
def _eval_type_ctor_const(self, call_node, var_type, Gen):
|
||
"""Try to evaluate t.CDouble(val), t.CFloat(val), t.CInt(val) etc. as a compile-time constant."""
|
||
if not isinstance(call_node.func, ast.Attribute):
|
||
return None
|
||
if not isinstance(call_node.func.value, ast.Name):
|
||
return None
|
||
if call_node.func.value.id != 't':
|
||
return None
|
||
if not call_node.args or len(call_node.args) != 1:
|
||
return None
|
||
arg = call_node.args[0]
|
||
# Recursively evaluate the argument as a constant
|
||
arg_val = self._eval_global_const(arg, var_type, Gen)
|
||
if arg_val is not None:
|
||
return arg_val
|
||
return None
|
||
|
||
def _store_bitfield_member(self, struct_ptr, ClassName, field_name, Value):
|
||
Gen = self.Trans.LlvmGen
|
||
bitfield_offsets = Gen.class_member_bitoffsets.get(ClassName, {})
|
||
bitfield_widths = Gen.class_member_bitfields.get(ClassName, {})
|
||
if field_name not in bitfield_offsets:
|
||
return False
|
||
bit_offset = bitfield_offsets[field_name]
|
||
bit_width = bitfield_widths.get(field_name, 0)
|
||
if bit_width == 0:
|
||
return False
|
||
struct_type = Gen.structs.get(ClassName)
|
||
if struct_type is None or struct_type.elements is None or len(struct_type.elements) == 0:
|
||
return False
|
||
storage_type = struct_type.elements[0]
|
||
member_ptr = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{field_name}_storage_ptr")
|
||
old_val = Gen._load(member_ptr, name=f"{field_name}_old_storage")
|
||
mask = (1 << bit_width) - 1
|
||
shifted_mask = mask << bit_offset
|
||
inverted_mask = ir.Constant(storage_type, ~shifted_mask & ((1 << storage_type.width) - 1))
|
||
cleared = Gen.builder.and_(old_val, inverted_mask, name=f"{field_name}_cleared")
|
||
if isinstance(Value.type, ir.IntType) and Value.type.width < storage_type.width:
|
||
if getattr(Value.type, 'is_signed', False):
|
||
Value = Gen.builder.sext(Value, storage_type, name=f"{field_name}_sext_store")
|
||
else:
|
||
Value = Gen.builder.zext(Value, storage_type, name=f"{field_name}_zext_store")
|
||
elif Value.type.width != storage_type.width:
|
||
Value = Gen.builder.trunc(Value, storage_type, name=f"{field_name}_trunc_store") if Value.type.width > storage_type.width else Gen.builder.zext(Value, storage_type, name=f"{field_name}_zext_store")
|
||
shifted_value = Gen.builder.shl(Value, ir.Constant(storage_type, bit_offset), name=f"{field_name}_shift_store")
|
||
masked_value = Gen.builder.and_(shifted_value, ir.Constant(storage_type, shifted_mask), name=f"{field_name}_mask_store")
|
||
new_val = Gen.builder.or_(cleared, masked_value, name=f"{field_name}_new_storage")
|
||
Gen._store(new_val, member_ptr)
|
||
return True
|
||
|
||
def _CheckEnumMemberAssignment(self, node):
|
||
def _get_attr_path(n):
|
||
if isinstance(n, ast.Name):
|
||
return n.id
|
||
elif isinstance(n, ast.Attribute):
|
||
return f"{_get_attr_path(n.value)}.{n.attr}"
|
||
return None
|
||
attr_path = _get_attr_path(node)
|
||
if not attr_path:
|
||
return None
|
||
parts = attr_path.split('.')
|
||
if len(parts) >= 2:
|
||
enum_class_name = parts[-2]
|
||
member_name = parts[-1]
|
||
qualified_name = f"{enum_class_name}.{member_name}"
|
||
if self.Trans.SymbolTable.has(qualified_name):
|
||
info = self.Trans.SymbolTable[qualified_name]
|
||
if info.IsEnumMember and info.EnumName:
|
||
return info.EnumName
|
||
under_name = f"{enum_class_name}_{member_name}"
|
||
if self.Trans.SymbolTable.has(under_name):
|
||
info = self.Trans.SymbolTable[under_name]
|
||
if info.IsEnumMember and info.EnumName:
|
||
return info.EnumName
|
||
for key in self.Trans.SymbolTable:
|
||
if key == member_name:
|
||
info = self.Trans.SymbolTable[key]
|
||
if info.IsEnumMember and info.EnumName and info.EnumName == enum_class_name:
|
||
return enum_class_name
|
||
return None
|
||
|
||
def _IsTypedefAnnotation(self, annotation):
|
||
if isinstance(annotation, ast.Attribute):
|
||
if annotation.attr in ('CTypedef', 'CDefine'):
|
||
return True
|
||
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
|
||
return self._IsTypedefAnnotation(annotation.left) or self._IsTypedefAnnotation(annotation.right)
|
||
if isinstance(annotation, ast.Subscript):
|
||
return self._IsTypedefAnnotation(annotation.value)
|
||
return False
|
||
|
||
def _EmitGlobalAssignLlvm(self, Node, Gen):
|
||
if len(Node.targets) == 1 and isinstance(Node.targets[0], ast.Name):
|
||
VarName = Node.targets[0].id
|
||
if VarName in Gen.variables or VarName in Gen.module.globals:
|
||
return
|
||
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
|
||
FuncName = Node.value.func.id
|
||
if FuncName in Gen.structs:
|
||
IsUnion = False
|
||
if self.Trans.SymbolTable.has(FuncName):
|
||
TypeInfo = self.Trans.SymbolTable[FuncName]
|
||
if TypeInfo.IsUnion:
|
||
IsUnion = True
|
||
if IsUnion:
|
||
StructType = Gen.structs[FuncName]
|
||
StructPtrType = ir.PointerType(StructType)
|
||
GlobalVar = ir.GlobalVariable(Gen.module, StructPtrType, name=VarName)
|
||
GlobalVar.initializer = ir.Constant(StructPtrType, None)
|
||
Gen.variables[VarName] = GlobalVar
|
||
Gen.global_struct_class[VarName] = FuncName
|
||
return
|
||
StructName = FuncName
|
||
VarType = Gen.structs[StructName]
|
||
GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName)
|
||
const = self.Trans.ClassHandler._BuildStructConstant(Node.value, StructName, Gen)
|
||
if const:
|
||
GlobalVar.initializer = const
|
||
else:
|
||
GlobalVar.linkage = 'common'
|
||
Gen.variables[VarName] = GlobalVar
|
||
Gen.global_struct_class[VarName] = StructName
|
||
return
|
||
GlobalVar = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName)
|
||
init_val = self._eval_global_const(Node.value, ir.IntType(32), Gen)
|
||
GlobalVar.initializer = init_val if init_val else ir.Constant(ir.IntType(32), 0)
|
||
Gen.variables[VarName] = GlobalVar
|
||
|
||
def _eval_const_expr(self, node, Gen):
|
||
"""计算常量表达式的值(委托到 ConstEvaluator)"""
|
||
from lib.core.ConstEvaluator import ConstEvaluator, EvalContext
|
||
ctx = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable)
|
||
return ConstEvaluator.eval_full(node, ctx)
|
||
|
||
def _eval_global_count(self, node, Gen):
|
||
val = self._eval_const_expr(node, Gen)
|
||
if val is not None:
|
||
if isinstance(val, float):
|
||
return int(val)
|
||
if isinstance(val, int):
|
||
return val
|
||
return None
|