sphinx/runtime/
function.rs

1use core::fmt;
2use core::cell::Cell;
3use crate::codegen::{FunctionID, FunctionProto};
4use crate::runtime::Variant;
5use crate::runtime::module::{Module, NamespaceEnv};
6use crate::runtime::vm::VirtualMachine;
7use crate::runtime::gc::{Gc, GcTrace};
8use crate::runtime::errors::ExecResult;
9
10mod signature;
11
12pub use signature::{Signature, Parameter};
13pub use crate::codegen::opcodes::UpvalueIndex;
14
15
16/// Call directive
17
18pub enum Call {
19    Chunk {
20        module: Gc<Module>,
21        chunk_id: FunctionID,
22    },
23    Native {
24        func: Gc<NativeFunction>,
25        nargs: usize,
26    },
27}
28
29pub trait Callable {
30    fn signature(&self) -> &Signature;
31    fn raw_call(&self, args: &[Variant]) -> Call;
32
33    fn checked_call(&self, args: &[Variant]) -> ExecResult<Call> {
34        self.signature().check_args(args)?;
35        Ok(self.raw_call(args))
36    }
37
38}
39
40// Compiled Functions
41
42#[derive(Debug)]
43pub struct Function {
44    fun_id: FunctionID,
45    module: Gc<Module>,
46    upvalues: Box<[Upvalue]>,
47}
48
49impl Function {
50    pub fn new(fun_id: FunctionID, module: Gc<Module>, upvalues: Box<[Upvalue]>) -> Self {
51        Self { fun_id, module, upvalues }
52    }
53    
54    pub fn upvalues(&self) -> &[Upvalue] { &self.upvalues }
55    
56    pub fn proto(&self) -> &FunctionProto {
57        self.module.data().get_function(self.fun_id)
58    }
59    
60    pub fn signature(&self) -> &Signature {
61        self.proto().signature()
62    }
63}
64
65impl Callable for Function {
66    fn signature(&self) -> &Signature { self.proto().signature() }
67    
68    fn raw_call(&self, _args: &[Variant]) -> Call {
69        Call::Chunk {
70            module: self.module,
71            chunk_id: self.fun_id,
72        }
73    }
74}
75
76impl Callable for Gc<Function> {
77    fn signature(&self) -> &Signature {
78        <Function as Callable>::signature(self)
79    }
80    
81    fn raw_call(&self, args: &[Variant]) -> Call {
82        <Function as Callable>::raw_call(self, args)
83    }
84}
85
86unsafe impl GcTrace for Function {
87    fn trace(&self) {
88        self.module.mark_trace();
89        for upval in self.upvalues.iter() {
90            if let Closure::Closed(gc_cell) = upval.closure() {
91                gc_cell.mark_trace();
92            }
93        }
94    }
95    
96    fn size_hint(&self) -> usize {
97        core::mem::size_of::<Upvalue>() * self.upvalues.len()
98    }
99}
100
101
102// Closures
103
104#[derive(Debug, Clone, Copy)]
105pub enum Closure {
106    Open(usize),
107    Closed(Gc<Cell<Variant>>),
108}
109
110impl Closure {
111    pub fn is_open(&self) -> bool { matches!(self, Self::Open(..)) }
112    pub fn is_closed(&self) -> bool { matches!(self, Self::Closed(..)) }
113}
114
115
116#[derive(Debug, Clone)]
117pub struct Upvalue {
118    value: Cell<Closure>,
119}
120
121impl Upvalue {
122    pub fn new(index: usize) -> Self {
123        Self {
124            value: Cell::new(Closure::Open(index)),
125        }
126    }
127    
128    #[inline]
129    pub fn closure(&self) -> Closure { self.value.get() }
130    
131    #[inline]
132    pub fn close(&self, gc_cell: Gc<Cell<Variant>>) {
133        self.value.set(Closure::Closed(gc_cell))
134    }
135}
136
137
138
139// Native Functions
140
141pub type NativeFn = fn(self_fun: &NativeFunction, vm: &mut VirtualMachine<'_>, args: &[Variant]) -> ExecResult<Variant>;
142
143pub struct NativeFunction {
144    signature: Signature,
145    defaults: Option<Box<[Variant]>>,
146    env: Gc<NamespaceEnv>,
147    func: NativeFn,
148}
149
150impl NativeFunction {
151    pub fn new(signature: Signature, defaults: Option<Box<[Variant]>>, env: Gc<NamespaceEnv>, func: NativeFn) -> Self {
152        Self { signature, defaults, env, func }
153    }
154    
155    pub fn signature(&self) -> &Signature { &self.signature }
156    
157    pub fn env(&self) -> Gc<NamespaceEnv> { self.env }
158    
159    pub fn defaults(&self) -> &[Variant] {
160        match self.defaults.as_ref() {
161            Some(defaults) => &*defaults,
162            None => &[],
163        }
164    }
165    
166    /// actually execute a native function
167    pub fn exec_fun(&self, vm: &mut VirtualMachine<'_>, args: &[Variant]) -> ExecResult<Variant> {
168        self.signature().check_args(args)?;
169        (self.func)(self, vm, args)
170    }
171}
172
173impl Callable for Gc<NativeFunction> {
174    fn signature(&self) -> &Signature { &self.signature }
175    
176    fn raw_call(&self, args: &[Variant]) -> Call {
177        Call::Native {
178            func: *self,
179            nargs: args.len(),
180        }
181    }
182}
183
184unsafe impl GcTrace for NativeFunction {
185    fn trace(&self) {
186        self.env.mark_trace();
187        
188        if let Some(defaults) = self.defaults.as_ref() {
189            defaults.trace()
190        }
191    }
192    
193    fn size_hint(&self) -> usize {
194        self.defaults.as_ref()
195        .map_or(0, |defaults| {
196            core::mem::size_of::<Variant>() * defaults.len()
197        })
198    }
199}
200
201impl fmt::Debug for NativeFunction {
202    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
203        fmt.debug_struct("NativeFunction")
204            .field("signature", &self.signature)
205            .field("defaults", &self.defaults)
206            .field("env", &self.env)
207            .field("func", &core::ptr::addr_of!(self.func))
208            .finish()
209    }
210}