diff options
Diffstat (limited to 'widget/android/jni/Accessors.h')
-rw-r--r-- | widget/android/jni/Accessors.h | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/widget/android/jni/Accessors.h b/widget/android/jni/Accessors.h new file mode 100644 index 0000000000..7496cbcb5a --- /dev/null +++ b/widget/android/jni/Accessors.h @@ -0,0 +1,251 @@ +/* -*- 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 <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 { + 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 <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.ClassRef(), 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.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 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.ClassRef(), 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.ClassRef(), 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.ClassRef(), 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; + +} // namespace jni +} // namespace mozilla + +#endif // mozilla_jni_Accessors_h__ |