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 894419b0 fix(reflection): require exact field and method name matches
(#674)
894419b0 is described below
commit 894419b0021b284d25883519c21e4faadfe8389c
Author: Kathryn (Jinqi) Chen <[email protected]>
AuthorDate: Sat Jul 18 00:08:09 2026 -0700
fix(reflection): require exact field and method name matches (#674)
GetFieldInfo and GetMethodInfo previously compared only the stored name
length, allowing longer queries with the same prefix to resolve to the
wrong member. This PR fixes the bug.
---
include/tvm/ffi/reflection/accessor.h | 6 ++++--
tests/cpp/test_reflection.cc | 23 +++++++++++++++++++++++
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/include/tvm/ffi/reflection/accessor.h
b/include/tvm/ffi/reflection/accessor.h
index 9ea14b4d..daa0f26d 100644
--- a/include/tvm/ffi/reflection/accessor.h
+++ b/include/tvm/ffi/reflection/accessor.h
@@ -43,7 +43,8 @@ inline const TVMFFIFieldInfo* GetFieldInfo(std::string_view
type_key, const char
TVM_FFI_CHECK_SAFE_CALL(TVMFFITypeKeyToIndex(&type_key_array, &type_index));
const TypeInfo* info = TVMFFIGetTypeInfo(type_index);
for (int32_t i = 0; i < info->num_fields; ++i) {
- if (std::strncmp(info->fields[i].name.data, field_name,
info->fields[i].name.size) == 0) {
+ if (std::strncmp(info->fields[i].name.data, field_name,
info->fields[i].name.size) == 0 &&
+ field_name[info->fields[i].name.size] == '\0') {
return &(info->fields[i]);
}
}
@@ -207,7 +208,8 @@ inline const TVMFFIMethodInfo*
GetMethodInfo(std::string_view type_key, const ch
TVM_FFI_CHECK_SAFE_CALL(TVMFFITypeKeyToIndex(&type_key_array, &type_index));
const TypeInfo* info = TVMFFIGetTypeInfo(type_index);
for (int32_t i = 0; i < info->num_methods; ++i) {
- if (std::strncmp(info->methods[i].name.data, method_name,
info->methods[i].name.size) == 0) {
+ if (std::strncmp(info->methods[i].name.data, method_name,
info->methods[i].name.size) == 0 &&
+ method_name[info->methods[i].name.size] == '\0') {
return &(info->methods[i]);
}
}
diff --git a/tests/cpp/test_reflection.cc b/tests/cpp/test_reflection.cc
index 711c9faf..89746fc6 100644
--- a/tests/cpp/test_reflection.cc
+++ b/tests/cpp/test_reflection.cc
@@ -62,6 +62,12 @@ struct TestObjRefADerived : public ObjectRef {
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(TestObjRefADerived, ObjectRef,
TestObjADerived);
};
+struct PrefixLookupObj : public Object {
+ int64_t stage;
+
+ TVM_FFI_DECLARE_OBJECT_INFO_FINAL("test.PrefixLookup", PrefixLookupObj,
Object);
+};
+
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
@@ -91,6 +97,9 @@ TVM_FFI_STATIC_INIT_BLOCK() {
refl::ObjectDef<TestObjADerived>()
.def(refl::init<int64_t, int64_t, int64_t>())
.def_ro("z", &TestObjADerived::z);
+ refl::ObjectDef<PrefixLookupObj>()
+ .def_ro("stage", &PrefixLookupObj::stage)
+ .def_static("run", []() -> int64_t { return 1; });
refl::TypeAttrDef<TestObjADerived>()
.def("test.attr.type_attr_def.literal", "derived-literal")
.def("test.attr.type_attr_def.string", String("derived-string"))
@@ -114,6 +123,13 @@ TEST(Reflection, FieldGetter) {
EXPECT_EQ(getter_float(b).cast<double>(), 10.0);
}
+TEST(Reflection, FieldLookupRequiresExactName) {
+ const TVMFFIFieldInfo* info =
reflection::GetFieldInfo(PrefixLookupObj::_type_key, "stage");
+ EXPECT_EQ(std::string_view(info->name.data, info->name.size), "stage");
+
+ EXPECT_THROW(reflection::GetFieldInfo(PrefixLookupObj::_type_key,
"stage_bytes"), Error);
+}
+
TEST(Reflection, FieldSetter) {
ObjectRef a = TFloat(10.0);
reflection::FieldSetter setter("test.Float", "value");
@@ -233,6 +249,13 @@ TEST(Reflection, MethodInfo) {
EXPECT_EQ(Bytes(info_float_sub->doc).operator std::string(), "");
}
+TEST(Reflection, MethodLookupRequiresExactName) {
+ const TVMFFIMethodInfo* info =
reflection::GetMethodInfo(PrefixLookupObj::_type_key, "run");
+ EXPECT_EQ(std::string_view(info->name.data, info->name.size), "run");
+
+ EXPECT_THROW(reflection::GetMethodInfo(PrefixLookupObj::_type_key,
"runner"), Error);
+}
+
TEST(Reflection, CallMethod) {
Function static_int_add = reflection::GetMethod("test.Int", "static_add");
EXPECT_EQ(static_int_add(TInt(1), TInt(2)).cast<TInt>()->value, 3);