Bug 1736737 - Move SafelyInitialized into a struct to allow partial specialization. r=jonco

Differential Revision: https://phabricator.services.mozilla.com/D129345
This commit is contained in:
Steve Fink 2021-10-25 17:35:20 +00:00
parent b4cd30a411
commit ff8a4526e9
6 changed files with 69 additions and 60 deletions

View File

@ -160,7 +160,7 @@ class Builder {
PersistentRooted<T> value;
BuiltThing(JSContext* cx, Builder& owner_,
T value_ = SafelyInitialized<T>())
T value_ = SafelyInitialized<T>::create())
: owner(owner_), value(cx, value_) {
owner.assertBuilt(value_);
}

View File

@ -215,37 +215,41 @@ JS_PUBLIC_API void HeapScriptWriteBarriers(JSScript** objp, JSScript* prev,
JSScript* next);
/**
* Create a safely-initialized |T|, suitable for use as a default value in
* situations requiring a safe but arbitrary |T| value.
* SafelyInitialized<T>::create() creates a safely-initialized |T|, suitable for
* use as a default value in situations requiring a safe but arbitrary |T|
* value. Implemented as a static method of a struct to allow partial
* specialization for subclasses via the Enable template parameter.
*/
template <typename T>
inline T SafelyInitialized() {
// This function wants to presume that |T()| -- which value-initializes a
// |T| per C++11 [expr.type.conv]p2 -- will produce a safely-initialized,
// safely-usable T that it can return.
template <typename T, typename Enable = void>
struct SafelyInitialized {
static T create() {
// This function wants to presume that |T()| -- which value-initializes a
// |T| per C++11 [expr.type.conv]p2 -- will produce a safely-initialized,
// safely-usable T that it can return.
#if defined(XP_WIN) || defined(XP_MACOSX) || \
(defined(XP_UNIX) && !defined(__clang__))
// That presumption holds for pointers, where value initialization produces
// a null pointer.
constexpr bool IsPointer = std::is_pointer_v<T>;
// That presumption holds for pointers, where value initialization produces
// a null pointer.
constexpr bool IsPointer = std::is_pointer_v<T>;
// For classes and unions we *assume* that if |T|'s default constructor is
// non-trivial it'll initialize correctly. (This is unideal, but C++
// doesn't offer a type trait indicating whether a class's constructor is
// user-defined, which better approximates our desired semantics.)
constexpr bool IsNonTriviallyDefaultConstructibleClassOrUnion =
(std::is_class_v<T> ||
std::is_union_v<T>)&&!std::is_trivially_default_constructible_v<T>;
// For classes and unions we *assume* that if |T|'s default constructor is
// non-trivial it'll initialize correctly. (This is unideal, but C++
// doesn't offer a type trait indicating whether a class's constructor is
// user-defined, which better approximates our desired semantics.)
constexpr bool IsNonTriviallyDefaultConstructibleClassOrUnion =
(std::is_class_v<T> ||
std::is_union_v<T>)&&!std::is_trivially_default_constructible_v<T>;
static_assert(IsPointer || IsNonTriviallyDefaultConstructibleClassOrUnion,
"T() must evaluate to a safely-initialized T");
static_assert(IsPointer || IsNonTriviallyDefaultConstructibleClassOrUnion,
"T() must evaluate to a safely-initialized T");
#endif
return T();
}
return T();
}
};
#ifdef JS_DEBUG
/**
@ -301,13 +305,13 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapOperations<T, Heap<T>> {
public:
using ElementType = T;
Heap() : ptr(SafelyInitialized<T>()) {
Heap() : ptr(SafelyInitialized<T>::create()) {
// No barriers are required for initialization to the default value.
static_assert(sizeof(T) == sizeof(Heap<T>),
"Heap<T> must be binary compatible with T.");
}
explicit Heap(const T& p) : ptr(p) {
postWriteBarrier(SafelyInitialized<T>(), ptr);
postWriteBarrier(SafelyInitialized<T>::create(), ptr);
}
/*
@ -317,19 +321,19 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapOperations<T, Heap<T>> {
* though they are equivalent.
*/
explicit Heap(const Heap<T>& other) : ptr(other.getWithoutExpose()) {
postWriteBarrier(SafelyInitialized<T>(), ptr);
postWriteBarrier(SafelyInitialized<T>::create(), ptr);
}
Heap(Heap<T>&& other) : ptr(other.getWithoutExpose()) {
postWriteBarrier(SafelyInitialized<T>(), ptr);
postWriteBarrier(SafelyInitialized<T>::create(), ptr);
}
Heap& operator=(Heap<T>&& other) {
set(other.getWithoutExpose());
other.set(SafelyInitialized<T>());
other.set(SafelyInitialized<T>::create());
return *this;
}
~Heap() { postWriteBarrier(ptr, SafelyInitialized<T>()); }
~Heap() { postWriteBarrier(ptr, SafelyInitialized<T>::create()); }
DECLARE_POINTER_CONSTREF_OPS(T);
DECLARE_POINTER_ASSIGN_OPS(Heap, T);
@ -1146,7 +1150,8 @@ class MOZ_RAII Rooted : public detail::RootedTraits<T>::StackBase,
template <typename RootingContext,
typename = std::enable_if_t<std::is_copy_constructible_v<T>,
RootingContext>>
explicit Rooted(const RootingContext& cx) : ptr(SafelyInitialized<T>()) {
explicit Rooted(const RootingContext& cx)
: ptr(SafelyInitialized<T>::create()) {
registerWithRootLists(rootLists(cx));
}
@ -1394,13 +1399,13 @@ class PersistentRooted : public detail::RootedTraits<T>::PersistentBase,
public:
using ElementType = T;
PersistentRooted() : ptr(SafelyInitialized<T>()) {}
PersistentRooted() : ptr(SafelyInitialized<T>::create()) {}
template <
typename RootHolder,
typename = std::enable_if_t<std::is_copy_constructible_v<T>, RootHolder>>
explicit PersistentRooted(const RootHolder& cx)
: ptr(SafelyInitialized<T>()) {
: ptr(SafelyInitialized<T>::create()) {
registerWithRootLists(cx);
}
@ -1433,7 +1438,7 @@ class PersistentRooted : public detail::RootedTraits<T>::PersistentBase,
bool initialized() const { return this->isInList(); }
void init(RootingContext* cx) { init(cx, SafelyInitialized<T>()); }
void init(RootingContext* cx) { init(cx, SafelyInitialized<T>::create()); }
void init(JSContext* cx) { init(RootingContext::get(cx)); }
template <typename U>
@ -1449,7 +1454,7 @@ class PersistentRooted : public detail::RootedTraits<T>::PersistentBase,
void reset() {
if (initialized()) {
set(SafelyInitialized<T>());
set(SafelyInitialized<T>::create());
this->remove();
}
}

View File

@ -501,7 +501,7 @@ class WriteBarriered : public BarrieredBase<T>,
template <class T>
class PreBarriered : public WriteBarriered<T> {
public:
PreBarriered() : WriteBarriered<T>(JS::SafelyInitialized<T>()) {}
PreBarriered() : WriteBarriered<T>(JS::SafelyInitialized<T>::create()) {}
/*
* Allow implicit construction for use in generic contexts, such as
* DebuggerWeakMap::markKeys.
@ -518,7 +518,7 @@ class PreBarriered : public WriteBarriered<T> {
void init(const T& v) { this->value = v; }
/* Use to set the pointer to nullptr. */
void clear() { set(JS::SafelyInitialized<T>()); }
void clear() { set(JS::SafelyInitialized<T>::create()); }
DECLARE_POINTER_ASSIGN_AND_MOVE_OPS(PreBarriered, T);
@ -535,7 +535,7 @@ class PreBarriered : public WriteBarriered<T> {
T release() {
T tmp = this->value;
this->value = JS::SafelyInitialized<T>();
this->value = JS::SafelyInitialized<T>::create();
return tmp;
}
};
@ -571,14 +571,14 @@ namespace js {
template <class T>
class GCPtr : public WriteBarriered<T> {
public:
GCPtr() : WriteBarriered<T>(JS::SafelyInitialized<T>()) {}
GCPtr() : WriteBarriered<T>(JS::SafelyInitialized<T>::create()) {}
explicit GCPtr(const T& v) : WriteBarriered<T>(v) {
this->post(JS::SafelyInitialized<T>(), v);
this->post(JS::SafelyInitialized<T>::create(), v);
}
explicit GCPtr(const GCPtr<T>& v) : WriteBarriered<T>(v) {
this->post(JS::SafelyInitialized<T>(), v);
this->post(JS::SafelyInitialized<T>::create(), v);
}
#ifdef DEBUG
@ -597,7 +597,7 @@ class GCPtr : public WriteBarriered<T> {
void init(const T& v) {
AssertTargetIsNotGray(v);
this->value = v;
this->post(JS::SafelyInitialized<T>(), v);
this->post(JS::SafelyInitialized<T>::create(), v);
}
DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
@ -667,31 +667,31 @@ namespace js {
template <class T>
class HeapPtr : public WriteBarriered<T> {
public:
HeapPtr() : WriteBarriered<T>(JS::SafelyInitialized<T>()) {}
HeapPtr() : WriteBarriered<T>(JS::SafelyInitialized<T>::create()) {}
// Implicitly adding barriers is a reasonable default.
MOZ_IMPLICIT HeapPtr(const T& v) : WriteBarriered<T>(v) {
this->post(JS::SafelyInitialized<T>(), this->value);
this->post(JS::SafelyInitialized<T>::create(), this->value);
}
MOZ_IMPLICIT HeapPtr(const HeapPtr<T>& other) : WriteBarriered<T>(other) {
this->post(JS::SafelyInitialized<T>(), this->value);
this->post(JS::SafelyInitialized<T>::create(), this->value);
}
HeapPtr(HeapPtr<T>&& other) : WriteBarriered<T>(other.release()) {
this->post(JS::SafelyInitialized<T>(), this->value);
this->post(JS::SafelyInitialized<T>::create(), this->value);
}
~HeapPtr() {
this->pre();
this->post(this->value, JS::SafelyInitialized<T>());
this->post(this->value, JS::SafelyInitialized<T>::create());
}
void init(const T& v) {
MOZ_ASSERT(this->value == JS::SafelyInitialized<T>());
MOZ_ASSERT(this->value == JS::SafelyInitialized<T>::create());
AssertTargetIsNotGray(v);
this->value = v;
this->post(JS::SafelyInitialized<T>(), this->value);
this->post(JS::SafelyInitialized<T>::create(), this->value);
}
DECLARE_POINTER_ASSIGN_AND_MOVE_OPS(HeapPtr, T);
@ -720,7 +720,7 @@ class HeapPtr : public WriteBarriered<T> {
T release() {
T tmp = this->value;
postBarrieredSet(JS::SafelyInitialized<T>());
postBarrieredSet(JS::SafelyInitialized<T>::create());
return tmp;
}
};
@ -769,26 +769,28 @@ class WeakHeapPtr : public ReadBarriered<T>,
using ReadBarriered<T>::value;
public:
WeakHeapPtr() : ReadBarriered<T>(JS::SafelyInitialized<T>()) {}
WeakHeapPtr() : ReadBarriered<T>(JS::SafelyInitialized<T>::create()) {}
// It is okay to add barriers implicitly.
MOZ_IMPLICIT WeakHeapPtr(const T& v) : ReadBarriered<T>(v) {
this->post(JS::SafelyInitialized<T>(), v);
this->post(JS::SafelyInitialized<T>::create(), v);
}
// The copy constructor creates a new weak edge but the wrapped pointer does
// not escape, so no read barrier is necessary.
explicit WeakHeapPtr(const WeakHeapPtr& other) : ReadBarriered<T>(other) {
this->post(JS::SafelyInitialized<T>(), value);
this->post(JS::SafelyInitialized<T>::create(), value);
}
// Move retains the lifetime status of the source edge, so does not fire
// the read barrier of the defunct edge.
WeakHeapPtr(WeakHeapPtr&& other) : ReadBarriered<T>(other.release()) {
this->post(JS::SafelyInitialized<T>(), value);
this->post(JS::SafelyInitialized<T>::create(), value);
}
~WeakHeapPtr() { this->post(this->value, JS::SafelyInitialized<T>()); }
~WeakHeapPtr() {
this->post(this->value, JS::SafelyInitialized<T>::create());
}
WeakHeapPtr& operator=(const WeakHeapPtr& v) {
AssertTargetIsNotGray(v.value);
@ -832,7 +834,7 @@ class WeakHeapPtr : public ReadBarriered<T>,
T release() {
T tmp = value;
set(JS::SafelyInitialized<T>());
set(JS::SafelyInitialized<T>::create());
return tmp;
}
};
@ -844,7 +846,7 @@ class WeakHeapPtr : public ReadBarriered<T>,
template <typename T>
class UnsafeBarePtr : public BarrieredBase<T> {
public:
UnsafeBarePtr() : BarrieredBase<T>(JS::SafelyInitialized<T>()) {}
UnsafeBarePtr() : BarrieredBase<T>(JS::SafelyInitialized<T>::create()) {}
MOZ_IMPLICIT UnsafeBarePtr(T v) : BarrieredBase<T>(v) {}
const T& get() const { return this->value; }
void set(T newValue) { this->value = newValue; }

View File

@ -32,7 +32,8 @@ class MOZ_RAII FakeRooted : public RootedOperations<T, FakeRooted<T>> {
public:
using ElementType = T;
explicit FakeRooted(JSContext* cx) : ptr(JS::SafelyInitialized<T>()) {}
explicit FakeRooted(JSContext* cx)
: ptr(JS::SafelyInitialized<T>::create()) {}
FakeRooted(JSContext* cx, T initial) : ptr(initial) {}

View File

@ -23,7 +23,8 @@ namespace detail {
template <typename T>
class UnsafeBareWeakHeapPtr : public ReadBarriered<T> {
public:
UnsafeBareWeakHeapPtr() : ReadBarriered<T>(JS::SafelyInitialized<T>()) {}
UnsafeBareWeakHeapPtr()
: ReadBarriered<T>(JS::SafelyInitialized<T>::create()) {}
MOZ_IMPLICIT UnsafeBareWeakHeapPtr(const T& v) : ReadBarriered<T>(v) {}
explicit UnsafeBareWeakHeapPtr(const UnsafeBareWeakHeapPtr& v)
: ReadBarriered<T>(v) {}
@ -42,7 +43,7 @@ class UnsafeBareWeakHeapPtr : public ReadBarriered<T> {
const T get() const {
if (!InternalBarrierMethods<T>::isMarkable(this->value)) {
return JS::SafelyInitialized<T>();
return JS::SafelyInitialized<T>::create();
}
this->read();
return this->value;

View File

@ -1787,10 +1787,10 @@ class ReservedRooted : public RootedOperations<T, ReservedRooted<T>> {
}
explicit ReservedRooted(Rooted<T>* root) : savedRoot(root) {
*root = JS::SafelyInitialized<T>();
*root = JS::SafelyInitialized<T>::create();
}
~ReservedRooted() { *savedRoot = JS::SafelyInitialized<T>(); }
~ReservedRooted() { *savedRoot = JS::SafelyInitialized<T>::create(); }
void set(const T& p) const { *savedRoot = p; }
operator Handle<T>() { return *savedRoot; }