Skip to main content

tvm_ffi/
object.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19use std::ops::{Deref, DerefMut};
20use std::sync::atomic::AtomicU64;
21
22use crate::derive::ObjectRef;
23use crate::type_traits::AnyCompatible;
24pub use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
25/// Object related ABI handling
26use tvm_ffi_sys::{TVMFFIAny, TVMFFIGetTypeInfo, TVMFFIObject, COMBINED_REF_COUNT_BOTH_ONE};
27
28/// Object type is by default the TVMFFIObject
29#[repr(C)]
30pub struct Object {
31    /// example implementation of the object
32    header: TVMFFIObject,
33}
34
35/// Arc-like wrapper for Object that allows shared ownership
36///
37/// \tparam T The type of the object to be wrapped
38#[repr(C)]
39pub struct ObjectArc<T: ObjectCore> {
40    ptr: std::ptr::NonNull<T>,
41    _phantom: std::marker::PhantomData<T>,
42}
43
44unsafe impl<T: Send + Sync + ObjectCore> Send for ObjectArc<T> {}
45unsafe impl<T: Send + Sync + ObjectCore> Sync for ObjectArc<T> {}
46
47/// Traits that can be used to check if a type is an object
48///
49/// This trait is unsafe because it is used to access the object header
50/// and the object header is unsafe to access
51pub unsafe trait ObjectCore: Sized + 'static {
52    /// the type key of the object
53    const TYPE_KEY: &'static str;
54    /// Depth of this type in the object inheritance tree.
55    ///
56    /// The root [`Object`] has depth zero, and every registered subtype has
57    /// depth one greater than its parent. This value must be non-negative and
58    /// agree with the runtime type table entry for `Self`.
59    const TYPE_DEPTH: i32;
60    /// Whether every instance of this type has exactly `Self::type_index()`.
61    ///
62    /// A final type has no separately registered object-system subtype.
63    #[doc(hidden)]
64    const TYPE_FINAL: bool = false;
65    // return the type index of the object
66    fn type_index() -> i32;
67    /// Return the object header
68    /// This function is implemented as a static function so
69    ///
70    /// # Arguments
71    /// * `this` - The object to get the header
72    ///
73    /// # Returns
74    /// * `&mut TVMFFIObject` - The object header
75    /// \return The object header
76    unsafe fn object_header_mut(this: &mut Self) -> &mut TVMFFIObject;
77}
78
79/// Traits for objects with extra items that follows the object
80///
81/// This extra trait can be helpful to implement array types and string types
82pub unsafe trait ObjectCoreWithExtraItems: ObjectCore {
83    /// type of extra items storage that follows the object
84    type ExtraItem;
85    /// Return the number of extra items
86    fn extra_items_count(this: &Self) -> usize;
87    /// Return the extra items data pointer
88    unsafe fn extra_items(this: &Self) -> &[Self::ExtraItem] {
89        let extra_items_ptr = (this as *const Self as *const u8).add(std::mem::size_of::<Self>());
90        std::slice::from_raw_parts(
91            extra_items_ptr as *const Self::ExtraItem,
92            Self::extra_items_count(this),
93        )
94    }
95    /// Return the extra items data pointer
96    unsafe fn extra_items_mut(this: &mut Self) -> &mut [Self::ExtraItem] {
97        let extra_items_ptr = (this as *mut Self as *mut u8).add(std::mem::size_of::<Self>());
98        std::slice::from_raw_parts_mut(
99            extra_items_ptr as *mut Self::ExtraItem,
100            Self::extra_items_count(this),
101        )
102    }
103}
104
105/// Traits to specify core operations of ObjectRef
106///
107/// used by the ffi Any system and not user facing
108///
109/// We mark as unsafe since it moves out the internal of the ObjectRef
110///
111/// # Safety
112///
113/// `data`, `into_data`, and `from_data` must preserve the same object
114/// allocation and form an ownership-preserving round trip. That allocation must
115/// start with a valid `TVMFFIObject` header whose registered object-range
116/// runtime type index correctly describes its layout and inheritance.
117///
118/// When `Self` also implements [`AnyCompatible`], `copy_to_any_view` must
119/// produce a non-owning view, while `move_to_any` must transfer ownership of
120/// the same object pointer and dynamic type index. `move_from_any_after_check`
121/// must be able to reclaim that owned representation exactly once, and a true
122/// `check_any_strict` result must guarantee that both after-check constructors
123/// are valid for it.
124pub unsafe trait ObjectRefCore: Sized + Clone {
125    type ContainerType: ObjectCore;
126    fn data(this: &Self) -> &ObjectArc<Self::ContainerType>;
127    fn into_data(this: Self) -> ObjectArc<Self::ContainerType>;
128
129    /// Construct a reference view from an owning container handle.
130    ///
131    /// # Safety
132    ///
133    /// In addition to containing a valid `ContainerType` allocation, `data`
134    /// must satisfy every semantic invariant imposed by `Self`. This matters
135    /// for zero-state views that share a container type but accept only a
136    /// subset of its values, such as a typed expression view.
137    unsafe fn from_data(data: ObjectArc<Self::ContainerType>) -> Self;
138
139    /// Return whether two object references point to the same allocation.
140    #[inline]
141    fn same_as<Other: ObjectRefCore>(&self, other: &Other) -> bool {
142        unsafe {
143            ObjectArc::as_raw(Self::data(self)).cast::<()>()
144                == ObjectArc::as_raw(Other::data(other)).cast::<()>()
145        }
146    }
147
148    /// Borrow the underlying object as node type `N` when its runtime type matches.
149    ///
150    /// Unlike [`ObjectRefCast::try_cast`], this method neither consumes the
151    /// reference nor changes the object's reference count. The returned node
152    /// cannot outlive `self`.
153    #[inline(always)]
154    fn as_node<N: ObjectCore>(&self) -> Option<&N> {
155        let object = unsafe { ObjectArc::as_raw(Self::data(self)) };
156        let type_index = unsafe { (*object.cast::<TVMFFIObject>()).type_index };
157        if !is_instance_of::<N>(type_index) {
158            return None;
159        }
160        Some(unsafe { &*object.cast::<N>() })
161    }
162}
163
164/// An owning, hashable identity key for an FFI object.
165///
166/// The retained strong reference prevents the allocation address from being
167/// reused while the key is alive. This makes it suitable for identity-based
168/// maps without exposing raw object pointers to downstream code.
169#[derive(Clone)]
170pub struct ObjectIdentity {
171    data: ObjectArc<Object>,
172}
173
174impl ObjectIdentity {
175    /// Retain the allocation referenced by `value` as an identity key.
176    pub fn of<T: ObjectRefCore>(value: &T) -> Self {
177        unsafe {
178            let ptr = ObjectArc::as_raw(T::data(value)) as *mut TVMFFIObject;
179            unsafe_::inc_ref(ptr);
180            Self {
181                data: ObjectArc::from_raw(ptr.cast::<Object>()),
182            }
183        }
184    }
185
186    #[inline]
187    fn as_ptr(&self) -> *const TVMFFIObject {
188        unsafe { ObjectArc::as_raw(&self.data).cast::<TVMFFIObject>() }
189    }
190}
191
192impl PartialEq for ObjectIdentity {
193    #[inline]
194    fn eq(&self, other: &Self) -> bool {
195        self.as_ptr() == other.as_ptr()
196    }
197}
198
199impl Eq for ObjectIdentity {}
200
201impl std::hash::Hash for ObjectIdentity {
202    #[inline]
203    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
204        self.as_ptr().hash(state);
205    }
206}
207
208impl std::fmt::Debug for ObjectIdentity {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        f.debug_tuple("ObjectIdentity")
211            .field(&self.as_ptr())
212            .finish()
213    }
214}
215
216/// Check whether a runtime type index refers to `Target` or one of its
217/// subtypes.
218///
219/// The subtype relation lives in the process-wide type table maintained by the
220/// tvm-ffi library: every registered type records its depth in the single
221/// inheritance tree together with the chain of its ancestors. The check is
222/// O(1) — if `target` really is an ancestor, it must appear in the candidate's
223/// ancestor array exactly at `target`'s depth.
224///
225/// This is a hidden support function for derive-generated object checks. Object
226/// indices in the registered range must refer to entries in the runtime type
227/// table.
228#[doc(hidden)]
229#[inline(always)]
230pub fn is_instance_of<Target: ObjectCore>(object_type_index: i32) -> bool {
231    let target_type_index = Target::type_index();
232    if object_type_index == target_type_index {
233        return true;
234    }
235    // A final type cannot have a separately registered subtype. Keep common
236    // borrowed checks, such as `IntImmObj`, to one integer comparison.
237    if Target::TYPE_FINAL {
238        return false;
239    }
240    let object_begin = TypeIndex::kTVMFFIStaticObjectBegin as i32;
241    // Only object types participate in the type hierarchy.
242    if object_type_index < object_begin || target_type_index < object_begin {
243        return false;
244    }
245    // Parent indices are always smaller than their descendants.
246    if object_type_index < target_type_index {
247        return false;
248    }
249    unsafe {
250        let object_info = TVMFFIGetTypeInfo(object_type_index);
251        if object_info.is_null() {
252            return false;
253        }
254        let target_depth = Target::TYPE_DEPTH;
255        if (*object_info).type_depth <= target_depth {
256            return false;
257        }
258        let ancestor = *(*object_info).type_acenstors.add(target_depth as usize);
259        !ancestor.is_null() && (*ancestor).type_index == target_type_index
260    }
261}
262
263/// Runtime-checked casting between arbitrary `ObjectRef` types.
264///
265/// The cast uses the target's [`AnyCompatible::check_any_strict`] implementation,
266/// mirroring the semantics of `ObjectRef::as<T>` in C++. This supports both
267/// object hierarchies and parameterized object containers.
268///
269/// This trait is blanket-implemented for every [`ObjectRefCore`] type that is
270/// also [`AnyCompatible`].
271pub trait ObjectRefCast: ObjectRefCore + AnyCompatible {
272    /// Consume `self` and rewrap the underlying object as `B` without copying.
273    #[inline(always)]
274    fn try_cast<B>(self) -> crate::error::Result<B>
275    where
276        B: ObjectRefCore + AnyCompatible,
277    {
278        let mut any_data = TVMFFIAny::new();
279        unsafe {
280            // Keep ownership in `self` while the target check runs. This makes
281            // the failure and panic paths unwind normally instead of stranding
282            // an owned object inside a raw TVMFFIAny.
283            Self::copy_to_any_view(&self, &mut any_data);
284            debug_assert!(any_data.type_index >= TypeIndex::kTVMFFIStaticObjectBegin as i32);
285            // SAFETY: ObjectRefCore's contract requires its AnyCompatible
286            // representation to contain a valid object-range type index.
287            std::hint::assert_unchecked(
288                any_data.type_index >= TypeIndex::kTVMFFIStaticObjectBegin as i32,
289            );
290
291            if B::check_any_strict(&any_data) {
292                // Transfer ownership only after the borrowed representation has
293                // passed the target's complete hierarchy/container check.
294                Self::move_to_any(self, &mut any_data);
295                Ok(B::move_from_any_after_check(&mut any_data))
296            } else {
297                let msg = format!(
298                    "Cannot convert from type `{}` to `{}`",
299                    B::get_mismatch_type_info(&any_data),
300                    B::type_str()
301                );
302                Err(crate::error::Error::new(crate::error::TYPE_ERROR, &msg, ""))
303            }
304        }
305    }
306}
307
308impl<T: ObjectRefCore + AnyCompatible> ObjectRefCast for T {}
309
310/// A generic managed reference to a TVM-FFI object.
311///
312/// Holds an `ObjectArc<Object>` that manages the underlying object's lifetime
313/// through reference counting.
314#[repr(C)]
315#[derive(ObjectRef, Clone)]
316pub struct ObjectRef {
317    data: ObjectArc<Object>,
318}
319
320/// Unsafe operations on object
321#[doc(hidden)]
322pub mod unsafe_ {
323    use tvm_ffi_sys::{
324        COMBINED_REF_COUNT_BOTH_ONE, COMBINED_REF_COUNT_MASK_U32, COMBINED_REF_COUNT_STRONG_ONE,
325        COMBINED_REF_COUNT_WEAK_ONE,
326    };
327
328    use std::ffi::c_void;
329    use std::sync::atomic::{fence, Ordering};
330    use tvm_ffi_sys::TVMFFIObject;
331    use tvm_ffi_sys::TVMFFIObjectDeleterFlagBitMask::{
332        kTVMFFIObjectDeleterFlagBitMaskBoth, kTVMFFIObjectDeleterFlagBitMaskStrong,
333        kTVMFFIObjectDeleterFlagBitMaskWeak,
334    };
335
336    /// Increase the strong reference count of the object
337    ///
338    /// This function is same as TVMFFIObjectIncRef but implemented natively in Rust
339    ///
340    /// # Arguments
341    /// * `obj` - The object to increase the reference count
342    #[inline]
343    pub unsafe fn inc_ref(handle: *mut TVMFFIObject) {
344        let obj = &mut *handle;
345        obj.combined_ref_count.fetch_add(1, Ordering::Relaxed);
346    }
347
348    /// Decrease the strong reference count of the object
349    ///
350    /// This function is same as TVMFFIObjectDecRef but implemented natively in Rust
351    ///
352    /// # Arguments
353    /// * `obj` - The object to decrease the reference count
354    #[inline]
355    pub(crate) unsafe fn dec_ref(handle: *mut TVMFFIObject) {
356        let obj = &mut *handle;
357        let old_combined_count = obj
358            .combined_ref_count
359            .fetch_sub(COMBINED_REF_COUNT_STRONG_ONE, Ordering::Relaxed);
360        if old_combined_count == COMBINED_REF_COUNT_BOTH_ONE {
361            if let Some(deleter) = obj.deleter {
362                fence(Ordering::Acquire);
363                deleter(
364                    obj as *mut TVMFFIObject as *mut c_void,
365                    kTVMFFIObjectDeleterFlagBitMaskBoth as i32,
366                );
367            }
368        } else if (old_combined_count & COMBINED_REF_COUNT_MASK_U32)
369            == COMBINED_REF_COUNT_STRONG_ONE
370        {
371            // slow path, there is still a weak reference left
372            // need to run two phase decrement
373            fence(Ordering::Acquire);
374            if let Some(deleter) = obj.deleter {
375                deleter(
376                    obj as *mut TVMFFIObject as *mut c_void,
377                    kTVMFFIObjectDeleterFlagBitMaskStrong as i32,
378                );
379            }
380            let old_weak_count = obj
381                .combined_ref_count
382                .fetch_sub(COMBINED_REF_COUNT_WEAK_ONE, Ordering::Release);
383            if old_weak_count == COMBINED_REF_COUNT_WEAK_ONE {
384                fence(Ordering::Acquire);
385                if let Some(deleter) = obj.deleter {
386                    deleter(
387                        obj as *mut TVMFFIObject as *mut c_void,
388                        kTVMFFIObjectDeleterFlagBitMaskWeak as i32,
389                    );
390                }
391            }
392        }
393    }
394
395    #[inline]
396    pub(crate) unsafe fn strong_count(handle: *mut TVMFFIObject) -> usize {
397        let obj = &mut *handle;
398        (obj.combined_ref_count.load(Ordering::Relaxed) & COMBINED_REF_COUNT_MASK_U32) as usize
399    }
400
401    #[inline]
402    pub(crate) unsafe fn weak_count(handle: *mut TVMFFIObject) -> usize {
403        let obj = &mut *handle;
404        (obj.combined_ref_count.load(Ordering::Relaxed) >> 32) as usize
405    }
406
407    /// Generic object deleter for objects allocated through Rust's global allocator.
408    pub(crate) unsafe extern "C" fn object_deleter_for_new<T>(ptr: *mut c_void, flags: i32)
409    where
410        T: super::ObjectCore,
411    {
412        let obj = ptr as *mut T;
413        if flags & kTVMFFIObjectDeleterFlagBitMaskStrong as i32 != 0 {
414            std::ptr::drop_in_place(obj);
415        }
416        if flags & kTVMFFIObjectDeleterFlagBitMaskWeak as i32 != 0 {
417            std::alloc::dealloc(ptr as *mut u8, std::alloc::Layout::new::<T>());
418        }
419    }
420
421    pub(crate) unsafe extern "C" fn object_deleter_for_new_with_extra_items<T, U>(
422        ptr: *mut c_void,
423        flags: i32,
424    ) where
425        T: super::ObjectCoreWithExtraItems<ExtraItem = U>,
426    {
427        let obj = ptr as *mut T;
428        if flags == kTVMFFIObjectDeleterFlagBitMaskBoth as i32 {
429            let extra_items_count = T::extra_items_count(&(*obj));
430            std::ptr::drop_in_place(obj);
431            let layout = std::alloc::Layout::from_size_align(
432                std::mem::size_of::<T>() + extra_items_count * std::mem::size_of::<U>(),
433                std::mem::align_of::<T>(),
434            )
435            .unwrap();
436            std::alloc::dealloc(ptr as *mut u8, layout);
437        } else {
438            assert_eq!(std::mem::size_of::<T>() % std::mem::size_of::<u64>(), 0);
439            if flags & kTVMFFIObjectDeleterFlagBitMaskStrong as i32 != 0 {
440                let extra_items_count = T::extra_items_count(&(*obj));
441                std::ptr::drop_in_place(obj);
442                std::ptr::write(obj as *mut u64, extra_items_count as u64);
443            }
444            if flags & kTVMFFIObjectDeleterFlagBitMaskWeak as i32 != 0 {
445                let extra_items_count = std::ptr::read(obj as *mut u64) as usize;
446                let layout = std::alloc::Layout::from_size_align(
447                    std::mem::size_of::<T>() + extra_items_count * std::mem::size_of::<U>(),
448                    std::mem::align_of::<T>(),
449                )
450                .unwrap();
451                std::alloc::dealloc(ptr as *mut u8, layout);
452            }
453        }
454    }
455}
456
457//---------------------
458// Object
459//---------------------
460
461impl Object {
462    pub fn new() -> Self {
463        Self {
464            header: TVMFFIObject::new(),
465        }
466    }
467}
468
469unsafe impl ObjectCore for Object {
470    const TYPE_KEY: &'static str = "ffi.Object";
471    const TYPE_DEPTH: i32 = 0;
472    #[inline]
473    fn type_index() -> i32 {
474        TypeIndex::kTVMFFIStaticObjectBegin as i32
475    }
476    #[inline]
477    unsafe fn object_header_mut(this: &mut Self) -> &mut TVMFFIObject {
478        &mut this.header
479    }
480}
481
482//---------------------
483// ObjectArc
484//---------------------
485
486impl<T: ObjectCore> ObjectArc<T> {
487    pub fn new(data: T) -> Self {
488        unsafe {
489            let layout = std::alloc::Layout::new::<T>();
490            let raw_data_ptr = std::alloc::alloc(layout);
491            if raw_data_ptr.is_null() {
492                std::alloc::handle_alloc_error(layout);
493            }
494            let ptr = raw_data_ptr as *mut T;
495            std::ptr::write(ptr, data);
496            // now override the header directly
497            std::ptr::write(
498                ptr as *mut TVMFFIObject,
499                TVMFFIObject {
500                    combined_ref_count: AtomicU64::new(COMBINED_REF_COUNT_BOTH_ONE),
501                    type_index: T::type_index(),
502                    __padding: 0,
503                    deleter: Some(unsafe_::object_deleter_for_new::<T>),
504                },
505            );
506            // move into the object arc ptr
507            Self {
508                ptr: std::ptr::NonNull::new_unchecked(ptr as *mut T),
509                _phantom: std::marker::PhantomData,
510            }
511        }
512    }
513    pub fn new_with_extra_items<U>(data: T) -> Self
514    where
515        T: ObjectCoreWithExtraItems<ExtraItem = U>,
516    {
517        unsafe {
518            // ensure strict alignment requirements
519            // so we can have { T, U*extra_items } layout
520            assert_eq!(std::mem::align_of::<T>() % std::mem::align_of::<U>(), 0);
521            assert_eq!(std::mem::size_of::<T>() % std::mem::align_of::<U>(), 0);
522            let extra_items_count = T::extra_items_count(&data);
523            let layout = std::alloc::Layout::from_size_align(
524                std::mem::size_of::<T>() + extra_items_count * std::mem::size_of::<U>(),
525                std::mem::align_of::<T>(),
526            )
527            .unwrap();
528            let raw_data_ptr = std::alloc::alloc(layout);
529            if raw_data_ptr.is_null() {
530                std::alloc::handle_alloc_error(layout);
531            }
532            let ptr = raw_data_ptr as *mut T;
533            std::ptr::write(ptr, data);
534            // now override the header directly
535            std::ptr::write(
536                ptr as *mut TVMFFIObject,
537                TVMFFIObject {
538                    combined_ref_count: AtomicU64::new(COMBINED_REF_COUNT_BOTH_ONE),
539                    type_index: T::type_index(),
540                    __padding: 0,
541                    deleter: Some(unsafe_::object_deleter_for_new_with_extra_items::<T, U>),
542                },
543            );
544            // move into the object arc ptr
545            Self {
546                ptr: std::ptr::NonNull::new_unchecked(ptr as *mut T),
547                _phantom: std::marker::PhantomData,
548            }
549        }
550    }
551
552    /// Move a previously allocated object into the ObjectArc
553    ///
554    /// # Arguments
555    /// * `ptr` - The raw pointer to move into the ObjectArc
556    ///
557    /// # Returns
558    /// * `ObjectArc<T>` - The ObjectArc
559    /// \return The ObjectArc
560    #[inline]
561    pub unsafe fn from_raw(ptr: *const T) -> Self {
562        Self {
563            ptr: std::ptr::NonNull::new_unchecked(ptr as *mut T),
564            _phantom: std::marker::PhantomData,
565        }
566    }
567
568    /// Move the ObjectArc into a raw pointer
569    ///
570    /// # Arguments
571    /// * `this` - The ObjectArc to move into a raw pointer
572    ///
573    /// # Returns
574    /// * `*const T` - The raw pointer
575    #[inline]
576    pub unsafe fn into_raw(this: Self) -> *const T {
577        let droped_this = std::mem::ManuallyDrop::new(this);
578        droped_this.ptr.as_ptr() as *const T
579    }
580
581    /// Get the raw pointer from the ObjectArc
582    ///
583    /// Caller should view this as a non-owning reference
584    ///
585    /// # Arguments
586    /// * `this` - The ObjectArc to get the raw pointer
587    ///
588    /// # Returns
589    /// * `*const T` - The raw pointer
590    /// \return The raw pointer
591    #[inline]
592    pub unsafe fn as_raw(this: &Self) -> *const T {
593        this.ptr.as_ptr() as *const T
594    }
595
596    /// Get the raw mutable pointer from the ObjectArc
597    ///
598    /// Caller should view this as a non-owning reference
599    ///
600    /// # Arguments
601    /// * `this` - The ObjectArc to get the raw pointer
602    ///
603    /// # Returns
604    /// * `*mut T` - The raw pointer
605    #[inline]
606    pub unsafe fn as_raw_mut(this: &mut Self) -> *mut T {
607        this.ptr.as_mut()
608    }
609
610    /// Get the strong reference count of the ObjectArc
611    ///
612    /// # Arguments
613    /// * `this` - The ObjectArc to get the strong reference count
614    ///
615    /// # Returns
616    /// * `usize` - The strong reference count
617    #[inline]
618    pub fn strong_count(this: &Self) -> usize {
619        unsafe {
620            unsafe_::strong_count(this.ptr.as_ref() as *const T as *mut T as *mut TVMFFIObject)
621        }
622    }
623
624    /// Get the weak reference count of the ObjectArc
625    ///
626    /// # Arguments
627    /// * `this` - The ObjectArc to get the weak reference count
628    ///
629    /// # Returns
630    /// * `usize` - The weak reference count
631    #[inline]
632    pub fn weak_count(this: &Self) -> usize {
633        unsafe { unsafe_::weak_count(this.ptr.as_ref() as *const T as *mut T as *mut TVMFFIObject) }
634    }
635}
636
637// implement Deref for ObjectArc
638impl<T: ObjectCore> Deref for ObjectArc<T> {
639    type Target = T;
640    #[inline]
641    fn deref(&self) -> &Self::Target {
642        unsafe { self.ptr.as_ref() }
643    }
644}
645
646// implement DerefMut for ObjectArc
647impl<T: ObjectCore> DerefMut for ObjectArc<T> {
648    #[inline]
649    fn deref_mut(&mut self) -> &mut Self::Target {
650        unsafe { self.ptr.as_mut() }
651    }
652}
653
654// implement Drop for ObjectArc
655impl<T: ObjectCore> Drop for ObjectArc<T> {
656    fn drop(&mut self) {
657        unsafe { unsafe_::dec_ref(self.ptr.as_mut() as *mut T as *mut TVMFFIObject) }
658    }
659}
660
661// implement Clone for ObjectArc
662impl<T: ObjectCore> Clone for ObjectArc<T> {
663    #[inline]
664    fn clone(&self) -> Self {
665        unsafe { unsafe_::inc_ref(self.ptr.as_ref() as *const T as *mut T as *mut TVMFFIObject) }
666        Self {
667            ptr: self.ptr,
668            _phantom: std::marker::PhantomData,
669        }
670    }
671}