Skip to main content

rune/core/object/
func.rs

1use super::{
2    super::gc::{Block, Context},
3    CloneIn, IntoObject, LispVec, ObjCell, display_slice,
4};
5use super::{Object, WithLifetime};
6use crate::{
7    core::{
8        env::Env,
9        gc::{GcHeap, Rt, Slot},
10    },
11    derive_GcMoveable,
12};
13use anyhow::{Result, bail, ensure};
14use rune_macros::Trace;
15use std::fmt::{self, Debug, Display};
16
17#[derive(PartialEq, Eq, Trace)]
18pub(crate) struct ByteFnPrototype {
19    #[no_trace]
20    pub(crate) args: FnArgs,
21    #[no_trace]
22    pub(crate) depth: usize,
23    #[no_trace]
24    pub(super) op_codes: Box<[u8]>,
25    // TODO: remove a level of pointer indirection here.
26    pub(super) constants: Slot<&'static LispVec>,
27}
28
29/// A function implemented in lisp. Note that all functions are byte compiled,
30/// so this contains the byte-code representation of the function.
31#[derive(PartialEq, Eq, Trace)]
32pub(crate) struct ByteFn(GcHeap<ByteFnPrototype>);
33
34derive_GcMoveable!(ByteFn);
35
36impl std::ops::Deref for ByteFn {
37    type Target = ByteFnPrototype;
38
39    fn deref(&self) -> &Self::Target {
40        &self.0
41    }
42}
43
44define_unbox!(ByteFn, Func, &'ob ByteFn);
45
46impl ByteFn {
47    pub(in crate::core) fn new(inner: ByteFnPrototype, constant: bool) -> ByteFn {
48        ByteFn(GcHeap::new(inner, constant))
49    }
50    // SAFETY: The caller must ensure that the constants are part of the same
51    // block as the bytecode function. Otherwise they will be collected and we
52    // will have a dangling pointer. Also this type must immediatly be put into
53    // the GC heap, because holding it past garbage collections is unsafe.
54    pub(crate) unsafe fn make(
55        op_codes: &[u8],
56        consts: &LispVec,
57        args: FnArgs,
58        depth: usize,
59    ) -> ByteFnPrototype {
60        let op_codes = op_codes.to_vec().into_boxed_slice();
61        #[cfg(miri)]
62        {
63            // TODO: the opcodes live outside of the heap because we are relying
64            // on them having a stable address. Therefore they get leaked. We
65            // need to find some way to address this.
66            unsafe extern "Rust" {
67                fn miri_static_root(ptr: *const u8);
68            }
69            let ptr: *const u8 = op_codes.as_ptr();
70            miri_static_root(ptr)
71        }
72        ByteFnPrototype {
73            constants: unsafe { Slot::new(consts.with_lifetime()) },
74            op_codes,
75            args,
76            depth,
77        }
78    }
79}
80
81impl ByteFnPrototype {
82    pub(crate) fn codes(&self) -> &[u8] {
83        &self.op_codes
84    }
85
86    pub(crate) fn consts<'ob>(&'ob self) -> &'ob [Object<'ob>] {
87        unsafe { std::mem::transmute::<&'ob [ObjCell], &'ob [Object<'ob>]>(&self.constants) }
88    }
89
90    /// Mutable view of the constants vector. Used by the pdump loader
91    /// to patch placeholder values after allocation.
92    pub(crate) fn consts_mut(&self) -> anyhow::Result<&[super::MutObjCell]> {
93        self.constants.try_mut()
94    }
95
96    pub(crate) fn index<'ob>(&self, index: usize, cx: &'ob Context) -> Option<Object<'ob>> {
97        match index {
98            0 => Some((self.args.into_arg_spec() as i64).into()),
99            1 => Some(cx.add(self.codes().to_vec())),
100            2 => Some(cx.add(self.consts())),
101            3 => Some(self.depth.into()),
102            _ => None,
103        }
104    }
105
106    pub(crate) const fn len(&self) -> usize {
107        4
108    }
109}
110
111impl<'new> CloneIn<'new, &'new Self> for ByteFn {
112    fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> super::Gc<&'new Self> {
113        let constants = self.constants.clone_in(bk);
114        let byte_fn =
115            unsafe { ByteFn::make(&self.op_codes, constants.untag(), self.args, self.depth) };
116        byte_fn.into_obj(bk)
117    }
118}
119
120impl Display for ByteFn {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        let spec = self.args.into_arg_spec();
123        let code = display_slice(&self.op_codes);
124        let consts = display_slice(&self.constants);
125        let depth = self.depth;
126        write!(f, "#[{spec} {code} {consts} {depth}]")
127    }
128}
129
130impl Debug for ByteFn {
131    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132        f.debug_struct("ByteFn")
133            .field("args", &self.args)
134            .field("op_code", &self.op_codes)
135            .field("constants", &self.constants)
136            .finish_non_exhaustive()
137    }
138}
139
140/// Argument requirments to a function.
141#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
142pub(crate) struct FnArgs {
143    /// a &rest argument.
144    pub(crate) rest: bool,
145    /// minimum required arguments.
146    pub(crate) required: u16,
147    /// &optional arguments.
148    pub(crate) optional: u16,
149    /// If this function is advised.
150    pub(crate) advice: bool,
151}
152
153impl FnArgs {
154    pub(crate) fn from_arg_spec(spec: i64) -> Result<Self> {
155        // The spec is an integer of the form NNNNNNNRMMMMMMM where the 7bit
156        // MMMMMMM specifies the minimum number of arguments, the 7-bit NNNNNNN
157        // specifies the maximum number of arguments (ignoring &rest) and the R
158        // bit specifies whether there is a &rest argument to catch the
159        // left-over arguments.
160        ensure!(spec >= 0, "Invalid bytecode argument spec: bits out of range");
161        ensure!(spec <= 0x7FFF, "Invalid bytecode argument spec: bits out of range");
162        let spec = spec as u16;
163        let required = spec & 0x7F;
164        let max = (spec >> 8) & 0x7F;
165        let Some(optional) = max.checked_sub(required) else {
166            bail!("Invalid bytecode argument spec: max of {max} was smaller then min {required}")
167        };
168        let rest = spec & 0x80 != 0;
169        Ok(FnArgs { required, optional, rest, advice: false })
170    }
171
172    pub(crate) fn into_arg_spec(self) -> u64 {
173        let mut spec = self.required;
174        let max = self.required + self.optional;
175        spec |= max << 8;
176        spec |= u16::from(self.rest) << 7;
177        u64::from(spec)
178    }
179}
180
181pub(crate) type BuiltInFn =
182    for<'ob> fn(usize, &mut Rt<Env>, &'ob mut Context) -> Result<Object<'ob>>;
183
184#[derive(Eq)]
185pub(crate) struct SubrFn {
186    pub(crate) subr: BuiltInFn,
187    pub(crate) args: FnArgs,
188    pub(crate) name: &'static str,
189}
190define_unbox!(SubrFn, Func, &'ob SubrFn);
191
192impl SubrFn {
193    pub(crate) fn call<'ob>(
194        &self,
195        arg_cnt: usize,
196        env: &mut Rt<Env>,
197        cx: &'ob mut Context,
198    ) -> Result<Object<'ob>> {
199        (self.subr)(arg_cnt, env, cx)
200    }
201}
202
203impl<'new> WithLifetime<'new> for &SubrFn {
204    type Out = &'new SubrFn;
205
206    unsafe fn with_lifetime(self) -> Self::Out {
207        &*(self as *const SubrFn)
208    }
209}
210
211impl Display for SubrFn {
212    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213        write!(f, "#<subr {}>", self.name)
214    }
215}
216
217impl Debug for SubrFn {
218    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219        write!(f, "{self}")
220    }
221}
222
223impl PartialEq for SubrFn {
224    fn eq(&self, other: &Self) -> bool {
225        let lhs = self.subr as *const BuiltInFn;
226        let rhs = other.subr as *const BuiltInFn;
227        lhs == rhs
228    }
229}
230
231#[cfg(test)]
232mod test {
233    use super::*;
234
235    fn check_arg_spec(spec: i64) {
236        assert_eq!(spec, FnArgs::from_arg_spec(spec).unwrap().into_arg_spec().try_into().unwrap());
237    }
238
239    #[test]
240    fn test_arg_spec() {
241        check_arg_spec(0);
242        check_arg_spec(257);
243        check_arg_spec(513);
244        check_arg_spec(128);
245        check_arg_spec(771);
246
247        assert!(FnArgs::from_arg_spec(12345).is_err());
248        assert!(FnArgs::from_arg_spec(1).is_err());
249        assert!(FnArgs::from_arg_spec(0xFFFF).is_err());
250    }
251}