#ifndef gc_Barrier_h
#define gc_Barrier_h
#include "NamespaceImports.h"
#include "gc/Cell.h"
#include "gc/StoreBuffer.h"
#include "js/HeapAPI.h"
#include "js/Id.h"
#include "js/RootingAPI.h"
#include "js/Value.h"
class JSFlatString;
class JSLinearString;
namespace js {
class AccessorShape;
class ArrayObject;
class ArgumentsObject;
class ArrayBufferObjectMaybeShared;
class ArrayBufferObject;
class ArrayBufferViewObject;
class SharedArrayBufferObject;
class BaseShape;
class DebugEnvironmentProxy;
class GlobalObject;
class LazyScript;
class ModuleObject;
class ModuleEnvironmentObject;
class ModuleNamespaceObject;
class NativeObject;
class PlainObject;
class PropertyName;
class SavedFrame;
class EnvironmentObject;
class ScriptSourceObject;
class Shape;
class UnownedBaseShape;
class ObjectGroup;
namespace jit {
class JitCode;
}
#ifdef DEBUG
bool CurrentThreadIsIonCompiling();
bool CurrentThreadIsIonCompilingSafeForMinorGC();
bool CurrentThreadIsGCSweeping();
bool IsMarkedBlack(JSObject* obj);
bool CurrentThreadIsTouchingGrayThings();
#endif
struct MOZ_RAII AutoTouchingGrayThings {
#ifdef DEBUG
AutoTouchingGrayThings();
~AutoTouchingGrayThings();
#else
AutoTouchingGrayThings() {}
#endif
};
template <typename T>
struct InternalBarrierMethods {};
template <typename T>
struct InternalBarrierMethods<T*> {
static bool isMarkable(T* v) { return v != nullptr; }
static void preBarrier(T* v) { T::writeBarrierPre(v); }
static void postBarrier(T** vp, T* prev, T* next) {
T::writeBarrierPost(vp, prev, next);
}
static void readBarrier(T* v) { T::readBarrier(v); }
#ifdef DEBUG
static void assertThingIsNotGray(T* v) { return T::assertThingIsNotGray(v); }
#endif
};
template <>
struct InternalBarrierMethods<Value> {
static bool isMarkable(const Value& v) { return v.isGCThing(); }
static void preBarrier(const Value& v);
static MOZ_ALWAYS_INLINE void postBarrier(Value* vp, const Value& prev,
const Value& next) {
MOZ_ASSERT(!CurrentThreadIsIonCompiling());
MOZ_ASSERT(vp);
js::gc::StoreBuffer* sb;
if ((next.isObject() || next.isString()) &&
(sb = next.toGCThing()->storeBuffer())) {
if ((prev.isObject() || prev.isString()) &&
prev.toGCThing()->storeBuffer()) {
return;
}
sb->putValue(vp);
return;
}
if ((prev.isObject() || prev.isString()) &&
(sb = prev.toGCThing()->storeBuffer())) {
sb->unputValue(vp);
}
}
static void readBarrier(const Value& v);
#ifdef DEBUG
static void assertThingIsNotGray(const Value& v) {
JS::AssertValueIsNotGray(v);
}
#endif
};
template <>
struct InternalBarrierMethods<jsid> {
static bool isMarkable(jsid id) { return JSID_IS_GCTHING(id); }
static void preBarrier(jsid id);
static void postBarrier(jsid* idp, jsid prev, jsid next) {}
#ifdef DEBUG
static void assertThingIsNotGray(jsid id) { JS::AssertIdIsNotGray(id); }
#endif
};
template <typename T>
static inline void AssertTargetIsNotGray(const T& v) {
#ifdef DEBUG
if (!CurrentThreadIsTouchingGrayThings()) {
InternalBarrierMethods<T>::assertThingIsNotGray(v);
}
#endif
}
template <typename T>
class MOZ_NON_MEMMOVABLE BarrieredBase {
protected:
explicit BarrieredBase(const T& v) : value(v) {}
BarrieredBase(const BarrieredBase<T>& other) = default;
T value;
public:
T* unsafeUnbarrieredForTracing() { return &value; }
};
template <class T>
class WriteBarrieredBase
: public BarrieredBase<T>,
public WrappedPtrOperations<T, WriteBarrieredBase<T>> {
protected:
using BarrieredBase<T>::value;
explicit WriteBarrieredBase(const T& v) : BarrieredBase<T>(v) {}
public:
using ElementType = T;
DECLARE_POINTER_CONSTREF_OPS(T);
const T& get() const { return this->value; }
void unsafeSet(const T& v) { this->value = v; }
static void writeBarrierPre(const T& v) {
InternalBarrierMethods<T>::preBarrier(v);
}
protected:
void pre() { InternalBarrierMethods<T>::preBarrier(this->value); }
MOZ_ALWAYS_INLINE void post(const T& prev, const T& next) {
InternalBarrierMethods<T>::postBarrier(&this->value, prev, next);
}
};
template <class T>
class PreBarriered : public WriteBarrieredBase<T> {
public:
PreBarriered() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
MOZ_IMPLICIT PreBarriered(const T& v) : WriteBarrieredBase<T>(v) {}
explicit PreBarriered(const PreBarriered<T>& v)
: WriteBarrieredBase<T>(v.value) {}
~PreBarriered() { this->pre(); }
void init(const T& v) { this->value = v; }
void clear() {
this->pre();
this->value = nullptr;
}
DECLARE_POINTER_ASSIGN_OPS(PreBarriered, T);
private:
void set(const T& v) {
AssertTargetIsNotGray(v);
this->pre();
this->value = v;
}
};
template <class T>
class GCPtr : public WriteBarrieredBase<T> {
public:
GCPtr() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
explicit GCPtr(const T& v) : WriteBarrieredBase<T>(v) {
this->post(JS::SafelyInitialized<T>(), v);
}
explicit GCPtr(const GCPtr<T>& v) : WriteBarrieredBase<T>(v) {
this->post(JS::SafelyInitialized<T>(), v);
}
#ifdef DEBUG
~GCPtr() {
MOZ_ASSERT(CurrentThreadIsGCSweeping() ||
this->value == JS::SafelyInitialized<T>());
Poison(this, JS_FREED_HEAP_PTR_PATTERN, sizeof(*this),
MemCheckKind::MakeNoAccess);
}
#endif
void init(const T& v) {
AssertTargetIsNotGray(v);
this->value = v;
this->post(JS::SafelyInitialized<T>(), v);
}
DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
private:
void set(const T& v) {
AssertTargetIsNotGray(v);
this->pre();
T tmp = this->value;
this->value = v;
this->post(tmp, this->value);
}
GCPtr(GCPtr<T>&&) = delete;
GCPtr<T>& operator=(GCPtr<T>&&) = delete;
};
template <class T>
class HeapPtr : public WriteBarrieredBase<T> {
public:
HeapPtr() : WriteBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
MOZ_IMPLICIT HeapPtr(const T& v) : WriteBarrieredBase<T>(v) {
this->post(JS::SafelyInitialized<T>(), this->value);
}
MOZ_IMPLICIT HeapPtr(const HeapPtr<T>& v) : WriteBarrieredBase<T>(v) {
this->post(JS::SafelyInitialized<T>(), this->value);
}
~HeapPtr() {
this->pre();
this->post(this->value, JS::SafelyInitialized<T>());
}
void init(const T& v) {
AssertTargetIsNotGray(v);
this->value = v;
this->post(JS::SafelyInitialized<T>(), this->value);
}
DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
template <class T1, class T2>
friend inline void BarrieredSetPair(Zone* zone, HeapPtr<T1*>& v1, T1* val1,
HeapPtr<T2*>& v2, T2* val2);
protected:
void set(const T& v) {
AssertTargetIsNotGray(v);
this->pre();
postBarrieredSet(v);
}
void postBarrieredSet(const T& v) {
AssertTargetIsNotGray(v);
T tmp = this->value;
this->value = v;
this->post(tmp, this->value);
}
};
template <typename T>
class ReadBarrieredBase : public BarrieredBase<T> {
protected:
explicit ReadBarrieredBase(const T& v) : BarrieredBase<T>(v) {}
protected:
void read() const { InternalBarrierMethods<T>::readBarrier(this->value); }
void post(const T& prev, const T& next) {
InternalBarrierMethods<T>::postBarrier(&this->value, prev, next);
}
};
template <typename T>
class ReadBarriered : public ReadBarrieredBase<T>,
public WrappedPtrOperations<T, ReadBarriered<T>> {
protected:
using ReadBarrieredBase<T>::value;
public:
ReadBarriered() : ReadBarrieredBase<T>(JS::SafelyInitialized<T>()) {}
MOZ_IMPLICIT ReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {
this->post(JS::SafelyInitialized<T>(), v);
}
explicit ReadBarriered(const ReadBarriered& v) : ReadBarrieredBase<T>(v) {
this->post(JS::SafelyInitialized<T>(), v.unbarrieredGet());
}
ReadBarriered(ReadBarriered&& v) : ReadBarrieredBase<T>(std::move(v)) {
this->post(JS::SafelyInitialized<T>(), v.value);
}
~ReadBarriered() { this->post(this->value, JS::SafelyInitialized<T>()); }
ReadBarriered& operator=(const ReadBarriered& v) {
AssertTargetIsNotGray(v.value);
T prior = this->value;
this->value = v.value;
this->post(prior, v.value);
return *this;
}
const T& get() const {
if (InternalBarrierMethods<T>::isMarkable(this->value)) {
this->read();
}
return this->value;
}
const T& unbarrieredGet() const { return this->value; }
explicit operator bool() const { return bool(this->value); }
operator const T&() const { return get(); }
const T& operator->() const { return get(); }
T* unsafeGet() { return &this->value; }
T const* unsafeGet() const { return &this->value; }
void set(const T& v) {
AssertTargetIsNotGray(v);
T tmp = this->value;
this->value = v;
this->post(tmp, v);
}
};
template <typename T>
using WeakRef = ReadBarriered<T>;
class HeapSlot : public WriteBarrieredBase<Value> {
public:
enum Kind { Slot = 0, Element = 1 };
void init(NativeObject* owner, Kind kind, uint32_t slot, const Value& v) {
value = v;
post(owner, kind, slot, v);
}
void destroy() { pre(); }
#ifdef DEBUG
bool preconditionForSet(NativeObject* owner, Kind kind, uint32_t slot) const;
void assertPreconditionForWriteBarrierPost(NativeObject* obj, Kind kind,
uint32_t slot,
const Value& target) const;
#endif
MOZ_ALWAYS_INLINE void set(NativeObject* owner, Kind kind, uint32_t slot,
const Value& v) {
MOZ_ASSERT(preconditionForSet(owner, kind, slot));
pre();
value = v;
post(owner, kind, slot, v);
}
private:
void post(NativeObject* owner, Kind kind, uint32_t slot,
const Value& target) {
#ifdef DEBUG
assertPreconditionForWriteBarrierPost(owner, kind, slot, target);
#endif
if (this->value.isObject() || this->value.isString()) {
gc::Cell* cell = this->value.toGCThing();
if (cell->storeBuffer()) {
cell->storeBuffer()->putSlot(owner, kind, slot, 1);
}
}
}
};
class HeapSlotArray {
HeapSlot* array;
#ifdef DEBUG
bool allowWrite_;
#endif
public:
explicit HeapSlotArray(HeapSlot* array, bool allowWrite)
: array(array)
#ifdef DEBUG
,
allowWrite_(allowWrite)
#endif
{
}
operator const Value*() const {
JS_STATIC_ASSERT(sizeof(GCPtr<Value>) == sizeof(Value));
JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value));
return reinterpret_cast<const Value*>(array);
}
operator HeapSlot*() const {
MOZ_ASSERT(allowWrite());
return array;
}
HeapSlotArray operator+(int offset) const {
return HeapSlotArray(array + offset, allowWrite());
}
HeapSlotArray operator+(uint32_t offset) const {
return HeapSlotArray(array + offset, allowWrite());
}
private:
bool allowWrite() const {
#ifdef DEBUG
return allowWrite_;
#else
return true;
#endif
}
};
template <class T1, class T2>
static inline void BarrieredSetPair(Zone* zone, HeapPtr<T1*>& v1, T1* val1,
HeapPtr<T2*>& v2, T2* val2) {
if (T1::needWriteBarrierPre(zone)) {
v1.pre();
v2.pre();
}
v1.postBarrieredSet(val1);
v2.postBarrieredSet(val2);
}
template <typename T>
class ImmutableTenuredPtr {
T value;
public:
operator T() const { return value; }
T operator->() const { return value; }
operator Handle<T>() const { return Handle<T>::fromMarkedLocation(&value); }
void init(T ptr) {
MOZ_ASSERT(ptr->isTenured());
AssertTargetIsNotGray(ptr);
value = ptr;
}
T get() const { return value; }
const T* address() { return &value; }
};
template <typename T>
struct MovableCellHasher<PreBarriered<T>> {
using Key = PreBarriered<T>;
using Lookup = T;
static bool hasHash(const Lookup& l) {
return MovableCellHasher<T>::hasHash(l);
}
static bool ensureHash(const Lookup& l) {
return MovableCellHasher<T>::ensureHash(l);
}
static HashNumber hash(const Lookup& l) {
return MovableCellHasher<T>::hash(l);
}
static bool match(const Key& k, const Lookup& l) {
return MovableCellHasher<T>::match(k, l);
}
static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
};
template <typename T>
struct MovableCellHasher<HeapPtr<T>> {
using Key = HeapPtr<T>;
using Lookup = T;
static bool hasHash(const Lookup& l) {
return MovableCellHasher<T>::hasHash(l);
}
static bool ensureHash(const Lookup& l) {
return MovableCellHasher<T>::ensureHash(l);
}
static HashNumber hash(const Lookup& l) {
return MovableCellHasher<T>::hash(l);
}
static bool match(const Key& k, const Lookup& l) {
return MovableCellHasher<T>::match(k, l);
}
static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
};
template <typename T>
struct MovableCellHasher<ReadBarriered<T>> {
using Key = ReadBarriered<T>;
using Lookup = T;
static bool hasHash(const Lookup& l) {
return MovableCellHasher<T>::hasHash(l);
}
static bool ensureHash(const Lookup& l) {
return MovableCellHasher<T>::ensureHash(l);
}
static HashNumber hash(const Lookup& l) {
return MovableCellHasher<T>::hash(l);
}
static bool match(const Key& k, const Lookup& l) {
return MovableCellHasher<T>::match(k.unbarrieredGet(), l);
}
static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
};
template <class T>
struct GCPtrHasher {
typedef GCPtr<T> Key;
typedef T Lookup;
static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
static bool match(const Key& k, Lookup l) { return k.get() == l; }
static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
};
template <class T>
struct PreBarrieredHasher {
typedef PreBarriered<T> Key;
typedef T Lookup;
static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
static bool match(const Key& k, Lookup l) { return k.get() == l; }
static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
};
template <class T>
struct ReadBarrieredHasher {
typedef ReadBarriered<T> Key;
typedef T Lookup;
static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
static bool match(const Key& k, Lookup l) { return k.unbarrieredGet() == l; }
static void rekey(Key& k, const Key& newKey) {
k.set(newKey.unbarrieredGet());
}
};
}
namespace mozilla {
template <class T>
struct DefaultHasher<js::GCPtr<T>> : js::GCPtrHasher<T> {};
template <class T>
struct DefaultHasher<js::PreBarriered<T>> : js::PreBarrieredHasher<T> {};
template <class T>
struct DefaultHasher<js::ReadBarriered<T>> : js::ReadBarrieredHasher<T> {};
}
namespace js {
class ArrayObject;
class ArrayBufferObject;
class GlobalObject;
class Scope;
class ScriptSourceObject;
class Shape;
class BaseShape;
class UnownedBaseShape;
class WasmInstanceObject;
class WasmTableObject;
namespace jit {
class JitCode;
}
typedef PreBarriered<JSObject*> PreBarrieredObject;
typedef PreBarriered<JSScript*> PreBarrieredScript;
typedef PreBarriered<jit::JitCode*> PreBarrieredJitCode;
typedef PreBarriered<JSString*> PreBarrieredString;
typedef PreBarriered<JSAtom*> PreBarrieredAtom;
typedef GCPtr<NativeObject*> GCPtrNativeObject;
typedef GCPtr<ArrayObject*> GCPtrArrayObject;
typedef GCPtr<ArrayBufferObjectMaybeShared*> GCPtrArrayBufferObjectMaybeShared;
typedef GCPtr<ArrayBufferObject*> GCPtrArrayBufferObject;
typedef GCPtr<BaseShape*> GCPtrBaseShape;
typedef GCPtr<JSAtom*> GCPtrAtom;
typedef GCPtr<JSFlatString*> GCPtrFlatString;
typedef GCPtr<JSFunction*> GCPtrFunction;
typedef GCPtr<JSLinearString*> GCPtrLinearString;
typedef GCPtr<JSObject*> GCPtrObject;
typedef GCPtr<JSScript*> GCPtrScript;
typedef GCPtr<JSString*> GCPtrString;
typedef GCPtr<ModuleObject*> GCPtrModuleObject;
typedef GCPtr<ModuleEnvironmentObject*> GCPtrModuleEnvironmentObject;
typedef GCPtr<ModuleNamespaceObject*> GCPtrModuleNamespaceObject;
typedef GCPtr<PlainObject*> GCPtrPlainObject;
typedef GCPtr<PropertyName*> GCPtrPropertyName;
typedef GCPtr<Shape*> GCPtrShape;
typedef GCPtr<UnownedBaseShape*> GCPtrUnownedBaseShape;
typedef GCPtr<jit::JitCode*> GCPtrJitCode;
typedef GCPtr<ObjectGroup*> GCPtrObjectGroup;
typedef GCPtr<Scope*> GCPtrScope;
typedef PreBarriered<Value> PreBarrieredValue;
typedef GCPtr<Value> GCPtrValue;
typedef PreBarriered<jsid> PreBarrieredId;
typedef GCPtr<jsid> GCPtrId;
typedef ImmutableTenuredPtr<PropertyName*> ImmutablePropertyNamePtr;
typedef ImmutableTenuredPtr<JS::Symbol*> ImmutableSymbolPtr;
typedef ReadBarriered<DebugEnvironmentProxy*>
ReadBarrieredDebugEnvironmentProxy;
typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
typedef ReadBarriered<JSObject*> ReadBarrieredObject;
typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
typedef ReadBarriered<JSScript*> ReadBarrieredScript;
typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject;
typedef ReadBarriered<Shape*> ReadBarrieredShape;
typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode;
typedef ReadBarriered<ObjectGroup*> ReadBarrieredObjectGroup;
typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol;
typedef ReadBarriered<WasmInstanceObject*> ReadBarrieredWasmInstanceObject;
typedef ReadBarriered<WasmTableObject*> ReadBarrieredWasmTableObject;
typedef ReadBarriered<Value> ReadBarrieredValue;
namespace detail {
template <typename T>
struct DefineComparisonOps<PreBarriered<T>> : mozilla::TrueType {
static const T& get(const PreBarriered<T>& v) { return v.get(); }
};
template <typename T>
struct DefineComparisonOps<GCPtr<T>> : mozilla::TrueType {
static const T& get(const GCPtr<T>& v) { return v.get(); }
};
template <typename T>
struct DefineComparisonOps<HeapPtr<T>> : mozilla::TrueType {
static const T& get(const HeapPtr<T>& v) { return v.get(); }
};
template <typename T>
struct DefineComparisonOps<ReadBarriered<T>> : mozilla::TrueType {
static const T& get(const ReadBarriered<T>& v) { return v.unbarrieredGet(); }
};
template <>
struct DefineComparisonOps<HeapSlot> : mozilla::TrueType {
static const Value& get(const HeapSlot& v) { return v.get(); }
};
}
}
#endif