import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View 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__

View 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
View 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
View 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__

View 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
View 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__

View 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',
]