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