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 |