diff --git a/BUILD b/BUILD index 18e0707f..ab58a025 100644 --- a/BUILD +++ b/BUILD @@ -56,6 +56,7 @@ cc_library( "//implementation:no_idx", "//implementation:params", "//implementation:promotion_mechanics", + "//implementation:promotion_mechanics_tags", "//implementation:return", "//implementation:selector_static_info", "//implementation:self", diff --git a/implementation/BUILD b/implementation/BUILD index 8858846f..aca5dee7 100644 --- a/implementation/BUILD +++ b/implementation/BUILD @@ -29,10 +29,13 @@ cc_library( ":array", ":array_view", ":class", + ":class_ref", ":default_class_loader", + ":forward_declarations", ":jni_type", ":local_object", ":object_ref", + ":promotion_mechanics_tags", ":ref_base", "//:jni_dep", "//implementation/jni_helper:jni_array_helper", @@ -292,7 +295,10 @@ cc_library( ":field_selection", ":id", ":id_type", + ":promotion_mechanics_tags", ":proxy", + ":proxy_convenience_aliases", + ":ref_base", "//:jni_dep", "//implementation/jni_helper", "//implementation/jni_helper:field_value_getter", @@ -526,6 +532,7 @@ cc_library( ":jvm", ":no_idx", ":object", + ":promotion_mechanics_tags", "//:jni_dep", "//implementation/jni_helper:jni_array_helper", "//implementation/jni_helper:lifecycle_object", @@ -594,10 +601,12 @@ cc_library( ":class", ":class_loader", ":class_loader_ref", + ":forward_declarations", ":jvm", ":jvm_ref", ":local_object", ":promotion_mechanics", + ":promotion_mechanics_tags", "//:jni_dep", "//class_defs:java_lang_classes", "//implementation/jni_helper:lifecycle_object", @@ -610,11 +619,13 @@ cc_library( deps = [ ":class", ":class_loader", + ":forward_declarations", ":jni_type", ":jvm", ":jvm_ref", ":object_ref", ":promotion_mechanics", + ":promotion_mechanics_tags", "//:jni_dep", "//implementation/jni_helper:lifecycle_object", ], @@ -635,8 +646,10 @@ cc_library( name = "local_string", hdrs = ["local_string.h"], deps = [ + ":forward_declarations", ":local_object", ":promotion_mechanics", + ":promotion_mechanics_tags", ":ref_base", ":string_ref", "//:jni_dep", @@ -663,6 +676,7 @@ cc_library( ":jni_type", ":method", ":params", + ":promotion_mechanics_tags", ":proxy", ":proxy_convenience_aliases", ":proxy_definitions", @@ -819,6 +833,7 @@ cc_library( ":forward_declarations", ":jni_type", ":object_ref", + ":promotion_mechanics_tags", ":ref_base", "//:jni_dep", "//implementation/jni_helper", @@ -829,6 +844,11 @@ cc_library( ], ) +cc_library( + name = "promotion_mechanics_tags", + hdrs = ["promotion_mechanics_tags.h"], +) + cc_library( name = "proxy", hdrs = ["proxy.h"], diff --git a/implementation/array_ref.h b/implementation/array_ref.h index 002fa778..5a03857a 100644 --- a/implementation/array_ref.h +++ b/implementation/array_ref.h @@ -21,13 +21,16 @@ #include "implementation/array.h" #include "implementation/array_view.h" #include "implementation/class.h" +#include "implementation/class_ref.h" #include "implementation/default_class_loader.h" +#include "implementation/forward_declarations.h" #include "implementation/jni_helper/jni_array_helper.h" #include "implementation/jni_helper/lifecycle.h" #include "implementation/jni_helper/lifecycle_object.h" #include "implementation/jni_type.h" #include "implementation/local_object.h" #include "implementation/object_ref.h" +#include "implementation/promotion_mechanics_tags.h" #include "implementation/ref_base.h" #include "jni_dep.h" @@ -48,7 +51,12 @@ class ArrayRef : public ScopedArrayImpl { using SpanType = typename JniT::SpanType; ArrayRef(std::size_t size) - : Base(JniArrayHelper::NewArray(size)) {} + : Base(AdoptLocal{}, + JniArrayHelper::NewArray(size)) {} + + template + ArrayRef(const ArrayViewHelper& array_view_helper) + : Base(AdoptLocal{}, array_view_helper.val) {} explicit ArrayRef(int size) : ArrayRef(static_cast(size)) {} @@ -79,17 +87,18 @@ class ArrayRefBase : public ScopedArrayImpl { // Construct array with given size and null values. explicit ArrayRefBase(std::size_t size) - : Base(JniArrayHelper::NewArray( - size, ClassRef_t::GetAndMaybeLoadClassRef(nullptr), - static_cast(nullptr))) {} + : Base(AdoptLocal{}, + JniArrayHelper::NewArray( + size, ClassRef_t::GetAndMaybeLoadClassRef(nullptr), + static_cast(nullptr))) {} // Construct from jobject lvalue (object is used as template). explicit ArrayRefBase(std::size_t size, jobject obj) - : Base(JniArrayHelper::NewArray( - size, - ClassRef_t::GetAndMaybeLoadClassRef( - static_cast(obj)), - static_cast(obj))) {} + : Base(AdoptLocal{}, JniArrayHelper::NewArray( + size, + ClassRef_t::GetAndMaybeLoadClassRef( + static_cast(obj)), + static_cast(obj))) {} // Object arrays cannot be efficiently pinned like primitive types can. ArrayView Pin() { @@ -108,8 +117,8 @@ class ArrayRefBase : public ScopedArrayImpl { void Set( std::size_t idx, LocalObject&& val) { - JniArrayHelper::SetArrayElement(Base::object_ref_, - idx, val.Release()); + AdoptLocal{}, JniArrayHelper::SetArrayElement( + Base::object_ref_, idx, val.Release()); } }; @@ -158,9 +167,11 @@ class ArrayRef 1)>> LocalArray Get(std::size_t idx) { - return {static_cast( - JniArrayHelper::GetArrayElement( - Base::object_ref_, idx))}; + return {AdoptLocal{}, + static_cast( + JniArrayHelper::GetArrayElement(Base::object_ref_, + idx))}; } template +struct ArrayViewHelper { + const T& val_; + operator T() const { return val_; } + + ArrayViewHelper(const T& val) : val_(val) {} +}; + // Primitive Rank 1 Arrays. template class ArrayView { @@ -145,12 +153,12 @@ class ArrayView< return tmp; } - PinHelper_t operator*() const { + ArrayViewHelper operator*() const { if constexpr (kRank >= 2) { - return static_cast( - JniArrayHelper::GetArrayElement(arr_, idx_)); + return {static_cast( + JniArrayHelper::GetArrayElement(arr_, idx_))}; } else { - return JniArrayHelper::GetArrayElement(arr_, idx_); + return {JniArrayHelper::GetArrayElement(arr_, idx_)}; } } diff --git a/implementation/array_view_test.cc b/implementation/array_view_test.cc index 5143fc7d..1346e2fb 100644 --- a/implementation/array_view_test.cc +++ b/implementation/array_view_test.cc @@ -23,6 +23,7 @@ namespace { +using ::jni::AdoptLocal; using ::jni::ArrayView; using ::jni::CDecl_t; using ::jni::Class; @@ -95,14 +96,14 @@ TEST_F(JniTest, ArrayView_GetsAndReleaseArrayBuffer) { Eq(Fake()), Eq(Fake()), 0)); - LocalArray boolean_array{Fake()}; - LocalArray byte_array{Fake()}; - LocalArray char_array{Fake()}; - LocalArray short_array{Fake()}; - LocalArray int_array{Fake()}; - LocalArray long_array{Fake()}; - LocalArray float_array{Fake()}; - LocalArray double_array{Fake()}; + LocalArray boolean_array{AdoptLocal{}, Fake()}; + LocalArray byte_array{AdoptLocal{}, Fake()}; + LocalArray char_array{AdoptLocal{}, Fake()}; + LocalArray short_array{AdoptLocal{}, Fake()}; + LocalArray int_array{AdoptLocal{}, Fake()}; + LocalArray long_array{AdoptLocal{}, Fake()}; + LocalArray float_array{AdoptLocal{}, Fake()}; + LocalArray double_array{AdoptLocal{}, Fake()}; ArrayView boolean_array_pin = {boolean_array.Pin()}; ArrayView byte_array_pin = {byte_array.Pin()}; @@ -121,7 +122,7 @@ TEST_F(JniTest, LocalArrayView_AllowsCTAD) { Eq(Fake()), Eq(Fake()), 0)); - LocalArray boolean_array{Fake()}; + LocalArray boolean_array{AdoptLocal{}, Fake()}; ArrayView ctad_array_view{boolean_array.Pin()}; // Despite supporting construction from xvalue, move ctor is deleted (good). @@ -300,7 +301,7 @@ TEST_F(JniTest, ArrayView_ShallowObjectsAreIterable) { .WillOnce(::testing::Return(Fake(2))) .WillOnce(::testing::Return(Fake(3))); - LocalArray obj_arr{Fake()}; + LocalArray obj_arr{AdoptLocal{}, Fake()}; ArrayView obj_view = obj_arr.Pin(); EXPECT_TRUE(std::equal(obj_view.begin(), obj_view.end(), fake_vals.begin(), @@ -318,7 +319,7 @@ TEST_F(JniTest, ArrayView_RichObjectsAreIterable) { .WillOnce(::testing::Return(Fake(2))) .WillOnce(::testing::Return(Fake(3))); - LocalArray obj_arr{Fake()}; + LocalArray obj_arr{AdoptLocal{}, Fake()}; auto obj_view = obj_arr.Pin(); // Note: GlobalObject will fail to compile here. This is good, the user @@ -350,7 +351,7 @@ TEST_F(JniTest, ArrayView_Rank2IntArraysAreIterable) { EXPECT_CALL(*env_, GetObjectArrayElement(Fake(), 2)) .WillOnce(::testing::Return(Fake(3))); - LocalArray int_arr_rank_2{Fake()}; + LocalArray int_arr_rank_2{AdoptLocal{}, Fake()}; ArrayView int_rank2_view = int_arr_rank_2.Pin(); EXPECT_TRUE(std::equal(int_rank2_view.begin(), int_rank2_view.end(), @@ -375,7 +376,7 @@ TEST_F(JniTest, ArrayView_Rank2ObjectkArraysAreIterable) { EXPECT_CALL(*env_, GetObjectArrayElement(Fake(), 2)) .WillOnce(::testing::Return(Fake(3))); - LocalArray int_arr_rank_2{Fake()}; + LocalArray int_arr_rank_2{AdoptLocal{}, Fake()}; ArrayView int_rank2_view = int_arr_rank_2.Pin(); EXPECT_TRUE(std::equal(int_rank2_view.begin(), int_rank2_view.end(), @@ -403,7 +404,7 @@ TEST_F(JniTest, ArrayView_Rank3IntArraysAreIterable) { EXPECT_CALL(*env_, GetObjectArrayElement(Fake(), 2)) .WillOnce(::testing::Return(Fake())); - LocalArray int_arr_rank_3{Fake()}; + LocalArray int_arr_rank_3{AdoptLocal{}, Fake()}; ArrayView int_rank_3_view = int_arr_rank_3.Pin(); EXPECT_TRUE(std::equal(int_rank_3_view.begin(), int_rank_3_view.end(), @@ -426,7 +427,7 @@ TEST_F(JniTest, ArrayView_Rank3ObjectkArraysAreIterable) { EXPECT_CALL(*env_, GetObjectArrayElement(Fake(0), 2)) .WillOnce(::testing::Return(Fake(3))); - LocalArray object_arr_rank_3{Fake(0)}; + LocalArray object_arr_rank_3{AdoptLocal{}, Fake(0)}; ArrayView object_rank_3_view = object_arr_rank_3.Pin(); EXPECT_TRUE(std::equal(object_rank_3_view.begin(), object_rank_3_view.end(), diff --git a/implementation/class_loader_ref_second_test.cc b/implementation/class_loader_ref_second_test.cc index b383f965..23044a7e 100644 --- a/implementation/class_loader_ref_second_test.cc +++ b/implementation/class_loader_ref_second_test.cc @@ -6,6 +6,7 @@ namespace { +using ::jni::AdoptLocal; using ::jni::Class; using ::jni::ClassLoader; using ::jni::Constructor; @@ -83,7 +84,7 @@ TEST_F(JniTestWithNoDefaultJvmRef, // Code under test. JvmRef jvm_ref{jvm_.get()}; LocalClassLoader class_loader{ - Fake(1)}; + AdoptLocal{}, Fake(1)}; auto custom_loader_object = class_loader.BuildLocalObject(jint{1}); diff --git a/implementation/class_loader_ref_test.cc b/implementation/class_loader_ref_test.cc index d7fb43d7..162df558 100644 --- a/implementation/class_loader_ref_test.cc +++ b/implementation/class_loader_ref_test.cc @@ -25,6 +25,7 @@ namespace { using ::jni::AdoptGlobal; +using ::jni::AdoptLocal; using ::jni::Class; using ::jni::ClassLoader; using ::jni::Constructor; @@ -157,7 +158,8 @@ TEST_F(JniTestForClassLoaders, ClassLoaderRefTest_DefaultLoadedObjectBuildsWithClassLoadedObject) { JvmRef jvm_ref{jvm_.get()}; - LocalClassLoader local_class_loader{Fake()}; + LocalClassLoader local_class_loader{AdoptLocal{}, + Fake()}; LocalObject a = local_class_loader.BuildLocalObject(); LocalObject b{a}; @@ -308,7 +310,7 @@ TEST_F(JniTestWithNoDefaultJvmRef, // Code under test. jni::JvmRef jvm_ref{jvm_.get()}; jni::LocalObject - obj1{Fake(1)}; + obj1{AdoptLocal{}, Fake(1)}; obj1("Foo"); this->TearDown(); diff --git a/implementation/field_ref.h b/implementation/field_ref.h index 22baa5de..24c9b9ff 100644 --- a/implementation/field_ref.h +++ b/implementation/field_ref.h @@ -29,7 +29,10 @@ #include "implementation/jni_helper/field_value.h" #include "implementation/jni_helper/jni_helper.h" #include "implementation/jni_helper/static_field_value.h" +#include "implementation/promotion_mechanics_tags.h" #include "implementation/proxy.h" +#include "implementation/proxy_convenience_aliases.h" +#include "implementation/ref_base.h" #include "jni_dep.h" #include "metaprogramming/double_locked_value.h" #include "metaprogramming/optional_wrap.h" @@ -91,9 +94,16 @@ class FieldRef { } ReturnProxied Get() { - return {FieldHelper, IdT::kRank, - IdT::kIsStatic>::GetValue(SelfVal(), - GetFieldID(class_ref_))}; + if constexpr (std::is_base_of_v) { + return {AdoptLocal{}, + FieldHelper, IdT::kRank, + IdT::kIsStatic>::GetValue(SelfVal(), + GetFieldID(class_ref_))}; + } else { + return {FieldHelper, IdT::kRank, + IdT::kIsStatic>::GetValue(SelfVal(), + GetFieldID(class_ref_))}; + } } template diff --git a/implementation/field_ref_test.cc b/implementation/field_ref_test.cc index d260b1f1..f788d506 100644 --- a/implementation/field_ref_test.cc +++ b/implementation/field_ref_test.cc @@ -24,6 +24,7 @@ namespace { +using ::jni::AdoptLocal; using ::jni::Class; using ::jni::Field; using ::jni::GlobalObject; @@ -159,9 +160,9 @@ TEST_F(JniTest, Field_ObjectSet) { Field{"classField", kClass2}}; LocalObject obj{}; - LocalObject some_obj{Fake()}; + LocalObject some_obj{AdoptLocal{}, Fake()}; obj["classField"].Set(Fake()); - obj["classField"].Set(LocalObject{Fake()}); + obj["classField"].Set(LocalObject{AdoptLocal{}, Fake()}); obj["classField"].Set(some_obj); obj["classField"].Set(std::move(some_obj)); } diff --git a/implementation/forward_declarations.h b/implementation/forward_declarations.h index 3c165f4e..581307d5 100644 --- a/implementation/forward_declarations.h +++ b/implementation/forward_declarations.h @@ -31,6 +31,10 @@ namespace jni { #define JNI_BIND_C_ENTRYPOINT(class_name, return_type, method_name, ...) \ return_type class_name_##method_name(JNIEnv*, jclass, ##__VA_ARGS__) +// ArrayView Helper. +template +struct ArrayViewHelper; + // Id. template diff --git a/implementation/global_object_test.cc b/implementation/global_object_test.cc index 6a63314c..8048606a 100644 --- a/implementation/global_object_test.cc +++ b/implementation/global_object_test.cc @@ -25,6 +25,7 @@ namespace { using ::jni::AdoptGlobal; +using ::jni::AdoptLocal; using ::jni::Class; using ::jni::Constructor; using ::jni::CreateCopy; @@ -95,7 +96,7 @@ TEST_F(JniTest, GlobalObject_PromotesDecoratedLocals) { EXPECT_CALL(*env_, DeleteGlobalRef(AsGlobal(Fake()))); static constexpr Class kClass{"kClass"}; - LocalObject local_obj{Fake()}; + LocalObject local_obj{AdoptLocal{}, Fake()}; // GlobalObject global_object{local_obj}; // doesn't compile (good). GlobalObject global_object{std::move(local_obj)}; @@ -109,7 +110,8 @@ TEST_F(JniTest, GlobalObject_PromotesDecoratedLocalsFromXValue) { EXPECT_CALL(*env_, DeleteGlobalRef(AsGlobal(Fake()))); static constexpr Class kClass{"kClass"}; - GlobalObject global_object{LocalObject{Fake()}}; + GlobalObject global_object{ + LocalObject{AdoptLocal{}, Fake()}}; EXPECT_EQ(jobject{global_object}, AsGlobal(Fake())); } diff --git a/implementation/local_array.h b/implementation/local_array.h index 707d16a9..0410ca52 100644 --- a/implementation/local_array.h +++ b/implementation/local_array.h @@ -32,6 +32,7 @@ #include "implementation/jvm.h" #include "implementation/no_idx.h" #include "implementation/object.h" +#include "implementation/promotion_mechanics_tags.h" #include "jni_dep.h" namespace jni { @@ -64,20 +65,25 @@ class LocalArray // RefTag ctor (supports multi-dimensions, `jobject` if rank > 1). LocalArray(std::size_t size, RefTag arr) - : Base(JniArrayHelper::NewArray( - size, - ClassRef_t::GetAndMaybeLoadClassRef( - static_cast(arr)), - arr)) {} + : Base(AdoptLocal{}, JniArrayHelper::NewArray( + size, + ClassRef_t::GetAndMaybeLoadClassRef( + static_cast(arr)), + arr)) {} + + template + LocalArray(ArrayViewHelper array_view_helper) + : LocalArray(AdoptLocal{}, array_view_helper.val_) {} // Rvalue ctor. - LocalArray(LocalArray&& rhs) : Base(rhs.Release()) {} + LocalArray(LocalArray&& rhs) + : Base(AdoptLocal{}, rhs.Release()) {} // Rvalue ctor. template LocalArray(LocalArray&& rhs) - : Base(rhs.Release()) { + : Base(AdoptLocal{}, rhs.Release()) { static_assert(std::is_same_v && kRank == kRank_ && class_v == class_v_ && class_loader_v == class_loader_v_); } diff --git a/implementation/local_array_field_test.cc b/implementation/local_array_field_test.cc index d0984f1d..6ead8955 100644 --- a/implementation/local_array_field_test.cc +++ b/implementation/local_array_field_test.cc @@ -21,6 +21,7 @@ namespace { +using ::jni::AdoptLocal; using ::jni::Array; using ::jni::Class; using ::jni::Field; @@ -68,7 +69,7 @@ TEST_F(JniTest, Array_FieldTests) { EXPECT_CALL(*env_, GetFieldID(_, StrEq("ObjectArrayRank3"), StrEq("[[[LkClass2;"))); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; obj["BooleanArray"].Get(); obj["ByteArray"].Get(); obj["CharArray"].Get(); @@ -100,13 +101,15 @@ TEST_F(JniTest, Array_Field_Boolean_Test) { ReleaseBooleanArrayElements(Fake(), fake_storage_ptr.get(), JNI_ABORT)); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr{obj["BooleanArray"].Get()}; - LocalArray arr2{Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; obj["BooleanArray"].Set(Fake()); - obj["BooleanArray"].Set(LocalArray{Fake()}); + obj["BooleanArray"].Set( + LocalArray{AdoptLocal{}, Fake()}); obj["BooleanArray"].Set(arr2); obj["BooleanArray"].Set(obj["BooleanArray"].Get()); + EXPECT_EQ(obj["BooleanArray"].Get().Pin().ptr(), fake_storage_ptr.get()); EXPECT_EQ(obj["BooleanArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); } @@ -129,11 +132,11 @@ TEST_F(JniTest, Array_Field_Byte_Test) { ReleaseByteArrayElements(Fake(), fake_storage_ptr.get(), JNI_ABORT)); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr{obj["ByteArray"].Get()}; - LocalArray arr2{Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; obj["ByteArray"].Set(Fake()); - obj["ByteArray"].Set(LocalArray{Fake()}); + obj["ByteArray"].Set(LocalArray{AdoptLocal{}, Fake()}); obj["ByteArray"].Set(arr2); obj["ByteArray"].Set(obj["ByteArray"].Get()); EXPECT_EQ(obj["ByteArray"].Get().Pin().ptr(), fake_storage_ptr.get()); @@ -158,11 +161,11 @@ TEST_F(JniTest, Array_Field_Char_Test) { ReleaseCharArrayElements(Fake(), fake_storage_ptr.get(), JNI_ABORT)); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr{obj["CharArray"].Get()}; - LocalArray arr2{Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; obj["CharArray"].Set(Fake()); - obj["CharArray"].Set(LocalArray{Fake()}); + obj["CharArray"].Set(LocalArray{AdoptLocal{}, Fake()}); obj["CharArray"].Set(arr2); obj["CharArray"].Set(obj["CharArray"].Get()); EXPECT_EQ(obj["CharArray"].Get().Pin().ptr(), fake_storage_ptr.get()); @@ -187,11 +190,11 @@ TEST_F(JniTest, Array_Field_Short_Test) { ReleaseShortArrayElements(Fake(), fake_storage_ptr.get(), JNI_ABORT)); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr{obj["ShortArray"].Get()}; - LocalArray arr2{Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; obj["ShortArray"].Set(Fake()); - obj["ShortArray"].Set(LocalArray{Fake()}); + obj["ShortArray"].Set(LocalArray{AdoptLocal{}, Fake()}); obj["ShortArray"].Set(arr2); obj["ShortArray"].Set(obj["ShortArray"].Get()); EXPECT_EQ(obj["ShortArray"].Get().Pin().ptr(), fake_storage_ptr.get()); @@ -215,11 +218,11 @@ TEST_F(JniTest, Array_Field_Int_Test) { EXPECT_CALL(*env_, ReleaseIntArrayElements( Fake(), fake_storage_ptr.get(), JNI_ABORT)); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr{obj["IntArray"].Get()}; - LocalArray arr2{Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; obj["IntArray"].Set(Fake()); - obj["IntArray"].Set(LocalArray{Fake()}); + obj["IntArray"].Set(LocalArray{AdoptLocal{}, Fake()}); obj["IntArray"].Set(arr2); obj["IntArray"].Set(obj["IntArray"].Get()); EXPECT_EQ(obj["IntArray"].Get().Pin().ptr(), fake_storage_ptr.get()); @@ -244,11 +247,11 @@ TEST_F(JniTest, Array_Field_Float_Test) { ReleaseFloatArrayElements(Fake(), fake_storage_ptr.get(), JNI_ABORT)); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr{obj["FloatArray"].Get()}; - LocalArray arr2{Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; obj["FloatArray"].Set(Fake()); - obj["FloatArray"].Set(LocalArray{Fake()}); + obj["FloatArray"].Set(LocalArray{AdoptLocal{}, Fake()}); obj["FloatArray"].Set(arr2); obj["FloatArray"].Set(obj["FloatArray"].Get()); EXPECT_EQ(obj["FloatArray"].Get().Pin().ptr(), fake_storage_ptr.get()); @@ -273,11 +276,12 @@ TEST_F(JniTest, Array_Field_Double_Test) { ReleaseDoubleArrayElements(Fake(), fake_storage_ptr.get(), JNI_ABORT)); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr{obj["DoubleArray"].Get()}; - LocalArray arr2{Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; obj["DoubleArray"].Set(Fake()); - obj["DoubleArray"].Set(LocalArray{Fake()}); + obj["DoubleArray"].Set( + LocalArray{AdoptLocal{}, Fake()}); obj["DoubleArray"].Set(arr2); obj["DoubleArray"].Set(obj["DoubleArray"].Get()); EXPECT_EQ(obj["DoubleArray"].Get().Pin().ptr(), fake_storage_ptr.get()); @@ -302,11 +306,11 @@ TEST_F(JniTest, Array_Field_Long_Test) { ReleaseLongArrayElements(Fake(), fake_storage_ptr.get(), JNI_ABORT)); - LocalObject obj{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr{obj["LongArray"].Get()}; - LocalArray arr2{Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; obj["LongArray"].Set(Fake()); - obj["LongArray"].Set(LocalArray{Fake()}); + obj["LongArray"].Set(LocalArray{AdoptLocal{}, Fake()}); obj["LongArray"].Set(arr2); obj["LongArray"].Set(obj["LongArray"].Get()); EXPECT_EQ(obj["LongArray"].Get().Pin().ptr(), fake_storage_ptr.get()); @@ -324,12 +328,12 @@ TEST_F(JniTest, Array_Field_Object_Test) { .Times(4); EXPECT_CALL(*env_, GetObjectArrayElement(Fake(), 2)); - LocalObject obj{Fake()}; - LocalArray arr2{Fake()}; + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; LocalArray arr{obj["ObjectArrayRank1"].Get()}; obj["ObjectArrayRank1"].Set(Fake()); obj["ObjectArrayRank1"].Set( - LocalArray{Fake()}); + LocalArray{AdoptLocal{}, Fake()}); obj["ObjectArrayRank1"].Set(arr2); obj["ObjectArrayRank1"].Set(obj["ObjectArrayRank1"].Get()); obj["ObjectArrayRank1"].Get().Get(2); diff --git a/implementation/local_array_multidimensional_test.cc b/implementation/local_array_multidimensional_test.cc index c420012a..7c386f23 100644 --- a/implementation/local_array_multidimensional_test.cc +++ b/implementation/local_array_multidimensional_test.cc @@ -19,6 +19,7 @@ namespace { +using ::jni::AdoptLocal; using ::jni::ArrayView; using ::jni::Class; using ::jni::LocalArray; @@ -44,13 +45,14 @@ TEST_F(JniTest, Array_BuildsFromSizeForMultiDimensionalArray_primitive_xref) { EXPECT_CALL(*env_, FindClass(StrEq("[I"))); EXPECT_CALL(*env_, NewObjectArray(10, _, Fake())); - LocalArray{std::size_t{10}, LocalArray{Fake()}}; + LocalArray{std::size_t{10}, + LocalArray{AdoptLocal{}, Fake()}}; } TEST_F(JniTest, Array_BuildsFromSizeForMultiDimensionalArray_primitive_lvalue) { EXPECT_CALL(*env_, NewObjectArray(10, _, Fake())); - LocalArray arr{Fake()}; + LocalArray arr{AdoptLocal{}, Fake()}; LocalArray{std::size_t{10}, arr}; } @@ -82,7 +84,7 @@ TEST_F(JniTest, Array_SetsIntValues) { EXPECT_CALL( *env_, SetObjectArrayElement(Fake(), 2, Fake())); - LocalArray array_arg{Fake()}; + LocalArray array_arg{AdoptLocal{}, Fake()}; LocalArray arr{std::size_t{10}, Fake()}; arr.Set(0, array_arg); arr.Set(1, array_arg); @@ -97,8 +99,8 @@ TEST_F(JniTest, Array_SetsObjectValues) { EXPECT_CALL(*env_, SetObjectArrayElement(Fake(1), 2, Fake(2))); - LocalArray array_arg{Fake(2)}; - LocalArray arr{Fake(1)}; + LocalArray array_arg{AdoptLocal{}, Fake(2)}; + LocalArray arr{AdoptLocal{}, Fake(1)}; arr.Set(0, array_arg); arr.Set(1, array_arg); arr.Set(2, std::move(array_arg)); @@ -114,7 +116,7 @@ TEST_F(JniTest, Array_IteratesOver1DRange) { EXPECT_CALL(*env_, GetIntArrayElements) .WillRepeatedly(Return(expected.data())); - LocalArray new_array{Fake()}; + LocalArray new_array{AdoptLocal{}, Fake()}; int i{0}; for (jint val : new_array.Pin()) { @@ -131,7 +133,7 @@ TEST_F(JniTest, Array_WorksWithSTLComparison) { EXPECT_CALL(*env_, GetIntArrayElements) .WillRepeatedly(Return(expected.data())); - LocalArray new_array{Fake()}; + LocalArray new_array{AdoptLocal{}, Fake()}; ArrayView array_view = new_array.Pin(); EXPECT_TRUE( std::equal(array_view.begin(), array_view.end(), expected.begin())); @@ -153,9 +155,9 @@ TEST_F(JniTest, Array_WorksWithSTLComparisonOfObjects) { } TEST_F(JniTest, Array_WorksWithSTLComparisonOfRichlyDecoratedObjects) { - std::array expected{LocalObject{Fake(1)}, - LocalObject{Fake(2)}, - LocalObject{Fake(3)}}; + std::array expected{LocalObject{AdoptLocal{}, Fake(1)}, + LocalObject{AdoptLocal{}, Fake(2)}, + LocalObject{AdoptLocal{}, Fake(3)}}; EXPECT_CALL(*env_, GetArrayLength).WillOnce(Return(3)); EXPECT_CALL(*env_, GetObjectArrayElement) @@ -163,7 +165,7 @@ TEST_F(JniTest, Array_WorksWithSTLComparisonOfRichlyDecoratedObjects) { .WillOnce(Return(Fake(2))) .WillOnce(Return(Fake(3))); - LocalArray new_array{Fake()}; + LocalArray new_array{AdoptLocal{}, Fake()}; ArrayView array_view = new_array.Pin(); EXPECT_TRUE( std::equal(array_view.begin(), array_view.end(), expected.begin())); @@ -250,12 +252,12 @@ TEST_F(JniTest, Array_2D_Iterates_Raw_loops) { .WillOnce(Return(Fake(5))); // All the returned intArrays are deleted. - EXPECT_CALL(*env_, DeleteLocalRef(Fake())); EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))); EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); EXPECT_CALL(*env_, DeleteLocalRef(Fake(3))); EXPECT_CALL(*env_, DeleteLocalRef(Fake(4))); EXPECT_CALL(*env_, DeleteLocalRef(Fake(5))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); EXPECT_CALL(*env_, DeleteLocalRef(Fake())); // 5 (outer array length) * 2 (pins per) = 10 diff --git a/implementation/local_array_test.cc b/implementation/local_array_test.cc index 906cc4d2..bb0a2de8 100644 --- a/implementation/local_array_test.cc +++ b/implementation/local_array_test.cc @@ -22,6 +22,7 @@ namespace { using ::jni::AdoptGlobal; +using ::jni::AdoptLocal; using ::jni::Array; using ::jni::CDecl_t; using ::jni::Class; @@ -33,6 +34,7 @@ using ::jni::LocalObject; using ::jni::LocalString; using ::jni::Method; using ::jni::Params; +using ::jni::test::AsNewLocalReference; using ::jni::test::Fake; using ::jni::test::JniTest; using ::testing::_; @@ -52,8 +54,14 @@ TEST_F(JniTest, LocalArray_BuildsAndDestroys) { } TEST_F(JniTest, LocalArray_IsImplicitlyConvertibleToSpanType) { + EXPECT_EQ( + static_cast(LocalArray{AdoptLocal{}, Fake()}), + Fake()); +} + +TEST_F(JniTest, LocalArray_MakesNewReferenceByDefault) { EXPECT_EQ(static_cast(LocalArray{Fake()}), - Fake()); + AsNewLocalReference(Fake())); } TEST_F(JniTest, LocalArray_ConstructsIntArrayWithCorrectSize) { @@ -104,7 +112,7 @@ TEST_F(JniTest, LocalArray_ConstructsObjectsForLValues) { // object. This makes for a slightly different API then other objects. EXPECT_CALL(*env_, NewObjectArray(5, _, Fake())); - LocalObject default_object{Fake()}; + LocalObject default_object{AdoptLocal{}, Fake()}; LocalArray local_object_array{5, default_object}; } diff --git a/implementation/local_class_loader.h b/implementation/local_class_loader.h index 72b4d3ef..7bdd4153 100644 --- a/implementation/local_class_loader.h +++ b/implementation/local_class_loader.h @@ -21,11 +21,13 @@ #include "implementation/class.h" #include "implementation/class_loader.h" #include "implementation/class_loader_ref.h" +#include "implementation/forward_declarations.h" #include "implementation/jni_helper/lifecycle_object.h" #include "implementation/jvm.h" #include "implementation/jvm_ref.h" #include "implementation/local_object.h" #include "implementation/promotion_mechanics.h" +#include "implementation/promotion_mechanics_tags.h" #include "jni_dep.h" namespace jni { @@ -39,7 +41,7 @@ class LocalClassLoader template explicit LocalClassLoader(LocalClassLoader&& lhs) - : LocalClassLoader(lhs.Release()) {} + : LocalClassLoader(AdoptLocal{}, lhs.Release()) {} private: template diff --git a/implementation/local_object.h b/implementation/local_object.h index c8f357bd..79af3368 100644 --- a/implementation/local_object.h +++ b/implementation/local_object.h @@ -20,12 +20,14 @@ #include "implementation/class.h" #include "implementation/class_loader.h" +#include "implementation/forward_declarations.h" #include "implementation/jni_helper/lifecycle_object.h" #include "implementation/jni_type.h" #include "implementation/jvm.h" #include "implementation/jvm_ref.h" #include "implementation/object_ref.h" #include "implementation/promotion_mechanics.h" +#include "implementation/promotion_mechanics_tags.h" #include "jni_dep.h" namespace jni { @@ -43,9 +45,13 @@ class LocalObject : public LocalObjectImpl { using Base = LocalObjectImpl; using Base::Base; + template + LocalObject(ArrayViewHelper array_view_helper) + : LocalObject(AdoptLocal{}, array_view_helper.val_) {} + template LocalObject(LocalObject&& obj) - : Base(obj.Release()) {} + : Base(AdoptLocal{}, obj.Release()) {} }; template diff --git a/implementation/local_object_test.cc b/implementation/local_object_test.cc index bf01bcd0..dadc2cd3 100644 --- a/implementation/local_object_test.cc +++ b/implementation/local_object_test.cc @@ -34,6 +34,7 @@ using ::jni::kDefaultJvm; using ::jni::LocalObject; using ::jni::Method; using ::jni::Params; +using ::jni::test::AsNewLocalReference; using ::jni::test::Fake; using ::jni::test::JniTest; using ::testing::_; @@ -43,6 +44,14 @@ using ::testing::StrEq; static constexpr Class kClass{"kClass"}; static constexpr Class kClass2{"kClass2"}; +TEST_F(JniTest, LocalObject_DoesntTryToDeleteNull) { + EXPECT_CALL(*env_, NewLocalRef).Times(0); + EXPECT_CALL(*env_, DeleteLocalRef).Times(0); + + LocalObject obj{jobject{nullptr}}; + EXPECT_EQ(jobject{obj}, nullptr); +} + TEST_F(JniTest, LocalObject_CallsNewAndDeleteOnNewObject) { EXPECT_CALL(*env_, NewObjectV).WillOnce(testing::Return(Fake())); EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(1); @@ -53,13 +62,22 @@ TEST_F(JniTest, LocalObject_CallsNewAndDeleteOnNewObject) { } TEST_F(JniTest, LocalObject_CallsOnlyDeleteOnWrapCtor) { - EXPECT_CALL(*env_, NewLocalRef).Times(0); - EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(1); + EXPECT_CALL(*env_, NewLocalRef).Times(1); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))) + .Times(1); LocalObject obj{Fake()}; EXPECT_NE(jobject{obj}, nullptr); } +TEST_F(JniTest, LocalObject_CallsNewLocalRefByDefault) { + EXPECT_CALL(*env_, NewLocalRef).WillOnce(::testing::Return(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); + + LocalObject obj{Fake(1)}; + EXPECT_EQ(jobject{obj}, Fake(2)); +} + TEST_F(JniTest, LocalObject_CallsNewLocalRefOnCopy) { EXPECT_CALL(*env_, NewLocalRef).WillOnce(::testing::Return(Fake(2))); EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); @@ -107,8 +125,9 @@ TEST_F(JniTest, LocalObject_ObjectReturnsInstanceMethods) { } TEST_F(JniTest, LocalObject_CallsDeleteOnceAfterAMoveConstruction) { - EXPECT_CALL(*env_, NewLocalRef).Times(0); - EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(1); + EXPECT_CALL(*env_, NewLocalRef).Times(1); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))) + .Times(1); LocalObject obj_1{Fake()}; @@ -120,9 +139,9 @@ TEST_F(JniTest, LocalObject_CallsDeleteOnceAfterAMoveConstruction) { } TEST_F(JniTest, LocalObject_FunctionsProperlyInSTLContainer) { - EXPECT_CALL(*env_, NewLocalRef).Times(0); - EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))); - EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); + EXPECT_CALL(*env_, NewLocalRef).Times(2); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake(1)))); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake(2)))); LocalObject obj_1{Fake(1)}; LocalObject obj_2{Fake(2)}; @@ -175,11 +194,11 @@ TEST_F(JniTest, LocalObject_ComparesAgainstjobjects) { static constexpr Class kClass{"kClass1"}; LocalObject val_1{Fake()}; - EXPECT_TRUE(val_1 == Fake()); - EXPECT_TRUE(Fake() == val_1); + EXPECT_TRUE(val_1 == AsNewLocalReference(Fake())); + EXPECT_TRUE(AsNewLocalReference(Fake()) == val_1); - EXPECT_FALSE(val_1 != Fake()); - EXPECT_FALSE(Fake() != val_1); + EXPECT_FALSE(val_1 != AsNewLocalReference(Fake())); + EXPECT_FALSE(AsNewLocalReference(Fake()) != val_1); } TEST_F(JniTest, LocalObject_ComparesAgainstOtherLocalObjects_InContainers) { @@ -278,7 +297,7 @@ TEST_F(JniTest, LocalObject_MovesInContainerStruct) { A(LocalObject&& in) : val(std::move(in)) {} }; - EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); A a{LocalObject{Fake()}}; } diff --git a/implementation/local_string.h b/implementation/local_string.h index a87e7676..979a3d3f 100644 --- a/implementation/local_string.h +++ b/implementation/local_string.h @@ -18,10 +18,12 @@ #define JNI_BIND_LOCAL_STRING_H_ #include "class_defs/java_lang_classes.h" +#include "implementation/forward_declarations.h" #include "implementation/jni_helper/jni_helper.h" #include "implementation/jni_helper/lifecycle_string.h" #include "implementation/local_object.h" #include "implementation/promotion_mechanics.h" +#include "implementation/promotion_mechanics_tags.h" #include "implementation/ref_base.h" #include "implementation/string_ref.h" #include "jni_dep.h" @@ -46,7 +48,11 @@ class LocalString : public LocalStringImpl { LocalString(std::nullptr_t) : Base(jstring{nullptr}) {} LocalString(LocalObject&& obj) - : Base(static_cast(obj.Release())) {} + : Base(AdoptLocal{}, static_cast(obj.Release())) {} + + template + LocalString(ArrayViewHelper array_view_helper) + : LocalString(AdoptLocal{}, array_view_helper.val_) {} // Returns a StringView which possibly performs an expensive pinning // operation. String objects can be pinned multiple times. diff --git a/implementation/overload_ref.h b/implementation/overload_ref.h index e49b4e27..90c39ec7 100644 --- a/implementation/overload_ref.h +++ b/implementation/overload_ref.h @@ -33,6 +33,7 @@ #include "implementation/jni_type.h" #include "implementation/method.h" #include "implementation/params.h" +#include "implementation/promotion_mechanics_tags.h" #include "implementation/proxy.h" #include "implementation/proxy_convenience_aliases.h" #include "implementation/proxy_definitions.h" @@ -110,14 +111,23 @@ struct OverloadRef { Proxy_t::ProxyAsArg(std::forward(params))...); } else if constexpr (IdT::kIsConstructor) { return ReturnProxied{ + AdoptLocal{}, LifecycleHelper::Construct( clazz, mthd, Proxy_t::ProxyAsArg(std::forward(params))...)}; } else { - return static_cast( - InvokeHelper::Invoke( - object, clazz, mthd, - Proxy_t::ProxyAsArg(std::forward(params))...)); + if constexpr (std::is_base_of_v) { + return ReturnProxied{ + AdoptLocal{}, + InvokeHelper::Invoke( + object, clazz, mthd, + Proxy_t::ProxyAsArg(std::forward(params))...)}; + } else { + return static_cast( + InvokeHelper::Invoke( + object, clazz, mthd, + Proxy_t::ProxyAsArg(std::forward(params))...)); + } } } }; diff --git a/implementation/promotion_mechanics.h b/implementation/promotion_mechanics.h index 8218a473..d50bb6f6 100644 --- a/implementation/promotion_mechanics.h +++ b/implementation/promotion_mechanics.h @@ -24,6 +24,7 @@ #include "implementation/jni_helper/lifecycle_object.h" #include "implementation/jni_type.h" #include "implementation/object_ref.h" +#include "implementation/promotion_mechanics_tags.h" #include "implementation/ref_base.h" #include "jni_dep.h" #include "metaprogramming/deep_equal_diminished.h" @@ -31,17 +32,6 @@ namespace jni { -// Creates an additional reference to the underlying object. -// When used for local, presumes local, for global, presumes global. -struct CreateCopy {}; - -// This tag allows the constructor to promote underlying jobject for you. -struct PromoteToGlobal {}; - -// CAUTION: This tag assume the underlying jobject has been pinned as a global. -// This is atypical when solely using JNI Bind, use with caution. -struct AdoptGlobal {}; - // Marks the end of `ScopeEntry` daisy chain. struct ScopedTerminalTag {}; @@ -58,12 +48,16 @@ struct EntryBase : public Base { (::jni::metaprogramming::DeepEqualDiminished_v || std::is_base_of_v, T>)>> EntryBase(T&& rhs) : Base(rhs.Release()) {} + EntryBase(AdoptLocal, ViableSpan object) : Base(object) {} // "Copy" constructor: Additional reference to object will be created. EntryBase(CreateCopy, ViableSpan object) - : Base(static_cast( - LifecycleHelper::NewReference( - static_cast(object)))) {} + : EntryBase(AdoptLocal{}, + object + ? static_cast( + LifecycleHelper::NewReference( + static_cast(object))) + : nullptr) {} // Comparison operator for pinned Scoped type (not deep equality). template ; using Base::Base; - // "Wrap" constructor: Object released at end of scope. + // "Wrap" constructor: Newly created object released at end of scope. + // Wrap constructors automatically create a new local because objects passed + // into JNI should not be released, and LocalObject(jni_arg) is + // common. Entry(ViableSpan object) - : Base(static_cast(object)) {} + : Base(AdoptLocal{}, + object ? LifecycleHelper:: + NewReference( + static_cast(object)) + : nullptr) {} + + Entry(AdoptLocal, ViableSpan object) + : Base(AdoptLocal{}, static_cast(object)) {} }; // Shared implementation common to all *global* `Entry`. diff --git a/implementation/promotion_mechanics_tags.h b/implementation/promotion_mechanics_tags.h new file mode 100644 index 00000000..1af18755 --- /dev/null +++ b/implementation/promotion_mechanics_tags.h @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed 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. + */ +#ifndef JNI_BIND_IMPLEMENTATION_PROMOTION_MECHANICS_TAGS_H_ +#define JNI_BIND_IMPLEMENTATION_PROMOTION_MECHANICS_TAGS_H_ + +namespace jni { + +// Adopts a local. +struct AdoptLocal {}; + +// Creates an additional reference to the underlying object. +// When used for local, presumes local, for global, presumes global. +struct CreateCopy {}; + +// This tag allows the constructor to promote underlying jobject for you. +struct PromoteToGlobal {}; + +// CAUTION: This tag assume the underlying jobject has been pinned as a global. +// This is atypical when solely using JNI Bind, use with caution. +struct AdoptGlobal {}; + +} // namespace jni + +#endif // JNI_BIND_IMPLEMENTATION_PROMOTION_MECHANICS_TAGS_H_ diff --git a/implementation/ref_base.h b/implementation/ref_base.h index 4b8f93b3..4075faa5 100644 --- a/implementation/ref_base.h +++ b/implementation/ref_base.h @@ -26,11 +26,13 @@ namespace jni { +struct RefBaseBase {}; + // Used to detect RefBase in type proxying. // This is useful, e.g. when you want to say "an object that might be passed" // but the object's type (i.e. full name + loader information) is unknown. template -class RefBaseTag { +class RefBaseTag : public RefBaseBase { public: template diff --git a/implementation/static_ref_test.cc b/implementation/static_ref_test.cc index 49ac0c24..14b83bd9 100644 --- a/implementation/static_ref_test.cc +++ b/implementation/static_ref_test.cc @@ -21,6 +21,7 @@ namespace { +using ::jni::AdoptLocal; using ::jni::Array; using ::jni::Class; using ::jni::Field; @@ -174,7 +175,8 @@ TEST_F(JniTest, StaticField_StringSet) { EXPECT_CALL(*env_, SetStaticObjectField(_, Fake(), Fake())); - StaticRef{}["stringField"].Set(LocalString{Fake()}); + StaticRef{}["stringField"].Set( + LocalString{AdoptLocal{}, Fake()}); } TEST_F(JniTest, StaticField_ObjectSet) { @@ -184,7 +186,8 @@ TEST_F(JniTest, StaticField_ObjectSet) { EXPECT_CALL(*env_, SetStaticObjectField(_, Fake(), Fake())); - StaticRef{}["classField"].Set(LocalObject{Fake()}); + StaticRef{}["classField"].Set( + LocalObject{AdoptLocal{}, Fake()}); } //////////////////////////////////////////////////////////////////////////////// diff --git a/implementation/string_ref_test.cc b/implementation/string_ref_test.cc index e081bf64..7ccf08c9 100644 --- a/implementation/string_ref_test.cc +++ b/implementation/string_ref_test.cc @@ -28,6 +28,7 @@ using ::jni::kJavaLangString; using ::jni::LocalObject; using ::jni::LocalString; using ::jni::UtfStringView; +using ::jni::test::AsNewLocalReference; using ::jni::test::Fake; using ::jni::test::JniTest; using ::testing::_; @@ -49,7 +50,7 @@ TEST_F(JniTest, LocalString_NullPtrT) { LocalString str{nullptr}; } TEST_F(JniTest, LocalString_IsImplicitlyConvertible) { LocalString str{Fake()}; - EXPECT_EQ(static_cast(str), Fake()); + EXPECT_EQ(static_cast(str), AsNewLocalReference(Fake())); } TEST_F(JniTest, LocalString_NullWorks) { @@ -58,19 +59,18 @@ TEST_F(JniTest, LocalString_NullWorks) { } TEST_F(JniTest, LocalString_ConstructsFromObject) { - EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); LocalObject undecorated_object{Fake()}; LocalString decorated_object{std::move(undecorated_object)}; } TEST_F(JniTest, LocalString_CopiesFromObject) { - EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); - EXPECT_CALL(*env_, NewLocalRef(Fake(1))) - .WillOnce(::testing::Return(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); + EXPECT_CALL(*env_, NewLocalRef(Fake())); - LocalString decorated_object{CreateCopy{}, Fake(1)}; + LocalString decorated_object{CreateCopy{}, Fake()}; - EXPECT_EQ(jstring{decorated_object}, Fake(2)); + EXPECT_EQ(jstring{decorated_object}, AsNewLocalReference(Fake())); } TEST_F(JniTest, LocalString_CopiesFromJString) { diff --git a/jni_bind.h b/jni_bind.h index 5058cee2..84fb1ae2 100644 --- a/jni_bind.h +++ b/jni_bind.h @@ -65,6 +65,7 @@ #include "implementation/local_object.h" #include "implementation/local_string.h" #include "implementation/promotion_mechanics.h" +#include "implementation/promotion_mechanics_tags.h" // IWYU pragma: end_exports diff --git a/jni_test.h b/jni_test.h index e661f48d..8278b5d0 100644 --- a/jni_test.h +++ b/jni_test.h @@ -32,6 +32,7 @@ namespace jni::test { static constexpr std::size_t kGlobalOffset = 0xBABA0000000000; +static constexpr std::size_t kCopyOffset = 0X100000000000A0; // "Translates" a fake local object into its global counterpart. // This obviously isn't doing anything meaningful, however, it provides distinct @@ -40,6 +41,11 @@ inline jclass AsGlobal(jclass clazz) { return reinterpret_cast(clazz + kGlobalOffset); } +template +inline T AsNewLocalReference(T obj) { + return reinterpret_cast(obj + kCopyOffset); +} + inline jobject AsGlobal(jobject object) { // Strangely the use of nullptr plus an integral constant causes sanitizer // failures. nullptr is only used as a null (which mock NewObject returns). @@ -100,6 +106,10 @@ class JniTestWithNoDefaultJvmRef : public ::testing::Test { ON_CALL(*env_, NewObjectArray).WillByDefault(Return(Fake())); ON_CALL(*env_, NewObjectV).WillByDefault(Return(Fake())); + ON_CALL(*env_, NewLocalRef) + .WillByDefault(testing::Invoke( + [&](jobject object) { return AsNewLocalReference(object); })); + ON_CALL(*env_, NewGlobalRef) .WillByDefault(testing::Invoke([&](jobject object) { jobject return_value = AsGlobal(object);