Bug 951213 - Improve robustness when rolling back properties after the definite properties analysis fails, allow metadata objects to be in the nursery, r=jandem,terrence.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 26 Dec 2013 15:25:33 -0700
changeset 161800 057498186852828732cf77a84b58efdcf794a5cc
parent 161799 8eacfa0523bd23fbb5cdcfc22fa1c00abd99e781
child 161801 07f7597000b705d6f6c0c31f3488d0f054e43713
push id37989
push userbhackett@mozilla.com
push dateThu, 26 Dec 2013 22:25:38 +0000
treeherdermozilla-inbound@057498186852 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, terrence
bugs951213
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 951213 - Improve robustness when rolling back properties after the definite properties analysis fails, allow metadata objects to be in the nursery, r=jandem,terrence.
js/src/jit-test/tests/basic/bug951213.js
js/src/jsinfer.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/vm/Shape.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug951213.js
@@ -0,0 +1,8 @@
+
+setObjectMetadataCallback(function(obj) {});
+function foo(x, y) {
+  this.g = x + y;
+}
+var a = 0;
+var b = { valueOf: function() Object.defineProperty(Object.prototype, 'g', {}) };
+var c = new foo(a, b);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3124,18 +3124,20 @@ TypeObject::clearNewScriptAddendum(Exclu
                     }
                 } else {
                     JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE);
                     finished = true;
                     break;
                 }
             }
 
-            if (!finished)
-                obj->rollbackProperties(cx, numProperties);
+            if (!finished) {
+                if (!obj->rollbackProperties(cx, numProperties))
+                    cx->compartment()->types.setPendingNukeTypes(cx);
+            }
         }
     } else {
         // Threads with an ExclusiveContext are not allowed to run scripts.
         JS_ASSERT(!cx->perThreadData->activation());
     }
 }
 
 void
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -411,17 +411,17 @@ class JSObject : public js::ObjectImpl
     }
 
     void prepareElementRangeForOverwrite(size_t start, size_t end) {
         JS_ASSERT(end <= getDenseInitializedLength());
         for (size_t i = start; i < end; i++)
             elements[i].js::HeapSlot::~HeapSlot();
     }
 
-    void rollbackProperties(js::ExclusiveContext *cx, uint32_t slotSpan);
+    bool rollbackProperties(js::ExclusiveContext *cx, uint32_t slotSpan);
 
     void nativeSetSlot(uint32_t slot, const js::Value &value) {
         JS_ASSERT(isNative());
         JS_ASSERT(slot < slotSpan());
         return setSlot(slot, value);
     }
 
     inline bool nativeSetSlotIfHasType(js::Shape *shape, const js::Value &value);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1053,25 +1053,22 @@ NewObjectMetadata(ExclusiveContext *cxAr
 {
     // The metadata callback is invoked before each created object, except when
     // analysis/compilation is active, to avoid recursion.
     JS_ASSERT(!*pmetadata);
     if (JSContext *cx = cxArg->maybeJSContext()) {
         if (JS_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) &&
             !cx->compartment()->activeAnalysis)
         {
-            JS::DisableGenerationalGC(cx->runtime());
-
             // Use AutoEnterAnalysis to prohibit both any GC activity under the
             // callback, and any reentering of JS via Invoke() etc.
             types::AutoEnterAnalysis enter(cx);
 
-            bool status = cx->compartment()->callObjectMetadataCallback(cx, pmetadata);
-            JS::EnableGenerationalGC(cx->runtime());
-            return status;
+            if (!cx->compartment()->callObjectMetadataCallback(cx, pmetadata))
+                return false;
         }
     }
     return true;
 }
 
 inline bool
 DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value,
                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1151,29 +1151,40 @@ JSObject::clear(JSContext *cx, HandleObj
         shape->listp = &obj->shape_;
 
     JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, obj, shape));
 
     ++cx->runtime()->propertyRemovals;
     obj->checkShapeConsistency();
 }
 
-void
+bool
 JSObject::rollbackProperties(ExclusiveContext *cx, uint32_t slotSpan)
 {
     /*
      * Remove properties from this object until it has a matching slot span.
      * The object cannot have escaped in a way which would prevent safe
      * removal of the last properties.
      */
     JS_ASSERT(!inDictionaryMode() && slotSpan <= this->slotSpan());
-    while (this->slotSpan() != slotSpan) {
-        JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined());
-        removeLastProperty(cx);
+    while (true) {
+        if (lastProperty()->isEmptyShape()) {
+            JS_ASSERT(slotSpan == 0);
+            break;
+        } else {
+            uint32_t slot = lastProperty()->slot();
+            if (slot < slotSpan)
+                break;
+            JS_ASSERT(getSlot(slot).isUndefined());
+        }
+        if (!removeProperty(cx, lastProperty()->propid()))
+            return false;
     }
+
+    return true;
 }
 
 Shape *
 ObjectImpl::replaceWithNewEquivalentShape(ThreadSafeContext *cx, Shape *oldShape, Shape *newShape)
 {
     JS_ASSERT(cx->isThreadLocal(this));
     JS_ASSERT(cx->isThreadLocal(oldShape));
     JS_ASSERT(cx->isInsideCurrentCompartment(oldShape));
@@ -1583,19 +1594,16 @@ InitialShapeEntry::match(const InitialSh
 
 /* static */ Shape *
 EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProto proto,
                             JSObject *parent, JSObject *metadata,
                             size_t nfixed, uint32_t objectFlags)
 {
     JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
     JS_ASSERT_IF(parent, cx->isInsideCurrentCompartment(parent));
-#ifdef JSGC_GENERATIONAL
-    JS_ASSERT_IF(metadata && cx->hasNursery(), !cx->nursery().isInside(metadata));
-#endif
 
     InitialShapeSet &table = cx->compartment()->initialShapes;
 
     if (!table.initialized() && !table.init())
         return nullptr;
 
     typedef InitialShapeEntry::Lookup Lookup;
     DependentAddPtr<InitialShapeSet>
@@ -1656,20 +1664,16 @@ NewObjectCache::invalidateEntriesForShap
 
 /* static */ void
 EmptyShape::insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto)
 {
     InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto),
                                      shape->getObjectParent(), shape->getObjectMetadata(),
                                      shape->numFixedSlots(), shape->getObjectFlags());
 
-    /* Bug 929547 - we do not rekey based on metadata object moves */
-    DebugOnly<JSObject *> metadata = shape->getObjectMetadata();
-    JS_ASSERT_IF(metadata, !gc::IsInsideNursery(cx->compartment()->runtimeFromAnyThread(), metadata));
-
     InitialShapeSet::Ptr p = cx->compartment()->initialShapes.lookup(lookup);
     JS_ASSERT(p);
 
     InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p);
 
     /* The new shape had better be rooted at the old one. */
 #ifdef DEBUG
     Shape *nshape = shape;