Differential D3094 Diff 14018 ps/trunk/libraries/source/spidermonkey/include-win32-debug/js/RootingAPI.h
Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/libraries/source/spidermonkey/include-win32-debug/js/RootingAPI.h
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | ||||
* vim: set ts=8 sts=4 et sw=4 tw=99: | * vim: set ts=8 sts=4 et sw=4 tw=99: | ||||
* This Source Code Form is subject to the terms of the Mozilla Public | * This Source Code Form is subject to the terms of the Mozilla Public | ||||
* License, v. 2.0. If a copy of the MPL was not distributed with this | * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||
#ifndef js_RootingAPI_h | #ifndef js_RootingAPI_h | ||||
#define js_RootingAPI_h | #define js_RootingAPI_h | ||||
#include "mozilla/Attributes.h" | #include "mozilla/Attributes.h" | ||||
#include "mozilla/DebugOnly.h" | #include "mozilla/DebugOnly.h" | ||||
#include "mozilla/GuardObjects.h" | #include "mozilla/GuardObjects.h" | ||||
#include "mozilla/LinkedList.h" | #include "mozilla/LinkedList.h" | ||||
#include "mozilla/Move.h" | #include "mozilla/Move.h" | ||||
#include "mozilla/TypeTraits.h" | #include "mozilla/TypeTraits.h" | ||||
#include <type_traits> | |||||
#include "jspubtd.h" | #include "jspubtd.h" | ||||
#include "js/GCAnnotations.h" | |||||
#include "js/GCAPI.h" | #include "js/GCAPI.h" | ||||
#include "js/GCPolicyAPI.h" | |||||
#include "js/HeapAPI.h" | #include "js/HeapAPI.h" | ||||
#include "js/TypeDecls.h" | #include "js/TypeDecls.h" | ||||
#include "js/UniquePtr.h" | |||||
#include "js/Utility.h" | #include "js/Utility.h" | ||||
/* | /* | ||||
* Moving GC Stack Rooting | * Moving GC Stack Rooting | ||||
* | * | ||||
* A moving GC may change the physical location of GC allocated things, even | * A moving GC may change the physical location of GC allocated things, even | ||||
* when they are rooted, updating all pointers to the thing to refer to its new | * when they are rooted, updating all pointers to the thing to refer to its new | ||||
* location. The GC must therefore know about all live pointers to a thing, | * location. The GC must therefore know about all live pointers to a thing, | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | |||||
* (via &) | * (via &) | ||||
* | * | ||||
* All of these types have an implicit conversion to raw pointers. | * All of these types have an implicit conversion to raw pointers. | ||||
*/ | */ | ||||
namespace js { | namespace js { | ||||
template <typename T> | template <typename T> | ||||
struct GCMethods { | struct BarrierMethods { | ||||
static T initial() { return T(); } | |||||
}; | }; | ||||
template <typename T> | template <typename T> | ||||
class RootedBase {}; | class RootedBase {}; | ||||
template <typename T> | template <typename T> | ||||
class HandleBase {}; | class HandleBase {}; | ||||
template <typename T> | template <typename T> | ||||
class MutableHandleBase {}; | class MutableHandleBase {}; | ||||
template <typename T> | template <typename T> | ||||
class HeapBase {}; | class HeapBase {}; | ||||
// 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; }; | |||||
#define DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE(T) \ | |||||
template <> struct IsHeapConstructibleType<T> { static constexpr bool value = true; }; | |||||
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> | template <typename T> | ||||
class PersistentRootedBase {}; | class PersistentRootedBase {}; | ||||
static void* const ConstNullValue = nullptr; | static void* const ConstNullValue = nullptr; | ||||
namespace gc { | namespace gc { | ||||
struct Cell; | struct Cell; | ||||
template<typename T> | template<typename T> | ||||
struct PersistentRootedMarker; | struct PersistentRootedMarker; | ||||
} /* namespace gc */ | } /* namespace gc */ | ||||
#define DECLARE_POINTER_COMPARISON_OPS(T) \ | #define DECLARE_POINTER_COMPARISON_OPS(T) \ | ||||
bool operator==(const T& other) const { return get() == other; } \ | bool operator==(const T& other) const { return get() == other; } \ | ||||
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 | // Important: Return a reference so passing a Rooted<T>, etc. to | ||||
// something that takes a |const T&| is not a GC hazard. | // something that takes a |const T&| is not a GC hazard. | ||||
#define DECLARE_POINTER_CONSTREF_OPS(T) \ | #define DECLARE_POINTER_CONSTREF_OPS(T) \ | ||||
operator const T&() const { return get(); } \ | operator const T&() const { return get(); } \ | ||||
const T& operator->() const { return get(); } | const T& operator->() const { return get(); } | ||||
// Assignment operators on a base class are hidden by the implicitly defined | // Assignment operators on a base class are hidden by the implicitly defined | ||||
// operator= on the derived class. Thus, define the operator= directly on the | // operator= on the derived class. Thus, define the operator= directly on the | ||||
// class as we would need to manually pass it through anyway. | // class as we would need to manually pass it through anyway. | ||||
#define DECLARE_POINTER_ASSIGN_OPS(Wrapper, T) \ | #define DECLARE_POINTER_ASSIGN_OPS(Wrapper, T) \ | ||||
Wrapper<T>& operator=(const T& p) { \ | Wrapper<T>& operator=(const T& p) { \ | ||||
set(p); \ | set(p); \ | ||||
return *this; \ | return *this; \ | ||||
} \ | } \ | ||||
Wrapper<T>& operator=(T&& p) { \ | |||||
set(mozilla::Move(p)); \ | |||||
return *this; \ | |||||
} \ | |||||
Wrapper<T>& operator=(const Wrapper<T>& other) { \ | Wrapper<T>& operator=(const Wrapper<T>& other) { \ | ||||
set(other.get()); \ | set(other.get()); \ | ||||
return *this; \ | return *this; \ | ||||
} \ | } \ | ||||
#define DELETE_ASSIGNMENT_OPS(Wrapper, T) \ | #define DELETE_ASSIGNMENT_OPS(Wrapper, T) \ | ||||
template <typename S> Wrapper<T>& operator=(S) = delete; \ | template <typename S> Wrapper<T>& operator=(S) = delete; \ | ||||
Wrapper<T>& operator=(const Wrapper<T>&) = delete; | Wrapper<T>& operator=(const Wrapper<T>&) = delete; | ||||
#define DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr) \ | #define DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr) \ | ||||
const T* address() const { return &(ptr); } \ | const T* address() const { return &(ptr); } \ | ||||
const T& get() const { return (ptr); } \ | const T& get() const { return (ptr); } \ | ||||
#define DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr) \ | #define DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr) \ | ||||
Show All 40 Lines | |||||
* which keeps track of all pointers into the nursery. | * which keeps track of all pointers into the nursery. | ||||
* | * | ||||
* Heap<T> instances must be traced when their containing object is traced to | * Heap<T> instances must be traced when their containing object is traced to | ||||
* keep the pointed-to GC thing alive. | * keep the pointed-to GC thing alive. | ||||
* | * | ||||
* Heap<T> objects should only be used on the heap. GC references stored on the | * Heap<T> objects should only be used on the heap. GC references stored on the | ||||
* C/C++ stack must use Rooted/Handle/MutableHandle instead. | * C/C++ stack must use Rooted/Handle/MutableHandle instead. | ||||
* | * | ||||
* Type T must be one of: JS::Value, jsid, JSObject*, JSString*, JSScript* | * Type T must be a public GC pointer type. | ||||
*/ | */ | ||||
template <typename T> | template <typename T> | ||||
class Heap : public js::HeapBase<T> | class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<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: | public: | ||||
Heap() { | Heap() { | ||||
static_assert(sizeof(T) == sizeof(Heap<T>), | static_assert(sizeof(T) == sizeof(Heap<T>), | ||||
"Heap<T> must be binary compatible with T."); | "Heap<T> must be binary compatible with T."); | ||||
init(js::GCMethods<T>::initial()); | init(GCPolicy<T>::initial()); | ||||
} | } | ||||
explicit Heap(T p) { init(p); } | explicit Heap(const T& p) { init(p); } | ||||
/* | /* | ||||
* For Heap, move semantics are equivalent to copy semantics. In C++, a | * For Heap, move semantics are equivalent to copy semantics. In C++, a | ||||
* copy constructor taking const-ref is the way to get a single function | * copy constructor taking const-ref is the way to get a single function | ||||
* that will be used for both lvalue and rvalue copies, so we can simply | * that will be used for both lvalue and rvalue copies, so we can simply | ||||
* omit the rvalue variant. | * omit the rvalue variant. | ||||
*/ | */ | ||||
explicit Heap(const Heap<T>& p) { init(p.ptr); } | explicit Heap(const Heap<T>& p) { init(p.ptr); } | ||||
~Heap() { | ~Heap() { | ||||
post(ptr, js::GCMethods<T>::initial()); | post(ptr, GCPolicy<T>::initial()); | ||||
} | } | ||||
DECLARE_POINTER_CONSTREF_OPS(T); | DECLARE_POINTER_CONSTREF_OPS(T); | ||||
DECLARE_POINTER_ASSIGN_OPS(Heap, T); | DECLARE_POINTER_ASSIGN_OPS(Heap, T); | ||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); | |||||
T* unsafeGet() { return &ptr; } | const T* address() const { return &ptr; } | ||||
/* | void exposeToActiveJS() const { | ||||
* Set the pointer to a value which will cause a crash if it is | js::BarrierMethods<T>::exposeToJS(ptr); | ||||
* dereferenced. | |||||
*/ | |||||
void setToCrashOnTouch() { | |||||
ptr = reinterpret_cast<T>(crashOnTouchPointer); | |||||
} | } | ||||
const T& get() const { | |||||
exposeToActiveJS(); | |||||
return ptr; | |||||
} | |||||
const T& unbarrieredGet() const { | |||||
return ptr; | |||||
} | |||||
T* unsafeGet() { return &ptr; } | |||||
bool isSetToCrashOnTouch() { | explicit operator bool() const { | ||||
return ptr == crashOnTouchPointer; | return bool(js::BarrierMethods<T>::asGCThingOrNull(ptr)); | ||||
} | |||||
explicit operator bool() { | |||||
return bool(js::BarrierMethods<T>::asGCThingOrNull(ptr)); | |||||
} | } | ||||
private: | private: | ||||
void init(T newPtr) { | void init(const T& newPtr) { | ||||
ptr = newPtr; | ptr = newPtr; | ||||
post(js::GCMethods<T>::initial(), ptr); | post(GCPolicy<T>::initial(), ptr); | ||||
} | } | ||||
void set(T newPtr) { | void set(const T& newPtr) { | ||||
T tmp = ptr; | T tmp = ptr; | ||||
ptr = newPtr; | ptr = newPtr; | ||||
post(tmp, ptr); | post(tmp, ptr); | ||||
} | } | ||||
void post(const T& prev, const T& next) { | void post(const T& prev, const T& next) { | ||||
js::GCMethods<T>::postBarrier(&ptr, prev, next); | js::BarrierMethods<T>::postBarrier(&ptr, prev, next); | ||||
} | } | ||||
enum { | |||||
crashOnTouchPointer = 1 | |||||
}; | |||||
T ptr; | T ptr; | ||||
}; | }; | ||||
static MOZ_ALWAYS_INLINE bool | |||||
ObjectIsTenured(JSObject* obj) | |||||
{ | |||||
return !js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(obj)); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | |||||
ObjectIsTenured(const Heap<JSObject*>& obj) | |||||
{ | |||||
return ObjectIsTenured(obj.unbarrieredGet()); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | |||||
ObjectIsMarkedGray(JSObject* obj) | |||||
{ | |||||
auto cell = reinterpret_cast<js::gc::Cell*>(obj); | |||||
return js::gc::detail::CellIsMarkedGrayIfKnown(cell); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | |||||
ObjectIsMarkedGray(const JS::Heap<JSObject*>& obj) | |||||
{ | |||||
return ObjectIsMarkedGray(obj.unbarrieredGet()); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | |||||
ScriptIsMarkedGray(JSScript* script) | |||||
{ | |||||
auto cell = reinterpret_cast<js::gc::Cell*>(script); | |||||
return js::gc::detail::CellIsMarkedGrayIfKnown(cell); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | |||||
ScriptIsMarkedGray(const Heap<JSScript*>& script) | |||||
{ | |||||
return ScriptIsMarkedGray(script.unbarrieredGet()); | |||||
} | |||||
/** | /** | ||||
* The TenuredHeap<T> class is similar to the Heap<T> class above in that it | * The TenuredHeap<T> class is similar to the Heap<T> class above in that it | ||||
* encapsulates the GC concerns of an on-heap reference to a JS object. However, | * encapsulates the GC concerns of an on-heap reference to a JS object. However, | ||||
* it has two important differences: | * it has two important differences: | ||||
* | * | ||||
* 1) Pointers which are statically known to only reference "tenured" objects | * 1) Pointers which are statically known to only reference "tenured" objects | ||||
* can avoid the extra overhead of SpiderMonkey's write barriers. | * can avoid the extra overhead of SpiderMonkey's write barriers. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | void unsetFlags(uintptr_t flagsToUnset) { | ||||
bits &= ~flagsToUnset; | bits &= ~flagsToUnset; | ||||
} | } | ||||
bool hasFlag(uintptr_t flag) const { | bool hasFlag(uintptr_t flag) const { | ||||
MOZ_ASSERT((flag & ~flagsMask) == 0); | MOZ_ASSERT((flag & ~flagsMask) == 0); | ||||
return (bits & flag) != 0; | return (bits & flag) != 0; | ||||
} | } | ||||
T getPtr() const { return reinterpret_cast<T>(bits & ~flagsMask); } | T unbarrieredGetPtr() const { return reinterpret_cast<T>(bits & ~flagsMask); } | ||||
uintptr_t getFlags() const { return bits & flagsMask; } | uintptr_t getFlags() const { return bits & flagsMask; } | ||||
void exposeToActiveJS() const { | |||||
js::BarrierMethods<T>::exposeToJS(unbarrieredGetPtr()); | |||||
} | |||||
T getPtr() const { | |||||
exposeToActiveJS(); | |||||
return unbarrieredGetPtr(); | |||||
} | |||||
operator T() const { return getPtr(); } | operator T() const { return getPtr(); } | ||||
T operator->() const { return getPtr(); } | T operator->() const { return getPtr(); } | ||||
explicit operator bool() const { | |||||
return bool(js::BarrierMethods<T>::asGCThingOrNull(unbarrieredGetPtr())); | |||||
} | |||||
explicit operator bool() { | |||||
return bool(js::BarrierMethods<T>::asGCThingOrNull(unbarrieredGetPtr())); | |||||
} | |||||
TenuredHeap<T>& operator=(T p) { | TenuredHeap<T>& operator=(T p) { | ||||
setPtr(p); | setPtr(p); | ||||
return *this; | return *this; | ||||
} | } | ||||
TenuredHeap<T>& operator=(const TenuredHeap<T>& other) { | TenuredHeap<T>& operator=(const TenuredHeap<T>& other) { | ||||
bits = other.bits; | bits = other.bits; | ||||
return *this; | return *this; | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | public: | ||||
* 1) the location of the T is guaranteed to be marked (for some reason | * 1) the location of the T is guaranteed to be marked (for some reason | ||||
* other than being a Rooted), e.g., if it is guaranteed to be reachable | * other than being a Rooted), e.g., if it is guaranteed to be reachable | ||||
* from an implicit root. | * from an implicit root. | ||||
* | * | ||||
* 2) the contents of the location are immutable, or at least cannot change | * 2) the contents of the location are immutable, or at least cannot change | ||||
* for the lifetime of the handle, as its users may not expect its value | * for the lifetime of the handle, as its users may not expect its value | ||||
* to change underneath them. | * to change underneath them. | ||||
*/ | */ | ||||
static MOZ_CONSTEXPR Handle fromMarkedLocation(const T* p) { | static constexpr Handle fromMarkedLocation(const T* p) { | ||||
return Handle(p, DeliberatelyChoosingThisOverload, | return Handle(p, DeliberatelyChoosingThisOverload, | ||||
ImUsingThisOnlyInFromFromMarkedLocation); | ImUsingThisOnlyInFromFromMarkedLocation); | ||||
} | } | ||||
/* | /* | ||||
* Construct a handle from an explicitly rooted location. This is the | * Construct a handle from an explicitly rooted location. This is the | ||||
* normal way to create a handle, and normally happens implicitly. | * normal way to create a handle, and normally happens implicitly. | ||||
*/ | */ | ||||
Show All 18 Lines | public: | ||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr); | DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr); | ||||
private: | private: | ||||
Handle() {} | Handle() {} | ||||
DELETE_ASSIGNMENT_OPS(Handle, T); | DELETE_ASSIGNMENT_OPS(Handle, T); | ||||
enum Disambiguator { DeliberatelyChoosingThisOverload = 42 }; | enum Disambiguator { DeliberatelyChoosingThisOverload = 42 }; | ||||
enum CallerIdentity { ImUsingThisOnlyInFromFromMarkedLocation = 17 }; | enum CallerIdentity { ImUsingThisOnlyInFromFromMarkedLocation = 17 }; | ||||
MOZ_CONSTEXPR Handle(const T* p, Disambiguator, CallerIdentity) : ptr(p) {} | constexpr Handle(const T* p, Disambiguator, CallerIdentity) : ptr(p) {} | ||||
const T* ptr; | const T* ptr; | ||||
}; | }; | ||||
/** | /** | ||||
* Similar to a handle, but the underlying storage can be changed. This is | * Similar to a handle, but the underlying storage can be changed. This is | ||||
* useful for outparams. | * useful for outparams. | ||||
* | * | ||||
* If you want to add additional methods to MutableHandle for a specific | * If you want to add additional methods to MutableHandle for a specific | ||||
* specialization, define a MutableHandleBase<T> specialization containing | * specialization, define a MutableHandleBase<T> specialization containing | ||||
* them. | * them. | ||||
*/ | */ | ||||
template <typename T> | template <typename T> | ||||
class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T> | class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T> | ||||
{ | { | ||||
public: | public: | ||||
inline MOZ_IMPLICIT MutableHandle(Rooted<T>* root); | inline MOZ_IMPLICIT MutableHandle(Rooted<T>* root); | ||||
inline MOZ_IMPLICIT MutableHandle(PersistentRooted<T>* root); | inline MOZ_IMPLICIT MutableHandle(PersistentRooted<T>* root); | ||||
private: | private: | ||||
// Disallow nullptr for overloading purposes. | // Disallow nullptr for overloading purposes. | ||||
MutableHandle(decltype(nullptr)) = delete; | MutableHandle(decltype(nullptr)) = delete; | ||||
public: | public: | ||||
void set(T v) { | void set(const T& v) { | ||||
*ptr = v; | *ptr = v; | ||||
} | } | ||||
void set(T&& v) { | |||||
*ptr = mozilla::Move(v); | |||||
} | |||||
/* | /* | ||||
* This may be called only if the location of the T is guaranteed | * This may be called only if the location of the T is guaranteed | ||||
* to be marked (for some reason other than being a Rooted), | * to be marked (for some reason other than being a Rooted), | ||||
* e.g., if it is guaranteed to be reachable from an implicit root. | * e.g., if it is guaranteed to be reachable from an implicit root. | ||||
* | * | ||||
* Create a MutableHandle from a raw location of a T. | * Create a MutableHandle from a raw location of a T. | ||||
*/ | */ | ||||
Show All 13 Lines | private: | ||||
T* ptr; | T* ptr; | ||||
}; | }; | ||||
} /* namespace JS */ | } /* namespace JS */ | ||||
namespace js { | namespace js { | ||||
/** | |||||
* By default, things should use the inheritance hierarchy to find their | |||||
* ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that | |||||
* Rooted<T> may be used without the class definition being available. | |||||
*/ | |||||
template <typename T> | |||||
struct RootKind | |||||
{ | |||||
static ThingRootKind rootKind() { return T::rootKind(); } | |||||
}; | |||||
template <typename T> | template <typename T> | ||||
struct RootKind<T*> | struct BarrierMethods<T*> | ||||
{ | |||||
static ThingRootKind rootKind() { return T::rootKind(); } | |||||
}; | |||||
template <typename T> | |||||
struct GCMethods<T*> | |||||
{ | { | ||||
static T* initial() { return nullptr; } | static T* initial() { return nullptr; } | ||||
static gc::Cell* asGCThingOrNull(T* v) { | |||||
if (!v) | |||||
return nullptr; | |||||
MOZ_ASSERT(uintptr_t(v) > 32); | |||||
return reinterpret_cast<gc::Cell*>(v); | |||||
} | |||||
static void postBarrier(T** vp, T* prev, T* next) { | static void postBarrier(T** vp, T* prev, T* next) { | ||||
if (next) | if (next) | ||||
JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(next)); | JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(next)); | ||||
} | } | ||||
static void relocate(T** vp) {} | static void exposeToJS(T* t) { | ||||
if (t) | |||||
js::gc::ExposeGCThingToActiveJS(JS::GCCellPtr(t)); | |||||
} | |||||
}; | }; | ||||
template <> | template <> | ||||
struct GCMethods<JSObject*> | struct BarrierMethods<JSObject*> | ||||
{ | { | ||||
static JSObject* initial() { return nullptr; } | static JSObject* initial() { return nullptr; } | ||||
static gc::Cell* asGCThingOrNull(JSObject* v) { | static gc::Cell* asGCThingOrNull(JSObject* v) { | ||||
if (!v) | if (!v) | ||||
return nullptr; | return nullptr; | ||||
MOZ_ASSERT(uintptr_t(v) > 32); | MOZ_ASSERT(uintptr_t(v) > 32); | ||||
return reinterpret_cast<gc::Cell*>(v); | return reinterpret_cast<gc::Cell*>(v); | ||||
} | } | ||||
static void postBarrier(JSObject** vp, JSObject* prev, JSObject* next) { | static void postBarrier(JSObject** vp, JSObject* prev, JSObject* next) { | ||||
JS::HeapObjectPostBarrier(vp, prev, next); | JS::HeapObjectPostBarrier(vp, prev, next); | ||||
} | } | ||||
static void exposeToJS(JSObject* obj) { | |||||
if (obj) | |||||
JS::ExposeObjectToActiveJS(obj); | |||||
} | |||||
}; | }; | ||||
template <> | template <> | ||||
struct GCMethods<JSFunction*> | struct BarrierMethods<JSFunction*> | ||||
{ | { | ||||
static JSFunction* initial() { return nullptr; } | static JSFunction* initial() { return nullptr; } | ||||
static gc::Cell* asGCThingOrNull(JSFunction* v) { | |||||
if (!v) | |||||
return nullptr; | |||||
MOZ_ASSERT(uintptr_t(v) > 32); | |||||
return reinterpret_cast<gc::Cell*>(v); | |||||
} | |||||
static void postBarrier(JSFunction** vp, JSFunction* prev, JSFunction* next) { | static void postBarrier(JSFunction** vp, JSFunction* prev, JSFunction* next) { | ||||
JS::HeapObjectPostBarrier(reinterpret_cast<JSObject**>(vp), | JS::HeapObjectPostBarrier(reinterpret_cast<JSObject**>(vp), | ||||
reinterpret_cast<JSObject*>(prev), | reinterpret_cast<JSObject*>(prev), | ||||
reinterpret_cast<JSObject*>(next)); | reinterpret_cast<JSObject*>(next)); | ||||
} | } | ||||
static void exposeToJS(JSFunction* fun) { | |||||
if (fun) | |||||
JS::ExposeObjectToActiveJS(reinterpret_cast<JSObject*>(fun)); | |||||
} | |||||
}; | }; | ||||
// Provide hash codes for Cell kinds that may be relocated and, thus, not have | // Provide hash codes for Cell kinds that may be relocated and, thus, not have | ||||
// a stable address to use as the base for a hash code. Instead of the address, | // a stable address to use as the base for a hash code. Instead of the address, | ||||
// this hasher uses Cell::getUniqueId to provide exact matches and as a base | // this hasher uses Cell::getUniqueId to provide exact matches and as a base | ||||
// for generating hash codes. | // for generating hash codes. | ||||
// | // | ||||
// Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr | // Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr | ||||
// would not likely be a useful key, there are some cases where being able to | // would not likely be a useful key, there are some cases where being able to | ||||
// hash a nullptr is useful, either on purpose or because of bugs: | // hash a nullptr is useful, either on purpose or because of bugs: | ||||
// (1) existence checks where the key may happen to be null and (2) some | // (1) existence checks where the key may happen to be null and (2) some | ||||
// aggregate Lookup kinds embed a JSObject* that is frequently null and do not | // aggregate Lookup kinds embed a JSObject* that is frequently null and do not | ||||
// null test before dispatching to the hasher. | // null test before dispatching to the hasher. | ||||
template <typename T> | template <typename T> | ||||
struct JS_PUBLIC_API(MovableCellHasher) | struct JS_PUBLIC_API(MovableCellHasher) | ||||
{ | { | ||||
using Key = T; | using Key = T; | ||||
using Lookup = T; | using Lookup = T; | ||||
static bool hasHash(const Lookup& l); | |||||
static bool ensureHash(const Lookup& l); | |||||
static HashNumber hash(const Lookup& l); | static HashNumber hash(const Lookup& l); | ||||
static bool match(const Key& k, const Lookup& l); | static bool match(const Key& k, const Lookup& l); | ||||
static void rekey(Key& k, const Key& newKey) { k = newKey; } | static void rekey(Key& k, const Key& newKey) { k = newKey; } | ||||
}; | }; | ||||
template <typename T> | template <typename T> | ||||
struct JS_PUBLIC_API(MovableCellHasher<JS::Heap<T>>) | struct JS_PUBLIC_API(MovableCellHasher<JS::Heap<T>>) | ||||
{ | { | ||||
using Key = JS::Heap<T>; | using Key = JS::Heap<T>; | ||||
using Lookup = 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 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 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); } | static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); } | ||||
}; | }; | ||||
} /* namespace js */ | template <typename T> | ||||
struct FallibleHashMethods<MovableCellHasher<T>> | |||||
namespace JS { | |||||
// Non pointer types -- structs or classes that contain GC pointers, either as | |||||
// a member or in a more complex container layout -- can also be stored in a | |||||
// [Persistent]Rooted if it derives from JS::Traceable. A JS::Traceable stored | |||||
// in a [Persistent]Rooted must implement the method: | |||||
// |static void trace(T*, JSTracer*)| | |||||
class Traceable | |||||
{ | { | ||||
public: | template <typename Lookup> static bool hasHash(Lookup&& l) { | ||||
static js::ThingRootKind rootKind() { return js::THING_ROOT_TRACEABLE; } | return MovableCellHasher<T>::hasHash(mozilla::Forward<Lookup>(l)); | ||||
} | |||||
template <typename Lookup> static bool ensureHash(Lookup&& l) { | |||||
return MovableCellHasher<T>::ensureHash(mozilla::Forward<Lookup>(l)); | |||||
} | |||||
}; | }; | ||||
} /* namespace JS */ | } /* namespace js */ | ||||
namespace js { | namespace js { | ||||
// The alignment must be set because the Rooted and PersistentRooted ptr fields | |||||
// may be accessed through reinterpret_cast<Rooted<ConcreteTraceable>*>, and | |||||
// the compiler may choose a different alignment for the ptr field when it | |||||
// knows the actual type stored in DispatchWrapper<T>. | |||||
// | |||||
// It would make more sense to align only those specific fields of type | |||||
// DispatchWrapper, rather than DispatchWrapper itself, but that causes MSVC to | |||||
// fail when Rooted is used in an IsConvertible test. | |||||
template <typename T> | template <typename T> | ||||
class DispatchWrapper | class alignas(8) DispatchWrapper | ||||
{ | { | ||||
static_assert(mozilla::IsBaseOf<JS::Traceable, T>::value, | static_assert(JS::MapTypeToRootKind<T>::kind == JS::RootKind::Traceable, | ||||
"DispatchWrapper is intended only for usage with a Traceable"); | "DispatchWrapper is intended only for usage with a Traceable"); | ||||
using TraceFn = void (*)(T*, JSTracer*); | using TraceFn = void (*)(JSTracer*, T*, const char*); | ||||
TraceFn tracer; | TraceFn tracer; | ||||
#if JS_BITS_PER_WORD == 32 | alignas(gc::CellSize) T storage; | ||||
uint32_t padding; // Ensure the storage fields have CellSize alignment. | |||||
#endif | |||||
T storage; | |||||
public: | public: | ||||
template <typename U> | template <typename U> | ||||
MOZ_IMPLICIT DispatchWrapper(U&& initial) | MOZ_IMPLICIT DispatchWrapper(U&& initial) | ||||
: tracer(&T::trace), | : tracer(&JS::GCPolicy<T>::trace), | ||||
storage(mozilla::Forward<U>(initial)) | storage(mozilla::Forward<U>(initial)) | ||||
{ } | { } | ||||
// Mimic a pointer type, so that we can drop into Rooted. | // Mimic a pointer type, so that we can drop into Rooted. | ||||
T* operator &() { return &storage; } | T* operator &() { return &storage; } | ||||
const T* operator &() const { return &storage; } | const T* operator &() const { return &storage; } | ||||
operator T&() { return storage; } | operator T&() { return storage; } | ||||
operator const T&() const { return storage; } | operator const T&() const { return storage; } | ||||
// Trace the contained storage (of unknown type) using the trace function | // Trace the contained storage (of unknown type) using the trace function | ||||
// we set aside when we did know the type. | // we set aside when we did know the type. | ||||
static void TraceWrapped(JSTracer* trc, JS::Traceable* thingp, const char* name) { | static void TraceWrapped(JSTracer* trc, T* thingp, const char* name) { | ||||
auto wrapper = reinterpret_cast<DispatchWrapper*>( | auto wrapper = reinterpret_cast<DispatchWrapper*>( | ||||
uintptr_t(thingp) - offsetof(DispatchWrapper, storage)); | uintptr_t(thingp) - offsetof(DispatchWrapper, storage)); | ||||
wrapper->tracer(&wrapper->storage, trc); | wrapper->tracer(trc, &wrapper->storage, name); | ||||
} | } | ||||
}; | }; | ||||
inline RootLists& | |||||
RootListsForRootingContext(JSContext* cx) | |||||
{ | |||||
return ContextFriendFields::get(cx)->roots; | |||||
} | |||||
inline RootLists& | |||||
RootListsForRootingContext(js::ContextFriendFields* cx) | |||||
{ | |||||
return cx->roots; | |||||
} | |||||
inline RootLists& | |||||
RootListsForRootingContext(JSRuntime* rt) | |||||
{ | |||||
return PerThreadDataFriendFields::getMainThread(rt)->roots; | |||||
} | |||||
inline RootLists& | |||||
RootListsForRootingContext(js::PerThreadDataFriendFields* pt) | |||||
{ | |||||
return pt->roots; | |||||
} | |||||
} /* namespace js */ | } /* namespace js */ | ||||
namespace JS { | namespace JS { | ||||
/** | /** | ||||
* Local variable of type T whose value is always rooted. This is typically | * Local variable of type T whose value is always rooted. This is typically | ||||
* used for local variables, or for non-rooted values being passed to a | * used for local variables, or for non-rooted values being passed to a | ||||
* function that requires a handle, e.g. Foo(Root<T>(cx, x)). | * function that requires a handle, e.g. Foo(Root<T>(cx, x)). | ||||
* | * | ||||
* If you want to add additional methods to Rooted for a specific | * If you want to add additional methods to Rooted for a specific | ||||
* specialization, define a RootedBase<T> specialization containing them. | * specialization, define a RootedBase<T> specialization containing them. | ||||
*/ | */ | ||||
template <typename T> | template <typename T> | ||||
class MOZ_RAII Rooted : public js::RootedBase<T> | class MOZ_RAII Rooted : public js::RootedBase<T> | ||||
{ | { | ||||
static_assert(!mozilla::IsConvertible<T, Traceable*>::value, | inline void registerWithRootLists(js::RootedListHeads& roots) { | ||||
"Rooted takes pointer or Traceable types but not Traceable* type"); | this->stack = &roots[JS::MapTypeToRootKind<T>::kind]; | ||||
/* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */ | |||||
void registerWithRootLists(js::RootLists& roots) { | |||||
js::ThingRootKind kind = js::RootKind<T>::rootKind(); | |||||
this->stack = &roots.stackRoots_[kind]; | |||||
this->prev = *stack; | this->prev = *stack; | ||||
*stack = reinterpret_cast<Rooted<void*>*>(this); | *stack = reinterpret_cast<Rooted<void*>*>(this); | ||||
} | } | ||||
inline js::RootedListHeads& rootLists(JS::RootingContext* cx) { | |||||
return rootLists(static_cast<js::ContextFriendFields*>(cx)); | |||||
} | |||||
inline js::RootedListHeads& rootLists(js::ContextFriendFields* cx) { | |||||
if (JS::Zone* zone = cx->zone_) | |||||
return JS::shadow::Zone::asShadowZone(zone)->stackRoots_; | |||||
MOZ_ASSERT(cx->isJSContext); | |||||
return cx->roots.stackRoots_; | |||||
} | |||||
inline js::RootedListHeads& rootLists(JSContext* cx) { | |||||
return rootLists(js::ContextFriendFields::get(cx)); | |||||
} | |||||
public: | public: | ||||
template <typename RootingContext> | template <typename RootingContext> | ||||
explicit Rooted(const RootingContext& cx) | explicit Rooted(const RootingContext& cx) | ||||
: ptr(js::GCMethods<T>::initial()) | : ptr(GCPolicy<T>::initial()) | ||||
{ | { | ||||
registerWithRootLists(js::RootListsForRootingContext(cx)); | registerWithRootLists(rootLists(cx)); | ||||
} | } | ||||
template <typename RootingContext, typename S> | template <typename RootingContext, typename S> | ||||
Rooted(const RootingContext& cx, S&& initial) | Rooted(const RootingContext& cx, S&& initial) | ||||
: ptr(mozilla::Forward<S>(initial)) | : ptr(mozilla::Forward<S>(initial)) | ||||
{ | { | ||||
registerWithRootLists(js::RootListsForRootingContext(cx)); | registerWithRootLists(rootLists(cx)); | ||||
} | } | ||||
~Rooted() { | ~Rooted() { | ||||
MOZ_ASSERT(*stack == reinterpret_cast<Rooted<void*>*>(this)); | MOZ_ASSERT(*stack == reinterpret_cast<Rooted<void*>*>(this)); | ||||
*stack = prev; | *stack = prev; | ||||
} | } | ||||
Rooted<T>* previous() { return reinterpret_cast<Rooted<T>*>(prev); } | Rooted<T>* previous() { return reinterpret_cast<Rooted<T>*>(prev); } | ||||
/* | /* | ||||
* This method is public for Rooted so that Codegen.py can use a Rooted | * This method is public for Rooted so that Codegen.py can use a Rooted | ||||
* interchangeably with a MutableHandleValue. | * interchangeably with a MutableHandleValue. | ||||
*/ | */ | ||||
void set(T value) { | void set(const T& value) { | ||||
ptr = value; | ptr = value; | ||||
} | } | ||||
void set(T&& value) { | |||||
ptr = mozilla::Move(value); | |||||
} | |||||
DECLARE_POINTER_COMPARISON_OPS(T); | DECLARE_POINTER_COMPARISON_OPS(T); | ||||
DECLARE_POINTER_CONSTREF_OPS(T); | DECLARE_POINTER_CONSTREF_OPS(T); | ||||
DECLARE_POINTER_ASSIGN_OPS(Rooted, T); | DECLARE_POINTER_ASSIGN_OPS(Rooted, T); | ||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); | DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); | ||||
DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr); | DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr); | ||||
private: | private: | ||||
/* | /* | ||||
* These need to be templated on void* to avoid aliasing issues between, for | * These need to be templated on void* to avoid aliasing issues between, for | ||||
* example, Rooted<JSObject> and Rooted<JSFunction>, which use the same | * example, Rooted<JSObject> and Rooted<JSFunction>, which use the same | ||||
* stack head pointer for different classes. | * stack head pointer for different classes. | ||||
*/ | */ | ||||
Rooted<void*>** stack; | Rooted<void*>** stack; | ||||
Rooted<void*>* prev; | Rooted<void*>* prev; | ||||
/* | /* | ||||
* For pointer types, the TraceKind for tracing is based on the list it is | * For pointer types, the TraceKind for tracing is based on the list it is | ||||
* in (selected via rootKind), so no additional storage is required here. | * in (selected via MapTypeToRootKind), so no additional storage is | ||||
* All Traceable, however, share the same list, so the function to | * required here. Non-pointer types, however, share the same list, so the | ||||
* call for tracing is stored adjacent to the struct. Since C++ cannot | * function to call for tracing is stored adjacent to the struct. Since C++ | ||||
* templatize on storage class, this is implemented via the wrapper class | * cannot templatize on storage class, this is implemented via the wrapper | ||||
* DispatchWrapper. | * class DispatchWrapper. | ||||
*/ | */ | ||||
using MaybeWrapped = typename mozilla::Conditional< | using MaybeWrapped = typename mozilla::Conditional< | ||||
mozilla::IsBaseOf<Traceable, T>::value, | MapTypeToRootKind<T>::kind == JS::RootKind::Traceable, | ||||
js::DispatchWrapper<T>, | js::DispatchWrapper<T>, | ||||
T>::Type; | T>::Type; | ||||
MaybeWrapped ptr; | MaybeWrapped ptr; | ||||
Rooted(const Rooted&) = delete; | Rooted(const Rooted&) = delete; | ||||
}; | } JS_HAZ_ROOTED; | ||||
} /* namespace JS */ | } /* namespace JS */ | ||||
namespace js { | namespace js { | ||||
/** | /** | ||||
* Augment the generic Rooted<T> interface when T = JSObject* with | * Augment the generic Rooted<T> interface when T = JSObject* with | ||||
* class-querying and downcasting operations. | * class-querying and downcasting operations. | ||||
Show All 31 Lines | |||||
}; | }; | ||||
/** Interface substitute for Rooted<T> which does not root the variable's memory. */ | /** Interface substitute for Rooted<T> which does not root the variable's memory. */ | ||||
template <typename T> | template <typename T> | ||||
class MOZ_RAII FakeRooted : public RootedBase<T> | class MOZ_RAII FakeRooted : public RootedBase<T> | ||||
{ | { | ||||
public: | public: | ||||
template <typename CX> | template <typename CX> | ||||
explicit FakeRooted(CX* cx) : ptr(GCMethods<T>::initial()) {} | explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {} | ||||
template <typename CX> | template <typename CX> | ||||
FakeRooted(CX* cx, T initial) : ptr(initial) {} | FakeRooted(CX* cx, T initial) : ptr(initial) {} | ||||
DECLARE_POINTER_COMPARISON_OPS(T); | DECLARE_POINTER_COMPARISON_OPS(T); | ||||
DECLARE_POINTER_CONSTREF_OPS(T); | DECLARE_POINTER_CONSTREF_OPS(T); | ||||
DECLARE_POINTER_ASSIGN_OPS(FakeRooted, T); | DECLARE_POINTER_ASSIGN_OPS(FakeRooted, T); | ||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); | DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); | ||||
Show All 17 Lines | public: | ||||
MOZ_IMPLICIT FakeMutableHandle(T* t) { | MOZ_IMPLICIT FakeMutableHandle(T* t) { | ||||
ptr = t; | ptr = t; | ||||
} | } | ||||
MOZ_IMPLICIT FakeMutableHandle(FakeRooted<T>* root) { | MOZ_IMPLICIT FakeMutableHandle(FakeRooted<T>* root) { | ||||
ptr = root->address(); | ptr = root->address(); | ||||
} | } | ||||
void set(T v) { | void set(const T& v) { | ||||
*ptr = v; | *ptr = v; | ||||
} | } | ||||
DECLARE_POINTER_CONSTREF_OPS(T); | DECLARE_POINTER_CONSTREF_OPS(T); | ||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr); | DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr); | ||||
DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(*ptr); | DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(*ptr); | ||||
private: | private: | ||||
Show All 40 Lines | public: | ||||
static inline JS::Handle<T2*> downcastHandle(HandleType v) { | static inline JS::Handle<T2*> downcastHandle(HandleType v) { | ||||
return v.template as<T2>(); | return v.template as<T2>(); | ||||
} | } | ||||
}; | }; | ||||
template <typename T> class MaybeRooted<T, NoGC> | template <typename T> class MaybeRooted<T, NoGC> | ||||
{ | { | ||||
public: | public: | ||||
typedef T HandleType; | typedef const T& HandleType; | ||||
typedef FakeRooted<T> RootType; | typedef FakeRooted<T> RootType; | ||||
typedef FakeMutableHandle<T> MutableHandleType; | typedef FakeMutableHandle<T> MutableHandleType; | ||||
static JS::Handle<T> toHandle(HandleType v) { | static JS::Handle<T> toHandle(HandleType v) { | ||||
MOZ_CRASH("Bad conversion"); | MOZ_CRASH("Bad conversion"); | ||||
} | } | ||||
static JS::MutableHandle<T> toMutableHandle(MutableHandleType v) { | static JS::MutableHandle<T> toMutableHandle(MutableHandleType v) { | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* A copyable, assignable global GC root type with arbitrary lifetime, an | * A copyable, assignable global GC root type with arbitrary lifetime, an | ||||
* infallible constructor, and automatic unrooting on destruction. | * infallible constructor, and automatic unrooting on destruction. | ||||
* | * | ||||
* These roots can be used in heap-allocated data structures, so they are not | * These roots can be used in heap-allocated data structures, so they are not | ||||
* associated with any particular JSContext or stack. They are registered with | * associated with any particular JSContext or stack. They are registered with | ||||
* the JSRuntime itself, without locking, so they require a full JSContext to be | * the JSRuntime itself, without locking, so they require a full JSContext to be | ||||
* initialized, not one of its more restricted superclasses. Initialization may | * initialized, not one of its more restricted superclasses. Initialization may | ||||
* take place on construction, or in two phases if the no-argument constructor | * take place on construction, or in two phases if the no-argument constructor | ||||
* is called followed by init(). | * is called followed by init(). | ||||
* | * | ||||
* Note that you must not use an PersistentRooted in an object owned by a JS | * Note that you must not use an PersistentRooted in an object owned by a JS | ||||
* object: | * object: | ||||
* | * | ||||
* Whenever one object whose lifetime is decided by the GC refers to another | * Whenever one object whose lifetime is decided by the GC refers to another | ||||
* such object, that edge must be traced only if the owning JS object is traced. | * such object, that edge must be traced only if the owning JS object is traced. | ||||
Show All 14 Lines | |||||
* TenuredHeap<T> would be better types. It's up to the implementor of the type | * TenuredHeap<T> would be better types. It's up to the implementor of the type | ||||
* containing Heap<T> or TenuredHeap<T> members to make sure their referents get | * containing Heap<T> or TenuredHeap<T> members to make sure their referents get | ||||
* marked when the object itself is marked. | * marked when the object itself is marked. | ||||
*/ | */ | ||||
template<typename T> | template<typename T> | ||||
class PersistentRooted : public js::PersistentRootedBase<T>, | class PersistentRooted : public js::PersistentRootedBase<T>, | ||||
private mozilla::LinkedListElement<PersistentRooted<T>> | private mozilla::LinkedListElement<PersistentRooted<T>> | ||||
{ | { | ||||
typedef mozilla::LinkedListElement<PersistentRooted<T>> ListBase; | using ListBase = mozilla::LinkedListElement<PersistentRooted<T>>; | ||||
friend class mozilla::LinkedList<PersistentRooted>; | friend class mozilla::LinkedList<PersistentRooted>; | ||||
friend class mozilla::LinkedListElement<PersistentRooted>; | friend class mozilla::LinkedListElement<PersistentRooted>; | ||||
friend struct js::gc::PersistentRootedMarker<T>; | |||||
friend void js::gc::FinishPersistentRootedChains(js::RootLists&); | |||||
void registerWithRootLists(js::RootLists& roots) { | void registerWithRootLists(js::RootLists& roots) { | ||||
MOZ_ASSERT(!initialized()); | MOZ_ASSERT(!initialized()); | ||||
js::ThingRootKind kind = js::RootKind<T>::rootKind(); | JS::RootKind kind = JS::MapTypeToRootKind<T>::kind; | ||||
roots.heapRoots_[kind].insertBack(reinterpret_cast<JS::PersistentRooted<void*>*>(this)); | roots.heapRoots_[kind].insertBack(reinterpret_cast<JS::PersistentRooted<void*>*>(this)); | ||||
// Until marking and destruction support the full set, we assert that | |||||
// we don't try to add any unsupported types. | |||||
MOZ_ASSERT(kind == js::THING_ROOT_OBJECT || | |||||
kind == js::THING_ROOT_SCRIPT || | |||||
kind == js::THING_ROOT_STRING || | |||||
kind == js::THING_ROOT_SYMBOL || | |||||
kind == js::THING_ROOT_ID || | |||||
kind == js::THING_ROOT_VALUE || | |||||
kind == js::THING_ROOT_TRACEABLE); | |||||
} | } | ||||
js::RootLists& rootLists(JSContext* cx) { | |||||
return rootLists(JS::RootingContext::get(cx)); | |||||
} | |||||
js::RootLists& rootLists(JS::RootingContext* cx) { | |||||
MOZ_ASSERT(cx->isJSContext); | |||||
return cx->roots; | |||||
} | |||||
// Disallow ExclusiveContext*. | |||||
js::RootLists& rootLists(js::ContextFriendFields* cx) = delete; | |||||
public: | public: | ||||
PersistentRooted() : ptr(js::GCMethods<T>::initial()) {} | PersistentRooted() : ptr(GCPolicy<T>::initial()) {} | ||||
template <typename RootingContext> | template <typename RootingContext> | ||||
explicit PersistentRooted(const RootingContext& cx) | explicit PersistentRooted(const RootingContext& cx) | ||||
: ptr(js::GCMethods<T>::initial()) | : ptr(GCPolicy<T>::initial()) | ||||
{ | { | ||||
registerWithRootLists(js::RootListsForRootingContext(cx)); | registerWithRootLists(rootLists(cx)); | ||||
} | } | ||||
template <typename RootingContext, typename U> | template <typename RootingContext, typename U> | ||||
PersistentRooted(const RootingContext& cx, U&& initial) | PersistentRooted(const RootingContext& cx, U&& initial) | ||||
: ptr(mozilla::Forward<U>(initial)) | : ptr(mozilla::Forward<U>(initial)) | ||||
{ | { | ||||
registerWithRootLists(js::RootListsForRootingContext(cx)); | registerWithRootLists(rootLists(cx)); | ||||
} | } | ||||
PersistentRooted(const PersistentRooted& rhs) | PersistentRooted(const PersistentRooted& rhs) | ||||
: mozilla::LinkedListElement<PersistentRooted<T>>(), | : mozilla::LinkedListElement<PersistentRooted<T>>(), | ||||
ptr(rhs.ptr) | ptr(rhs.ptr) | ||||
{ | { | ||||
/* | /* | ||||
* Copy construction takes advantage of the fact that the original | * Copy construction takes advantage of the fact that the original | ||||
* is already inserted, and simply adds itself to whatever list the | * is already inserted, and simply adds itself to whatever list the | ||||
* original was on - no JSRuntime pointer needed. | * original was on - no JSRuntime pointer needed. | ||||
* | * | ||||
* This requires mutating rhs's links, but those should be 'mutable' | * This requires mutating rhs's links, but those should be 'mutable' | ||||
* anyway. C++ doesn't let us declare mutable base classes. | * anyway. C++ doesn't let us declare mutable base classes. | ||||
*/ | */ | ||||
const_cast<PersistentRooted&>(rhs).setNext(this); | const_cast<PersistentRooted&>(rhs).setNext(this); | ||||
} | } | ||||
bool initialized() { | bool initialized() { | ||||
return ListBase::isInList(); | return ListBase::isInList(); | ||||
} | } | ||||
template <typename RootingContext> | template <typename RootingContext> | ||||
void init(const RootingContext& cx) { | void init(const RootingContext& cx) { | ||||
init(cx, js::GCMethods<T>::initial()); | init(cx, GCPolicy<T>::initial()); | ||||
} | } | ||||
template <typename RootingContext, typename U> | template <typename RootingContext, typename U> | ||||
void init(const RootingContext& cx, U&& initial) { | void init(const RootingContext& cx, U&& initial) { | ||||
ptr = mozilla::Forward<U>(initial); | ptr = mozilla::Forward<U>(initial); | ||||
registerWithRootLists(js::RootListsForRootingContext(cx)); | registerWithRootLists(rootLists(cx)); | ||||
} | } | ||||
void reset() { | void reset() { | ||||
if (initialized()) { | if (initialized()) { | ||||
set(js::GCMethods<T>::initial()); | set(GCPolicy<T>::initial()); | ||||
ListBase::remove(); | ListBase::remove(); | ||||
} | } | ||||
} | } | ||||
DECLARE_POINTER_COMPARISON_OPS(T); | DECLARE_POINTER_COMPARISON_OPS(T); | ||||
DECLARE_POINTER_CONSTREF_OPS(T); | DECLARE_POINTER_CONSTREF_OPS(T); | ||||
DECLARE_POINTER_ASSIGN_OPS(PersistentRooted, T); | DECLARE_POINTER_ASSIGN_OPS(PersistentRooted, T); | ||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); | DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr); | ||||
// These are the same as DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS, except | // These are the same as DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS, except | ||||
// they check that |this| is initialized in case the caller later stores | // they check that |this| is initialized in case the caller later stores | ||||
// something in |ptr|. | // something in |ptr|. | ||||
T* address() { | T* address() { | ||||
MOZ_ASSERT(initialized()); | MOZ_ASSERT(initialized()); | ||||
return &ptr; | return &ptr; | ||||
} | } | ||||
T& get() { | T& get() { | ||||
MOZ_ASSERT(initialized()); | MOZ_ASSERT(initialized()); | ||||
return ptr; | return ptr; | ||||
} | } | ||||
private: | private: | ||||
void set(T value) { | template <typename U> | ||||
void set(U&& value) { | |||||
MOZ_ASSERT(initialized()); | MOZ_ASSERT(initialized()); | ||||
ptr = value; | ptr = mozilla::Forward<U>(value); | ||||
} | } | ||||
// See the comment above Rooted::ptr. | // See the comment above Rooted::ptr. | ||||
using MaybeWrapped = typename mozilla::Conditional< | using MaybeWrapped = typename mozilla::Conditional< | ||||
mozilla::IsBaseOf<Traceable, T>::value, | MapTypeToRootKind<T>::kind == JS::RootKind::Traceable, | ||||
js::DispatchWrapper<T>, | js::DispatchWrapper<T>, | ||||
T>::Type; | T>::Type; | ||||
MaybeWrapped ptr; | MaybeWrapped ptr; | ||||
}; | } JS_HAZ_ROOTED; | ||||
class JS_PUBLIC_API(ObjectPtr) | class JS_PUBLIC_API(ObjectPtr) | ||||
{ | { | ||||
Heap<JSObject*> value; | Heap<JSObject*> value; | ||||
public: | public: | ||||
ObjectPtr() : value(nullptr) {} | ObjectPtr() : value(nullptr) {} | ||||
explicit ObjectPtr(JSObject* obj) : value(obj) {} | explicit ObjectPtr(JSObject* obj) : value(obj) {} | ||||
ObjectPtr(const ObjectPtr& other) : value(other.value) {} | |||||
ObjectPtr(ObjectPtr&& other) | |||||
: value(other.value) | |||||
{ | |||||
other.value = nullptr; | |||||
} | |||||
/* Always call finalize before the destructor. */ | /* Always call finalize before the destructor. */ | ||||
~ObjectPtr() { MOZ_ASSERT(!value); } | ~ObjectPtr() { MOZ_ASSERT(!value); } | ||||
void finalize(JSRuntime* rt) { | void finalize(JSRuntime* rt); | ||||
if (IsIncrementalBarrierNeeded(rt)) | void finalize(JSContext* cx); | ||||
IncrementalObjectBarrier(value); | |||||
value = nullptr; | |||||
} | |||||
void init(JSObject* obj) { value = obj; } | void init(JSObject* obj) { value = obj; } | ||||
JSObject* get() const { return value; } | JSObject* get() const { return value; } | ||||
JSObject* unbarrieredGet() const { return value.unbarrieredGet(); } | |||||
void writeBarrierPre(JSRuntime* rt) { | void writeBarrierPre(JSContext* cx) { | ||||
IncrementalObjectBarrier(value); | IncrementalObjectBarrier(value); | ||||
} | } | ||||
void updateWeakPointerAfterGC(); | void updateWeakPointerAfterGC(); | ||||
ObjectPtr& operator=(JSObject* obj) { | ObjectPtr& operator=(JSObject* obj) { | ||||
IncrementalObjectBarrier(value); | IncrementalObjectBarrier(value); | ||||
value = obj; | value = obj; | ||||
return *this; | return *this; | ||||
} | } | ||||
void trace(JSTracer* trc, const char* name); | void trace(JSTracer* trc, const char* name); | ||||
JSObject& operator*() const { return *value; } | JSObject& operator*() const { return *value; } | ||||
JSObject* operator->() const { return value; } | JSObject* operator->() const { return value; } | ||||
operator JSObject*() const { return value; } | operator JSObject*() const { return value; } | ||||
explicit operator bool() const { return value.unbarrieredGet(); } | |||||
explicit operator bool() { return value.unbarrieredGet(); } | |||||
}; | }; | ||||
} /* namespace JS */ | } /* namespace JS */ | ||||
namespace js { | namespace js { | ||||
template <typename Outer, typename T, typename D> | |||||
class UniquePtrOperations | |||||
{ | |||||
const UniquePtr<T, D>& uniquePtr() const { return static_cast<const Outer*>(this)->get(); } | |||||
public: | |||||
explicit operator bool() const { return !!uniquePtr(); } | |||||
T* get() const { return uniquePtr().get(); } | |||||
T* operator->() const { return get(); } | |||||
T& operator*() const { return *uniquePtr(); } | |||||
}; | |||||
template <typename Outer, typename T, typename D> | |||||
class MutableUniquePtrOperations : public UniquePtrOperations<Outer, T, D> | |||||
{ | |||||
UniquePtr<T, D>& uniquePtr() { return static_cast<Outer*>(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 { | namespace gc { | ||||
template <typename T, typename TraceCallbacks> | template <typename T, typename TraceCallbacks> | ||||
void | void | ||||
CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks, const char* aName, void* aClosure) | CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks, const char* aName, void* aClosure) | ||||
{ | { | ||||
static_assert(sizeof(T) == sizeof(JS::Heap<T>), "T and Heap<T> must be compatible."); | static_assert(sizeof(T) == sizeof(JS::Heap<T>), "T and Heap<T> must be compatible."); | ||||
MOZ_ASSERT(v); | MOZ_ASSERT(v); | ||||
mozilla::DebugOnly<Cell*> cell = GCMethods<T>::asGCThingOrNull(*v); | mozilla::DebugOnly<Cell*> cell = BarrierMethods<T>::asGCThingOrNull(*v); | ||||
MOZ_ASSERT(cell); | MOZ_ASSERT(cell); | ||||
MOZ_ASSERT(!IsInsideNursery(cell)); | MOZ_ASSERT(!IsInsideNursery(cell)); | ||||
JS::Heap<T>* asHeapT = reinterpret_cast<JS::Heap<T>*>(v); | JS::Heap<T>* asHeapT = reinterpret_cast<JS::Heap<T>*>(v); | ||||
aCallbacks.Trace(asHeapT, aName, aClosure); | aCallbacks.Trace(asHeapT, aName, aClosure); | ||||
} | } | ||||
} /* namespace gc */ | } /* namespace gc */ | ||||
} /* namespace js */ | } /* namespace js */ | ||||
Show All 28 Lines |
Wildfire Games · Phabricator