Bug 1804253 part 3 - Add NativeShape base class. r=jonco

The `Shape` methods related to property information and number of fixed slots are
moved into `NativeShape`, to improve type safety.

Differential Revision: https://phabricator.services.mozilla.com/D164213
This commit is contained in:
Jan de Mooij 2022-12-13 10:58:29 +00:00
Родитель 41fa7887cc
Коммит 326592ff60
17 изменённых файлов: 201 добавлений и 133 удалений

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

@ -1025,7 +1025,7 @@ static bool TryAssignNative(JSContext* cx, HandleObject to, HandleObject from,
Rooted<PropertyInfoWithKeyVector> props(cx, PropertyInfoWithKeyVector(cx));
Rooted<Shape*> fromShape(cx, fromNative->shape());
Rooted<NativeShape*> fromShape(cx, fromNative->shape());
for (ShapePropertyIter<NoGC> iter(fromShape); !iter.done(); iter++) {
// Symbol properties need to be assigned last. For now fall back to the
// slow path if we see a symbol property.
@ -1715,7 +1715,7 @@ static bool TryEnumerableOwnPropertiesNative(JSContext* cx, HandleObject obj,
Rooted<PropertyInfoWithKeyVector> props(cx, PropertyInfoWithKeyVector(cx));
// Collect all non-symbol properties.
Rooted<Shape*> objShape(cx, nobj->shape());
Rooted<NativeShape*> objShape(cx, nobj->shape());
for (ShapePropertyIter<NoGC> iter(objShape); !iter.done(); iter++) {
if (iter->key().isSymbol()) {
continue;

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

@ -43,7 +43,11 @@ inline void js::BaseScript::traceChildren(JSTracer* trc) {
inline void js::Shape::traceChildren(JSTracer* trc) {
TraceCellHeaderEdge(trc, this, "base");
TraceNullableEdge(trc, &propMap_, "propertymap");
if (isNative()) {
TraceNullableEdge(trc, &nativePropMap_, "propertymap");
} else {
assertHasNoPropMap();
}
}
template <uint32_t opts>
void js::GCMarker::eagerlyMarkChildren(Shape* shape) {
@ -55,8 +59,12 @@ void js::GCMarker::eagerlyMarkChildren(Shape* shape) {
base->traceChildren(tracer());
}
if (PropMap* map = shape->propMap()) {
markAndTraverseEdge<opts>(shape, map);
if (shape->isNative()) {
if (PropMap* map = shape->asNative().propMap()) {
markAndTraverseEdge<opts>(shape, map);
}
} else {
shape->assertHasNoPropMap();
}
}

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

@ -4979,17 +4979,24 @@ AttachDecision SetPropIRGenerator::tryAttachAddSlotStub(
PropertyInfo propInfo = prop.propertyInfo();
NativeObject* holder = nobj;
if (holder->inDictionaryMode()) {
return AttachDecision::NoAction;
}
SharedShape* oldSharedShape = &oldShape->asShared();
// The property must be the last added property of the object.
Shape* newShape = holder->shape();
SharedShape* newShape = holder->sharedShape();
MOZ_RELEASE_ASSERT(newShape->lastProperty() == propInfo);
#ifdef DEBUG
// Verify exactly one property was added by comparing the property map
// lengths.
if (oldShape->propMapLength() == PropMap::Capacity) {
if (oldSharedShape->propMapLength() == PropMap::Capacity) {
MOZ_ASSERT(newShape->propMapLength() == 1);
} else {
MOZ_ASSERT(newShape->propMapLength() == oldShape->propMapLength() + 1);
MOZ_ASSERT(newShape->propMapLength() ==
oldSharedShape->propMapLength() + 1);
}
#endif
@ -4998,14 +5005,11 @@ AttachDecision SetPropIRGenerator::tryAttachAddSlotStub(
JSOp op = JSOp(*pc_);
PropertyFlags flags = SetPropertyFlags(op, isFunctionPrototype);
// Basic shape checks.
if (newShape->isDictionary() || !propInfo.isDataProperty() ||
propInfo.flags() != flags) {
// Basic property checks.
if (!propInfo.isDataProperty() || propInfo.flags() != flags) {
return AttachDecision::NoAction;
}
SharedShape* oldSharedShape = &oldShape->asShared();
ObjOperandId objId = writer.guardToObject(objValId);
maybeEmitIdGuard(id);

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

@ -110,14 +110,15 @@ void CacheIRHealth::spewShapeInformation(AutoStructuredSpewer& spew,
spew->beginListProperty("shapes");
}
const PropMap* propMap = shape->propMap();
const PropMap* propMap =
shape->isNative() ? shape->asNative().propMap() : nullptr;
if (propMap) {
spew->beginObject();
{
if (!propMap->isDictionary()) {
uint32_t mapLength = shape->propMapLength();
uint32_t mapLength = shape->asNative().propMapLength();
if (mapLength) {
PropertyKey lastKey = shape->lastProperty().key();
PropertyKey lastKey = shape->asNative().lastProperty().key();
if (lastKey.isInt()) {
spew->property("lastProperty", lastKey.toInt());
} else if (lastKey.isString()) {

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

@ -4754,7 +4754,7 @@ MObjectState::MObjectState(const Shape* shape)
setRecoveredOnBailout();
numSlots_ = shape->asShared().slotSpan();
numFixedSlots_ = shape->numFixedSlots();
numFixedSlots_ = shape->asShared().numFixedSlots();
}
/* static */

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

@ -2258,8 +2258,8 @@ void MacroAssembler::emitMegamorphicCacheLookup(
// scratch1 = outputScratch->numFixedSlots()
loadPtr(Address(outputScratch, JSObject::offsetOfShape()), scratch1);
load32(Address(scratch1, Shape::offsetOfImmutableFlags()), scratch1);
and32(Imm32(Shape::fixedSlotsMask()), scratch1);
rshift32(Imm32(Shape::fixedSlotsShift()), scratch1);
and32(Imm32(NativeShape::fixedSlotsMask()), scratch1);
rshift32(Imm32(NativeShape::fixedSlotsShift()), scratch1);
// scratch3 = scratch2->slot()
load16ZeroExtend(Address(scratch2, MegamorphicCache::Entry::offsetOfSlot()),
@ -4781,7 +4781,7 @@ void MacroAssembler::debugAssertObjHasFixedSlots(Register obj,
loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
branchTest32(Assembler::NonZero,
Address(scratch, Shape::offsetOfImmutableFlags()),
Imm32(Shape::fixedSlotsMask()), &hasFixedSlots);
Imm32(NativeShape::fixedSlotsMask()), &hasFixedSlots);
assumeUnreachable("Expected a fixed slot");
bind(&hasFixedSlots);
#endif

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

@ -1862,7 +1862,8 @@ bool HasNativeDataPropertyPure(JSContext* cx, JSObject* obj, Value* vp) {
MOZ_ASSERT(!obj->getOpsLookupProperty());
uint32_t index;
if (PropMap* map = obj->shape()->lookup(cx, id, &index)) {
if (PropMap* map =
obj->as<NativeObject>().shape()->lookup(cx, id, &index)) {
if (JitOptions.enableWatchtowerMegamorphic) {
PropertyInfo prop = map->getPropertyInfo(index);
if (prop.isDataProperty()) {
@ -2002,9 +2003,10 @@ static bool TryAddOrSetPlainObjectProperty(JSContext* cx,
if (!proto->is<PlainObject>()) {
return true;
}
if (proto->as<PlainObject>().hasNonWritableOrAccessorPropExclProto()) {
PlainObject* plainProto = &proto->as<PlainObject>();
if (plainProto->hasNonWritableOrAccessorPropExclProto()) {
uint32_t index;
if (PropMap* map = proto->shape()->lookup(cx, key, &index)) {
if (PropMap* map = plainProto->shape()->lookup(cx, key, &index)) {
PropertyInfo prop = map->getPropertyInfo(index);
if (!prop.isDataProperty() || !prop.writable()) {
return true;
@ -2012,7 +2014,7 @@ static bool TryAddOrSetPlainObjectProperty(JSContext* cx,
break;
}
}
proto = proto->as<PlainObject>().staticPrototype();
proto = plainProto->staticPrototype();
}
#ifdef DEBUG

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

@ -355,7 +355,7 @@ BEGIN_TEST(testGCHandleHashMap) {
}
END_TEST(testGCHandleHashMap)
using ShapeVec = GCVector<Shape*>;
using ShapeVec = GCVector<NativeShape*>;
BEGIN_TEST(testGCRootedVector) {
JS::Rooted<ShapeVec> shapes(cx, cx);
@ -369,7 +369,7 @@ BEGIN_TEST(testGCRootedVector) {
buffer[0] = 'a' + i;
buffer[1] = '\0';
CHECK(JS_SetProperty(cx, obj, buffer, val));
CHECK(shapes.append(obj->shape()));
CHECK(shapes.append(obj->as<NativeObject>().shape()));
}
JS_GC(cx);
@ -398,7 +398,8 @@ BEGIN_TEST(testGCRootedVector) {
return true;
}
bool receiveConstRefToShapeVector(const JS::Rooted<GCVector<Shape*>>& rooted) {
bool receiveConstRefToShapeVector(
const JS::Rooted<GCVector<NativeShape*>>& rooted) {
// Ensure range enumeration works through the reference.
for (auto shape : rooted) {
CHECK(shape);
@ -406,7 +407,7 @@ bool receiveConstRefToShapeVector(const JS::Rooted<GCVector<Shape*>>& rooted) {
return true;
}
bool receiveHandleToShapeVector(JS::Handle<GCVector<Shape*>> handle) {
bool receiveHandleToShapeVector(JS::Handle<GCVector<NativeShape*>> handle) {
// Ensure range enumeration works through the handle.
for (auto shape : handle) {
CHECK(shape);
@ -415,7 +416,7 @@ bool receiveHandleToShapeVector(JS::Handle<GCVector<Shape*>> handle) {
}
bool receiveMutableHandleToShapeVector(
JS::MutableHandle<GCVector<Shape*>> handle) {
JS::MutableHandle<GCVector<NativeShape*>> handle) {
// Ensure range enumeration works through the handle.
for (auto shape : handle) {
CHECK(shape);
@ -425,7 +426,7 @@ bool receiveMutableHandleToShapeVector(
END_TEST(testGCRootedVector)
BEGIN_TEST(testTraceableFifo) {
using ShapeFifo = TraceableFifo<Shape*>;
using ShapeFifo = TraceableFifo<NativeShape*>;
JS::Rooted<ShapeFifo> shapes(cx, ShapeFifo(cx));
CHECK(shapes.empty());
@ -438,7 +439,7 @@ BEGIN_TEST(testTraceableFifo) {
buffer[0] = 'a' + i;
buffer[1] = '\0';
CHECK(JS_SetProperty(cx, obj, buffer, val));
CHECK(shapes.pushBack(obj->shape()));
CHECK(shapes.pushBack(obj->as<NativeObject>().shape()));
}
CHECK(shapes.length() == 10);
@ -461,7 +462,7 @@ BEGIN_TEST(testTraceableFifo) {
}
END_TEST(testTraceableFifo)
using ShapeVec = GCVector<Shape*>;
using ShapeVec = GCVector<NativeShape*>;
static bool FillVector(JSContext* cx, MutableHandle<ShapeVec> shapes) {
for (size_t i = 0; i < 10; ++i) {
@ -475,7 +476,7 @@ static bool FillVector(JSContext* cx, MutableHandle<ShapeVec> shapes) {
if (!JS_SetProperty(cx, obj, buffer, val)) {
return false;
}
if (!shapes.append(obj->shape())) {
if (!shapes.append(obj->as<NativeObject>().shape())) {
return false;
}
}

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

@ -611,7 +611,7 @@ struct ColorCheckFunctor {
return false;
}
Shape* shape = nobj.shape();
NativeShape* shape = nobj.shape();
if (!CheckCellColor(shape, color)) {
return false;
}

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

@ -2685,7 +2685,8 @@ void GetObjectSlotNameFunctor::operator()(JS::TracingContext* tcx, char* buf,
Maybe<PropertyKey> key;
if (obj->is<NativeObject>()) {
for (ShapePropertyIter<NoGC> iter(obj->shape()); !iter.done(); iter++) {
NativeShape* shape = obj->as<NativeObject>().shape();
for (ShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) {
if (iter->hasSlot() && iter->slot() == slot) {
key.emplace(iter->key());
break;
@ -3580,11 +3581,12 @@ void JSObject::debugCheckNewObject(Shape* shape, js::gc::AllocKind allocKind,
const JSClass* clasp = shape->getObjectClass();
if (!ClassCanHaveFixedData(clasp)) {
NativeShape* nshape = &shape->asNative();
if (clasp == &ArrayObject::class_) {
// Arrays can store the ObjectElements header inline.
MOZ_ASSERT(shape->numFixedSlots() == 0);
MOZ_ASSERT(nshape->numFixedSlots() == 0);
} else {
MOZ_ASSERT(gc::GetGCKindSlots(allocKind) == shape->numFixedSlots());
MOZ_ASSERT(gc::GetGCKindSlots(allocKind) == nshape->numFixedSlots());
}
}
@ -3620,7 +3622,6 @@ void JSObject::debugCheckNewObject(Shape* shape, js::gc::AllocKind allocKind,
// included in numFixedSlots.
if (!clasp->isNativeObject()) {
MOZ_ASSERT_IF(!clasp->isProxyObject(), JSCLASS_RESERVED_SLOTS(clasp) == 0);
MOZ_ASSERT_IF(shape, shape->numFixedSlots() == 0);
}
}
#endif

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

@ -41,7 +41,7 @@ extern bool js::IsExtendedPrimitive(const JSObject& obj);
namespace js {
inline uint32_t NativeObject::numFixedSlotsMaybeForwarded() const {
return gc::MaybeForwarded(shape())->numFixedSlots();
return gc::MaybeForwarded(JSObject::shape())->asNative().numFixedSlots();
}
inline uint8_t* NativeObject::fixedData(size_t nslots) const {

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

@ -1338,12 +1338,12 @@ static MOZ_ALWAYS_INLINE bool AddDataProperty(JSContext* cx,
bool js::AddSlotAndCallAddPropHook(JSContext* cx, Handle<NativeObject*> obj,
HandleValue v, Handle<Shape*> newShape) {
MOZ_ASSERT(obj->getClass()->getAddProperty());
MOZ_ASSERT(newShape->lastProperty().isDataProperty());
MOZ_ASSERT(newShape->asShared().lastProperty().isDataProperty());
RootedId id(cx, newShape->lastProperty().key());
RootedId id(cx, newShape->asShared().lastProperty().key());
MOZ_ASSERT(!id.isInt());
uint32_t slot = newShape->lastProperty().slot();
uint32_t slot = newShape->asShared().lastProperty().slot();
if (!obj->setShapeAndAddNewSlot(cx, &newShape->asShared(), slot)) {
return false;
}
@ -2737,7 +2737,7 @@ bool js::CopyDataPropertiesNative(JSContext* cx, Handle<PlainObject*> target,
// Collect all enumerable data properties.
Rooted<PropertyInfoWithKeyVector> props(cx, PropertyInfoWithKeyVector(cx));
Rooted<Shape*> fromShape(cx, from->shape());
Rooted<NativeShape*> fromShape(cx, from->shape());
for (ShapePropertyIter<NoGC> iter(fromShape); !iter.done(); iter++) {
jsid id = iter->key();
MOZ_ASSERT(!id.isInt());

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

@ -587,6 +587,7 @@ class NativeObject : public JSObject {
}
public:
NativeShape* shape() const { return &JSObject::shape()->asNative(); }
SharedShape* sharedShape() const { return &shape()->asShared(); }
DictionaryShape* dictionaryShape() const { return &shape()->asDictionary(); }
@ -623,10 +624,12 @@ class NativeObject : public JSObject {
MOZ_ALWAYS_INLINE bool setShapeAndAddNewSlot(JSContext* cx,
SharedShape* newShape,
uint32_t slot);
void setShapeAndRemoveLastSlot(JSContext* cx, Shape* newShape, uint32_t slot);
void setShapeAndRemoveLastSlot(JSContext* cx, SharedShape* newShape,
uint32_t slot);
MOZ_ALWAYS_INLINE bool canReuseShapeForNewProperties(Shape* newShape) const {
Shape* oldShape = shape();
MOZ_ALWAYS_INLINE bool canReuseShapeForNewProperties(
NativeShape* newShape) const {
NativeShape* oldShape = shape();
MOZ_ASSERT(oldShape->propMapLength() == 0,
"object must have no properties");
MOZ_ASSERT(newShape->propMapLength() > 0,

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

@ -100,8 +100,8 @@ PlainObject* PlainObject::createWithTemplateFromDifferentRealm(
MOZ_ASSERT(!templateObject->shape()->isDictionary());
TaggedProto proto = TaggedProto(nullptr);
Shape* templateShape = templateObject->shape();
Rooted<SharedPropMap*> map(cx, templateShape->propMap()->asShared());
SharedShape* templateShape = templateObject->sharedShape();
Rooted<SharedPropMap*> map(cx, templateShape->propMap());
Rooted<SharedShape*> shape(
cx, SharedShape::getInitialOrPropMapShape(

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

@ -47,15 +47,16 @@ template <class ObjectSubclass>
return true;
}
MOZ_ALWAYS_INLINE PropMap* Shape::lookup(JSContext* cx, PropertyKey key,
uint32_t* index) {
MOZ_ALWAYS_INLINE PropMap* NativeShape::lookup(JSContext* cx, PropertyKey key,
uint32_t* index) {
uint32_t len = propMapLength();
return len > 0 ? propMap_->lookup(cx, len, key, index) : nullptr;
return len > 0 ? nativePropMap_->lookup(cx, len, key, index) : nullptr;
}
MOZ_ALWAYS_INLINE PropMap* Shape::lookupPure(PropertyKey key, uint32_t* index) {
MOZ_ALWAYS_INLINE PropMap* NativeShape::lookupPure(PropertyKey key,
uint32_t* index) {
uint32_t len = propMapLength();
return len > 0 ? propMap_->lookupPure(len, key, index) : nullptr;
return len > 0 ? nativePropMap_->lookupPure(len, key, index) : nullptr;
}
inline void Shape::purgeCache(JS::GCContext* gcx) {
@ -73,7 +74,7 @@ inline void Shape::finalize(JS::GCContext* gcx) {
inline SharedPropMap* SharedShape::propMapMaybeForwarded() const {
MOZ_ASSERT(isShared());
PropMap* propMap = propMap_;
PropMap* propMap = nativePropMap_;
return propMap ? MaybeForwarded(propMap)->asShared() : nullptr;
}

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

@ -36,8 +36,8 @@ bool Shape::replaceShape(JSContext* cx, HandleObject obj,
Shape* newShape;
switch (obj->shape()->kind()) {
case Kind::Shared: {
if (obj->shape()->propMap()) {
Handle<NativeObject*> nobj = obj.as<NativeObject>();
Handle<NativeObject*> nobj = obj.as<NativeObject>();
if (nobj->shape()->propMap()) {
Rooted<BaseShape*> base(cx, obj->shape()->base());
if (proto != base->proto()) {
Rooted<TaggedProto> protoRoot(cx, proto);
@ -47,7 +47,7 @@ bool Shape::replaceShape(JSContext* cx, HandleObject obj,
}
}
Rooted<SharedPropMap*> map(cx, nobj->sharedShape()->propMap());
uint32_t mapLength = obj->shape()->propMapLength();
uint32_t mapLength = nobj->shape()->propMapLength();
newShape = SharedShape::getPropMapShape(cx, base, nfixed, map,
mapLength, objectFlags);
} else {
@ -86,7 +86,7 @@ bool js::NativeObject::toDictionaryMode(JSContext* cx,
MOZ_ASSERT(!obj->inDictionaryMode());
MOZ_ASSERT(cx->isInsideCurrentCompartment(obj));
Rooted<Shape*> shape(cx, obj->shape());
Rooted<NativeShape*> shape(cx, obj->shape());
uint32_t span = obj->slotSpan();
uint32_t mapLength = shape->propMapLength();
@ -737,11 +737,12 @@ void NativeObject::maybeFreeDictionaryPropSlots(JSContext* cx,
map->setFreeList(SHAPE_INVALID_SLOT);
}
void NativeObject::setShapeAndRemoveLastSlot(JSContext* cx, Shape* newShape,
void NativeObject::setShapeAndRemoveLastSlot(JSContext* cx,
SharedShape* newShape,
uint32_t slot) {
MOZ_ASSERT(!inDictionaryMode());
MOZ_ASSERT(!newShape->isDictionary());
MOZ_ASSERT(newShape->asShared().slotSpan() == slot);
MOZ_ASSERT(newShape->isShared());
MOZ_ASSERT(newShape->slotSpan() == slot);
uint32_t numFixed = newShape->numFixedSlots();
if (slot < numFixed) {
@ -995,17 +996,21 @@ bool JSObject::setFlag(JSContext* cx, HandleObject obj, ObjectFlag flag) {
ObjectFlags objectFlags = obj->shape()->objectFlags();
objectFlags.setFlag(flag);
if (obj->is<NativeObject>() && obj->as<NativeObject>().inDictionaryMode()) {
uint32_t numFixed = 0;
if (obj->is<NativeObject>()) {
Handle<NativeObject*> nobj = obj.as<NativeObject>();
if (!NativeObject::generateNewDictionaryShape(cx, nobj)) {
return false;
if (nobj->inDictionaryMode()) {
if (!NativeObject::generateNewDictionaryShape(cx, nobj)) {
return false;
}
nobj->dictionaryShape()->setObjectFlagsOfNewShape(objectFlags);
return true;
}
nobj->dictionaryShape()->setObjectFlagsOfNewShape(objectFlags);
return true;
numFixed = nobj->numFixedSlots();
}
return Shape::replaceShape(cx, obj, objectFlags, obj->shape()->proto(),
obj->shape()->numFixedSlots());
numFixed);
}
static bool SetObjectIsUsedAsPrototype(JSContext* cx, Handle<JSObject*> proto) {
@ -1042,24 +1047,28 @@ bool JSObject::setProtoUnchecked(JSContext* cx, HandleObject obj,
}
}
if (obj->is<NativeObject>() && obj->as<NativeObject>().inDictionaryMode()) {
uint32_t numFixed = 0;
if (obj->is<NativeObject>()) {
Handle<NativeObject*> nobj = obj.as<NativeObject>();
Rooted<BaseShape*> nbase(
cx, BaseShape::get(cx, nobj->getClass(), nobj->realm(), proto));
if (!nbase) {
return false;
}
if (nobj->inDictionaryMode()) {
Rooted<BaseShape*> nbase(
cx, BaseShape::get(cx, nobj->getClass(), nobj->realm(), proto));
if (!nbase) {
return false;
}
if (!NativeObject::generateNewDictionaryShape(cx, nobj)) {
return false;
}
if (!NativeObject::generateNewDictionaryShape(cx, nobj)) {
return false;
}
nobj->dictionaryShape()->setBaseOfNewShape(nbase);
return true;
nobj->dictionaryShape()->setBaseOfNewShape(nbase);
return true;
}
numFixed = nobj->numFixedSlots();
}
return Shape::replaceShape(cx, obj, obj->shape()->objectFlags(), proto,
obj->shape()->numFixedSlots());
numFixed);
}
/* static */
@ -1167,13 +1176,28 @@ MOZ_ALWAYS_INLINE bool ShapeForAddHasher::match(SharedShape* shape,
void Shape::dump(js::GenericPrinter& out) const {
out.printf("shape @ 0x%p\n", this);
out.printf("base: 0x%p\n", base());
out.printf("mapLength: %u\n", propMapLength());
out.printf("dictionary: %s\n", isDictionary() ? "yes" : "no");
if (propMap_) {
out.printf("map:\n");
propMap_->dump(out);
} else {
out.printf("map: (none)\n");
switch (kind()) {
case Kind::Shared:
out.printf("kind: Shared\n");
break;
case Kind::Dictionary:
out.printf("kind: Dictionary\n");
break;
case Kind::Proxy:
out.printf("kind: Proxy\n");
break;
case Kind::WasmGC:
out.printf("kind: WasmGC\n");
break;
}
if (isNative()) {
out.printf("mapLength: %u\n", asNative().propMapLength());
if (nativePropMap_) {
out.printf("map:\n");
nativePropMap_->dump(out);
} else {
out.printf("map: (none)\n");
}
}
}

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

@ -90,6 +90,7 @@ MOZ_ALWAYS_INLINE size_t JSSLOT_FREE(const JSClass* clasp) {
namespace js {
class NativeShape;
class Shape;
class PropertyIteratorObject;
@ -322,7 +323,7 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
// The shape's property map. This is either nullptr for shared initial (empty)
// shapes, a SharedPropMap for SharedPropMap shapes, or a DictionaryPropMap
// for dictionary shapes.
GCPtr<PropMap*> propMap_;
GCPtr<PropMap*> nativePropMap_;
// Cache used to speed up common operations on shapes.
ShapeCachePtr cache_;
@ -342,24 +343,12 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
}
}
PropMap* propMap() const { return propMap_; }
ShapeCachePtr& cacheRef() { return cache_; }
ShapeCachePtr cache() const { return cache_; }
void maybeCacheIterator(JSContext* cx, PropertyIteratorObject* iter);
uint32_t propMapLength() const { return immutableFlags & MAP_LENGTH_MASK; }
PropertyInfoWithKey lastProperty() const {
MOZ_ASSERT(propMapLength() > 0);
size_t index = propMapLength() - 1;
return propMap()->getPropertyInfoWithKey(index);
}
MOZ_ALWAYS_INLINE PropMap* lookup(JSContext* cx, PropertyKey key,
uint32_t* index);
MOZ_ALWAYS_INLINE PropMap* lookupPure(PropertyKey key, uint32_t* index);
void assertHasNoPropMap() const { MOZ_ASSERT(!nativePropMap_); }
const JSClass* getObjectClass() const { return base()->clasp(); }
JS::Realm* realm() const { return base()->realm(); }
@ -377,15 +366,11 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
}
protected:
Shape(Kind kind, BaseShape* base, ObjectFlags objectFlags, uint32_t nfixed,
PropMap* map, uint32_t mapLength)
Shape(Kind kind, BaseShape* base, ObjectFlags objectFlags)
: CellWithTenuredGCPointer(base),
immutableFlags((nfixed << FIXED_SLOTS_SHIFT) |
(uint32_t(kind) << KIND_SHIFT) | mapLength),
objectFlags_(objectFlags),
propMap_(map) {
immutableFlags(uint32_t(kind) << KIND_SHIFT),
objectFlags_(objectFlags) {
MOZ_ASSERT(base);
MOZ_ASSERT(mapLength <= PropMap::Capacity);
MOZ_ASSERT(this->kind() == kind, "kind must fit in KIND_MASK");
}
@ -400,17 +385,14 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
bool isProxy() const { return kind() == Kind::Proxy; }
bool isWasmGC() const { return kind() == Kind::WasmGC; }
inline NativeShape& asNative();
inline SharedShape& asShared();
inline DictionaryShape& asDictionary();
inline const NativeShape& asNative() const;
inline const SharedShape& asShared() const;
inline const DictionaryShape& asDictionary() const;
uint32_t numFixedSlots() const {
return (immutableFlags & FIXED_SLOTS_MASK) >> FIXED_SLOTS_SHIFT;
}
public:
#ifdef DEBUG
void dump(js::GenericPrinter& out) const;
void dump() const;
@ -433,8 +415,6 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
static inline size_t offsetOfImmutableFlags() {
return offsetof(Shape, immutableFlags);
}
static inline uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; }
static inline uint32_t fixedSlotsShift() { return FIXED_SLOTS_SHIFT; }
static constexpr size_t offsetOfCachePtr() { return offsetof(Shape, cache_); }
@ -454,13 +434,47 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
}
};
// Shared or dictionary shape for a NativeObject.
class NativeShape : public Shape {
protected:
NativeShape(Kind kind, BaseShape* base, ObjectFlags objectFlags,
uint32_t nfixed, PropMap* map, uint32_t mapLength)
: Shape(kind, base, objectFlags) {
MOZ_ASSERT(base->clasp()->isNativeObject());
MOZ_ASSERT(mapLength <= PropMap::Capacity);
immutableFlags |= (nfixed << FIXED_SLOTS_SHIFT) | mapLength;
nativePropMap_ = map;
}
public:
PropMap* propMap() const { return nativePropMap_; }
uint32_t propMapLength() const { return immutableFlags & MAP_LENGTH_MASK; }
PropertyInfoWithKey lastProperty() const {
MOZ_ASSERT(propMapLength() > 0);
size_t index = propMapLength() - 1;
return propMap()->getPropertyInfoWithKey(index);
}
MOZ_ALWAYS_INLINE PropMap* lookup(JSContext* cx, PropertyKey key,
uint32_t* index);
MOZ_ALWAYS_INLINE PropMap* lookupPure(PropertyKey key, uint32_t* index);
uint32_t numFixedSlots() const {
return (immutableFlags & FIXED_SLOTS_MASK) >> FIXED_SLOTS_SHIFT;
}
// For JIT usage.
static constexpr uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; }
static constexpr uint32_t fixedSlotsShift() { return FIXED_SLOTS_SHIFT; }
};
// Shared shape for a NativeObject.
class SharedShape : public Shape {
class SharedShape : public NativeShape {
friend class js::gc::CellAllocator;
SharedShape(BaseShape* base, ObjectFlags objectFlags, uint32_t nfixed,
PropMap* map, uint32_t mapLength)
: Shape(Kind::Shared, base, objectFlags, nfixed, map, mapLength) {
MOZ_ASSERT(base->clasp()->isNativeObject());
: NativeShape(Kind::Shared, base, objectFlags, nfixed, map, mapLength) {
initSmallSlotSpan();
}
@ -481,7 +495,7 @@ class SharedShape : public Shape {
public:
SharedPropMap* propMap() const {
MOZ_ASSERT(isShared());
return propMap_ ? propMap_->asShared() : nullptr;
return nativePropMap_ ? nativePropMap_->asShared() : nullptr;
}
inline SharedPropMap* propMapMaybeForwarded() const;
@ -509,7 +523,6 @@ class SharedShape : public Shape {
}
uint32_t slotSpan() const {
MOZ_ASSERT(isShared());
MOZ_ASSERT(getObjectClass()->isNativeObject());
uint32_t span =
(immutableFlags & SMALL_SLOTSPAN_MASK) >> SMALL_SLOTSPAN_SHIFT;
if (MOZ_LIKELY(span < SMALL_SLOTSPAN_MAX)) {
@ -565,16 +578,16 @@ class SharedShape : public Shape {
};
// Dictionary shape for a NativeObject.
class DictionaryShape : public Shape {
class DictionaryShape : public NativeShape {
friend class ::JSObject;
friend class js::gc::CellAllocator;
friend class NativeObject;
DictionaryShape(BaseShape* base, ObjectFlags objectFlags, uint32_t nfixed,
PropMap* map, uint32_t mapLength)
: Shape(Kind::Dictionary, base, objectFlags, nfixed, map, mapLength) {
: NativeShape(Kind::Dictionary, base, objectFlags, nfixed, map,
mapLength) {
MOZ_ASSERT(map);
MOZ_ASSERT(base->clasp()->isNativeObject());
}
// Methods to set fields of a new dictionary shape. Must not be used for
@ -583,7 +596,7 @@ class DictionaryShape : public Shape {
uint32_t mapLength) {
MOZ_ASSERT(isDictionary());
objectFlags_ = flags;
propMap_ = map;
nativePropMap_ = map;
immutableFlags = (immutableFlags & ~MAP_LENGTH_MASK) | mapLength;
MOZ_ASSERT(propMapLength() == mapLength);
}
@ -611,8 +624,8 @@ class DictionaryShape : public Shape {
DictionaryPropMap* propMap() const {
MOZ_ASSERT(isDictionary());
MOZ_ASSERT(propMap_);
return propMap_->asDictionary();
MOZ_ASSERT(nativePropMap_);
return nativePropMap_->asDictionary();
}
};
@ -620,7 +633,7 @@ class DictionaryShape : public Shape {
class ProxyShape : public Shape {
friend class js::gc::CellAllocator;
ProxyShape(BaseShape* base, ObjectFlags objectFlags)
: Shape(Kind::Proxy, base, objectFlags, 0, nullptr, 0) {
: Shape(Kind::Proxy, base, objectFlags) {
MOZ_ASSERT(base->clasp()->isProxyObject());
}
@ -637,7 +650,7 @@ class ProxyShape : public Shape {
class WasmGCShape : public Shape {
friend class js::gc::CellAllocator;
WasmGCShape(BaseShape* base, ObjectFlags objectFlags)
: Shape(Kind::WasmGC, base, objectFlags, 0, nullptr, 0) {
: Shape(Kind::WasmGC, base, objectFlags) {
MOZ_ASSERT(!base->clasp()->isProxyObject());
MOZ_ASSERT(!base->clasp()->isNativeObject());
}
@ -651,6 +664,11 @@ class WasmGCShape : public Shape {
ObjectFlags objectFlags);
};
inline NativeShape& js::Shape::asNative() {
MOZ_ASSERT(isNative());
return *static_cast<NativeShape*>(this);
}
inline SharedShape& js::Shape::asShared() {
MOZ_ASSERT(isShared());
return *static_cast<SharedShape*>(this);
@ -661,6 +679,11 @@ inline DictionaryShape& js::Shape::asDictionary() {
return *static_cast<DictionaryShape*>(this);
}
inline const NativeShape& js::Shape::asNative() const {
MOZ_ASSERT(isNative());
return *static_cast<const NativeShape*>(this);
}
inline const SharedShape& js::Shape::asShared() const {
MOZ_ASSERT(isShared());
return *static_cast<const SharedShape*>(this);
@ -687,28 +710,28 @@ class MOZ_RAII ShapePropertyIter {
const bool isDictionary_;
protected:
ShapePropertyIter(JSContext* cx, Shape* shape, bool isDictionary)
ShapePropertyIter(JSContext* cx, NativeShape* shape, bool isDictionary)
: map_(cx, shape->propMap()),
mapLength_(shape->propMapLength()),
isDictionary_(isDictionary) {
static_assert(allowGC == CanGC);
MOZ_ASSERT(shape->isDictionary() == isDictionary);
MOZ_ASSERT(shape->getObjectClass()->isNativeObject());
MOZ_ASSERT(shape->isNative());
}
ShapePropertyIter(Shape* shape, bool isDictionary)
ShapePropertyIter(NativeShape* shape, bool isDictionary)
: map_(nullptr, shape->propMap()),
mapLength_(shape->propMapLength()),
isDictionary_(isDictionary) {
static_assert(allowGC == NoGC);
MOZ_ASSERT(shape->isDictionary() == isDictionary);
MOZ_ASSERT(shape->getObjectClass()->isNativeObject());
MOZ_ASSERT(shape->isNative());
}
public:
ShapePropertyIter(JSContext* cx, Shape* shape)
ShapePropertyIter(JSContext* cx, NativeShape* shape)
: ShapePropertyIter(cx, shape, shape->isDictionary()) {}
explicit ShapePropertyIter(Shape* shape)
explicit ShapePropertyIter(NativeShape* shape)
: ShapePropertyIter(shape, shape->isDictionary()) {}
// Deleted constructors: use SharedShapePropertyIter instead.