| 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>( |
| 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>( |
| 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>( |
| 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>( |
| 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 jmethodID MethodID::Get(JNIEnv* env, |
| 188 const char* method_name, | 162 jclass clazz, |
| 189 const char* jni_signature) { | 163 const char* method_name, |
| 190 // clazz.env() can not be used as that may be from a different thread. | 164 const char* jni_signature) { |
| 191 return GetMethodID(env, clazz.obj(), method_name, jni_signature); | 165 jmethodID id = method_type == METHODTYPE_STATIC ? |
| 166 env->GetStaticMethodID(clazz, method_name, jni_signature) : |
| 167 env->GetMethodID(clazz, method_name, jni_signature); |
| 168 CHECK(base::android::ClearException(env) || id) << |
| 169 "Failed to find " << |
| 170 (method_type == METHODTYPE_STATIC ? "static " : "") << |
| 171 "method " << method_name << " " << jni_signature; |
| 172 return id; |
| 192 } | 173 } |
| 193 | 174 |
| 194 jmethodID GetMethodID(JNIEnv* env, | 175 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call |
| 195 jclass clazz, | 176 // into ::Get() above. If there's a race, it's ok since the values are the same |
| 196 const char* method_name, | 177 // (and the duplicated effort will happen only once). |
| 197 const char* jni_signature) { | 178 template<MethodID::MethodType method_type> |
| 198 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_YES>( | 179 jmethodID MethodID::LazyGet(JNIEnv* env, |
| 180 jclass clazz, |
| 181 const char* method_name, |
| 182 const char* jni_signature, |
| 183 base::subtle::AtomicWord* atomic_method_id) { |
| 184 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID), |
| 185 AtomicWord_SmallerThan_jMethodID); |
| 186 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id); |
| 187 if (value) |
| 188 return reinterpret_cast<jmethodID>(value); |
| 189 jmethodID id = MethodID::Get<method_type>( |
| 199 env, clazz, method_name, jni_signature); | 190 env, clazz, method_name, jni_signature); |
| 191 base::subtle::Release_Store( |
| 192 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id)); |
| 193 return id; |
| 200 } | 194 } |
| 201 | 195 |
| 202 jmethodID GetMethodIDOrNull(JNIEnv* env, | 196 // Various template instantiations. |
| 203 jclass clazz, | 197 template jmethodID MethodID::Get<MethodID::METHODTYPE_STATIC>( |
| 204 const char* method_name, | 198 JNIEnv* env, jclass clazz, const char* method_name, |
| 205 const char* jni_signature) { | 199 const char* jni_signature); |
| 206 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_NO>( | |
| 207 env, clazz, method_name, jni_signature); | |
| 208 } | |
| 209 | 200 |
| 210 jmethodID GetStaticMethodID(JNIEnv* env, | 201 template jmethodID MethodID::Get<MethodID::METHODTYPE_NORMAL>( |
| 211 const JavaRef<jclass>& clazz, | 202 JNIEnv* env, jclass clazz, const char* method_name, |
| 212 const char* method_name, | 203 const char* jni_signature); |
| 213 const char* jni_signature) { | |
| 214 return GetStaticMethodID(env, clazz.obj(), method_name, | |
| 215 jni_signature); | |
| 216 } | |
| 217 | 204 |
| 218 jmethodID GetStaticMethodID(JNIEnv* env, | 205 template jmethodID MethodID::LazyGet<MethodID::METHODTYPE_STATIC>( |
| 219 jclass clazz, | 206 JNIEnv* env, jclass clazz, const char* method_name, |
| 220 const char* method_name, | 207 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 221 const char* jni_signature) { | |
| 222 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_YES>( | |
| 223 env, clazz, method_name, jni_signature); | |
| 224 } | |
| 225 | 208 |
| 226 jmethodID GetStaticMethodIDOrNull(JNIEnv* env, | 209 template jmethodID MethodID::LazyGet<MethodID::METHODTYPE_NORMAL>( |
| 227 jclass clazz, | 210 JNIEnv* env, jclass clazz, const char* method_name, |
| 228 const char* method_name, | 211 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 229 const char* jni_signature) { | |
| 230 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_NO>( | |
| 231 env, clazz, method_name, jni_signature); | |
| 232 } | |
| 233 | |
| 234 bool HasMethod(JNIEnv* env, | |
| 235 const JavaRef<jclass>& clazz, | |
| 236 const char* method_name, | |
| 237 const char* jni_signature) { | |
| 238 jmethodID method_id = | |
| 239 env->GetMethodID(clazz.obj(), method_name, jni_signature); | |
| 240 if (!method_id) { | |
| 241 ClearException(env); | |
| 242 return false; | |
| 243 } | |
| 244 bool error = ClearException(env); | |
| 245 DCHECK(!error); | |
| 246 return true; | |
| 247 } | |
| 248 | 212 |
| 249 jfieldID GetFieldID(JNIEnv* env, | 213 jfieldID GetFieldID(JNIEnv* env, |
| 250 const JavaRef<jclass>& clazz, | 214 const JavaRef<jclass>& clazz, |
| 251 const char* field_name, | 215 const char* field_name, |
| 252 const char* jni_signature) { | 216 const char* jni_signature) { |
| 253 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); | 217 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); |
| 254 CHECK(!ClearException(env) && field_id) << "Failed to find field " << | 218 CHECK(!ClearException(env) && field_id) << "Failed to find field " << |
| 255 field_name << " " << jni_signature; | 219 field_name << " " << jni_signature; |
| 256 return field_id; | 220 return field_id; |
| 257 } | 221 } |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 303 found = true; | 267 found = true; |
| 304 } | 268 } |
| 305 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); | 269 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); |
| 306 | 270 |
| 307 // Addition to the map does not invalidate this iterator. | 271 // Addition to the map does not invalidate this iterator. |
| 308 if (found) { | 272 if (found) { |
| 309 return iter->second; | 273 return iter->second; |
| 310 } | 274 } |
| 311 | 275 |
| 312 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | 276 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); |
| 313 jmethodID id = GetMethodID(env, clazz, method, jni_signature); | 277 jmethodID id = MethodID::Get<MethodID::METHODTYPE_NORMAL>( |
| 278 env, clazz.obj(), method, jni_signature); |
| 314 | 279 |
| 315 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, | 280 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, |
| 316 kUnlocked, | 281 kUnlocked, |
| 317 kLocked) != kUnlocked) { | 282 kLocked) != kUnlocked) { |
| 318 base::PlatformThread::YieldCurrentThread(); | 283 base::PlatformThread::YieldCurrentThread(); |
| 319 } | 284 } |
| 320 // Another thread may have populated the map already. | 285 // Another thread may have populated the map already. |
| 321 std::pair<MethodIDMap::const_iterator, bool> result = | 286 std::pair<MethodIDMap::const_iterator, bool> result = |
| 322 map->insert(std::make_pair(key, id)); | 287 map->insert(std::make_pair(key, id)); |
| 323 DCHECK_EQ(id, result.first->second); | 288 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. | 320 // RVO should avoid any extra copies of the exception string. |
| 356 base::android::BuildInfo::GetInstance()->set_java_exception_info( | 321 base::android::BuildInfo::GetInstance()->set_java_exception_info( |
| 357 GetJavaExceptionInfo(env, java_throwable)); | 322 GetJavaExceptionInfo(env, java_throwable)); |
| 358 | 323 |
| 359 // Now, feel good about it and die. | 324 // Now, feel good about it and die. |
| 360 CHECK(false); | 325 CHECK(false); |
| 361 } | 326 } |
| 362 | 327 |
| 363 } // namespace android | 328 } // namespace android |
| 364 } // namespace base | 329 } // namespace base |
| OLD | NEW |