intuicio_core/
object.rs

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