LCOV - code coverage report
Current view: top level - js/src/vm - UbiNodeCensus.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 589 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 92 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "js/UbiNodeCensus.h"
       8             : 
       9             : #include "jscntxt.h"
      10             : #include "jscompartment.h"
      11             : #include "jsobjinlines.h"
      12             : 
      13             : #include "vm/NativeObject-inl.h"
      14             : 
      15             : using namespace js;
      16             : 
      17             : namespace JS {
      18             : namespace ubi {
      19             : 
      20             : JS_PUBLIC_API(void)
      21           0 : CountDeleter::operator()(CountBase* ptr)
      22             : {
      23           0 :     if (!ptr)
      24           0 :         return;
      25             : 
      26             :     // Downcast to our true type and destruct, as guided by our CountType
      27             :     // pointer.
      28           0 :     ptr->destruct();
      29           0 :     js_free(ptr);
      30             : }
      31             : 
      32             : JS_PUBLIC_API(bool)
      33           0 : Census::init() {
      34           0 :     AutoLockForExclusiveAccess lock(cx);
      35           0 :     atomsZone = cx->runtime()->atomsCompartment(lock)->zone();
      36           0 :     return targetZones.init();
      37             : }
      38             : 
      39             : 
      40             : /*** Count Types ***********************************************************************************/
      41             : 
      42             : // The simplest type: just count everything.
      43           0 : class SimpleCount : public CountType {
      44             : 
      45           0 :     struct Count : CountBase {
      46             :         size_t totalBytes_;
      47             : 
      48           0 :         explicit Count(SimpleCount& count)
      49           0 :           : CountBase(count),
      50           0 :             totalBytes_(0)
      51           0 :         { }
      52             :     };
      53             : 
      54             :     UniqueTwoByteChars label;
      55             :     bool reportCount : 1;
      56             :     bool reportBytes : 1;
      57             : 
      58             :   public:
      59           0 :     explicit SimpleCount(UniqueTwoByteChars& label,
      60             :                          bool reportCount=true,
      61             :                          bool reportBytes=true)
      62           0 :       : CountType(),
      63           0 :         label(Move(label)),
      64             :         reportCount(reportCount),
      65           0 :         reportBytes(reportBytes)
      66           0 :     { }
      67             : 
      68           0 :     explicit SimpleCount()
      69           0 :         : CountType(),
      70             :           label(nullptr),
      71             :           reportCount(true),
      72           0 :           reportBytes(true)
      73           0 :     { }
      74             : 
      75           0 :     void destructCount(CountBase& countBase) override {
      76           0 :         Count& count = static_cast<Count&>(countBase);
      77           0 :         count.~Count();
      78           0 :     }
      79             : 
      80           0 :     CountBasePtr makeCount() override { return CountBasePtr(js_new<Count>(*this)); }
      81           0 :     void traceCount(CountBase& countBase, JSTracer* trc) override { }
      82             :     bool count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) override;
      83             :     bool report(JSContext* cx, CountBase& countBase, MutableHandleValue report) override;
      84             : };
      85             : 
      86             : bool
      87           0 : SimpleCount::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node)
      88             : {
      89           0 :     Count& count = static_cast<Count&>(countBase);
      90           0 :     if (reportBytes)
      91           0 :         count.totalBytes_ += node.size(mallocSizeOf);
      92           0 :     return true;
      93             : }
      94             : 
      95             : bool
      96           0 : SimpleCount::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
      97             : {
      98           0 :     Count& count = static_cast<Count&>(countBase);
      99             : 
     100           0 :     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     101           0 :     if (!obj)
     102           0 :         return false;
     103             : 
     104           0 :     RootedValue countValue(cx, NumberValue(count.total_));
     105           0 :     if (reportCount && !DefineProperty(cx, obj, cx->names().count, countValue))
     106           0 :         return false;
     107             : 
     108           0 :     RootedValue bytesValue(cx, NumberValue(count.totalBytes_));
     109           0 :     if (reportBytes && !DefineProperty(cx, obj, cx->names().bytes, bytesValue))
     110           0 :         return false;
     111             : 
     112           0 :     if (label) {
     113           0 :         JSString* labelString = JS_NewUCStringCopyZ(cx, label.get());
     114           0 :         if (!labelString)
     115           0 :             return false;
     116           0 :         RootedValue labelValue(cx, StringValue(labelString));
     117           0 :         if (!DefineProperty(cx, obj, cx->names().label, labelValue))
     118           0 :             return false;
     119             :     }
     120             : 
     121           0 :     report.setObject(*obj);
     122           0 :     return true;
     123             : }
     124             : 
     125             : 
     126             : // A count type that collects all matching nodes in a bucket.
     127           0 : class BucketCount : public CountType {
     128             : 
     129           0 :     struct Count : CountBase {
     130             :         JS::ubi::Vector<JS::ubi::Node::Id> ids_;
     131             : 
     132           0 :         explicit Count(BucketCount& count)
     133           0 :           : CountBase(count),
     134           0 :             ids_()
     135           0 :         { }
     136             :     };
     137             : 
     138             :   public:
     139           0 :     explicit BucketCount()
     140           0 :       : CountType()
     141           0 :     { }
     142             : 
     143           0 :     void destructCount(CountBase& countBase) override {
     144           0 :         Count& count = static_cast<Count&>(countBase);
     145           0 :         count.~Count();
     146           0 :     }
     147             : 
     148           0 :     CountBasePtr makeCount() override { return CountBasePtr(js_new<Count>(*this)); }
     149           0 :     void traceCount(CountBase& countBase, JSTracer* trc) final { }
     150             :     bool count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) override;
     151             :     bool report(JSContext* cx, CountBase& countBase, MutableHandleValue report) override;
     152             : };
     153             : 
     154             : bool
     155           0 : BucketCount::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node)
     156             : {
     157           0 :     Count& count = static_cast<Count&>(countBase);
     158           0 :     return count.ids_.append(node.identifier());
     159             : }
     160             : 
     161             : bool
     162           0 : BucketCount::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
     163             : {
     164           0 :     Count& count = static_cast<Count&>(countBase);
     165             : 
     166           0 :     size_t length = count.ids_.length();
     167           0 :     RootedArrayObject arr(cx, NewDenseFullyAllocatedArray(cx, length));
     168           0 :     if (!arr)
     169           0 :         return false;
     170           0 :     arr->ensureDenseInitializedLength(cx, 0, length);
     171             : 
     172           0 :     for (size_t i = 0; i < length; i++)
     173           0 :         arr->setDenseElement(i, NumberValue(count.ids_[i]));
     174             : 
     175           0 :     report.setObject(*arr);
     176           0 :     return true;
     177             : }
     178             : 
     179             : 
     180             : // A type that categorizes nodes by their JavaScript type -- 'objects',
     181             : // 'strings', 'scripts', and 'other' -- and then passes the nodes to child
     182             : // types.
     183             : //
     184             : // Implementation details of scripts like jitted code are counted under
     185             : // 'scripts'.
     186           0 : class ByCoarseType : public CountType {
     187             :     CountTypePtr objects;
     188             :     CountTypePtr scripts;
     189             :     CountTypePtr strings;
     190             :     CountTypePtr other;
     191             : 
     192           0 :     struct Count : CountBase {
     193           0 :         Count(CountType& type,
     194             :               CountBasePtr& objects,
     195             :               CountBasePtr& scripts,
     196             :               CountBasePtr& strings,
     197             :               CountBasePtr& other)
     198           0 :           : CountBase(type),
     199           0 :             objects(Move(objects)),
     200           0 :             scripts(Move(scripts)),
     201           0 :             strings(Move(strings)),
     202           0 :             other(Move(other))
     203           0 :         { }
     204             : 
     205             :         CountBasePtr objects;
     206             :         CountBasePtr scripts;
     207             :         CountBasePtr strings;
     208             :         CountBasePtr other;
     209             :     };
     210             : 
     211             :   public:
     212           0 :     ByCoarseType(CountTypePtr& objects,
     213             :                  CountTypePtr& scripts,
     214             :                  CountTypePtr& strings,
     215             :                  CountTypePtr& other)
     216           0 :       : CountType(),
     217           0 :         objects(Move(objects)),
     218           0 :         scripts(Move(scripts)),
     219           0 :         strings(Move(strings)),
     220           0 :         other(Move(other))
     221           0 :     { }
     222             : 
     223           0 :     void destructCount(CountBase& countBase) override {
     224           0 :         Count& count = static_cast<Count&>(countBase);
     225           0 :         count.~Count();
     226           0 :     }
     227             : 
     228             :     CountBasePtr makeCount() override;
     229             :     void traceCount(CountBase& countBase, JSTracer* trc) override;
     230             :     bool count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) override;
     231             :     bool report(JSContext* cx, CountBase& countBase, MutableHandleValue report) override;
     232             : };
     233             : 
     234             : CountBasePtr
     235           0 : ByCoarseType::makeCount()
     236             : {
     237           0 :     CountBasePtr objectsCount(objects->makeCount());
     238           0 :     CountBasePtr scriptsCount(scripts->makeCount());
     239           0 :     CountBasePtr stringsCount(strings->makeCount());
     240           0 :     CountBasePtr otherCount(other->makeCount());
     241             : 
     242           0 :     if (!objectsCount || !scriptsCount || !stringsCount || !otherCount)
     243           0 :         return CountBasePtr(nullptr);
     244             : 
     245           0 :     return CountBasePtr(js_new<Count>(*this,
     246             :                                       objectsCount,
     247             :                                       scriptsCount,
     248             :                                       stringsCount,
     249           0 :                                       otherCount));
     250             : }
     251             : 
     252             : void
     253           0 : ByCoarseType::traceCount(CountBase& countBase, JSTracer* trc)
     254             : {
     255           0 :     Count& count = static_cast<Count&>(countBase);
     256           0 :     count.objects->trace(trc);
     257           0 :     count.scripts->trace(trc);
     258           0 :     count.strings->trace(trc);
     259           0 :     count.other->trace(trc);
     260           0 : }
     261             : 
     262             : bool
     263           0 : ByCoarseType::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node)
     264             : {
     265           0 :     Count& count = static_cast<Count&>(countBase);
     266             : 
     267           0 :     switch (node.coarseType()) {
     268             :       case JS::ubi::CoarseType::Object:
     269           0 :         return count.objects->count(mallocSizeOf, node);
     270             :       case JS::ubi::CoarseType::Script:
     271           0 :         return count.scripts->count(mallocSizeOf, node);
     272             :       case JS::ubi::CoarseType::String:
     273           0 :         return count.strings->count(mallocSizeOf, node);
     274             :       case JS::ubi::CoarseType::Other:
     275           0 :         return count.other->count(mallocSizeOf, node);
     276             :       default:
     277           0 :         MOZ_CRASH("bad JS::ubi::CoarseType in JS::ubi::ByCoarseType::count");
     278             :         return false;
     279             :     }
     280             : }
     281             : 
     282             : bool
     283           0 : ByCoarseType::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
     284             : {
     285           0 :     Count& count = static_cast<Count&>(countBase);
     286             : 
     287           0 :     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     288           0 :     if (!obj)
     289           0 :         return false;
     290             : 
     291           0 :     RootedValue objectsReport(cx);
     292           0 :     if (!count.objects->report(cx, &objectsReport) ||
     293           0 :         !DefineProperty(cx, obj, cx->names().objects, objectsReport))
     294           0 :         return false;
     295             : 
     296           0 :     RootedValue scriptsReport(cx);
     297           0 :     if (!count.scripts->report(cx, &scriptsReport) ||
     298           0 :         !DefineProperty(cx, obj, cx->names().scripts, scriptsReport))
     299           0 :         return false;
     300             : 
     301           0 :     RootedValue stringsReport(cx);
     302           0 :     if (!count.strings->report(cx, &stringsReport) ||
     303           0 :         !DefineProperty(cx, obj, cx->names().strings, stringsReport))
     304           0 :         return false;
     305             : 
     306           0 :     RootedValue otherReport(cx);
     307           0 :     if (!count.other->report(cx, &otherReport) ||
     308           0 :         !DefineProperty(cx, obj, cx->names().other, otherReport))
     309           0 :         return false;
     310             : 
     311           0 :     report.setObject(*obj);
     312           0 :     return true;
     313             : }
     314             : 
     315             : 
     316             : // Comparison function for sorting hash table entries by the smallest node ID
     317             : // they counted. Node IDs are stable and unique, which ensures ordering of
     318             : // results never depends on hash table placement or sort algorithm vagaries. The
     319             : // arguments are doubly indirect: they're pointers to elements in an array of
     320             : // pointers to table entries.
     321             : template<typename Entry>
     322           0 : static int compareEntries(const void* lhsVoid, const void* rhsVoid) {
     323           0 :     auto lhs = (*static_cast<const Entry* const*>(lhsVoid))->value()->smallestNodeIdCounted_;
     324           0 :     auto rhs = (*static_cast<const Entry* const*>(rhsVoid))->value()->smallestNodeIdCounted_;
     325             : 
     326             :     // We don't want to just subtract the values, as they're unsigned.
     327           0 :     if (lhs < rhs)
     328           0 :         return 1;
     329           0 :     if (lhs > rhs)
     330           0 :         return -1;
     331           0 :     return 0;
     332             : }
     333             : 
     334             : // A hash map mapping from C strings to counts.
     335             : using CStringCountMap = HashMap<const char*, CountBasePtr, CStringHasher, SystemAllocPolicy>;
     336             : 
     337             : // Convert a HashMap into an object with each key one of the entries from the
     338             : // map and each value the associated count's report. For use during census
     339             : // reporting.
     340             : //
     341             : // `Map` must be a `HashMap` from some key type to a `CountBasePtr`.
     342             : //
     343             : // `GetName` must be a callable type which takes `const Map::Key&` and returns
     344             : // `const char*`.
     345             : template <class Map, class GetName>
     346             : static PlainObject*
     347           0 : countMapToObject(JSContext* cx, Map& map, GetName getName) {
     348             :     // Build a vector of pointers to entries; sort by total; and then use
     349             :     // that to build the result object. This makes the ordering of entries
     350             :     // more interesting, and a little less non-deterministic.
     351             : 
     352           0 :     JS::ubi::Vector<typename Map::Entry*> entries;
     353           0 :     if (!entries.reserve(map.count())) {
     354           0 :         ReportOutOfMemory(cx);
     355           0 :         return nullptr;
     356             :     }
     357             : 
     358           0 :     for (auto r = map.all(); !r.empty(); r.popFront())
     359           0 :         entries.infallibleAppend(&r.front());
     360             : 
     361           0 :     qsort(entries.begin(), entries.length(), sizeof(*entries.begin()),
     362             :           compareEntries<typename Map::Entry>);
     363             : 
     364           0 :     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     365           0 :     if (!obj)
     366           0 :         return nullptr;
     367             : 
     368           0 :     for (auto& entry : entries) {
     369           0 :         CountBasePtr& thenCount = entry->value();
     370           0 :         RootedValue thenReport(cx);
     371           0 :         if (!thenCount->report(cx, &thenReport))
     372           0 :             return nullptr;
     373             : 
     374           0 :         const char* name = getName(entry->key());
     375           0 :         MOZ_ASSERT(name);
     376           0 :         JSAtom* atom = Atomize(cx, name, strlen(name));
     377           0 :         if (!atom)
     378           0 :             return nullptr;
     379             : 
     380           0 :         RootedId entryId(cx, AtomToId(atom));
     381           0 :         if (!DefineProperty(cx, obj, entryId, thenReport))
     382           0 :             return nullptr;
     383             :     }
     384             : 
     385           0 :     return obj;
     386             : }
     387             : 
     388             : 
     389             : // A type that categorizes nodes that are JSObjects by their class name,
     390             : // and places all other nodes in an 'other' category.
     391           0 : class ByObjectClass : public CountType {
     392             :     // A table mapping class names to their counts. Note that we treat js::Class
     393             :     // instances with the same name as equal keys. If you have several
     394             :     // js::Classes with equal names (and we do; as of this writing there were
     395             :     // six named "Object"), you will get several different js::Classes being
     396             :     // counted in the same table entry.
     397             :     using Table = CStringCountMap;
     398             :     using Entry = Table::Entry;
     399             : 
     400           0 :     struct Count : public CountBase {
     401             :         Table table;
     402             :         CountBasePtr other;
     403             : 
     404           0 :         Count(CountType& type, CountBasePtr& other)
     405           0 :           : CountBase(type),
     406           0 :             other(Move(other))
     407           0 :         { }
     408             : 
     409           0 :         bool init() { return table.init(); }
     410             :     };
     411             : 
     412             :     CountTypePtr classesType;
     413             :     CountTypePtr otherType;
     414             : 
     415             :   public:
     416           0 :     ByObjectClass(CountTypePtr& classesType, CountTypePtr& otherType)
     417           0 :         : CountType(),
     418           0 :           classesType(Move(classesType)),
     419           0 :           otherType(Move(otherType))
     420           0 :     { }
     421             : 
     422           0 :     void destructCount(CountBase& countBase) override {
     423           0 :         Count& count = static_cast<Count&>(countBase);
     424           0 :         count.~Count();
     425           0 :     }
     426             : 
     427             :     CountBasePtr makeCount() override;
     428             :     void traceCount(CountBase& countBase, JSTracer* trc) override;
     429             :     bool count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) override;
     430             :     bool report(JSContext* cx, CountBase& countBase, MutableHandleValue report) override;
     431             : };
     432             : 
     433             : CountBasePtr
     434           0 : ByObjectClass::makeCount()
     435             : {
     436           0 :     CountBasePtr otherCount(otherType->makeCount());
     437           0 :     if (!otherCount)
     438           0 :         return nullptr;
     439             : 
     440           0 :     UniquePtr<Count> count(js_new<Count>(*this, otherCount));
     441           0 :     if (!count || !count->init())
     442           0 :         return nullptr;
     443             : 
     444           0 :     return CountBasePtr(count.release());
     445             : }
     446             : 
     447             : void
     448           0 : ByObjectClass::traceCount(CountBase& countBase, JSTracer* trc)
     449             : {
     450           0 :     Count& count = static_cast<Count&>(countBase);
     451           0 :     for (Table::Range r = count.table.all(); !r.empty(); r.popFront())
     452           0 :         r.front().value()->trace(trc);
     453           0 :     count.other->trace(trc);
     454           0 : }
     455             : 
     456             : bool
     457           0 : ByObjectClass::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node)
     458             : {
     459           0 :     Count& count = static_cast<Count&>(countBase);
     460             : 
     461           0 :     const char* className = node.jsObjectClassName();
     462           0 :     if (!className)
     463           0 :         return count.other->count(mallocSizeOf, node);
     464             : 
     465           0 :     Table::AddPtr p = count.table.lookupForAdd(className);
     466           0 :     if (!p) {
     467           0 :         CountBasePtr classCount(classesType->makeCount());
     468           0 :         if (!classCount || !count.table.add(p, className, Move(classCount)))
     469           0 :             return false;
     470             :     }
     471           0 :     return p->value()->count(mallocSizeOf, node);
     472             : }
     473             : 
     474             : bool
     475           0 : ByObjectClass::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
     476             : {
     477           0 :     Count& count = static_cast<Count&>(countBase);
     478             : 
     479           0 :     RootedPlainObject obj(cx, countMapToObject(cx, count.table, [](const char* key) {
     480             :         return key;
     481           0 :     }));
     482           0 :     if (!obj)
     483           0 :         return false;
     484             : 
     485           0 :     RootedValue otherReport(cx);
     486           0 :     if (!count.other->report(cx, &otherReport) ||
     487           0 :         !DefineProperty(cx, obj, cx->names().other, otherReport))
     488           0 :         return false;
     489             : 
     490           0 :     report.setObject(*obj);
     491           0 :     return true;
     492             : }
     493             : 
     494             : 
     495             : // A count type that categorizes nodes by their ubi::Node::typeName.
     496           0 : class ByUbinodeType : public CountType {
     497             :     // Note that, because ubi::Node::typeName promises to return a specific
     498             :     // pointer, not just any string whose contents are correct, we can use their
     499             :     // addresses as hash table keys.
     500             :     using Table = HashMap<const char16_t*, CountBasePtr, DefaultHasher<const char16_t*>,
     501             :                           SystemAllocPolicy>;
     502             :     using Entry = Table::Entry;
     503             : 
     504           0 :     struct Count: public CountBase {
     505             :         Table table;
     506             : 
     507           0 :         explicit Count(CountType& type) : CountBase(type) { }
     508             : 
     509           0 :         bool init() { return table.init(); }
     510             :     };
     511             : 
     512             :     CountTypePtr entryType;
     513             : 
     514             :   public:
     515           0 :     explicit ByUbinodeType(CountTypePtr& entryType)
     516           0 :       : CountType(),
     517           0 :         entryType(Move(entryType))
     518           0 :     { }
     519             : 
     520           0 :     void destructCount(CountBase& countBase) override {
     521           0 :         Count& count = static_cast<Count&>(countBase);
     522           0 :         count.~Count();
     523           0 :     }
     524             : 
     525             :     CountBasePtr makeCount() override;
     526             :     void traceCount(CountBase& countBase, JSTracer* trc) override;
     527             :     bool count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) override;
     528             :     bool report(JSContext* cx, CountBase& countBase, MutableHandleValue report) override;
     529             : };
     530             : 
     531             : CountBasePtr
     532           0 : ByUbinodeType::makeCount()
     533             : {
     534           0 :     UniquePtr<Count> count(js_new<Count>(*this));
     535           0 :     if (!count || !count->init())
     536           0 :         return nullptr;
     537             : 
     538           0 :     return CountBasePtr(count.release());
     539             : }
     540             : 
     541             : void
     542           0 : ByUbinodeType::traceCount(CountBase& countBase, JSTracer* trc)
     543             : {
     544           0 :     Count& count = static_cast<Count&>(countBase);
     545           0 :     for (Table::Range r = count.table.all(); !r.empty(); r.popFront())
     546           0 :         r.front().value()->trace(trc);
     547           0 : }
     548             : 
     549             : bool
     550           0 : ByUbinodeType::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node)
     551             : {
     552           0 :     Count& count = static_cast<Count&>(countBase);
     553             : 
     554           0 :     const char16_t* key = node.typeName();
     555           0 :     MOZ_ASSERT(key);
     556           0 :     Table::AddPtr p = count.table.lookupForAdd(key);
     557           0 :     if (!p) {
     558           0 :         CountBasePtr typesCount(entryType->makeCount());
     559           0 :         if (!typesCount || !count.table.add(p, key, Move(typesCount)))
     560           0 :             return false;
     561             :     }
     562           0 :     return p->value()->count(mallocSizeOf, node);
     563             : }
     564             : 
     565             : bool
     566           0 : ByUbinodeType::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
     567             : {
     568           0 :     Count& count = static_cast<Count&>(countBase);
     569             : 
     570             :     // Build a vector of pointers to entries; sort by total; and then use
     571             :     // that to build the result object. This makes the ordering of entries
     572             :     // more interesting, and a little less non-deterministic.
     573           0 :     JS::ubi::Vector<Entry*> entries;
     574           0 :     if (!entries.reserve(count.table.count()))
     575           0 :         return false;
     576           0 :     for (Table::Range r = count.table.all(); !r.empty(); r.popFront())
     577           0 :         entries.infallibleAppend(&r.front());
     578           0 :     qsort(entries.begin(), entries.length(), sizeof(*entries.begin()), compareEntries<Entry>);
     579             : 
     580             :     // Now build the result by iterating over the sorted vector.
     581           0 :     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     582           0 :     if (!obj)
     583           0 :         return false;
     584           0 :     for (Entry** entryPtr = entries.begin(); entryPtr < entries.end(); entryPtr++) {
     585           0 :         Entry& entry = **entryPtr;
     586           0 :         CountBasePtr& typeCount = entry.value();
     587           0 :         RootedValue typeReport(cx);
     588           0 :         if (!typeCount->report(cx, &typeReport))
     589           0 :             return false;
     590             : 
     591           0 :         const char16_t* name = entry.key();
     592           0 :         MOZ_ASSERT(name);
     593           0 :         JSAtom* atom = AtomizeChars(cx, name, js_strlen(name));
     594           0 :         if (!atom)
     595           0 :             return false;
     596           0 :         RootedId entryId(cx, AtomToId(atom));
     597             : 
     598           0 :         if (!DefineProperty(cx, obj, entryId, typeReport))
     599           0 :             return false;
     600             :     }
     601             : 
     602           0 :     report.setObject(*obj);
     603           0 :     return true;
     604             : }
     605             : 
     606             : 
     607             : // A count type that categorizes nodes by the JS stack under which they were
     608             : // allocated.
     609           0 : class ByAllocationStack : public CountType {
     610             :     using Table = HashMap<StackFrame, CountBasePtr, DefaultHasher<StackFrame>,
     611             :                           SystemAllocPolicy>;
     612             :     using Entry = Table::Entry;
     613             : 
     614           0 :     struct Count : public CountBase {
     615             :         // NOTE: You may look up entries in this table by JS::ubi::StackFrame
     616             :         // key only during traversal, NOT ONCE TRAVERSAL IS COMPLETE. Once
     617             :         // traversal is complete, you may only iterate over it.
     618             :         //
     619             :         // In this hash table, keys are JSObjects (with some indirection), and
     620             :         // we use JSObject identity (that is, address identity) as key
     621             :         // identity. The normal way to support such a table is to make the trace
     622             :         // function notice keys that have moved and re-key them in the
     623             :         // table. However, our trace function does *not* rehash; the first GC
     624             :         // may render the hash table unsearchable.
     625             :         //
     626             :         // This is as it should be:
     627             :         //
     628             :         // First, the heap traversal phase needs lookups by key to work. But no
     629             :         // GC may ever occur during a traversal; this is enforced by the
     630             :         // JS::ubi::BreadthFirst template. So the traceCount function doesn't
     631             :         // need to do anything to help traversal; it never even runs then.
     632             :         //
     633             :         // Second, the report phase needs iteration over the table to work, but
     634             :         // never looks up entries by key. GC may well occur during this phase:
     635             :         // we allocate a Map object, and probably cross-compartment wrappers for
     636             :         // SavedFrame instances as well. If a GC were to occur, it would call
     637             :         // our traceCount function; if traceCount were to re-key, that would
     638             :         // ruin the traversal in progress.
     639             :         //
     640             :         // So depending on the phase, we either don't need re-keying, or
     641             :         // can't abide it.
     642             :         Table table;
     643             :         CountBasePtr noStack;
     644             : 
     645           0 :         Count(CountType& type, CountBasePtr& noStack)
     646           0 :           : CountBase(type),
     647           0 :             noStack(Move(noStack))
     648           0 :         { }
     649           0 :         bool init() { return table.init(); }
     650             :     };
     651             : 
     652             :     CountTypePtr entryType;
     653             :     CountTypePtr noStackType;
     654             : 
     655             :   public:
     656           0 :     ByAllocationStack(CountTypePtr& entryType, CountTypePtr& noStackType)
     657           0 :       : CountType(),
     658           0 :         entryType(Move(entryType)),
     659           0 :         noStackType(Move(noStackType))
     660           0 :     { }
     661             : 
     662           0 :     void destructCount(CountBase& countBase) override {
     663           0 :         Count& count = static_cast<Count&>(countBase);
     664           0 :         count.~Count();
     665           0 :     }
     666             : 
     667             :     CountBasePtr makeCount() override;
     668             :     void traceCount(CountBase& countBase, JSTracer* trc) override;
     669             :     bool count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) override;
     670             :     bool report(JSContext* cx, CountBase& countBase, MutableHandleValue report) override;
     671             : };
     672             : 
     673             : CountBasePtr
     674           0 : ByAllocationStack::makeCount()
     675             : {
     676           0 :     CountBasePtr noStackCount(noStackType->makeCount());
     677           0 :     if (!noStackCount)
     678           0 :         return nullptr;
     679             : 
     680           0 :     UniquePtr<Count> count(js_new<Count>(*this, noStackCount));
     681           0 :     if (!count || !count->init())
     682           0 :         return nullptr;
     683           0 :     return CountBasePtr(count.release());
     684             : }
     685             : 
     686             : void
     687           0 : ByAllocationStack::traceCount(CountBase& countBase, JSTracer* trc)
     688             : {
     689           0 :     Count& count = static_cast<Count&>(countBase);
     690           0 :     for (Table::Range r = count.table.all(); !r.empty(); r.popFront()) {
     691             :         // Trace our child Counts.
     692           0 :         r.front().value()->trace(trc);
     693             : 
     694             :         // Trace the StackFrame that is this entry's key. Do not re-key if
     695             :         // it has moved; see comments for ByAllocationStack::Count::table.
     696           0 :         const StackFrame* key = &r.front().key();
     697           0 :         auto& k = *const_cast<StackFrame*>(key);
     698           0 :         k.trace(trc);
     699             :     }
     700           0 :     count.noStack->trace(trc);
     701           0 : }
     702             : 
     703             : bool
     704           0 : ByAllocationStack::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node)
     705             : {
     706           0 :     Count& count = static_cast<Count&>(countBase);
     707             : 
     708             :     // If we do have an allocation stack for this node, include it in the
     709             :     // count for that stack.
     710           0 :     if (node.hasAllocationStack()) {
     711           0 :         auto allocationStack = node.allocationStack();
     712           0 :         auto p = count.table.lookupForAdd(allocationStack);
     713           0 :         if (!p) {
     714           0 :             CountBasePtr stackCount(entryType->makeCount());
     715           0 :             if (!stackCount || !count.table.add(p, allocationStack, Move(stackCount)))
     716           0 :                 return false;
     717             :         }
     718           0 :         MOZ_ASSERT(p);
     719           0 :         return p->value()->count(mallocSizeOf, node);
     720             :     }
     721             : 
     722             :     // Otherwise, count it in the "no stack" category.
     723           0 :     return count.noStack->count(mallocSizeOf, node);
     724             : }
     725             : 
     726             : bool
     727           0 : ByAllocationStack::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
     728             : {
     729           0 :     Count& count = static_cast<Count&>(countBase);
     730             : 
     731             : #ifdef DEBUG
     732             :     // Check that nothing rehashes our table while we hold pointers into it.
     733           0 :     Generation generation = count.table.generation();
     734             : #endif
     735             : 
     736             :     // Build a vector of pointers to entries; sort by total; and then use
     737             :     // that to build the result object. This makes the ordering of entries
     738             :     // more interesting, and a little less non-deterministic.
     739           0 :     JS::ubi::Vector<Entry*> entries;
     740           0 :     if (!entries.reserve(count.table.count()))
     741           0 :         return false;
     742           0 :     for (Table::Range r = count.table.all(); !r.empty(); r.popFront())
     743           0 :         entries.infallibleAppend(&r.front());
     744           0 :     qsort(entries.begin(), entries.length(), sizeof(*entries.begin()), compareEntries<Entry>);
     745             : 
     746             :     // Now build the result by iterating over the sorted vector.
     747           0 :     Rooted<MapObject*> map(cx, MapObject::create(cx));
     748           0 :     if (!map)
     749           0 :         return false;
     750           0 :     for (Entry** entryPtr = entries.begin(); entryPtr < entries.end(); entryPtr++) {
     751           0 :         Entry& entry = **entryPtr;
     752           0 :         MOZ_ASSERT(entry.key());
     753             : 
     754           0 :         RootedObject stack(cx);
     755           0 :         if (!entry.key().constructSavedFrameStack(cx, &stack) ||
     756           0 :             !cx->compartment()->wrap(cx, &stack))
     757             :             {
     758           0 :                 return false;
     759             :             }
     760           0 :         RootedValue stackVal(cx, ObjectValue(*stack));
     761             : 
     762           0 :         CountBasePtr& stackCount = entry.value();
     763           0 :         RootedValue stackReport(cx);
     764           0 :         if (!stackCount->report(cx, &stackReport))
     765           0 :             return false;
     766             : 
     767           0 :         if (!MapObject::set(cx, map, stackVal, stackReport))
     768           0 :             return false;
     769             :     }
     770             : 
     771           0 :     if (count.noStack->total_ > 0) {
     772           0 :         RootedValue noStackReport(cx);
     773           0 :         if (!count.noStack->report(cx, &noStackReport))
     774           0 :             return false;
     775           0 :         RootedValue noStack(cx, StringValue(cx->names().noStack));
     776           0 :         if (!MapObject::set(cx, map, noStack, noStackReport))
     777           0 :             return false;
     778             :     }
     779             : 
     780           0 :     MOZ_ASSERT(generation == count.table.generation());
     781             : 
     782           0 :     report.setObject(*map);
     783           0 :     return true;
     784             : }
     785             : 
     786             : // A count type that categorizes nodes by their script's filename.
     787           0 : class ByFilename : public CountType {
     788             :     using UniqueCString = UniquePtr<char, JS::FreePolicy>;
     789             : 
     790             :     struct UniqueCStringHasher {
     791             :         using Lookup = UniqueCString;
     792             : 
     793           0 :         static js::HashNumber hash(const Lookup& lookup) {
     794           0 :             return CStringHasher::hash(lookup.get());
     795             :         }
     796             : 
     797           0 :         static bool match(const UniqueCString& key, const Lookup& lookup) {
     798           0 :             return CStringHasher::match(key.get(), lookup.get());
     799             :         }
     800             :     };
     801             : 
     802             :     // A table mapping filenames to their counts. Note that we treat scripts
     803             :     // with the same filename as equivalent. If you have several sources with
     804             :     // the same filename, then all their scripts will get bucketed together.
     805             :     using Table = HashMap<UniqueCString, CountBasePtr, UniqueCStringHasher,
     806             :                           SystemAllocPolicy>;
     807             :     using Entry = Table::Entry;
     808             : 
     809           0 :     struct Count : public CountBase {
     810             :         Table table;
     811             :         CountBasePtr then;
     812             :         CountBasePtr noFilename;
     813             : 
     814           0 :         Count(CountType& type, CountBasePtr&& then, CountBasePtr&& noFilename)
     815           0 :           : CountBase(type)
     816           0 :           , then(Move(then))
     817           0 :           , noFilename(Move(noFilename))
     818           0 :         { }
     819             : 
     820           0 :         bool init() { return table.init(); }
     821             :     };
     822             : 
     823             :     CountTypePtr thenType;
     824             :     CountTypePtr noFilenameType;
     825             : 
     826             :   public:
     827           0 :     ByFilename(CountTypePtr&& thenType, CountTypePtr&& noFilenameType)
     828           0 :         : CountType(),
     829           0 :           thenType(Move(thenType)),
     830           0 :           noFilenameType(Move(noFilenameType))
     831           0 :     { }
     832             : 
     833           0 :     void destructCount(CountBase& countBase) override {
     834           0 :         Count& count = static_cast<Count&>(countBase);
     835           0 :         count.~Count();
     836           0 :     }
     837             : 
     838             :     CountBasePtr makeCount() override;
     839             :     void traceCount(CountBase& countBase, JSTracer* trc) override;
     840             :     bool count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) override;
     841             :     bool report(JSContext* cx, CountBase& countBase, MutableHandleValue report) override;
     842             : };
     843             : 
     844             : CountBasePtr
     845           0 : ByFilename::makeCount()
     846             : {
     847           0 :     CountBasePtr thenCount(thenType->makeCount());
     848           0 :     if (!thenCount)
     849           0 :         return nullptr;
     850             : 
     851           0 :     CountBasePtr noFilenameCount(noFilenameType->makeCount());
     852           0 :     if (!noFilenameCount)
     853           0 :         return nullptr;
     854             : 
     855           0 :     UniquePtr<Count> count(js_new<Count>(*this, Move(thenCount), Move(noFilenameCount)));
     856           0 :     if (!count || !count->init())
     857           0 :         return nullptr;
     858             : 
     859           0 :     return CountBasePtr(count.release());
     860             : }
     861             : 
     862             : void
     863           0 : ByFilename::traceCount(CountBase& countBase, JSTracer* trc)
     864             : {
     865           0 :     Count& count = static_cast<Count&>(countBase);
     866           0 :     for (Table::Range r = count.table.all(); !r.empty(); r.popFront())
     867           0 :         r.front().value()->trace(trc);
     868           0 :     count.noFilename->trace(trc);
     869           0 : }
     870             : 
     871             : bool
     872           0 : ByFilename::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node)
     873             : {
     874           0 :     Count& count = static_cast<Count&>(countBase);
     875             : 
     876           0 :     const char* filename = node.scriptFilename();
     877           0 :     if (!filename)
     878           0 :         return count.noFilename->count(mallocSizeOf, node);
     879             : 
     880           0 :     UniqueCString myFilename(js_strdup(filename));
     881           0 :     if (!myFilename)
     882           0 :         return false;
     883             : 
     884           0 :     Table::AddPtr p = count.table.lookupForAdd(myFilename);
     885           0 :     if (!p) {
     886           0 :         CountBasePtr thenCount(thenType->makeCount());
     887           0 :         if (!thenCount || !count.table.add(p, Move(myFilename), Move(thenCount)))
     888           0 :             return false;
     889             :     }
     890           0 :     return p->value()->count(mallocSizeOf, node);
     891             : }
     892             : 
     893             : bool
     894           0 : ByFilename::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
     895             : {
     896           0 :     Count& count = static_cast<Count&>(countBase);
     897             : 
     898           0 :     RootedPlainObject obj(cx, countMapToObject(cx, count.table, [](const UniqueCString& key) {
     899             :         return key.get();
     900           0 :     }));
     901           0 :     if (!obj)
     902           0 :         return false;
     903             : 
     904           0 :     RootedValue noFilenameReport(cx);
     905           0 :     if (!count.noFilename->report(cx, &noFilenameReport) ||
     906           0 :         !DefineProperty(cx, obj, cx->names().noFilename, noFilenameReport))
     907             :     {
     908           0 :         return false;
     909             :     }
     910             : 
     911           0 :     report.setObject(*obj);
     912           0 :     return true;
     913             : }
     914             : 
     915             : 
     916             : /*** Census Handler *******************************************************************************/
     917             : 
     918             : JS_PUBLIC_API(bool)
     919           0 : CensusHandler::operator() (BreadthFirst<CensusHandler>& traversal,
     920             :                            Node origin, const Edge& edge,
     921             :                            NodeData* referentData, bool first)
     922             : {
     923             :     // We're only interested in the first time we reach edge.referent, not
     924             :     // in every edge arriving at that node.
     925           0 :     if (!first)
     926           0 :         return true;
     927             : 
     928             :     // Don't count nodes outside the debuggee zones. Do count things in the
     929             :     // special atoms zone, but don't traverse their outgoing edges, on the
     930             :     // assumption that they are shared resources that debuggee is using.
     931             :     // Symbols are always allocated in the atoms zone, even if they were
     932             :     // created for exactly one compartment and never shared; this rule will
     933             :     // include such nodes in the count.
     934           0 :     const Node& referent = edge.referent;
     935           0 :     Zone* zone = referent.zone();
     936             : 
     937           0 :     if (census.targetZones.count() == 0 || census.targetZones.has(zone))
     938           0 :         return rootCount->count(mallocSizeOf, referent);
     939             : 
     940           0 :     if (zone == census.atomsZone) {
     941           0 :         traversal.abandonReferent();
     942           0 :         return rootCount->count(mallocSizeOf, referent);
     943             :     }
     944             : 
     945           0 :     traversal.abandonReferent();
     946           0 :     return true;
     947             : }
     948             : 
     949             : 
     950             : /*** Parsing Breakdowns ***************************************************************************/
     951             : 
     952             : static CountTypePtr
     953           0 : ParseChildBreakdown(JSContext* cx, HandleObject breakdown, PropertyName* prop)
     954             : {
     955           0 :     RootedValue v(cx);
     956           0 :     if (!GetProperty(cx, breakdown, breakdown, prop, &v))
     957           0 :         return nullptr;
     958           0 :     return ParseBreakdown(cx, v);
     959             : }
     960             : 
     961             : JS_PUBLIC_API(CountTypePtr)
     962           0 : ParseBreakdown(JSContext* cx, HandleValue breakdownValue)
     963             : {
     964           0 :     if (breakdownValue.isUndefined()) {
     965             :         // Construct the default type, { by: 'count' }
     966           0 :         CountTypePtr simple(cx->new_<SimpleCount>());
     967           0 :         return simple;
     968             :     }
     969             : 
     970           0 :     RootedObject breakdown(cx, ToObject(cx, breakdownValue));
     971           0 :     if (!breakdown)
     972           0 :         return nullptr;
     973             : 
     974           0 :     RootedValue byValue(cx);
     975           0 :     if (!GetProperty(cx, breakdown, breakdown, cx->names().by, &byValue))
     976           0 :         return nullptr;
     977           0 :     RootedString byString(cx, ToString(cx, byValue));
     978           0 :     if (!byString)
     979           0 :         return nullptr;
     980           0 :     RootedLinearString by(cx, byString->ensureLinear(cx));
     981           0 :     if (!by)
     982           0 :         return nullptr;
     983             : 
     984           0 :     if (StringEqualsAscii(by, "count")) {
     985           0 :         RootedValue countValue(cx), bytesValue(cx);
     986           0 :         if (!GetProperty(cx, breakdown, breakdown, cx->names().count, &countValue) ||
     987           0 :             !GetProperty(cx, breakdown, breakdown, cx->names().bytes, &bytesValue))
     988           0 :             return nullptr;
     989             : 
     990             :         // Both 'count' and 'bytes' default to true if omitted, but ToBoolean
     991             :         // naturally treats 'undefined' as false; fix this up.
     992           0 :         if (countValue.isUndefined()) countValue.setBoolean(true);
     993           0 :         if (bytesValue.isUndefined()) bytesValue.setBoolean(true);
     994             : 
     995             :         // Undocumented feature, for testing: { by: 'count' } breakdowns can have
     996             :         // a 'label' property whose value is converted to a string and included as
     997             :         // a 'label' property on the report object.
     998           0 :         RootedValue label(cx);
     999           0 :         if (!GetProperty(cx, breakdown, breakdown, cx->names().label, &label))
    1000           0 :             return nullptr;
    1001             : 
    1002           0 :         UniqueTwoByteChars labelUnique(nullptr);
    1003           0 :         if (!label.isUndefined()) {
    1004           0 :             RootedString labelString(cx, ToString(cx, label));
    1005           0 :             if (!labelString)
    1006           0 :                 return nullptr;
    1007             : 
    1008           0 :             JSFlatString* flat = labelString->ensureFlat(cx);
    1009           0 :             if (!flat)
    1010           0 :                 return nullptr;
    1011             : 
    1012           0 :             AutoStableStringChars chars(cx);
    1013           0 :             if (!chars.initTwoByte(cx, flat))
    1014           0 :                 return nullptr;
    1015             : 
    1016             :             // Since flat strings are null-terminated, and AutoStableStringChars
    1017             :             // null- terminates if it needs to make a copy, we know that
    1018             :             // chars.twoByteChars() is null-terminated.
    1019           0 :             labelUnique = DuplicateString(cx, chars.twoByteChars());
    1020           0 :             if (!labelUnique)
    1021           0 :                 return nullptr;
    1022             :         }
    1023             : 
    1024           0 :         CountTypePtr simple(cx->new_<SimpleCount>(labelUnique,
    1025           0 :                                                   ToBoolean(countValue),
    1026           0 :                                                   ToBoolean(bytesValue)));
    1027           0 :         return simple;
    1028             :     }
    1029             : 
    1030           0 :     if (StringEqualsAscii(by, "bucket"))
    1031           0 :         return CountTypePtr(cx->new_<BucketCount>());
    1032             : 
    1033           0 :     if (StringEqualsAscii(by, "objectClass")) {
    1034           0 :         CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then));
    1035           0 :         if (!thenType)
    1036           0 :             return nullptr;
    1037             : 
    1038           0 :         CountTypePtr otherType(ParseChildBreakdown(cx, breakdown, cx->names().other));
    1039           0 :         if (!otherType)
    1040           0 :             return nullptr;
    1041             : 
    1042           0 :         return CountTypePtr(cx->new_<ByObjectClass>(thenType, otherType));
    1043             :     }
    1044             : 
    1045           0 :     if (StringEqualsAscii(by, "coarseType")) {
    1046           0 :         CountTypePtr objectsType(ParseChildBreakdown(cx, breakdown, cx->names().objects));
    1047           0 :         if (!objectsType)
    1048           0 :             return nullptr;
    1049           0 :         CountTypePtr scriptsType(ParseChildBreakdown(cx, breakdown, cx->names().scripts));
    1050           0 :         if (!scriptsType)
    1051           0 :             return nullptr;
    1052           0 :         CountTypePtr stringsType(ParseChildBreakdown(cx, breakdown, cx->names().strings));
    1053           0 :         if (!stringsType)
    1054           0 :             return nullptr;
    1055           0 :         CountTypePtr otherType(ParseChildBreakdown(cx, breakdown, cx->names().other));
    1056           0 :         if (!otherType)
    1057           0 :             return nullptr;
    1058             : 
    1059           0 :         return CountTypePtr(cx->new_<ByCoarseType>(objectsType,
    1060             :                                                    scriptsType,
    1061             :                                                    stringsType,
    1062           0 :                                                    otherType));
    1063             :     }
    1064             : 
    1065           0 :     if (StringEqualsAscii(by, "internalType")) {
    1066           0 :         CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then));
    1067           0 :         if (!thenType)
    1068           0 :             return nullptr;
    1069             : 
    1070           0 :         return CountTypePtr(cx->new_<ByUbinodeType>(thenType));
    1071             :     }
    1072             : 
    1073           0 :     if (StringEqualsAscii(by, "allocationStack")) {
    1074           0 :         CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then));
    1075           0 :         if (!thenType)
    1076           0 :             return nullptr;
    1077           0 :         CountTypePtr noStackType(ParseChildBreakdown(cx, breakdown, cx->names().noStack));
    1078           0 :         if (!noStackType)
    1079           0 :             return nullptr;
    1080             : 
    1081           0 :         return CountTypePtr(cx->new_<ByAllocationStack>(thenType, noStackType));
    1082             :     }
    1083             : 
    1084           0 :     if (StringEqualsAscii(by, "filename")) {
    1085           0 :         CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then));
    1086           0 :         if (!thenType)
    1087           0 :             return nullptr;
    1088             : 
    1089           0 :         CountTypePtr noFilenameType(ParseChildBreakdown(cx, breakdown, cx->names().noFilename));
    1090           0 :         if (!noFilenameType)
    1091           0 :             return nullptr;
    1092             : 
    1093           0 :         return CountTypePtr(cx->new_<ByFilename>(Move(thenType), Move(noFilenameType)));
    1094             :     }
    1095             : 
    1096             :     // We didn't recognize the breakdown type; complain.
    1097           0 :     RootedString bySource(cx, ValueToSource(cx, byValue));
    1098           0 :     if (!bySource)
    1099           0 :         return nullptr;
    1100             : 
    1101           0 :     JSAutoByteString byBytes(cx, bySource);
    1102           0 :     if (!byBytes)
    1103           0 :         return nullptr;
    1104             : 
    1105           0 :     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CENSUS_BREAKDOWN,
    1106           0 :                                byBytes.ptr());
    1107           0 :     return nullptr;
    1108             : }
    1109             : 
    1110             : // Get the default census breakdown:
    1111             : //
    1112             : // { by: "coarseType",
    1113             : //   objects: { by: "objectClass" },
    1114             : //   other:   { by: "internalType" }
    1115             : // }
    1116             : static CountTypePtr
    1117           0 : GetDefaultBreakdown(JSContext* cx)
    1118             : {
    1119           0 :     CountTypePtr byClass(cx->new_<SimpleCount>());
    1120           0 :     if (!byClass)
    1121           0 :         return nullptr;
    1122             : 
    1123           0 :     CountTypePtr byClassElse(cx->new_<SimpleCount>());
    1124           0 :     if (!byClassElse)
    1125           0 :         return nullptr;
    1126             : 
    1127           0 :     CountTypePtr objects(cx->new_<ByObjectClass>(byClass, byClassElse));
    1128           0 :     if (!objects)
    1129           0 :         return nullptr;
    1130             : 
    1131           0 :     CountTypePtr scripts(cx->new_<SimpleCount>());
    1132           0 :     if (!scripts)
    1133           0 :         return nullptr;
    1134             : 
    1135           0 :     CountTypePtr strings(cx->new_<SimpleCount>());
    1136           0 :     if (!strings)
    1137           0 :         return nullptr;
    1138             : 
    1139           0 :     CountTypePtr byType(cx->new_<SimpleCount>());
    1140           0 :     if (!byType)
    1141           0 :         return nullptr;
    1142             : 
    1143           0 :     CountTypePtr other(cx->new_<ByUbinodeType>(byType));
    1144           0 :     if (!other)
    1145           0 :         return nullptr;
    1146             : 
    1147           0 :     return CountTypePtr(cx->new_<ByCoarseType>(objects,
    1148             :                                                scripts,
    1149             :                                                strings,
    1150           0 :                                                other));
    1151             : }
    1152             : 
    1153             : JS_PUBLIC_API(bool)
    1154           0 : ParseCensusOptions(JSContext* cx, Census& census, HandleObject options, CountTypePtr& outResult)
    1155             : {
    1156           0 :     RootedValue breakdown(cx, UndefinedValue());
    1157           0 :     if (options && !GetProperty(cx, options, options, cx->names().breakdown, &breakdown))
    1158           0 :         return false;
    1159             : 
    1160           0 :     outResult = breakdown.isUndefined()
    1161           0 :         ? GetDefaultBreakdown(cx)
    1162           0 :         : ParseBreakdown(cx, breakdown);
    1163           0 :     return !!outResult;
    1164             : }
    1165             : 
    1166             : } // namespace ubi
    1167             : } // namespace JS

Generated by: LCOV version 1.13