intuicio_framework_pointer/
lib.rs

1//! Experiments with highly unsafe pointer access.
2//! A.k.a. what could go wrong when trying to emulate direct pointer access in scripting.
3
4use std::{
5    marker::PhantomData,
6    ops::{Deref, DerefMut},
7};
8
9use intuicio_core::{registry::Registry, transformer::ValueTransformer};
10
11pub type VoidPtr = Ptr<()>;
12
13#[repr(transparent)]
14pub struct Ptr<T> {
15    pointer: *mut T,
16}
17
18impl<T> Default for Ptr<T> {
19    fn default() -> Self {
20        Self {
21            pointer: std::ptr::null_mut(),
22        }
23    }
24}
25
26impl<T> Ptr<T> {
27    pub fn is_null(self) -> bool {
28        self.pointer.is_null()
29    }
30
31    pub fn to_ptr(self) -> *const T {
32        self.pointer
33    }
34
35    pub fn to_ptr_mut(self) -> *mut T {
36        self.pointer
37    }
38
39    /// # Safety
40    pub unsafe fn as_ref(&self) -> Option<&T> {
41        if self.is_null() {
42            None
43        } else {
44            Some(unsafe { &*(self.pointer as *const T) })
45        }
46    }
47
48    /// # Safety
49    pub unsafe fn as_ref_mut(&mut self) -> Option<&mut T> {
50        if self.is_null() {
51            None
52        } else {
53            Some(unsafe { &mut *self.pointer })
54        }
55    }
56
57    /// # Safety
58    pub unsafe fn cast<U>(self) -> Ptr<U> {
59        Ptr {
60            pointer: self.pointer as *mut U,
61        }
62    }
63
64    /// # Safety
65    pub unsafe fn into_box(self) -> Box<T> {
66        unsafe { Box::from_raw(self.pointer) }
67    }
68
69    /// # Safety
70    pub unsafe fn from_box(value: Box<T>) -> Self {
71        Self {
72            pointer: Box::leak(value) as *mut T,
73        }
74    }
75}
76
77impl<T> From<*mut T> for Ptr<T> {
78    fn from(value: *mut T) -> Self {
79        Self { pointer: value }
80    }
81}
82
83impl<T> From<*const T> for Ptr<T> {
84    fn from(value: *const T) -> Self {
85        Self {
86            pointer: value as *mut T,
87        }
88    }
89}
90
91impl<T> From<&mut T> for Ptr<T> {
92    fn from(value: &mut T) -> Self {
93        Self {
94            pointer: value as *mut T,
95        }
96    }
97}
98
99impl<T> From<&T> for Ptr<T> {
100    fn from(value: &T) -> Self {
101        Self {
102            pointer: value as *const T as *mut T,
103        }
104    }
105}
106
107impl<T> From<Ptr<T>> for *const T {
108    fn from(value: Ptr<T>) -> Self {
109        value.pointer as *const T
110    }
111}
112
113impl<T> From<Ptr<T>> for *mut T {
114    fn from(value: Ptr<T>) -> Self {
115        value.pointer
116    }
117}
118
119impl<T> Deref for Ptr<T> {
120    type Target = T;
121
122    fn deref(&self) -> &Self::Target {
123        unsafe { self.as_ref().expect("Trying to dereference null pointer!") }
124    }
125}
126
127impl<T> DerefMut for Ptr<T> {
128    fn deref_mut(&mut self) -> &mut Self::Target {
129        unsafe {
130            self.as_ref_mut()
131                .expect("Trying to dereference null pointer!")
132        }
133    }
134}
135
136impl<T> Copy for Ptr<T> {}
137
138impl<T> Clone for Ptr<T> {
139    fn clone(&self) -> Self {
140        *self
141    }
142}
143
144// NOTE: I know this is bad, don't kill me - again, it's for experiments only sake,
145// some day it might disappear in favor of some smarter solution.
146unsafe impl<T> Send for Ptr<T> where T: Send {}
147unsafe impl<T> Sync for Ptr<T> where T: Sync {}
148
149impl<T> std::fmt::Debug for Ptr<T> {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        write!(f, "{:?}", self.pointer)
152    }
153}
154
155impl<T> std::fmt::Display for Ptr<T> {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        write!(f, "{:?}", self.pointer)
158    }
159}
160
161pub struct PtrValueTransformer<T: Default + Clone + 'static>(PhantomData<fn() -> T>);
162
163impl<T: Default + Clone + 'static> ValueTransformer for PtrValueTransformer<T> {
164    type Type = T;
165    type Borrow<'r> = &'r T;
166    type BorrowMut<'r> = &'r mut T;
167    type Dependency = ();
168    type Owned = T;
169    type Ref = Ptr<T>;
170    type RefMut = Ptr<T>;
171
172    fn from_owned(_: &Registry, value: Self::Type) -> Self::Owned {
173        value
174    }
175
176    fn from_ref(_: &Registry, value: &Self::Type, _: Option<Self::Dependency>) -> Self::Ref {
177        Ptr::from(value)
178    }
179
180    fn from_ref_mut(
181        _: &Registry,
182        value: &mut Self::Type,
183        _: Option<Self::Dependency>,
184    ) -> Self::RefMut {
185        Ptr::from(value)
186    }
187
188    fn into_owned(value: Self::Owned) -> Self::Type {
189        value
190    }
191
192    fn into_ref(value: &Self::Ref) -> Self::Borrow<'_> {
193        unsafe { value.as_ref().unwrap() }
194    }
195
196    fn into_ref_mut(value: &mut Self::RefMut) -> Self::BorrowMut<'_> {
197        unsafe { value.as_ref_mut().unwrap() }
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204    use intuicio_core::prelude::*;
205    use intuicio_derive::intuicio_function;
206
207    #[test]
208    fn test_async() {
209        fn is_async<T: Send + Sync>() {}
210
211        is_async::<Ptr<usize>>();
212        is_async::<Ptr<Ptr<usize>>>();
213    }
214
215    #[intuicio_function(transformer = "PtrValueTransformer")]
216    fn add(a: &usize, b: &mut usize) -> usize {
217        *a + *b
218    }
219
220    #[test]
221    fn test_raw_pointer_on_stack() {
222        let mut registry = Registry::default().with_basic_types();
223        registry.add_type(define_native_struct! {
224            registry => struct (Ptr<usize>) {}
225        });
226        let add = registry.add_function(add::define_function(&registry));
227        let mut context = Context::new(10240, 10240);
228        let a = 40usize;
229        let mut b = 2usize;
230        let (r,) = add.call::<(usize,), _>(
231            &mut context,
232            &registry,
233            (Ptr::from(&a), Ptr::from(&mut b)),
234            true,
235        );
236        assert_eq!(r, 42);
237    }
238
239    #[test]
240    fn test_allocation() {
241        unsafe {
242            let a = Box::new(42usize);
243            let mut b = Ptr::from_box(a);
244            *b.as_ref_mut().unwrap() = 10;
245            let c = b.into_box();
246            let d = *c;
247            assert_eq!(d, 10);
248        }
249    }
250}