417 lines
18 KiB
Python
417 lines
18 KiB
Python
from __future__ import annotations
|
||
import re
|
||
import ast
|
||
import os
|
||
import hashlib
|
||
import llvmlite.ir as ir
|
||
from llvmlite.ir.values import _Undefined
|
||
|
||
|
||
class VaArgInstruction(ir.Instruction):
|
||
def descr(self, buf):
|
||
ptr = self.operands[0]
|
||
buf.append("va_arg {0} {1}, {2}\n".format(
|
||
ptr.type, ptr.get_reference(), self.type))
|
||
|
||
|
||
class BaseGenMixin:
|
||
|
||
@staticmethod
|
||
def _parse_triple(triple):
|
||
"""解析目标三元组,返回平台信息字典"""
|
||
info = {
|
||
'is_windows': False, 'is_linux': False, 'is_macos': False,
|
||
'is_x86_64': False, 'is_arm64': False, 'is_32bit': False,
|
||
'ptr_size': 8, 'long_size': 8, 'wchar_t_size': 32,
|
||
}
|
||
if not triple:
|
||
return info
|
||
triple_lower = triple.lower()
|
||
# 检测操作系统
|
||
if 'windows' in triple_lower or 'win32' in triple_lower or 'mingw' in triple_lower or 'msvc' in triple_lower or 'cygwin' in triple_lower:
|
||
info['is_windows'] = True
|
||
# Windows LLP64: long = 32 位, wchar_t = 16 位
|
||
info['long_size'] = 32
|
||
info['wchar_t_size'] = 16
|
||
elif 'linux' in triple_lower:
|
||
info['is_linux'] = True
|
||
elif 'darwin' in triple_lower or 'macos' in triple_lower or 'apple' in triple_lower:
|
||
info['is_macos'] = True
|
||
# 检测架构
|
||
if 'x86_64' in triple_lower or 'amd64' in triple_lower or 'x64' in triple_lower:
|
||
info['is_x86_64'] = True
|
||
elif 'aarch64' in triple_lower or 'arm64' in triple_lower:
|
||
info['is_arm64'] = True
|
||
elif 'i386' in triple_lower or 'i686' in triple_lower or 'arm' in triple_lower:
|
||
info['is_32bit'] = True
|
||
info['ptr_size'] = 4
|
||
info['long_size'] = 32
|
||
if info['is_windows']:
|
||
info['wchar_t_size'] = 16
|
||
else:
|
||
info['wchar_t_size'] = 32
|
||
return info
|
||
|
||
def __init__(self, triple=None, datalayout=None):
|
||
self.module = ir.Module(name="transpyc_module")
|
||
if triple:
|
||
self.module.triple = triple
|
||
else:
|
||
self.module.triple = "x86_64-none-elf"
|
||
if datalayout:
|
||
self.module.data_layout = datalayout
|
||
else:
|
||
self.module.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||
|
||
# 解析目标平台信息
|
||
self._platform_info = self._parse_triple(self.module.triple)
|
||
self.ptr_size = self._platform_info['ptr_size']
|
||
# 根据目标平台配置 t 模块中的平台相关类型大小
|
||
import t as _t
|
||
_t.configure_platform(self.module.triple)
|
||
self.builder = None
|
||
self.func = None
|
||
self.functions = {}
|
||
self.variables = {}
|
||
self._direct_values = {}
|
||
self._reg_values = {}
|
||
self.var_signedness = {}
|
||
self._unsigned_results = {}
|
||
self.string_const_counter = 0
|
||
self.Vtables = {}
|
||
self.class_methods = {}
|
||
self.class_members = {}
|
||
self.class_member_defaults = {}
|
||
self.class_member_signeds = {}
|
||
self.class_member_bitfields = {}
|
||
self.class_member_byteorders = {}
|
||
self.class_member_element_class = {}
|
||
self.class_member_bitoffsets = {}
|
||
self.structs = {}
|
||
self.typedef_int_widths = {} # typedef名称 -> 底层整数位宽 (如 'UINT' -> 32, 'BYTE' -> 8)
|
||
self._cross_module_vtable_classes = set()
|
||
self.class_vtable = set()
|
||
self.class_parent = {}
|
||
# Store member annotations for deferred type resolution (cross-module class references)
|
||
self.class_member_annotations = {}
|
||
self.loop_break_targets = []
|
||
self.loop_continue_targets = []
|
||
self.eh_jmp_buf_stack = []
|
||
self.eh_except_block_stack = []
|
||
self.eh_finally_stack = []
|
||
self.global_vars = set()
|
||
self.nonlocal_params = {}
|
||
self._variable_scope_stack = [] # Stack of outer scope variables for nested function nonlocal lookup
|
||
self.var_struct_class = {}
|
||
self.global_struct_class = {}
|
||
self.var_type_info = {}
|
||
self.var_const_flags = {}
|
||
self._current_source_file = None
|
||
self._current_source_lines = []
|
||
self._ns_cache = {}
|
||
self._type_cache = {}
|
||
self._typedef_cache = {}
|
||
self._func_sig_cache = {}
|
||
self.var_type_assignments = {}
|
||
self._temp_struct_ptrs = []
|
||
self._stop_iter_flag_param = None
|
||
self._local_heap_ptrs = []
|
||
self._var_to_heap_ptr = {}
|
||
self.known_return_types = {}
|
||
self._current_lineno = 0
|
||
self._current_node_info = ""
|
||
self.module_sha1 = None
|
||
self.ModuleSha1Map = {}
|
||
self._temp_dir = None
|
||
self._export_funcs = set()
|
||
self._export_extern_funcs = set()
|
||
self._current_module_func_names = set()
|
||
self.class_packed = set()
|
||
self._define_constants = {}
|
||
pi = self._platform_info
|
||
# 根据目标平台设置宏(声明式,从三元组推导)
|
||
if pi['is_windows']:
|
||
self._define_constants['_WIN32'] = 1
|
||
if not pi['is_32bit']:
|
||
self._define_constants['_WIN64'] = 1
|
||
self._define_constants['WIN64'] = 1
|
||
self._define_constants['WIN32'] = 1
|
||
if pi['is_linux']:
|
||
self._define_constants['__linux__'] = 1
|
||
if pi['is_macos']:
|
||
self._define_constants['__APPLE__'] = 1
|
||
self._define_constants['__MACH__'] = 1
|
||
if pi['is_x86_64']:
|
||
self._define_constants['__x86_64__'] = 1
|
||
self._define_constants['__x86_64'] = 1
|
||
self._define_constants['_M_X64'] = 1
|
||
elif pi['is_arm64']:
|
||
self._define_constants['__aarch64__'] = 1
|
||
self._define_constants['_M_ARM64'] = 1
|
||
if not pi['is_32bit'] and pi['is_linux']:
|
||
self._define_constants['__LP64__'] = 1
|
||
elif pi['is_32bit']:
|
||
self._define_constants['_ILP32'] = 1
|
||
self._define_constants['SIZEOF_VOID_P'] = pi['ptr_size']
|
||
self._define_constants['__SIZEOF_POINTER__'] = pi['ptr_size']
|
||
|
||
def find_struct_by_pointee(self, pointee):
|
||
"""根据 pointee 类型查找匹配的结构体,返回 (class_name, struct_type) 或 None"""
|
||
for class_name, struct_type in self.structs.items():
|
||
if pointee == struct_type:
|
||
return (class_name, struct_type)
|
||
return None
|
||
|
||
def _get_or_create_struct(self, name, source_sha1=None, packed=False):
|
||
clean_name = name.replace(' *', '').replace('*', '').replace(' ', '_').replace('const', 'const_').replace('volatile', 'volatile_')
|
||
if clean_name in self.structs:
|
||
existing = self.structs[clean_name]
|
||
if packed and isinstance(existing, ir.IdentifiedStructType) and not existing.packed:
|
||
existing.packed = True
|
||
return existing
|
||
if clean_name in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum', 'Void', 'void'}:
|
||
return ir.IntType(8)
|
||
if hasattr(self, 'SymbolTable') and self.SymbolTable and clean_name in self.SymbolTable:
|
||
Entry = self.SymbolTable[clean_name]
|
||
if Entry.IsTypedef:
|
||
resolved_str = self._resolve_typedef(clean_name)
|
||
if resolved_str != clean_name:
|
||
return self._type_str_to_llvm(resolved_str)
|
||
if Entry.BaseType:
|
||
from lib.includes import t as _t
|
||
if not isinstance(Entry.BaseType, (_t._CTypedef,)) or Entry.PtrCount > 0:
|
||
resolved = self._resolve_ctype_to_str(Entry)
|
||
if resolved:
|
||
return self._type_str_to_llvm(resolved)
|
||
if Entry.IsRenum:
|
||
if clean_name in self.structs:
|
||
return self.structs[clean_name]
|
||
if Entry.IsEnum:
|
||
return ir.IntType(32)
|
||
if Entry.IsExceptionClass:
|
||
return ir.IntType(32)
|
||
if Entry.IsFunction:
|
||
return ir.PointerType(ir.IntType(8))
|
||
if Entry.IsVariable:
|
||
return ir.PointerType(ir.IntType(8))
|
||
if Entry.BaseType:
|
||
from lib.includes import t as _t
|
||
if isinstance(Entry.BaseType, type) and issubclass(Entry.BaseType, _t.CEnum):
|
||
return ir.IntType(32)
|
||
if Entry.BaseType is _t.CEnum:
|
||
return ir.IntType(32)
|
||
if source_sha1:
|
||
ns_name = f"{source_sha1}.{clean_name}"
|
||
elif hasattr(self, '_struct_sha1_map') and clean_name in self._struct_sha1_map:
|
||
ns_name = f"{self._struct_sha1_map[clean_name]}.{clean_name}"
|
||
else:
|
||
ns_name = self._mangle_name(clean_name)
|
||
st = ir.IdentifiedStructType(self.module, name=ns_name, packed=packed)
|
||
self.structs[clean_name] = st
|
||
# 尝试解析 typedef 的底层整数位宽
|
||
width = self._resolve_typedef_int_width(clean_name)
|
||
if width is not None:
|
||
self.typedef_int_widths[clean_name] = width
|
||
return st
|
||
|
||
def _resolve_typedef_int_width(self, name: str):
|
||
"""解析 typedef 名称的底层整数位宽
|
||
|
||
通过 CTypeRegistry 和 BuiltinTypeMap 动态解析,
|
||
替代硬编码的 TYPEDEF_NAMES 映射表。
|
||
返回 int 位宽或 None(无法解析)。
|
||
"""
|
||
from lib.includes.t import CTypeRegistry
|
||
from lib.core.Handles.HandlesBase import BuiltinTypeMap
|
||
|
||
# 1. 先查 BuiltinTypeMap(覆盖 BYTEPTR 等指针别名)
|
||
bm = BuiltinTypeMap.Get(name)
|
||
if bm:
|
||
ctype_cls, ptr_count = bm
|
||
if ptr_count > 0:
|
||
return None # 指针类型,不是整数 typedef
|
||
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
|
||
if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit():
|
||
return int(llvm_str[1:])
|
||
|
||
# 2. 再查 SymbolTable(用户定义的 typedef)
|
||
if hasattr(self, 'SymbolTable') and self.SymbolTable and name in self.SymbolTable:
|
||
Entry = self.SymbolTable[name]
|
||
if Entry.IsTypedef:
|
||
if Entry.BaseType:
|
||
from lib.includes import t as _t
|
||
if not isinstance(Entry.BaseType, (_t._CTypedef,)) and Entry.PtrCount == 0:
|
||
llvm_str = CTypeRegistry.CTypeToLLVM(type(Entry.BaseType) if isinstance(Entry.BaseType, _t.CType) else Entry.BaseType)
|
||
if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit():
|
||
return int(llvm_str[1:])
|
||
if Entry.OriginalType:
|
||
from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo
|
||
if isinstance(Entry.OriginalType, _CTypeInfo) and Entry.OriginalType.BaseType:
|
||
if not isinstance(Entry.OriginalType.BaseType, (_t._CTypedef,)) and Entry.OriginalType.PtrCount == 0:
|
||
llvm_str = CTypeRegistry.CTypeToLLVM(type(Entry.OriginalType.BaseType) if isinstance(Entry.OriginalType.BaseType, _t.CType) else Entry.OriginalType.BaseType)
|
||
if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit():
|
||
return int(llvm_str[1:])
|
||
|
||
# 3. 尝试 CTypeRegistry 直接按名称查找
|
||
ctype_cls = CTypeRegistry.GetClassByName(name)
|
||
if ctype_cls:
|
||
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
|
||
if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit():
|
||
return int(llvm_str[1:])
|
||
|
||
# 4. 特殊名称(非整数类型,但需要标记为 typedef 以跳过 opaque 定义)
|
||
_SPECIAL_TYPEDEFS = {
|
||
'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion',
|
||
'CStruct', 'enum', 'REnum', 'renum',
|
||
'list', 'dict', 'tuple', 'set', 'str', 'bytes', 'array', 'type',
|
||
}
|
||
if name in _SPECIAL_TYPEDEFS:
|
||
return 0 # 标记为 typedef,但位宽为 0(非整数)
|
||
|
||
return None
|
||
|
||
def _set_node_info(self, node, extra_info=""):
|
||
if hasattr(node, 'lineno'):
|
||
self._current_lineno = node.lineno
|
||
else:
|
||
self._current_lineno = 0
|
||
if hasattr(node, 'col_offset'):
|
||
self._current_node_info = f"line {self._current_lineno}, col {node.col_offset}"
|
||
else:
|
||
self._current_node_info = f"line {self._current_lineno}"
|
||
if extra_info:
|
||
self._current_node_info += f" ({extra_info})"
|
||
|
||
def _set_source_info(self, filepath):
|
||
self._current_source_file = filepath
|
||
try:
|
||
with open(filepath, 'r', encoding='utf-8') as f:
|
||
self._current_source_lines = f.readlines()
|
||
except Exception: # 文件读取失败时回退为空行列表
|
||
self._current_source_lines = []
|
||
|
||
def _ns_hash(self):
|
||
if 'workspace' in self._ns_cache:
|
||
return self._ns_cache['workspace']
|
||
cwd = os.getcwd().replace('\\', '/').lower()
|
||
digest = hashlib.sha1(cwd.encode('utf-8')).hexdigest()[:12]
|
||
self._ns_cache['workspace'] = digest
|
||
return digest
|
||
|
||
def _skip_mangle(self, name):
|
||
skip_prefixes = ('llvm.', 'str_const_', 'printf', 'memset_pattern')
|
||
if any(name.startswith(p) for p in skip_prefixes):
|
||
return True
|
||
if '.' in name:
|
||
parts = name.split('.', 1)
|
||
if len(parts[0]) >= 8 and all(c in '0123456789abcdef' for c in parts[0]):
|
||
return True
|
||
return False
|
||
|
||
def _mangle_name(self, name):
|
||
if self._skip_mangle(name):
|
||
return name
|
||
if name in self._export_funcs:
|
||
return name
|
||
if self.module_sha1:
|
||
return f"{self.module_sha1}.{name}"
|
||
return name
|
||
|
||
def _find_function(self, name):
|
||
if name in self.functions:
|
||
return self.functions[name]
|
||
g = self.module.globals.get(name)
|
||
if g and isinstance(g, ir.Function):
|
||
self.functions[name] = g
|
||
return g
|
||
mangled = self._mangle_func_name(name)
|
||
if mangled != name:
|
||
if mangled in self.functions:
|
||
self.functions[name] = self.functions[mangled]
|
||
return self.functions[mangled]
|
||
g = self.module.globals.get(mangled)
|
||
if g and isinstance(g, ir.Function):
|
||
self.functions[mangled] = g
|
||
self.functions[name] = g
|
||
return g
|
||
for sha1 in self.ModuleSha1Map.values():
|
||
prefixed = f"{sha1}.{name}"
|
||
if prefixed in self.functions:
|
||
self.functions[name] = self.functions[prefixed]
|
||
return self.functions[prefixed]
|
||
g = self.module.globals.get(prefixed)
|
||
if g and isinstance(g, ir.Function):
|
||
self.functions[prefixed] = g
|
||
self.functions[name] = g
|
||
return g
|
||
return None
|
||
|
||
def _has_function(self, name):
|
||
return self._find_function(name) is not None
|
||
|
||
def _get_or_declare_function(self, mangled_name, func_type):
|
||
for g in self.module.globals:
|
||
if isinstance(g, ir.Function) and g.name == mangled_name:
|
||
return g
|
||
return ir.Function(self.module, func_type, name=mangled_name)
|
||
|
||
def _get_function(self, name):
|
||
func = self._find_function(name)
|
||
return func
|
||
|
||
def _mangle_func_name(self, name, module_name=None):
|
||
if name in self._export_funcs:
|
||
return name
|
||
if name in self._export_extern_funcs and name not in self._current_module_func_names:
|
||
return name
|
||
# main 函数作为程序入口点,永远不混淆
|
||
if name == 'main':
|
||
return name
|
||
# Try class_sha1_map first for class methods (e.g., 'md5.__init__' -> class 'md5')
|
||
class_sha1_map = getattr(self, 'class_sha1_map', {})
|
||
if class_sha1_map:
|
||
if '.' in name:
|
||
base_class = name.split('.')[0]
|
||
if base_class in class_sha1_map:
|
||
sha1 = class_sha1_map[base_class]
|
||
return f"{sha1}.{name}"
|
||
elif name in class_sha1_map:
|
||
sha1 = class_sha1_map[name]
|
||
return f"{sha1}.{name}"
|
||
# If module_name is already a SHA1 hash (16 hex chars), use it directly
|
||
if module_name and len(module_name) == 16 and all(c in '0123456789abcdef' for c in module_name):
|
||
return f"{module_name}.{name}"
|
||
if module_name and module_name in self.ModuleSha1Map:
|
||
sha1 = self.ModuleSha1Map[module_name]
|
||
return f"{sha1}.{name}"
|
||
# Try parent package name (e.g., 'hashlib' -> 'hashlib.__init__')
|
||
if module_name:
|
||
init_name = f"{module_name}.__init__"
|
||
if init_name in self.ModuleSha1Map:
|
||
sha1 = self.ModuleSha1Map[init_name]
|
||
return f"{sha1}.{name}"
|
||
if self._skip_mangle(name):
|
||
return name
|
||
if self.module_sha1:
|
||
return f"{self.module_sha1}.{name}"
|
||
return name
|
||
|
||
def _get_source_line(self, lineno):
|
||
if 0 < lineno <= len(self._current_source_lines):
|
||
return self._current_source_lines[lineno - 1].rstrip('\r\n')
|
||
return None
|
||
|
||
def _get_node_info(self):
|
||
info = ""
|
||
if self._current_node_info:
|
||
info = f" [{self._current_node_info}]"
|
||
if self._current_source_file:
|
||
info += f" in {self._current_source_file}"
|
||
lineno = self._current_lineno
|
||
if lineno > 0:
|
||
info += f", line {lineno}:"
|
||
source_line = self._get_source_line(lineno)
|
||
if source_line:
|
||
info += f"\n → {source_line}"
|
||
return info
|