зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
41fa7887cc
Коммит
326592ff60
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче