use std::{collections::{LinkedList, BTreeMap}, cmp::Ordering, sync::Arc, iter::{Copied, Rev}}; use ion_rs::SymbolTable; use crate::ion::{IonReader, IonValue}; #[repr(u8)] pub enum OpCode { Jump = 0x01, Dup = 0x02, Dup2 = 0x03, SwapPop = 0x04, Invoke = 0x05, Yield = 0x06, Return = 0x07, /// Push a typed Ion value onto the stack. /// operands: T and L /// length /// repr /// stack: [] -> [representation, length, T and L] /// Note: lengths are reversed on the stack. /// representations are not reversed. TypePush = 0x10, /// Pop a typed Ion value from the stack. /// operands: /// stack: [repr, length, T and L] -> [] TypePop = 0x11, /// Load a typed Ion value from a variable onto the stack. /// operands: variableId (VarUInt) /// stack: [] -> [repr, length, T and L] TypeLoad = 0x12, /// Save a typed Ion value from the stack into a variable. /// operands: variableId (VarUInt) /// stack: [repr, length, T and L] -> [] TypeStore = 0x13, /// Push the length of the type. /// operands: variableId (VarUInt) /// stack: [repr, length, T+L] -> [Int] TypeLength = 0x14, /// Append bytes onto the end of a typed sequence of bytes. /// This operation is valid with string, clob, and blob types. /// operands: variableId (VarUInt) /// number of octets to push /// stack: [repr, length, T+L, bytes] -> [repr, bytes, length', T+L'] BytesAppend = 0x15, /// Append a typed value onto the end of a list or sexp. /// operands: variableId (VarUInt) /// stack: [repr, length, T+L] -> [] ListAppend = 0x16, /// Push the element from a list or sexp onto the stack. /// operands: variableId (VarUInt) /// stack: [offset (Int)] -> [{next offset}, {value}] ListLoad = 0x17, /// Append a typed value onto the end of a struct. /// operands: variableId (VarUInt) /// stack: [{value}, {symbol}] -> [] FieldAppend = 0x18, /// Prepare an iterator an iterator over the variable. /// operands: variableId (VarUInt) /// stack: [{offset (UInt)}] -> [{next offset}] Iterator = 0x19, /// Move an iterator to the next value. /// operands: variableId (VarUInt) /// stack: [{offset (UInt)}] -> [{next offset}] Next = 0x1A, /// Move an iterator to a nested value. /// operands: variableId (VarUInt) /// stack: [{offset (UInt)}] -> [{offset}, {next end}, {next offset}] StepIn = 0x1B, /// Move an iterator out of a nested value. /// operands: variableId (VarUInt) /// stack: [{offset (UInt)}] -> [{next offset}] /// stack: [{{end}, {offset}] -> [] StepOut = 0x1C, /// Copy a value onto the stack /// operands: variableId (VarUInt) /// stack: [{offset (UInt)}] -> [{offset}, {value}] LoadValue = 0x1D, /// Copy a struct field symbol onto the stack /// operands: variableId (VarUInt) /// stack: [{offset (UInt)}] -> [{offset}, {value}] StructField = 0x1E, /// Add two ints. /// operands: /// stack -> [{value1}, {value2} -> {result}] IntAdd = 0x21, /// Subtract value2 from value1. /// operands: /// stack -> [{value1}, {value2} -> {result}] IntSub = 0x22, /// Multiply value1 and value2. /// operands: /// stack -> [{value1}, {value2} -> {result}] IntMul = 0x23, /// Divide value2 by value1. /// operands: /// stack -> [{value1}, {value2} -> {result}] IntDiv = 0x24, /// Add two floats. /// operands: /// stack -> [{value1}, {value2} -> {result}] FloatAdd = 0x41, /// Subtract value2 from value1. /// operands: /// stack -> [{value1}, {value2} -> {result}] FloatSub = 0x42, /// Multiply value1 and value2. /// operands: /// stack -> [{value1}, {value2} -> {result}] FloatMul = 0x43, /// Divide value2 by value1. /// operands: /// stack -> [{value1}, {value2} -> {result}] FloatDiv = 0x44, /// Add two decimals. /// operands: /// stack -> [{value1}, {value2} -> {result}] DecimalAdd = 0x51, /// Subtract value2 from value1. /// operands: /// stack -> [{value1}, {value2} -> {result}] DecimalSub = 0x52, /// Multiply value1 and value2. /// operands: /// stack -> [{value1}, {value2} -> {result}] DecimalMul = 0x53, /// Divide value2 by value1. /// operands: /// stack -> [{value1}, {value2} -> {result}] DecimalDiv = 0x54, /// Jump to the given instruction if value == 0. /// operands: instruction (VarUInt) /// stack: [{value}] -> [] IfEq = 0x30, /// Jump to the given instruction if values != 0. /// operands: instruction (VarUInt) /// stack: [{value}] -> [] IfNe = 0x31, /// Jump to the given instruction if value >= 0 /// operands: instruction (VarUInt) /// stack: [{value}] -> [] IfGe = 0x32, /// Jump to the given instruction if value > 0. /// operands: instruction (VarUInt) /// stack: [{value}] -> [] IfGt = 0x33, /// Jump to the given instruction if value <= 0. /// operands: instruction (VarUInt) /// stack: [{value}] -> [] IfLe = 0x34, /// Jump to the given instruction if value < 0. /// operands: instruction (VarUInt) /// stack: [{value}] -> [] IfLt = 0x35, /// Jump to the given instruction if two values are equal. /// operands: instruction (VarUInt) /// stack: [{value1}, {value2}] -> [] IfEq2 = 0x36, /// Jump to the given instruction if two values are not equal. /// operands: instruction (VarUInt) /// stack: [{value1}, {value2}] -> [] IfNe2 = 0x37, /// Jump to the given instruction if value2 >= value1. /// operands: instruction (VarUInt) /// stack: [{value1}, {value2}] -> [] IfGe2 = 0x38, /// Jump to the given instruction if value2 > value1. /// operands: instruction (VarUInt) /// stack: [{value1}, {value2}] -> [] IfGt2 = 0x39, /// Jump to the given instruction if value2 <= value1. /// operands: instruction (VarUInt) /// stack: [{value1}, {value2}] -> [] IfLe2 = 0x3a, /// Jump to the given instruction if value2 < value1. /// operands: instruction (VarUInt) /// stack: [{value1}, {value2}] -> [] IfLt2 = 0x3b, NewList = 0xB1, NewStruct = 0xD1, } impl OpCode { pub fn as_u8(&self) -> u8 { unsafe { *(self as *const Self as *const u8) } } } impl From for u8 { fn from(value: OpCode) -> Self { value.as_u8() } } pub struct Runtime { symbols: SymbolTable, bytecode: Arc, functions: BTreeMap, stacks: LinkedList, } pub enum ExecutionState { Continue, Halt, } #[derive(Copy, Clone)] pub struct Function { pub(crate) name_symbol: usize, pub(crate) arguments: u16, pub(crate) variables: u16, pub(crate) pc: usize, pub(crate) expected_stack_depth: usize, } pub type ByteCode = Vec; struct Stack(Vec); pub struct StackFrame { bytecode: Arc, pc: usize, variables: Vec, stack: Stack, } impl Runtime { pub fn new(symbols: SymbolTable, bytecode: ByteCode, functions: Vec) -> Runtime { Runtime { symbols, functions: functions.into_iter().map(|f| (f.name_symbol, f)).collect(), bytecode: bytecode.into(), stacks: LinkedList::new(), } } pub fn to_symbol_id(&self, s: S) -> Option where S: AsRef { self.symbols.sid_for(&s) } pub fn resolve_symbol(&self, id: usize) -> Option<&str> { self.symbols.text_for(id) } pub fn invoke(&mut self, fnref: usize, arguments: Vec) -> Vec { let func = *self.functions.get(&fnref).expect("Undefined function"); let mut frame = StackFrame::new(self.bytecode.clone(), &func, arguments); loop { match self.execute(&mut frame) { ExecutionState::Continue => (), ExecutionState::Halt => { //frame.variables.truncate(func.arguments.into()); return frame.variables; }, } } } fn execute(&mut self, frame: &mut StackFrame) -> ExecutionState { if frame.eof() { return ExecutionState::Halt; } let op = frame.next_byte(); #[cfg(debugger)] { for x in &frame.stack.0 { print!(" {x:02X}"); } println!(); println!("PC: {:02X}, OP: {:02X}", frame.pc - 1, op); } match op { 0x01 => { // OpType::Jump let pc = frame.next_u32().try_into().unwrap(); frame.jump(pc); }, 0x02 => { // OpType::Dup let value1 = frame.stack.pop_value(); frame.stack.push_value(&value1); frame.stack.push_value(&value1); }, 0x03 => { // OpType::Dup2 let value1 = frame.stack.pop_value(); let value2 = frame.stack.pop_value(); frame.stack.push_value(&value2); frame.stack.push_value(&value1); frame.stack.push_value(&value2); frame.stack.push_value(&value1); }, 0x04 => { // OpType::SwapPop let value1 = frame.stack.pop_value(); frame.stack.drop_value(); frame.stack.push_value(&value1); }, 0x10 => { // OpType::Push let buf = &self.bytecode[frame.pc..]; let mut reader = IonReader::new(buf.iter().copied(), 0); let value = reader.next_value(); frame.pc += reader.offset(); frame.stack.push_value(&value); }, 0x11 => { // OpCode::TypePop frame.stack.drop_value(); }, 0x12 => { // OpCode::TypeLoad let var = frame.next_varuint(); frame.stack.push_value(&frame.variables[var]); }, 0x13 => { // OpCode::TypeStore let var = frame.next_varuint(); frame.variables[var] = frame.stack.pop_value(); }, 0x14 => { // OpCode::TypeLength let var = frame.next_varuint(); let buf = frame.variables.get(var).expect("Undefined variable"); let len = buf.len(); frame.stack.push_usize(len); }, 0x15 => todo!(), // OpCode::BytesAppend 0x16 => todo!(), // OpCode::ListAppend 0x17 => todo!(), // OpCode::ListLoad 0x18 => todo!(), // OpCode::FieldAppend 0x19 => { // OpCode::Iterate let (ion_type, end, pos) = { let mut reader = frame.stack.peek_reader(); let (ion_type, end) = reader.step_in(); let pos = reader.offset(); (ion_type, end, pos) }; frame.stack.push_usize(ion_type as usize); frame.stack.push_usize(end); frame.stack.push_usize(pos); }, 0x1A => { // OpCode::Next let pos = frame.stack.pop_value().to_usize(); let len = frame.stack.pop_value().to_usize(); let ion_type = frame.stack.pop_value().to_usize(); let next = { let mut reader = if ion_type == 0x0D { frame.stack.peek_struct_reader_at(pos) } else { frame.stack.peek_reader_at(pos) }; reader.skip_value(); reader.offset() }; frame.stack.push_usize(ion_type); // TODO peek frame.stack.push_usize(len); // TODO peek frame.stack.push_usize(next); }, 0x1B => { // OpCode::StepIn let pos = frame.stack.pop_value().to_usize(); let len = frame.stack.pop_value().to_usize(); let ion_type = frame.stack.pop_value().to_usize(); let (next_type, next_end, next_pos) = { let mut reader = if ion_type == 0x0D { frame.stack.peek_struct_reader_at(pos) } else { frame.stack.peek_reader_at(pos) }; let (next_type, next_end) = reader.step_in(); let next_pos = reader.offset(); (next_type, next_end, next_pos) }; frame.stack.push_usize(ion_type); // TODO peek frame.stack.push_usize(len); // TODO peek frame.stack.push_usize(pos); // TODO peek frame.stack.push_usize(next_type as usize); frame.stack.push_usize(next_end); frame.stack.push_usize(next_pos); }, 0x1C => { // OpCode::StepOut frame.stack.drop_value(); frame.stack.drop_value(); frame.stack.drop_value(); }, 0x1D => { // OpCode::LoadValue let pos = frame.stack.pop_value().to_usize(); let len = frame.stack.pop_value().to_usize(); let ion_type = frame.stack.pop_value().to_usize(); let next_value = { let mut reader = if ion_type == 0x0D { frame.stack.peek_struct_reader_at(pos) } else { frame.stack.peek_reader_at(pos) }; reader.next_value() }; frame.stack.push_usize(ion_type); // TODO peek frame.stack.push_usize(len); // TODO peek frame.stack.push_usize(pos); // TODO peek frame.stack.push_value(&next_value); }, 0x1E => { // OpCode::StructField let pos = frame.stack.pop_value().to_usize(); let len = frame.stack.pop_value().to_usize(); let ion_type = frame.stack.pop_value().to_usize(); let field_id = { let mut reader = if ion_type == 0x0D { frame.stack.peek_struct_reader_at(pos) } else { frame.stack.peek_reader_at(pos) }; reader.skip_value(); reader.field_id() }; frame.stack.push_usize(ion_type); // TODO peek frame.stack.push_usize(len); // TODO peek frame.stack.push_usize(pos); // TODO peek if let Some(field_id) = field_id { frame.stack.push_symbol(field_id); } else { panic!("Not a struct"); } }, 0x21 => self.int_add(frame), // OpCode::IntAdd 0x22 => todo!(), // OpCode::IntSub 0x23 => todo!(), // OpCode::IntMul 0x24 => todo!(), // OpCode::IntDiv 0x41 => todo!(), // OpCode::FloatAdd 0x42 => todo!(), // OpCode::FloatSub 0x43 => todo!(), // OpCode::FloatMul 0x44 => todo!(), // OpCode::FloatDiv 0x51 => todo!(), // OpCode::DecimalAdd 0x52 => todo!(), // OpCode::DecimalSub 0x53 => todo!(), // OpCode::DecimalMul 0x54 => todo!(), // OpCode::DecimalDiv 0x30 => todo!(), // OpCode::IfEq 0x31 => todo!(), // OpCode::IfNe 0x32 => todo!(), // OpCode::IfGe 0x33 => todo!(), // OpCode::IfGt 0x34 => todo!(), // OpCode::IfLe 0x35 => todo!(), // OpCode::IfLt 0x36 => { // OpCode::IfEq2 let next_pc = frame.next_u32().try_into().unwrap(); let value1 = frame.stack.pop_value(); let value2 = frame.stack.pop_value(); if value1 == value2 { frame.pc = next_pc; } }, 0x37 => { // OpCode::IfNe2 let next_pc = frame.next_u32().try_into().unwrap(); let value1 = frame.stack.pop_value(); let value2 = frame.stack.pop_value(); if value1 != value2 { frame.pc = next_pc; } }, 0x38 => { // OpCode::IfGe2 let next_pc = frame.next_u32().try_into().unwrap(); let value1 = frame.stack.pop_value(); let value2 = frame.stack.pop_value(); if value1 >= value2 { frame.pc = next_pc; } }, 0x39 => { // OpCode::IfGt2 let next_pc = frame.next_u32().try_into().unwrap(); let value1 = frame.stack.pop_value(); let value2 = frame.stack.pop_value(); if value1 > value2 { frame.pc = next_pc; } }, 0x3A => { // OpCode::IfLe2 let next_pc = frame.next_u32().try_into().unwrap(); let value1 = frame.stack.pop_value(); let value2 = frame.stack.pop_value(); if value1 <= value2 { frame.pc = next_pc; } }, 0x3B => { // OpCode::IfLt2 let next_pc = frame.next_u32().try_into().unwrap(); let value1 = frame.stack.pop_value(); let value2 = frame.stack.pop_value(); if value1 < value2 { frame.pc = next_pc; } }, 0xB1 => { // OpCode::NewList // TODO: Yuck let len = frame.stack.pop_usize(); let mut list = Vec::new(); let mut sz = 0; for _ in 0..len { let value = frame.stack.pop_value(); sz += value.len(); list.push(value); } for v in list { frame.stack.push_value(&v); } if sz >= 14 { frame.stack.push_varuint(sz); frame.stack.push_byte(0xBE); } else { frame.stack.push_byte(0xB0 | (sz as u8)); } }, 0xD1 => { // OpCode::NewStruct // TODO: Yuck let len = frame.stack.pop_usize(); let mut pairs = Vec::new(); for _ in 0..len { let sym = frame.stack.pop_value(); let value = frame.stack.pop_value(); pairs.push((sym, value)); } let mark = frame.stack.len(); for (sym, v) in pairs { frame.stack.push_value(&v); frame.stack.push_varuint(sym.reader().next_symbol_id()); } let sz = frame.stack.len() - mark; if sz >= 14 { frame.stack.push_varuint(sz); frame.stack.push_byte(0xDE); } else { frame.stack.push_byte(0xD0 | (sz as u8)); } }, _ => panic!("Invalid opcode {:02x} at {:02x}", op, frame.pc - 1), } ExecutionState::Continue } fn int_add(&self, frame: &mut StackFrame) { todo!() } } impl StackFrame { pub fn new(bytecode: Arc, func: &Function, arguments: Vec) -> StackFrame { let stack = Stack::new(func.expected_stack_depth); let mut frame = StackFrame { bytecode, pc: func.pc, variables: arguments, stack, }; for _ in func.arguments..func.variables { frame.variables.push(IonValue::new_null()); } frame } fn jump(&mut self, pc: usize) { self.pc = pc; } fn eof(&self) -> bool { self.pc >= self.bytecode.len() } fn next_byte(&mut self) -> u8 { let b = self.bytecode[self.pc]; self.pc += 1; b } fn next_bytes(&mut self, len: usize) -> &[u8] { let end = self.pc + len; let bytes = &self.bytecode[self.pc..end]; self.pc = end; bytes } fn next_u32(&mut self) -> u32 { let mut v: u32 = 0; for _ in 0..4 { v <<= 8; v |= self.next_byte() as u32; } v } fn next_varuint(&mut self) -> usize { let mut b: usize = self.next_byte().into(); let mut v = b & 0x7f; while (b & 0x80) == 0 { b = self.next_byte().into(); v <<= 7; v |= b & 0x7f; } v } } impl Stack { pub fn new(expected_depth: usize) -> Stack { Stack(Vec::with_capacity(expected_depth)) } pub fn len(&self) -> usize { self.0.len() } pub fn peek_reader(&self) -> IonReader + '_> { IonReader::new(self.0.iter().copied().rev(), 0) } pub fn peek_reader_at(&self, offset: usize) -> IonReader>>> { let end = self.0.len() - offset; IonReader::new(self.0[..end].iter().copied().rev(), offset) } pub fn peek_struct_reader_at(&self, offset: usize) -> IonReader>>> { let end = self.0.len() - offset; IonReader::new_struct(self.0[..end].iter().copied().rev(), offset) } pub fn drop_value(&mut self) { let mut it = IonReader::new(self.0.iter().copied().rev(), 0); it.skip_value(); let offset = it.offset(); self.0.truncate(self.0.len() - offset); } pub fn pop_value(&mut self) -> IonValue { let mut it = IonReader::new(self.0.iter().copied().rev(), 0); let value = it.next_value(); let offset = it.offset(); self.0.truncate(self.0.len() - offset); value } pub fn pop_usize(&mut self) -> usize { let mut it = IonReader::new(self.0.iter().copied().rev(), 0); let value = it.next_usize(); let offset = it.offset(); self.0.truncate(self.0.len() - offset); value } #[inline] pub fn push_byte(&mut self, value: u8) { self.0.push(value); } pub fn push_varuint(&mut self, mut value: usize) { self.push_byte((value & 0x7F | 0x80) as u8); value >>= 7; while value != 0 { self.push_byte((value & 0x7F) as u8); value >>= 7; } } pub fn push_value(&mut self, value: &IonValue) { for byte in value.bytes().rev() { self.push_byte(byte); } } pub fn push_symbol(&mut self, value: usize) { match value.cmp(&0) { Ordering::Equal => self.push_byte(0x70), Ordering::Greater => { let mut v = value; let mut octets = 0; while v != 0 { self.push_byte((v & 0xFF) as u8); octets += 1; v >>= 8; } let tl = 0x70 | octets; self.push_byte(tl); }, Ordering::Less => (), } } pub fn push_usize(&mut self, value: usize) { match value.cmp(&0) { Ordering::Equal => self.push_byte(0x20), Ordering::Greater => { let mut v = value; let mut octets = 0; while v != 0 { self.push_byte((v & 0xFF) as u8); octets += 1; v >>= 8; } let tl = 0x20 | octets; self.push_byte(tl); }, Ordering::Less => (), } } }