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 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#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
142pub(crate) struct FnArgs {
143 pub(crate) rest: bool,
145 pub(crate) required: u16,
147 pub(crate) optional: u16,
149 pub(crate) advice: bool,
151}
152
153impl FnArgs {
154 pub(crate) fn from_arg_spec(spec: i64) -> Result<Self> {
155 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}