gecko-dev/widget/android/jni/Natives.h
2020-03-28 13:57:19 +00:00

814 lines
28 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_Natives_h__
#define mozilla_jni_Natives_h__
#include <jni.h>
#include <type_traits>
#include <utility>
#include "mozilla/RefPtr.h"
#include "mozilla/Tuple.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/jni/Accessors.h"
#include "mozilla/jni/Refs.h"
#include "mozilla/jni/Types.h"
#include "mozilla/jni/Utils.h"
#include "nsThreadUtils.h"
struct NativeException {
const char* str;
};
template <class T>
static NativeException NullHandle() {
return {__func__};
}
template <class T>
static NativeException NullWeakPtr() {
return {__func__};
}
namespace mozilla {
namespace jni {
/**
* C++ classes implementing instance (non-static) native methods can choose
* from one of two ownership models, when associating a C++ object with a Java
* instance.
*
* * If the C++ class inherits from mozilla::SupportsWeakPtr, weak pointers
* will be used. The Java instance will store and own the pointer to a
* WeakPtr object. The C++ class itself is otherwise not owned or directly
* referenced. Note that mozilla::SupportsWeakPtr only supports being used on
* a single thread. To attach a Java instance to a C++ instance, pass in a
* mozilla::SupportsWeakPtr pointer to the C++ class (i.e. MyClass*).
*
* class MyClass : public SupportsWeakPtr<MyClass>
* , public MyJavaClass::Natives<MyClass>
* {
* // ...
*
* public:
* MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MyClass)
* using MyJavaClass::Natives<MyClass>::DisposeNative;
*
* void AttachTo(const MyJavaClass::LocalRef& instance)
* {
* MyJavaClass::Natives<MyClass>::AttachNative(
* instance, static_cast<SupportsWeakPtr<MyClass>*>(this));
*
* // "instance" does NOT own "this", so the C++ object
* // lifetime is separate from the Java object lifetime.
* }
* };
*
* * If the C++ class contains public members AddRef() and Release(), the Java
* instance will store and own the pointer to a RefPtr object, which holds a
* strong reference on the C++ instance. Normal ref-counting considerations
* apply in this case; for example, disposing may cause the C++ instance to
* be deleted and the destructor to be run on the current thread, which may
* not be desirable. To attach a Java instance to a C++ instance, pass in a
* pointer to the C++ class (i.e. MyClass*).
*
* class MyClass : public RefCounted<MyClass>
* , public MyJavaClass::Natives<MyClass>
* {
* // ...
*
* public:
* using MyJavaClass::Natives<MyClass>::DisposeNative;
*
* void AttachTo(const MyJavaClass::LocalRef& instance)
* {
* MyJavaClass::Natives<MyClass>::AttachNative(instance, this);
*
* // "instance" owns "this" through the RefPtr, so the C++ object
* // may be destroyed as soon as instance.disposeNative() is called.
* }
* };
*
* * In other cases, the Java instance will store and own a pointer to the C++
* object itself. This pointer must not be stored or deleted elsewhere. To
* attach a Java instance to a C++ instance, pass in a reference to a
* UniquePtr of the C++ class (i.e. UniquePtr<MyClass>).
*
* class MyClass : public MyJavaClass::Natives<MyClass>
* {
* // ...
*
* public:
* using MyJavaClass::Natives<MyClass>::DisposeNative;
*
* static void AttachTo(const MyJavaClass::LocalRef& instance)
* {
* MyJavaClass::Natives<MyClass>::AttachNative(
* instance, mozilla::MakeUnique<MyClass>());
*
* // "instance" owns the newly created C++ object, so the C++
* // object is destroyed as soon as instance.disposeNative() is
* // called.
* }
* };
*/
namespace detail {
enum NativePtrType { OWNING, WEAK, REFPTR };
template <class Impl>
class NativePtrPicker {
template <class I>
static std::enable_if_t<std::is_base_of<SupportsWeakPtr<I>, I>::value,
char (&)[NativePtrType::WEAK]>
Test(char);
template <class I, typename = decltype(&I::AddRef, &I::Release)>
static char (&Test(int))[NativePtrType::REFPTR];
template <class>
static char (&Test(...))[NativePtrType::OWNING];
public:
static const int value = sizeof(Test<Impl>('\0')) / sizeof(char);
};
template <class Impl>
inline uintptr_t CheckNativeHandle(JNIEnv* env, uintptr_t handle) {
if (!handle) {
if (!env->ExceptionCheck()) {
ThrowException(env, "java/lang/NullPointerException",
NullHandle<Impl>().str);
}
return 0;
}
return handle;
}
template <class Impl, int Type = NativePtrPicker<Impl>::value>
struct NativePtr;
template <class Impl>
struct NativePtr<Impl, /* Type = */ NativePtrType::OWNING> {
static Impl* Get(JNIEnv* env, jobject instance) {
return reinterpret_cast<Impl*>(
CheckNativeHandle<Impl>(env, GetNativeHandle(env, instance)));
}
template <class LocalRef>
static Impl* Get(const LocalRef& instance) {
return Get(instance.Env(), instance.Get());
}
template <class LocalRef>
static void Set(const LocalRef& instance, UniquePtr<Impl>&& ptr) {
Clear(instance);
SetNativeHandle(instance.Env(), instance.Get(),
reinterpret_cast<uintptr_t>(ptr.release()));
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
}
template <class LocalRef>
static void Clear(const LocalRef& instance) {
UniquePtr<Impl> ptr(reinterpret_cast<Impl*>(
GetNativeHandle(instance.Env(), instance.Get())));
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
if (ptr) {
SetNativeHandle(instance.Env(), instance.Get(), 0);
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
}
}
};
template <class Impl>
struct NativePtr<Impl, /* Type = */ NativePtrType::WEAK> {
static Impl* Get(JNIEnv* env, jobject instance) {
const auto ptr = reinterpret_cast<WeakPtr<Impl>*>(
CheckNativeHandle<Impl>(env, GetNativeHandle(env, instance)));
if (!ptr) {
return nullptr;
}
Impl* const impl = *ptr;
if (!impl) {
ThrowException(env, "java/lang/NullPointerException",
NullWeakPtr<Impl>().str);
}
return impl;
}
template <class LocalRef>
static Impl* Get(const LocalRef& instance) {
return Get(instance.Env(), instance.Get());
}
template <class LocalRef>
static void Set(const LocalRef& instance, Impl* ptr) {
// Create the new handle first before clearing any old handle, so the
// new handle is guaranteed to have different value than any old handle.
const uintptr_t handle =
reinterpret_cast<uintptr_t>(new WeakPtr<Impl>(ptr));
Clear(instance);
SetNativeHandle(instance.Env(), instance.Get(), handle);
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
}
template <class LocalRef>
static void Clear(const LocalRef& instance) {
const auto ptr = reinterpret_cast<WeakPtr<Impl>*>(
GetNativeHandle(instance.Env(), instance.Get()));
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
if (ptr) {
SetNativeHandle(instance.Env(), instance.Get(), 0);
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
delete ptr;
}
}
};
template <class Impl>
struct NativePtr<Impl, /* Type = */ NativePtrType::REFPTR> {
static Impl* Get(JNIEnv* env, jobject instance) {
const auto ptr = reinterpret_cast<RefPtr<Impl>*>(
CheckNativeHandle<Impl>(env, GetNativeHandle(env, instance)));
if (!ptr) {
return nullptr;
}
MOZ_ASSERT(*ptr);
return *ptr;
}
template <class LocalRef>
static Impl* Get(const LocalRef& instance) {
return Get(instance.Env(), instance.Get());
}
template <class LocalRef>
static void Set(const LocalRef& instance, Impl* ptr) {
// Create the new handle first before clearing any old handle, so the
// new handle is guaranteed to have different value than any old handle.
const uintptr_t handle = reinterpret_cast<uintptr_t>(new RefPtr<Impl>(ptr));
Clear(instance);
SetNativeHandle(instance.Env(), instance.Get(), handle);
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
}
template <class LocalRef>
static void Clear(const LocalRef& instance) {
const auto ptr = reinterpret_cast<RefPtr<Impl>*>(
GetNativeHandle(instance.Env(), instance.Get()));
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
if (ptr) {
SetNativeHandle(instance.Env(), instance.Get(), 0);
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
delete ptr;
}
}
};
} // namespace detail
using namespace detail;
/**
* For JNI native methods that are dispatched to a proxy, i.e. using
* @WrapForJNI(dispatchTo = "proxy"), the implementing C++ class must provide a
* OnNativeCall member. Subsequently, every native call is automatically
* wrapped in a functor object, and the object is passed to OnNativeCall. The
* OnNativeCall implementation can choose to invoke the call, save it, dispatch
* it to a different thread, etc. Each copy of functor may only be invoked
* once.
*
* class MyClass : public MyJavaClass::Natives<MyClass>
* {
* // ...
*
* template<class Functor>
* class ProxyRunnable final : public Runnable
* {
* Functor mCall;
* public:
* ProxyRunnable(Functor&& call) : mCall(std::move(call)) {}
* virtual void run() override { mCall(); }
* };
*
* public:
* template<class Functor>
* static void OnNativeCall(Functor&& call)
* {
* RunOnAnotherThread(new ProxyRunnable(std::move(call)));
* }
* };
*/
namespace detail {
// ProxyArg is used to handle JNI ref arguments for proxies. Because a proxied
// call may happen outside of the original JNI native call, we must save all
// JNI ref arguments as global refs to avoid the arguments going out of scope.
template <typename T>
struct ProxyArg {
static_assert(mozilla::IsPod<T>::value, "T must be primitive type");
// Primitive types can be saved by value.
typedef T Type;
typedef typename TypeAdapter<T>::JNIType JNIType;
static void Clear(JNIEnv* env, Type&) {}
static Type From(JNIEnv* env, JNIType val) {
return TypeAdapter<T>::ToNative(env, val);
}
};
template <class C, typename T>
struct ProxyArg<Ref<C, T>> {
// Ref types need to be saved by global ref.
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, C::Ref::From(val));
}
};
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 OnNativeCall
template <class Impl, class Owner, bool IsStatic,
bool HasThisArg /* has instance/class local ref in the call */,
typename... Args>
class ProxyNativeCall {
// "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.
using ThisArgClass = std::conditional_t<IsStatic, Class, Owner>;
using ThisArgJNIType = std::conditional_t<IsStatic, jclass, jobject>;
// Type signature of the destination C++ function, which matches the
// Method template parameter in NativeStubImpl::Wrap.
using NativeCallType = std::conditional_t<
IsStatic,
std::conditional_t<HasThisArg, void (*)(const Class::LocalRef&, Args...),
void (*)(Args...)>,
std::conditional_t<
HasThisArg, void (Impl::*)(const typename Owner::LocalRef&, Args...),
void (Impl::*)(Args...)>>;
// Destination C++ function.
NativeCallType mNativeCall;
// Saved this arg.
typename ThisArgClass::GlobalRef mThisArg;
// Saved arguments.
mozilla::Tuple<typename ProxyArg<Args>::Type...> mArgs;
// We cannot use IsStatic and HasThisArg directly (without going through
// extra hoops) because GCC complains about invalid overloads, so we use
// another pair of template parameters, Static and ThisArg.
template <bool Static, bool ThisArg, size_t... Indices>
std::enable_if_t<Static && ThisArg, void> Call(
const Class::LocalRef& cls, std::index_sequence<Indices...>) const {
(*mNativeCall)(cls, mozilla::Get<Indices>(mArgs)...);
}
template <bool Static, bool ThisArg, size_t... Indices>
std::enable_if_t<Static && !ThisArg, void> Call(
const Class::LocalRef& cls, std::index_sequence<Indices...>) const {
(*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
}
template <bool Static, bool ThisArg, size_t... Indices>
std::enable_if_t<!Static && ThisArg, void> Call(
const typename Owner::LocalRef& inst,
std::index_sequence<Indices...>) const {
Impl* const impl = NativePtr<Impl>::Get(inst);
MOZ_CATCH_JNI_EXCEPTION(inst.Env());
(impl->*mNativeCall)(inst, mozilla::Get<Indices>(mArgs)...);
}
template <bool Static, bool ThisArg, size_t... Indices>
std::enable_if_t<!Static && !ThisArg, void> Call(
const typename Owner::LocalRef& inst,
std::index_sequence<Indices...>) const {
Impl* const impl = NativePtr<Impl>::Get(inst);
MOZ_CATCH_JNI_EXCEPTION(inst.Env());
(impl->*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
}
template <size_t... Indices>
void Clear(JNIEnv* env, std::index_sequence<Indices...>) {
int dummy[] = {(ProxyArg<Args>::Clear(env, Get<Indices>(mArgs)), 0)...};
mozilla::Unused << dummy;
}
static Impl* GetNativeObject(Class::Param thisArg) { return nullptr; }
static Impl* GetNativeObject(typename Owner::Param thisArg) {
return NativePtr<Impl>::Get(GetEnvForThread(), thisArg.Get());
}
public:
// The class that implements the call target.
typedef Impl TargetClass;
typedef typename ThisArgClass::Param ThisArgType;
static const bool isStatic = IsStatic;
ProxyNativeCall(ThisArgJNIType thisArg, NativeCallType nativeCall,
JNIEnv* env, typename ProxyArg<Args>::JNIType... args)
: mNativeCall(nativeCall),
mThisArg(env, ThisArgClass::Ref::From(thisArg)),
mArgs(ProxyArg<Args>::From(env, args)...) {}
ProxyNativeCall(ProxyNativeCall&&) = default;
ProxyNativeCall(const ProxyNativeCall&) = default;
// Get class ref for static calls or object ref for instance calls.
typename ThisArgClass::Param GetThisArg() const { return mThisArg; }
// Get the native object targeted by this call.
// Returns nullptr for static calls.
Impl* GetNativeObject() const { return GetNativeObject(mThisArg); }
// Return if target is the given function pointer / pointer-to-member.
// Because we can only compare pointers of the same type, we use a
// templated overload that is chosen only if given a different type of
// pointer than our target pointer type.
bool IsTarget(NativeCallType call) const { return call == mNativeCall; }
template <typename T>
bool IsTarget(T&&) const {
return false;
}
// Redirect the call to another function / class member with the same
// signature as the original target. Crash if given a wrong signature.
void SetTarget(NativeCallType call) { mNativeCall = call; }
template <typename T>
void SetTarget(T&&) const {
MOZ_CRASH();
}
void operator()() {
JNIEnv* const env = GetEnvForThread();
typename ThisArgClass::LocalRef thisArg(env, mThisArg);
Call<IsStatic, HasThisArg>(thisArg, std::index_sequence_for<Args...>{});
// Clear all saved global refs. We do this after the call is invoked,
// and not inside the destructor because we already have a JNIEnv here,
// so it's more efficient to clear out the saved args here. The
// downside is that the call can only be invoked once.
Clear(env, std::index_sequence_for<Args...>{});
mThisArg.Clear(env);
}
};
template <class Impl, bool HasThisArg, typename... Args>
struct Dispatcher {
template <class Traits, bool IsStatic = Traits::isStatic,
typename... ProxyArgs>
static std::enable_if_t<Traits::dispatchTarget == DispatchTarget::PROXY, void>
Run(ProxyArgs&&... args) {
Impl::OnNativeCall(
ProxyNativeCall<Impl, typename Traits::Owner, IsStatic, HasThisArg,
Args...>(std::forward<ProxyArgs>(args)...));
}
template <class Traits, bool IsStatic = Traits::isStatic, typename ThisArg,
typename... ProxyArgs>
static std::enable_if_t<
Traits::dispatchTarget == DispatchTarget::GECKO_PRIORITY, void>
Run(ThisArg thisArg, ProxyArgs&&... args) {
// For a static method, do not forward the "this arg" (i.e. the class
// local ref) if the implementation does not request it. This saves us
// a pair of calls to add/delete global ref.
auto proxy =
ProxyNativeCall<Impl, typename Traits::Owner, IsStatic, HasThisArg,
Args...>((HasThisArg || !IsStatic) ? thisArg : nullptr,
std::forward<ProxyArgs>(args)...);
DispatchToGeckoPriorityQueue(
NS_NewRunnableFunction("PriorityNativeCall", std::move(proxy)));
}
template <class Traits, bool IsStatic = Traits::isStatic, typename ThisArg,
typename... ProxyArgs>
static std::enable_if_t<Traits::dispatchTarget == DispatchTarget::GECKO, void>
Run(ThisArg thisArg, ProxyArgs&&... args) {
// For a static method, do not forward the "this arg" (i.e. the class
// local ref) if the implementation does not request it. This saves us
// a pair of calls to add/delete global ref.
auto proxy =
ProxyNativeCall<Impl, typename Traits::Owner, IsStatic, HasThisArg,
Args...>((HasThisArg || !IsStatic) ? thisArg : nullptr,
std::forward<ProxyArgs>(args)...);
NS_DispatchToMainThread(
NS_NewRunnableFunction("GeckoNativeCall", std::move(proxy)));
}
template <class Traits, bool IsStatic = false, typename... ProxyArgs>
static std::enable_if_t<Traits::dispatchTarget == DispatchTarget::CURRENT,
void>
Run(ProxyArgs&&... args) {
MOZ_CRASH("Unreachable code");
}
};
} // namespace detail
// Wrapper methods that convert arguments from the JNI types to the native
// types, e.g. from jobject to jni::Object::Ref. For instance methods, the
// wrapper methods also convert calls to calls on objects.
//
// We need specialization for static/non-static because the two have different
// signatures (jobject vs jclass and Impl::*Method vs *Method).
// We need specialization for return type, because void return type requires
// us to not deal with the return value.
// Bug 1207642 - Work around Dalvik bug by realigning stack on JNI entry
#ifdef __i386__
# define MOZ_JNICALL JNICALL __attribute__((force_align_arg_pointer))
#else
# define MOZ_JNICALL JNICALL
#endif
template <class Traits, class Impl, class Args = typename Traits::Args>
class NativeStub;
template <class Traits, class Impl, typename... Args>
class NativeStub<Traits, Impl, jni::Args<Args...>> {
using Owner = typename Traits::Owner;
using ReturnType = typename Traits::ReturnType;
static constexpr bool isStatic = Traits::isStatic;
static constexpr bool isVoid = std::is_void_v<ReturnType>;
struct VoidType {
using JNIType = void;
};
using ReturnJNIType =
typename std::conditional_t<isVoid, VoidType,
TypeAdapter<ReturnType>>::JNIType;
using ReturnTypeForNonVoidInstance =
std::conditional_t<!isStatic && !isVoid, ReturnType, VoidType>;
using ReturnTypeForVoidInstance =
std::conditional_t<!isStatic && isVoid, ReturnType, VoidType&>;
using ReturnTypeForNonVoidStatic =
std::conditional_t<isStatic && !isVoid, ReturnType, VoidType>;
using ReturnTypeForVoidStatic =
std::conditional_t<isStatic && isVoid, ReturnType, VoidType&>;
static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT || isVoid,
"Dispatched calls must have void return type");
public:
// Non-void instance method
template <ReturnTypeForNonVoidInstance (Impl::*Method)(Args...)>
static MOZ_JNICALL ReturnJNIType
Wrap(JNIEnv* env, jobject instance,
typename TypeAdapter<Args>::JNIType... args) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
Impl* const impl = NativePtr<Impl>::Get(env, instance);
if (!impl) {
// There is a pending JNI exception at this point.
return ReturnJNIType();
}
return TypeAdapter<ReturnType>::FromNative(
env, (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...));
}
// Non-void instance method with instance reference
template <ReturnTypeForNonVoidInstance (Impl::*Method)(
const typename Owner::LocalRef&, Args...)>
static MOZ_JNICALL ReturnJNIType
Wrap(JNIEnv* env, jobject instance,
typename TypeAdapter<Args>::JNIType... args) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
Impl* const impl = NativePtr<Impl>::Get(env, instance);
if (!impl) {
// There is a pending JNI exception at this point.
return ReturnJNIType();
}
auto self = Owner::LocalRef::Adopt(env, instance);
const auto res = TypeAdapter<ReturnType>::FromNative(
env, (impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...));
self.Forget();
return res;
}
// Void instance method
template <ReturnTypeForVoidInstance (Impl::*Method)(Args...)>
static MOZ_JNICALL void Wrap(JNIEnv* env, jobject instance,
typename TypeAdapter<Args>::JNIType... args) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
Dispatcher<Impl, /* HasThisArg */ false, Args...>::template Run<Traits>(
instance, Method, env, args...);
return;
}
Impl* const impl = NativePtr<Impl>::Get(env, instance);
if (!impl) {
// There is a pending JNI exception at this point.
return;
}
(impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...);
}
// Void instance method with instance reference
template <ReturnTypeForVoidInstance (Impl::*Method)(
const typename Owner::LocalRef&, Args...)>
static MOZ_JNICALL void Wrap(JNIEnv* env, jobject instance,
typename TypeAdapter<Args>::JNIType... args) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
Dispatcher<Impl, /* HasThisArg */ true, Args...>::template Run<Traits>(
instance, Method, env, args...);
return;
}
Impl* const impl = NativePtr<Impl>::Get(env, instance);
if (!impl) {
// There is a pending JNI exception at this point.
return;
}
auto self = Owner::LocalRef::Adopt(env, instance);
(impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...);
self.Forget();
}
// Overload for DisposeNative
template <ReturnTypeForVoidInstance (*DisposeNative)(
const typename Owner::LocalRef&)>
static MOZ_JNICALL void Wrap(JNIEnv* env, jobject instance) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
using LocalRef = typename Owner::LocalRef;
Dispatcher<Impl, /* HasThisArg */ false, const LocalRef&>::template Run<
Traits, /* IsStatic */ true>(
/* ThisArg */ nullptr, DisposeNative, env, instance);
return;
}
auto self = Owner::LocalRef::Adopt(env, instance);
DisposeNative(self);
self.Forget();
}
// Non-void static method
template <ReturnTypeForNonVoidStatic (*Method)(Args...)>
static MOZ_JNICALL ReturnJNIType
Wrap(JNIEnv* env, jclass, typename TypeAdapter<Args>::JNIType... args) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
return TypeAdapter<ReturnType>::FromNative(
env, (*Method)(TypeAdapter<Args>::ToNative(env, args)...));
}
// Non-void static method with class reference
template <ReturnTypeForNonVoidStatic (*Method)(const Class::LocalRef&,
Args...)>
static MOZ_JNICALL ReturnJNIType
Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
auto clazz = Class::LocalRef::Adopt(env, cls);
const auto res = TypeAdapter<ReturnType>::FromNative(
env, (*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...));
clazz.Forget();
return res;
}
// Void static method
template <ReturnTypeForVoidStatic (*Method)(Args...)>
static MOZ_JNICALL void Wrap(JNIEnv* env, jclass cls,
typename TypeAdapter<Args>::JNIType... args) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
Dispatcher<Impl, /* HasThisArg */ false, Args...>::template Run<Traits>(
cls, Method, env, args...);
return;
}
(*Method)(TypeAdapter<Args>::ToNative(env, args)...);
}
// Void static method with class reference
template <ReturnTypeForVoidStatic (*Method)(const Class::LocalRef&, Args...)>
static MOZ_JNICALL void Wrap(JNIEnv* env, jclass cls,
typename TypeAdapter<Args>::JNIType... args) {
MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
Dispatcher<Impl, /* HasThisArg */ true, Args...>::template Run<Traits>(
cls, Method, env, args...);
return;
}
auto clazz = Class::LocalRef::Adopt(env, cls);
(*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...);
clazz.Forget();
}
};
// Generate a JNINativeMethod from a native
// method's traits class and a wrapped stub.
template <class Traits, typename Ret, typename... Args>
constexpr JNINativeMethod MakeNativeMethod(MOZ_JNICALL Ret (*stub)(JNIEnv*,
Args...)) {
return {Traits::name, Traits::signature, reinterpret_cast<void*>(stub)};
}
// Class inherited by implementing class.
template <class Cls, class Impl>
class NativeImpl {
typedef typename Cls::template Natives<Impl> Natives;
static bool sInited;
public:
static void Init() {
if (sInited) {
return;
}
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;
}
protected:
// Associate a C++ instance with a Java instance.
static void AttachNative(const typename Cls::LocalRef& instance,
SupportsWeakPtr<Impl>* ptr) {
static_assert(NativePtrPicker<Impl>::value == NativePtrType::WEAK,
"Use another AttachNative for non-WeakPtr usage");
return NativePtr<Impl>::Set(instance, static_cast<Impl*>(ptr));
}
static void AttachNative(const typename Cls::LocalRef& instance,
UniquePtr<Impl>&& ptr) {
static_assert(NativePtrPicker<Impl>::value == NativePtrType::OWNING,
"Use another AttachNative for WeakPtr or RefPtr usage");
return NativePtr<Impl>::Set(instance, std::move(ptr));
}
static void AttachNative(const typename Cls::LocalRef& instance, Impl* ptr) {
static_assert(NativePtrPicker<Impl>::value == NativePtrType::REFPTR,
"Use another AttachNative for non-RefPtr usage");
return NativePtr<Impl>::Set(instance, ptr);
}
// Get the C++ instance associated with a Java instance.
// There is always a pending exception if the return value is nullptr.
static Impl* GetNative(const typename Cls::LocalRef& instance) {
return NativePtr<Impl>::Get(instance);
}
static void DisposeNative(const typename Cls::LocalRef& instance) {
NativePtr<Impl>::Clear(instance);
}
NativeImpl() {
// Initialize on creation if not already initialized.
Init();
}
};
// Define static member.
template <class C, class I>
bool NativeImpl<C, I>::sInited;
} // namespace jni
} // namespace mozilla
#endif // mozilla_jni_Natives_h__