зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1479900 - Part 2: Refactor GC relocation to use a reserved flag. r=sfink
This refactors gc::Cell derived types to start with a uintptr_t-sized field with the low bits reserved for the GC and uses these bits for relocation mechanism. - JSString now stores flags in a uintptr_t. On 32-bit platforms, a second field is used to hold length. - Redefine JSString flag bit positions to avoid cell reserved bits. - Forwarded Cells are now indicated by a reserved flag instead of a magic invalid-pointer-like value. - gc::RelocationOverlay now extends gc::Cell - Update js::Symbol, js::Scope and js::BigInt fields to be compatible. MozReview-Commit-ID: Cs5OavbHmqK
This commit is contained in:
Родитель
c8528d6865
Коммит
3d03b347e6
|
@ -26,12 +26,6 @@ namespace gc {
|
|||
|
||||
struct Cell;
|
||||
|
||||
/*
|
||||
* The low bit is set so this should never equal a normal pointer, and the high
|
||||
* bit is set so this should never equal the upper 32 bits of a 64-bit pointer.
|
||||
*/
|
||||
const uint32_t Relocated = uintptr_t(0xbad0bad1);
|
||||
|
||||
const size_t ArenaShift = 12;
|
||||
const size_t ArenaSize = size_t(1) << ArenaShift;
|
||||
const size_t ArenaMask = ArenaSize - 1;
|
||||
|
@ -205,17 +199,20 @@ struct Zone
|
|||
|
||||
struct String
|
||||
{
|
||||
static const uint32_t NON_ATOM_BIT = JS_BIT(0);
|
||||
static const uint32_t LINEAR_BIT = JS_BIT(1);
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
|
||||
static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
|
||||
static const uint32_t EXTERNAL_FLAGS = LINEAR_BIT | NON_ATOM_BIT | JS_BIT(5);
|
||||
static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
|
||||
static const uint32_t PERMANENT_ATOM_MASK = NON_ATOM_BIT | JS_BIT(5);
|
||||
static const uint32_t PERMANENT_ATOM_FLAGS = JS_BIT(5);
|
||||
static const uint32_t NON_ATOM_BIT = JS_BIT(1);
|
||||
static const uint32_t LINEAR_BIT = JS_BIT(4);
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(6);
|
||||
static const uint32_t LATIN1_CHARS_BIT = JS_BIT(9);
|
||||
static const uint32_t EXTERNAL_FLAGS = LINEAR_BIT | NON_ATOM_BIT | JS_BIT(8);
|
||||
static const uint32_t TYPE_FLAGS_MASK = JS_BITMASK(9) - JS_BIT(2) - JS_BIT(0);
|
||||
static const uint32_t PERMANENT_ATOM_MASK = NON_ATOM_BIT | JS_BIT(8);
|
||||
static const uint32_t PERMANENT_ATOM_FLAGS = JS_BIT(8);
|
||||
|
||||
uint32_t flags_;
|
||||
uintptr_t flags_;
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
uint32_t length_;
|
||||
#endif
|
||||
|
||||
union {
|
||||
const JS::Latin1Char* nonInlineCharsLatin1;
|
||||
const char16_t* nonInlineCharsTwoByte;
|
||||
|
@ -225,15 +222,14 @@ struct String
|
|||
const JSStringFinalizer* externalFinalizer;
|
||||
|
||||
inline uint32_t flags() const {
|
||||
return flags_;
|
||||
return uint32_t(flags_);
|
||||
}
|
||||
inline uint32_t length() const {
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
return length_;
|
||||
}
|
||||
|
||||
static bool nurseryCellIsString(const js::gc::Cell* cell) {
|
||||
MOZ_ASSERT(IsInsideNursery(cell));
|
||||
return reinterpret_cast<const String*>(cell)->flags() & NON_ATOM_BIT;
|
||||
#else
|
||||
return uint32_t(flags_ >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool isPermanentAtom(const js::gc::Cell* cell) {
|
||||
|
@ -243,6 +239,7 @@ struct String
|
|||
};
|
||||
|
||||
struct Symbol {
|
||||
uintptr_t reserved_;
|
||||
uint32_t code_;
|
||||
static const uint32_t WellKnownAPILimit = 0x80000000;
|
||||
|
||||
|
|
|
@ -48,10 +48,35 @@ struct Chunk;
|
|||
class StoreBuffer;
|
||||
class TenuredCell;
|
||||
|
||||
// A GC cell is the base class for all GC things.
|
||||
// [SMDOC] GC Cell
|
||||
//
|
||||
// A GC cell is the base class for all GC things. All types allocated on the GC
|
||||
// heap extend either gc::Cell or gc::TenuredCell. If a type is always tenured,
|
||||
// prefer the TenuredCell class as base.
|
||||
//
|
||||
// The first word (a pointer or uintptr_t) of each Cell must reserve the low
|
||||
// Cell::ReservedBits bits for GC purposes. The remaining bits are available to
|
||||
// sub-classes and typically store a pointer to another gc::Cell.
|
||||
//
|
||||
// During moving GC operation a Cell may be marked as forwarded. This indicates
|
||||
// that a gc::RelocationOverlay is currently stored in the Cell's memory and
|
||||
// should be used to find the new location of the Cell.
|
||||
struct alignas(gc::CellAlignBytes) Cell
|
||||
{
|
||||
public:
|
||||
// The low bits of the first word of each Cell are reserved for GC flags.
|
||||
static constexpr int ReservedBits = 2;
|
||||
static constexpr uintptr_t RESERVED_MASK = JS_BITMASK(ReservedBits);
|
||||
|
||||
// Indicates if the cell is currently a RelocationOverlay
|
||||
static constexpr uintptr_t FORWARD_BIT = JS_BIT(0);
|
||||
|
||||
// When a Cell is in the nursery, this will indicate if it is a JSString (1)
|
||||
// or JSObject (0). When not in nursery, this bit is still reserved for
|
||||
// JSString to use as JSString::NON_ATOM bit. This may be removed by Bug
|
||||
// 1376646.
|
||||
static constexpr uintptr_t JSSTRING_BIT = JS_BIT(1);
|
||||
|
||||
MOZ_ALWAYS_INLINE bool isTenured() const { return !IsInsideNursery(this); }
|
||||
MOZ_ALWAYS_INLINE const TenuredCell& asTenured() const;
|
||||
MOZ_ALWAYS_INLINE TenuredCell& asTenured();
|
||||
|
@ -77,6 +102,17 @@ struct alignas(gc::CellAlignBytes) Cell
|
|||
|
||||
static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone);
|
||||
|
||||
inline bool isForwarded() const {
|
||||
uintptr_t firstWord = *reinterpret_cast<const uintptr_t*>(this);
|
||||
return firstWord & FORWARD_BIT;
|
||||
}
|
||||
|
||||
inline bool nurseryCellIsString() const {
|
||||
MOZ_ASSERT(!isTenured());
|
||||
uintptr_t firstWord = *reinterpret_cast<const uintptr_t*>(this);
|
||||
return firstWord & JSSTRING_BIT;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool is() const {
|
||||
return getTraceKind() == JS::MapTypeToTraceKind<T>::kind;
|
||||
|
@ -255,7 +291,7 @@ Cell::getTraceKind() const
|
|||
{
|
||||
if (isTenured())
|
||||
return asTenured().getTraceKind();
|
||||
if (JS::shadow::String::nurseryCellIsString(this))
|
||||
if (nurseryCellIsString())
|
||||
return JS::TraceKind::String;
|
||||
return JS::TraceKind::Object;
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@
|
|||
#include "jit/IonCode.h"
|
||||
#include "jit/JitcodeMap.h"
|
||||
#include "jit/JitRealm.h"
|
||||
#include "jit/MacroAssembler.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "proxy/DeadObjectProxy.h"
|
||||
#include "util/Windows.h"
|
||||
|
@ -384,6 +385,17 @@ const AllocKind gc::slotsToThingKind[] = {
|
|||
/* 16 */ AllocKind::OBJECT16
|
||||
};
|
||||
|
||||
// Check that reserved bits of a Cell are compatible with our typical allocators
|
||||
// since most derived classes will store a pointer in the first word.
|
||||
static_assert(js::detail::LIFO_ALLOC_ALIGN > JS_BITMASK(Cell::ReservedBits),
|
||||
"Cell::ReservedBits should support LifoAlloc");
|
||||
static_assert(CellAlignBytes > JS_BITMASK(Cell::ReservedBits),
|
||||
"Cell::ReservedBits should support gc::Cell");
|
||||
static_assert(sizeof(uintptr_t) > JS_BITMASK(Cell::ReservedBits),
|
||||
"Cell::ReservedBits should support small malloc / aligned globals");
|
||||
static_assert(js::jit::CodeAlignment > JS_BITMASK(Cell::ReservedBits),
|
||||
"Cell::ReservedBits should support JIT code");
|
||||
|
||||
static_assert(mozilla::ArrayLength(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT,
|
||||
"We have defined a slot count for each kind.");
|
||||
|
||||
|
@ -2344,7 +2356,7 @@ RelocateArena(Arena* arena, SliceBudget& sliceBudget)
|
|||
#ifdef DEBUG
|
||||
for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
|
||||
TenuredCell* src = i.getCell();
|
||||
MOZ_ASSERT(RelocationOverlay::isCellForwarded(src));
|
||||
MOZ_ASSERT(src->isForwarded());
|
||||
TenuredCell* dest = Forwarded(src);
|
||||
MOZ_ASSERT(src->isMarkedBlack() == dest->isMarkedBlack());
|
||||
MOZ_ASSERT(src->isMarkedGray() == dest->isMarkedGray());
|
||||
|
@ -8472,8 +8484,8 @@ js::gc::AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind)
|
|||
MOZ_ASSERT(IsCellPointerValid(cell));
|
||||
|
||||
if (IsInsideNursery(cell)) {
|
||||
MOZ_ASSERT(kind == (JSString::nurseryCellIsString(cell) ? JS::TraceKind::String
|
||||
: JS::TraceKind::Object));
|
||||
MOZ_ASSERT(kind == (cell->nurseryCellIsString() ? JS::TraceKind::String
|
||||
: JS::TraceKind::Object));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ struct MovingTracer : JS::CallbackTracer
|
|||
void onScopeEdge(Scope** basep) override;
|
||||
void onRegExpSharedEdge(RegExpShared** sharedp) override;
|
||||
void onChild(const JS::GCCellPtr& thing) override {
|
||||
MOZ_ASSERT(!RelocationOverlay::isCellForwarded(thing.asCell()));
|
||||
MOZ_ASSERT(!thing.asCell()->isForwarded());
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -40,13 +40,12 @@ template <typename T>
|
|||
inline bool
|
||||
IsForwarded(const T* t)
|
||||
{
|
||||
const RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
|
||||
if (!MightBeForwarded<T>::value) {
|
||||
MOZ_ASSERT(!overlay->isForwarded());
|
||||
MOZ_ASSERT(!t->isForwarded());
|
||||
return false;
|
||||
}
|
||||
|
||||
return overlay->isForwarded();
|
||||
return t->isForwarded();
|
||||
}
|
||||
|
||||
struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
|
||||
|
@ -93,16 +92,14 @@ inline void
|
|||
RelocationOverlay::forwardTo(Cell* cell)
|
||||
{
|
||||
MOZ_ASSERT(!isForwarded());
|
||||
// The location of magic_ is important because it must never be valid to see
|
||||
// the value Relocated there in a GC thing that has not been moved.
|
||||
static_assert(offsetof(RelocationOverlay, magic_) == offsetof(JSObject, group_) + sizeof(uint32_t),
|
||||
"RelocationOverlay::magic_ is in the wrong location");
|
||||
static_assert(offsetof(RelocationOverlay, magic_) == offsetof(js::Shape, base_) + sizeof(uint32_t),
|
||||
"RelocationOverlay::magic_ is in the wrong location");
|
||||
static_assert(offsetof(RelocationOverlay, magic_) == offsetof(JSString, d.u1.length_),
|
||||
"RelocationOverlay::magic_ is in the wrong location");
|
||||
magic_ = Relocated;
|
||||
newLocation_ = cell;
|
||||
|
||||
// Preserve old flags because nursery may check them before checking
|
||||
// if this is a forwarded Cell.
|
||||
//
|
||||
// This is pretty terrible and we should find a better way to implement
|
||||
// Cell::getTrackKind() that doesn't rely on this behavior.
|
||||
uintptr_t gcFlags = dataWithTag_ & Cell::RESERVED_MASK;
|
||||
dataWithTag_ = uintptr_t(cell) | gcFlags | Cell::FORWARD_BIT;
|
||||
}
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
|
@ -111,7 +108,7 @@ template <typename T>
|
|||
inline bool
|
||||
IsGCThingValidAfterMovingGC(T* t)
|
||||
{
|
||||
return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
|
||||
return !IsInsideNursery(t) && !t->isForwarded();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -2862,7 +2862,7 @@ js::gc::StoreBuffer::CellPtrEdge::trace(TenuringTracer& mover) const
|
|||
if (!IsInsideNursery(*edge))
|
||||
return;
|
||||
|
||||
if (JSString::nurseryCellIsString(*edge))
|
||||
if ((*edge)->nurseryCellIsString())
|
||||
mover.traverse(reinterpret_cast<JSString**>(edge));
|
||||
else
|
||||
mover.traverse(reinterpret_cast<JSObject**>(edge));
|
||||
|
|
|
@ -28,11 +28,11 @@ js::Nursery::isInside(const SharedMem<T>& p) const
|
|||
MOZ_ALWAYS_INLINE /* static */ bool
|
||||
js::Nursery::getForwardedPointer(js::gc::Cell** ref)
|
||||
{
|
||||
MOZ_ASSERT(ref);
|
||||
MOZ_ASSERT(IsInsideNursery(*ref));
|
||||
const gc::RelocationOverlay* overlay = reinterpret_cast<const gc::RelocationOverlay*>(*ref);
|
||||
if (!overlay->isForwarded())
|
||||
js::gc::Cell* cell = (*ref);
|
||||
MOZ_ASSERT(IsInsideNursery(cell));
|
||||
if (!cell->isForwarded())
|
||||
return false;
|
||||
const gc::RelocationOverlay* overlay = gc::RelocationOverlay::fromCell(cell);
|
||||
*ref = overlay->forwardingAddress();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gc/Cell.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "vm/JSObject.h"
|
||||
#include "vm/Shape.h"
|
||||
|
@ -23,49 +24,36 @@
|
|||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
struct Cell;
|
||||
|
||||
/*
|
||||
* This structure overlays a Cell that has been moved and provides a way to find
|
||||
* its new location. It's used during generational and compacting GC.
|
||||
*/
|
||||
class RelocationOverlay
|
||||
class RelocationOverlay : public Cell
|
||||
{
|
||||
/* See comment in js/public/HeapAPI.h. */
|
||||
static const uint32_t Relocated = js::gc::Relocated;
|
||||
|
||||
/*
|
||||
* Keep the low 32 bits untouched. Use them to distinguish strings from
|
||||
* objects in the nursery.
|
||||
*/
|
||||
uint32_t preserve_;
|
||||
|
||||
/* Set to Relocated when moved. */
|
||||
uint32_t magic_;
|
||||
|
||||
/* The location |this| was moved to. */
|
||||
Cell* newLocation_;
|
||||
// First word of a Cell has additional requirements from GC. The GC flags
|
||||
// determine if a Cell is a normal entry or is a RelocationOverlay.
|
||||
// 3 0
|
||||
// -------------------------
|
||||
// | NewLocation | GCFlags |
|
||||
// -------------------------
|
||||
uintptr_t dataWithTag_;
|
||||
|
||||
/* A list entry to track all relocated things. */
|
||||
RelocationOverlay* next_;
|
||||
|
||||
public:
|
||||
static const RelocationOverlay* fromCell(const Cell* cell) {
|
||||
return reinterpret_cast<const RelocationOverlay*>(cell);
|
||||
return static_cast<const RelocationOverlay*>(cell);
|
||||
}
|
||||
|
||||
static RelocationOverlay* fromCell(Cell* cell) {
|
||||
return reinterpret_cast<RelocationOverlay*>(cell);
|
||||
}
|
||||
|
||||
bool isForwarded() const {
|
||||
(void) preserve_; // Suppress warning
|
||||
return magic_ == Relocated;
|
||||
return static_cast<RelocationOverlay*>(cell);
|
||||
}
|
||||
|
||||
Cell* forwardingAddress() const {
|
||||
MOZ_ASSERT(isForwarded());
|
||||
return newLocation_;
|
||||
uintptr_t newLocation = dataWithTag_ & ~Cell::RESERVED_MASK;
|
||||
return reinterpret_cast<Cell*>(newLocation);
|
||||
}
|
||||
|
||||
void forwardTo(Cell* cell);
|
||||
|
@ -79,10 +67,6 @@ class RelocationOverlay
|
|||
MOZ_ASSERT(isForwarded());
|
||||
return next_;
|
||||
}
|
||||
|
||||
static bool isCellForwarded(const Cell* cell) {
|
||||
return fromCell(cell)->isForwarded();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gc
|
||||
|
|
|
@ -643,8 +643,7 @@ CheckHeapTracer::checkCell(Cell* cell)
|
|||
// Moving
|
||||
if (!IsValidGCThingPointer(cell) ||
|
||||
((gcType == GCType::Moving) && !IsGCThingValidAfterMovingGC(cell)) ||
|
||||
((gcType == GCType::NonMoving) &&
|
||||
RelocationOverlay::isCellForwarded(cell)))
|
||||
((gcType == GCType::NonMoving) && cell->isForwarded()))
|
||||
{
|
||||
failures++;
|
||||
fprintf(stderr, "Bad pointer %p\n", cell);
|
||||
|
|
|
@ -38,13 +38,16 @@ class BigInt final : public js::gc::TenuredCell
|
|||
friend bool js::StringToBigIntImpl(const mozilla::Range<const CharT>& chars,
|
||||
uint8_t radix, Handle<BigInt*> res);
|
||||
|
||||
protected:
|
||||
// Reserved word for Cell GC invariants. This also ensures minimum
|
||||
// structure size.
|
||||
uintptr_t reserved_;
|
||||
|
||||
private:
|
||||
// The minimum allocation size is currently 16 bytes (see
|
||||
// SortedArenaList in gc/ArenaList.h).
|
||||
union {
|
||||
mpz_t num_;
|
||||
uint8_t unused_[js::gc::MinCellSize];
|
||||
};
|
||||
mpz_t num_;
|
||||
|
||||
protected:
|
||||
BigInt() : reserved_(0) { }
|
||||
|
||||
public:
|
||||
// Allocate and initialize a BigInt value
|
||||
|
|
|
@ -305,19 +305,12 @@ class Scope : public js::gc::TenuredCell
|
|||
{
|
||||
friend class GCMarker;
|
||||
|
||||
// The kind determines data_.
|
||||
//
|
||||
// The memory here must be fully initialized, since otherwise the magic_
|
||||
// value for gc::RelocationOverlay will land in the padding and may be
|
||||
// stale.
|
||||
union {
|
||||
ScopeKind kind_;
|
||||
uintptr_t paddedKind_;
|
||||
};
|
||||
|
||||
// The enclosing scope or nullptr.
|
||||
GCPtrScope enclosing_;
|
||||
|
||||
// The kind determines data_.
|
||||
ScopeKind kind_;
|
||||
|
||||
// If there are any aliased bindings, the shape for the
|
||||
// EnvironmentObject. Otherwise nullptr.
|
||||
GCPtrShape environmentShape_;
|
||||
|
@ -327,12 +320,9 @@ class Scope : public js::gc::TenuredCell
|
|||
|
||||
Scope(ScopeKind kind, Scope* enclosing, Shape* environmentShape)
|
||||
: enclosing_(enclosing),
|
||||
kind_(kind),
|
||||
environmentShape_(environmentShape),
|
||||
data_(nullptr)
|
||||
{
|
||||
paddedKind_ = 0;
|
||||
kind_ = kind;
|
||||
}
|
||||
data_(nullptr) { }
|
||||
|
||||
static Scope* create(JSContext* cx, ScopeKind kind, HandleScope enclosing,
|
||||
HandleShape envShape);
|
||||
|
|
|
@ -165,13 +165,21 @@ class JSString : public js::gc::Cell
|
|||
/* Fields only apply to string types commented on the right. */
|
||||
struct Data
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
uint32_t flags_; /* JSString */
|
||||
uint32_t length_; /* JSString */
|
||||
};
|
||||
uintptr_t flattenData_; /* JSRope (temporary while flattening) */
|
||||
} u1;
|
||||
// First word of a Cell has additional requirements from GC and normally
|
||||
// would store a pointer. If a single word isn't large enough, the length
|
||||
// is stored separately.
|
||||
// 32 16 0
|
||||
// --------------------------
|
||||
// | Length | Index | Flags |
|
||||
// --------------------------
|
||||
//
|
||||
// NOTE: This is also used for temporary storage while linearizing a Rope.
|
||||
uintptr_t flags_; /* JSString */
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
// Additional storage for length if |flags_| is too small to fit both.
|
||||
uint32_t length_; /* JSString */
|
||||
#endif
|
||||
union {
|
||||
union {
|
||||
/* JS(Fat)InlineString */
|
||||
|
@ -198,9 +206,11 @@ class JSString : public js::gc::Cell
|
|||
/* Flags exposed only for jits */
|
||||
|
||||
/*
|
||||
* The Flags Word
|
||||
* Flag Encoding
|
||||
*
|
||||
* The flags word stores both the string's type and its character encoding.
|
||||
* The first word of a JSString stores flags, index, and (on some
|
||||
* platforms) the length. The flags store both the string's type and its
|
||||
* character encoding.
|
||||
*
|
||||
* If LATIN1_CHARS_BIT is set, the string's characters are stored as Latin1
|
||||
* instead of TwoByte. This flag can also be set for ropes, if both the
|
||||
|
@ -252,31 +262,30 @@ class JSString : public js::gc::Cell
|
|||
* to be null-terminated. In such cases, the string must keep marking its base since
|
||||
* there may be any number of *other* JSDependentStrings transitively depending on it.
|
||||
*
|
||||
* The atom bit (NON_ATOM_BIT) is inverted so that objects and strings can
|
||||
* be differentiated in the nursery: atoms are never in the nursery, so
|
||||
* this bit is always 1 for a nursery string. For an object on a
|
||||
* little-endian architecture, this is the low-order bit of the ObjectGroup
|
||||
* pointer in a JSObject, which will always be zero. A 64-bit big-endian
|
||||
* architecture will need to do something else (the ObjectGroup* is in the
|
||||
* same place as a string's struct { uint32_t flags; uint32_t length; }).
|
||||
* The atom bit (NON_ATOM_BIT) is inverted and stored in a Cell
|
||||
* ReservedBit. Atoms are never stored in nursery, so the nursery can use
|
||||
* this bit to distinguish between JSString (1) and JSObject (0).
|
||||
*
|
||||
* If the INDEX_VALUE_BIT is set the upper 16 bits of the flag word hold the integer
|
||||
* index.
|
||||
* If the INDEX_VALUE_BIT is set, flags will also hold an integer index.
|
||||
*/
|
||||
|
||||
static const uint32_t NON_ATOM_BIT = JS_BIT(0);
|
||||
static const uint32_t LINEAR_BIT = JS_BIT(1);
|
||||
static const uint32_t HAS_BASE_BIT = JS_BIT(2);
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
|
||||
// The low bits of flag word are reserved by GC.
|
||||
static_assert(js::gc::Cell::ReservedBits <= 3,
|
||||
"JSString::flags must reserve enough bits for Cell");
|
||||
|
||||
static const uint32_t NON_ATOM_BIT = js::gc::Cell::JSSTRING_BIT;
|
||||
static const uint32_t LINEAR_BIT = JS_BIT(4);
|
||||
static const uint32_t HAS_BASE_BIT = JS_BIT(5);
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(6);
|
||||
|
||||
static const uint32_t DEPENDENT_FLAGS = NON_ATOM_BIT | LINEAR_BIT | HAS_BASE_BIT;
|
||||
static const uint32_t UNDEPENDED_FLAGS = NON_ATOM_BIT | LINEAR_BIT | HAS_BASE_BIT | JS_BIT(4);
|
||||
static const uint32_t EXTENSIBLE_FLAGS = NON_ATOM_BIT | LINEAR_BIT | JS_BIT(4);
|
||||
static const uint32_t EXTERNAL_FLAGS = NON_ATOM_BIT | LINEAR_BIT | JS_BIT(5);
|
||||
static const uint32_t UNDEPENDED_FLAGS = NON_ATOM_BIT | LINEAR_BIT | HAS_BASE_BIT | JS_BIT(7);
|
||||
static const uint32_t EXTENSIBLE_FLAGS = NON_ATOM_BIT | LINEAR_BIT | JS_BIT(7);
|
||||
static const uint32_t EXTERNAL_FLAGS = NON_ATOM_BIT | LINEAR_BIT | JS_BIT(8);
|
||||
|
||||
static const uint32_t FAT_INLINE_MASK = INLINE_CHARS_BIT | JS_BIT(4);
|
||||
static const uint32_t PERMANENT_ATOM_MASK = NON_ATOM_BIT | JS_BIT(5);
|
||||
static const uint32_t PERMANENT_ATOM_FLAGS = JS_BIT(5);
|
||||
static const uint32_t FAT_INLINE_MASK = INLINE_CHARS_BIT | JS_BIT(7);
|
||||
static const uint32_t PERMANENT_ATOM_MASK = NON_ATOM_BIT | JS_BIT(8);
|
||||
static const uint32_t PERMANENT_ATOM_FLAGS = JS_BIT(8);
|
||||
|
||||
/* Initial flags for thin inline and fat inline strings. */
|
||||
static const uint32_t INIT_THIN_INLINE_FLAGS = NON_ATOM_BIT | LINEAR_BIT | INLINE_CHARS_BIT;
|
||||
|
@ -284,14 +293,14 @@ class JSString : public js::gc::Cell
|
|||
static const uint32_t INIT_ROPE_FLAGS = NON_ATOM_BIT;
|
||||
static const uint32_t INIT_FLAT_FLAGS = NON_ATOM_BIT | LINEAR_BIT;
|
||||
|
||||
static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
|
||||
static const uint32_t TYPE_FLAGS_MASK = JS_BITMASK(9) - JS_BITMASK(3) + js::gc::Cell::JSSTRING_BIT;
|
||||
|
||||
static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
|
||||
static const uint32_t LATIN1_CHARS_BIT = JS_BIT(9);
|
||||
|
||||
static const uint32_t INDEX_VALUE_BIT = JS_BIT(7);
|
||||
static const uint32_t INDEX_VALUE_BIT = JS_BIT(10);
|
||||
static const uint32_t INDEX_VALUE_SHIFT = 16;
|
||||
|
||||
static const uint32_t PINNED_ATOM_BIT = JS_BIT(8);
|
||||
static const uint32_t PINNED_ATOM_BIT = JS_BIT(11);
|
||||
|
||||
static const uint32_t MAX_LENGTH = js::MaxStringLength;
|
||||
|
||||
|
@ -317,10 +326,12 @@ class JSString : public js::gc::Cell
|
|||
|
||||
/* Ensure js::shadow::String has the same layout. */
|
||||
using JS::shadow::String;
|
||||
static_assert(offsetof(JSString, d.u1.length_) == offsetof(String, length_),
|
||||
"shadow::String length offset must match JSString");
|
||||
static_assert(offsetof(JSString, d.u1.flags_) == offsetof(String, flags_),
|
||||
static_assert(offsetof(JSString, d.flags_) == offsetof(String, flags_),
|
||||
"shadow::String flags offset must match JSString");
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
static_assert(offsetof(JSString, d.length_) == offsetof(String, length_),
|
||||
"shadow::String length offset must match JSString");
|
||||
#endif
|
||||
static_assert(offsetof(JSString, d.s.u2.nonInlineCharsLatin1) == offsetof(String, nonInlineCharsLatin1),
|
||||
"shadow::String nonInlineChars offset must match JSString");
|
||||
static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) == offsetof(String, nonInlineCharsTwoByte),
|
||||
|
@ -357,30 +368,38 @@ class JSString : public js::gc::Cell
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
uint32_t flags() const {
|
||||
return d.u1.flags_;
|
||||
return uint32_t(d.flags_);
|
||||
}
|
||||
|
||||
public:
|
||||
MOZ_ALWAYS_INLINE
|
||||
size_t length() const {
|
||||
return d.u1.length_;
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
return d.length_;
|
||||
#else
|
||||
return uint32_t(d.flags_ >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
MOZ_ALWAYS_INLINE
|
||||
void setFlagBit(uint32_t flags) {
|
||||
d.u1.flags_ |= flags;
|
||||
d.flags_ |= uintptr_t(flags);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void clearFlagBit(uint32_t flags) {
|
||||
d.u1.flags_ &= ~flags;
|
||||
d.flags_ &= ~uintptr_t(flags);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void setLengthAndFlags(uint32_t len, uint32_t flags) {
|
||||
d.u1.flags_ = flags;
|
||||
d.u1.length_ = len;
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
d.flags_ = flags;
|
||||
d.length_ = len;
|
||||
#else
|
||||
d.flags_ = uint64_t(len) << 32 | uint64_t(flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Flatten algorithm stores a temporary word by clobbering flags. This is
|
||||
|
@ -388,14 +407,14 @@ class JSString : public js::gc::Cell
|
|||
// (including by asserts) while this data is stored.
|
||||
MOZ_ALWAYS_INLINE
|
||||
void setFlattenData(uintptr_t data) {
|
||||
d.u1.flattenData_ = data;
|
||||
d.flags_ = data;
|
||||
}
|
||||
|
||||
// To get back the data, values to safely re-initialize clobbered flags
|
||||
// must be provided.
|
||||
MOZ_ALWAYS_INLINE
|
||||
uintptr_t unsetFlattenData(uint32_t len, uint32_t flags) {
|
||||
uintptr_t data = d.u1.flattenData_;
|
||||
uintptr_t data = d.flags_;
|
||||
setLengthAndFlags(len, flags);
|
||||
return data;
|
||||
}
|
||||
|
@ -544,14 +563,6 @@ class JSString : public js::gc::Cell
|
|||
return *(JSAtom*)this;
|
||||
}
|
||||
|
||||
// Used for distinguishing strings from objects in the nursery. The caller
|
||||
// must ensure that cell is in the nursery (and not forwarded).
|
||||
MOZ_ALWAYS_INLINE
|
||||
static bool nurseryCellIsString(js::gc::Cell* cell) {
|
||||
MOZ_ASSERT(!cell->isTenured());
|
||||
return !static_cast<JSString*>(cell)->isAtom();
|
||||
}
|
||||
|
||||
// Fills |array| with various strings that represent the different string
|
||||
// kinds and character encodings.
|
||||
static bool fillWithRepresentatives(JSContext* cx, js::HandleArrayObject array);
|
||||
|
@ -574,14 +585,30 @@ class JSString : public js::gc::Cell
|
|||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
/* Offsets for direct field from jit code. */
|
||||
|
||||
static size_t offsetOfLength() {
|
||||
return offsetof(JSString, d.u1.length_);
|
||||
// Offsets for direct field from jit code. A number of places directly
|
||||
// access 32-bit length and flags fields so do endian trickery here.
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
static constexpr size_t offsetOfFlags() {
|
||||
return offsetof(JSString, d.flags_);
|
||||
}
|
||||
static size_t offsetOfFlags() {
|
||||
return offsetof(JSString, d.u1.flags_);
|
||||
static constexpr size_t offsetOfLength() {
|
||||
return offsetof(JSString, d.length_);
|
||||
}
|
||||
#elif defined(MOZ_LITTLE_ENDIAN)
|
||||
static constexpr size_t offsetOfFlags() {
|
||||
return offsetof(JSString, d.flags_);
|
||||
}
|
||||
static constexpr size_t offsetOfLength() {
|
||||
return offsetof(JSString, d.flags_) + sizeof(uint32_t);
|
||||
}
|
||||
#else
|
||||
static constexpr size_t offsetOfFlags() {
|
||||
return offsetof(JSString, d.flags_) + sizeof(uint32_t);
|
||||
}
|
||||
static constexpr size_t offsetOfLength() {
|
||||
return offsetof(JSString, d.flags_);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler
|
||||
|
|
|
@ -32,6 +32,11 @@ namespace JS {
|
|||
|
||||
class Symbol : public js::gc::TenuredCell
|
||||
{
|
||||
protected:
|
||||
// Reserved word for Cell GC invariants. This also ensures minimum
|
||||
// structure size.
|
||||
uintptr_t reserved_;
|
||||
|
||||
private:
|
||||
SymbolCode code_;
|
||||
|
||||
|
@ -41,19 +46,8 @@ class Symbol : public js::gc::TenuredCell
|
|||
|
||||
JSAtom* description_;
|
||||
|
||||
// The minimum allocation size is sizeof(JSString): 16 bytes on 32-bit
|
||||
// architectures and 24 bytes on 64-bit. A size_t of padding makes Symbol
|
||||
// the minimum size on both.
|
||||
size_t unused_;
|
||||
|
||||
Symbol(SymbolCode code, js::HashNumber hash, JSAtom* desc)
|
||||
: code_(code), hash_(hash), description_(desc)
|
||||
{
|
||||
// Silence warnings about unused_ being... unused.
|
||||
(void)unused_;
|
||||
static_assert(uint32_t(SymbolCode::WellKnownAPILimit) == JS::shadow::Symbol::WellKnownAPILimit,
|
||||
"JS::shadow::Symbol::WellKnownAPILimit must match SymbolCode::WellKnownAPILimit");
|
||||
}
|
||||
: reserved_(0), code_(code), hash_(hash), description_(desc) { }
|
||||
|
||||
Symbol(const Symbol&) = delete;
|
||||
void operator=(const Symbol&) = delete;
|
||||
|
@ -61,6 +55,15 @@ class Symbol : public js::gc::TenuredCell
|
|||
static Symbol*
|
||||
newInternal(JSContext* cx, SymbolCode code, js::HashNumber hash, JSAtom* description);
|
||||
|
||||
static void staticAsserts() {
|
||||
static_assert(uint32_t(SymbolCode::WellKnownAPILimit) == JS::shadow::Symbol::WellKnownAPILimit,
|
||||
"JS::shadow::Symbol::WellKnownAPILimit must match SymbolCode::WellKnownAPILimit");
|
||||
static_assert(offsetof(Symbol, reserved_) == offsetof(JS::shadow::Symbol, reserved_),
|
||||
"JS::shadow::Symbol::reserved_ offset must match SymbolCode::reserved_");
|
||||
static_assert(offsetof(Symbol, code_) == offsetof(JS::shadow::Symbol, code_),
|
||||
"JS::shadow::Symbol::code_ offset must match SymbolCode::code_");
|
||||
}
|
||||
|
||||
public:
|
||||
static Symbol* new_(JSContext* cx, SymbolCode code, JSString* description);
|
||||
static Symbol* for_(JSContext* cx, js::HandleString description);
|
||||
|
|
Загрузка…
Ссылка в новой задаче