Skip to main content

tvm_ffi/extra/
structural_common.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 */
19
20use crate::any::{Any, AnyView};
21use crate::error::Error;
22use crate::object::{self, ObjectCore};
23use crate::tvm_ffi_sys::{TVMFFIAny, TVMFFIGetTypeInfo, TVMFFITypeIndex};
24
25/// Add one structural traversal frame to an error's backtrace.
26pub(crate) fn with_structural_error_context(error: Error, operation: &str, frame: &str) -> Error {
27    Error::with_appended_backtrace(error, &format!("[native structural {operation}] {frame}\n"))
28}
29
30// Generate the tuple arities supported by the standard library (1 through 12).
31macro_rules! impl_callback_chain_tuple_arities {
32    ($impl_chain:ident) => {
33        impl_callback_chain_tuple_arities!(
34            @prefixes $impl_chain;
35            [];
36            (F0, M0, 0),
37            (F1, M1, 1),
38            (F2, M2, 2),
39            (F3, M3, 3),
40            (F4, M4, 4),
41            (F5, M5, 5),
42            (F6, M6, 6),
43            (F7, M7, 7),
44            (F8, M8, 8),
45            (F9, M9, 9),
46            (F10, M10, 10),
47            (F11, M11, 11)
48        );
49    };
50    (@prefixes $impl_chain:ident; [$($prefix:tt)*];) => {};
51    (
52        @prefixes $impl_chain:ident;
53        [$($prefix:tt)*];
54        $next:tt $(, $rest:tt)*
55    ) => {
56        $impl_chain!($($prefix)* $next);
57        impl_callback_chain_tuple_arities!(
58            @prefixes $impl_chain;
59            [$($prefix)* $next,];
60            $($rest),*
61        );
62    };
63}
64
65pub(crate) use impl_callback_chain_tuple_arities;
66
67/// A borrowed value shared by structural visit and map callbacks.
68///
69/// This type centralizes the audited unsafe operations used to cast FFI
70/// values and borrow object nodes. The public APIs expose it as `VisitValue`
71/// or `MapValue` according to the callback context.
72#[repr(transparent)]
73pub struct StructuralValue(TVMFFIAny);
74
75impl StructuralValue {
76    #[inline]
77    pub(crate) fn from_raw(raw: TVMFFIAny) -> Self {
78        Self(raw)
79    }
80
81    #[inline]
82    pub(crate) fn raw(&self) -> TVMFFIAny {
83        self.0
84    }
85
86    /// Copy this borrowed value into an owning [`Any`].
87    #[inline]
88    pub fn to_owned(&self) -> Any {
89        if let Some(owned) = try_to_owned_without_normalization(self.0) {
90            return owned;
91        }
92        Any::from(unsafe { AnyView::from_raw_ffi_any(self.0) })
93    }
94
95    /// Convert the value into an owned typed handle.
96    #[inline]
97    pub fn cast<R: crate::type_traits::AnyCompatible>(&self) -> Option<R> {
98        unsafe {
99            if R::check_any_strict(&self.0) {
100                Some(R::copy_from_any_view_after_check(&self.0))
101            } else {
102                None
103            }
104        }
105    }
106
107    /// Runtime type index stored in this value.
108    #[inline]
109    pub fn type_index(&self) -> i32 {
110        self.0.type_index
111    }
112
113    /// Borrow the value as node type `N` if it is an instance of that type.
114    #[inline]
115    pub fn as_node<N: ObjectCore>(&self) -> Option<&N> {
116        if self.0.type_index < TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32 {
117            return None;
118        }
119        let base_type_index = N::type_index();
120        if self.0.type_index != base_type_index {
121            // A final type has no registered subtype, so a differing index can
122            // never match: reject with the integer compare alone, mirroring the
123            // `_type_final` fast path of C++ `IsObjectInstance`.
124            if N::TYPE_FINAL {
125                return None;
126            }
127            if !is_instance_at_depth(self.0.type_index, base_type_index, N::TYPE_DEPTH) {
128                return None;
129            }
130        }
131        Some(unsafe { &*(self.0.data_union.v_obj as *const N) })
132    }
133}
134
135/// Copy a borrowed FFI value when its owning representation is unchanged.
136///
137/// Raw string/byte views and `ObjectRValueRef` return `None` because they need
138/// the runtime's normalization or move logic. A null object pointer also
139/// returns `None` instead of constructing an invalid owning value.
140#[inline]
141pub(crate) fn try_to_owned_without_normalization(raw: TVMFFIAny) -> Option<Any> {
142    if is_plain_inline(raw.type_index) {
143        return Some(unsafe { Any::from_raw_ffi_any(raw) });
144    }
145    if raw.type_index >= TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32 {
146        let object = unsafe { raw.data_union.v_obj };
147        if object.is_null() {
148            return None;
149        }
150        unsafe { object::unsafe_::inc_ref(object) };
151        return Some(unsafe { Any::from_raw_ffi_any(raw) });
152    }
153    None
154}
155
156pub(crate) use crate::any::is_plain_inline;
157
158#[inline]
159pub(crate) fn same_shallow(lhs: TVMFFIAny, rhs: TVMFFIAny) -> bool {
160    lhs.type_index == rhs.type_index
161        && lhs.small_str_len == rhs.small_str_len
162        && unsafe { lhs.data_union.v_uint64 == rhs.data_union.v_uint64 }
163}
164
165/// Subtype check with the base's inheritance depth supplied by the caller
166/// (`ObjectCore::TYPE_DEPTH`), so only the object's type info is fetched.
167#[inline]
168fn is_instance_at_depth(object_type_index: i32, base_type_index: i32, base_depth: i32) -> bool {
169    if object_type_index == base_type_index {
170        return true;
171    }
172    // Parent type indices are registered before their descendants. An object
173    // whose index precedes the target therefore cannot be its subtype.
174    if object_type_index < base_type_index {
175        return false;
176    }
177    unsafe {
178        let info = TVMFFIGetTypeInfo(object_type_index);
179        if info.is_null() {
180            return false;
181        }
182        if (*info).type_depth <= base_depth {
183            return false;
184        }
185        let ancestors = (*info).type_acenstors;
186        if ancestors.is_null() {
187            return false;
188        }
189        let ancestor = *ancestors.offset(base_depth as isize);
190        !ancestor.is_null() && (*ancestor).type_index == base_type_index
191    }
192}