blob: 02699ad52fb678526af8cf10304875fa233288c9 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// This file is used for testing the FFI API.
// NOTE: TVM_FFI_DLL_EXPORT_INCLUDE_METADATA=1 is set via CMake target_compile_definitions
#include <dlpack/dlpack.h>
#include <tvm/ffi/any.h>
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/container/dict.h>
#include <tvm/ffi/container/list.h>
#include <tvm/ffi/container/map.h>
#include <tvm/ffi/container/tensor.h>
#include <tvm/ffi/container/variant.h>
#include <tvm/ffi/device.h>
#include <tvm/ffi/dtype.h>
#include <tvm/ffi/enum.h>
#include <tvm/ffi/extra/c_env_api.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/optional.h>
#include <tvm/ffi/reflection/accessor.h>
#include <tvm/ffi/reflection/enum_def.h>
#include <tvm/ffi/reflection/registry.h>
#include <chrono>
#include <iostream>
#include <thread>
#include <utility>
namespace tvm {
namespace ffi {
// Step 1: Define the object class (stores the actual data)
class TestIntPairObj : public tvm::ffi::Object {
public:
int64_t a;
int64_t b;
TestIntPairObj() = default;
TestIntPairObj(int64_t a, int64_t b) : a(a), b(b) {}
// Required: declare type information
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestIntPair", TestIntPairObj, tvm::ffi::Object);
};
// Step 2: Define the reference wrapper (user-facing interface)
class TestIntPair : public tvm::ffi::ObjectRef {
public:
// Constructor
explicit TestIntPair(int64_t a, int64_t b) {
data_ = tvm::ffi::make_object<TestIntPairObj>(a, b);
}
int64_t Sum() const { return get()->a + get()->b; }
// Required: define object reference methods
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TestIntPair, tvm::ffi::ObjectRef, TestIntPairObj);
};
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<TestIntPairObj>()
.def(refl::init<int64_t, int64_t>())
.def_ro("a", &TestIntPairObj::a, "Field `a`")
.def_ro("b", &TestIntPairObj::b, "Field `b`")
.def("sum", &TestIntPair::Sum, "Method to compute sum of a and b");
refl::TypeAttrDef<TestIntPairObj>().def_convert<TestIntPair>();
}
// C++-backed enum used by the Python ``Enum`` tests to exercise both
// ``EnumDef``-registered entries and the Python ``ClassVar``-based binding.
class TestEnumVariantObj : public tvm::ffi::EnumObj {
public:
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestEnumVariant", TestEnumVariantObj,
tvm::ffi::EnumObj);
};
class TestEnumVariant : public tvm::ffi::Enum {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TestEnumVariant, tvm::ffi::Enum, TestEnumVariantObj);
};
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
// ObjectDef registers the type on destruction, so the temporary is intentional;
// silence clang-tidy's bugprone-unused-raii since the RAII finalisation is the point.
refl::ObjectDef<TestEnumVariantObj>(refl::init(false)); // NOLINT(bugprone-unused-raii)
refl::TypeAttrDef<TestEnumVariantObj>().def(
refl::type_attr::kConvert, &refl::details::FFIConvertFromAnyViewToObjectRef<TestEnumVariant>);
refl::EnumDef<TestEnumVariantObj>("Alpha").set_attr("code", int64_t{10});
refl::EnumDef<TestEnumVariantObj>("Beta").set_attr("code", int64_t{20});
}
class TestObjectBase : public Object {
public:
int64_t v_i64;
double v_f64;
String v_str;
int64_t AddI64(int64_t other) const { return v_i64 + other; }
// declare as one slot, with float as overflow
static constexpr bool _type_mutable = true;
static constexpr uint32_t _type_child_slots = 1;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestObjectBase", TestObjectBase, Object);
};
class TestObjectDerived : public TestObjectBase {
public:
Map<Any, Any> v_map;
Array<Any> v_array;
// declare as one slot, with float as overflow
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestObjectDerived", TestObjectDerived, TestObjectBase);
};
class TestCxxClassBase : public Object {
public:
int64_t v_i64 = 0;
int32_t v_i32 = 0;
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxClassBase", TestCxxClassBase, Object);
};
class TestCxxClassDerived : public TestCxxClassBase {
public:
double v_f64 = 0.0;
float v_f32 = 0.0f;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxClassDerived", TestCxxClassDerived, TestCxxClassBase);
};
class TestCxxClassDerivedDerived : public TestCxxClassDerived {
public:
String v_str;
bool v_bool = false;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxClassDerivedDerived", TestCxxClassDerivedDerived,
TestCxxClassDerived);
};
class TestCxxInitSubsetObj : public Object {
public:
int64_t required_field = 0;
int64_t optional_field = -1;
String note;
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxInitSubset", TestCxxInitSubsetObj, Object);
};
class TestCxxKwOnly : public Object {
public:
int64_t x = 0;
int64_t y = 0;
int64_t z = 0;
int64_t w = 0;
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxKwOnly", TestCxxKwOnly, Object);
};
// Test: auto-generated __ffi_init__ with init(false) and KwOnly(true) per-field.
// No explicit refl::init<>() — the auto-init is generated in ObjectDef's destructor.
class TestCxxAutoInitObj : public Object {
public:
int64_t a; ///< init=True, positional (default)
int64_t b; ///< init=False, has default
int64_t c; ///< init=True, kw_only
int64_t d; ///< init=True, positional, has default
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxAutoInit", TestCxxAutoInitObj, Object);
};
// Test: auto-generated init with all fields init=True (no Init/KwOnly traits).
class TestCxxAutoInitSimpleObj : public Object {
public:
int64_t x;
int64_t y;
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxAutoInitSimple", TestCxxAutoInitSimpleObj, Object);
};
// Test: auto-generated init with all fields excluded from init.
class TestCxxAutoInitAllInitOffObj : public Object {
public:
int64_t x = 7; ///< init=False, has reflection default
int64_t y = 0; ///< init=False, has reflection default
int64_t z = 1234; ///< init=False, no reflection default (creator default is kept)
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxAutoInitAllInitOff", TestCxxAutoInitAllInitOffObj,
Object);
};
// Test: mixed positional + kw_only + defaults + init=False field.
class TestCxxAutoInitKwOnlyDefaultsObj : public Object {
public:
int64_t p_required; ///< init=True, positional, required
int64_t p_default; ///< init=True, positional, default=11
int64_t k_required; ///< init=True, kw_only, required
int64_t k_default; ///< init=True, kw_only, default=22
int64_t hidden; ///< init=False, default=33
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxAutoInitKwOnlyDefaults",
TestCxxAutoInitKwOnlyDefaultsObj, Object);
};
// Test: inheritance + auto-generated init.
class TestCxxAutoInitParentObj : public Object {
public:
int64_t parent_required;
int64_t parent_default;
static constexpr bool _type_mutable = true;
static constexpr uint32_t _type_child_slots = 1;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxAutoInitParent", TestCxxAutoInitParentObj, Object);
};
class TestCxxAutoInitChildObj : public TestCxxAutoInitParentObj {
public:
int64_t child_required;
int64_t child_kw_only;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestCxxAutoInitChild", TestCxxAutoInitChildObj,
TestCxxAutoInitParentObj);
};
// Test: init(false) at class level suppresses auto-generated __ffi_init__.
class TestCxxNoAutoInitObj : public Object {
public:
int64_t x;
int64_t y;
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestCxxNoAutoInit", TestCxxNoAutoInitObj, Object);
};
class TestDeepCopyEdgesObj : public Object {
public:
Any v_any;
ObjectRef v_obj;
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestDeepCopyEdges", TestDeepCopyEdgesObj, Object);
};
class TestNonCopyable : public Object {
public:
int64_t value;
explicit TestNonCopyable(int64_t value) : value(value) {}
TestNonCopyable(const TestNonCopyable&) = delete;
TestNonCopyable& operator=(const TestNonCopyable&) = delete;
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestNonCopyable", TestNonCopyable, Object);
};
class TestUnregisteredBaseObject : public Object {
public:
int64_t v1;
explicit TestUnregisteredBaseObject(int64_t v1) : v1(v1) {}
int64_t GetV1PlusOne() const { return v1 + 1; }
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestUnregisteredBaseObject", TestUnregisteredBaseObject,
Object);
};
class TestUnregisteredObject : public TestUnregisteredBaseObject {
public:
int64_t v2;
explicit TestUnregisteredObject(int64_t v1, int64_t v2)
: TestUnregisteredBaseObject(v1), v2(v2) {}
int64_t GetV2PlusTwo() const { return v2 + 2; }
TVM_FFI_DECLARE_OBJECT_INFO("testing.TestUnregisteredObject", TestUnregisteredObject,
TestUnregisteredBaseObject);
};
class TestCompareObj : public Object {
public:
int64_t key;
String name;
int64_t ignored_field;
TestCompareObj() = default;
TestCompareObj(int64_t key, String name, int64_t ignored_field)
: key(key), name(std::move(name)), ignored_field(ignored_field) {}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestCompare", TestCompareObj, Object);
};
class TestCompare : public ObjectRef {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TestCompare, ObjectRef, TestCompareObj);
};
class TestHashObj : public Object {
public:
int64_t key;
String name;
int64_t hash_ignored;
TestHashObj() = default;
TestHashObj(int64_t key, String name, int64_t hash_ignored)
: key(key), name(std::move(name)), hash_ignored(hash_ignored) {}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestHash", TestHashObj, Object);
};
class TestHash : public ObjectRef {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TestHash, ObjectRef, TestHashObj);
};
class TestCustomHashObj : public Object {
public:
int64_t key;
String label;
TestCustomHashObj() = default;
TestCustomHashObj(int64_t key, String label) : key(key), label(std::move(label)) {}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestCustomHash", TestCustomHashObj, Object);
};
class TestCustomHash : public ObjectRef {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TestCustomHash, ObjectRef, TestCustomHashObj);
};
class TestCustomCompareObj : public Object {
public:
int64_t key;
String label;
TestCustomCompareObj() = default;
TestCustomCompareObj(int64_t key, String label) : key(key), label(std::move(label)) {}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestCustomCompare", TestCustomCompareObj, Object);
};
class TestCustomCompare : public ObjectRef {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TestCustomCompare, ObjectRef, TestCustomCompareObj);
};
// Test object with __ffi_eq__ but deliberately no __ffi_hash__.
// Used to verify the RecursiveHash guard that rejects eq-without-hash types.
class TestEqWithoutHashObj : public Object {
public:
int64_t key;
String label;
TestEqWithoutHashObj() = default;
TestEqWithoutHashObj(int64_t key, String label) : key(key), label(std::move(label)) {}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("testing.TestEqWithoutHash", TestEqWithoutHashObj, Object);
};
class TestEqWithoutHash : public ObjectRef {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TestEqWithoutHash, ObjectRef, TestEqWithoutHashObj);
};
// NOLINTNEXTLINE(performance-unnecessary-value-param)
TVM_FFI_NO_INLINE void TestRaiseError(String kind, String msg) {
// keep name and no liner for testing backtrace
throw ffi::Error(kind, msg, TVMFFIBacktrace(__FILE__, __LINE__, TVM_FFI_FUNC_SIG, 0));
}
TVM_FFI_NO_INLINE void TestApply(PackedArgs args, Any* ret) {
// keep name and no liner for testing backtrace
auto f = args[0].cast<Function>();
f.CallPacked(args.Slice(1), ret);
}
// NOLINTNEXTLINE(bugprone-reserved-identifier)
int __add_one_c_symbol(void*, const TVMFFIAny* args, int32_t num_args, TVMFFIAny* ret) {
TVM_FFI_SAFE_CALL_BEGIN();
int x = reinterpret_cast<const AnyView*>(args)[0].cast<int>();
reinterpret_cast<Any*>(ret)[0] = x + 1;
TVM_FFI_SAFE_CALL_END();
}
void _mlir_add_one_c_symbol(void** packed_args) {
void* handle = *reinterpret_cast<void**>(packed_args[0]);
const TVMFFIAny* args = *reinterpret_cast<const TVMFFIAny**>(packed_args[1]);
int32_t num_args = *reinterpret_cast<int32_t*>(packed_args[2]);
TVMFFIAny* rv = *reinterpret_cast<TVMFFIAny**>(packed_args[3]);
int* ret_code = reinterpret_cast<int*>(packed_args[4]);
*ret_code = __add_one_c_symbol(handle, args, num_args, rv);
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<TestObjectBase>()
.def_rw("v_i64", &TestObjectBase::v_i64, refl::default_value(10), "i64 field")
.def_ro("v_f64", &TestObjectBase::v_f64, refl::default_value(10.0))
.def_rw("v_str", &TestObjectBase::v_str, refl::default_value("hello"))
.def("add_i64", &TestObjectBase::AddI64, "add_i64 method");
refl::ObjectDef<TestObjectDerived>()
.def_rw("v_map", &TestObjectDerived::v_map)
.def_rw("v_array", &TestObjectDerived::v_array);
refl::ObjectDef<TestCxxClassBase>()
.def_rw("v_i64", &TestCxxClassBase::v_i64, refl::repr(false))
.def_rw("v_i32", &TestCxxClassBase::v_i32, refl::repr(false));
refl::ObjectDef<TestCxxClassDerived>()
.def_rw("v_f64", &TestCxxClassDerived::v_f64)
.def_rw("v_f32", &TestCxxClassDerived::v_f32, refl::default_value(float{8.0f}));
refl::ObjectDef<TestCxxClassDerivedDerived>()
.def_rw("v_str", &TestCxxClassDerivedDerived::v_str, refl::default_value(String("default")))
.def_rw("v_bool", &TestCxxClassDerivedDerived::v_bool);
refl::ObjectDef<TestCxxInitSubsetObj>()
.def_rw("required_field", &TestCxxInitSubsetObj::required_field)
.def_rw("optional_field", &TestCxxInitSubsetObj::optional_field, refl::init(false),
refl::default_value(int64_t{-1}))
.def_rw("note", &TestCxxInitSubsetObj::note, refl::init(false),
refl::default_value(String("default")));
refl::ObjectDef<TestCxxKwOnly>()
.def_rw("x", &TestCxxKwOnly::x, refl::kw_only(true))
.def_rw("y", &TestCxxKwOnly::y, refl::kw_only(true))
.def_rw("z", &TestCxxKwOnly::z, refl::kw_only(true))
.def_rw("w", &TestCxxKwOnly::w, refl::kw_only(true), refl::default_value(int64_t{100}));
// No refl::init<>() — auto-generates __ffi_init__ in ObjectDef destructor.
refl::ObjectDef<TestCxxAutoInitObj>()
.def_rw("a", &TestCxxAutoInitObj::a)
.def_rw("b", &TestCxxAutoInitObj::b, refl::init(false), refl::default_value(int64_t{42}))
.def_rw("c", &TestCxxAutoInitObj::c, refl::kw_only(true))
.def_rw("d", &TestCxxAutoInitObj::d, refl::default_value(int64_t{99}));
refl::ObjectDef<TestCxxAutoInitSimpleObj>()
.def_rw("x", &TestCxxAutoInitSimpleObj::x)
.def_rw("y", &TestCxxAutoInitSimpleObj::y);
refl::ObjectDef<TestCxxAutoInitAllInitOffObj>()
.def_rw("x", &TestCxxAutoInitAllInitOffObj::x, refl::init(false),
refl::default_value(int64_t{7}))
.def_rw("y", &TestCxxAutoInitAllInitOffObj::y, refl::init(false),
refl::default_value(int64_t{9}))
.def_rw("z", &TestCxxAutoInitAllInitOffObj::z, refl::init(false));
refl::ObjectDef<TestCxxAutoInitKwOnlyDefaultsObj>()
.def_rw("p_required", &TestCxxAutoInitKwOnlyDefaultsObj::p_required)
.def_rw("p_default", &TestCxxAutoInitKwOnlyDefaultsObj::p_default,
refl::default_value(int64_t{11}))
.def_rw("k_required", &TestCxxAutoInitKwOnlyDefaultsObj::k_required, refl::kw_only(true))
.def_rw("k_default", &TestCxxAutoInitKwOnlyDefaultsObj::k_default, refl::kw_only(true),
refl::default_value(int64_t{22}))
.def_rw("hidden", &TestCxxAutoInitKwOnlyDefaultsObj::hidden, refl::init(false),
refl::default_value(int64_t{33}));
refl::ObjectDef<TestCxxAutoInitParentObj>()
.def_rw("parent_required", &TestCxxAutoInitParentObj::parent_required)
.def_rw("parent_default", &TestCxxAutoInitParentObj::parent_default,
refl::default_value(int64_t{5}));
refl::ObjectDef<TestCxxAutoInitChildObj>()
.def_rw("child_required", &TestCxxAutoInitChildObj::child_required)
.def_rw("child_kw_only", &TestCxxAutoInitChildObj::child_kw_only, refl::kw_only(true));
// init(false) at class level: has fields, has creator, but no __ffi_init__.
refl::ObjectDef<TestCxxNoAutoInitObj>(refl::init(false))
.def_rw("x", &TestCxxNoAutoInitObj::x)
.def_rw("y", &TestCxxNoAutoInitObj::y);
refl::ObjectDef<TestDeepCopyEdgesObj>()
.def_rw("v_any", &TestDeepCopyEdgesObj::v_any)
.def_rw("v_obj", &TestDeepCopyEdgesObj::v_obj);
refl::ObjectDef<TestNonCopyable>()
.def(refl::init<int64_t>())
.def_ro("value", &TestNonCopyable::value);
refl::ObjectDef<TestUnregisteredBaseObject>()
.def(refl::init<int64_t>(), "Constructor of TestUnregisteredBaseObject")
.def_ro("v1", &TestUnregisteredBaseObject::v1)
.def("get_v1_plus_one", &TestUnregisteredBaseObject::GetV1PlusOne,
"Get (v1 + 1) from TestUnregisteredBaseObject");
refl::ObjectDef<TestUnregisteredObject>()
.def(refl::init<int64_t, int64_t>(), "Constructor of TestUnregisteredObject")
.def_ro("v2", &TestUnregisteredObject::v2)
.def("get_v2_plus_two", &TestUnregisteredObject::GetV2PlusTwo,
"Get (v2 + 2) from TestUnregisteredObject");
refl::ObjectDef<TestCompareObj>()
.def(refl::init<int64_t, String, int64_t>())
.def_ro("key", &TestCompareObj::key)
.def_ro("name", &TestCompareObj::name)
.def_ro("ignored_field", &TestCompareObj::ignored_field, refl::compare(false));
refl::ObjectDef<TestHashObj>()
.def(refl::init<int64_t, String, int64_t>())
.def_ro("key", &TestHashObj::key)
.def_ro("name", &TestHashObj::name)
.def_ro("hash_ignored", &TestHashObj::hash_ignored, refl::hash(false));
refl::ObjectDef<TestCustomHashObj>()
.def(refl::init<int64_t, String>())
.def_ro("key", &TestCustomHashObj::key)
.def_ro("label", &TestCustomHashObj::label);
refl::ObjectDef<TestCustomCompareObj>()
.def(refl::init<int64_t, String>())
.def_ro("key", &TestCustomCompareObj::key)
.def_ro("label", &TestCustomCompareObj::label);
refl::ObjectDef<TestEqWithoutHashObj>()
.def(refl::init<int64_t, String>())
.def_ro("key", &TestEqWithoutHashObj::key)
.def_ro("label", &TestEqWithoutHashObj::label);
refl::GlobalDef()
.def("testing.test_raise_error", TestRaiseError)
.def("testing.add_one", [](int x) { return x + 1; })
.def_packed("testing.nop", [](PackedArgs args, Any* ret) {})
.def_packed("testing.echo", [](PackedArgs args, Any* ret) { *ret = args[0]; })
.def_packed("testing.apply", TestApply)
.def("testing.run_check_signal",
[](int nsec) {
for (int i = 0; i < nsec; ++i) {
if (TVMFFIEnvCheckSignals() != 0) {
throw ffi::EnvErrorAlreadySet();
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Function finished without catching signal" << std::endl;
})
.def("testing.object_use_count", [](const Object* obj) { return obj->use_count(); })
.def("testing.make_unregistered_object",
[]() { return ObjectRef(make_object<TestUnregisteredObject>(41, 42)); })
.def("testing.get_add_one_c_symbol",
[]() {
TVMFFISafeCallType symbol = __add_one_c_symbol;
// NOLINTNEXTLINE(bugprone-casting-through-void)
return reinterpret_cast<int64_t>(reinterpret_cast<void*>(symbol));
})
.def("testing.get_mlir_add_one_c_symbol",
[]() {
// NOLINTNEXTLINE(bugprone-casting-through-void)
return reinterpret_cast<int64_t>(reinterpret_cast<void*>(_mlir_add_one_c_symbol));
})
.def("testing.optional_tensor_view_has_value",
[](const Optional<TensorView>& t) { return t.has_value(); })
.def("testing.enum_variant_get",
[](const String& name) -> Enum { return EnumObj::Get<TestEnumVariantObj>(name); })
.def_method("testing.TestIntPairSum", &TestIntPair::Sum, "Get sum of the pair")
// Container-with-tensor test helpers for DLPack container conversion
// NOLINTBEGIN(performance-unnecessary-value-param)
.def("testing.make_array_with_tensor", [](Tensor t) -> Array<Any> { return {std::move(t)}; })
.def("testing.make_array_with_mixed",
[](Tensor t, int64_t x) -> Array<Any> { return {std::move(t), x, String("hello")}; })
.def("testing.make_nested_array_with_tensor",
[](const Tensor& t) -> Array<Any> {
Array<Any> inner{t, 42};
return {std::move(inner), t};
})
.def("testing.make_list_with_tensor",
[](Tensor t, int64_t x) -> List<Any> {
List<Any> result;
result.push_back(std::move(t));
result.push_back(x);
return result;
})
.def("testing.make_map_with_tensor",
[](Tensor t) -> Map<String, Any> {
Map<String, Any> result;
result.Set("tensor", std::move(t));
result.Set("value", 42);
result.Set("name", String("test"));
return result;
})
.def("testing.make_dict_with_tensor",
[](Tensor t) -> Dict<String, Any> {
Dict<String, Any> result;
result.Set("tensor", std::move(t));
result.Set("value", 42);
return result;
})
.def("testing.make_empty_array_with_tensor_input",
[](const Tensor& t) -> Array<Any> { return Array<Any>(); })
.def("testing.make_nested_map_with_tensor",
[](const Tensor& t1, Tensor t2) -> Map<String, Any> {
Array<Any> arr{t1, std::move(t2)};
Map<String, Any> inner;
inner.Set("t", t1);
Map<String, Any> result;
result.Set("array", std::move(arr));
result.Set("map", std::move(inner));
result.Set("scalar", 99);
return result;
});
// NOLINTEND(performance-unnecessary-value-param)
}
} // namespace ffi
} // namespace tvm
// -----------------------------------------------------------------------------
// Additional comprehensive schema coverage
// -----------------------------------------------------------------------------
namespace tvm {
namespace ffi {
// -----------------------------------------------------------------------------
// Implementation functions for schema testing
// -----------------------------------------------------------------------------
namespace schema_test_impl {
// Simple types
int64_t schema_id_int(int64_t x) { return x; }
double schema_id_float(double x) { return x; }
bool schema_id_bool(bool x) { return x; }
DLDevice schema_id_device(DLDevice d) { return d; }
DLDataType schema_id_dtype(DLDataType dt) { return dt; }
String schema_id_string(String s) { return s; }
Bytes schema_id_bytes(Bytes b) { return b; }
Function schema_id_func(Function f) { return f; }
TypedFunction<void(int64_t, float, Function)> schema_id_func_typed(
TypedFunction<void(int64_t, float, Function)> f) {
return f;
}
Any schema_id_any(Any a) { return a; }
ObjectRef schema_id_object(ObjectRef o) { return o; }
DLTensor* schema_id_dltensor(DLTensor* t) { return t; }
Tensor schema_id_tensor(Tensor t) { return t; }
void schema_tensor_view_input(TensorView t) {}
// Optional types
Optional<int64_t> schema_id_opt_int(Optional<int64_t> o) { return o; }
Optional<String> schema_id_opt_str(Optional<String> o) { return o; }
Optional<ObjectRef> schema_id_opt_obj(Optional<ObjectRef> o) { return o; }
// Array types
Array<int64_t> schema_id_arr_int(Array<int64_t> arr) { return arr; }
Array<String> schema_id_arr_str(Array<String> arr) { return arr; }
Array<ObjectRef> schema_id_arr_obj(Array<ObjectRef> arr) { return arr; }
const ArrayObj* schema_id_arr(const ArrayObj* arr) { return arr; }
// Map types
Map<String, int64_t> schema_id_map_str_int(Map<String, int64_t> m) { return m; }
Map<String, String> schema_id_map_str_str(Map<String, String> m) { return m; }
Map<String, ObjectRef> schema_id_map_str_obj(Map<String, ObjectRef> m) { return m; }
const MapObj* schema_id_map(const MapObj* m) { return m; }
// Variant types
Variant<int64_t, String> schema_id_variant_int_str(Variant<int64_t, String> v) { return v; }
Variant<int64_t, String, Array<int64_t>> schema_variant_mix(
Variant<int64_t, String, Array<int64_t>> v) {
return v;
}
// List types
List<int64_t> schema_id_list_int(List<int64_t> lst) { return lst; }
List<String> schema_id_list_str(List<String> lst) { return lst; }
List<ObjectRef> schema_id_list_obj(List<ObjectRef> lst) { return lst; }
// Dict types
Dict<String, int64_t> schema_id_dict_str_int(Dict<String, int64_t> d) { return d; }
Dict<String, String> schema_id_dict_str_str(Dict<String, String> d) { return d; }
// Complex nested types
Map<String, Array<int64_t>> schema_arr_map_opt(const Array<Optional<int64_t>>& arr,
Map<String, Array<int64_t>> mp,
const Optional<String>& os) {
// no-op combine
if (os.has_value()) {
Array<int64_t> extra;
for (const auto& i : arr) {
if (i.has_value()) extra.push_back(i.value());
}
mp.Set(os.value(), extra);
}
return mp;
}
// Edge cases
int64_t schema_no_args() { return 1; }
void schema_no_return(int64_t x) {}
void schema_no_args_no_return() {}
// Member function pattern
int64_t test_int_pair_sum_wrapper(const TestIntPair& target) { return target.Sum(); }
// Documentation export
int64_t test_add_with_docstring(int64_t a, int64_t b) { return a + b; }
} // namespace schema_test_impl
// A class with a wide variety of field types and method signatures
class SchemaAllTypesObj : public Object {
public:
// POD and builtin types
bool v_bool{true};
int64_t v_int;
double v_float;
DLDevice v_device;
DLDataType v_dtype;
// Atomic object types
String v_string;
Bytes v_bytes;
// Containers and combinations
Optional<int64_t> v_opt_int;
Optional<String> v_opt_str;
Array<int64_t> v_arr_int;
Array<String> v_arr_str;
Map<String, int64_t> v_map_str_int;
Map<String, Array<int64_t>> v_map_str_arr_int;
Variant<String, Array<int64_t>, Map<String, int64_t>> v_variant;
Optional<Array<Variant<int64_t, String>>> v_opt_arr_variant;
// Constructor used by refl::init in make_with
SchemaAllTypesObj(int64_t vi, double vf, String s) // NOLINT(*): explicit not necessary here
: v_int(vi),
v_float(vf),
v_device(TVMFFIDLDeviceFromIntPair(kDLCPU, 0)),
v_dtype(StringToDLDataType("float32")),
v_string(std::move(s)),
v_variant(String("v")) {}
// Some methods to exercise RegisterMethod
int64_t AddInt(int64_t x) const { return v_int + x; }
Array<int64_t> AppendInt(Array<int64_t> xs, int64_t y) const {
xs.push_back(y);
return xs;
}
Optional<String> MaybeConcat(Optional<String> a, Optional<String> b) const {
if (a.has_value() && b.has_value()) return String(a.value() + b.value());
if (a.has_value()) return a;
if (b.has_value()) return b;
return Optional<String>(std::nullopt);
}
Map<String, Array<int64_t>> MergeMap(Map<String, Array<int64_t>> lhs,
// NOLINTNEXTLINE(performance-unnecessary-value-param)
Map<String, Array<int64_t>> rhs) const {
for (const auto& kv : rhs) {
if (!lhs.count(kv.first)) {
lhs.Set(kv.first, kv.second);
} else {
Array<int64_t> arr = lhs[kv.first];
for (const auto& v : kv.second) arr.push_back(v);
lhs.Set(kv.first, arr);
}
}
return lhs;
}
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("testing.SchemaAllTypes", SchemaAllTypesObj, Object);
};
class SchemaAllTypes : public ObjectRef {
public:
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(SchemaAllTypes, ObjectRef, SchemaAllTypesObj);
};
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
// Register fields of various types (RegisterField usage)
refl::ObjectDef<SchemaAllTypesObj>()
.def_rw("v_bool", &SchemaAllTypesObj::v_bool, "bool field",
refl::Metadata{{"bool_attr", true}, //
{"int_attr", 1}, //
{"str_attr", "hello"}})
.def_rw("v_int", &SchemaAllTypesObj::v_int, refl::default_value(0), "int field")
.def_rw("v_float", &SchemaAllTypesObj::v_float, refl::default_value(0.0), "float field")
.def_rw("v_device", &SchemaAllTypesObj::v_device, "device field")
.def_rw("v_dtype", &SchemaAllTypesObj::v_dtype, "dtype field")
.def_rw("v_string", &SchemaAllTypesObj::v_string, refl::default_value("s"), "string field")
.def_rw("v_bytes", &SchemaAllTypesObj::v_bytes, "bytes field")
.def_rw("v_opt_int", &SchemaAllTypesObj::v_opt_int, "optional int")
.def_rw("v_opt_str", &SchemaAllTypesObj::v_opt_str, "optional str")
.def_rw("v_arr_int", &SchemaAllTypesObj::v_arr_int, "array<int>")
.def_rw("v_arr_str", &SchemaAllTypesObj::v_arr_str, "array<str>")
.def_rw("v_map_str_int", &SchemaAllTypesObj::v_map_str_int, "map<str,int>")
.def_rw("v_map_str_arr_int", &SchemaAllTypesObj::v_map_str_arr_int, "map<str,array<int>>")
.def_rw("v_variant", &SchemaAllTypesObj::v_variant, "variant<str,array<int>,map<str,int>>")
.def_rw("v_opt_arr_variant", &SchemaAllTypesObj::v_opt_arr_variant,
"optional<array<variant<int,str>>>")
// Register methods (RegisterMethod usage)
.def("add_int", &SchemaAllTypesObj::AddInt, "add int method",
refl::Metadata{{"bool_attr", true}, //
{"int_attr", 1}, //
{"str_attr", "hello"}})
.def("append_int", &SchemaAllTypesObj::AppendInt, "append int to array")
.def("maybe_concat", &SchemaAllTypesObj::MaybeConcat, "optional concat")
.def("merge_map", &SchemaAllTypesObj::MergeMap, "merge maps")
// Register static creator (also a static method)
.def_static(
"make_with",
[](int64_t vi, double vf, String s) {
return SchemaAllTypes(make_object<SchemaAllTypesObj>(vi, vf, std::move(s)));
},
"Constructor with subset of fields");
// Global typed functions to exercise RegisterFunc with various schemas
refl::GlobalDef()
.def("testing.schema_id_int", schema_test_impl::schema_id_int,
refl::Metadata{{"bool_attr", true}, //
{"int_attr", 1}, //
{"str_attr", "hello"}})
.def("testing.schema_id_float", schema_test_impl::schema_id_float)
.def("testing.schema_id_bool", schema_test_impl::schema_id_bool)
.def("testing.schema_id_device", schema_test_impl::schema_id_device)
.def("testing.schema_id_dtype", schema_test_impl::schema_id_dtype)
.def("testing.schema_id_string", schema_test_impl::schema_id_string)
.def("testing.schema_id_bytes", schema_test_impl::schema_id_bytes)
.def("testing.schema_id_func", schema_test_impl::schema_id_func)
.def("testing.schema_id_func_typed", schema_test_impl::schema_id_func_typed)
.def("testing.schema_id_any", schema_test_impl::schema_id_any)
.def("testing.schema_id_object", schema_test_impl::schema_id_object)
.def("testing.schema_id_dltensor", schema_test_impl::schema_id_dltensor)
.def("testing.schema_id_tensor", schema_test_impl::schema_id_tensor)
.def("testing.schema_tensor_view_input", schema_test_impl::schema_tensor_view_input)
.def("testing.schema_id_opt_int", schema_test_impl::schema_id_opt_int)
.def("testing.schema_id_opt_str", schema_test_impl::schema_id_opt_str)
.def("testing.schema_id_opt_obj", schema_test_impl::schema_id_opt_obj)
.def("testing.schema_id_arr_int", schema_test_impl::schema_id_arr_int)
.def("testing.schema_id_arr_str", schema_test_impl::schema_id_arr_str)
.def("testing.schema_id_arr_obj", schema_test_impl::schema_id_arr_obj)
.def("testing.schema_id_arr", schema_test_impl::schema_id_arr)
.def("testing.schema_id_list_int", schema_test_impl::schema_id_list_int)
.def("testing.schema_id_list_str", schema_test_impl::schema_id_list_str)
.def("testing.schema_id_list_obj", schema_test_impl::schema_id_list_obj)
.def("testing.schema_id_map_str_int", schema_test_impl::schema_id_map_str_int)
.def("testing.schema_id_map_str_str", schema_test_impl::schema_id_map_str_str)
.def("testing.schema_id_map_str_obj", schema_test_impl::schema_id_map_str_obj)
.def("testing.schema_id_map", schema_test_impl::schema_id_map)
.def("testing.schema_id_dict_str_int", schema_test_impl::schema_id_dict_str_int)
.def("testing.schema_id_dict_str_str", schema_test_impl::schema_id_dict_str_str)
.def("testing.schema_id_variant_int_str", schema_test_impl::schema_id_variant_int_str)
.def_packed("testing.schema_packed", [](PackedArgs args, Any* ret) {})
.def("testing.schema_arr_map_opt", schema_test_impl::schema_arr_map_opt)
.def("testing.schema_variant_mix", schema_test_impl::schema_variant_mix,
"variant passthrough")
.def("testing.schema_no_args", schema_test_impl::schema_no_args)
.def("testing.schema_no_return", schema_test_impl::schema_no_return)
.def("testing.schema_no_args_no_return", schema_test_impl::schema_no_args_no_return);
TVMFFIEnvModRegisterSystemLibSymbol("__tvm_ffi_testing.add_one",
reinterpret_cast<void*>(__add_one_c_symbol));
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
// Register __ffi_hash__ for TestCustomHash: only hashes `key`, ignores `label`.
refl::TypeAttrDef<TestCustomHashObj>().def(
refl::type_attr::kHash, [](const Object* self, const Function& fn_hash) -> int64_t {
auto* obj = static_cast<const TestCustomHashObj*>(self);
return fn_hash(AnyView(obj->key)).cast<int64_t>();
});
// Register __ffi_hash__ for TestCustomCompare: only hashes `key`, consistent with eq/compare.
refl::TypeAttrDef<TestCustomCompareObj>().def(
refl::type_attr::kHash, [](const Object* self, const Function& fn_hash) -> int64_t {
auto* obj = static_cast<const TestCustomCompareObj*>(self);
return fn_hash(AnyView(obj->key)).cast<int64_t>();
});
// Register __ffi_eq__ for TestCustomCompare: compares only `key`.
refl::TypeAttrDef<TestCustomCompareObj>().def(
refl::type_attr::kEq,
[](const Object* lhs, const Object* rhs, const Function& fn_eq) -> bool {
auto* a = static_cast<const TestCustomCompareObj*>(lhs);
auto* b = static_cast<const TestCustomCompareObj*>(rhs);
return fn_eq(AnyView(a->key), AnyView(b->key)).cast<bool>();
});
// Register __ffi_compare__ for TestCustomCompare: three-way ordering on `key`.
refl::TypeAttrDef<TestCustomCompareObj>().def(
refl::type_attr::kCompare,
[](const Object* lhs, const Object* rhs, const Function& fn_cmp) -> int32_t {
auto* a = static_cast<const TestCustomCompareObj*>(lhs);
auto* b = static_cast<const TestCustomCompareObj*>(rhs);
return fn_cmp(AnyView(a->key), AnyView(b->key)).cast<int32_t>();
});
// Register __ffi_eq__ for TestEqWithoutHash: deliberately no __ffi_hash__.
// This exercises the RecursiveHash guard that rejects eq-without-hash types.
refl::TypeAttrDef<TestEqWithoutHashObj>().def(
refl::type_attr::kEq,
[](const Object* lhs, const Object* rhs, const Function& fn_eq) -> bool {
auto* a = static_cast<const TestEqWithoutHashObj*>(lhs);
auto* b = static_cast<const TestEqWithoutHashObj*>(rhs);
return fn_eq(AnyView(a->key), AnyView(b->key)).cast<bool>();
});
}
} // namespace ffi
} // namespace tvm
// -----------------------------------------------------------------------------
// Exported symbols for metadata testing on DLL-exported functions
// -----------------------------------------------------------------------------
// We keep minimal DLL exports here to verify the export mechanism.
TVM_FFI_DLL_EXPORT_TYPED_FUNC(testing_dll_schema_id_int, tvm::ffi::schema_test_impl::schema_id_int)
// Documentation export
TVM_FFI_DLL_EXPORT_TYPED_FUNC(testing_dll_test_add_with_docstring,
tvm::ffi::schema_test_impl::test_add_with_docstring);
TVM_FFI_DLL_EXPORT_TYPED_FUNC_DOC(testing_dll_test_add_with_docstring,
R"(Add two integers and return the sum.
Parameters
----------
a : int
First integer
b : int
Second integer
Returns
-------
result : int
Sum of a and b)");
extern "C" TVM_FFI_DLL_EXPORT int TVMFFITestingDummyTarget() { return 0; }