Bug 727135 - Make jsid marking interfaces indirect; r=billm
authorTerrence Cole <terrence@mozilla.com>
Fri, 17 Feb 2012 17:27:37 -0800
changeset 87149 ece583b83508f0dda79f1e2ff63393150d9b0731
parent 87148 f0ac2d720c7b1e75b61a51ba31751b063c9a6e41
child 87150 1dcee8f1559a0dac6b9f49c5c052417f03e754df
push id22083
push userbmo@edmorley.co.uk
push dateSat, 18 Feb 2012 11:19:19 +0000
treeherdermozilla-central@20478b673212 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs727135
milestone13.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 727135 - Make jsid marking interfaces indirect; r=billm This will allow the GC to move jsid referenced objects.
js/src/gc/Barrier.h
js/src/jsfriendapi.cpp
js/src/jsgc.cpp
js/src/jsgcmark.cpp
js/src/jsgcmark.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jswatchpoint.cpp
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -401,16 +401,17 @@ class HeapId
 
     inline HeapId &operator=(jsid id);
     inline HeapId &operator=(const HeapId &v);
 
     bool operator==(jsid id) const { return value == id; }
     bool operator!=(jsid id) const { return value != id; }
 
     jsid get() const { return value; }
+    jsid *unsafeGet() { return &value; }
     operator jsid() const { return value; }
 
   private:
     inline void pre();
     inline void post();
 
     HeapId(const HeapId &v);
 };
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -172,17 +172,17 @@ JS_FRIEND_API(JSBool)
 JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc)
 {
     return cx->compartment->wrap(cx, desc);
 }
 
 JS_FRIEND_API(void)
 JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape)
 {
-    MarkCycleCollectorChildren(trc, (const Shape *)shape);
+    MarkCycleCollectorChildren(trc, (Shape *)shape);
 }
 
 AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx
                                                  JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT)
   : cx(cx), oldCompartment(cx->compartment)
 {
     JS_GUARD_OBJECT_NOTIFIER_INIT;
 }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2261,17 +2261,17 @@ AutoGCRooter::trace(JSTracer *trc)
 
       case OBJECT:
         if (static_cast<AutoObjectRooter *>(this)->obj)
             MarkObjectRoot(trc, &static_cast<AutoObjectRooter *>(this)->obj,
                            "JS::AutoObjectRooter.obj");
         return;
 
       case ID:
-        MarkIdRoot(trc, static_cast<AutoIdRooter *>(this)->id_, "JS::AutoIdRooter.id_");
+        MarkIdRoot(trc, &static_cast<AutoIdRooter *>(this)->id_, "JS::AutoIdRooter.id_");
         return;
 
       case VALVECTOR: {
         AutoValueVector::VectorImpl &vector = static_cast<AutoValueVector *>(this)->vector;
         MarkValueRootRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
         return;
       }
 
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -56,17 +56,17 @@ PushMarkStack(GCMarker *gcmarker, JSObje
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSScript *thing);
 
 static inline void
