refactor(vm): DRY jump safety checks

This commit is contained in:
Alain Zscheile 2022-09-23 23:25:47 +02:00
parent bc96f9f130
commit b410e7a5b8

View file

@ -1,4 +1,4 @@
use fogtix_bytecode::{Atom, Parse, Pointer, Value as BcValue};
use fogtix_bytecode::{Atom, Instr, Parse, Pointer, Value as BcValue};
use std::marker::PhantomData;
use std::sync::Arc;
@ -59,10 +59,37 @@ struct Process {
}
impl Process {
fn is_call2jump(instrp: &(Arc<Module>, usize)) -> bool {
let mut call2jump = false;
if let Some(n2xti_arr) = instrp.0.h.get(instrp.1..) {
if let Ok((_, Instr::Return)) = Instr::parse(n2xti_arr) {
// tail-call optimization (would otherwise require another opcode)
call2jump = true;
}
}
call2jump
}
fn verify_jumptarget(&self, previptr: usize, jinstr: &str) -> bool {
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
if let Ok((_, trgi)) = Instr::parse(n2xti_arr) {
if trgi != Instr::Label {
tracing::error!(
"`{}` arrived at non-jump target {:?} @ {}",
jinstr,
trgi,
previptr,
);
return false;
}
}
}
true
}
fn run(&mut self) {
loop {
use fogtix_bytecode::consts::{AtomOp, MathBinOp};
use fogtix_bytecode::Instr;
let previptr = self.instrp.1;
tracing::trace!("previptr = {}", previptr);
let nxti_arr = match self.instrp.0.h.get(previptr..) {
@ -90,14 +117,7 @@ impl Process {
match nxti {
Instr::Label => {}
Instr::CallRemote(atom, arity) => {
let mut call2jump = false;
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
if let Ok((_, Instr::Return)) = Instr::parse(n2xti_arr) {
// tail-call optimization (would otherwise require another opcode)
call2jump = true;
}
}
if !call2jump {
if !Self::is_call2jump(&self.instrp) {
self.callstack.push(self.instrp.clone());
}
match self.stack.pop() {
@ -120,28 +140,12 @@ impl Process {
break;
}
}
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
if let Ok((_, trgi)) = Instr::parse(n2xti_arr) {
if trgi != Instr::Label {
tracing::error!(
"`call-r` arrived at non-jump target {:?} @ {}",
trgi,
previptr
);
break;
}
}
if !self.verify_jumptarget(previptr, "call-r") {
break;
}
}
Instr::CallLocal(x) => {
let mut call2jump = false;
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
if let Ok((_, Instr::Return)) = Instr::parse(n2xti_arr) {
// tail-call optimization (would otherwise require another opcode)
call2jump = true;
}
}
if !call2jump {
if !Self::is_call2jump(&self.instrp) {
self.callstack.push(self.instrp.clone());
}
self.instrp.1 = match x.try_into() {
@ -155,17 +159,8 @@ impl Process {
break;
}
};
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
if let Ok((_, trgi)) = Instr::parse(n2xti_arr) {
if trgi != Instr::Label {
tracing::error!(
"`call-l` arrived at non-jump target {:?} @ {}",
trgi,
previptr
);
break;
}
}
if !self.verify_jumptarget(previptr, "call-l") {
break;
}
}
Instr::Jump(x) => {
@ -180,17 +175,8 @@ impl Process {
break;
}
};
if let Some(n2xti_arr) = self.instrp.0.h.get(self.instrp.1..) {
if let Ok((_, trgi)) = Instr::parse(n2xti_arr) {
if trgi != Instr::Label {
tracing::error!(
"jump arrived at non-jump target {:?} @ {}",
trgi,
previptr
);
break;
}
}
if !self.verify_jumptarget(previptr, "jump") {
break;
}
}
Instr::Return => match self.callstack.pop() {