This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new 75e693f8 [FEAT][RUST] Resolve reflected type methods and constructors
from Rust (#707)
75e693f8 is described below
commit 75e693f88338e5264a02f73da2eeb2d5aae2163e
Author: Linzhang Li <[email protected]>
AuthorDate: Tue Aug 11 20:19:13 2026 -0400
[FEAT][RUST] Resolve reflected type methods and constructors from Rust
(#707)
Add Function::from_type_method(type_index, name) and
Function::from_type_key_method(type_key, name) to look up methods
registered through the C++ reflection registry
(`efl::ObjectDef<T>::def(...)`), which live in the per-type method table
rather than the global function table. Constructors registered via
refl::init are reachable under the reserved name __ffi_init__.
Downstream Rust consumers of libraries that follow the ObjectDef idiom
previously had to hand-roll this table walk against raw tvm-ffi-sys
structs; this provides the missing primitive in the crate itself.
Signed-off-by: yuchuan <[email protected]>
---
docs/guides/rust_lang_guide.md | 24 +++++++++++
rust/tvm-ffi/src/function.rs | 81 ++++++++++++++++++++++++++++++++++++-
rust/tvm-ffi/tests/test_function.rs | 38 +++++++++++++++++
3 files changed, 142 insertions(+), 1 deletion(-)
diff --git a/docs/guides/rust_lang_guide.md b/docs/guides/rust_lang_guide.md
index f2ae5eff..226b5e31 100644
--- a/docs/guides/rust_lang_guide.md
+++ b/docs/guides/rust_lang_guide.md
@@ -137,6 +137,30 @@ let my_func = Function::from_packed(|args: &[AnyView]| ->
Result<Any> {
Function::register_global("my_custom_func", my_func)?;
```
+### Reflected Type Methods
+
+Libraries that register their API through the C++ reflection registry
+(`refl::ObjectDef<T>().def(...)`) store methods in a per-type method table
+rather than the global function table. Resolve them by type key (or type
+index) and method name; constructors registered via `refl::init` are
+reachable under the reserved name `__ffi_init__`:
+
+```rust
+use tvm_ffi::{AnyView, Function};
+
+// Resolve the reflected constructor and construct an instance
+let ctor = Function::from_type_key_method("testing.TestIntPair",
"__ffi_init__")?;
+let pair = ctor.call_tuple((1i64, 2i64))?;
+
+// Resolve an instance method; the first packed argument is the object itself
+let sum = Function::from_type_key_method("testing.TestIntPair", "sum")?;
+let result = sum.call_packed(&[AnyView::from(&pair)])?;
+assert_eq!(i64::try_from(result)?, 3);
+```
+
+`Function::from_type_method(type_index, name)` performs the same lookup when
+the type index is already known (e.g. from `Any::type_index`).
+
### Type-Erased Functions
Create functions from Rust closures:
diff --git a/rust/tvm-ffi/src/function.rs b/rust/tvm-ffi/src/function.rs
index 4af971bd..2d37a2bd 100644
--- a/rust/tvm-ffi/src/function.rs
+++ b/rust/tvm-ffi/src/function.rs
@@ -21,9 +21,11 @@ use crate::derive::{Object, ObjectRef};
use crate::error::{Error, Result};
use crate::function_internal::{AsPackedCallable, TupleAsPackedArgs};
use crate::object::{Object, ObjectArc, ObjectCore};
+use crate::type_traits::AnyCompatible;
use tvm_ffi_sys::{
TVMFFIAny, TVMFFIByteArray, TVMFFIFunctionCell, TVMFFIFunctionCreate,
TVMFFIFunctionGetGlobal,
- TVMFFIFunctionSetGlobal, TVMFFIObjectHandle, TVMFFISafeCallType,
TVMFFITypeIndex,
+ TVMFFIFunctionSetGlobal, TVMFFIGetTypeInfo, TVMFFIObjectHandle,
TVMFFISafeCallType,
+ TVMFFITypeIndex, TVMFFITypeKeyToIndex,
};
/// function object
@@ -196,6 +198,83 @@ impl Function {
}
}
+ /// Look up a reflected method of a type by type index and method name
+ ///
+ /// Methods registered through the C++ reflection registry
+ /// (`refl::ObjectDef<T>().def(...)`) live in the per-type method table
+ /// rather than the global function table. Constructors registered via
+ /// `refl::init` are reachable under the reserved name `__ffi_init__`.
+ /// For instance methods, the first packed argument is the object itself.
+ ///
+ /// `type_index` must be a registered type index (e.g. obtained from a
+ /// live object via `Any::type_index` or from a type key); the underlying
+ /// C API treats an unregistered index as a fatal error.
+ ///
+ /// # Arguments
+ /// * `type_index` - The type index of the type that owns the method
+ /// * `method_name` - The name of the method
+ ///
+ /// # Returns
+ /// * `Function` - The reflected method
+ pub fn from_type_method(type_index: i32, method_name: &str) ->
Result<Function> {
+ unsafe {
+ let type_info = TVMFFIGetTypeInfo(type_index);
+ if type_info.is_null() {
+ crate::bail!(
+ crate::error::TYPE_ERROR,
+ "Cannot find type info for type_index={}",
+ type_index
+ );
+ }
+ let type_info = &*type_info;
+ for i in 0..type_info.num_methods as usize {
+ let method_info = &*type_info.methods.add(i);
+ if method_info.name.as_str() != method_name {
+ continue;
+ }
+ if !<Function as
AnyCompatible>::check_any_strict(&method_info.method) {
+ crate::bail!(
+ crate::error::TYPE_ERROR,
+ "Method `{}` of type `{}` is not a Function",
+ method_name,
+ type_info.type_key.as_str()
+ );
+ }
+ // the table entry stores the method as a non-owning AnyView;
+ // copy out a strong reference
+ return Ok(<Function as
AnyCompatible>::copy_from_any_view_after_check(
+ &method_info.method,
+ ));
+ }
+ crate::bail!(
+ crate::error::TYPE_ERROR,
+ "Cannot find method `{}` of type `{}`",
+ method_name,
+ type_info.type_key.as_str()
+ );
+ }
+ }
+
+ /// Look up a reflected method of a type by type key and method name
+ ///
+ /// Same as [`Function::from_type_method`], but resolves `type_key` to a
+ /// type index first.
+ ///
+ /// # Arguments
+ /// * `type_key` - The type key of the type that owns the method
+ /// * `method_name` - The name of the method
+ ///
+ /// # Returns
+ /// * `Function` - The reflected method
+ pub fn from_type_key_method(type_key: &str, method_name: &str) ->
Result<Function> {
+ unsafe {
+ let type_key_arg = TVMFFIByteArray::from_str(type_key);
+ let mut type_index: i32 = 0;
+ crate::check_safe_call!(TVMFFITypeKeyToIndex(&type_key_arg, &mut
type_index))?;
+ Self::from_type_method(type_index, method_name)
+ }
+ }
+
/// Register a function as a global function
/// # Arguments
/// * `name` - The name of the function
diff --git a/rust/tvm-ffi/tests/test_function.rs
b/rust/tvm-ffi/tests/test_function.rs
index 42205eeb..9ed53172 100644
--- a/rust/tvm-ffi/tests/test_function.rs
+++ b/rust/tvm-ffi/tests/test_function.rs
@@ -131,6 +131,44 @@ fn test_function_echo_tensor_typed() {
assert_eq!(result_data[3], 4.0);
}
+#[test]
+fn test_function_from_type_key_method_ctor_and_method() {
+ // constructors registered via refl::init are reachable as `__ffi_init__`
+ let ctor = Function::from_type_key_method("testing.TestIntPair",
"__ffi_init__").unwrap();
+ let pair = ctor.call_tuple((1i64, 2i64)).unwrap();
+ // instance method: the first packed argument is the object itself
+ let sum = Function::from_type_key_method("testing.TestIntPair",
"sum").unwrap();
+ let result = sum.call_packed(&[AnyView::from(&pair)]).unwrap();
+ assert_eq!(i64::try_from(result).unwrap(), 3);
+}
+
+#[test]
+fn test_function_from_type_method_by_index() {
+ let ctor = Function::from_type_key_method("testing.TestIntPair",
"__ffi_init__").unwrap();
+ let pair = ctor.call_tuple((5i64, 7i64)).unwrap();
+ let sum = Function::from_type_method(pair.type_index(), "sum").unwrap();
+ let result = sum.call_packed(&[AnyView::from(&pair)]).unwrap();
+ assert_eq!(i64::try_from(result).unwrap(), 12);
+}
+
+#[test]
+fn test_function_from_type_method_unknown_method() {
+ let error = Function::from_type_key_method("testing.TestIntPair",
"nonexistent_method")
+ .err()
+ .unwrap();
+ assert_eq!(error.kind(), TYPE_ERROR);
+ assert!(error.message().contains("nonexistent_method"));
+ assert!(error.message().contains("testing.TestIntPair"));
+}
+
+#[test]
+fn test_function_from_type_key_method_unknown_type_key() {
+ let error = Function::from_type_key_method("testing.NonExistentType",
"sum")
+ .err()
+ .unwrap();
+ assert!(error.message().contains("testing.NonExistentType"));
+}
+
fn testing_add_one(x: i32) -> Result<i32> {
Ok(x + 1)
}