Bug 1394831 part 19 - Factor out maybeToDictionaryModeForPut. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 13 Nov 2017 10:54:12 +0100
changeset 436023 cb39c30dc214bd0161fa8a8620edd547cd969c7d
parent 436022 53a3033449d325ffb8c326e6ace749eb9f5d027b
child 436024 7664017a8e916b6dbe7f66e86de09d1f685abb19
push id117
push userfmarier@mozilla.com
push dateTue, 28 Nov 2017 20:17:16 +0000
reviewersbhackett
bugs1394831
milestone59.0a1
Bug 1394831 part 19 - Factor out maybeToDictionaryModeForPut. r=bhackett
js/src/vm/NativeObject.h
js/src/vm/Shape.cpp
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -854,16 +854,19 @@ class NativeObject : public ShapedObject
                                                              HandleShape parent,
                                                              MutableHandle<StackShape> child);
 
     static MOZ_ALWAYS_INLINE bool
     maybeConvertToOrGrowDictionaryForAdd(JSContext* cx, HandleNativeObject obj, HandleId id,
                                          ShapeTable** table, ShapeTable::Entry** entry,
                                          const AutoKeepShapeTables& keep);
 
+    static bool maybeToDictionaryModeForPut(JSContext* cx, HandleNativeObject obj,
+                                            MutableHandleShape shape);
+
   public:
     /* Add a property whose id is not yet in this scope. */
     static MOZ_ALWAYS_INLINE Shape* addDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                                                     uint32_t slot, unsigned attrs);
 
     static MOZ_ALWAYS_INLINE Shape* addAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                                                         JSGetterOp getter, JSSetterOp setter,
                                                         unsigned attrs);
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -772,16 +772,37 @@ AssertValidArrayIndex(NativeObject* obj,
         ArrayObject* arr = &obj->as<ArrayObject>();
         uint32_t index;
         if (IdIsIndex(id, &index))
             MOZ_ASSERT(index < arr->length() || arr->lengthIsWritable());
     }
 #endif
 }
 
+/* static */ bool
+NativeObject::maybeToDictionaryModeForPut(JSContext* cx, HandleNativeObject obj,
+                                          MutableHandleShape shape)
+{
+    // Overwriting a non-last property requires switching to dictionary mode.
+    // The shape tree is shared immutable, and we can't removeProperty and then
+    // addAccessorPropertyInternal because a failure under add would lose data.
+
+    if (shape == obj->lastProperty() || obj->inDictionaryMode())
+        return true;
+
+    if (!toDictionaryMode(cx, obj))
+        return false;
+
+    AutoCheckCannotGC nogc;
+    ShapeTable* table = obj->lastProperty()->maybeTable(nogc);
+    MOZ_ASSERT(table);
+    shape.set(table->search<MaybeAdding::NotAdding>(shape->propid(), nogc).shape());
+    return true;
+}
+
 /* static */ Shape*
 NativeObject::putDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                               unsigned attrs)
 {
     MOZ_ASSERT(!JSID_IS_VOID(id));
 
     AutoCheckShapeConsistency check(obj);
     AssertValidArrayIndex(obj, id);
@@ -826,26 +847,18 @@ NativeObject::putDataProperty(JSContext*
             return nullptr;
     }
 
     // Now that we've possibly preserved slot, check whether all members match.
     // If so, this is a redundant "put" and we can return without more work.
     if (shape->matchesParamsAfterId(nbase, slot, attrs, nullptr, nullptr))
         return shape;
 
-    // Overwriting a non-last property requires switching to dictionary mode.
-    // The shape tree is shared immutable, and we can't removeProperty and then
-    // addDataPropertyInternal because a failure under add would lose data.
-    if (shape != obj->lastProperty() && !obj->inDictionaryMode()) {
-        if (!toDictionaryMode(cx, obj))
-            return nullptr;
-        ShapeTable* table = obj->lastProperty()->maybeTable(keep);
-        MOZ_ASSERT(table);
-        shape = table->search<MaybeAdding::NotAdding>(shape->propid(), keep).shape();
-    }
+    if (!maybeToDictionaryModeForPut(cx, obj, &shape))
+        return nullptr;
 
     MOZ_ASSERT_IF(shape->isDataProperty(), shape->slot() == slot);
 
     if (obj->inDictionaryMode()) {
         // Updating some property in a dictionary-mode object. Create a new
         // shape for the existing property, and also generate a new shape for
         // the last property of the dictionary (unless the modified property
         // is also the last property).
@@ -940,26 +953,18 @@ NativeObject::putAccessorProperty(JSCont
             return nullptr;
     }
 
     // Check whether all members match. If so, this is a redundant "put" and we can
     // return without more work.
     if (shape->matchesParamsAfterId(nbase, SHAPE_INVALID_SLOT, attrs, getter, setter))
         return shape;
 
-    // Overwriting a non-last property requires switching to dictionary mode.
-    // The shape tree is shared immutable, and we can't removeProperty and then
-    // addAccessorPropertyInternal because a failure under add would lose data.
-    if (shape != obj->lastProperty() && !obj->inDictionaryMode()) {
-        if (!toDictionaryMode(cx, obj))
-            return nullptr;
-        ShapeTable* table = obj->lastProperty()->maybeTable(keep);
-        MOZ_ASSERT(table);
-        shape = table->search<MaybeAdding::NotAdding>(shape->propid(), keep).shape();
-    }
+    if (!maybeToDictionaryModeForPut(cx, obj, &shape))
+        return nullptr;
 
     if (obj->inDictionaryMode()) {
         // Updating some property in a dictionary-mode object. Create a new
         // shape for the existing property, and also generate a new shape for
         // the last property of the dictionary (unless the modified property
         // is also the last property).
         bool updateLast = (shape == obj->lastProperty());
         shape = NativeObject::replaceWithNewEquivalentShape(cx, obj, shape, nullptr,