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 pub(super) constants: Slot<&'static LispVec>,
27}
28
29#[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 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 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 pub(crate) fn index<'ob>(&self, index: usize, cx: &'ob Context) -> Option<Object<'ob>> {
91 match index {
92 0 => Some((self.args.into_arg_spec() as i64).into()),
93 1 => Some(cx.add(self.codes().to_vec())),
94 2 => Some(cx.add(self.consts())),
95 3 => Some(self.depth.into()),
96 _ => None,
97 }
98 }
99
100 pub(crate) const fn len(&self) -> usize {
101 4
102 }
103}
104
105impl<'new> CloneIn<'new, &'new Self> for ByteFn {
106 fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> super::Gc<&'new Self> {
107 let constants = self.constants.clone_in(bk);
108 let byte_fn =
109 unsafe { ByteFn::make(&self.op_codes, constants.untag(), self.args, self.depth) };
110 byte_fn.into_obj(bk)
111 }
112}
113
114impl Display for ByteFn {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 let spec = self.args.into_arg_spec();
117 let code = display_slice(&self.op_codes);
118 let consts = display_slice(&self.constants);
119 let depth = self.depth;
120 write!(f, "#[{spec} {code} {consts} {depth}]")
121 }
122}
123
124impl Debug for ByteFn {
125 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126 f.debug_struct("ByteFn")
127 .field("args", &self.args)
128 .field("op_code", &self.op_codes)
129 .field("constants", &self.constants)
130 .finish_non_exhaustive()
131 }
132}
133
134#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
136pub(crate) struct FnArgs {
137 pub(crate) rest: bool,
139 pub(crate) required: u16,
141 pub(crate) optional: u16,
143 pub(crate) advice: bool,
145}
146
147impl FnArgs {
148 pub(crate) fn from_arg_spec(spec: i64) -> Result<Self> {
149 ensure!(spec >= 0, "Invalid bytecode argument spec: bits out of range");
155 ensure!(spec <= 0x7FFF, "Invalid bytecode argument spec: bits out of range");
156 let spec = spec as u16;
157 let required = spec & 0x7F;
158 let max = (spec >> 8) & 0x7F;
159 let Some(optional) = max.checked_sub(required) else {
160 bail!("Invalid bytecode argument spec: max of {max} was smaller then min {required}")
161 };
162 let rest = spec & 0x80 != 0;
163 Ok(FnArgs { required, optional, rest, advice: false })
164 }
165
166 pub(crate) fn into_arg_spec(self) -> u64 {
167 let mut spec = self.required;
168 let max = self.required + self.optional;
169 spec |= max << 8;
170 spec |= u16::from(self.rest) << 7;
171 u64::from(spec)
172 }
173}
174
175pub(crate) type BuiltInFn =
176 for<'ob> fn(usize, &mut Rt<Env>, &'ob mut Context) -> Result<Object<'ob>>;
177
178#[derive(Eq)]
179pub(crate) struct SubrFn {
180 pub(crate) subr: BuiltInFn,
181 pub(crate) args: FnArgs,
182 pub(crate) name: &'static str,
183}
184define_unbox!(SubrFn, Func, &'ob SubrFn);
185
186impl SubrFn {
187 pub(crate) fn call<'ob>(
188 &self,
189 arg_cnt: usize,
190 env: &mut Rt<Env>,
191 cx: &'ob mut Context,
192 ) -> Result<Object<'ob>> {
193 (self.subr)(arg_cnt, env, cx)
194 }
195}
196
197impl<'new> WithLifetime<'new> for &SubrFn {
198 type Out = &'new SubrFn;
199
200 unsafe fn with_lifetime(self) -> Self::Out {
201 &*(self as *const SubrFn)
202 }
203}
204
205impl Display for SubrFn {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 write!(f, "#<subr {}>", &self.name)
208 }
209}
210
211impl Debug for SubrFn {
212 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213 write!(f, "{self}")
214 }
215}
216
217impl PartialEq for SubrFn {
218 #[expect(clippy::fn_to_numeric_cast_any)]
219 fn eq(&self, other: &Self) -> bool {
220 let lhs = self.subr as *const BuiltInFn;
221 let rhs = other.subr as *const BuiltInFn;
222 lhs == rhs
223 }
224}
225
226#[cfg(test)]
227mod test {
228 use super::*;
229
230 fn check_arg_spec(spec: i64) {
231 assert_eq!(spec, FnArgs::from_arg_spec(spec).unwrap().into_arg_spec().try_into().unwrap());
232 }
233
234 #[test]
235 fn test_arg_spec() {
236 check_arg_spec(0);
237 check_arg_spec(257);
238 check_arg_spec(513);
239 check_arg_spec(128);
240 check_arg_spec(771);
241
242 assert!(FnArgs::from_arg_spec(12345).is_err());
243 assert!(FnArgs::from_arg_spec(1).is_err());
244 assert!(FnArgs::from_arg_spec(0xFFFF).is_err());
245 }
246}