-PushMarkStack(GCMarker *gcmarker, const Shape *thing);
+PushMarkStack(GCMarker *gcmarker, Shape *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
 
 /*** Object Marking ***/
@@ -259,55 +259,60 @@ MarkGCThingRoot(JSTracer *trc, void *thi
     if (!thing)
         return;
     MarkKind(trc, thing, GetGCThingTraceKind(thing));
 }
 
 /*** ID Marking ***/
 
 static inline void
-MarkIdInternal(JSTracer *trc, const jsid &id)
+MarkIdInternal(JSTracer *trc, jsid *id)
 {
-    if (JSID_IS_STRING(id))
-        MarkInternal(trc, JSID_TO_STRING(id));
-    else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
-        MarkInternal(trc, JSID_TO_OBJECT(id));
+    if (JSID_IS_STRING(*id)) {
+        JSString *str = JSID_TO_STRING(*id);
+        MarkInternal(trc, str);
+        *id = ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
+    } else if (JS_UNLIKELY(JSID_IS_OBJECT(*id))) {
+        JSObject *obj = JSID_TO_OBJECT(*id);
+        MarkInternal(trc, obj);
+        *id = OBJECT_TO_JSID(obj);
+    }
 }
 
 void
-MarkId(JSTracer *trc, const HeapId &id, const char *name)
+MarkId(JSTracer *trc, HeapId *id, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
-    MarkIdInternal(trc, id);
+    MarkIdInternal(trc, id->unsafeGet());
 }
 
 void
-MarkIdRoot(JSTracer *trc, const jsid &id, const char *name)
+MarkIdRoot(JSTracer *trc, jsid *id, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
     JS_SET_TRACING_NAME(trc, name);
     MarkIdInternal(trc, id);
 }
 
 void
 MarkIdRange(JSTracer *trc, size_t len, HeapId *vec, const char *name)
 {
     for (size_t i = 0; i < len; ++i) {
         JS_SET_TRACING_INDEX(trc, name, i);
-        MarkIdInternal(trc, vec[i]);
+        MarkIdInternal(trc, vec[i].unsafeGet());
     }
 }
 
 void
 MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
     for (size_t i = 0; i < len; ++i) {
         JS_SET_TRACING_INDEX(trc, name, i);
-        MarkIdInternal(trc, vec[i]);
+        MarkIdInternal(trc, &vec[i]);
     }
 }
 
 /*** Value Marking ***/
 
 static inline void
 MarkValueInternal(JSTracer *trc, Value *v)
 {
@@ -360,23 +365,16 @@ MarkValueRootRange(JSTracer *trc, size_t
 static void
 MarkObject(JSTracer *trc, const HeapPtr<GlobalObject, JSScript *> &thing, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkInternal(trc, thing.get());
 }
 
 void
-MarkShape(JSTracer *trc, HeapPtr<const Shape> *thing, const char *name)
-{
-    JS_SET_TRACING_NAME(trc, name);
-    MarkInternal(trc, const_cast<Shape *>(thing->get()));
-}
-
-void
 MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkValueInternal(trc, v);
 }
 
 void
 MarkCrossCompartmentValue(JSTracer *trc, HeapValue *v, const char *name)
@@ -455,20 +453,20 @@ PushMarkStack(GCMarker *gcmarker, JSScri
      * refer to other scripts only indirectly (like via nested functions) and
      * we cannot get to deep recursion.
      */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         MarkChildren(gcmarker, thing);
 }
 
 static void
-ScanShape(GCMarker *gcmarker, const Shape *shape);
+ScanShape(GCMarker *gcmarker, Shape *shape);
 
 static void
-PushMarkStack(GCMarker *gcmarker, const Shape *thing)
+PushMarkStack(GCMarker *gcmarker, Shape *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     /* We mark shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanShape(gcmarker, thing);
 }
 
@@ -481,22 +479,22 @@ PushMarkStack(GCMarker *gcmarker, BaseSh
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     /* We mark base shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanBaseShape(gcmarker, thing);
 }
 
 static void
-ScanShape(GCMarker *gcmarker, const Shape *shape)
+ScanShape(GCMarker *gcmarker, Shape *shape)
 {
   restart:
     PushMarkStack(gcmarker, shape->base());
 
-    jsid id = shape->maybePropid();
+    const HeapId &id = shape->propidRef();
     if (JSID_IS_STRING(id))
         PushMarkStack(gcmarker, JSID_TO_STRING(id));
     else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
         PushMarkStack(gcmarker, JSID_TO_OBJECT(id));
 
     shape = shape->previous();
     if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
         goto restart;
@@ -707,17 +705,17 @@ MarkChildren(JSTracer *trc, JSScript *sc
     if (script->hasAnyBreakpointsOrStepMode())
         script->markTrapClosures(trc);
 }
 
 static void
 MarkChildren(JSTracer *trc, Shape *shape)
 {
     MarkBaseShapeUnbarriered(trc, shape->base(), "base");
-    MarkId(trc, shape->maybePropid(), "propid");
+    MarkId(trc, &shape->propidRef(), "propid");
     if (shape->previous())
         MarkShape(trc, &shape->previousRef(), "parent");
 }
 
 static inline void
 MarkBaseShapeGetterSetter(JSTracer *trc, BaseShape *base)
 {
     if (base->hasGetterObject())
@@ -770,22 +768,22 @@ MarkCycleCollectorChildren(JSTracer *trc
  * This function is used by the cycle collector to trace through a
  * shape. The cycle collector does not care about shapes or base
  * shapes, so those are not marked. Instead, any shapes or base shapes
  * that are encountered have their children marked. Stack space is
  * bounded. If two shapes in a row have the same parent pointer, the
  * parent pointer will only be marked once.
  */
 void
-MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape)
+MarkCycleCollectorChildren(JSTracer *trc, Shape *shape)
 {
     JSObject *prevParent = NULL;
     do {
         MarkCycleCollectorChildren(trc, shape->base(), &prevParent);
-        MarkId(trc, shape->maybePropid(), "propid");
+        MarkId(trc, &shape->propidRef(), "propid");
         shape = shape->previous();
     } while (shape);
 }
 
 static void
 ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
 {
     if (!type->singleton) {
@@ -818,17 +816,17 @@ ScanTypeObject(GCMarker *gcmarker, types
 static void
 MarkChildren(JSTracer *trc, types::TypeObject *type)
 {
     if (!type->singleton) {
         unsigned count = type->getPropertyCount();
         for (unsigned i = 0; i < count; i++) {
             types::Property *prop = type->getProperty(i);
             if (prop)
-                MarkId(trc, prop->id, "type_prop");
+                MarkId(trc, &prop->id, "type_prop");
         }
     }
 
     if (type->proto)
         MarkObject(trc, &type->proto, "type_proto");
 
     if (type->singleton && !type->lazy())
         MarkObject(trc, &type->singleton, "type_singleton");
--- a/js/src/jsgcmark.h
+++ b/js/src/jsgcmark.h
@@ -78,20 +78,20 @@ void
 MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind);
 
 void
 MarkGCThingRoot(JSTracer *trc, void *thing, const char *name);
 
 /*** ID Marking ***/
 
 void
-MarkId(JSTracer *trc, const HeapId &id, const char *name);
+MarkId(JSTracer *trc, HeapId *id, const char *name);
 
 void
-MarkIdRoot(JSTracer *trc, const jsid &id, const char *name);
+MarkIdRoot(JSTracer *trc, jsid *id, const char *name);
 
 void
 MarkIdRange(JSTracer *trc, size_t len, js::HeapId *vec, const char *name);
 
 void
 MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name);
 
 /*** Value Marking ***/
@@ -111,20 +111,16 @@ MarkValueRootRange(JSTracer *trc, size_t
 inline void
 MarkValueRootRange(JSTracer *trc, Value *begin, Value *end, const char *name)
 {
     MarkValueRootRange(trc, end - begin, begin, name);
 }
 
 /*** Special Cases ***/
 
-/* TypeNewObject contains a HeapPtr<const Shape> that needs a unique cast. */
-void
-MarkShape(JSTracer *trc, HeapPtr<const Shape> *thing, const char *name);
-
 /* Direct value access used by the write barriers and the methodjit */
 void
 MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
 
 /*
  * Mark a value that may be in a different compartment from the compartment
  * being GC'd. (Although it won't be marked if it's in the wrong compartment.)
  */
@@ -139,17 +135,17 @@ void
 MarkChildren(JSTracer *trc, JSObject *obj);
 
 /*
  * Trace through the shape and any shapes it contains to mark
  * non-shape children. This is exposed to the JS API as
  * JS_TraceShapeCycleCollectorChildren.
  */
 void
-MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape);
+MarkCycleCollectorChildren(JSTracer *trc, Shape *shape);
 
 void
 PushArena(GCMarker *gcmarker, ArenaHeader *aheader);
 
 /*** Generic ***/
 
 /*
  * The Mark() functions interface should only be used by code that must be
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2613,17 +2613,17 @@ struct types::ObjectTableKey
     jsid *ids;
     uint32_t nslots;
     uint32_t nfixed;
     JSObject *proto;
 
     typedef JSObject * Lookup;
 
     static inline uint32_t hash(JSObject *obj) {
-        return (uint32_t) (JSID_BITS(obj->lastProperty()->propid()) ^
+        return (uint32_t) (JSID_BITS(obj->lastProperty()->propid().get()) ^
                          obj->slotSpan() ^ obj->numFixedSlots() ^
                          ((uint32_t)(size_t)obj->getProto() >> 2));
     }
 
     static inline bool match(const ObjectTableKey &v, JSObject *obj) {
         if (obj->slotSpan() != v.nslots ||
             obj->numFixedSlots() != v.nfixed ||
             obj->getProto() != v.proto) {
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -663,17 +663,17 @@ struct TypeNewScript
 
     /* Allocation kind to use for newly constructed objects. */
     gc::AllocKind allocKind;
 
     /*
      * Shape to use for newly constructed objects. Reflects all definite
      * properties the object will have.
      */
-    HeapPtr<const Shape> shape;
+    HeapPtrShape  shape;
 
     /*
      * Order in which properties become initialized. We need this in case a
      * scripted setter is added to one of the object's prototypes while it is
      * in the middle of being initialized, so we can walk the stack and fixup
      * any objects which look for in-progress objects which were prematurely
      * set with their final shape. Initialization can traverse stack frames,
      * in which case FRAME_PUSH/FRAME_POP are used.
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1057,17 +1057,17 @@ JSObject::rollbackProperties(JSContext *
     }
 }
 
 Shape *
 JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *newShape)
 {
     JS_ASSERT_IF(oldShape != lastProperty(),
                  inDictionaryMode() &&
-                 nativeLookup(cx, oldShape->maybePropid()) == oldShape);
+                 nativeLookup(cx, oldShape->propidRef()) == oldShape);
 
     JSObject *self = this;
 
     if (!inDictionaryMode()) {
         RootObject selfRoot(cx, &self);
         RootShape newRoot(cx, &newShape);
         if (!toDictionaryMode(cx))
             return NULL;
@@ -1081,17 +1081,17 @@ JSObject::replaceWithNewEquivalentShape(
         if (!newShape)
             return NULL;
         new (newShape) Shape(oldShape->base()->unowned(), 0);
     }
 
     PropertyTable &table = self->lastProperty()->table();
     Shape **spp = oldShape->isEmptyShape()
                   ? NULL
-                  : table.search(oldShape->maybePropid(), false);
+                  : table.search(oldShape->propidRef(), false);
 
     /*
      * Splice the new shape into the same position as the old shape, preserving
      * enumeration order (see bug 601399).
      */
     StackShape nshape(oldShape);
     newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp);
 
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -769,18 +769,22 @@ struct Shape : public js::gc::Cell
 
     void incrementNumLinearSearches() {
         uint32_t count = numLinearSearches();
         JS_ASSERT(count < LINEAR_SEARCHES_MAX);
         slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK;
         slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
     }
 
-    jsid propid() const { JS_ASSERT(!isEmptyShape()); return maybePropid(); }
-    const HeapId &maybePropid() const { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
+    const HeapId &propid() const {
+        JS_ASSERT(!isEmptyShape());
+        JS_ASSERT(!JSID_IS_VOID(propid_));
+        return propid_;
+    }
+    HeapId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
 
     int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); }
     int16_t maybeShortid() const { return shortid_; }
 
     /*
      * If SHORTID is set in shape->flags, we use shape->shortid rather
      * than id when calling shape's getter or setter.
      */
@@ -994,17 +998,17 @@ struct StackShape
     {
         JS_ASSERT(base);
         JS_ASSERT(!JSID_IS_VOID(propid));
         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
     }
 
     StackShape(const Shape *shape)
       : base(shape->base()->unowned()),
-        propid(shape->maybePropid()),
+        propid(const_cast<Shape *>(shape)->propidRef()),
         slot_(shape->slotInfo & Shape::SLOT_MASK),
         attrs(shape->attrs),
         flags(shape->flags),
         shortid(shape->shortid_)
     {}
 
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
@@ -1080,27 +1084,27 @@ Shape::search(JSContext *cx, Shape *star
         if (start->isBigEnoughForAPropertyTable()) {
             RootShape startRoot(cx, &start);
             RootId idRoot(cx, &id);
             if (start->hashify(cx)) {
                 Shape **spp = start->table().search(id, adding);
                 return SHAPE_FETCH(spp);
             }
         }
-        /* 
+        /*
          * No table built -- there weren't enough entries, or OOM occurred.
          * Don't increment numLinearSearches, to keep hasTable() false.
          */
         JS_ASSERT(!start->hasTable());
     } else {
         start->incrementNumLinearSearches();
     }
 
     for (Shape *shape = start; shape; shape = shape->parent) {
-        if (shape->maybePropid() == id)
+        if (shape->propidRef() == id)
             return shape;
     }
 
     return NULL;
 }
 
 } // namespace js
 
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -205,39 +205,43 @@ WatchpointMap::markIteratively(JSTracer 
                 HeapPtrObject tmp(e.key.object);
                 MarkObject(trc, &tmp, "held Watchpoint object");
                 JS_ASSERT(tmp == e.key.object);
                 marked = true;
             }
 
             const HeapId &id = e.key.id;
             JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
