зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1137180 - Allow unboxed objects to be extended with new properties, r=jandem.
This commit is contained in:
Родитель
394a2dd441
Коммит
94ad027986
|
@ -1708,6 +1708,9 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
|
|||
goto scan_unboxed;
|
||||
}
|
||||
if (clasp == &UnboxedPlainObject::class_) {
|
||||
JSObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
if (expando && mark(expando))
|
||||
repush(expando);
|
||||
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||
unboxedTraceList = layout.traceList();
|
||||
if (!unboxedTraceList)
|
||||
|
|
|
@ -320,7 +320,6 @@ class GCMarker : public JSTracer
|
|||
|
||||
void markAndScanString(JSObject *source, JSString *str);
|
||||
void markAndScanSymbol(JSObject *source, JS::Symbol *sym);
|
||||
bool markObject(JSObject *source, JSObject *obj);
|
||||
|
||||
void appendGrayRoot(void *thing, JSGCTraceKind kind);
|
||||
|
||||
|
|
|
@ -168,7 +168,6 @@ ICStub::updateCode(JitCode *code)
|
|||
void
|
||||
ReceiverGuard::trace(JSTracer *trc)
|
||||
{
|
||||
MOZ_ASSERT(!!shape_ != !!group_);
|
||||
if (shape_)
|
||||
MarkShape(trc, &shape_, "receiver_guard_shape");
|
||||
else
|
||||
|
@ -347,12 +346,12 @@ ICStub::trace(JSTracer *trc)
|
|||
}
|
||||
case ICStub::GetProp_Native: {
|
||||
ICGetProp_Native *propStub = toGetProp_Native();
|
||||
MarkShape(trc, &propStub->shape(), "baseline-getpropnative-stub-shape");
|
||||
propStub->receiverGuard().trace(trc);
|
||||
break;
|
||||
}
|
||||
case ICStub::GetProp_NativePrototype: {
|
||||
ICGetProp_NativePrototype *propStub = toGetProp_NativePrototype();
|
||||
propStub->guard().trace(trc);
|
||||
propStub->receiverGuard().trace(trc);
|
||||
MarkObject(trc, &propStub->holder(), "baseline-getpropnativeproto-stub-holder");
|
||||
MarkShape(trc, &propStub->holderShape(), "baseline-getpropnativeproto-stub-holdershape");
|
||||
break;
|
||||
|
@ -426,13 +425,12 @@ ICStub::trace(JSTracer *trc)
|
|||
}
|
||||
case ICStub::SetProp_Native: {
|
||||
ICSetProp_Native *propStub = toSetProp_Native();
|
||||
MarkShape(trc, &propStub->shape(), "baseline-setpropnative-stub-shape");
|
||||
MarkObjectGroup(trc, &propStub->group(), "baseline-setpropnative-stub-group");
|
||||
propStub->receiverGuard().trace(trc);
|
||||
break;
|
||||
}
|
||||
case ICStub::SetProp_NativeAdd: {
|
||||
ICSetProp_NativeAdd *propStub = toSetProp_NativeAdd();
|
||||
MarkObjectGroup(trc, &propStub->group(), "baseline-setpropnativeadd-stub-group");
|
||||
propStub->receiverGuard().trace(trc);
|
||||
MarkShape(trc, &propStub->newShape(), "baseline-setpropnativeadd-stub-newshape");
|
||||
if (propStub->newGroup())
|
||||
MarkObjectGroup(trc, &propStub->newGroup(), "baseline-setpropnativeadd-stub-new-group");
|
||||
|
@ -3448,16 +3446,22 @@ IsCacheableSetPropWriteSlot(JSObject *obj, Shape *oldShape, JSObject *holder, Sh
|
|||
}
|
||||
|
||||
static bool
|
||||
IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape, uint32_t oldSlots,
|
||||
HandleId id, HandleObject holder, HandleShape shape,
|
||||
IsCacheableSetPropAddSlot(JSContext *cx, JSObject *obj, Shape *oldShape,
|
||||
jsid id, JSObject *holder, Shape *shape,
|
||||
size_t *protoChainDepth)
|
||||
{
|
||||
if (!shape)
|
||||
if (!shape || obj != holder)
|
||||
return false;
|
||||
|
||||
// Property must be set directly on object, and be last added property of object.
|
||||
if (!obj->isNative() || obj != holder || shape != obj->as<NativeObject>().lastProperty())
|
||||
return false;
|
||||
if (obj->isNative()) {
|
||||
if (shape != obj->as<NativeObject>().lastProperty())
|
||||
return false;
|
||||
} else if (obj->is<UnboxedPlainObject>()) {
|
||||
UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
if (!expando || shape != expando->lastProperty())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Object must be extensible, oldShape must be immediate parent of curShape.
|
||||
if (!obj->nonProxyIsExtensible() || shape->previous() != oldShape)
|
||||
|
@ -3497,7 +3501,7 @@ IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape,
|
|||
// Only add a IC entry if the dynamic slots didn't change when the shapes
|
||||
// changed. Need to ensure that a shape change for a subsequent object
|
||||
// won't involve reallocating the slot array.
|
||||
if (obj->as<NativeObject>().numDynamicSlots() != oldSlots)
|
||||
if (NativeObject::dynamicSlotsCount(shape) != NativeObject::dynamicSlotsCount(oldShape))
|
||||
return false;
|
||||
|
||||
*protoChainDepth = chainDepth;
|
||||
|
@ -5778,7 +5782,7 @@ UpdateExistingGetPropCallStubs(ICFallbackStub *fallbackStub,
|
|||
|
||||
bool isOwnGetter = (holder == receiver);
|
||||
bool foundMatchingStub = false;
|
||||
ReceiverGuard::Token receiverGuard = ReceiverGuard::objectToken(receiver);
|
||||
ReceiverGuard::StackGuard receiverGuard(receiver);
|
||||
for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
|
||||
if (iter->kind() == kind) {
|
||||
ICGetPropCallGetter *getPropStub = static_cast<ICGetPropCallGetter *>(*iter);
|
||||
|
@ -5804,10 +5808,12 @@ UpdateExistingGetPropCallStubs(ICFallbackStub *fallbackStub,
|
|||
// matches as protos with the old shape flow into it, but always
|
||||
// matches post-get, which is where we are now.
|
||||
getPropStub->holderShape() = holder->lastProperty();
|
||||
|
||||
// Make sure to update the getter, since a shape change might
|
||||
// have changed which getter we want to use.
|
||||
getPropStub->getter() = getter;
|
||||
if (receiverGuard == getPropStub->receiverGuard().token())
|
||||
|
||||
if (getPropStub->receiverGuard().matches(receiverGuard))
|
||||
foundMatchingStub = true;
|
||||
}
|
||||
}
|
||||
|
@ -5822,7 +5828,7 @@ static bool
|
|||
UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
|
||||
ICStub::Kind kind,
|
||||
NativeObject *holder,
|
||||
ReceiverGuard::Token receiverGuard,
|
||||
ReceiverGuard::StackGuard receiverGuard,
|
||||
JSFunction *setter)
|
||||
{
|
||||
MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
|
||||
|
@ -5835,13 +5841,13 @@ UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
|
|||
// We want to update the holder shape to match the new one no
|
||||
// matter what, even if the receiver shape is different.
|
||||
MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
|
||||
setPropStub->guard().token() != receiverGuard,
|
||||
!setPropStub->guard().matches(receiverGuard),
|
||||
"Why didn't we end up using this stub?");
|
||||
setPropStub->holderShape() = holder->lastProperty();
|
||||
// Make sure to update the setter, since a shape change might
|
||||
// have changed which setter we want to use.
|
||||
setPropStub->setter() = setter;
|
||||
if (receiverGuard == setPropStub->guard().token())
|
||||
if (setPropStub->guard().matches(receiverGuard))
|
||||
foundMatchingStub = true;
|
||||
}
|
||||
}
|
||||
|
@ -6712,6 +6718,45 @@ TryAttachUnboxedGetPropStub(JSContext *cx, HandleScript script,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
TryAttachUnboxedExpandoGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
|
||||
ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
|
||||
bool *attached)
|
||||
{
|
||||
MOZ_ASSERT(!*attached);
|
||||
|
||||
if (!val.isObject() || !val.toObject().is<UnboxedPlainObject>())
|
||||
return true;
|
||||
Rooted<UnboxedPlainObject *> obj(cx, &val.toObject().as<UnboxedPlainObject>());
|
||||
|
||||
Rooted<UnboxedExpandoObject *> expando(cx, obj->maybeExpando());
|
||||
if (!expando)
|
||||
return true;
|
||||
|
||||
Shape *shape = expando->lookup(cx, name);
|
||||
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
|
||||
return true;
|
||||
|
||||
bool isFixedSlot;
|
||||
uint32_t offset;
|
||||
GetFixedOrDynamicSlotOffset(expando, shape->slot(), &isFixedSlot, &offset);
|
||||
|
||||
ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
|
||||
|
||||
bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
|
||||
ICGetPropNativeCompiler compiler(cx, ICStub::GetProp_Native, isCallProp, monitorStub, obj, obj,
|
||||
name, isFixedSlot, offset);
|
||||
ICGetPropNativeStub *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
|
||||
StripPreliminaryObjectStubs(cx, stub);
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
TryAttachTypedObjectGetPropStub(JSContext *cx, HandleScript script,
|
||||
ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
|
||||
|
@ -6967,6 +7012,11 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_
|
|||
if (attached)
|
||||
return true;
|
||||
|
||||
if (!TryAttachUnboxedExpandoGetPropStub(cx, script, pc, stub, name, val, &attached))
|
||||
return false;
|
||||
if (attached)
|
||||
return true;
|
||||
|
||||
if (!TryAttachTypedObjectGetPropStub(cx, script, stub, name, val, &attached))
|
||||
return false;
|
||||
if (attached)
|
||||
|
@ -7145,16 +7195,16 @@ ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
ICGetPropNativeStub *
|
||||
ICGetPropNativeCompiler::getStub(ICStubSpace *space)
|
||||
{
|
||||
ReceiverGuard::StackGuard guard(obj_);
|
||||
|
||||
switch (kind) {
|
||||
case ICStub::GetProp_Native: {
|
||||
MOZ_ASSERT(obj_ == holder_);
|
||||
RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
|
||||
return ICStub::New<ICGetProp_Native>(space, getStubCode(), firstMonitorStub_, shape, offset_);
|
||||
return ICStub::New<ICGetProp_Native>(space, getStubCode(), firstMonitorStub_, guard, offset_);
|
||||
}
|
||||
|
||||
case ICStub::GetProp_NativePrototype: {
|
||||
MOZ_ASSERT(obj_ != holder_);
|
||||
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
|
||||
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
|
||||
return ICStub::New<ICGetProp_NativePrototype>(space, getStubCode(), firstMonitorStub_, guard,
|
||||
offset_, holder_, holderShape);
|
||||
|
@ -7166,21 +7216,40 @@ ICGetPropNativeCompiler::getStub(ICStubSpace *space)
|
|||
}
|
||||
|
||||
static void
|
||||
GuardNativeOrUnboxedReceiver(MacroAssembler &masm, JSObject *obj,
|
||||
GuardNativeOrUnboxedReceiver(MacroAssembler &masm, ReceiverGuard::StackGuard guard,
|
||||
Register object, Register scratch,
|
||||
size_t receiverGuardOffset, Label *failure)
|
||||
{
|
||||
if (obj->isNative()) {
|
||||
masm.loadPtr(Address(BaselineStubReg,
|
||||
receiverGuardOffset + ReceiverGuard::offsetOfShape()),
|
||||
scratch);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
|
||||
} else {
|
||||
MOZ_ASSERT(obj->is<UnboxedPlainObject>());
|
||||
masm.loadPtr(Address(BaselineStubReg,
|
||||
receiverGuardOffset + ReceiverGuard::offsetOfGroup()),
|
||||
scratch);
|
||||
Address groupAddress(BaselineStubReg, receiverGuardOffset + ReceiverGuard::offsetOfGroup());
|
||||
Address shapeAddress(BaselineStubReg, receiverGuardOffset + ReceiverGuard::offsetOfShape());
|
||||
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
|
||||
|
||||
if (guard.group) {
|
||||
masm.loadPtr(groupAddress, scratch);
|
||||
masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure);
|
||||
|
||||
if (guard.group->maybeUnboxedLayout() && !guard.shape) {
|
||||
// Guard the unboxed object has no expando object.
|
||||
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
|
||||
}
|
||||
}
|
||||
|
||||
if (guard.shape) {
|
||||
masm.loadPtr(shapeAddress, scratch);
|
||||
if (guard.group && guard.group->maybeUnboxedLayout()) {
|
||||
// Guard the unboxed object has a matching expando object.
|
||||
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
|
||||
Label done;
|
||||
masm.push(object);
|
||||
masm.loadPtr(expandoAddress, object);
|
||||
masm.branchTestObjShape(Assembler::Equal, object, scratch, &done);
|
||||
masm.pop(object);
|
||||
masm.jump(failure);
|
||||
masm.bind(&done);
|
||||
masm.pop(object);
|
||||
} else {
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7204,14 +7273,18 @@ ICGetPropNativeCompiler::generateStubCode(MacroAssembler &masm)
|
|||
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
|
||||
|
||||
// Shape/group guard.
|
||||
MOZ_ASSERT(ICGetProp_Native::offsetOfShape() ==
|
||||
ICGetProp_NativePrototype::offsetOfGuard() + ReceiverGuard::offsetOfShape());
|
||||
GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
|
||||
ICGetProp_NativePrototype::offsetOfGuard(), &failure);
|
||||
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch,
|
||||
ICGetPropNativeStub::offsetOfReceiverGuard(), &failure);
|
||||
|
||||
Register holderReg;
|
||||
if (obj_ == holder_) {
|
||||
holderReg = objReg;
|
||||
if (obj_->is<UnboxedPlainObject>()) {
|
||||
// We are loading off the expando object, so use that for the holder.
|
||||
holderReg = regs.takeAny();
|
||||
masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
|
||||
} else {
|
||||
holderReg = objReg;
|
||||
}
|
||||
} else {
|
||||
// Shape guard holder.
|
||||
holderReg = regs.takeAny();
|
||||
|
@ -7348,7 +7421,7 @@ ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler &masm)
|
|||
|
||||
// Unbox and guard against old shape/group.
|
||||
Register objReg = masm.extractObject(R0, ExtractTemp0);
|
||||
GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
|
||||
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch,
|
||||
ICGetProp_NativeDoesNotExist::offsetOfGuard(), &failure);
|
||||
|
||||
Register protoReg = regs.takeAny();
|
||||
|
@ -7387,7 +7460,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
|
||||
// Unbox and shape guard.
|
||||
Register objReg = masm.extractObject(R0, ExtractTemp0);
|
||||
GuardNativeOrUnboxedReceiver(masm, receiver_, objReg, scratch,
|
||||
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(receiver_), objReg, scratch,
|
||||
ICGetProp_CallScripted::offsetOfReceiverGuard(), &failure);
|
||||
|
||||
if (receiver_ != holder_) {
|
||||
|
@ -7495,7 +7568,7 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
|
||||
|
||||
// Shape guard.
|
||||
GuardNativeOrUnboxedReceiver(masm, receiver_, objReg, scratch,
|
||||
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(receiver_), objReg, scratch,
|
||||
ICGetProp_CallNative::offsetOfReceiverGuard(), &failure);
|
||||
|
||||
if (receiver_ != holder_ ) {
|
||||
|
@ -7985,21 +8058,34 @@ BaselineScript::noteAccessedGetter(uint32_t pcOffset)
|
|||
// value property.
|
||||
static bool
|
||||
TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
|
||||
HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup, uint32_t oldSlots,
|
||||
HandleObject obj, const ReceiverGuard::RootedStackGuard &oldGuard,
|
||||
HandlePropertyName name, HandleId id, HandleValue rhs, bool *attached)
|
||||
{
|
||||
MOZ_ASSERT(!*attached);
|
||||
|
||||
if (!obj->isNative() || obj->watched())
|
||||
if (obj->watched())
|
||||
return true;
|
||||
|
||||
RootedShape shape(cx);
|
||||
RootedObject holder(cx);
|
||||
if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
|
||||
return false;
|
||||
if (!holder)
|
||||
return true;
|
||||
|
||||
if (holder->is<UnboxedPlainObject>()) {
|
||||
UnboxedExpandoObject *expando = holder->as<UnboxedPlainObject>().maybeExpando();
|
||||
if (expando) {
|
||||
shape = expando->lookup(cx, name);
|
||||
if (!shape)
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
size_t chainDepth;
|
||||
if (IsCacheableSetPropAddSlot(cx, obj, oldShape, oldSlots, id, holder, shape, &chainDepth)) {
|
||||
if (IsCacheableSetPropAddSlot(cx, obj, oldGuard.shape, id, holder, shape, &chainDepth)) {
|
||||
// Don't attach if proto chain depth is too high.
|
||||
if (chainDepth > ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH)
|
||||
return true;
|
||||
|
@ -8008,18 +8094,22 @@ TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, IC
|
|||
// script properties analysis hasn't been performed for yet, as there
|
||||
// may be a shape change required here afterwards. Pretend we attached
|
||||
// a stub, though, so the access is not marked as unoptimizable.
|
||||
if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
|
||||
if (oldGuard.group->newScript() && !oldGuard.group->newScript()->analyzed()) {
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isFixedSlot;
|
||||
uint32_t offset;
|
||||
GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
|
||||
if (obj->is<NativeObject>()) {
|
||||
GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
|
||||
} else {
|
||||
GetFixedOrDynamicSlotOffset(obj->as<UnboxedPlainObject>().maybeExpando(),
|
||||
shape->slot(), &isFixedSlot, &offset);
|
||||
}
|
||||
|
||||
JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.ADD) stub");
|
||||
ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldGroup,
|
||||
chainDepth, isFixedSlot, offset);
|
||||
ICSetPropNativeAddCompiler compiler(cx, obj, oldGuard, chainDepth, isFixedSlot, offset);
|
||||
ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
|
@ -8031,7 +8121,10 @@ TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, IC
|
|||
return true;
|
||||
}
|
||||
|
||||
if (IsCacheableSetPropWriteSlot(obj, oldShape, holder, shape)) {
|
||||
if (!obj->isNative())
|
||||
return true;
|
||||
|
||||
if (IsCacheableSetPropWriteSlot(obj, oldGuard.shape, holder, shape)) {
|
||||
// For some property writes, such as the initial overwrite of global
|
||||
// properties, TI will not mark the property as having been
|
||||
// overwritten. Don't attach a stub in this case, so that we don't
|
||||
|
@ -8047,7 +8140,7 @@ TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, IC
|
|||
GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
|
||||
|
||||
JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.PROP) stub");
|
||||
MOZ_ASSERT(obj->as<NativeObject>().lastProperty() == oldShape,
|
||||
MOZ_ASSERT(obj->as<NativeObject>().lastProperty() == oldGuard.shape,
|
||||
"Should this really be a SetPropWriteSlot?");
|
||||
ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
|
||||
ICSetProp_Native *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
|
@ -8072,8 +8165,10 @@ TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, IC
|
|||
// Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on
|
||||
// an accessor property.
|
||||
static bool
|
||||
TryAttachSetAccessorPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
|
||||
HandleObject obj, ReceiverGuard::Token receiverGuard, HandlePropertyName name,
|
||||
TryAttachSetAccessorPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
|
||||
ICSetProp_Fallback *stub,
|
||||
HandleObject obj, const ReceiverGuard::RootedStackGuard &receiverGuard,
|
||||
HandlePropertyName name,
|
||||
HandleId id, HandleValue rhs, bool *attached,
|
||||
bool *isTemporarilyUnoptimizable)
|
||||
{
|
||||
|
@ -8179,6 +8274,41 @@ TryAttachUnboxedSetPropStub(JSContext *cx, HandleScript script,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
TryAttachUnboxedExpandoSetPropStub(JSContext *cx, HandleScript script,
|
||||
ICSetProp_Fallback *stub, HandleId id,
|
||||
HandleObject obj, Shape *oldShape, HandleValue rhs, bool *attached)
|
||||
{
|
||||
MOZ_ASSERT(!*attached);
|
||||
|
||||
if (!obj->is<UnboxedPlainObject>())
|
||||
return true;
|
||||
|
||||
UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
if (!expando || expando->lastProperty() != oldShape)
|
||||
return true;
|
||||
|
||||
Shape *shape = expando->lookup(cx, id);
|
||||
if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable())
|
||||
return true;
|
||||
|
||||
bool isFixedSlot;
|
||||
uint32_t offset;
|
||||
GetFixedOrDynamicSlotOffset(expando, shape->slot(), &isFixedSlot, &offset);
|
||||
|
||||
ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
|
||||
ICSetProp_Native *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub || !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
|
||||
StripPreliminaryObjectStubs(cx, stub);
|
||||
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
TryAttachTypedObjectSetPropStub(JSContext *cx, HandleScript script,
|
||||
ICSetProp_Fallback *stub, HandleId id,
|
||||
|
@ -8253,14 +8383,9 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
|
|||
RootedId id(cx, NameToId(name));
|
||||
|
||||
RootedObject obj(cx, ToObjectFromStack(cx, lhs));
|
||||
if (!obj)
|
||||
if (!obj || !obj->getGroup(cx))
|
||||
return false;
|
||||
ReceiverGuard::Token oldGuard = ReceiverGuard::objectToken(obj);
|
||||
RootedShape oldShape(cx, obj->maybeShape());
|
||||
RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
|
||||
if (!oldGroup)
|
||||
return false;
|
||||
uint32_t oldSlots = obj->isNative() ? obj->as<NativeObject>().numDynamicSlots() : 0;
|
||||
ReceiverGuard::RootedStackGuard oldGuard(cx, ReceiverGuard::StackGuard(obj, true));
|
||||
|
||||
bool attached = false;
|
||||
// There are some reasons we can fail to attach a stub that are temporary.
|
||||
|
@ -8313,8 +8438,8 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
|
|||
|
||||
if (!attached &&
|
||||
lhs.isObject() &&
|
||||
!TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldShape,
|
||||
oldGroup, oldSlots, name, id, rhs, &attached))
|
||||
!TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldGuard,
|
||||
name, id, rhs, &attached))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -8330,6 +8455,15 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
|
|||
if (attached)
|
||||
return true;
|
||||
|
||||
if (!attached &&
|
||||
lhs.isObject() &&
|
||||
!TryAttachUnboxedExpandoSetPropStub(cx, script, stub, id, obj, oldGuard.shape, rhs, &attached))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (attached)
|
||||
return true;
|
||||
|
||||
if (!attached &&
|
||||
lhs.isObject() &&
|
||||
!TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
|
||||
|
@ -8402,23 +8536,17 @@ ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler &masm, Handle<
|
|||
bool
|
||||
ICSetProp_Native::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
Label failure;
|
||||
Label failure, failurePopObject;
|
||||
|
||||
// Guard input is an object.
|
||||
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
|
||||
Register objReg = masm.extractObject(R0, ExtractTemp0);
|
||||
|
||||
GeneralRegisterSet regs(availableGeneralRegs(2));
|
||||
Register scratch = regs.takeAny();
|
||||
|
||||
// Unbox and shape guard.
|
||||
Register objReg = masm.extractObject(R0, ExtractTemp0);
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfShape()), scratch);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
|
||||
|
||||
// Guard that the object group matches.
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfGroup()), scratch);
|
||||
masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch,
|
||||
&failure);
|
||||
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_, true), objReg, scratch,
|
||||
ICSetProp_Native::offsetOfReceiverGuard(), &failure);
|
||||
|
||||
// Stow both R0 and R1 (object and value).
|
||||
EmitStowICValues(masm, 2);
|
||||
|
@ -8437,7 +8565,13 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
regs.takeUnchecked(objReg);
|
||||
|
||||
Register holderReg;
|
||||
if (isFixedSlot_) {
|
||||
if (obj_->is<UnboxedPlainObject>()) {
|
||||
// We are loading off the expando object, so use that for the holder.
|
||||
holderReg = regs.takeAny();
|
||||
masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
|
||||
if (!isFixedSlot_)
|
||||
masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
|
||||
} else if (isFixedSlot_) {
|
||||
holderReg = objReg;
|
||||
} else {
|
||||
holderReg = regs.takeAny();
|
||||
|
@ -8462,6 +8596,11 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
masm.moveValue(R1, R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
if (failurePopObject.used()) {
|
||||
masm.bind(&failurePopObject);
|
||||
masm.pop(objReg);
|
||||
}
|
||||
|
||||
// Failure case - jump to next stub
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
|
@ -8472,8 +8611,6 @@ ICUpdatedStub *
|
|||
ICSetPropNativeAddCompiler::getStub(ICStubSpace *space)
|
||||
{
|
||||
AutoShapeVector shapes(cx);
|
||||
if (!shapes.append(oldShape_))
|
||||
return nullptr;
|
||||
|
||||
if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
|
||||
return nullptr;
|
||||
|
@ -8497,8 +8634,7 @@ ICSetPropNativeAddCompiler::getStub(ICStubSpace *space)
|
|||
bool
|
||||
ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
Label failure;
|
||||
Label failureUnstow;
|
||||
Label failure, failurePopObject, failureUnstow;
|
||||
|
||||
// Guard input is an object.
|
||||
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
|
||||
|
@ -8506,15 +8642,10 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
|
|||
GeneralRegisterSet regs(availableGeneralRegs(2));
|
||||
Register scratch = regs.takeAny();
|
||||
|
||||
// Unbox and guard against old shape.
|
||||
// Unbox and guard that the object group matches.
|
||||
Register objReg = masm.extractObject(R0, ExtractTemp0);
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<0>::offsetOfShape(0)), scratch);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
|
||||
|
||||
// Guard that the object group matches.
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfGroup()), scratch);
|
||||
masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch,
|
||||
&failure);
|
||||
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_, true), objReg, scratch,
|
||||
ICSetProp_NativeAdd::offsetOfReceiverGuard(), &failure);
|
||||
|
||||
// Stow both R0 and R1 (object and value).
|
||||
EmitStowICValues(masm, 2);
|
||||
|
@ -8526,7 +8657,7 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
|
|||
for (size_t i = 0; i < protoChainDepth_; i++) {
|
||||
masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg);
|
||||
masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failureUnstow);
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<0>::offsetOfShape(i + 1)),
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<1>::offsetOfShape(i)),
|
||||
scratch);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failureUnstow);
|
||||
}
|
||||
|
@ -8546,44 +8677,60 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
|
|||
regs = availableGeneralRegs(2);
|
||||
scratch = regs.takeAny();
|
||||
|
||||
// Changing object shape. Write the object's new shape.
|
||||
Address shapeAddr(objReg, JSObject::offsetOfShape());
|
||||
EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
|
||||
masm.storePtr(scratch, shapeAddr);
|
||||
if (obj_->is<PlainObject>()) {
|
||||
// Try to change the object's group.
|
||||
Label noGroupChange;
|
||||
|
||||
// Try to change the object's group.
|
||||
Label noGroupChange;
|
||||
// Check if the cache has a new group to change to.
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
|
||||
masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange);
|
||||
|
||||
// Check if the cache has a new group to change to.
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
|
||||
masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange);
|
||||
// Check if the old group still has a newScript.
|
||||
masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch);
|
||||
masm.branchPtr(Assembler::Equal,
|
||||
Address(scratch, ObjectGroup::offsetOfAddendum()),
|
||||
ImmWord(0),
|
||||
&noGroupChange);
|
||||
|
||||
// Check if the old group still has a newScript.
|
||||
masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch);
|
||||
masm.branchPtr(Assembler::Equal,
|
||||
Address(scratch, ObjectGroup::offsetOfAddendum()),
|
||||
ImmWord(0),
|
||||
&noGroupChange);
|
||||
// Reload the new group from the cache.
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
|
||||
|
||||
// Reload the new group from the cache.
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
|
||||
// Change the object's group.
|
||||
Address groupAddr(objReg, JSObject::offsetOfGroup());
|
||||
EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup);
|
||||
masm.storePtr(scratch, groupAddr);
|
||||
|
||||
// Change the object's group.
|
||||
Address groupAddr(objReg, JSObject::offsetOfGroup());
|
||||
EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup);
|
||||
masm.storePtr(scratch, groupAddr);
|
||||
|
||||
masm.bind(&noGroupChange);
|
||||
masm.bind(&noGroupChange);
|
||||
}
|
||||
|
||||
Register holderReg;
|
||||
regs.add(R0);
|
||||
regs.takeUnchecked(objReg);
|
||||
if (isFixedSlot_) {
|
||||
holderReg = objReg;
|
||||
} else {
|
||||
if (obj_->is<UnboxedPlainObject>()) {
|
||||
holderReg = regs.takeAny();
|
||||
masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
|
||||
masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
|
||||
|
||||
// Write the expando object's new shape.
|
||||
Address shapeAddr(holderReg, JSObject::offsetOfShape());
|
||||
EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
|
||||
masm.storePtr(scratch, shapeAddr);
|
||||
|
||||
if (!isFixedSlot_)
|
||||
masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
|
||||
} else {
|
||||
// Write the object's new shape.
|
||||
Address shapeAddr(objReg, JSObject::offsetOfShape());
|
||||
EmitPreBarrier(masm, shapeAddr, MIRType_Shape);
|
||||
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
|
||||
masm.storePtr(scratch, shapeAddr);
|
||||
|
||||
if (isFixedSlot_) {
|
||||
holderReg = objReg;
|
||||
} else {
|
||||
holderReg = regs.takeAny();
|
||||
masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the store. No write barrier required since this is a new
|
||||
|
@ -8605,6 +8752,12 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
|
|||
masm.moveValue(R1, R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
if (failurePopObject.used()) {
|
||||
masm.bind(&failurePopObject);
|
||||
masm.pop(objReg);
|
||||
masm.jump(&failure);
|
||||
}
|
||||
|
||||
// Failure case - jump to next stub
|
||||
masm.bind(&failureUnstow);
|
||||
EmitUnstowICValues(masm, 2);
|
||||
|
@ -8823,7 +8976,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
|
||||
// Unbox and shape guard.
|
||||
Register objReg = masm.extractObject(R0, ExtractTemp0);
|
||||
GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
|
||||
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch,
|
||||
ICSetProp_CallScripted::offsetOfGuard(), &failureUnstow);
|
||||
|
||||
Register holderReg = regs.takeAny();
|
||||
|
@ -8942,7 +9095,7 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
|
||||
// Unbox and shape guard.
|
||||
Register objReg = masm.extractObject(R0, ExtractTemp0);
|
||||
GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
|
||||
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch,
|
||||
ICSetProp_CallNative::offsetOfGuard(), &failureUnstow);
|
||||
|
||||
Register holderReg = regs.takeAny();
|
||||
|
@ -11765,8 +11918,10 @@ ICGetProp_Primitive::ICGetProp_Primitive(JitCode *stubCode, ICStub *firstMonitor
|
|||
{ }
|
||||
|
||||
ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode *stubCode,
|
||||
ICStub *firstMonitorStub, uint32_t offset)
|
||||
ICStub *firstMonitorStub,
|
||||
ReceiverGuard::StackGuard guard, uint32_t offset)
|
||||
: ICMonitoredStub(kind, stubCode, firstMonitorStub),
|
||||
receiverGuard_(guard),
|
||||
offset_(offset)
|
||||
{ }
|
||||
|
||||
|
@ -11774,15 +11929,14 @@ ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode *stubCode,
|
|||
ICGetProp_Native::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
|
||||
ICGetProp_Native &other)
|
||||
{
|
||||
return New<ICGetProp_Native>(space, other.jitCode(), firstMonitorStub, other.shape(),
|
||||
return New<ICGetProp_Native>(space, other.jitCode(), firstMonitorStub, other.receiverGuard(),
|
||||
other.offset());
|
||||
}
|
||||
|
||||
ICGetProp_NativePrototype::ICGetProp_NativePrototype(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
ReceiverGuard::Token guard, uint32_t offset,
|
||||
ReceiverGuard::StackGuard guard, uint32_t offset,
|
||||
JSObject *holder, Shape *holderShape)
|
||||
: ICGetPropNativeStub(GetProp_NativePrototype, stubCode, firstMonitorStub, offset),
|
||||
guard_(guard),
|
||||
: ICGetPropNativeStub(GetProp_NativePrototype, stubCode, firstMonitorStub, guard, offset),
|
||||
holder_(holder),
|
||||
holderShape_(holderShape)
|
||||
{ }
|
||||
|
@ -11792,12 +11946,13 @@ ICGetProp_NativePrototype::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
|
|||
ICGetProp_NativePrototype &other)
|
||||
{
|
||||
return New<ICGetProp_NativePrototype>(space, other.jitCode(), firstMonitorStub,
|
||||
other.guard().token(),
|
||||
other.offset(), other.holder_, other.holderShape_);
|
||||
other.receiverGuard(), other.offset(),
|
||||
other.holder_, other.holderShape_);
|
||||
}
|
||||
|
||||
ICGetProp_NativeDoesNotExist::ICGetProp_NativeDoesNotExist(
|
||||
JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::Token guard, size_t protoChainDepth)
|
||||
JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::StackGuard guard,
|
||||
size_t protoChainDepth)
|
||||
: ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub),
|
||||
guard_(guard)
|
||||
{
|
||||
|
@ -11816,7 +11971,7 @@ ICGetProp_NativeDoesNotExist::offsetOfShape(size_t idx)
|
|||
|
||||
template <size_t ProtoChainDepth>
|
||||
ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>::ICGetProp_NativeDoesNotExistImpl(
|
||||
JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::Token guard,
|
||||
JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::StackGuard guard,
|
||||
const AutoShapeVector *shapes)
|
||||
: ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, guard, ProtoChainDepth)
|
||||
{
|
||||
|
@ -11838,7 +11993,7 @@ ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler(
|
|||
}
|
||||
|
||||
ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
ReceiverGuard::Token receiverGuard, JSObject *holder,
|
||||
ReceiverGuard::StackGuard receiverGuard, JSObject *holder,
|
||||
Shape *holderShape, JSFunction *getter,
|
||||
uint32_t pcOffset)
|
||||
: ICMonitoredStub(kind, stubCode, firstMonitorStub),
|
||||
|
@ -11867,7 +12022,7 @@ ICGetProp_CallScripted::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
|
|||
ICGetProp_CallScripted &other)
|
||||
{
|
||||
return New<ICGetProp_CallScripted>(space, other.jitCode(), firstMonitorStub,
|
||||
other.receiverGuard().token(),
|
||||
other.receiverGuard(),
|
||||
other.holder_, other.holderShape_,
|
||||
other.getter_, other.pcOffset_);
|
||||
}
|
||||
|
@ -11877,39 +12032,34 @@ ICGetProp_CallNative::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
|
|||
ICGetProp_CallNative &other)
|
||||
{
|
||||
return New<ICGetProp_CallNative>(space, other.jitCode(), firstMonitorStub,
|
||||
other.receiverGuard().token(), other.holder_,
|
||||
other.receiverGuard(), other.holder_,
|
||||
other.holderShape_, other.getter_, other.pcOffset_);
|
||||
}
|
||||
|
||||
ICSetProp_Native::ICSetProp_Native(JitCode *stubCode, ObjectGroup *group, Shape *shape,
|
||||
ICSetProp_Native::ICSetProp_Native(JitCode *stubCode, ReceiverGuard::StackGuard guard,
|
||||
uint32_t offset)
|
||||
: ICUpdatedStub(SetProp_Native, stubCode),
|
||||
group_(group),
|
||||
shape_(shape),
|
||||
receiverGuard_(guard),
|
||||
offset_(offset)
|
||||
{ }
|
||||
|
||||
ICSetProp_Native *
|
||||
ICSetProp_Native::Compiler::getStub(ICStubSpace *space)
|
||||
{
|
||||
RootedObjectGroup group(cx, obj_->getGroup(cx));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
|
||||
ICSetProp_Native *stub = ICStub::New<ICSetProp_Native>(space, getStubCode(), group, shape, offset_);
|
||||
ReceiverGuard::StackGuard guard(obj_, true);
|
||||
ICSetProp_Native *stub = ICStub::New<ICSetProp_Native>(space, getStubCode(), guard, offset_);
|
||||
if (!stub || !stub->initUpdatingChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
}
|
||||
|
||||
ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group,
|
||||
ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, ReceiverGuard::StackGuard guard,
|
||||
size_t protoChainDepth,
|
||||
Shape *newShape,
|
||||
ObjectGroup *newGroup,
|
||||
uint32_t offset)
|
||||
: ICUpdatedStub(SetProp_NativeAdd, stubCode),
|
||||
group_(group),
|
||||
receiverGuard_(guard),
|
||||
newShape_(newShape),
|
||||
newGroup_(newGroup),
|
||||
offset_(offset)
|
||||
|
@ -11920,28 +12070,26 @@ ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group,
|
|||
|
||||
template <size_t ProtoChainDepth>
|
||||
ICSetProp_NativeAddImpl<ProtoChainDepth>::ICSetProp_NativeAddImpl(JitCode *stubCode,
|
||||
ObjectGroup *group,
|
||||
ReceiverGuard::StackGuard guard,
|
||||
const AutoShapeVector *shapes,
|
||||
Shape *newShape,
|
||||
ObjectGroup *newGroup,
|
||||
uint32_t offset)
|
||||
: ICSetProp_NativeAdd(stubCode, group, ProtoChainDepth, newShape, newGroup, offset)
|
||||
: ICSetProp_NativeAdd(stubCode, guard, ProtoChainDepth, newShape, newGroup, offset)
|
||||
{
|
||||
MOZ_ASSERT(shapes->length() == NumShapes);
|
||||
for (size_t i = 0; i < NumShapes; i++)
|
||||
for (int i = 0; i < int(NumShapes); i++) // Use an int here to avoid compiler warnings.
|
||||
shapes_[i].init((*shapes)[i]);
|
||||
}
|
||||
|
||||
ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext *cx, HandleObject obj,
|
||||
HandleShape oldShape,
|
||||
HandleObjectGroup oldGroup,
|
||||
ReceiverGuard::StackGuard oldGuard,
|
||||
size_t protoChainDepth,
|
||||
bool isFixedSlot,
|
||||
uint32_t offset)
|
||||
: ICStubCompiler(cx, ICStub::SetProp_NativeAdd),
|
||||
obj_(cx, obj),
|
||||
oldShape_(cx, oldShape),
|
||||
oldGroup_(cx, oldGroup),
|
||||
oldGuard_(cx, oldGuard),
|
||||
protoChainDepth_(protoChainDepth),
|
||||
isFixedSlot_(isFixedSlot),
|
||||
offset_(offset)
|
||||
|
@ -11949,7 +12097,7 @@ ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext *cx, HandleObje
|
|||
MOZ_ASSERT(protoChainDepth_ <= ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH);
|
||||
}
|
||||
|
||||
ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::Token guard,
|
||||
ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::StackGuard guard,
|
||||
JSObject *holder, Shape *holderShape,
|
||||
JSFunction *setter, uint32_t pcOffset)
|
||||
: ICStub(kind, stubCode),
|
||||
|
@ -11965,14 +12113,14 @@ ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverG
|
|||
/* static */ ICSetProp_CallScripted *
|
||||
ICSetProp_CallScripted::Clone(ICStubSpace *space, ICStub *, ICSetProp_CallScripted &other)
|
||||
{
|
||||
return New<ICSetProp_CallScripted>(space, other.jitCode(), other.guard().token(), other.holder_,
|
||||
return New<ICSetProp_CallScripted>(space, other.jitCode(), other.guard(), other.holder_,
|
||||
other.holderShape_, other.setter_, other.pcOffset_);
|
||||
}
|
||||
|
||||
/* static */ ICSetProp_CallNative *
|
||||
ICSetProp_CallNative::Clone(ICStubSpace *space, ICStub *, ICSetProp_CallNative &other)
|
||||
{
|
||||
return New<ICSetProp_CallNative>(space, other.jitCode(), other.guard().token(), other.holder_,
|
||||
return New<ICSetProp_CallNative>(space, other.jitCode(), other.guard(), other.holder_,
|
||||
other.holderShape_, other.setter_, other.pcOffset_);
|
||||
}
|
||||
|
||||
|
@ -12082,8 +12230,8 @@ ICGetPropCallDOMProxyNativeStub::ICGetPropCallDOMProxyNativeStub(Kind kind, JitC
|
|||
Shape *holderShape,
|
||||
JSFunction *getter,
|
||||
uint32_t pcOffset)
|
||||
: ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::shapeToken(shape),
|
||||
holder, holderShape, getter, pcOffset),
|
||||
: ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(shape),
|
||||
holder, holderShape, getter, pcOffset),
|
||||
expandoShape_(expandoShape)
|
||||
{ }
|
||||
|
||||
|
|
|
@ -3841,26 +3841,147 @@ class ICGetProp_StringLength : public ICStub
|
|||
};
|
||||
};
|
||||
|
||||
// Structure encapsulating the guarding that needs to be done on an object
|
||||
// before it can be accessed or modified.
|
||||
class ReceiverGuard
|
||||
{
|
||||
// Group to guard on, or null. If the object is not unboxed and the IC does
|
||||
// not require the object to have a specific group, this is null.
|
||||
// Otherwise, this is the object's group.
|
||||
HeapPtrObjectGroup group_;
|
||||
|
||||
// Shape to guard on, or null. If the object is not unboxed then this is
|
||||
// the object's shape. If the object is unboxed, then this is the shape of
|
||||
// the object's expando, null if the object has no expando.
|
||||
HeapPtrShape shape_;
|
||||
|
||||
public:
|
||||
struct StackGuard;
|
||||
|
||||
struct RootedStackGuard
|
||||
{
|
||||
RootedObjectGroup group;
|
||||
RootedShape shape;
|
||||
|
||||
RootedStackGuard(JSContext *cx, const StackGuard &guard)
|
||||
: group(cx, guard.group), shape(cx, guard.shape)
|
||||
{}
|
||||
};
|
||||
|
||||
struct StackGuard
|
||||
{
|
||||
ObjectGroup *group;
|
||||
Shape *shape;
|
||||
|
||||
MOZ_IMPLICIT StackGuard(const ReceiverGuard &guard)
|
||||
: group(guard.group_), shape(guard.shape_)
|
||||
{}
|
||||
|
||||
MOZ_IMPLICIT StackGuard(const RootedStackGuard &guard)
|
||||
: group(guard.group), shape(guard.shape)
|
||||
{}
|
||||
|
||||
explicit StackGuard(JSObject *obj, bool guardGroup = false)
|
||||
: group(nullptr), shape(nullptr)
|
||||
{
|
||||
if (obj) {
|
||||
shape = obj->maybeShape();
|
||||
if (shape) {
|
||||
if (guardGroup)
|
||||
group = obj->group();
|
||||
} else {
|
||||
group = obj->group();
|
||||
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando())
|
||||
shape = expando->lastProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
explicit StackGuard(Shape *shape)
|
||||
: group(nullptr), shape(shape)
|
||||
{}
|
||||
|
||||
Shape *ownShape() const {
|
||||
// Get a shape belonging to the object itself, rather than an unboxed expando.
|
||||
if (!group || !group->maybeUnboxedLayout())
|
||||
return shape;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
explicit ReceiverGuard(const StackGuard &guard)
|
||||
: group_(guard.group), shape_(guard.shape)
|
||||
{}
|
||||
|
||||
bool matches(const StackGuard &guard) {
|
||||
return group_ == guard.group && shape_ == guard.shape;
|
||||
}
|
||||
|
||||
void update(const StackGuard &other) {
|
||||
group_ = other.group;
|
||||
shape_ = other.shape;
|
||||
}
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
Shape *shape() const {
|
||||
return shape_;
|
||||
}
|
||||
ObjectGroup *group() const {
|
||||
return group_;
|
||||
}
|
||||
|
||||
Shape *ownShape() const {
|
||||
return StackGuard(*this).ownShape();
|
||||
}
|
||||
|
||||
static size_t offsetOfShape() {
|
||||
return offsetof(ReceiverGuard, shape_);
|
||||
}
|
||||
static size_t offsetOfGroup() {
|
||||
return offsetof(ReceiverGuard, group_);
|
||||
}
|
||||
|
||||
// Bits to munge into IC compiler keys when that IC has a ReceiverGuard.
|
||||
// This uses at two bits for data.
|
||||
static int32_t keyBits(JSObject *obj) {
|
||||
if (obj->maybeShape())
|
||||
return 0;
|
||||
return obj->as<UnboxedPlainObject>().maybeExpando() ? 1 : 2;
|
||||
}
|
||||
};
|
||||
|
||||
// Base class for native GetProp stubs.
|
||||
class ICGetPropNativeStub : public ICMonitoredStub
|
||||
{
|
||||
// Object shape/group.
|
||||
ReceiverGuard receiverGuard_;
|
||||
|
||||
// Fixed or dynamic slot offset.
|
||||
uint32_t offset_;
|
||||
|
||||
protected:
|
||||
ICGetPropNativeStub(ICStub::Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
uint32_t offset);
|
||||
ReceiverGuard::StackGuard guard, uint32_t offset);
|
||||
|
||||
public:
|
||||
ReceiverGuard &receiverGuard() {
|
||||
return receiverGuard_;
|
||||
}
|
||||
uint32_t offset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
void notePreliminaryObject() {
|
||||
extra_ = 1;
|
||||
}
|
||||
bool hasPreliminaryObject() const {
|
||||
return extra_;
|
||||
}
|
||||
|
||||
static size_t offsetOfReceiverGuard() {
|
||||
return offsetof(ICGetPropNativeStub, receiverGuard_);
|
||||
}
|
||||
static size_t offsetOfOffset() {
|
||||
return offsetof(ICGetPropNativeStub, offset_);
|
||||
}
|
||||
|
@ -3871,103 +3992,16 @@ class ICGetProp_Native : public ICGetPropNativeStub
|
|||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
// Object shape (lastProperty).
|
||||
HeapPtrShape shape_;
|
||||
|
||||
ICGetProp_Native(JitCode *stubCode, ICStub *firstMonitorStub, Shape *shape,
|
||||
ICGetProp_Native(JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::StackGuard guard,
|
||||
uint32_t offset)
|
||||
: ICGetPropNativeStub(GetProp_Native, stubCode, firstMonitorStub, offset),
|
||||
shape_(shape)
|
||||
: ICGetPropNativeStub(GetProp_Native, stubCode, firstMonitorStub, guard, offset)
|
||||
{}
|
||||
|
||||
public:
|
||||
HeapPtrShape &shape() {
|
||||
return shape_;
|
||||
}
|
||||
static size_t offsetOfShape() {
|
||||
return offsetof(ICGetProp_Native, shape_);
|
||||
}
|
||||
|
||||
static ICGetProp_Native *Clone(ICStubSpace *space, ICStub *firstMonitorStub,
|
||||
ICGetProp_Native &other);
|
||||
};
|
||||
|
||||
// Structure encapsulating the guarding that needs to be done on an object
|
||||
// which might be either native or unboxed. In the former case, only the
|
||||
// object's shape needs to be guarded. In the latter case, only the object's
|
||||
// group needs to be guarded.
|
||||
class ReceiverGuard
|
||||
{
|
||||
HeapPtrShape shape_;
|
||||
HeapPtrObjectGroup group_;
|
||||
|
||||
public:
|
||||
typedef uintptr_t Token;
|
||||
|
||||
static Token shapeToken(Shape *shape) {
|
||||
return reinterpret_cast<Token>(shape) | 1;
|
||||
}
|
||||
|
||||
static Token groupToken(ObjectGroup *group) {
|
||||
return reinterpret_cast<Token>(group);
|
||||
}
|
||||
|
||||
static Shape *tokenShape(Token token) {
|
||||
if (token & 1)
|
||||
return reinterpret_cast<Shape *>(token & ~1);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static ObjectGroup *tokenGroup(Token token) {
|
||||
if (!(token & 1))
|
||||
return reinterpret_cast<ObjectGroup *>(token);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Token objectToken(JSObject *obj) {
|
||||
if (obj->is<UnboxedPlainObject>())
|
||||
return groupToken(obj->group());
|
||||
return shapeToken(obj->maybeShape());
|
||||
}
|
||||
|
||||
explicit ReceiverGuard(Token token)
|
||||
: shape_(tokenShape(token)), group_(tokenGroup(token))
|
||||
{
|
||||
MOZ_ASSERT(shape_ || group_);
|
||||
}
|
||||
|
||||
void update(Token token) {
|
||||
MOZ_ASSERT(!!shape_ == !!tokenShape(token));
|
||||
MOZ_ASSERT(!!group_ == !!tokenGroup(token));
|
||||
shape_ = tokenShape(token);
|
||||
group_ = tokenGroup(token);
|
||||
MOZ_ASSERT(shape_ || group_);
|
||||
}
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
Token token() {
|
||||
MOZ_ASSERT(!!shape_ != !!group_);
|
||||
if (shape_)
|
||||
return shapeToken(shape_);
|
||||
return groupToken(group_);
|
||||
}
|
||||
|
||||
Shape *shape() const {
|
||||
return shape_;
|
||||
}
|
||||
ObjectGroup *group() const {
|
||||
return group_;
|
||||
}
|
||||
|
||||
static size_t offsetOfShape() {
|
||||
return offsetof(ReceiverGuard, shape_);
|
||||
}
|
||||
static size_t offsetOfGroup() {
|
||||
return offsetof(ReceiverGuard, group_);
|
||||
}
|
||||
};
|
||||
|
||||
// Stub for accessing a property on the native prototype of a native or unboxed
|
||||
// object. Note that due to the shape teleporting optimization, we only have to
|
||||
// guard on the object's shape/group and the holder's shape.
|
||||
|
@ -3976,15 +4010,12 @@ class ICGetProp_NativePrototype : public ICGetPropNativeStub
|
|||
friend class ICStubSpace;
|
||||
|
||||
protected:
|
||||
// Object shape/group.
|
||||
ReceiverGuard guard_;
|
||||
|
||||
// Holder and its shape.
|
||||
HeapPtrObject holder_;
|
||||
HeapPtrShape holderShape_;
|
||||
|
||||
ICGetProp_NativePrototype(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
ReceiverGuard::Token guard,
|
||||
ReceiverGuard::StackGuard guard,
|
||||
uint32_t offset, JSObject *holder, Shape *holderShape);
|
||||
|
||||
public:
|
||||
|
@ -3993,18 +4024,12 @@ class ICGetProp_NativePrototype : public ICGetPropNativeStub
|
|||
ICGetProp_NativePrototype &other);
|
||||
|
||||
public:
|
||||
ReceiverGuard &guard() {
|
||||
return guard_;
|
||||
}
|
||||
HeapPtrObject &holder() {
|
||||
return holder_;
|
||||
}
|
||||
HeapPtrShape &holderShape() {
|
||||
return holderShape_;
|
||||
}
|
||||
static size_t offsetOfGuard() {
|
||||
return offsetof(ICGetProp_NativePrototype, guard_);
|
||||
}
|
||||
static size_t offsetOfHolder() {
|
||||
return offsetof(ICGetProp_NativePrototype, holder_);
|
||||
}
|
||||
|
@ -4033,7 +4058,7 @@ class ICGetPropNativeCompiler : public ICStubCompiler
|
|||
(static_cast<int32_t>(isCallProp_) << 16) |
|
||||
(static_cast<int32_t>(isFixedSlot_) << 17) |
|
||||
(static_cast<int32_t>(inputDefinitelyObject_) << 18) |
|
||||
(static_cast<int32_t>(obj_->isNative()) << 19);
|
||||
(ReceiverGuard::keyBits(obj_) << 19);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -4067,7 +4092,7 @@ class ICGetProp_NativeDoesNotExist : public ICMonitoredStub
|
|||
|
||||
protected:
|
||||
ICGetProp_NativeDoesNotExist(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
ReceiverGuard::Token guard,
|
||||
ReceiverGuard::StackGuard guard,
|
||||
size_t protoChainDepth);
|
||||
|
||||
public:
|
||||
|
@ -4105,7 +4130,7 @@ class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist
|
|||
mozilla::Array<HeapPtrShape, NumShapes> shapes_;
|
||||
|
||||
ICGetProp_NativeDoesNotExistImpl(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
ReceiverGuard::Token guard,
|
||||
ReceiverGuard::StackGuard guard,
|
||||
const AutoShapeVector *shapes);
|
||||
|
||||
public:
|
||||
|
@ -4129,8 +4154,8 @@ class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
|
|||
protected:
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) |
|
||||
(static_cast<int32_t>(obj_->isNative()) << 16) |
|
||||
(static_cast<int32_t>(protoChainDepth_) << 17);
|
||||
(ReceiverGuard::keyBits(obj_) << 16) |
|
||||
(static_cast<int32_t>(protoChainDepth_) << 18);
|
||||
}
|
||||
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
@ -4141,8 +4166,9 @@ class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
|
|||
|
||||
template <size_t ProtoChainDepth>
|
||||
ICStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) {
|
||||
ReceiverGuard::StackGuard guard(obj_);
|
||||
return ICStub::New<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> >
|
||||
(space, getStubCode(), firstMonitorStub_, ReceiverGuard::objectToken(obj_), shapes);
|
||||
(space, getStubCode(), firstMonitorStub_, guard, shapes);
|
||||
}
|
||||
|
||||
ICStub *getStub(ICStubSpace *space);
|
||||
|
@ -4299,7 +4325,7 @@ class ICGetPropCallGetter : public ICMonitoredStub
|
|||
uint32_t pcOffset_;
|
||||
|
||||
ICGetPropCallGetter(Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
ReceiverGuard::Token receiverGuard, JSObject *holder,
|
||||
ReceiverGuard::StackGuard receiverGuard, JSObject *holder,
|
||||
Shape *holderShape, JSFunction *getter, uint32_t pcOffset);
|
||||
|
||||
public:
|
||||
|
@ -4351,9 +4377,9 @@ class ICGetPropCallGetter : public ICMonitoredStub
|
|||
// ICGetProp_CallNative::Compiler::getKey adds more bits to our
|
||||
// return value, so be careful when making changes here.
|
||||
return static_cast<int32_t>(kind) |
|
||||
(static_cast<int32_t>(receiver_->isNative()) << 16) |
|
||||
(static_cast<int32_t>(!!outerClass_) << 17) |
|
||||
(static_cast<int32_t>(receiver_ != holder_) << 18);
|
||||
(ReceiverGuard::keyBits(receiver_) << 16) |
|
||||
(static_cast<int32_t>(!!outerClass_) << 18) |
|
||||
(static_cast<int32_t>(receiver_ != holder_) << 19);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -4382,7 +4408,7 @@ class ICGetProp_CallScripted : public ICGetPropCallGetter
|
|||
|
||||
protected:
|
||||
ICGetProp_CallScripted(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
ReceiverGuard::Token receiverGuard,
|
||||
ReceiverGuard::StackGuard receiverGuard,
|
||||
JSObject *holder, Shape *holderShape,
|
||||
JSFunction *getter, uint32_t pcOffset)
|
||||
: ICGetPropCallGetter(GetProp_CallScripted, stubCode, firstMonitorStub,
|
||||
|
@ -4406,7 +4432,7 @@ class ICGetProp_CallScripted : public ICGetPropCallGetter
|
|||
{}
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
|
||||
ReceiverGuard::StackGuard guard(receiver_);
|
||||
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
|
||||
return ICStub::New<ICGetProp_CallScripted>(space, getStubCode(), firstMonitorStub_,
|
||||
guard, holder_, holderShape, getter_,
|
||||
|
@ -4423,7 +4449,8 @@ class ICGetProp_CallNative : public ICGetPropCallGetter
|
|||
protected:
|
||||
|
||||
ICGetProp_CallNative(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
ReceiverGuard::Token receiverGuard, JSObject *holder, Shape *holderShape,
|
||||
ReceiverGuard::StackGuard receiverGuard,
|
||||
JSObject *holder, Shape *holderShape,
|
||||
JSFunction *getter, uint32_t pcOffset)
|
||||
: ICGetPropCallGetter(GetProp_CallNative, stubCode, firstMonitorStub,
|
||||
receiverGuard, holder, holderShape, getter, pcOffset)
|
||||
|
@ -4441,8 +4468,8 @@ class ICGetProp_CallNative : public ICGetPropCallGetter
|
|||
|
||||
virtual int32_t getKey() const {
|
||||
int32_t baseKey = ICGetPropCallGetter::Compiler::getKey();
|
||||
MOZ_ASSERT((baseKey >> 19) == 0);
|
||||
return baseKey | (static_cast<int32_t>(inputDefinitelyObject_) << 19);
|
||||
MOZ_ASSERT((baseKey >> 20) == 0);
|
||||
return baseKey | (static_cast<int32_t>(inputDefinitelyObject_) << 20);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -4456,7 +4483,7 @@ class ICGetProp_CallNative : public ICGetPropCallGetter
|
|||
{}
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
|
||||
ReceiverGuard::StackGuard guard(receiver_);
|
||||
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
|
||||
return ICStub::New<ICGetProp_CallNative>(space, getStubCode(), firstMonitorStub_,
|
||||
guard, holder_, holderShape,
|
||||
|
@ -4736,18 +4763,14 @@ class ICSetProp_Native : public ICUpdatedStub
|
|||
friend class ICStubSpace;
|
||||
|
||||
protected: // Protected to silence Clang warning.
|
||||
HeapPtrObjectGroup group_;
|
||||
HeapPtrShape shape_;
|
||||
ReceiverGuard receiverGuard_;
|
||||
uint32_t offset_;
|
||||
|
||||
ICSetProp_Native(JitCode *stubCode, ObjectGroup *group, Shape *shape, uint32_t offset);
|
||||
ICSetProp_Native(JitCode *stubCode, ReceiverGuard::StackGuard guard, uint32_t offset);
|
||||
|
||||
public:
|
||||
HeapPtrObjectGroup &group() {
|
||||
return group_;
|
||||
}
|
||||
HeapPtrShape &shape() {
|
||||
return shape_;
|
||||
ReceiverGuard &receiverGuard() {
|
||||
return receiverGuard_;
|
||||
}
|
||||
void notePreliminaryObject() {
|
||||
extra_ = 1;
|
||||
|
@ -4755,11 +4778,8 @@ class ICSetProp_Native : public ICUpdatedStub
|
|||
bool hasPreliminaryObject() const {
|
||||
return extra_;
|
||||
}
|
||||
static size_t offsetOfGroup() {
|
||||
return offsetof(ICSetProp_Native, group_);
|
||||
}
|
||||
static size_t offsetOfShape() {
|
||||
return offsetof(ICSetProp_Native, shape_);
|
||||
static size_t offsetOfReceiverGuard() {
|
||||
return offsetof(ICSetProp_Native, receiverGuard_);
|
||||
}
|
||||
static size_t offsetOfOffset() {
|
||||
return offsetof(ICSetProp_Native, offset_);
|
||||
|
@ -4772,7 +4792,9 @@ class ICSetProp_Native : public ICUpdatedStub
|
|||
|
||||
protected:
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16);
|
||||
return static_cast<int32_t>(kind) |
|
||||
(static_cast<int32_t>(isFixedSlot_) << 16) |
|
||||
(ReceiverGuard::keyBits(obj_) << 17);
|
||||
}
|
||||
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
@ -4798,20 +4820,20 @@ class ICSetProp_NativeAdd : public ICUpdatedStub
|
|||
static const size_t MAX_PROTO_CHAIN_DEPTH = 4;
|
||||
|
||||
protected: // Protected to silence Clang warning.
|
||||
HeapPtrObjectGroup group_;
|
||||
ReceiverGuard receiverGuard_;
|
||||
HeapPtrShape newShape_;
|
||||
HeapPtrObjectGroup newGroup_;
|
||||
uint32_t offset_;
|
||||
|
||||
ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group, size_t protoChainDepth,
|
||||
ICSetProp_NativeAdd(JitCode *stubCode, ReceiverGuard::StackGuard guard, size_t protoChainDepth,
|
||||
Shape *newShape, ObjectGroup *newGroup, uint32_t offset);
|
||||
|
||||
public:
|
||||
size_t protoChainDepth() const {
|
||||
return extra_;
|
||||
}
|
||||
HeapPtrObjectGroup &group() {
|
||||
return group_;
|
||||
ReceiverGuard &receiverGuard() {
|
||||
return receiverGuard_;
|
||||
}
|
||||
HeapPtrShape &newShape() {
|
||||
return newShape_;
|
||||
|
@ -4826,8 +4848,8 @@ class ICSetProp_NativeAdd : public ICUpdatedStub
|
|||
return static_cast<ICSetProp_NativeAddImpl<ProtoChainDepth> *>(this);
|
||||
}
|
||||
|
||||
static size_t offsetOfGroup() {
|
||||
return offsetof(ICSetProp_NativeAdd, group_);
|
||||
static size_t offsetOfReceiverGuard() {
|
||||
return offsetof(ICSetProp_NativeAdd, receiverGuard_);
|
||||
}
|
||||
static size_t offsetOfNewShape() {
|
||||
return offsetof(ICSetProp_NativeAdd, newShape_);
|
||||
|
@ -4845,20 +4867,23 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd
|
|||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
static const size_t NumShapes = ProtoChainDepth + 1;
|
||||
static const size_t NumShapes = ProtoChainDepth;
|
||||
mozilla::Array<HeapPtrShape, NumShapes> shapes_;
|
||||
|
||||
ICSetProp_NativeAddImpl(JitCode *stubCode, ObjectGroup *group,
|
||||
ICSetProp_NativeAddImpl(JitCode *stubCode, ReceiverGuard::StackGuard guard,
|
||||
const AutoShapeVector *shapes,
|
||||
Shape *newShape, ObjectGroup *newGroup, uint32_t offset);
|
||||
|
||||
public:
|
||||
void traceShapes(JSTracer *trc) {
|
||||
for (size_t i = 0; i < NumShapes; i++)
|
||||
// Note: using int32_t here to avoid gcc warning.
|
||||
for (int32_t i = 0; i < int32_t(NumShapes); i++)
|
||||
MarkShape(trc, &shapes_[i], "baseline-setpropnativeadd-stub-shape");
|
||||
}
|
||||
|
||||
static size_t offsetOfShape(size_t idx) {
|
||||
// The shape array might be aligned differently if its length is zero.
|
||||
JS_STATIC_ASSERT(NumShapes != 0);
|
||||
return offsetof(ICSetProp_NativeAddImpl, shapes_) + (idx * sizeof(HeapPtrShape));
|
||||
}
|
||||
};
|
||||
|
@ -4866,23 +4891,24 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd
|
|||
class ICSetPropNativeAddCompiler : public ICStubCompiler
|
||||
{
|
||||
RootedObject obj_;
|
||||
RootedShape oldShape_;
|
||||
RootedObjectGroup oldGroup_;
|
||||
ReceiverGuard::RootedStackGuard oldGuard_;
|
||||
size_t protoChainDepth_;
|
||||
bool isFixedSlot_;
|
||||
uint32_t offset_;
|
||||
|
||||
protected:
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16) |
|
||||
(static_cast<int32_t>(protoChainDepth_) << 20);
|
||||
return static_cast<int32_t>(kind) |
|
||||
(static_cast<int32_t>(isFixedSlot_) << 16) |
|
||||
(static_cast<int32_t>(ReceiverGuard::keyBits(obj_)) << 17) |
|
||||
(static_cast<int32_t>(protoChainDepth_) << 19);
|
||||
}
|
||||
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
public:
|
||||
ICSetPropNativeAddCompiler(JSContext *cx, HandleObject obj,
|
||||
HandleShape oldShape, HandleObjectGroup oldGroup,
|
||||
ReceiverGuard::StackGuard oldGuard,
|
||||
size_t protoChainDepth, bool isFixedSlot, uint32_t offset);
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
|
@ -4895,13 +4921,17 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
|
|||
// Only specify newGroup when the object's group changes due to the
|
||||
// object becoming fully initialized per the acquired properties
|
||||
// analysis.
|
||||
if (newGroup == oldGroup_)
|
||||
if (newGroup == oldGuard_.group)
|
||||
newGroup = nullptr;
|
||||
|
||||
RootedShape newShape(cx, obj_->as<NativeObject>().lastProperty());
|
||||
RootedShape newShape(cx);
|
||||
if (obj_->is<UnboxedPlainObject>())
|
||||
newShape = obj_->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
|
||||
else
|
||||
newShape = obj_->as<NativeObject>().lastProperty();
|
||||
|
||||
return ICStub::New<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
|
||||
space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
|
||||
space, getStubCode(), oldGuard_, shapes, newShape, newGroup, offset_);
|
||||
}
|
||||
|
||||
ICUpdatedStub *getStub(ICStubSpace *space);
|
||||
|
@ -5076,8 +5106,9 @@ class ICSetPropCallSetter : public ICStub
|
|||
// PC of call, for profiler
|
||||
uint32_t pcOffset_;
|
||||
|
||||
ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
|
||||
Shape *holderShape, JSFunction *setter, uint32_t pcOffset);
|
||||
ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::StackGuard guard,
|
||||
JSObject *holder, Shape *holderShape, JSFunction *setter,
|
||||
uint32_t pcOffset);
|
||||
|
||||
public:
|
||||
ReceiverGuard &guard() {
|
||||
|
@ -5118,7 +5149,7 @@ class ICSetPropCallSetter : public ICStub
|
|||
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) |
|
||||
(static_cast<int32_t>(obj_->isNative()) << 16);
|
||||
(ReceiverGuard::keyBits(obj_) << 16);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -5141,7 +5172,7 @@ class ICSetProp_CallScripted : public ICSetPropCallSetter
|
|||
friend class ICStubSpace;
|
||||
|
||||
protected:
|
||||
ICSetProp_CallScripted(JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
|
||||
ICSetProp_CallScripted(JitCode *stubCode, ReceiverGuard::StackGuard guard, JSObject *holder,
|
||||
Shape *holderShape, JSFunction *setter, uint32_t pcOffset)
|
||||
: ICSetPropCallSetter(SetProp_CallScripted, stubCode, guard, holder, holderShape,
|
||||
setter, pcOffset)
|
||||
|
@ -5163,7 +5194,7 @@ class ICSetProp_CallScripted : public ICSetPropCallSetter
|
|||
{}
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
|
||||
ReceiverGuard::StackGuard guard(obj_);
|
||||
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
|
||||
return ICStub::New<ICSetProp_CallScripted>(space, getStubCode(), guard, holder_,
|
||||
holderShape, setter_, pcOffset_);
|
||||
|
@ -5177,7 +5208,7 @@ class ICSetProp_CallNative : public ICSetPropCallSetter
|
|||
friend class ICStubSpace;
|
||||
|
||||
protected:
|
||||
ICSetProp_CallNative(JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
|
||||
ICSetProp_CallNative(JitCode *stubCode, ReceiverGuard::StackGuard guard, JSObject *holder,
|
||||
Shape *holderShape, JSFunction *setter, uint32_t pcOffset)
|
||||
: ICSetPropCallSetter(SetProp_CallNative, stubCode, guard, holder, holderShape,
|
||||
setter, pcOffset)
|
||||
|
@ -5199,7 +5230,7 @@ class ICSetProp_CallNative : public ICSetPropCallSetter
|
|||
{}
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
|
||||
ReceiverGuard::StackGuard guard(obj_);
|
||||
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
|
||||
return ICStub::New<ICSetProp_CallNative>(space, getStubCode(), guard, holder_,
|
||||
holderShape, setter_, pcOffset_);
|
||||
|
|
|
@ -116,14 +116,16 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode *pc,
|
|||
Shape *shape = nullptr;
|
||||
ObjectGroup *group = nullptr;
|
||||
if (stub->isGetProp_Native()) {
|
||||
shape = stub->toGetProp_Native()->shape();
|
||||
shape = stub->toGetProp_Native()->receiverGuard().ownShape();
|
||||
} else if (stub->isSetProp_Native()) {
|
||||
shape = stub->toSetProp_Native()->shape();
|
||||
shape = stub->toSetProp_Native()->receiverGuard().ownShape();
|
||||
} else if (stub->isGetProp_Unboxed()) {
|
||||
group = stub->toGetProp_Unboxed()->group();
|
||||
} else if (stub->isSetProp_Unboxed()) {
|
||||
group = stub->toSetProp_Unboxed()->group();
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!shape && !group) {
|
||||
nativeShapes.clear();
|
||||
unboxedGroups.clear();
|
||||
return true;
|
||||
|
@ -589,12 +591,17 @@ GlobalShapeForGetPropFunction(ICStub *stub)
|
|||
static bool
|
||||
AddReceiver(BaselineInspector::ShapeVector &nativeShapes,
|
||||
BaselineInspector::ObjectGroupVector &unboxedGroups,
|
||||
ReceiverGuard::Token receiver)
|
||||
ReceiverGuard::StackGuard receiver)
|
||||
{
|
||||
if (Shape *shape = ReceiverGuard::tokenShape(receiver))
|
||||
if (Shape *shape = receiver.ownShape())
|
||||
return VectorAppendNoDuplicate(nativeShapes, shape);
|
||||
ObjectGroup *group = ReceiverGuard::tokenGroup(receiver);
|
||||
return VectorAppendNoDuplicate(unboxedGroups, group);
|
||||
|
||||
// Only unboxed objects with no expandos are handled by the common
|
||||
// getprop/setprop optimizations.
|
||||
if (!receiver.shape)
|
||||
return VectorAppendNoDuplicate(unboxedGroups, receiver.group);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -605,13 +612,7 @@ AddReceiverForGetPropFunction(BaselineInspector::ShapeVector &nativeShapes,
|
|||
if (stub->isOwnGetter())
|
||||
return true;
|
||||
|
||||
ReceiverGuard::Token token;
|
||||
if (stub->isGetProp_CallScripted())
|
||||
token = stub->toGetProp_CallScripted()->receiverGuard().token();
|
||||
else
|
||||
token = stub->toGetProp_CallNative()->receiverGuard().token();
|
||||
|
||||
return AddReceiver(nativeShapes, unboxedGroups, token);
|
||||
return AddReceiver(nativeShapes, unboxedGroups, stub->receiverGuard());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -690,7 +691,7 @@ BaselineInspector::commonSetPropFunction(jsbytecode *pc, JSObject **holder, Shap
|
|||
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
|
||||
ICSetPropCallSetter *nstub = static_cast<ICSetPropCallSetter *>(stub);
|
||||
if (!AddReceiver(nativeShapes, unboxedGroups, nstub->guard().token()))
|
||||
if (!AddReceiver(nativeShapes, unboxedGroups, nstub->guard()))
|
||||
return false;
|
||||
|
||||
if (!*holder) {
|
||||
|
|
|
@ -2564,6 +2564,10 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic *lir)
|
|||
}
|
||||
|
||||
if (mir->numUnboxedGroups()) {
|
||||
// The guard requires that unboxed objects not have expandos.
|
||||
bailoutCmpPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfShape()),
|
||||
ImmWord(0), lir->snapshot());
|
||||
|
||||
masm.loadObjGroup(obj, temp);
|
||||
|
||||
for (size_t i = 0; i < mir->numUnboxedGroups(); i++) {
|
||||
|
|
|
@ -10521,7 +10521,7 @@ IonBuilder::addShapeGuardsForGetterSetter(MDefinition *obj, JSObject *holder, Sh
|
|||
MDefinition *holderDef = constantMaybeNursery(holder);
|
||||
addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
|
||||
|
||||
return addShapeGuardPolymorphic(obj, receiverShapes, receiverUnboxedGroups);
|
||||
return addGuardReceiverPolymorphic(obj, receiverShapes, receiverUnboxedGroups);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -10787,7 +10787,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName
|
|||
return false;
|
||||
|
||||
if (sameSlot && unboxedGroups.empty()) {
|
||||
obj = addShapeGuardPolymorphic(obj, nativeShapes, unboxedGroups);
|
||||
obj = addGuardReceiverPolymorphic(obj, nativeShapes, unboxedGroups);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -11467,7 +11467,7 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
|
|||
return false;
|
||||
|
||||
if (sameSlot && unboxedGroups.empty()) {
|
||||
obj = addShapeGuardPolymorphic(obj, nativeShapes, unboxedGroups);
|
||||
obj = addGuardReceiverPolymorphic(obj, nativeShapes, unboxedGroups);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -12379,11 +12379,12 @@ IonBuilder::addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bail
|
|||
}
|
||||
|
||||
MInstruction *
|
||||
IonBuilder::addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind)
|
||||
IonBuilder::addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind,
|
||||
bool checkUnboxedExpando)
|
||||
{
|
||||
MGuardObjectGroup *guard = MGuardObjectGroup::New(alloc(), obj, group,
|
||||
/* bailOnEquality = */ false,
|
||||
bailoutKind);
|
||||
bailoutKind, checkUnboxedExpando);
|
||||
current->add(guard);
|
||||
|
||||
// If a shape guard failed in the past, don't optimize group guards.
|
||||
|
@ -12398,15 +12399,19 @@ IonBuilder::addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bail
|
|||
}
|
||||
|
||||
MInstruction *
|
||||
IonBuilder::addShapeGuardPolymorphic(MDefinition *obj,
|
||||
const BaselineInspector::ShapeVector &shapes,
|
||||
const BaselineInspector::ObjectGroupVector &unboxedGroups)
|
||||
IonBuilder::addGuardReceiverPolymorphic(MDefinition *obj,
|
||||
const BaselineInspector::ShapeVector &shapes,
|
||||
const BaselineInspector::ObjectGroupVector &unboxedGroups)
|
||||
{
|
||||
if (shapes.length() == 1 && unboxedGroups.empty())
|
||||
return addShapeGuard(obj, shapes[0], Bailout_ShapeGuard);
|
||||
|
||||
if (shapes.empty() && unboxedGroups.length() == 1)
|
||||
return addGroupGuard(obj, unboxedGroups[0], Bailout_ShapeGuard);
|
||||
if (shapes.empty() && unboxedGroups.length() == 1) {
|
||||
// The guard requires that unboxed objects not have expando objects.
|
||||
// An inline cache will be used in these cases.
|
||||
return addGroupGuard(obj, unboxedGroups[0], Bailout_ShapeGuard,
|
||||
/* checkUnboxedExpando = */ true);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(shapes.length() + unboxedGroups.length() > 1);
|
||||
|
||||
|
|
|
@ -398,12 +398,13 @@ class IonBuilder
|
|||
MDefinition *addMaybeCopyElementsForWrite(MDefinition *object);
|
||||
MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length);
|
||||
MInstruction *addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bailoutKind);
|
||||
MInstruction *addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind);
|
||||
MInstruction *addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind,
|
||||
bool checkUnboxedExpando = false);
|
||||
|
||||
MInstruction *
|
||||
addShapeGuardPolymorphic(MDefinition *obj,
|
||||
const BaselineInspector::ShapeVector &shapes,
|
||||
const BaselineInspector::ObjectGroupVector &unboxedGroups);
|
||||
addGuardReceiverPolymorphic(MDefinition *obj,
|
||||
const BaselineInspector::ShapeVector &shapes,
|
||||
const BaselineInspector::ObjectGroupVector &unboxedGroups);
|
||||
|
||||
MDefinition *convertShiftToMaskForStaticTypedArray(MDefinition *id,
|
||||
Scalar::Type viewType);
|
||||
|
|
|
@ -699,6 +699,41 @@ IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, Shape *shape)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
TestMatchingReceiver(MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
Register object, JSObject *obj, Label *failure,
|
||||
bool alwaysCheckGroup = false)
|
||||
{
|
||||
if (Shape *shape = obj->maybeShape()) {
|
||||
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
||||
Address(object, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(shape), failure);
|
||||
|
||||
if (alwaysCheckGroup)
|
||||
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
|
||||
} else {
|
||||
MOZ_ASSERT(obj->is<UnboxedPlainObject>());
|
||||
MOZ_ASSERT(failure);
|
||||
|
||||
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
|
||||
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
|
||||
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
||||
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
|
||||
Label success;
|
||||
masm.push(object);
|
||||
masm.loadPtr(expandoAddress, object);
|
||||
masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(),
|
||||
&success);
|
||||
masm.pop(object);
|
||||
masm.jump(failure);
|
||||
masm.bind(&success);
|
||||
masm.pop(object);
|
||||
} else {
|
||||
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
EmitLoadSlot(MacroAssembler &masm, NativeObject *holder, Shape *shape, Register holderReg,
|
||||
TypedOrValueRegister output, Register scratchReg)
|
||||
|
@ -791,14 +826,16 @@ CheckDOMProxyExpandoDoesNotShadow(JSContext *cx, MacroAssembler &masm, JSObject
|
|||
|
||||
static void
|
||||
GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
||||
IonCache::StubAttacher &attacher, JSObject *obj, NativeObject *holder,
|
||||
IonCache::StubAttacher &attacher, JSObject *obj, JSObject *holder,
|
||||
Shape *shape, Register object, TypedOrValueRegister output,
|
||||
Label *failures = nullptr)
|
||||
{
|
||||
// If there's a single jump to |failures|, we can patch the shape guard
|
||||
// jump directly. Otherwise, jump to the end of the stub, so there's a
|
||||
// common point to patch.
|
||||
bool multipleFailureJumps = (obj != holder) || (failures != nullptr && failures->used());
|
||||
bool multipleFailureJumps = (obj != holder)
|
||||
|| obj->is<UnboxedPlainObject>()
|
||||
|| (failures != nullptr && failures->used());
|
||||
|
||||
// If we have multiple failure jumps but didn't get a label from the
|
||||
// outside, make one ourselves.
|
||||
|
@ -806,18 +843,7 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
|||
if (multipleFailureJumps && !failures)
|
||||
failures = &failures_;
|
||||
|
||||
// Guard on the shape or type of the object, depending on whether it is native.
|
||||
if (obj->isNative()) {
|
||||
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
||||
Address(object, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(obj->as<NativeObject>().lastProperty()),
|
||||
failures);
|
||||
} else {
|
||||
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
||||
Address(object, JSObject::offsetOfGroup()),
|
||||
ImmGCPtr(obj->group()),
|
||||
failures);
|
||||
}
|
||||
TestMatchingReceiver(masm, attacher, object, obj, failures);
|
||||
|
||||
// If we need a scratch register, use either an output register or the
|
||||
// object register. After this point, we cannot jump directly to
|
||||
|
@ -825,7 +851,10 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
|||
bool restoreScratch = false;
|
||||
Register scratchReg = Register::FromCode(0); // Quell compiler warning.
|
||||
|
||||
if (obj != holder || !holder->isFixedSlot(shape->slot())) {
|
||||
if (obj != holder ||
|
||||
obj->is<UnboxedPlainObject>() ||
|
||||
!holder->as<NativeObject>().isFixedSlot(shape->slot()))
|
||||
{
|
||||
if (output.hasValue()) {
|
||||
scratchReg = output.valueReg().scratchReg();
|
||||
} else if (output.type() == MIRType_Double) {
|
||||
|
@ -839,7 +868,7 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
|||
|
||||
// Fast path: single failure jump, no prototype guards.
|
||||
if (!multipleFailureJumps) {
|
||||
EmitLoadSlot(masm, holder, shape, object, output, scratchReg);
|
||||
EmitLoadSlot(masm, &holder->as<NativeObject>(), shape, object, output, scratchReg);
|
||||
if (restoreScratch)
|
||||
masm.pop(scratchReg);
|
||||
attacher.jumpRejoin(masm);
|
||||
|
@ -860,7 +889,7 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
|||
masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(holderReg, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(holder->lastProperty()),
|
||||
ImmGCPtr(holder->as<NativeObject>().lastProperty()),
|
||||
&prototypeFailures);
|
||||
} else {
|
||||
// The property does not exist. Guard on everything in the
|
||||
|
@ -883,13 +912,17 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
|||
|
||||
holderReg = InvalidReg;
|
||||
}
|
||||
} else if (obj->is<UnboxedPlainObject>()) {
|
||||
holder = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
holderReg = scratchReg;
|
||||
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg);
|
||||
} else {
|
||||
holderReg = object;
|
||||
}
|
||||
|
||||
// Slot access.
|
||||
if (holder)
|
||||
EmitLoadSlot(masm, holder, shape, holderReg, output, scratchReg);
|
||||
EmitLoadSlot(masm, &holder->as<NativeObject>(), shape, holderReg, output, scratchReg);
|
||||
else
|
||||
masm.moveValue(UndefinedValue(), output.valueReg());
|
||||
|
||||
|
@ -1098,15 +1131,6 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
|
|||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
TestMatchingReceiver(MacroAssembler &masm, Register object, JSObject *obj, Label *failure)
|
||||
{
|
||||
if (Shape *shape = obj->maybeShape())
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, shape, failure);
|
||||
else
|
||||
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
|
||||
}
|
||||
|
||||
static bool
|
||||
GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
||||
IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
|
||||
|
@ -1119,7 +1143,7 @@ GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
|||
Label stubFailure;
|
||||
failures = failures ? failures : &stubFailure;
|
||||
|
||||
TestMatchingReceiver(masm, object, obj, failures);
|
||||
TestMatchingReceiver(masm, attacher, object, obj, failures);
|
||||
|
||||
Register scratchReg = output.valueReg().scratchReg();
|
||||
bool spillObjReg = scratchReg == object;
|
||||
|
@ -1428,6 +1452,35 @@ GetPropertyIC::tryAttachUnboxed(JSContext *cx, HandleScript outerScript, IonScri
|
|||
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed");
|
||||
}
|
||||
|
||||
bool
|
||||
GetPropertyIC::tryAttachUnboxedExpando(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void *returnAddr, bool *emitted)
|
||||
{
|
||||
MOZ_ASSERT(canAttachStub());
|
||||
MOZ_ASSERT(!*emitted);
|
||||
MOZ_ASSERT(outerScript->ionScript() == ion);
|
||||
|
||||
if (!obj->is<UnboxedPlainObject>())
|
||||
return true;
|
||||
Rooted<UnboxedExpandoObject *> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
|
||||
if (!expando)
|
||||
return true;
|
||||
|
||||
Shape *shape = expando->lookup(cx, name);
|
||||
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
|
||||
return true;
|
||||
|
||||
*emitted = true;
|
||||
|
||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
||||
|
||||
RepatchStubAppender attacher(*this);
|
||||
GenerateReadSlot(cx, ion, masm, attacher, obj, obj,
|
||||
shape, object(), output());
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando");
|
||||
}
|
||||
|
||||
bool
|
||||
GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject obj, HandlePropertyName name, bool *emitted)
|
||||
|
@ -1867,6 +1920,9 @@ GetPropertyIC::tryAttachStub(JSContext *cx, HandleScript outerScript, IonScript
|
|||
if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, name, returnAddr, emitted))
|
||||
return false;
|
||||
|
||||
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, name, returnAddr, emitted))
|
||||
return false;
|
||||
|
||||
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, name, emitted))
|
||||
return false;
|
||||
|
||||
|
@ -1997,37 +2053,33 @@ CheckTypeSetForWrite(MacroAssembler &masm, JSObject *obj, jsid id,
|
|||
|
||||
static void
|
||||
GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
NativeObject *obj, Shape *shape, Register object, ConstantOrRegister value,
|
||||
JSObject *obj, Shape *shape, Register object, ConstantOrRegister value,
|
||||
bool needsTypeBarrier, bool checkTypeset)
|
||||
{
|
||||
MOZ_ASSERT(obj->isNative());
|
||||
Label failures, failurePopObject;
|
||||
|
||||
Label failures, barrierFailure;
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(object, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(obj->lastProperty()), &failures);
|
||||
TestMatchingReceiver(masm, attacher, object, obj, &failures, needsTypeBarrier);
|
||||
|
||||
// Guard that the incoming value is in the type set for the property
|
||||
// if a type barrier is required.
|
||||
if (needsTypeBarrier) {
|
||||
// We can't do anything that would change the HeapTypeSet, so
|
||||
// just guard that it's already there.
|
||||
|
||||
// Obtain and guard on the ObjectGroup of the object.
|
||||
ObjectGroup *group = obj->group();
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(object, JSObject::offsetOfGroup()),
|
||||
ImmGCPtr(group), &failures);
|
||||
|
||||
if (checkTypeset) {
|
||||
masm.push(object);
|
||||
CheckTypeSetForWrite(masm, obj, shape->propid(), object, value, &barrierFailure);
|
||||
CheckTypeSetForWrite(masm, obj, shape->propid(), object, value, &failurePopObject);
|
||||
masm.pop(object);
|
||||
}
|
||||
}
|
||||
|
||||
NativeObject::slotsSizeMustNotOverflow();
|
||||
if (obj->isFixedSlot(shape->slot())) {
|
||||
|
||||
if (obj->is<UnboxedPlainObject>()) {
|
||||
obj = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
|
||||
}
|
||||
|
||||
if (obj->as<NativeObject>().isFixedSlot(shape->slot())) {
|
||||
Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
|
||||
|
||||
if (cx->zone()->needsIncrementalBarrier())
|
||||
|
@ -2038,7 +2090,7 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
|||
Register slotsReg = object;
|
||||
masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), slotsReg);
|
||||
|
||||
Address addr(slotsReg, obj->dynamicSlotIndex(shape->slot()) * sizeof(Value));
|
||||
Address addr(slotsReg, obj->as<NativeObject>().dynamicSlotIndex(shape->slot()) * sizeof(Value));
|
||||
|
||||
if (cx->zone()->needsIncrementalBarrier())
|
||||
masm.callPreBarrier(addr, MIRType_Value);
|
||||
|
@ -2048,8 +2100,8 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
|||
|
||||
attacher.jumpRejoin(masm);
|
||||
|
||||
if (barrierFailure.used()) {
|
||||
masm.bind(&barrierFailure);
|
||||
if (failurePopObject.used()) {
|
||||
masm.bind(&failurePopObject);
|
||||
masm.pop(object);
|
||||
}
|
||||
|
||||
|
@ -2059,7 +2111,7 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
|||
|
||||
bool
|
||||
SetPropertyIC::attachSetSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleNativeObject obj, HandleShape shape, bool checkTypeset)
|
||||
HandleObject obj, HandleShape shape, bool checkTypeset)
|
||||
{
|
||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
||||
RepatchStubAppender attacher(*this);
|
||||
|
@ -2663,7 +2715,7 @@ SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScri
|
|||
RepatchStubAppender attacher(*this);
|
||||
|
||||
Label failure;
|
||||
TestMatchingReceiver(masm, object(), obj, &failure);
|
||||
TestMatchingReceiver(masm, attacher, object(), obj, &failure);
|
||||
|
||||
if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
|
||||
object(), value(), &failure, liveRegs_, returnAddr))
|
||||
|
@ -2683,31 +2735,42 @@ SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScri
|
|||
|
||||
static void
|
||||
GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
NativeObject *obj, Shape *oldShape, ObjectGroup *oldGroup,
|
||||
JSObject *obj, Shape *oldShape, ObjectGroup *oldGroup,
|
||||
Register object, ConstantOrRegister value,
|
||||
bool checkTypeset)
|
||||
{
|
||||
MOZ_ASSERT(obj->isNative());
|
||||
Label failures, failuresPopObject;
|
||||
|
||||
Label failures;
|
||||
// Use a modified version of TestMatchingReceiver that uses the old shape and group.
|
||||
masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, &failures);
|
||||
if (obj->maybeShape()) {
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failures);
|
||||
} else {
|
||||
MOZ_ASSERT(obj->is<UnboxedPlainObject>());
|
||||
|
||||
// Guard the type of the object
|
||||
masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()),
|
||||
ImmGCPtr(oldGroup), &failures);
|
||||
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
|
||||
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), &failures);
|
||||
|
||||
// Guard shapes along prototype chain.
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failures);
|
||||
masm.push(object);
|
||||
masm.loadPtr(expandoAddress, object);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failuresPopObject);
|
||||
masm.pop(object);
|
||||
}
|
||||
|
||||
Shape *newShape = obj->maybeShape();
|
||||
if (!newShape)
|
||||
newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
|
||||
|
||||
Label failuresPopObject;
|
||||
masm.push(object); // save object reg because we clobber it
|
||||
|
||||
// Guard that the incoming value is in the type set for the property
|
||||
// if a type barrier is required.
|
||||
if (checkTypeset) {
|
||||
CheckTypeSetForWrite(masm, obj, obj->lastProperty()->propid(), object, value, &failuresPopObject);
|
||||
CheckTypeSetForWrite(masm, obj, newShape->propid(), object, value, &failuresPopObject);
|
||||
masm.loadPtr(Address(StackPointer, 0), object);
|
||||
}
|
||||
|
||||
// Guard shapes along prototype chain.
|
||||
JSObject *proto = obj->getProto();
|
||||
Register protoReg = object;
|
||||
while (proto) {
|
||||
|
@ -2724,14 +2787,19 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
|||
|
||||
masm.pop(object); // restore object reg
|
||||
|
||||
// Changing object shape. Write the object's new shape.
|
||||
Shape *newShape = obj->lastProperty();
|
||||
// Write the object or expando object's new shape.
|
||||
if (obj->is<UnboxedPlainObject>()) {
|
||||
obj = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
|
||||
}
|
||||
Address shapeAddr(object, JSObject::offsetOfShape());
|
||||
if (cx->zone()->needsIncrementalBarrier())
|
||||
masm.callPreBarrier(shapeAddr, MIRType_Shape);
|
||||
masm.storePtr(ImmGCPtr(newShape), shapeAddr);
|
||||
|
||||
if (oldGroup != obj->group()) {
|
||||
MOZ_ASSERT(!obj->is<UnboxedPlainObject>());
|
||||
|
||||
// Changing object's group from a partially to fully initialized group,
|
||||
// per the acquired properties analysis. Only change the group if the
|
||||
// old group still has a newScript.
|
||||
|
@ -2759,7 +2827,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
|||
// Set the value on the object. Since this is an add, obj->lastProperty()
|
||||
// must be the shape of the property we are adding.
|
||||
NativeObject::slotsSizeMustNotOverflow();
|
||||
if (obj->isFixedSlot(newShape->slot())) {
|
||||
if (obj->as<NativeObject>().isFixedSlot(newShape->slot())) {
|
||||
Address addr(object, NativeObject::getFixedSlotOffset(newShape->slot()));
|
||||
masm.storeConstantOrRegister(value, addr);
|
||||
} else {
|
||||
|
@ -2767,7 +2835,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
|||
|
||||
masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), slotsReg);
|
||||
|
||||
Address addr(slotsReg, obj->dynamicSlotIndex(newShape->slot()) * sizeof(Value));
|
||||
Address addr(slotsReg, obj->as<NativeObject>().dynamicSlotIndex(newShape->slot()) * sizeof(Value));
|
||||
masm.storeConstantOrRegister(value, addr);
|
||||
}
|
||||
|
||||
|
@ -2784,7 +2852,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
|
|||
|
||||
bool
|
||||
SetPropertyIC::attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleNativeObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
|
||||
HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
|
||||
bool checkTypeset)
|
||||
{
|
||||
MOZ_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
|
||||
|
@ -2860,7 +2928,33 @@ IsPropertySetInlineable(NativeObject *obj, HandleId id, MutableHandleShape pshap
|
|||
}
|
||||
|
||||
static bool
|
||||
IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val, uint32_t oldSlots,
|
||||
PrototypeChainShadowsPropertyAdd(JSObject *obj, jsid id)
|
||||
{
|
||||
// Walk up the object prototype chain and ensure that all prototypes
|
||||
// are native, and that all prototypes have no getter or setter
|
||||
// defined on the property
|
||||
for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) {
|
||||
// If prototype is non-native, don't optimize
|
||||
if (!proto->isNative())
|
||||
return true;
|
||||
|
||||
// If prototype defines this property in a non-plain way, don't optimize
|
||||
Shape *protoShape = proto->as<NativeObject>().lookupPure(id);
|
||||
if (protoShape && !protoShape->hasDefaultSetter())
|
||||
return true;
|
||||
|
||||
// Otherwise, if there's no such property, watch out for a resolve
|
||||
// hook that would need to be invoked and thus prevent inlining of
|
||||
// property addition.
|
||||
if (proto->getClass()->resolve)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val,
|
||||
HandleShape oldShape, bool needsTypeBarrier, bool *checkTypeset)
|
||||
{
|
||||
// If the shape of the object did not change, then this was not an add.
|
||||
|
@ -2886,35 +2980,18 @@ IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val,
|
|||
if (!obj->nonProxyIsExtensible() || !shape->writable())
|
||||
return false;
|
||||
|
||||
// Walk up the object prototype chain and ensure that all prototypes
|
||||
// are native, and that all prototypes have no getter or setter
|
||||
// defined on the property
|
||||
for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) {
|
||||
// If prototype is non-native, don't optimize
|
||||
if (!proto->isNative())
|
||||
return false;
|
||||
|
||||
// If prototype defines this property in a non-plain way, don't optimize
|
||||
Shape *protoShape = proto->as<NativeObject>().lookupPure(id);
|
||||
if (protoShape && !protoShape->hasDefaultSetter())
|
||||
return false;
|
||||
|
||||
// Otherwise, if there's no such property, watch out for a resolve
|
||||
// hook that would need to be invoked and thus prevent inlining of
|
||||
// property addition.
|
||||
if (proto->getClass()->resolve)
|
||||
return false;
|
||||
}
|
||||
if (PrototypeChainShadowsPropertyAdd(obj, id))
|
||||
return false;
|
||||
|
||||
// Only add a IC entry if the dynamic slots didn't change when the shapes
|
||||
// changed. Need to ensure that a shape change for a subsequent object
|
||||
// won't involve reallocating the slot array.
|
||||
if (obj->numDynamicSlots() != oldSlots)
|
||||
if (obj->numDynamicSlots() != NativeObject::dynamicSlotsCount(oldShape))
|
||||
return false;
|
||||
|
||||
// Don't attach if we are adding a property to an object which the new
|
||||
// script properties analysis hasn't been performed for yet, as there
|
||||
// may be a shape change required here afterwards.
|
||||
// may be a group change required here afterwards.
|
||||
if (obj->group()->newScript() && !obj->group()->newScript()->analyzed())
|
||||
return false;
|
||||
|
||||
|
@ -2946,7 +3023,7 @@ CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrR
|
|||
// a stub to add the property until we do the VM call to add. If the
|
||||
// property exists as a data property on the prototype, we should add
|
||||
// a new, shadowing property.
|
||||
if (!shape || (obj != holder && shape->hasDefaultSetter() && shape->hasSlot()))
|
||||
if (obj->isNative() && (!shape || (obj != holder && shape->hasDefaultSetter() && shape->hasSlot())))
|
||||
return SetPropertyIC::MaybeCanAttachAddSlot;
|
||||
|
||||
if (IsImplicitNonNativeProperty(shape))
|
||||
|
@ -3041,6 +3118,58 @@ CanAttachSetUnboxed(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegi
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
CanAttachSetUnboxedExpando(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegister val,
|
||||
bool needsTypeBarrier, bool *checkTypeset, Shape **pshape)
|
||||
{
|
||||
if (!obj->is<UnboxedPlainObject>())
|
||||
return false;
|
||||
|
||||
Rooted<UnboxedExpandoObject *> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
|
||||
if (!expando)
|
||||
return false;
|
||||
|
||||
Shape *shape = expando->lookupPure(id);
|
||||
if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable())
|
||||
return false;
|
||||
|
||||
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
|
||||
return false;
|
||||
|
||||
*pshape = shape;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CanAttachAddUnboxedExpando(JSContext *cx, HandleObject obj, HandleShape oldShape,
|
||||
HandleId id, ConstantOrRegister val,
|
||||
bool needsTypeBarrier, bool *checkTypeset)
|
||||
{
|
||||
if (!obj->is<UnboxedPlainObject>())
|
||||
return false;
|
||||
|
||||
Rooted<UnboxedExpandoObject *> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
|
||||
if (!expando || expando->inDictionaryMode())
|
||||
return false;
|
||||
|
||||
Shape *newShape = expando->lastProperty();
|
||||
if (newShape->propid() != id || newShape->previous() != oldShape)
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable());
|
||||
|
||||
if (PrototypeChainShadowsPropertyAdd(obj, id))
|
||||
return false;
|
||||
|
||||
if (NativeObject::dynamicSlotsCount(oldShape) != NativeObject::dynamicSlotsCount(newShape))
|
||||
return false;
|
||||
|
||||
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SetPropertyIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex, HandleObject obj,
|
||||
HandleValue value)
|
||||
|
@ -3109,9 +3238,9 @@ SetPropertyIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex
|
|||
checkTypeset = false;
|
||||
uint32_t unboxedOffset;
|
||||
JSValueType unboxedType;
|
||||
if (!addedSetterStub && CanAttachSetUnboxed(cx, obj, id, cache.value(),
|
||||
cache.needsTypeBarrier(),
|
||||
&checkTypeset, &unboxedOffset, &unboxedType))
|
||||
if (!addedSetterStub &&
|
||||
CanAttachSetUnboxed(cx, obj, id, cache.value(), cache.needsTypeBarrier(),
|
||||
&checkTypeset, &unboxedOffset, &unboxedType))
|
||||
{
|
||||
if (!cache.attachSetUnboxed(cx, outerScript, ion, obj, id, unboxedOffset, unboxedType,
|
||||
checkTypeset))
|
||||
|
@ -3120,10 +3249,23 @@ SetPropertyIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex
|
|||
}
|
||||
addedSetterStub = true;
|
||||
}
|
||||
|
||||
checkTypeset = false;
|
||||
if (!addedSetterStub &&
|
||||
CanAttachSetUnboxedExpando(cx, obj, id, cache.value(), cache.needsTypeBarrier(),
|
||||
&checkTypeset, shape.address()))
|
||||
{
|
||||
if (!cache.attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset))
|
||||
return false;
|
||||
addedSetterStub = true;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t oldSlots = obj->is<NativeObject>() ? obj->as<NativeObject>().numDynamicSlots() : 0;
|
||||
RootedShape oldShape(cx, obj->maybeShape());
|
||||
if (!oldShape) {
|
||||
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando())
|
||||
oldShape = expando->lastProperty();
|
||||
}
|
||||
|
||||
// Set/Add the property on the object, the inlined cache are setup for the next execution.
|
||||
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
|
||||
|
@ -3132,12 +3274,20 @@ SetPropertyIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex
|
|||
// The property did not exist before, now we can try to inline the property add.
|
||||
bool checkTypeset;
|
||||
if (!addedSetterStub && canCache == MaybeCanAttachAddSlot &&
|
||||
IsPropertyAddInlineable(&obj->as<NativeObject>(), id,
|
||||
cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
|
||||
&checkTypeset))
|
||||
IsPropertyAddInlineable(&obj->as<NativeObject>(), id, cache.value(), oldShape,
|
||||
cache.needsTypeBarrier(), &checkTypeset))
|
||||
{
|
||||
RootedNativeObject nobj(cx, &obj->as<NativeObject>());
|
||||
if (!cache.attachAddSlot(cx, outerScript, ion, nobj, oldShape, oldGroup, checkTypeset))
|
||||
if (!cache.attachAddSlot(cx, outerScript, ion, obj, oldShape, oldGroup, checkTypeset))
|
||||
return false;
|
||||
addedSetterStub = true;
|
||||
}
|
||||
|
||||
checkTypeset = false;
|
||||
if (!addedSetterStub && cache.canAttachStub() &&
|
||||
CanAttachAddUnboxedExpando(cx, obj, oldShape, id, cache.value(),
|
||||
cache.needsTypeBarrier(), &checkTypeset))
|
||||
{
|
||||
if (!cache.attachAddSlot(cx, outerScript, ion, obj, oldShape, oldGroup, checkTypeset))
|
||||
return false;
|
||||
addedSetterStub = true;
|
||||
}
|
||||
|
|
|
@ -670,6 +670,10 @@ class GetPropertyIC : public RepatchIonCache
|
|||
HandleObject obj, HandlePropertyName name,
|
||||
void *returnAddr, bool *emitted);
|
||||
|
||||
bool tryAttachUnboxedExpando(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void *returnAddr, bool *emitted);
|
||||
|
||||
bool tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject obj, HandlePropertyName name, bool *emitted);
|
||||
|
||||
|
@ -739,14 +743,14 @@ class SetPropertyIC : public RepatchIonCache
|
|||
};
|
||||
|
||||
bool attachSetSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleNativeObject obj, HandleShape shape, bool checkTypeset);
|
||||
HandleObject obj, HandleShape shape, bool checkTypeset);
|
||||
|
||||
bool attachCallSetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject obj, HandleObject holder, HandleShape shape,
|
||||
void *returnAddr);
|
||||
|
||||
bool attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleNativeObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
|
||||
HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
|
||||
bool checkTypeset);
|
||||
|
||||
bool attachSetUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
|
|
|
@ -4909,7 +4909,8 @@ AddGroupGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj,
|
|||
|
||||
if (key->isGroup()) {
|
||||
guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality,
|
||||
Bailout_ObjectIdentityOrTypeGuard);
|
||||
Bailout_ObjectIdentityOrTypeGuard,
|
||||
/* checkUnboxedExpando = */ false);
|
||||
} else {
|
||||
MConstant *singletonConst = MConstant::NewConstraintlessObject(alloc, key->singleton());
|
||||
current->add(singletonConst);
|
||||
|
|
|
@ -10157,13 +10157,15 @@ class MGuardObjectGroup
|
|||
AlwaysTenured<ObjectGroup *> group_;
|
||||
bool bailOnEquality_;
|
||||
BailoutKind bailoutKind_;
|
||||
bool checkUnboxedExpando_;
|
||||
|
||||
MGuardObjectGroup(MDefinition *obj, ObjectGroup *group, bool bailOnEquality,
|
||||
BailoutKind bailoutKind)
|
||||
BailoutKind bailoutKind, bool checkUnboxedExpando)
|
||||
: MUnaryInstruction(obj),
|
||||
group_(group),
|
||||
bailOnEquality_(bailOnEquality),
|
||||
bailoutKind_(bailoutKind)
|
||||
bailoutKind_(bailoutKind),
|
||||
checkUnboxedExpando_(checkUnboxedExpando)
|
||||
{
|
||||
setGuard();
|
||||
setMovable();
|
||||
|
@ -10178,8 +10180,10 @@ class MGuardObjectGroup
|
|||
INSTRUCTION_HEADER(GuardObjectGroup)
|
||||
|
||||
static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, ObjectGroup *group,
|
||||
bool bailOnEquality, BailoutKind bailoutKind) {
|
||||
return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind);
|
||||
bool bailOnEquality, BailoutKind bailoutKind,
|
||||
bool checkUnboxedExpando) {
|
||||
return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind,
|
||||
checkUnboxedExpando);
|
||||
}
|
||||
|
||||
MDefinition *obj() const {
|
||||
|
@ -10194,6 +10198,9 @@ class MGuardObjectGroup
|
|||
BailoutKind bailoutKind() const {
|
||||
return bailoutKind_;
|
||||
}
|
||||
bool checkUnboxedExpando() const {
|
||||
return checkUnboxedExpando_;
|
||||
}
|
||||
bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
|
||||
if (!ins->isGuardObjectGroup())
|
||||
return false;
|
||||
|
|
|
@ -1281,8 +1281,7 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj,
|
|||
offset += sizeof(uintptr_t);
|
||||
}
|
||||
} else if (templateObj->is<UnboxedPlainObject>()) {
|
||||
storePtr(ImmWord(0), Address(obj, JSObject::offsetOfShape()));
|
||||
|
||||
storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando()));
|
||||
if (initContents)
|
||||
initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
|
||||
} else {
|
||||
|
|
|
@ -1662,6 +1662,13 @@ CodeGeneratorARM::visitGuardObjectGroup(LGuardObjectGroup *guard)
|
|||
{
|
||||
Register obj = ToRegister(guard->input());
|
||||
Register tmp = ToRegister(guard->tempInt());
|
||||
MOZ_ASSERT(obj != tmp);
|
||||
|
||||
if (guard->mir()->checkUnboxedExpando()) {
|
||||
masm.ma_ldr(DTRAddr(obj, DtrOffImm(UnboxedPlainObject::offsetOfExpando())), tmp);
|
||||
masm.ma_cmp(tmp, ImmWord(0));
|
||||
bailoutIf(Assembler::NotEqual, guard->snapshot());
|
||||
}
|
||||
|
||||
masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfGroup())), tmp);
|
||||
masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group()));
|
||||
|
|
|
@ -1751,6 +1751,12 @@ CodeGeneratorMIPS::visitGuardObjectGroup(LGuardObjectGroup *guard)
|
|||
{
|
||||
Register obj = ToRegister(guard->input());
|
||||
Register tmp = ToRegister(guard->tempInt());
|
||||
MOZ_ASSERT(obj != tmp);
|
||||
|
||||
if (guard->mir()->checkUnboxedExpando()) {
|
||||
masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), tmp);
|
||||
bailoutCmpPtr(Assembler::NotEqual, tmp, ImmWord(0), guard->snapshot());
|
||||
}
|
||||
|
||||
masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp);
|
||||
Assembler::Condition cond = guard->mir()->bailOnEquality()
|
||||
|
|
|
@ -2070,6 +2070,12 @@ void
|
|||
CodeGeneratorX86Shared::visitGuardObjectGroup(LGuardObjectGroup *guard)
|
||||
{
|
||||
Register obj = ToRegister(guard->input());
|
||||
|
||||
if (guard->mir()->checkUnboxedExpando()) {
|
||||
masm.cmpPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), ImmWord(0));
|
||||
bailoutIf(Assembler::NotEqual, guard->snapshot());
|
||||
}
|
||||
|
||||
masm.cmpPtr(Operand(obj, JSObject::offsetOfGroup()), ImmGCPtr(guard->mir()->group()));
|
||||
|
||||
Assembler::Condition cond =
|
||||
|
|
|
@ -2931,7 +2931,7 @@ js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **
|
|||
// are not handled here.
|
||||
if (!obj->is<UnboxedPlainObject>())
|
||||
return false;
|
||||
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
|
||||
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
|
||||
*objp = obj;
|
||||
MarkNonNativePropertyFound<NoGC>(propp);
|
||||
return true;
|
||||
|
|
|
@ -626,6 +626,12 @@ class NativeObject : public JSObject
|
|||
return lookup(cx, shape->propid()) == shape;
|
||||
}
|
||||
|
||||
bool containsShapeOrElement(ExclusiveContext *cx, jsid id) {
|
||||
if (JSID_IS_INT(id) && containsDenseElement(JSID_TO_INT(id)))
|
||||
return true;
|
||||
return contains(cx, id);
|
||||
}
|
||||
|
||||
/* Contextless; can be called from other pure code. */
|
||||
Shape *lookupPure(jsid id);
|
||||
Shape *lookupPure(PropertyName *name) {
|
||||
|
@ -874,6 +880,9 @@ class NativeObject : public JSObject
|
|||
* array is kept in sync with this count.
|
||||
*/
|
||||
static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class *clasp);
|
||||
static uint32_t dynamicSlotsCount(Shape *shape) {
|
||||
return dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), shape->getObjectClass());
|
||||
}
|
||||
|
||||
/* Elements accessors. */
|
||||
|
||||
|
|
|
@ -428,7 +428,8 @@ class ObjectGroup : public gc::TenuredCell
|
|||
* objects, property types account for plain data properties (those with a
|
||||
* slot and no getter or setter hook) and dense elements. In typed objects
|
||||
* and unboxed objects, property types account for object and value
|
||||
* properties and elements in the object.
|
||||
* properties and elements in the object, and expando properties in unboxed
|
||||
* objects.
|
||||
*
|
||||
* For accesses on these properties, the correspondence is as follows:
|
||||
*
|
||||
|
|
|
@ -346,6 +346,11 @@ UnboxedPlainObject::getValue(const UnboxedLayout::Property &property)
|
|||
void
|
||||
UnboxedPlainObject::trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
if (obj->as<UnboxedPlainObject>().expando_) {
|
||||
MarkObjectUnbarriered(trc, reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_),
|
||||
"unboxed_expando");
|
||||
}
|
||||
|
||||
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
|
||||
const int32_t *list = layout.traceList();
|
||||
if (!list)
|
||||
|
@ -369,6 +374,39 @@ UnboxedPlainObject::trace(JSTracer *trc, JSObject *obj)
|
|||
MOZ_ASSERT(*(list + 1) == -1);
|
||||
}
|
||||
|
||||
/* static */ UnboxedExpandoObject *
|
||||
UnboxedPlainObject::ensureExpando(JSContext *cx, Handle<UnboxedPlainObject *> obj)
|
||||
{
|
||||
if (obj->expando_)
|
||||
return obj->expando_;
|
||||
|
||||
UnboxedExpandoObject *expando = NewObjectWithGivenProto<UnboxedExpandoObject>(cx, NullPtr());
|
||||
if (!expando)
|
||||
return nullptr;
|
||||
|
||||
// As with setValue(), we need to manually trigger post barriers on the
|
||||
// whole object. If we treat the field as a HeapPtrObject and later convert
|
||||
// the object to its native representation, we will end up with a corrupted
|
||||
// store buffer entry.
|
||||
if (IsInsideNursery(expando) && !IsInsideNursery(obj))
|
||||
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(obj);
|
||||
|
||||
obj->expando_ = expando;
|
||||
return expando;
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext *cx, jsid id) const
|
||||
{
|
||||
if (layout().lookup(id))
|
||||
return true;
|
||||
|
||||
if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedLayout::makeNativeGroup(JSContext *cx, ObjectGroup *group)
|
||||
{
|
||||
|
@ -471,6 +509,7 @@ UnboxedLayout::makeNativeGroup(JSContext *cx, ObjectGroup *group)
|
|||
UnboxedPlainObject::convertToNative(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||
UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
|
||||
if (!layout.nativeGroup()) {
|
||||
if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
|
||||
|
@ -493,6 +532,41 @@ UnboxedPlainObject::convertToNative(JSContext *cx, JSObject *obj)
|
|||
for (size_t i = 0; i < values.length(); i++)
|
||||
obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
|
||||
|
||||
if (expando) {
|
||||
// Add properties from the expando object to the object, in order.
|
||||
// Suppress GC here, so that callers don't need to worry about this
|
||||
// method collecting. The stuff below can only fail due to OOM, in
|
||||
// which case the object will not have been completely filled back in.
|
||||
gc::AutoSuppressGC suppress(cx);
|
||||
|
||||
Vector<jsid> ids(cx);
|
||||
for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
|
||||
if (!ids.append(r.front().propid()))
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
|
||||
if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
|
||||
if (!ids.append(INT_TO_JSID(i)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
::Reverse(ids.begin(), ids.end());
|
||||
|
||||
RootedPlainObject nobj(cx, &obj->as<PlainObject>());
|
||||
Rooted<UnboxedExpandoObject *> nexpando(cx, expando);
|
||||
RootedId id(cx);
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
for (size_t i = 0; i < ids.length(); i++) {
|
||||
id = ids[i];
|
||||
if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc))
|
||||
return false;
|
||||
ObjectOpResult result;
|
||||
if (!StandardDefineProperty(cx, nobj, id, desc, result))
|
||||
return false;
|
||||
MOZ_ASSERT(result.ok());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -508,8 +582,8 @@ UnboxedPlainObject::create(ExclusiveContext *cx, HandleObjectGroup group, NewObj
|
|||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
// Avoid spurious shape guard hits.
|
||||
res->dummy_ = nullptr;
|
||||
// Overwrite the dummy shape which was written to the object's expando field.
|
||||
res->initExpando();
|
||||
|
||||
// Initialize reference fields of the object. All fields in the object will
|
||||
// be overwritten shortly, but references need to be safe for the GC.
|
||||
|
@ -585,7 +659,7 @@ UnboxedPlainObject::obj_lookupProperty(JSContext *cx, HandleObject obj,
|
|||
HandleId id, MutableHandleObject objp,
|
||||
MutableHandleShape propp)
|
||||
{
|
||||
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
|
||||
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
|
||||
MarkNonNativePropertyFound<CanGC>(propp);
|
||||
objp.set(obj);
|
||||
return true;
|
||||
|
@ -606,16 +680,38 @@ UnboxedPlainObject::obj_defineProperty(JSContext *cx, HandleObject obj, HandleId
|
|||
GetterOp getter, SetterOp setter, unsigned attrs,
|
||||
ObjectOpResult &result)
|
||||
{
|
||||
if (!convertToNative(cx, obj))
|
||||
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||
|
||||
if (const UnboxedLayout::Property *property = layout.lookup(id)) {
|
||||
if (!getter && !setter && attrs == JSPROP_ENUMERATE) {
|
||||
// This define is equivalent to setting an existing property.
|
||||
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, v))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Trying to incompatibly redefine an existing property requires the
|
||||
// object to be converted to a native object.
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
|
||||
return DefineProperty(cx, obj, id, v, getter, setter, attrs);
|
||||
}
|
||||
|
||||
// Define the property on the expando object.
|
||||
Rooted<UnboxedExpandoObject *> expando(cx, ensureExpando(cx, obj.as<UnboxedPlainObject>()));
|
||||
if (!expando)
|
||||
return false;
|
||||
|
||||
return DefineProperty(cx, obj, id, v, getter, setter, attrs, result);
|
||||
// Update property types on the unboxed object as well.
|
||||
AddTypePropertyId(cx, obj, id, v);
|
||||
|
||||
return DefineProperty(cx, expando, id, v, getter, setter, attrs, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_hasProperty(JSContext *cx, HandleObject obj, HandleId id, bool *foundp)
|
||||
{
|
||||
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
|
||||
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
|
||||
*foundp = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -640,6 +736,14 @@ UnboxedPlainObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObjec
|
|||
return true;
|
||||
}
|
||||
|
||||
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
||||
if (expando->containsShapeOrElement(cx, id)) {
|
||||
RootedObject nexpando(cx, expando);
|
||||
RootedObject nreceiver(cx, (obj == receiver) ? expando : receiver.get());
|
||||
return GetProperty(cx, nexpando, nreceiver, id, vp);
|
||||
}
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->getProto());
|
||||
if (!proto) {
|
||||
vp.setUndefined();
|
||||
|
@ -668,6 +772,17 @@ UnboxedPlainObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleObjec
|
|||
return SetPropertyByDefining(cx, obj, receiver, id, vp, false, result);
|
||||
}
|
||||
|
||||
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
||||
if (expando->containsShapeOrElement(cx, id)) {
|
||||
// Update property types on the unboxed object as well.
|
||||
AddTypePropertyId(cx, obj, id, vp);
|
||||
|
||||
RootedObject nexpando(cx, expando);
|
||||
RootedObject nreceiver(cx, (obj == receiver) ? expando : receiver.get());
|
||||
return SetProperty(cx, nexpando, nreceiver, id, vp, result);
|
||||
}
|
||||
}
|
||||
|
||||
return SetPropertyOnProto(cx, obj, receiver, id, vp, result);
|
||||
}
|
||||
|
||||
|
@ -684,6 +799,13 @@ UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj
|
|||
return true;
|
||||
}
|
||||
|
||||
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
||||
if (expando->containsShapeOrElement(cx, id)) {
|
||||
RootedObject nexpando(cx, expando);
|
||||
return GetOwnPropertyDescriptor(cx, nexpando, id, desc);
|
||||
}
|
||||
}
|
||||
|
||||
desc.object().set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
@ -708,16 +830,45 @@ UnboxedPlainObject::obj_watch(JSContext *cx, HandleObject obj, HandleId id, Hand
|
|||
/* static */ bool
|
||||
UnboxedPlainObject::obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties)
|
||||
{
|
||||
UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
||||
|
||||
// Add dense elements in the expando first, for consistency with plain objects.
|
||||
if (expando) {
|
||||
for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
|
||||
if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
|
||||
if (!properties.append(INT_TO_JSID(i)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const UnboxedLayout::PropertyVector &unboxed = obj->as<UnboxedPlainObject>().layout().properties();
|
||||
for (size_t i = 0; i < unboxed.length(); i++) {
|
||||
if (!properties.append(NameToId(unboxed[i].name)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expando) {
|
||||
Vector<jsid> ids(cx);
|
||||
for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
|
||||
if (!ids.append(r.front().propid()))
|
||||
return false;
|
||||
}
|
||||
::Reverse(ids.begin(), ids.end());
|
||||
if (!properties.append(ids.begin(), ids.length()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Class UnboxedExpandoObject::class_ = {
|
||||
"UnboxedExpandoObject",
|
||||
JSCLASS_IMPLEMENTS_BARRIERS
|
||||
};
|
||||
|
||||
const Class UnboxedPlainObject::class_ = {
|
||||
"Object",
|
||||
js_Object_str,
|
||||
Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
|
@ -1000,6 +1151,7 @@ js::TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
|
|||
if (!objects->get(i))
|
||||
continue;
|
||||
UnboxedPlainObject *obj = &objects->get(i)->as<UnboxedPlainObject>();
|
||||
obj->initExpando();
|
||||
memset(obj->data(), 0, layout->size());
|
||||
for (size_t j = 0; j < templateShape->slotSpan(); j++) {
|
||||
Value v = values[valueCursor++];
|
||||
|
|
|
@ -162,14 +162,25 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
static bool makeConstructorCode(JSContext *cx, HandleObjectGroup group);
|
||||
};
|
||||
|
||||
// Class for expando objects holding extra properties given to an unboxed plain
|
||||
// object. These objects behave identically to normal native plain objects, and
|
||||
// have a separate Class to distinguish them for memory usage reporting.
|
||||
class UnboxedExpandoObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
};
|
||||
|
||||
// Class for a plain object using an unboxed representation. The physical
|
||||
// layout of these objects is identical to that of an InlineTypedObject, though
|
||||
// these objects use an UnboxedLayout instead of a TypeDescr to keep track of
|
||||
// how their properties are stored.
|
||||
class UnboxedPlainObject : public JSObject
|
||||
{
|
||||
// Placeholder for extra properties. See bug 1137180.
|
||||
void *dummy_;
|
||||
// Optional object which stores extra properties on this object. This is
|
||||
// not automatically barriered to avoid problems if the object is converted
|
||||
// to a native. See ensureExpando().
|
||||
UnboxedExpandoObject *expando_;
|
||||
|
||||
// Start of the inline data, which immediately follows the group and extra properties.
|
||||
uint8_t data_[1];
|
||||
|
@ -214,6 +225,18 @@ class UnboxedPlainObject : public JSObject
|
|||
return &data_[0];
|
||||
}
|
||||
|
||||
UnboxedExpandoObject *maybeExpando() const {
|
||||
return expando_;
|
||||
}
|
||||
|
||||
void initExpando() {
|
||||
expando_ = nullptr;
|
||||
}
|
||||
|
||||
bool containsUnboxedOrExpandoProperty(ExclusiveContext *cx, jsid id) const;
|
||||
|
||||
static UnboxedExpandoObject *ensureExpando(JSContext *cx, Handle<UnboxedPlainObject *> obj);
|
||||
|
||||
bool setValue(ExclusiveContext *cx, const UnboxedLayout::Property &property, const Value &v);
|
||||
Value getValue(const UnboxedLayout::Property &property);
|
||||
|
||||
|
@ -225,6 +248,10 @@ class UnboxedPlainObject : public JSObject
|
|||
|
||||
static void trace(JSTracer *trc, JSObject *object);
|
||||
|
||||
static size_t offsetOfExpando() {
|
||||
return offsetof(UnboxedPlainObject, expando_);
|
||||
}
|
||||
|
||||
static size_t offsetOfData() {
|
||||
return offsetof(UnboxedPlainObject, data_[0]);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче