Skip to main content

tvm_ffi/
reflection.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
20//! Safe access to object reflection metadata.
21
22use std::ffi::c_void;
23use std::ptr::NonNull;
24
25use crate::tvm_ffi_sys::{
26    TVMFFIAny, TVMFFIByteArray, TVMFFIFieldGetter, TVMFFIFieldInfo, TVMFFIGetTypeAttrColumn,
27    TVMFFIGetTypeInfo, TVMFFIObject, TVMFFITypeAttrColumn, TVMFFITypeIndex,
28};
29use crate::{Any, AnyView, Error, ObjectCore, Result, TYPE_ERROR};
30
31/// A registry-owned type-attribute column indexed by runtime type.
32///
33/// [`TypeAttrColumn::get`] returns owning copies. Registration must not race
34/// with reads.
35#[derive(Clone, Copy)]
36pub struct TypeAttrColumn(NonNull<TVMFFITypeAttrColumn>);
37
38// Type-attribute columns and their cells are registry-owned process-lifetime
39// data. Once registration is complete, reading a cell does not mutate the
40// registry and is safe from any thread.
41unsafe impl Send for TypeAttrColumn {}
42unsafe impl Sync for TypeAttrColumn {}
43
44impl TypeAttrColumn {
45    /// Look up a registered type-attribute column by name.
46    pub fn new(name: &str) -> Option<Self> {
47        unsafe {
48            let name = TVMFFIByteArray::from_str(name);
49            NonNull::new(TVMFFIGetTypeAttrColumn(&name).cast_mut()).map(Self)
50        }
51    }
52
53    /// Return an owning copy of this attribute for `type_index`.
54    pub fn get(self, type_index: i32) -> Option<Any> {
55        let raw = self.get_raw(type_index)?;
56        if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
57            return None;
58        }
59        Some(Any::from(unsafe { AnyView::from_raw_ffi_any(raw) }))
60    }
61
62    pub(crate) unsafe fn from_non_null(pointer: NonNull<TVMFFITypeAttrColumn>) -> Self {
63        Self(pointer)
64    }
65
66    pub(crate) fn as_ptr(self) -> *mut TVMFFITypeAttrColumn {
67        self.0.as_ptr()
68    }
69
70    /// Copy one borrowed cell without taking ownership.
71    pub(crate) fn get_raw(self, type_index: i32) -> Option<TVMFFIAny> {
72        unsafe {
73            let column = self.0.as_ref();
74            let index = type_index - column.begin_index;
75            if index < 0 || index >= column.size || column.data.is_null() {
76                None
77            } else {
78                Some(*column.data.offset(index as isize))
79            }
80        }
81    }
82}
83
84/// Look up one type attribute and copy it into an owning value.
85pub fn get_type_attr(type_index: i32, attr_name: &str) -> Option<Any> {
86    TypeAttrColumn::new(attr_name)?.get(type_index)
87}
88
89/// Resolves a reflected field once, then uses its registered C ABI getter.
90#[derive(Clone, Copy)]
91pub struct FieldGetter {
92    owner_type_index: i32,
93    owner_type_depth: i32,
94    field_offset: i64,
95    getter: TVMFFIFieldGetter,
96}
97
98impl FieldGetter {
99    /// Resolve a reflected field declared by `type_index` or one of its bases.
100    pub fn new(type_index: i32, field_name: &str) -> Result<Self> {
101        let type_info = unsafe { TVMFFIGetTypeInfo(type_index) };
102        if type_info.is_null() {
103            return Err(Error::new(
104                TYPE_ERROR,
105                &format!("Cannot find type info for type_index={type_index}"),
106                "",
107            ));
108        }
109
110        let field = unsafe { find_field(type_info, field_name) }.ok_or_else(|| {
111            let type_key = unsafe { (*type_info).type_key.as_str() };
112            Error::new(
113                TYPE_ERROR,
114                &format!("Cannot find reflected field `{field_name}` in type `{type_key}`"),
115                "",
116            )
117        })?;
118        let field = unsafe { field.as_ref() };
119        let getter = field.getter.ok_or_else(|| {
120            Error::new(
121                TYPE_ERROR,
122                &format!("Reflected field `{}` has no getter", field.name.as_str()),
123                "",
124            )
125        })?;
126        Ok(Self {
127            owner_type_index: type_index,
128            owner_type_depth: unsafe { (*type_info).type_depth },
129            field_offset: field.offset,
130            getter,
131        })
132    }
133
134    /// Read the field as an owning [`Any`].
135    ///
136    /// `object` may have the declared owner type or any registered subtype.
137    pub fn get_any<N: ObjectCore>(&self, object: &N) -> Result<Any> {
138        let object_pointer = std::ptr::from_ref(object);
139        let header = object_pointer.cast::<TVMFFIObject>();
140        let dynamic_type_index = unsafe { (*header).type_index };
141        if !unsafe {
142            is_type_or_subtype(
143                dynamic_type_index,
144                self.owner_type_index,
145                self.owner_type_depth,
146            )
147        } {
148            return Err(Error::new(
149                TYPE_ERROR,
150                &format!(
151                    "Cannot read a field of type_index={} from object type_index={dynamic_type_index}",
152                    self.owner_type_index
153                ),
154                "",
155            ));
156        }
157
158        let field_address = unsafe {
159            object_pointer
160                .cast::<u8>()
161                .offset(self.field_offset as isize)
162                .cast_mut()
163                .cast::<c_void>()
164        };
165        let mut result = Any::new();
166        if unsafe { (self.getter)(field_address, Any::as_data_ptr(&mut result)) } != 0 {
167            return Err(Error::from_raised());
168        }
169        Ok(result)
170    }
171
172    /// Read and convert the field to `T`.
173    pub fn get<N, T>(&self, object: &N) -> Result<T>
174    where
175        N: ObjectCore,
176        T: TryFrom<Any, Error = Error>,
177    {
178        T::try_from(self.get_any(object)?)
179    }
180}
181
182unsafe fn find_field(
183    type_info: *const crate::tvm_ffi_sys::TVMFFITypeInfo,
184    field_name: &str,
185) -> Option<NonNull<TVMFFIFieldInfo>> {
186    // Prefer the most-derived declaration, then search nearest ancestors.
187    if let Some(field) = find_field_at_level(type_info, field_name) {
188        return Some(field);
189    }
190    for depth in (0..(*type_info).type_depth).rev() {
191        let ancestor = *(*type_info).type_acenstors.add(depth as usize);
192        if let Some(field) = find_field_at_level(ancestor, field_name) {
193            return Some(field);
194        }
195    }
196    None
197}
198
199unsafe fn find_field_at_level(
200    type_info: *const crate::tvm_ffi_sys::TVMFFITypeInfo,
201    field_name: &str,
202) -> Option<NonNull<TVMFFIFieldInfo>> {
203    if type_info.is_null() || (*type_info).fields.is_null() {
204        return None;
205    }
206    for index in 0..(*type_info).num_fields as usize {
207        let field = (*type_info).fields.add(index);
208        if (*field).name.as_str() == field_name {
209            return NonNull::new(field.cast_mut());
210        }
211    }
212    None
213}
214
215unsafe fn is_type_or_subtype(
216    dynamic_type_index: i32,
217    target_type_index: i32,
218    target_type_depth: i32,
219) -> bool {
220    if dynamic_type_index == target_type_index {
221        return true;
222    }
223    let dynamic_info = TVMFFIGetTypeInfo(dynamic_type_index);
224    if dynamic_info.is_null()
225        || (*dynamic_info).type_depth <= target_type_depth
226        || (*dynamic_info).type_acenstors.is_null()
227    {
228        return false;
229    }
230    let ancestor = *(*dynamic_info)
231        .type_acenstors
232        .add(target_type_depth as usize);
233    !ancestor.is_null() && (*ancestor).type_index == target_type_index
234}