Bug 1244875 - Refactor JNI templates; r=snorp

Improve the performance of JNI calls by making JNI calls require a
Context object. LocalRef inherits from Context and can make calls
directly. Non-local Ref classes will generate a Context object when
making a call. The patch also makes the template design cleaner in
several cases.
This commit is contained in:
Jim Chen 2016-02-09 17:27:28 -05:00
parent 0b0b6cd0df
commit 46edf980e0
6 changed files with 550 additions and 550 deletions

View File

@ -9,7 +9,7 @@
#include "AndroidBridge.h"
namespace mozilla {
namespace jni{
namespace jni {
namespace {
@ -31,23 +31,16 @@ struct Value
}
// Base class for Method<>, Field<>, and Constructor<>.
class Accessor {
public:
template<class Cls>
static jclass EnsureClassRef(JNIEnv* env)
{
if (!Cls::sClassRef) {
MOZ_ALWAYS_TRUE(Cls::sClassRef =
AndroidBridge::GetClassGlobalRef(env, Cls::name));
}
return Cls::sClassRef;
}
private:
// Base class for Method<>, Field<>, and Constructor<>.
class Accessor
{
static void GetNsresult(JNIEnv* env, nsresult* rv)
{
if (env->ExceptionCheck()) {
#ifdef DEBUG
env->ExceptionDescribe();
#endif
env->ExceptionClear();
*rv = NS_ERROR_FAILURE;
} else {
@ -56,26 +49,16 @@ private:
}
protected:
// Called before making a JNIEnv call.
template<class Traits>
static JNIEnv* BeginAccess()
{
JNIEnv* const env = Traits::isMultithreaded
? GetEnvForThread() : GetGeckoThreadEnv();
EnsureClassRef<typename Traits::Owner>(env);
return env;
}
// Called after making a JNIEnv call.
template<class Traits>
static void EndAccess(JNIEnv* env, nsresult* rv)
static void EndAccess(const typename Traits::Owner::Context& ctx,
nsresult* rv)
{
if (Traits::exceptionMode == ExceptionMode::ABORT) {
MOZ_CATCH_JNI_EXCEPTION(env);
MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
} else if (Traits::exceptionMode == ExceptionMode::NSRESULT) {
GetNsresult(env, rv);
GetNsresult(ctx.Env(), rv);
}
}
};
@ -86,39 +69,37 @@ template<class Traits, typename ReturnType = typename Traits::ReturnType>
class Method : public Accessor
{
typedef Accessor Base;
typedef typename Traits::Owner Owner;
typedef typename Traits::Owner::Context Context;
protected:
static jmethodID sID;
static JNIEnv* BeginAccess()
static void BeginAccess(const Context& ctx)
{
JNIEnv* const env = Base::BeginAccess<Traits>();
if (sID) {
return env;
return;
}
if (Traits::isStatic) {
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticMethodID(
env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
} else {
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetMethodID(
env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
}
return env;
}
static void EndAccess(JNIEnv* env, nsresult* rv)
static void EndAccess(const Context& ctx, nsresult* rv)
{
return Base::EndAccess<Traits>(env, rv);
return Base::EndAccess<Traits>(ctx, rv);
}
public:
template<typename... Args>
static ReturnType Call(const Owner* cls, nsresult* rv, const Args&... args)
static ReturnType Call(const Context& ctx, nsresult* rv, const Args&... args)
{
JNIEnv* const env = BeginAccess();
JNIEnv* const env = ctx.Env();
BeginAccess(ctx);
jvalue jargs[] = {
Value(TypeAdapter<Args>::FromNative(env, args)).val ...
@ -127,11 +108,11 @@ public:
auto result = TypeAdapter<ReturnType>::ToNative(env,
Traits::isStatic ?
(env->*TypeAdapter<ReturnType>::StaticCall)(
Owner::sClassRef, sID, jargs) :
ctx.RawClassRef(), sID, jargs) :
(env->*TypeAdapter<ReturnType>::Call)(
cls->mInstance, sID, jargs));
ctx.Get(), sID, jargs));
EndAccess(env, rv);
EndAccess(ctx, rv);
return result;
}
};
@ -146,26 +127,27 @@ template<class Traits>
class Method<Traits, void> : public Method<Traits, bool>
{
typedef Method<Traits, bool> Base;
typedef typename Traits::Owner Owner;
typedef typename Traits::Owner::Context Context;
public:
template<typename... Args>
static void Call(const Owner* cls, nsresult* rv,
static void Call(const Context& ctx, nsresult* rv,
const Args&... args)
{
JNIEnv* const env = Base::BeginAccess();
JNIEnv* const env = ctx.Env();
Base::BeginAccess(ctx);
jvalue jargs[] = {
Value(TypeAdapter<Args>::FromNative(env, args)).val ...
};
if (Traits::isStatic) {
env->CallStaticVoidMethodA(Owner::sClassRef, Base::sID, jargs);
env->CallStaticVoidMethodA(ctx.RawClassRef(), Base::sID, jargs);
} else {
env->CallVoidMethodA(cls->mInstance, Base::sID, jargs);
env->CallVoidMethodA(ctx.Get(), Base::sID, jargs);
}
Base::EndAccess(env, rv);
Base::EndAccess(ctx, rv);
}
};
@ -173,25 +155,26 @@ public:
// 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 Owner;
typedef typename Traits::Owner::Context Context;
typedef typename Traits::ReturnType ReturnType;
typedef Method<Traits, ReturnType> Base;
public:
template<typename... Args>
static ReturnType Call(const Owner* cls, nsresult* rv,
static ReturnType Call(const Context& ctx, nsresult* rv,
const Args&... args)
{
JNIEnv* const env = Base::BeginAccess();
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(Owner::sClassRef, Base::sID, jargs));
env, env->NewObjectA(ctx.RawClassRef(), Base::sID, jargs));
Base::EndAccess(env, rv);
Base::EndAccess(ctx, rv);
return result;
}
};
@ -202,7 +185,7 @@ template<class Traits>
class Field : public Accessor
{
typedef Accessor Base;
typedef typename Traits::Owner Owner;
typedef typename Traits::Owner::Context Context;
typedef typename Traits::ReturnType GetterType;
typedef typename Traits::SetterType SetterType;
@ -210,62 +193,61 @@ private:
static jfieldID sID;
static JNIEnv* BeginAccess()
static void BeginAccess(const Context& ctx)
{
JNIEnv* const env = Base::BeginAccess<Traits>();
if (sID) {
return env;
return;
}
if (Traits::isStatic) {
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticFieldID(
env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
} else {
MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetFieldID(
env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
}
return env;
}
static void EndAccess(JNIEnv* env, nsresult* rv)
static void EndAccess(const Context& ctx, nsresult* rv)
{
return Base::EndAccess<Traits>(env, rv);
return Base::EndAccess<Traits>(ctx, rv);
}
public:
static GetterType Get(const Owner* cls, nsresult* rv)
static GetterType Get(const Context& ctx, nsresult* rv)
{
JNIEnv* const env = BeginAccess();
JNIEnv* const env = ctx.Env();
BeginAccess(ctx);
auto result = TypeAdapter<GetterType>::ToNative(
env, Traits::isStatic ?
(env->*TypeAdapter<GetterType>::StaticGet)
(Owner::sClassRef, sID) :
(ctx.RawClassRef(), sID) :
(env->*TypeAdapter<GetterType>::Get)
(cls->mInstance, sID));
(ctx.Get(), sID));
EndAccess(env, rv);
EndAccess(ctx, rv);
return result;
}
static void Set(const Owner* cls, nsresult* rv, SetterType val)
static void Set(const Context& ctx, nsresult* rv, SetterType val)
{
JNIEnv* const env = BeginAccess();
JNIEnv* const env = ctx.Env();
BeginAccess(ctx);
if (Traits::isStatic) {
(env->*TypeAdapter<SetterType>::StaticSet)(
Owner::sClassRef, sID,
ctx.RawClassRef(), sID,
TypeAdapter<SetterType>::FromNative(env, val));
} else {
(env->*TypeAdapter<SetterType>::Set)(
cls->mInstance, sID,
ctx.Get(), sID,
TypeAdapter<SetterType>::FromNative(env, val));
}
EndAccess(env, rv);
EndAccess(ctx, rv);
}
};
@ -275,7 +257,7 @@ template<class T> jfieldID Field<T>::sID;
// Define the sClassRef member declared in Refs.h and
// used by Method and Field above.
template<class C> jclass Class<C>::sClassRef;
template<class C, typename T> jclass Context<C, T>::sClassRef;
} // namespace jni
} // namespace mozilla

View File

@ -239,24 +239,24 @@ struct ProxyArg
}
};
template<class T>
struct ProxyArg<Ref<T>>
template<class C, typename T>
struct ProxyArg<Ref<C, T>>
{
// Ref types need to be saved by global ref.
typedef typename T::GlobalRef Type;
typedef typename TypeAdapter<Ref<T>>::JNIType JNIType;
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, T::Ref::From(val));
return Type(env, C::Ref::From(val));
}
};
template<typename T> struct ProxyArg<const T&> : ProxyArg<T> {};
template<> struct ProxyArg<Param<String>> : ProxyArg<Ref<String>> {};
template<class T> struct ProxyArg<LocalRef<T>> : ProxyArg<Ref<T>> {};
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
// UsesNativeCallProxy::OnNativeCall
@ -268,11 +268,11 @@ class ProxyNativeCall
template<class T, class I, class A, bool S, bool V>
friend class NativeStubImpl;
// "this arg" refers to the ClassObject::LocalRef (for static methods) or
// "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,
ClassObject, Owner>::Type ThisArgClass;
Class, Owner>::Type ThisArgClass;
typedef typename mozilla::Conditional<IsStatic,
jclass, jobject>::Type ThisArgJNIType;
@ -280,7 +280,7 @@ class ProxyNativeCall
// Method template parameter in NativeStubImpl::Wrap.
typedef typename mozilla::Conditional<IsStatic,
typename mozilla::Conditional<HasThisArg,
void (*) (const ClassObject::LocalRef&, Args...),
void (*) (const Class::LocalRef&, Args...),
void (*) (Args...)>::Type,
typename mozilla::Conditional<HasThisArg,
void (Impl::*) (const typename Owner::LocalRef&, Args...),
@ -308,7 +308,7 @@ class ProxyNativeCall
template<bool Static, bool ThisArg, size_t... Indices>
typename mozilla::EnableIf<Static && ThisArg, void>::Type
Call(const ClassObject::LocalRef& cls,
Call(const Class::LocalRef& cls,
mozilla::IndexSequence<Indices...>) const
{
(*mNativeCall)(cls, mozilla::Get<Indices>(mArgs)...);
@ -316,7 +316,7 @@ class ProxyNativeCall
template<bool Static, bool ThisArg, size_t... Indices>
typename mozilla::EnableIf<Static && !ThisArg, void>::Type
Call(const ClassObject::LocalRef& cls,
Call(const Class::LocalRef& cls,
mozilla::IndexSequence<Indices...>) const
{
(*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
@ -531,7 +531,7 @@ public:
static MOZ_JNICALL void Wrap(JNIEnv* env, jobject instance)
{
if (mozilla::IsBaseOf<UsesNativeCallProxy, Impl>::value) {
auto cls = ClassObject::LocalRef::Adopt(
auto cls = Class::LocalRef::Adopt(
env, env->GetObjectClass(instance));
Dispatch(ProxyNativeCall<Impl, Owner, /* IsStatic */ true,
/* HasThisArg */ false, const typename Owner::LocalRef&>(
@ -566,14 +566,14 @@ public:
}
// Static method with class reference
template<ReturnType (*Method) (const ClassObject::LocalRef&, Args...)>
template<ReturnType (*Method) (const Class::LocalRef&, Args...)>
static MOZ_JNICALL ReturnJNIType Wrap(JNIEnv* env,
jclass cls, typename TypeAdapter<Args>::JNIType... args)
{
static_assert(!mozilla::IsBaseOf<UsesNativeCallProxy, Impl>::value,
"Native call proxy only supports void return type");
auto clazz = ClassObject::LocalRef::Adopt(env, cls);
auto clazz = Class::LocalRef::Adopt(env, cls);
const auto res = TypeAdapter<ReturnType>::FromNative(env,
(*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...));
clazz.Forget();
@ -604,7 +604,7 @@ public:
}
// Static method with class reference
template<void (*Method) (const ClassObject::LocalRef&, Args...)>
template<void (*Method) (const Class::LocalRef&, Args...)>
static MOZ_JNICALL void Wrap(JNIEnv* env,
jclass cls, typename TypeAdapter<Args>::JNIType... args)
{
@ -614,7 +614,7 @@ public:
Args...>(Method, env, cls, args...));
return;
}
auto clazz = ClassObject::LocalRef::Adopt(env, cls);
auto clazz = Class::LocalRef::Adopt(env, cls);
(*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...);
clazz.Forget();
}
@ -656,11 +656,11 @@ public:
if (sInited) {
return;
}
JNIEnv* const env = GetEnvForThread();
MOZ_ALWAYS_TRUE(!env->RegisterNatives(
Accessor::EnsureClassRef<Cls>(env),
Natives::methods,
sizeof(Natives::methods) / sizeof(Natives::methods[0])));
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@ template<typename T> struct TypeAdapter;
// TypeAdapter<LocalRef<Cls>> applies when jobject is a return value.
template<class Cls> struct TypeAdapter<LocalRef<Cls>> {
typedef decltype(Ref<Cls>(nullptr).Get()) JNIType;
using JNIType = typename Cls::Ref::JNIType;
static constexpr auto Call = &JNIEnv::CallObjectMethodA;
static constexpr auto StaticCall = &JNIEnv::CallStaticObjectMethodA;
@ -37,7 +37,7 @@ template<class Cls> struct TypeAdapter<LocalRef<Cls>> {
// 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, instance);
return LocalRef<Cls>::Adopt(env, JNIType(instance));
}
static JNIType FromNative(JNIEnv*, LocalRef<Cls>&& instance) {
@ -64,29 +64,29 @@ template<class Cls> constexpr jobject
// TypeAdapter<Ref<Cls>> applies when jobject is a parameter value.
template<class Cls> struct TypeAdapter<Ref<Cls>> {
typedef decltype(Ref<Cls>(nullptr).Get()) JNIType;
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 Ref<Cls> ToNative(JNIEnv* env, JNIType instance) {
return Ref<Cls>::From(instance);
static DependentRef<Cls> ToNative(JNIEnv* env, JNIType instance) {
return DependentRef<Cls>(instance);
}
static JNIType FromNative(JNIEnv*, const Ref<Cls>& instance) {
static JNIType FromNative(JNIEnv*, const Ref<Cls, T>& instance) {
return instance.Get();
}
};
template<class Cls> constexpr void
(JNIEnv::*TypeAdapter<Ref<Cls>>::Set)(jobject, jfieldID, jobject);
template<class Cls> constexpr void
(JNIEnv::*TypeAdapter<Ref<Cls>>::StaticSet)(jclass, jfieldID, jobject);
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<Param<String>>
template<> struct TypeAdapter<StringParam>
: public TypeAdapter<String::Ref>
{};
@ -98,7 +98,7 @@ template<class Cls> struct TypeAdapter<const Cls&>
#define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName) \
\
template<> struct TypeAdapter<NativeType> { \
typedef JNIType JNI##Type; \
using JNI##Type = JNIType; \
\
static constexpr auto Call = &JNIEnv::Call ## JNIName ## MethodA; \
static constexpr auto StaticCall = &JNIEnv::CallStatic ## JNIName ## MethodA; \

View File

@ -47,19 +47,19 @@ DEFINE_PRIMITIVE_TYPE_ADAPTER(double, jdouble, Double, MOZ_JNICALL_ABI);
} // namespace detail
constexpr char Object::name[];
template<> const char TypedObject<jstring>::name[] = "java/lang/String";
template<> const char TypedObject<jclass>::name[] = "java/lang/Class";
template<> const char TypedObject<jthrowable>::name[] = "java/lang/Throwable";
template<> const char TypedObject<jbooleanArray>::name[] = "[Z";
template<> const char TypedObject<jbyteArray>::name[] = "[B";
template<> const char TypedObject<jcharArray>::name[] = "[C";
template<> const char TypedObject<jshortArray>::name[] = "[S";
template<> const char TypedObject<jintArray>::name[] = "[I";
template<> const char TypedObject<jlongArray>::name[] = "[J";
template<> const char TypedObject<jfloatArray>::name[] = "[F";
template<> const char TypedObject<jdoubleArray>::name[] = "[D";
template<> const char TypedObject<jobjectArray>::name[] = "[Ljava/lang/Object;";
template<> const char Context<Object, jobject>::name[] = "java/lang/Object";
template<> const char Context<TypedObject<jstring>, jstring>::name[] = "java/lang/String";
template<> const char Context<TypedObject<jclass>, jclass>::name[] = "java/lang/Class";
template<> const char Context<TypedObject<jthrowable>, jthrowable>::name[] = "java/lang/Throwable";
template<> const char Context<TypedObject<jbooleanArray>, jbooleanArray>::name[] = "[Z";
template<> const char Context<TypedObject<jbyteArray>, jbyteArray>::name[] = "[B";
template<> const char Context<TypedObject<jcharArray>, jcharArray>::name[] = "[C";
template<> const char Context<TypedObject<jshortArray>, jshortArray>::name[] = "[S";
template<> const char Context<TypedObject<jintArray>, jintArray>::name[] = "[I";
template<> const char Context<TypedObject<jlongArray>, jlongArray>::name[] = "[J";
template<> const char Context<TypedObject<jfloatArray>, jfloatArray>::name[] = "[F";
template<> const char Context<TypedObject<jdoubleArray>, jdoubleArray>::name[] = "[D";
template<> const char Context<TypedObject<jobjectArray>, jobjectArray>::name[] = "[Ljava/lang/Object;";
JNIEnv* sGeckoThreadEnv;
@ -128,8 +128,7 @@ bool ThrowException(JNIEnv *aEnv, const char *aClass,
{
MOZ_ASSERT(aEnv, "Invalid thread JNI env");
ClassObject::LocalRef cls =
ClassObject::LocalRef::Adopt(aEnv->FindClass(aClass));
Class::LocalRef cls = Class::LocalRef::Adopt(aEnv->FindClass(aClass));
MOZ_ASSERT(cls, "Cannot find exception class");
return !aEnv->ThrowNew(cls.Get(), aMessage);
@ -158,7 +157,7 @@ bool HandleUncaughtException(JNIEnv* aEnv)
if (stack) {
// GeckoAppShell wants us to annotate and trigger the crash reporter.
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("AuxiliaryJavaStack"), nsCString(stack));
NS_LITERAL_CSTRING("AuxiliaryJavaStack"), stack->ToCString());
}
#endif // MOZ_CRASHREPORTER
@ -205,5 +204,10 @@ void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle)
static_cast<jlong>(handle));
}
jclass GetClassGlobalRef(JNIEnv* aEnv, const char* aClassName)
{
return AndroidBridge::GetClassGlobalRef(aEnv, aClassName);
}
} // jni
} // mozilla

View File

@ -66,6 +66,8 @@ uintptr_t GetNativeHandle(JNIEnv* env, jobject instance);
void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle);
jclass GetClassGlobalRef(JNIEnv* aEnv, const char* aClassName);
} // jni
} // mozilla