facet_reflect/partial/partial_api/
ptr.rs

1use super::*;
2use crate::AllocatedShape;
3
4////////////////////////////////////////////////////////////////////////////////////////////////////
5// Smart pointers
6////////////////////////////////////////////////////////////////////////////////////////////////////
7impl<const BORROW: bool> Partial<'_, BORROW> {
8    /// Pushes a frame to initialize the inner value of a smart pointer (`Box<T>`, `Arc<T>`, etc.)
9    pub fn begin_smart_ptr(mut self) -> Result<Self, ReflectError> {
10        crate::trace!("begin_smart_ptr()");
11
12        // Check that we have a SmartPointer and get necessary data
13        let (smart_ptr_def, pointee_shape) = {
14            let frame = self.frames().last().unwrap();
15
16            match &frame.allocated.shape().def {
17                Def::Pointer(smart_ptr_def) if smart_ptr_def.constructible_from_pointee() => {
18                    let pointee_shape = match smart_ptr_def.pointee() {
19                        Some(shape) => shape,
20                        None => {
21                            return Err(ReflectError::OperationFailed {
22                                shape: frame.allocated.shape(),
23                                operation: "Smart pointer must have a pointee shape",
24                            });
25                        }
26                    };
27                    (*smart_ptr_def, pointee_shape)
28                }
29                _ => {
30                    return Err(ReflectError::OperationFailed {
31                        shape: frame.allocated.shape(),
32                        operation: "push_smart_ptr can only be called on compatible types",
33                    });
34                }
35            }
36        };
37
38        // Handle re-initialization if the smart pointer is already initialized
39        self.prepare_for_reinitialization();
40
41        let frame = self.frames_mut().last_mut().unwrap();
42
43        if pointee_shape.layout.sized_layout().is_ok() {
44            // pointee is sized, we can allocate it — for `Arc<T>` we'll be allocating a `T` and
45            // holding onto it. We'll build a new Arc with it when ending the smart pointer frame.
46
47            frame.tracker = Tracker::SmartPointer;
48
49            let inner_layout = match pointee_shape.layout.sized_layout() {
50                Ok(layout) => layout,
51                Err(_) => {
52                    return Err(ReflectError::Unsized {
53                        shape: pointee_shape,
54                        operation: "begin_smart_ptr, calculating inner value layout",
55                    });
56                }
57            };
58            let inner_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(inner_layout) };
59            let Some(inner_ptr) = NonNull::new(inner_ptr) else {
60                return Err(ReflectError::OperationFailed {
61                    shape: frame.allocated.shape(),
62                    operation: "failed to allocate memory for smart pointer inner value",
63                });
64            };
65
66            // Push a new frame for the inner value
67            self.frames_mut().push(Frame::new(
68                PtrUninit::new(inner_ptr.as_ptr()),
69                AllocatedShape::new(pointee_shape, inner_layout.size()),
70                FrameOwnership::Owned,
71            ));
72        } else {
73            // pointee is unsized, we only support a handful of cases there
74            if pointee_shape == str::SHAPE {
75                crate::trace!("Pointee is str");
76
77                // Allocate space for a String
78                let string_layout = String::SHAPE
79                    .layout
80                    .sized_layout()
81                    .expect("String must have a sized layout");
82                let string_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(string_layout) };
83                let Some(string_ptr) = NonNull::new(string_ptr) else {
84                    return Err(ReflectError::OperationFailed {
85                        shape: frame.allocated.shape(),
86                        operation: "failed to allocate memory for string",
87                    });
88                };
89                let string_size = string_layout.size();
90                let frame = Frame::new(
91                    PtrUninit::new(string_ptr.as_ptr()),
92                    AllocatedShape::new(String::SHAPE, string_size),
93                    FrameOwnership::Owned,
94                );
95                // Frame::new already sets tracker = Scalar and is_init = false
96                self.frames_mut().push(frame);
97            } else if let Type::Sequence(SequenceType::Slice(_st)) = pointee_shape.ty {
98                crate::trace!("Pointee is [{}]", _st.t);
99
100                // Get the slice builder vtable
101                let slice_builder_vtable = smart_ptr_def.vtable.slice_builder_vtable.ok_or(
102                    ReflectError::OperationFailed {
103                        shape: frame.allocated.shape(),
104                        operation: "smart pointer does not support slice building",
105                    },
106                )?;
107
108                // Create a new builder
109                let builder_ptr = (slice_builder_vtable.new_fn)();
110
111                // Deallocate the original Arc allocation before replacing with slice builder
112                if let FrameOwnership::Owned = frame.ownership
113                    && let Ok(layout) = frame.allocated.shape().layout.sized_layout()
114                    && layout.size() > 0
115                {
116                    unsafe { ::alloc::alloc::dealloc(frame.data.as_mut_byte_ptr(), layout) };
117                }
118
119                // Update the current frame to use the slice builder
120                frame.data = builder_ptr.as_uninit();
121                frame.tracker = Tracker::SmartPointerSlice {
122                    vtable: slice_builder_vtable,
123                    building_item: false,
124                };
125                // Keep the original ownership (e.g., Field) so parent tracking works correctly.
126                // The slice builder memory itself is managed by the vtable's convert_fn/free_fn.
127            } else {
128                return Err(ReflectError::OperationFailed {
129                    shape: frame.allocated.shape(),
130                    operation: "push_smart_ptr can only be called on pointers to supported pointee types",
131                });
132            }
133        }
134
135        Ok(self)
136    }
137}