Skip to main content

qtbridge_runtime/
qobjectholder.rs

1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
3
4use std::cell::RefCell;
5use std::ptr::NonNull;
6use std::rc::Rc;
7
8use qtbridge_type_lib::{QMetaType, QObject};
9use qtbridge_type_lib::core::qobject;
10use crate::qproxies::{QCppProxy, QRustProxy, PlacementAddress, AdapterUpcast};
11use crate::registry::Owner;
12use crate::rustobjectgetter::get_rust_proxy;
13use crate::{DispatchMetaCall, DynamicMetaObjectData, QMetaInfo};
14
15/// The C++ half of the proxy pair of `T`.
16pub type CppProxyOf<T> = <<T as QObjectHolder>::ProxyRust as QRustProxy>::ProxyCppType;
17
18/// Bridge proxy selection and connector behind every `#[qobject]` type.
19#[doc(hidden)]
20pub unsafe trait QObjectHolder : DispatchMetaCall + QMetaInfo + Sized + 'static
21where
22    Self::ProxyRust: AdapterUpcast<Self>,
23    Self::ProxyRust: QRustProxy,
24{
25    /// Alias for the Rust proxy type corresponding to the user-defined type.
26    /// The Rust proxy is an intermediate layer between the Rust object and the C++ proxy,
27    /// forwarding calls in both directions and managing borrowing of the Rust object
28    /// during C++ calls.
29    type ProxyRust;
30
31    /// Creates a new `DynamicMetaObjectData` object and returns
32    /// a raw pointer to the heap-allocated object.
33    /// Ownership is not managed internally; the caller is responsible for it.
34    fn create_dynamic_meta_object_data_for_type() -> *const DynamicMetaObjectData {
35        let mut builder = crate::create_dynamic_meta_object_builder(
36            Self::class_name(),
37            <CppProxyOf<Self>>::get_static_meta_object());
38        Self::build_dynamic_meta_type(builder.pin_mut());
39        builder.pin_mut()
40            .take_dynamic_metaobject_data()
41    }
42
43    /// Return DynamicMetaObjectData containing information
44    /// about signals/slots/properties for given Rust object.
45    ///
46    /// The `#[qobject]` macro overrides this with a per-type `OnceLock` body; the
47    /// default serves generic types and hand-written impls.
48    fn get_shared_dynamic_meta_object_data() -> &'static DynamicMetaObjectData {
49        dynamic_meta_object_data_for_generic::<Self>()
50    }
51
52    /// Returns the [`QMetaType`] for a pointer to this type (`Self *`).
53    ///
54    /// The `#[qobject]` macro overrides this with a per-type `OnceLock` body; the
55    /// default serves generic types and hand-written impls.
56    fn get_qobject_ptr_qmetatype() -> QMetaType {
57        let iface = crate::qmetatypeforqobject::ptr_interface_for_generic::<Self>();
58        QMetaType::new_with_interface(iface)
59    }
60
61    /// Return a pointer to the Rust proxy associated with the specified object,
62    /// or `None` if no proxy is registered.
63    fn try_get_rust_proxy_ptr_from_ptr(rust_obj_ptr: *const Self) -> Option<*mut Self::ProxyRust> {
64        let proxy_ptr = crate::registry::proxy_ptr(rust_obj_ptr.cast::<u8>());
65        NonNull::new(proxy_ptr as *mut Self::ProxyRust).map(|nn| nn.as_ptr())
66    }
67
68    /// Return a pointer to the Rust proxy associated with the specified object,
69    /// or `None` if no proxy is registered.
70    fn try_get_rust_proxy_ptr(&self) -> Option<*mut Self::ProxyRust> {
71        Self::try_get_rust_proxy_ptr_from_ptr(std::ptr::from_ref(self))
72    }
73
74    /// Return `QObject` attached to the specified Rust object.
75    fn get_qobject_ptr(&self) -> *mut QObject {
76        let Some(proxy_ptr) = Self::try_get_rust_proxy_ptr(self) else {
77            return std::ptr::null_mut()
78        };
79        let rust_proxy = unsafe { &*proxy_ptr };
80        let cpp_proxy = rust_proxy.get_cpp_proxy();
81        cpp_proxy as *mut QObject
82    }
83
84    /// Return the `QObject` attached to the given object, attaching one
85    /// first if none exists.
86    fn rc_ref_cell_to_qobject(self_obj: &Rc<RefCell<Self>>) -> *const QObject {
87        let proxy_ptr = Self::try_get_rust_proxy_ptr_from_ptr(self_obj.as_ptr())
88            .unwrap_or_else( || {
89                Self::register_instance(self_obj.clone(), Owner::RustRegistry);
90                Self::try_get_rust_proxy_ptr_from_ptr(self_obj.as_ptr())
91                    .expect("Failed to attach and register a proxy")
92            }
93        );
94        let rust_proxy = unsafe { &*proxy_ptr };
95        rust_proxy.get_cpp_proxy() as *mut QObject
96    }
97
98    /// Return the Rust object attached to the specified `QObject`.
99    unsafe fn qobject_to_rc_ref_cell(qobj_ptr: *const QObject) -> Rc<RefCell<Self>>
100    {
101        let qobj_ref = unsafe { qobj_ptr.as_ref() }
102            .expect("Input QObject is null");
103        let proxy_ptr = get_rust_proxy(qobj_ref);
104        debug_assert!(!proxy_ptr.is_null());
105
106        // Verify the QObject really is a `Self` before reinterpreting its
107        // proxy/object as `Self`'s - otherwise the casts below are UB. Use
108        // an inherits check rather than meta-object identity: a `Self`
109        // instantiated and extended in QML carries a derived `QMetaObject`
110        // that is not identical to `Self`'s dynamic meta-object, yet the
111        // underlying proxy/object is still a `Self` (QML only layers a
112        // meta-object on top, it does not change the Rust type).
113        let qobj_meta_obj = unsafe { qobject::meta_object(qobj_ref).as_ref() };
114        let self_meta_obj = unsafe {
115            Self::get_shared_dynamic_meta_object_data().get_meta_object().as_ref()
116        };
117        let inherits = match (qobj_meta_obj, self_meta_obj) {
118            (Some(d), Some(b)) => d.inherits(b),
119            _ => false,
120        };
121        if !inherits {
122            let qobj_name = qobj_meta_obj.map_or("<null>".into(), |m| m.meta_type().name());
123            let self_name = self_meta_obj.map_or("<null>".into(), |m| m.meta_type().name());
124            panic!("Value of wrong type: '{qobj_name}' is not a '{self_name}' (nor a subclass)")
125        }
126
127        let proxy = unsafe { &*(proxy_ptr as *const Self::ProxyRust) };
128        let rc_adapter = proxy.get_rust_object_rc();
129
130        // Rust interest exists again: take ownership back if it was handed
131        // to the engine.
132        crate::registry::repin(rc_adapter.as_ptr() as *const u8);
133
134        // SAFETY: the inherits check above proves the `QObject` is a `Self` (or
135        // a QML-derived subclass of it) - and therefore the allocation behind
136        // `rc_adapter` - was created as `RefCell<Self>`.
137        // The adapter `Rc` only layers a vtable over that same allocation, so
138        // its data pointer addresses a real `RefCell<Self>` with matching size
139        // and alignment; reinterpreting it back is sound. `into_raw` parks the
140        // `+1` produced by `get_rust_object_rc` and `from_raw` reclaims it, so
141        // the reference count stays balanced.
142        let raw_ref_cell = Rc::into_raw(rc_adapter).cast();
143        unsafe { Rc::from_raw(raw_ref_cell) }
144    }
145
146    /// Creates the proxy pair for the given Rust object instance, links
147    /// them together and registers the object in `crate::registry`.
148    /// The C++ proxy is created on the heap.
149    #[doc(hidden)]
150    fn register_instance(rust_obj_rc: Rc<RefCell<Self>>, owner: Owner) {
151        // SAFETY: Passing no placement address is always safe.
152        unsafe { Self::register_instance_at(rust_obj_rc, owner, None) }
153    }
154
155    /// Like [`register_instance`](Self::register_instance), but the C++
156    /// proxy is created with placement new at `at_address` if
157    /// given (QML-created elements), on the heap otherwise.
158    ///
159    /// # Safety
160    ///
161    /// If `at_address` is `Some(addr)`, `addr` must point to uninitialized
162    /// memory that is valid for writes of a `QCppProxy`. Ownership of that
163    /// storage remains with the caller (in practice the QML engine), which
164    /// must destruct the proxy in place.
165    ///
166    /// Passing `None` is always safe.
167    #[doc(hidden)]
168    unsafe fn register_instance_at(
169        rust_obj_rc: Rc<RefCell<Self>>, owner: Owner, at_address: Option<PlacementAddress>,
170    ) {
171        let key = (*rust_obj_rc).as_ptr() as *const u8;
172        let keep: Rc<RefCell<Self>> = rust_obj_rc.clone();
173        let dyn_rc = <Self::ProxyRust as AdapterUpcast<Self>>::upcast(rust_obj_rc);
174        let dynamic_meta = Self::get_shared_dynamic_meta_object_data();
175        let proxy = Self::ProxyRust::new(&dyn_rc, dynamic_meta, at_address, Box::new(move || {
176            crate::registry::unregister(key);
177        }));
178        // SAFETY: We constructed proxy just above.
179        let qobject = unsafe { &*proxy }.get_cpp_proxy() as *mut QObject;
180        crate::registry::register(key, proxy as *const u8, qobject,
181            Rc::<RefCell<Self>>::downgrade(&keep), owner);
182    }
183}
184
185/// [`QObjectHolder::get_shared_dynamic_meta_object_data`] through a
186/// TypeId-keyed cache, for types where a per-type static is not available.
187pub fn dynamic_meta_object_data_for_generic<T: QObjectHolder>() -> &'static DynamicMetaObjectData
188where
189    T::ProxyRust: crate::qproxies::AdapterUpcast<T>,
190{
191    use std::any::TypeId;
192    use std::collections::HashMap;
193    thread_local!(static DYNAMIC_META_MAP: RefCell<HashMap<TypeId, *const DynamicMetaObjectData>> =
194        RefCell::new(HashMap::new()));
195
196    let type_id = TypeId::of::<T>();
197    {
198        let meta_data_ptr = DYNAMIC_META_MAP.with_borrow(|dynamic_meta_builder_map| {
199            dynamic_meta_builder_map.get(&type_id)
200                .copied()
201                .unwrap_or_default()
202        });
203        if let Some(meta_data_ref) = unsafe { meta_data_ptr.as_ref() } {
204            return meta_data_ref;
205        }
206    }
207
208    let meta_data_ptr = T::create_dynamic_meta_object_data_for_type();
209    let meta_data_ref = unsafe { meta_data_ptr.as_ref() }.unwrap();
210    DYNAMIC_META_MAP.with_borrow_mut(|dynamic_meta_builder_map| {
211        dynamic_meta_builder_map.insert(type_id, meta_data_ptr);
212    });
213
214    meta_data_ref
215}