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:
Ted Campbell 2018-08-02 11:32:16 -07:00
Родитель c8528d6865
Коммит 3d03b347e6
13 изменённых файлов: 215 добавлений и 167 удалений

Просмотреть файл

@ -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);