use crate::{
config::PROGRAM_MAX_SIZE,
instruction_utils::{Instructions, A2B},
raw_type::{StaticRawType, TypeId},
utils::{AddressingModes, AddressingValues, ProgramReader},
};
use alloc::vec::Vec;
use core::mem;
use ellie_core::defs::{NativeCallTrace, PlatformArchitecture};
#[derive(Debug, Clone, Copy)]
pub struct ReadInstruction {
pub instruction: Instructions,
pub addressing_mode: AddressingModes,
pub addressing_value: AddressingValues,
pub op_code: u8,
}
#[derive(PartialEq, Debug)]
pub enum ProgramReadErrors {
ReadError,
UnexpectedPlatformArchitecture,
UnmatchedPlatformArchitecture(PlatformArchitecture, PlatformArchitecture),
NoMainFunction,
BrokenMainFunction,
IllegalOpCode,
Complete,
}
impl Default for ReadInstruction {
fn default() -> Self {
Self {
instruction: Instructions::A2B(A2B {
addressing_mode: AddressingModes::Implicit,
}),
addressing_mode: AddressingModes::Implicit,
addressing_value: AddressingValues::Implicit,
op_code: 0,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct MainProgram {
pub hash: usize,
pub start: usize,
pub length: usize,
}
#[derive(Debug, Clone)]
pub struct Program {
pub main: MainProgram,
pub arch: PlatformArchitecture,
pub native_call_traces: Vec<NativeCallTrace>,
pub instructions: Vec<ReadInstruction>,
}
impl Default for Program {
fn default() -> Self {
Self::new()
}
}
impl Program {
pub fn new() -> Self {
Self {
main: MainProgram {
hash: 0,
start: 0,
length: 0,
},
arch: PlatformArchitecture::B32,
instructions: Vec::new(),
native_call_traces: Vec::new(),
}
}
pub fn generate_main_from_function(&self, target_hash: usize) -> Result<MainProgram, u8> {
let mut i = 0;
while i < self.instructions.len() {
let instruction = self.instructions[i];
match instruction.instruction {
Instructions::FN(_) => match instruction.addressing_value {
AddressingValues::Immediate(static_raw_type) => {
if static_raw_type.type_id.id == 1 {
let hash = static_raw_type.to_int();
if static_raw_type.to_int() as usize == target_hash {
let program_len_instruction = self.instructions[i + 1];
let program_len = match program_len_instruction.instruction {
Instructions::STA(_) => {
match program_len_instruction.addressing_value {
AddressingValues::Immediate(e) => e.to_int(),
_ => return Err(2),
}
}
_ => return Err(1),
};
return Ok(MainProgram {
hash: hash as usize,
start: i,
length: program_len as usize,
});
}
} else {
return Err(2);
}
}
_ => return Err(1),
},
_ => (),
}
i += 1;
}
Err(3)
}
pub fn build_from_reader(
&mut self,
reader: &mut ProgramReader,
) -> Result<(), ProgramReadErrors> {
let arch = match reader.read_u8() {
Some(byte) => match PlatformArchitecture::from_byte(byte) {
Some(e) => e,
None => return Err(ProgramReadErrors::UnexpectedPlatformArchitecture),
},
None => return Err(ProgramReadErrors::ReadError),
};
if arch.usize_len() > mem::size_of::<usize>() as u8 {
return Err(ProgramReadErrors::UnmatchedPlatformArchitecture(
arch,
PlatformArchitecture::from_byte(mem::size_of::<usize>() as u8 * 8).unwrap(),
));
}
let main_exists = match reader.read_u8() {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
if main_exists == 0 {
return Err(ProgramReadErrors::NoMainFunction);
}
let start = match reader.read_usize(arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
let end = match reader.read_usize(arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
let hash = match reader.read_usize(arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
self.main = MainProgram {
hash,
start,
length: end,
};
self.arch = arch;
let native_call_trace_count = match reader.read_usize(arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
for _ in 0..native_call_trace_count {
let module_name_len = match reader.read_usize(arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
let module_name = match reader.read_string(module_name_len) {
Some(name) => name,
None => return Err(ProgramReadErrors::ReadError),
};
let function_hash = match reader.read_usize(arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
let function_name_len = match reader.read_usize(arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
let function_name = match reader.read_string(function_name_len) {
Some(name) => name,
None => return Err(ProgramReadErrors::ReadError),
};
self.native_call_traces.push(NativeCallTrace {
module_name,
function_hash,
function_name,
})
}
loop {
let read_instruction = self.read_instruction(reader);
match read_instruction {
Ok(instruction) => {
self.instructions.push(instruction);
if self.instructions.len() - 1 == self.main.length {
break;
}
}
Err(error) => {
return Err(error);
}
}
}
Ok(())
}
fn read_instruction<'a>(
&self,
reader: &mut ProgramReader,
) -> Result<ReadInstruction, ProgramReadErrors> {
let read_byte = match reader.read_u8() {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
match Instructions::from(&read_byte) {
Some(instruction) => {
let addressing_mode = instruction.addressing_mode();
let mut addressing_value: AddressingValues = AddressingValues::Implicit;
if addressing_mode == AddressingModes::Implicit {
Ok(ReadInstruction {
instruction,
addressing_mode,
addressing_value,
op_code: read_byte,
})
} else {
match addressing_mode {
AddressingModes::Immediate => {
let id = reader.read_u8().unwrap();
let size = reader.read_usize(self.arch.usize_len()).unwrap();
let type_id = TypeId::from(id, size);
let mut data: [u8; 8] = [0; 8];
for i in 0..type_id.size {
data[i] = reader.read_u8().unwrap();
}
addressing_value =
AddressingValues::Immediate(StaticRawType { type_id, data });
}
AddressingModes::Absolute => {
let address = match reader.read_usize(self.arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
addressing_value = AddressingValues::Absolute(address);
}
AddressingModes::AbsoluteIndex => {
let pointer = match reader.read_usize(self.arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
let index = match reader.read_usize(self.arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
addressing_value = AddressingValues::AbsoluteIndex(pointer, index);
}
AddressingModes::AbsoluteProperty => {
let pointer = match reader.read_usize(self.arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
let index = match reader.read_usize(self.arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
addressing_value = AddressingValues::AbsoluteProperty(pointer, index);
}
AddressingModes::AbsoluteStatic => {
let address = match reader.read_usize(self.arch.usize_len()) {
Some(byte) => byte,
None => return Err(ProgramReadErrors::ReadError),
};
addressing_value = AddressingValues::AbsoluteStatic(address);
}
AddressingModes::Implicit => {
addressing_value = AddressingValues::Implicit;
}
AddressingModes::IndirectA => {
addressing_value = AddressingValues::IndirectA;
}
AddressingModes::IndirectB => {
addressing_value = AddressingValues::IndirectB;
}
AddressingModes::IndirectC => {
addressing_value = AddressingValues::IndirectC;
}
AddressingModes::IndirectX => {
addressing_value = AddressingValues::IndirectX;
}
AddressingModes::IndirectY => {
addressing_value = AddressingValues::IndirectY;
}
}
Ok(ReadInstruction {
instruction,
addressing_mode,
addressing_value,
op_code: read_byte,
})
}
}
None => Err(ProgramReadErrors::IllegalOpCode),
}
}
}
pub struct VmProgram {
pub instructions: [ReadInstruction; PROGRAM_MAX_SIZE],
pub traces: Vec<NativeCallTrace>,
pub length: usize,
}
impl Default for VmProgram {
fn default() -> Self {
Self::new()
}
}
impl VmProgram {
pub fn new() -> Self {
VmProgram {
instructions: [ReadInstruction::default(); PROGRAM_MAX_SIZE],
traces: Vec::new(),
length: 0,
}
}
#[deprecated = "Use fill_from_vector to prevent stack overflow."]
pub fn new_from_vector(program: Vec<ReadInstruction>) -> Self {
let mut vm_program = VmProgram::new();
vm_program.fill_from_vector(program);
vm_program
}
pub fn fill_from_vector(&mut self, program: Vec<ReadInstruction>) {
for (idx, instruction) in program.iter().enumerate() {
self.instructions[idx] = *instruction;
}
self.length = program.len();
}
pub fn fill_traces(&mut self, traces: Vec<NativeCallTrace>) {
self.traces = traces
}
}