js/src/shell/jsheaptools.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "shell/jsheaptools.h"
michael@0 8
michael@0 9 #include "mozilla/Move.h"
michael@0 10
michael@0 11 #include <string.h>
michael@0 12
michael@0 13 #include "jsalloc.h"
michael@0 14 #include "jsapi.h"
michael@0 15 #include "jscntxt.h"
michael@0 16 #include "jscompartment.h"
michael@0 17 #include "jsobj.h"
michael@0 18 #include "jsprf.h"
michael@0 19
michael@0 20 #include "jsobjinlines.h"
michael@0 21
michael@0 22 using namespace js;
michael@0 23
michael@0 24 using mozilla::Move;
michael@0 25
michael@0 26 #ifdef DEBUG
michael@0 27
michael@0 28
michael@0 29 /*** class HeapReverser **************************************************************************/
michael@0 30
michael@0 31 /*
michael@0 32 * A class for constructing a map of the JavaScript heap, with all
michael@0 33 * reference edges reversed.
michael@0 34 *
michael@0 35 * Unfortunately, it's not possible to build the results for findReferences
michael@0 36 * while visiting things solely in the order that JS_TraceRuntime and
michael@0 37 * JS_TraceChildren reaches them. For example, as you work outward from the
michael@0 38 * roots, suppose an edge from thing T reaches a "gray" thing G --- G being gray
michael@0 39 * because you're still in the midst of traversing its descendants. At this
michael@0 40 * point, you don't know yet whether G will be a referrer or not, and so you
michael@0 41 * can't tell whether T should be a referrer either. And you won't visit T
michael@0 42 * again.
michael@0 43 *
michael@0 44 * So we take a brute-force approach. We reverse the entire graph, and then walk
michael@0 45 * outward from |target| to the representable objects that refer to it, stopping
michael@0 46 * at such objects.
michael@0 47 */
michael@0 48
michael@0 49 /*
michael@0 50 * A JSTracer that produces a map of the heap with edges reversed.
michael@0 51 *
michael@0 52 * HeapReversers must be allocated in a stack frame. (They are derived from
michael@0 53 * CustomAutoRooter, and those must be allocated and destroyed in a stack-like
michael@0 54 * order.)
michael@0 55 *
michael@0 56 * HeapReversers keep all the roots they find in their traversal alive until
michael@0 57 * they are destroyed. So you don't need to worry about nodes going away while
michael@0 58 * you're using them.
michael@0 59 */
michael@0 60 class HeapReverser : public JSTracer, public JS::CustomAutoRooter
michael@0 61 {
michael@0 62 public:
michael@0 63 struct Edge;
michael@0 64
michael@0 65 /* Metadata for a given Cell we have visited. */
michael@0 66 class Node {
michael@0 67 public:
michael@0 68 Node() { }
michael@0 69 Node(JSGCTraceKind kind)
michael@0 70 : kind(kind), incoming(), marked(false) { }
michael@0 71
michael@0 72 /*
michael@0 73 * Move constructor and move assignment. These allow us to store our
michael@0 74 * incoming edge Vector in the hash table: Vectors support moves, but
michael@0 75 * not assignments or copy construction.
michael@0 76 */
michael@0 77 Node(Node &&rhs)
michael@0 78 : kind(rhs.kind), incoming(Move(rhs.incoming)), marked(rhs.marked) { }
michael@0 79 Node &operator=(Node &&rhs) {
michael@0 80 MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
michael@0 81 this->~Node();
michael@0 82 new(this) Node(Move(rhs));
michael@0 83 return *this;
michael@0 84 }
michael@0 85
michael@0 86 void trace(JSTracer *trc) {
michael@0 87 for (Edge *e = incoming.begin(); e != incoming.end(); e++)
michael@0 88 e->trace(trc);
michael@0 89 }
michael@0 90
michael@0 91 /* What kind of Cell this is. */
michael@0 92 JSGCTraceKind kind;
michael@0 93
michael@0 94 /*
michael@0 95 * A vector of this Cell's incoming edges.
michael@0 96 * This must use SystemAllocPolicy because HashMap requires its elements to
michael@0 97 * be constructible with no arguments.
michael@0 98 */
michael@0 99 Vector<Edge, 0, SystemAllocPolicy> incoming;
michael@0 100
michael@0 101 /* A mark bit, for other traversals. */
michael@0 102 bool marked;
michael@0 103
michael@0 104 private:
michael@0 105 Node(const Node &) MOZ_DELETE;
michael@0 106 Node &operator=(const Node &) MOZ_DELETE;
michael@0 107 };
michael@0 108
michael@0 109 /* Metadata for a heap edge we have traversed. */
michael@0 110 struct Edge {
michael@0 111 public:
michael@0 112 Edge(char *name, void *origin) : name(name), origin(origin) { }
michael@0 113 ~Edge() { js_free(name); }
michael@0 114
michael@0 115 /*
michael@0 116 * Move constructor and move assignment. These allow us to live in
michael@0 117 * Vectors without needing to copy our name string when the vector is
michael@0 118 * resized.
michael@0 119 */
michael@0 120 Edge(Edge &&rhs) : name(rhs.name), origin(rhs.origin) {
michael@0 121 rhs.name = nullptr;
michael@0 122 }
michael@0 123 Edge &operator=(Edge &&rhs) {
michael@0 124 MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
michael@0 125 this->~Edge();
michael@0 126 new(this) Edge(Move(rhs));
michael@0 127 return *this;
michael@0 128 }
michael@0 129
michael@0 130 void trace(JSTracer *trc) {
michael@0 131 if (origin)
michael@0 132 gc::MarkGCThingRoot(trc, &origin, "HeapReverser::Edge");
michael@0 133 }
michael@0 134
michael@0 135 /* The name of this heap edge. Owned by this Edge. */
michael@0 136 char *name;
michael@0 137
michael@0 138 /*
michael@0 139 * The Cell from which this edge originates. nullptr means a root. This
michael@0 140 * is a cell address instead of a Node * because Nodes live in HashMap
michael@0 141 * table entries; if the HashMap reallocates its table, all pointers to
michael@0 142 * the Nodes it contains would become invalid. You should look up the
michael@0 143 * address here in |map| to find its Node.
michael@0 144 */
michael@0 145 void *origin;
michael@0 146 };
michael@0 147
michael@0 148 /*
michael@0 149 * The result of a reversal is a map from Cells' addresses to Node
michael@0 150 * structures describing their incoming edges.
michael@0 151 */
michael@0 152 typedef HashMap<void *, Node, DefaultHasher<void *>, SystemAllocPolicy> Map;
michael@0 153 Map map;
michael@0 154
michael@0 155 /* Construct a HeapReverser for |context|'s heap. */
michael@0 156 HeapReverser(JSContext *cx)
michael@0 157 : JSTracer(cx->runtime(), traverseEdgeWithThis),
michael@0 158 JS::CustomAutoRooter(cx),
michael@0 159 noggc(JS_GetRuntime(cx)),
michael@0 160 runtime(JS_GetRuntime(cx)),
michael@0 161 parent(nullptr)
michael@0 162 {
michael@0 163 }
michael@0 164
michael@0 165 bool init() { return map.init(); }
michael@0 166
michael@0 167 /* Build a reversed map of the heap in |map|. */
michael@0 168 bool reverseHeap();
michael@0 169
michael@0 170 private:
michael@0 171 JS::AutoDisableGenerationalGC noggc;
michael@0 172
michael@0 173 /* A runtime pointer for use by the destructor. */
michael@0 174 JSRuntime *runtime;
michael@0 175
michael@0 176 /*
michael@0 177 * Return the name of the most recent edge this JSTracer has traversed. The
michael@0 178 * result is allocated with malloc; if we run out of memory, raise an error
michael@0 179 * in this HeapReverser's context and return nullptr.
michael@0 180 *
michael@0 181 * This may not be called after that edge's call to traverseEdge has
michael@0 182 * returned.
michael@0 183 */
michael@0 184 char *getEdgeDescription();
michael@0 185
michael@0 186 /* Class for setting new parent, and then restoring the original. */
michael@0 187 class AutoParent {
michael@0 188 public:
michael@0 189 AutoParent(HeapReverser *reverser, void *newParent) : reverser(reverser) {
michael@0 190 savedParent = reverser->parent;
michael@0 191 reverser->parent = newParent;
michael@0 192 }
michael@0 193 ~AutoParent() {
michael@0 194 reverser->parent = savedParent;
michael@0 195 }
michael@0 196 private:
michael@0 197 HeapReverser *reverser;
michael@0 198 void *savedParent;
michael@0 199 };
michael@0 200
michael@0 201 /* A work item in the stack of nodes whose children we need to traverse. */
michael@0 202 struct Child {
michael@0 203 Child(void *cell, JSGCTraceKind kind) : cell(cell), kind(kind) { }
michael@0 204 void *cell;
michael@0 205 JSGCTraceKind kind;
michael@0 206 };
michael@0 207
michael@0 208 /*
michael@0 209 * A stack of work items. We represent the stack explicitly to avoid
michael@0 210 * overflowing the C++ stack when traversing long chains of objects.
michael@0 211 */
michael@0 212 Vector<Child, 0, SystemAllocPolicy> work;
michael@0 213
michael@0 214 /* When traverseEdge is called, the Cell and kind at which the edge originated. */
michael@0 215 void *parent;
michael@0 216
michael@0 217 /* Traverse an edge. */
michael@0 218 bool traverseEdge(void *cell, JSGCTraceKind kind);
michael@0 219
michael@0 220 /*
michael@0 221 * JS_TraceRuntime and JS_TraceChildren don't propagate error returns,
michael@0 222 * and out-of-memory errors, by design, don't establish an exception in
michael@0 223 * |context|, so traverseEdgeWithThis uses this to communicate the
michael@0 224 * result of the traversal to reverseHeap.
michael@0 225 */
michael@0 226 bool traversalStatus;
michael@0 227
michael@0 228 /* Static member function wrapping 'traverseEdge'. */
michael@0 229 static void traverseEdgeWithThis(JSTracer *tracer, void **thingp, JSGCTraceKind kind) {
michael@0 230 HeapReverser *reverser = static_cast<HeapReverser *>(tracer);
michael@0 231 if (!reverser->traverseEdge(*thingp, kind))
michael@0 232 reverser->traversalStatus = false;
michael@0 233 }
michael@0 234
michael@0 235 /* Return a jsval representing a node, if possible; otherwise, return JSVAL_VOID. */
michael@0 236 jsval nodeToValue(void *cell, int kind) {
michael@0 237 if (kind != JSTRACE_OBJECT)
michael@0 238 return JSVAL_VOID;
michael@0 239 JSObject *object = static_cast<JSObject *>(cell);
michael@0 240 return OBJECT_TO_JSVAL(object);
michael@0 241 }
michael@0 242
michael@0 243 /* Keep all tracked objects live across GC. */
michael@0 244 virtual void trace(JSTracer *trc) MOZ_OVERRIDE {
michael@0 245 if (!map.initialized())
michael@0 246 return;
michael@0 247 for (Map::Enum e(map); !e.empty(); e.popFront()) {
michael@0 248 gc::MarkGCThingRoot(trc, const_cast<void **>(&e.front().key()), "HeapReverser::map::key");
michael@0 249 e.front().value().trace(trc);
michael@0 250 }
michael@0 251 for (Child *c = work.begin(); c != work.end(); ++c)
michael@0 252 gc::MarkGCThingRoot(trc, &c->cell, "HeapReverser::Child");
michael@0 253 }
michael@0 254 };
michael@0 255
michael@0 256 bool
michael@0 257 HeapReverser::traverseEdge(void *cell, JSGCTraceKind kind)
michael@0 258 {
michael@0 259 /* Capture this edge before the JSTracer members get overwritten. */
michael@0 260 char *edgeDescription = getEdgeDescription();
michael@0 261 if (!edgeDescription)
michael@0 262 return false;
michael@0 263 Edge e(edgeDescription, parent);
michael@0 264
michael@0 265 Map::AddPtr a = map.lookupForAdd(cell);
michael@0 266 if (!a) {
michael@0 267 /*
michael@0 268 * We've never visited this cell before. Add it to the map (thus
michael@0 269 * marking it as visited), and put it on the work stack, to be
michael@0 270 * visited from the main loop.
michael@0 271 */
michael@0 272 Node n(kind);
michael@0 273 uint32_t generation = map.generation();
michael@0 274 if (!map.add(a, cell, Move(n)) ||
michael@0 275 !work.append(Child(cell, kind)))
michael@0 276 return false;
michael@0 277 /* If the map has been resized, re-check the pointer. */
michael@0 278 if (map.generation() != generation)
michael@0 279 a = map.lookupForAdd(cell);
michael@0 280 }
michael@0 281
michael@0 282 /* Add this edge to the reversed map. */
michael@0 283 return a->value().incoming.append(Move(e));
michael@0 284 }
michael@0 285
michael@0 286 bool
michael@0 287 HeapReverser::reverseHeap()
michael@0 288 {
michael@0 289 traversalStatus = true;
michael@0 290
michael@0 291 /* Prime the work stack with the roots of collection. */
michael@0 292 JS_TraceRuntime(this);
michael@0 293 if (!traversalStatus)
michael@0 294 return false;
michael@0 295
michael@0 296 /* Traverse children until the stack is empty. */
michael@0 297 while (!work.empty()) {
michael@0 298 const Child child = work.popCopy();
michael@0 299 AutoParent autoParent(this, child.cell);
michael@0 300 JS_TraceChildren(this, child.cell, child.kind);
michael@0 301 if (!traversalStatus)
michael@0 302 return false;
michael@0 303 }
michael@0 304
michael@0 305 return true;
michael@0 306 }
michael@0 307
michael@0 308 char *
michael@0 309 HeapReverser::getEdgeDescription()
michael@0 310 {
michael@0 311 if (!debugPrinter() && debugPrintIndex() == (size_t) -1) {
michael@0 312 const char *arg = static_cast<const char *>(debugPrintArg());
michael@0 313 char *name = js_pod_malloc<char>(strlen(arg) + 1);
michael@0 314 if (!name)
michael@0 315 return nullptr;
michael@0 316 strcpy(name, arg);
michael@0 317 return name;
michael@0 318 }
michael@0 319
michael@0 320 /* Lovely; but a fixed size is required by JSTraceNamePrinter. */
michael@0 321 static const int nameSize = 200;
michael@0 322 char *name = js_pod_malloc<char>(nameSize);
michael@0 323 if (!name)
michael@0 324 return nullptr;
michael@0 325 if (debugPrinter())
michael@0 326 debugPrinter()(this, name, nameSize);
michael@0 327 else
michael@0 328 JS_snprintf(name, nameSize, "%s[%lu]",
michael@0 329 static_cast<const char *>(debugPrintArg()), debugPrintIndex());
michael@0 330
michael@0 331 /* Shrink storage to fit. */
michael@0 332 return static_cast<char *>(js_realloc(name, strlen(name) + 1));
michael@0 333 }
michael@0 334
michael@0 335
michael@0 336 /*** class ReferenceFinder ***********************************************************************/
michael@0 337
michael@0 338 /* A class for finding an object's referrers, given a reversed heap map. */
michael@0 339 class ReferenceFinder {
michael@0 340 public:
michael@0 341 ReferenceFinder(JSContext *cx, const HeapReverser &reverser)
michael@0 342 : context(cx), reverser(reverser), result(cx) { }
michael@0 343
michael@0 344 /* Produce an object describing all references to |target|. */
michael@0 345 JSObject *findReferences(HandleObject target);
michael@0 346
michael@0 347 private:
michael@0 348 /* The context in which to do allocation and error-handling. */
michael@0 349 JSContext *context;
michael@0 350
michael@0 351 /* A reversed map of the current heap. */
michael@0 352 const HeapReverser &reverser;
michael@0 353
michael@0 354 /* The results object we're currently building. */
michael@0 355 RootedObject result;
michael@0 356
michael@0 357 /* A list of edges we've traversed to get to a certain point. */
michael@0 358 class Path {
michael@0 359 public:
michael@0 360 Path(const HeapReverser::Edge &edge, Path *next) : edge(edge), next(next) { }
michael@0 361
michael@0 362 /*
michael@0 363 * Compute the full path represented by this Path. The result is
michael@0 364 * owned by the caller.
michael@0 365 */
michael@0 366 char *computeName(JSContext *cx);
michael@0 367
michael@0 368 private:
michael@0 369 const HeapReverser::Edge &edge;
michael@0 370 Path *next;
michael@0 371 };
michael@0 372
michael@0 373 struct AutoNodeMarker {
michael@0 374 AutoNodeMarker(HeapReverser::Node *node) : node(node) { node->marked = true; }
michael@0 375 ~AutoNodeMarker() { node->marked = false; }
michael@0 376 private:
michael@0 377 HeapReverser::Node *node;
michael@0 378 };
michael@0 379
michael@0 380 /*
michael@0 381 * Given that we've reached |cell| via |path|, with all Nodes along that
michael@0 382 * path marked, add paths from all reportable objects reachable from cell
michael@0 383 * to |result|.
michael@0 384 */
michael@0 385 bool visit(void *cell, Path *path);
michael@0 386
michael@0 387 /*
michael@0 388 * If |cell|, of |kind|, is representable as a JavaScript value, return that
michael@0 389 * value; otherwise, return JSVAL_VOID.
michael@0 390 */
michael@0 391 jsval representable(void *cell, int kind) {
michael@0 392 if (kind == JSTRACE_OBJECT) {
michael@0 393 JSObject *object = static_cast<JSObject *>(cell);
michael@0 394
michael@0 395 /* Certain classes of object are for internal use only. */
michael@0 396 if (object->is<BlockObject>() ||
michael@0 397 object->is<CallObject>() ||
michael@0 398 object->is<StaticWithObject>() ||
michael@0 399 object->is<DynamicWithObject>() ||
michael@0 400 object->is<DeclEnvObject>()) {
michael@0 401 return JSVAL_VOID;
michael@0 402 }
michael@0 403
michael@0 404 /* Internal function objects should also not be revealed. */
michael@0 405 if (JS_ObjectIsFunction(context, object) && IsInternalFunctionObject(object))
michael@0 406 return JSVAL_VOID;
michael@0 407
michael@0 408 return OBJECT_TO_JSVAL(object);
michael@0 409 }
michael@0 410
michael@0 411 return JSVAL_VOID;
michael@0 412 }
michael@0 413
michael@0 414 /* Add |referrer| as something that refers to |target| via |path|. */
michael@0 415 bool addReferrer(jsval referrer, Path *path);
michael@0 416 };
michael@0 417
michael@0 418 bool
michael@0 419 ReferenceFinder::visit(void *cell, Path *path)
michael@0 420 {
michael@0 421 /* In ReferenceFinder, paths will almost certainly fit on the C++ stack. */
michael@0 422 JS_CHECK_RECURSION(context, return false);
michael@0 423
michael@0 424 /* Have we reached a root? Always report that. */
michael@0 425 if (!cell)
michael@0 426 return addReferrer(JSVAL_NULL, path);
michael@0 427
michael@0 428 HeapReverser::Map::Ptr p = reverser.map.lookup(cell);
michael@0 429 JS_ASSERT(p);
michael@0 430 HeapReverser::Node *node = &p->value();
michael@0 431
michael@0 432 /* Is |cell| a representable cell, reached via a non-empty path? */
michael@0 433 if (path != nullptr) {
michael@0 434 jsval representation = representable(cell, node->kind);
michael@0 435 if (!JSVAL_IS_VOID(representation))
michael@0 436 return addReferrer(representation, path);
michael@0 437 }
michael@0 438
michael@0 439 /*
michael@0 440 * If we've made a cycle, don't traverse further. We *do* want to include
michael@0 441 * paths from the target to itself, so we don't want to do this check until
michael@0 442 * after we've possibly reported this cell as a referrer.
michael@0 443 */
michael@0 444 if (node->marked)
michael@0 445 return true;
michael@0 446 AutoNodeMarker marker(node);
michael@0 447
michael@0 448 /* Visit the origins of all |cell|'s incoming edges. */
michael@0 449 for (size_t i = 0; i < node->incoming.length(); i++) {
michael@0 450 const HeapReverser::Edge &edge = node->incoming[i];
michael@0 451 Path extendedPath(edge, path);
michael@0 452 if (!visit(edge.origin, &extendedPath))
michael@0 453 return false;
michael@0 454 }
michael@0 455
michael@0 456 return true;
michael@0 457 }
michael@0 458
michael@0 459 char *
michael@0 460 ReferenceFinder::Path::computeName(JSContext *cx)
michael@0 461 {
michael@0 462 /* Walk the edge list and compute the total size of the path. */
michael@0 463 size_t size = 6;
michael@0 464 for (Path *l = this; l; l = l->next)
michael@0 465 size += strlen(l->edge.name) + (l->next ? 2 : 0);
michael@0 466 size += 1;
michael@0 467
michael@0 468 char *path = cx->pod_malloc<char>(size);
michael@0 469 if (!path)
michael@0 470 return nullptr;
michael@0 471
michael@0 472 /*
michael@0 473 * Walk the edge list again, and copy the edge names into place, with
michael@0 474 * appropriate separators. Note that we constructed the edge list from
michael@0 475 * target to referrer, which means that the list links point *towards* the
michael@0 476 * target, so we can walk the list and build the path from left to right.
michael@0 477 */
michael@0 478 strcpy(path, "edge: ");
michael@0 479 char *next = path + 6;
michael@0 480 for (Path *l = this; l; l = l->next) {
michael@0 481 strcpy(next, l->edge.name);
michael@0 482 next += strlen(next);
michael@0 483 if (l->next) {
michael@0 484 strcpy(next, "; ");
michael@0 485 next += 2;
michael@0 486 }
michael@0 487 }
michael@0 488 JS_ASSERT(next + 1 == path + size);
michael@0 489
michael@0 490 return path;
michael@0 491 }
michael@0 492
michael@0 493 bool
michael@0 494 ReferenceFinder::addReferrer(jsval referrerArg, Path *path)
michael@0 495 {
michael@0 496 RootedValue referrer(context, referrerArg);
michael@0 497
michael@0 498 if (!context->compartment()->wrap(context, &referrer))
michael@0 499 return false;
michael@0 500
michael@0 501 ScopedJSFreePtr<char> pathName(path->computeName(context));
michael@0 502 if (!pathName)
michael@0 503 return false;
michael@0 504
michael@0 505 /* Find the property of the results object named |pathName|. */
michael@0 506 RootedValue v(context);
michael@0 507
michael@0 508 if (!JS_GetProperty(context, result, pathName, &v))
michael@0 509 return false;
michael@0 510 if (v.isUndefined()) {
michael@0 511 /* Create an array to accumulate referents under this path. */
michael@0 512 JSObject *array = JS_NewArrayObject(context, referrer);
michael@0 513 if (!array)
michael@0 514 return false;
michael@0 515 v.setObject(*array);
michael@0 516 return !!JS_SetProperty(context, result, pathName, v);
michael@0 517 }
michael@0 518
michael@0 519 /* The property's value had better be an array. */
michael@0 520 RootedObject array(context, &v.toObject());
michael@0 521 JS_ASSERT(JS_IsArrayObject(context, array));
michael@0 522
michael@0 523 /* Append our referrer to this array. */
michael@0 524 uint32_t length;
michael@0 525 return JS_GetArrayLength(context, array, &length) &&
michael@0 526 JS_SetElement(context, array, length, referrer);
michael@0 527 }
michael@0 528
michael@0 529 JSObject *
michael@0 530 ReferenceFinder::findReferences(HandleObject target)
michael@0 531 {
michael@0 532 result = JS_NewObject(context, nullptr, JS::NullPtr(), JS::NullPtr());
michael@0 533 if (!result)
michael@0 534 return nullptr;
michael@0 535 if (!visit(target, nullptr))
michael@0 536 return nullptr;
michael@0 537
michael@0 538 return result;
michael@0 539 }
michael@0 540
michael@0 541 /* See help(findReferences). */
michael@0 542 bool
michael@0 543 FindReferences(JSContext *cx, unsigned argc, jsval *vp)
michael@0 544 {
michael@0 545 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 546 if (args.length() < 1) {
michael@0 547 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
michael@0 548 "findReferences", "0", "s");
michael@0 549 return false;
michael@0 550 }
michael@0 551
michael@0 552 RootedValue target(cx, args[0]);
michael@0 553 if (!target.isObject()) {
michael@0 554 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
michael@0 555 "argument", "not an object");
michael@0 556 return false;
michael@0 557 }
michael@0 558
michael@0 559 /* Walk the JSRuntime, producing a reversed map of the heap. */
michael@0 560 HeapReverser reverser(cx);
michael@0 561 if (!reverser.init() || !reverser.reverseHeap())
michael@0 562 return false;
michael@0 563
michael@0 564 /* Given the reversed map, find the referents of target. */
michael@0 565 ReferenceFinder finder(cx, reverser);
michael@0 566 Rooted<JSObject*> targetObj(cx, &target.toObject());
michael@0 567 JSObject *references = finder.findReferences(targetObj);
michael@0 568 if (!references)
michael@0 569 return false;
michael@0 570
michael@0 571 args.rval().setObject(*references);
michael@0 572 return true;
michael@0 573 }
michael@0 574
michael@0 575 #endif /* DEBUG */

mercurial