abi_stable/type_layout/
tl_field.rs

1use super::*;
2
3use crate::sabi_types::Constructor;
4
5/// The layout of a field.
6#[repr(C)]
7#[derive(Debug, Copy, Clone, StableAbi)]
8#[sabi(unsafe_sabi_opaque_fields)]
9pub struct TLField {
10    /// The field's name.
11    name: RStr<'static>,
12    /// Which lifetimes in the struct are referenced in the field type.
13    lifetime_indices: LifetimeArrayOrSlice<'static>,
14    /// The layout of the field's type.
15    ///
16    /// This is a function pointer to avoid infinite recursion,
17    /// if you have a `&'static TypeLayout`s with the same address as one of its parent type,
18    /// you've encountered a cycle.
19    layout: Constructor<&'static TypeLayout>,
20
21    /// The function pointer types within the field.
22    function_range: TLFunctionSlice,
23
24    /// Whether this field is only a function pointer.
25    is_function: bool,
26
27    /// How this field is accessed.
28    field_accessor: FieldAccessor,
29}
30
31///////////////////////////
32
33impl TLField {
34    /// Constructs a field which does not contain function pointers,or lifetime indices.
35    pub const fn new(
36        name: RStr<'static>,
37        layout: extern "C" fn() -> &'static TypeLayout,
38        vars: &'static SharedVars,
39    ) -> Self {
40        Self {
41            name,
42            lifetime_indices: LifetimeArrayOrSlice::EMPTY,
43            layout: Constructor(layout),
44            function_range: TLFunctionSlice::empty(vars),
45            is_function: false,
46            field_accessor: FieldAccessor::Direct,
47        }
48    }
49
50    /// Gets a printable version of the field type.
51    pub fn full_type(&self) -> FmtFullType {
52        self.layout.get().full_type()
53    }
54
55    /// Gets the name of the field
56    pub fn name(&self) -> &'static str {
57        self.name.as_str()
58    }
59
60    /// Gets the lifetimes that the field references.
61    pub const fn lifetime_indices(&self) -> LifetimeArrayOrSlice<'static> {
62        self.lifetime_indices
63    }
64    /// Gets the layout of the field type
65    pub fn layout(&self) -> &'static TypeLayout {
66        self.layout.get()
67    }
68    /// Gets all the function pointer types in the field.
69    pub const fn function_range(&self) -> TLFunctionSlice {
70        self.function_range
71    }
72    /// Gets whether the field is itself a function pointer.
73    pub const fn is_function(&self) -> bool {
74        self.is_function
75    }
76    /// Gets the `FieldAccessor` for the type,
77    /// which describes whether a field is accessible,and how it is accessed.
78    pub const fn field_accessor(&self) -> FieldAccessor {
79        self.field_accessor
80    }
81
82    /// Used for calling recursive methods,
83    /// so as to avoid infinite recursion in types that reference themselves(even indirectly).
84    fn recursive<F, U>(self, f: F) -> U
85    where
86        F: FnOnce(usize, TLFieldShallow) -> U,
87    {
88        let mut already_recursed = false;
89        let mut recursion_depth = !0;
90        let mut visited_nodes = !0;
91
92        ALREADY_RECURSED.with(|state| {
93            let mut state = state.borrow_mut();
94            recursion_depth = state.recursion_depth;
95            visited_nodes = state.visited_nodes;
96            state.recursion_depth += 1;
97            state.visited_nodes += 1;
98            already_recursed = state.visited.replace(self.layout.get()).is_some();
99        });
100
101        let _guard = if visited_nodes == 0 {
102            Some(ResetRecursion)
103        } else {
104            None
105        };
106
107        let field = TLFieldShallow::new(self, !already_recursed);
108        let res = f(recursion_depth, field);
109
110        ALREADY_RECURSED.with(|state| {
111            let mut state = state.borrow_mut();
112            state.recursion_depth -= 1;
113        });
114
115        res
116    }
117}
118
119impl Eq for TLField {}
120
121impl PartialEq for TLField {
122    fn eq(&self, other: &Self) -> bool {
123        self.recursive(|_, this| {
124            let r = TLFieldShallow::new(*other, this.layout.is_some());
125            this == r
126        })
127    }
128}
129
130impl Display for TLField {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        let layout = self.layout.get();
133        let (package, version) = layout.item_info().package_and_version();
134        writeln!(
135            f,
136            "field_name:{name}\n\
137             type:{ty}\n\
138             size:{size} align:{align}\n\
139             package:'{package}' version:'{version}'",
140            name = self.name,
141            ty = layout.full_type(),
142            size = layout.size(),
143            align = layout.alignment(),
144            package = package,
145            version = version,
146        )?;
147
148        if !self.function_range.is_empty() {
149            writeln!(f, "fn pointer(s):")?;
150            for func in self.function_range.iter() {
151                writeln!(f, "{}", func.to_string().left_padder(4))?;
152            }
153        }
154
155        if !self.lifetime_indices.is_empty() {
156            writeln!(f, "lifetime indices:{:?}", self.lifetime_indices)?;
157        }
158
159        Ok(())
160    }
161}
162
163///////////////////////////
164
165struct ResetRecursion;
166
167impl Drop for ResetRecursion {
168    fn drop(&mut self) {
169        ALREADY_RECURSED.with(|state| {
170            let mut state = state.borrow_mut();
171            state.recursion_depth = 0;
172            state.visited_nodes = 0;
173            state.visited.clear();
174        });
175    }
176}
177
178struct RecursionState {
179    recursion_depth: usize,
180    visited_nodes: u64,
181    visited: HashSet<*const TypeLayout>,
182}
183
184thread_local! {
185    static ALREADY_RECURSED: RefCell<RecursionState> = RefCell::new(RecursionState{
186        recursion_depth:0,
187        visited_nodes:0,
188        visited: HashSet::default(),
189    });
190}
191
192////////////////////////////////////
193
194#[derive(Debug, Copy, Clone, PartialEq)]
195struct TLFieldShallow {
196    name: RStr<'static>,
197
198    full_type: FmtFullType,
199
200    lifetime_indices: LifetimeArrayOrSlice<'static>,
201
202    /// This is None if it already printed that TypeLayout
203    layout: Option<&'static TypeLayout>,
204
205    function_range: TLFunctionSlice,
206
207    is_function: bool,
208
209    field_accessor: FieldAccessor,
210}
211
212impl TLFieldShallow {
213    fn new(field: TLField, include_type_layout: bool) -> Self {
214        let layout = field.layout.get();
215        TLFieldShallow {
216            name: field.name,
217            lifetime_indices: field.lifetime_indices,
218            layout: if include_type_layout {
219                Some(layout)
220            } else {
221                None
222            },
223            full_type: layout.full_type(),
224
225            function_range: field.function_range,
226            is_function: field.is_function,
227            field_accessor: field.field_accessor,
228        }
229    }
230}
231
232////////////////////////////////////
233
234abi_stable_shared::declare_comp_tl_field! {
235    attrs=[
236        derive(StableAbi),
237        sabi(unsafe_sabi_opaque_fields),
238    ]
239}
240
241impl CompTLField {
242    /// Gets the name of the field from `SharedVars`'s string slice.
243    pub fn name(&self, strings: &'static str) -> &'static str {
244        &strings[self.name_start_len().to_range()]
245    }
246
247    /// Gets the name of the field from `SharedVars`'s slice of lifetime indices.
248    pub fn lifetime_indices(
249        &self,
250        indices: &'static [LifetimeIndexPair],
251    ) -> LifetimeArrayOrSlice<'static> {
252        let comp = LifetimeRange::from_u21(self.lifetime_indices_bits());
253        comp.slicing(indices)
254    }
255
256    /// Gets the `FieldAccessor` for the type from `SharedVars`'s string slice,
257    /// which describes whether a field is accessible,and how it is accessed..
258    pub fn field_accessor(&self, strings: &'static str) -> FieldAccessor {
259        let name_end = self.name_start_len().end();
260        let comp = CompFieldAccessor::from_u3((self.bits0 >> Self::FIELD_ACCESSOR_OFFSET) as u8);
261        let accessor_payload = if comp.requires_payload() {
262            strings[name_end..].split(';').next().unwrap_or("")
263        } else {
264            ""
265        };
266        comp.expand(accessor_payload)
267            .unwrap_or(FieldAccessor::Opaque)
268    }
269
270    /// Gets the name of the field from `SharedVars`'s slice of type layouts.
271    pub const fn type_layout(
272        &self,
273        type_layouts: &'static [extern "C" fn() -> &'static TypeLayout],
274    ) -> extern "C" fn() -> &'static TypeLayout {
275        type_layouts[self.type_layout_index()]
276    }
277
278    /// Expands this CompTLField into a TLField.
279    pub fn expand(
280        &self,
281        field_index: usize,
282        functions: Option<&'static TLFunctions>,
283        vars: &'static SharedVars,
284    ) -> TLField {
285        let strings = vars.strings();
286        let function_range = TLFunctionSlice::for_field(field_index, functions, vars);
287
288        TLField {
289            name: self.name(strings).into(),
290            lifetime_indices: self.lifetime_indices(vars.lifetime_indices()),
291            layout: Constructor(self.type_layout(vars.type_layouts())),
292            function_range,
293            is_function: self.is_function(),
294            field_accessor: self.field_accessor(strings),
295        }
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302    use crate::{abi_stability::stable_abi_trait::get_type_layout, std_types::RString};
303
304    #[test]
305    fn roundtrip() {
306        const UNIT_CTOR: extern "C" fn() -> &'static TypeLayout = get_type_layout::<()>;
307        const U32_CTOR: extern "C" fn() -> &'static TypeLayout = get_type_layout::<u32>;
308        const RSTRING_CTOR: extern "C" fn() -> &'static TypeLayout = get_type_layout::<RString>;
309
310        const MONO_VARS: &MonoSharedVars = &MonoSharedVars::new(rstr!("foo;bar; baz; "), rslice![]);
311
312        const VARS: &SharedVars = &SharedVars::new(
313            MONO_VARS,
314            rslice![UNIT_CTOR, U32_CTOR, RSTRING_CTOR],
315            rslice![],
316        );
317
318        let vars = VARS;
319
320        let mut arr = [LifetimeIndex::NONE; 5];
321        arr[0] = LifetimeIndex::STATIC;
322        let lifetime_range = LifetimeRange::from_array(arr);
323
324        let field = CompTLField::new(
325            StartLen::new(9, 3),
326            lifetime_range,
327            CompFieldAccessor::DIRECT,
328            TypeLayoutIndex::from_u10(2),
329            false,
330        );
331
332        assert_eq!(field.name(vars.strings()), "baz",);
333        assert_eq!(
334            field.lifetime_indices(vars.lifetime_indices()),
335            lifetime_range.slicing(vars.lifetime_indices()),
336        );
337        assert_eq!(field.type_layout(vars.type_layouts()), RSTRING_CTOR,);
338        assert_eq!(field.field_accessor(vars.strings()), FieldAccessor::Direct,);
339    }
340}