use crate::context::Handler;
use crate::VmErrorKind;
use crate::{
Call, CallVm, Context, FromValue, Future, Generator, Hash, IntoArgs, Shared, Stack, StopReason,
Stream, Tuple, Unit, Value, Vm, VmError,
};
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug)]
pub struct FnPtr {
inner: Inner,
}
impl FnPtr {
pub fn call<A, T>(&self, args: A) -> Result<T, VmError>
where
A: IntoArgs,
T: FromValue,
{
let value = match &self.inner {
Inner::FnHandler(handler) => {
let mut stack = Stack::with_capacity(A::count());
args.into_args(&mut stack)?;
(handler.handler)(&mut stack, A::count())?;
stack.pop()?
}
Inner::FnPtrOffset(offset) => {
Self::check_args(A::count(), offset.args)?;
let mut vm = Vm::new(offset.context.clone(), offset.unit.clone());
vm.set_ip(offset.offset);
args.into_args(vm.stack_mut())?;
match offset.call {
Call::Stream => Value::from(Stream::new(vm)),
Call::Generator => Value::from(Generator::new(vm)),
Call::Immediate => vm.complete()?,
Call::Async => Value::from(Future::new(vm.async_complete())),
}
}
Inner::FnClosureOffset(offset) => {
Self::check_args(A::count(), offset.args)?;
let mut vm = Vm::new(offset.context.clone(), offset.unit.clone());
vm.set_ip(offset.offset);
args.into_args(vm.stack_mut())?;
vm.stack_mut().push(offset.environment.clone());
match offset.call {
Call::Stream => Value::from(Stream::new(vm)),
Call::Generator => Value::from(Generator::new(vm)),
Call::Immediate => vm.complete()?,
Call::Async => Value::from(Future::new(vm.async_complete())),
}
}
Inner::FnTuple(tuple) => {
Self::check_args(A::count(), tuple.args)?;
Value::typed_tuple(tuple.hash, args.into_vec()?)
}
Inner::FnVariantTuple(tuple) => {
Self::check_args(A::count(), tuple.args)?;
Value::variant_tuple(tuple.enum_hash, tuple.hash, args.into_vec()?)
}
};
Ok(T::from_value(value)?)
}
pub(crate) fn from_handler(handler: Arc<Handler>) -> Self {
Self {
inner: Inner::FnHandler(FnHandler { handler }),
}
}
pub(crate) fn from_offset(
context: Rc<Context>,
unit: Rc<Unit>,
offset: usize,
call: Call,
args: usize,
) -> Self {
Self {
inner: Inner::FnPtrOffset(FnPtrOffset {
context,
unit,
offset,
call,
args,
}),
}
}
pub(crate) fn from_closure(
context: Rc<Context>,
unit: Rc<Unit>,
environment: Shared<Tuple>,
offset: usize,
call: Call,
args: usize,
) -> Self {
Self {
inner: Inner::FnClosureOffset(FnClosureOffset {
context,
unit,
environment,
offset,
call,
args,
}),
}
}
pub(crate) fn from_tuple(hash: Hash, args: usize) -> Self {
Self {
inner: Inner::FnTuple(FnTuple { hash, args }),
}
}
pub(crate) fn from_variant_tuple(enum_hash: Hash, hash: Hash, args: usize) -> Self {
Self {
inner: Inner::FnVariantTuple(FnVariantTuple {
enum_hash,
hash,
args,
}),
}
}
pub(crate) fn call_with_vm(
&self,
vm: &mut Vm,
args: usize,
) -> Result<Option<StopReason>, VmError> {
let reason = match &self.inner {
Inner::FnHandler(handler) => {
(handler.handler)(vm.stack_mut(), args)?;
None
}
Inner::FnPtrOffset(offset) => {
Self::check_args(args, offset.args)?;
if let Call::Immediate = offset.call {
if vm.is_same(&offset.context, &offset.unit) {
vm.push_call_frame(offset.offset, args)?;
return Ok(None);
}
}
let new_stack = vm.stack_mut().drain_stack_top(args)?.collect::<Stack>();
let mut vm =
Vm::new_with_stack(offset.context.clone(), offset.unit.clone(), new_stack);
vm.set_ip(offset.offset);
Some(StopReason::CallVm(CallVm::new(offset.call, vm)))
}
Inner::FnClosureOffset(offset) => {
Self::check_args(args, offset.args)?;
if let Call::Immediate = offset.call {
if vm.is_same(&offset.context, &offset.unit) {
vm.push_call_frame(offset.offset, args)?;
vm.stack_mut()
.push(Value::Tuple(offset.environment.clone()));
return Ok(None);
}
}
let mut new_stack = Stack::new();
new_stack.extend(vm.stack_mut().drain_stack_top(args)?);
new_stack.push(Value::Tuple(offset.environment.clone()));
let mut vm =
Vm::new_with_stack(offset.context.clone(), offset.unit.clone(), new_stack);
vm.set_ip(offset.offset);
Some(StopReason::CallVm(CallVm::new(offset.call, vm)))
}
Inner::FnTuple(tuple) => {
Self::check_args(args, tuple.args)?;
let value = Value::typed_tuple(tuple.hash, vm.stack_mut().pop_sequence(args)?);
vm.stack_mut().push(value);
None
}
Inner::FnVariantTuple(tuple) => {
Self::check_args(args, tuple.args)?;
let value = Value::variant_tuple(
tuple.enum_hash,
tuple.hash,
vm.stack_mut().pop_sequence(args)?,
);
vm.stack_mut().push(value);
None
}
};
Ok(reason)
}
#[inline]
fn check_args(actual: usize, expected: usize) -> Result<(), VmError> {
if actual != expected {
return Err(VmError::from(VmErrorKind::ArgumentCountMismatch {
expected,
actual,
}));
}
Ok(())
}
}
#[derive(Debug)]
enum Inner {
FnHandler(FnHandler),
FnPtrOffset(FnPtrOffset),
FnTuple(FnTuple),
FnClosureOffset(FnClosureOffset),
FnVariantTuple(FnVariantTuple),
}
struct FnHandler {
handler: Arc<Handler>,
}
impl fmt::Debug for FnHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FnHandler")
}
}
struct FnPtrOffset {
context: Rc<Context>,
unit: Rc<Unit>,
offset: usize,
call: Call,
args: usize,
}
impl fmt::Debug for FnPtrOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FnPtrOffset")
.field("context", &(&self.context as *const _))
.field("unit", &(&self.unit as *const _))
.field("offset", &self.offset)
.field("call", &self.call)
.field("args", &self.args)
.finish()
}
}
struct FnClosureOffset {
context: Rc<Context>,
unit: Rc<Unit>,
environment: Shared<Tuple>,
offset: usize,
call: Call,
args: usize,
}
impl fmt::Debug for FnClosureOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FnPtrOffset")
.field("context", &(&self.context as *const _))
.field("unit", &(&self.unit as *const _))
.field("environment", &self.environment)
.field("offset", &self.offset)
.field("call", &self.call)
.field("args", &self.args)
.finish()
}
}
#[derive(Debug)]
struct FnTuple {
hash: Hash,
args: usize,
}
#[derive(Debug)]
struct FnVariantTuple {
enum_hash: Hash,
hash: Hash,
args: usize,
}