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 |