Backed out changeset 73ea3aa3ccec
authorNick Fitzgerald <fitzgen@gmail.com>
Wed, 19 Nov 2014 10:00:02 -0800
changeset 216514 3f5f0844f2f45389a3cd884e2f69e748d4219c9f
parent 216513 577bc6a54c1daa6060c960de2ad083a8d817c877
child 216515 82967a14f25fd37e336ee7d76954cc99a1ae6e44
push idunknown
push userunknown
push dateunknown
milestone36.0a1
backs out73ea3aa3ccec755656cd0f40a5491eca05a492d3
Backed out changeset 73ea3aa3ccec
js/public/Debug.h
js/public/UbiNode.h
js/src/jit-test/tests/debug/Memory-takeCensus-04.js
js/src/jit-test/tests/debug/Memory-takeCensus-05.js
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/DebuggerMemory.cpp
js/src/vm/UbiNode.cpp
--- a/js/public/Debug.h
+++ b/js/public/Debug.h
@@ -245,31 +245,29 @@ class BuilderOrigin : public Builder {
   public:
     BuilderOrigin(JSContext *cx, js::Debugger *debugger_)
       : Builder(cx, debugger_)
     { }
 
     JSObject *unwrap(Object &object) { return unwrapAny(object); }
 };
 
-
 
 // Finding the size of blocks allocated with malloc
 // ------------------------------------------------
 //
 // Debugger.Memory wants to be able to report how many bytes items in memory are
 // consuming. To do this, it needs a function that accepts a pointer to a block,
 // and returns the number of bytes allocated to that block. SpiderMonkey itself
 // doesn't know which function is appropriate to use, but the embedding does.
 
 // Tell Debuggers in |runtime| to use |mallocSizeOf| to find the size of
 // malloc'd blocks.
 void SetDebuggerMallocSizeOf(JSRuntime *runtime, mozilla::MallocSizeOf mallocSizeOf);
 
-
 
 // Handlers for observing Promises
 // -------------------------------
 //
 // The Debugger wants to observe behavior of promises, which are implemented by
 // Gecko with webidl and which SpiderMonkey knows nothing about. On the other
 // hand, Gecko knows nothing about which (if any) debuggers are observing a
 // promise's global. The compromise is that Gecko is responsible for calling
@@ -289,19 +287,13 @@ onNewPromise(JSContext *cx, HandleObject
 // promise, in which case neither promise has settled yet.
 //
 // It is Gecko's responsibility to ensure that this is never called on the same
 // promise more than once (because a promise can only make the transition from
 // unsettled to settled once).
 JS_PUBLIC_API(void)
 onPromiseSettled(JSContext *cx, HandleObject promise);
 
-
-
-// Return true if the given value is a Debugger object, false otherwise.
-JS_PUBLIC_API(bool)
-IsDebugger(JS::Value val);
-
 } // namespace dbg
 } // namespace JS
 
 
 #endif /* js_Debug_h */
--- a/js/public/UbiNode.h
+++ b/js/public/UbiNode.h
@@ -5,27 +5,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_UbiNode_h
 #define js_UbiNode_h
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 
 #include "jspubtd.h"
 
 #include "js/GCAPI.h"
 #include "js/HashTable.h"
 #include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
-#include "js/Vector.h"
 
 // JS::ubi::Node
 //
 // JS::ubi::Node is a pointer-like type designed for internal use by heap
 // analysis tools. A ubi::Node can refer to:
 //
 // - a JS value, like a string, object, or symbol;
 // - an internal SpiderMonkey structure, like a shape or a scope chain object
