mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 07:18:39 +09:00
Bug 903519 - allocate JSStrings and string data in the Nursery
903519 part 2: Reparent JSString from TenuredCell to Cell. 903519 Part 3: Make js::Allocate cast strings to requested type. 903519 Part 4: Force non-atom strings to have their low flag bit set in order to distinguish them from JSObjects in the nursery. 903519 Part 5: Strings in the nursery: allocation. Mixed reset later.
This commit is contained in:
parent
44bd6d9e6b
commit
b9a2a715cd
18 changed files with 274 additions and 65 deletions
|
|
@ -226,7 +226,7 @@ js::Allocate(ExclusiveContext* cx)
|
|||
#define DECL_ALLOCATOR_INSTANCES(allocKind, traceKind, type, sizedType, bgFinal, nursery) \
|
||||
template type* js::Allocate<type, NoGC>(JSContext* cx);\
|
||||
template type* js::Allocate<type, CanGC>(JSContext* cx);
|
||||
FOR_EACH_NONOBJECT_ALLOCKIND(DECL_ALLOCATOR_INSTANCES)
|
||||
FOR_EACH_NONOBJECT_NONNURSERY_ALLOCKIND(DECL_ALLOCATOR_INSTANCES)
|
||||
#undef DECL_ALLOCATOR_INSTANCES
|
||||
|
||||
template <typename T, AllowGC allowGC>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include "gc/Heap.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
class JSFatInlineString;
|
||||
|
||||
namespace js {
|
||||
|
||||
struct Class;
|
||||
|
|
@ -18,19 +20,49 @@ struct Class;
|
|||
// fully initialize the thing before calling any function that can potentially
|
||||
// trigger GC. This will ensure that GC tracing never sees junk values stored
|
||||
// in the partially initialized thing.
|
||||
//
|
||||
// Note that JSObject allocation must use the longer signature below that
|
||||
// includes slot, heap, and finalizer information in support of various
|
||||
// object-specific optimizations.
|
||||
template <typename T, AllowGC allowGC = CanGC>
|
||||
T*
|
||||
Allocate(ExclusiveContext* cx);
|
||||
|
||||
// Note that JSObject allocation must use the longer signature below that
|
||||
// includes slot, heap, and finalizer information in support of various
|
||||
// object-specific optimizations.
|
||||
template <typename, AllowGC allowGC = CanGC>
|
||||
JSObject*
|
||||
Allocate(ExclusiveContext* cx, gc::AllocKind kind, size_t nDynamicSlots, gc::InitialHeap heap,
|
||||
const Class* clasp);
|
||||
|
||||
// Internal function used for nursery-allocatable strings.
|
||||
template <typename StringAllocT, AllowGC allowGC = CanGC>
|
||||
StringAllocT*
|
||||
AllocateString(JSContext* cx, gc::InitialHeap heap);
|
||||
|
||||
// Use for nursery-allocatable strings. Returns a value cast to the correct
|
||||
// type.
|
||||
template <typename StringT, AllowGC allowGC = CanGC>
|
||||
StringT*
|
||||
Allocate(JSContext* cx, gc::InitialHeap heap)
|
||||
{
|
||||
return static_cast<StringT*>(js::AllocateString<JSString, allowGC>(cx, heap));
|
||||
}
|
||||
|
||||
// Specialization for JSFatInlineString that must use a different allocation
|
||||
// type. Note that we have to explicitly specialize for both values of AllowGC
|
||||
// because partial function specialization is not allowed.
|
||||
template <>
|
||||
inline JSFatInlineString*
|
||||
Allocate<JSFatInlineString, CanGC>(JSContext* cx, gc::InitialHeap heap)
|
||||
{
|
||||
return static_cast<JSFatInlineString*>(js::AllocateString<JSFatInlineString, CanGC>(cx, heap));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline JSFatInlineString*
|
||||
Allocate<JSFatInlineString, NoGC>(JSContext* cx, gc::InitialHeap heap)
|
||||
{
|
||||
return static_cast<JSFatInlineString*>(js::AllocateString<JSFatInlineString, NoGC>(cx, heap));
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // gc_Allocator_h
|
||||
|
|
|
|||
77
js/src/gc/AtomMarking-inl.h
Normal file
77
js/src/gc/AtomMarking-inl.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gc/AtomMarking.h"
|
||||
|
||||
#include "jscompartment.h"
|
||||
|
||||
#include "gc/Heap-inl.h"
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
inline size_t
|
||||
GetAtomBit(TenuredCell* thing)
|
||||
{
|
||||
MOZ_ASSERT(thing->zoneFromAnyThread()->isAtomsZone());
|
||||
Arena* arena = thing->arena();
|
||||
size_t arenaBit = (reinterpret_cast<uintptr_t>(thing) - arena->address()) / CellBytesPerMarkBit;
|
||||
return arena->atomBitmapStart() * JS_BITS_PER_WORD + arenaBit;
|
||||
}
|
||||
|
||||
inline bool
|
||||
ThingIsPermanent(JSAtom* atom)
|
||||
{
|
||||
return atom->isPermanentAtom();
|
||||
}
|
||||
|
||||
inline bool
|
||||
ThingIsPermanent(JS::Symbol* symbol)
|
||||
{
|
||||
return symbol->isWellKnownSymbol();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MOZ_ALWAYS_INLINE void
|
||||
AtomMarkingRuntime::inlinedMarkAtom(JSContext* cx, T* thing)
|
||||
{
|
||||
static_assert(mozilla::IsSame<T, JSAtom>::value ||
|
||||
mozilla::IsSame<T, JS::Symbol>::value,
|
||||
"Should only be called with JSAtom* or JS::Symbol* argument");
|
||||
|
||||
MOZ_ASSERT(thing);
|
||||
js::gc::TenuredCell* cell = &thing->asTenured();
|
||||
MOZ_ASSERT(cell->zoneFromAnyThread()->isAtomsZone());
|
||||
|
||||
// The context's zone will be null during initialization of the runtime.
|
||||
if (!cx->zone())
|
||||
return;
|
||||
MOZ_ASSERT(!cx->zone()->isAtomsZone());
|
||||
|
||||
if (ThingIsPermanent(thing))
|
||||
return;
|
||||
|
||||
size_t bit = GetAtomBit(cell);
|
||||
MOZ_ASSERT(bit / JS_BITS_PER_WORD < allocatedWords);
|
||||
|
||||
cx->zone()->markedAtoms().setBit(bit);
|
||||
|
||||
if (!cx->helperThread()) {
|
||||
// Trigger a read barrier on the atom, in case there is an incremental
|
||||
// GC in progress. This is necessary if the atom is being marked
|
||||
// because a reference to it was obtained from another zone which is
|
||||
// not being collected by the incremental GC.
|
||||
T::readBarrier(thing);
|
||||
}
|
||||
|
||||
// Children of the thing also need to be marked in the context's zone.
|
||||
// We don't have a JSTracer for this so manually handle the cases in which
|
||||
// an atom can reference other atoms.
|
||||
markChildren(cx, thing);
|
||||
}
|
||||
|
||||
} // namespace gc
|
||||
} // namespace js
|
||||
|
|
@ -225,7 +225,7 @@ AtomMarkingRuntime::atomIsMarked(Zone* zone, T* thing)
|
|||
return true;
|
||||
}
|
||||
|
||||
size_t bit = GetAtomBit(thing);
|
||||
size_t bit = GetAtomBit(&thing->asTenured());
|
||||
return zone->markedAtoms().getBit(bit);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1028,7 +1028,9 @@ class GCRuntime
|
|||
static JSObject* tryNewTenuredObject(ExclusiveContext* cx, AllocKind kind, size_t thingSize,
|
||||
size_t nDynamicSlots);
|
||||
template <typename T, AllowGC allowGC>
|
||||
static T* tryNewTenuredThing(ExclusiveContext* cx, AllocKind kind, size_t thingSize);
|
||||
static T* tryNewTenuredThing(JSContext* cx, AllocKind kind, size_t thingSize);
|
||||
template <AllowGC allowGC>
|
||||
JSString* tryNewNurseryString(JSContext* cx, size_t thingSize, AllocKind kind);
|
||||
static TenuredCell* refillFreeListInGC(Zone* zone, AllocKind thingKind);
|
||||
|
||||
void bufferGrayRoots();
|
||||
|
|
|
|||
|
|
@ -132,6 +132,13 @@ js::gc::TraceNurseryAlloc(Cell* thing, size_t size)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
js::gc::TraceNurseryAlloc(Cell* thing, AllocKind kind)
|
||||
{
|
||||
if (thing)
|
||||
TraceEvent(TraceEventNurseryAlloc, uint64_t(thing), kind);
|
||||
}
|
||||
|
||||
void
|
||||
js::gc::TraceTenuredAlloc(Cell* thing, AllocKind kind)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ extern MOZ_MUST_USE bool InitTrace(GCRuntime& gc);
|
|||
extern void FinishTrace();
|
||||
extern bool TraceEnabled();
|
||||
extern void TraceNurseryAlloc(Cell* thing, size_t size);
|
||||
extern void TraceNurseryAlloc(Cell* thing, AllocKind kind);
|
||||
extern void TraceTenuredAlloc(Cell* thing, AllocKind kind);
|
||||
extern void TraceCreateObject(JSObject* object);
|
||||
extern void TraceMinorGCStart();
|
||||
|
|
@ -36,6 +37,7 @@ inline MOZ_MUST_USE bool InitTrace(GCRuntime& gc) { return true; }
|
|||
inline void FinishTrace() {}
|
||||
inline bool TraceEnabled() { return false; }
|
||||
inline void TraceNurseryAlloc(Cell* thing, size_t size) {}
|
||||
inline void TraceNurseryAlloc(Cell* thing, AllocKind kind) {}
|
||||
inline void TraceTenuredAlloc(Cell* thing, AllocKind kind) {}
|
||||
inline void TraceCreateObject(JSObject* object) {}
|
||||
inline void TraceMinorGCStart() {}
|
||||
|
|
|
|||
|
|
@ -624,7 +624,7 @@ js::TraceProcessGlobalRoot(JSTracer* trc, T* thing, const char* name)
|
|||
// permanent atoms, so likewise require no subsquent marking.
|
||||
CheckTracedThing(trc, *ConvertToBase(&thing));
|
||||
if (trc->isMarkingTracer())
|
||||
thing->markIfUnmarked(gc::MarkColor::Black);
|
||||
thing->asTenured().markIfUnmarked(gc::MarkColor::Black);
|
||||
else
|
||||
DoCallback(trc->asCallbackTracer(), ConvertToBase(&thing), name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -321,6 +321,23 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const
|
|||
return obj;
|
||||
}
|
||||
|
||||
Cell*
|
||||
js::Nursery::allocateString(JSContext* cx, Zone* zone, size_t size, AllocKind kind)
|
||||
{
|
||||
/* Ensure there's enough space to replace the contents with a RelocationOverlay. */
|
||||
MOZ_ASSERT(size >= sizeof(RelocationOverlay));
|
||||
|
||||
size_t allocSize = JS_ROUNDUP(sizeof(StringLayout) - 1 + size, CellAlignBytes);
|
||||
auto header = static_cast<StringLayout*>(allocate(allocSize));
|
||||
if (!header)
|
||||
return nullptr;
|
||||
header->zone = zone;
|
||||
|
||||
auto cell = reinterpret_cast<Cell*>(&header->cell);
|
||||
TraceNurseryAlloc(cell, kind);
|
||||
return cell;
|
||||
}
|
||||
|
||||
void*
|
||||
js::Nursery::allocate(size_t size)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ class TenuringTracer : public JSTracer
|
|||
public:
|
||||
Nursery& nursery() { return nursery_; }
|
||||
|
||||
// Returns true if the pointer was updated.
|
||||
template <typename T> void traverse(T** thingp);
|
||||
template <typename T> void traverse(T* thingp);
|
||||
|
||||
|
|
@ -136,6 +135,15 @@ class Nursery
|
|||
static const size_t Alignment = gc::ChunkSize;
|
||||
static const size_t ChunkShift = gc::ChunkShift;
|
||||
|
||||
struct alignas(gc::CellAlignBytes) CellAlignedByte {
|
||||
char byte;
|
||||
};
|
||||
|
||||
struct StringLayout {
|
||||
JS::Zone* zone;
|
||||
CellAlignedByte cell;
|
||||
};
|
||||
|
||||
explicit Nursery(JSRuntime* rt);
|
||||
~Nursery();
|
||||
|
||||
|
|
@ -187,6 +195,29 @@ class Nursery
|
|||
*/
|
||||
JSObject* allocateObject(JSContext* cx, size_t size, size_t numDynamic, const js::Class* clasp);
|
||||
|
||||
/*
|
||||
* Allocate and return a pointer to a new string. Returns nullptr if the
|
||||
* Nursery is full.
|
||||
*/
|
||||
gc::Cell* allocateString(JSContext* cx, JS::Zone* zone, size_t size, gc::AllocKind kind);
|
||||
|
||||
/*
|
||||
* String zones are stored just before the string in nursery memory.
|
||||
*/
|
||||
static JS::Zone* getStringZone(const JSString* str) {
|
||||
#ifdef DEBUG
|
||||
auto cell = reinterpret_cast<const js::gc::Cell*>(str); // JSString type is incomplete here
|
||||
MOZ_ASSERT(js::gc::IsInsideNursery(cell), "getStringZone must be passed a nursery string");
|
||||
#endif
|
||||
|
||||
auto layout = reinterpret_cast<const uint8_t*>(str) - offsetof(StringLayout, cell);
|
||||
return reinterpret_cast<const StringLayout*>(layout)->zone;
|
||||
}
|
||||
|
||||
static size_t stringHeaderSize() {
|
||||
return offsetof(StringLayout, cell);
|
||||
}
|
||||
|
||||
/* Allocate a buffer for a given zone, using the nursery if possible. */
|
||||
void* allocateBuffer(JS::Zone* zone, size_t nbytes);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class JSStringTypeCache(object):
|
|||
def __init__(self, cache):
|
||||
dummy = gdb.Value(0).cast(cache.JSString_ptr_t)
|
||||
self.ROPE_FLAGS = dummy['ROPE_FLAGS']
|
||||
self.ATOM_BIT = dummy['ATOM_BIT']
|
||||
self.NON_ATOM_BIT = dummy['NON_ATOM_BIT']
|
||||
self.INLINE_CHARS_BIT = dummy['INLINE_CHARS_BIT']
|
||||
self.TYPE_FLAGS_MASK = dummy['TYPE_FLAGS_MASK']
|
||||
self.LATIN1_CHARS_BIT = dummy['LATIN1_CHARS_BIT']
|
||||
|
|
|
|||
|
|
@ -1591,13 +1591,13 @@ CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
|
|||
static void*
|
||||
AllocateString(JSContext* cx)
|
||||
{
|
||||
return js::Allocate<JSString, NoGC>(cx);
|
||||
return js::Allocate<JSString, NoGC>(cx, js::gc::TenuredHeap);
|
||||
}
|
||||
|
||||
static void*
|
||||
AllocateFatInlineString(JSContext* cx)
|
||||
{
|
||||
return js::Allocate<JSFatInlineString, NoGC>(cx);
|
||||
return js::Allocate<JSFatInlineString, NoGC>(cx, js::gc::TenuredHeap);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -7700,10 +7700,13 @@ JitCompartment::generateStringConcatStub(JSContext* cx)
|
|||
masm.newGCString(output, temp3, &failure, stringsCanBeInNursery);
|
||||
|
||||
// Store rope length and flags. temp1 still holds the result of AND'ing the
|
||||
// lhs and rhs flags, so we just have to clear the other flags to get our
|
||||
// rope flags (Latin1 if both lhs and rhs are Latin1).
|
||||
static_assert(JSString::ROPE_FLAGS == 0, "Rope flags must be 0");
|
||||
// lhs and rhs flags, so we just have to clear the other flags and set
|
||||
// NON_ATOM_BIT to get our rope flags (Latin1 if both lhs and rhs are
|
||||
// Latin1).
|
||||
static_assert(JSString::INIT_ROPE_FLAGS == JSString::NON_ATOM_BIT,
|
||||
"Rope type flags must be NON_ATOM_BIT only");
|
||||
masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1);
|
||||
masm.or32(Imm32(JSString::NON_ATOM_BIT), temp1);
|
||||
masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
|
||||
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
|
||||
|
||||
|
|
|
|||
|
|
@ -1276,9 +1276,9 @@ MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register
|
|||
|
||||
Label notAtom;
|
||||
// Optimize the equality operation to a pointer compare for two atoms.
|
||||
Imm32 atomBit(JSString::ATOM_BIT);
|
||||
branchTest32(Assembler::Zero, Address(left, JSString::offsetOfFlags()), atomBit, ¬Atom);
|
||||
branchTest32(Assembler::Zero, Address(right, JSString::offsetOfFlags()), atomBit, ¬Atom);
|
||||
Imm32 nonAtomBit(JSString::NON_ATOM_BIT);
|
||||
branchTest32(Assembler::NonZero, Address(left, JSString::offsetOfFlags()), nonAtomBit, ¬Atom);
|
||||
branchTest32(Assembler::NonZero, Address(right, JSString::offsetOfFlags()), nonAtomBit, ¬Atom);
|
||||
|
||||
cmpPtrSet(JSOpToCondition(MCompare::Compare_String, op), left, right, result);
|
||||
jump(&done);
|
||||
|
|
|
|||
|
|
@ -585,7 +585,8 @@ struct Function {
|
|||
|
||||
struct String
|
||||
{
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(2);
|
||||
static const uint32_t NON_ATOM_BIT = JS_BIT(0);
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
|
||||
static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
|
||||
static const uint32_t ROPE_FLAGS = NON_ATOM_BIT;
|
||||
static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
|
||||
|
|
@ -597,6 +598,11 @@ struct String
|
|||
JS::Latin1Char inlineStorageLatin1[1];
|
||||
char16_t inlineStorageTwoByte[1];
|
||||
};
|
||||
|
||||
static bool nurseryCellIsString(const js::gc::Cell* cell) {
|
||||
MOZ_ASSERT(IsInsideNursery(cell));
|
||||
return reinterpret_cast<const String*>(cell)->flags & NON_ATOM_BIT;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace shadow */
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ class ArenaCellIterImpl
|
|||
template<typename T> T* get() const {
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
|
||||
return static_cast<T*>(getCell());
|
||||
return reinterpret_cast<T*>(getCell());
|
||||
}
|
||||
|
||||
void next() {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ JSRope::new_(js::ExclusiveContext* cx,
|
|||
{
|
||||
if (!validateLength(cx, length))
|
||||
return nullptr;
|
||||
JSRope* str = static_cast<JSRope*>(js::Allocate<JSString, allowGC>(cx));
|
||||
JSRope* str = js::Allocate<JSRope, allowGC>(cx, js::gc::TenuredHeap);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
str->init(cx, left, right, length);
|
||||
|
|
@ -181,7 +181,7 @@ JSDependentString::new_(js::ExclusiveContext* cx, JSLinearString* baseArg, size_
|
|||
if (baseArg->isExternal() && !baseArg->ensureFlat(cx->asJSContext()))
|
||||
return nullptr;
|
||||
|
||||
JSDependentString* str = static_cast<JSDependentString*>(js::Allocate<JSString, js::NoGC>(cx));
|
||||
JSDependentString* str = js::Allocate<JSDependentString, js::NoGC>(cx, js::gc::TenuredHeap);
|
||||
if (str) {
|
||||
str->init(cx, baseArg, start, length);
|
||||
return str;
|
||||
|
|
@ -189,7 +189,7 @@ JSDependentString::new_(js::ExclusiveContext* cx, JSLinearString* baseArg, size_
|
|||
|
||||
js::RootedLinearString base(cx, baseArg);
|
||||
|
||||
str = static_cast<JSDependentString*>(js::Allocate<JSString>(cx));
|
||||
str = js::Allocate<JSDependentString>(cx, js::gc::TenuredHeap);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
str->init(cx, base, start, length);
|
||||
|
|
@ -200,7 +200,7 @@ MOZ_ALWAYS_INLINE void
|
|||
JSFlatString::init(const char16_t* chars, size_t length)
|
||||
{
|
||||
d.u1.length = length;
|
||||
d.u1.flags = FLAT_BIT;
|
||||
d.u1.flags = INIT_FLAT_FLAGS;
|
||||
d.s.u2.nonInlineCharsTwoByte = chars;
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ MOZ_ALWAYS_INLINE void
|
|||
JSFlatString::init(const JS::Latin1Char* chars, size_t length)
|
||||
{
|
||||
d.u1.length = length;
|
||||
d.u1.flags = FLAT_BIT | LATIN1_CHARS_BIT;
|
||||
d.u1.flags = INIT_FLAT_FLAGS | LATIN1_CHARS_BIT;
|
||||
d.s.u2.nonInlineCharsLatin1 = chars;
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ JSFlatString::new_(js::ExclusiveContext* cx, const CharT* chars, size_t length)
|
|||
if (cx->compartment()->isAtomsCompartment())
|
||||
str = js::Allocate<js::NormalAtom, allowGC>(cx);
|
||||
else
|
||||
str = static_cast<JSFlatString*>(js::Allocate<JSString, allowGC>(cx));
|
||||
str = js::Allocate<JSFlatString, allowGC>(cx, js::gc::TenuredHeap);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ JSThinInlineString::new_(js::ExclusiveContext* cx)
|
|||
if (cx->compartment()->isAtomsCompartment())
|
||||
return (JSThinInlineString*)(js::Allocate<js::NormalAtom, allowGC>(cx));
|
||||
|
||||
return static_cast<JSThinInlineString*>(js::Allocate<JSString, allowGC>(cx));
|
||||
return js::Allocate<JSThinInlineString, allowGC>(cx, js::gc::TenuredHeap);
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
|
|
@ -279,7 +279,7 @@ JSFatInlineString::new_(js::ExclusiveContext* cx)
|
|||
if (cx->compartment()->isAtomsCompartment())
|
||||
return (JSFatInlineString*)(js::Allocate<js::FatInlineAtom, allowGC>(cx));
|
||||
|
||||
return js::Allocate<JSFatInlineString, allowGC>(cx);
|
||||
return js::Allocate<JSFatInlineString, allowGC>(cx, js::gc::TenuredHeap);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
|
|
|||
|
|
@ -196,9 +196,10 @@ JSString::dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) c
|
|||
if (flags & FLAT_BIT) fputs(" FLAT", fp);
|
||||
if (flags & HAS_BASE_BIT) fputs(" HAS_BASE", fp);
|
||||
if (flags & INLINE_CHARS_BIT) fputs(" INLINE_CHARS", fp);
|
||||
if (flags & ATOM_BIT) fputs(" ATOM", fp);
|
||||
if (flags & NON_ATOM_BIT) fputs(" NON_ATOM", fp);
|
||||
if (isPermanentAtom()) fputs(" PERMANENT", fp);
|
||||
if (flags & LATIN1_CHARS_BIT) fputs(" LATIN1", fp);
|
||||
if (!isTenured()) fputs(" NURSERY", fp);
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
|
|
@ -1092,9 +1093,11 @@ JSExternalString::ensureFlat(JSContext* cx)
|
|||
// Release the external chars.
|
||||
finalize(cx->runtime()->defaultFreeOp());
|
||||
|
||||
// Transform the string into a non-external, flat string.
|
||||
// Transform the string into a non-external, flat string. Note that the
|
||||
// resulting string will still be in an AllocKind::EXTERNAL_STRING arena,
|
||||
// but will no longer be an external string.
|
||||
setNonInlineChars<char16_t>(s);
|
||||
d.u1.flags = FLAT_BIT;
|
||||
d.u1.flags = INIT_FLAT_FLAGS;
|
||||
|
||||
return &this->asFlat();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "gc/Barrier.h"
|
||||
#include "gc/Heap.h"
|
||||
#include "gc/Nursery.h"
|
||||
#include "gc/Cell.h"
|
||||
#include "gc/Marking.h"
|
||||
#include "gc/Rooting.h"
|
||||
#include "js/CharacterEncoding.h"
|
||||
|
|
@ -148,7 +149,7 @@ static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
|
|||
* at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString).
|
||||
*/
|
||||
|
||||
class JSString : public js::gc::TenuredCell
|
||||
class JSString : public js::gc::Cell
|
||||
{
|
||||
protected:
|
||||
static const size_t NUM_INLINE_CHARS_LATIN1 = 2 * sizeof(void*) / sizeof(JS::Latin1Char);
|
||||
|
|
@ -215,41 +216,49 @@ class JSString : public js::gc::TenuredCell
|
|||
* String Instance Subtype
|
||||
* type encoding predicate
|
||||
* ------------------------------------
|
||||
* Rope 000000 000000
|
||||
* Linear - !000000
|
||||
* HasBase - xxxx1x
|
||||
* Dependent 000010 000010
|
||||
* External 100000 100000
|
||||
* Rope 000001 000000
|
||||
* Linear - !000010
|
||||
* HasBase - xxx1xx
|
||||
* Dependent 000110 000110
|
||||
* External 100010 100010
|
||||
* Flat - xxxxx1
|
||||
* Undepended 000011 000011
|
||||
* Extensible 010001 010001
|
||||
* Inline 000101 xxx1xx
|
||||
* FatInline 010101 x1x1xx
|
||||
* Atom 001001 xx1xxx
|
||||
* PermanentAtom 101001 1x1xxx
|
||||
* InlineAtom - xx11xx
|
||||
* FatInlineAtom - x111xx
|
||||
* Undepended 000111 000111
|
||||
* Extensible 010011 010011
|
||||
* Inline 001011 xx1xxx
|
||||
* FatInline 011011 x11xxx
|
||||
* NormalAtom 000010 xxxxx0
|
||||
* PermanentAtom 100010 1xxxx0
|
||||
* InlineAtom - xx1xx0
|
||||
* FatInlineAtom - x11xx0
|
||||
*
|
||||
* Note that the first 4 flag bits (from right to left in the previous table)
|
||||
* have the following meaning and can be used for some hot queries:
|
||||
*
|
||||
* Bit 0: IsFlat
|
||||
* Bit 1: HasBase (Dependent, Undepended)
|
||||
* Bit 2: IsInline (Inline, FatInline)
|
||||
* Bit 3: IsAtom (Atom, PermanentAtom)
|
||||
* Bit 0: !IsAtom (Atom, PermanentAtom)
|
||||
* Bit 1: IsFlat
|
||||
* Bit 2: HasBase (Dependent, Undepended)
|
||||
* Bit 3: IsInline (Inline, FatInline)
|
||||
*
|
||||
* "HasBase" here refers to the two string types that have a 'base' field:
|
||||
* JSDependentString and JSUndependedString.
|
||||
* A JSUndependedString is a JSDependentString which has been 'fixed' (by ensureFixed)
|
||||
* 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; }).
|
||||
*
|
||||
*/
|
||||
|
||||
static const uint32_t FLAT_BIT = JS_BIT(0);
|
||||
static const uint32_t HAS_BASE_BIT = JS_BIT(1);
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(2);
|
||||
static const uint32_t ATOM_BIT = JS_BIT(3);
|
||||
static const uint32_t NON_ATOM_BIT = JS_BIT(0);
|
||||
static const uint32_t FLAT_BIT = JS_BIT(1);
|
||||
static const uint32_t HAS_BASE_BIT = JS_BIT(2);
|
||||
static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
|
||||
|
||||
static const uint32_t ROPE_FLAGS = NON_ATOM_BIT;
|
||||
static const uint32_t DEPENDENT_FLAGS = NON_ATOM_BIT | HAS_BASE_BIT;
|
||||
|
|
@ -258,11 +267,13 @@ class JSString : public js::gc::TenuredCell
|
|||
static const uint32_t EXTERNAL_FLAGS = NON_ATOM_BIT | JS_BIT(5);
|
||||
|
||||
static const uint32_t FAT_INLINE_MASK = INLINE_CHARS_BIT | JS_BIT(4);
|
||||
static const uint32_t PERMANENT_ATOM_MASK = ATOM_BIT | JS_BIT(5);
|
||||
static const uint32_t PERMANENT_ATOM_MASK = NON_ATOM_BIT | JS_BIT(5);
|
||||
static const uint32_t PERMANENT_ATOM_FLAGS = JS_BIT(5);
|
||||
|
||||
/* Initial flags for thin inline and fat inline strings. */
|
||||
static const uint32_t INIT_THIN_INLINE_FLAGS = FLAT_BIT | INLINE_CHARS_BIT;
|
||||
static const uint32_t INIT_FAT_INLINE_FLAGS = FLAT_BIT | FAT_INLINE_MASK;
|
||||
static const uint32_t INIT_THIN_INLINE_FLAGS = NON_ATOM_BIT | FLAT_BIT | INLINE_CHARS_BIT;
|
||||
static const uint32_t INIT_FAT_INLINE_FLAGS = NON_ATOM_BIT | FLAT_BIT | FAT_INLINE_MASK;
|
||||
static const uint32_t INIT_FLAT_FLAGS = NON_ATOM_BIT | FLAT_BIT;
|
||||
|
||||
static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
|
||||
|
||||
|
|
@ -304,6 +315,8 @@ class JSString : public js::gc::TenuredCell
|
|||
"shadow::String inlineStorage offset must match JSString");
|
||||
static_assert(offsetof(JSString, d.inlineStorageTwoByte) == offsetof(String, inlineStorageTwoByte),
|
||||
"shadow::String inlineStorage offset must match JSString");
|
||||
static_assert(NON_ATOM_BIT == String::NON_ATOM_BIT,
|
||||
"shadow::String::NON_ATOM_BIT must match JSString::NON_ATOM_BIT");
|
||||
static_assert(INLINE_CHARS_BIT == String::INLINE_CHARS_BIT,
|
||||
"shadow::String::INLINE_CHARS_BIT must match JSString::INLINE_CHARS_BIT");
|
||||
static_assert(LATIN1_CHARS_BIT == String::LATIN1_CHARS_BIT,
|
||||
|
|
@ -447,12 +460,12 @@ class JSString : public js::gc::TenuredCell
|
|||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isAtom() const {
|
||||
return d.u1.flags & ATOM_BIT;
|
||||
return !(d.u1.flags & NON_ATOM_BIT);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
bool isPermanentAtom() const {
|
||||
return (d.u1.flags & PERMANENT_ATOM_MASK) == PERMANENT_ATOM_MASK;
|
||||
return (d.u1.flags & PERMANENT_ATOM_MASK) == PERMANENT_ATOM_FLAGS;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
|
@ -461,8 +474,8 @@ class JSString : public js::gc::TenuredCell
|
|||
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).
|
||||
// Used for distinguishing strings from objects in the nursery. 'cell' must
|
||||
// be in the nursery.
|
||||
MOZ_ALWAYS_INLINE
|
||||
static bool nurseryCellIsString(js::gc::Cell* cell) {
|
||||
MOZ_ASSERT(!cell->isTenured());
|
||||
|
|
@ -573,14 +586,14 @@ class JSString : public js::gc::TenuredCell
|
|||
if (thing->isPermanentAtom() || js::gc::IsInsideNursery(thing))
|
||||
return;
|
||||
|
||||
TenuredCell::readBarrier(thing);
|
||||
js::gc::TenuredCell::readBarrier(&thing->asTenured());
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void writeBarrierPre(JSString* thing) {
|
||||
if (!thing || thing->isPermanentAtom() || js::gc::IsInsideNursery(thing))
|
||||
return;
|
||||
|
||||
TenuredCell::writeBarrierPre(thing);
|
||||
js::gc::TenuredCell::writeBarrierPre(&thing->asTenured());
|
||||
}
|
||||
|
||||
static void writeBarrierPost(void* cellp, JSString* prev, JSString* next) {
|
||||
|
|
@ -1015,6 +1028,11 @@ class JSExternalString : public JSLinearString
|
|||
|
||||
inline void finalize(js::FreeOp* fop);
|
||||
|
||||
/*
|
||||
* Free the external chars and allocate a new buffer, converting this to a
|
||||
* flat string (which still lives in an AllocKind::EXTERNAL_STRING
|
||||
* arena).
|
||||
*/
|
||||
JSFlatString* ensureFlat(JSContext* cx);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
@ -1057,7 +1075,8 @@ class JSAtom : public JSFlatString
|
|||
// Transform this atom into a permanent atom. This is only done during
|
||||
// initialization of the runtime.
|
||||
MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() {
|
||||
d.u1.flags |= PERMANENT_ATOM_MASK;
|
||||
MOZ_ASSERT(static_cast<JSString*>(this)->isAtom());
|
||||
d.u1.flags |= PERMANENT_ATOM_FLAGS;
|
||||
}
|
||||
|
||||
inline js::HashNumber hash() const;
|
||||
|
|
@ -1134,7 +1153,8 @@ JSAtom::initHash(js::HashNumber hash)
|
|||
MOZ_ALWAYS_INLINE JSAtom*
|
||||
JSFlatString::morphAtomizedStringIntoAtom(js::HashNumber hash)
|
||||
{
|
||||
d.u1.flags |= ATOM_BIT;
|
||||
MOZ_ASSERT(!isAtom());
|
||||
d.u1.flags &= ~NON_ATOM_BIT;
|
||||
JSAtom* atom = &asAtom();
|
||||
atom->initHash(hash);
|
||||
return atom;
|
||||
|
|
@ -1143,7 +1163,9 @@ JSFlatString::morphAtomizedStringIntoAtom(js::HashNumber hash)
|
|||
MOZ_ALWAYS_INLINE JSAtom*
|
||||
JSFlatString::morphAtomizedStringIntoPermanentAtom(js::HashNumber hash)
|
||||
{
|
||||
d.u1.flags |= PERMANENT_ATOM_MASK;
|
||||
MOZ_ASSERT(!isAtom());
|
||||
d.u1.flags |= PERMANENT_ATOM_FLAGS;
|
||||
d.u1.flags &= ~NON_ATOM_BIT;
|
||||
JSAtom* atom = &asAtom();
|
||||
atom->initHash(hash);
|
||||
return atom;
|
||||
|
|
@ -1597,6 +1619,13 @@ Cell::as<JSString>() {
|
|||
MOZ_ASSERT(is<JSString>());
|
||||
return reinterpret_cast<JSString*>(this);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline JSString*
|
||||
TenuredCell::as<JSString>() {
|
||||
MOZ_ASSERT(is<JSString>());
|
||||
return reinterpret_cast<JSString*>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue