Bug 1338828 part 3 - Add CacheIR SetProp/SetElem stubs for unshadowed setter calls on DOM proxies. r=h4writer
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 21 Feb 2017 13:49:29 +0100
changeset 373089 eb661732bcdec7e929eee1cbfb7f3fbab915ff1e
parent 373088 8f7c7758fda8b7df58b796a747b2c73810bfc7f0
child 373090 0be3bfee0e9ea84ad2dcfcdaca010d97cd50c4f1
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs1338828
milestone54.0a1
Bug 1338828 part 3 - Add CacheIR SetProp/SetElem stubs for unshadowed setter calls on DOM proxies. r=h4writer
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2160,19 +2160,46 @@ SetPropIRGenerator::trackNotAttached()
         sp.valueProperty(guard, "base", lhsVal_);
         sp.valueProperty(guard, "property", idVal_);
         sp.valueProperty(guard, "value", rhsVal_);
         sp.endCache(guard);
     }
 #endif
 }
 
+static bool
+CanAttachSetter(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id,
+                MutableHandleObject holder, MutableHandleShape propShape,
+                bool* isTemporarilyUnoptimizable)
+{
+    // Don't attach a setter stub for ops like JSOP_INITELEM.
+    if (IsPropertyInitOp(JSOp(*pc)))
+        return false;
+    MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
+
+    PropertyResult prop;
+    if (!LookupPropertyPure(cx, obj, id, holder.address(), &prop))
+        return false;
+
+    if (prop.isNonNativeProperty())
+        return false;
+
+    propShape.set(prop.maybeShape());
+    if (!IsCacheableSetPropCallScripted(obj, holder, propShape, isTemporarilyUnoptimizable) &&
+        !IsCacheableSetPropCallNative(obj, holder, propShape))
+    {
+        return false;
+    }
+
+    return true;
+}
+
 static void
-EmitCallSetterResultNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
-                             Shape* shape, ObjOperandId objId, ValOperandId rhsId)
+EmitCallSetterNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
+                       Shape* shape, ObjOperandId objId, ValOperandId rhsId)
 {
     if (IsCacheableSetPropCallNative(obj, holder, shape)) {
         JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
         MOZ_ASSERT(target->isNative());
         writer.callNativeSetter(objId, target, rhsId);
         writer.returnFromIC();
         return;
     }
@@ -2184,50 +2211,35 @@ EmitCallSetterResultNoGuards(CacheIRWrit
     writer.callScriptedSetter(objId, target, rhsId);
     writer.returnFromIC();
 }
 
 bool
 SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
                                     ValOperandId rhsId)
 {
-    // Don't attach a setter stub for ops like JSOP_INITELEM.
-    if (IsPropertyInitOp(JSOp(*pc_)))
-        return false;
-    MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
-
-    PropertyResult prop;
-    JSObject* holder;
-    if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
+    RootedObject holder(cx_);
+    RootedShape propShape(cx_);
+    if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &propShape, isTemporarilyUnoptimizable_))
         return false;
 
-    if (prop.isNonNativeProperty())
-        return false;
-
-    Shape* shape = prop.maybeShape();
-    if (!IsCacheableSetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable_) &&
-        !IsCacheableSetPropCallNative(obj, holder, shape))
-    {
-        return false;
-    }
-
     maybeEmitIdGuard(id);
 
     Maybe<ObjOperandId> expandoId;
-    TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
+    TestMatchingReceiver(writer, obj, propShape, objId, &expandoId);
 
     if (obj != holder) {
         GeneratePrototypeGuards(writer, obj, holder, objId);
 
         // Guard on the holder's shape.
         ObjOperandId holderId = writer.loadObject(holder);
         writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
     }
 
-    EmitCallSetterResultNoGuards(writer, obj, holder, shape, objId, rhsId);
+    EmitCallSetterNoGuards(writer, obj, holder, propShape, objId, rhsId);
 
     trackAttached("Setter");
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id,
                                             ValOperandId rhsId)
