API BREAK: bytecode: get rid of Label
This commit is contained in:
parent
e83e9f8394
commit
4ff3b98de9
|
@ -5,7 +5,6 @@ use int_enum::IntEnum;
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, IntEnum)]
|
||||
pub enum OpType {
|
||||
// control flow
|
||||
Label = 0x4c, /* L */
|
||||
CallRemote = 0x43, /* C */
|
||||
CallLocal = 0x49, /* I */
|
||||
CallLDefer = 0x64, /* d */
|
||||
|
|
|
@ -4,9 +4,6 @@ use core::fmt;
|
|||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Instr {
|
||||
// control flow
|
||||
/// defines a destination label
|
||||
Label,
|
||||
|
||||
/// pops the descriptor table pointer from the stack,
|
||||
/// then tries to call it with the top $0 stack elements as arguments.
|
||||
/// thus all-in-all this pops `1+$0` values from the stack, then
|
||||
|
@ -64,7 +61,6 @@ impl Instr {
|
|||
#[inline]
|
||||
pub fn typ(&self) -> OpType {
|
||||
match self {
|
||||
Instr::Label => OpType::Label,
|
||||
Instr::CallRemote => OpType::CallRemote,
|
||||
Instr::CallLocal(_) => OpType::CallLocal,
|
||||
Instr::CallLDefer(_) => OpType::CallLDefer,
|
||||
|
@ -119,7 +115,6 @@ impl<'a> crate::Parse<'a> for Instr {
|
|||
OpType::Shift => i8::parse(inp).map(|(inp, shv)| (inp, Instr::Shift(shv))),
|
||||
OpType::DoMath1 => MathUnOp::parse(inp).map(|(inp, val)| (inp, Instr::DoMath1(val))),
|
||||
OpType::DoMath2 => MathBinOp::parse(inp).map(|(inp, val)| (inp, Instr::DoMath2(val))),
|
||||
OpType::Label => Ok((inp, Instr::Label)),
|
||||
OpType::CallRemote => Ok((inp, Instr::CallRemote)),
|
||||
OpType::Return => Ok((inp, Instr::Return)),
|
||||
OpType::DupFrom => Ok((inp, Instr::DupFrom)),
|
||||
|
@ -144,7 +139,7 @@ impl Instr {
|
|||
Instr::Shift(shv) => writer.write_all(&shv.to_be_bytes()),
|
||||
Instr::DoMath1(val) => writer.write_all(&val.int_value().to_be_bytes()),
|
||||
Instr::DoMath2(val) => writer.write_all(&val.int_value().to_be_bytes()),
|
||||
Instr::Label | Instr::CallRemote | Instr::Return | Instr::DupFrom => Ok(()),
|
||||
Instr::CallRemote | Instr::Return | Instr::DupFrom => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ pub type StackEntValue = u64;
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error<'m> {
|
||||
InstrpOutOfBounds,
|
||||
UnparsableInstruction(&'m [u8]),
|
||||
OutOfFuel,
|
||||
NotEnoughStacked,
|
||||
|
@ -33,7 +32,6 @@ pub enum Error<'m> {
|
|||
impl fmt::Display for Error<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::InstrpOutOfBounds => write!(f, "instruction pointer out-of-bounds"),
|
||||
Self::UnparsableInstruction(x) => write!(f, "reached unparsable instruction: {:x?}", x),
|
||||
Self::OutOfFuel => write!(f, "out of fuel"),
|
||||
Self::NotEnoughStacked => write!(f, "not enough operands on stack"),
|
||||
|
@ -50,7 +48,6 @@ pub struct VerifyError<'m> {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum VerifyErrorKind<'m> {
|
||||
InvalidJumpTarget { invoked_by: &'static str, to: usize },
|
||||
InstrpOutOfBounds,
|
||||
UnparsableInstruction(&'m [u8]),
|
||||
}
|
||||
|
@ -60,9 +57,6 @@ impl fmt::Display for VerifyError<'_> {
|
|||
use VerifyErrorKind as K;
|
||||
write!(f, "at instruction {}: ", self.from)?;
|
||||
match &self.kind {
|
||||
K::InvalidJumpTarget { invoked_by, to } => {
|
||||
write!(f, "`{}` arrived at non-jump target `{}`", invoked_by, to)
|
||||
}
|
||||
K::InstrpOutOfBounds => write!(f, "instruction pointer out-of-bounds"),
|
||||
K::UnparsableInstruction(x) => write!(f, "reached unparsable instruction: {:x?}", x),
|
||||
}
|
||||
|
@ -73,25 +67,6 @@ impl fmt::Display for VerifyError<'_> {
|
|||
/// that jump targets are valid (this makes it possible to avoid unnecessary
|
||||
/// checks during tight loops)
|
||||
pub fn verify(m: &[u8]) -> Result<(), VerifyError<'_>> {
|
||||
let check_jump =
|
||||
|jinstr: &'static str, from: usize, to: u64| -> Result<(), VerifyError<'static>> {
|
||||
match usize::try_from(to) {
|
||||
Ok(to) if to == 0 => Ok(()),
|
||||
Ok(to) if matches!(next_instr(m, to), Some(Ok((_, Instr::Label)))) => Ok(()),
|
||||
Ok(to) => Err(VerifyError {
|
||||
from,
|
||||
kind: VerifyErrorKind::InvalidJumpTarget {
|
||||
invoked_by: jinstr,
|
||||
to,
|
||||
},
|
||||
}),
|
||||
Err(_) => Err(VerifyError {
|
||||
from,
|
||||
kind: VerifyErrorKind::InstrpOutOfBounds,
|
||||
}),
|
||||
}
|
||||
};
|
||||
|
||||
let mut instrp = 0;
|
||||
|
||||
while let Some(nxti) = next_instr(m, instrp) {
|
||||
|
@ -102,11 +77,24 @@ pub fn verify(m: &[u8]) -> Result<(), VerifyError<'_>> {
|
|||
assert_ne!(nxtidelta, 0);
|
||||
instrp += nxtidelta;
|
||||
match nxti {
|
||||
Instr::CallLocal(x) => check_jump("call-l", instrp, x)?,
|
||||
Instr::CallLDefer(x) => check_jump("call-l-defer", instrp, x)?,
|
||||
Instr::JumpCond(x) => check_jump("jump-cond", instrp, x)?,
|
||||
Instr::Label
|
||||
| Instr::CallRemote
|
||||
Instr::CallLocal(x) | Instr::CallLDefer(x) | Instr::JumpCond(x) => {
|
||||
match usize::try_from(x) {
|
||||
Err(_) => {
|
||||
return Err(VerifyError {
|
||||
from: usize::MAX,
|
||||
kind: VerifyErrorKind::InstrpOutOfBounds,
|
||||
})
|
||||
}
|
||||
Ok(x) if x >= m.len() => {
|
||||
return Err(VerifyError {
|
||||
from: x,
|
||||
kind: VerifyErrorKind::InstrpOutOfBounds,
|
||||
})
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
}
|
||||
Instr::CallRemote
|
||||
| Instr::Return
|
||||
| Instr::Push(_)
|
||||
| Instr::Pop(_)
|
||||
|
@ -144,14 +132,11 @@ impl<'m> Process<'m> {
|
|||
}
|
||||
**x -= 1;
|
||||
}
|
||||
let (nxtidelta, nxti) = match next_instr(self.m, self.instrp) {
|
||||
None => return Err(Error::InstrpOutOfBounds),
|
||||
Some(Err(code)) => return Err(Error::UnparsableInstruction(code)),
|
||||
Some(Ok(x)) => x,
|
||||
};
|
||||
let (nxtidelta, nxti) = next_instr(self.m, self.instrp)
|
||||
.unwrap()
|
||||
.map_err(Error::UnparsableInstruction)?;
|
||||
self.instrp += nxtidelta;
|
||||
match nxti {
|
||||
Instr::Label => {}
|
||||
Instr::CallRemote => {
|
||||
if !is_call2jump(self.m, self.instrp) {
|
||||
self.callstack.push(self.instrp);
|
||||
|
@ -162,28 +147,19 @@ impl<'m> Process<'m> {
|
|||
if !is_call2jump(self.m, self.instrp) {
|
||||
self.callstack.push(self.instrp);
|
||||
}
|
||||
self.instrp = match x.try_into() {
|
||||
Ok(y) => y,
|
||||
Err(_) => return Err(Error::InstrpOutOfBounds),
|
||||
};
|
||||
self.instrp = x.try_into().unwrap();
|
||||
}
|
||||
Instr::CallLDefer(x) => match x.try_into() {
|
||||
Ok(jtip) => {
|
||||
Instr::CallLDefer(x) => {
|
||||
let jtip = x.try_into().unwrap();
|
||||
if is_call2jump(self.m, self.instrp) {
|
||||
self.instrp = jtip;
|
||||
} else {
|
||||
self.callstack.push(jtip);
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(Error::InstrpOutOfBounds),
|
||||
},
|
||||
Instr::JumpCond(x) => {
|
||||
let x: usize = match x.try_into() {
|
||||
Ok(y) => y,
|
||||
Err(_) => return Err(Error::InstrpOutOfBounds),
|
||||
};
|
||||
if self.stpop()? != 0 {
|
||||
self.instrp = x;
|
||||
self.instrp = x.try_into().unwrap();
|
||||
}
|
||||
}
|
||||
Instr::Return => match self.callstack.pop() {
|
||||
|
@ -288,7 +264,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn doesnt_crash(inp in proptest::collection::vec(0..=u8::MAX, 0..1024)) {
|
||||
let _ = verify(&inp[..]);
|
||||
if verify(&inp[..]).is_err() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut p = Process {
|
||||
stack: Vec::new(),
|
||||
callstack: Vec::new(),
|
||||
|
|
Loading…
Reference in a new issue