123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/android/scoped_java_ref.h"
- #include <iterator>
- #include <type_traits>
- #include "base/android/jni_android.h"
- #include "base/android/jni_string.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #define EXPECT_SAME_OBJECT(a, b) \
- EXPECT_TRUE(env->IsSameObject((a).obj(), (b).obj()))
- namespace base {
- namespace android {
- namespace {
- int g_local_refs = 0;
- int g_global_refs = 0;
- const JNINativeInterface* g_previous_functions;
- jobject NewGlobalRef(JNIEnv* env, jobject obj) {
- ++g_global_refs;
- return g_previous_functions->NewGlobalRef(env, obj);
- }
- void DeleteGlobalRef(JNIEnv* env, jobject obj) {
- --g_global_refs;
- return g_previous_functions->DeleteGlobalRef(env, obj);
- }
- jobject NewLocalRef(JNIEnv* env, jobject obj) {
- ++g_local_refs;
- return g_previous_functions->NewLocalRef(env, obj);
- }
- void DeleteLocalRef(JNIEnv* env, jobject obj) {
- --g_local_refs;
- return g_previous_functions->DeleteLocalRef(env, obj);
- }
- } // namespace
- class ScopedJavaRefTest : public testing::Test {
- protected:
- void SetUp() override {
- g_local_refs = 0;
- g_global_refs = 0;
- JNIEnv* env = AttachCurrentThread();
- g_previous_functions = env->functions;
- hooked_functions = *g_previous_functions;
- env->functions = &hooked_functions;
- // We inject our own functions in JNINativeInterface so we can keep track
- // of the reference counting ourselves.
- hooked_functions.NewGlobalRef = &NewGlobalRef;
- hooked_functions.DeleteGlobalRef = &DeleteGlobalRef;
- hooked_functions.NewLocalRef = &NewLocalRef;
- hooked_functions.DeleteLocalRef = &DeleteLocalRef;
- }
- void TearDown() override {
- JNIEnv* env = AttachCurrentThread();
- env->functions = g_previous_functions;
- }
- // From JellyBean release, the instance of this struct provided in JNIEnv is
- // read-only, so we deep copy it to allow individual functions to be hooked.
- JNINativeInterface hooked_functions;
- };
- // The main purpose of this is testing the various conversions compile.
- TEST_F(ScopedJavaRefTest, Conversions) {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string");
- ScopedJavaGlobalRef<jstring> global(str);
- // Contextual conversions to bool should be allowed.
- EXPECT_TRUE(str);
- EXPECT_FALSE(JavaRef<jobject>());
- // All the types should convert from nullptr, even JavaRef.
- {
- JavaRef<jstring> null_ref(nullptr);
- EXPECT_FALSE(null_ref);
- ScopedJavaLocalRef<jobject> null_local(nullptr);
- EXPECT_FALSE(null_local);
- ScopedJavaGlobalRef<jarray> null_global(nullptr);
- EXPECT_FALSE(null_global);
- }
- // Local and global refs should {copy,move}-{construct,assign}.
- // Moves should leave the source as null.
- {
- ScopedJavaLocalRef<jstring> str2(str);
- EXPECT_SAME_OBJECT(str2, str);
- ScopedJavaLocalRef<jstring> str3(std::move(str2));
- EXPECT_SAME_OBJECT(str3, str);
- EXPECT_FALSE(str2);
- ScopedJavaLocalRef<jstring> str4;
- str4 = str;
- EXPECT_SAME_OBJECT(str4, str);
- ScopedJavaLocalRef<jstring> str5;
- str5 = std::move(str4);
- EXPECT_SAME_OBJECT(str5, str);
- EXPECT_FALSE(str4);
- }
- {
- ScopedJavaGlobalRef<jstring> str2(global);
- EXPECT_SAME_OBJECT(str2, str);
- ScopedJavaGlobalRef<jstring> str3(std::move(str2));
- EXPECT_SAME_OBJECT(str3, str);
- EXPECT_FALSE(str2);
- ScopedJavaGlobalRef<jstring> str4;
- str4 = global;
- EXPECT_SAME_OBJECT(str4, str);
- ScopedJavaGlobalRef<jstring> str5;
- str5 = std::move(str4);
- EXPECT_SAME_OBJECT(str5, str);
- EXPECT_FALSE(str4);
- }
- // As above but going from jstring to jobject.
- {
- ScopedJavaLocalRef<jobject> obj2(str);
- EXPECT_SAME_OBJECT(obj2, str);
- ScopedJavaLocalRef<jobject> obj3(std::move(obj2));
- EXPECT_SAME_OBJECT(obj3, str);
- EXPECT_FALSE(obj2);
- ScopedJavaLocalRef<jobject> obj4;
- obj4 = str;
- EXPECT_SAME_OBJECT(obj4, str);
- ScopedJavaLocalRef<jobject> obj5;
- obj5 = std::move(obj4);
- EXPECT_SAME_OBJECT(obj5, str);
- EXPECT_FALSE(obj4);
- }
- {
- ScopedJavaGlobalRef<jobject> obj2(global);
- EXPECT_SAME_OBJECT(obj2, str);
- ScopedJavaGlobalRef<jobject> obj3(std::move(obj2));
- EXPECT_SAME_OBJECT(obj3, str);
- EXPECT_FALSE(obj2);
- ScopedJavaGlobalRef<jobject> obj4;
- obj4 = global;
- EXPECT_SAME_OBJECT(obj4, str);
- ScopedJavaGlobalRef<jobject> obj5;
- obj5 = std::move(obj4);
- EXPECT_SAME_OBJECT(obj5, str);
- EXPECT_FALSE(obj4);
- }
- // Explicit copy construction or assignment between global<->local is allowed,
- // but not implicit conversions.
- {
- ScopedJavaLocalRef<jstring> new_local(global);
- EXPECT_SAME_OBJECT(new_local, str);
- new_local = global;
- EXPECT_SAME_OBJECT(new_local, str);
- ScopedJavaGlobalRef<jstring> new_global(str);
- EXPECT_SAME_OBJECT(new_global, str);
- new_global = str;
- EXPECT_SAME_OBJECT(new_local, str);
- static_assert(!std::is_convertible<ScopedJavaLocalRef<jobject>,
- ScopedJavaGlobalRef<jobject>>::value,
- "");
- static_assert(!std::is_convertible<ScopedJavaGlobalRef<jobject>,
- ScopedJavaLocalRef<jobject>>::value,
- "");
- }
- // Converting between local/global while also converting to jobject also works
- // because JavaRef<jobject> is the base class.
- {
- ScopedJavaGlobalRef<jobject> global_obj(str);
- ScopedJavaLocalRef<jobject> local_obj(global);
- const JavaRef<jobject>& obj_ref1(str);
- const JavaRef<jobject>& obj_ref2(global);
- EXPECT_SAME_OBJECT(obj_ref1, obj_ref2);
- EXPECT_SAME_OBJECT(global_obj, obj_ref2);
- }
- global.Reset(str);
- const JavaRef<jstring>& str_ref = str;
- EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref));
- str.Reset();
- }
- TEST_F(ScopedJavaRefTest, RefCounts) {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jstring> str;
- // The ConvertJavaStringToUTF8 below creates a new string that would normally
- // return a local ref. We simulate that by starting the g_local_refs count at
- // 1.
- g_local_refs = 1;
- str.Reset(ConvertUTF8ToJavaString(env, "string"));
- EXPECT_EQ(1, g_local_refs);
- EXPECT_EQ(0, g_global_refs);
- {
- ScopedJavaGlobalRef<jstring> global_str(str);
- ScopedJavaGlobalRef<jobject> global_obj(global_str);
- EXPECT_EQ(1, g_local_refs);
- EXPECT_EQ(2, g_global_refs);
- auto str2 = ScopedJavaLocalRef<jstring>::Adopt(env, str.Release());
- EXPECT_EQ(1, g_local_refs);
- {
- ScopedJavaLocalRef<jstring> str3(str2);
- EXPECT_EQ(2, g_local_refs);
- }
- EXPECT_EQ(1, g_local_refs);
- {
- ScopedJavaLocalRef<jstring> str4((ScopedJavaLocalRef<jstring>(str2)));
- EXPECT_EQ(2, g_local_refs);
- }
- EXPECT_EQ(1, g_local_refs);
- {
- ScopedJavaLocalRef<jstring> str5;
- str5 = ScopedJavaLocalRef<jstring>(str2);
- EXPECT_EQ(2, g_local_refs);
- }
- EXPECT_EQ(1, g_local_refs);
- str2.Reset();
- EXPECT_EQ(0, g_local_refs);
- global_str.Reset();
- EXPECT_EQ(1, g_global_refs);
- ScopedJavaGlobalRef<jobject> global_obj2(global_obj);
- EXPECT_EQ(2, g_global_refs);
- }
- EXPECT_EQ(0, g_local_refs);
- EXPECT_EQ(0, g_global_refs);
- }
- class JavaObjectArrayReaderTest : public testing::Test {
- protected:
- void SetUp() override {
- JNIEnv* env = AttachCurrentThread();
- int_class_ = GetClass(env, "java/lang/Integer");
- int_constructor_ = MethodID::Get<MethodID::TYPE_INSTANCE>(
- env, int_class_.obj(), "<init>", "(I)V");
- array_ = MakeArray(array_len_);
- // Make array_len_ different Integer objects, keep a reference to each,
- // and add them to the array.
- for (jint i = 0; i < array_len_; ++i) {
- jobject member = env->NewObject(int_class_.obj(), int_constructor_, i);
- ASSERT_NE(member, nullptr);
- array_members_[i] = ScopedJavaLocalRef<jobject>::Adopt(env, member);
- env->SetObjectArrayElement(array_.obj(), i, member);
- }
- }
- // Make an Integer[] with len elements, all initialized to null.
- ScopedJavaLocalRef<jobjectArray> MakeArray(jsize len) {
- JNIEnv* env = AttachCurrentThread();
- jobjectArray array = env->NewObjectArray(len, int_class_.obj(), nullptr);
- EXPECT_NE(array, nullptr);
- return ScopedJavaLocalRef<jobjectArray>::Adopt(env, array);
- }
- static constexpr jsize array_len_ = 10;
- ScopedJavaLocalRef<jclass> int_class_;
- jmethodID int_constructor_;
- ScopedJavaLocalRef<jobject> array_members_[array_len_];
- ScopedJavaLocalRef<jobjectArray> array_;
- };
- // Must actually define the variable until C++17 :(
- constexpr jsize JavaObjectArrayReaderTest::array_len_;
- TEST_F(JavaObjectArrayReaderTest, ZeroLengthArray) {
- JavaObjectArrayReader<jobject> zero_length(MakeArray(0));
- EXPECT_TRUE(zero_length.empty());
- EXPECT_EQ(zero_length.size(), 0);
- EXPECT_EQ(zero_length.begin(), zero_length.end());
- }
- // Verify that we satisfy the C++ "InputIterator" named requirements.
- TEST_F(JavaObjectArrayReaderTest, InputIteratorRequirements) {
- typedef JavaObjectArrayReader<jobject>::iterator It;
- JNIEnv* env = AttachCurrentThread();
- JavaObjectArrayReader<jobject> reader(array_);
- It i = reader.begin();
- EXPECT_TRUE(std::is_copy_constructible<It>::value);
- It copy = i;
- EXPECT_EQ(copy, i);
- EXPECT_EQ(It(i), i);
- EXPECT_TRUE(std::is_copy_assignable<It>::value);
- It assign = reader.end();
- It& assign2 = (assign = i);
- EXPECT_EQ(assign, i);
- EXPECT_EQ(assign2, assign);
- EXPECT_TRUE(std::is_destructible<It>::value);
- // Swappable
- It left = reader.begin(), right = reader.end();
- std::swap(left, right);
- EXPECT_EQ(left, reader.end());
- EXPECT_EQ(right, reader.begin());
- // Basic check that iterator_traits works
- bool same_type = std::is_same<std::iterator_traits<It>::iterator_category,
- std::input_iterator_tag>::value;
- EXPECT_TRUE(same_type);
- // Comparisons
- EXPECT_EQ(reader.begin(), reader.begin());
- EXPECT_NE(reader.begin(), reader.end());
- // Dereferencing
- ScopedJavaLocalRef<jobject> o = *(reader.begin());
- EXPECT_SAME_OBJECT(o, array_members_[0]);
- EXPECT_TRUE(env->IsSameObject(o.obj(), reader.begin()->obj()));
- // Incrementing
- It preinc = ++(reader.begin());
- EXPECT_SAME_OBJECT(*preinc, array_members_[1]);
- It postinc = reader.begin();
- EXPECT_SAME_OBJECT(*postinc++, array_members_[0]);
- EXPECT_SAME_OBJECT(*postinc, array_members_[1]);
- }
- // Check that range-based for and the convenience function work as expected.
- TEST_F(JavaObjectArrayReaderTest, RangeBasedFor) {
- JNIEnv* env = AttachCurrentThread();
- int i = 0;
- for (ScopedJavaLocalRef<jobject> element : array_.ReadElements<jobject>()) {
- EXPECT_SAME_OBJECT(element, array_members_[i++]);
- }
- EXPECT_EQ(i, array_len_);
- }
- } // namespace android
- } // namespace base
|