@@ -136,18 +134,16 @@
 //
 // If this restriction prevents us from implementing interesting tools, we may
 // teach the GC how to root ubi::Nodes, fix up hash tables that use them as
 // keys, etc.
 
 namespace JS {
 namespace ubi {
 
-using mozilla::Maybe;
-
 class Edge;
 class EdgeRange;
 
 // The base class implemented by each ubi::Node referent type. Subclasses must
 // not add data members to this class.
 class Base {
     friend class Node;
 
@@ -421,114 +417,18 @@ class EdgeRange {
     virtual void popFront() = 0;
 
   private:
     EdgeRange(const EdgeRange &) MOZ_DELETE;
     EdgeRange &operator=(const EdgeRange &) MOZ_DELETE;
 };
 
 
-// A dumb Edge concrete class. All but the most essential members have the
-// default behavior.
-class SimpleEdge : public Edge {
-    SimpleEdge(SimpleEdge &) MOZ_DELETE;
-    SimpleEdge &operator=(const SimpleEdge &) MOZ_DELETE;
-
-  public:
-    SimpleEdge() : Edge() { }
-
-    // Construct an initialized SimpleEdge, taking ownership of |name|.
-    SimpleEdge(char16_t *name, const Node &referent) {
-        this->name = name;
-        this->referent = referent;
-    }
-    ~SimpleEdge() {
-        js_free(const_cast<char16_t *>(name));
-    }
-
-    // Move construction and assignment.
-    SimpleEdge(SimpleEdge &&rhs) {
-        name = rhs.name;
-        referent = rhs.referent;
-
-        rhs.name = nullptr;
-    }
-    SimpleEdge &operator=(SimpleEdge &&rhs) {
-        MOZ_ASSERT(&rhs != this);
-        this->~SimpleEdge();
-        new(this) SimpleEdge(mozilla::Move(rhs));
-        return *this;
-    }
-};
-
-typedef mozilla::Vector<SimpleEdge, 8, js::TempAllocPolicy> SimpleEdgeVector;
-
-
-// RootList is a class that can be pointed to by a |ubi::Node|, creating a
-// fictional root-of-roots which has edges to every GC root in the JS
-// runtime. Having a single root |ubi::Node| is useful for algorithms written
-// with the assumption that there aren't multiple roots (such as computing
-// dominator trees) and you want a single point of entry. It also ensures that
-// the roots themselves get visited by |ubi::BreadthFirst| (they would otherwise
-// only be used as starting points).
-//
-// RootList::init itself causes a minor collection, but once the list of roots
-// has been created, GC must not occur, as the referent ubi::Nodes are not
-// stable across GC. The init calls emplace |gcp|'s AutoCheckCannotGC, whose
-// lifetime must extend at least as long as the RootList itself.
-//
-// Example usage:
-//
-//    {
-//        mozilla::Maybe<JS::AutoCheckCannotGC> maybeNoGC;
-//        JS::ubi::RootList rootList(cx, maybeNoGC);
-//        if (!rootList.init(cx))
-//            return false;
-//
-//        // The AutoCheckCannotGC is guaranteed to exist if init returned true.
-//        MOZ_ASSERT(maybeNoGC.isSome());
-//
-//        JS::ubi::Node root(&rootList);
-//
-//        ...
-//    }
-class MOZ_STACK_CLASS RootList {
-    Maybe<AutoCheckCannotGC> &noGC;
-
-  public:
-    SimpleEdgeVector edges;
-    bool             wantNames;
-
-    RootList(JSContext *cx, Maybe<AutoCheckCannotGC> &noGC, bool wantNames = false);
-
-    // Find all GC roots.
-    bool init(JSContext *cx);
-    // Find only GC roots in the provided set of |Zone|s.
-    bool init(JSContext *cx, ZoneSet &debuggees);
-    // Find only GC roots in the given Debugger object's set of debuggee zones.
-    bool init(JSContext *cx, HandleObject debuggees);
-};
-
-
 // Concrete classes for ubi::Node referent types.
 
-template<>
-struct Concrete<RootList> : public Base {
-    EdgeRange *edges(JSContext *cx, bool wantNames) const MOZ_OVERRIDE;
-    const char16_t *typeName() const MOZ_OVERRIDE { return concreteTypeName; }
-
-  protected:
-    explicit Concrete(RootList *ptr) : Base(ptr) { }
-    RootList &get() const { return *static_cast<RootList *>(ptr); }
-
-  public:
-    static const char16_t concreteTypeName[];
-    static void construct(void *storage, RootList *ptr) { new (storage) Concrete(ptr); }
-};
-
 // A reusable ubi::Concrete specialization base class for types supported by
 // JS_TraceChildren.
 template<typename Referent>
 class TracerConcrete : public Base {
     const char16_t *typeName() const MOZ_OVERRIDE { return concreteTypeName; }
     EdgeRange *edges(JSContext *, bool wantNames) const MOZ_OVERRIDE;
     JS::Zone *zone() const MOZ_OVERRIDE;
 
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-takeCensus-04.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// Test that Debugger.Memory.prototype.takeCensus finds GC roots that are on the
-// stack.
-
-var g = newGlobal();
-var dbg = new Debugger(g);
-
-g.eval(`
-  function withTypedArrayOnStack(f) {
-    (function () {
-      var onStack = new Int8Array();
-      f();
-    }())
-  }
-`);
-
-assertEq("Int8Array" in dbg.memory.takeCensus().objects, false,
-         "There shouldn't exist any typed arrays in the census.");
-
-var typedArrayCount;
-g.withTypedArrayOnStack(() => {
-  typedArrayCount = dbg.memory.takeCensus().objects.Int8Array.count;
-});
-
-assertEq(typedArrayCount, 1,
-         "Should have one typed array in the census, because there " +
-         "was one on the stack.");
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-takeCensus-05.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// Test that Debugger.Memory.prototype.takeCensus finds cross compartment
-// wrapper GC roots.
-
-var g = newGlobal();
-var dbg = new Debugger(g);
-
-assertEq("Int8Array" in dbg.memory.takeCensus().objects, false,
-         "There shouldn't exist any typed arrays in the census.");
-
-this.ccw = g.eval("new Int8Array()");
-
-assertEq(dbg.memory.takeCensus().objects.Int8Array.count, 1,
-         "Should have one typed array in the census, because there " +
-         "is one cross-compartment wrapper.");
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -7249,21 +7249,9 @@ JS::dbg::onNewPromise(JSContext *cx, Han
     Debugger::slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
 }
 
 JS_PUBLIC_API(void)
 JS::dbg::onPromiseSettled(JSContext *cx, HandleObject promise)
 {
     AssertIsPromise(cx, promise);
     Debugger::slowPathPromiseHook(cx, Debugger::OnPromiseSettled, promise);
-
-JS_PUBLIC_API(bool)
-JS::dbg::IsDebugger(JS::Value val)
-{
-    if (!val.isObject())
-        return false;
-
-    JSObject &obj = val.toObject();
-    if (obj.getClass() != &Debugger::jsclass)
-        return false;
-
-    return js::Debugger::fromJSObject(&obj) != nullptr;
-}
+}
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -172,17 +172,16 @@ typedef JSObject Env;
 
 class Debugger : private mozilla::LinkedListElement<Debugger>
 {
     friend class Breakpoint;
     friend class DebuggerMemory;
     friend class SavedStacks;
     friend class mozilla::LinkedListElement<Debugger>;
     friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj);
-    friend bool (::JS::dbg::IsDebugger)(JS::Value val);
     friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
     friend void JS::dbg::onNewPromise(JSContext *cx, HandleObject promise);
     friend void JS::dbg::onPromiseSettled(JSContext *cx, HandleObject promise);
 
   public:
     enum Hook {
         OnDebuggerStatement,
         OnExceptionUnwind,
@@ -482,18 +481,16 @@ class Debugger : private mozilla::Linked
     inline const js::HeapPtrNativeObject &toJSObject() const;
     inline js::HeapPtrNativeObject &toJSObjectRef();
     static inline Debugger *fromJSObject(JSObject *obj);
     static Debugger *fromChildJSObject(JSObject *obj);
 
     bool hasMemory() const;
     DebuggerMemory &memory() const;
 
-    GlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }
-
     /*********************************** Methods for interaction with the GC. */
 
     /*
      * A Debugger object is live if:
      *   * the Debugger JSObject is live (Debugger::trace handles this case); OR
      *   * it is in the middle of dispatching an event (the event dispatching
      *     code roots it in this case); OR
      *   * it is enabled, and it is debugging at least one live compartment,
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -29,17 +29,16 @@
 using namespace js;
 
 using JS::ubi::BreadthFirst;
 using JS::ubi::Edge;
 using JS::ubi::Node;
 
 using mozilla::Maybe;
 using mozilla::Move;
-using mozilla::Nothing;
 
 /* static */ DebuggerMemory *
 DebuggerMemory::create(JSContext *cx, Debugger *dbg)
 {
 
     Value memoryProto = dbg->object->getReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO);
     RootedNativeObject memory(cx, NewNativeObjectWithGivenProto(cx, &class_,
                                                                 &memoryProto.toObject(), nullptr));
@@ -750,54 +749,44 @@ typedef BreadthFirst<DefaultCensusHandle
 
 } // namespace dbg
 } // namespace js
 
 bool
 DebuggerMemory::takeCensus(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGGER_MEMORY(cx, argc, vp, "Debugger.Memory.prototype.census", args, memory);
+    Debugger *debugger = memory->getDebugger();
 
     dbg::Census census(cx);
     if (!census.init())
         return false;
-
     dbg::DefaultCensusHandler handler(census);
     if (!handler.init(census))
         return false;
 
-    Debugger *dbg = memory->getDebugger();
+    {
+        JS::AutoCheckCannotGC noGC;
 
-    // Populate census.debuggeeZones and ensure that all of our debuggee globals
-    // are rooted so that they are visible in the RootList.
-    JS::AutoObjectVector debuggees(cx);
-    for (GlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
-        if (!census.debuggeeZones.put(r.front()->zone()) ||
-            !debuggees.append(static_cast<JSObject *>(r.front())))
-        {
-            return false;
-        }
-    }
-
-    {
-        Maybe<JS::AutoCheckCannotGC> maybeNoGC;
-        JS::ubi::RootList rootList(cx, maybeNoGC);
-        if (!rootList.init(cx, census.debuggeeZones))
-            return false;
-
-        dbg::DefaultCensusTraversal traversal(cx, handler, maybeNoGC.ref());
+        dbg::DefaultCensusTraversal traversal(cx, handler, noGC);
         if (!traversal.init())
             return false;
         traversal.wantNames = false;
 
-        if (!traversal.addStart(JS::ubi::Node(&rootList)) ||
-            !traversal.traverse())
-        {
+        // Walk the debuggee compartments, using it to set the starting points
+        // (the debuggee globals) for the traversal, and to populate
+        // census.debuggeeZones.
+        for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
+            if (!census.debuggeeZones.put(r.front()->zone()) ||
+                !traversal.addStart(static_cast<JSObject *>(r.front())))
+                return false;
+        }
+
+        if (!traversal.traverse())
             return false;
-        }
     }
 
     return handler.report(census, args.rval());
 }
 
 
 
 /* Debugger.Memory property and method tables. */
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -11,40 +11,33 @@
 #include "mozilla/Scoped.h"
 
 #include "jscntxt.h"
 #include "jsinfer.h"
 #include "jsobj.h"
 #include "jsscript.h"
 
 #include "jit/IonCode.h"
-#include "js/Debug.h"
 #include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
-#include "vm/GlobalObject.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/Symbol.h"
 
 #include "jsobjinlines.h"
-#include "vm/Debugger-inl.h"
 
-using mozilla::Some;
 using JS::HandleValue;
 using JS::Value;
-using JS::ZoneSet;
 using JS::ubi::Concrete;
 using JS::ubi::Edge;
 using JS::ubi::EdgeRange;
 using JS::ubi::Node;
-using JS::ubi::SimpleEdge;
-using JS::ubi::SimpleEdgeVector;
 using JS::ubi::TracerConcrete;
 using JS::ubi::TracerConcreteWithCompartment;
 
 // All operations on null ubi::Nodes crash.
 const char16_t *Concrete<void>::typeName() const          { MOZ_CRASH("null ubi::Node"); }
 EdgeRange *Concrete<void>::edges(JSContext *, bool) const { MOZ_CRASH("null ubi::Node"); }
 JS::Zone *Concrete<void>::zone() const                    { MOZ_CRASH("null ubi::Node"); }
 JSCompartment *Concrete<void>::compartment() const        { MOZ_CRASH("null ubi::Node"); }
@@ -105,16 +98,52 @@ Node::exposeToJS() const
         v.setSymbol(as<JS::Symbol>());
     } else {
         v.setUndefined();
     }
 
     return v;
 }
 
