corevm-engine 0.1.28

CoreVM engine that drives program execution either on the builder or CoreVM service side
Documentation
use corevm_types::{RecvHostMessageOutcome, RecvMessageOutcome};

/// Args for invoking an inner PVM.
#[derive(Debug)]
pub struct InvokeArgs {
	pub gas: i64,
	pub regs: [u64; 13],
}

impl InvokeArgs {
	/// Initialize the gas to the specified value, initialize the registers to zeroes.
	pub fn new(gas: i64) -> Self {
		Self { gas, regs: Default::default() }
	}

	/// Get the value of the specified register.
	pub fn reg(&self, reg: Reg) -> u64 {
		self.regs[reg as usize]
	}

	/// Set the value of the specified register.
	pub fn set_reg(&mut self, reg: Reg, value: u64) {
		self.regs[reg as usize] = value;
	}

	/// Set the contents of `A0` and optinally `A1` registers that typically store the return value
	/// of a function call.
	pub fn set_return_value<T: SetReturnValue>(&mut self, value: T) {
		value.set_return_value(self);
	}

	/// Get the value of the `i`-th argument's register.
	pub fn get_arg_reg(&self, i: usize) -> u64 {
		self.reg(Reg::ARG_REGS[i])
	}

	/// Get the value of the `i`-th argument.
	pub fn get_arg<T: Arg>(&mut self, i: &mut usize) -> T {
		Arg::get(self, i)
	}

	/// Set the value of the `i`-th argument's register.
	pub fn set_arg_reg(&mut self, i: usize, value: u64) {
		self.set_reg(Reg::ARG_REGS[i], value)
	}

	/// Set the value of the `i`-th argument.
	pub fn set_arg<T: Arg>(&mut self, i: &mut usize, value: T) {
		value.set(self, i)
	}
}

impl From<jam_types::InvokeArgs> for InvokeArgs {
	fn from(other: jam_types::InvokeArgs) -> Self {
		Self { gas: other.gas, regs: other.regs }
	}
}

impl From<InvokeArgs> for jam_types::InvokeArgs {
	fn from(other: InvokeArgs) -> Self {
		Self { gas: other.gas, regs: other.regs }
	}
}

/// Available registers.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(u32)]
pub enum Reg {
	RA = 0,
	SP = 1,
	T0 = 2,
	T1 = 3,
	T2 = 4,
	S0 = 5,
	S1 = 6,
	A0 = 7,
	A1 = 8,
	A2 = 9,
	A3 = 10,
	A4 = 11,
	A5 = 12,
}

impl Reg {
	pub const ARG_REGS: [Reg; 9] =
		[Reg::A0, Reg::A1, Reg::A2, Reg::A3, Reg::A4, Reg::A5, Reg::T0, Reg::T1, Reg::T2];
}

pub trait SetReturnValue {
	fn set_return_value(self, args: &mut InvokeArgs);
}

impl SetReturnValue for () {
	fn set_return_value(self, _args: &mut InvokeArgs) {}
}

impl SetReturnValue for u64 {
	fn set_return_value(self, args: &mut InvokeArgs) {
		args.set_reg(Reg::A0, self);
	}
}

impl SetReturnValue for RecvMessageOutcome {
	fn set_return_value(self, args: &mut InvokeArgs) {
		args.set_reg(Reg::A0, self.to_reg());
	}
}

impl SetReturnValue for RecvHostMessageOutcome {
	fn set_return_value(self, args: &mut InvokeArgs) {
		args.set_reg(Reg::A0, self.to_reg());
	}
}

set_return_value_impl_as! {
	bool => u64,
	u8 => u64,
	u16 => u64,
	u32 => u64,
	i8 => u64,
	i16 => u64,
	i32 => u64,
	i64 => u64
}

pub trait Arg {
	fn get(args: &InvokeArgs, i: &mut usize) -> Self
	where
		Self: Sized;

	fn set(self, args: &mut InvokeArgs, i: &mut usize);
}

impl Arg for u64 {
	fn get(args: &InvokeArgs, i: &mut usize) -> Self {
		let value = args.get_arg_reg(*i);
		*i += 1;
		value
	}

	fn set(self, args: &mut InvokeArgs, i: &mut usize) {
		args.set_arg_reg(*i, self);
		*i += 1;
	}
}

impl Arg for usize {
	fn get(args: &InvokeArgs, i: &mut usize) -> Self {
		u64::get(args, i) as usize
	}

	fn set(self, args: &mut InvokeArgs, i: &mut usize) {
		(self as u64).set(args, i);
	}
}

impl<T> Arg for *const T {
	fn get(args: &InvokeArgs, i: &mut usize) -> Self {
		usize::get(args, i) as Self
	}

	fn set(self, args: &mut InvokeArgs, i: &mut usize) {
		(self as usize).set(args, i);
	}
}

impl<T> Arg for *mut T {
	fn get(args: &InvokeArgs, i: &mut usize) -> Self {
		usize::get(args, i) as Self
	}

	fn set(self, args: &mut InvokeArgs, i: &mut usize) {
		(self as usize).set(args, i);
	}
}

impl Arg for bool {
	fn get(args: &InvokeArgs, i: &mut usize) -> Self {
		u64::get(args, i) != 0
	}

	fn set(self, args: &mut InvokeArgs, i: &mut usize) {
		(self as u64).set(args, i);
	}
}

// TODO @ivan We should use `try_into` rather than `as` for narrowing conversions.
arg_impl_as! {
	u8 => u64,
	u16 => u64,
	u32 => u64,
	i8 => u64,
	i16 => u64,
	i32 => u64,
	i64 => u64
}

macro_rules! set_return_value_impl_as {
    ($($type:ty => $as_type:ty),+) => {
        $(
            impl SetReturnValue for $type {
                fn set_return_value(self, args: &mut InvokeArgs) {
                    (self as $as_type).set_return_value(args);
                }
            }
        )+
    };
}

use set_return_value_impl_as;

macro_rules! arg_impl_as {
    ($($type:ty => $as_type:ty),+) => {
        $(
            impl Arg for $type {
                fn get(args: &InvokeArgs, i: &mut usize) -> Self {
                    <$as_type>::get(args, i) as Self
                }

                fn set(self, args: &mut InvokeArgs, i: &mut usize) {
                    (self as $as_type).set(args, i);
                }
            }
        )+
    };
}

use arg_impl_as;