mod emit;
mod literal;
mod expr;
mod stmt;
mod array;
mod control;
mod method;
mod cast;
use syn::{ItemFn, Pat, FnArg};
use std::collections::BTreeMap;
use crate::opcodes::exec;
use crate::crypto::OpcodeTable;
use crate::mba::MbaTransformer;
use crate::substitution::Substitution;
use crate::value_cryptor::ValueCryptor;
#[derive(Debug)]
pub struct CompileError(pub String);
impl std::fmt::Display for CompileError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[allow(dead_code)] #[derive(Debug, Clone, PartialEq)]
pub(crate) enum VarType {
Integer,
IntegerSized(u8),
String,
Vector,
Bool,
Struct(std::string::String),
Tuple(Vec<VarType>),
}
#[allow(dead_code)] impl VarType {
pub fn size_bytes(&self) -> usize {
match self {
VarType::Integer => 8,
VarType::IntegerSized(size) => *size as usize,
VarType::String => 8, VarType::Vector => 8, VarType::Bool => 8, VarType::Struct(_) => 8, VarType::Tuple(elems) => {
elems.iter().map(|e| e.aligned_size()).sum()
}
}
}
pub fn aligned_size(&self) -> usize {
8
}
pub fn needs_cleanup(&self) -> bool {
match self {
VarType::String | VarType::Vector => true,
VarType::Struct(_) => true, VarType::Tuple(elems) => {
!elems.is_empty() || elems.iter().any(|e| e.needs_cleanup())
}
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct FieldDef {
pub name: std::string::String,
pub offset: usize,
}
#[derive(Debug, Clone)]
pub(crate) struct StructDef {
#[allow(dead_code)]
pub name: std::string::String,
pub fields: Vec<FieldDef>,
pub size: usize,
}
impl StructDef {
pub fn get_field_offset(&self, field_name: &str) -> Option<usize> {
self.fields.iter()
.find(|f| f.name == field_name)
.map(|f| f.offset)
}
}
#[derive(Debug, Clone)]
pub(crate) struct VarInfo {
pub reg: u8,
pub var_type: VarType,
pub is_signed: bool,
pub needs_cleanup: bool,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(crate) enum VarLocation {
InputOffset(usize),
Register(u8),
Array(u8, u8),
String(u8),
}
#[derive(Debug, Clone)]
pub(crate) struct LoopContext {
pub continue_label: String,
pub break_label: String,
pub scope_depth: usize,
}
pub struct Compiler {
pub(crate) bytecode: Vec<u8>,
pub(crate) arg_offsets: BTreeMap<String, usize>,
pub(crate) scopes: Vec<BTreeMap<String, VarInfo>>,
pub(crate) var_types: BTreeMap<String, VarLocation>,
pub(crate) struct_defs: BTreeMap<String, StructDef>,
pub(crate) next_local_reg: u8,
pub(crate) next_arg_offset: usize,
pub(crate) labels: BTreeMap<String, usize>,
pub(crate) fixups: Vec<(usize, String)>,
pub(crate) label_counter: usize,
pub(crate) loop_stack: Vec<LoopContext>,
pub(crate) opcode_table: OpcodeTable,
pub(crate) mba: MbaTransformer,
pub(crate) mba_enabled: bool,
pub(crate) subst: Substitution,
pub(crate) value_cryptor: ValueCryptor,
pub(crate) value_cryptor_enabled: bool,
pub(crate) opaque_predicates_enabled: bool,
}
impl Compiler {
#[allow(dead_code)]
pub fn new() -> Self {
Self::with_options(false, false)
}
#[allow(dead_code)]
pub fn with_mba(mba_enabled: bool) -> Self {
Self::with_options(mba_enabled, false)
}
pub fn with_options(mba_enabled: bool, substitution_enabled: bool) -> Self {
let seed = crate::crypto::get_opcode_table().get_seed();
let scopes = vec![BTreeMap::new()];
Self {
bytecode: Vec::new(),
arg_offsets: BTreeMap::new(),
scopes,
var_types: BTreeMap::new(),
struct_defs: BTreeMap::new(),
next_local_reg: 0,
next_arg_offset: 0,
labels: BTreeMap::new(),
fixups: Vec::new(),
label_counter: 0,
loop_stack: Vec::new(),
opcode_table: crate::crypto::get_opcode_table(),
mba: MbaTransformer::new(seed),
mba_enabled,
subst: Substitution::new(seed, substitution_enabled),
value_cryptor: ValueCryptor::new(seed),
value_cryptor_enabled: mba_enabled,
opaque_predicates_enabled: substitution_enabled,
}
}
pub(crate) fn pos(&self) -> usize {
self.bytecode.len()
}
pub(crate) fn unique_label(&mut self, prefix: &str) -> String {
let label = format!("{}_{}", prefix, self.label_counter);
self.label_counter += 1;
label
}
pub(crate) fn mark_label(&mut self, name: &str) {
self.labels.insert(name.to_string(), self.pos());
}
pub(crate) fn register_arg(&mut self, name: &str) {
self.arg_offsets.insert(name.to_string(), self.next_arg_offset);
self.next_arg_offset += 8;
}
pub(crate) fn push_scope(&mut self) {
self.scopes.push(BTreeMap::new());
}
pub(crate) fn pop_scope(&mut self) {
if let Some(scope) = self.scopes.pop() {
for (_name, info) in scope.iter() {
if info.needs_cleanup {
self.emit_push_reg(info.reg);
self.emit_heap_free();
}
}
}
}
pub(crate) fn current_scope_depth(&self) -> usize {
self.scopes.len()
}
pub(crate) fn emit_scope_cleanup(&mut self, target_depth: usize) {
let current_depth = self.scopes.len();
if current_depth <= target_depth {
return; }
let mut regs_to_free = Vec::new();
for depth in (target_depth..current_depth).rev() {
if let Some(scope) = self.scopes.get(depth) {
for (_name, info) in scope.iter() {
if info.needs_cleanup {
regs_to_free.push(info.reg);
}
}
}
}
for reg in regs_to_free {
self.emit_push_reg(reg);
self.emit_heap_free();
}
}
pub(crate) fn define_var(&mut self, name: &str, var_type: VarType, is_signed: bool) -> Result<u8, CompileError> {
self.define_var_internal(name, var_type, is_signed, false)
}
pub(crate) fn define_var_borrowed(&mut self, name: &str, var_type: VarType, is_signed: bool) -> Result<u8, CompileError> {
self.define_var_internal(name, var_type, is_signed, true)
}
fn define_var_internal(&mut self, name: &str, var_type: VarType, is_signed: bool, is_borrowed: bool) -> Result<u8, CompileError> {
if self.next_local_reg >= 248 {
return Err(CompileError("Too many local variables (max 248)".to_string()));
}
let reg = self.next_local_reg;
self.next_local_reg += 1;
let needs_cleanup = if is_borrowed {
false
} else {
match &var_type {
VarType::String | VarType::Vector => true,
VarType::Struct(struct_name) => {
self.struct_defs.get(struct_name)
.map(|def| def.size > 0)
.unwrap_or(false)
}
VarType::Tuple(elems) => !elems.is_empty(), _ => false,
}
};
match &var_type {
VarType::String => {
self.var_types.insert(name.to_string(), VarLocation::String(reg));
}
VarType::Vector => {
self.var_types.insert(name.to_string(), VarLocation::Array(reg, 8));
}
_ => {
self.var_types.insert(name.to_string(), VarLocation::Register(reg));
}
}
let info = VarInfo {
reg,
var_type,
is_signed,
needs_cleanup,
};
if let Some(scope) = self.scopes.last_mut() {
scope.insert(name.to_string(), info);
}
Ok(reg)
}
pub(crate) fn lookup_var(&self, name: &str) -> Option<&VarInfo> {
for scope in self.scopes.iter().rev() {
if let Some(info) = scope.get(name) {
return Some(info);
}
}
None
}
pub(crate) fn is_var_signed(&self, name: &str) -> bool {
if let Some(info) = self.lookup_var(name) {
return info.is_signed;
}
false
}
pub(crate) fn get_var_type(&self, name: &str) -> Option<VarType> {
if let Some(info) = self.lookup_var(name) {
return Some(info.var_type.clone());
}
None
}
pub(crate) fn get_var_location(&self, name: &str) -> Option<VarLocation> {
if let Some(info) = self.lookup_var(name) {
return match info.var_type {
VarType::String => Some(VarLocation::String(info.reg)),
VarType::Vector => Some(VarLocation::Array(info.reg, 8)),
_ => Some(VarLocation::Register(info.reg)),
};
}
if let Some(&offset) = self.arg_offsets.get(name) {
return Some(VarLocation::InputOffset(offset));
}
if let Some(loc) = self.var_types.get(name) {
return Some(loc.clone());
}
None
}
pub(crate) fn apply_fixups(&mut self) -> Result<(), CompileError> {
for (fixup_pos, label) in &self.fixups {
let target = self.labels.get(label)
.ok_or_else(|| CompileError(format!("Unknown label: {}", label)))?;
let from = fixup_pos + 2;
let offset = (*target as isize) - (from as isize);
if offset < i16::MIN as isize || offset > i16::MAX as isize {
return Err(CompileError(format!("Jump offset out of range: {}", offset)));
}
let offset_bytes = (offset as i16).to_le_bytes();
self.bytecode[*fixup_pos] = offset_bytes[0];
self.bytecode[*fixup_pos + 1] = offset_bytes[1];
}
Ok(())
}
pub(crate) fn extract_pat_name(pat: &Pat) -> Result<String, CompileError> {
match pat {
Pat::Ident(pat_ident) => Ok(pat_ident.ident.to_string()),
Pat::Type(pat_type) => Self::extract_pat_name(&pat_type.pat),
_ => Err(CompileError("Unsupported pattern in let binding".to_string())),
}
}
pub(crate) fn finalize(&mut self) -> Result<Vec<u8>, CompileError> {
self.apply_fixups()?;
if self.bytecode.is_empty() || self.bytecode.last().copied() != Some(exec::HALT) {
self.emit_op(exec::HALT);
}
Ok(self.bytecode.clone())
}
}
#[allow(dead_code)]
pub fn compile_function(func: &ItemFn) -> Result<Vec<u8>, CompileError> {
compile_function_full(func, false, false)
}
#[allow(dead_code)]
pub fn compile_function_with_mba(func: &ItemFn) -> Result<Vec<u8>, CompileError> {
compile_function_full(func, true, false)
}
#[allow(dead_code)]
pub fn compile_function_with_substitution(func: &ItemFn) -> Result<Vec<u8>, CompileError> {
compile_function_full(func, false, true)
}
#[allow(dead_code)]
pub fn compile_function_paranoid(func: &ItemFn) -> Result<Vec<u8>, CompileError> {
compile_function_full(func, true, true)
}
pub fn compile_function_full(func: &ItemFn, mba_enabled: bool, substitution_enabled: bool) -> Result<Vec<u8>, CompileError> {
let mut compiler = Compiler::with_options(mba_enabled, substitution_enabled);
for arg in &func.sig.inputs {
if let FnArg::Typed(pat_type) = arg {
if let Pat::Ident(pat_ident) = &*pat_type.pat {
compiler.register_arg(&pat_ident.ident.to_string());
}
}
}
compiler.compile_block(&func.block)?;
compiler.finalize()
}