2022-09-27 15:41:35 +00:00
|
|
|
use async_trait::async_trait;
|
2022-09-27 23:48:32 +00:00
|
|
|
use fogtix_bytecode::{consts, Atom, Instr, Parse, Pointer, Value as BcValue};
|
2022-09-27 14:39:28 +00:00
|
|
|
use std::sync::{Arc, RwLock};
|
2022-09-23 17:57:02 +00:00
|
|
|
|
2022-09-26 01:29:25 +00:00
|
|
|
pub type Module = Arc<dyn ModuleKind>;
|
|
|
|
|
|
|
|
pub trait ModuleKind: Send + Sync {
|
|
|
|
fn as_slice(&self) -> &[u8];
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: AsRef<[u8]> + Send + Sync + ?Sized> ModuleKind for T {
|
|
|
|
#[inline(always)]
|
|
|
|
fn as_slice(&self) -> &[u8] {
|
|
|
|
self.as_ref()
|
|
|
|
}
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
|
|
|
|
2022-09-26 01:29:25 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct InstrPtr {
|
|
|
|
pub m: Module,
|
|
|
|
pub pos: usize,
|
|
|
|
}
|
|
|
|
|
2022-09-26 01:33:31 +00:00
|
|
|
fn next_instr(m: &Module, pos: usize) -> Option<Result<(usize, Instr<'_>), &[u8]>> {
|
2022-09-26 01:29:25 +00:00
|
|
|
m.as_slice()
|
|
|
|
.get(pos..)
|
|
|
|
.map(|nxti_arr| match Instr::parse(nxti_arr) {
|
|
|
|
Err(_) => Err(&nxti_arr[..core::cmp::min(nxti_arr.len(), 20)]),
|
|
|
|
Ok((ptr, i)) => Ok((nxti_arr.len() - ptr.len(), i)),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
impl InstrPtr {
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn next_instr(&self) -> Option<Result<(usize, Instr<'_>), &[u8]>> {
|
|
|
|
next_instr(&self.m, self.pos)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_call2jump(&self) -> bool {
|
|
|
|
// tail-call optimization (would otherwise require much more opcodes)
|
|
|
|
matches!(self.next_instr(), Some(Ok((_, Instr::Return))))
|
|
|
|
}
|
|
|
|
}
|
2022-09-23 16:34:53 +00:00
|
|
|
|
2022-09-27 15:41:35 +00:00
|
|
|
#[async_trait]
|
2022-09-23 17:57:02 +00:00
|
|
|
pub trait Origin: Send + Sync + core::fmt::Debug {
|
2022-09-27 23:48:32 +00:00
|
|
|
async fn call(&self, p: &Pointer, stack: &mut Vec<StackEntValue>) -> InstrPtr;
|
2022-09-23 16:34:53 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 14:39:28 +00:00
|
|
|
pub type KnownOrigins = Arc<[RwLock<Option<Box<dyn Origin>>>; 0x10000]>;
|
|
|
|
|
|
|
|
fn make_large_arc_slice<const N: usize, T, F: Fn() -> T>(f: F) -> Arc<[T; N]> {
|
|
|
|
// see also: https://stackoverflow.com/a/68122278
|
|
|
|
// license: CC BY-SA 4.0; author: "Johannes Maria Frank"
|
|
|
|
let bxs = core::iter::repeat_with(f).take(N).collect::<Arc<[T]>>();
|
|
|
|
let ptr = Arc::into_raw(bxs) as *mut [T; N];
|
|
|
|
unsafe { Arc::from_raw(ptr) }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn make_default_origins() -> KnownOrigins {
|
|
|
|
make_large_arc_slice::<0x10000, RwLock<Option<Box<dyn Origin>>>, _>(|| RwLock::new(None))
|
2022-09-23 16:34:53 +00:00
|
|
|
}
|
|
|
|
|
2022-09-23 14:22:21 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2022-09-23 17:57:02 +00:00
|
|
|
pub enum StackEntValue {
|
2022-09-23 14:22:21 +00:00
|
|
|
Bytes(Vec<u8>),
|
|
|
|
Int(u64),
|
|
|
|
Atom(Atom),
|
2022-09-27 14:39:28 +00:00
|
|
|
Pointer(Pointer),
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 16:22:00 +00:00
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub enum Error {
|
|
|
|
#[error("`{0}` arrived at non-jump target `{1}` @ {2}")]
|
|
|
|
InvalidJumpTarget(&'static str, String, usize),
|
|
|
|
|
|
|
|
#[error("instruction pointer out-of-bounds")]
|
|
|
|
InstrpOutOfBounds,
|
|
|
|
|
|
|
|
#[error("reached unparsable instruction: {0:?}")]
|
|
|
|
UnparsableInstruction(Vec<u8>),
|
|
|
|
|
|
|
|
#[error("out of fuel")]
|
|
|
|
OutOfFuel,
|
|
|
|
|
|
|
|
#[error("not enough operands on stack")]
|
|
|
|
NotEnoughStacked,
|
|
|
|
|
|
|
|
#[error("call-r called with invalid/poisoned origin {0:x}")]
|
|
|
|
InvalidOrigin(u16),
|
|
|
|
|
|
|
|
#[error("operand from stack doesn't have correct data type: expected={expected}, got={got}")]
|
|
|
|
StackedInvalidType { expected: &'static str, got: String },
|
2022-09-27 23:48:32 +00:00
|
|
|
|
|
|
|
#[error("tried to divide by zero")]
|
|
|
|
DivisionByZero,
|
2022-09-27 16:22:00 +00:00
|
|
|
}
|
|
|
|
|
2022-09-26 01:29:25 +00:00
|
|
|
pub struct Process {
|
2022-09-27 14:39:28 +00:00
|
|
|
pub korigs: KnownOrigins,
|
2022-09-26 03:09:14 +00:00
|
|
|
pub stack: Vec<StackEntValue>,
|
|
|
|
pub callstack: Vec<InstrPtr>,
|
|
|
|
pub instrp: InstrPtr,
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 16:22:00 +00:00
|
|
|
fn verify_jumptarget_explicit(
|
|
|
|
previptr: usize,
|
|
|
|
jinstr: &'static str,
|
|
|
|
jtip: &InstrPtr,
|
|
|
|
) -> Result<(), Error> {
|
2022-09-27 14:39:28 +00:00
|
|
|
if let Some(Ok((_, trgi))) = jtip.next_instr() {
|
|
|
|
if trgi != Instr::Label {
|
2022-09-27 16:22:00 +00:00
|
|
|
return Err(Error::InvalidJumpTarget(
|
2022-09-27 14:39:28 +00:00
|
|
|
jinstr,
|
2022-09-27 16:22:00 +00:00
|
|
|
format!("{:?}", trgi),
|
2022-09-27 14:39:28 +00:00
|
|
|
previptr,
|
2022-09-27 16:22:00 +00:00
|
|
|
));
|
2022-09-23 21:25:47 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-27 16:22:00 +00:00
|
|
|
Ok(())
|
2022-09-27 14:39:28 +00:00
|
|
|
}
|
2022-09-23 21:25:47 +00:00
|
|
|
|
2022-09-28 00:08:00 +00:00
|
|
|
fn u128_binmath(a: u128, b: u128, mbo: consts::MathBinOp) -> Result<StackEntValue, Error> {
|
2022-09-27 23:48:32 +00:00
|
|
|
use consts::MathBinOp as B;
|
2022-09-28 00:08:00 +00:00
|
|
|
Ok(StackEntValue::Atom(Atom::from(match mbo {
|
2022-09-27 23:48:32 +00:00
|
|
|
B::Concat => {
|
|
|
|
return Err(Error::StackedInvalidType {
|
|
|
|
expected: "bytes,bytes",
|
|
|
|
got: "[int|atom, int|atom]".to_string(),
|
|
|
|
})
|
|
|
|
}
|
2022-09-28 00:08:00 +00:00
|
|
|
B::Lt => return Ok(StackEntValue::Int(if a < b { 1 } else { 0 })),
|
|
|
|
B::Eq => return Ok(StackEntValue::Int(if a < b { 1 } else { 0 })),
|
2022-09-27 23:48:32 +00:00
|
|
|
B::And => a & b,
|
|
|
|
B::Or => a | b,
|
|
|
|
B::Xor => a ^ b,
|
|
|
|
B::Add => a.wrapping_add(b),
|
|
|
|
B::Sub => a.wrapping_sub(b),
|
|
|
|
B::Mul => a.wrapping_mul(b),
|
|
|
|
B::Div => match a.checked_div(b) {
|
|
|
|
Some(x) => x,
|
|
|
|
None => return Err(Error::DivisionByZero),
|
|
|
|
},
|
|
|
|
B::Rem => match a.checked_rem(b) {
|
|
|
|
Some(x) => x,
|
|
|
|
None => return Err(Error::DivisionByZero),
|
|
|
|
},
|
2022-09-28 00:08:00 +00:00
|
|
|
})))
|
2022-09-27 23:48:32 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 14:39:28 +00:00
|
|
|
impl Process {
|
2022-09-27 16:22:00 +00:00
|
|
|
fn verify_jumptarget(&self, previptr: usize, jinstr: &'static str) -> Result<(), Error> {
|
2022-09-27 14:39:28 +00:00
|
|
|
verify_jumptarget_explicit(previptr, jinstr, &self.instrp)
|
2022-09-25 14:50:24 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 23:48:32 +00:00
|
|
|
fn stpop(&mut self) -> Result<StackEntValue, Error> {
|
|
|
|
self.stack.pop().ok_or(Error::NotEnoughStacked)
|
|
|
|
}
|
|
|
|
|
2022-09-27 16:22:00 +00:00
|
|
|
pub async fn run(&mut self, mut fuel: Option<&mut u64>) -> Result<(), Error> {
|
2022-09-23 14:22:21 +00:00
|
|
|
loop {
|
2022-09-26 01:29:25 +00:00
|
|
|
let previptr = self.instrp.pos;
|
2022-09-23 19:49:12 +00:00
|
|
|
tracing::trace!("previptr = {}", previptr);
|
2022-09-27 14:39:28 +00:00
|
|
|
if let Some(ref mut x) = fuel {
|
|
|
|
if **x == 0 {
|
2022-09-27 16:22:00 +00:00
|
|
|
return Err(Error::OutOfFuel);
|
2022-09-26 03:56:51 +00:00
|
|
|
}
|
2022-09-27 14:39:28 +00:00
|
|
|
**x -= 1;
|
2022-09-26 03:56:51 +00:00
|
|
|
}
|
2022-09-26 01:29:25 +00:00
|
|
|
let (nxtidelta, nxti) = match next_instr(&self.instrp.m, self.instrp.pos) {
|
2022-09-27 16:22:00 +00:00
|
|
|
None => return Err(Error::InstrpOutOfBounds),
|
|
|
|
Some(Err(code)) => return Err(Error::UnparsableInstruction(code.to_vec())),
|
2022-09-26 01:29:25 +00:00
|
|
|
Some(Ok(x)) => x,
|
2022-09-23 14:22:21 +00:00
|
|
|
};
|
2022-09-26 01:29:25 +00:00
|
|
|
self.instrp.pos += nxtidelta;
|
2022-09-23 14:22:21 +00:00
|
|
|
match nxti {
|
|
|
|
Instr::Label => {}
|
2022-09-27 23:48:32 +00:00
|
|
|
Instr::CallRemote => {
|
2022-09-26 01:29:25 +00:00
|
|
|
if !self.instrp.is_call2jump() {
|
2022-09-23 15:34:08 +00:00
|
|
|
self.callstack.push(self.instrp.clone());
|
|
|
|
}
|
2022-09-27 23:48:32 +00:00
|
|
|
let mut ptr = match self.stpop()? {
|
|
|
|
StackEntValue::Pointer(ptr) => ptr,
|
|
|
|
x => {
|
2022-09-27 16:22:00 +00:00
|
|
|
return Err(Error::StackedInvalidType {
|
|
|
|
expected: "pointer",
|
|
|
|
got: format!("{:?}", x),
|
2022-09-27 23:48:32 +00:00
|
|
|
})
|
2022-09-23 16:34:53 +00:00
|
|
|
}
|
2022-09-27 23:48:32 +00:00
|
|
|
};
|
|
|
|
let argc = match self.stpop()? {
|
|
|
|
StackEntValue::Int(i) => i,
|
|
|
|
x => {
|
|
|
|
return Err(Error::StackedInvalidType {
|
|
|
|
expected: "int",
|
|
|
|
got: format!("{:?}", x),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let ssl = self.stack.len();
|
|
|
|
let argstart = match usize::try_from(argc)
|
|
|
|
.ok()
|
|
|
|
.and_then(|argc| ssl.checked_sub(argc))
|
|
|
|
{
|
|
|
|
Some(x) => x,
|
|
|
|
None => return Err(Error::NotEnoughStacked),
|
|
|
|
};
|
|
|
|
let mut args = self.stack.drain(argstart..).collect();
|
|
|
|
let origin_id = ptr.origin();
|
|
|
|
ptr.set_origin(0);
|
|
|
|
self.instrp = match self.korigs[usize::from(origin_id)].read() {
|
|
|
|
Ok(origin) => match origin.as_ref() {
|
|
|
|
Some(origin) => origin.call(&ptr, &mut args).await,
|
|
|
|
None => return Err(Error::InvalidOrigin(origin_id)),
|
|
|
|
},
|
|
|
|
Err(_) => return Err(Error::InvalidOrigin(origin_id)),
|
|
|
|
};
|
|
|
|
self.verify_jumptarget(previptr, "call-r")?;
|
|
|
|
let rargc = u64::try_from(args.len()).unwrap();
|
|
|
|
self.stack.extend(args);
|
|
|
|
self.stack.push(StackEntValue::Int(rargc));
|
2022-09-23 18:16:19 +00:00
|
|
|
}
|
|
|
|
Instr::CallLocal(x) => {
|
2022-09-26 01:29:25 +00:00
|
|
|
if !self.instrp.is_call2jump() {
|
2022-09-23 18:16:19 +00:00
|
|
|
self.callstack.push(self.instrp.clone());
|
|
|
|
}
|
2022-09-26 01:29:25 +00:00
|
|
|
self.instrp.pos = match x.try_into() {
|
2022-09-23 18:16:19 +00:00
|
|
|
Ok(y) => y,
|
2022-09-27 16:22:00 +00:00
|
|
|
Err(_) => return Err(Error::InstrpOutOfBounds),
|
2022-09-23 18:16:19 +00:00
|
|
|
};
|
2022-09-27 16:22:00 +00:00
|
|
|
self.verify_jumptarget(previptr, "call-l")?;
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
2022-09-25 14:50:24 +00:00
|
|
|
Instr::CallLDefer(x) => match x.try_into() {
|
2022-09-26 01:29:25 +00:00
|
|
|
Ok(pos) => {
|
|
|
|
let jtip = InstrPtr {
|
|
|
|
m: self.instrp.m.clone(),
|
|
|
|
pos,
|
|
|
|
};
|
2022-09-27 16:22:00 +00:00
|
|
|
verify_jumptarget_explicit(previptr, "call-l-defer", &jtip)?;
|
2022-09-26 01:29:25 +00:00
|
|
|
if self.instrp.is_call2jump() {
|
2022-09-25 15:08:23 +00:00
|
|
|
self.instrp = jtip;
|
|
|
|
} else {
|
|
|
|
self.callstack.push(jtip);
|
|
|
|
}
|
2022-09-25 14:50:24 +00:00
|
|
|
}
|
2022-09-27 16:22:00 +00:00
|
|
|
Err(_) => return Err(Error::InstrpOutOfBounds),
|
2022-09-25 14:50:24 +00:00
|
|
|
},
|
2022-09-23 23:49:44 +00:00
|
|
|
Instr::JumpCond(x) => {
|
|
|
|
let x: usize = match x.try_into() {
|
|
|
|
Ok(y) => y,
|
2022-09-27 16:22:00 +00:00
|
|
|
Err(_) => return Err(Error::InstrpOutOfBounds),
|
2022-09-23 23:49:44 +00:00
|
|
|
};
|
2022-09-27 23:48:32 +00:00
|
|
|
let doit = match self.stpop()? {
|
|
|
|
StackEntValue::Int(i) => i != 0,
|
|
|
|
StackEntValue::Atom(a) => a != Atom([0; 16]),
|
|
|
|
z => {
|
2022-09-27 16:22:00 +00:00
|
|
|
return Err(Error::StackedInvalidType {
|
|
|
|
expected: "atom | int",
|
|
|
|
got: format!("{:?}", z),
|
2022-09-27 23:48:32 +00:00
|
|
|
})
|
2022-09-23 23:49:44 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
if doit {
|
2022-09-26 01:29:25 +00:00
|
|
|
self.instrp.pos = x;
|
2022-09-27 16:22:00 +00:00
|
|
|
self.verify_jumptarget(previptr, "jump-cond")?;
|
2022-09-23 23:49:44 +00:00
|
|
|
}
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
|
|
|
Instr::Return => match self.callstack.pop() {
|
|
|
|
Some(x) => self.instrp = x,
|
2022-09-27 16:22:00 +00:00
|
|
|
None => break Ok(()),
|
2022-09-23 14:22:21 +00:00
|
|
|
},
|
|
|
|
Instr::Push(v) => {
|
|
|
|
self.stack.push(match v {
|
|
|
|
BcValue::Bytes(v) => StackEntValue::Bytes(v.to_vec()),
|
|
|
|
BcValue::Int(i) => StackEntValue::Int(i),
|
|
|
|
BcValue::Atom(a) => StackEntValue::Atom(a),
|
2022-09-27 14:39:28 +00:00
|
|
|
BcValue::Pointer(p) => StackEntValue::Pointer(p),
|
2022-09-23 14:22:21 +00:00
|
|
|
});
|
|
|
|
}
|
2022-09-27 16:22:00 +00:00
|
|
|
Instr::Pop(_) if self.stack.is_empty() => return Err(Error::NotEnoughStacked),
|
2022-09-23 14:22:21 +00:00
|
|
|
Instr::Pop(cnt) => {
|
|
|
|
let ssl = self.stack.len() - 1;
|
2022-09-24 23:58:51 +00:00
|
|
|
let cnt = usize::from(cnt);
|
|
|
|
if cnt >= ssl {
|
|
|
|
self.stack = Vec::new();
|
|
|
|
} else {
|
|
|
|
self.stack.truncate(ssl - cnt - 1);
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-24 23:58:51 +00:00
|
|
|
Instr::Dup(delta) => {
|
|
|
|
let x = match self.stack.len().checked_sub(usize::from(delta) + 1) {
|
2022-09-27 16:22:00 +00:00
|
|
|
None => return Err(Error::NotEnoughStacked),
|
2022-09-24 23:58:51 +00:00
|
|
|
// SAFETY: the value x is always smaller than the stack height
|
|
|
|
Some(x) => self.stack[x].clone(),
|
2022-09-23 14:22:21 +00:00
|
|
|
};
|
|
|
|
self.stack.push(x);
|
|
|
|
}
|
2022-09-25 13:05:01 +00:00
|
|
|
Instr::Swap(delta) => {
|
2022-09-23 14:22:21 +00:00
|
|
|
let ssl = self.stack.len();
|
2022-09-25 13:05:01 +00:00
|
|
|
let (y, z) = match ssl.checked_sub(usize::from(delta) + 2) {
|
2022-09-27 16:22:00 +00:00
|
|
|
None => return Err(Error::NotEnoughStacked),
|
2022-09-25 13:05:01 +00:00
|
|
|
Some(ltrg) => self.stack[ltrg..].split_at_mut(1),
|
|
|
|
};
|
2022-09-23 14:22:21 +00:00
|
|
|
core::mem::swap(&mut y[0], &mut z[0]);
|
|
|
|
}
|
2022-09-27 23:48:32 +00:00
|
|
|
Instr::ADecon => match self.stpop()? {
|
|
|
|
StackEntValue::Atom(atom) => {
|
2022-09-27 13:19:25 +00:00
|
|
|
let (b, a) = atom.into();
|
2022-09-23 14:22:21 +00:00
|
|
|
self.stack.push(StackEntValue::Int(b));
|
|
|
|
self.stack.push(StackEntValue::Int(a));
|
|
|
|
}
|
|
|
|
x => {
|
2022-09-27 16:22:00 +00:00
|
|
|
return Err(Error::StackedInvalidType {
|
|
|
|
expected: "atom",
|
|
|
|
got: format!("{:?}", x),
|
|
|
|
})
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
|
|
|
},
|
2022-09-27 23:48:32 +00:00
|
|
|
Instr::DoMath1(ubo) => {
|
|
|
|
use consts::MathUnOp as U;
|
|
|
|
use StackEntValue as V;
|
|
|
|
let x = self.stpop()?;
|
|
|
|
self.stack.push(match (x, ubo) {
|
|
|
|
(V::Int(x), U::Not) => V::Int(!x),
|
|
|
|
(V::Atom(x), U::Not) => V::Atom(Atom::from(!u128::from(x))),
|
|
|
|
(V::Bytes(mut x), U::Not) => {
|
|
|
|
x.iter_mut().for_each(|i| *i = !*i);
|
|
|
|
V::Bytes(x)
|
|
|
|
}
|
|
|
|
(x, _) => {
|
|
|
|
return Err(Error::StackedInvalidType {
|
|
|
|
expected: "int|atom|bytes",
|
|
|
|
got: format!("{:?}", x),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2022-09-23 14:22:21 +00:00
|
|
|
Instr::DoMath2(mbo) => {
|
2022-09-27 23:48:32 +00:00
|
|
|
let b = self.stpop()?;
|
|
|
|
let a = self.stpop()?;
|
|
|
|
use consts::MathBinOp as B;
|
|
|
|
use StackEntValue as V;
|
2022-09-23 14:22:21 +00:00
|
|
|
self.stack.push(match (a, b) {
|
2022-09-27 23:48:32 +00:00
|
|
|
(V::Int(a), V::Int(b)) => match mbo {
|
|
|
|
B::Concat => V::Atom(Atom::from((a, b))),
|
2022-09-28 00:08:00 +00:00
|
|
|
B::Lt => V::Int(if a < b { 1 } else { 0 }),
|
|
|
|
B::Eq => V::Int(if a == b { 1 } else { 0 }),
|
2022-09-27 23:48:32 +00:00
|
|
|
B::And => V::Int(a & b),
|
|
|
|
B::Or => V::Int(a | b),
|
|
|
|
B::Xor => V::Int(a ^ b),
|
|
|
|
B::Add => V::Int(a.wrapping_add(b)),
|
|
|
|
B::Sub => V::Int(a.wrapping_sub(b)),
|
|
|
|
B::Mul => {
|
|
|
|
V::Atom(Atom::from(u128::from(a).wrapping_mul(u128::from(b))))
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
2022-09-27 23:48:32 +00:00
|
|
|
B::Div => V::Int(match a.checked_div(b) {
|
|
|
|
Some(x) => x,
|
|
|
|
None => return Err(Error::DivisionByZero),
|
|
|
|
}),
|
|
|
|
B::Rem => V::Int(match a.checked_rem(b) {
|
|
|
|
Some(x) => x,
|
|
|
|
None => return Err(Error::DivisionByZero),
|
|
|
|
}),
|
2022-09-23 14:22:21 +00:00
|
|
|
},
|
2022-09-27 23:48:32 +00:00
|
|
|
(V::Atom(a), V::Atom(b)) => {
|
2022-09-28 00:08:00 +00:00
|
|
|
u128_binmath(u128::from(a), u128::from(b), mbo)?
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
2022-09-28 00:08:00 +00:00
|
|
|
(V::Int(a), V::Atom(b)) => u128_binmath(u128::from(a), u128::from(b), mbo)?,
|
|
|
|
(V::Atom(a), V::Int(b)) => u128_binmath(u128::from(a), u128::from(b), mbo)?,
|
|
|
|
(V::Bytes(a), V::Bytes(b)) => match mbo {
|
2022-09-27 23:48:32 +00:00
|
|
|
B::Concat => {
|
|
|
|
let mut x = Vec::with_capacity(a.len() + b.len());
|
|
|
|
x.extend_from_slice(&a);
|
|
|
|
x.extend_from_slice(&b);
|
2022-09-28 00:08:00 +00:00
|
|
|
V::Bytes(x)
|
2022-09-27 23:48:32 +00:00
|
|
|
}
|
2022-09-28 00:08:00 +00:00
|
|
|
B::Eq => V::Int(if a == b { 1 } else { 0 }),
|
2022-09-27 23:48:32 +00:00
|
|
|
B::And => {
|
|
|
|
let mut x: Vec<_> =
|
|
|
|
a.iter().zip(b.iter()).map(|(w, v)| w & v).collect();
|
|
|
|
let minp = x.len();
|
|
|
|
x.extend_from_slice(
|
|
|
|
&(if a.len() < b.len() { b } else { a })[minp..],
|
|
|
|
);
|
2022-09-28 00:08:00 +00:00
|
|
|
V::Bytes(x)
|
2022-09-27 23:48:32 +00:00
|
|
|
}
|
|
|
|
B::Or => {
|
|
|
|
let mut x: Vec<_> =
|
|
|
|
a.iter().zip(b.iter()).map(|(w, v)| w | v).collect();
|
|
|
|
let minp = x.len();
|
|
|
|
x.extend_from_slice(
|
|
|
|
&(if a.len() < b.len() { b } else { a })[minp..],
|
|
|
|
);
|
2022-09-28 00:08:00 +00:00
|
|
|
V::Bytes(x)
|
2022-09-27 23:48:32 +00:00
|
|
|
}
|
|
|
|
B::Xor => {
|
|
|
|
let mut x: Vec<_> =
|
|
|
|
a.iter().zip(b.iter()).map(|(w, v)| w ^ v).collect();
|
|
|
|
let minp = x.len();
|
|
|
|
x.extend_from_slice(
|
|
|
|
&(if a.len() < b.len() { b } else { a })[minp..],
|
|
|
|
);
|
2022-09-28 00:08:00 +00:00
|
|
|
V::Bytes(x)
|
2022-09-27 23:48:32 +00:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(Error::StackedInvalidType {
|
|
|
|
expected: "[int|atom, int|atom]",
|
|
|
|
got: "[bytes, bytes]".to_string(),
|
|
|
|
})
|
|
|
|
}
|
2022-09-28 00:08:00 +00:00
|
|
|
},
|
|
|
|
_ if mbo == B::Eq => V::Int(0),
|
2022-09-27 23:48:32 +00:00
|
|
|
(a, b) => {
|
2022-09-27 16:22:00 +00:00
|
|
|
return Err(Error::StackedInvalidType {
|
2022-09-27 23:48:32 +00:00
|
|
|
expected: "[int|atom, int|atom] | [bytes,bytes]",
|
|
|
|
got: format!("{:?}, {:?}", a, b),
|
2022-09-27 16:22:00 +00:00
|
|
|
})
|
2022-09-23 14:22:21 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-26 03:56:51 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2022-09-27 14:39:28 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn stack_item_size() {
|
|
|
|
assert_eq!(core::mem::size_of::<StackEntValue>(), 32);
|
|
|
|
}
|
|
|
|
|
2022-09-26 03:56:51 +00:00
|
|
|
proptest::proptest! {
|
2022-09-28 00:08:00 +00:00
|
|
|
#![proptest_config(proptest::prelude::ProptestConfig::with_cases(8192))]
|
2022-09-26 03:56:51 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn doesnt_crash(inp in proptest::collection::vec(0..=u8::MAX, 0..1024)) {
|
2022-09-27 14:39:28 +00:00
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
// we use a static here to avoid allocating the very large array on every iteration
|
|
|
|
static KORIGS: Lazy<KnownOrigins> = Lazy::new(|| make_default_origins());
|
2022-09-26 03:56:51 +00:00
|
|
|
let inp: Arc<Vec<u8>> = Arc::new(inp);
|
|
|
|
let module: Module = inp;
|
2022-09-27 14:39:28 +00:00
|
|
|
let mut p = Process {
|
|
|
|
korigs: Arc::clone(&*KORIGS),
|
|
|
|
stack: Vec::new(),
|
|
|
|
callstack: Vec::new(),
|
|
|
|
instrp: InstrPtr {
|
|
|
|
m: module,
|
|
|
|
pos: 0,
|
|
|
|
},
|
|
|
|
};
|
2022-09-28 00:08:00 +00:00
|
|
|
let _ = futures_lite::future::block_on(p.run(Some(&mut 2048)));
|
2022-09-26 03:56:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|