OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/android/jni_android.h" | 5 #include "base/android/jni_android.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
8 | 8 |
9 #include "base/android/build_info.h" | 9 #include "base/android/build_info.h" |
10 #include "base/android/jni_string.h" | 10 #include "base/android/jni_string.h" |
11 #include "base/atomicops.h" | |
12 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
13 #include "base/logging.h" | 12 #include "base/logging.h" |
14 #include "base/threading/platform_thread.h" | 13 #include "base/threading/platform_thread.h" |
15 | 14 |
16 namespace { | 15 namespace { |
17 using base::android::GetClass; | 16 using base::android::GetClass; |
18 using base::android::GetMethodID; | 17 using base::android::MethodID; |
19 using base::android::ScopedJavaLocalRef; | 18 using base::android::ScopedJavaLocalRef; |
20 | 19 |
21 struct MethodIdentifier { | 20 struct MethodIdentifier { |
22 const char* class_name; | 21 const char* class_name; |
23 const char* method; | 22 const char* method; |
24 const char* jni_signature; | 23 const char* jni_signature; |
25 | 24 |
26 bool operator<(const MethodIdentifier& other) const { | 25 bool operator<(const MethodIdentifier& other) const { |
27 int r = strcmp(class_name, other.class_name); | 26 int r = strcmp(class_name, other.class_name); |
28 if (r < 0) { | 27 if (r < 0) { |
(...skipping 22 matching lines...) Expand all Loading... |
51 // Leak the global app context, as it is used from a non-joinable worker thread | 50 // Leak the global app context, as it is used from a non-joinable worker thread |
52 // that may still be running at shutdown. There is no harm in doing this. | 51 // that may still be running at shutdown. There is no harm in doing this. |
53 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky | 52 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky |
54 g_application_context = LAZY_INSTANCE_INITIALIZER; | 53 g_application_context = LAZY_INSTANCE_INITIALIZER; |
55 base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER; | 54 base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER; |
56 | 55 |
57 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { | 56 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { |
58 ScopedJavaLocalRef<jclass> throwable_clazz = | 57 ScopedJavaLocalRef<jclass> throwable_clazz = |
59 GetClass(env, "java/lang/Throwable"); | 58 GetClass(env, "java/lang/Throwable"); |
60 jmethodID throwable_printstacktrace = | 59 jmethodID throwable_printstacktrace = |
61 GetMethodID(env, throwable_clazz, "printStackTrace", | 60 MethodID::Get<MethodID::TYPE_INSTANCE>( |
62 "(Ljava/io/PrintStream;)V"); | 61 env, throwable_clazz.obj(), "printStackTrace", |
| 62 "(Ljava/io/PrintStream;)V"); |
63 | 63 |
64 // Create an instance of ByteArrayOutputStream. | 64 // Create an instance of ByteArrayOutputStream. |
65 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz = | 65 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz = |
66 GetClass(env, "java/io/ByteArrayOutputStream"); | 66 GetClass(env, "java/io/ByteArrayOutputStream"); |
67 jmethodID bytearray_output_stream_constructor = | 67 jmethodID bytearray_output_stream_constructor = |
68 GetMethodID(env, bytearray_output_stream_clazz, "<init>", "()V"); | 68 MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 69 env, bytearray_output_stream_clazz.obj(), "<init>", "()V"); |
69 jmethodID bytearray_output_stream_tostring = | 70 jmethodID bytearray_output_stream_tostring = |
70 GetMethodID(env, bytearray_output_stream_clazz, "toString", | 71 MethodID::Get<MethodID::TYPE_INSTANCE>( |
71 "()Ljava/lang/String;"); | 72 env, bytearray_output_stream_clazz.obj(), "toString", |
| 73 "()Ljava/lang/String;"); |
72 ScopedJavaLocalRef<jobject> bytearray_output_stream(env, | 74 ScopedJavaLocalRef<jobject> bytearray_output_stream(env, |
73 env->NewObject(bytearray_output_stream_clazz.obj(), | 75 env->NewObject(bytearray_output_stream_clazz.obj(), |
74 bytearray_output_stream_constructor)); | 76 bytearray_output_stream_constructor)); |
75 | 77 |
76 // Create an instance of PrintStream. | 78 // Create an instance of PrintStream. |
77 ScopedJavaLocalRef<jclass> printstream_clazz = | 79 ScopedJavaLocalRef<jclass> printstream_clazz = |
78 GetClass(env, "java/io/PrintStream"); | 80 GetClass(env, "java/io/PrintStream"); |
79 jmethodID printstream_constructor = | 81 jmethodID printstream_constructor = |
80 GetMethodID(env, printstream_clazz, "<init>", | 82 MethodID::Get<MethodID::TYPE_INSTANCE>( |
81 "(Ljava/io/OutputStream;)V"); | 83 env, printstream_clazz.obj(), "<init>", |
| 84 "(Ljava/io/OutputStream;)V"); |
82 ScopedJavaLocalRef<jobject> printstream(env, | 85 ScopedJavaLocalRef<jobject> printstream(env, |
83 env->NewObject(printstream_clazz.obj(), printstream_constructor, | 86 env->NewObject(printstream_clazz.obj(), printstream_constructor, |
84 bytearray_output_stream.obj())); | 87 bytearray_output_stream.obj())); |
85 | 88 |
86 // Call Throwable.printStackTrace(PrintStream) | 89 // Call Throwable.printStackTrace(PrintStream) |
87 env->CallVoidMethod(java_throwable, throwable_printstacktrace, | 90 env->CallVoidMethod(java_throwable, throwable_printstacktrace, |
88 printstream.obj()); | 91 printstream.obj()); |
89 | 92 |
90 // Call ByteArrayOutputStream.toString() | 93 // Call ByteArrayOutputStream.toString() |
91 ScopedJavaLocalRef<jstring> exception_string( | 94 ScopedJavaLocalRef<jstring> exception_string( |
92 env, static_cast<jstring>( | 95 env, static_cast<jstring>( |
93 env->CallObjectMethod(bytearray_output_stream.obj(), | 96 env->CallObjectMethod(bytearray_output_stream.obj(), |
94 bytearray_output_stream_tostring))); | 97 bytearray_output_stream_tostring))); |
95 | 98 |
96 return ConvertJavaStringToUTF8(exception_string); | 99 return ConvertJavaStringToUTF8(exception_string); |
97 } | 100 } |
98 | 101 |
99 enum MethodType { | |
100 METHODTYPE_STATIC, | |
101 METHODTYPE_NORMAL, | |
102 }; | |
103 | |
104 enum ExceptionCheck { | |
105 EXCEPTIONCHECK_YES, | |
106 EXCEPTIONCHECK_NO, | |
107 }; | |
108 | |
109 template<MethodType method_type, ExceptionCheck exception_check> | |
110 jmethodID GetMethodIDInternal(JNIEnv* env, | |
111 jclass clazz, | |
112 const char* method_name, | |
113 const char* jni_signature) { | |
114 jmethodID method_id = method_type == METHODTYPE_STATIC ? | |
115 env->GetStaticMethodID(clazz, method_name, jni_signature) : | |
116 env->GetMethodID(clazz, method_name, jni_signature); | |
117 if (exception_check == EXCEPTIONCHECK_YES) { | |
118 CHECK(!base::android::ClearException(env) && method_id) << | |
119 "Failed to find " << | |
120 (method_type == METHODTYPE_STATIC ? "static " : "") << | |
121 "method " << method_name << " " << jni_signature; | |
122 } else if (base::android::HasException(env)) { | |
123 env->ExceptionClear(); | |
124 } | |
125 return method_id; | |
126 } | |
127 | |
128 } // namespace | 102 } // namespace |
129 | 103 |
130 namespace base { | 104 namespace base { |
131 namespace android { | 105 namespace android { |
132 | 106 |
133 JNIEnv* AttachCurrentThread() { | 107 JNIEnv* AttachCurrentThread() { |
134 DCHECK(g_jvm); | 108 DCHECK(g_jvm); |
135 JNIEnv* env = NULL; | 109 JNIEnv* env = NULL; |
136 jint ret = g_jvm->AttachCurrentThread(&env, NULL); | 110 jint ret = g_jvm->AttachCurrentThread(&env, NULL); |
137 DCHECK_EQ(JNI_OK, ret); | 111 DCHECK_EQ(JNI_OK, ret); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | 148 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); |
175 if (!clazz.obj()) { | 149 if (!clazz.obj()) { |
176 ClearException(env); | 150 ClearException(env); |
177 return false; | 151 return false; |
178 } | 152 } |
179 bool error = ClearException(env); | 153 bool error = ClearException(env); |
180 DCHECK(!error); | 154 DCHECK(!error); |
181 return true; | 155 return true; |
182 } | 156 } |
183 | 157 |
184 jmethodID GetMethodID(JNIEnv* env, | 158 template<MethodID::Type type> |
185 const JavaRef<jclass>& clazz, | 159 jmethodID MethodID::Get(JNIEnv* env, |
186 const char* method_name, | 160 jclass clazz, |
187 const char* jni_signature) { | 161 const char* method_name, |
188 // clazz.env() can not be used as that may be from a different thread. | 162 const char* jni_signature) { |
189 return GetMethodID(env, clazz.obj(), method_name, jni_signature); | 163 jmethodID id = type == TYPE_STATIC ? |
| 164 env->GetStaticMethodID(clazz, method_name, jni_signature) : |
| 165 env->GetMethodID(clazz, method_name, jni_signature); |
| 166 CHECK(base::android::ClearException(env) || id) << |
| 167 "Failed to find " << |
| 168 (type == TYPE_STATIC ? "static " : "") << |
| 169 "method " << method_name << " " << jni_signature; |
| 170 return id; |
190 } | 171 } |
191 | 172 |
192 jmethodID GetMethodID(JNIEnv* env, | 173 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call |
193 jclass clazz, | 174 // into ::Get() above. If there's a race, it's ok since the values are the same |
194 const char* method_name, | 175 // (and the duplicated effort will happen only once). |
195 const char* jni_signature) { | 176 template<MethodID::Type type> |
196 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_YES>( | 177 jmethodID MethodID::LazyGet(JNIEnv* env, |
197 env, clazz, method_name, jni_signature); | 178 jclass clazz, |
| 179 const char* method_name, |
| 180 const char* jni_signature, |
| 181 base::subtle::AtomicWord* atomic_method_id) { |
| 182 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID), |
| 183 AtomicWord_SmallerThan_jMethodID); |
| 184 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id); |
| 185 if (value) |
| 186 return reinterpret_cast<jmethodID>(value); |
| 187 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature); |
| 188 base::subtle::Release_Store( |
| 189 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id)); |
| 190 return id; |
198 } | 191 } |
199 | 192 |
200 jmethodID GetMethodIDOrNull(JNIEnv* env, | 193 // Various template instantiations. |
201 jclass clazz, | 194 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>( |
202 const char* method_name, | 195 JNIEnv* env, jclass clazz, const char* method_name, |
203 const char* jni_signature) { | 196 const char* jni_signature); |
204 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_NO>( | |
205 env, clazz, method_name, jni_signature); | |
206 } | |
207 | 197 |
208 jmethodID GetStaticMethodID(JNIEnv* env, | 198 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>( |
209 const JavaRef<jclass>& clazz, | 199 JNIEnv* env, jclass clazz, const char* method_name, |
210 const char* method_name, | 200 const char* jni_signature); |
211 const char* jni_signature) { | |
212 return GetStaticMethodID(env, clazz.obj(), method_name, | |
213 jni_signature); | |
214 } | |
215 | 201 |
216 jmethodID GetStaticMethodID(JNIEnv* env, | 202 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>( |
217 jclass clazz, | 203 JNIEnv* env, jclass clazz, const char* method_name, |
218 const char* method_name, | 204 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
219 const char* jni_signature) { | |
220 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_YES>( | |
221 env, clazz, method_name, jni_signature); | |
222 } | |
223 | 205 |
224 jmethodID GetStaticMethodIDOrNull(JNIEnv* env, | 206 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>( |
225 jclass clazz, | 207 JNIEnv* env, jclass clazz, const char* method_name, |
226 const char* method_name, | 208 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
227 const char* jni_signature) { | |
228 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_NO>( | |
229 env, clazz, method_name, jni_signature); | |
230 } | |
231 | |
232 bool HasMethod(JNIEnv* env, | |
233 const JavaRef<jclass>& clazz, | |
234 const char* method_name, | |
235 const char* jni_signature) { | |
236 jmethodID method_id = | |
237 env->GetMethodID(clazz.obj(), method_name, jni_signature); | |
238 if (!method_id) { | |
239 ClearException(env); | |
240 return false; | |
241 } | |
242 bool error = ClearException(env); | |
243 DCHECK(!error); | |
244 return true; | |
245 } | |
246 | 209 |
247 jfieldID GetFieldID(JNIEnv* env, | 210 jfieldID GetFieldID(JNIEnv* env, |
248 const JavaRef<jclass>& clazz, | 211 const JavaRef<jclass>& clazz, |
249 const char* field_name, | 212 const char* field_name, |
250 const char* jni_signature) { | 213 const char* jni_signature) { |
251 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); | 214 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); |
252 CHECK(!ClearException(env) && field_id) << "Failed to find field " << | 215 CHECK(!ClearException(env) && field_id) << "Failed to find field " << |
253 field_name << " " << jni_signature; | 216 field_name << " " << jni_signature; |
254 return field_id; | 217 return field_id; |
255 } | 218 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 found = true; | 264 found = true; |
302 } | 265 } |
303 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); | 266 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); |
304 | 267 |
305 // Addition to the map does not invalidate this iterator. | 268 // Addition to the map does not invalidate this iterator. |
306 if (found) { | 269 if (found) { |
307 return iter->second; | 270 return iter->second; |
308 } | 271 } |
309 | 272 |
310 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | 273 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); |
311 jmethodID id = GetMethodID(env, clazz, method, jni_signature); | 274 jmethodID id = MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 275 env, clazz.obj(), method, jni_signature); |
312 | 276 |
313 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, | 277 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, |
314 kUnlocked, | 278 kUnlocked, |
315 kLocked) != kUnlocked) { | 279 kLocked) != kUnlocked) { |
316 base::PlatformThread::YieldCurrentThread(); | 280 base::PlatformThread::YieldCurrentThread(); |
317 } | 281 } |
318 // Another thread may have populated the map already. | 282 // Another thread may have populated the map already. |
319 std::pair<MethodIDMap::const_iterator, bool> result = | 283 std::pair<MethodIDMap::const_iterator, bool> result = |
320 map->insert(std::make_pair(key, id)); | 284 map->insert(std::make_pair(key, id)); |
321 DCHECK_EQ(id, result.first->second); | 285 DCHECK_EQ(id, result.first->second); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
354 // RVO should avoid any extra copies of the exception string. | 318 // RVO should avoid any extra copies of the exception string. |
355 base::android::BuildInfo::GetInstance()->set_java_exception_info( | 319 base::android::BuildInfo::GetInstance()->set_java_exception_info( |
356 GetJavaExceptionInfo(env, java_throwable)); | 320 GetJavaExceptionInfo(env, java_throwable)); |
357 | 321 |
358 // Now, feel good about it and die. | 322 // Now, feel good about it and die. |
359 CHECK(false); | 323 CHECK(false); |
360 } | 324 } |
361 | 325 |
362 } // namespace android | 326 } // namespace android |
363 } // namespace base | 327 } // namespace base |
OLD | NEW |