refactor(vm): make run non-async, return Error::RemoteCall when remote-calls are invoked

This commit is contained in:
Alain Zscheile 2022-09-28 03:03:18 +02:00
parent b67f42b773
commit d193a66f04
7 changed files with 15 additions and 172 deletions

78
Cargo.lock generated
View file

@ -11,17 +11,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "async-trait"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -98,15 +87,6 @@ dependencies = [
"syn",
]
[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "fogtix-bytecode"
version = "0.0.0"
@ -120,10 +100,7 @@ dependencies = [
name = "fogtix-vm"
version = "0.0.0"
dependencies = [
"async-trait",
"fogtix-bytecode",
"futures-lite",
"once_cell",
"proptest",
"thiserror",
"tracing",
@ -135,38 +112,10 @@ version = "0.1.0"
dependencies = [
"clap",
"fogtix-vm",
"futures-lite",
"readfilez",
"tracing-subscriber",
]
[[package]]
name = "futures-core"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
[[package]]
name = "futures-io"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
[[package]]
name = "futures-lite"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -192,15 +141,6 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "int-enum"
version = "0.4.0"
@ -245,12 +185,6 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memmap2"
version = "0.5.7"
@ -281,12 +215,6 @@ version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
@ -551,12 +479,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "winapi"
version = "0.3.9"

View file

@ -10,8 +10,3 @@ clap = "3.2"
fogtix-vm.path = "../fogtix-vm"
readfilez = "0.3"
tracing-subscriber = "0.3"
[dependencies.futures-lite]
version = "1.12"
default-features = false
features = ["std"]

View file

@ -23,7 +23,6 @@ fn main() {
);
let mut p = fogtix_vm::Process {
korigs: fogtix_vm::make_default_origins(),
stack: Vec::new(),
callstack: Vec::new(),
instrp: fogtix_vm::InstrPtr {
@ -32,7 +31,7 @@ fn main() {
},
};
if let Err(e) = futures_lite::future::block_on(p.run(None)) {
if let Err(e) = p.run(None) {
eprintln!("ERROR@{}: {}", p.instrp.pos, e);
}
}

View file

@ -6,7 +6,6 @@ license = "GPL-2+"
publish = false
[dependencies]
async-trait = "0.1"
thiserror = "1.0"
tracing = "0.1"
@ -15,12 +14,6 @@ path = "../fogtix-bytecode"
features = ["std"]
[dev-dependencies]
once_cell = "1.15"
[dev-dependencies.futures-lite]
version = "1.12"
default-features = false
features = ["std"]
[dev-dependencies.proptest]
version = "1.0"

View file

@ -1,6 +1,5 @@
use async_trait::async_trait;
use fogtix_bytecode::{consts, Atom, Instr, Parse, Pointer, Value as BcValue};
use std::sync::{Arc, RwLock};
use std::sync::Arc;
pub type Module = Arc<dyn ModuleKind>;
@ -42,25 +41,6 @@ impl InstrPtr {
}
}
#[async_trait]
pub trait Origin: Send + Sync + core::fmt::Debug {
async fn call(&self, p: &Pointer, stack: &mut Vec<StackEntValue>) -> InstrPtr;
}
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))
}
#[derive(Clone, Debug)]
pub enum StackEntValue {
Bytes(Vec<u8>),
@ -86,18 +66,17 @@ pub enum Error {
#[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 },
#[error("tried to divide by zero")]
DivisionByZero,
#[error("tried to call remote @ {:x?}", 0.0)]
RemoteCall(Pointer),
}
pub struct Process {
pub korigs: KnownOrigins,
pub stack: Vec<StackEntValue>,
pub callstack: Vec<InstrPtr>,
pub instrp: InstrPtr,
@ -108,6 +87,9 @@ fn verify_jumptarget_explicit(
jinstr: &'static str,
jtip: &InstrPtr,
) -> Result<(), Error> {
if jtip.pos == 0 {
return Ok(());
}
if let Some(Ok((_, trgi))) = jtip.next_instr() {
if trgi != Instr::Label {
return Err(Error::InvalidJumpTarget(
@ -157,7 +139,9 @@ impl Process {
self.stack.pop().ok_or(Error::NotEnoughStacked)
}
pub async fn run(&mut self, mut fuel: Option<&mut u64>) -> Result<(), Error> {
pub fn run(&mut self, mut fuel: Option<&mut u64>) -> Result<(), Error> {
// especially necessary if we resume from a remote call...
verify_jumptarget_explicit(self.instrp.pos, "init-run", &self.instrp)?;
loop {
let previptr = self.instrp.pos;
tracing::trace!("previptr = {}", previptr);
@ -179,7 +163,7 @@ impl Process {
if !self.instrp.is_call2jump() {
self.callstack.push(self.instrp.clone());
}
let mut ptr = match self.stpop()? {
let ptr = match self.stpop()? {
StackEntValue::Pointer(ptr) => ptr,
x => {
return Err(Error::StackedInvalidType {
@ -188,37 +172,7 @@ impl Process {
})
}
};
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));
return Err(Error::RemoteCall(ptr));
}
Instr::CallLocal(x) => {
if !self.instrp.is_call2jump() {
@ -437,13 +391,9 @@ mod tests {
#[test]
fn doesnt_crash(inp in proptest::collection::vec(0..=u8::MAX, 0..1024)) {
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());
let inp: Arc<Vec<u8>> = Arc::new(inp);
let module: Module = inp;
let mut p = Process {
korigs: Arc::clone(&*KORIGS),
stack: Vec::new(),
callstack: Vec::new(),
instrp: InstrPtr {
@ -451,7 +401,7 @@ mod tests {
pos: 0,
},
};
let _ = futures_lite::future::block_on(p.run(Some(&mut 2048)));
let _ = p.run(Some(&mut 2048));
}
}
}

View file

@ -16,4 +16,4 @@
each module starts with the entry point, this avoids the need for a separate "entry pointer".
the entry point of the main module is what's initially called by the VM to start execution.
the entry point of library modules are called to build the library descriptor table,
which should leave an additional pointer on the stack which can be passed to `call (idx:atom)`.
which should leave an additional pointer on the stack which can be passed to `call-r`.

View file

@ -1,16 +0,0 @@
Label,
Dup(1),
Dup(1),
// [S] a b a b
DoMath2(MathBinOp::NotAnd),
Dup(0),
// [S] a b !(a&b) !(a&b)
Swap(1),
// [S] a !(a&b) !(a&b) b
DoMath2(MathBinOp::NotAnd),
// [S] a !(a&b) !(!(a&b)&b)
Swap(1),
// [S] !(!(a&b)&b) !(a&b) a
DoMath2(MathBinOp::NotAnd),
// [S] !(!(a&b)&b) !(!(a&b)&a)
DoMath2(MathBinOp::NotAnd),