intuicio_core/
object.rs

1use crate::types::{StructFieldQuery, Type, TypeHandle, TypeQuery};
2use intuicio_data::{Initialize, type_hash::TypeHash};
3use std::{
4    alloc::{alloc, dealloc},
5    collections::HashMap,
6};
7
8pub struct RuntimeObject;
9
10impl Initialize for RuntimeObject {
11    fn initialize() -> Self {
12        Self
13    }
14}
15
16pub struct Object {
17    handle: TypeHandle,
18    memory: *mut u8,
19    drop: bool,
20}
21
22impl Drop for Object {
23    fn drop(&mut self) {
24        if self.drop {
25            unsafe {
26                if self.handle.is_native() {
27                    self.handle.finalize(self.memory.cast::<()>());
28                } else {
29                    match &*self.handle {
30                        Type::Struct(type_) => {
31                            for field in type_.fields() {
32                                field
33                                    .type_handle()
34                                    .finalize(self.memory.add(field.address_offset()).cast::<()>());
35                            }
36                        }
37                        Type::Enum(type_) => {
38                            let discriminant = self.memory.read();
39                            if let Some(variant) = type_.find_variant_by_discriminant(discriminant)
40                            {
41                                for field in &variant.fields {
42                                    field.type_handle().finalize(
43                                        self.memory.add(field.address_offset()).cast::<()>(),
44                                    );
45                                }
46                            }
47                        }
48                    }
49                }
50                dealloc(self.memory, *self.handle.layout());
51            }
52        }
53    }
54}
55
56impl Object {
57    pub fn new(handle: TypeHandle) -> Self {
58        if !handle.can_initialize() {
59            panic!(
60                "Objects of type `{}::{}` cannot be initialized!",
61                handle.module_name().unwrap_or(""),
62                handle.name()
63            );
64        }
65        let memory = unsafe { alloc(*handle.layout()) };
66        let mut result = Self {
67            memory,
68            handle,
69            drop: true,
70        };
71        unsafe { result.initialize() };
72        result
73    }
74
75    pub fn try_new(handle: TypeHandle) -> Option<Self> {
76        if handle.can_initialize() {
77            let memory = unsafe { alloc(*handle.layout()) };
78            if memory.is_null() {
79                None
80            } else {
81                let mut result = Self {
82                    memory,
83                    handle,
84                    drop: true,
85                };
86                unsafe { result.initialize() };
87                Some(result)
88            }
89        } else {
90            None
91        }
92    }
93
94    /// # Safety
95    pub unsafe fn new_uninitialized(handle: TypeHandle) -> Option<Self> {
96        let memory = unsafe { alloc(*handle.layout()) };
97        if memory.is_null() {
98            None
99        } else {
100            Some(Self {
101                memory,
102                handle,
103                drop: true,
104            })
105        }
106    }
107
108    /// # Safety
109    pub unsafe fn new_raw(handle: TypeHandle, memory: *mut u8) -> Self {
110        Self {
111            memory,
112            handle,
113            drop: true,
114        }
115    }
116
117    /// # Safety
118    pub unsafe fn from_bytes(handle: TypeHandle, bytes: &[u8]) -> Option<Self> {
119        if handle.layout().size() == bytes.len() {
120            let memory = unsafe { alloc(*handle.layout()) };
121            if memory.is_null() {
122                None
123            } else {
124                unsafe { memory.copy_from(bytes.as_ptr(), bytes.len()) };
125                Some(Self {
126                    memory,
127                    handle,
128                    drop: true,
129                })
130            }
131        } else {
132            None
133        }
134    }
135
136    pub fn with_value<T: 'static>(handle: TypeHandle, value: T) -> Option<Self> {
137        if handle.type_hash() == TypeHash::of::<T>() {
138            unsafe {
139                let mut result = Self::new_uninitialized(handle)?;
140                result.as_mut_ptr().cast::<T>().write(value);
141                Some(result)
142            }
143        } else {
144            None
145        }
146    }
147
148    /// # Safety
149    pub unsafe fn initialize(&mut self) {
150        if self.handle.is_native() {
151            unsafe { self.handle.initialize(self.memory.cast::<()>()) };
152        } else {
153            match &*self.handle {
154                Type::Struct(type_) => {
155                    for field in type_.fields() {
156                        unsafe {
157                            field
158                                .type_handle()
159                                .initialize(self.memory.add(field.address_offset()).cast::<()>())
160                        };
161                    }
162                }
163                Type::Enum(type_) => {
164                    if let Some(variant) = type_.default_variant() {
165                        unsafe { self.memory.write(variant.discriminant()) };
166                        for field in &variant.fields {
167                            unsafe {
168                                field.type_handle().initialize(
169                                    self.memory.add(field.address_offset()).cast::<()>(),
170                                )
171                            };
172                        }
173                    }
174                }
175            }
176        }
177    }
178
179    pub fn consume<T: 'static>(mut self) -> Result<T, Self> {
180        if self.handle.type_hash() == TypeHash::of::<T>() {
181            self.drop = false;
182            unsafe { Ok(self.memory.cast::<T>().read()) }
183        } else {
184            Err(self)
185        }
186    }
187
188    /// # Safety
189    pub unsafe fn into_inner(mut self) -> (TypeHandle, *mut u8) {
190        self.drop = false;
191        (self.handle.clone(), self.memory)
192    }
193
194    pub fn type_handle(&self) -> &TypeHandle {
195        &self.handle
196    }
197
198    /// # Safety
199    pub unsafe fn memory(&self) -> &[u8] {
200        unsafe { std::slice::from_raw_parts(self.memory, self.type_handle().layout().size()) }
201    }
202
203    /// # Safety
204    pub unsafe fn memory_mut(&mut self) -> &mut [u8] {
205        unsafe { std::slice::from_raw_parts_mut(self.memory, self.type_handle().layout().size()) }
206    }
207
208    /// # Safety
209    pub unsafe fn field_memory<'a>(&'a self, query: StructFieldQuery<'a>) -> Option<&'a [u8]> {
210        match &*self.handle {
211            Type::Struct(type_) => {
212                let field = type_.find_field(query)?;
213                Some(unsafe {
214                    std::slice::from_raw_parts(
215                        self.memory.add(field.address_offset()),
216                        field.type_handle().layout().size(),
217                    )
218                })
219            }
220            Type::Enum(type_) => {
221                let discriminant = unsafe { self.memory.read() };
222                let variant = type_.find_variant_by_discriminant(discriminant)?;
223                let field = variant.find_field(query)?;
224                Some(unsafe {
225                    std::slice::from_raw_parts(
226                        self.memory.add(field.address_offset()),
227                        field.type_handle().layout().size(),
228                    )
229                })
230            }
231        }
232    }
233
234    /// # Safety
235    pub unsafe fn field_memory_mut<'a>(
236        &'a mut self,
237        query: StructFieldQuery<'a>,
238    ) -> Option<&'a mut [u8]> {
239        match &*self.handle {
240            Type::Struct(type_) => {
241                let field = type_.find_field(query)?;
242                Some(unsafe {
243                    std::slice::from_raw_parts_mut(
244                        self.memory.add(field.address_offset()),
245                        field.type_handle().layout().size(),
246                    )
247                })
248            }
249            Type::Enum(type_) => {
250                let discriminant = unsafe { self.memory.read() };
251                let variant = type_.find_variant_by_discriminant(discriminant)?;
252                let field = variant.find_field(query)?;
253                Some(unsafe {
254                    std::slice::from_raw_parts_mut(
255                        self.memory.add(field.address_offset()),
256                        field.type_handle().layout().size(),
257                    )
258                })
259            }
260        }
261    }
262
263    pub fn read<T: 'static>(&self) -> Option<&T> {
264        if self.handle.type_hash() == TypeHash::of::<T>() {
265            unsafe { self.memory.cast::<T>().as_ref() }
266        } else {
267            None
268        }
269    }
270
271    pub fn write<T: 'static>(&mut self) -> Option<&mut T> {
272        if self.handle.type_hash() == TypeHash::of::<T>() {
273            unsafe { self.memory.cast::<T>().as_mut() }
274        } else {
275            None
276        }
277    }
278
279    pub fn read_field<'a, T: 'static>(&'a self, field: &str) -> Option<&'a T> {
280        let query = StructFieldQuery {
281            name: Some(field.into()),
282            type_query: Some(TypeQuery::of::<T>()),
283            ..Default::default()
284        };
285        let field = match &*self.handle {
286            Type::Struct(type_) => type_.find_field(query),
287            Type::Enum(type_) => {
288                let discriminant = unsafe { self.memory.read() };
289                let variant = type_.find_variant_by_discriminant(discriminant)?;
290                variant.find_field(query)
291            }
292        }?;
293        unsafe { self.memory.add(field.address_offset()).cast::<T>().as_ref() }
294    }
295
296    pub fn write_field<'a, T: 'static>(&'a mut self, field: &str) -> Option<&'a mut T> {
297        let query = StructFieldQuery {
298            name: Some(field.into()),
299            type_query: Some(TypeQuery::of::<T>()),
300            ..Default::default()
301        };
302        let field = match &*self.handle {
303            Type::Struct(type_) => type_.find_field(query),
304            Type::Enum(type_) => {
305                let discriminant = unsafe { self.memory.read() };
306                let variant = type_.find_variant_by_discriminant(discriminant)?;
307                variant.find_field(query)
308            }
309        }?;
310        unsafe { self.memory.add(field.address_offset()).cast::<T>().as_mut() }
311    }
312
313    /// # Safety
314    pub unsafe fn as_ptr(&self) -> *const u8 {
315        self.memory
316    }
317
318    /// # Safety
319    pub unsafe fn as_mut_ptr(&mut self) -> *mut u8 {
320        self.memory
321    }
322
323    /// # Safety
324    pub unsafe fn prevent_drop(&mut self) {
325        self.drop = false;
326    }
327}
328
329impl std::fmt::Debug for Object {
330    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331        unsafe {
332            f.debug_struct("Object")
333                .field("address", &(self.as_ptr() as usize))
334                .field(
335                    "type",
336                    &format!(
337                        "{}::{}",
338                        self.handle.module_name().unwrap_or_default(),
339                        self.handle.name()
340                    ),
341                )
342                .finish()
343        }
344    }
345}
346
347#[derive(Default)]
348pub struct DynamicObject {
349    properties: HashMap<String, Object>,
350}
351
352impl DynamicObject {
353    pub fn get(&self, name: &str) -> Option<&Object> {
354        self.properties.get(name)
355    }
356
357    pub fn get_mut(&mut self, name: &str) -> Option<&mut Object> {
358        self.properties.get_mut(name)
359    }
360
361    pub fn set(&mut self, name: impl ToString, value: Object) {
362        self.properties.insert(name.to_string(), value);
363    }
364
365    pub fn delete(&mut self, name: &str) -> Option<Object> {
366        self.properties.remove(name)
367    }
368
369    pub fn drain(&mut self) -> impl Iterator<Item = (String, Object)> + '_ {
370        self.properties.drain()
371    }
372
373    pub fn properties(&self) -> impl Iterator<Item = (&str, &Object)> + '_ {
374        self.properties
375            .iter()
376            .map(|(key, value)| (key.as_str(), value))
377    }
378
379    pub fn properties_mut(&mut self) -> impl Iterator<Item = (&str, &mut Object)> + '_ {
380        self.properties
381            .iter_mut()
382            .map(|(key, value)| (key.as_str(), value))
383    }
384
385    pub fn property_names(&self) -> impl Iterator<Item = &str> + '_ {
386        self.properties.keys().map(|key| key.as_str())
387    }
388
389    pub fn property_values(&self) -> impl Iterator<Item = &Object> + '_ {
390        self.properties.values()
391    }
392
393    pub fn property_values_mut(&mut self) -> impl Iterator<Item = &mut Object> + '_ {
394        self.properties.values_mut()
395    }
396}
397
398#[derive(Default)]
399pub struct TypedDynamicObject {
400    properties: HashMap<TypeHash, Object>,
401}
402
403impl TypedDynamicObject {
404    pub fn get<T: 'static>(&self) -> Option<&Object> {
405        self.properties.get(&TypeHash::of::<T>())
406    }
407
408    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut Object> {
409        self.properties.get_mut(&TypeHash::of::<T>())
410    }
411
412    pub fn set<T: 'static>(&mut self, value: Object) {
413        self.properties.insert(TypeHash::of::<T>(), value);
414    }
415
416    pub fn delete<T: 'static>(&mut self) -> Option<Object> {
417        self.properties.remove(&TypeHash::of::<T>())
418    }
419
420    pub fn drain(&mut self) -> impl Iterator<Item = (TypeHash, Object)> + '_ {
421        self.properties.drain()
422    }
423
424    pub fn properties(&self) -> impl Iterator<Item = (&TypeHash, &Object)> + '_ {
425        self.properties.iter()
426    }
427
428    pub fn properties_mut(&mut self) -> impl Iterator<Item = (&TypeHash, &mut Object)> + '_ {
429        self.properties.iter_mut()
430    }
431
432    pub fn property_types(&self) -> impl Iterator<Item = &TypeHash> + '_ {
433        self.properties.keys()
434    }
435
436    pub fn property_values(&self) -> impl Iterator<Item = &Object> + '_ {
437        self.properties.values()
438    }
439
440    pub fn property_values_mut(&mut self) -> impl Iterator<Item = &mut Object> + '_ {
441        self.properties.values_mut()
442    }
443}
444
445#[cfg(test)]
446mod tests {
447    use crate::{
448        object::*,
449        registry::Registry,
450        types::struct_type::*,
451        utils::{object_pop_from_stack, object_push_to_stack},
452    };
453    use intuicio_data::prelude::*;
454    use std::{
455        alloc::Layout,
456        rc::{Rc, Weak},
457    };
458
459    #[test]
460    fn test_object() {
461        struct Droppable(Option<Weak<()>>);
462
463        impl Default for Droppable {
464            fn default() -> Self {
465                println!("Wrapper created!");
466                Self(None)
467            }
468        }
469
470        impl Drop for Droppable {
471            fn drop(&mut self) {
472                println!("Wrapper dropped!");
473            }
474        }
475
476        struct Pass;
477
478        impl Default for Pass {
479            fn default() -> Self {
480                println!("Pass created!");
481                Self
482            }
483        }
484
485        impl Drop for Pass {
486            fn drop(&mut self) {
487                println!("Pass dropped!");
488            }
489        }
490
491        let bool_handle = NativeStructBuilder::new::<bool>()
492            .build()
493            .into_type()
494            .into_handle();
495        let f32_handle = NativeStructBuilder::new::<f32>()
496            .build()
497            .into_type()
498            .into_handle();
499        let usize_handle = NativeStructBuilder::new::<usize>()
500            .build()
501            .into_type()
502            .into_handle();
503        let pass_handle = NativeStructBuilder::new::<Pass>()
504            .build()
505            .into_type()
506            .into_handle();
507        let droppable_handle = NativeStructBuilder::new::<Droppable>()
508            .build()
509            .into_type()
510            .into_handle();
511        let handle = RuntimeStructBuilder::new("Foo")
512            .field(StructField::new("a", bool_handle))
513            .field(StructField::new("b", f32_handle))
514            .field(StructField::new("c", usize_handle))
515            .field(StructField::new("d", pass_handle))
516            .field(StructField::new("e", droppable_handle))
517            .build()
518            .into_type()
519            .into_handle();
520        assert_eq!(handle.layout().size(), 24);
521        assert_eq!(handle.layout().align(), 8);
522        assert_eq!(handle.as_struct().unwrap().fields().len(), 5);
523        assert_eq!(
524            handle.as_struct().unwrap().fields()[0]
525                .type_handle()
526                .layout()
527                .size(),
528            1
529        );
530        assert_eq!(
531            handle.as_struct().unwrap().fields()[0]
532                .type_handle()
533                .layout()
534                .align(),
535            1
536        );
537        assert_eq!(handle.as_struct().unwrap().fields()[0].address_offset(), 0);
538        assert_eq!(
539            handle.as_struct().unwrap().fields()[1]
540                .type_handle()
541                .layout()
542                .size(),
543            4
544        );
545        assert_eq!(
546            handle.as_struct().unwrap().fields()[1]
547                .type_handle()
548                .layout()
549                .align(),
550            4
551        );
552        assert_eq!(handle.as_struct().unwrap().fields()[1].address_offset(), 4);
553        assert_eq!(
554            handle.as_struct().unwrap().fields()[2]
555                .type_handle()
556                .layout()
557                .size(),
558            8
559        );
560        assert_eq!(
561            handle.as_struct().unwrap().fields()[2]
562                .type_handle()
563                .layout()
564                .align(),
565            8
566        );
567        assert_eq!(handle.as_struct().unwrap().fields()[2].address_offset(), 8);
568        assert_eq!(
569            handle.as_struct().unwrap().fields()[3]
570                .type_handle()
571                .layout()
572                .size(),
573            0
574        );
575        assert_eq!(
576            handle.as_struct().unwrap().fields()[3]
577                .type_handle()
578                .layout()
579                .align(),
580            1
581        );
582        assert_eq!(handle.as_struct().unwrap().fields()[3].address_offset(), 16);
583        assert_eq!(
584            handle.as_struct().unwrap().fields()[4]
585                .type_handle()
586                .layout()
587                .size(),
588            8
589        );
590        assert_eq!(
591            handle.as_struct().unwrap().fields()[4]
592                .type_handle()
593                .layout()
594                .align(),
595            8
596        );
597        assert_eq!(handle.as_struct().unwrap().fields()[4].address_offset(), 16);
598        let mut object = Object::new(handle);
599        *object.write_field::<bool>("a").unwrap() = true;
600        *object.write_field::<f32>("b").unwrap() = 4.2;
601        *object.write_field::<usize>("c").unwrap() = 42;
602        let dropped = Rc::new(());
603        let dropped_weak = Rc::downgrade(&dropped);
604        object.write_field::<Droppable>("e").unwrap().0 = Some(dropped_weak);
605        assert!(*object.read_field::<bool>("a").unwrap());
606        assert_eq!(*object.read_field::<f32>("b").unwrap(), 4.2);
607        assert_eq!(*object.read_field::<usize>("c").unwrap(), 42);
608        assert_eq!(Rc::weak_count(&dropped), 1);
609        assert!(object.read_field::<()>("e").is_none());
610        drop(object);
611        assert_eq!(Rc::weak_count(&dropped), 0);
612    }
613
614    #[test]
615    fn test_drop() {
616        type Wrapper = LifetimeRefMut;
617
618        let lifetime = Lifetime::default();
619        assert!(lifetime.state().can_write(0));
620        let handle = NativeStructBuilder::new_uninitialized::<Wrapper>()
621            .build()
622            .into_type()
623            .into_handle();
624        let object = Object::with_value(handle, lifetime.borrow_mut().unwrap()).unwrap();
625        assert!(!lifetime.state().can_write(0));
626        drop(object);
627        assert!(lifetime.state().can_write(0));
628    }
629
630    #[test]
631    fn test_inner() {
632        let mut stack = DataStack::new(10240, DataStackMode::Values);
633        assert_eq!(stack.position(), 0);
634        let registry = Registry::default().with_basic_types();
635        let handle = registry.find_type(TypeQuery::of::<usize>()).unwrap();
636        let mut object = Object::new(handle);
637        *object.write::<usize>().unwrap() = 42;
638        let (handle, data) = unsafe { object.into_inner() };
639        assert_eq!(handle.type_hash(), TypeHash::of::<usize>());
640        assert_eq!(*handle.layout(), Layout::new::<usize>().pad_to_align());
641        let object = unsafe { Object::new_raw(handle, data) };
642        assert!(object_push_to_stack(object, &mut stack));
643        assert_eq!(stack.position(), 16);
644        let object = object_pop_from_stack(&mut stack, &registry).unwrap();
645        assert_eq!(*object.read::<usize>().unwrap(), 42);
646        assert_eq!(stack.position(), 0);
647    }
648}