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::prelude::*;
455    use std::{
456        alloc::Layout,
457        rc::{Rc, Weak},
458    };
459
460    #[test]
461    fn test_object() {
462        struct Droppable(Option<Weak<()>>);
463
464        impl Default for Droppable {
465            fn default() -> Self {
466                println!("Wrapper created!");
467                Self(None)
468            }
469        }
470
471        impl Drop for Droppable {
472            fn drop(&mut self) {
473                println!("Wrapper dropped!");
474            }
475        }
476
477        struct Pass;
478
479        impl Default for Pass {
480            fn default() -> Self {
481                println!("Pass created!");
482                Self
483            }
484        }
485
486        impl Drop for Pass {
487            fn drop(&mut self) {
488                println!("Pass dropped!");
489            }
490        }
491
492        let bool_handle = NativeStructBuilder::new::<bool>()
493            .build()
494            .into_type()
495            .into_handle();
496        let f32_handle = NativeStructBuilder::new::<f32>()
497            .build()
498            .into_type()
499            .into_handle();
500        let usize_handle = NativeStructBuilder::new::<usize>()
501            .build()
502            .into_type()
503            .into_handle();
504        let pass_handle = NativeStructBuilder::new::<Pass>()
505            .build()
506            .into_type()
507            .into_handle();
508        let droppable_handle = NativeStructBuilder::new::<Droppable>()
509            .build()
510            .into_type()
511            .into_handle();
512        let handle = RuntimeStructBuilder::new("Foo")
513            .field(StructField::new("a", bool_handle))
514            .field(StructField::new("b", f32_handle))
515            .field(StructField::new("c", usize_handle))
516            .field(StructField::new("d", pass_handle))
517            .field(StructField::new("e", droppable_handle))
518            .build()
519            .into_type()
520            .into_handle();
521        assert_eq!(handle.layout().size(), 24);
522        assert_eq!(handle.layout().align(), 8);
523        assert_eq!(handle.as_struct().unwrap().fields().len(), 5);
524        assert_eq!(
525            handle.as_struct().unwrap().fields()[0]
526                .type_handle()
527                .layout()
528                .size(),
529            1
530        );
531        assert_eq!(
532            handle.as_struct().unwrap().fields()[0]
533                .type_handle()
534                .layout()
535                .align(),
536            1
537        );
538        assert_eq!(handle.as_struct().unwrap().fields()[0].address_offset(), 0);
539        assert_eq!(
540            handle.as_struct().unwrap().fields()[1]
541                .type_handle()
542                .layout()
543                .size(),
544            4
545        );
546        assert_eq!(
547            handle.as_struct().unwrap().fields()[1]
548                .type_handle()
549                .layout()
550                .align(),
551            4
552        );
553        assert_eq!(handle.as_struct().unwrap().fields()[1].address_offset(), 4);
554        assert_eq!(
555            handle.as_struct().unwrap().fields()[2]
556                .type_handle()
557                .layout()
558                .size(),
559            8
560        );
561        assert_eq!(
562            handle.as_struct().unwrap().fields()[2]
563                .type_handle()
564                .layout()
565                .align(),
566            8
567        );
568        assert_eq!(handle.as_struct().unwrap().fields()[2].address_offset(), 8);
569        assert_eq!(
570            handle.as_struct().unwrap().fields()[3]
571                .type_handle()
572                .layout()
573                .size(),
574            0
575        );
576        assert_eq!(
577            handle.as_struct().unwrap().fields()[3]
578                .type_handle()
579                .layout()
580                .align(),
581            1
582        );
583        assert_eq!(handle.as_struct().unwrap().fields()[3].address_offset(), 16);
584        assert_eq!(
585            handle.as_struct().unwrap().fields()[4]
586                .type_handle()
587                .layout()
588                .size(),
589            8
590        );
591        assert_eq!(
592            handle.as_struct().unwrap().fields()[4]
593                .type_handle()
594                .layout()
595                .align(),
596            8
597        );
598        assert_eq!(handle.as_struct().unwrap().fields()[4].address_offset(), 16);
599        let mut object = Object::new(handle);
600        *object.write_field::<bool>("a").unwrap() = true;
601        *object.write_field::<f32>("b").unwrap() = 4.2;
602        *object.write_field::<usize>("c").unwrap() = 42;
603        let dropped = Rc::new(());
604        let dropped_weak = Rc::downgrade(&dropped);
605        object.write_field::<Droppable>("e").unwrap().0 = Some(dropped_weak);
606        assert!(*object.read_field::<bool>("a").unwrap());
607        assert_eq!(*object.read_field::<f32>("b").unwrap(), 4.2);
608        assert_eq!(*object.read_field::<usize>("c").unwrap(), 42);
609        assert_eq!(Rc::weak_count(&dropped), 1);
610        assert!(object.read_field::<()>("e").is_none());
611        drop(object);
612        assert_eq!(Rc::weak_count(&dropped), 0);
613    }
614
615    #[test]
616    fn test_drop() {
617        type Wrapper = LifetimeRefMut;
618
619        let lifetime = Lifetime::default();
620        assert!(lifetime.state().can_write(0));
621        let handle = NativeStructBuilder::new_uninitialized::<Wrapper>()
622            .build()
623            .into_type()
624            .into_handle();
625        let object = Object::with_value(handle, lifetime.borrow_mut().unwrap()).unwrap();
626        assert!(!lifetime.state().can_write(0));
627        drop(object);
628        assert!(lifetime.state().can_write(0));
629    }
630
631    #[test]
632    fn test_inner() {
633        let mut stack = DataStack::new(10240, DataStackMode::Values);
634        assert_eq!(stack.position(), 0);
635        let registry = Registry::default().with_basic_types();
636        let handle = registry.find_type(TypeQuery::of::<usize>()).unwrap();
637        let mut object = Object::new(handle);
638        *object.write::<usize>().unwrap() = 42;
639        let (handle, data) = unsafe { object.into_inner() };
640        assert_eq!(handle.type_hash(), TypeHash::of::<usize>());
641        assert_eq!(*handle.layout(), Layout::new::<usize>().pad_to_align());
642        let object = unsafe { Object::new_raw(handle, data) };
643        assert!(object_push_to_stack(object, &mut stack));
644        assert_eq!(stack.position(), 16);
645        let object = object_pop_from_stack(&mut stack, &registry).unwrap();
646        assert_eq!(*object.read::<usize>().unwrap(), 42);
647        assert_eq!(stack.position(), 0);
648    }
649}