diff options
author | peter@chromium.org <peter@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-09 21:02:27 +0000 |
---|---|---|
committer | peter@chromium.org <peter@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-09 21:02:27 +0000 |
commit | fe0f1aba6584072a969924656771becb71589fec (patch) | |
tree | f74019055cf4b2193f4b05e247b0e42c3d676c0b /base/android | |
parent | 1153596a4543e779244f410b2ecf804d9c8c54e9 (diff) | |
download | chromium_src-fe0f1aba6584072a969924656771becb71589fec.zip chromium_src-fe0f1aba6584072a969924656771becb71589fec.tar.gz chromium_src-fe0f1aba6584072a969924656771becb71589fec.tar.bz2 |
Upstream Android JNI code, allowing us to use more ScopedJava references.
BUG=
TEST=
Review URL: http://codereview.chromium.org/9358028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@121284 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/android')
-rw-r--r-- | base/android/jni_android.cc | 154 | ||||
-rw-r--r-- | base/android/jni_android.h | 86 | ||||
-rw-r--r-- | base/android/jni_array.cc | 54 | ||||
-rw-r--r-- | base/android/jni_array.h | 26 | ||||
-rw-r--r-- | base/android/jni_array_unittest.cc | 31 | ||||
-rw-r--r-- | base/android/jni_string.cc | 36 | ||||
-rw-r--r-- | base/android/jni_string.h | 11 | ||||
-rw-r--r-- | base/android/scoped_java_ref.cc | 23 | ||||
-rw-r--r-- | base/android/scoped_java_ref.h | 12 | ||||
-rw-r--r-- | base/android/scoped_java_ref_unittest.cc | 53 |
10 files changed, 358 insertions, 128 deletions
diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc index f99f1ab..69399ad 100644 --- a/base/android/jni_android.cc +++ b/base/android/jni_android.cc @@ -12,8 +12,11 @@ #include "base/threading/platform_thread.h" namespace { -JavaVM* g_jvm = 0; -jobject g_application_context = NULL; + +JavaVM* g_jvm = NULL; + +base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> > + g_application_context = LAZY_INSTANCE_INITIALIZER; struct MethodIdentifier { const char* class_name; @@ -73,13 +76,106 @@ void InitVM(JavaVM* vm) { } void InitApplicationContext(const JavaRef<jobject>& context) { - DCHECK(!g_application_context); - g_application_context = context.env()->NewGlobalRef(context.obj()); + DCHECK(g_application_context.Get().is_null()); + g_application_context.Get().Reset(context); +} + +const jobject GetApplicationContext() { + DCHECK(!g_application_context.Get().is_null()); + return g_application_context.Get().obj(); +} + +ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) { + jclass clazz = env->FindClass(class_name); + CHECK(clazz && !ClearException(env)) << "Failed to find class " << class_name; + return ScopedJavaLocalRef<jclass>(env, clazz); +} + +bool HasClass(JNIEnv* env, const char* class_name) { + ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); + if (!clazz.obj()) { + ClearException(env); + return false; + } + bool error = ClearException(env); + DCHECK(!error); + return true; +} + +jmethodID GetMethodID(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* method_name, + const char* jni_signature) { + jmethodID method_id = + env->GetMethodID(clazz.obj(), method_name, jni_signature); + CHECK(method_id && !ClearException(env)) << "Failed to find method " << + method_name << " " << jni_signature; + return method_id; +} + +jmethodID GetStaticMethodID(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* method_name, + const char* jni_signature) { + jmethodID method_id = + env->GetStaticMethodID(clazz.obj(), method_name, jni_signature); + CHECK(method_id && !ClearException(env)) << "Failed to find static method " << + method_name << " " << jni_signature; + return method_id; +} + +bool HasMethod(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* method_name, + const char* jni_signature) { + jmethodID method_id = + env->GetMethodID(clazz.obj(), method_name, jni_signature); + if (!method_id) { + ClearException(env); + return false; + } + bool error = ClearException(env); + DCHECK(!error); + return true; } -jobject GetApplicationContext() { - DCHECK(g_application_context); - return g_application_context; +jfieldID GetFieldID(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* field_name, + const char* jni_signature) { + jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); + CHECK(field_id && !ClearException(env)) << "Failed to find field " << + field_name << " " << jni_signature; + bool error = ClearException(env); + DCHECK(!error); + return field_id; +} + +bool HasField(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* field_name, + const char* jni_signature) { + jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); + if (!field_id) { + ClearException(env); + return false; + } + bool error = ClearException(env); + DCHECK(!error); + return true; +} + +jfieldID GetStaticFieldID(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* field_name, + const char* jni_signature) { + jfieldID field_id = + env->GetStaticFieldID(clazz.obj(), field_name, jni_signature); + CHECK(field_id && !ClearException(env)) << "Failed to find static field " << + field_name << " " << jni_signature; + bool error = ClearException(env); + DCHECK(!error); + return field_id; } jmethodID GetMethodIDFromClassName(JNIEnv* env, @@ -111,7 +207,7 @@ jmethodID GetMethodIDFromClassName(JNIEnv* env, } ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); - jmethodID id = GetMethodID(env, clazz.obj(), method, jni_signature); + jmethodID id = GetMethodID(env, clazz, method, jni_signature); while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, kUnlocked, @@ -127,43 +223,23 @@ jmethodID GetMethodIDFromClassName(JNIEnv* env, return id; } -jmethodID GetMethodID(JNIEnv* env, - jclass clazz, - const char* const method, - const char* const jni_signature) { - jmethodID id = env->GetMethodID(clazz, method, jni_signature); - DCHECK(id) << method; - CheckException(env); - return id; +bool HasException(JNIEnv* env) { + return env->ExceptionCheck() != JNI_FALSE; } -jmethodID GetStaticMethodID(JNIEnv* env, - jclass clazz, - const char* const method, - const char* const jni_signature) { - jmethodID id = env->GetStaticMethodID(clazz, method, jni_signature); - DCHECK(id) << method; - CheckException(env); - return id; -} - -jfieldID GetFieldID(JNIEnv* env, - jclass clazz, - const char* field, - const char* jni_signature) { - jfieldID id = env->GetFieldID(clazz, field, jni_signature); - DCHECK(id) << field; - CheckException(env); - return id; -} - -bool CheckException(JNIEnv* env) { - if (env->ExceptionCheck() == JNI_FALSE) +bool ClearException(JNIEnv* env) { + if (!HasException(env)) return false; - env->ExceptionDescribe(); env->ExceptionClear(); return true; } +void CheckException(JNIEnv* env) { + if (HasException(env)) { + env->ExceptionDescribe(); + CHECK(false); + } +} + } // namespace android } // namespace base diff --git a/base/android/jni_android.h b/base/android/jni_android.h index a5571e0..0e7b557 100644 --- a/base/android/jni_android.h +++ b/base/android/jni_android.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -26,13 +26,45 @@ void InitVM(JavaVM* vm); // Initializes the global application context object. The |context| can be any // valid reference to the application context. Internally holds a global ref to // the context. InitVM and InitApplicationContext maybe called in either order. -// TODO: We might combine InitVM() and InitApplicationContext() into one method. void InitApplicationContext(const JavaRef<jobject>& context); // Gets a global ref to the application context set with // InitApplicationContext(). Ownership is retained by the function - the caller // must NOT release it. -jobject GetApplicationContext(); +const jobject GetApplicationContext(); + +// Finds the class named |class_name| and returns it. +// Use this method instead of invoking directly the JNI FindClass method (to +// prevent leaking local references). +// This method triggers a fatal assertion if the class could not be found. +// Use HasClass if you need to check whether the class exists. +ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name); + +// Returns true iff the class |class_name| could be found. +bool HasClass(JNIEnv* env, const char* class_name); + +// Returns the method ID for the method with the specified name and signature. +// This method triggers a fatal assertion if the method could not be found. +// Use HasMethod if you need to check whether a method exists. +jmethodID GetMethodID(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* method_name, + const char* jni_signature); + +// Returns the method ID for the static method with the specified name and +// signature. +// This method triggers a fatal assertion if the method could not be found. +// Use HasMethod if you need to check whether a method exists. +jmethodID GetStaticMethodID(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* method_name, + const char* jni_signature); + +// Returns true iff |clazz| has a method with the specified name and signature. +bool HasMethod(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* method_name, + const char* jni_signature); // Gets the method ID from the class name. Clears the pending Java exception // and returns NULL if the method is not found. Caches results. Note that @@ -46,30 +78,36 @@ jmethodID GetMethodIDFromClassName(JNIEnv* env, const char* method, const char* jni_signature); -// Get the method ID for a method. Will clear the pending Java -// exception and return 0 if the method is not found. -jmethodID GetMethodID(JNIEnv* env, - jclass clazz, - const char* const method, - const char* const jni_signature); - -// Get the method ID for a class static method. Will clear the pending Java -// exception and return 0 if the method is not found. -jmethodID GetStaticMethodID(JNIEnv* env, - jclass clazz, - const char* const method, - const char* const jni_signature); - -// Gets the field ID for a class field. Clears the pending Java exception and -// returns NULL if the field is not found. +// Gets the field ID for a class field. +// This method triggers a fatal assertion if the field could not be found. jfieldID GetFieldID(JNIEnv* env, - jclass clazz, - const char* field, + const JavaRef<jclass>& clazz, + const char* field_name, const char* jni_signature); -// Returns true if an exception is pending in the provided JNIEnv*. If an -// exception is pending, this function prints and then clears it. -bool CheckException(JNIEnv* env); +// Returns true if |clazz| as a field with the given name and signature. +// TODO(jcivelli): Determine whether we explicitly have to pass the environment. +bool HasField(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* field_name, + const char* jni_signature); + +// Gets the field ID for a static class field. +// This method triggers a fatal assertion if the field could not be found. +jfieldID GetStaticFieldID(JNIEnv* env, + const JavaRef<jclass>& clazz, + const char* field_name, + const char* jni_signature); + +// Returns true if an exception is pending in the provided JNIEnv*. +bool HasException(JNIEnv* env); + +// If an exception is pending in the provided JNIEnv*, this function clears it +// and returns true. +bool ClearException(JNIEnv* env); + +// This function will call CHECK() macro if there's any pending exception. +void CheckException(JNIEnv* env); } // namespace android } // namespace base diff --git a/base/android/jni_array.cc b/base/android/jni_array.cc index ae2f185..8e0547c 100644 --- a/base/android/jni_array.cc +++ b/base/android/jni_array.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,15 +6,13 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" -#include "base/android/scoped_java_ref.h" #include "base/logging.h" namespace base { namespace android { -jbyteArray ToJavaByteArray(JNIEnv* env, - const unsigned char* bytes, - size_t len) { +ScopedJavaLocalRef<jbyteArray> ToJavaByteArray( + JNIEnv* env, const uint8* bytes, size_t len) { jbyteArray byte_array = env->NewByteArray(len); CheckException(env); DCHECK(byte_array); @@ -24,37 +22,55 @@ jbyteArray ToJavaByteArray(JNIEnv* env, env->ReleaseByteArrayElements(byte_array, elements, 0); CheckException(env); - return byte_array; + return ScopedJavaLocalRef<jbyteArray>(env, byte_array); } -jobjectArray ToJavaArrayOfByteArray(JNIEnv* env, - const std::vector<std::string>& v) { - ScopedJavaLocalRef<jclass> byte_array_clazz(env, env->FindClass("[B")); +ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray( + JNIEnv* env, const std::vector<std::string>& v) { + ScopedJavaLocalRef<jclass> byte_array_clazz = GetClass(env, "[B"); jobjectArray joa = env->NewObjectArray(v.size(), byte_array_clazz.obj(), NULL); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { - ScopedJavaLocalRef<jobject> byte_array(env, ToJavaByteArray(env, - reinterpret_cast<const uint8*>(v[i].data()), v[i].length())); + ScopedJavaLocalRef<jbyteArray> byte_array = ToJavaByteArray(env, + reinterpret_cast<const uint8*>(v[i].data()), v[i].length()); env->SetObjectArrayElement(joa, i, byte_array.obj()); } - return joa; + return ScopedJavaLocalRef<jobjectArray>(env, joa); } -jobjectArray ToJavaArrayOfStrings(JNIEnv* env, - const std::vector<std::string>& v) { - ScopedJavaLocalRef<jclass> string_clazz(env, - env->FindClass("java/lang/String")); +ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings( + JNIEnv* env, const std::vector<std::string>& v) { + ScopedJavaLocalRef<jclass> string_clazz = GetClass(env, "java/lang/String"); jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), NULL); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { - ScopedJavaLocalRef<jstring> item(env, - ConvertUTF8ToJavaString(env, v[i])); + ScopedJavaLocalRef<jstring> item = ConvertUTF8ToJavaString(env, v[i]); env->SetObjectArrayElement(joa, i, item.obj()); } - return joa; + return ScopedJavaLocalRef<jobjectArray>(env, joa); +} + +void AppendJavaByteArrayToByteVector(JNIEnv* env, + jbyteArray byte_array, + std::vector<uint8>* out) { + DCHECK(out); + if (!byte_array) + return; + jsize len = env->GetArrayLength(byte_array); + jbyte* bytes = env->GetByteArrayElements(byte_array, NULL); + out->insert(out->end(), bytes, bytes + len); + env->ReleaseByteArrayElements(byte_array, bytes, JNI_ABORT); +} + +void JavaByteArrayToByteVector(JNIEnv* env, + jbyteArray byte_array, + std::vector<uint8>* out) { + DCHECK(out); + out->clear(); + AppendJavaByteArrayToByteVector(env, byte_array, out); } } // namespace android diff --git a/base/android/jni_array.h b/base/android/jni_array.h index edf05c6..fea5548 100644 --- a/base/android/jni_array.h +++ b/base/android/jni_array.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -9,18 +9,32 @@ #include <string> #include <vector> +#include "base/android/scoped_java_ref.h" +#include "base/basictypes.h" + namespace base { namespace android { // Returns a new Java byte array converted from the given bytes array. -jbyteArray ToJavaByteArray(JNIEnv* env, const unsigned char* bytes, size_t len); +ScopedJavaLocalRef<jbyteArray> ToJavaByteArray( + JNIEnv* env, const uint8* bytes, size_t len); // Returns a array of Java byte array converted from |v|. -jobjectArray ToJavaArrayOfByteArray(JNIEnv* env, - const std::vector<std::string>& v); +ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray( + JNIEnv* env, const std::vector<std::string>& v); + +ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings( + JNIEnv* env, const std::vector<std::string>& v); + +// Appends the Java bytes in |bytes_array| onto the end of |out|. +void AppendJavaByteArrayToByteVector(JNIEnv* env, + jbyteArray byte_array, + std::vector<uint8>* out); -jobjectArray ToJavaArrayOfStrings(JNIEnv* env, - const std::vector<std::string>& v); +// Replaces the content of |out| with the Java bytes in |bytes_array|. +void JavaByteArrayToByteVector(JNIEnv* env, + jbyteArray byte_array, + std::vector<uint8>* out); } // namespace android } // namespace base diff --git a/base/android/jni_array_unittest.cc b/base/android/jni_array_unittest.cc new file mode 100644 index 0000000..7529335 --- /dev/null +++ b/base/android/jni_array_unittest.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/jni_array.h" + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace android { + +TEST(JniArray, BasicConversions) { + const uint8 kBytes[] = { 0, 1, 2, 3 }; + const size_t kLen = arraysize(kBytes); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jbyteArray> bytes = ToJavaByteArray(env, kBytes, kLen); + ASSERT_TRUE(bytes.obj()); + + std::vector<uint8> vec(5); + JavaByteArrayToByteVector(env, bytes.obj(), &vec); + EXPECT_EQ(4U, vec.size()); + EXPECT_EQ(std::vector<uint8>(kBytes, kBytes + kLen), vec); + + AppendJavaByteArrayToByteVector(env, bytes.obj(), &vec); + EXPECT_EQ(8U, vec.size()); +} + +} // namespace android +} // namespace base diff --git a/base/android/jni_string.cc b/base/android/jni_string.cc index 26e7114..94ec0df 100644 --- a/base/android/jni_string.cc +++ b/base/android/jni_string.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,17 @@ #include "base/logging.h" #include "base/utf_string_conversions.h" +namespace { + +// Internal version that does not use a scoped local pointer. +jstring ConvertUTF16ToJavaStringImpl(JNIEnv* env, const string16& str) { + jstring result = env->NewString(str.data(), str.length()); + base::android::CheckException(env); + return result; +} + +} + namespace base { namespace android { @@ -17,7 +28,12 @@ std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str) { return UTF16ToUTF8(ConvertJavaStringToUTF16(env, str)); } -jstring ConvertUTF8ToJavaString(JNIEnv* env, const base::StringPiece& str) { +std::string ConvertJavaStringToUTF8(const JavaRef<jstring>& str) { + return ConvertJavaStringToUTF8(str.env(), str.obj()); +} + +ScopedJavaLocalRef<jstring> ConvertUTF8ToJavaString( + JNIEnv* env, const base::StringPiece& str) { // JNI's NewStringUTF expects "modified" UTF8 so instead create the string // via our own UTF16 conversion utility. // Further, Dalvik requires the string passed into NewStringUTF() to come from @@ -25,7 +41,8 @@ jstring ConvertUTF8ToJavaString(JNIEnv* env, const base::StringPiece& str) { // it gets here, so constructing via UTF16 side-steps this issue. // (Dalvik stores strings internally as UTF16 anyway, so there shouldn't be // a significant performance hit by doing it this way). - return ConvertUTF16ToJavaString(env, UTF8ToUTF16(str)); + return ScopedJavaLocalRef<jstring>(env, ConvertUTF16ToJavaStringImpl( + env, UTF8ToUTF16(str))); } string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str) { @@ -39,10 +56,15 @@ string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str) { return result; } -jstring ConvertUTF16ToJavaString(JNIEnv* env, const string16& str) { - jstring result = env->NewString(str.data(), str.length()); - CheckException(env); - return result; +string16 ConvertJavaStringToUTF16(const JavaRef<jstring>& str) { + return ConvertJavaStringToUTF16(str.env(), str.obj()); +} + +// TODO(joth): change this to accept const StringPiece16&. +ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString(JNIEnv* env, + const string16& str) { + return ScopedJavaLocalRef<jstring>(env, + ConvertUTF16ToJavaStringImpl(env, str)); } } // namespace android diff --git a/base/android/jni_string.h b/base/android/jni_string.h index bb902d1..1987f04 100644 --- a/base/android/jni_string.h +++ b/base/android/jni_string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,7 @@ #include <jni.h> #include <string> +#include "base/android/scoped_java_ref.h" #include "base/string16.h" #include "base/string_piece.h" @@ -16,15 +17,19 @@ namespace android { // Convert a Java string to UTF8. Returns a std string. std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str); +std::string ConvertJavaStringToUTF8(const JavaRef<jstring>& str); // Convert a std string to Java string. -jstring ConvertUTF8ToJavaString(JNIEnv* env, const base::StringPiece& str); +ScopedJavaLocalRef<jstring> ConvertUTF8ToJavaString( + JNIEnv* env, const base::StringPiece& str); // Convert a Java string to UTF16. Returns a string16. string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str); +string16 ConvertJavaStringToUTF16(const JavaRef<jstring>& str); // Convert a string16 to a Java string. -jstring ConvertUTF16ToJavaString(JNIEnv* env, const string16& str); +ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString( + JNIEnv* env, const string16& str); } // namespace android } // namespace base diff --git a/base/android/scoped_java_ref.cc b/base/android/scoped_java_ref.cc index da932f8..2cbcc75 100644 --- a/base/android/scoped_java_ref.cc +++ b/base/android/scoped_java_ref.cc @@ -1,9 +1,11 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/android/scoped_java_ref.h" +#include "base/logging.h" + namespace base { namespace android { @@ -12,7 +14,10 @@ JavaRef<jobject>::JavaRef() : env_(NULL), obj_(NULL) {} JavaRef<jobject>::JavaRef(JNIEnv* env, jobject obj) : env_(env), obj_(obj) { - DCHECK_EQ(JNILocalRefType, env->GetObjectRefType(obj)); + if (obj) { + DCHECK(env); + DCHECK_EQ(JNILocalRefType, env->GetObjectRefType(obj)); + } } JavaRef<jobject>::~JavaRef() { @@ -36,6 +41,20 @@ void JavaRef<jobject>::SetNewGlobalRef(JNIEnv* env, jobject obj) { obj_ = obj; } +void JavaRef<jobject>::ResetLocalRef() { + if (obj_) + env_->DeleteLocalRef(obj_); + env_ = NULL; + obj_ = NULL; +} + +void JavaRef<jobject>::ResetGlobalRef() { + if (obj_) + env_->DeleteGlobalRef(obj_); + env_ = NULL; + obj_ = NULL; +} + jobject JavaRef<jobject>::ReleaseInternal() { jobject obj = obj_; env_ = NULL; diff --git a/base/android/scoped_java_ref.h b/base/android/scoped_java_ref.h index a74cef7..dd93b25 100644 --- a/base/android/scoped_java_ref.h +++ b/base/android/scoped_java_ref.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,7 +8,7 @@ #include <jni.h> #include <stddef.h> -#include "base/logging.h" +#include "base/basictypes.h" namespace base { namespace android { @@ -25,6 +25,8 @@ class JavaRef<jobject> { JNIEnv* env() const { return env_; } jobject obj() const { return obj_; } + bool is_null() const { return obj_ == NULL; } + protected: // Initializes a NULL reference. JavaRef(); @@ -39,6 +41,8 @@ class JavaRef<jobject> { // use by the sub-classes. void SetNewLocalRef(JNIEnv* env, jobject obj); void SetNewGlobalRef(JNIEnv* env, jobject obj); + void ResetLocalRef(); + void ResetGlobalRef(); jobject ReleaseInternal(); private: @@ -104,7 +108,7 @@ class ScopedJavaLocalRef : public JavaRef<T> { } void Reset() { - this->SetNewLocalRef(NULL, NULL); + this->ResetLocalRef(); } template<typename U> @@ -148,7 +152,7 @@ class ScopedJavaGlobalRef : public JavaRef<T> { } void Reset() { - this->SetNewGlobalRef(NULL, NULL); + this->ResetGlobalRef(); } template<typename U> diff --git a/base/android/scoped_java_ref_unittest.cc b/base/android/scoped_java_ref_unittest.cc index a6dfc7a..f8bc675 100644 --- a/base/android/scoped_java_ref_unittest.cc +++ b/base/android/scoped_java_ref_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,24 +15,26 @@ namespace { int g_local_refs = 0; int g_global_refs = 0; +JNINativeInterface g_previous_functions = {0}; + jobject NewGlobalRef(JNIEnv* env, jobject obj) { ++g_global_refs; - return AttachCurrentThread()->NewGlobalRef(obj); + return g_previous_functions.NewGlobalRef(env, obj); } void DeleteGlobalRef(JNIEnv* env, jobject obj) { --g_global_refs; - return AttachCurrentThread()->DeleteGlobalRef(obj); + return g_previous_functions.DeleteGlobalRef(env, obj); } jobject NewLocalRef(JNIEnv* env, jobject obj) { ++g_local_refs; - return AttachCurrentThread()->NewLocalRef(obj); + return g_previous_functions.NewLocalRef(env, obj); } void DeleteLocalRef(JNIEnv* env, jobject obj) { --g_local_refs; - return AttachCurrentThread()->DeleteLocalRef(obj); + return g_previous_functions.DeleteLocalRef(env, obj); } } // namespace @@ -42,27 +44,27 @@ class ScopedJavaRefTest : public testing::Test { g_local_refs = 0; g_global_refs = 0; JNIEnv* env = AttachCurrentThread(); - counting_env = *env; - counting_functions = *counting_env.functions; - counting_functions.NewGlobalRef = &NewGlobalRef; - counting_functions.DeleteGlobalRef = &DeleteGlobalRef; - counting_functions.NewLocalRef = &NewLocalRef; - counting_functions.DeleteLocalRef = &DeleteLocalRef; - counting_env.functions = &counting_functions; + g_previous_functions = *env->functions; + // We inject our own functions in JNINativeInterface so we can keep track + // of the reference counting ourselves. + JNINativeInterface* native_interface = + const_cast<JNINativeInterface*>(env->functions); + native_interface->NewGlobalRef = &NewGlobalRef; + native_interface->DeleteGlobalRef = &DeleteGlobalRef; + native_interface->NewLocalRef = &NewLocalRef; + native_interface->DeleteLocalRef = &DeleteLocalRef; } - // Special JNI env configured in SetUp to count in and out all local & global - // reference instances. Be careful to only use this with the ScopedJavaRef - // classes under test, else it's easy to get system references counted in - // here too. - JNIEnv counting_env; - JNINativeInterface counting_functions; + virtual void TearDown() { + JNIEnv* env = AttachCurrentThread(); + *(const_cast<JNINativeInterface*>(env->functions)) = g_previous_functions; + } }; // The main purpose of this is testing the various conversions compile. TEST_F(ScopedJavaRefTest, Conversions) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> str(env, ConvertUTF8ToJavaString(env, "string")); + ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string"); ScopedJavaGlobalRef<jstring> global(str); { ScopedJavaGlobalRef<jobject> global_obj(str); @@ -74,24 +76,27 @@ TEST_F(ScopedJavaRefTest, Conversions) { } global.Reset(str); const JavaRef<jstring>& str_ref = str; - EXPECT_EQ("string", ConvertJavaStringToUTF8(env, str_ref.obj())); + EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref)); str.Reset(); } TEST_F(ScopedJavaRefTest, RefCounts) { + JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> str; - str.Reset(&counting_env, ConvertUTF8ToJavaString(AttachCurrentThread(), - "string")); + // The ConvertJavaStringToUTF8 below creates a new string that would normally + // return a local ref. We simulate that by starting the g_local_refs count at + // 1. + g_local_refs = 1; + str.Reset(ConvertUTF8ToJavaString(env, "string")); EXPECT_EQ(1, g_local_refs); EXPECT_EQ(0, g_global_refs); - { ScopedJavaGlobalRef<jstring> global_str(str); ScopedJavaGlobalRef<jobject> global_obj(global_str); EXPECT_EQ(1, g_local_refs); EXPECT_EQ(2, g_global_refs); - ScopedJavaLocalRef<jstring> str2(&counting_env, str.Release()); + ScopedJavaLocalRef<jstring> str2(env, str.Release()); EXPECT_EQ(1, g_local_refs); { ScopedJavaLocalRef<jstring> str3(str2); |