mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 1133423 - Optimize sets of expando properties and expando setter calls on DOM proxies. r=evilpie
This commit is contained in:
parent
dc82ea3ccb
commit
b86962b1de
@ -19,45 +19,56 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=965992
|
||||
// Ensure we are in JIT code and attach IC stubs.
|
||||
const iterations = 50;
|
||||
|
||||
function testFoo(obj, expected, kind) {
|
||||
function testFoo(obj, kind, expected) {
|
||||
for (var i = 0; i < iterations; i++) {
|
||||
is(obj.foo, expected, "Looking up an expando should work - " + kind);
|
||||
obj.foo = i;
|
||||
is(obj.foo, (expected === undefined) ? i : expected,
|
||||
"Looking up an expando should work - " + kind);
|
||||
}
|
||||
}
|
||||
|
||||
function getPropTests(obj) {
|
||||
// Start with a plain data property.
|
||||
obj.foo = "bar";
|
||||
testFoo(obj, "bar", "plain");
|
||||
testFoo(obj, "plain");
|
||||
|
||||
// Now change it to a scripted getter.
|
||||
// Now change it to a scripted getter/setter.
|
||||
var count = 0;
|
||||
Object.defineProperty(obj, "foo", {get:function() {
|
||||
var getterSetterVal = 0;
|
||||
Object.defineProperty(obj, "foo", {configurable: true, get: function() {
|
||||
is(this, obj, "Getter should have the proxy as |this|");
|
||||
is(arguments.length, 0, "Shouldn't pass arguments to getters");
|
||||
count++;
|
||||
return 123;
|
||||
}, configurable: true});
|
||||
testFoo(obj, 123, "scripted getter");
|
||||
is(count, iterations, "Should have called the getter enough times");
|
||||
return getterSetterVal;
|
||||
}, set: function(v) {
|
||||
is(this, obj, "Setter should have the proxy as |this|");
|
||||
is(arguments.length, 1, "Should pass 1 argument to setters");
|
||||
getterSetterVal = v;
|
||||
count++;
|
||||
}});
|
||||
testFoo(obj, "scripted getter/setter");
|
||||
is(count, iterations * 2, "Should have called the getter/setter enough times");
|
||||
|
||||
// Now try a native getter.
|
||||
Object.defineProperty(obj, "foo", {get: Object.prototype.valueOf, configurable: true});
|
||||
testFoo(obj, obj, "native getter");
|
||||
// Now try a native getter/setter.
|
||||
Object.defineProperty(obj, "foo", {get: Math.abs, set: Math.abs, configurable: true});
|
||||
testFoo(obj, "native getter/setter", NaN);
|
||||
}
|
||||
|
||||
function getElemTests(obj) {
|
||||
// Define two expando properties, then test inline caches for obj[prop]
|
||||
// correctly guard on prop being the same.
|
||||
var count = 0;
|
||||
var count = 0, getterSetterVal = 0;
|
||||
obj.elem1 = 1;
|
||||
Object.defineProperty(obj, "elem2", {get: function() { count++; return 2; }});
|
||||
|
||||
Object.defineProperty(obj, "elem2", {
|
||||
get: function() { count++; return getterSetterVal; },
|
||||
set: function(v) { getterSetterVal = v; count++; }
|
||||
});
|
||||
for (var i = 0; i < iterations; i++) {
|
||||
var prop = ((i & 1) == 0) ? "elem1" : "elem2";
|
||||
is(obj[prop], (i & 1) + 1, "Should return correct property value");
|
||||
obj[prop] = i;
|
||||
is(obj[prop], i, "Should return correct property value");
|
||||
}
|
||||
is(count, iterations / 2, "Should have called the getter enough times");
|
||||
is(count, iterations, "Should have called the getter/setter enough times");
|
||||
}
|
||||
|
||||
var directExpando = document.getElementsByTagName("*");
|
||||
|
@ -773,6 +773,27 @@ GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId,
|
||||
return true;
|
||||
}
|
||||
|
||||
ObjOperandId
|
||||
IRGenerator::guardDOMProxyExpandoObjectAndShape(JSObject* obj, ObjOperandId objId,
|
||||
const Value& expandoVal, JSObject* expandoObj)
|
||||
{
|
||||
MOZ_ASSERT(IsCacheableDOMProxy(obj));
|
||||
|
||||
writer.guardShape(objId, obj->maybeShape());
|
||||
|
||||
// Shape determines Class, so now it must be a DOM proxy.
|
||||
ValOperandId expandoValId;
|
||||
if (expandoVal.isObject())
|
||||
expandoValId = writer.loadDOMExpandoValue(objId);
|
||||
else
|
||||
expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
|
||||
|
||||
// Guard the expando is an object and shape guard.
|
||||
ObjOperandId expandoObjId = writer.guardIsObject(expandoValId);
|
||||
writer.guardShape(expandoObjId, expandoObj->as<NativeObject>().shape());
|
||||
return expandoObjId;
|
||||
}
|
||||
|
||||
bool
|
||||
GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id)
|
||||
{
|
||||
@ -780,13 +801,12 @@ GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objI
|
||||
|
||||
RootedValue expandoVal(cx_, GetProxyExtra(obj, GetDOMProxyExpandoSlot()));
|
||||
RootedObject expandoObj(cx_);
|
||||
ExpandoAndGeneration* expandoAndGeneration = nullptr;
|
||||
if (expandoVal.isObject()) {
|
||||
expandoObj = &expandoVal.toObject();
|
||||
} else {
|
||||
MOZ_ASSERT(!expandoVal.isUndefined(),
|
||||
"How did a missing expando manage to shadow things?");
|
||||
expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
|
||||
auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
|
||||
MOZ_ASSERT(expandoAndGeneration);
|
||||
expandoObj = &expandoAndGeneration->expando.toObject();
|
||||
}
|
||||
@ -805,20 +825,8 @@ GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objI
|
||||
MOZ_ASSERT(holder == expandoObj);
|
||||
|
||||
maybeEmitIdGuard(id);
|
||||
writer.guardShape(objId, obj->maybeShape());
|
||||
|
||||
// Shape determines Class, so now it must be a DOM proxy.
|
||||
ValOperandId expandoValId;
|
||||
if (expandoVal.isObject()) {
|
||||
expandoValId = writer.loadDOMExpandoValue(objId);
|
||||
} else {
|
||||
MOZ_ASSERT(expandoAndGeneration);
|
||||
expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
|
||||
}
|
||||
|
||||
// Guard the expando is an object and shape guard.
|
||||
ObjOperandId expandoObjId = writer.guardIsObject(expandoValId);
|
||||
writer.guardShape(expandoObjId, expandoObj->as<NativeObject>().shape());
|
||||
ObjOperandId expandoObjId =
|
||||
guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
|
||||
|
||||
if (canCache == CanAttachReadSlot) {
|
||||
// Load from the expando's slots.
|
||||
@ -2128,20 +2136,20 @@ LookupShapeForSetSlot(NativeObject* obj, jsid id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
|
||||
ValOperandId rhsId)
|
||||
static bool
|
||||
CanAttachNativeSetSlot(JSContext* cx, HandleObject obj, HandleId id,
|
||||
bool* isTemporarilyUnoptimizable, MutableHandleShape propShape)
|
||||
{
|
||||
if (!obj->isNative())
|
||||
return false;
|
||||
|
||||
RootedShape propShape(cx_, LookupShapeForSetSlot(&obj->as<NativeObject>(), id));
|
||||
propShape.set(LookupShapeForSetSlot(&obj->as<NativeObject>(), id));
|
||||
if (!propShape)
|
||||
return false;
|
||||
|
||||
RootedObjectGroup group(cx_, JSObject::getGroup(cx_, obj));
|
||||
ObjectGroup* group = JSObject::getGroup(cx, obj);
|
||||
if (!group) {
|
||||
cx_->recoverFromOutOfMemory();
|
||||
cx->recoverFromOutOfMemory();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2149,12 +2157,23 @@ SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId,
|
||||
// properties, TI will not mark the property as having been
|
||||
// overwritten. Don't attach a stub in this case, so that we don't
|
||||
// execute another write to the property without TI seeing that write.
|
||||
EnsureTrackPropertyTypes(cx_, obj, id);
|
||||
EnsureTrackPropertyTypes(cx, obj, id);
|
||||
if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
|
||||
*isTemporarilyUnoptimizable_ = true;
|
||||
*isTemporarilyUnoptimizable = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
|
||||
ValOperandId rhsId)
|
||||
{
|
||||
RootedShape propShape(cx_);
|
||||
if (!CanAttachNativeSetSlot(cx_, obj, id, isTemporarilyUnoptimizable_, &propShape))
|
||||
return false;
|
||||
|
||||
if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp) {
|
||||
writer.megamorphicStoreSlot(objId, JSID_TO_ATOM(id)->asPropertyName(), rhsId,
|
||||
typeCheckInfo_.needsTypeBarrier());
|
||||
@ -2802,6 +2821,58 @@ SetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId o
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id,
|
||||
ValOperandId rhsId)
|
||||
{
|
||||
MOZ_ASSERT(IsCacheableDOMProxy(obj));
|
||||
|
||||
RootedValue expandoVal(cx_, GetProxyExtra(obj, GetDOMProxyExpandoSlot()));
|
||||
RootedObject expandoObj(cx_);
|
||||
if (expandoVal.isObject()) {
|
||||
expandoObj = &expandoVal.toObject();
|
||||
} else {
|
||||
MOZ_ASSERT(!expandoVal.isUndefined(),
|
||||
"How did a missing expando manage to shadow things?");
|
||||
auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
|
||||
MOZ_ASSERT(expandoAndGeneration);
|
||||
expandoObj = &expandoAndGeneration->expando.toObject();
|
||||
}
|
||||
|
||||
RootedShape propShape(cx_);
|
||||
if (CanAttachNativeSetSlot(cx_, expandoObj, id, isTemporarilyUnoptimizable_, &propShape)) {
|
||||
maybeEmitIdGuard(id);
|
||||
ObjOperandId expandoObjId =
|
||||
guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
|
||||
|
||||
NativeObject* nativeExpandoObj = &expandoObj->as<NativeObject>();
|
||||
writer.guardGroup(expandoObjId, nativeExpandoObj->group());
|
||||
typeCheckInfo_.set(nativeExpandoObj->group(), id);
|
||||
|
||||
EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, propShape, rhsId);
|
||||
trackAttached("DOMProxyExpandoSlot");
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject holder(cx_);
|
||||
if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &propShape,
|
||||
isTemporarilyUnoptimizable_))
|
||||
{
|
||||
// Note that we don't actually use the expandoObjId here after the
|
||||
// shape guard. The DOM proxy (objId) is passed to the setter as
|
||||
// |this|.
|
||||
maybeEmitIdGuard(id);
|
||||
guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
|
||||
|
||||
MOZ_ASSERT(holder == expandoObj);
|
||||
EmitCallSetterNoGuards(writer, expandoObj, expandoObj, propShape, objId, rhsId);
|
||||
trackAttached("DOMProxyExpandoSetter");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id,
|
||||
ValOperandId rhsId)
|
||||
@ -2820,6 +2891,13 @@ SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleI
|
||||
case ProxyStubType::None:
|
||||
break;
|
||||
case ProxyStubType::DOMExpando:
|
||||
if (tryAttachDOMProxyExpando(obj, objId, id, rhsId))
|
||||
return true;
|
||||
if (*isTemporarilyUnoptimizable_) {
|
||||
// Scripted setter without JIT code. Just wait.
|
||||
return false;
|
||||
}
|
||||
MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
|
||||
case ProxyStubType::DOMShadowed:
|
||||
return tryAttachDOMProxyShadowed(obj, objId, id, rhsId);
|
||||
case ProxyStubType::DOMUnshadowed:
|
||||
|
@ -982,6 +982,9 @@ class MOZ_RAII IRGenerator
|
||||
bool maybeGuardInt32Index(const Value& index, ValOperandId indexId,
|
||||
uint32_t* int32Index, Int32OperandId* int32IndexId);
|
||||
|
||||
ObjOperandId guardDOMProxyExpandoObjectAndShape(JSObject* obj, ObjOperandId objId,
|
||||
const Value& expandoVal, JSObject* expandoObj);
|
||||
|
||||
void emitIdGuard(ValOperandId valId, jsid id);
|
||||
|
||||
friend class CacheIRSpewer;
|
||||
@ -1186,6 +1189,8 @@ class MOZ_RAII SetPropIRGenerator : public IRGenerator
|
||||
ValOperandId rhsId);
|
||||
bool tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id,
|
||||
ValOperandId rhsId);
|
||||
bool tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id,
|
||||
ValOperandId rhsId);
|
||||
bool tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId);
|
||||
bool tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user