| 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/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/threading/platform_thread.h" | |
| 14 | 13 |
| 15 namespace { | 14 namespace { |
| 16 using base::android::GetClass; | 15 using base::android::GetClass; |
| 17 using base::android::MethodID; | 16 using base::android::MethodID; |
| 18 using base::android::ScopedJavaLocalRef; | 17 using base::android::ScopedJavaLocalRef; |
| 19 | 18 |
| 20 struct MethodIdentifier { | |
| 21 const char* class_name; | |
| 22 const char* method; | |
| 23 const char* jni_signature; | |
| 24 | |
| 25 bool operator<(const MethodIdentifier& other) const { | |
| 26 int r = strcmp(class_name, other.class_name); | |
| 27 if (r < 0) { | |
| 28 return true; | |
| 29 } else if (r > 0) { | |
| 30 return false; | |
| 31 } | |
| 32 | |
| 33 r = strcmp(method, other.method); | |
| 34 if (r < 0) { | |
| 35 return true; | |
| 36 } else if (r > 0) { | |
| 37 return false; | |
| 38 } | |
| 39 | |
| 40 return strcmp(jni_signature, other.jni_signature) < 0; | |
| 41 } | |
| 42 }; | |
| 43 | |
| 44 typedef std::map<MethodIdentifier, jmethodID> MethodIDMap; | |
| 45 | |
| 46 const base::subtle::AtomicWord kUnlocked = 0; | |
| 47 const base::subtle::AtomicWord kLocked = 1; | |
| 48 base::subtle::AtomicWord g_method_id_map_lock = kUnlocked; | |
| 49 JavaVM* g_jvm = NULL; | 19 JavaVM* g_jvm = NULL; |
| 50 // Leak the global app context, as it is used from a non-joinable worker thread | 20 // Leak the global app context, as it is used from a non-joinable worker thread |
| 51 // that may still be running at shutdown. There is no harm in doing this. | 21 // that may still be running at shutdown. There is no harm in doing this. |
| 52 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky | 22 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky |
| 53 g_application_context = LAZY_INSTANCE_INITIALIZER; | 23 g_application_context = LAZY_INSTANCE_INITIALIZER; |
| 54 base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER; | |
| 55 | 24 |
| 56 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { | 25 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { |
| 57 ScopedJavaLocalRef<jclass> throwable_clazz = | 26 ScopedJavaLocalRef<jclass> throwable_clazz = |
| 58 GetClass(env, "java/lang/Throwable"); | 27 GetClass(env, "java/lang/Throwable"); |
| 59 jmethodID throwable_printstacktrace = | 28 jmethodID throwable_printstacktrace = |
| 60 MethodID::Get<MethodID::TYPE_INSTANCE>( | 29 MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 61 env, throwable_clazz.obj(), "printStackTrace", | 30 env, throwable_clazz.obj(), "printStackTrace", |
| 62 "(Ljava/io/PrintStream;)V"); | 31 "(Ljava/io/PrintStream;)V"); |
| 63 | 32 |
| 64 // Create an instance of ByteArrayOutputStream. | 33 // Create an instance of ByteArrayOutputStream. |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 DCHECK(!g_application_context.Get().is_null()); | 106 DCHECK(!g_application_context.Get().is_null()); |
| 138 return g_application_context.Get().obj(); | 107 return g_application_context.Get().obj(); |
| 139 } | 108 } |
| 140 | 109 |
| 141 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) { | 110 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) { |
| 142 jclass clazz = env->FindClass(class_name); | 111 jclass clazz = env->FindClass(class_name); |
| 143 CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name; | 112 CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name; |
| 144 return ScopedJavaLocalRef<jclass>(env, clazz); | 113 return ScopedJavaLocalRef<jclass>(env, clazz); |
| 145 } | 114 } |
| 146 | 115 |
| 147 bool HasClass(JNIEnv* env, const char* class_name) { | |
| 148 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | |
| 149 if (!clazz.obj()) { | |
| 150 ClearException(env); | |
| 151 return false; | |
| 152 } | |
| 153 bool error = ClearException(env); | |
| 154 DCHECK(!error); | |
| 155 return true; | |
| 156 } | |
| 157 | |
| 158 template<MethodID::Type type> | 116 template<MethodID::Type type> |
| 159 jmethodID MethodID::Get(JNIEnv* env, | 117 jmethodID MethodID::Get(JNIEnv* env, |
| 160 jclass clazz, | 118 jclass clazz, |
| 161 const char* method_name, | 119 const char* method_name, |
| 162 const char* jni_signature) { | 120 const char* jni_signature) { |
| 163 jmethodID id = type == TYPE_STATIC ? | 121 jmethodID id = type == TYPE_STATIC ? |
| 164 env->GetStaticMethodID(clazz, method_name, jni_signature) : | 122 env->GetStaticMethodID(clazz, method_name, jni_signature) : |
| 165 env->GetMethodID(clazz, method_name, jni_signature); | 123 env->GetMethodID(clazz, method_name, jni_signature); |
| 166 CHECK(base::android::ClearException(env) || id) << | 124 CHECK(base::android::ClearException(env) || id) << |
| 167 "Failed to find " << | 125 "Failed to find " << |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 200 const char* jni_signature); | 158 const char* jni_signature); |
| 201 | 159 |
| 202 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>( | 160 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>( |
| 203 JNIEnv* env, jclass clazz, const char* method_name, | 161 JNIEnv* env, jclass clazz, const char* method_name, |
| 204 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); | 162 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 205 | 163 |
| 206 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>( | 164 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>( |
| 207 JNIEnv* env, jclass clazz, const char* method_name, | 165 JNIEnv* env, jclass clazz, const char* method_name, |
| 208 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); | 166 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 209 | 167 |
| 210 jfieldID GetFieldID(JNIEnv* env, | |
| 211 const JavaRef<jclass>& clazz, | |
| 212 const char* field_name, | |
| 213 const char* jni_signature) { | |
| 214 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); | |
| 215 CHECK(!ClearException(env) && field_id) << "Failed to find field " << | |
| 216 field_name << " " << jni_signature; | |
| 217 return field_id; | |
| 218 } | |
| 219 | |
| 220 bool HasField(JNIEnv* env, | |
| 221 const JavaRef<jclass>& clazz, | |
| 222 const char* field_name, | |
| 223 const char* jni_signature) { | |
| 224 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); | |
| 225 if (!field_id) { | |
| 226 ClearException(env); | |
| 227 return false; | |
| 228 } | |
| 229 bool error = ClearException(env); | |
| 230 DCHECK(!error); | |
| 231 return true; | |
| 232 } | |
| 233 | |
| 234 jfieldID GetStaticFieldID(JNIEnv* env, | |
| 235 const JavaRef<jclass>& clazz, | |
| 236 const char* field_name, | |
| 237 const char* jni_signature) { | |
| 238 jfieldID field_id = | |
| 239 env->GetStaticFieldID(clazz.obj(), field_name, jni_signature); | |
| 240 CHECK(!ClearException(env) && field_id) << "Failed to find static field " << | |
| 241 field_name << " " << jni_signature; | |
| 242 return field_id; | |
| 243 } | |
| 244 | |
| 245 jmethodID GetMethodIDFromClassName(JNIEnv* env, | |
| 246 const char* class_name, | |
| 247 const char* method, | |
| 248 const char* jni_signature) { | |
| 249 MethodIdentifier key; | |
| 250 key.class_name = class_name; | |
| 251 key.method = method; | |
| 252 key.jni_signature = jni_signature; | |
| 253 | |
| 254 MethodIDMap* map = g_method_id_map.Pointer(); | |
| 255 bool found = false; | |
| 256 | |
| 257 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, | |
| 258 kUnlocked, | |
| 259 kLocked) != kUnlocked) { | |
| 260 base::PlatformThread::YieldCurrentThread(); | |
| 261 } | |
| 262 MethodIDMap::const_iterator iter = map->find(key); | |
| 263 if (iter != map->end()) { | |
| 264 found = true; | |
| 265 } | |
| 266 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); | |
| 267 | |
| 268 // Addition to the map does not invalidate this iterator. | |
| 269 if (found) { | |
| 270 return iter->second; | |
| 271 } | |
| 272 | |
| 273 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | |
| 274 jmethodID id = MethodID::Get<MethodID::TYPE_INSTANCE>( | |
| 275 env, clazz.obj(), method, jni_signature); | |
| 276 | |
| 277 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, | |
| 278 kUnlocked, | |
| 279 kLocked) != kUnlocked) { | |
| 280 base::PlatformThread::YieldCurrentThread(); | |
| 281 } | |
| 282 // Another thread may have populated the map already. | |
| 283 std::pair<MethodIDMap::const_iterator, bool> result = | |
| 284 map->insert(std::make_pair(key, id)); | |
| 285 DCHECK_EQ(id, result.first->second); | |
| 286 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); | |
| 287 | |
| 288 return id; | |
| 289 } | |
| 290 | |
| 291 bool HasException(JNIEnv* env) { | 168 bool HasException(JNIEnv* env) { |
| 292 return env->ExceptionCheck() != JNI_FALSE; | 169 return env->ExceptionCheck() != JNI_FALSE; |
| 293 } | 170 } |
| 294 | 171 |
| 295 bool ClearException(JNIEnv* env) { | 172 bool ClearException(JNIEnv* env) { |
| 296 if (!HasException(env)) | 173 if (!HasException(env)) |
| 297 return false; | 174 return false; |
| 298 env->ExceptionDescribe(); | 175 env->ExceptionDescribe(); |
| 299 env->ExceptionClear(); | 176 env->ExceptionClear(); |
| 300 return true; | 177 return true; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 318 // RVO should avoid any extra copies of the exception string. | 195 // RVO should avoid any extra copies of the exception string. |
| 319 base::android::BuildInfo::GetInstance()->set_java_exception_info( | 196 base::android::BuildInfo::GetInstance()->set_java_exception_info( |
| 320 GetJavaExceptionInfo(env, java_throwable)); | 197 GetJavaExceptionInfo(env, java_throwable)); |
| 321 | 198 |
| 322 // Now, feel good about it and die. | 199 // Now, feel good about it and die. |
| 323 CHECK(false); | 200 CHECK(false); |
| 324 } | 201 } |
| 325 | 202 |
| 326 } // namespace android | 203 } // namespace android |
| 327 } // namespace base | 204 } // namespace base |
| OLD | NEW |