зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1746699 - Avoid UB when allocating JSStrings and BigInts r=tcampbell
Add emplace methods to JSString and BigInt types to reduce amount of C++ undefined-behaviour by running the constructor of the concrete derived type. This is in contrast to previous behaviour of allocating a gc::Cell and then simply casting to the final type. We still first allocate as a Cell from the GC and then use placement-new to initialize as the correct type. Differential Revision: https://phabricator.services.mozilla.com/D134095
This commit is contained in:
Родитель
23fc627ed6
Коммит
876f784fce
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "gc/Allocator.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/OperatorNewExtensions.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include "gc/GCInternals.h"
|
||||
|
@ -150,25 +152,27 @@ JSObject* GCRuntime::tryNewTenuredObject(JSContext* cx, AllocKind kind,
|
|||
Debug_SetSlotRangeToCrashOnTouch(slotsHeader->slots(), nDynamicSlots);
|
||||
}
|
||||
|
||||
JSObject* obj = tryNewTenuredThing<JSObject, allowGC>(cx, kind, thingSize);
|
||||
|
||||
if (obj) {
|
||||
if (nDynamicSlots) {
|
||||
static_cast<NativeObject*>(obj)->initSlots(slotsHeader->slots());
|
||||
AddCellMemory(obj, ObjectSlots::allocSize(nDynamicSlots),
|
||||
MemoryUse::ObjectSlots);
|
||||
}
|
||||
} else {
|
||||
TenuredCell* cell = tryNewTenuredThing<allowGC>(cx, kind, thingSize);
|
||||
if (!cell) {
|
||||
js_free(slotsHeader);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return obj;
|
||||
if (nDynamicSlots) {
|
||||
NativeObject* nobj = new (mozilla::KnownNotNull, cell) NativeObject();
|
||||
nobj->initSlots(slotsHeader->slots());
|
||||
AddCellMemory(nobj, ObjectSlots::allocSize(nDynamicSlots),
|
||||
MemoryUse::ObjectSlots);
|
||||
return nobj;
|
||||
}
|
||||
|
||||
return new (mozilla::KnownNotNull, cell) JSObject();
|
||||
}
|
||||
|
||||
// Attempt to allocate a new string out of the nursery. If there is not enough
|
||||
// room in the nursery or there is an OOM, this method will return nullptr.
|
||||
template <AllowGC allowGC>
|
||||
JSString* GCRuntime::tryNewNurseryString(JSContext* cx, size_t thingSize,
|
||||
Cell* GCRuntime::tryNewNurseryStringCell(JSContext* cx, size_t thingSize,
|
||||
AllocKind kind) {
|
||||
MOZ_ASSERT(IsNurseryAllocable(kind));
|
||||
MOZ_ASSERT(cx->isNurseryAllocAllowed());
|
||||
|
@ -178,7 +182,7 @@ JSString* GCRuntime::tryNewNurseryString(JSContext* cx, size_t thingSize,
|
|||
AllocSite* site = cx->zone()->unknownAllocSite();
|
||||
Cell* cell = cx->nursery().allocateString(site, thingSize);
|
||||
if (cell) {
|
||||
return static_cast<JSString*>(cell);
|
||||
return cell;
|
||||
}
|
||||
|
||||
if (allowGC && !cx->suppressGC) {
|
||||
|
@ -187,16 +191,15 @@ JSString* GCRuntime::tryNewNurseryString(JSContext* cx, size_t thingSize,
|
|||
// Exceeding gcMaxBytes while tenuring can disable the Nursery, and
|
||||
// other heuristics can disable nursery strings for this zone.
|
||||
if (cx->nursery().isEnabled() && cx->zone()->allocNurseryStrings) {
|
||||
return static_cast<JSString*>(
|
||||
cx->nursery().allocateString(site, thingSize));
|
||||
return cx->nursery().allocateString(site, thingSize);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <AllowGC allowGC /* = CanGC */>
|
||||
JSString* js::AllocateStringImpl(JSContext* cx, AllocKind kind, size_t size,
|
||||
InitialHeap heap) {
|
||||
Cell* js::AllocateStringCell(JSContext* cx, AllocKind kind, size_t size,
|
||||
InitialHeap heap) {
|
||||
MOZ_ASSERT(!cx->isHelperThreadContext());
|
||||
MOZ_ASSERT(size == Arena::thingSize(kind));
|
||||
MOZ_ASSERT(size == sizeof(JSString) || size == sizeof(JSFatInlineString));
|
||||
|
@ -205,12 +208,11 @@ JSString* js::AllocateStringImpl(JSContext* cx, AllocKind kind, size_t size,
|
|||
|
||||
// Off-thread alloc cannot trigger GC or make runtime assertions.
|
||||
if (cx->isNurseryAllocSuppressed()) {
|
||||
JSString* str =
|
||||
GCRuntime::tryNewTenuredThing<JSString, NoGC>(cx, kind, size);
|
||||
if (MOZ_UNLIKELY(allowGC && !str)) {
|
||||
TenuredCell* cell = GCRuntime::tryNewTenuredThing<NoGC>(cx, kind, size);
|
||||
if (MOZ_UNLIKELY(allowGC && !cell)) {
|
||||
ReportOutOfMemory(cx);
|
||||
}
|
||||
return str;
|
||||
return cell;
|
||||
}
|
||||
|
||||
JSRuntime* rt = cx->runtime();
|
||||
|
@ -220,10 +222,9 @@ JSString* js::AllocateStringImpl(JSContext* cx, AllocKind kind, size_t size,
|
|||
|
||||
if (cx->nursery().isEnabled() && heap != TenuredHeap &&
|
||||
cx->nursery().canAllocateStrings() && cx->zone()->allocNurseryStrings) {
|
||||
auto* str = static_cast<JSString*>(
|
||||
rt->gc.tryNewNurseryString<allowGC>(cx, size, kind));
|
||||
if (str) {
|
||||
return str;
|
||||
Cell* cell = rt->gc.tryNewNurseryStringCell<allowGC>(cx, size, kind);
|
||||
if (cell) {
|
||||
return cell;
|
||||
}
|
||||
|
||||
// Our most common non-jit allocation path is NoGC; thus, if we fail the
|
||||
|
@ -236,13 +237,13 @@ JSString* js::AllocateStringImpl(JSContext* cx, AllocKind kind, size_t size,
|
|||
}
|
||||
}
|
||||
|
||||
return GCRuntime::tryNewTenuredThing<JSString, allowGC>(cx, kind, size);
|
||||
return GCRuntime::tryNewTenuredThing<allowGC>(cx, kind, size);
|
||||
}
|
||||
|
||||
template JSString* js::AllocateStringImpl<NoGC>(JSContext*, AllocKind, size_t,
|
||||
InitialHeap);
|
||||
template JSString* js::AllocateStringImpl<CanGC>(JSContext*, AllocKind, size_t,
|
||||
InitialHeap);
|
||||
template Cell* js::AllocateStringCell<NoGC>(JSContext*, AllocKind, size_t,
|
||||
InitialHeap);
|
||||
template Cell* js::AllocateStringCell<CanGC>(JSContext*, AllocKind, size_t,
|
||||
InitialHeap);
|
||||
|
||||
// Attempt to allocate a new BigInt out of the nursery. If there is not enough
|
||||
// room in the nursery or there is an OOM, this method will return nullptr.
|
||||
|
@ -257,7 +258,7 @@ JS::BigInt* GCRuntime::tryNewNurseryBigInt(JSContext* cx, size_t thingSize,
|
|||
AllocSite* site = cx->zone()->unknownAllocSite();
|
||||
Cell* cell = cx->nursery().allocateBigInt(site, thingSize);
|
||||
if (cell) {
|
||||
return static_cast<JS::BigInt*>(cell);
|
||||
return JS::BigInt::emplace(cell);
|
||||
}
|
||||
|
||||
if (allowGC && !cx->suppressGC) {
|
||||
|
@ -266,8 +267,10 @@ JS::BigInt* GCRuntime::tryNewNurseryBigInt(JSContext* cx, size_t thingSize,
|
|||
// Exceeding gcMaxBytes while tenuring can disable the Nursery, and
|
||||
// other heuristics can disable nursery BigInts for this zone.
|
||||
if (cx->nursery().isEnabled() && cx->zone()->allocNurseryBigInts) {
|
||||
return static_cast<JS::BigInt*>(
|
||||
cx->nursery().allocateBigInt(site, thingSize));
|
||||
Cell* cell = cx->nursery().allocateBigInt(site, thingSize);
|
||||
if (cell) {
|
||||
return JS::BigInt::emplace(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -283,12 +286,12 @@ JS::BigInt* js::AllocateBigInt(JSContext* cx, InitialHeap heap) {
|
|||
|
||||
// Off-thread alloc cannot trigger GC or make runtime assertions.
|
||||
if (cx->isNurseryAllocSuppressed()) {
|
||||
JS::BigInt* bi =
|
||||
GCRuntime::tryNewTenuredThing<JS::BigInt, NoGC>(cx, kind, size);
|
||||
if (MOZ_UNLIKELY(allowGC && !bi)) {
|
||||
TenuredCell* cell = GCRuntime::tryNewTenuredThing<NoGC>(cx, kind, size);
|
||||
if (MOZ_UNLIKELY(allowGC && !cell)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
return bi;
|
||||
return JS::BigInt::emplace(cell);
|
||||
}
|
||||
|
||||
JSRuntime* rt = cx->runtime();
|
||||
|
@ -298,8 +301,7 @@ JS::BigInt* js::AllocateBigInt(JSContext* cx, InitialHeap heap) {
|
|||
|
||||
if (cx->nursery().isEnabled() && heap != TenuredHeap &&
|
||||
cx->nursery().canAllocateBigInts() && cx->zone()->allocNurseryBigInts) {
|
||||
auto* bi = static_cast<JS::BigInt*>(
|
||||
rt->gc.tryNewNurseryBigInt<allowGC>(cx, size, kind));
|
||||
auto* bi = rt->gc.tryNewNurseryBigInt<allowGC>(cx, size, kind);
|
||||
if (bi) {
|
||||
return bi;
|
||||
}
|
||||
|
@ -314,7 +316,12 @@ JS::BigInt* js::AllocateBigInt(JSContext* cx, InitialHeap heap) {
|
|||
}
|
||||
}
|
||||
|
||||
return GCRuntime::tryNewTenuredThing<JS::BigInt, allowGC>(cx, kind, size);
|
||||
TenuredCell* cell = GCRuntime::tryNewTenuredThing<allowGC>(cx, kind, size);
|
||||
if (!cell) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return JS::BigInt::emplace(cell);
|
||||
}
|
||||
template JS::BigInt* js::AllocateBigInt<NoGC>(JSContext* cx,
|
||||
gc::InitialHeap heap);
|
||||
|
@ -322,7 +329,8 @@ template JS::BigInt* js::AllocateBigInt<CanGC>(JSContext* cx,
|
|||
gc::InitialHeap heap);
|
||||
|
||||
template <AllowGC allowGC /* = CanGC */>
|
||||
Cell* js::AllocateTenuredImpl(JSContext* cx, gc::AllocKind kind, size_t size) {
|
||||
TenuredCell* js::AllocateTenuredImpl(JSContext* cx, gc::AllocKind kind,
|
||||
size_t size) {
|
||||
MOZ_ASSERT(!cx->isHelperThreadContext());
|
||||
MOZ_ASSERT(!IsNurseryAllocable(kind));
|
||||
MOZ_ASSERT(size == Arena::thingSize(kind));
|
||||
|
@ -334,46 +342,49 @@ Cell* js::AllocateTenuredImpl(JSContext* cx, gc::AllocKind kind, size_t size) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return GCRuntime::tryNewTenuredThing<Cell, allowGC>(cx, kind, size);
|
||||
return GCRuntime::tryNewTenuredThing<allowGC>(cx, kind, size);
|
||||
}
|
||||
|
||||
template Cell* js::AllocateTenuredImpl<NoGC>(JSContext*, AllocKind, size_t);
|
||||
template Cell* js::AllocateTenuredImpl<CanGC>(JSContext*, AllocKind, size_t);
|
||||
template TenuredCell* js::AllocateTenuredImpl<NoGC>(JSContext*, AllocKind,
|
||||
size_t);
|
||||
template TenuredCell* js::AllocateTenuredImpl<CanGC>(JSContext*, AllocKind,
|
||||
size_t);
|
||||
|
||||
template <typename T, AllowGC allowGC>
|
||||
template <AllowGC allowGC>
|
||||
/* static */
|
||||
T* GCRuntime::tryNewTenuredThing(JSContext* cx, AllocKind kind,
|
||||
size_t thingSize) {
|
||||
TenuredCell* GCRuntime::tryNewTenuredThing(JSContext* cx, AllocKind kind,
|
||||
size_t thingSize) {
|
||||
// Bump allocate in the arena's current free-list span.
|
||||
Zone* zone = cx->zone();
|
||||
auto* t = reinterpret_cast<T*>(zone->arenas.freeLists().allocate(kind));
|
||||
void* t = zone->arenas.freeLists().allocate(kind);
|
||||
if (MOZ_UNLIKELY(!t)) {
|
||||
// Get the next available free list and allocate out of it. This may
|
||||
// acquire a new arena, which will lock the chunk list. If there are no
|
||||
// chunks available it may also allocate new memory directly.
|
||||
t = reinterpret_cast<T*>(refillFreeList(cx, kind));
|
||||
t = refillFreeList(cx, kind);
|
||||
|
||||
if (MOZ_UNLIKELY(!t)) {
|
||||
if (allowGC) {
|
||||
if constexpr (allowGC) {
|
||||
cx->runtime()->gc.attemptLastDitchGC(cx);
|
||||
t = tryNewTenuredThing<T, NoGC>(cx, kind, thingSize);
|
||||
}
|
||||
if (!t) {
|
||||
if (allowGC) {
|
||||
ReportOutOfMemory(cx);
|
||||
TenuredCell* cell = tryNewTenuredThing<NoGC>(cx, kind, thingSize);
|
||||
if (cell) {
|
||||
return cell;
|
||||
}
|
||||
return nullptr;
|
||||
ReportOutOfMemory(cx);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
checkIncrementalZoneState(cx, t);
|
||||
gcprobes::TenuredAlloc(t, kind);
|
||||
TenuredCell* cell = new (mozilla::KnownNotNull, t) TenuredCell();
|
||||
checkIncrementalZoneState(cx, cell);
|
||||
gcprobes::TenuredAlloc(cell, kind);
|
||||
// We count this regardless of the profiler's state, assuming that it costs
|
||||
// just as much to count it, as to check the profiler's state and decide not
|
||||
// to count it.
|
||||
zone->noteTenuredAlloc();
|
||||
return t;
|
||||
return cell;
|
||||
}
|
||||
|
||||
void GCRuntime::attemptLastDitchGC(JSContext* cx) {
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace js {
|
|||
namespace gc {
|
||||
class AllocSite;
|
||||
struct Cell;
|
||||
class TenuredCell;
|
||||
} // namespace gc
|
||||
|
||||
// [SMDOC] AllowGC template parameter
|
||||
|
@ -37,9 +38,10 @@ enum AllowGC { NoGC = 0, CanGC = 1 };
|
|||
|
||||
// Allocator implementation functions.
|
||||
template <AllowGC allowGC = CanGC>
|
||||
gc::Cell* AllocateTenuredImpl(JSContext* cx, gc::AllocKind kind, size_t size);
|
||||
gc::TenuredCell* AllocateTenuredImpl(JSContext* cx, gc::AllocKind kind,
|
||||
size_t size);
|
||||
template <AllowGC allowGC = CanGC>
|
||||
JSString* AllocateStringImpl(JSContext* cx, gc::AllocKind kind, size_t size,
|
||||
gc::Cell* AllocateStringCell(JSContext* cx, gc::AllocKind kind, size_t size,
|
||||
gc::InitialHeap heap);
|
||||
|
||||
// Allocate a new tenured GC thing that's not nursery-allocatable.
|
||||
|
@ -74,9 +76,11 @@ template <typename StringT, AllowGC allowGC = CanGC>
|
|||
StringT* AllocateString(JSContext* cx, gc::InitialHeap heap) {
|
||||
static_assert(std::is_base_of_v<JSString, StringT>);
|
||||
gc::AllocKind kind = gc::MapTypeToAllocKind<StringT>::kind;
|
||||
JSString* string =
|
||||
AllocateStringImpl<allowGC>(cx, kind, sizeof(StringT), heap);
|
||||
return static_cast<StringT*>(string);
|
||||
gc::Cell* cell = AllocateStringCell<allowGC>(cx, kind, sizeof(StringT), heap);
|
||||
if (!cell) {
|
||||
return nullptr;
|
||||
}
|
||||
return StringT::emplace(cell);
|
||||
}
|
||||
|
||||
// Allocate a BigInt.
|
||||
|
|
|
@ -629,10 +629,11 @@ class GCRuntime {
|
|||
template <AllowGC allowGC>
|
||||
static JSObject* tryNewTenuredObject(JSContext* cx, AllocKind kind,
|
||||
size_t thingSize, size_t nDynamicSlots);
|
||||
template <typename T, AllowGC allowGC>
|
||||
static T* tryNewTenuredThing(JSContext* cx, AllocKind kind, size_t thingSize);
|
||||
template <AllowGC allowGC>
|
||||
JSString* tryNewNurseryString(JSContext* cx, size_t thingSize,
|
||||
static TenuredCell* tryNewTenuredThing(JSContext* cx, AllocKind kind,
|
||||
size_t thingSize);
|
||||
template <AllowGC allowGC>
|
||||
Cell* tryNewNurseryStringCell(JSContext* cx, size_t thingSize,
|
||||
AllocKind kind);
|
||||
template <AllowGC allowGC>
|
||||
JS::BigInt* tryNewNurseryBigInt(JSContext* cx, size_t thingSize,
|
||||
|
|
|
@ -103,7 +103,7 @@ namespace jit {
|
|||
_(js::jit::AllocateAndInitTypedArrayBuffer) \
|
||||
_(js::jit::AllocateBigIntNoGC) \
|
||||
_(js::jit::AllocateFatInlineString) \
|
||||
_(js::jit::AllocateString) \
|
||||
_(js::jit::AllocateDependentString) \
|
||||
_(js::jit::AssertMapObjectHash) \
|
||||
_(js::jit::AssertSetObjectHash) \
|
||||
_(js::jit::AssertValidBigIntPtr) \
|
||||
|
|
|
@ -2459,7 +2459,7 @@ void CreateDependentString::generateFallback(MacroAssembler& masm) {
|
|||
if (kind == FallbackKind::FatInlineString) {
|
||||
masm.callWithABI<Fn, AllocateFatInlineString>();
|
||||
} else {
|
||||
masm.callWithABI<Fn, AllocateString>();
|
||||
masm.callWithABI<Fn, AllocateDependentString>();
|
||||
}
|
||||
masm.storeCallPointerResult(string_);
|
||||
|
||||
|
|
|
@ -2081,9 +2081,9 @@ bool IsPossiblyWrappedTypedArray(JSContext* cx, JSObject* obj, bool* result) {
|
|||
}
|
||||
|
||||
// Called from CreateDependentString::generateFallback.
|
||||
void* AllocateString(JSContext* cx) {
|
||||
void* AllocateDependentString(JSContext* cx) {
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return js::AllocateString<JSString, NoGC>(cx, js::gc::DefaultHeap);
|
||||
return js::AllocateString<JSDependentString, NoGC>(cx, js::gc::DefaultHeap);
|
||||
}
|
||||
void* AllocateFatInlineString(JSContext* cx) {
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
|
|
@ -577,7 +577,7 @@ bool DoConcatStringObject(JSContext* cx, HandleValue lhs, HandleValue rhs,
|
|||
|
||||
bool IsPossiblyWrappedTypedArray(JSContext* cx, JSObject* obj, bool* result);
|
||||
|
||||
void* AllocateString(JSContext* cx);
|
||||
void* AllocateDependentString(JSContext* cx);
|
||||
void* AllocateFatInlineString(JSContext* cx);
|
||||
void* AllocateBigIntNoGC(JSContext* cx, bool requestMinorGC);
|
||||
void AllocateAndInitTypedArrayBuffer(JSContext* cx, TypedArrayObject* obj,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define vm_BigIntType_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/OperatorNewExtensions.h"
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/Span.h"
|
||||
|
||||
|
@ -64,6 +65,10 @@ class BigInt final : public js::gc::CellWithLengthAndFlags {
|
|||
public:
|
||||
static const JS::TraceKind TraceKind = JS::TraceKind::BigInt;
|
||||
|
||||
static BigInt* emplace(js::gc::Cell* cell) {
|
||||
return new (mozilla::KnownNotNull, cell) BigInt();
|
||||
}
|
||||
|
||||
void fixupAfterMovingGC() {}
|
||||
|
||||
js::gc::AllocKind getAllocKind() const { return js::gc::AllocKind::BIGINT; }
|
||||
|
@ -411,7 +416,6 @@ class BigInt final : public js::gc::CellWithLengthAndFlags {
|
|||
friend struct ::JSStructuredCloneReader;
|
||||
friend struct ::JSStructuredCloneWriter;
|
||||
|
||||
BigInt() = delete;
|
||||
BigInt(const BigInt& other) = delete;
|
||||
void operator=(const BigInt& other) = delete;
|
||||
|
||||
|
@ -436,6 +440,10 @@ class BigInt final : public js::gc::CellWithLengthAndFlags {
|
|||
|
||||
private:
|
||||
friend class js::TenuringTracer;
|
||||
|
||||
protected:
|
||||
// For calling by emplace().
|
||||
BigInt() = default;
|
||||
};
|
||||
|
||||
static_assert(
|
||||
|
|
|
@ -555,9 +555,13 @@ class JSObject
|
|||
static constexpr size_t offsetOfShape() { return offsetOfHeaderPtr(); }
|
||||
|
||||
private:
|
||||
JSObject() = delete;
|
||||
JSObject(const JSObject& other) = delete;
|
||||
void operator=(const JSObject& other) = delete;
|
||||
|
||||
protected:
|
||||
// For the allocator only, to be used with placement new.
|
||||
friend class js::gc::GCRuntime;
|
||||
JSObject() = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
|
|
|
@ -706,9 +706,12 @@ class JSString : public js::gc::CellWithLengthAndFlags {
|
|||
}
|
||||
|
||||
private:
|
||||
JSString() = delete;
|
||||
JSString(const JSString& other) = delete;
|
||||
void operator=(const JSString& other) = delete;
|
||||
|
||||
protected:
|
||||
// For calling by concrete subclasses' emplace() static methods.
|
||||
JSString() = default;
|
||||
};
|
||||
|
||||
class JSRope : public JSString {
|
||||
|
@ -785,6 +788,9 @@ class JSRope : public JSString {
|
|||
|
||||
static size_t offsetOfLeft() { return offsetof(JSRope, d.s.u2.left); }
|
||||
static size_t offsetOfRight() { return offsetof(JSRope, d.s.u3.right); }
|
||||
|
||||
public:
|
||||
static JSRope* emplace(Cell* cell) { return new (cell) JSRope(); }
|
||||
};
|
||||
|
||||
static_assert(sizeof(JSRope) == sizeof(JSString),
|
||||
|
@ -829,6 +835,10 @@ class JSLinearString : public JSString {
|
|||
void init(const char16_t* chars, size_t length);
|
||||
void init(const JS::Latin1Char* chars, size_t length);
|
||||
|
||||
static JSLinearString* emplace(Cell* cell) {
|
||||
return new (cell) JSLinearString();
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC, typename CharT>
|
||||
static inline JSLinearString* new_(
|
||||
JSContext* cx, js::UniquePtr<CharT[], JS::FreePolicy> chars,
|
||||
|
@ -988,6 +998,12 @@ class JSDependentString : public JSLinearString {
|
|||
}
|
||||
|
||||
public:
|
||||
static JSDependentString* emplace(Cell* cell) {
|
||||
return new (cell) JSDependentString();
|
||||
}
|
||||
|
||||
// This may return an inline string if the chars fit rather than a dependent
|
||||
// string.
|
||||
static inline JSLinearString* new_(JSContext* cx, JSLinearString* base,
|
||||
size_t start, size_t length,
|
||||
js::gc::InitialHeap heap);
|
||||
|
@ -1036,6 +1052,10 @@ static_assert(sizeof(JSExtensibleString) == sizeof(JSString),
|
|||
|
||||
class JSInlineString : public JSLinearString {
|
||||
public:
|
||||
static JSInlineString* emplace(Cell* cell) {
|
||||
return new (cell) JSInlineString();
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const JS::Latin1Char* latin1Chars(const JS::AutoRequireNoGC& nogc) const {
|
||||
MOZ_ASSERT(JSString::isInline());
|
||||
|
@ -1079,6 +1099,10 @@ class JSThinInlineString : public JSInlineString {
|
|||
static const size_t MAX_LENGTH_LATIN1 = NUM_INLINE_CHARS_LATIN1;
|
||||
static const size_t MAX_LENGTH_TWO_BYTE = NUM_INLINE_CHARS_TWO_BYTE;
|
||||
|
||||
static JSThinInlineString* emplace(Cell* cell) {
|
||||
return new (cell) JSThinInlineString();
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
static inline JSThinInlineString* new_(JSContext* cx,
|
||||
js::gc::InitialHeap heap);
|
||||
|
@ -1120,6 +1144,10 @@ class JSFatInlineString : public JSInlineString {
|
|||
};
|
||||
|
||||
public:
|
||||
static JSFatInlineString* emplace(Cell* cell) {
|
||||
return new (cell) JSFatInlineString();
|
||||
}
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
static inline JSFatInlineString* new_(JSContext* cx,
|
||||
js::gc::InitialHeap heap);
|
||||
|
@ -1156,6 +1184,10 @@ class JSExternalString : public JSLinearString {
|
|||
JSExternalString& asExternal() const = delete;
|
||||
|
||||
public:
|
||||
static JSExternalString* emplace(Cell* cell) {
|
||||
return new (cell) JSExternalString();
|
||||
}
|
||||
|
||||
static inline JSExternalString* new_(
|
||||
JSContext* cx, const char16_t* chars, size_t length,
|
||||
const JSExternalStringCallbacks* callbacks);
|
||||
|
@ -1242,6 +1274,8 @@ class NormalAtom : public JSAtom {
|
|||
HashNumber hash_;
|
||||
|
||||
public:
|
||||
static NormalAtom* emplace(Cell* cell) { return new (cell) NormalAtom(); }
|
||||
|
||||
HashNumber hash() const { return hash_; }
|
||||
void initHash(HashNumber hash) { hash_ = hash; }
|
||||
|
||||
|
@ -1258,6 +1292,11 @@ class FatInlineAtom : public JSAtom {
|
|||
HashNumber hash_;
|
||||
|
||||
public:
|
||||
// For a fresh allocation.
|
||||
static FatInlineAtom* emplace(Cell* cell) {
|
||||
return new (cell) FatInlineAtom();
|
||||
}
|
||||
|
||||
HashNumber hash() const { return hash_; }
|
||||
void initHash(HashNumber hash) { hash_ = hash; }
|
||||
|
||||
|
@ -1289,11 +1328,18 @@ inline void JSAtom::initHash(js::HashNumber hash) {
|
|||
return static_cast<js::NormalAtom*>(this)->initHash(hash);
|
||||
}
|
||||
|
||||
// Note that any pre-existing pointers to this string are invalidated
|
||||
// (UB, though they will most likely be fine.)
|
||||
MOZ_ALWAYS_INLINE JSAtom* JSLinearString::morphAtomizedStringIntoAtom(
|
||||
js::HashNumber hash) {
|
||||
MOZ_ASSERT(!isAtom());
|
||||
setFlagBit(ATOM_BIT);
|
||||
JSAtom* atom = &asAtom();
|
||||
if (isFatInline()) {
|
||||
auto* atom = static_cast<js::FatInlineAtom*>(this);
|
||||
atom->initHash(hash);
|
||||
return atom;
|
||||
}
|
||||
auto* atom = static_cast<js::NormalAtom*>(this);
|
||||
atom->initHash(hash);
|
||||
return atom;
|
||||
}
|
||||
|
@ -1302,7 +1348,12 @@ MOZ_ALWAYS_INLINE JSAtom* JSLinearString::morphAtomizedStringIntoPermanentAtom(
|
|||
js::HashNumber hash) {
|
||||
MOZ_ASSERT(!isAtom());
|
||||
setFlagBit(PERMANENT_ATOM_MASK);
|
||||
JSAtom* atom = &asAtom();
|
||||
if (isFatInline()) {
|
||||
auto* atom = static_cast<js::FatInlineAtom*>(this);
|
||||
atom->initHash(hash);
|
||||
return atom;
|
||||
}
|
||||
auto* atom = static_cast<js::NormalAtom*>(this);
|
||||
atom->initHash(hash);
|
||||
return atom;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче