summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2023-12-28 17:12:54 -0800
committerJesse Morgan <jesse@jesterpm.net>2023-12-28 17:12:54 -0800
commitd04b4dfa993681060d363b5ef994c1a635f3c1f9 (patch)
tree6d7ae65ed7c14e32df50858c0bc28bf4996cd730 /src
Checkpoint
Diffstat (limited to 'src')
-rw-r--r--src/ion.rs241
-rw-r--r--src/main.rs65
-rw-r--r--src/runtime.rs671
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 => (),
+ }
+ }
+
+}
+