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