abi_stable/type_layout/
tl_functions.rs

1use super::*;
2
3use crate::{
4    abi_stability::stable_abi_trait::get_type_layout, sabi_types::Constructor, std_types::RVec,
5    traits::IntoReprC,
6};
7
8use std::{
9    cmp::{Eq, PartialEq},
10    ops::Range,
11};
12
13///////////////////////////////////////////////////////////////////////////////
14
15#[cfg(test)]
16mod tests;
17
18///////////////////////////////////////////////////////////////////////////////
19
20/// All the function pointer types in a type declaration.
21#[repr(C)]
22#[derive(Debug, Copy, Clone, StableAbi)]
23#[sabi(unsafe_sabi_opaque_fields)]
24pub struct TLFunctions {
25    functions: *const CompTLFunction,
26    /// The range of `CompTLFunction` that each field in TLFields owns.
27    field_fn_ranges: *const StartLen,
28
29    functions_len: u16,
30    field_fn_ranges_len: u16,
31}
32
33unsafe impl Sync for TLFunctions {}
34unsafe impl Send for TLFunctions {}
35
36impl TLFunctions {
37    /// Constructs a TLFunctions.
38    pub const fn new(
39        functions: RSlice<'static, CompTLFunction>,
40        field_fn_ranges: RSlice<'static, StartLenRepr>,
41    ) -> Self {
42        Self {
43            functions: functions.as_ptr(),
44            functions_len: functions.len() as u16,
45            field_fn_ranges: field_fn_ranges.as_ptr() as *const StartLenRepr as *const StartLen,
46            field_fn_ranges_len: field_fn_ranges.len() as u16,
47        }
48    }
49
50    fn functions(&self) -> &'static [CompTLFunction] {
51        unsafe { std::slice::from_raw_parts(self.functions, self.functions_len as usize) }
52    }
53
54    fn field_fn_ranges(&self) -> &'static [StartLen] {
55        unsafe {
56            std::slice::from_raw_parts(self.field_fn_ranges, self.field_fn_ranges_len as usize)
57        }
58    }
59
60    /// Gets the `nth` `TLFunction` in this `TLFunctions`.
61    /// Returns None if there is not `nth` TLFunction.
62    pub fn get(&'static self, nth: usize, shared_vars: &'static SharedVars) -> Option<TLFunction> {
63        let func = self.functions().get(nth)?;
64        Some(func.expand(shared_vars))
65    }
66
67    /// Gets the `nth` `TLFunction` in this `TLFunctions`.
68    ///
69    /// # Panics
70    ///
71    /// This function panics if `nth` is out of bounds
72    /// (when `nth` is greater than or equal to `self.len()`)
73    pub fn index(&'static self, nth: usize, shared_vars: &'static SharedVars) -> TLFunction {
74        self.functions()[nth].expand(shared_vars)
75    }
76
77    /// Gets the amount of `TLFunction` in this `TLFunctions`.
78    #[inline]
79    pub const fn len(&'static self) -> usize {
80        self.functions_len as usize
81    }
82
83    /// Whether this is empty.
84    pub const fn is_empty(&'static self) -> bool {
85        self.functions_len == 0
86    }
87}
88
89///////////////////////////////////////////////////////////////////////////////
90
91/// A slice of functions from a `TLFunctions`.
92#[repr(C)]
93#[derive(Copy, Clone, StableAbi)]
94#[sabi(unsafe_sabi_opaque_fields)]
95pub struct TLFunctionSlice {
96    functions: Option<&'static TLFunctions>,
97    shared_vars: &'static SharedVars,
98    fn_range: StartLen,
99}
100
101impl TLFunctionSlice {
102    /// Constructs an empty `TLFunctionSlice`.
103    pub const fn empty(shared_vars: &'static SharedVars) -> Self {
104        Self {
105            functions: None,
106            shared_vars,
107            fn_range: StartLen::EMPTY,
108        }
109    }
110
111    /// Constructs the `TLFunctionSlice` for the function pointers in the `i`th field.
112    pub fn for_field(
113        i: usize,
114        functions: Option<&'static TLFunctions>,
115        shared_vars: &'static SharedVars,
116    ) -> Self {
117        let fn_range = functions
118            .and_then(|fns| fns.field_fn_ranges().get(i).cloned())
119            .unwrap_or(StartLen::EMPTY);
120
121        Self {
122            functions,
123            fn_range,
124            shared_vars,
125        }
126    }
127
128    /// Gets the `&'static SharedVars` associated with this slice.
129    pub const fn shared_vars(&self) -> &'static SharedVars {
130        self.shared_vars
131    }
132    /// Returns an iterator over the `TLFunction`s in the slice.
133    #[inline]
134    pub fn iter(self) -> TLFunctionIter {
135        TLFunctionIter::new(self.fn_range, self.functions, self.shared_vars)
136    }
137
138    /// Gets a `TLFunction` at the `index`.
139    /// This returns `None` if `index` is outside the slice.
140    pub fn get(self, index: usize) -> Option<TLFunction> {
141        self.functions?
142            .get(self.fn_range.start_usize() + index, self.shared_vars)
143    }
144
145    /// Gets a `TLFunction` at the `index`.
146    ///
147    /// # Panic
148    ///
149    /// This panics if the `TLFunction` is outside the slice.
150    pub fn index(self, index: usize) -> TLFunction {
151        self.functions
152            .expect("self.functions must be Some(..) to index a TLFunctionSlice")
153            .index(self.fn_range.start_usize() + index, self.shared_vars)
154    }
155
156    /// Gets the length of this slice.
157    #[inline]
158    pub const fn len(self) -> usize {
159        self.fn_range.len_usize()
160    }
161    /// Gets whether this slice is empty.
162    #[inline]
163    pub const fn is_empty(self) -> bool {
164        self.fn_range.len() == 0
165    }
166}
167
168impl IntoIterator for TLFunctionSlice {
169    type IntoIter = TLFunctionIter;
170    type Item = TLFunction;
171
172    #[inline]
173    fn into_iter(self) -> TLFunctionIter {
174        self.iter()
175    }
176}
177
178impl Debug for TLFunctionSlice {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        f.debug_list().entries(self.iter()).finish()
181    }
182}
183
184impl Eq for TLFunctionSlice {}
185
186impl PartialEq for TLFunctionSlice {
187    fn eq(&self, other: &Self) -> bool {
188        self.fn_range.len() == other.fn_range.len() && self.iter().eq(other.iter())
189    }
190}
191
192///////////////////////////////////////////////////////////////////////////////
193
194/// Stores all the supported function qualifiers.
195///
196/// Currently only these are supported:
197/// - `unsafe`
198///
199/// More may be added in an ABI compatible version
200#[repr(transparent)]
201#[derive(Copy, Clone, Debug, PartialEq, Eq, StableAbi)]
202pub struct TLFunctionQualifiers(u16);
203
204impl TLFunctionQualifiers {
205    /// Constructs a `TLFunctionQualifiers` with no qualifiers enabled.
206    pub const NEW: Self = Self(0);
207
208    const UNSAFE_BIT: u16 = 1;
209
210    /// Whether the function is `unsafe`
211    pub const fn is_unsafe(&self) -> bool {
212        (self.0 & Self::UNSAFE_BIT) != 0
213    }
214    /// Marks the function as `unsafe`
215    pub const fn set_unsafe(mut self) -> Self {
216        self.0 |= Self::UNSAFE_BIT;
217        self
218    }
219}
220
221///////////////////////////////////////////////////////////////////////////////
222
223/// A compressed version of `TLFunction`,
224/// which can be expanded into a `TLFunction` by calling the `expand` method.
225#[repr(C)]
226#[derive(Copy, Clone, Debug, PartialEq, Eq, StableAbi)]
227#[sabi(unsafe_sabi_opaque_fields)]
228pub struct CompTLFunction {
229    name: StartLen,
230    contiguous_strings_offset: u16,
231    bound_lifetimes_len: u16,
232    param_names_len: u16,
233    /// Stores `!0` if the return type is `()`.
234    return_type_layout: u16,
235    paramret_lifetime_range: LifetimeRange,
236    param_type_layouts: TypeLayoutRange,
237    fn_qualifs: TLFunctionQualifiers,
238}
239
240impl CompTLFunction {
241    /// Constructs a CompTLFunction.
242    #[allow(clippy::too_many_arguments)]
243    pub const fn new(
244        name: StartLenRepr,
245        contiguous_strings_offset: u16,
246        bound_lifetimes_len: u16,
247        param_names_len: u16,
248        return_type_layout: u16,
249        paramret_lifetime_range: u32,
250        param_type_layouts: u64,
251        fn_qualifs: TLFunctionQualifiers,
252    ) -> Self {
253        Self {
254            name: StartLen::from_u32(name),
255            contiguous_strings_offset,
256            bound_lifetimes_len,
257            param_names_len,
258            return_type_layout,
259            paramret_lifetime_range: LifetimeRange::from_u21(paramret_lifetime_range),
260            param_type_layouts: TypeLayoutRange::from_u64(param_type_layouts),
261            fn_qualifs,
262        }
263    }
264
265    /// Decompresses this CompTLFunction into a TLFunction.
266    pub fn expand(&self, shared_vars: &'static SharedVars) -> TLFunction {
267        let strings = shared_vars.strings().into_c();
268        let lifetime_indices = shared_vars.lifetime_indices();
269        let type_layouts = shared_vars.type_layouts();
270
271        let cs_offset = self.contiguous_strings_offset as usize;
272
273        let bound_lifetimes = cs_offset..cs_offset + (self.bound_lifetimes_len as usize);
274        let param_names =
275            bound_lifetimes.end..bound_lifetimes.end + (self.param_names_len as usize);
276
277        TLFunction {
278            shared_vars: CmpIgnored::new(shared_vars),
279            name: strings.slice(self.name.to_range()),
280            bound_lifetimes: strings.slice(bound_lifetimes),
281            param_names: strings.slice(param_names),
282            param_type_layouts: self.param_type_layouts.expand(type_layouts),
283            paramret_lifetime_indices: self.paramret_lifetime_range.slicing(lifetime_indices),
284            return_type_layout: type_layouts
285                .get(self.return_type_layout as usize)
286                .map(|fnp| Constructor(*fnp)),
287            fn_qualifs: self.fn_qualifs,
288        }
289    }
290}
291
292///////////////////////////////////////////////////////////////////////////////
293
294/// A function pointer in a field.
295#[repr(C)]
296#[derive(Copy, Clone, Debug, Eq, StableAbi)]
297#[sabi(unsafe_sabi_opaque_fields)]
298pub struct TLFunction {
299    pub(super) shared_vars: CmpIgnored<&'static SharedVars>,
300
301    /// The name of the field this is used inside of.
302    pub name: RStr<'static>,
303
304    /// The named lifetime parameters of the function itself (declared in `for<>`),
305    /// separated by ';'.
306    pub bound_lifetimes: RStr<'static>,
307
308    /// A ';' separated list of all the parameter names.
309    pub param_names: RStr<'static>,
310
311    /// All the type layouts of the parameters.
312    pub param_type_layouts: MultipleTypeLayouts<'static>,
313    /// The lifetimes that the parameters and return types reference.
314    pub paramret_lifetime_indices: LifetimeArrayOrSlice<'static>,
315
316    /// The return type of the function.
317    return_type_layout: Option<Constructor<&'static TypeLayout>>,
318
319    /// The function qualifiers
320    pub fn_qualifs: TLFunctionQualifiers,
321}
322
323impl PartialEq for TLFunction {
324    fn eq(&self, other: &Self) -> bool {
325        self.name == other.name
326            && self.bound_lifetimes == other.bound_lifetimes
327            && self.param_names == other.param_names
328            && self.get_params_ret_iter().eq(other.get_params_ret_iter())
329            && self.paramret_lifetime_indices == other.paramret_lifetime_indices
330            && self.return_type_layout.map(|x| x.get()) == other.return_type_layout.map(|x| x.get())
331            && self.fn_qualifs == other.fn_qualifs
332    }
333}
334
335impl TLFunction {
336    pub(crate) fn get_param_names(&self) -> GetParamNames {
337        GetParamNames {
338            split: self.param_names.as_str().split(';'),
339            length: self.param_type_layouts.len(),
340            current: 0,
341        }
342    }
343
344    /// Gets the parameter types
345    pub(crate) fn get_params(&self) -> impl ExactSizeIterator<Item = TLField> + Clone + Debug {
346        let shared_vars = *self.shared_vars;
347        self.get_param_names()
348            .zip(self.param_type_layouts.iter())
349            .map(move |(param_name, layout)| TLField::new(param_name.into(), layout, shared_vars))
350    }
351
352    pub(crate) fn get_return(&self) -> TLField {
353        const UNIT_GET_ABI_INFO: extern "C" fn() -> &'static TypeLayout = get_type_layout::<()>;
354
355        TLField::new(
356            rstr!("__returns"),
357            match self.return_type_layout {
358                Some(Constructor(x)) => x,
359                None => UNIT_GET_ABI_INFO,
360            },
361            &self.shared_vars,
362        )
363    }
364
365    /// Gets the type layout of the return type
366    pub const fn return_type_layout(&self) -> Option<extern "C" fn() -> &'static TypeLayout> {
367        match self.return_type_layout {
368            Some(x) => Some(x.0),
369            None => None,
370        }
371    }
372
373    /// Gets the parameters and return types
374    pub(crate) fn get_params_ret_iter(
375        &self,
376    ) -> impl ExactSizeIterator<Item = TLField> + Clone + Debug {
377        ChainOnce::new(self.get_params(), self.get_return())
378    }
379
380    /// Gets the parameters and return types
381    #[allow(dead_code)]
382    pub(crate) fn get_params_ret_vec(&self) -> RVec<TLField> {
383        self.get_params_ret_iter().collect()
384    }
385
386    pub(crate) const fn qualifiers(&self) -> TLFunctionQualifiers {
387        self.fn_qualifs
388    }
389}
390
391impl Display for TLFunction {
392    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393        if self.fn_qualifs.is_unsafe() {
394            f.write_str("unsafe ")?;
395        }
396        f.write_str("fn(")?;
397        let params = self.get_params();
398        let param_count = params.len();
399        for (param_i, param) in params.enumerate() {
400            Display::fmt(&param.name(), f)?;
401            Display::fmt(&": ", f)?;
402            Display::fmt(&param.full_type(), f)?;
403            if param_i + 1 != param_count {
404                Display::fmt(&", ", f)?;
405            }
406        }
407        write!(f, ")")?;
408
409        let returns = self.get_return();
410        Display::fmt(&"->", f)?;
411        Display::fmt(&returns.full_type(), f)?;
412
413        if !self.paramret_lifetime_indices.is_empty() {
414            writeln!(f, "\nlifetime indices:{:?}", self.paramret_lifetime_indices)?;
415        }
416
417        Ok(())
418    }
419}
420
421///////////////////////////////////////////////////////////////////////////////
422
423/// An iterator over a range of `TLFunction`s.
424pub struct TLFunctionIter {
425    start: usize,
426    end: usize,
427    functions: Option<&'static TLFunctions>,
428    shared_vars: &'static SharedVars,
429}
430
431#[allow(clippy::missing_const_for_fn)]
432impl TLFunctionIter {
433    fn new(
434        start_len: StartLen,
435        functions: Option<&'static TLFunctions>,
436        shared_vars: &'static SharedVars,
437    ) -> Self {
438        let Range { start, end } = start_len.to_range();
439        if let Some(functions) = functions {
440            assert!(start <= functions.len(), "{} < {}", start, functions.len());
441            assert!(end <= functions.len(), "{} < {}", end, functions.len());
442        }
443        Self {
444            start,
445            end,
446            functions,
447            shared_vars,
448        }
449    }
450    fn length(&self) -> usize {
451        self.end - self.start
452    }
453}
454
455impl Iterator for TLFunctionIter {
456    type Item = TLFunction;
457
458    fn next(&mut self) -> Option<TLFunction> {
459        let functions = self.functions?;
460        if self.start >= self.end {
461            return None;
462        }
463        let ret = functions.index(self.start, self.shared_vars);
464        self.start += 1;
465        Some(ret)
466    }
467
468    fn size_hint(&self) -> (usize, Option<usize>) {
469        let len = self.length();
470        (len, Some(len))
471    }
472
473    fn count(self) -> usize {
474        self.length()
475    }
476}
477
478impl ExactSizeIterator for TLFunctionIter {}
479
480////////////////////////////////////
481
482#[derive(Debug, Clone)]
483pub struct GetParamNames {
484    split: std::str::Split<'static, char>,
485    length: usize,
486    current: usize,
487}
488
489impl Iterator for GetParamNames {
490    type Item = &'static str;
491    fn next(&mut self) -> Option<Self::Item> {
492        if self.length == self.current {
493            return None;
494        }
495        let current = self.current;
496        self.current += 1;
497        match self.split.next().filter(|&x| !x.is_empty() || x == "_") {
498            Some(x) => Some(x),
499            None => Some(PARAM_INDEX[current]),
500        }
501    }
502
503    fn size_hint(&self) -> (usize, Option<usize>) {
504        let len = self.length - self.current;
505        (len, Some(len))
506    }
507    fn count(self) -> usize {
508        self.length - self.current
509    }
510}
511
512impl std::iter::ExactSizeIterator for GetParamNames {}
513
514static PARAM_INDEX: [&str; 64] = [
515    "param_0", "param_1", "param_2", "param_3", "param_4", "param_5", "param_6", "param_7",
516    "param_8", "param_9", "param_10", "param_11", "param_12", "param_13", "param_14", "param_15",
517    "param_16", "param_17", "param_18", "param_19", "param_20", "param_21", "param_22", "param_23",
518    "param_24", "param_25", "param_26", "param_27", "param_28", "param_29", "param_30", "param_31",
519    "param_32", "param_33", "param_34", "param_35", "param_36", "param_37", "param_38", "param_39",
520    "param_40", "param_41", "param_42", "param_43", "param_44", "param_45", "param_46", "param_47",
521    "param_48", "param_49", "param_50", "param_51", "param_52", "param_53", "param_54", "param_55",
522    "param_56", "param_57", "param_58", "param_59", "param_60", "param_61", "param_62", "param_63",
523];