Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Side by Side Diff: base/android/jni_android.cc

Issue 11038015: Android: lazy initialization for method id. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/android/jni_android.h ('k') | base/android/jni_android_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 22 matching lines...) Expand all
51 // Leak the global app context, as it is used from a non-joinable worker thread 50 // Leak the global app context, as it is used from a non-joinable worker thread
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> g_method_id_map = LAZY_INSTANCE_INITIALIZER; 54 base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER;
56 55
57 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { 56 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
58 ScopedJavaLocalRef<jclass> throwable_clazz = 57 ScopedJavaLocalRef<jclass> throwable_clazz =
59 GetClass(env, "java/lang/Throwable"); 58 GetClass(env, "java/lang/Throwable");
60 jmethodID throwable_printstacktrace = 59 jmethodID throwable_printstacktrace =
61 GetMethodID(env, throwable_clazz, "printStackTrace", 60 MethodID::Get<MethodID::TYPE_INSTANCE>(
62 "(Ljava/io/PrintStream;)V"); 61 env, throwable_clazz.obj(), "printStackTrace",
62 "(Ljava/io/PrintStream;)V");
63 63
64 // Create an instance of ByteArrayOutputStream. 64 // Create an instance of ByteArrayOutputStream.
65 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz = 65 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz =
66 GetClass(env, "java/io/ByteArrayOutputStream"); 66 GetClass(env, "java/io/ByteArrayOutputStream");
67 jmethodID bytearray_output_stream_constructor = 67 jmethodID bytearray_output_stream_constructor =
68 GetMethodID(env, bytearray_output_stream_clazz, "<init>", "()V"); 68 MethodID::Get<MethodID::TYPE_INSTANCE>(
69 env, bytearray_output_stream_clazz.obj(), "<init>", "()V");
69 jmethodID bytearray_output_stream_tostring = 70 jmethodID bytearray_output_stream_tostring =
70 GetMethodID(env, bytearray_output_stream_clazz, "toString", 71 MethodID::Get<MethodID::TYPE_INSTANCE>(
71 "()Ljava/lang/String;"); 72 env, bytearray_output_stream_clazz.obj(), "toString",
73 "()Ljava/lang/String;");
72 ScopedJavaLocalRef<jobject> bytearray_output_stream(env, 74 ScopedJavaLocalRef<jobject> bytearray_output_stream(env,
73 env->NewObject(bytearray_output_stream_clazz.obj(), 75 env->NewObject(bytearray_output_stream_clazz.obj(),
74 bytearray_output_stream_constructor)); 76 bytearray_output_stream_constructor));
75 77
76 // Create an instance of PrintStream. 78 // Create an instance of PrintStream.
77 ScopedJavaLocalRef<jclass> printstream_clazz = 79 ScopedJavaLocalRef<jclass> printstream_clazz =
78 GetClass(env, "java/io/PrintStream"); 80 GetClass(env, "java/io/PrintStream");
79 jmethodID printstream_constructor = 81 jmethodID printstream_constructor =
80 GetMethodID(env, printstream_clazz, "<init>", 82 MethodID::Get<MethodID::TYPE_INSTANCE>(
81 "(Ljava/io/OutputStream;)V"); 83 env, printstream_clazz.obj(), "<init>",
84 "(Ljava/io/OutputStream;)V");
82 ScopedJavaLocalRef<jobject> printstream(env, 85 ScopedJavaLocalRef<jobject> printstream(env,
83 env->NewObject(printstream_clazz.obj(), printstream_constructor, 86 env->NewObject(printstream_clazz.obj(), printstream_constructor,
84 bytearray_output_stream.obj())); 87 bytearray_output_stream.obj()));
85 88
86 // Call Throwable.printStackTrace(PrintStream) 89 // Call Throwable.printStackTrace(PrintStream)
87 env->CallVoidMethod(java_throwable, throwable_printstacktrace, 90 env->CallVoidMethod(java_throwable, throwable_printstacktrace,
88 printstream.obj()); 91 printstream.obj());
89 92
90 // Call ByteArrayOutputStream.toString() 93 // Call ByteArrayOutputStream.toString()
91 ScopedJavaLocalRef<jstring> exception_string( 94 ScopedJavaLocalRef<jstring> exception_string(
92 env, static_cast<jstring>( 95 env, static_cast<jstring>(
93 env->CallObjectMethod(bytearray_output_stream.obj(), 96 env->CallObjectMethod(bytearray_output_stream.obj(),
94 bytearray_output_stream_tostring))); 97 bytearray_output_stream_tostring)));
95 98
96 return ConvertJavaStringToUTF8(exception_string); 99 return ConvertJavaStringToUTF8(exception_string);
97 } 100 }
98 101
99 enum MethodType {
100 METHODTYPE_STATIC,
101 METHODTYPE_NORMAL,
102 };
103
104 enum ExceptionCheck {
105 EXCEPTIONCHECK_YES,
106 EXCEPTIONCHECK_NO,
107 };
108
109 template<MethodType method_type, ExceptionCheck exception_check>
110 jmethodID GetMethodIDInternal(JNIEnv* env,
111 jclass clazz,
112 const char* method_name,
113 const char* jni_signature) {
114 jmethodID method_id = method_type == METHODTYPE_STATIC ?
115 env->GetStaticMethodID(clazz, method_name, jni_signature) :
116 env->GetMethodID(clazz, method_name, jni_signature);
117 if (exception_check == EXCEPTIONCHECK_YES) {
118 CHECK(!base::android::ClearException(env) && method_id) <<
119 "Failed to find " <<
120 (method_type == METHODTYPE_STATIC ? "static " : "") <<
121 "method " << method_name << " " << jni_signature;
122 } else if (base::android::HasException(env)) {
123 env->ExceptionClear();
124 }
125 return method_id;
126 }
127
128 } // namespace 102 } // namespace
129 103
130 namespace base { 104 namespace base {
131 namespace android { 105 namespace android {
132 106
133 JNIEnv* AttachCurrentThread() { 107 JNIEnv* AttachCurrentThread() {
134 DCHECK(g_jvm); 108 DCHECK(g_jvm);
135 JNIEnv* env = NULL; 109 JNIEnv* env = NULL;
136 jint ret = g_jvm->AttachCurrentThread(&env, NULL); 110 jint ret = g_jvm->AttachCurrentThread(&env, NULL);
137 DCHECK_EQ(JNI_OK, ret); 111 DCHECK_EQ(JNI_OK, ret);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); 148 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
175 if (!clazz.obj()) { 149 if (!clazz.obj()) {
176 ClearException(env); 150 ClearException(env);
177 return false; 151 return false;
178 } 152 }
179 bool error = ClearException(env); 153 bool error = ClearException(env);
180 DCHECK(!error); 154 DCHECK(!error);
181 return true; 155 return true;
182 } 156 }
183 157
184 jmethodID GetMethodID(JNIEnv* env, 158 template<MethodID::Type type>
185 const JavaRef<jclass>& clazz, 159 jmethodID MethodID::Get(JNIEnv* env,
186 const char* method_name, 160 jclass clazz,
187 const char* jni_signature) { 161 const char* method_name,
188 // clazz.env() can not be used as that may be from a different thread. 162 const char* jni_signature) {
189 return GetMethodID(env, clazz.obj(), method_name, jni_signature); 163 jmethodID id = type == TYPE_STATIC ?
164 env->GetStaticMethodID(clazz, method_name, jni_signature) :
165 env->GetMethodID(clazz, method_name, jni_signature);
166 CHECK(base::android::ClearException(env) || id) <<
167 "Failed to find " <<
168 (type == TYPE_STATIC ? "static " : "") <<
169 "method " << method_name << " " << jni_signature;
170 return id;
190 } 171 }
191 172
192 jmethodID GetMethodID(JNIEnv* env, 173 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
193 jclass clazz, 174 // into ::Get() above. If there's a race, it's ok since the values are the same
194 const char* method_name, 175 // (and the duplicated effort will happen only once).
195 const char* jni_signature) { 176 template<MethodID::Type type>
196 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_YES>( 177 jmethodID MethodID::LazyGet(JNIEnv* env,
197 env, clazz, method_name, jni_signature); 178 jclass clazz,
179 const char* method_name,
180 const char* jni_signature,
181 base::subtle::AtomicWord* atomic_method_id) {
182 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID),
183 AtomicWord_SmallerThan_jMethodID);
184 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id);
185 if (value)
186 return reinterpret_cast<jmethodID>(value);
187 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
188 base::subtle::Release_Store(
189 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id));
190 return id;
198 } 191 }
199 192
200 jmethodID GetMethodIDOrNull(JNIEnv* env, 193 // Various template instantiations.
201 jclass clazz, 194 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
202 const char* method_name, 195 JNIEnv* env, jclass clazz, const char* method_name,
203 const char* jni_signature) { 196 const char* jni_signature);
204 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_NO>(
205 env, clazz, method_name, jni_signature);
206 }
207 197
208 jmethodID GetStaticMethodID(JNIEnv* env, 198 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
209 const JavaRef<jclass>& clazz, 199 JNIEnv* env, jclass clazz, const char* method_name,
210 const char* method_name, 200 const char* jni_signature);
211 const char* jni_signature) {
212 return GetStaticMethodID(env, clazz.obj(), method_name,
213 jni_signature);
214 }
215 201
216 jmethodID GetStaticMethodID(JNIEnv* env, 202 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
217 jclass clazz, 203 JNIEnv* env, jclass clazz, const char* method_name,
218 const char* method_name, 204 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
219 const char* jni_signature) {
220 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_YES>(
221 env, clazz, method_name, jni_signature);
222 }
223 205
224 jmethodID GetStaticMethodIDOrNull(JNIEnv* env, 206 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
225 jclass clazz, 207 JNIEnv* env, jclass clazz, const char* method_name,
226 const char* method_name, 208 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
227 const char* jni_signature) {
228 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_NO>(
229 env, clazz, method_name, jni_signature);
230 }
231
232 bool HasMethod(JNIEnv* env,
233 const JavaRef<jclass>& clazz,
234 const char* method_name,
235 const char* jni_signature) {
236 jmethodID method_id =
237 env->GetMethodID(clazz.obj(), method_name, jni_signature);
238 if (!method_id) {
239 ClearException(env);
240 return false;
241 }
242 bool error = ClearException(env);
243 DCHECK(!error);
244 return true;
245 }
246 209
247 jfieldID GetFieldID(JNIEnv* env, 210 jfieldID GetFieldID(JNIEnv* env,
248 const JavaRef<jclass>& clazz, 211 const JavaRef<jclass>& clazz,
249 const char* field_name, 212 const char* field_name,
250 const char* jni_signature) { 213 const char* jni_signature) {
251 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); 214 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature);
252 CHECK(!ClearException(env) && field_id) << "Failed to find field " << 215 CHECK(!ClearException(env) && field_id) << "Failed to find field " <<
253 field_name << " " << jni_signature; 216 field_name << " " << jni_signature;
254 return field_id; 217 return field_id;
255 } 218 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 found = true; 264 found = true;
302 } 265 }
303 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); 266 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
304 267
305 // Addition to the map does not invalidate this iterator. 268 // Addition to the map does not invalidate this iterator.
306 if (found) { 269 if (found) {
307 return iter->second; 270 return iter->second;
308 } 271 }
309 272
310 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); 273 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
311 jmethodID id = GetMethodID(env, clazz, method, jni_signature); 274 jmethodID id = MethodID::Get<MethodID::TYPE_INSTANCE>(
275 env, clazz.obj(), method, jni_signature);
312 276
313 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, 277 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
314 kUnlocked, 278 kUnlocked,
315 kLocked) != kUnlocked) { 279 kLocked) != kUnlocked) {
316 base::PlatformThread::YieldCurrentThread(); 280 base::PlatformThread::YieldCurrentThread();
317 } 281 }
318 // Another thread may have populated the map already. 282 // Another thread may have populated the map already.
319 std::pair<MethodIDMap::const_iterator, bool> result = 283 std::pair<MethodIDMap::const_iterator, bool> result =
320 map->insert(std::make_pair(key, id)); 284 map->insert(std::make_pair(key, id));
321 DCHECK_EQ(id, result.first->second); 285 DCHECK_EQ(id, result.first->second);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 // RVO should avoid any extra copies of the exception string. 318 // RVO should avoid any extra copies of the exception string.
355 base::android::BuildInfo::GetInstance()->set_java_exception_info( 319 base::android::BuildInfo::GetInstance()->set_java_exception_info(
356 GetJavaExceptionInfo(env, java_throwable)); 320 GetJavaExceptionInfo(env, java_throwable));
357 321
358 // Now, feel good about it and die. 322 // Now, feel good about it and die.
359 CHECK(false); 323 CHECK(false);
360 } 324 }
361 325
362 } // namespace android 326 } // namespace android
363 } // namespace base 327 } // namespace base
OLDNEW
« no previous file with comments | « base/android/jni_android.h ('k') | base/android/jni_android_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698