summaryrefslogtreecommitdiff
path: root/js/public/RootingAPI.h
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2022-05-29 00:47:25 -0500
committerMatt A. Tobin <email@mattatobin.com>2022-05-29 00:47:25 -0500
commitf11b40c3ab4a5a766b0b71ab1e9a6199b23bbfeb (patch)
tree7b10fdf57c04235448662d0256ef76fa48a1d076 /js/public/RootingAPI.h
parent5310bcfbad6c8687d0bdbe5e49fb73858dcc1631 (diff)
downloadaura-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.h283
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 */