gecko-dev/widget/android/jni/Accessors.h

252 lines
7.2 KiB
C++

/* -*- 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__