+// A dumb Edge concrete class. All but the most essential members have the
+// default behavior.
+class SimpleEdge : public Edge {
+    SimpleEdge(SimpleEdge &) MOZ_DELETE;
+    SimpleEdge &operator=(const SimpleEdge &) MOZ_DELETE;
+
+  public:
+    SimpleEdge() : Edge() { }
+
+    // Construct an initialized SimpleEdge, taking ownership of |name|.
+    SimpleEdge(char16_t *name, const Node &referent) {
+        this->name = name;
+        this->referent = referent;
+    }
+    ~SimpleEdge() {
+        js_free(const_cast<char16_t *>(name));
+    }
+
+    // Move construction and assignment.
+    SimpleEdge(SimpleEdge &&rhs) {
+        name = rhs.name;
+        referent = rhs.referent;
+
+        rhs.name = nullptr;
+    }
+    SimpleEdge &operator=(SimpleEdge &&rhs) {
+        MOZ_ASSERT(&rhs != this);
+        this->~SimpleEdge();
+        new(this) SimpleEdge(mozilla::Move(rhs));
+        return *this;
+    }
+};
+
+
+typedef mozilla::Vector<SimpleEdge, 8, js::TempAllocPolicy> SimpleEdgeVector;
+
 
 // A JSTracer subclass that adds a SimpleEdge to a Vector for each edge on
 // which it is invoked.
 class SimpleEdgeVectorTracer : public JSTracer {
     // The vector to which we add SimpleEdges.
     SimpleEdgeVector *vec;
 
     // True if we should populate the edge's names.
@@ -251,105 +280,8 @@ template class TracerConcrete<JS::Symbol
 template class TracerConcreteWithCompartment<JSScript>;
 template class TracerConcrete<js::LazyScript>;
 template class TracerConcrete<js::jit::JitCode>;
 template class TracerConcreteWithCompartment<js::Shape>;
 template class TracerConcreteWithCompartment<js::BaseShape>;
 template class TracerConcrete<js::types::TypeObject>;
 }
 }
