Bug 1186467 - Add some JNI utilities for convenience; r=snorp
authorJim Chen <nchen@mozilla.com>
Tue, 04 Aug 2015 17:47:28 -0400
changeset 287888 7cbc7f63aba7b931a473558244802f010bd77595
parent 287887 def7ba7179be24f99dcc7d5730566b727df8c11d
child 287889 c5e9a116e55913cae4be231ba549719375907ed2
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1186467
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1186467 - Add some JNI utilities for convenience; r=snorp This patch adds: * Conversion operator from String::Param to String::LocalRef. * More overloads of the jni::ThrowException function. * name members to built-in types like jni::Object, jni::String, etc. This allows using jni::Accessors::EnsureClassRef on built-in types to get built-in class refs (e.g. jclass for java/lang/String). * Ability to implicitly convert LocalRef<Cls> to LocalRef<Object> * Fixes for bugs in LocalRef/GlobalRef where new refs are not created. * Fixes for inaccurate uses of mozilla::Forward in favor of mozilla::Move
widget/android/jni/Refs.h
widget/android/jni/Utils.cpp
widget/android/jni/Utils.h
--- a/widget/android/jni/Refs.h
+++ b/widget/android/jni/Refs.h
@@ -83,33 +83,37 @@ class Object : public Class<Object>
 protected:
     Object(jobject instance) : Class(instance) {}
 
 public:
     typedef jni::Ref<Object> Ref;
     typedef jni::LocalRef<Object>  LocalRef;
     typedef jni::GlobalRef<Object> GlobalRef;
     typedef const jni::Param<Object>& Param;
+
+    static constexpr char name[] = "java/lang/Object";
 };
 
 
 // Binding for a built-in object reference other than jobject.
 template<typename T>
-class TypedObject : public Object
+class TypedObject : public Class<TypedObject<T>>
 {
     typedef TypedObject<T> Self;
 
 protected:
-    TypedObject(jobject instance) : Object(instance) {}
+    TypedObject(jobject instance) : Class<TypedObject<T>>(instance) {}
 
 public:
     typedef jni::Ref<Self> Ref;
     typedef jni::LocalRef<Self>  LocalRef;
     typedef jni::GlobalRef<Self> GlobalRef;
     typedef const jni::Param<Self>& Param;
+
+    static const char name[];
 };
 
 // Define bindings for built-in types.
 typedef TypedObject<jstring>    String;
 typedef TypedObject<jclass>     ClassObject;
 typedef TypedObject<jthrowable> Throwable;
 
 typedef TypedObject<jbooleanArray> BooleanArray;
