/* -*- 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/. */
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 {};
/** * 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 {};
/** * 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.
*/ enumclass 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)> staticchar (&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> staticchar (&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. staticconst 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.
*/ enumclass 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> staticauto 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> staticauto 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> staticauto 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> staticchar (&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. staticconst NativePtrType value = static_cast<NativePtrType>(sizeof(Test<Impl>(nullptr)));
};
/** * 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"); returnreinterpret_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> staticvoid 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> staticvoid 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());
}
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> staticvoid 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());
}
// 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>;
// 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;
~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);
}
#ifdefined(DEBUG) // This is kind of expensive, so we only support it in debug builds. explicitoperatorbool() const {
AutoReadLock lock(mLock); return !!mNativeImpl;
} #endif// defined(DEBUG)
/** * 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 explicitoperatorbool() 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;
}
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;
}
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) {}
/** * 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());
/** * 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());
/** * 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;
// 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> staticvoid 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> staticbool 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. constauto holder = reinterpret_cast<HandleType>(GetNativeHandle(env, instance.Get()));
MOZ_CATCH_JNI_EXCEPTION(env);
if (!holder || !holder->IsAttached()) { returntrue;
}
/** * 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; typedeftypename TypeAdapter<T>::JNIType JNIType;
// 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 (*)(constClass::LocalRef&, Args...), void (*)(Args...)>,
std::conditional_t<
HasThisArg, void (Impl::*)(consttypename 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.
// 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 { returnfalse;
}
// 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();
}
// 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 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)));
}
// 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;
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)...));
}
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); constauto res = TypeAdapter<ReturnType>::FromNative(
env, (impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...));
self.Forget(); return res;
}
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)...);
}
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();
}
// 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 { typedeftypename Cls::template Natives<Impl> Natives;
protected: // Associate a C++ instance with a Java instance. staticvoid AttachNative(consttypename 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));
}
staticvoid AttachNative(consttypename 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));
}
staticvoid AttachNative(consttypename 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(consttypename Cls::LocalRef& instance) { return NativePtrTraits<Impl>::Get(instance);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.