Bug 1116868 - Add templated JNI classes; r=snorp

This commit is contained in:
Jim Chen 2015-01-09 19:33:57 -05:00
Родитель d60c1e78f2
Коммит 52b4f608fa
8 изменённых файлов: 1152 добавлений и 21 удалений

Просмотреть файл

@ -28,6 +28,7 @@
#include "mozilla/StaticPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Types.h"
#include "mozilla/jni/Utils.h"
// Some debug #defines
// #define DEBUG_ANDROID_EVENTS
@ -38,9 +39,6 @@ class nsIDOMMozSmsMessage;
class nsIObserver;
class Task;
/* See the comment in AndroidBridge about this function before using it */
extern "C" MOZ_EXPORT JNIEnv * GetJNIForThread();
extern bool mozilla_AndroidBridge_SetMainThread(pthread_t);
namespace base {
@ -166,29 +164,16 @@ public:
static bool ThrowException(JNIEnv *aEnv, const char *aClass,
const char *aMessage) {
MOZ_ASSERT(aEnv, "Invalid thread JNI env");
jclass cls = aEnv->FindClass(aClass);
MOZ_ASSERT(cls, "Cannot find exception class");
bool ret = !aEnv->ThrowNew(cls, aMessage);
aEnv->DeleteLocalRef(cls);
return ret;
return jni::ThrowException(aEnv, aClass, aMessage);
}
static bool ThrowException(JNIEnv *aEnv, const char *aMessage) {
return ThrowException(aEnv, "java/lang/Exception", aMessage);
return jni::ThrowException(aEnv, aMessage);
}
static void HandleUncaughtException(JNIEnv *aEnv) {
MOZ_ASSERT(aEnv);
if (!aEnv->ExceptionCheck()) {
return;
}
jthrowable e = aEnv->ExceptionOccurred();
MOZ_ASSERT(e);
aEnv->ExceptionClear();
mozilla::widget::android::GeckoAppShell::HandleUncaughtException(nullptr, e);
// Should be dead by now...
MOZ_CRASH("Failed to handle uncaught exception");
jni::HandleUncaughtException(aEnv);
}
// The bridge needs to be constructed via ConstructBridge first,
@ -557,7 +542,7 @@ public:
bool CheckForException() {
if (mJNIEnv->ExceptionCheck()) {
AndroidBridge::HandleUncaughtException(mJNIEnv);
jni::HandleUncaughtException(mJNIEnv);
return true;
}
return false;

Просмотреть файл

@ -0,0 +1,287 @@
#ifndef mozilla_jni_Accessors_h__
#define mozilla_jni_Accessors_h__
#include <jni.h>
#include "mozilla/Attributes.h"
#include "mozilla/jni/Refs.h"
#include "mozilla/jni/Types.h"
#include "AndroidBridge.h"
namespace mozilla {
namespace jni{
namespace {
// Helper class to convert an arbitrary type to a jvalue, e.g. Value(123).val.
struct Value
{
Value(jboolean z) { val.z = z; }
Value(jbyte b) { val.b = b; }
Value(jchar c) { val.c = c; }
Value(jshort s) { val.s = s; }
Value(jint i) { val.i = i; }
Value(jlong j) { val.j = j; }
Value(jfloat f) { val.f = f; }
Value(jdouble d) { val.d = d; }
Value(jobject l) { val.l = l; }
jvalue val;
};
}
// Base class for Method<>, Field<>, and Constructor<>.
class Accessor {
private:
template<class Cls>
static void EnsureClassRef(JNIEnv* env)
{
if (!Cls::sClassRef) {
MOZ_ALWAYS_TRUE(Cls::sClassRef =
AndroidBridge::GetClassGlobalRef(env, Cls::name));
}
}
static void GetNsresult(JNIEnv* env, nsresult* rv)
{
if (env->ExceptionCheck()) {
env->ExceptionClear();
*rv = NS_ERROR_FAILURE;
} else {
*rv = NS_OK;
}
}
protected:
// Called before making a JNIEnv call.
template<class Traits>
static JNIEnv* BeginAccess()
{
JNIEnv* const env = Traits::isMultithreaded
? GetJNIForThread() : AndroidBridge::GetJNIEnv();
EnsureClassRef<class Traits::Owner>(env);
return env;
}
// Called after making a JNIEnv call.
template<class Traits>
static void EndAccess(JNIEnv* env, nsresult* rv)
{
if (Traits::exceptionMode == ExceptionMode::ABORT) {
return HandleUncaughtException(env);
} else if (Traits::exceptionMode == ExceptionMode::NSRESULT) {
return GetNsresult(env, rv);
}
}
};
// Member<> is used to call a JNI method given a traits class.
template<class Traits, typename ReturnType = typename Traits::ReturnType>
class Method : public Accessor
{
typedef Accessor Base;
typedef class Traits::Owner Owner;
protected:
static jmethodID sID;
static JNIEnv* BeginAccess()
{
JNIEnv* const env = Base::BeginAccess<Traits>();
if (sID) {
return env;
}
if (Traits::isStatic) {
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticMethodID(
env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
} else {
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetMethodID(
env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
}
return env;
}
static void EndAccess(JNIEnv* env, nsresult* rv)
{
return Base::EndAccess<Traits>(env, rv);
}
public:
template<typename... Args>
static ReturnType Call(const Owner* cls, nsresult* rv, const Args&... args)
{
JNIEnv* const env = BeginAccess();
jvalue jargs[] = {
Value(TypeAdapter<Args>::FromNative(env, args)).val ...
};
auto result = TypeAdapter<ReturnType>::ToNative(env,
Traits::isStatic ?
(env->*TypeAdapter<ReturnType>::StaticCall)(
Owner::sClassRef, sID, jargs) :
(env->*TypeAdapter<ReturnType>::Call)(
cls->mInstance, sID, jargs));
EndAccess(env, rv);
return result;
}
};
// Define sID member.
template<class T, typename R> jmethodID Method<T, R>::sID;
// Specialize void because C++ forbids us from
// using a "void" temporary result variable.
template<class Traits>
class Method<Traits, void> : public Method<Traits, bool>
{
typedef Method<Traits, bool> Base;
typedef typename Traits::Owner Owner;
public:
template<typename... Args>
static void Call(const Owner* cls, nsresult* rv,
const Args&... args) MOZ_OVERRIDE
{
JNIEnv* const env = Base::BeginAccess();
jvalue jargs[] = {
Value(TypeAdapter<Args>::FromNative(env, args)).val ...
};
if (Traits::isStatic) {
env->CallStaticVoidMethodA(Owner::sClassRef, Base::sID, jargs);
} else {
env->CallVoidMethodA(cls->mInstance, Base::sID, jargs);
}
Base::EndAccess(env, rv);
}
};
// Constructor<> is used to construct a JNI instance given a traits class.
template<class Traits>
class Constructor : protected Method<Traits, typename Traits::ReturnType> {
typedef class Traits::Owner Owner;
typedef typename Traits::ReturnType ReturnType;
typedef Method<Traits, ReturnType> Base;
public:
template<typename... Args>
static ReturnType Call(const Owner* cls, nsresult* rv,
const Args&... args) MOZ_OVERRIDE
{
JNIEnv* const env = Base::BeginAccess();
jvalue jargs[] = {
Value(TypeAdapter<Args>::FromNative(env, args)).val ...
};
auto result = TypeAdapter<ReturnType>::ToNative(
env, env->NewObjectA(Owner::sClassRef, Base::sID, jargs));
Base::EndAccess(env, rv);
return result;
}
};
// Field<> is used to access a JNI field given a traits class.
template<class Traits>
class Field : public Accessor
{
typedef Accessor Base;
typedef class Traits::Owner Owner;
typedef typename Traits::ReturnType GetterType;
typedef typename Traits::SetterType SetterType;
template<typename T> struct RemoveRef { typedef T Type; };
template<typename T> struct RemoveRef<const T&> { typedef T Type; };
// Setter type without any const/& added
typedef typename RemoveRef<SetterType>::Type SetterBaseType;
private:
static jfieldID sID;
static JNIEnv* BeginAccess()
{
JNIEnv* const env = Base::BeginAccess<Traits>();
if (sID) {
return env;
}
if (Traits::isStatic) {
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticFieldID(
env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
} else {
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetFieldID(
env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
}
return env;
}
static void EndAccess(JNIEnv* env, nsresult* rv)
{
return Base::EndAccess<Traits>(env, rv);
}
public:
static GetterType Get(const Owner* cls, nsresult* rv)
{
JNIEnv* const env = BeginAccess();
auto result = TypeAdapter<GetterType>::ToNative(
env, Traits::isStatic ?
(env->*TypeAdapter<GetterType>::StaticGet)
(Owner::sClassRef, sID) :
(env->*TypeAdapter<GetterType>::Get)
(cls->mInstance, sID));
EndAccess(env, rv);
return result;
}
static void Set(const Owner* cls, nsresult* rv, SetterType val)
{
JNIEnv* const env = BeginAccess();
if (Traits::isStatic) {
(env->*TypeAdapter<SetterBaseType>::StaticSet)(
Owner::sClassRef, sID,
TypeAdapter<SetterBaseType>::FromNative(env, val));
} else {
(env->*TypeAdapter<SetterBaseType>::Set)(
cls->mInstance, sID,
TypeAdapter<SetterBaseType>::FromNative(env, val));
}
EndAccess(env, rv);
}
};
// Define sID member.
template<class T> jfieldID Field<T>::sID;
// Define the sClassRef member declared in Refs.h and
// used by Method and Field above.
template<class C> jclass Class<C>::sClassRef;
} // namespace jni
} // namespace mozilla
#endif // mozilla_jni_Accessors_h__

644
widget/android/jni/Refs.h Normal file
Просмотреть файл

@ -0,0 +1,644 @@
#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__

120
widget/android/jni/Types.h Normal file
Просмотреть файл

@ -0,0 +1,120 @@
#ifndef mozilla_jni_Types_h__
#define mozilla_jni_Types_h__
#include <jni.h>
#include "mozilla/jni/Refs.h"
#include "AndroidBridge.h"
namespace mozilla {
namespace jni {
namespace {
// TypeAdapter specializations are the interfaces between naive (C++) types such
// as int32_t and JNI types such as jint. The template parameter T is the native
// type, and each TypeAdapter specialization can have the following members:
//
// * Call: JNIEnv member pointer for making a method call that returns T.
// * StaticCall: JNIEnv member pointer for making a static call that returns T.
// * Get: JNIEnv member pointer for getting a field of type T.
// * StaticGet: JNIEnv member pointer for getting a static field of type T.
// * Set: JNIEnv member pointer for setting a field of type T.
// * StaticGet: JNIEnv member pointer for setting a static field of type T.
// * ToNative: static function that converts the JNI type to the native type.
// * FromNative: static function that converts the native type to the JNI type.
template<typename T> struct TypeAdapter;
// TypeAdapter<LocalRef<Cls>> applies when jobject is a return value.
template<class Cls> struct TypeAdapter<LocalRef<Cls>> {
static constexpr auto Call = &JNIEnv::CallObjectMethodA;
static constexpr auto StaticCall = &JNIEnv::CallStaticObjectMethodA;
static constexpr auto Get = &JNIEnv::GetObjectField;
static constexpr auto StaticGet = &JNIEnv::GetStaticObjectField;
static LocalRef<Cls> ToNative(JNIEnv* env, jobject instance) {
return LocalRef<Cls>::Adopt(env, instance);
}
};
template<class Cls> constexpr jobject
(JNIEnv::*TypeAdapter<LocalRef<Cls>>::Call)(jobject, jmethodID, jvalue*);
template<class Cls> constexpr jobject
(JNIEnv::*TypeAdapter<LocalRef<Cls>>::StaticCall)(jclass, jmethodID, jvalue*);
template<class Cls> constexpr jobject
(JNIEnv::*TypeAdapter<LocalRef<Cls>>::Get)(jobject, jfieldID);
template<class Cls> constexpr jobject
(JNIEnv::*TypeAdapter<LocalRef<Cls>>::StaticGet)(jclass, jfieldID);
// TypeAdapter<Ref<Cls>> applies when jobject is a parameter value.
template<class Cls> struct TypeAdapter<Ref<Cls>> {
static constexpr auto Set = &JNIEnv::SetObjectField;
static constexpr auto StaticSet = &JNIEnv::SetStaticObjectField;
static jobject FromNative(JNIEnv*, const Ref<Cls>& instance) {
return instance.Get();
}
};
template<class Cls> constexpr void
(JNIEnv::*TypeAdapter<Ref<Cls>>::Set)(jobject, jfieldID, jobject);
template<class Cls> constexpr void
(JNIEnv::*TypeAdapter<Ref<Cls>>::StaticSet)(jclass, jfieldID, jobject);
// jstring has its own Param type.
template<> struct TypeAdapter<class Param<String>::Type>
: public TypeAdapter<String::Ref>
{};
#define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName) \
\
template<> struct TypeAdapter<NativeType> { \
static constexpr auto Call = &JNIEnv::Call ## JNIName ## MethodA; \
static constexpr auto StaticCall = &JNIEnv::CallStatic ## JNIName ## MethodA; \
static constexpr auto Get = &JNIEnv::Get ## JNIName ## Field; \
static constexpr auto StaticGet = &JNIEnv::GetStatic ## JNIName ## Field; \
static constexpr auto Set = &JNIEnv::Set ## JNIName ## Field; \
static constexpr auto StaticSet = &JNIEnv::SetStatic ## JNIName ## Field; \
\
static JNIType FromNative(JNIEnv*, NativeType val) { \
return static_cast<JNIType>(val); \
} \
static NativeType ToNative(JNIEnv*, JNIType val) { \
return static_cast<NativeType>(val); \
} \
}; \
\
constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Call) \
(jobject, jmethodID, jvalue*); \
constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticCall) \
(jclass, jmethodID, jvalue*); \
constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Get) \
(jobject, jfieldID); \
constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticGet) \
(jclass, jfieldID); \
constexpr void (JNIEnv::*TypeAdapter<NativeType>::Set) \
(jobject, jfieldID, JNIType); \
constexpr void (JNIEnv::*TypeAdapter<NativeType>::StaticSet) \
(jclass, jfieldID, JNIType)
DEFINE_PRIMITIVE_TYPE_ADAPTER(bool, jboolean, Boolean);
DEFINE_PRIMITIVE_TYPE_ADAPTER(int8_t, jbyte, Byte);
DEFINE_PRIMITIVE_TYPE_ADAPTER(char16_t, jchar, Char);
DEFINE_PRIMITIVE_TYPE_ADAPTER(int16_t, jshort, Short);
DEFINE_PRIMITIVE_TYPE_ADAPTER(int32_t, jint, Int);
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
} // namespace jni
} // namespace mozilla
#endif // mozilla_jni_Types_h__

Просмотреть файл

@ -0,0 +1,43 @@
#include "Utils.h"
#include "mozilla/Assertions.h"
#include "GeneratedJNIWrappers.h"
#include "Refs.h"
namespace mozilla {
namespace jni {
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));
MOZ_ASSERT(cls, "Cannot find exception class");
return !aEnv->ThrowNew(cls.Get(), aMessage);
}
void HandleUncaughtException(JNIEnv *aEnv)
{
MOZ_ASSERT(aEnv, "Invalid thread JNI env");
if (!aEnv->ExceptionCheck()) {
return;
}
Throwable::LocalRef e =
Throwable::LocalRef::Adopt(aEnv->ExceptionOccurred());
MOZ_ASSERT(e);
aEnv->ExceptionClear();
widget::GeckoAppShell::HandleUncaughtException(nullptr, e);
// Should be dead by now...
MOZ_CRASH("Failed to handle uncaught exception");
}
} // jni
} // mozilla

Просмотреть файл

@ -0,0 +1,27 @@
#ifndef mozilla_jni_Utils_h__
#define mozilla_jni_Utils_h__
#include <jni.h>
#include "mozilla/Types.h"
/* See the comment in AndroidBridge about this function before using it */
extern "C" MOZ_EXPORT JNIEnv * GetJNIForThread();
namespace mozilla {
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);
}
void HandleUncaughtException(JNIEnv *aEnv);
} // jni
} // mozilla
#endif // mozilla_jni_Utils_h__

Просмотреть файл

@ -0,0 +1,24 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.jni += [
'Accessors.h',
'Refs.h',
'Types.h',
'Utils.h',
]
SOURCES += [
'Utils.cpp',
]
FAIL_ON_WARNINGS = True
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/widget/android',
]

Просмотреть файл

@ -6,6 +6,7 @@
DIRS += [
'bindings',
'jni',
]
XPIDL_SOURCES += [