• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/android/scoped_java_ref.h"
11 
12 #include <iterator>
13 #include <type_traits>
14 
15 #include "base/android/jni_android.h"
16 #include "base/android/jni_string.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 
19 #define EXPECT_SAME_OBJECT(a, b) \
20   EXPECT_TRUE(env->IsSameObject((a).obj(), (b).obj()))
21 
22 namespace base {
23 namespace android {
24 
25 namespace {
26 int g_local_refs = 0;
27 int g_global_refs = 0;
28 
29 const JNINativeInterface* g_previous_functions;
30 
NewGlobalRef(JNIEnv * env,jobject obj)31 jobject NewGlobalRef(JNIEnv* env, jobject obj) {
32   ++g_global_refs;
33   return g_previous_functions->NewGlobalRef(env, obj);
34 }
35 
DeleteGlobalRef(JNIEnv * env,jobject obj)36 void DeleteGlobalRef(JNIEnv* env, jobject obj) {
37   --g_global_refs;
38   return g_previous_functions->DeleteGlobalRef(env, obj);
39 }
40 
NewLocalRef(JNIEnv * env,jobject obj)41 jobject NewLocalRef(JNIEnv* env, jobject obj) {
42   ++g_local_refs;
43   return g_previous_functions->NewLocalRef(env, obj);
44 }
45 
DeleteLocalRef(JNIEnv * env,jobject obj)46 void DeleteLocalRef(JNIEnv* env, jobject obj) {
47   --g_local_refs;
48   return g_previous_functions->DeleteLocalRef(env, obj);
49 }
50 }  // namespace
51 
52 class ScopedJavaRefTest : public testing::Test {
53  protected:
SetUp()54   void SetUp() override {
55     g_local_refs = 0;
56     g_global_refs = 0;
57     JNIEnv* env = AttachCurrentThread();
58     g_previous_functions = env->functions;
59     hooked_functions = *g_previous_functions;
60     env->functions = &hooked_functions;
61     // We inject our own functions in JNINativeInterface so we can keep track
62     // of the reference counting ourselves.
63     hooked_functions.NewGlobalRef = &NewGlobalRef;
64     hooked_functions.DeleteGlobalRef = &DeleteGlobalRef;
65     hooked_functions.NewLocalRef = &NewLocalRef;
66     hooked_functions.DeleteLocalRef = &DeleteLocalRef;
67   }
68 
TearDown()69   void TearDown() override {
70     JNIEnv* env = AttachCurrentThread();
71     env->functions = g_previous_functions;
72   }
73   // From JellyBean release, the instance of this struct provided in JNIEnv is
74   // read-only, so we deep copy it to allow individual functions to be hooked.
75   JNINativeInterface hooked_functions;
76 };
77 
78 // The main purpose of this is testing the various conversions compile.
TEST_F(ScopedJavaRefTest,Conversions)79 TEST_F(ScopedJavaRefTest, Conversions) {
80   JNIEnv* env = AttachCurrentThread();
81   ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string");
82   ScopedJavaGlobalRef<jstring> global(str);
83 
84   // Contextual conversions to bool should be allowed.
85   EXPECT_TRUE(str);
86   EXPECT_FALSE(JavaRef<jobject>());
87 
88   // All the types should convert from nullptr, even JavaRef.
89   {
90     JavaRef<jstring> null_ref(nullptr);
91     EXPECT_FALSE(null_ref);
92     ScopedJavaLocalRef<jobject> null_local(nullptr);
93     EXPECT_FALSE(null_local);
94     ScopedJavaGlobalRef<jarray> null_global(nullptr);
95     EXPECT_FALSE(null_global);
96   }
97 
98   // Local and global refs should {copy,move}-{construct,assign}.
99   // Moves should leave the source as null.
100   {
101     ScopedJavaLocalRef<jstring> str2(str);
102     EXPECT_SAME_OBJECT(str2, str);
103     ScopedJavaLocalRef<jstring> str3(std::move(str2));
104     EXPECT_SAME_OBJECT(str3, str);
105     EXPECT_FALSE(str2);
106     ScopedJavaLocalRef<jstring> str4;
107     str4 = str;
108     EXPECT_SAME_OBJECT(str4, str);
109     ScopedJavaLocalRef<jstring> str5;
110     str5 = std::move(str4);
111     EXPECT_SAME_OBJECT(str5, str);
112     EXPECT_FALSE(str4);
113   }
114   {
115     ScopedJavaGlobalRef<jstring> str2(global);
116     EXPECT_SAME_OBJECT(str2, str);
117     ScopedJavaGlobalRef<jstring> str3(std::move(str2));
118     EXPECT_SAME_OBJECT(str3, str);
119     EXPECT_FALSE(str2);
120     ScopedJavaGlobalRef<jstring> str4;
121     str4 = global;
122     EXPECT_SAME_OBJECT(str4, str);
123     ScopedJavaGlobalRef<jstring> str5;
124     str5 = std::move(str4);
125     EXPECT_SAME_OBJECT(str5, str);
126     EXPECT_FALSE(str4);
127   }
128 
129   // As above but going from jstring to jobject.
130   {
131     ScopedJavaLocalRef<jobject> obj2(str);
132     EXPECT_SAME_OBJECT(obj2, str);
133     ScopedJavaLocalRef<jobject> obj3(std::move(obj2));
134     EXPECT_SAME_OBJECT(obj3, str);
135     EXPECT_FALSE(obj2);
136     ScopedJavaLocalRef<jobject> obj4;
137     obj4 = str;
138     EXPECT_SAME_OBJECT(obj4, str);
139     ScopedJavaLocalRef<jobject> obj5;
140     obj5 = std::move(obj4);
141     EXPECT_SAME_OBJECT(obj5, str);
142     EXPECT_FALSE(obj4);
143   }
144   {
145     ScopedJavaGlobalRef<jobject> obj2(global);
146     EXPECT_SAME_OBJECT(obj2, str);
147     ScopedJavaGlobalRef<jobject> obj3(std::move(obj2));
148     EXPECT_SAME_OBJECT(obj3, str);
149     EXPECT_FALSE(obj2);
150     ScopedJavaGlobalRef<jobject> obj4;
151     obj4 = global;
152     EXPECT_SAME_OBJECT(obj4, str);
153     ScopedJavaGlobalRef<jobject> obj5;
154     obj5 = std::move(obj4);
155     EXPECT_SAME_OBJECT(obj5, str);
156     EXPECT_FALSE(obj4);
157   }
158 
159   // Explicit copy construction or assignment between global<->local is allowed,
160   // but not implicit conversions.
161   {
162     ScopedJavaLocalRef<jstring> new_local(global);
163     EXPECT_SAME_OBJECT(new_local, str);
164     new_local = global;
165     EXPECT_SAME_OBJECT(new_local, str);
166     ScopedJavaGlobalRef<jstring> new_global(str);
167     EXPECT_SAME_OBJECT(new_global, str);
168     new_global = str;
169     EXPECT_SAME_OBJECT(new_local, str);
170     static_assert(!std::is_convertible_v<ScopedJavaLocalRef<jobject>,
171                                          ScopedJavaGlobalRef<jobject>>,
172                   "");
173     static_assert(!std::is_convertible_v<ScopedJavaGlobalRef<jobject>,
174                                          ScopedJavaLocalRef<jobject>>,
175                   "");
176   }
177 
178   // Converting between local/global while also converting to jobject also works
179   // because JavaRef<jobject> is the base class.
180   {
181     ScopedJavaGlobalRef<jobject> global_obj(str);
182     ScopedJavaLocalRef<jobject> local_obj(global);
183     const JavaRef<jobject>& obj_ref1(str);
184     const JavaRef<jobject>& obj_ref2(global);
185     EXPECT_SAME_OBJECT(obj_ref1, obj_ref2);
186     EXPECT_SAME_OBJECT(global_obj, obj_ref2);
187   }
188   global.Reset(str);
189   const JavaRef<jstring>& str_ref = str;
190   EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref));
191   str.Reset();
192 }
193 
TEST_F(ScopedJavaRefTest,RefCounts)194 TEST_F(ScopedJavaRefTest, RefCounts) {
195   JNIEnv* env = AttachCurrentThread();
196   ScopedJavaLocalRef<jstring> str;
197   // The ConvertJavaStringToUTF8 below creates a new string that would normally
198   // return a local ref. We simulate that by starting the g_local_refs count at
199   // 1.
200   g_local_refs = 1;
201   str.Reset(ConvertUTF8ToJavaString(env, "string"));
202   EXPECT_EQ(1, g_local_refs);
203   EXPECT_EQ(0, g_global_refs);
204   {
205     ScopedJavaGlobalRef<jstring> global_str(str);
206     ScopedJavaGlobalRef<jobject> global_obj(global_str);
207     EXPECT_EQ(1, g_local_refs);
208     EXPECT_EQ(2, g_global_refs);
209 
210     auto str2 = ScopedJavaLocalRef<jstring>::Adopt(env, str.Release());
211     EXPECT_EQ(1, g_local_refs);
212     {
213       ScopedJavaLocalRef<jstring> str3(str2);
214       EXPECT_EQ(2, g_local_refs);
215     }
216     EXPECT_EQ(1, g_local_refs);
217     {
218       ScopedJavaLocalRef<jstring> str4((ScopedJavaLocalRef<jstring>(str2)));
219       EXPECT_EQ(2, g_local_refs);
220     }
221     EXPECT_EQ(1, g_local_refs);
222     {
223       ScopedJavaLocalRef<jstring> str5;
224       str5 = ScopedJavaLocalRef<jstring>(str2);
225       EXPECT_EQ(2, g_local_refs);
226     }
227     EXPECT_EQ(1, g_local_refs);
228     str2.Reset();
229     EXPECT_EQ(0, g_local_refs);
230     global_str.Reset();
231     EXPECT_EQ(1, g_global_refs);
232     ScopedJavaGlobalRef<jobject> global_obj2(global_obj);
233     EXPECT_EQ(2, g_global_refs);
234   }
235 
236   EXPECT_EQ(0, g_local_refs);
237   EXPECT_EQ(0, g_global_refs);
238 }
239 
240 class JavaObjectArrayReaderTest : public testing::Test {
241  protected:
SetUp()242   void SetUp() override {
243     JNIEnv* env = AttachCurrentThread();
244     int_class_ = GetClass(env, "java/lang/Integer");
245     int_constructor_ = MethodID::Get<MethodID::TYPE_INSTANCE>(
246         env, int_class_.obj(), "<init>", "(I)V");
247     array_ = MakeArray(array_len_);
248 
249     // Make array_len_ different Integer objects, keep a reference to each,
250     // and add them to the array.
251     for (jint i = 0; i < array_len_; ++i) {
252       jobject member = env->NewObject(int_class_.obj(), int_constructor_, i);
253       ASSERT_NE(member, nullptr);
254       array_members_[i] = ScopedJavaLocalRef<jobject>::Adopt(env, member);
255       env->SetObjectArrayElement(array_.obj(), i, member);
256     }
257   }
258 
259   // Make an Integer[] with len elements, all initialized to null.
MakeArray(jsize len)260   ScopedJavaLocalRef<jobjectArray> MakeArray(jsize len) {
261     JNIEnv* env = AttachCurrentThread();
262     jobjectArray array = env->NewObjectArray(len, int_class_.obj(), nullptr);
263     EXPECT_NE(array, nullptr);
264     return ScopedJavaLocalRef<jobjectArray>::Adopt(env, array);
265   }
266 
267   static constexpr jsize array_len_ = 10;
268   ScopedJavaLocalRef<jclass> int_class_;
269   jmethodID int_constructor_;
270   ScopedJavaLocalRef<jobject> array_members_[array_len_];
271   ScopedJavaLocalRef<jobjectArray> array_;
272 };
273 
274 // Must actually define the variable until C++17 :(
275 constexpr jsize JavaObjectArrayReaderTest::array_len_;
276 
TEST_F(JavaObjectArrayReaderTest,ZeroLengthArray)277 TEST_F(JavaObjectArrayReaderTest, ZeroLengthArray) {
278   JavaObjectArrayReader<jobject> zero_length(MakeArray(0));
279   EXPECT_TRUE(zero_length.empty());
280   EXPECT_EQ(zero_length.size(), 0);
281   EXPECT_EQ(zero_length.begin(), zero_length.end());
282 }
283 
284 // Verify that we satisfy the C++ "InputIterator" named requirements.
TEST_F(JavaObjectArrayReaderTest,InputIteratorRequirements)285 TEST_F(JavaObjectArrayReaderTest, InputIteratorRequirements) {
286   typedef JavaObjectArrayReader<jobject>::iterator It;
287 
288   JNIEnv* env = AttachCurrentThread();
289   JavaObjectArrayReader<jobject> reader(array_);
290   It i = reader.begin();
291 
292   EXPECT_TRUE(std::is_copy_constructible_v<It>);
293   It copy = i;
294   EXPECT_EQ(copy, i);
295   EXPECT_EQ(It(i), i);
296 
297   EXPECT_TRUE(std::is_copy_assignable_v<It>);
298   It assign = reader.end();
299   It& assign2 = (assign = i);
300   EXPECT_EQ(assign, i);
301   EXPECT_EQ(assign2, assign);
302 
303   EXPECT_TRUE(std::is_destructible_v<It>);
304 
305   // Swappable
306   It left = reader.begin(), right = reader.end();
307   std::swap(left, right);
308   EXPECT_EQ(left, reader.end());
309   EXPECT_EQ(right, reader.begin());
310 
311   // Basic check that iterator_traits works
312   bool same_type = std::is_same_v<std::iterator_traits<It>::iterator_category,
313                                   std::input_iterator_tag>;
314   EXPECT_TRUE(same_type);
315 
316   // Comparisons
317   EXPECT_EQ(reader.begin(), reader.begin());
318   EXPECT_NE(reader.begin(), reader.end());
319 
320   // Dereferencing
321   ScopedJavaLocalRef<jobject> o = *(reader.begin());
322   EXPECT_SAME_OBJECT(o, array_members_[0]);
323   EXPECT_TRUE(env->IsSameObject(o.obj(), reader.begin()->obj()));
324 
325   // Incrementing
326   It preinc = ++(reader.begin());
327   EXPECT_SAME_OBJECT(*preinc, array_members_[1]);
328   It postinc = reader.begin();
329   EXPECT_SAME_OBJECT(*postinc++, array_members_[0]);
330   EXPECT_SAME_OBJECT(*postinc, array_members_[1]);
331 }
332 
333 // Check that range-based for and the convenience function work as expected.
TEST_F(JavaObjectArrayReaderTest,RangeBasedFor)334 TEST_F(JavaObjectArrayReaderTest, RangeBasedFor) {
335   JNIEnv* env = AttachCurrentThread();
336 
337   int i = 0;
338   for (ScopedJavaLocalRef<jobject> element : array_.ReadElements<jobject>()) {
339     EXPECT_SAME_OBJECT(element, array_members_[i++]);
340   }
341   EXPECT_EQ(i, array_len_);
342 }
343 
344 }  // namespace android
345 }  // namespace base
346