mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 00:07:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
274
widget/android/jni/Accessors.h
Normal file
274
widget/android/jni/Accessors.h
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
#ifndef mozilla_jni_Accessors_h__
|
||||
#define mozilla_jni_Accessors_h__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "mozilla/jni/Refs.h"
|
||||
#include "mozilla/jni/Types.h"
|
||||
#include "mozilla/jni/Utils.h"
|
||||
#include "AndroidBridge.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using namespace detail;
|
||||
|
||||
// Base class for Method<>, Field<>, and Constructor<>.
|
||||
class Accessor
|
||||
{
|
||||
static void GetNsresult(JNIEnv* env, nsresult* rv)
|
||||
{
|
||||
if (env->ExceptionCheck()) {
|
||||
#ifdef MOZ_CHECK_JNI
|
||||
env->ExceptionDescribe();
|
||||
#endif
|
||||
env->ExceptionClear();
|
||||
*rv = NS_ERROR_FAILURE;
|
||||
} else {
|
||||
*rv = NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// Called after making a JNIEnv call.
|
||||
template<class Traits>
|
||||
static void EndAccess(const typename Traits::Owner::Context& ctx,
|
||||
nsresult* rv)
|
||||
{
|
||||
if (Traits::exceptionMode == ExceptionMode::ABORT) {
|
||||
MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
|
||||
|
||||
} else if (Traits::exceptionMode == ExceptionMode::NSRESULT) {
|
||||
GetNsresult(ctx.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 typename Traits::Owner::Context Context;
|
||||
|
||||
protected:
|
||||
static jmethodID sID;
|
||||
|
||||
static void BeginAccess(const Context& ctx)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
|
||||
"Dispatching not supported for method call");
|
||||
|
||||
if (sID) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Traits::isStatic) {
|
||||
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticMethodID(
|
||||
ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetMethodID(
|
||||
ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
|
||||
}
|
||||
}
|
||||
|
||||
static void EndAccess(const Context& ctx, nsresult* rv)
|
||||
{
|
||||
return Base::EndAccess<Traits>(ctx, rv);
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
static ReturnType Call(const Context& ctx, nsresult* rv, const Args&... args)
|
||||
{
|
||||
JNIEnv* const env = ctx.Env();
|
||||
BeginAccess(ctx);
|
||||
|
||||
jvalue jargs[] = {
|
||||
Value(TypeAdapter<Args>::FromNative(env, args)).val ...
|
||||
};
|
||||
|
||||
auto result = TypeAdapter<ReturnType>::ToNative(env,
|
||||
Traits::isStatic ?
|
||||
(env->*TypeAdapter<ReturnType>::StaticCall)(
|
||||
ctx.RawClassRef(), sID, jargs) :
|
||||
(env->*TypeAdapter<ReturnType>::Call)(
|
||||
ctx.Get(), sID, jargs));
|
||||
|
||||
EndAccess(ctx, 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::Context Context;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
static void Call(const Context& ctx, nsresult* rv,
|
||||
const Args&... args)
|
||||
{
|
||||
JNIEnv* const env = ctx.Env();
|
||||
Base::BeginAccess(ctx);
|
||||
|
||||
jvalue jargs[] = {
|
||||
Value(TypeAdapter<Args>::FromNative(env, args)).val ...
|
||||
};
|
||||
|
||||
if (Traits::isStatic) {
|
||||
env->CallStaticVoidMethodA(ctx.RawClassRef(), Base::sID, jargs);
|
||||
} else {
|
||||
env->CallVoidMethodA(ctx.Get(), Base::sID, jargs);
|
||||
}
|
||||
|
||||
Base::EndAccess(ctx, 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 typename Traits::Owner::Context Context;
|
||||
typedef typename Traits::ReturnType ReturnType;
|
||||
typedef Method<Traits, ReturnType> Base;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
static ReturnType Call(const Context& ctx, nsresult* rv,
|
||||
const Args&... args)
|
||||
{
|
||||
JNIEnv* const env = ctx.Env();
|
||||
Base::BeginAccess(ctx);
|
||||
|
||||
jvalue jargs[] = {
|
||||
Value(TypeAdapter<Args>::FromNative(env, args)).val ...
|
||||
};
|
||||
|
||||
auto result = TypeAdapter<ReturnType>::ToNative(
|
||||
env, env->NewObjectA(ctx.RawClassRef(), Base::sID, jargs));
|
||||
|
||||
Base::EndAccess(ctx, 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 typename Traits::Owner::Context Context;
|
||||
typedef typename Traits::ReturnType GetterType;
|
||||
typedef typename Traits::SetterType SetterType;
|
||||
|
||||
private:
|
||||
|
||||
static jfieldID sID;
|
||||
|
||||
static void BeginAccess(const Context& ctx)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
|
||||
"Dispatching not supported for field access");
|
||||
|
||||
if (sID) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Traits::isStatic) {
|
||||
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticFieldID(
|
||||
ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetFieldID(
|
||||
ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
|
||||
}
|
||||
}
|
||||
|
||||
static void EndAccess(const Context& ctx, nsresult* rv)
|
||||
{
|
||||
return Base::EndAccess<Traits>(ctx, rv);
|
||||
}
|
||||
|
||||
public:
|
||||
static GetterType Get(const Context& ctx, nsresult* rv)
|
||||
{
|
||||
JNIEnv* const env = ctx.Env();
|
||||
BeginAccess(ctx);
|
||||
|
||||
auto result = TypeAdapter<GetterType>::ToNative(
|
||||
env, Traits::isStatic ?
|
||||
|
||||
(env->*TypeAdapter<GetterType>::StaticGet)
|
||||
(ctx.RawClassRef(), sID) :
|
||||
|
||||
(env->*TypeAdapter<GetterType>::Get)
|
||||
(ctx.Get(), sID));
|
||||
|
||||
EndAccess(ctx, rv);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Set(const Context& ctx, nsresult* rv, SetterType val)
|
||||
{
|
||||
JNIEnv* const env = ctx.Env();
|
||||
BeginAccess(ctx);
|
||||
|
||||
if (Traits::isStatic) {
|
||||
(env->*TypeAdapter<SetterType>::StaticSet)(
|
||||
ctx.RawClassRef(), sID,
|
||||
TypeAdapter<SetterType>::FromNative(env, val));
|
||||
} else {
|
||||
(env->*TypeAdapter<SetterType>::Set)(
|
||||
ctx.Get(), sID,
|
||||
TypeAdapter<SetterType>::FromNative(env, val));
|
||||
}
|
||||
|
||||
EndAccess(ctx, 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, typename T> jclass Context<C, T>::sClassRef;
|
||||
|
||||
} // namespace jni
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_jni_Accessors_h__
|
||||
707
widget/android/jni/Natives.h
Normal file
707
widget/android/jni/Natives.h
Normal file
|
|
@ -0,0 +1,707 @@
|
|||
#ifndef mozilla_jni_Natives_h__
|
||||
#define mozilla_jni_Natives_h__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "mozilla/IndexSequence.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/jni/Accessors.h"
|
||||
#include "mozilla/jni/Refs.h"
|
||||
#include "mozilla/jni/Types.h"
|
||||
#include "mozilla/jni/Utils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace jni {
|
||||
|
||||
/**
|
||||
* C++ classes implementing instance (non-static) native methods can choose
|
||||
* from one of two ownership models, when associating a C++ object with a Java
|
||||
* instance.
|
||||
*
|
||||
* * If the C++ class inherits from mozilla::SupportsWeakPtr, weak pointers
|
||||
* will be used. The Java instance will store and own the pointer to a
|
||||
* WeakPtr object. The C++ class itself is otherwise not owned or directly
|
||||
* referenced. To attach a Java instance to a C++ instance, pass in a pointer
|
||||
* to the C++ class (i.e. MyClass*).
|
||||
*
|
||||
* class MyClass : public SupportsWeakPtr<MyClass>
|
||||
* , public MyJavaClass::Natives<MyClass>
|
||||
* {
|
||||
* // ...
|
||||
*
|
||||
* public:
|
||||
* MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MyClass)
|
||||
* using MyJavaClass::Natives<MyClass>::Dispose;
|
||||
*
|
||||
* void AttachTo(const MyJavaClass::LocalRef& instance)
|
||||
* {
|
||||
* MyJavaClass::Natives<MyClass>::AttachInstance(instance, this);
|
||||
*
|
||||
* // "instance" does NOT own "this", so the C++ object
|
||||
* // lifetime is separate from the Java object lifetime.
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* * If the C++ class doesn't inherit from mozilla::SupportsWeakPtr, the Java
|
||||
* instance will store and own a pointer to the C++ object itself. This
|
||||
* pointer must not be stored or deleted elsewhere. To attach a Java instance
|
||||
* to a C++ instance, pass in a reference to a UniquePtr of the C++ class
|
||||
* (i.e. UniquePtr<MyClass>).
|
||||
*
|
||||
* class MyClass : public MyJavaClass::Natives<MyClass>
|
||||
* {
|
||||
* // ...
|
||||
*
|
||||
* public:
|
||||
* using MyJavaClass::Natives<MyClass>::Dispose;
|
||||
*
|
||||
* static void AttachTo(const MyJavaClass::LocalRef& instance)
|
||||
* {
|
||||
* MyJavaClass::Natives<MyClass>::AttachInstance(
|
||||
* instance, mozilla::MakeUnique<MyClass>());
|
||||
*
|
||||
* // "instance" owns the newly created C++ object, so the C++
|
||||
* // object is destroyed as soon as instance.dispose() is called.
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline uintptr_t CheckNativeHandle(JNIEnv* env, uintptr_t handle)
|
||||
{
|
||||
if (!handle) {
|
||||
if (!env->ExceptionCheck()) {
|
||||
ThrowException(env, "java/lang/NullPointerException",
|
||||
"Null native pointer");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
template<class Impl, bool UseWeakPtr = mozilla::IsBaseOf<
|
||||
SupportsWeakPtr<Impl>, Impl>::value /* = false */>
|
||||
struct NativePtr
|
||||
{
|
||||
static Impl* Get(JNIEnv* env, jobject instance)
|
||||
{
|
||||
return reinterpret_cast<Impl*>(CheckNativeHandle(
|
||||
env, GetNativeHandle(env, instance)));
|
||||
}
|
||||
|
||||
template<class LocalRef>
|
||||
static Impl* Get(const LocalRef& instance)
|
||||
{
|
||||
return Get(instance.Env(), instance.Get());
|
||||
}
|
||||
|
||||
template<class LocalRef>
|
||||
static void Set(const LocalRef& instance, UniquePtr<Impl>&& ptr)
|
||||
{
|
||||
Clear(instance);
|
||||
SetNativeHandle(instance.Env(), instance.Get(),
|
||||
reinterpret_cast<uintptr_t>(ptr.release()));
|
||||
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
|
||||
}
|
||||
|
||||
template<class LocalRef>
|
||||
static void Clear(const LocalRef& instance)
|
||||
{
|
||||
UniquePtr<Impl> ptr(reinterpret_cast<Impl*>(
|
||||
GetNativeHandle(instance.Env(), instance.Get())));
|
||||
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
|
||||
|
||||
if (ptr) {
|
||||
SetNativeHandle(instance.Env(), instance.Get(), 0);
|
||||
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class Impl>
|
||||
struct NativePtr<Impl, /* UseWeakPtr = */ true>
|
||||
{
|
||||
static Impl* Get(JNIEnv* env, jobject instance)
|
||||
{
|
||||
const auto ptr = reinterpret_cast<WeakPtr<Impl>*>(
|
||||
CheckNativeHandle(env, GetNativeHandle(env, instance)));
|
||||
if (!ptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Impl* const impl = *ptr;
|
||||
if (!impl) {
|
||||
ThrowException(env, "java/lang/NullPointerException",
|
||||
"Native object already released");
|
||||
}
|
||||
return impl;
|
||||
}
|
||||
|
||||
template<class LocalRef>
|
||||
static Impl* Get(const LocalRef& instance)
|
||||
{
|
||||
return Get(instance.Env(), instance.Get());
|
||||
}
|
||||
|
||||
template<class LocalRef>
|
||||
static void Set(const LocalRef& instance, Impl* ptr)
|
||||
{
|
||||
Clear(instance);
|
||||
SetNativeHandle(instance.Env(), instance.Get(),
|
||||
reinterpret_cast<uintptr_t>(new WeakPtr<Impl>(ptr)));
|
||||
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
|
||||
}
|
||||
|
||||
template<class LocalRef>
|
||||
static void Clear(const LocalRef& instance)
|
||||
{
|
||||
const auto ptr = reinterpret_cast<WeakPtr<Impl>*>(
|
||||
GetNativeHandle(instance.Env(), instance.Get()));
|
||||
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
|
||||
|
||||
if (ptr) {
|
||||
SetNativeHandle(instance.Env(), instance.Get(), 0);
|
||||
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using namespace detail;
|
||||
|
||||
/**
|
||||
* For JNI native methods that are dispatched to a proxy, i.e. using
|
||||
* @WrapForJNI(dispatchTo = "proxy"), the implementing C++ class must provide a
|
||||
* OnNativeCall member. Subsequently, every native call is automatically
|
||||
* wrapped in a functor object, and the object is passed to OnNativeCall. The
|
||||
* OnNativeCall implementation can choose to invoke the call, save it, dispatch
|
||||
* it to a different thread, etc. Each copy of functor may only be invoked
|
||||
* once.
|
||||
*
|
||||
* class MyClass : public MyJavaClass::Natives<MyClass>
|
||||
* {
|
||||
* // ...
|
||||
*
|
||||
* template<class Functor>
|
||||
* class ProxyRunnable final : public Runnable
|
||||
* {
|
||||
* Functor mCall;
|
||||
* public:
|
||||
* ProxyRunnable(Functor&& call) : mCall(mozilla::Move(call)) {}
|
||||
* virtual void run() override { mCall(); }
|
||||
* };
|
||||
*
|
||||
* public:
|
||||
* template<class Functor>
|
||||
* static void OnNativeCall(Functor&& call)
|
||||
* {
|
||||
* RunOnAnotherThread(new ProxyRunnable(mozilla::Move(call)));
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
|
||||
namespace detail {
|
||||
|
||||
// ProxyArg is used to handle JNI ref arguments for proxies. Because a proxied
|
||||
// call may happen outside of the original JNI native call, we must save all
|
||||
// JNI ref arguments as global refs to avoid the arguments going out of scope.
|
||||
template<typename T>
|
||||
struct ProxyArg
|
||||
{
|
||||
static_assert(mozilla::IsPod<T>::value, "T must be primitive type");
|
||||
|
||||
// Primitive types can be saved by value.
|
||||
typedef T Type;
|
||||
typedef typename TypeAdapter<T>::JNIType JNIType;
|
||||
|
||||
static void Clear(JNIEnv* env, Type&) {}
|
||||
|
||||
static Type From(JNIEnv* env, JNIType val)
|
||||
{
|
||||
return TypeAdapter<T>::ToNative(env, val);
|
||||
}
|
||||
};
|
||||
|
||||
template<class C, typename T>
|
||||
struct ProxyArg<Ref<C, T>>
|
||||
{
|
||||
// Ref types need to be saved by global ref.
|
||||
typedef typename C::GlobalRef Type;
|
||||
typedef typename TypeAdapter<Ref<C, T>>::JNIType JNIType;
|
||||
|
||||
static void Clear(JNIEnv* env, Type& ref) { ref.Clear(env); }
|
||||
|
||||
static Type From(JNIEnv* env, JNIType val)
|
||||
{
|
||||
return Type(env, C::Ref::From(val));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename C> struct ProxyArg<const C&> : ProxyArg<C> {};
|
||||
template<> struct ProxyArg<StringParam> : ProxyArg<String::Ref> {};
|
||||
template<class C> struct ProxyArg<LocalRef<C>> : ProxyArg<typename C::Ref> {};
|
||||
|
||||
// ProxyNativeCall implements the functor object that is passed to OnNativeCall
|
||||
template<class Impl, class Owner, bool IsStatic,
|
||||
bool HasThisArg /* has instance/class local ref in the call */,
|
||||
typename... Args>
|
||||
class ProxyNativeCall : public AbstractCall
|
||||
{
|
||||
// "this arg" refers to the Class::LocalRef (for static methods) or
|
||||
// Owner::LocalRef (for instance methods) that we optionally (as indicated
|
||||
// by HasThisArg) pass into the destination C++ function.
|
||||
typedef typename mozilla::Conditional<IsStatic,
|
||||
Class, Owner>::Type ThisArgClass;
|
||||
typedef typename mozilla::Conditional<IsStatic,
|
||||
jclass, jobject>::Type ThisArgJNIType;
|
||||
|
||||
// Type signature of the destination C++ function, which matches the
|
||||
// Method template parameter in NativeStubImpl::Wrap.
|
||||
typedef typename mozilla::Conditional<IsStatic,
|
||||
typename mozilla::Conditional<HasThisArg,
|
||||
void (*) (const Class::LocalRef&, Args...),
|
||||
void (*) (Args...)>::Type,
|
||||
typename mozilla::Conditional<HasThisArg,
|
||||
void (Impl::*) (const typename Owner::LocalRef&, Args...),
|
||||
void (Impl::*) (Args...)>::Type>::Type NativeCallType;
|
||||
|
||||
// Destination C++ function.
|
||||
NativeCallType mNativeCall;
|
||||
// Saved this arg.
|
||||
typename ThisArgClass::GlobalRef mThisArg;
|
||||
// Saved arguments.
|
||||
mozilla::Tuple<typename ProxyArg<Args>::Type...> mArgs;
|
||||
|
||||
// We cannot use IsStatic and HasThisArg directly (without going through
|
||||
// extra hoops) because GCC complains about invalid overloads, so we use
|
||||
// another pair of template parameters, Static and ThisArg.
|
||||
|
||||
template<bool Static, bool ThisArg, size_t... Indices>
|
||||
typename mozilla::EnableIf<Static && ThisArg, void>::Type
|
||||
Call(const Class::LocalRef& cls,
|
||||
mozilla::IndexSequence<Indices...>) const
|
||||
{
|
||||
(*mNativeCall)(cls, mozilla::Get<Indices>(mArgs)...);
|
||||
}
|
||||
|
||||
template<bool Static, bool ThisArg, size_t... Indices>
|
||||
typename mozilla::EnableIf<Static && !ThisArg, void>::Type
|
||||
Call(const Class::LocalRef& cls,
|
||||
mozilla::IndexSequence<Indices...>) const
|
||||
{
|
||||
(*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
|
||||
}
|
||||
|
||||
template<bool Static, bool ThisArg, size_t... Indices>
|
||||
typename mozilla::EnableIf<!Static && ThisArg, void>::Type
|
||||
Call(const typename Owner::LocalRef& inst,
|
||||
mozilla::IndexSequence<Indices...>) const
|
||||
{
|
||||
Impl* const impl = NativePtr<Impl>::Get(inst);
|
||||
MOZ_CATCH_JNI_EXCEPTION(inst.Env());
|
||||
(impl->*mNativeCall)(inst, mozilla::Get<Indices>(mArgs)...);
|
||||
}
|
||||
|
||||
template<bool Static, bool ThisArg, size_t... Indices>
|
||||
typename mozilla::EnableIf<!Static && !ThisArg, void>::Type
|
||||
Call(const typename Owner::LocalRef& inst,
|
||||
mozilla::IndexSequence<Indices...>) const
|
||||
{
|
||||
Impl* const impl = NativePtr<Impl>::Get(inst);
|
||||
MOZ_CATCH_JNI_EXCEPTION(inst.Env());
|
||||
(impl->*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
|
||||
}
|
||||
|
||||
template<size_t... Indices>
|
||||
void Clear(JNIEnv* env, mozilla::IndexSequence<Indices...>)
|
||||
{
|
||||
int dummy[] = {
|
||||
(ProxyArg<Args>::Clear(env, Get<Indices>(mArgs)), 0)...
|
||||
};
|
||||
mozilla::Unused << dummy;
|
||||
}
|
||||
|
||||
public:
|
||||
// The class that implements the call target.
|
||||
typedef Impl TargetClass;
|
||||
typedef typename ThisArgClass::Param ThisArgType;
|
||||
|
||||
static const bool isStatic = IsStatic;
|
||||
|
||||
ProxyNativeCall(ThisArgJNIType thisArg,
|
||||
NativeCallType nativeCall,
|
||||
JNIEnv* env,
|
||||
typename ProxyArg<Args>::JNIType... args)
|
||||
: mNativeCall(nativeCall)
|
||||
, mThisArg(env, ThisArgClass::Ref::From(thisArg))
|
||||
, mArgs(ProxyArg<Args>::From(env, args)...)
|
||||
{}
|
||||
|
||||
ProxyNativeCall(ProxyNativeCall&&) = default;
|
||||
ProxyNativeCall(const ProxyNativeCall&) = default;
|
||||
|
||||
// Get class ref for static calls or object ref for instance calls.
|
||||
typename ThisArgClass::Param GetThisArg() const { return mThisArg; }
|
||||
|
||||
// Return if target is the given function pointer / pointer-to-member.
|
||||
// Because we can only compare pointers of the same type, we use a
|
||||
// templated overload that is chosen only if given a different type of
|
||||
// pointer than our target pointer type.
|
||||
bool IsTarget(NativeCallType call) const { return call == mNativeCall; }
|
||||
template<typename T> bool IsTarget(T&&) const { return false; }
|
||||
|
||||
// Redirect the call to another function / class member with the same
|
||||
// signature as the original target. Crash if given a wrong signature.
|
||||
void SetTarget(NativeCallType call) { mNativeCall = call; }
|
||||
template<typename T> void SetTarget(T&&) const { MOZ_CRASH(); }
|
||||
|
||||
void operator()() override
|
||||
{
|
||||
JNIEnv* const env = GetEnvForThread();
|
||||
typename ThisArgClass::LocalRef thisArg(env, mThisArg);
|
||||
Call<IsStatic, HasThisArg>(
|
||||
thisArg, typename IndexSequenceFor<Args...>::Type());
|
||||
|
||||
// Clear all saved global refs. We do this after the call is invoked,
|
||||
// and not inside the destructor because we already have a JNIEnv here,
|
||||
// so it's more efficient to clear out the saved args here. The
|
||||
// downside is that the call can only be invoked once.
|
||||
Clear(env, typename IndexSequenceFor<Args...>::Type());
|
||||
mThisArg.Clear(env);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Impl, bool HasThisArg, typename... Args>
|
||||
struct Dispatcher
|
||||
{
|
||||
template<class Traits, bool IsStatic = Traits::isStatic,
|
||||
typename... ProxyArgs>
|
||||
static typename EnableIf<
|
||||
Traits::dispatchTarget == DispatchTarget::PROXY, void>::Type
|
||||
Run(ProxyArgs&&... args)
|
||||
{
|
||||
Impl::OnNativeCall(ProxyNativeCall<
|
||||
Impl, typename Traits::Owner, IsStatic,
|
||||
HasThisArg, Args...>(Forward<ProxyArgs>(args)...));
|
||||
}
|
||||
|
||||
template<class Traits, bool IsStatic = Traits::isStatic,
|
||||
typename ThisArg, typename... ProxyArgs>
|
||||
static typename EnableIf<
|
||||
Traits::dispatchTarget == DispatchTarget::GECKO, void>::Type
|
||||
Run(ThisArg thisArg, ProxyArgs&&... args)
|
||||
{
|
||||
// For a static method, do not forward the "this arg" (i.e. the class
|
||||
// local ref) if the implementation does not request it. This saves us
|
||||
// a pair of calls to add/delete global ref.
|
||||
DispatchToGeckoThread(MakeUnique<ProxyNativeCall<
|
||||
Impl, typename Traits::Owner, IsStatic, HasThisArg,
|
||||
Args...>>(HasThisArg || !IsStatic ? thisArg : nullptr,
|
||||
Forward<ProxyArgs>(args)...));
|
||||
}
|
||||
|
||||
template<class Traits, bool IsStatic = false, typename... ProxyArgs>
|
||||
static typename EnableIf<
|
||||
Traits::dispatchTarget == DispatchTarget::CURRENT, void>::Type
|
||||
Run(ProxyArgs&&... args) {}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Wrapper methods that convert arguments from the JNI types to the native
|
||||
// types, e.g. from jobject to jni::Object::Ref. For instance methods, the
|
||||
// wrapper methods also convert calls to calls on objects.
|
||||
//
|
||||
// We need specialization for static/non-static because the two have different
|
||||
// signatures (jobject vs jclass and Impl::*Method vs *Method).
|
||||
// We need specialization for return type, because void return type requires
|
||||
// us to not deal with the return value.
|
||||
|
||||
// Bug 1207642 - Work around Dalvik bug by realigning stack on JNI entry
|
||||
#ifdef __i386__
|
||||
#define MOZ_JNICALL JNICALL __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define MOZ_JNICALL JNICALL
|
||||
#endif
|
||||
|
||||
template<class Traits, class Impl, class Args = typename Traits::Args>
|
||||
class NativeStub;
|
||||
|
||||
template<class Traits, class Impl, typename... Args>
|
||||
class NativeStub<Traits, Impl, jni::Args<Args...>>
|
||||
{
|
||||
using Owner = typename Traits::Owner;
|
||||
using ReturnType = typename Traits::ReturnType;
|
||||
|
||||
static constexpr bool isStatic = Traits::isStatic;
|
||||
static constexpr bool isVoid = mozilla::IsVoid<ReturnType>::value;
|
||||
|
||||
struct VoidType { using JNIType = void; };
|
||||
using ReturnJNIType = typename Conditional<
|
||||
isVoid, VoidType, TypeAdapter<ReturnType>>::Type::JNIType;
|
||||
|
||||
using ReturnTypeForNonVoidInstance = typename Conditional<
|
||||
!isStatic && !isVoid, ReturnType, VoidType>::Type;
|
||||
using ReturnTypeForVoidInstance = typename Conditional<
|
||||
!isStatic && isVoid, ReturnType, VoidType&>::Type;
|
||||
using ReturnTypeForNonVoidStatic = typename Conditional<
|
||||
isStatic && !isVoid, ReturnType, VoidType>::Type;
|
||||
using ReturnTypeForVoidStatic = typename Conditional<
|
||||
isStatic && isVoid, ReturnType, VoidType&>::Type;
|
||||
|
||||
static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT || isVoid,
|
||||
"Dispatched calls must have void return type");
|
||||
|
||||
public:
|
||||
// Non-void instance method
|
||||
template<ReturnTypeForNonVoidInstance (Impl::*Method) (Args...)>
|
||||
static MOZ_JNICALL ReturnJNIType
|
||||
Wrap(JNIEnv* env, jobject instance,
|
||||
typename TypeAdapter<Args>::JNIType... args)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
Impl* const impl = NativePtr<Impl>::Get(env, instance);
|
||||
if (!impl) {
|
||||
// There is a pending JNI exception at this point.
|
||||
return ReturnJNIType();
|
||||
}
|
||||
return TypeAdapter<ReturnType>::FromNative(env,
|
||||
(impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...));
|
||||
}
|
||||
|
||||
// Non-void instance method with instance reference
|
||||
template<ReturnTypeForNonVoidInstance (Impl::*Method)
|
||||
(const typename Owner::LocalRef&, Args...)>
|
||||
static MOZ_JNICALL ReturnJNIType
|
||||
Wrap(JNIEnv* env, jobject instance,
|
||||
typename TypeAdapter<Args>::JNIType... args)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
Impl* const impl = NativePtr<Impl>::Get(env, instance);
|
||||
if (!impl) {
|
||||
// There is a pending JNI exception at this point.
|
||||
return ReturnJNIType();
|
||||
}
|
||||
auto self = Owner::LocalRef::Adopt(env, instance);
|
||||
const auto res = TypeAdapter<ReturnType>::FromNative(env,
|
||||
(impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...));
|
||||
self.Forget();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Void instance method
|
||||
template<ReturnTypeForVoidInstance (Impl::*Method) (Args...)>
|
||||
static MOZ_JNICALL void
|
||||
Wrap(JNIEnv* env, jobject instance,
|
||||
typename TypeAdapter<Args>::JNIType... args)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
|
||||
Dispatcher<Impl, /* HasThisArg */ false, Args...>::
|
||||
template Run<Traits>(instance, Method, env, args...);
|
||||
return;
|
||||
}
|
||||
|
||||
Impl* const impl = NativePtr<Impl>::Get(env, instance);
|
||||
if (!impl) {
|
||||
// There is a pending JNI exception at this point.
|
||||
return;
|
||||
}
|
||||
(impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...);
|
||||
}
|
||||
|
||||
// Void instance method with instance reference
|
||||
template<ReturnTypeForVoidInstance (Impl::*Method)
|
||||
(const typename Owner::LocalRef&, Args...)>
|
||||
static MOZ_JNICALL void
|
||||
Wrap(JNIEnv* env, jobject instance,
|
||||
typename TypeAdapter<Args>::JNIType... args)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
|
||||
Dispatcher<Impl, /* HasThisArg */ true, Args...>::
|
||||
template Run<Traits>(instance, Method, env, args...);
|
||||
return;
|
||||
}
|
||||
|
||||
Impl* const impl = NativePtr<Impl>::Get(env, instance);
|
||||
if (!impl) {
|
||||
// There is a pending JNI exception at this point.
|
||||
return;
|
||||
}
|
||||
auto self = Owner::LocalRef::Adopt(env, instance);
|
||||
(impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...);
|
||||
self.Forget();
|
||||
}
|
||||
|
||||
// Overload for DisposeNative
|
||||
template<ReturnTypeForVoidInstance (*DisposeNative)
|
||||
(const typename Owner::LocalRef&)>
|
||||
static MOZ_JNICALL void
|
||||
Wrap(JNIEnv* env, jobject instance)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
|
||||
using LocalRef = typename Owner::LocalRef;
|
||||
Dispatcher<Impl, /* HasThisArg */ false, const LocalRef&>::
|
||||
template Run<Traits, /* IsStatic */ true>(
|
||||
/* ThisArg */ nullptr, DisposeNative, env, instance);
|
||||
return;
|
||||
}
|
||||
|
||||
auto self = Owner::LocalRef::Adopt(env, instance);
|
||||
(Impl::DisposeNative)(self);
|
||||
self.Forget();
|
||||
}
|
||||
|
||||
// Non-void static method
|
||||
template<ReturnTypeForNonVoidStatic (*Method) (Args...)>
|
||||
static MOZ_JNICALL ReturnJNIType
|
||||
Wrap(JNIEnv* env, jclass, typename TypeAdapter<Args>::JNIType... args)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
return TypeAdapter<ReturnType>::FromNative(env,
|
||||
(*Method)(TypeAdapter<Args>::ToNative(env, args)...));
|
||||
}
|
||||
|
||||
// Non-void static method with class reference
|
||||
template<ReturnTypeForNonVoidStatic (*Method)
|
||||
(const Class::LocalRef&, Args...)>
|
||||
static MOZ_JNICALL ReturnJNIType
|
||||
Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
auto clazz = Class::LocalRef::Adopt(env, cls);
|
||||
const auto res = TypeAdapter<ReturnType>::FromNative(env,
|
||||
(*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...));
|
||||
clazz.Forget();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Void static method
|
||||
template<ReturnTypeForVoidStatic (*Method) (Args...)>
|
||||
static MOZ_JNICALL void
|
||||
Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
|
||||
Dispatcher<Impl, /* HasThisArg */ false, Args...>::
|
||||
template Run<Traits>(cls, Method, env, args...);
|
||||
return;
|
||||
}
|
||||
|
||||
(*Method)(TypeAdapter<Args>::ToNative(env, args)...);
|
||||
}
|
||||
|
||||
// Void static method with class reference
|
||||
template<ReturnTypeForVoidStatic (*Method)
|
||||
(const Class::LocalRef&, Args...)>
|
||||
static MOZ_JNICALL void
|
||||
Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
|
||||
{
|
||||
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
|
||||
|
||||
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
|
||||
Dispatcher<Impl, /* HasThisArg */ true, Args...>::
|
||||
template Run<Traits>(cls, Method, env, args...);
|
||||
return;
|
||||
}
|
||||
|
||||
auto clazz = Class::LocalRef::Adopt(env, cls);
|
||||
(*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...);
|
||||
clazz.Forget();
|
||||
}
|
||||
};
|
||||
|
||||
// Generate a JNINativeMethod from a native
|
||||
// method's traits class and a wrapped stub.
|
||||
template<class Traits, typename Ret, typename... Args>
|
||||
constexpr JNINativeMethod MakeNativeMethod(MOZ_JNICALL Ret (*stub)(JNIEnv*, Args...))
|
||||
{
|
||||
return {
|
||||
Traits::name,
|
||||
Traits::signature,
|
||||
reinterpret_cast<void*>(stub)
|
||||
};
|
||||
}
|
||||
|
||||
// Class inherited by implementing class.
|
||||
template<class Cls, class Impl>
|
||||
class NativeImpl
|
||||
{
|
||||
typedef typename Cls::template Natives<Impl> Natives;
|
||||
|
||||
static bool sInited;
|
||||
|
||||
public:
|
||||
static void Init() {
|
||||
if (sInited) {
|
||||
return;
|
||||
}
|
||||
const auto& ctx = typename Cls::Context();
|
||||
ctx.Env()->RegisterNatives(
|
||||
ctx.ClassRef(), Natives::methods,
|
||||
sizeof(Natives::methods) / sizeof(Natives::methods[0]));
|
||||
MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
|
||||
sInited = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Associate a C++ instance with a Java instance.
|
||||
static void AttachNative(const typename Cls::LocalRef& instance,
|
||||
SupportsWeakPtr<Impl>* ptr)
|
||||
{
|
||||
static_assert(mozilla::IsBaseOf<SupportsWeakPtr<Impl>, Impl>::value,
|
||||
"Attach with UniquePtr&& when not using WeakPtr");
|
||||
return NativePtr<Impl>::Set(instance, static_cast<Impl*>(ptr));
|
||||
}
|
||||
|
||||
static void AttachNative(const typename Cls::LocalRef& instance,
|
||||
UniquePtr<Impl>&& ptr)
|
||||
{
|
||||
static_assert(!mozilla::IsBaseOf<SupportsWeakPtr<Impl>, Impl>::value,
|
||||
"Attach with SupportsWeakPtr* when using WeakPtr");
|
||||
return NativePtr<Impl>::Set(instance, mozilla::Move(ptr));
|
||||
}
|
||||
|
||||
// Get the C++ instance associated with a Java instance.
|
||||
// There is always a pending exception if the return value is nullptr.
|
||||
static Impl* GetNative(const typename Cls::LocalRef& instance) {
|
||||
return NativePtr<Impl>::Get(instance);
|
||||
}
|
||||
|
||||
static void DisposeNative(const typename Cls::LocalRef& instance) {
|
||||
NativePtr<Impl>::Clear(instance);
|
||||
}
|
||||
|
||||
NativeImpl() {
|
||||
// Initialize on creation if not already initialized.
|
||||
Init();
|
||||
}
|
||||
};
|
||||
|
||||
// Define static member.
|
||||
template<class C, class I>
|
||||
bool NativeImpl<C, I>::sInited;
|
||||
|
||||
} // namespace jni
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_jni_Natives_h__
|
||||
953
widget/android/jni/Refs.h
Normal file
953
widget/android/jni/Refs.h
Normal file
|
|
@ -0,0 +1,953 @@
|
|||
#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"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace jni {
|
||||
|
||||
// Wrapped object reference (e.g. jobject, jclass, etc...)
|
||||
template<class Cls, typename JNIType> class Ref;
|
||||
// Represents a calling context for JNI methods.
|
||||
template<class Cls, typename JNIType> class Context;
|
||||
// Wrapped local reference that inherits from Ref.
|
||||
template<class Cls> class LocalRef;
|
||||
// Wrapped global reference that inherits from Ref.
|
||||
template<class Cls> class GlobalRef;
|
||||
// Wrapped dangling reference that's owned by someone else.
|
||||
template<class Cls> class DependentRef;
|
||||
|
||||
|
||||
// Class to hold the native types of a method's arguments.
|
||||
// For example, if a method has signature (ILjava/lang/String;)V,
|
||||
// its arguments class would be jni::Args<int32_t, jni::String::Param>
|
||||
template<typename...>
|
||||
struct Args {};
|
||||
|
||||
|
||||
class Object;
|
||||
|
||||
// Base class for Ref and its specializations.
|
||||
template<class Cls, typename Type>
|
||||
class Ref
|
||||
{
|
||||
template<class C, typename T> friend class Ref;
|
||||
|
||||
using Self = Ref<Cls, Type>;
|
||||
using bool_type = void (Self::*)() const;
|
||||
void non_null_reference() const {}
|
||||
|
||||
// A Cls-derivative that allows copying
|
||||
// (e.g. when acting as a return value).
|
||||
struct CopyableCtx : public Context<Cls, Type>
|
||||
{
|
||||
CopyableCtx(JNIEnv* env, Type instance)
|
||||
: Context<Cls, Type>(env, instance)
|
||||
{}
|
||||
|
||||
CopyableCtx(const CopyableCtx& cls)
|
||||
: Context<Cls, Type>(cls.Env(), cls.Get())
|
||||
{}
|
||||
};
|
||||
|
||||
// Private 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&) = default;
|
||||
|
||||
protected:
|
||||
static JNIEnv* FindEnv()
|
||||
{
|
||||
return Cls::callingThread == CallingThread::GECKO ?
|
||||
GetGeckoThreadEnv() : GetEnvForThread();
|
||||
}
|
||||
|
||||
Type mInstance;
|
||||
|
||||
// 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.
|
||||
explicit Ref(Type instance) : mInstance(instance) {}
|
||||
|
||||
public:
|
||||
using JNIType = Type;
|
||||
|
||||
// Construct a Ref form a raw JNI reference.
|
||||
static Ref<Cls, Type> From(JNIType obj)
|
||||
{
|
||||
return Ref<Cls, Type>(obj);
|
||||
}
|
||||
|
||||
// Construct a Ref form a generic object reference.
|
||||
static Ref<Cls, Type> From(const Ref<Object, jobject>& obj)
|
||||
{
|
||||
return Ref<Cls, Type>(JNIType(obj.Get()));
|
||||
}
|
||||
|
||||
MOZ_IMPLICIT Ref(decltype(nullptr)) : mInstance(nullptr) {}
|
||||
|
||||
// Get the raw JNI reference.
|
||||
JNIType Get() const
|
||||
{
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
bool operator==(const Ref& other) const
|
||||
{
|
||||
// Treat two references of the same object as being the same.
|
||||
return mInstance == other.mInstance || JNI_FALSE !=
|
||||
FindEnv()->IsSameObject(mInstance, other.mInstance);
|
||||
}
|
||||
|
||||
bool operator!=(const Ref& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool operator==(decltype(nullptr)) const
|
||||
{
|
||||
return !mInstance;
|
||||
}
|
||||
|
||||
bool operator!=(decltype(nullptr)) const
|
||||
{
|
||||
return !!mInstance;
|
||||
}
|
||||
|
||||
CopyableCtx operator->() const
|
||||
{
|
||||
return CopyableCtx(FindEnv(), mInstance);
|
||||
}
|
||||
|
||||
// Any ref can be cast to an object ref.
|
||||
operator Ref<Object, jobject>() const
|
||||
{
|
||||
return Ref<Object, jobject>(mInstance);
|
||||
}
|
||||
|
||||
// Null checking (e.g. !!ref) using the safe-bool idiom.
|
||||
operator bool_type() const
|
||||
{
|
||||
return 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;
|
||||
};
|
||||
|
||||
|
||||
// Represents a calling context for JNI methods.
|
||||
template<class Cls, typename Type>
|
||||
class Context : public Ref<Cls, Type>
|
||||
{
|
||||
using Ref = jni::Ref<Cls, Type>;
|
||||
|
||||
static jclass sClassRef; // global reference
|
||||
|
||||
protected:
|
||||
JNIEnv* const mEnv;
|
||||
|
||||
public:
|
||||
static jclass RawClassRef()
|
||||
{
|
||||
return sClassRef;
|
||||
}
|
||||
|
||||
Context()
|
||||
: Ref(nullptr)
|
||||
, mEnv(Ref::FindEnv())
|
||||
{}
|
||||
|
||||
Context(JNIEnv* env, Type instance)
|
||||
: Ref(instance)
|
||||
, mEnv(env)
|
||||
{}
|
||||
|
||||
jclass ClassRef() const
|
||||
{
|
||||
if (!sClassRef) {
|
||||
const jclass cls = GetClassRef(mEnv, Cls::name);
|
||||
sClassRef = jclass(mEnv->NewGlobalRef(cls));
|
||||
mEnv->DeleteLocalRef(cls);
|
||||
}
|
||||
return sClassRef;
|
||||
}
|
||||
|
||||
JNIEnv* Env() const
|
||||
{
|
||||
return mEnv;
|
||||
}
|
||||
|
||||
bool operator==(const Ref& other) const
|
||||
{
|
||||
// Treat two references of the same object as being the same.
|
||||
return Ref::mInstance == other.mInstance || JNI_FALSE !=
|
||||
mEnv->IsSameObject(Ref::mInstance, other.mInstance);
|
||||
}
|
||||
|
||||
bool operator!=(const Ref& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool operator==(decltype(nullptr)) const
|
||||
{
|
||||
return !Ref::mInstance;
|
||||
}
|
||||
|
||||
bool operator!=(decltype(nullptr)) const
|
||||
{
|
||||
return !!Ref::mInstance;
|
||||
}
|
||||
|
||||
Cls operator->() const
|
||||
{
|
||||
MOZ_ASSERT(Ref::mInstance, "Null jobject");
|
||||
return Cls(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class Cls, typename Type = jobject>
|
||||
class ObjectBase
|
||||
{
|
||||
protected:
|
||||
const jni::Context<Cls, Type>& mCtx;
|
||||
|
||||
jclass ClassRef() const { return mCtx.ClassRef(); }
|
||||
JNIEnv* Env() const { return mCtx.Env(); }
|
||||
Type Instance() const { return mCtx.Get(); }
|
||||
|
||||
public:
|
||||
using Ref = jni::Ref<Cls, Type>;
|
||||
using Context = jni::Context<Cls, Type>;
|
||||
using LocalRef = jni::LocalRef<Cls>;
|
||||
using GlobalRef = jni::GlobalRef<Cls>;
|
||||
using Param = const Ref&;
|
||||
|
||||
static const CallingThread callingThread = CallingThread::ANY;
|
||||
static const char name[];
|
||||
|
||||
explicit ObjectBase(const Context& ctx) : mCtx(ctx) {}
|
||||
|
||||
Cls* operator->()
|
||||
{
|
||||
return static_cast<Cls*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
// Binding for a plain jobject.
|
||||
class Object : public ObjectBase<Object, jobject>
|
||||
{
|
||||
public:
|
||||
explicit Object(const Context& ctx) : ObjectBase<Object, jobject>(ctx) {}
|
||||
};
|
||||
|
||||
// Binding for a built-in object reference other than jobject.
|
||||
template<typename T>
|
||||
class TypedObject : public ObjectBase<TypedObject<T>, T>
|
||||
{
|
||||
public:
|
||||
explicit TypedObject(const Context<TypedObject<T>, T>& ctx)
|
||||
: ObjectBase<TypedObject<T>, T>(ctx)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
// Define bindings for built-in types.
|
||||
using String = TypedObject<jstring>;
|
||||
using Class = TypedObject<jclass>;
|
||||
using Throwable = TypedObject<jthrowable>;
|
||||
|
||||
using BooleanArray = TypedObject<jbooleanArray>;
|
||||
using ByteArray = TypedObject<jbyteArray>;
|
||||
using CharArray = TypedObject<jcharArray>;
|
||||
using ShortArray = TypedObject<jshortArray>;
|
||||
using IntArray = TypedObject<jintArray>;
|
||||
using LongArray = TypedObject<jlongArray>;
|
||||
using FloatArray = TypedObject<jfloatArray>;
|
||||
using DoubleArray = TypedObject<jdoubleArray>;
|
||||
using ObjectArray = TypedObject<jobjectArray>;
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
// See explanation in LocalRef.
|
||||
template<class Cls> struct GenericObject { using Type = Object; };
|
||||
template<> struct GenericObject<Object>
|
||||
{
|
||||
struct Type {
|
||||
using Ref = jni::Ref<Type, jobject>;
|
||||
using Context = jni::Context<Type, jobject>;
|
||||
};
|
||||
};
|
||||
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 Cls::Context
|
||||
{
|
||||
template<class C> friend class LocalRef;
|
||||
|
||||
using Ctx = typename Cls::Context;
|
||||
using Ref = typename Cls::Ref;
|
||||
using JNIType = typename Ref::JNIType;
|
||||
|
||||
// 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>.
|
||||
using GenericObject = typename detail::GenericObject<Cls>::Type;
|
||||
|
||||
// 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 detail::GenericLocalRef<Cls>::template Type<C>;
|
||||
|
||||
static JNIType NewLocalRef(JNIEnv* env, JNIType obj)
|
||||
{
|
||||
return JNIType(obj ? env->NewLocalRef(obj) : nullptr);
|
||||
}
|
||||
|
||||
LocalRef(JNIEnv* env, JNIType instance) : Ctx(env, instance) {}
|
||||
|
||||
LocalRef& swap(LocalRef& other)
|
||||
{
|
||||
auto instance = other.mInstance;
|
||||
other.mInstance = Ctx::mInstance;
|
||||
Ctx::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(JNIType instance)
|
||||
{
|
||||
return LocalRef(Ref::FindEnv(), instance);
|
||||
}
|
||||
|
||||
static LocalRef Adopt(JNIEnv* env, JNIType instance)
|
||||
{
|
||||
return LocalRef(env, instance);
|
||||
}
|
||||
|
||||
// Copy constructor.
|
||||
LocalRef(const LocalRef<Cls>& ref)
|
||||
: Ctx(ref.mEnv, NewLocalRef(ref.mEnv, ref.mInstance))
|
||||
{}
|
||||
|
||||
// Move constructor.
|
||||
LocalRef(LocalRef<Cls>&& ref)
|
||||
: Ctx(ref.mEnv, ref.mInstance)
|
||||
{
|
||||
ref.mInstance = nullptr;
|
||||
}
|
||||
|
||||
explicit LocalRef(JNIEnv* env = Ref::FindEnv())
|
||||
: Ctx(env, nullptr)
|
||||
{}
|
||||
|
||||
// Construct a LocalRef from any Ref,
|
||||
// which means creating a new local reference.
|
||||
MOZ_IMPLICIT LocalRef(const Ref& ref)
|
||||
: Ctx(Ref::FindEnv(), nullptr)
|
||||
{
|
||||
Ctx::mInstance = NewLocalRef(Ctx::mEnv, ref.Get());
|
||||
}
|
||||
|
||||
LocalRef(JNIEnv* env, const Ref& ref)
|
||||
: Ctx(env, NewLocalRef(env, ref.Get()))
|
||||
{}
|
||||
|
||||
// Move a LocalRef<Object> into a LocalRef<Cls> without
|
||||
// creating/deleting local references.
|
||||
MOZ_IMPLICIT LocalRef(LocalRef<GenericObject>&& ref)
|
||||
: Ctx(ref.mEnv, JNIType(ref.mInstance))
|
||||
{
|
||||
ref.mInstance = nullptr;
|
||||
}
|
||||
|
||||
template<class C>
|
||||
MOZ_IMPLICIT LocalRef(GenericLocalRef<C>&& ref)
|
||||
: Ctx(ref.mEnv, ref.mInstance)
|
||||
{
|
||||
ref.mInstance = nullptr;
|
||||
}
|
||||
|
||||
// Implicitly converts nullptr to LocalRef.
|
||||
MOZ_IMPLICIT LocalRef(decltype(nullptr))
|
||||
: Ctx(Ref::FindEnv(), nullptr)
|
||||
{}
|
||||
|
||||
~LocalRef()
|
||||
{
|
||||
if (Ctx::mInstance) {
|
||||
Ctx::mEnv->DeleteLocalRef(Ctx::mInstance);
|
||||
Ctx::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.
|
||||
typename Ref::JNIType Forget()
|
||||
{
|
||||
const auto obj = Ctx::Get();
|
||||
Ctx::mInstance = nullptr;
|
||||
return obj;
|
||||
}
|
||||
|
||||
LocalRef<Cls>& operator=(LocalRef<Cls> ref)
|
||||
{
|
||||
return swap(ref);
|
||||
}
|
||||
|
||||
LocalRef<Cls>& operator=(const Ref& ref)
|
||||
{
|
||||
LocalRef<Cls> newRef(Ctx::mEnv, ref);
|
||||
return swap(newRef);
|
||||
}
|
||||
|
||||
LocalRef<Cls>& operator=(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(Ctx::mEnv, nullptr);
|
||||
return swap(newRef);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class Cls>
|
||||
class GlobalRef : public Cls::Ref
|
||||
{
|
||||
using Ref = typename Cls::Ref;
|
||||
using JNIType = typename Ref::JNIType;
|
||||
|
||||
static JNIType NewGlobalRef(JNIEnv* env, JNIType instance)
|
||||
{
|
||||
return JNIType(instance ? env->NewGlobalRef(instance) : nullptr);
|
||||
}
|
||||
|
||||
GlobalRef& swap(GlobalRef& other)
|
||||
{
|
||||
auto instance = other.mInstance;
|
||||
other.mInstance = Ref::mInstance;
|
||||
Ref::mInstance = instance;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
GlobalRef()
|
||||
: Ref(nullptr)
|
||||
{}
|
||||
|
||||
// Copy constructor
|
||||
GlobalRef(const GlobalRef& ref)
|
||||
: Ref(NewGlobalRef(GetEnvForThread(), ref.mInstance))
|
||||
{}
|
||||
|
||||
// Move constructor
|
||||
GlobalRef(GlobalRef&& ref)
|
||||
: Ref(ref.mInstance)
|
||||
{
|
||||
ref.mInstance = nullptr;
|
||||
}
|
||||
|
||||
MOZ_IMPLICIT GlobalRef(const Ref& ref)
|
||||
: Ref(NewGlobalRef(GetEnvForThread(), ref.Get()))
|
||||
{}
|
||||
|
||||
GlobalRef(JNIEnv* env, const Ref& ref)
|
||||
: Ref(NewGlobalRef(env, ref.Get()))
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT GlobalRef(const LocalRef<Cls>& ref)
|
||||
: Ref(NewGlobalRef(ref.Env(), ref.Get()))
|
||||
{}
|
||||
|
||||
// Implicitly converts nullptr to GlobalRef.
|
||||
MOZ_IMPLICIT GlobalRef(decltype(nullptr))
|
||||
: Ref(nullptr)
|
||||
{}
|
||||
|
||||
~GlobalRef()
|
||||
{
|
||||
if (Ref::mInstance) {
|
||||
Clear(GetEnvForThread());
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
typename Ref::JNIType Forget()
|
||||
{
|
||||
const auto obj = Ref::Get();
|
||||
Ref::mInstance = nullptr;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Clear(JNIEnv* env)
|
||||
{
|
||||
if (Ref::mInstance) {
|
||||
env->DeleteGlobalRef(Ref::mInstance);
|
||||
Ref::mInstance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalRef<Cls>& operator=(GlobalRef<Cls> ref)
|
||||
{
|
||||
return swap(ref);
|
||||
}
|
||||
|
||||
GlobalRef<Cls>& operator=(const Ref& ref)
|
||||
{
|
||||
GlobalRef<Cls> newRef(ref);
|
||||
return swap(newRef);
|
||||
}
|
||||
|
||||
GlobalRef<Cls>& operator=(const LocalRef<Cls>& ref)
|
||||
{
|
||||
GlobalRef<Cls> newRef(ref);
|
||||
return swap(newRef);
|
||||
}
|
||||
|
||||
GlobalRef<Cls>& operator=(decltype(nullptr))
|
||||
{
|
||||
GlobalRef<Cls> newRef(nullptr);
|
||||
return swap(newRef);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class Cls>
|
||||
class DependentRef : public Cls::Ref
|
||||
{
|
||||
using Ref = typename Cls::Ref;
|
||||
|
||||
public:
|
||||
DependentRef(typename Ref::JNIType instance)
|
||||
: Ref(instance)
|
||||
{}
|
||||
|
||||
DependentRef(const DependentRef& ref)
|
||||
: Ref(ref.Get())
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
class StringParam;
|
||||
|
||||
template<>
|
||||
class TypedObject<jstring> : public ObjectBase<TypedObject<jstring>, jstring>
|
||||
{
|
||||
using Base = ObjectBase<TypedObject<jstring>, jstring>;
|
||||
|
||||
public:
|
||||
using Param = const StringParam&;
|
||||
|
||||
explicit TypedObject(const Context& ctx) : Base(ctx) {}
|
||||
|
||||
size_t Length() const
|
||||
{
|
||||
const size_t ret = Base::Env()->GetStringLength(Base::Instance());
|
||||
MOZ_CATCH_JNI_EXCEPTION(Base::Env());
|
||||
return ret;
|
||||
}
|
||||
|
||||
nsString ToString() const
|
||||
{
|
||||
const jchar* const str = Base::Env()->GetStringChars(
|
||||
Base::Instance(), nullptr);
|
||||
const jsize len = Base::Env()->GetStringLength(Base::Instance());
|
||||
|
||||
nsString result(reinterpret_cast<const char16_t*>(str), len);
|
||||
Base::Env()->ReleaseStringChars(Base::Instance(), str);
|
||||
return result;
|
||||
}
|
||||
|
||||
nsCString ToCString() const
|
||||
{
|
||||
return NS_ConvertUTF16toUTF8(ToString());
|
||||
}
|
||||
|
||||
// Convert jstring to a nsString.
|
||||
operator nsString() const
|
||||
{
|
||||
return ToString();
|
||||
}
|
||||
|
||||
// Convert jstring to a nsCString.
|
||||
operator nsCString() const
|
||||
{
|
||||
return ToCString();
|
||||
}
|
||||
};
|
||||
|
||||
// Define a custom parameter type for String,
|
||||
// which accepts both String::Ref and nsAString/nsACString
|
||||
class StringParam : public String::Ref
|
||||
{
|
||||
using Ref = String::Ref;
|
||||
|
||||
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());
|
||||
MOZ_CATCH_JNI_EXCEPTION(env);
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
MOZ_IMPLICIT StringParam(decltype(nullptr))
|
||||
: Ref(nullptr)
|
||||
, mEnv(nullptr)
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT StringParam(const Ref& ref)
|
||||
: Ref(ref.Get())
|
||||
, mEnv(nullptr)
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env = Ref::FindEnv())
|
||||
: Ref(GetString(env, str))
|
||||
, mEnv(env)
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT StringParam(const char16_t* str, JNIEnv* env = Ref::FindEnv())
|
||||
: Ref(GetString(env, nsDependentString(str)))
|
||||
, mEnv(env)
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env = Ref::FindEnv())
|
||||
: Ref(GetString(env, NS_ConvertUTF8toUTF16(str)))
|
||||
, mEnv(env)
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT StringParam(const char* str, JNIEnv* env = Ref::FindEnv())
|
||||
: Ref(GetString(env, NS_ConvertUTF8toUTF16(str)))
|
||||
, mEnv(env)
|
||||
{}
|
||||
|
||||
StringParam(StringParam&& other)
|
||||
: Ref(other.Get())
|
||||
, mEnv(other.mEnv)
|
||||
{
|
||||
other.mInstance = nullptr;
|
||||
}
|
||||
|
||||
~StringParam()
|
||||
{
|
||||
if (mEnv && Get()) {
|
||||
mEnv->DeleteLocalRef(Get());
|
||||
}
|
||||
}
|
||||
|
||||
operator String::LocalRef() const
|
||||
{
|
||||
// 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(mEnv ? mEnv : Ref::FindEnv(), *this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace detail {
|
||||
template<typename T> struct TypeAdapter;
|
||||
}
|
||||
|
||||
// Ref specialization for arrays.
|
||||
template<typename JNIType, class ElementType>
|
||||
class ArrayRefBase : public ObjectBase<TypedObject<JNIType>, JNIType>
|
||||
{
|
||||
using Base = ObjectBase<TypedObject<JNIType>, JNIType>;
|
||||
|
||||
public:
|
||||
explicit ArrayRefBase(const Context<TypedObject<JNIType>, JNIType>& ctx)
|
||||
: Base(ctx)
|
||||
{}
|
||||
|
||||
static typename Base::LocalRef New(const ElementType* data, size_t length) {
|
||||
using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
|
||||
static_assert(sizeof(ElementType) == sizeof(JNIElemType),
|
||||
"Size of native type must match size of JNI type");
|
||||
JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
|
||||
auto result =
|
||||
(jenv->*detail::TypeAdapter<ElementType>::NewArray)(length);
|
||||
MOZ_CATCH_JNI_EXCEPTION(jenv);
|
||||
(jenv->*detail::TypeAdapter<ElementType>::SetArray)(
|
||||
result, jsize(0), length,
|
||||
reinterpret_cast<const JNIElemType*>(data));
|
||||
MOZ_CATCH_JNI_EXCEPTION(jenv);
|
||||
return Base::LocalRef::Adopt(jenv, result);
|
||||
}
|
||||
|
||||
size_t Length() const
|
||||
{
|
||||
const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
|
||||
MOZ_CATCH_JNI_EXCEPTION(Base::Env());
|
||||
return ret;
|
||||
}
|
||||
|
||||
ElementType GetElement(size_t index) const
|
||||
{
|
||||
using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
|
||||
static_assert(sizeof(ElementType) == sizeof(JNIElemType),
|
||||
"Size of native type must match size of JNI type");
|
||||
|
||||
ElementType ret;
|
||||
(Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
|
||||
Base::Instance(), jsize(index), 1,
|
||||
reinterpret_cast<JNIElemType*>(&ret));
|
||||
MOZ_CATCH_JNI_EXCEPTION(Base::Env());
|
||||
return ret;
|
||||
}
|
||||
|
||||
nsTArray<ElementType> GetElements() const
|
||||
{
|
||||
using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
|
||||
static_assert(sizeof(ElementType) == sizeof(JNIElemType),
|
||||
"Size of native type must match size of JNI type");
|
||||
|
||||
const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
|
||||
|
||||
nsTArray<ElementType> array((size_t(len)));
|
||||
array.SetLength(size_t(len));
|
||||
(Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
|
||||
Base::Instance(), 0, len,
|
||||
reinterpret_cast<JNIElemType*>(array.Elements()));
|
||||
return array;
|
||||
}
|
||||
|
||||
ElementType operator[](size_t index) const
|
||||
{
|
||||
return GetElement(index);
|
||||
}
|
||||
|
||||
operator nsTArray<ElementType>() const
|
||||
{
|
||||
return GetElements();
|
||||
}
|
||||
};
|
||||
|
||||
#define DEFINE_PRIMITIVE_ARRAY_REF(JNIType, ElementType) \
|
||||
template<> \
|
||||
class TypedObject<JNIType> : public ArrayRefBase<JNIType, ElementType> \
|
||||
{ \
|
||||
public: \
|
||||
explicit TypedObject(const Context& ctx) \
|
||||
: ArrayRefBase<JNIType, ElementType>(ctx) \
|
||||
{} \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_ARRAY_REF(jbooleanArray, bool);
|
||||
DEFINE_PRIMITIVE_ARRAY_REF(jbyteArray, int8_t);
|
||||
DEFINE_PRIMITIVE_ARRAY_REF(jcharArray, char16_t);
|
||||
DEFINE_PRIMITIVE_ARRAY_REF(jshortArray, int16_t);
|
||||
DEFINE_PRIMITIVE_ARRAY_REF(jintArray, int32_t);
|
||||
DEFINE_PRIMITIVE_ARRAY_REF(jlongArray, int64_t);
|
||||
DEFINE_PRIMITIVE_ARRAY_REF(jfloatArray, float);
|
||||
DEFINE_PRIMITIVE_ARRAY_REF(jdoubleArray, double);
|
||||
|
||||
#undef DEFINE_PRIMITIVE_ARRAY_REF
|
||||
|
||||
|
||||
class ByteBuffer : public ObjectBase<ByteBuffer, jobject>
|
||||
{
|
||||
public:
|
||||
explicit ByteBuffer(const Context& ctx)
|
||||
: ObjectBase<ByteBuffer, jobject>(ctx)
|
||||
{}
|
||||
|
||||
static LocalRef New(void* data, size_t capacity)
|
||||
{
|
||||
JNIEnv* const env = GetEnvForThread();
|
||||
const auto ret = LocalRef::Adopt(
|
||||
env, env->NewDirectByteBuffer(data, jlong(capacity)));
|
||||
MOZ_CATCH_JNI_EXCEPTION(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* Address()
|
||||
{
|
||||
void* const ret = Env()->GetDirectBufferAddress(Instance());
|
||||
MOZ_CATCH_JNI_EXCEPTION(Env());
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t Capacity()
|
||||
{
|
||||
const size_t ret = size_t(Env()->GetDirectBufferCapacity(Instance()));
|
||||
MOZ_CATCH_JNI_EXCEPTION(Env());
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
class TypedObject<jobjectArray>
|
||||
: public ObjectBase<TypedObject<jobjectArray>, jobjectArray>
|
||||
{
|
||||
using Base = ObjectBase<TypedObject<jobjectArray>, jobjectArray>;
|
||||
|
||||
public:
|
||||
explicit TypedObject(const Context& ctx) : Base(ctx) {}
|
||||
|
||||
size_t Length() const
|
||||
{
|
||||
const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
|
||||
MOZ_CATCH_JNI_EXCEPTION(Base::Env());
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object::LocalRef GetElement(size_t index) const
|
||||
{
|
||||
auto ret = Object::LocalRef::Adopt(
|
||||
Base::Env(), Base::Env()->GetObjectArrayElement(
|
||||
Base::Instance(), jsize(index)));
|
||||
MOZ_CATCH_JNI_EXCEPTION(Base::Env());
|
||||
return ret;
|
||||
}
|
||||
|
||||
nsTArray<Object::LocalRef> GetElements() const
|
||||
{
|
||||
const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
|
||||
|
||||
nsTArray<Object::LocalRef> array((size_t(len)));
|
||||
for (jsize i = 0; i < len; i++) {
|
||||
array.AppendElement(Object::LocalRef::Adopt(
|
||||
Base::Env(), Base::Env()->GetObjectArrayElement(
|
||||
Base::Instance(), i)));
|
||||
MOZ_CATCH_JNI_EXCEPTION(Base::Env());
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
Object::LocalRef operator[](size_t index) const
|
||||
{
|
||||
return GetElement(index);
|
||||
}
|
||||
|
||||
operator nsTArray<Object::LocalRef>() const
|
||||
{
|
||||
return GetElements();
|
||||
}
|
||||
|
||||
void SetElement(size_t index, Object::Param element) const
|
||||
{
|
||||
Base::Env()->SetObjectArrayElement(
|
||||
Base::Instance(), jsize(index), element.Get());
|
||||
MOZ_CATCH_JNI_EXCEPTION(Base::Env());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 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__
|
||||
140
widget/android/jni/Types.h
Normal file
140
widget/android/jni/Types.h
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
#ifndef mozilla_jni_Types_h__
|
||||
#define mozilla_jni_Types_h__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "mozilla/jni/Refs.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace jni {
|
||||
namespace detail {
|
||||
|
||||
// TypeAdapter specializations are the interfaces between native/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>> {
|
||||
using JNIType = typename Cls::Ref::JNIType;
|
||||
|
||||
static constexpr auto Call = &JNIEnv::CallObjectMethodA;
|
||||
static constexpr auto StaticCall = &JNIEnv::CallStaticObjectMethodA;
|
||||
static constexpr auto Get = &JNIEnv::GetObjectField;
|
||||
static constexpr auto StaticGet = &JNIEnv::GetStaticObjectField;
|
||||
|
||||
// Declare instance as jobject because JNI methods return
|
||||
// jobject even if the return value is really jstring, etc.
|
||||
static LocalRef<Cls> ToNative(JNIEnv* env, jobject instance) {
|
||||
return LocalRef<Cls>::Adopt(env, JNIType(instance));
|
||||
}
|
||||
|
||||
static JNIType FromNative(JNIEnv*, LocalRef<Cls>&& instance) {
|
||||
return instance.Forget();
|
||||
}
|
||||
};
|
||||
|
||||
// clang is picky about function types, including attributes that modify the calling
|
||||
// convention, lining up. GCC appears to be somewhat less so.
|
||||
#ifdef __clang__
|
||||
#define MOZ_JNICALL_ABI JNICALL
|
||||
#else
|
||||
#define MOZ_JNICALL_ABI
|
||||
#endif
|
||||
|
||||
template<class Cls> constexpr jobject
|
||||
(JNIEnv::*TypeAdapter<LocalRef<Cls>>::Call)(jobject, jmethodID, jvalue*) MOZ_JNICALL_ABI;
|
||||
template<class Cls> constexpr jobject
|
||||
(JNIEnv::*TypeAdapter<LocalRef<Cls>>::StaticCall)(jclass, jmethodID, jvalue*) MOZ_JNICALL_ABI;
|
||||
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, typename T> struct TypeAdapter<Ref<Cls, T>> {
|
||||
using JNIType = typename Ref<Cls, T>::JNIType;
|
||||
|
||||
static constexpr auto Set = &JNIEnv::SetObjectField;
|
||||
static constexpr auto StaticSet = &JNIEnv::SetStaticObjectField;
|
||||
|
||||
static DependentRef<Cls> ToNative(JNIEnv* env, JNIType instance) {
|
||||
return DependentRef<Cls>(instance);
|
||||
}
|
||||
|
||||
static JNIType FromNative(JNIEnv*, const Ref<Cls, T>& instance) {
|
||||
return instance.Get();
|
||||
}
|
||||
};
|
||||
|
||||
template<class Cls, typename T> constexpr void
|
||||
(JNIEnv::*TypeAdapter<Ref<Cls, T>>::Set)(jobject, jfieldID, jobject);
|
||||
template<class Cls, typename T> constexpr void
|
||||
(JNIEnv::*TypeAdapter<Ref<Cls, T>>::StaticSet)(jclass, jfieldID, jobject);
|
||||
|
||||
|
||||
// jstring has its own Param type.
|
||||
template<> struct TypeAdapter<StringParam>
|
||||
: public TypeAdapter<String::Ref>
|
||||
{};
|
||||
|
||||
template<class Cls> struct TypeAdapter<const Cls&>
|
||||
: public TypeAdapter<Cls>
|
||||
{};
|
||||
|
||||
|
||||
#define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName) \
|
||||
\
|
||||
template<> struct TypeAdapter<NativeType> { \
|
||||
using JNI##Type = JNIType; \
|
||||
\
|
||||
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 constexpr auto GetArray = &JNIEnv::Get ## JNIName ## ArrayRegion; \
|
||||
static constexpr auto SetArray = &JNIEnv::Set ## JNIName ## ArrayRegion; \
|
||||
static constexpr auto NewArray = &JNIEnv::New ## JNIName ## Array; \
|
||||
\
|
||||
static JNIType FromNative(JNIEnv*, NativeType val) { \
|
||||
return static_cast<JNIType>(val); \
|
||||
} \
|
||||
static NativeType ToNative(JNIEnv*, JNIType val) { \
|
||||
return static_cast<NativeType>(val); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
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 detail
|
||||
|
||||
using namespace detail;
|
||||
|
||||
} // namespace jni
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_jni_Types_h__
|
||||
301
widget/android/jni/Utils.cpp
Normal file
301
widget/android/jni/Utils.cpp
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
#include "Utils.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
#include "GeneratedJNIWrappers.h"
|
||||
#include "nsAppShell.h"
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
#include "nsExceptionHandler.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName, ABIName) \
|
||||
\
|
||||
constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Call) \
|
||||
(jobject, jmethodID, jvalue*) MOZ_JNICALL_ABI; \
|
||||
constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticCall) \
|
||||
(jclass, jmethodID, jvalue*) MOZ_JNICALL_ABI; \
|
||||
constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Get) \
|
||||
(jobject, jfieldID) ABIName; \
|
||||
constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticGet) \
|
||||
(jclass, jfieldID) ABIName; \
|
||||
constexpr void (JNIEnv::*TypeAdapter<NativeType>::Set) \
|
||||
(jobject, jfieldID, JNIType) ABIName; \
|
||||
constexpr void (JNIEnv::*TypeAdapter<NativeType>::StaticSet) \
|
||||
(jclass, jfieldID, JNIType) ABIName; \
|
||||
constexpr void (JNIEnv::*TypeAdapter<NativeType>::GetArray) \
|
||||
(JNIType ## Array, jsize, jsize, JNIType*)
|
||||
|
||||
DEFINE_PRIMITIVE_TYPE_ADAPTER(bool, jboolean, Boolean, /*nothing*/);
|
||||
DEFINE_PRIMITIVE_TYPE_ADAPTER(int8_t, jbyte, Byte, /*nothing*/);
|
||||
DEFINE_PRIMITIVE_TYPE_ADAPTER(char16_t, jchar, Char, /*nothing*/);
|
||||
DEFINE_PRIMITIVE_TYPE_ADAPTER(int16_t, jshort, Short, /*nothing*/);
|
||||
DEFINE_PRIMITIVE_TYPE_ADAPTER(int32_t, jint, Int, /*nothing*/);
|
||||
DEFINE_PRIMITIVE_TYPE_ADAPTER(int64_t, jlong, Long, /*nothing*/);
|
||||
DEFINE_PRIMITIVE_TYPE_ADAPTER(float, jfloat, Float, MOZ_JNICALL_ABI);
|
||||
DEFINE_PRIMITIVE_TYPE_ADAPTER(double, jdouble, Double, MOZ_JNICALL_ABI);
|
||||
|
||||
#undef DEFINE_PRIMITIVE_TYPE_ADAPTER
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<> const char ObjectBase<Object, jobject>::name[] = "java/lang/Object";
|
||||
template<> const char ObjectBase<TypedObject<jstring>, jstring>::name[] = "java/lang/String";
|
||||
template<> const char ObjectBase<TypedObject<jclass>, jclass>::name[] = "java/lang/Class";
|
||||
template<> const char ObjectBase<TypedObject<jthrowable>, jthrowable>::name[] = "java/lang/Throwable";
|
||||
template<> const char ObjectBase<TypedObject<jbooleanArray>, jbooleanArray>::name[] = "[Z";
|
||||
template<> const char ObjectBase<TypedObject<jbyteArray>, jbyteArray>::name[] = "[B";
|
||||
template<> const char ObjectBase<TypedObject<jcharArray>, jcharArray>::name[] = "[C";
|
||||
template<> const char ObjectBase<TypedObject<jshortArray>, jshortArray>::name[] = "[S";
|
||||
template<> const char ObjectBase<TypedObject<jintArray>, jintArray>::name[] = "[I";
|
||||
template<> const char ObjectBase<TypedObject<jlongArray>, jlongArray>::name[] = "[J";
|
||||
template<> const char ObjectBase<TypedObject<jfloatArray>, jfloatArray>::name[] = "[F";
|
||||
template<> const char ObjectBase<TypedObject<jdoubleArray>, jdoubleArray>::name[] = "[D";
|
||||
template<> const char ObjectBase<TypedObject<jobjectArray>, jobjectArray>::name[] = "[Ljava/lang/Object;";
|
||||
template<> const char ObjectBase<ByteBuffer, jobject>::name[] = "java/nio/ByteBuffer";
|
||||
|
||||
|
||||
JNIEnv* sGeckoThreadEnv;
|
||||
|
||||
namespace {
|
||||
|
||||
JavaVM* sJavaVM;
|
||||
pthread_key_t sThreadEnvKey;
|
||||
jclass sOOMErrorClass;
|
||||
jobject sClassLoader;
|
||||
jmethodID sClassLoaderLoadClass;
|
||||
bool sIsFennec;
|
||||
|
||||
void UnregisterThreadEnv(void* env)
|
||||
{
|
||||
if (!env) {
|
||||
// We were never attached.
|
||||
return;
|
||||
}
|
||||
// The thread may have already been detached. In that case, it's still
|
||||
// okay to call DetachCurrentThread(); it'll simply return an error.
|
||||
// However, we must not access | env | because it may be invalid.
|
||||
MOZ_ASSERT(sJavaVM);
|
||||
sJavaVM->DetachCurrentThread();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SetGeckoThreadEnv(JNIEnv* aEnv)
|
||||
{
|
||||
MOZ_ASSERT(aEnv);
|
||||
MOZ_ASSERT(!sGeckoThreadEnv || sGeckoThreadEnv == aEnv);
|
||||
|
||||
if (!sGeckoThreadEnv
|
||||
&& pthread_key_create(&sThreadEnvKey, UnregisterThreadEnv)) {
|
||||
MOZ_CRASH("Failed to initialize required TLS");
|
||||
}
|
||||
|
||||
sGeckoThreadEnv = aEnv;
|
||||
MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, aEnv));
|
||||
|
||||
MOZ_ALWAYS_TRUE(!aEnv->GetJavaVM(&sJavaVM));
|
||||
MOZ_ASSERT(sJavaVM);
|
||||
|
||||
sOOMErrorClass = Class::GlobalRef(Class::LocalRef::Adopt(
|
||||
aEnv->FindClass("java/lang/OutOfMemoryError"))).Forget();
|
||||
aEnv->ExceptionClear();
|
||||
|
||||
sClassLoader = Object::GlobalRef(java::GeckoThread::ClsLoader()).Forget();
|
||||
sClassLoaderLoadClass = aEnv->GetMethodID(
|
||||
Class::LocalRef::Adopt(aEnv->GetObjectClass(sClassLoader)).Get(),
|
||||
"loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
MOZ_ASSERT(sClassLoader && sClassLoaderLoadClass);
|
||||
|
||||
auto geckoAppClass = Class::LocalRef::Adopt(
|
||||
aEnv->FindClass("org/mozilla/gecko/GeckoApp"));
|
||||
aEnv->ExceptionClear();
|
||||
sIsFennec = !!geckoAppClass;
|
||||
}
|
||||
|
||||
JNIEnv* GetEnvForThread()
|
||||
{
|
||||
MOZ_ASSERT(sGeckoThreadEnv);
|
||||
|
||||
JNIEnv* env = static_cast<JNIEnv*>(pthread_getspecific(sThreadEnvKey));
|
||||
if (env) {
|
||||
return env;
|
||||
}
|
||||
|
||||
// We don't have a saved JNIEnv, so try to get one.
|
||||
// AttachCurrentThread() does the same thing as GetEnv() when a thread is
|
||||
// already attached, so we don't have to call GetEnv() at all.
|
||||
if (!sJavaVM->AttachCurrentThread(&env, nullptr)) {
|
||||
MOZ_ASSERT(env);
|
||||
MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, env));
|
||||
return env;
|
||||
}
|
||||
|
||||
MOZ_CRASH("Failed to get JNIEnv for thread");
|
||||
return nullptr; // unreachable
|
||||
}
|
||||
|
||||
bool ThrowException(JNIEnv *aEnv, const char *aClass,
|
||||
const char *aMessage)
|
||||
{
|
||||
MOZ_ASSERT(aEnv, "Invalid thread JNI env");
|
||||
|
||||
Class::LocalRef cls = Class::LocalRef::Adopt(aEnv->FindClass(aClass));
|
||||
MOZ_ASSERT(cls, "Cannot find exception class");
|
||||
|
||||
return !aEnv->ThrowNew(cls.Get(), aMessage);
|
||||
}
|
||||
|
||||
bool HandleUncaughtException(JNIEnv* aEnv)
|
||||
{
|
||||
MOZ_ASSERT(aEnv, "Invalid thread JNI env");
|
||||
|
||||
if (!aEnv->ExceptionCheck()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef MOZ_CHECK_JNI
|
||||
aEnv->ExceptionDescribe();
|
||||
#endif
|
||||
|
||||
Throwable::LocalRef e =
|
||||
Throwable::LocalRef::Adopt(aEnv, aEnv->ExceptionOccurred());
|
||||
MOZ_ASSERT(e);
|
||||
aEnv->ExceptionClear();
|
||||
|
||||
String::LocalRef stack = java::GeckoAppShell::GetExceptionStackTrace(e);
|
||||
if (stack && ReportException(aEnv, e.Get(), stack.Get())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
aEnv->ExceptionClear();
|
||||
java::GeckoAppShell::HandleUncaughtException(e);
|
||||
|
||||
if (NS_WARN_IF(aEnv->ExceptionCheck())) {
|
||||
aEnv->ExceptionDescribe();
|
||||
aEnv->ExceptionClear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReportException(JNIEnv* aEnv, jthrowable aExc, jstring aStack)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
result &= NS_SUCCEEDED(CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("JavaStackTrace"),
|
||||
String::Ref::From(aStack)->ToCString()));
|
||||
#endif // MOZ_CRASHREPORTER
|
||||
|
||||
if (sOOMErrorClass && aEnv->IsInstanceOf(aExc, sOOMErrorClass)) {
|
||||
NS_ABORT_OOM(0); // Unknown OOM size
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
jclass sJNIObjectClass;
|
||||
jfieldID sJNIObjectHandleField;
|
||||
|
||||
bool EnsureJNIObject(JNIEnv* env, jobject instance) {
|
||||
if (!sJNIObjectClass) {
|
||||
sJNIObjectClass = Class::GlobalRef(Class::LocalRef::Adopt(GetClassRef(
|
||||
env, "org/mozilla/gecko/mozglue/JNIObject"))).Forget();
|
||||
|
||||
sJNIObjectHandleField = env->GetFieldID(
|
||||
sJNIObjectClass, "mHandle", "J");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(env->IsInstanceOf(instance, sJNIObjectClass));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
uintptr_t GetNativeHandle(JNIEnv* env, jobject instance)
|
||||
{
|
||||
if (!EnsureJNIObject(env, instance)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return static_cast<uintptr_t>(
|
||||
env->GetLongField(instance, sJNIObjectHandleField));
|
||||
}
|
||||
|
||||
void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle)
|
||||
{
|
||||
if (!EnsureJNIObject(env, instance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->SetLongField(instance, sJNIObjectHandleField,
|
||||
static_cast<jlong>(handle));
|
||||
}
|
||||
|
||||
jclass GetClassRef(JNIEnv* aEnv, const char* aClassName)
|
||||
{
|
||||
// First try the default class loader.
|
||||
auto classRef = Class::LocalRef::Adopt(aEnv, aEnv->FindClass(aClassName));
|
||||
|
||||
if (!classRef && sClassLoader) {
|
||||
// If the default class loader failed but we have an app class loader, try that.
|
||||
// Clear the pending exception from failed FindClass call above.
|
||||
aEnv->ExceptionClear();
|
||||
classRef = Class::LocalRef::Adopt(aEnv, jclass(
|
||||
aEnv->CallObjectMethod(sClassLoader, sClassLoaderLoadClass,
|
||||
StringParam(aClassName, aEnv).Get())));
|
||||
}
|
||||
|
||||
if (classRef) {
|
||||
return classRef.Forget();
|
||||
}
|
||||
|
||||
__android_log_print(
|
||||
ANDROID_LOG_ERROR, "Gecko",
|
||||
">>> FATAL JNI ERROR! FindClass(className=\"%s\") failed. "
|
||||
"Did ProGuard optimize away something it shouldn't have?",
|
||||
aClassName);
|
||||
aEnv->ExceptionDescribe();
|
||||
MOZ_CRASH("Cannot find JNI class");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DispatchToGeckoThread(UniquePtr<AbstractCall>&& aCall)
|
||||
{
|
||||
class AbstractCallEvent : public nsAppShell::Event
|
||||
{
|
||||
UniquePtr<AbstractCall> mCall;
|
||||
|
||||
public:
|
||||
AbstractCallEvent(UniquePtr<AbstractCall>&& aCall)
|
||||
: mCall(Move(aCall))
|
||||
{}
|
||||
|
||||
void Run() override
|
||||
{
|
||||
(*mCall)();
|
||||
}
|
||||
};
|
||||
|
||||
nsAppShell::PostEvent(MakeUnique<AbstractCallEvent>(Move(aCall)));
|
||||
}
|
||||
|
||||
bool IsFennec()
|
||||
{
|
||||
return sIsFennec;
|
||||
}
|
||||
|
||||
} // jni
|
||||
} // mozilla
|
||||
147
widget/android/jni/Utils.h
Normal file
147
widget/android/jni/Utils.h
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#ifndef mozilla_jni_Utils_h__
|
||||
#define mozilla_jni_Utils_h__
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
|
||||
#define MOZ_CHECK_JNI
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CHECK_JNI
|
||||
#include <pthread.h>
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "APKOpen.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace jni {
|
||||
|
||||
// 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,
|
||||
};
|
||||
|
||||
// Thread that a particular JNI call is allowed on.
|
||||
enum class CallingThread
|
||||
{
|
||||
// Can be called from any thread (default).
|
||||
ANY,
|
||||
// Can be called from the Gecko thread.
|
||||
GECKO,
|
||||
// Can be called from the Java UI thread.
|
||||
UI,
|
||||
};
|
||||
|
||||
// If and where a JNI call will be dispatched.
|
||||
enum class DispatchTarget
|
||||
{
|
||||
// Call happens synchronously on the calling thread (default).
|
||||
CURRENT,
|
||||
// Call happens synchronously on the calling thread, but the call is
|
||||
// wrapped in a function object and is passed thru UsesNativeCallProxy.
|
||||
// Method must return void.
|
||||
PROXY,
|
||||
// Call is dispatched asynchronously on the Gecko thread. Method must
|
||||
// return void.
|
||||
GECKO,
|
||||
};
|
||||
|
||||
|
||||
extern JNIEnv* sGeckoThreadEnv;
|
||||
|
||||
inline bool IsAvailable()
|
||||
{
|
||||
return !!sGeckoThreadEnv;
|
||||
}
|
||||
|
||||
inline JNIEnv* GetGeckoThreadEnv()
|
||||
{
|
||||
#ifdef MOZ_CHECK_JNI
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Must be on Gecko thread");
|
||||
MOZ_RELEASE_ASSERT(sGeckoThreadEnv, "Must have a JNIEnv");
|
||||
#endif
|
||||
return sGeckoThreadEnv;
|
||||
}
|
||||
|
||||
void SetGeckoThreadEnv(JNIEnv* aEnv);
|
||||
|
||||
JNIEnv* GetEnvForThread();
|
||||
|
||||
#ifdef MOZ_CHECK_JNI
|
||||
#define MOZ_ASSERT_JNI_THREAD(thread) \
|
||||
do { \
|
||||
if ((thread) == mozilla::jni::CallingThread::GECKO) { \
|
||||
MOZ_RELEASE_ASSERT(::NS_IsMainThread()); \
|
||||
} else if ((thread) == mozilla::jni::CallingThread::UI) { \
|
||||
const bool isOnUiThread = ::pthread_equal(::pthread_self(), \
|
||||
::getJavaUiThread()); \
|
||||
MOZ_RELEASE_ASSERT(isOnUiThread); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define MOZ_ASSERT_JNI_THREAD(thread) do {} while (0)
|
||||
#endif
|
||||
|
||||
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(GetEnvForThread(), aClass, aMessage);
|
||||
}
|
||||
|
||||
inline bool ThrowException(const char *aMessage)
|
||||
{
|
||||
return ThrowException(GetEnvForThread(), aMessage);
|
||||
}
|
||||
|
||||
bool HandleUncaughtException(JNIEnv* aEnv);
|
||||
|
||||
bool ReportException(JNIEnv* aEnv, jthrowable aExc, jstring aStack);
|
||||
|
||||
#define MOZ_CATCH_JNI_EXCEPTION(env) \
|
||||
do { \
|
||||
if (mozilla::jni::HandleUncaughtException((env))) { \
|
||||
MOZ_CRASH("JNI exception"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
uintptr_t GetNativeHandle(JNIEnv* env, jobject instance);
|
||||
|
||||
void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle);
|
||||
|
||||
jclass GetClassRef(JNIEnv* aEnv, const char* aClassName);
|
||||
|
||||
struct AbstractCall
|
||||
{
|
||||
virtual ~AbstractCall() {}
|
||||
virtual void operator()() = 0;
|
||||
};
|
||||
|
||||
void DispatchToGeckoThread(UniquePtr<AbstractCall>&& aCall);
|
||||
|
||||
/**
|
||||
* Returns whether Gecko is running in a Fennec environment, as determined by
|
||||
* the presence of the GeckoApp class.
|
||||
*/
|
||||
bool IsFennec();
|
||||
|
||||
} // jni
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_jni_Utils_h__
|
||||
24
widget/android/jni/moz.build
Normal file
24
widget/android/jni/moz.build
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# -*- Mode: python; 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',
|
||||
'Natives.h',
|
||||
'Refs.h',
|
||||
'Types.h',
|
||||
'Utils.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Utils.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/widget',
|
||||
'/widget/android',
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue