// The takeCensus function works in three phases:
//
// 1) We examine the 'breakdown' property of our 'options' argument, and
//    use that to build a CountType tree.
//
// 2) We create a count node for the root of our CountType tree, and then walk
//    the heap, counting each node we find, expanding our tree of counts as we
//    go.
//
// 3) We walk the tree of counts and produce JavaScript objects reporting the
//    accumulated results.
bool
DebuggerMemory::takeCensus(JSContext* cx, unsigned argc, Value* vp)
{
    THIS_DEBUGGER_MEMORY(cx, argc, vp, "Debugger.Memory.prototype.census", args, memory);

    Census census(cx);
    if (!census.init())
        return false;
    CountTypePtr rootType;

    RootedObject options(cx);
    if (args.get(0).isObject())
        options = &args[0].toObject();

    if (!JS::ubi::ParseCensusOptions(cx, census, options, rootType))
        return false;

    JS::ubi::RootedCount rootCount(cx, rootType->makeCount());
    if (!rootCount)
        return false;
    JS::ubi::CensusHandler handler(census, rootCount, cx->runtime()->debuggerMallocSizeOf);

    Debugger* dbg = memory->getDebugger();
    RootedObject dbgObj(cx, dbg->object);

    // Populate our target set of debuggee zones.
    for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
        if (!census.targetZones.put(r.front()->zone()))
            return false;
    }

    {
        Maybe<JS::AutoCheckCannotGC> maybeNoGC;
        JS::ubi::RootList rootList(cx->runtime(), maybeNoGC);
        if (!rootList.init(dbgObj)) {
            ReportOutOfMemory(cx);
            return false;
        }

        JS::ubi::CensusTraversal traversal(cx->runtime(), handler, maybeNoGC.ref());
        if (!traversal.init()) {
            ReportOutOfMemory(cx);
            return false;
        }
        traversal.wantNames = false;

        if (!traversal.addStart(JS::ubi::Node(&rootList)) ||
                !traversal.traverse())
        {
            ReportOutOfMemory(cx);
            return false;
        }
    }

    return handler.report(cx, args.rval());
}