/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #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 detail { // Helper class to convert an arbitrary type to a jvalue, e.g. Value(123).val. struct Value { explicit Value(jboolean z) { val.z = z; } explicit Value(jbyte b) { val.b = b; } explicit Value(jchar c) { val.c = c; } explicit Value(jshort s) { val.s = s; } explicit Value(jint i) { val.i = i; } explicit Value(jlong j) { val.j = j; } explicit Value(jfloat f) { val.f = f; } explicit Value(jdouble d) { val.d = d; } explicit 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 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 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(ctx, rv); } public: template static ReturnType Call(const Context& ctx, nsresult* rv, const Args&... args) { JNIEnv* const env = ctx.Env(); BeginAccess(ctx); jvalue jargs[] = {Value(TypeAdapter::FromNative(env, args)).val...}; auto result = TypeAdapter::ToNative( env, Traits::isStatic ? (env->*TypeAdapter::StaticCall)( ctx.ClassRef(), sID, jargs) : (env->*TypeAdapter::Call)( ctx.Get(), sID, jargs)); EndAccess(ctx, 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::Context Context; public: template static void Call(const Context& ctx, nsresult* rv, const Args&... args) { JNIEnv* const env = ctx.Env(); Base::BeginAccess(ctx); jvalue jargs[] = {Value(TypeAdapter::FromNative(env, args)).val...}; if (Traits::isStatic) { env->CallStaticVoidMethodA(ctx.ClassRef(), 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 Constructor : protected Method { typedef typename Traits::Owner::Context Context; typedef typename Traits::ReturnType ReturnType; typedef Method Base; public: template static ReturnType Call(const Context& ctx, nsresult* rv, const Args&... args) { JNIEnv* const env = ctx.Env(); Base::BeginAccess(ctx); jvalue jargs[] = {Value(TypeAdapter::FromNative(env, args)).val...}; auto result = TypeAdapter::ToNative( env, env->NewObjectA(ctx.ClassRef(), Base::sID, jargs)); Base::EndAccess(ctx, 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::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(ctx, rv); } public: static GetterType Get(const Context& ctx, nsresult* rv) { JNIEnv* const env = ctx.Env(); BeginAccess(ctx); auto result = TypeAdapter::ToNative( env, Traits::isStatic ? (env->*TypeAdapter::StaticGet)(ctx.ClassRef(), sID) : (env->*TypeAdapter::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::StaticSet)( ctx.ClassRef(), sID, TypeAdapter::FromNative(env, val)); } else { (env->*TypeAdapter::Set)( ctx.Get(), sID, TypeAdapter::FromNative(env, val)); } EndAccess(ctx, rv); } }; // Define sID member. template jfieldID Field::sID; } // namespace jni } // namespace mozilla #endif // mozilla_jni_Accessors_h__