зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1700052 part 11 - Store getter/setter objects in slots instead of in the shape tree. r=jonco,evilpie
There will be additional cleanup of the Shape code in follow-up patches. Differential Revision: https://phabricator.services.mozilla.com/D110258
This commit is contained in:
Родитель
1001dc3ad6
Коммит
b4ce5b51de
|
@ -1291,15 +1291,7 @@ void Shape::traceChildren(JSTracer* trc) {
|
|||
dictNext.setObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
cache_.trace(trc);
|
||||
|
||||
if (hasGetterObject()) {
|
||||
TraceManuallyBarrieredEdge(trc, &asAccessorShape().getter_, "getter");
|
||||
}
|
||||
if (hasSetterObject()) {
|
||||
TraceManuallyBarrieredEdge(trc, &asAccessorShape().setter_, "setter");
|
||||
}
|
||||
}
|
||||
inline void js::GCMarker::eagerlyMarkChildren(Shape* shape) {
|
||||
MOZ_ASSERT(shape->isMarked(markColor()));
|
||||
|
@ -1325,16 +1317,6 @@ inline void js::GCMarker::eagerlyMarkChildren(Shape* shape) {
|
|||
// be traced by this loop they do not need to be traced here as well.
|
||||
MOZ_ASSERT(shape->canSkipMarkingShapeCache());
|
||||
|
||||
// When triggered between slices on behalf of a barrier, these
|
||||
// objects may reside in the nursery, so require an extra check.
|
||||
// FIXME: Bug 1157967 - remove the isTenured checks.
|
||||
if (shape->hasGetterObject() && shape->getterObject()->isTenured()) {
|
||||
markAndTraverseEdge(shape, shape->getterObject());
|
||||
}
|
||||
if (shape->hasSetterObject() && shape->setterObject()->isTenured()) {
|
||||
markAndTraverseEdge(shape, shape->setterObject());
|
||||
}
|
||||
|
||||
shape = shape->previous();
|
||||
} while (shape && mark(shape));
|
||||
}
|
||||
|
|
|
@ -236,14 +236,6 @@ void StackShape::trace(JSTracer* trc) {
|
|||
}
|
||||
|
||||
TraceRoot(trc, (jsid*)&propid, "StackShape id");
|
||||
|
||||
if (getter) {
|
||||
TraceRoot(trc, &getter, "StackShape getter");
|
||||
}
|
||||
|
||||
if (setter) {
|
||||
TraceRoot(trc, &setter, "StackShape setter");
|
||||
}
|
||||
}
|
||||
|
||||
void StackBaseShape::trace(JSTracer* trc) { proto.trace(trc); }
|
||||
|
|
|
@ -101,18 +101,6 @@ void gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape) {
|
|||
|
||||
// Don't trace the propid because the CC doesn't care about jsid.
|
||||
|
||||
if (shape->hasGetterObject()) {
|
||||
JSObject* tmp = shape->getterObject();
|
||||
TraceEdgeInternal(trc, &tmp, "getter");
|
||||
MOZ_ASSERT(tmp == shape->getterObject());
|
||||
}
|
||||
|
||||
if (shape->hasSetterObject()) {
|
||||
JSObject* tmp = shape->setterObject();
|
||||
TraceEdgeInternal(trc, &tmp, "setter");
|
||||
MOZ_ASSERT(tmp == shape->setterObject());
|
||||
}
|
||||
|
||||
shape = shape->previous();
|
||||
} while (shape);
|
||||
}
|
||||
|
|
|
@ -877,12 +877,39 @@ static void EmitCallGetterResultNoGuards(JSContext* cx, CacheIRWriter& writer,
|
|||
}
|
||||
}
|
||||
|
||||
// See the SMDOC comment in vm/GetterSetter.h for more info on Getter/Setter
|
||||
// properties
|
||||
static void EmitGuardGetterSetterSlot(CacheIRWriter& writer,
|
||||
NativeObject* holder, Shape* shape,
|
||||
ObjOperandId holderId,
|
||||
bool holderIsConstant = false) {
|
||||
// If the holder is guaranteed to be the same object, and it never had a
|
||||
// slot holding a GetterSetter mutated or deleted, its Shape will change when
|
||||
// that does happen so we don't need to guard on the GetterSetter.
|
||||
if (holderIsConstant && !holder->hadGetterSetterChange()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t slot = shape->slot();
|
||||
Value slotVal = holder->getSlot(slot);
|
||||
MOZ_ASSERT(slotVal.isPrivateGCThing());
|
||||
|
||||
if (holder->isFixedSlot(slot)) {
|
||||
size_t offset = NativeObject::getFixedSlotOffset(slot);
|
||||
writer.guardFixedSlotValue(holderId, offset, slotVal);
|
||||
} else {
|
||||
size_t offset = holder->dynamicSlotIndex(slot) * sizeof(Value);
|
||||
writer.guardDynamicSlotValue(holderId, offset, slotVal);
|
||||
}
|
||||
}
|
||||
|
||||
static void EmitCallGetterResultGuards(CacheIRWriter& writer, NativeObject* obj,
|
||||
NativeObject* holder, Shape* shape,
|
||||
ObjOperandId objId, ICState::Mode mode) {
|
||||
// Use the megamorphic guard if we're in megamorphic mode, except if |obj|
|
||||
// is a Window as GuardHasGetterSetter doesn't support this yet (Window may
|
||||
// require outerizing).
|
||||
|
||||
if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
|
||||
TestMatchingNativeReceiver(writer, obj, objId);
|
||||
|
||||
|
@ -892,9 +919,15 @@ static void EmitCallGetterResultGuards(CacheIRWriter& writer, NativeObject* obj,
|
|||
// Guard on the holder's shape.
|
||||
ObjOperandId holderId = writer.loadObject(holder);
|
||||
TestMatchingHolder(writer, holder, holderId);
|
||||
|
||||
EmitGuardGetterSetterSlot(writer, holder, shape, holderId,
|
||||
/* holderIsConstant = */ true);
|
||||
} else {
|
||||
EmitGuardGetterSetterSlot(writer, holder, shape, objId);
|
||||
}
|
||||
} else {
|
||||
writer.guardHasGetterSetter(objId, shape->propid(), shape);
|
||||
GetterSetter* gs = holder->getGetterSetter(shape);
|
||||
writer.guardHasGetterSetter(objId, shape->propid(), gs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1495,6 +1528,8 @@ AttachDecision GetPropIRGenerator::tryAttachDOMProxyExpando(
|
|||
// and not the expando object.
|
||||
MOZ_ASSERT(canCache == CanAttachNativeGetter ||
|
||||
canCache == CanAttachScriptedGetter);
|
||||
EmitGuardGetterSetterSlot(writer, nativeExpandoObj, propShape,
|
||||
expandoObjId);
|
||||
EmitCallGetterResultNoGuards(cx_, writer, nativeExpandoObj,
|
||||
nativeExpandoObj, propShape, receiverId);
|
||||
}
|
||||
|
@ -1598,6 +1633,8 @@ AttachDecision GetPropIRGenerator::tryAttachDOMProxyUnshadowed(
|
|||
MOZ_ASSERT(canCache == CanAttachNativeGetter ||
|
||||
canCache == CanAttachScriptedGetter);
|
||||
MOZ_ASSERT(!isSuper());
|
||||
EmitGuardGetterSetterSlot(writer, holder, shape, holderId,
|
||||
/* holderIsConstant = */ true);
|
||||
EmitCallGetterResultNoGuards(cx_, writer, nativeCheckObj, holder, shape,
|
||||
receiverId);
|
||||
}
|
||||
|
@ -2822,6 +2859,13 @@ AttachDecision GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId,
|
|||
// Shape guard holder.
|
||||
ObjOperandId holderId = writer.loadObject(holder);
|
||||
writer.guardShape(holderId, holder->lastProperty());
|
||||
EmitGuardGetterSetterSlot(writer, holder, shape, holderId,
|
||||
/* holderIsConstant = */ true);
|
||||
} else {
|
||||
// Note: pass true for |holderIsConstant| because the holder must be the
|
||||
// current global object.
|
||||
EmitGuardGetterSetterSlot(writer, holder, shape, globalId,
|
||||
/* holderIsConstant = */ true);
|
||||
}
|
||||
|
||||
if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, global, holder, shape,
|
||||
|
@ -3842,9 +3886,15 @@ AttachDecision SetPropIRGenerator::tryAttachSetter(HandleObject obj,
|
|||
// Guard on the holder's shape.
|
||||
ObjOperandId holderId = writer.loadObject(holder);
|
||||
TestMatchingHolder(writer, holder, holderId);
|
||||
|
||||
EmitGuardGetterSetterSlot(writer, holder, propShape, holderId,
|
||||
/* holderIsConstant = */ true);
|
||||
} else {
|
||||
EmitGuardGetterSetterSlot(writer, holder, propShape, objId);
|
||||
}
|
||||
} else {
|
||||
writer.guardHasGetterSetter(objId, id, propShape);
|
||||
GetterSetter* gs = holder->getGetterSetter(propShape);
|
||||
writer.guardHasGetterSetter(objId, id, gs);
|
||||
}
|
||||
|
||||
if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Setter, nobj, holder, propShape,
|
||||
|
@ -4233,6 +4283,9 @@ AttachDecision SetPropIRGenerator::tryAttachDOMProxyUnshadowed(
|
|||
ObjOperandId holderId = writer.loadObject(holder);
|
||||
TestMatchingHolder(writer, holder, holderId);
|
||||
|
||||
EmitGuardGetterSetterSlot(writer, holder, propShape, holderId,
|
||||
/* holderIsConstant = */ true);
|
||||
|
||||
// EmitCallSetterNoGuards expects |obj| to be the object the property is
|
||||
// on to do some checks. Since we actually looked at proto, and no extra
|
||||
// guards will be generated, we can just pass that instead.
|
||||
|
@ -4278,14 +4331,15 @@ AttachDecision SetPropIRGenerator::tryAttachDOMProxyExpando(
|
|||
if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &propShape)) {
|
||||
auto* nativeExpandoObj = &expandoObj->as<NativeObject>();
|
||||
|
||||
// Note that we don't actually use the expandoObjId here after the
|
||||
// shape guard. The DOM proxy (objId) is passed to the setter as
|
||||
// |this|.
|
||||
// Call the setter. Note that we pass objId, the DOM proxy, as |this|
|
||||
// and not the expando object.
|
||||
maybeEmitIdGuard(id);
|
||||
guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal,
|
||||
nativeExpandoObj);
|
||||
ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape(
|
||||
obj, objId, expandoVal, nativeExpandoObj);
|
||||
|
||||
MOZ_ASSERT(holder == nativeExpandoObj);
|
||||
EmitGuardGetterSetterSlot(writer, nativeExpandoObj, propShape,
|
||||
expandoObjId);
|
||||
EmitCallSetterNoGuards(cx_, writer, nativeExpandoObj, nativeExpandoObj,
|
||||
propShape, objId, rhsId);
|
||||
trackAttached("DOMProxyExpandoSetter");
|
||||
|
|
|
@ -7042,13 +7042,14 @@ bool CacheIRCompiler::emitMegamorphicStoreSlot(ObjOperandId objId,
|
|||
|
||||
bool CacheIRCompiler::emitGuardHasGetterSetter(ObjOperandId objId,
|
||||
uint32_t idOffset,
|
||||
uint32_t shapeOffset) {
|
||||
uint32_t getterSetterOffset) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
||||
Register obj = allocator.useRegister(masm, objId);
|
||||
|
||||
StubFieldOffset id(idOffset, StubField::Type::Id);
|
||||
StubFieldOffset shape(shapeOffset, StubField::Type::Shape);
|
||||
StubFieldOffset getterSetter(getterSetterOffset,
|
||||
StubField::Type::GetterSetter);
|
||||
|
||||
AutoScratchRegister scratch1(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
|
@ -7065,15 +7066,15 @@ bool CacheIRCompiler::emitGuardHasGetterSetter(ObjOperandId objId,
|
|||
volatileRegs.takeUnchecked(scratch2);
|
||||
masm.PushRegsInMask(volatileRegs);
|
||||
|
||||
using Fn =
|
||||
bool (*)(JSContext * cx, JSObject * obj, jsid id, Shape * propShape);
|
||||
using Fn = bool (*)(JSContext * cx, JSObject * obj, jsid id,
|
||||
GetterSetter * getterSetter);
|
||||
masm.setupUnalignedABICall(scratch1);
|
||||
masm.loadJSContext(scratch1);
|
||||
masm.passABIArg(scratch1);
|
||||
masm.passABIArg(obj);
|
||||
emitLoadStubField(id, scratch2);
|
||||
masm.passABIArg(scratch2);
|
||||
emitLoadStubField(shape, scratch3);
|
||||
emitLoadStubField(getterSetter, scratch3);
|
||||
masm.passABIArg(scratch3);
|
||||
masm.callWithABI<Fn, ObjectHasGetterSetterPure>();
|
||||
masm.mov(ReturnReg, scratch1);
|
||||
|
|
|
@ -530,7 +530,7 @@
|
|||
args:
|
||||
obj: ObjId
|
||||
id: IdField
|
||||
shape: ShapeField
|
||||
getterSetter: GetterSetterField
|
||||
|
||||
- name: GuardInt32IsNonNegative
|
||||
shared: true
|
||||
|
|
|
@ -14894,10 +14894,10 @@ void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) {
|
|||
Register temp3 = ToRegister(lir->temp3());
|
||||
|
||||
masm.movePropertyKey(lir->mir()->propId(), temp2);
|
||||
masm.movePtr(ImmGCPtr(lir->mir()->shape()), temp3);
|
||||
masm.movePtr(ImmGCPtr(lir->mir()->getterSetter()), temp3);
|
||||
|
||||
using Fn =
|
||||
bool (*)(JSContext * cx, JSObject * obj, jsid id, Shape * propShape);
|
||||
using Fn = bool (*)(JSContext * cx, JSObject * obj, jsid id,
|
||||
GetterSetter * getterSetter);
|
||||
masm.setupUnalignedABICall(temp1);
|
||||
masm.loadJSContext(temp1);
|
||||
masm.passABIArg(temp1);
|
||||
|
|
|
@ -11948,10 +11948,10 @@ class MLoadWrapperTarget : public MUnaryInstruction,
|
|||
class MGuardHasGetterSetter : public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data {
|
||||
jsid propId_;
|
||||
CompilerShape shape_;
|
||||
CompilerGetterSetter getterSetter_;
|
||||
|
||||
MGuardHasGetterSetter(MDefinition* obj, jsid id, Shape* shape)
|
||||
: MUnaryInstruction(classOpcode, obj), propId_(id), shape_(shape) {
|
||||
MGuardHasGetterSetter(MDefinition* obj, jsid id, GetterSetter* gs)
|
||||
: MUnaryInstruction(classOpcode, obj), propId_(id), getterSetter_(gs) {
|
||||
setResultType(MIRType::Object);
|
||||
setMovable();
|
||||
setGuard();
|
||||
|
@ -11963,7 +11963,7 @@ class MGuardHasGetterSetter : public MUnaryInstruction,
|
|||
NAMED_OPERANDS((0, object))
|
||||
|
||||
jsid propId() const { return propId_; }
|
||||
Shape* shape() const { return shape_; }
|
||||
GetterSetter* getterSetter() const { return getterSetter_; }
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
|
@ -11976,7 +11976,7 @@ class MGuardHasGetterSetter : public MUnaryInstruction,
|
|||
if (ins->toGuardHasGetterSetter()->propId() != propId()) {
|
||||
return false;
|
||||
}
|
||||
if (ins->toGuardHasGetterSetter()->shape() != shape()) {
|
||||
if (ins->toGuardHasGetterSetter()->getterSetter() != getterSetter()) {
|
||||
return false;
|
||||
}
|
||||
return congruentIfOperandsEqual(ins);
|
||||
|
|
|
@ -1926,13 +1926,9 @@ bool SetNativeDataPropertyPure(JSContext* cx, JSObject* obj, PropertyName* name,
|
|||
}
|
||||
|
||||
bool ObjectHasGetterSetterPure(JSContext* cx, JSObject* objArg, jsid id,
|
||||
Shape* propShape) {
|
||||
GetterSetter* getterSetter) {
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(propShape->propid() == id);
|
||||
|
||||
MOZ_ASSERT(propShape->hasGetterObject() || propShape->hasSetterObject());
|
||||
|
||||
// Window objects may require outerizing (passing the WindowProxy to the
|
||||
// getter/setter), so we don't support them here.
|
||||
if (MOZ_UNLIKELY(!objArg->is<NativeObject>() || IsWindow(objArg))) {
|
||||
|
@ -1943,14 +1939,15 @@ bool ObjectHasGetterSetterPure(JSContext* cx, JSObject* objArg, jsid id,
|
|||
|
||||
while (true) {
|
||||
if (Shape* shape = nobj->lastProperty()->search(cx, id)) {
|
||||
if (shape == propShape) {
|
||||
if (!shape->isAccessorDescriptor()) {
|
||||
return false;
|
||||
}
|
||||
GetterSetter* actualGetterSetter = nobj->getGetterSetter(shape);
|
||||
if (actualGetterSetter == getterSetter) {
|
||||
return true;
|
||||
}
|
||||
if (shape->getterOrUndefined() == propShape->getterOrUndefined() &&
|
||||
shape->setterOrUndefined() == propShape->setterOrUndefined()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return (actualGetterSetter->getter() == getterSetter->getter() &&
|
||||
actualGetterSetter->setter() == getterSetter->setter());
|
||||
}
|
||||
|
||||
// Property not found. Watch out for Class hooks.
|
||||
|
|
|
@ -560,8 +560,8 @@ bool HasNativeElementPure(JSContext* cx, NativeObject* obj, int32_t index,
|
|||
bool SetNativeDataPropertyPure(JSContext* cx, JSObject* obj, PropertyName* name,
|
||||
Value* val);
|
||||
|
||||
bool ObjectHasGetterSetterPure(JSContext* cx, JSObject* obj, jsid id,
|
||||
Shape* propShape);
|
||||
bool ObjectHasGetterSetterPure(JSContext* cx, JSObject* objArg, jsid id,
|
||||
GetterSetter* getterSetter);
|
||||
|
||||
JSString* TypeOfObject(JSObject* obj, JSRuntime* rt);
|
||||
|
||||
|
|
|
@ -449,14 +449,13 @@ bool WarpCacheIRTranspiler::emitGuardIsNotDOMProxy(ObjOperandId objId) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitGuardHasGetterSetter(ObjOperandId objId,
|
||||
uint32_t idOffset,
|
||||
uint32_t shapeOffset) {
|
||||
bool WarpCacheIRTranspiler::emitGuardHasGetterSetter(
|
||||
ObjOperandId objId, uint32_t idOffset, uint32_t getterSetterOffset) {
|
||||
MDefinition* obj = getOperand(objId);
|
||||
jsid id = idStubField(idOffset);
|
||||
Shape* shape = shapeStubField(shapeOffset);
|
||||
GetterSetter* gs = getterSetterStubField(getterSetterOffset);
|
||||
|
||||
auto* ins = MGuardHasGetterSetter::New(alloc(), obj, id, shape);
|
||||
auto* ins = MGuardHasGetterSetter::New(alloc(), obj, id, gs);
|
||||
add(ins);
|
||||
|
||||
setOperand(objId, ins);
|
||||
|
|
|
@ -3273,6 +3273,9 @@ void JSObject::dump(js::GenericPrinter& out) const {
|
|||
if (nobj->hadElementsAccess()) {
|
||||
out.put(" had_elements_access");
|
||||
}
|
||||
if (nobj->hadGetterSetterChange()) {
|
||||
out.put(" had_getter_setter_change");
|
||||
}
|
||||
if (nobj->isIndexed()) {
|
||||
out.put(" indexed");
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "js/Value.h"
|
||||
#include "util/Memory.h"
|
||||
#include "vm/EqualityOperations.h" // js::SameValue
|
||||
#include "vm/GetterSetter.h" // js::GetterSetter
|
||||
#include "vm/PlainObject.h" // js::PlainObject
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
|
@ -1280,6 +1281,41 @@ bool NativeObject::reshapeForShadowedProp(JSContext* cx,
|
|||
return generateOwnShape(cx, obj);
|
||||
}
|
||||
|
||||
static bool ChangeProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
|
||||
HandleObject getter, HandleObject setter,
|
||||
unsigned attrs, PropertyResult* existing) {
|
||||
MOZ_ASSERT(existing);
|
||||
|
||||
Rooted<GetterSetter*> gs(cx);
|
||||
|
||||
// If we're redefining a getter/setter property but the getter and setter
|
||||
// objects are still the same, use the existing GetterSetter.
|
||||
if (existing->isNativeProperty()) {
|
||||
Shape* prop = existing->shape();
|
||||
if (prop->isAccessorDescriptor()) {
|
||||
GetterSetter* current = obj->getGetterSetter(prop);
|
||||
if (current->getter() == getter && current->setter() == setter) {
|
||||
gs = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!gs) {
|
||||
gs = GetterSetter::create(cx, getter, setter);
|
||||
if (!gs) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Shape* shape = NativeObject::putDataProperty(cx, obj, id, attrs);
|
||||
if (!shape) {
|
||||
return false;
|
||||
}
|
||||
|
||||
obj->setSlot(shape->slot(), PrivateGCThingValue(gs));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Whether we're adding a new property or changing an existing property (this
|
||||
// can be either a property stored in the shape tree or a dense element).
|
||||
enum class IsAddOrChange { Add, Change };
|
||||
|
@ -1330,11 +1366,17 @@ static MOZ_ALWAYS_INLINE bool AddOrChangeProperty(
|
|||
// the slower putProperty.
|
||||
if constexpr (AddOrChange == IsAddOrChange::Add) {
|
||||
if (desc.isAccessorDescriptor()) {
|
||||
if (!NativeObject::addAccessorProperty(cx, obj, id, desc.getterObject(),
|
||||
desc.setterObject(),
|
||||
desc.attributes())) {
|
||||
Rooted<GetterSetter*> gs(cx, GetterSetter::create(cx, desc.getterObject(),
|
||||
desc.setterObject()));
|
||||
if (!gs) {
|
||||
return false;
|
||||
}
|
||||
Shape* shape = NativeObject::addDataProperty(
|
||||
cx, obj, id, SHAPE_INVALID_SLOT, desc.attributes());
|
||||
if (!shape) {
|
||||
return false;
|
||||
}
|
||||
obj->initSlot(shape->slot(), PrivateGCThingValue(gs));
|
||||
} else {
|
||||
Shape* shape = NativeObject::addDataProperty(
|
||||
cx, obj, id, SHAPE_INVALID_SLOT, desc.attributes());
|
||||
|
@ -1345,9 +1387,8 @@ static MOZ_ALWAYS_INLINE bool AddOrChangeProperty(
|
|||
}
|
||||
} else {
|
||||
if (desc.isAccessorDescriptor()) {
|
||||
if (!NativeObject::putAccessorProperty(cx, obj, id, desc.getterObject(),
|
||||
desc.setterObject(),
|
||||
desc.attributes())) {
|
||||
if (!ChangeProperty(cx, obj, id, desc.getterObject(), desc.setterObject(),
|
||||
desc.attributes(), existing)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "js/shadow/Object.h" // JS::shadow::Object
|
||||
#include "js/shadow/Zone.h" // JS::shadow::Zone
|
||||
#include "js/Value.h"
|
||||
#include "vm/GetterSetter.h"
|
||||
#include "vm/JSObject.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/StringType.h"
|
||||
|
@ -865,6 +866,13 @@ class NativeObject : public JSObject {
|
|||
return hasFlag(ObjectFlag::HasInterestingSymbol);
|
||||
}
|
||||
|
||||
static bool setHadGetterSetterChange(JSContext* cx, HandleNativeObject obj) {
|
||||
return setFlag(cx, obj, ObjectFlag::HadGetterSetterChange);
|
||||
}
|
||||
bool hadGetterSetterChange() const {
|
||||
return hasFlag(ObjectFlag::HadGetterSetterChange);
|
||||
}
|
||||
|
||||
/*
|
||||
* Grow or shrink slots immediately before changing the slot span.
|
||||
* The number of allocated slots is not stored explicitly, and changes to
|
||||
|
@ -1088,20 +1096,46 @@ class NativeObject : public JSObject {
|
|||
getSlotAddressUnchecked(slot)->init(this, HeapSlot::Slot, slot, value);
|
||||
}
|
||||
|
||||
// Returns the GetterSetter for an accessor property.
|
||||
GetterSetter* getGetterSetter(Shape* shape) const {
|
||||
MOZ_ASSERT(shape->isAccessorDescriptor());
|
||||
return getSlot(shape->slot()).toGCThing()->as<GetterSetter>();
|
||||
}
|
||||
|
||||
// Returns the (possibly nullptr) getter or setter object. The shape must be
|
||||
// for an accessor property.
|
||||
JSObject* getGetter(Shape* shape) const { return shape->getterObject(); }
|
||||
JSObject* getSetter(Shape* shape) const { return shape->setterObject(); }
|
||||
JSObject* getGetter(Shape* shape) const {
|
||||
return getGetterSetter(shape)->getter();
|
||||
}
|
||||
JSObject* getSetter(Shape* shape) const {
|
||||
return getGetterSetter(shape)->setter();
|
||||
}
|
||||
|
||||
// Returns true if the property has a non-nullptr getter or setter object. The
|
||||
// shape can be any property shape.
|
||||
bool hasGetter(Shape* shape) const { return shape->hasGetterObject(); }
|
||||
bool hasSetter(Shape* shape) const { return shape->hasSetterObject(); }
|
||||
bool hasGetter(Shape* shape) const {
|
||||
return shape->hasGetterValue() && getGetter(shape);
|
||||
}
|
||||
bool hasSetter(Shape* shape) const {
|
||||
return shape->hasSetterValue() && getSetter(shape);
|
||||
}
|
||||
|
||||
// If the property has a non-nullptr getter/setter, return it as ObjectValue.
|
||||
// Else return |undefined|. The shape must be for an accessor property.
|
||||
Value getGetterValue(Shape* shape) const { return shape->getterValue(); }
|
||||
Value getSetterValue(Shape* shape) const { return shape->setterValue(); }
|
||||
Value getGetterValue(Shape* shape) const {
|
||||
MOZ_ASSERT(shape->hasGetterValue());
|
||||
if (JSObject* getterObj = getGetter(shape)) {
|
||||
return ObjectValue(*getterObj);
|
||||
}
|
||||
return UndefinedValue();
|
||||
}
|
||||
Value getSetterValue(Shape* shape) const {
|
||||
MOZ_ASSERT(shape->hasSetterValue());
|
||||
if (JSObject* setterObj = getSetter(shape)) {
|
||||
return ObjectValue(*setterObj);
|
||||
}
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
// MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
|
||||
// size classes will give an object.
|
||||
|
|
|
@ -108,20 +108,13 @@ template <MaybeAdding Adding>
|
|||
|
||||
inline Shape* Shape::new_(JSContext* cx, Handle<StackShape> other,
|
||||
uint32_t nfixed) {
|
||||
Shape* shape = other.isAccessorShape() ? js::Allocate<AccessorShape>(cx)
|
||||
: js::Allocate<Shape>(cx);
|
||||
Shape* shape = js::Allocate<Shape>(cx);
|
||||
if (!shape) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (other.isAccessorShape()) {
|
||||
new (shape) AccessorShape(other, nfixed);
|
||||
} else {
|
||||
new (shape) Shape(other, nfixed);
|
||||
}
|
||||
|
||||
return shape;
|
||||
return new (shape) Shape(other, nfixed);
|
||||
}
|
||||
|
||||
inline void Shape::updateBaseShapeAfterMovingGC() {
|
||||
|
@ -131,70 +124,9 @@ inline void Shape::updateBaseShapeAfterMovingGC() {
|
|||
}
|
||||
}
|
||||
|
||||
static inline void GetterSetterPreWriteBarrier(AccessorShape* shape) {
|
||||
if (shape->hasGetterObject()) {
|
||||
PreWriteBarrier(shape->getterObject());
|
||||
}
|
||||
if (shape->hasSetterObject()) {
|
||||
PreWriteBarrier(shape->setterObject());
|
||||
}
|
||||
}
|
||||
|
||||
static inline void GetterSetterPostWriteBarrier(AccessorShape* shape) {
|
||||
// If the shape contains any nursery pointers then add it to a vector on the
|
||||
// zone that we fixup on minor GC. Prevent this vector growing too large
|
||||
// since we don't tolerate OOM here.
|
||||
|
||||
static const size_t MaxShapeVectorLength = 5000;
|
||||
|
||||
MOZ_ASSERT(shape);
|
||||
|
||||
gc::StoreBuffer* sb = nullptr;
|
||||
if (shape->hasGetterObject()) {
|
||||
sb = shape->getterObject()->storeBuffer();
|
||||
}
|
||||
if (!sb && shape->hasSetterObject()) {
|
||||
sb = shape->setterObject()->storeBuffer();
|
||||
}
|
||||
if (!sb) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& nurseryShapes = shape->zone()->nurseryShapes();
|
||||
|
||||
{
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!nurseryShapes.append(shape)) {
|
||||
oomUnsafe.crash("GetterSetterPostWriteBarrier");
|
||||
}
|
||||
}
|
||||
|
||||
if (nurseryShapes.length() == 1) {
|
||||
sb->putGeneric(NurseryShapesRef(shape->zone()));
|
||||
} else if (nurseryShapes.length() == MaxShapeVectorLength) {
|
||||
sb->setAboutToOverflow(JS::GCReason::FULL_SHAPE_BUFFER);
|
||||
}
|
||||
}
|
||||
|
||||
inline AccessorShape::AccessorShape(const StackShape& other, uint32_t nfixed)
|
||||
: Shape(other, nfixed), getter_(other.getter), setter_(other.setter) {
|
||||
MOZ_ASSERT(getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
|
||||
GetterSetterPostWriteBarrier(this);
|
||||
}
|
||||
|
||||
inline AccessorShape::AccessorShape(BaseShape* base, ObjectFlags objectFlags,
|
||||
uint32_t nfixed)
|
||||
: Shape(base, objectFlags, nfixed), getter_(nullptr), setter_(nullptr) {
|
||||
MOZ_ASSERT(getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
|
||||
}
|
||||
|
||||
inline void Shape::initDictionaryShape(const StackShape& child, uint32_t nfixed,
|
||||
DictionaryShapeLink next) {
|
||||
if (child.isAccessorShape()) {
|
||||
new (this) AccessorShape(child, nfixed);
|
||||
} else {
|
||||
new (this) Shape(child, nfixed);
|
||||
}
|
||||
new (this) Shape(child, nfixed);
|
||||
this->immutableFlags |= IN_DICTIONARY;
|
||||
|
||||
MOZ_ASSERT(dictNext.isNone());
|
||||
|
|
|
@ -358,7 +358,7 @@ Shape* Shape::replaceLastProperty(JSContext* cx, ObjectFlags objectFlags,
|
|||
/* static */ MOZ_ALWAYS_INLINE Shape* NativeObject::getChildDataProperty(
|
||||
JSContext* cx, HandleNativeObject obj, HandleShape parent,
|
||||
MutableHandle<StackShape> child) {
|
||||
MOZ_ASSERT(child.isDataProperty());
|
||||
MOZ_ASSERT(!child.isCustomDataProperty());
|
||||
|
||||
if (child.hasMissingSlot()) {
|
||||
uint32_t slot;
|
||||
|
@ -427,14 +427,14 @@ Shape* Shape::replaceLastProperty(JSContext* cx, ObjectFlags objectFlags,
|
|||
/* static */ MOZ_ALWAYS_INLINE Shape* NativeObject::getChildAccessorProperty(
|
||||
JSContext* cx, HandleNativeObject obj, HandleShape parent,
|
||||
MutableHandle<StackShape> child) {
|
||||
MOZ_ASSERT(!child.isDataProperty());
|
||||
MOZ_ASSERT(child.isCustomDataProperty());
|
||||
|
||||
// Accessor properties have no slot, but slot_ will reflect that of parent.
|
||||
child.setSlot(parent->maybeSlot());
|
||||
|
||||
if (obj->inDictionaryMode()) {
|
||||
MOZ_ASSERT(parent == obj->lastProperty());
|
||||
Shape* shape = Allocate<AccessorShape>(cx);
|
||||
Shape* shape = Allocate<Shape>(cx);
|
||||
if (!shape) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -475,8 +475,7 @@ bool js::NativeObject::toDictionaryMode(JSContext* cx, HandleNativeObject obj) {
|
|||
while (shape) {
|
||||
MOZ_ASSERT(!shape->inDictionary());
|
||||
|
||||
Shape* dprop = shape->isAccessorShape() ? Allocate<AccessorShape>(cx)
|
||||
: Allocate<Shape>(cx);
|
||||
Shape* dprop = Allocate<Shape>(cx);
|
||||
if (!dprop) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
@ -635,7 +634,6 @@ Shape* NativeObject::addAccessorPropertyInternal(
|
|||
|
||||
Rooted<StackShape> child(cx, StackShape(last->base(), objectFlags, id,
|
||||
SHAPE_INVALID_SLOT, attrs));
|
||||
child.updateGetterSetter(getter, setter);
|
||||
shape = getChildAccessorProperty(cx, obj, last, &child);
|
||||
if (!shape) {
|
||||
return nullptr;
|
||||
|
@ -750,8 +748,7 @@ Shape* NativeObject::addEnumerableDataProperty(JSContext* cx,
|
|||
MOZ_ASSERT(!child->inDictionary());
|
||||
|
||||
if (child->propidRaw() != id || child->objectFlags() != objectFlags ||
|
||||
child->isAccessorShape() || child->attributes() != attrs ||
|
||||
child->base() != lastProperty->base()) {
|
||||
child->attributes() != attrs || child->base() != lastProperty->base()) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -942,17 +939,20 @@ Shape* NativeObject::putDataProperty(JSContext* cx, HandleNativeObject obj,
|
|||
// If the caller wants to allocate a slot, but doesn't care which slot,
|
||||
// copy the existing shape's slot into slot so we can match shape, if all
|
||||
// other members match.
|
||||
bool hadSlot = shape->isDataProperty();
|
||||
uint32_t oldSlot = shape->maybeSlot();
|
||||
uint32_t slot = hadSlot ? oldSlot : SHAPE_INVALID_SLOT;
|
||||
uint32_t slot =
|
||||
shape->isCustomDataProperty() ? SHAPE_INVALID_SLOT : shape->slot();
|
||||
|
||||
ObjectFlags objectFlags =
|
||||
GetObjectFlagsForNewProperty(obj->lastProperty(), id, attrs, cx);
|
||||
|
||||
if (shape->isAccessorDescriptor()) {
|
||||
objectFlags.setFlag(ObjectFlag::HadGetterSetterChange);
|
||||
}
|
||||
|
||||
// Now that we've possibly preserved slot, check whether the property info and
|
||||
// object flags match. If so, this is a redundant "put" and we can return
|
||||
// without more work.
|
||||
if (shape->matchesPropertyParamsAfterId(slot, attrs, nullptr, nullptr) &&
|
||||
if (shape->matchesPropertyParamsAfterId(slot, attrs) &&
|
||||
obj->lastProperty()->objectFlags() == objectFlags) {
|
||||
return shape;
|
||||
}
|
||||
|
@ -961,7 +961,7 @@ Shape* NativeObject::putDataProperty(JSContext* cx, HandleNativeObject obj,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(shape->isDataProperty(), shape->slot() == slot);
|
||||
MOZ_ASSERT_IF(!shape->isCustomDataProperty(), shape->slot() == slot);
|
||||
|
||||
if (obj->inDictionaryMode()) {
|
||||
// Updating some property in a dictionary-mode object. Create a new
|
||||
|
@ -993,7 +993,6 @@ Shape* NativeObject::putDataProperty(JSContext* cx, HandleNativeObject obj,
|
|||
shape->setBase(obj->lastProperty()->base());
|
||||
shape->setSlot(slot);
|
||||
shape->attrs = uint8_t(attrs);
|
||||
shape->immutableFlags &= ~Shape::ACCESSOR_SHAPE;
|
||||
shape->immutableFlags |= Shape::IN_DICTIONARY;
|
||||
} else {
|
||||
// Updating the last property in a non-dictionary-mode object. Find an
|
||||
|
@ -1012,8 +1011,6 @@ Shape* NativeObject::putDataProperty(JSContext* cx, HandleNativeObject obj,
|
|||
}
|
||||
|
||||
MOZ_ASSERT(obj->lastProperty()->objectFlags() == objectFlags);
|
||||
|
||||
MOZ_ASSERT(shape->isDataProperty());
|
||||
return shape;
|
||||
}
|
||||
|
||||
|
@ -1053,7 +1050,7 @@ Shape* NativeObject::putAccessorProperty(JSContext* cx, HandleNativeObject obj,
|
|||
|
||||
AssertCanChangeAttrs(shape, attrs);
|
||||
|
||||
bool hadSlot = shape->isDataProperty();
|
||||
bool hadSlot = !shape->isCustomDataProperty();
|
||||
uint32_t oldSlot = shape->maybeSlot();
|
||||
|
||||
ObjectFlags objectFlags =
|
||||
|
@ -1061,8 +1058,7 @@ Shape* NativeObject::putAccessorProperty(JSContext* cx, HandleNativeObject obj,
|
|||
|
||||
// Check whether the property info and object flags match. If so, this is a
|
||||
// redundant "put" and we can return without more work.
|
||||
if (shape->matchesPropertyParamsAfterId(SHAPE_INVALID_SLOT, attrs, getter,
|
||||
setter) &&
|
||||
if (shape->matchesPropertyParamsAfterId(SHAPE_INVALID_SLOT, attrs) &&
|
||||
obj->lastProperty()->objectFlags() == objectFlags) {
|
||||
return shape;
|
||||
}
|
||||
|
@ -1095,13 +1091,7 @@ Shape* NativeObject::putAccessorProperty(JSContext* cx, HandleNativeObject obj,
|
|||
shape->setBase(obj->lastProperty()->base());
|
||||
shape->setSlot(SHAPE_INVALID_SLOT);
|
||||
shape->attrs = uint8_t(attrs);
|
||||
shape->immutableFlags |= Shape::IN_DICTIONARY | Shape::ACCESSOR_SHAPE;
|
||||
|
||||
AccessorShape& accShape = shape->asAccessorShape();
|
||||
GetterSetterPreWriteBarrier(&accShape);
|
||||
accShape.getter_ = getter;
|
||||
accShape.setter_ = setter;
|
||||
GetterSetterPostWriteBarrier(&accShape);
|
||||
shape->immutableFlags |= Shape::IN_DICTIONARY;
|
||||
} else {
|
||||
// Updating the last property in a non-dictionary-mode object. Find an
|
||||
// alternate shared child of the last property's previous shape.
|
||||
|
@ -1112,7 +1102,6 @@ Shape* NativeObject::putAccessorProperty(JSContext* cx, HandleNativeObject obj,
|
|||
Rooted<StackShape> child(
|
||||
cx, StackShape(obj->lastProperty()->base(), objectFlags, id,
|
||||
SHAPE_INVALID_SLOT, attrs));
|
||||
child.updateGetterSetter(getter, setter);
|
||||
RootedShape parent(cx, shape->parent);
|
||||
shape = getChildAccessorProperty(cx, obj, parent, &child);
|
||||
if (!shape) {
|
||||
|
@ -1152,6 +1141,21 @@ bool NativeObject::removeProperty(JSContext* cx, HandleNativeObject obj,
|
|||
return true;
|
||||
}
|
||||
|
||||
// If we're removing an accessor property, ensure the HadGetterSetterChange
|
||||
// object flag is set. This is necessary because the slot holding the
|
||||
// GetterSetter can be changed indirectly by removing the property and then
|
||||
// adding it back with a different GetterSetter value but the same shape.
|
||||
if (shape->isAccessorDescriptor() && !obj->hadGetterSetterChange()) {
|
||||
if (!NativeObject::setHadGetterSetterChange(cx, obj)) {
|
||||
return false;
|
||||
}
|
||||
// Relookup shape/table/entry in case setHadGetterSetterChange changed them.
|
||||
if (!Shape::search(cx, obj->lastProperty(), id, keep, shape.address(),
|
||||
&table, &entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const bool removingLastProperty = (shape == obj->lastProperty());
|
||||
|
||||
/*
|
||||
|
@ -1178,8 +1182,7 @@ bool NativeObject::removeProperty(JSContext* cx, HandleNativeObject obj,
|
|||
*/
|
||||
RootedShape spare(cx);
|
||||
if (obj->inDictionaryMode()) {
|
||||
/* For simplicity, always allocate an accessor shape for now. */
|
||||
spare = Allocate<AccessorShape>(cx);
|
||||
spare = Allocate<Shape>(cx);
|
||||
if (!spare) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1187,7 +1190,7 @@ bool NativeObject::removeProperty(JSContext* cx, HandleNativeObject obj,
|
|||
}
|
||||
|
||||
/* If shape has a slot, free its slot number. */
|
||||
if (shape->isDataProperty()) {
|
||||
if (!shape->isCustomDataProperty()) {
|
||||
obj->freeSlot(cx, shape->slot());
|
||||
}
|
||||
|
||||
|
@ -1283,22 +1286,13 @@ Shape* NativeObject::replaceWithNewEquivalentShape(JSContext* cx,
|
|||
|
||||
if (!newShape) {
|
||||
RootedShape oldRoot(cx, oldShape);
|
||||
bool allocAccessorShape = accessorShape || oldShape->isAccessorShape();
|
||||
if (allocAccessorShape) {
|
||||
newShape = Allocate<AccessorShape>(cx);
|
||||
} else {
|
||||
newShape = Allocate<Shape>(cx);
|
||||
}
|
||||
|
||||
newShape = Allocate<Shape>(cx);
|
||||
if (!newShape) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (allocAccessorShape) {
|
||||
new (newShape) AccessorShape(oldRoot->base(), ObjectFlags(), 0);
|
||||
} else {
|
||||
new (newShape) Shape(oldRoot->base(), ObjectFlags(), 0);
|
||||
}
|
||||
new (newShape) Shape(oldRoot->base(), ObjectFlags(), 0);
|
||||
|
||||
oldShape = oldRoot;
|
||||
}
|
||||
|
@ -1781,19 +1775,8 @@ void Shape::fixupShapeTreeAfterMovingGC() {
|
|||
Shape* key = MaybeForwarded(e.front());
|
||||
BaseShape* base = MaybeForwarded(key->base());
|
||||
|
||||
JSObject* getter = key->maybeGetterObject();
|
||||
if (getter) {
|
||||
getter = MaybeForwarded(getter);
|
||||
}
|
||||
|
||||
JSObject* setter = key->maybeSetterObject();
|
||||
if (setter) {
|
||||
setter = MaybeForwarded(setter);
|
||||
}
|
||||
|
||||
StackShape lookup(base, key->objectFlags(), key->propidRef(),
|
||||
key->immutableFlags & Shape::SLOT_MASK, key->attrs);
|
||||
lookup.updateGetterSetter(getter, setter);
|
||||
e.rekeyFront(lookup, key);
|
||||
}
|
||||
}
|
||||
|
@ -1806,60 +1789,6 @@ void Shape::fixupAfterMovingGC() {
|
|||
}
|
||||
}
|
||||
|
||||
void NurseryShapesRef::trace(JSTracer* trc) {
|
||||
auto& shapes = zone_->nurseryShapes();
|
||||
for (auto shape : shapes) {
|
||||
shape->fixupGetterSetterForBarrier(trc);
|
||||
}
|
||||
shapes.clearAndFree();
|
||||
}
|
||||
|
||||
void Shape::fixupGetterSetterForBarrier(JSTracer* trc) {
|
||||
if (!hasGetterValue() && !hasSetterValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject* priorGetter = asAccessorShape().getterObject();
|
||||
JSObject* priorSetter = asAccessorShape().setterObject();
|
||||
if (!priorGetter && !priorSetter) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject* postGetter = priorGetter;
|
||||
JSObject* postSetter = priorSetter;
|
||||
if (priorGetter) {
|
||||
TraceManuallyBarrieredEdge(trc, &postGetter, "getterObj");
|
||||
}
|
||||
if (priorSetter) {
|
||||
TraceManuallyBarrieredEdge(trc, &postSetter, "setterObj");
|
||||
}
|
||||
if (priorGetter == postGetter && priorSetter == postSetter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent && !parent->inDictionary() && parent->children.isShapeSet()) {
|
||||
// Relocating the getterObj or setterObj will have changed our location in
|
||||
// our parent's ShapeSet, so take care to update it. We must do this before
|
||||
// we update the shape itself, since the shape is used to match the original
|
||||
// entry in the hash set.
|
||||
|
||||
StackShape original(this);
|
||||
StackShape updated(this);
|
||||
updated.getter = postGetter;
|
||||
updated.setter = postSetter;
|
||||
|
||||
ShapeSet* set = parent->children.toShapeSet();
|
||||
MOZ_ALWAYS_TRUE(set->rekeyAs(original, updated, this));
|
||||
}
|
||||
|
||||
asAccessorShape().getter_ = postGetter;
|
||||
asAccessorShape().setter_ = postSetter;
|
||||
|
||||
MOZ_ASSERT_IF(
|
||||
parent && !parent->inDictionary() && parent->children.isShapeSet(),
|
||||
parent->children.toShapeSet()->has(StackShape(this)));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
void ShapeChildren::checkHasChild(Shape* child) const {
|
||||
|
@ -1891,9 +1820,8 @@ void Shape::dump(js::GenericPrinter& out) const {
|
|||
JSID_TO_SYMBOL(propid)->dump(out);
|
||||
}
|
||||
|
||||
out.printf(" g/s %p/%p slot %d attrs %x ", maybeGetterObject(),
|
||||
maybeSetterObject(), isDataProperty() ? int32_t(slot()) : -1,
|
||||
attrs);
|
||||
out.printf(" slot %d attrs %x ",
|
||||
isCustomDataProperty() ? -1 : int32_t(slot()), attrs);
|
||||
|
||||
if (attrs) {
|
||||
int first = 1;
|
||||
|
|
|
@ -666,6 +666,11 @@ enum class ObjectFlag : uint16_t {
|
|||
// most proto chains. Code using this flag must check for "__proto__"
|
||||
// property names separately.
|
||||
HasNonWritableOrAccessorPropExclProto = 1 << 9,
|
||||
|
||||
// If set, the object either mutated or deleted an accessor property. This is
|
||||
// used to invalidate IC/Warp code specializing on specific getter/setter
|
||||
// objects. See also the SMDOC comment in vm/GetterSetter.h.
|
||||
HadGetterSetterChange = 1 << 10,
|
||||
};
|
||||
|
||||
using ObjectFlags = EnumFlags<ObjectFlag>;
|
||||
|
@ -930,7 +935,7 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
|
|||
MOZ_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
|
||||
p->maybeSlot() <= maybeSlot());
|
||||
MOZ_ASSERT_IF(p && !inDictionary(),
|
||||
isDataProperty() == (p->maybeSlot() != maybeSlot()));
|
||||
!isCustomDataProperty() == (p->maybeSlot() != maybeSlot()));
|
||||
parent = p;
|
||||
}
|
||||
|
||||
|
@ -1100,63 +1105,21 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
|
|||
public:
|
||||
bool inDictionary() const { return immutableFlags & IN_DICTIONARY; }
|
||||
|
||||
inline JSObject* maybeGetterObject() const;
|
||||
inline JSObject* getterObject() const;
|
||||
bool hasGetterObject() const { return hasGetterValue() && getterObject(); }
|
||||
|
||||
// Per ES5, decode null getterObj as the undefined value, which encodes as
|
||||
// null.
|
||||
Value getterValue() const {
|
||||
MOZ_ASSERT(hasGetterValue());
|
||||
if (JSObject* getterObj = getterObject()) {
|
||||
return ObjectValue(*getterObj);
|
||||
}
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
Value getterOrUndefined() const {
|
||||
return hasGetterValue() ? getterValue() : UndefinedValue();
|
||||
}
|
||||
|
||||
inline JSObject* maybeSetterObject() const;
|
||||
inline JSObject* setterObject() const;
|
||||
bool hasSetterObject() const { return hasSetterValue() && setterObject(); }
|
||||
|
||||
// Per ES5, decode null setterObj as the undefined value, which encodes as
|
||||
// null.
|
||||
Value setterValue() const {
|
||||
MOZ_ASSERT(hasSetterValue());
|
||||
if (JSObject* setterObj = setterObject()) {
|
||||
return ObjectValue(*setterObj);
|
||||
}
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
Value setterOrUndefined() const {
|
||||
return hasSetterValue() ? setterValue() : UndefinedValue();
|
||||
}
|
||||
|
||||
bool matches(const Shape* other) const {
|
||||
return propid_.get() == other->propid_.get() &&
|
||||
matchesParamsAfterId(other->base(), other->objectFlags(),
|
||||
other->maybeSlot(), other->attrs,
|
||||
other->maybeGetterObject(),
|
||||
other->maybeSetterObject());
|
||||
other->maybeSlot(), other->attrs);
|
||||
}
|
||||
|
||||
inline bool matches(const StackShape& other) const;
|
||||
|
||||
bool matchesParamsAfterId(BaseShape* base, ObjectFlags aobjectFlags,
|
||||
uint32_t aslot, unsigned aattrs, JSObject* getter,
|
||||
JSObject* setter) const {
|
||||
uint32_t aslot, unsigned aattrs) const {
|
||||
return base == this->base() && objectFlags() == aobjectFlags &&
|
||||
matchesPropertyParamsAfterId(aslot, aattrs, getter, setter);
|
||||
matchesPropertyParamsAfterId(aslot, aattrs);
|
||||
}
|
||||
|
||||
bool matchesPropertyParamsAfterId(uint32_t aslot, unsigned aattrs,
|
||||
JSObject* getter, JSObject* setter) const {
|
||||
return maybeSlot() == aslot && attrs == aattrs &&
|
||||
maybeGetterObject() == getter && maybeSetterObject() == setter;
|
||||
bool matchesPropertyParamsAfterId(uint32_t aslot, unsigned aattrs) const {
|
||||
return maybeSlot() == aslot && attrs == aattrs;
|
||||
}
|
||||
|
||||
// Note: this returns true only for plain data properties with a slot. Returns
|
||||
|
@ -1170,7 +1133,8 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
|
|||
return isDataProperty(attrs);
|
||||
}
|
||||
uint32_t slot() const {
|
||||
MOZ_ASSERT(isDataProperty() && !hasMissingSlot());
|
||||
MOZ_ASSERT(!isCustomDataProperty());
|
||||
MOZ_ASSERT(!hasMissingSlot());
|
||||
return maybeSlot();
|
||||
}
|
||||
uint32_t maybeSlot() const { return immutableFlags & SLOT_MASK; }
|
||||
|
@ -1326,7 +1290,6 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
|
|||
MOZ_ALWAYS_INLINE Shape* searchLinear(jsid id);
|
||||
|
||||
void fixupAfterMovingGC();
|
||||
void fixupGetterSetterForBarrier(JSTracer* trc);
|
||||
void updateBaseShapeAfterMovingGC();
|
||||
|
||||
// For JIT usage.
|
||||
|
@ -1363,23 +1326,7 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
|
|||
};
|
||||
|
||||
/* Fat Shape used for accessor properties. */
|
||||
class AccessorShape : public Shape {
|
||||
friend class Shape;
|
||||
friend class NativeObject;
|
||||
|
||||
// If hasGetterValue(), the getter object or null.
|
||||
JSObject* getter_;
|
||||
|
||||
// If hasSetterValue(), the setter object or null.
|
||||
JSObject* setter_;
|
||||
|
||||
public:
|
||||
/* Get a shape identical to this one, without parent/children information. */
|
||||
inline AccessorShape(const StackShape& other, uint32_t nfixed);
|
||||
|
||||
inline AccessorShape(BaseShape* base, ObjectFlags objectFlags,
|
||||
uint32_t nfixed);
|
||||
};
|
||||
class AccessorShape : public Shape {};
|
||||
|
||||
struct EmptyShape : public js::Shape {
|
||||
EmptyShape(BaseShape* base, ObjectFlags objectFlags, uint32_t nfixed)
|
||||
|
@ -1488,8 +1435,6 @@ struct StackShape {
|
|||
/* For performance, StackShape only roots when absolutely necessary. */
|
||||
BaseShape* base;
|
||||
jsid propid;
|
||||
JSObject* getter;
|
||||
JSObject* setter;
|
||||
uint32_t immutableFlags;
|
||||
ObjectFlags objectFlags;
|
||||
uint8_t attrs;
|
||||
|
@ -1499,8 +1444,6 @@ struct StackShape {
|
|||
uint32_t slot, unsigned attrs)
|
||||
: base(base),
|
||||
propid(propid),
|
||||
getter(nullptr),
|
||||
setter(nullptr),
|
||||
immutableFlags(slot),
|
||||
objectFlags(objectFlags),
|
||||
attrs(uint8_t(attrs)),
|
||||
|
@ -1513,32 +1456,21 @@ struct StackShape {
|
|||
explicit StackShape(Shape* shape)
|
||||
: base(shape->base()),
|
||||
propid(shape->propidRef()),
|
||||
getter(shape->maybeGetterObject()),
|
||||
setter(shape->maybeSetterObject()),
|
||||
immutableFlags(shape->immutableFlags),
|
||||
objectFlags(shape->objectFlags()),
|
||||
attrs(shape->attrs),
|
||||
mutableFlags(shape->mutableFlags) {}
|
||||
|
||||
void updateGetterSetter(JSObject* getter, JSObject* setter) {
|
||||
if (getter || setter || (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
||||
immutableFlags |= Shape::ACCESSOR_SHAPE;
|
||||
} else {
|
||||
immutableFlags &= ~Shape::ACCESSOR_SHAPE;
|
||||
}
|
||||
|
||||
this->getter = getter;
|
||||
this->setter = setter;
|
||||
}
|
||||
|
||||
bool isDataProperty() const {
|
||||
MOZ_ASSERT(!JSID_IS_EMPTY(propid));
|
||||
return Shape::isDataProperty(attrs);
|
||||
}
|
||||
bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
|
||||
|
||||
bool isCustomDataProperty() const { return attrs & JSPROP_CUSTOM_DATA_PROP; }
|
||||
|
||||
uint32_t slot() const {
|
||||
MOZ_ASSERT(isDataProperty() && !hasMissingSlot());
|
||||
MOZ_ASSERT(!hasMissingSlot());
|
||||
return maybeSlot();
|
||||
}
|
||||
uint32_t maybeSlot() const { return immutableFlags & Shape::SLOT_MASK; }
|
||||
|
@ -1548,15 +1480,11 @@ struct StackShape {
|
|||
immutableFlags = (immutableFlags & ~Shape::SLOT_MASK) | slot;
|
||||
}
|
||||
|
||||
bool isAccessorShape() const {
|
||||
return immutableFlags & Shape::ACCESSOR_SHAPE;
|
||||
}
|
||||
|
||||
HashNumber hash() const {
|
||||
HashNumber hash = HashId(propid);
|
||||
return mozilla::AddToHash(
|
||||
hash, mozilla::HashGeneric(base, objectFlags.toRaw(), attrs,
|
||||
maybeSlot(), getter, setter));
|
||||
hash,
|
||||
mozilla::HashGeneric(base, objectFlags.toRaw(), attrs, maybeSlot()));
|
||||
}
|
||||
|
||||
// StructGCPolicy implementation.
|
||||
|
@ -1571,11 +1499,11 @@ class WrappedPtrOperations<StackShape, Wrapper> {
|
|||
|
||||
public:
|
||||
bool isDataProperty() const { return ss().isDataProperty(); }
|
||||
bool isCustomDataProperty() const { return ss().isCustomDataProperty(); }
|
||||
bool hasMissingSlot() const { return ss().hasMissingSlot(); }
|
||||
uint32_t slot() const { return ss().slot(); }
|
||||
uint32_t maybeSlot() const { return ss().maybeSlot(); }
|
||||
uint32_t slotSpan() const { return ss().slotSpan(); }
|
||||
bool isAccessorShape() const { return ss().isAccessorShape(); }
|
||||
uint8_t attrs() const { return ss().attrs; }
|
||||
ObjectFlags objectFlags() const { return ss().objectFlags; }
|
||||
jsid propid() const { return ss().propid; }
|
||||
|
@ -1587,9 +1515,6 @@ class MutableWrappedPtrOperations<StackShape, Wrapper>
|
|||
StackShape& ss() { return static_cast<Wrapper*>(this)->get(); }
|
||||
|
||||
public:
|
||||
void updateGetterSetter(JSObject* getter, JSObject* setter) {
|
||||
ss().updateGetterSetter(getter, setter);
|
||||
}
|
||||
void setSlot(uint32_t slot) { ss().setSlot(slot); }
|
||||
void setBase(BaseShape* base) { ss().base = base; }
|
||||
void setAttrs(uint8_t attrs) { ss().attrs = attrs; }
|
||||
|
@ -1608,29 +1533,11 @@ inline Shape::Shape(const StackShape& other, uint32_t nfixed)
|
|||
parent(nullptr) {
|
||||
setNumFixedSlots(nfixed);
|
||||
|
||||
#ifdef DEBUG
|
||||
gc::AllocKind allocKind = getAllocKind();
|
||||
MOZ_ASSERT_IF(other.isAccessorShape(),
|
||||
allocKind == gc::AllocKind::ACCESSOR_SHAPE);
|
||||
MOZ_ASSERT_IF(allocKind == gc::AllocKind::SHAPE, !other.isAccessorShape());
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT_IF(!isEmptyShape(), AtomIsMarked(zone(), propid()));
|
||||
|
||||
children.setNone();
|
||||
}
|
||||
|
||||
// This class is used to update any shapes in a zone that have nursery objects
|
||||
// as getters/setters. It updates the pointers and the shapes' entries in the
|
||||
// parents' ShapeSet tables.
|
||||
class NurseryShapesRef : public gc::BufferableRef {
|
||||
Zone* zone_;
|
||||
|
||||
public:
|
||||
explicit NurseryShapesRef(Zone* zone) : zone_(zone) {}
|
||||
void trace(JSTracer* trc) override;
|
||||
};
|
||||
|
||||
inline Shape::Shape(BaseShape* base, ObjectFlags objectFlags, uint32_t nfixed)
|
||||
: CellWithTenuredGCPointer(base),
|
||||
propid_(JSID_EMPTY),
|
||||
|
@ -1643,24 +1550,6 @@ inline Shape::Shape(BaseShape* base, ObjectFlags objectFlags, uint32_t nfixed)
|
|||
children.setNone();
|
||||
}
|
||||
|
||||
inline JSObject* Shape::maybeGetterObject() const {
|
||||
return isAccessorShape() ? asAccessorShape().getter_ : nullptr;
|
||||
}
|
||||
|
||||
inline JSObject* Shape::maybeSetterObject() const {
|
||||
return isAccessorShape() ? asAccessorShape().setter_ : nullptr;
|
||||
}
|
||||
|
||||
inline JSObject* Shape::getterObject() const {
|
||||
MOZ_ASSERT(hasGetterValue());
|
||||
return asAccessorShape().getter_;
|
||||
}
|
||||
|
||||
inline JSObject* Shape::setterObject() const {
|
||||
MOZ_ASSERT(hasSetterValue());
|
||||
return asAccessorShape().setter_;
|
||||
}
|
||||
|
||||
inline Shape* Shape::searchLinear(jsid id) {
|
||||
for (Shape* shape = this; shape;) {
|
||||
if (shape->propidRef() == id) {
|
||||
|
@ -1675,7 +1564,7 @@ inline Shape* Shape::searchLinear(jsid id) {
|
|||
inline bool Shape::matches(const StackShape& other) const {
|
||||
return propid_.get() == other.propid &&
|
||||
matchesParamsAfterId(other.base, other.objectFlags, other.maybeSlot(),
|
||||
other.attrs, other.getter, other.setter);
|
||||
other.attrs);
|
||||
}
|
||||
|
||||
template <MaybeAdding Adding>
|
||||
|
|
Загрузка…
Ссылка в новой задаче