use core::fmt;
use core::cell::Cell;
use crate::codegen::{FunctionID, FunctionProto};
use crate::runtime::Variant;
use crate::runtime::module::{Module, NamespaceEnv};
use crate::runtime::vm::VirtualMachine;
use crate::runtime::gc::{Gc, GcTrace};
use crate::runtime::errors::ExecResult;
mod signature;
pub use signature::{Signature, Parameter};
pub use crate::codegen::opcodes::UpvalueIndex;
pub enum Call {
Chunk {
module: Gc<Module>,
chunk_id: FunctionID,
},
Native {
func: Gc<NativeFunction>,
nargs: usize,
},
}
pub trait Callable {
fn signature(&self) -> &Signature;
fn raw_call(&self, args: &[Variant]) -> Call;
fn checked_call(&self, args: &[Variant]) -> ExecResult<Call> {
self.signature().check_args(args)?;
Ok(self.raw_call(args))
}
}
#[derive(Debug)]
pub struct Function {
fun_id: FunctionID,
module: Gc<Module>,
upvalues: Box<[Upvalue]>,
}
impl Function {
pub fn new(fun_id: FunctionID, module: Gc<Module>, upvalues: Box<[Upvalue]>) -> Self {
Self { fun_id, module, upvalues }
}
pub fn upvalues(&self) -> &[Upvalue] { &self.upvalues }
pub fn proto(&self) -> &FunctionProto {
self.module.data().get_function(self.fun_id)
}
pub fn signature(&self) -> &Signature {
self.proto().signature()
}
}
impl Callable for Function {
fn signature(&self) -> &Signature { self.proto().signature() }
fn raw_call(&self, _args: &[Variant]) -> Call {
Call::Chunk {
module: self.module,
chunk_id: self.fun_id,
}
}
}
impl Callable for Gc<Function> {
fn signature(&self) -> &Signature {
<Function as Callable>::signature(self)
}
fn raw_call(&self, args: &[Variant]) -> Call {
<Function as Callable>::raw_call(self, args)
}
}
unsafe impl GcTrace for Function {
fn trace(&self) {
self.module.mark_trace();
for upval in self.upvalues.iter() {
if let Closure::Closed(gc_cell) = upval.closure() {
gc_cell.mark_trace();
}
}
}
fn size_hint(&self) -> usize {
core::mem::size_of::<Upvalue>() * self.upvalues.len()
}
}
#[derive(Debug, Clone, Copy)]
pub enum Closure {
Open(usize),
Closed(Gc<Cell<Variant>>),
}
impl Closure {
pub fn is_open(&self) -> bool { matches!(self, Self::Open(..)) }
pub fn is_closed(&self) -> bool { matches!(self, Self::Closed(..)) }
}
#[derive(Debug, Clone)]
pub struct Upvalue {
value: Cell<Closure>,
}
impl Upvalue {
pub fn new(index: usize) -> Self {
Self {
value: Cell::new(Closure::Open(index)),
}
}
#[inline]
pub fn closure(&self) -> Closure { self.value.get() }
#[inline]
pub fn close(&self, gc_cell: Gc<Cell<Variant>>) {
self.value.set(Closure::Closed(gc_cell))
}
}
pub type NativeFn = fn(self_fun: &NativeFunction, vm: &mut VirtualMachine<'_>, args: &[Variant]) -> ExecResult<Variant>;
pub struct NativeFunction {
signature: Signature,
defaults: Option<Box<[Variant]>>,
env: Gc<NamespaceEnv>,
func: NativeFn,
}
impl NativeFunction {
pub fn new(signature: Signature, defaults: Option<Box<[Variant]>>, env: Gc<NamespaceEnv>, func: NativeFn) -> Self {
Self { signature, defaults, env, func }
}
pub fn signature(&self) -> &Signature { &self.signature }
pub fn env(&self) -> Gc<NamespaceEnv> { self.env }
pub fn defaults(&self) -> &[Variant] {
match self.defaults.as_ref() {
Some(defaults) => &*defaults,
None => &[],
}
}
pub fn exec_fun(&self, vm: &mut VirtualMachine<'_>, args: &[Variant]) -> ExecResult<Variant> {
self.signature().check_args(args)?;
(self.func)(self, vm, args)
}
}
impl Callable for Gc<NativeFunction> {
fn signature(&self) -> &Signature { &self.signature }
fn raw_call(&self, args: &[Variant]) -> Call {
Call::Native {
func: *self,
nargs: args.len(),
}
}
}
unsafe impl GcTrace for NativeFunction {
fn trace(&self) {
self.env.mark_trace();
if let Some(defaults) = self.defaults.as_ref() {
defaults.trace()
}
}
fn size_hint(&self) -> usize {
self.defaults.as_ref()
.map_or(0, |defaults| {
core::mem::size_of::<Variant>() * defaults.len()
})
}
}
impl fmt::Debug for NativeFunction {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("NativeFunction")
.field("signature", &self.signature)
.field("defaults", &self.defaults)
.field("env", &self.env)
.field("func", &core::ptr::addr_of!(self.func))
.finish()
}
}