diff options
author | Jesse Morgan <jesse@jesterpm.net> | 2023-12-28 17:12:54 -0800 |
---|---|---|
committer | Jesse Morgan <jesse@jesterpm.net> | 2023-12-28 17:12:54 -0800 |
commit | d04b4dfa993681060d363b5ef994c1a635f3c1f9 (patch) | |
tree | 6d7ae65ed7c14e32df50858c0bc28bf4996cd730 /src |
Checkpoint
Diffstat (limited to 'src')
-rw-r--r-- | src/ion.rs | 241 | ||||
-rw-r--r-- | src/main.rs | 65 | ||||
-rw-r--r-- | src/runtime.rs | 671 |
3 files changed, 977 insertions, 0 deletions
diff --git a/src/ion.rs b/src/ion.rs new file mode 100644 index 0000000..6773539 --- /dev/null +++ b/src/ion.rs @@ -0,0 +1,241 @@ +use std::iter::Copied; + +pub enum IonType { + Null = 0x00, + Bool = 0x01, + IntPos = 0x02, + IntNeg= 0x03, + Float = 0x04, + Decimal = 0x05, + Timestamp = 0x06, + Symbol = 0x07, + String = 0x08, + Clob = 0x09, + Blob = 0x0a, + List = 0x0b, + Sexp = 0x0c, + Struct = 0x0d, + Annotations = 0x0e, +} + +#[derive(PartialOrd, PartialEq)] +pub struct IonValue(Vec<u8>); + +impl IonValue { + pub fn new_null() -> IonValue { + IonValue(vec![0x0F]) + } + + pub fn ion_type(&self) -> u8 { + self.0[0] >> 4 + } + + pub fn len(&self) -> usize { + // TODO: Should this read the type length? + self.0.len() + } + + pub fn repr_offset(&self) -> usize { + let mut reader = self.reader(); + let tl = reader.next_byte(); + let _len = reader.extract_length(tl); + reader.offset() + } + + pub fn bytes(&self) -> impl DoubleEndedIterator<Item = u8> + '_ { + self.0.iter().copied() + } + + pub fn reader(&self) -> IonReader<impl DoubleEndedIterator<Item = u8> + '_> { + IonReader::new(self.0.iter().copied(), 0) + } + + pub fn reader_at(&self, offset: usize) -> IonReader<Copied<std::slice::Iter<'_, u8>>> { + IonReader::new(self.0[offset..].iter().copied(), offset) + } + + pub fn struct_reader_at(&self, offset: usize) -> IonReader<Copied<std::slice::Iter<'_, u8>>> { + IonReader::new_struct(self.0[offset..].iter().copied(), offset) + } + + pub fn to_usize(&self) -> usize { + self.reader().next_usize() + } + +} + +impl From<Vec<u8>> for IonValue { + fn from(value: Vec<u8>) -> Self { + if value.len() > 0 { + IonValue(value) + } else { + IonValue(vec![0x0F]) + } + } +} + + +pub struct IonReader<T> { + iter: T, + offset: usize, + is_struct: bool, + field_name: Option<usize>, +} + +impl <T> IonReader<T> + where T: Iterator<Item = u8> +{ + pub fn new(iter: T, offset: usize) -> IonReader<T> { + IonReader { + iter, + offset, + is_struct: false, + field_name: None, + } + } + + pub fn new_struct(iter: T, offset: usize) -> IonReader<T> { + IonReader { + iter, + offset, + is_struct: true, + field_name: None, + } + } + + pub fn offset(&self) -> usize { + self.offset + } + + pub fn field_id(&self) -> Option<usize> { + self.field_name + } + + /// Move the iterator to the first nested value. + pub fn step_in(&mut self) -> (u8, usize) { + self.prepare_next(); + let tl = self.next_byte(); + let len = self.extract_length(tl); + if (tl & 0xF0) == 0xD0 { + self.is_struct = true; + } + (tl >> 4, self.offset + len) + } + + fn prepare_next(&mut self) { + if self.is_struct { + // TODO: symbol length can be greater than max usize. + self.field_name = Some(self.next_varuint()); + } + } + + pub fn skip_value(&mut self) { + self.prepare_next(); + let tl = self.next_byte(); + let len = self.extract_length(tl); + for _ in 0..len { + self.next_byte(); + } + } + + pub fn next_value(&mut self) -> IonValue { + self.prepare_next(); + let tl = self.next_byte(); + let (mut buf, len) = match tl & 0x0F { + 0 | 15 => (vec![tl], 0), + 14 => { + let l = self.next_varuint(); + let mut v = Vec::with_capacity(5 + l); + v.push(tl); + push_varuint(&mut v, l); + (v, l) + }, + len => { + let l = len.into(); + let mut v = Vec::with_capacity(5 + l); + v.push(tl); + (v, l) + } + }; + for _ in 0..len { + let b = self.next_byte(); + buf.push(b); + } + buf.into() + } + + pub fn next_usize(&mut self) -> usize { + self.prepare_next(); + let tl = self.next_byte(); + if tl & 0xF0 != 0x20 { + panic!("Not a positive integer"); + } + + let len = self.extract_length(tl); + if len * 8 > usize::BITS as usize { + panic!("Integer too large for usize"); + } + + let mut value = 0; + for _ in 0..len { + let b = self.next_byte(); + value <<= 8; + value |= b as usize; + } + value + } + + fn next_byte(&mut self) -> u8 { + self.offset += 1; + self.iter.next().expect("Missing data") + } + + fn extract_length(&mut self, tl: u8) -> usize { + match tl & 0x0F { + 0 | 15 => 0, + 14 => self.next_varuint(), + len => len.into(), + } + } + + fn next_varuint(&mut self) -> usize { + let mut v: usize = 0; + while let Some(b) = self.iter.next() { + self.offset += 1; + v <<= 7; + v |= (b & 0x7f) as usize; + if b & 0x80 != 0 { + return v; + } + } + panic!("Truncated varuint"); + } +} + +fn push_varuint(v: &mut Vec<u8>, mut value: usize) { + let mut buf = [0; (usize::BITS / 7 + 1) as usize]; + let mut pos = 0; + while value != 0 { + buf[pos] = (value & 0x7F) as u8; + value >>= 7; + pos += 1; + } + buf[0] |= 0x80; + pos = pos.max(1); + + for i in (0..pos).rev() { + v.push(buf[i]); + } +} + +fn parse_varuint(buf: &[u8]) -> usize { + let mut value: usize = 0; + for b in buf { + value <<= 7; + value |= (b & 0x7F) as usize; + if b & 0x80 != 0 { + break; + } + } + value +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..78d1822 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,65 @@ +use std::fs::File; +use std::io; +use std::io::prelude::*; +use std::path::Path; + +use boa_interner::Interner; +use boa_parser::Parser; +use boa_parser::Source; +use ion::IonValue; +use runtime::Runtime; + +mod ion; +mod runtime; + +#[derive(Debug)] +pub enum RelJsError { + IoError(io::Error), + JsParseError(boa_parser::Error), + BytecodeEOF, +} + +impl From<io::Error> for RelJsError { + fn from(value: io::Error) -> Self { + RelJsError::IoError(value) + } +} + +impl From<boa_parser::Error> for RelJsError { + fn from(value: boa_parser::Error) -> Self { + RelJsError::JsParseError(value) + } +} + + + +fn main() -> Result<(), RelJsError> { + //let src = Source::from_filepath(Path::new("script.js"))?; + //let mut parser = Parser::new(src); + //let mut interner = Interner::new(); + //let script = parser.parse_script(&mut interner)?; + + let mut bin = File::open("demo/test-1.bin")?; + let mut data = Vec::new(); + bin.read_to_end(&mut data)?; + + let mut runtime = Runtime::new(); + let fnref = runtime.load_bytecode(data); + + let mut args = Vec::with_capacity(3); + args.push(IonValue::new_null()); + args.push(IonValue::new_null()); + args.push(IonValue::new_null()); + + let result = runtime.invoke(fnref, args); + + for (i, v) in result.iter().enumerate() { + println!("Result {i}:"); + for b in v.bytes() { + print!("{b:02x} "); + } + println!("\n"); + } + + Ok(()) +} diff --git a/src/runtime.rs b/src/runtime.rs new file mode 100644 index 0000000..3954a80 --- /dev/null +++ b/src/runtime.rs @@ -0,0 +1,671 @@ +use std::{collections::LinkedList, cmp::Ordering, sync::Arc}; + +use ion_rs::SymbolTable; + +use crate::ion::{IonReader, IonValue}; + + + + +#[repr(u8)] +pub enum OpCode { + + Jump = 0x01, + + Dup2 = 0x03, + + /// 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, + +} + +impl OpCode { + pub fn as_u8(&self) -> u8 { + unsafe { *(self as *const Self as *const u8) } + } +} + +pub struct Runtime { + symbols: SymbolTable, + stacks: LinkedList<StackFrame>, + functions: Vec<Arc<Function>>, +} + +pub enum ExecutionState { + Continue, + Halt, +} + +pub struct Function { + bytecode: ByteCode, + arguments: u16, + variables: u16, + expected_stack_depth: usize, +} + +pub type FnRef = usize; +pub type ByteCode = Vec<u8>; + +struct Stack(Vec<u8>); + +pub struct StackFrame { + func: Arc<Function>, + pc: usize, + variables: Vec<IonValue>, + stack: Stack, +} + +impl Runtime { + pub fn new() -> Runtime { + Runtime { + symbols: SymbolTable::new(), + stacks: LinkedList::new(), + functions: Vec::new(), + } + } + + pub fn load_bytecode(&mut self, data: Vec<u8>) -> FnRef { + let arguments = (data[0] as u16) << 8 | (data[1] as u16); + let variables = (data[2] as u16) << 8 | (data[3] as u16); + let func = Function { + bytecode: data.into_iter().skip(4).collect(), + arguments, + variables, + expected_stack_depth: 32, + }; + self.functions.push(func.into()); + self.functions.len() - 1 + } + + pub fn invoke(&mut self, fnref: FnRef, arguments: Vec<IonValue>) -> Vec<IonValue> { + let func = self.functions.get(fnref).expect("Undefined function").clone(); + let mut frame = StackFrame::new(func.clone(), 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(); + println!("PC: {:02X}, OP: {:02X}, Stack: {:02X}", frame.pc, op, frame.stack.0.len()); + for x in &frame.stack.0 { + print!(" {x:02X}"); + } + println!(); + + match op { + 0x01 => { // OpType::Jump + let pc = frame.next_varuint(); + frame.jump(pc); + }, + 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); + }, + 0x10 => { // OpType::Push + let buf = &frame.func.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 varid = frame.next_varuint(); + let value = frame.variables + .get(varid) + .expect("Undefined variable"); + + let mut reader = value.reader(); + let (ion_type, end) = reader.step_in(); + let pos = reader.offset(); + + 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 varid = frame.next_varuint(); + let mut value = if ion_type == 0x0D { + frame.variables + .get(varid) + .expect("Undefined variable") + .struct_reader_at(pos) + } else { + frame.variables + .get(varid) + .expect("Undefined variable") + .reader_at(pos) + }; + value.skip_value(); + let next = value.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(); + + frame.stack.push_usize(ion_type); // TODO peek + frame.stack.push_usize(len); // TODO peek + frame.stack.push_usize(pos); // TODO peek + + let varid = frame.next_varuint(); + let mut value = if ion_type == 0x0D { + frame.variables + .get(varid) + .expect("Undefined variable") + .struct_reader_at(pos) + } else { + frame.variables + .get(varid) + .expect("Undefined variable") + .reader_at(pos) + }; + + let (next_type, next_end) = value.step_in(); + let next_pos = value.offset(); + + 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(); + + frame.stack.push_usize(ion_type); // TODO peek + frame.stack.push_usize(len); // TODO peek + frame.stack.push_usize(pos); // TODO peek + + let varid = frame.next_varuint(); + let mut value = if ion_type == 0x0D { + frame.variables + .get(varid) + .expect("Undefined variable") + .struct_reader_at(pos) + } else { + frame.variables + .get(varid) + .expect("Undefined variable") + .reader_at(pos) + }; + + let next_value = value.next_value(); + 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(); + + frame.stack.push_usize(ion_type); // TODO peek + frame.stack.push_usize(len); // TODO peek + frame.stack.push_usize(pos); // TODO peek + + let varid = frame.next_varuint(); + let mut value = if ion_type == 0x0D { + frame.variables + .get(varid) + .expect("Undefined variable") + .struct_reader_at(pos) + } else { + frame.variables + .get(varid) + .expect("Undefined variable") + .reader_at(pos) + }; + + value.skip_value(); + if let Some(field_id) = value.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_varuint(); + 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_varuint(); + 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_varuint(); + 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_varuint(); + 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_varuint(); + 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_varuint(); + let value1 = frame.stack.pop_value(); + let value2 = frame.stack.pop_value(); + if value1 < value2 { + frame.pc = next_pc; + } + }, + _ => panic!("Invalid opcode {:02x} at {}", op, frame.pc), + } + + ExecutionState::Continue + } + + fn int_add(&self, frame: &mut StackFrame) { + todo!() + } +} + +impl StackFrame { + pub fn new(func: Arc<Function>, arguments: Vec<IonValue>) -> StackFrame { + let stack = Stack::new(func.expected_stack_depth); + let mut frame = StackFrame { + func, + pc: 0, + variables: arguments, + stack, + }; + + for _ in frame.func.arguments..frame.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.func.bytecode.len() + } + + fn next_byte(&mut self) -> u8 { + let b = self.func.bytecode[self.pc]; + self.pc += 1; + b + } + + fn next_bytes(&mut self, len: usize) -> &[u8] { + let end = self.pc + len; + let bytes = &self.func.bytecode[self.pc..end]; + self.pc = end; + bytes + } + + 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 peak_reader(&self) -> IonReader<impl DoubleEndedIterator<Item = u8> + '_> { + IonReader::new(self.0.iter().copied().rev(), 0) + } + + 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 push_value(&mut self, value: &IonValue) { + for byte in value.bytes().rev() { + self.0.push(byte); + } + } + + pub fn push_symbol(&mut self, value: usize) { + match value.cmp(&0) { + Ordering::Equal => self.0.push(0x70), + Ordering::Greater => { + let mut v = value; + let mut octets = 0; + while v != 0 { + self.0.push((v & 0xFF) as u8); + octets += 1; + v >>= 8; + } + let tl = 0x70 | octets; + self.0.push(tl); + }, + Ordering::Less => (), + } + } + + pub fn push_usize(&mut self, value: usize) { + match value.cmp(&0) { + Ordering::Equal => self.0.push(0x20), + Ordering::Greater => { + let mut v = value; + let mut octets = 0; + while v != 0 { + self.0.push((v & 0xFF) as u8); + octets += 1; + v >>= 8; + } + let tl = 0x20 | octets; + self.0.push(tl); + }, + Ordering::Less => (), + } + } + +} + |