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)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum VarType {
Integer,
String,
Vector,
Bool,
}
#[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 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) 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,
}
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 mut scopes = Vec::new();
scopes.push(BTreeMap::new());
Self {
bytecode: Vec::new(),
arg_offsets: BTreeMap::new(),
scopes,
var_types: 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,
}
}
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 define_var(&mut self, name: &str, var_type: VarType, is_signed: 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 = matches!(var_type, VarType::String | VarType::Vector);
let info = VarInfo {
reg,
var_type,
is_signed,
needs_cleanup,
};
if let Some(scope) = self.scopes.last_mut() {
scope.insert(name.to_string(), info);
}
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));
}
}
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);
}
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()
}