/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
template <typename ResolveValueT, typename RejectValueT, bool IsExclusive> class MozPromise;
namespace jni {
/** *C++classesimplementinginstance(non-static)nativemethodscanchoose *fromoneoftwoownershipmodels,whenassociatingaC++objectwithaJava *instance. * **IftheC++classinheritsfrommozilla::SupportsWeakPtr,weakpointers *willbeused.TheJavainstancewillstoreandownthepointertoa *WeakPtrobject.TheC++classitselfisotherwisenotownedordirectly *referenced.Notethatmozilla::SupportsWeakPtronlysupportsbeingusedon *asinglethread.ToattachaJavainstancetoaC++instance,passina *mozilla::SupportsWeakPtrpointertotheC++class(i.e.MyClass*). * *classMyClass:publicSupportsWeakPtr *,publicMyJavaClass::Natives<MyClass> *{ *// ... * *public: *usingMyJavaClass::Natives<MyClass>::DisposeNative; * *voidAttachTo(constMyJavaClass::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. *} *}; * **IftheC++classcontainspublicmembersAddRef()andRelease(),theJava *instancewillstoreandownthepointertoaRefPtrobject,whichholdsa *strongreferenceontheC++instance.Normalref-countingconsiderations *applyinthiscase;forexample,disposingmaycausetheC++instanceto *bedeletedandthedestructortoberunonthecurrentthread,whichmay *notbedesirable.ToattachaJavainstancetoaC++instance,passina *pointertotheC++class(i.e.MyClass*). * *classMyClass:publicRefCounted<MyClass> *,publicMyJavaClass::Natives<MyClass> *{ *// ... * *public: *usingMyJavaClass::Natives<MyClass>::DisposeNative; * *voidAttachTo(constMyJavaClass::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. *} *}; * **Inothercases,theJavainstancewillstoreandownapointertotheC++ *objectitself.Thispointermustnotbestoredordeletedelsewhere.To *attachaJavainstancetoaC++instance,passinareferencetoa *UniquePtroftheC++class(i.e.UniquePtr<MyClass>). * *classMyClass:publicMyJavaClass::Natives<MyClass> *{ *// ... * *public: *usingMyJavaClass::Natives<MyClass>::DisposeNative; * *staticvoidAttachTo(constMyJavaClass::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. *} *};
*/
/** *NativePtrInternalPickerusessomeC++SFINAEtemplate-futofigureout *whattypeofpointertheclassspecifiedbyImplneedstobe. * *ItdoesthisbysupplyingmultipleoverloadsofamethodnamedTest. *VariousoverloadsareenabledordisableddependingonwhetherornotImpl *canpossiblysupportthem. * *Eachoverload"returns"areferencetoanarraywhosesizecorrespondstothe *valueofeachenuminNativePtrInternalType.Thatsizeisthenconvertedback *totheenumvalue,yieldingtherighttype.
*/ 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));
};
/** *NativePtrPickerusessomeC++SFINAEtemplate-futofigureoutwhattypeof *pointertheclassspecifiedbyImplneedstobe. * *ItdoesthisbysupplyingmultipleoverloadsofamethodnamedTest. *VariousoverloadsareenabledordisableddependingonwhetherornotImpl *canpossiblysupportthem. * *Eachoverload"returns"areferencetoanarraywhosesizecorrespondstothe *valueofeachenuminNativePtrInternalType.Thatsizeisthenconvertedback *totheenumvalue,yieldingtherighttype.
*/ 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)));
};
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()
/** *ReturnsaRefTypetothenativeimplementationbelongingto *thegivenJavaobject.
*/ 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)));
}
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());
}
#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)
/** *IfyouwanttotemporarilyaccesstheobjectheldbyaNativeWeakPtr,you *mustobtainoneoftheseAccessorobjectsfromthepointer.Accessmust *bedone_exclusively_usingonceoftheseobjects!
*/ 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> autooperator->*(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;
}
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) {}
/** *ApointertoaninstanceofthisclassshouldbestoredinaJavaobject's *JNIObjecthandle.NewinstancesofnativeobjectswrappedbyNativeWeakPtr *arecreatedusingthestaticmethodsofthisclass. * *WhydowehavedistinctmethodshereinsteadofusingAttachNativelikeother *pointertypesthatmaybestoredinJNIObject? * *Essentially,wewantthecreationanduseofNativeWeakPtrtobeas *deliberateaspossible.Forcingadifferentcreationmechanismispartof *thatemphasis. * *Example: * *classNativeFoo{ *public: *NativeFoo(); *voidBar(); *// The following method is required to be used with NativeWeakPtr *voidOnWeakNonIntrusiveDetach(already_AddRefed<Runnable>aDisposer); *}; * *java::Object::LocalRefjavaObj(...); * *// Create a new Foo that is attached to javaObj *autoweakFoo=NativeWeakPtrHolder<NativeFoo>::Attach(javaObj); * *// Now I can save weakFoo, access it, do whatever I want *if(autoaccWeakFoo=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;
// 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()) { return true;
}
// 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;
// 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::*)(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.
// 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 { typedef typename Cls::template Natives<Impl> Natives;
// 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);
}
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.