Chromium Code Reviews| 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 23 matching lines...) Expand all Loading... | |
| 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>::Leaky | 54 base::LazyInstance<MethodIDMap>::Leaky |
| 56 g_method_id_map = LAZY_INSTANCE_INITIALIZER; | 55 g_method_id_map = LAZY_INSTANCE_INITIALIZER; |
| 57 | 56 |
| 58 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { | 57 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { |
| 59 ScopedJavaLocalRef<jclass> throwable_clazz = | 58 ScopedJavaLocalRef<jclass> throwable_clazz = |
| 60 GetClass(env, "java/lang/Throwable"); | 59 GetClass(env, "java/lang/Throwable"); |
| 61 jmethodID throwable_printstacktrace = | 60 jmethodID throwable_printstacktrace = |
| 62 GetMethodID(env, throwable_clazz, "printStackTrace", | 61 MethodID::Get<MethodID::METHODTYPE_NORMAL, MethodID::EXCEPTIONCHECK_YES>( |
| 63 "(Ljava/io/PrintStream;)V"); | 62 env, throwable_clazz.obj(), "printStackTrace", |
| 63 "(Ljava/io/PrintStream;)V"); | |
| 64 | 64 |
| 65 // Create an instance of ByteArrayOutputStream. | 65 // Create an instance of ByteArrayOutputStream. |
| 66 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz = | 66 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz = |
| 67 GetClass(env, "java/io/ByteArrayOutputStream"); | 67 GetClass(env, "java/io/ByteArrayOutputStream"); |
| 68 jmethodID bytearray_output_stream_constructor = | 68 jmethodID bytearray_output_stream_constructor = |
| 69 GetMethodID(env, bytearray_output_stream_clazz, "<init>", "()V"); | 69 MethodID::Get<MethodID::METHODTYPE_NORMAL, MethodID::EXCEPTIONCHECK_YES>( |
| 70 env, bytearray_output_stream_clazz.obj(), "<init>", "()V"); | |
| 70 jmethodID bytearray_output_stream_tostring = | 71 jmethodID bytearray_output_stream_tostring = |
| 71 GetMethodID(env, bytearray_output_stream_clazz, "toString", | 72 MethodID::Get<MethodID::METHODTYPE_NORMAL, MethodID::EXCEPTIONCHECK_YES>( |
| 72 "()Ljava/lang/String;"); | 73 env, bytearray_output_stream_clazz.obj(), "toString", |
| 74 "()Ljava/lang/String;"); | |
| 73 ScopedJavaLocalRef<jobject> bytearray_output_stream(env, | 75 ScopedJavaLocalRef<jobject> bytearray_output_stream(env, |
| 74 env->NewObject(bytearray_output_stream_clazz.obj(), | 76 env->NewObject(bytearray_output_stream_clazz.obj(), |
| 75 bytearray_output_stream_constructor)); | 77 bytearray_output_stream_constructor)); |
| 76 | 78 |
| 77 // Create an instance of PrintStream. | 79 // Create an instance of PrintStream. |
| 78 ScopedJavaLocalRef<jclass> printstream_clazz = | 80 ScopedJavaLocalRef<jclass> printstream_clazz = |
| 79 GetClass(env, "java/io/PrintStream"); | 81 GetClass(env, "java/io/PrintStream"); |
| 80 jmethodID printstream_constructor = | 82 jmethodID printstream_constructor = |
| 81 GetMethodID(env, printstream_clazz, "<init>", | 83 MethodID::Get<MethodID::METHODTYPE_NORMAL, MethodID::EXCEPTIONCHECK_YES>( |
| 82 "(Ljava/io/OutputStream;)V"); | 84 env, printstream_clazz.obj(), "<init>", |
| 85 "(Ljava/io/OutputStream;)V"); | |
| 83 ScopedJavaLocalRef<jobject> printstream(env, | 86 ScopedJavaLocalRef<jobject> printstream(env, |
| 84 env->NewObject(printstream_clazz.obj(), printstream_constructor, | 87 env->NewObject(printstream_clazz.obj(), printstream_constructor, |
| 85 bytearray_output_stream.obj())); | 88 bytearray_output_stream.obj())); |
| 86 | 89 |
| 87 // Call Throwable.printStackTrace(PrintStream) | 90 // Call Throwable.printStackTrace(PrintStream) |
| 88 env->CallVoidMethod(java_throwable, throwable_printstacktrace, | 91 env->CallVoidMethod(java_throwable, throwable_printstacktrace, |
| 89 printstream.obj()); | 92 printstream.obj()); |
| 90 | 93 |
| 91 // Call ByteArrayOutputStream.toString() | 94 // Call ByteArrayOutputStream.toString() |
| 92 ScopedJavaLocalRef<jstring> exception_string( | 95 ScopedJavaLocalRef<jstring> exception_string( |
| 93 env, static_cast<jstring>( | 96 env, static_cast<jstring>( |
| 94 env->CallObjectMethod(bytearray_output_stream.obj(), | 97 env->CallObjectMethod(bytearray_output_stream.obj(), |
| 95 bytearray_output_stream_tostring))); | 98 bytearray_output_stream_tostring))); |
| 96 | 99 |
| 97 return ConvertJavaStringToUTF8(exception_string); | 100 return ConvertJavaStringToUTF8(exception_string); |
| 98 } | 101 } |
| 99 | 102 |
| 100 enum MethodType { | |
| 101 METHODTYPE_STATIC, | |
| 102 METHODTYPE_NORMAL, | |
| 103 }; | |
| 104 | |
| 105 enum ExceptionCheck { | |
| 106 EXCEPTIONCHECK_YES, | |
| 107 EXCEPTIONCHECK_NO, | |
| 108 }; | |
| 109 | |
| 110 template<MethodType method_type, ExceptionCheck exception_check> | |
| 111 jmethodID GetMethodIDInternal(JNIEnv* env, | |
| 112 jclass clazz, | |
| 113 const char* method_name, | |
| 114 const char* jni_signature) { | |
| 115 jmethodID method_id = method_type == METHODTYPE_STATIC ? | |
| 116 env->GetStaticMethodID(clazz, method_name, jni_signature) : | |
| 117 env->GetMethodID(clazz, method_name, jni_signature); | |
| 118 if (exception_check == EXCEPTIONCHECK_YES) { | |
| 119 CHECK(!base::android::ClearException(env) && method_id) << | |
| 120 "Failed to find " << | |
| 121 (method_type == METHODTYPE_STATIC ? "static " : "") << | |
| 122 "method " << method_name << " " << jni_signature; | |
| 123 } else if (base::android::HasException(env)) { | |
| 124 env->ExceptionClear(); | |
| 125 } | |
| 126 return method_id; | |
| 127 } | |
| 128 | |
| 129 } // namespace | 103 } // namespace |
| 130 | 104 |
| 131 namespace base { | 105 namespace base { |
| 132 namespace android { | 106 namespace android { |
| 133 | 107 |
| 134 JNIEnv* AttachCurrentThread() { | 108 JNIEnv* AttachCurrentThread() { |
| 135 if (!g_jvm) | 109 if (!g_jvm) |
| 136 return NULL; | 110 return NULL; |
| 137 JNIEnv* env = NULL; | 111 JNIEnv* env = NULL; |
| 138 jint ret = g_jvm->AttachCurrentThread(&env, NULL); | 112 jint ret = g_jvm->AttachCurrentThread(&env, NULL); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 176 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | 150 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); |
| 177 if (!clazz.obj()) { | 151 if (!clazz.obj()) { |
| 178 ClearException(env); | 152 ClearException(env); |
| 179 return false; | 153 return false; |
| 180 } | 154 } |
| 181 bool error = ClearException(env); | 155 bool error = ClearException(env); |
| 182 DCHECK(!error); | 156 DCHECK(!error); |
| 183 return true; | 157 return true; |
| 184 } | 158 } |
| 185 | 159 |
| 186 jmethodID GetMethodID(JNIEnv* env, | 160 template<MethodID::MethodType method_type, |
| 187 const JavaRef<jclass>& clazz, | 161 MethodID::ExceptionCheck exception_check> |
| 188 const char* method_name, | 162 jmethodID MethodID::Get(JNIEnv* env, |
| 189 const char* jni_signature) { | 163 jclass clazz, |
| 190 // clazz.env() can not be used as that may be from a different thread. | 164 const char* method_name, |
| 191 return GetMethodID(env, clazz.obj(), method_name, jni_signature); | 165 const char* jni_signature) { |
| 166 jmethodID id = method_type == METHODTYPE_STATIC ? | |
| 167 env->GetStaticMethodID(clazz, method_name, jni_signature) : | |
| 168 env->GetMethodID(clazz, method_name, jni_signature); | |
| 169 if (exception_check == EXCEPTIONCHECK_YES) { | |
| 170 CHECK(base::android::ClearException(env) || id) << | |
| 171 "Failed to find " << | |
| 172 (method_type == METHODTYPE_STATIC ? "static " : "") << | |
| 173 "method " << method_name << " " << jni_signature; | |
| 174 } else if (base::android::HasException(env)) { | |
| 175 env->ExceptionClear(); | |
| 176 } | |
| 177 return id; | |
| 192 } | 178 } |
| 193 | 179 |
| 194 jmethodID GetMethodID(JNIEnv* env, | 180 template<MethodID::MethodType method_type, |
| 195 jclass clazz, | 181 MethodID::ExceptionCheck exception_check> |
| 196 const char* method_name, | 182 jmethodID MethodID::LazyGet(JNIEnv* env, |
| 197 const char* jni_signature) { | 183 jclass clazz, |
| 198 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_YES>( | 184 const char* method_name, |
| 185 const char* jni_signature, | |
| 186 base::subtle::AtomicWord* atomic_method_id) { | |
| 187 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID), | |
| 188 AtomicWord_SmallerThan_jMethodID); | |
| 189 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id); | |
| 190 if (value) | |
| 191 return reinterpret_cast<jmethodID>(value); | |
| 192 jmethodID id = MethodID::Get<method_type, exception_check>( | |
| 199 env, clazz, method_name, jni_signature); | 193 env, clazz, method_name, jni_signature); |
| 194 base::subtle::Release_Store( | |
| 195 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id)); | |
| 196 return id; | |
| 200 } | 197 } |
| 201 | 198 |
| 202 jmethodID GetMethodIDOrNull(JNIEnv* env, | 199 // Various template instantiations. |
| 203 jclass clazz, | 200 template jmethodID MethodID::Get<MethodID::METHODTYPE_STATIC, |
| 204 const char* method_name, | 201 MethodID::EXCEPTIONCHECK_YES>( |
| 205 const char* jni_signature) { | 202 JNIEnv* env, jclass clazz, const char* method_name, |
| 206 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_NO>( | 203 const char* jni_signature); |
| 207 env, clazz, method_name, jni_signature); | |
| 208 } | |
| 209 | 204 |
| 210 jmethodID GetStaticMethodID(JNIEnv* env, | 205 template jmethodID MethodID::Get<MethodID::METHODTYPE_NORMAL, |
| 211 const JavaRef<jclass>& clazz, | 206 MethodID::EXCEPTIONCHECK_YES>( |
| 212 const char* method_name, | 207 JNIEnv* env, jclass clazz, const char* method_name, |
| 213 const char* jni_signature) { | 208 const char* jni_signature); |
| 214 return GetStaticMethodID(env, clazz.obj(), method_name, | |
| 215 jni_signature); | |
| 216 } | |
| 217 | 209 |
| 218 jmethodID GetStaticMethodID(JNIEnv* env, | 210 template jmethodID MethodID::Get<MethodID::METHODTYPE_STATIC, |
| 219 jclass clazz, | 211 MethodID::EXCEPTIONCHECK_NO>( |
| 220 const char* method_name, | 212 JNIEnv* env, jclass clazz, const char* method_name, |
| 221 const char* jni_signature) { | 213 const char* jni_signature); |
| 222 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_YES>( | |
| 223 env, clazz, method_name, jni_signature); | |
| 224 } | |
| 225 | 214 |
| 226 jmethodID GetStaticMethodIDOrNull(JNIEnv* env, | 215 template jmethodID MethodID::Get<MethodID::METHODTYPE_NORMAL, |
| 227 jclass clazz, | 216 MethodID::EXCEPTIONCHECK_NO>( |
| 228 const char* method_name, | 217 JNIEnv* env, jclass clazz, const char* method_name, |
| 229 const char* jni_signature) { | 218 const char* jni_signature); |
|
joth
2012/10/04 17:59:59
fwiw the ::Get() instantiations are not strictly n
bulach
2012/10/04 18:58:17
I'd love to delete, but I think this makes it clea
| |
| 230 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_NO>( | |
| 231 env, clazz, method_name, jni_signature); | |
| 232 } | |
| 233 | 219 |
| 234 bool HasMethod(JNIEnv* env, | 220 template jmethodID MethodID::LazyGet<MethodID::METHODTYPE_STATIC, |
| 235 const JavaRef<jclass>& clazz, | 221 MethodID::EXCEPTIONCHECK_YES>( |
| 236 const char* method_name, | 222 JNIEnv* env, jclass clazz, const char* method_name, |
| 237 const char* jni_signature) { | 223 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 238 jmethodID method_id = | 224 |
| 239 env->GetMethodID(clazz.obj(), method_name, jni_signature); | 225 template jmethodID MethodID::LazyGet<MethodID::METHODTYPE_NORMAL, |
| 240 if (!method_id) { | 226 MethodID::EXCEPTIONCHECK_YES>( |
| 241 ClearException(env); | 227 JNIEnv* env, jclass clazz, const char* method_name, |
| 242 return false; | 228 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 243 } | 229 |
| 244 bool error = ClearException(env); | 230 template jmethodID MethodID::LazyGet<MethodID::METHODTYPE_STATIC, |
| 245 DCHECK(!error); | 231 MethodID::EXCEPTIONCHECK_NO>( |
| 246 return true; | 232 JNIEnv* env, jclass clazz, const char* method_name, |
| 247 } | 233 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 234 | |
| 235 template jmethodID MethodID::LazyGet<MethodID::METHODTYPE_NORMAL, | |
| 236 MethodID::EXCEPTIONCHECK_NO>( | |
| 237 JNIEnv* env, jclass clazz, const char* method_name, | |
| 238 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); | |
| 248 | 239 |
| 249 jfieldID GetFieldID(JNIEnv* env, | 240 jfieldID GetFieldID(JNIEnv* env, |
| 250 const JavaRef<jclass>& clazz, | 241 const JavaRef<jclass>& clazz, |
| 251 const char* field_name, | 242 const char* field_name, |
| 252 const char* jni_signature) { | 243 const char* jni_signature) { |
| 253 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); | 244 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); |
| 254 CHECK(!ClearException(env) && field_id) << "Failed to find field " << | 245 CHECK(!ClearException(env) && field_id) << "Failed to find field " << |
| 255 field_name << " " << jni_signature; | 246 field_name << " " << jni_signature; |
| 256 return field_id; | 247 return field_id; |
| 257 } | 248 } |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 303 found = true; | 294 found = true; |
| 304 } | 295 } |
| 305 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); | 296 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); |
| 306 | 297 |
| 307 // Addition to the map does not invalidate this iterator. | 298 // Addition to the map does not invalidate this iterator. |
| 308 if (found) { | 299 if (found) { |
| 309 return iter->second; | 300 return iter->second; |
| 310 } | 301 } |
| 311 | 302 |
| 312 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | 303 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); |
| 313 jmethodID id = GetMethodID(env, clazz, method, jni_signature); | 304 jmethodID id = MethodID::Get< |
| 305 MethodID::METHODTYPE_NORMAL, MethodID::EXCEPTIONCHECK_YES>( | |
| 306 env, clazz.obj(), method, jni_signature); | |
| 314 | 307 |
| 315 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, | 308 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, |
| 316 kUnlocked, | 309 kUnlocked, |
| 317 kLocked) != kUnlocked) { | 310 kLocked) != kUnlocked) { |
| 318 base::PlatformThread::YieldCurrentThread(); | 311 base::PlatformThread::YieldCurrentThread(); |
| 319 } | 312 } |
| 320 // Another thread may have populated the map already. | 313 // Another thread may have populated the map already. |
| 321 std::pair<MethodIDMap::const_iterator, bool> result = | 314 std::pair<MethodIDMap::const_iterator, bool> result = |
| 322 map->insert(std::make_pair(key, id)); | 315 map->insert(std::make_pair(key, id)); |
| 323 DCHECK_EQ(id, result.first->second); | 316 DCHECK_EQ(id, result.first->second); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 355 // RVO should avoid any extra copies of the exception string. | 348 // RVO should avoid any extra copies of the exception string. |
| 356 base::android::BuildInfo::GetInstance()->set_java_exception_info( | 349 base::android::BuildInfo::GetInstance()->set_java_exception_info( |
| 357 GetJavaExceptionInfo(env, java_throwable)); | 350 GetJavaExceptionInfo(env, java_throwable)); |
| 358 | 351 |
| 359 // Now, feel good about it and die. | 352 // Now, feel good about it and die. |
| 360 CHECK(false); | 353 CHECK(false); |
| 361 } | 354 } |
| 362 | 355 |
| 363 } // namespace android | 356 } // namespace android |
| 364 } // namespace base | 357 } // namespace base |
| OLD | NEW |