-            MarkId(trc, id, "WatchKey::id");
+            HeapId tmp(id.get());
+            MarkId(trc, &tmp, "WatchKey::id");
+            JS_ASSERT(tmp.get() == id.get());
 
             if (e.value.closure && IsAboutToBeFinalized(e.value.closure)) {
                 MarkObject(trc, &e.value.closure, "Watchpoint::closure");
                 marked = true;
             }
         }
     }
     return marked;
 }
 
 void
 WatchpointMap::markAll(JSTracer *trc)
 {
     for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
         Map::Entry &e = r.front();
-        HeapPtrObject tmp(e.key.object);
-        MarkObject(trc, &tmp, "held Watchpoint object");
-        JS_ASSERT(tmp == e.key.object);
+        HeapPtrObject tmpObj(e.key.object);
+        MarkObject(trc, &tmpObj, "held Watchpoint object");
+        JS_ASSERT(tmpObj == e.key.object);
 
         const HeapId &id = e.key.id;
         JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
-        MarkId(trc, id, "WatchKey::id");
+        HeapId tmpId(id.get());
+        MarkId(trc, &tmpId, "WatchKey::id");
+        JS_ASSERT(tmpId.get() == id.get());
 
         MarkObject(trc, &e.value.closure, "Watchpoint::closure");
     }
 }
 
 void
 WatchpointMap::sweepAll(JSRuntime *rt)
 {