1use super::*;
2
3use crate::sabi_types::Constructor;
4
5#[repr(C)]
7#[derive(Debug, Copy, Clone, StableAbi)]
8#[sabi(unsafe_sabi_opaque_fields)]
9pub struct TLField {
10 name: RStr<'static>,
12 lifetime_indices: LifetimeArrayOrSlice<'static>,
14 layout: Constructor<&'static TypeLayout>,
20
21 function_range: TLFunctionSlice,
23
24 is_function: bool,
26
27 field_accessor: FieldAccessor,
29}
30
31impl TLField {
34 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 pub fn full_type(&self) -> FmtFullType {
52 self.layout.get().full_type()
53 }
54
55 pub fn name(&self) -> &'static str {
57 self.name.as_str()
58 }
59
60 pub const fn lifetime_indices(&self) -> LifetimeArrayOrSlice<'static> {
62 self.lifetime_indices
63 }
64 pub fn layout(&self) -> &'static TypeLayout {
66 self.layout.get()
67 }
68 pub const fn function_range(&self) -> TLFunctionSlice {
70 self.function_range
71 }
72 pub const fn is_function(&self) -> bool {
74 self.is_function
75 }
76 pub const fn field_accessor(&self) -> FieldAccessor {
79 self.field_accessor
80 }
81
82 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
163struct 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#[derive(Debug, Copy, Clone, PartialEq)]
195struct TLFieldShallow {
196 name: RStr<'static>,
197
198 full_type: FmtFullType,
199
200 lifetime_indices: LifetimeArrayOrSlice<'static>,
201
202 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
232abi_stable_shared::declare_comp_tl_field! {
235 attrs=[
236 derive(StableAbi),
237 sabi(unsafe_sabi_opaque_fields),
238 ]
239}
240
241impl CompTLField {
242 pub fn name(&self, strings: &'static str) -> &'static str {
244 &strings[self.name_start_len().to_range()]
245 }
246
247 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 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 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 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}