diff options
author | Matt A. Tobin <email@mattatobin.com> | 2022-05-29 00:47:25 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2022-05-29 00:47:25 -0500 |
commit | f11b40c3ab4a5a766b0b71ab1e9a6199b23bbfeb (patch) | |
tree | 7b10fdf57c04235448662d0256ef76fa48a1d076 /js/public/RootingAPI.h | |
parent | 5310bcfbad6c8687d0bdbe5e49fb73858dcc1631 (diff) | |
download | aura-central-f11b40c3ab4a5a766b0b71ab1e9a6199b23bbfeb.tar.gz |
[JS:Engine] Remove the use of tagged shape pointers
Diffstat (limited to 'js/public/RootingAPI.h')
-rw-r--r-- | js/public/RootingAPI.h | 283 |
1 files changed, 224 insertions, 59 deletions
diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index 5f0740d53..aec25b0ff 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -112,17 +112,23 @@ template <typename T> struct BarrierMethods { }; -template <typename T> -class RootedBase {}; +template <typename Element, typename Wrapper> +class WrappedPtrOperations {}; -template <typename T> -class HandleBase {}; +template <typename Element, typename Wrapper> +class MutableWrappedPtrOperations : public WrappedPtrOperations<Element, Wrapper> {}; -template <typename T> -class MutableHandleBase {}; +template <typename T, typename Wrapper> +class RootedBase : public MutableWrappedPtrOperations<T, Wrapper> {}; -template <typename T> -class HeapBase {}; +template <typename T, typename Wrapper> +class HandleBase : public WrappedPtrOperations<T, Wrapper> {}; + +template <typename T, typename Wrapper> +class MutableHandleBase : public MutableWrappedPtrOperations<T, Wrapper> {}; + +template <typename T, typename Wrapper> +class HeapBase : public MutableWrappedPtrOperations<T, Wrapper> {}; // Cannot use FOR_EACH_HEAP_ABLE_GC_POINTER_TYPE, as this would import too many macros into scope template <typename T> struct IsHeapConstructibleType { static constexpr bool value = false; }; @@ -132,8 +138,8 @@ FOR_EACH_PUBLIC_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE) FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE) #undef DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE -template <typename T> -class PersistentRootedBase {}; +template <typename T, typename Wrapper> +class PersistentRootedBase : public MutableWrappedPtrOperations<T, Wrapper> {}; static void* const ConstNullValue = nullptr; @@ -143,10 +149,6 @@ template<typename T> struct PersistentRootedMarker; } /* namespace gc */ -#define DECLARE_POINTER_COMPARISON_OPS(T) \ - bool operator==(const T& other) const { return get() == other; } \ - bool operator!=(const T& other) const { return get() != other; } - // Important: Return a reference so passing a Rooted<T>, etc. to // something that takes a |const T&| is not a GC hazard. #define DECLARE_POINTER_CONSTREF_OPS(T) \ @@ -230,12 +232,14 @@ AssertGCThingIsNotAnObjectSubclass(js::gc::Cell* cell) {} * Type T must be a public GC pointer type. */ template <typename T> -class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T> +class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T, Heap<T>> { // Please note: this can actually also be used by nsXBLMaybeCompiled<T>, for legacy reasons. static_assert(js::IsHeapConstructibleType<T>::value, "Type T must be a public GC pointer type"); public: + using ElementType = T; + Heap() { static_assert(sizeof(T) == sizeof(Heap<T>), "Heap<T> must be binary compatible with T."); @@ -367,9 +371,11 @@ ScriptIsMarkedGray(const Heap<JSScript*>& script) * - It is not possible to store flag bits in a Heap<T>. */ template <typename T> -class TenuredHeap : public js::HeapBase<T> +class TenuredHeap : public js::HeapBase<T, TenuredHeap<T>> { public: + using ElementType = T; + TenuredHeap() : bits(0) { static_assert(sizeof(T) == sizeof(TenuredHeap<T>), "TenuredHeap<T> must be binary compatible with T."); @@ -377,9 +383,6 @@ class TenuredHeap : public js::HeapBase<T> explicit TenuredHeap(T p) : bits(0) { setPtr(p); } explicit TenuredHeap(const TenuredHeap<T>& p) : bits(0) { setPtr(p.getPtr()); } - bool operator==(const TenuredHeap<T>& other) { return bits == other.bits; } - bool operator!=(const TenuredHeap<T>& other) { return bits != other.bits; } - void setPtr(T newPtr) { MOZ_ASSERT((reinterpret_cast<uintptr_t>(newPtr) & flagsMask) == 0); if (newPtr) @@ -451,11 +454,13 @@ class TenuredHeap : public js::HeapBase<T> * specialization, define a HandleBase<T> specialization containing them. */ template <typename T> -class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T> +class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T, Handle<T>> { friend class JS::MutableHandle<T>; public: + using ElementType = T; + /* Creates a handle from a handle of a type convertible to T. */ template <typename S> MOZ_IMPLICIT Handle(Handle<S> handle, @@ -516,7 +521,6 @@ class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T> MOZ_IMPLICIT Handle(MutableHandle<S>& root, typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0); - DECLARE_POINTER_COMPARISON_OPS(T); DECLARE_POINTER_CONSTREF_OPS(T); DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr); @@ -540,9 +544,11 @@ class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T> * them. */ template <typename T> -class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T> +class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T, MutableHandle<T>> { public: + using ElementType = T; + inline MOZ_IMPLICIT MutableHandle(Rooted<T>* root); inline MOZ_IMPLICIT MutableHandle(PersistentRooted<T>* root); @@ -753,7 +759,7 @@ namespace JS { * specialization, define a RootedBase<T> specialization containing them. */ template <typename T> -class MOZ_RAII Rooted : public js::RootedBase<T> +class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>> { inline void registerWithRootLists(js::RootedListHeads& roots) { this->stack = &roots[JS::MapTypeToRootKind<T>::kind]; @@ -775,6 +781,8 @@ class MOZ_RAII Rooted : public js::RootedBase<T> } public: + using ElementType = T; + template <typename RootingContext> explicit Rooted(const RootingContext& cx) : ptr(GCPolicy<T>::initial()) @@ -807,7 +815,6 @@ class MOZ_RAII Rooted : public js::RootedBase<T> ptr = mozilla::Move(value); } - DECLARE_POINTER_COMPARISON_OPS(T); DECLARE_POINTER_CONSTREF_OPS(T); DECLARE_POINTER_ASSIGN_OPS(Rooted, T); DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); @@ -853,8 +860,8 @@ namespace js { * Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>()); * Handle<StringObject*> h = rooted; */ -template <> -class RootedBase<JSObject*> +template <typename Container> +class RootedBase<JSObject*, Container> : public MutableWrappedPtrOperations<JSObject*, Container> { public: template <class U> @@ -871,8 +878,8 @@ class RootedBase<JSObject*> * Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>()); * Handle<StringObject*> h = rooted; */ -template <> -class HandleBase<JSObject*> +template <typename Container> +class HandleBase<JSObject*, Container> : public WrappedPtrOperations<JSObject*, Container> { public: template <class U> @@ -881,16 +888,17 @@ class HandleBase<JSObject*> /** Interface substitute for Rooted<T> which does not root the variable's memory. */ template <typename T> -class MOZ_RAII FakeRooted : public RootedBase<T> +class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>> { public: + using ElementType = T; + template <typename CX> explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {} template <typename CX> FakeRooted(CX* cx, T initial) : ptr(initial) {} - DECLARE_POINTER_COMPARISON_OPS(T); DECLARE_POINTER_CONSTREF_OPS(T); DECLARE_POINTER_ASSIGN_OPS(FakeRooted, T); DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); @@ -908,9 +916,11 @@ class MOZ_RAII FakeRooted : public RootedBase<T> /** Interface substitute for MutableHandle<T> which is not required to point to rooted memory. */ template <typename T> -class FakeMutableHandle : public js::MutableHandleBase<T> +class FakeMutableHandle : public js::MutableHandleBase<T, FakeMutableHandle<T>> { public: + using ElementType = T; + MOZ_IMPLICIT FakeMutableHandle(T* t) { ptr = t; } @@ -1075,7 +1085,7 @@ MutableHandle<T>::MutableHandle(PersistentRooted<T>* root) * marked when the object itself is marked. */ template<typename T> -class PersistentRooted : public js::PersistentRootedBase<T>, +class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>, private mozilla::LinkedListElement<PersistentRooted<T>> { using ListBase = mozilla::LinkedListElement<PersistentRooted<T>>; @@ -1101,6 +1111,8 @@ class PersistentRooted : public js::PersistentRootedBase<T>, js::RootLists& rootLists(js::ContextFriendFields* cx) = delete; public: + using ElementType = T; + PersistentRooted() : ptr(GCPolicy<T>::initial()) {} template <typename RootingContext> @@ -1154,7 +1166,6 @@ class PersistentRooted : public js::PersistentRootedBase<T>, } } - DECLARE_POINTER_COMPARISON_OPS(T); DECLARE_POINTER_CONSTREF_OPS(T); DECLARE_POINTER_ASSIGN_OPS(PersistentRooted, T); DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); @@ -1191,6 +1202,8 @@ class JS_PUBLIC_API(ObjectPtr) Heap<JSObject*> value; public: + using ElementType = JSObject*; + ObjectPtr() : value(nullptr) {} explicit ObjectPtr(JSObject* obj) : value(obj) {} @@ -1240,10 +1253,10 @@ class JS_PUBLIC_API(ObjectPtr) namespace js { -template <typename Outer, typename T, typename D> -class UniquePtrOperations +template <typename T, typename D, typename Container> +class WrappedPtrOperations<UniquePtr<T, D>, Container> { - const UniquePtr<T, D>& uniquePtr() const { return static_cast<const Outer*>(this)->get(); } + const UniquePtr<T, D>& uniquePtr() const { return static_cast<const Container*>(this)->get(); } public: explicit operator bool() const { return !!uniquePtr(); } @@ -1252,36 +1265,17 @@ class UniquePtrOperations T& operator*() const { return *uniquePtr(); } }; -template <typename Outer, typename T, typename D> -class MutableUniquePtrOperations : public UniquePtrOperations<Outer, T, D> +template <typename T, typename D, typename Container> +class MutableWrappedPtrOperations<UniquePtr<T, D>, Container> + : public WrappedPtrOperations<UniquePtr<T, D>, Container> { - UniquePtr<T, D>& uniquePtr() { return static_cast<Outer*>(this)->get(); } + UniquePtr<T, D>& uniquePtr() { return static_cast<Container*>(this)->get(); } public: MOZ_MUST_USE typename UniquePtr<T, D>::Pointer release() { return uniquePtr().release(); } void reset(T* ptr = T()) { uniquePtr().reset(ptr); } }; -template <typename T, typename D> -class RootedBase<UniquePtr<T, D>> - : public MutableUniquePtrOperations<JS::Rooted<UniquePtr<T, D>>, T, D> -{ }; - -template <typename T, typename D> -class MutableHandleBase<UniquePtr<T, D>> - : public MutableUniquePtrOperations<JS::MutableHandle<UniquePtr<T, D>>, T, D> -{ }; - -template <typename T, typename D> -class HandleBase<UniquePtr<T, D>> - : public UniquePtrOperations<JS::Handle<UniquePtr<T, D>>, T, D> -{ }; - -template <typename T, typename D> -class PersistentRootedBase<UniquePtr<T, D>> - : public MutableUniquePtrOperations<JS::PersistentRooted<UniquePtr<T, D>>, T, D> -{ }; - namespace gc { template <typename T, typename TraceCallbacks> @@ -1324,6 +1318,177 @@ Swap(JS::TenuredHeap<T>& aX, JS::TenuredHeap<T>& aY) } /* namespace mozilla */ +namespace js { +namespace detail { + +// DefineComparisonOps is a trait which selects which wrapper classes to define +// operator== and operator!= for. It supplies a getter function to extract the +// value to compare. This is used to avoid triggering the automatic read +// barriers where appropriate. +// +// If DefineComparisonOps is not specialized for a particular wrapper you may +// get errors such as 'invalid operands to binary expression' or 'no match for +// operator==' when trying to compare against instances of the wrapper. + +template <typename T> +struct DefineComparisonOps : mozilla::FalseType {}; + +template <typename T> +struct DefineComparisonOps<JS::Heap<T>> : mozilla::TrueType { + static const T& get(const JS::Heap<T>& v) { return v.unbarrieredGet(); } +}; + +template <typename T> +struct DefineComparisonOps<JS::TenuredHeap<T>> : mozilla::TrueType { + static const T get(const JS::TenuredHeap<T>& v) { return v.unbarrieredGetPtr(); } +}; + +template <> +struct DefineComparisonOps<JS::ObjectPtr> : mozilla::TrueType { + static const JSObject* get(const JS::ObjectPtr& v) { return v.unbarrieredGet(); } +}; + +template <typename T> +struct DefineComparisonOps<JS::Rooted<T>> : mozilla::TrueType { + static const T& get(const JS::Rooted<T>& v) { return v.get(); } +}; + +template <typename T> +struct DefineComparisonOps<JS::Handle<T>> : mozilla::TrueType { + static const T& get(const JS::Handle<T>& v) { return v.get(); } +}; + +template <typename T> +struct DefineComparisonOps<JS::MutableHandle<T>> : mozilla::TrueType { + static const T& get(const JS::MutableHandle<T>& v) { return v.get(); } +}; + +template <typename T> +struct DefineComparisonOps<JS::PersistentRooted<T>> : mozilla::TrueType { + static const T& get(const JS::PersistentRooted<T>& v) { return v.get(); } +}; + +template <typename T> +struct DefineComparisonOps<js::FakeRooted<T>> : mozilla::TrueType { + static const T& get(const js::FakeRooted<T>& v) { return v.get(); } +}; + +template <typename T> +struct DefineComparisonOps<js::FakeMutableHandle<T>> : mozilla::TrueType { + static const T& get(const js::FakeMutableHandle<T>& v) { return v.get(); } +}; + +} /* namespace detail */ +} /* namespace js */ + +// Overload operator== and operator!= for all types with the DefineComparisonOps +// trait using the supplied getter. +// +// There are four cases: + +// Case 1: comparison between two wrapper objects. + +template <typename T, typename U> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + js::detail::DefineComparisonOps<U>::value, bool>::Type +operator==(const T& a, const U& b) { + return js::detail::DefineComparisonOps<T>::get(a) == js::detail::DefineComparisonOps<U>::get(b); +} + +template <typename T, typename U> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + js::detail::DefineComparisonOps<U>::value, bool>::Type +operator!=(const T& a, const U& b) { + return !(a == b); +} + +// Case 2: comparison between a wrapper object and its unwrapped element type. + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value, bool>::Type +operator==(const T& a, const typename T::ElementType& b) { + return js::detail::DefineComparisonOps<T>::get(a) == b; +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value, bool>::Type +operator!=(const T& a, const typename T::ElementType& b) { + return !(a == b); +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value, bool>::Type +operator==(const typename T::ElementType& a, const T& b) { + return a == js::detail::DefineComparisonOps<T>::get(b); +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value, bool>::Type +operator!=(const typename T::ElementType& a, const T& b) { + return !(a == b); +} + +// Case 3: For pointer wrappers, comparison between the wrapper and a const +// element pointer. + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + mozilla::IsPointer<typename T::ElementType>::value, bool>::Type +operator==(const typename mozilla::RemovePointer<typename T::ElementType>::Type* a, const T& b) { + return a == js::detail::DefineComparisonOps<T>::get(b); +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + mozilla::IsPointer<typename T::ElementType>::value, bool>::Type +operator!=(const typename mozilla::RemovePointer<typename T::ElementType>::Type* a, const T& b) { + return !(a == b); +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + mozilla::IsPointer<typename T::ElementType>::value, bool>::Type +operator==(const T& a, const typename mozilla::RemovePointer<typename T::ElementType>::Type* b) { + return js::detail::DefineComparisonOps<T>::get(a) == b; +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + mozilla::IsPointer<typename T::ElementType>::value, bool>::Type +operator!=(const T& a, const typename mozilla::RemovePointer<typename T::ElementType>::Type* b) { + return !(a == b); +} + +// Case 4: For pointer wrappers, comparison between the wrapper and nullptr. + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + mozilla::IsPointer<typename T::ElementType>::value, bool>::Type +operator==(std::nullptr_t a, const T& b) { + return a == js::detail::DefineComparisonOps<T>::get(b); +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + mozilla::IsPointer<typename T::ElementType>::value, bool>::Type +operator!=(std::nullptr_t a, const T& b) { + return !(a == b); +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + mozilla::IsPointer<typename T::ElementType>::value, bool>::Type +operator==(const T& a, std::nullptr_t b) { + return js::detail::DefineComparisonOps<T>::get(a) == b; +} + +template <typename T> +typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value && + mozilla::IsPointer<typename T::ElementType>::value, bool>::Type +operator!=(const T& a, std::nullptr_t b) { + return !(a == b); +} + #undef DELETE_ASSIGNMENT_OPS #endif /* js_RootingAPI_h */ |