@@ -260,16 +264,24 @@ public:
 };
 
 
 namespace {
 
 // See explanation in LocalRef.
 template<class Cls> struct GenericObject { typedef Object Type; };
 template<> struct GenericObject<Object> { typedef struct {} Type; };
+template<class Cls> struct GenericLocalRef
+{
+    template<class C> struct Type : jni::Object {};
+};
+template<> struct GenericLocalRef<Object>
+{
+    template<class C> using Type = jni::LocalRef<C>;
+};
 
 } // namespace
 
 template<class Cls>
 class LocalRef : public Ref<Cls>
 {
     template<class C> friend class LocalRef;
 
@@ -277,16 +289,22 @@ private:
     // In order to be able to convert LocalRef<Object> to LocalRef<Cls>, we
     // need constructors and copy assignment operators that take in a
     // LocalRef<Object> argument. However, if Cls *is* Object, we would have
     // duplicated constructors and operators with LocalRef<Object> arguments. To
     // avoid this conflict, we use GenericObject, which is defined as Object for
     // LocalRef<non-Object> and defined as a dummy class for LocalRef<Object>.
     typedef typename GenericObject<Cls>::Type GenericObject;
 
+    // Similarly, GenericLocalRef is useed to convert LocalRef<Cls> to,
+    // LocalRef<Object>. It's defined as LocalRef<C> for Cls == Object,
+    // and defined as a dummy template class for Cls != Object.
+    template<class C> using GenericLocalRef
+            = typename GenericLocalRef<Cls>::template Type<C>;
+
     JNIEnv* const mEnv;
 
     LocalRef(JNIEnv* env, jobject instance)
         : Ref<Cls>(instance)
         , mEnv(env)
     {}
 
     LocalRef& swap(LocalRef& other)
@@ -343,16 +361,24 @@ public:
     // creating/deleting local references.
     MOZ_IMPLICIT LocalRef(LocalRef<GenericObject>&& ref)
         : Ref<Cls>(ref.mInstance)
         , mEnv(ref.mEnv)
     {
         ref.mInstance = nullptr;
     }
 
+    template<class C>
+    MOZ_IMPLICIT LocalRef(GenericLocalRef<C>&& ref)
+        : Ref<Cls>(ref.mInstance)
+        , mEnv(ref.mEnv)
+    {
+        ref.mInstance = nullptr;
+    }
+
     // Implicitly converts nullptr to LocalRef.
     MOZ_IMPLICIT LocalRef(decltype(nullptr))
         : Ref<Cls>(nullptr)
         , mEnv(GetJNIForThread())
     {}
 
     ~LocalRef()
     {
@@ -379,23 +405,30 @@ public:
 
     LocalRef<Cls>& operator=(LocalRef<Cls> ref)
     {
         return swap(ref);
     }
 
     LocalRef<Cls>& operator=(const Ref<Cls>& ref)
     {
-        LocalRef<Cls> newRef(mEnv, ref.mInstance);
+        LocalRef<Cls> newRef(mEnv, mEnv->NewLocalRef(ref.mInstance));
         return swap(newRef);
     }
 
     LocalRef<Cls>& operator=(LocalRef<GenericObject>&& ref)
     {
-        LocalRef<Cls> newRef(mozilla::Forward<LocalRef<GenericObject>>(ref));
+        LocalRef<Cls> newRef(mozilla::Move(ref));
+        return swap(newRef);
+    }
+
+    template<class C>
+    LocalRef<Cls>& operator=(GenericLocalRef<C>&& ref)
+    {
+        LocalRef<Cls> newRef(mozilla::Move(ref));
         return swap(newRef);
     }
 
     LocalRef<Cls>& operator=(decltype(nullptr))
     {
         LocalRef<Cls> newRef(mEnv, nullptr);
         return swap(newRef);
     }
@@ -427,17 +460,17 @@ private:
 
 public:
     GlobalRef()
         : Ref<Cls>(nullptr)
     {}
 
     // Copy constructor
     GlobalRef(const GlobalRef& ref)
-        : Ref<Cls>(ref.mInstance)
+        : Ref<Cls>(NewGlobalRef(nullptr, ref.mInstance))
     {}
 
     // Move constructor
     GlobalRef(GlobalRef&& ref)
         : Ref<Cls>(ref.mInstance)
     {
         ref.mInstance = nullptr;
     }
@@ -516,17 +549,17 @@ public:
     {
         JNIEnv* const env = GetJNIForThread();
         return env->GetStringLength(Get());
     }
 
     // Convert jstring to a nsString.
     operator nsString() const
     {
-        MOZ_ASSERT(Object::mInstance);
+        MOZ_ASSERT(String::mInstance);
 
         JNIEnv* const env = GetJNIForThread();
         const jchar* const str = env->GetStringChars(Get(), nullptr);
         const jsize len = env->GetStringLength(Get());
 
         nsString result(reinterpret_cast<const char16_t*>(str), len);
         env->ReleaseStringChars(Get(), str);
         return result;
@@ -584,16 +617,24 @@ public:
     {}
 
     ~Type()
     {
         if (mEnv) {
             mEnv->DeleteLocalRef(Get());
         }
     }
+
+    operator String::LocalRef() const
+    {
+        JNIEnv* const env = mEnv ? mEnv : GetJNIForThread();
+        // We can't return our existing ref because the returned
+        // LocalRef could be freed first, so we need a new local ref.
+        return String::LocalRef::Adopt(env, env->NewLocalRef(Get()));
+    }
 };
 
 
 // Support conversion from LocalRef<T>* to LocalRef<Object>*:
 //   LocalRef<Foo> foo;
 //   Foo::GetFoo(&foo); // error because parameter type is LocalRef<Object>*.
 //   Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
 template<class Cls>
--- a/widget/android/jni/Utils.cpp
+++ b/widget/android/jni/Utils.cpp
@@ -34,16 +34,30 @@ DEFINE_PRIMITIVE_TYPE_ADAPTER(int32_t,  
 DEFINE_PRIMITIVE_TYPE_ADAPTER(int64_t,  jlong,    Long);
 DEFINE_PRIMITIVE_TYPE_ADAPTER(float,    jfloat,   Float);
 DEFINE_PRIMITIVE_TYPE_ADAPTER(double,   jdouble,  Double);
 
 #undef DEFINE_PRIMITIVE_TYPE_ADAPTER
 
 } // namespace detail
 
+constexpr char Object::name[];
+template<> const char TypedObject<jstring>::name[] = "java/lang/String";
+template<> const char TypedObject<jclass>::name[] = "java/lang/Class";
+template<> const char TypedObject<jthrowable>::name[] = "java/lang/Throwable";
+template<> const char TypedObject<jbooleanArray>::name[] = "[Z";
+template<> const char TypedObject<jbyteArray>::name[] = "[B";
+template<> const char TypedObject<jcharArray>::name[] = "[C";
+template<> const char TypedObject<jshortArray>::name[] = "[S";
+template<> const char TypedObject<jintArray>::name[] = "[I";
+template<> const char TypedObject<jlongArray>::name[] = "[J";
+template<> const char TypedObject<jfloatArray>::name[] = "[F";
+template<> const char TypedObject<jdoubleArray>::name[] = "[D";
+template<> const char TypedObject<jobjectArray>::name[] = "[Ljava/lang/Object;";
+
 
 bool ThrowException(JNIEnv *aEnv, const char *aClass,
                     const char *aMessage)
 {
     MOZ_ASSERT(aEnv, "Invalid thread JNI env");
 
     ClassObject::LocalRef cls =
             ClassObject::LocalRef::Adopt(aEnv->FindClass(aClass));
--- a/widget/android/jni/Utils.h
+++ b/widget/android/jni/Utils.h
@@ -14,16 +14,26 @@ namespace jni {
 bool ThrowException(JNIEnv *aEnv, const char *aClass,
                     const char *aMessage);
 
 inline bool ThrowException(JNIEnv *aEnv, const char *aMessage)
 {
     return ThrowException(aEnv, "java/lang/Exception", aMessage);
 }
 
+inline bool ThrowException(const char *aClass, const char *aMessage)
+{
+    return ThrowException(GetJNIForThread(), aClass, aMessage);
+}
+
+inline bool ThrowException(const char *aMessage)
+{
+    return ThrowException(GetJNIForThread(), aMessage);
+}
+
 void HandleUncaughtException(JNIEnv *aEnv);
 
 uintptr_t GetNativeHandle(JNIEnv* env, jobject instance);
 
 void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle);
 
 } // jni
 } // mozilla