facet_reflect/wip/
drop.rs

1use alloc::{vec, vec::Vec};
2use facet_core::{Type, UserType};
3
4#[allow(unused_imports)]
5use owo_colors::OwoColorize;
6
7use super::Wip;
8use crate::{FrameFlags, FrameMode, Guard, ValueId, trace, wip::frame::Frame};
9
10impl<'facet, 'shape> Drop for Wip<'facet, 'shape> {
11    fn drop(&mut self) {
12        trace!("🧹🧹🧹 WIP is dropping");
13
14        while let Some(frame) = self.frames.pop() {
15            self.track(frame);
16        }
17
18        let Some((root_id, _)) = self.istates.iter().find(|(_k, istate)| istate.depth == 0) else {
19            trace!("No root found, we probably built already");
20            return;
21        };
22
23        let root_id = *root_id;
24        let root_istate = self.istates.remove(&root_id).unwrap();
25        let root = Frame::recompose(root_id, root_istate);
26        let mut to_clean = vec![root];
27
28        let mut _root_guard: Option<Guard> = None;
29
30        while let Some(mut frame) = to_clean.pop() {
31            trace!(
32                "Cleaning frame: shape={} at {:p}, flags={:?}, mode={:?}, fully_initialized={}",
33                frame.shape.blue(),
34                frame.data.as_byte_ptr(),
35                frame.istate.flags.bright_magenta(),
36                frame.istate.mode.yellow(),
37                if frame.is_fully_initialized() {
38                    "✅"
39                } else {
40                    "❌"
41                }
42            );
43
44            if frame.istate.flags.contains(FrameFlags::MOVED) {
45                trace!(
46                    "{}",
47                    "Frame was moved out of, nothing to dealloc/drop_in_place".yellow()
48                );
49                continue;
50            }
51
52            match frame.shape.ty {
53                Type::User(UserType::Struct(sd)) => {
54                    if frame.is_fully_initialized() {
55                        trace!(
56                            "Dropping fully initialized struct: {} at {:p}",
57                            frame.shape.green(),
58                            frame.data.as_byte_ptr()
59                        );
60                        let frame = self.evict_tree(frame);
61                        unsafe { frame.drop_and_dealloc_if_needed() };
62                    } else {
63                        let num_fields = sd.fields.len();
64                        trace!(
65                            "De-initializing struct {} at {:p} field-by-field ({} fields)",
66                            frame.shape.yellow(),
67                            frame.data.as_byte_ptr(),
68                            num_fields.bright_cyan()
69                        );
70                        for i in 0..num_fields {
71                            if frame.istate.fields.has(i) {
72                                let field = sd.fields[i];
73                                let field_shape = field.shape();
74                                let field_ptr = unsafe { frame.data.field_init_at(field.offset) };
75                                let field_id = ValueId::new(field_shape, field_ptr.as_byte_ptr());
76                                trace!(
77                                    "Recursively cleaning field #{} '{}' of {}: field_shape={}, field_ptr={:p}",
78                                    i.bright_cyan(),
79                                    field.name.bright_blue(),
80                                    frame.shape.blue(),
81                                    field_shape.green(),
82                                    field_ptr.as_byte_ptr()
83                                );
84                                let istate = self.istates.remove(&field_id).unwrap();
85                                let field_frame = Frame::recompose(field_id, istate);
86                                to_clean.push(field_frame);
87                            } else {
88                                trace!(
89                                    "Field #{} '{}' of {} was NOT initialized, skipping",
90                                    i.bright_cyan(),
91                                    sd.fields[i].name.bright_red(),
92                                    frame.shape.red()
93                                );
94                            }
95                        }
96
97                        // we'll also need to clean up if we're root
98                        if frame.istate.mode == FrameMode::Root {
99                            if let Ok(layout) = frame.shape.layout.sized_layout() {
100                                _root_guard = Some(Guard {
101                                    ptr: frame.data.as_mut_byte_ptr(),
102                                    layout,
103                                });
104                            }
105                        }
106                    }
107                }
108                Type::User(UserType::Enum(_ed)) => {
109                    trace!(
110                        "Handling enum deallocation for {} at {:p}",
111                        frame.shape.yellow(),
112                        frame.data.as_byte_ptr()
113                    );
114
115                    // Check if a variant is selected
116                    if let Some(variant) = &frame.istate.variant {
117                        trace!(
118                            "Dropping enum variant {} of {} with {} fields",
119                            variant.name.bright_yellow(),
120                            frame.shape.yellow(),
121                            variant.data.fields.len()
122                        );
123
124                        // Recursively clean fields of the variant that are initialized
125                        for (i, field) in variant.data.fields.iter().enumerate() {
126                            if frame.istate.fields.has(i) {
127                                let field_shape = field.shape();
128                                let field_ptr = unsafe { frame.data.field_init_at(field.offset) };
129                                let field_id = ValueId::new(field_shape, field_ptr.as_byte_ptr());
130                                trace!(
131                                    "Recursively cleaning field #{} '{}' of variant {}: field_shape={}, field_ptr={:p}",
132                                    i.bright_cyan(),
133                                    field.name.bright_blue(),
134                                    variant.name.yellow(),
135                                    field_shape.green(),
136                                    field_ptr.as_byte_ptr()
137                                );
138                                if let Some(istate) = self.istates.remove(&field_id) {
139                                    let field_frame = Frame::recompose(field_id, istate);
140                                    to_clean.push(field_frame);
141                                } else {
142                                    trace!(
143                                        "Field not found in istates: #{} '{}' of variant {}",
144                                        i.bright_cyan(),
145                                        field.name.bright_blue(),
146                                        variant.name.yellow()
147                                    );
148                                    // that means it's fully initialized and we need to drop it
149                                    unsafe {
150                                        if let Some(drop_in_place) =
151                                            field_shape.vtable.drop_in_place
152                                        {
153                                            drop_in_place(field_ptr);
154                                        }
155                                    }
156                                }
157                            } else {
158                                trace!(
159                                    "Field #{} '{}' of variant {} was NOT initialized, skipping",
160                                    i.bright_cyan(),
161                                    field.name.bright_red(),
162                                    variant.name.yellow()
163                                );
164                            }
165                        }
166                    } else {
167                        trace!(
168                            "Enum {} has no variant selected, nothing to drop for fields",
169                            frame.shape.yellow()
170                        );
171                    }
172
173                    // we'll also need to clean up if we're root
174                    if frame.istate.mode == FrameMode::Root {
175                        if let Ok(layout) = frame.shape.layout.sized_layout() {
176                            _root_guard = Some(Guard {
177                                ptr: frame.data.as_mut_byte_ptr(),
178                                layout,
179                            });
180                        }
181                    }
182                }
183                _ => {
184                    trace!(
185                        "Can drop all at once for shape {} (frame mode {:?}) at {:p}",
186                        frame.shape.cyan(),
187                        frame.istate.mode.yellow(),
188                        frame.data.as_byte_ptr(),
189                    );
190
191                    if frame.is_fully_initialized() {
192                        unsafe { frame.drop_and_dealloc_if_needed() }
193                    } else {
194                        frame.dealloc_if_needed();
195                    }
196                }
197            }
198        }
199
200        // We might have some frames left over to deallocate for temporary allocations for keymap insertion etc.
201        let mut all_ids = self.istates.keys().copied().collect::<Vec<_>>();
202        for frame_id in all_ids.drain(..) {
203            let frame_istate = self.istates.remove(&frame_id).unwrap();
204
205            trace!(
206                "Checking leftover istate: id.shape={} id.ptr={:p} mode={:?}",
207                frame_id.shape.cyan(),
208                frame_id.ptr.red(),
209                frame_istate.mode.yellow()
210            );
211            let mut frame = Frame::recompose(frame_id, frame_istate);
212
213            if frame.is_fully_initialized() {
214                trace!("It's fully initialized, we can drop it");
215                unsafe { frame.drop_and_dealloc_if_needed() };
216            } else if frame.istate.flags.contains(FrameFlags::ALLOCATED) {
217                trace!("Not initialized but allocated, let's free it");
218                frame.dealloc_if_needed();
219            }
220        }
221    }
222}