js/src/shell/jsheaptools.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/shell/jsheaptools.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,575 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "shell/jsheaptools.h"
    1.11 +
    1.12 +#include "mozilla/Move.h"
    1.13 +
    1.14 +#include <string.h>
    1.15 +
    1.16 +#include "jsalloc.h"
    1.17 +#include "jsapi.h"
    1.18 +#include "jscntxt.h"
    1.19 +#include "jscompartment.h"
    1.20 +#include "jsobj.h"
    1.21 +#include "jsprf.h"
    1.22 +
    1.23 +#include "jsobjinlines.h"
    1.24 +
    1.25 +using namespace js;
    1.26 +
    1.27 +using mozilla::Move;
    1.28 +
    1.29 +#ifdef DEBUG
    1.30 +
    1.31 +
    1.32 +/*** class HeapReverser **************************************************************************/
    1.33 +
    1.34 +/*
    1.35 + * A class for constructing a map of the JavaScript heap, with all
    1.36 + * reference edges reversed.
    1.37 + *
    1.38 + * Unfortunately, it's not possible to build the results for findReferences
    1.39 + * while visiting things solely in the order that JS_TraceRuntime and
    1.40 + * JS_TraceChildren reaches them. For example, as you work outward from the
    1.41 + * roots, suppose an edge from thing T reaches a "gray" thing G --- G being gray
    1.42 + * because you're still in the midst of traversing its descendants. At this
    1.43 + * point, you don't know yet whether G will be a referrer or not, and so you
    1.44 + * can't tell whether T should be a referrer either. And you won't visit T
    1.45 + * again.
    1.46 + *
    1.47 + * So we take a brute-force approach. We reverse the entire graph, and then walk
    1.48 + * outward from |target| to the representable objects that refer to it, stopping
    1.49 + * at such objects.
    1.50 + */
    1.51 +
    1.52 +/*
    1.53 + * A JSTracer that produces a map of the heap with edges reversed.
    1.54 + *
    1.55 + * HeapReversers must be allocated in a stack frame. (They are derived from
    1.56 + * CustomAutoRooter, and those must be allocated and destroyed in a stack-like
    1.57 + * order.)
    1.58 + *
    1.59 + * HeapReversers keep all the roots they find in their traversal alive until
    1.60 + * they are destroyed. So you don't need to worry about nodes going away while
    1.61 + * you're using them.
    1.62 + */
    1.63 +class HeapReverser : public JSTracer, public JS::CustomAutoRooter
    1.64 +{
    1.65 +  public:
    1.66 +    struct Edge;
    1.67 +
    1.68 +    /* Metadata for a given Cell we have visited. */
    1.69 +    class Node {
    1.70 +      public:
    1.71 +        Node() { }
    1.72 +        Node(JSGCTraceKind kind)
    1.73 +          : kind(kind), incoming(), marked(false) { }
    1.74 +
    1.75 +        /*
    1.76 +         * Move constructor and move assignment. These allow us to store our
    1.77 +         * incoming edge Vector in the hash table: Vectors support moves, but
    1.78 +         * not assignments or copy construction.
    1.79 +         */
    1.80 +        Node(Node &&rhs)
    1.81 +          : kind(rhs.kind), incoming(Move(rhs.incoming)), marked(rhs.marked) { }
    1.82 +        Node &operator=(Node &&rhs) {
    1.83 +            MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
    1.84 +            this->~Node();
    1.85 +            new(this) Node(Move(rhs));
    1.86 +            return *this;
    1.87 +        }
    1.88 +
    1.89 +        void trace(JSTracer *trc) {
    1.90 +            for (Edge *e = incoming.begin(); e != incoming.end(); e++)
    1.91 +                e->trace(trc);
    1.92 +        }
    1.93 +
    1.94 +        /* What kind of Cell this is. */
    1.95 +        JSGCTraceKind kind;
    1.96 +
    1.97 +        /*
    1.98 +         * A vector of this Cell's incoming edges.
    1.99 +         * This must use SystemAllocPolicy because HashMap requires its elements to
   1.100 +         * be constructible with no arguments.
   1.101 +         */
   1.102 +        Vector<Edge, 0, SystemAllocPolicy> incoming;
   1.103 +
   1.104 +        /* A mark bit, for other traversals. */
   1.105 +        bool marked;
   1.106 +
   1.107 +      private:
   1.108 +        Node(const Node &) MOZ_DELETE;
   1.109 +        Node &operator=(const Node &) MOZ_DELETE;
   1.110 +    };
   1.111 +
   1.112 +    /* Metadata for a heap edge we have traversed. */
   1.113 +    struct Edge {
   1.114 +      public:
   1.115 +        Edge(char *name, void *origin) : name(name), origin(origin) { }
   1.116 +        ~Edge() { js_free(name); }
   1.117 +
   1.118 +        /*
   1.119 +         * Move constructor and move assignment. These allow us to live in
   1.120 +         * Vectors without needing to copy our name string when the vector is
   1.121 +         * resized.
   1.122 +         */
   1.123 +        Edge(Edge &&rhs) : name(rhs.name), origin(rhs.origin) {
   1.124 +            rhs.name = nullptr;
   1.125 +        }
   1.126 +        Edge &operator=(Edge &&rhs) {
   1.127 +            MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
   1.128 +            this->~Edge();
   1.129 +            new(this) Edge(Move(rhs));
   1.130 +            return *this;
   1.131 +        }
   1.132 +
   1.133 +        void trace(JSTracer *trc) {
   1.134 +            if (origin)
   1.135 +                gc::MarkGCThingRoot(trc, &origin, "HeapReverser::Edge");
   1.136 +        }
   1.137 +
   1.138 +        /* The name of this heap edge. Owned by this Edge. */
   1.139 +        char *name;
   1.140 +
   1.141 +        /*
   1.142 +         * The Cell from which this edge originates. nullptr means a root. This
   1.143 +         * is a cell address instead of a Node * because Nodes live in HashMap
   1.144 +         * table entries; if the HashMap reallocates its table, all pointers to
   1.145 +         * the Nodes it contains would become invalid. You should look up the
   1.146 +         * address here in |map| to find its Node.
   1.147 +         */
   1.148 +        void *origin;
   1.149 +    };
   1.150 +
   1.151 +    /*
   1.152 +     * The result of a reversal is a map from Cells' addresses to Node
   1.153 +     * structures describing their incoming edges.
   1.154 +     */
   1.155 +    typedef HashMap<void *, Node, DefaultHasher<void *>, SystemAllocPolicy> Map;
   1.156 +    Map map;
   1.157 +
   1.158 +    /* Construct a HeapReverser for |context|'s heap. */
   1.159 +    HeapReverser(JSContext *cx)
   1.160 +      : JSTracer(cx->runtime(), traverseEdgeWithThis),
   1.161 +        JS::CustomAutoRooter(cx),
   1.162 +        noggc(JS_GetRuntime(cx)),
   1.163 +        runtime(JS_GetRuntime(cx)),
   1.164 +        parent(nullptr)
   1.165 +    {
   1.166 +    }
   1.167 +
   1.168 +    bool init() { return map.init(); }
   1.169 +
   1.170 +    /* Build a reversed map of the heap in |map|. */
   1.171 +    bool reverseHeap();
   1.172 +
   1.173 +  private:
   1.174 +    JS::AutoDisableGenerationalGC noggc;
   1.175 +
   1.176 +    /* A runtime pointer for use by the destructor. */
   1.177 +    JSRuntime *runtime;
   1.178 +
   1.179 +    /*
   1.180 +     * Return the name of the most recent edge this JSTracer has traversed. The
   1.181 +     * result is allocated with malloc; if we run out of memory, raise an error
   1.182 +     * in this HeapReverser's context and return nullptr.
   1.183 +     *
   1.184 +     * This may not be called after that edge's call to traverseEdge has
   1.185 +     * returned.
   1.186 +     */
   1.187 +    char *getEdgeDescription();
   1.188 +
   1.189 +    /* Class for setting new parent, and then restoring the original. */
   1.190 +    class AutoParent {
   1.191 +      public:
   1.192 +        AutoParent(HeapReverser *reverser, void *newParent) : reverser(reverser) {
   1.193 +            savedParent = reverser->parent;
   1.194 +            reverser->parent = newParent;
   1.195 +        }
   1.196 +        ~AutoParent() {
   1.197 +            reverser->parent = savedParent;
   1.198 +        }
   1.199 +      private:
   1.200 +        HeapReverser *reverser;
   1.201 +        void *savedParent;
   1.202 +    };
   1.203 +
   1.204 +    /* A work item in the stack of nodes whose children we need to traverse. */
   1.205 +    struct Child {
   1.206 +        Child(void *cell, JSGCTraceKind kind) : cell(cell), kind(kind) { }
   1.207 +        void *cell;
   1.208 +        JSGCTraceKind kind;
   1.209 +    };
   1.210 +
   1.211 +    /*
   1.212 +     * A stack of work items. We represent the stack explicitly to avoid
   1.213 +     * overflowing the C++ stack when traversing long chains of objects.
   1.214 +     */
   1.215 +    Vector<Child, 0, SystemAllocPolicy> work;
   1.216 +
   1.217 +    /* When traverseEdge is called, the Cell and kind at which the edge originated. */
   1.218 +    void *parent;
   1.219 +
   1.220 +    /* Traverse an edge. */
   1.221 +    bool traverseEdge(void *cell, JSGCTraceKind kind);
   1.222 +
   1.223 +    /*
   1.224 +     * JS_TraceRuntime and JS_TraceChildren don't propagate error returns,
   1.225 +     * and out-of-memory errors, by design, don't establish an exception in
   1.226 +     * |context|, so traverseEdgeWithThis uses this to communicate the
   1.227 +     * result of the traversal to reverseHeap.
   1.228 +     */
   1.229 +    bool traversalStatus;
   1.230 +
   1.231 +    /* Static member function wrapping 'traverseEdge'. */
   1.232 +    static void traverseEdgeWithThis(JSTracer *tracer, void **thingp, JSGCTraceKind kind) {
   1.233 +        HeapReverser *reverser = static_cast<HeapReverser *>(tracer);
   1.234 +        if (!reverser->traverseEdge(*thingp, kind))
   1.235 +            reverser->traversalStatus = false;
   1.236 +    }
   1.237 +
   1.238 +    /* Return a jsval representing a node, if possible; otherwise, return JSVAL_VOID. */
   1.239 +    jsval nodeToValue(void *cell, int kind) {
   1.240 +        if (kind != JSTRACE_OBJECT)
   1.241 +            return JSVAL_VOID;
   1.242 +        JSObject *object = static_cast<JSObject *>(cell);
   1.243 +        return OBJECT_TO_JSVAL(object);
   1.244 +    }
   1.245 +
   1.246 +    /* Keep all tracked objects live across GC. */
   1.247 +    virtual void trace(JSTracer *trc) MOZ_OVERRIDE {
   1.248 +        if (!map.initialized())
   1.249 +            return;
   1.250 +        for (Map::Enum e(map); !e.empty(); e.popFront()) {
   1.251 +            gc::MarkGCThingRoot(trc, const_cast<void **>(&e.front().key()), "HeapReverser::map::key");
   1.252 +            e.front().value().trace(trc);
   1.253 +        }
   1.254 +        for (Child *c = work.begin(); c != work.end(); ++c)
   1.255 +            gc::MarkGCThingRoot(trc, &c->cell, "HeapReverser::Child");
   1.256 +    }
   1.257 +};
   1.258 +
   1.259 +bool
   1.260 +HeapReverser::traverseEdge(void *cell, JSGCTraceKind kind)
   1.261 +{
   1.262 +    /* Capture this edge before the JSTracer members get overwritten. */
   1.263 +    char *edgeDescription = getEdgeDescription();
   1.264 +    if (!edgeDescription)
   1.265 +        return false;
   1.266 +    Edge e(edgeDescription, parent);
   1.267 +
   1.268 +    Map::AddPtr a = map.lookupForAdd(cell);
   1.269 +    if (!a) {
   1.270 +        /*
   1.271 +         * We've never visited this cell before. Add it to the map (thus
   1.272 +         * marking it as visited), and put it on the work stack, to be
   1.273 +         * visited from the main loop.
   1.274 +         */
   1.275 +        Node n(kind);
   1.276 +        uint32_t generation = map.generation();
   1.277 +        if (!map.add(a, cell, Move(n)) ||
   1.278 +            !work.append(Child(cell, kind)))
   1.279 +            return false;
   1.280 +        /* If the map has been resized, re-check the pointer. */
   1.281 +        if (map.generation() != generation)
   1.282 +            a = map.lookupForAdd(cell);
   1.283 +    }
   1.284 +
   1.285 +    /* Add this edge to the reversed map. */
   1.286 +    return a->value().incoming.append(Move(e));
   1.287 +}
   1.288 +
   1.289 +bool
   1.290 +HeapReverser::reverseHeap()
   1.291 +{
   1.292 +    traversalStatus = true;
   1.293 +
   1.294 +    /* Prime the work stack with the roots of collection. */
   1.295 +    JS_TraceRuntime(this);
   1.296 +    if (!traversalStatus)
   1.297 +        return false;
   1.298 +
   1.299 +    /* Traverse children until the stack is empty. */
   1.300 +    while (!work.empty()) {
   1.301 +        const Child child = work.popCopy();
   1.302 +        AutoParent autoParent(this, child.cell);
   1.303 +        JS_TraceChildren(this, child.cell, child.kind);
   1.304 +        if (!traversalStatus)
   1.305 +            return false;
   1.306 +    }
   1.307 +
   1.308 +    return true;
   1.309 +}
   1.310 +
   1.311 +char *
   1.312 +HeapReverser::getEdgeDescription()
   1.313 +{
   1.314 +    if (!debugPrinter() && debugPrintIndex() == (size_t) -1) {
   1.315 +        const char *arg = static_cast<const char *>(debugPrintArg());
   1.316 +        char *name = js_pod_malloc<char>(strlen(arg) + 1);
   1.317 +        if (!name)
   1.318 +            return nullptr;
   1.319 +        strcpy(name, arg);
   1.320 +        return name;
   1.321 +    }
   1.322 +
   1.323 +    /* Lovely; but a fixed size is required by JSTraceNamePrinter. */
   1.324 +    static const int nameSize = 200;
   1.325 +    char *name = js_pod_malloc<char>(nameSize);
   1.326 +    if (!name)
   1.327 +        return nullptr;
   1.328 +    if (debugPrinter())
   1.329 +        debugPrinter()(this, name, nameSize);
   1.330 +    else
   1.331 +        JS_snprintf(name, nameSize, "%s[%lu]",
   1.332 +                    static_cast<const char *>(debugPrintArg()), debugPrintIndex());
   1.333 +
   1.334 +    /* Shrink storage to fit. */
   1.335 +    return static_cast<char *>(js_realloc(name, strlen(name) + 1));
   1.336 +}
   1.337 +
   1.338 +
   1.339 +/*** class ReferenceFinder ***********************************************************************/
   1.340 +
   1.341 +/* A class for finding an object's referrers, given a reversed heap map. */
   1.342 +class ReferenceFinder {
   1.343 +  public:
   1.344 +    ReferenceFinder(JSContext *cx, const HeapReverser &reverser)
   1.345 +      : context(cx), reverser(reverser), result(cx) { }
   1.346 +
   1.347 +    /* Produce an object describing all references to |target|. */
   1.348 +    JSObject *findReferences(HandleObject target);
   1.349 +
   1.350 +  private:
   1.351 +    /* The context in which to do allocation and error-handling. */
   1.352 +    JSContext *context;
   1.353 +
   1.354 +    /* A reversed map of the current heap. */
   1.355 +    const HeapReverser &reverser;
   1.356 +
   1.357 +    /* The results object we're currently building. */
   1.358 +    RootedObject result;
   1.359 +
   1.360 +    /* A list of edges we've traversed to get to a certain point. */
   1.361 +    class Path {
   1.362 +      public:
   1.363 +        Path(const HeapReverser::Edge &edge, Path *next) : edge(edge), next(next) { }
   1.364 +
   1.365 +        /*
   1.366 +         * Compute the full path represented by this Path. The result is
   1.367 +         * owned by the caller.
   1.368 +         */
   1.369 +        char *computeName(JSContext *cx);
   1.370 +
   1.371 +      private:
   1.372 +        const HeapReverser::Edge &edge;
   1.373 +        Path *next;
   1.374 +    };
   1.375 +
   1.376 +    struct AutoNodeMarker {
   1.377 +        AutoNodeMarker(HeapReverser::Node *node) : node(node) { node->marked = true; }
   1.378 +        ~AutoNodeMarker() { node->marked = false; }
   1.379 +      private:
   1.380 +        HeapReverser::Node *node;
   1.381 +    };
   1.382 +
   1.383 +    /*
   1.384 +     * Given that we've reached |cell| via |path|, with all Nodes along that
   1.385 +     * path marked, add paths from all reportable objects reachable from cell
   1.386 +     * to |result|.
   1.387 +     */
   1.388 +    bool visit(void *cell, Path *path);
   1.389 +
   1.390 +    /*
   1.391 +     * If |cell|, of |kind|, is representable as a JavaScript value, return that
   1.392 +     * value; otherwise, return JSVAL_VOID.
   1.393 +     */
   1.394 +    jsval representable(void *cell, int kind) {
   1.395 +        if (kind == JSTRACE_OBJECT) {
   1.396 +            JSObject *object = static_cast<JSObject *>(cell);
   1.397 +
   1.398 +            /* Certain classes of object are for internal use only. */
   1.399 +            if (object->is<BlockObject>() ||
   1.400 +                object->is<CallObject>() ||
   1.401 +                object->is<StaticWithObject>() ||
   1.402 +                object->is<DynamicWithObject>() ||
   1.403 +                object->is<DeclEnvObject>()) {
   1.404 +                return JSVAL_VOID;
   1.405 +            }
   1.406 +
   1.407 +            /* Internal function objects should also not be revealed. */
   1.408 +            if (JS_ObjectIsFunction(context, object) && IsInternalFunctionObject(object))
   1.409 +                return JSVAL_VOID;
   1.410 +
   1.411 +            return OBJECT_TO_JSVAL(object);
   1.412 +        }
   1.413 +
   1.414 +        return JSVAL_VOID;
   1.415 +    }
   1.416 +
   1.417 +    /* Add |referrer| as something that refers to |target| via |path|. */
   1.418 +    bool addReferrer(jsval referrer, Path *path);
   1.419 +};
   1.420 +
   1.421 +bool
   1.422 +ReferenceFinder::visit(void *cell, Path *path)
   1.423 +{
   1.424 +    /* In ReferenceFinder, paths will almost certainly fit on the C++ stack. */
   1.425 +    JS_CHECK_RECURSION(context, return false);
   1.426 +
   1.427 +    /* Have we reached a root? Always report that. */
   1.428 +    if (!cell)
   1.429 +        return addReferrer(JSVAL_NULL, path);
   1.430 +
   1.431 +    HeapReverser::Map::Ptr p = reverser.map.lookup(cell);
   1.432 +    JS_ASSERT(p);
   1.433 +    HeapReverser::Node *node = &p->value();
   1.434 +
   1.435 +    /* Is |cell| a representable cell, reached via a non-empty path? */
   1.436 +    if (path != nullptr) {
   1.437 +        jsval representation = representable(cell, node->kind);
   1.438 +        if (!JSVAL_IS_VOID(representation))
   1.439 +            return addReferrer(representation, path);
   1.440 +    }
   1.441 +
   1.442 +    /*
   1.443 +     * If we've made a cycle, don't traverse further. We *do* want to include
   1.444 +     * paths from the target to itself, so we don't want to do this check until
   1.445 +     * after we've possibly reported this cell as a referrer.
   1.446 +     */
   1.447 +    if (node->marked)
   1.448 +        return true;
   1.449 +    AutoNodeMarker marker(node);
   1.450 +
   1.451 +    /* Visit the origins of all |cell|'s incoming edges. */
   1.452 +    for (size_t i = 0; i < node->incoming.length(); i++) {
   1.453 +        const HeapReverser::Edge &edge = node->incoming[i];
   1.454 +        Path extendedPath(edge, path);
   1.455 +        if (!visit(edge.origin, &extendedPath))
   1.456 +            return false;
   1.457 +    }
   1.458 +
   1.459 +    return true;
   1.460 +}
   1.461 +
   1.462 +char *
   1.463 +ReferenceFinder::Path::computeName(JSContext *cx)
   1.464 +{
   1.465 +    /* Walk the edge list and compute the total size of the path. */
   1.466 +    size_t size = 6;
   1.467 +    for (Path *l = this; l; l = l->next)
   1.468 +        size += strlen(l->edge.name) + (l->next ? 2 : 0);
   1.469 +    size += 1;
   1.470 +
   1.471 +    char *path = cx->pod_malloc<char>(size);
   1.472 +    if (!path)
   1.473 +        return nullptr;
   1.474 +
   1.475 +    /*
   1.476 +     * Walk the edge list again, and copy the edge names into place, with
   1.477 +     * appropriate separators. Note that we constructed the edge list from
   1.478 +     * target to referrer, which means that the list links point *towards* the
   1.479 +     * target, so we can walk the list and build the path from left to right.
   1.480 +     */
   1.481 +    strcpy(path, "edge: ");
   1.482 +    char *next = path + 6;
   1.483 +    for (Path *l = this; l; l = l->next) {
   1.484 +        strcpy(next, l->edge.name);
   1.485 +        next += strlen(next);
   1.486 +        if (l->next) {
   1.487 +            strcpy(next, "; ");
   1.488 +            next += 2;
   1.489 +        }
   1.490 +    }
   1.491 +    JS_ASSERT(next + 1 == path + size);
   1.492 +
   1.493 +    return path;
   1.494 +}
   1.495 +
   1.496 +bool
   1.497 +ReferenceFinder::addReferrer(jsval referrerArg, Path *path)
   1.498 +{
   1.499 +    RootedValue referrer(context, referrerArg);
   1.500 +
   1.501 +    if (!context->compartment()->wrap(context, &referrer))
   1.502 +        return false;
   1.503 +
   1.504 +    ScopedJSFreePtr<char> pathName(path->computeName(context));
   1.505 +    if (!pathName)
   1.506 +        return false;
   1.507 +
   1.508 +    /* Find the property of the results object named |pathName|. */
   1.509 +    RootedValue v(context);
   1.510 +
   1.511 +    if (!JS_GetProperty(context, result, pathName, &v))
   1.512 +        return false;
   1.513 +    if (v.isUndefined()) {
   1.514 +        /* Create an array to accumulate referents under this path. */
   1.515 +        JSObject *array = JS_NewArrayObject(context, referrer);
   1.516 +        if (!array)
   1.517 +            return false;
   1.518 +        v.setObject(*array);
   1.519 +        return !!JS_SetProperty(context, result, pathName, v);
   1.520 +    }
   1.521 +
   1.522 +    /* The property's value had better be an array. */
   1.523 +    RootedObject array(context, &v.toObject());
   1.524 +    JS_ASSERT(JS_IsArrayObject(context, array));
   1.525 +
   1.526 +    /* Append our referrer to this array. */
   1.527 +    uint32_t length;
   1.528 +    return JS_GetArrayLength(context, array, &length) &&
   1.529 +           JS_SetElement(context, array, length, referrer);
   1.530 +}
   1.531 +
   1.532 +JSObject *
   1.533 +ReferenceFinder::findReferences(HandleObject target)
   1.534 +{
   1.535 +    result = JS_NewObject(context, nullptr, JS::NullPtr(), JS::NullPtr());
   1.536 +    if (!result)
   1.537 +        return nullptr;
   1.538 +    if (!visit(target, nullptr))
   1.539 +        return nullptr;
   1.540 +
   1.541 +    return result;
   1.542 +}
   1.543 +
   1.544 +/* See help(findReferences). */
   1.545 +bool
   1.546 +FindReferences(JSContext *cx, unsigned argc, jsval *vp)
   1.547 +{
   1.548 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.549 +    if (args.length() < 1) {
   1.550 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   1.551 +                             "findReferences", "0", "s");
   1.552 +        return false;
   1.553 +    }
   1.554 +
   1.555 +    RootedValue target(cx, args[0]);
   1.556 +    if (!target.isObject()) {
   1.557 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   1.558 +                             "argument", "not an object");
   1.559 +        return false;
   1.560 +    }
   1.561 +
   1.562 +    /* Walk the JSRuntime, producing a reversed map of the heap. */
   1.563 +    HeapReverser reverser(cx);
   1.564 +    if (!reverser.init() || !reverser.reverseHeap())
   1.565 +        return false;
   1.566 +
   1.567 +    /* Given the reversed map, find the referents of target. */
   1.568 +    ReferenceFinder finder(cx, reverser);
   1.569 +    Rooted<JSObject*> targetObj(cx, &target.toObject());
   1.570 +    JSObject *references = finder.findReferences(targetObj);
   1.571 +    if (!references)
   1.572 +        return false;
   1.573 +
   1.574 +    args.rval().setObject(*references);
   1.575 +    return true;
   1.576 +}
   1.577 +
   1.578 +#endif /* DEBUG */

mercurial