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