gecko-dev/widget/android/jni/Refs.h
Wes Kocher 027fd9354a Backed out 5 changesets (bug 1178850) for android build bustage CLOSED TREE
Backed out changeset 79085d3894e8 (bug 1178850)
Backed out changeset c02b603104ea (bug 1178850)
Backed out changeset d6dab7810669 (bug 1178850)
Backed out changeset 8ee5809f349b (bug 1178850)
Backed out changeset 821b22ce79e1 (bug 1178850)
2015-07-10 14:17:53 -07:00

645 lines
16 KiB
C++

#ifndef mozilla_jni_Refs_h__
#define mozilla_jni_Refs_h__
#include <jni.h>
#include "mozilla/Move.h"
#include "mozilla/jni/Utils.h"
#include "nsError.h" // for nsresult
#include "nsString.h"
namespace mozilla {
namespace jni {
class Accessor;
template<class T> class Constructor;
template<class T> class Field;
template<class T, typename R> class Method;
// Wrapped object reference (e.g. jobject, jclass, etc...)
template<class Cls> class Ref;
// Wrapped local reference that inherits from Ref.
template<class Cls> class LocalRef;
// Wrapped global reference that inherits from Ref.
template<class Cls> class GlobalRef;
// Type used for a reference parameter. Default is a wrapped object
// reference, but Param can be specialized to define custom behavior,
// e.g. a StringParam class that automatically converts nsAString& and
// nsACString& to a jstring.
template<class Cls> struct Param { typedef Ref<Cls> Type; };
// How exception during a JNI call should be treated.
enum class ExceptionMode
{
// Abort on unhandled excepion (default).
ABORT,
// Ignore the exception and return to caller.
IGNORE,
// Catch any exception and return a nsresult.
NSRESULT,
};
// Base class for all JNI binding classes.
// Templated so that we have one sClassRef for each class.
template<class Cls>
class Class
{
friend class Accessor;
template<class T> friend class Constructor;
template<class T> friend class Field;
template<class T, typename R> friend class Method;
private:
static jclass sClassRef; // global reference
protected:
jobject mInstance; // local or global reference
Class(jobject instance) : mInstance(instance) {}
};
// Binding for a plain jobject.
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 typename jni::Param<Object>::Type& Param;
};
// Binding for a built-in object reference other than jobject.
template<typename T>
class TypedObject : public Object
{
typedef TypedObject<T> Self;
protected:
TypedObject(jobject instance) : Object(instance) {}
public:
typedef jni::Ref<Self> Ref;
typedef jni::LocalRef<Self> LocalRef;
typedef jni::GlobalRef<Self> GlobalRef;
typedef const typename jni::Param<Self>::Type& Param;
};
// Define bindings for built-in types.
typedef TypedObject<jstring> String;
typedef TypedObject<jclass> ClassObject;
typedef TypedObject<jthrowable> Throwable;
typedef TypedObject<jbooleanArray> BooleanArray;
typedef TypedObject<jbyteArray> ByteArray;
typedef TypedObject<jcharArray> CharArray;
typedef TypedObject<jshortArray> ShortArray;
typedef TypedObject<jintArray> IntArray;
typedef TypedObject<jlongArray> LongArray;
typedef TypedObject<jfloatArray> FloatArray;
typedef TypedObject<jdoubleArray> DoubleArray;
typedef TypedObject<jobjectArray> ObjectArray;
template<> struct Param<String> { class Type; };
// Base class for Ref and its specializations.
template<class Cls, typename JNIType>
class RefBase : protected Cls
{
typedef RefBase<Cls, JNIType> Self;
typedef void (Self::*bool_type)() const;
void non_null_reference() const {}
protected:
RefBase(jobject instance) : Cls(instance) {}
public:
// Construct a Ref form a raw JNI reference.
static Ref<Cls> From(JNIType obj)
{
return Ref<Cls>(static_cast<jobject>(obj));
}
// Get the raw JNI reference.
JNIType Get() const
{
return static_cast<JNIType>(Cls::mInstance);
}
bool operator==(const RefBase& other) const
{
// Treat two references of the same object as being the same.
return Cls::mInstance == other.mInstance &&
GetJNIForThread()->IsSameObject(
Cls::mInstance, other.mInstance) != JNI_FALSE;
}
bool operator!=(const RefBase& other) const
{
return !operator==(other);
}
bool operator==(decltype(nullptr)) const
{
return !Cls::mInstance;
}
bool operator!=(decltype(nullptr)) const
{
return !!Cls::mInstance;
}
Cls* operator->()
{
MOZ_ASSERT(Cls::mInstance);
return this;
}
const Cls* operator->() const
{
MOZ_ASSERT(Cls::mInstance);
return this;
}
// Any ref can be cast to an object ref.
operator Ref<Object>() const;
// Null checking (e.g. !!ref) using the safe-bool idiom.
operator bool_type() const
{
return Cls::mInstance ? &Self::non_null_reference : nullptr;
}
// We don't allow implicit conversion to jobject because that can lead
// to easy mistakes such as assigning a temporary LocalRef to a jobject,
// and using the jobject after the LocalRef has been freed.
// We don't allow explicit conversion, to make outside code use Ref::Get.
// Using Ref::Get makes it very easy to see which code is using raw JNI
// types to make future refactoring easier.
// operator JNIType() const = delete;
};
// Wrapped object reference (e.g. jobject, jclass, etc...)
template<class Cls>
class Ref : public RefBase<Cls, jobject>
{
template<class C, typename T> friend class RefBase;
friend class jni::LocalRef<Cls>;
friend class jni::GlobalRef<Cls>;
typedef RefBase<Cls, jobject> Base;
protected:
// Protected jobject constructor because outside code should be using
// Ref::From. Using Ref::From makes it very easy to see which code is using
// raw JNI types for future refactoring.
Ref(jobject instance) : Base(instance) {}
// Protected copy constructor so that there's no danger of assigning a
// temporary LocalRef/GlobalRef to a Ref, and potentially use the Ref
// after the source had been freed.
Ref(const Ref& ref) : Base(ref.mInstance) {}
public:
MOZ_IMPLICIT Ref(decltype(nullptr)) : Base(nullptr) {}
};
template<class Cls, typename JNIType>
RefBase<Cls, JNIType>::operator Ref<Object>() const
{
return Ref<Object>(Cls::mInstance);
}
template<typename T>
class Ref<TypedObject<T>>
: public RefBase<TypedObject<T>, T>
{
friend class RefBase<TypedObject<T>, T>;
friend class jni::LocalRef<TypedObject<T>>;
friend class jni::GlobalRef<TypedObject<T>>;
typedef RefBase<TypedObject<T>, T> Base;
protected:
Ref(jobject instance) : Base(instance) {}
Ref(const Ref& ref) : Base(ref.mInstance) {}
public:
MOZ_IMPLICIT Ref(decltype(nullptr)) : Base(nullptr) {}
};
namespace {
// See explanation in LocalRef.
template<class Cls> struct GenericObject { typedef Object Type; };
template<> struct GenericObject<Object> { typedef struct {} Type; };
} // namespace
template<class Cls>
class LocalRef : public Ref<Cls>
{
template<class C> friend class LocalRef;
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;
JNIEnv* const mEnv;
LocalRef(JNIEnv* env, jobject instance)
: Ref<Cls>(instance)
, mEnv(env)
{}
LocalRef& swap(LocalRef& other)
{
auto instance = other.mInstance;
other.mInstance = Ref<Cls>::mInstance;
Ref<Cls>::mInstance = instance;
return *this;
}
public:
// Construct a LocalRef from a raw JNI local reference. Unlike Ref::From,
// LocalRef::Adopt returns a LocalRef that will delete the local reference
// when going out of scope.
static LocalRef Adopt(jobject instance)
{
return LocalRef(GetJNIForThread(), instance);
}
static LocalRef Adopt(JNIEnv* env, jobject instance)
{
return LocalRef(env, instance);
}
// Copy constructor.
LocalRef(const LocalRef<Cls>& ref)
: Ref<Cls>(ref.mEnv->NewLocalRef(ref.mInstance))
, mEnv(ref.mEnv)
{}
// Move constructor.
LocalRef(LocalRef<Cls>&& ref)
: Ref<Cls>(ref.mInstance)
, mEnv(ref.mEnv)
{
ref.mInstance = nullptr;
}
explicit LocalRef(JNIEnv* env = GetJNIForThread())
: Ref<Cls>(nullptr)
, mEnv(env)
{}
// Construct a LocalRef from any Ref,
// which means creating a new local reference.
MOZ_IMPLICIT LocalRef(const Ref<Cls>& ref)
: Ref<Cls>(nullptr)
, mEnv(GetJNIForThread())
{
Ref<Cls>::mInstance = mEnv->NewLocalRef(ref.mInstance);
}
// Move a LocalRef<Object> into a LocalRef<Cls> without
// creating/deleting local references.
MOZ_IMPLICIT LocalRef(LocalRef<GenericObject>&& 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()
{
if (Ref<Cls>::mInstance) {
mEnv->DeleteLocalRef(Ref<Cls>::mInstance);
Ref<Cls>::mInstance = nullptr;
}
}
// Get the JNIEnv* associated with this local reference.
JNIEnv* Env() const
{
return mEnv;
}
// Get the raw JNI reference that can be used as a return value.
// Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
auto Forget() -> decltype(Ref<Cls>(nullptr).Get())
{
const auto obj = Ref<Cls>::Get();
Ref<Cls>::mInstance = nullptr;
return obj;
}
LocalRef<Cls>& operator=(LocalRef<Cls> ref)
{
return swap(ref);
}
LocalRef<Cls>& operator=(const Ref<Cls>& ref)
{
LocalRef<Cls> newRef(mEnv, ref.mInstance);
return swap(newRef);
}
LocalRef<Cls>& operator=(LocalRef<GenericObject>&& ref)
{
LocalRef<Cls> newRef(mozilla::Forward<LocalRef<GenericObject>>(ref));
return swap(newRef);
}
LocalRef<Cls>& operator=(decltype(nullptr))
{
LocalRef<Cls> newRef(mEnv, nullptr);
return swap(newRef);
}
};
template<class Cls>
class GlobalRef : public Ref<Cls>
{
private:
static jobject NewGlobalRef(JNIEnv* env, jobject instance)
{
if (!instance) {
return nullptr;
}
if (!env) {
env = GetJNIForThread();
}
return env->NewGlobalRef(instance);
}
GlobalRef& swap(GlobalRef& other)
{
auto instance = other.mInstance;
other.mInstance = Ref<Cls>::mInstance;
Ref<Cls>::mInstance = instance;
return *this;
}
public:
GlobalRef()
: Ref<Cls>(nullptr)
{}
// Copy constructor
GlobalRef(const GlobalRef& ref)
: Ref<Cls>(ref.mInstance)
{}
// Move constructor
GlobalRef(GlobalRef&& ref)
: Ref<Cls>(ref.mInstance)
{
ref.mInstance = nullptr;
}
MOZ_IMPLICIT GlobalRef(const Ref<Cls>& ref)
: Ref<Cls>(NewGlobalRef(nullptr, ref.mInstance))
{}
GlobalRef(JNIEnv* env, const Ref<Cls>& ref)
: Ref<Cls>(NewGlobalRef(env, ref.mInstance))
{}
// Implicitly converts nullptr to GlobalRef.
MOZ_IMPLICIT GlobalRef(decltype(nullptr))
: Ref<Cls>(nullptr)
{}
~GlobalRef()
{
if (Ref<Cls>::mInstance) {
JNIEnv* const env = GetJNIForThread();
env->DeleteGlobalRef(Ref<Cls>::mInstance);
Ref<Cls>::mInstance = nullptr;
}
}
// Get the raw JNI reference that can be used as a return value.
// Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
auto Forget() -> decltype(Ref<Cls>(nullptr).Get())
{
const auto obj = Ref<Cls>::Get();
Ref<Cls>::mInstance = nullptr;
return obj;
}
GlobalRef<Cls>& operator=(GlobalRef<Cls> ref)
{
return swap(ref);
}
GlobalRef<Cls>& operator=(const Ref<Cls>& ref)
{
GlobalRef<Cls> newRef(ref);
return swap(newRef);
}
GlobalRef<Cls>& operator=(decltype(nullptr))
{
GlobalRef<Cls> newRef(nullptr);
return swap(newRef);
}
};
// Ref specialization for jstring.
template<>
class Ref<String> : public RefBase<String, jstring>
{
friend class RefBase<String, jstring>;
friend class jni::LocalRef<String>;
friend class jni::GlobalRef<String>;
typedef RefBase<TypedObject<jstring>, jstring> Base;
protected:
Ref(jobject instance) : Base(instance) {}
Ref(const Ref& ref) : Base(ref.mInstance) {}
public:
MOZ_IMPLICIT Ref(decltype(nullptr)) : Base(nullptr) {}
// Get the length of the jstring.
size_t Length() const
{
JNIEnv* const env = GetJNIForThread();
return env->GetStringLength(Get());
}
// Convert jstring to a nsString.
operator nsString() const
{
MOZ_ASSERT(Object::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;
}
// Convert jstring to a nsCString.
operator nsCString() const
{
return NS_ConvertUTF16toUTF8(operator nsString());
}
};
// Define a custom parameter type for String,
// which accepts both String::Ref and nsAString/nsACString
class Param<String>::Type : public Ref<String>
{
private:
// Not null if we should delete ref on destruction.
JNIEnv* const mEnv;
static jstring GetString(JNIEnv* env, const nsAString& str)
{
const jstring result = env->NewString(
reinterpret_cast<const jchar*>(str.BeginReading()),
str.Length());
HandleUncaughtException(env);
return result;
}
public:
MOZ_IMPLICIT Type(const String::Ref& ref)
: Ref<String>(ref.Get())
, mEnv(nullptr)
{}
MOZ_IMPLICIT Type(const nsAString& str, JNIEnv* env = GetJNIForThread())
: Ref<String>(GetString(env, str))
, mEnv(env)
{}
MOZ_IMPLICIT Type(const char16_t* str, JNIEnv* env = GetJNIForThread())
: Ref<String>(GetString(env, nsDependentString(str)))
, mEnv(env)
{}
MOZ_IMPLICIT Type(const nsACString& str, JNIEnv* env = GetJNIForThread())
: Ref<String>(GetString(env, NS_ConvertUTF8toUTF16(str)))
, mEnv(env)
{}
MOZ_IMPLICIT Type(const char* str, JNIEnv* env = GetJNIForThread())
: Ref<String>(GetString(env, NS_ConvertUTF8toUTF16(str)))
, mEnv(env)
{}
~Type()
{
if (mEnv) {
mEnv->DeleteLocalRef(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>
class ReturnToLocal
{
private:
LocalRef<Cls>* const localRef;
LocalRef<Object> objRef;
public:
explicit ReturnToLocal(LocalRef<Cls>* ref) : localRef(ref) {}
operator LocalRef<Object>*() { return &objRef; }
~ReturnToLocal()
{
if (objRef) {
*localRef = mozilla::Move(objRef);
}
}
};
template<class Cls>
ReturnToLocal<Cls> ReturnTo(LocalRef<Cls>* ref)
{
return ReturnToLocal<Cls>(ref);
}
// Support conversion from GlobalRef<T>* to LocalRef<Object/T>*:
// GlobalRef<Foo> foo;
// Foo::GetFoo(&foo); // error because parameter type is LocalRef<Foo>*.
// Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
template<class Cls>
class ReturnToGlobal
{
private:
GlobalRef<Cls>* const globalRef;
LocalRef<Object> objRef;
LocalRef<Cls> clsRef;
public:
explicit ReturnToGlobal(GlobalRef<Cls>* ref) : globalRef(ref) {}
operator LocalRef<Object>*() { return &objRef; }
operator LocalRef<Cls>*() { return &clsRef; }
~ReturnToGlobal()
{
if (objRef) {
*globalRef = (clsRef = mozilla::Move(objRef));
} else if (clsRef) {
*globalRef = clsRef;
}
}
};
template<class Cls>
ReturnToGlobal<Cls> ReturnTo(GlobalRef<Cls>* ref)
{
return ReturnToGlobal<Cls>(ref);
}
} // namespace jni
} // namespace mozilla
#endif // mozilla_jni_Refs_h__