@@ -2507,25 +2519,29 @@ SetPropIRGenerator::tryAttachSetUnboxedA
     setUpdateStubInfo(aobj->group(), JSID_VOID);
 
     trackAttached("StoreUnboxedArrayElementHole");
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
-                                          ValOperandId rhsId)
+                                          ValOperandId rhsId, bool handleDOMProxies)
 {
     MOZ_ASSERT(obj->is<ProxyObject>());
 
     writer.guardIsProxy(objId);
 
-    // Ensure that the incoming object is not a DOM proxy, so that we can get to
-    // the specialized stubs
-    writer.guardNotDOMProxy(objId);
+    if (!handleDOMProxies) {
+        // Ensure that the incoming object is not a DOM proxy, so that we can
+        // get to the specialized stubs. If handleDOMProxies is true, we were
+        // unable to attach a specialized DOM stub, so we just handle all
+        // proxies here.
+        writer.guardNotDOMProxy(objId);
+    }
 
     if (cacheKind_ == CacheKind::SetProp) {
         writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
     } else {
         // We could call maybeEmitIdGuard here and then emit CallProxySet, but
         // for SetElem we prefer to attach a stub that can handle any Value
         // so we don't attach a new stub for every id.
         MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
@@ -2553,28 +2569,67 @@ SetPropIRGenerator::tryAttachDOMProxySha
     writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
     writer.returnFromIC();
 
     trackAttached("DOMProxyShadowed");
     return true;
 }
 
 bool
+SetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id,
+                                                ValOperandId rhsId)
+{
+    MOZ_ASSERT(IsCacheableDOMProxy(obj));
+
+    RootedObject proto(cx_, obj->staticPrototype());
+    if (!proto)
+        return false;
+
+    RootedObject holder(cx_);
+    RootedShape propShape(cx_);
+    if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &propShape, isTemporarilyUnoptimizable_))
+        return false;
+
+    maybeEmitIdGuard(id);
+    writer.guardShape(objId, obj->maybeShape());
+
+    // Guard that our expando object hasn't started shadowing this property.
+    CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
+
+    GeneratePrototypeGuards(writer, obj, holder, objId);
+
+    // Guard on the holder of the property.
+    ObjOperandId holderId = writer.loadObject(holder);
+    writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
+
+    // EmitCallSetterNoGuards expects |obj| to be the object the property is
+    // on to do some checks. Since we actually looked at proto, and no extra
+    // guards will be generated, we can just pass that instead.
+    EmitCallSetterNoGuards(writer, proto, holder, propShape, objId, rhsId);
+
+    trackAttached("DOMProxyUnshadowed");
+    return true;
+}
+
+bool
 SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id,
                                    ValOperandId rhsId)
 {
     switch (GetProxyStubType(cx_, obj, id)) {
       case ProxyStubType::None:
         return false;
       case ProxyStubType::DOMExpando:
       case ProxyStubType::DOMShadowed:
         return tryAttachDOMProxyShadowed(obj, objId, id, rhsId);
       case ProxyStubType::DOMUnshadowed:
+        if (tryAttachDOMProxyUnshadowed(obj, objId, id, rhsId))
+            return true;
+        return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
       case ProxyStubType::Generic:
-        return tryAttachGenericProxy(obj, objId, id, rhsId);
+        return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ false);
     }
 
     MOZ_CRASH("Unexpected ProxyStubType");
 }
 
 bool
 SetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId)
 {
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -1087,19 +1087,21 @@ class MOZ_RAII SetPropIRGenerator : publ
                                   Int32OperandId indexId, ValOperandId rhsId);
 
     bool tryAttachSetDenseElementHole(HandleObject obj, ObjOperandId objId, uint32_t index,
                                       Int32OperandId indexId, ValOperandId rhsId);
     bool tryAttachSetUnboxedArrayElementHole(HandleObject obj, ObjOperandId objId, uint32_t index,
                                              Int32OperandId indexId, ValOperandId rhsId);
 
     bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
-                               ValOperandId rhsId);
+                               ValOperandId rhsId, bool handleDOMProxies);
     bool tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id,
                                    ValOperandId rhsId);
+    bool tryAttachDOMProxyUnshadowed(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);
 
     void trackAttached(const char* name);
 
   public:
     SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
                        bool* isTemporarilyUnoptimizable, HandleValue lhsVal, HandleValue idVal,