Add internal error handling to VM, plus function arity
* VM is able to handle basic runtime errors although there is no way to catch this in executing code currently * If a function is called with the wrong number of arguments (arity) it will invoke a runtime error. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
use not_python::{syn::ast, compile::Compile, vm::{Vm, signal::*}};
|
use not_python::{syn::ast, compile::Compile, vm::Vm};
|
||||||
|
|
||||||
use std::{fs, path::PathBuf};
|
use std::{fs, path::PathBuf};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
@@ -57,8 +57,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
if action == "run" {
|
if action == "run" {
|
||||||
let mut vm = Vm::new(&const_pool);
|
let mut vm = Vm::new(&const_pool);
|
||||||
vm.handle_signal(Signal::Call(main, vec![]));
|
vm.call(main, vec![])?;
|
||||||
vm.resume();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl Compile {
|
|||||||
sym
|
sym
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok((self.const_data.const_pool, UserFun::new_obj(main, globals)))
|
Ok((self.const_data.const_pool, UserFun::new_obj(main, globals, 0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the constant data that is interned in this compile session.
|
/// Gets the constant data that is interned in this compile session.
|
||||||
|
|||||||
@@ -407,7 +407,7 @@ impl Visit for CompileBody<'_> {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let (hdl, _fun) = self.compile.push_const(UserFun::new_obj(code, locals));
|
let (hdl, _fun) = self.compile.push_const(UserFun::new_obj(code, locals, expr.params.len()));
|
||||||
|
|
||||||
// TODO(compile) : determine return value at the end of the body (preferably at parse-time)
|
// TODO(compile) : determine return value at the end of the body (preferably at parse-time)
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,46 @@
|
|||||||
use crate::{obj::{prelude::*, reserved::*}, vm::signal::*};
|
use crate::{obj::{prelude::*, reserved::*}, vm::{error::*, signal::*}};
|
||||||
use maplit::btreemap;
|
use maplit::btreemap;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub static PRINTLN_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| NativeFun::new_obj(|_, vm, args| {
|
pub static PRINTLN_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| NativeFun::new_obj(1, |_, vm, args| {
|
||||||
// TODO : use __get_attr__ when it gets added
|
// TODO : use __get_attr__ when it gets added
|
||||||
let to_string = {
|
let to_string = {
|
||||||
let obj_ref = &args[0];
|
let obj_ref = &args[0];
|
||||||
read_obj!(let obj = obj_ref);
|
read_obj!(let obj = obj_ref);
|
||||||
obj.get_attr(STR_MEMBER_NAME.sym)
|
obj.get_attr(STR_MEMBER_NAME.sym)
|
||||||
.or_else(|| obj.get_attr(REPR_MEMBER_NAME.sym))
|
.ok_or(Error::MissingAttr { attr: STR_MEMBER_NAME.sym })?
|
||||||
.expect("no __str__ or __repr__ member")
|
|
||||||
};
|
};
|
||||||
let return_value = vm.call(to_string, vec![]);
|
let return_value = vm.call(to_string, vec![])?;
|
||||||
{
|
{
|
||||||
read_obj!(let str_obj = return_value);
|
read_obj!(let str_obj = return_value);
|
||||||
let str_obj: &Str = str_obj.as_any().downcast_ref().unwrap();
|
let str_obj: &Str = str_obj.as_any().downcast_ref()
|
||||||
|
.ok_or_else(|| Error::ValueError { error: "expected str value".to_string(), value: return_value.clone() })?;
|
||||||
println!("{}", str_obj.value());
|
println!("{}", str_obj.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.push(NIL_NAME.sym_ref());
|
vm.push(NIL_NAME.sym_ref());
|
||||||
Signal::Return
|
Ok(Signal::Return)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
pub static PRINT_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| NativeFun::new_obj(|_, vm, args| {
|
pub static PRINT_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| NativeFun::new_obj(1, |_, vm, args| {
|
||||||
// TODO : use __get_attr__ when it gets added
|
// TODO : use __get_attr__ when it gets added
|
||||||
let to_string = {
|
let to_string = {
|
||||||
let obj_ref = &args[0];
|
let obj_ref = &args[0];
|
||||||
read_obj!(let obj = obj_ref);
|
read_obj!(let obj = obj_ref);
|
||||||
obj.get_attr(STR_MEMBER_NAME.sym)
|
obj.get_attr(STR_MEMBER_NAME.sym)
|
||||||
.or_else(|| obj.get_attr(REPR_MEMBER_NAME.sym))
|
.ok_or(Error::MissingAttr { attr: STR_MEMBER_NAME.sym })?
|
||||||
.expect("no __str__ or __repr__ member")
|
|
||||||
};
|
};
|
||||||
let return_value = vm.call(to_string, vec![]);
|
let return_value = vm.call(to_string, vec![])?;
|
||||||
{
|
{
|
||||||
read_obj!(let str_obj = return_value);
|
read_obj!(let str_obj = return_value);
|
||||||
let str_obj: &Str = str_obj.as_any().downcast_ref().unwrap();
|
let str_obj: &Str = str_obj.as_any().downcast_ref()
|
||||||
|
.ok_or_else(|| Error::ValueError { error: "expected str value".to_string(), value: return_value.clone() })?;
|
||||||
print!("{}", str_obj.value());
|
print!("{}", str_obj.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
Signal::Return
|
vm.push(NIL_NAME.sym_ref());
|
||||||
|
Ok(Signal::Return)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
pub static BUILTIN_OBJS: Lazy<BTreeMap<Sym, ObjRef>> = Lazy::new(|| btreemap! {
|
pub static BUILTIN_OBJS: Lazy<BTreeMap<Sym, ObjRef>> = Lazy::new(|| btreemap! {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{obj::{reserved::*, prelude::*}, vm::{consts::ConstPool, frame::*, inst::Inst, signal::*, Vm}};
|
use crate::{obj::{reserved::*, prelude::*}, vm::{consts::ConstPool, error::*, frame::*, inst::Inst, signal::*, Vm}};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use shredder::{GcSafeWrapper, Scan};
|
use shredder::{GcSafeWrapper, Scan};
|
||||||
use std::{fmt::{Debug, Formatter, self}, io::{self, Write}, sync::Arc};
|
use std::{fmt::{Debug, Formatter, self}, io::{self, Write}, sync::Arc};
|
||||||
@@ -8,7 +8,7 @@ pub type FunLocals = Vec<Sym>;
|
|||||||
/// A function object which can create a new stack frame.
|
/// A function object which can create a new stack frame.
|
||||||
pub trait Fun {
|
pub trait Fun {
|
||||||
/// Creates a new `vm::Frame` from this function object.
|
/// Creates a new `vm::Frame` from this function object.
|
||||||
fn create_frame(&self, callee: ObjRef, vm: &Vm, _args: Vec<ObjRef>) -> Frame;
|
fn create_frame(&self, callee: ObjRef, vm: &Vm, _args: Vec<ObjRef>) -> Result<Frame>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MethodRef = ObjRef<Method>;
|
pub type MethodRef = ObjRef<Method>;
|
||||||
@@ -55,7 +55,7 @@ impl Obj for Method {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fun for Method {
|
impl Fun for Method {
|
||||||
fn create_frame(&self, _callee: ObjRef, vm: &Vm, mut args: Vec<ObjRef>) -> Frame {
|
fn create_frame(&self, _callee: ObjRef, vm: &Vm, mut args: Vec<ObjRef>) -> Result<Frame> {
|
||||||
// insert self argument
|
// insert self argument
|
||||||
args.insert(0, self.get_attr(SELF_MEMBER_NAME.sym).unwrap());
|
args.insert(0, self.get_attr(SELF_MEMBER_NAME.sym).unwrap());
|
||||||
// get function and use its create_frame method with the updated args
|
// get function and use its create_frame method with the updated args
|
||||||
@@ -82,16 +82,18 @@ pub struct UserFun {
|
|||||||
// Safe because this is just an interner that points to symbols, which aren't GC'd
|
// Safe because this is just an interner that points to symbols, which aren't GC'd
|
||||||
#[shredder(unsafe_skip)]
|
#[shredder(unsafe_skip)]
|
||||||
locals: FunLocals,
|
locals: FunLocals,
|
||||||
|
arity: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserFun {
|
impl UserFun {
|
||||||
pub fn new_obj(code: Vec<Inst>, locals: FunLocals) -> UserFunRef {
|
pub fn new_obj(code: Vec<Inst>, locals: FunLocals, arity: usize) -> UserFunRef {
|
||||||
self_referring_obj! (
|
self_referring_obj! (
|
||||||
Self {
|
Self {
|
||||||
vtable: Default::default(), // this is a placeholder for the real vtable
|
vtable: Default::default(), // this is a placeholder for the real vtable
|
||||||
attrs: Default::default(),
|
attrs: Default::default(),
|
||||||
code: Arc::new(code),
|
code: Arc::new(code),
|
||||||
locals,
|
locals,
|
||||||
|
arity,
|
||||||
},
|
},
|
||||||
vtable: |obj_ref: ObjRef| vtable! {
|
vtable: |obj_ref: ObjRef| vtable! {
|
||||||
TY_MEMBER_NAME.sym => USER_FUN_TY.clone(),
|
TY_MEMBER_NAME.sym => USER_FUN_TY.clone(),
|
||||||
@@ -215,13 +217,19 @@ impl Obj for UserFun {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fun for UserFun {
|
impl Fun for UserFun {
|
||||||
fn create_frame(&self, callee: ObjRef, vm: &Vm, args: Vec<ObjRef>) -> Frame {
|
fn create_frame(&self, callee: ObjRef, vm: &Vm, args: Vec<ObjRef>) -> Result<Frame> {
|
||||||
|
if args.len() != self.arity {
|
||||||
|
return Err(Error::ArityError {
|
||||||
|
expected: self.arity,
|
||||||
|
got: args.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
let bindings: FrameBindings = args.into_iter()
|
let bindings: FrameBindings = args.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.collect();
|
.collect();
|
||||||
Frame::User(
|
Ok(Frame::User(
|
||||||
UserFrame::new(callee, bindings, vm.pc(), Arc::clone(&self.code))
|
UserFrame::new(callee, bindings, vm.pc(), Arc::clone(&self.code))
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +249,7 @@ pub static USER_FUN_TY: Lazy<ObjRef<Ty>> = Lazy::new(|| Ty::new_obj(USER_FUN_NAM
|
|||||||
// struct NativeFun
|
// struct NativeFun
|
||||||
//
|
//
|
||||||
|
|
||||||
pub type NativeFunPtr = fn(ObjRef, &mut Vm, Vec<ObjRef>) -> Signal;
|
pub type NativeFunPtr = fn(ObjRef, &mut Vm, Vec<ObjRef>) -> Result<Signal>;
|
||||||
pub type NativeFunRef = ObjRef<NativeFun>;
|
pub type NativeFunRef = ObjRef<NativeFun>;
|
||||||
|
|
||||||
#[derive(Scan)]
|
#[derive(Scan)]
|
||||||
@@ -250,6 +258,7 @@ pub struct NativeFun {
|
|||||||
attrs: Attrs,
|
attrs: Attrs,
|
||||||
#[shredder(skip)]
|
#[shredder(skip)]
|
||||||
fun: GcSafeWrapper<NativeFunPtr>,
|
fun: GcSafeWrapper<NativeFunPtr>,
|
||||||
|
arity: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -257,12 +266,13 @@ pub struct NativeFun {
|
|||||||
//
|
//
|
||||||
|
|
||||||
impl NativeFun {
|
impl NativeFun {
|
||||||
pub fn new_obj(fun: NativeFunPtr) -> ObjRef<Self> {
|
pub fn new_obj(arity: usize, fun: NativeFunPtr) -> ObjRef<Self> {
|
||||||
self_referring_obj! (
|
self_referring_obj! (
|
||||||
Self {
|
Self {
|
||||||
vtable: Default::default(),
|
vtable: Default::default(),
|
||||||
attrs: Default::default(),
|
attrs: Default::default(),
|
||||||
fun: GcSafeWrapper::new(fun),
|
fun: GcSafeWrapper::new(fun),
|
||||||
|
arity,
|
||||||
},
|
},
|
||||||
vtable: |obj_ref: ObjRef| vtable! {
|
vtable: |obj_ref: ObjRef| vtable! {
|
||||||
TY_MEMBER_NAME.sym => NATIVE_FUN_TY.clone(),
|
TY_MEMBER_NAME.sym => NATIVE_FUN_TY.clone(),
|
||||||
@@ -313,10 +323,16 @@ impl Obj for NativeFun {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fun for NativeFun {
|
impl Fun for NativeFun {
|
||||||
fn create_frame(&self, callee: ObjRef, _vm: &Vm, args: Vec<ObjRef>) -> Frame {
|
fn create_frame(&self, callee: ObjRef, _vm: &Vm, args: Vec<ObjRef>) -> Result<Frame> {
|
||||||
Frame::Native(
|
if args.len() != self.arity {
|
||||||
|
return Err(Error::ArityError {
|
||||||
|
expected: self.arity,
|
||||||
|
got: args.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(Frame::Native(
|
||||||
NativeFrame::new(callee, *self.fun, args)
|
NativeFrame::new(callee, *self.fun, args)
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,13 +44,14 @@ impl Debug for Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static INT_STR_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
|
static INT_STR_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
|
||||||
NativeFun::new_obj(|_callee, vm, args| {
|
NativeFun::new_obj(1, |_callee, vm, args| {
|
||||||
read_obj!(let int_obj = &args[0]);
|
read_obj!(let int_obj = &args[0]);
|
||||||
if let Some(int_obj) = std::any::Any::downcast_ref::<Int>(int_obj.as_any()) {
|
if let Some(int_obj) = std::any::Any::downcast_ref::<Int>(int_obj.as_any()) {
|
||||||
vm.push(Str::new_obj(int_obj.value.to_string()));
|
vm.push(Str::new_obj(int_obj.value.to_string()));
|
||||||
} else {
|
} else {
|
||||||
panic!("{:?} is not an int", int_obj)
|
panic!("{:?} is not an int", int_obj)
|
||||||
}
|
}
|
||||||
crate::vm::signal::Signal::Return
|
|
||||||
|
Ok(crate::vm::signal::Signal::Return)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub mod prelude {
|
|||||||
|
|
||||||
use shredder::{Gc, Scan};
|
use shredder::{Gc, Scan};
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt::{self, Debug, Formatter},
|
||||||
marker::Unsize,
|
marker::Unsize,
|
||||||
ops::{CoerceUnsized, Deref, DerefMut},
|
ops::{CoerceUnsized, Deref, DerefMut},
|
||||||
sync::RwLock,
|
sync::RwLock,
|
||||||
@@ -42,7 +43,7 @@ macro_rules! obj_attr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Obj: Scan + std::fmt::Debug {
|
pub trait Obj: Scan + Debug {
|
||||||
fn vtable(&self) -> &Vtable;
|
fn vtable(&self) -> &Vtable;
|
||||||
fn attrs(&self) -> &Attrs;
|
fn attrs(&self) -> &Attrs;
|
||||||
fn attrs_mut(&mut self) -> Option<&mut Attrs>;
|
fn attrs_mut(&mut self) -> Option<&mut Attrs>;
|
||||||
@@ -82,7 +83,7 @@ pub trait Obj: Scan + std::fmt::Debug {
|
|||||||
// struct ObjRef
|
// struct ObjRef
|
||||||
//
|
//
|
||||||
|
|
||||||
#[derive(Debug, Scan)]
|
#[derive(Scan)]
|
||||||
pub struct ObjRef<T = (dyn Obj + Send + Sync + 'static)>
|
pub struct ObjRef<T = (dyn Obj + Send + Sync + 'static)>
|
||||||
where
|
where
|
||||||
T: Obj + ?Sized + Send + Sync,
|
T: Obj + ?Sized + Send + Sync,
|
||||||
@@ -135,6 +136,18 @@ where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// impl Debug for ObjRef
|
||||||
|
//
|
||||||
|
impl<T> Debug for ObjRef<T>
|
||||||
|
where T: Obj + ?Sized + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||||
|
read_obj!(let obj: &T = self);
|
||||||
|
Debug::fmt(obj, fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// impl Deref for ObjRef
|
// impl Deref for ObjRef
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
|
use crate::obj::prelude::*;
|
||||||
use snafu::Snafu;
|
use snafu::Snafu;
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[snafu(display("attempted to pop an empty stack"))]
|
#[snafu(display("missing attribute: {}", global_sym_lookup(*attr).unwrap()))]
|
||||||
EmptyStack,
|
MissingAttr {
|
||||||
|
attr: Sym,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("{}", error))]
|
||||||
|
ValueError {
|
||||||
|
error: String,
|
||||||
|
value: ObjRef,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("incorrect function arity; expected {} but got {} instead", expected, got))]
|
||||||
|
ArityError {
|
||||||
|
expected: usize,
|
||||||
|
got: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ pub mod signal;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
obj::{builtin::BUILTIN_OBJS, reserved::*, prelude::*},
|
obj::{builtin::BUILTIN_OBJS, reserved::*, prelude::*},
|
||||||
vm::{consts::ConstPool, frame::*, inst::*, signal::*},
|
vm::{consts::ConstPool, error::*, frame::*, inst::*, signal::*},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -92,25 +92,26 @@ impl<'c> Vm<'c> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a function.
|
/// Calls a function.
|
||||||
pub fn call(&mut self, fun: ObjRef, args: Vec<ObjRef>) -> ObjRef {
|
pub fn call(&mut self, fun: ObjRef, args: Vec<ObjRef>) -> Result<ObjRef> {
|
||||||
self.handle_signal(Signal::Call(fun, args));
|
self.handle_signal(Signal::Call(fun, args))?;
|
||||||
self.resume_until_return();
|
self.resume_until_return()?;
|
||||||
self.pop().expect("return value")
|
Ok(self.pop().expect("return value"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resumes execution of the current program.
|
/// Resumes execution of the current program.
|
||||||
pub fn resume(&mut self) {
|
pub fn resume(&mut self) -> Result<()> {
|
||||||
while !self.frames().is_empty() {
|
while !self.frames().is_empty() {
|
||||||
self.resume_until_return();
|
self.resume_until_return()?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resume execution until the currently executing function returns.
|
/// Resume execution until the currently executing function returns.
|
||||||
pub fn resume_until_return(&mut self) {
|
pub fn resume_until_return(&mut self) -> Result<()> {
|
||||||
// resume execution until control is returned to the originally calling function
|
// resume execution until control is returned to the originally calling function
|
||||||
let start_frames = self.frames().len();
|
let start_frames = self.frames().len();
|
||||||
if start_frames == 0 {
|
if start_frames == 0 {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
while self.frames().len() >= start_frames {
|
while self.frames().len() >= start_frames {
|
||||||
@@ -119,14 +120,15 @@ impl<'c> Vm<'c> {
|
|||||||
Frame::Native(fun) => {
|
Frame::Native(fun) => {
|
||||||
let callee = fun.callee().clone();
|
let callee = fun.callee().clone();
|
||||||
let args = fun.args().clone();
|
let args = fun.args().clone();
|
||||||
(*fun.fun_ptr())(callee, self, args)
|
(*fun.fun_ptr())(callee, self, args)?
|
||||||
}
|
}
|
||||||
Frame::User(_) => {
|
Frame::User(_) => {
|
||||||
self.resume_user_fun()
|
self.resume_user_fun()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.handle_signal(signal);
|
self.handle_signal(signal)?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resume_user_fun(&mut self) -> Signal {
|
fn resume_user_fun(&mut self) -> Signal {
|
||||||
@@ -142,13 +144,13 @@ impl<'c> Vm<'c> {
|
|||||||
///
|
///
|
||||||
/// The signal may originate from the VM itself, or from an external location. Signals
|
/// The signal may originate from the VM itself, or from an external location. Signals
|
||||||
/// generally interrupt control flow of a program.
|
/// generally interrupt control flow of a program.
|
||||||
pub fn handle_signal(&mut self, signal: Signal) {
|
pub fn handle_signal(&mut self, signal: Signal) -> Result<()> {
|
||||||
match signal {
|
match signal {
|
||||||
Signal::Call(callee, args) => {
|
Signal::Call(callee, args) => {
|
||||||
read_obj!(let callee_obj = callee);
|
read_obj!(let callee_obj = callee);
|
||||||
let frame = callee_obj.as_fun()
|
let frame = callee_obj.as_fun()
|
||||||
.expect("callable function")
|
.ok_or_else(|| Error::ValueError { error: "cannot call this object".to_string(), value: callee.clone() })?
|
||||||
.create_frame(callee.clone(), self, args);
|
.create_frame(callee.clone(), self, args)?;
|
||||||
// Jump to the first address of the new function call if it's a user function
|
// Jump to the first address of the new function call if it's a user function
|
||||||
if let Frame::User(_) = &frame {
|
if let Frame::User(_) = &frame {
|
||||||
self.set_pc(0);
|
self.set_pc(0);
|
||||||
@@ -171,6 +173,7 @@ impl<'c> Vm<'c> {
|
|||||||
self.push(ret_val);
|
self.push(ret_val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current instruction.
|
/// Gets the current instruction.
|
||||||
|
|||||||
Reference in New Issue
Block a user