-
-
-namespace JS {
-namespace ubi {
-
-RootList::RootList(JSContext *cx, Maybe<AutoCheckCannotGC> &noGC, bool wantNames /* = false */)
-  : noGC(noGC),
-    edges(cx),
-    wantNames(wantNames)
-{ }
-
-
-bool
-RootList::init(JSContext *cx)
-{
-    SimpleEdgeVectorTracer tracer(cx, &edges, wantNames);
-    JS_TraceRuntime(&tracer);
-    if (!tracer.okay)
-        return false;
-    noGC.emplace(cx->runtime());
-    return true;
-}
-
-bool
-RootList::init(JSContext *cx, ZoneSet &debuggees)
-{
-    SimpleEdgeVector allRootEdges(cx);
-    SimpleEdgeVectorTracer tracer(cx, &allRootEdges, wantNames);
-
-    JS_TraceRuntime(&tracer);
-    if (!tracer.okay)
-        return false;
-    JS_TraceIncomingCCWs(&tracer, debuggees);
-    if (!tracer.okay)
-        return false;
-
-    for (SimpleEdgeVector::Range r = allRootEdges.all(); !r.empty(); r.popFront()) {
-        SimpleEdge &edge = r.front();
-        Zone *zone = edge.referent.zone();
-        if (zone && !debuggees.has(zone))
-            continue;
-        if (!edges.append(mozilla::Move(edge)))
-            return false;
-    }
-
-    noGC.emplace(cx->runtime());
-    return true;
-}
-
-bool
-RootList::init(JSContext *cx, HandleObject debuggees)
-{
-    MOZ_ASSERT(debuggees && JS::dbg::IsDebugger(ObjectValue(*debuggees)));
-    js::Debugger *dbg = js::Debugger::fromJSObject(debuggees);
-
-    ZoneSet debuggeeZones;
-    if (!debuggeeZones.init())
-        return false;
-
-    for (js::GlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
-        if (!debuggeeZones.put(r.front()->zone()))
-            return false;
-    }
-
-    return init(cx, debuggeeZones);
-}
-
-// An EdgeRange concrete class that holds a pre-existing vector of SimpleEdges.
-class PreComputedEdgeRange : public EdgeRange {
-    SimpleEdgeVector &edges;
-    size_t           i;
-
-    void settle() {
-        front_ = i < edges.length() ? &edges[i] : nullptr;
-    }
-
-  public:
-    explicit PreComputedEdgeRange(JSContext *cx, SimpleEdgeVector &edges)
-      : edges(edges),
-        i(0)
-    {
-        settle();
-    }
-
-    void popFront() MOZ_OVERRIDE { i++; settle(); }
-};
-
-const char16_t Concrete<RootList>::concreteTypeName[] = MOZ_UTF16("RootList");
-
-EdgeRange *
-Concrete<RootList>::edges(JSContext *cx, bool wantNames) const {
-    MOZ_ASSERT_IF(wantNames, get().wantNames);
-    return js_new<PreComputedEdgeRange>(cx, get().edges);
-}
-
-} // namespace ubi
-} // namespace JS