Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 <tuple>
#include <type_traits>
#include <utility>
#include "mozilla/RefPtr.h"
#include "mozilla/RWLock.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"
#if defined(_MSC_VER) // MSVC
# define FUNCTION_SIGNATURE __FUNCSIG__
#elif defined(__GNUC__) // GCC, Clang
# define FUNCTION_SIGNATURE __PRETTY_FUNCTION__
#endif
struct NativeException {
const char* str;
};
template <class T>
static NativeException NullHandle() {
return {FUNCTION_SIGNATURE};
}
template <class T>
static NativeException NullWeakPtr() {
return {FUNCTION_SIGNATURE};
}
namespace mozilla {
template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
class MozPromise;
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
* , public MyJavaClass::Natives<MyClass>
* {
* // ...
*
* public:
* using MyJavaClass::Natives<MyClass>::DisposeNative;
*
* void AttachTo(const MyJavaClass::LocalRef& instance)
* {
* MyJavaClass::Natives<MyClass>::AttachNative(
* instance, static_cast<SupportsWeakPtr*>(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 {
/**
* Type trait that determines whether a given class has a member named
* T::OnWeakNonIntrusiveDetach.
*
* Example usage:
* class Foo {};
* class Bar {
* public:
* void OnWeakNonIntrusiveDetach(already_AddRefed<nsIRunnable> aRunnable);
* };
*
* constexpr bool foo = HasWeakNonIntrusiveDetach<Foo>::value; // Expect false
* constexpr bool bar = HasWeakNonIntrusiveDetach<Bar>::value; // Expect true
*/
template <typename, typename = std::void_t<>>
struct HasWeakNonIntrusiveDetach : std::false_type {};
template <typename T>
struct HasWeakNonIntrusiveDetach<
T, std::void_t<decltype(std::declval<T>().OnWeakNonIntrusiveDetach(
std::declval<already_AddRefed<nsIRunnable>>()))>> : std::true_type {
};
/**
* Type trait that determines whether a given class is refcounted, ie. it has
* both T::AddRef and T::Release methods.
*
* Example usage:
* class Foo {};
* class Bar {
* public:
* void AddRef();
* void Release();
* };
*
* constexpr bool foo = IsRefCounted<Foo>::value; // Expect false
* constexpr bool bar = IsRefCounted<Bar>::value; // Expect true
*/
template <typename, typename = std::void_t<>>
struct IsRefCounted : std::false_type {};
template <typename T>
struct IsRefCounted<T, std::void_t<decltype(std::declval<T>().AddRef(),
std::declval<T>().Release())>>
: std::true_type {};
/**
* This enum is used for classifying the type of pointer that is stored
* within a NativeWeakPtr. This classification is different from the one used
* for normal native pointers.
*/
enum class NativePtrInternalType : size_t {
OWNING = 1,
WEAK = 2,
REFPTR = 3,
};
/**
* NativePtrInternalPicker uses some C++ SFINAE template-fu to figure out
* what type of pointer the class specified by Impl needs to be.
*
* It does this by supplying multiple overloads of a method named Test.
* Various overloads are enabled or disabled depending on whether or not Impl
* can possibly support them.
*
* Each overload "returns" a reference to an array whose size corresponds to the
* value of each enum in NativePtrInternalType. That size is then converted back
* to the enum value, yielding the right type.
*/
template <class Impl>
class NativePtrInternalPicker {
// Enable if Impl derives from SupportsWeakPtr, yielding type WEAK
template <class I>
static std::enable_if_t<
std::is_base_of<SupportsWeakPtr, I>::value,
char (&)[static_cast<size_t>(NativePtrInternalType::WEAK)]>
Test(char);
// Enable if Impl implements AddRef and Release, yielding type REFPTR
template <class I, typename = decltype(&I::AddRef, &I::Release)>
static char (&Test(int))[static_cast<size_t>(NativePtrInternalType::REFPTR)];
// This overload uses '...' as its param to make its arguments less specific;
// the compiler prefers more-specific overloads to less-specific ones.
// OWNING is the fallback type.
template <class>
static char (&Test(...))[static_cast<size_t>(NativePtrInternalType::OWNING)];
public:
// Given a hypothetical function call Test<Impl>, convert the size of its
// resulting array back into a NativePtrInternalType enum value.
static const NativePtrInternalType value = static_cast<NativePtrInternalType>(
sizeof(Test<Impl>('\0')) / sizeof(char));
};
/**
* This enum is used for classifying the type of pointer that is stored in a
* JNIObject's handle.
*
* We have two different weak pointer types:
* * WEAK_INTRUSIVE is a pointer to a class that derives from
* mozilla::SupportsWeakPtr.
* * WEAK_NON_INTRUSIVE is a pointer to a class that does not have any
* internal support for weak pointers, but does supply a
* OnWeakNonIntrusiveDetach method.
*/
enum class NativePtrType : size_t {
OWNING = 1,
WEAK_INTRUSIVE = 2,
WEAK_NON_INTRUSIVE = 3,
REFPTR = 4,
};
/**
* NativePtrPicker uses some C++ SFINAE template-fu to figure out what type of
* pointer the class specified by Impl needs to be.
*
* It does this by supplying multiple overloads of a method named Test.
* Various overloads are enabled or disabled depending on whether or not Impl
* can possibly support them.
*
* Each overload "returns" a reference to an array whose size corresponds to the
* value of each enum in NativePtrInternalType. That size is then converted back
* to the enum value, yielding the right type.
*/
template <class Impl>
class NativePtrPicker {
// Just shorthand for each overload's return type
template <NativePtrType PtrType>
using ResultTypeT = char (&)[static_cast<size_t>(PtrType)];
// Enable if Impl derives from SupportsWeakPtr, yielding type WEAK_INTRUSIVE
template <typename I>
static auto Test(void*)
-> std::enable_if_t<std::is_base_of<SupportsWeakPtr, I>::value,
ResultTypeT<NativePtrType::WEAK_INTRUSIVE>>;
// Enable if Impl implements OnWeakNonIntrusiveDetach, yielding type
// WEAK_NON_INTRUSIVE
template <typename I>
static auto Test(void*)
-> std::enable_if_t<HasWeakNonIntrusiveDetach<I>::value,
ResultTypeT<NativePtrType::WEAK_NON_INTRUSIVE>>;
// We want the WEAK_NON_INTRUSIVE overload to take precedence over this one,
// so we only enable this overload if Impl is refcounted AND it does not
// implement OnWeakNonIntrusiveDetach. Yields type REFPTR.
template <typename I>
static auto Test(void*) -> std::enable_if_t<
std::conjunction_v<IsRefCounted<I>,
std::negation<HasWeakNonIntrusiveDetach<I>>>,
ResultTypeT<NativePtrType::REFPTR>>;
// This overload uses '...' as its param to make its arguments less specific;
// the compiler prefers more-specific overloads to less-specific ones.
// OWNING is the fallback type.
template <typename>
static char (&Test(...))[static_cast<size_t>(NativePtrType::OWNING)];
public:
// Given a hypothetical function call Test<Impl>, convert the size of its
// resulting array back into a NativePtrType enum value.
static const NativePtrType value =
static_cast<NativePtrType>(sizeof(Test<Impl>(nullptr)));
};
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;
}
/**
* This struct is used to describe various traits of a native pointer of type
* Impl that will be attached to a JNIObject.
*
* See the definition of the NativePtrType::OWNING specialization for comments
* describing the required fields.
*/
template <class Impl, NativePtrType Type = NativePtrPicker<Impl>::value>
struct NativePtrTraits;
template <class Impl>
struct NativePtrTraits<Impl, /* Type = */ NativePtrType::OWNING> {
using AccessorType =
Impl*; // Pointer-like type returned by Access() (an actual pointer in
// this case, but this is not strictly necessary)
using HandleType = Impl*; // Type of the pointer stored in JNIObject.mHandle
using RefType = Impl*; // Type of the pointer returned by Get()
/**
* Returns a RefType to the native implementation belonging to
* the given Java object.
*/
static RefType Get(JNIEnv* env, jobject instance) {
static_assert(
std::is_same<HandleType, RefType>::value,
"HandleType and RefType must be identical for owning pointers");
return reinterpret_cast<HandleType>(
CheckNativeHandle<Impl>(env, GetNativeHandle(env, instance)));
}
/**
* Returns a RefType to the native implementation belonging to
* the given Java object.
*/
template <class LocalRef>
static RefType Get(const LocalRef& instance) {
return Get(instance.Env(), instance.Get());
}
/**
* Given a RefType, returns the pointer-like AccessorType used for
* manipulating the native object.
*/
static AccessorType Access(RefType aImpl, JNIEnv* aEnv = nullptr) {
static_assert(
std::is_same<AccessorType, RefType>::value,
"AccessorType and RefType must be identical for owning pointers");
return aImpl;
}
/**
* Set the JNIObject's handle to the provided pointer, clearing any previous
* handle if necessary.
*/
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());
}
/**
* Clear the JNIObject's handle.
*/
template <class LocalRef>
static void Clear(const LocalRef& instance) {
UniquePtr<Impl> ptr(reinterpret_cast<RefType>(
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 NativePtrTraits<Impl, /* Type = */ NativePtrType::WEAK_INTRUSIVE> {
using AccessorType = Impl*;
using HandleType = WeakPtr<Impl>*;
using RefType = WeakPtr<Impl>;
static RefType Get(JNIEnv* env, jobject instance) {
const auto ptr = reinterpret_cast<HandleType>(
CheckNativeHandle<Impl>(env, GetNativeHandle(env, instance)));
return *ptr;
}
template <class LocalRef>
static RefType Get(const LocalRef& instance) {
return Get(instance.Env(), instance.Get());
}
static AccessorType Access(RefType aPtr, JNIEnv* aEnv = nullptr) {
AccessorType const impl = *aPtr;
if (!impl) {
JNIEnv* env = aEnv ? aEnv : mozilla::jni::GetEnvForThread();
ThrowException(env, "java/lang/NullPointerException",
NullWeakPtr<Impl>().str);
}
return impl;
}
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<HandleType>(
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 NativePtrTraits<Impl, /* Type = */ NativePtrType::REFPTR> {
using AccessorType = Impl*;
using HandleType = RefPtr<Impl>*;
using RefType = Impl*;
static RefType Get(JNIEnv* env, jobject instance) {
const auto ptr = reinterpret_cast<HandleType>(
CheckNativeHandle<Impl>(env, GetNativeHandle(env, instance)));
if (!ptr) {
return nullptr;
}
MOZ_ASSERT(*ptr);
return *ptr;
}
template <class LocalRef>
static RefType Get(const LocalRef& instance) {
return Get(instance.Env(), instance.Get());
}
static AccessorType Access(RefType aImpl, JNIEnv* aEnv = nullptr) {
static_assert(std::is_same<AccessorType, RefType>::value,
"AccessorType and RefType must be identical for refpointers");
return aImpl;
}
template <class LocalRef>
static void Set(const LocalRef& instance, RefType 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<HandleType>(
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
// Forward declarations
template <typename NativeImpl>
class NativeWeakPtr;
template <typename NativeImpl>
class NativeWeakPtrHolder;
namespace detail {
/**
* Given the class of a native implementation, as well as its
* NativePtrInternalType, resolve traits for that type that will be used by
* the NativeWeakPtrControlBlock.
*
* Note that we only implement specializations for OWNING and REFPTR types,
* as a WEAK_INTRUSIVE type should not be using NativeWeakPtr anyway. The build
* will fail if such an attempt is made.
*
* Traits need to implement two things:
* 1. A |Type| field that resolves to a pointer type to be stored in the
* JNIObject's handle. It is assumed that setting a |Type| object to nullptr
* is sufficient to delete the underlying object.
* 2. A static |AsRaw| method that converts a pointer of |Type| into a raw
* pointer.
*/
template <
typename NativeImpl,
NativePtrInternalType PtrType =
::mozilla::jni::detail::NativePtrInternalPicker<NativeImpl>::value>
struct NativeWeakPtrControlBlockStorageTraits;
template <typename NativeImpl>
struct NativeWeakPtrControlBlockStorageTraits<
NativeImpl, ::mozilla::jni::detail::NativePtrInternalType::OWNING> {
using Type = UniquePtr<NativeImpl>;
static NativeImpl* AsRaw(const Type& aStorage) { return aStorage.get(); }
};
template <typename NativeImpl>
struct NativeWeakPtrControlBlockStorageTraits<
NativeImpl, ::mozilla::jni::detail::NativePtrInternalType::REFPTR> {
using Type = RefPtr<NativeImpl>;
static NativeImpl* AsRaw(const Type& aStorage) { return aStorage.get(); }
};
// Forward Declaration
template <typename NativeImpl>
class Accessor;
/**
* This class contains the shared data that is referenced by all NativeWeakPtr
* objects that reference the same object.
*
* It retains a WeakRef to the Java object that owns this native object.
* It uses a RWLock to control access to the native pointer itself.
* Read locks are used when accessing the pointer (even when calling non-const
* methods on the native object).
* A write lock is only used when it is time to destroy the native object and
* we need to clear the value of mNativeImpl.
*/
template <typename NativeImpl>
class MOZ_HEAP_CLASS NativeWeakPtrControlBlock final {
public:
using StorageTraits = NativeWeakPtrControlBlockStorageTraits<NativeImpl>;
using StorageType = typename StorageTraits::Type;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NativeWeakPtrControlBlock)
NativeWeakPtrControlBlock(const NativeWeakPtrControlBlock&) = delete;
NativeWeakPtrControlBlock(NativeWeakPtrControlBlock&&) = delete;
NativeWeakPtrControlBlock& operator=(const NativeWeakPtrControlBlock&) =
delete;
NativeWeakPtrControlBlock& operator=(NativeWeakPtrControlBlock&&) = delete;
// This is safe to call on any thread because mJavaOwner is immutable.
mozilla::jni::Object::WeakRef GetJavaOwner() const { return mJavaOwner; }
private:
NativeWeakPtrControlBlock(::mozilla::jni::Object::Param aJavaOwner,
StorageType&& aNativeImpl)
: mJavaOwner(aJavaOwner),
mLock("mozilla::jni::detail::NativeWeakPtrControlBlock"),
mNativeImpl(std::move(aNativeImpl)) {}
~NativeWeakPtrControlBlock() {
// Make sure that somebody, somewhere, has detached us before destroying.
MOZ_ASSERT(!(*this));
}
/**
* Clear the native pointer so that subsequent accesses to the native pointer
* via this control block are no longer available.
*
* We return the native pointer to the caller so that it may proceed with
* cleaning up its resources.
*/
StorageType Clear() {
StorageType nativeImpl(nullptr);
{ // Scope for lock
AutoWriteLock lock(mLock);
std::swap(mNativeImpl, nativeImpl);
}
return nativeImpl;
}
MOZ_PUSH_IGNORE_THREAD_SAFETY
void Lock() const { mLock.ReadLock(); }
void Unlock() const { mLock.ReadUnlock(); }
MOZ_POP_THREAD_SAFETY
#if defined(DEBUG)
// This is kind of expensive, so we only support it in debug builds.
explicit operator bool() const {
AutoReadLock lock(mLock);
return !!mNativeImpl;
}
#endif // defined(DEBUG)
private:
friend class Accessor<NativeImpl>;
friend class NativeWeakPtr<NativeImpl>;
friend class NativeWeakPtrHolder<NativeImpl>;
private:
const mozilla::jni::Object::WeakRef mJavaOwner;
mutable RWLock mLock MOZ_UNANNOTATED; // Protects mNativeImpl
StorageType mNativeImpl;
};
/**
* If you want to temporarily access the object held by a NativeWeakPtr, you
* must obtain one of these Accessor objects from the pointer. Access must
* be done _exclusively_ using once of these objects!
*/
template <typename NativeImpl>
class MOZ_STACK_CLASS Accessor final {
public:
~Accessor() {
if (mCtlBlock) {
mCtlBlock->Unlock();
}
}
// Check whether the object is still valid before doing anything else
explicit operator bool() const { return mCtlBlock && mCtlBlock->mNativeImpl; }
// Normal member access
NativeImpl* operator->() const {
return NativeWeakPtrControlBlockStorageTraits<NativeImpl>::AsRaw(
mCtlBlock->mNativeImpl);
}
// This allows us to support calling a pointer to a member function
template <typename Member>
auto operator->*(Member aMember) const {
NativeImpl* impl =
NativeWeakPtrControlBlockStorageTraits<NativeImpl>::AsRaw(
mCtlBlock->mNativeImpl);
return [impl, member = aMember](auto&&... aArgs) {
return (impl->*member)(std::forward<decltype(aArgs)>(aArgs)...);
};
}
// Only available for NativeImpl types that actually use refcounting.
// The idea here is that it should be possible to obtain a strong ref from
// a NativeWeakPtr if and only if NativeImpl supports refcounting.
template <typename I = NativeImpl>
auto AsRefPtr() const -> std::enable_if_t<IsRefCounted<I>::value, RefPtr<I>> {
MOZ_ASSERT(I::HasThreadSafeRefCnt::value || NS_IsMainThread());
return mCtlBlock->mNativeImpl;
}
Accessor(const Accessor&) = delete;
Accessor(Accessor&&) = delete;
Accessor& operator=(const Accessor&) = delete;
Accessor& operator=(Accessor&&) = delete;
private:
explicit Accessor(
const RefPtr<detail::NativeWeakPtrControlBlock<NativeImpl>>& aCtlBlock)
: mCtlBlock(aCtlBlock) {
if (aCtlBlock) {
aCtlBlock->Lock();
}
}
private:
friend class NativeWeakPtr<NativeImpl>;
friend class NativeWeakPtrHolder<NativeImpl>;
private:
const RefPtr<NativeWeakPtrControlBlock<NativeImpl>> mCtlBlock;
};
} // namespace detail
using DetachPromise = mozilla::MozPromise<bool, nsresult, true>;
/**
* This class implements support for thread-safe weak pointers to native objects
* that are owned by Java objects deriving from JNIObject.
*
* Any code that wants to access such a native object must have a copy of
* a NativeWeakPtr to that object.
*/
template <typename NativeImpl>
class NativeWeakPtr {
public:
using Accessor = detail::Accessor<NativeImpl>;
/**
* Call this method to access the underlying object referenced by this
* NativeWeakPtr.
*
* Always check the returned Accessor object for availability before calling
* methods on it.
*
* For example, given:
*
* NativeWeakPtr<Foo> foo;
* auto accessor = foo.Access();
* if (accessor) {
* // Okay, safe to work with
* accessor->DoStuff();
* } else {
* // The object's strong reference was cleared and is no longer available!
* }
*/
Accessor Access() const { return Accessor(mCtlBlock); }
/**
* Detach the underlying object's strong reference from its owning Java object
* and clean it up.
*/
RefPtr<DetachPromise> Detach();
/**
* This method does not indicate whether or not the weak pointer is still
* valid; it only indicates whether we're actually attached to one.
*/
bool IsAttached() const { return !!mCtlBlock; }
/**
* Does this pointer reference the same object as the one referenced by the
* provided Accessor?
*/
bool IsSame(const Accessor& aAccessor) const {
return mCtlBlock == aAccessor.mCtlBlock;
}
/**
* Does this pointer reference the same object as the one referenced by the
* provided Control Block?
*/
bool IsSame(const RefPtr<detail::NativeWeakPtrControlBlock<NativeImpl>>&
aOther) const {
return mCtlBlock == aOther;
}
NativeWeakPtr() = default;
MOZ_IMPLICIT NativeWeakPtr(decltype(nullptr)) {}
NativeWeakPtr(const NativeWeakPtr& aOther) = default;
NativeWeakPtr(NativeWeakPtr&& aOther) = default;
NativeWeakPtr& operator=(const NativeWeakPtr& aOther) = default;
NativeWeakPtr& operator=(NativeWeakPtr&& aOther) = default;
NativeWeakPtr& operator=(decltype(nullptr)) {
mCtlBlock = nullptr;
return *this;
}
protected:
// Construction of initial NativeWeakPtr for aCtlBlock
explicit NativeWeakPtr(
already_AddRefed<detail::NativeWeakPtrControlBlock<NativeImpl>> aCtlBlock)
: mCtlBlock(aCtlBlock) {}
private:
// Construction of subsequent NativeWeakPtrs for aCtlBlock
explicit NativeWeakPtr(
const RefPtr<detail::NativeWeakPtrControlBlock<NativeImpl>>& aCtlBlock)
: mCtlBlock(aCtlBlock) {}
friend class NativeWeakPtrHolder<NativeImpl>;
protected:
RefPtr<detail::NativeWeakPtrControlBlock<NativeImpl>> mCtlBlock;
};
/**
* A pointer to an instance of this class should be stored in a Java object's
* JNIObject handle. New instances of native objects wrapped by NativeWeakPtr
* are created using the static methods of this class.
*
* Why do we have distinct methods here instead of using AttachNative like other
* pointer types that may be stored in JNIObject?
*
* Essentially, we want the creation and use of NativeWeakPtr to be as
* deliberate as possible. Forcing a different creation mechanism is part of
* that emphasis.
*
* Example:
*
* class NativeFoo {
* public:
* NativeFoo();
* void Bar();
* // The following method is required to be used with NativeWeakPtr
* void OnWeakNonIntrusiveDetach(already_AddRefed<Runnable> aDisposer);
* };
*
* java::Object::LocalRef javaObj(...);
*
* // Create a new Foo that is attached to javaObj
* auto weakFoo = NativeWeakPtrHolder<NativeFoo>::Attach(javaObj);
*
* // Now I can save weakFoo, access it, do whatever I want
* if (auto accWeakFoo = weakFoo.Access()) {
* accWeakFoo->Bar();
* }
*
* // Detach from javaObj and clean up
* weakFoo.Detach();
*/
template <typename NativeImpl>
class MOZ_HEAP_CLASS NativeWeakPtrHolder final
: public NativeWeakPtr<NativeImpl> {
using Base = NativeWeakPtr<NativeImpl>;
public:
using Accessor = typename Base::Accessor;
using StorageTraits =
typename detail::NativeWeakPtrControlBlock<NativeImpl>::StorageTraits;
using StorageType = typename StorageTraits::Type;
/**
* Create a new NativeImpl object, wrap it in a NativeWeakPtr, and store it
* in the Java object's JNIObject handle.
*
* @return A NativeWeakPtr object that references the newly-attached object.
*/
template <typename Cls, typename JNIType, typename... Args>
static NativeWeakPtr<NativeImpl> Attach(const Ref<Cls, JNIType>& aJavaObject,
Args&&... aArgs) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
StorageType nativeImpl(new NativeImpl(std::forward<Args>(aArgs)...));
return AttachInternal(aJavaObject, std::move(nativeImpl));
}
/**
* Given a new NativeImpl object, wrap it in a NativeWeakPtr, and store it
* in the Java object's JNIObject handle.
*
* @return A NativeWeakPtr object that references the newly-attached object.
*/
template <typename Cls, typename JNIType>
static NativeWeakPtr<NativeImpl> AttachExisting(
const Ref<Cls, JNIType>& aJavaObject,
already_AddRefed<NativeImpl> aNativeImpl) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
StorageType nativeImpl(aNativeImpl);
return AttachInternal(aJavaObject, std::move(nativeImpl));
}
~NativeWeakPtrHolder() = default;
MOZ_IMPLICIT NativeWeakPtrHolder(decltype(nullptr)) = delete;
NativeWeakPtrHolder(const NativeWeakPtrHolder&) = delete;
NativeWeakPtrHolder(NativeWeakPtrHolder&&) = delete;
NativeWeakPtrHolder& operator=(const NativeWeakPtrHolder&) = delete;
NativeWeakPtrHolder& operator=(NativeWeakPtrHolder&&) = delete;
NativeWeakPtrHolder& operator=(decltype(nullptr)) = delete;
private:
template <typename Cls>
NativeWeakPtrHolder(const LocalRef<Cls>& aJavaObject,
StorageType&& aNativeImpl)
: NativeWeakPtr<NativeImpl>(
do_AddRef(new NativeWeakPtrControlBlock<NativeImpl>(
aJavaObject, std::move(aNativeImpl)))) {}
/**
* Internal function that actually wraps the native pointer, binds it to the
* JNIObject, and then returns the NativeWeakPtr result.
*/
template <typename Cls, typename JNIType>
static NativeWeakPtr<NativeImpl> AttachInternal(
const Ref<Cls, JNIType>& aJavaObject, StorageType&& aPtr) {
auto localJavaObject = ToLocalRef(aJavaObject);
NativeWeakPtrHolder<NativeImpl>* holder =
new NativeWeakPtrHolder<NativeImpl>(localJavaObject, std::move(aPtr));
static_assert(
NativePtrPicker<NativeImpl>::value == NativePtrType::WEAK_NON_INTRUSIVE,
"This type is not compatible with mozilla::jni::NativeWeakPtr");
NativePtrTraits<NativeImpl>::Set(localJavaObject, holder);
return NativeWeakPtr<NativeImpl>(holder->mCtlBlock);
}
};
namespace detail {
/**
* NativePtrTraits for the WEAK_NON_INTRUSIVE pointer type.
*/
template <class Impl>
struct NativePtrTraits<Impl, /* Type = */ NativePtrType::WEAK_NON_INTRUSIVE> {
using AccessorType = typename NativeWeakPtrHolder<Impl>::Accessor;
using HandleType = NativeWeakPtrHolder<Impl>*;
using RefType = NativeWeakPtrHolder<Impl>* const;
static RefType Get(JNIEnv* env, jobject instance) {
return GetHandle(env, instance);
}
template <typename Cls>
static RefType Get(const LocalRef<Cls>& instance) {
return GetHandle(instance.Env(), instance.Get());
}
static AccessorType Access(RefType aPtr) { return aPtr->Access(); }
template <typename Cls>
static void Set(const LocalRef<Cls>& instance, HandleType ptr) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
const uintptr_t handle = reinterpret_cast<uintptr_t>(ptr);
Clear(instance);
SetNativeHandle(instance.Env(), instance.Get(), handle);
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
}
template <typename Cls>
static void Clear(const LocalRef<Cls>& instance) {
auto ptr = reinterpret_cast<HandleType>(
GetNativeHandle(instance.Env(), instance.Get()));
MOZ_CATCH_JNI_EXCEPTION(instance.Env());
if (!ptr) {
return;
}
ptr->Detach();
}
// This call is not safe to do unless we know for sure that instance's
// native handle has not changed. It is up to NativeWeakPtrDetachRunnable
// to perform this check.
template <typename Cls>
static void ClearFinish(const LocalRef<Cls>& instance) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
JNIEnv* const env = instance.Env();
auto ptr =
reinterpret_cast<HandleType>(GetNativeHandle(env, instance.Get()));
MOZ_CATCH_JNI_EXCEPTION(env);
MOZ_RELEASE_ASSERT(!!ptr);
SetNativeHandle(env, instance.Get(), 0);
MOZ_CATCH_JNI_EXCEPTION(env);
// Deletion of ptr is done by the caller
}
// The call is stale if the native object has been destroyed on the
// Gecko side, but the Java object is still attached to it through
// a weak pointer. Stale calls should be discarded. Note that it's
// an error if holder is nullptr here; we return false but the
// native call will throw an error.
template <class LocalRef>
static bool IsStale(const LocalRef& instance) {
JNIEnv* const env = mozilla::jni::GetEnvForThread();
// We cannot use Get here because that method throws an exception when the
// object is null, which is a valid state for a stale call.
const auto holder =
reinterpret_cast<HandleType>(GetNativeHandle(env, instance.Get()));
MOZ_CATCH_JNI_EXCEPTION(env);
if (!holder || !holder->IsAttached()) {
return true;
}
auto acc(holder->Access());
return !acc;
}
private:
static HandleType GetHandle(JNIEnv* env, jobject instance) {
return reinterpret_cast<HandleType>(
CheckNativeHandle<Impl>(env, GetNativeHandle(env, instance)));
}
template <typename Cls>
static HandleType GetHandle(const LocalRef<Cls>& instance) {
return GetHandle(instance.Env(), instance.Get());
}
friend class NativeWeakPtrHolder<Impl>;
};
} // 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(std::is_trivial_v<T> && std::is_standard_layout_v<T>,
"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.
std::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, std::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)(std::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 {
auto impl = NativePtrTraits<Impl>::Access(NativePtrTraits<Impl>::Get(inst));
MOZ_CATCH_JNI_EXCEPTION(inst.Env());
(impl->*mNativeCall)(inst, std::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 {
auto impl = NativePtrTraits<Impl>::Access(NativePtrTraits<Impl>::Get(inst));
MOZ_CATCH_JNI_EXCEPTION(inst.Env());
(impl->*mNativeCall)(std::get<Indices>(mArgs)...);
}
template <size_t... Indices>
void Clear(JNIEnv* env, std::index_sequence<Indices...>) {
int dummy[] = {
(ProxyArg<Args>::Clear(env, std::get<Indices>(mArgs)), 0)...};
mozilla::Unused << dummy;
}
static decltype(auto) GetNativeObject(Class::Param thisArg) {
return nullptr;
}
static decltype(auto) GetNativeObject(typename Owner::Param thisArg) {
return NativePtrTraits<Impl>::Access(
NativePtrTraits<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.
decltype(auto) 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);
auto impl = NativePtrTraits<Impl>::Access(
NativePtrTraits<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);
auto impl = NativePtrTraits<Impl>::Access(
NativePtrTraits<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;
}
auto impl = NativePtrTraits<Impl>::Access(
NativePtrTraits<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;
}
auto impl = NativePtrTraits<Impl>::Access(
NativePtrTraits<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* ptr) {
static_assert(NativePtrPicker<Impl>::value == NativePtrType::WEAK_INTRUSIVE,
"Use another AttachNative for non-WeakPtr usage");
return NativePtrTraits<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 NativePtrTraits<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 NativePtrTraits<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 decltype(auto) GetNative(const typename Cls::LocalRef& instance) {
return NativePtrTraits<Impl>::Get(instance);
}
static void DisposeNative(const typename Cls::LocalRef& instance) {
NativePtrTraits<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__