mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 792631 - Add IC for missing properties. r=dvander
This commit is contained in:
parent
5327c7eaab
commit
3c85803aac
@ -157,6 +157,52 @@ IsCacheableGetPropReadSlot(JSObject *obj, JSObject *holder, const Shape *shape)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableNoProperty(JSObject *obj, JSObject *holder, const Shape *shape, jsbytecode *pc,
|
||||
const TypedOrValueRegister &output)
|
||||
{
|
||||
if (shape)
|
||||
return false;
|
||||
|
||||
JS_ASSERT(!holder);
|
||||
|
||||
// Just because we didn't find the property on the object doesn't mean it
|
||||
// won't magically appear through various engine hacks:
|
||||
if (obj->getClass()->getProperty && obj->getClass()->getProperty != JS_PropertyStub)
|
||||
return false;
|
||||
|
||||
// Don't generate missing property ICs if we skipped a non-native object, as
|
||||
// lookups may extend beyond the prototype chain (e.g. for ListBase
|
||||
// proxies).
|
||||
JSObject *obj2 = obj;
|
||||
while (obj2) {
|
||||
if (!obj2->isNative())
|
||||
return false;
|
||||
obj2 = obj2->getProto();
|
||||
}
|
||||
|
||||
#if JS_HAS_NO_SUCH_METHOD
|
||||
// This case cannot appear with an idempotent cache.
|
||||
if (pc) {
|
||||
// The __noSuchMethod__ hook may substitute in a valid method. Since,
|
||||
// if o.m is missing, o.m() will probably be an error, just mark all
|
||||
// missing callprops as uncacheable.
|
||||
if (JSOp(*pc) == JSOP_CALLPROP ||
|
||||
JSOp(*pc) == JSOP_CALLELEM)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// TI has not yet monitored an Undefined value. The fallback path will
|
||||
// monitor and invalidate the script.
|
||||
if (!output.hasValue())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, const Shape *shape)
|
||||
{
|
||||
@ -226,26 +272,55 @@ struct GetNativePropertyStub
|
||||
// Note: this may clobber the object register if it's used as scratch.
|
||||
GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg, &prototypeFailures);
|
||||
|
||||
// Guard on the holder's shape.
|
||||
holderReg = scratchReg;
|
||||
masm.movePtr(ImmGCPtr(holder), holderReg);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(holderReg, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(holder->lastProperty()),
|
||||
&prototypeFailures);
|
||||
if (holder) {
|
||||
// Guard on the holder's shape.
|
||||
holderReg = scratchReg;
|
||||
masm.movePtr(ImmGCPtr(holder), holderReg);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(holderReg, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(holder->lastProperty()),
|
||||
&prototypeFailures);
|
||||
} else {
|
||||
// The property does not exist. Guard on everything in the
|
||||
// prototype chain.
|
||||
JSObject *proto = obj->getTaggedProto().toObjectOrNull();
|
||||
Register lastReg = object;
|
||||
JS_ASSERT(scratchReg != object);
|
||||
while (proto) {
|
||||
Address addrType(lastReg, JSObject::offsetOfType());
|
||||
masm.loadPtr(addrType, scratchReg);
|
||||
Address addrProto(scratchReg, offsetof(types::TypeObject, proto));
|
||||
masm.loadPtr(addrProto, scratchReg);
|
||||
Address addrShape(scratchReg, JSObject::offsetOfShape());
|
||||
|
||||
// Guard the shape of the current prototype.
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(scratchReg, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(proto->lastProperty()),
|
||||
&prototypeFailures);
|
||||
|
||||
proto = proto->getProto();
|
||||
lastReg = scratchReg;
|
||||
}
|
||||
|
||||
holderReg = InvalidReg;
|
||||
}
|
||||
} else {
|
||||
holderReg = object;
|
||||
}
|
||||
|
||||
// Slot access.
|
||||
if (holder->isFixedSlot(shape->slot())) {
|
||||
if (holder && holder->isFixedSlot(shape->slot())) {
|
||||
Address addr(holderReg, JSObject::getFixedSlotOffset(shape->slot()));
|
||||
masm.loadTypedOrValue(addr, output);
|
||||
} else {
|
||||
} else if (holder) {
|
||||
masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), scratchReg);
|
||||
|
||||
Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
|
||||
masm.loadTypedOrValue(addr, output);
|
||||
} else {
|
||||
JS_ASSERT(!holder);
|
||||
masm.moveValue(UndefinedValue(), output.valueReg());
|
||||
}
|
||||
|
||||
if (restoreScratch)
|
||||
@ -654,7 +729,13 @@ TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
|
||||
bool readSlot = false;
|
||||
bool callGetter = false;
|
||||
|
||||
if (IsCacheableGetPropReadSlot(checkObj, holder, shape)) {
|
||||
RootedScript script(cx);
|
||||
jsbytecode *pc;
|
||||
cache.getScriptedLocation(&script, &pc);
|
||||
|
||||
if (IsCacheableGetPropReadSlot(checkObj, holder, shape) ||
|
||||
IsCacheableNoProperty(checkObj, holder, shape, pc, cache.output()))
|
||||
{
|
||||
readSlot = true;
|
||||
} else if (IsCacheableGetPropCallNative(checkObj, holder, shape) ||
|
||||
IsCacheableGetPropCallPropertyOp(checkObj, holder, shape))
|
||||
@ -675,6 +756,7 @@ TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
|
||||
// monitor the return type inside an idempotent cache though, so we don't
|
||||
// handle this case.
|
||||
if (cache.idempotent() &&
|
||||
holder &&
|
||||
holder->hasSingletonType() &&
|
||||
holder->getSlot(shape->slot()).isUndefined())
|
||||
{
|
||||
@ -1306,7 +1388,12 @@ IonCacheGetElement::attachGetProp(JSContext *cx, IonScript *ion, HandleObject ob
|
||||
if (!JSObject::lookupProperty(cx, obj, name, &holder, &shape))
|
||||
return false;
|
||||
|
||||
if (!IsCacheableGetPropReadSlot(obj, holder, shape)) {
|
||||
RootedScript script(cx);
|
||||
jsbytecode *pc;
|
||||
getScriptedLocation(&script, &pc);
|
||||
|
||||
if (!IsCacheableGetPropReadSlot(obj, holder, shape) &&
|
||||
!IsCacheableNoProperty(obj, holder, shape, pc, output())) {
|
||||
IonSpew(IonSpew_InlineCaches, "GETELEM uncacheable property");
|
||||
return true;
|
||||
}
|
||||
@ -1761,7 +1848,7 @@ IonCacheName::attach(JSContext *cx, IonScript *ion, HandleObject scopeChain, Han
|
||||
|
||||
static bool
|
||||
IsCacheableName(JSContext *cx, HandleObject scopeChain, HandleObject obj, HandleObject holder,
|
||||
HandleShape shape)
|
||||
HandleShape shape, const TypedOrValueRegister &output)
|
||||
{
|
||||
if (!shape)
|
||||
return false;
|
||||
@ -1772,7 +1859,8 @@ IsCacheableName(JSContext *cx, HandleObject scopeChain, HandleObject obj, Handle
|
||||
|
||||
if (obj->isGlobal()) {
|
||||
// Support only simple property lookups.
|
||||
if (!IsCacheableGetPropReadSlot(obj, holder, shape))
|
||||
if (!IsCacheableGetPropReadSlot(obj, holder, shape) &&
|
||||
!IsCacheableNoProperty(obj, holder, shape, NULL, output))
|
||||
return false;
|
||||
} else if (obj->isCall()) {
|
||||
if (!shape->hasDefaultGetter())
|
||||
@ -1818,7 +1906,7 @@ js::ion::GetNameCache(JSContext *cx, size_t cacheIndex, HandleObject scopeChain,
|
||||
return false;
|
||||
|
||||
if (cache.stubCount() < MAX_STUBS &&
|
||||
IsCacheableName(cx, scopeChain, obj, holder, shape))
|
||||
IsCacheableName(cx, scopeChain, obj, holder, shape, cache.outputReg()))
|
||||
{
|
||||
if (!cache.attach(cx, ion, scopeChain, obj, shape))
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user