Issue #1742 - Part 4: Don't trigger read barriers when comparing wrapped pointers types

This is actually an undocumented dependency of Bug 1325406 for Linux and
SunOS.

Ref: BZ 1308236
This commit is contained in:
Jeremy Andrews 2022-05-26 18:57:18 -05:00 • committed by roytam1
commit e632fc1e8c
9 changed files with 343 additions and 27 deletions

View file

@ -149,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) \
@ -242,6 +238,8 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T, Heap<T>>
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.");
@ -376,6 +374,8 @@ template <typename 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.");
@ -383,9 +383,6 @@ class TenuredHeap : public js::HeapBase<T, TenuredHeap<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)
@ -462,6 +459,8 @@ 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,
@ -522,7 +521,6 @@ class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T, Handle<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);
@ -549,6 +547,8 @@ template <typename 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);
@ -781,6 +781,8 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>>
}
public:
using ElementType = T;
template <typename RootingContext>
explicit Rooted(const RootingContext& cx)
: ptr(GCPolicy<T>::initial())
@ -813,7 +815,6 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<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);
@ -890,13 +891,14 @@ template <typename 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);
@ -917,6 +919,8 @@ template <typename T>
class FakeMutableHandle : public js::MutableHandleBase<T, FakeMutableHandle<T>>
{
public:
using ElementType = T;
MOZ_IMPLICIT FakeMutableHandle(T* t) {
ptr = t;
}
@ -1107,6 +1111,8 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
js::RootLists& rootLists(js::ContextFriendFields* cx) = delete;
public:
using ElementType = T;
PersistentRooted() : ptr(GCPolicy<T>::initial()) {}
template <typename RootingContext>
@ -1160,7 +1166,6 @@ class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
}
}
DECLARE_POINTER_COMPARISON_OPS(T);
DECLARE_POINTER_CONSTREF_OPS(T);
DECLARE_POINTER_ASSIGN_OPS(PersistentRooted, T);
DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
@ -1197,6 +1202,8 @@ class JS_PUBLIC_API(ObjectPtr)
Heap<JSObject*> value;
public:
using ElementType = JSObject*;
ObjectPtr() : value(nullptr) {}
explicit ObjectPtr(JSObject* obj) : value(obj) {}
@ -1311,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 */