Bug 1280407 - Use SystemAllocPolicy rather that the default with mozilla::Vector in the JS engine r=sfink r=fitzgen r=jandem
☠☠ backed out by 0d735d33bd84 ☠ ☠
authorJon Coppeard <jcoppeard@mozilla.com>
Sat, 18 Jun 2016 10:46:13 +0100
changeset 302054 afc3c6a5f93a4e5b5309659a13a84e7ec5c8fb2e
parent 302053 c6f2a2408e4d4904ba5f0da56f2eb2f58e511672
child 302055 0d735d33bd844616bf224f3fabd56dd694cd03e9
push id78559
push userjcoppeard@mozilla.com
push dateSat, 18 Jun 2016 12:18:11 +0000
treeherdermozilla-inbound@afc3c6a5f93a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink, fitzgen, jandem
bugs1280407
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1280407 - Use SystemAllocPolicy rather that the default with mozilla::Vector in the JS engine r=sfink r=fitzgen r=jandem
js/public/UbiNode.h
js/public/UbiNodeDominatorTree.h
js/public/UbiNodeShortestPaths.h
js/src/builtin/TestingFunctions.cpp
js/src/gc/Statistics.cpp
js/src/jit-test/tests/gc/oomInByteSize.js
js/src/jit-test/tests/gc/oomInFindPath.js
js/src/jsapi-tests/testThreadingThread.cpp
js/src/jsapi.h
js/src/jsgc.cpp
js/src/vm/Stopwatch.cpp
js/src/vm/Stopwatch.h
js/src/vm/UbiNodeCensus.cpp
toolkit/components/perfmonitoring/nsPerformanceStats.cpp
toolkit/components/perfmonitoring/nsPerformanceStats.h
--- a/js/public/UbiNode.h
+++ b/js/public/UbiNode.h
@@ -178,16 +178,19 @@ namespace JS {
 namespace ubi {
 
 using mozilla::Forward;
 using mozilla::Maybe;
 using mozilla::Move;
 using mozilla::RangedPtr;
 using mozilla::Variant;
 
+template <typename T>
+using Vector = mozilla::Vector<T, 0, js::SystemAllocPolicy>;
+
 /*** ubi::StackFrame ******************************************************************************/
 
 // Concrete JS::ubi::StackFrame instances backed by a live SavedFrame object
 // store their strings as JSAtom*, while deserialized stack frames from offline
 // heap snapshots store their strings as const char16_t*. In order to provide
 // zero-cost accessors to these strings in a single interface that works with
 // both cases, we use this variant type.
 class AtomOrTwoByteChars : public Variant<JSAtom*, const char16_t*> {
--- a/js/public/UbiNodeDominatorTree.h
+++ b/js/public/UbiNodeDominatorTree.h
@@ -89,20 +89,20 @@ class JS_PUBLIC_API(DominatorTree)
      * `DominatedSetRange`.
      *
      * @see JS::ubi::DominatorTree::getDominatedSet
      */
     class DominatedNodePtr
     {
         friend class DominatedSetRange;
 
-        const mozilla::Vector<Node>& postOrder;
+        const JS::ubi::Vector<Node>& postOrder;
         const uint32_t* ptr;
 
-        DominatedNodePtr(const mozilla::Vector<Node>& postOrder, const uint32_t* ptr)
+        DominatedNodePtr(const JS::ubi::Vector<Node>& postOrder, const uint32_t* ptr)
           : postOrder(postOrder)
           , ptr(ptr)
         { }
 
       public:
         bool operator!=(const DominatedNodePtr& rhs) const { return ptr != rhs.ptr; }
         void operator++() { ptr++; }
         const Node& operator*() const { return postOrder[*ptr]; }
@@ -113,21 +113,21 @@ class JS_PUBLIC_API(DominatorTree)
      * range-based for loops.
      *
      * @see JS::ubi::DominatorTree::getDominatedSet
      */
     class DominatedSetRange
     {
         friend class DominatedSets;
 
-        const mozilla::Vector<Node>& postOrder;
+        const JS::ubi::Vector<Node>& postOrder;
         const uint32_t* beginPtr;
         const uint32_t* endPtr;
 
-        DominatedSetRange(mozilla::Vector<Node>& postOrder, const uint32_t* begin, const uint32_t* end)
+        DominatedSetRange(JS::ubi::Vector<Node>& postOrder, const uint32_t* begin, const uint32_t* end)
           : postOrder(postOrder)
           , beginPtr(begin)
           , endPtr(end)
         {
             MOZ_ASSERT(begin <= end);
         }
 
       public:
@@ -174,20 +174,20 @@ class JS_PUBLIC_API(DominatorTree)
      * The set of all dominated sets in a dominator tree.
      *
      * Internally stores the sets in a contiguous array, with a side table of
      * indices into that contiguous array to denote the start index of each
      * individual set.
      */
     class DominatedSets
     {
-        mozilla::Vector<uint32_t> dominated;
-        mozilla::Vector<uint32_t> indices;
+        JS::ubi::Vector<uint32_t> dominated;
+        JS::ubi::Vector<uint32_t> indices;
 
-        DominatedSets(mozilla::Vector<uint32_t>&& dominated, mozilla::Vector<uint32_t>&& indices)
+        DominatedSets(JS::ubi::Vector<uint32_t>&& dominated, JS::ubi::Vector<uint32_t>&& indices)
           : dominated(mozilla::Move(dominated))
           , indices(mozilla::Move(indices))
         { }
 
       public:
         // DominatedSets is not copy-able.
         DominatedSets(const DominatedSets& rhs) = delete;
         DominatedSets& operator=(const DominatedSets& rhs) = delete;
@@ -205,17 +205,17 @@ class JS_PUBLIC_API(DominatorTree)
             return *this;
         }
 
         /**
          * Create the DominatedSets given the mapping of a node index to its
          * immediate dominator. Returns `Some` on success, `Nothing` on OOM
          * failure.
          */
-        static mozilla::Maybe<DominatedSets> Create(const mozilla::Vector<uint32_t>& doms) {
+        static mozilla::Maybe<DominatedSets> Create(const JS::ubi::Vector<uint32_t>& doms) {
             auto length = doms.length();
             MOZ_ASSERT(length < UINT32_MAX);
 
             // Create a vector `dominated` holding a flattened set of buckets of
             // immediately dominated children nodes, with a lookup table
             // `indices` mapping from each node to the beginning of its bucket.
             //
             // This has three phases:
@@ -230,18 +230,18 @@ class JS_PUBLIC_API(DominatorTree)
             //    bucket.
             //
             // 3. Iterate over the full set of nodes again, filling in bucket
             //    entries from the end of the bucket's range to its
             //    beginning. This decrements each index as a bucket entry is
             //    filled in. After having filled in all of a bucket's entries,
             //    the index points to the start of the bucket.
 
-            mozilla::Vector<uint32_t> dominated;
-            mozilla::Vector<uint32_t> indices;
+            JS::ubi::Vector<uint32_t> dominated;
+            JS::ubi::Vector<uint32_t> indices;
             if (!dominated.growBy(length) || !indices.growBy(length))
                 return mozilla::Nothing();
 
             // 1
             memset(indices.begin(), 0, length * sizeof(uint32_t));
             for (uint32_t i = 0; i < length; i++)
                 indices[doms[i]]++;
 
@@ -273,63 +273,63 @@ class JS_PUBLIC_API(DominatorTree)
 
             return mozilla::Some(DominatedSets(mozilla::Move(dominated), mozilla::Move(indices)));
         }
 
         /**
          * Get the set of nodes immediately dominated by the node at
          * `postOrder[nodeIndex]`.
          */
-        DominatedSetRange dominatedSet(mozilla::Vector<Node>& postOrder, uint32_t nodeIndex) const {
+        DominatedSetRange dominatedSet(JS::ubi::Vector<Node>& postOrder, uint32_t nodeIndex) const {
             MOZ_ASSERT(postOrder.length() == indices.length());
             MOZ_ASSERT(nodeIndex < indices.length());
             auto end = nodeIndex == indices.length() - 1
                 ? dominated.end()
                 : &dominated[indices[nodeIndex + 1]];
             return DominatedSetRange(postOrder, &dominated[indices[nodeIndex]], end);
         }
     };
 
   private:
     // Data members.
-    mozilla::Vector<Node> postOrder;
+    JS::ubi::Vector<Node> postOrder;
     NodeToIndexMap nodeToPostOrderIndex;
-    mozilla::Vector<uint32_t> doms;
+    JS::ubi::Vector<uint32_t> doms;
     DominatedSets dominatedSets;
-    mozilla::Maybe<mozilla::Vector<JS::ubi::Node::Size>> retainedSizes;
+    mozilla::Maybe<JS::ubi::Vector<JS::ubi::Node::Size>> retainedSizes;
 
   private:
     // We use `UNDEFINED` as a sentinel value in the `doms` vector to signal
     // that we haven't found any dominators for the node at the corresponding
     // index in `postOrder` yet.
     static const uint32_t UNDEFINED = UINT32_MAX;
 
-    DominatorTree(mozilla::Vector<Node>&& postOrder, NodeToIndexMap&& nodeToPostOrderIndex,
-                  mozilla::Vector<uint32_t>&& doms, DominatedSets&& dominatedSets)
+    DominatorTree(JS::ubi::Vector<Node>&& postOrder, NodeToIndexMap&& nodeToPostOrderIndex,
+                  JS::ubi::Vector<uint32_t>&& doms, DominatedSets&& dominatedSets)
         : postOrder(mozilla::Move(postOrder))
         , nodeToPostOrderIndex(mozilla::Move(nodeToPostOrderIndex))
         , doms(mozilla::Move(doms))
         , dominatedSets(mozilla::Move(dominatedSets))
         , retainedSizes(mozilla::Nothing())
     { }
 
-    static uint32_t intersect(mozilla::Vector<uint32_t>& doms, uint32_t finger1, uint32_t finger2) {
+    static uint32_t intersect(JS::ubi::Vector<uint32_t>& doms, uint32_t finger1, uint32_t finger2) {
         while (finger1 != finger2) {
             if (finger1 < finger2)
                 finger1 = doms[finger1];
             else if (finger2 < finger1)
                 finger2 = doms[finger2];
         }
         return finger1;
     }
 
     // Do the post order traversal of the heap graph and populate our
     // predecessor sets.
     static MOZ_MUST_USE bool doTraversal(JSRuntime* rt, AutoCheckCannotGC& noGC, const Node& root,
-                                         mozilla::Vector<Node>& postOrder,
+                                         JS::ubi::Vector<Node>& postOrder,
                                          PredecessorSets& predecessorSets) {
         uint32_t nodeCount = 0;
         auto onNode = [&](const Node& node) {
             nodeCount++;
             if (MOZ_UNLIKELY(nodeCount == UINT32_MAX))
                 return false;
             return postOrder.append(node);
         };
@@ -352,36 +352,36 @@ class JS_PUBLIC_API(DominatorTree)
         PostOrder traversal(rt, noGC);
         return traversal.init() &&
                traversal.addStart(root) &&
                traversal.traverse(onNode, onEdge);
     }
 
     // Populates the given `map` with an entry for each node to its index in
     // `postOrder`.
-    static MOZ_MUST_USE bool mapNodesToTheirIndices(mozilla::Vector<Node>& postOrder,
+    static MOZ_MUST_USE bool mapNodesToTheirIndices(JS::ubi::Vector<Node>& postOrder,
                                                     NodeToIndexMap& map) {
         MOZ_ASSERT(!map.initialized());
         MOZ_ASSERT(postOrder.length() < UINT32_MAX);
         uint32_t length = postOrder.length();
         if (!map.init(length))
             return false;
         for (uint32_t i = 0; i < length; i++)
             map.putNewInfallible(postOrder[i], i);
         return true;
     }
 
     // Convert the Node -> NodeSet predecessorSets to a index -> Vector<index>
     // form.
     static MOZ_MUST_USE bool convertPredecessorSetsToVectors(
         const Node& root,
-        mozilla::Vector<Node>& postOrder,
+        JS::ubi::Vector<Node>& postOrder,
         PredecessorSets& predecessorSets,
         NodeToIndexMap& nodeToPostOrderIndex,
-        mozilla::Vector<mozilla::Vector<uint32_t>>& predecessorVectors)
+        JS::ubi::Vector<JS::ubi::Vector<uint32_t>>& predecessorVectors)
     {
         MOZ_ASSERT(postOrder.length() < UINT32_MAX);
         uint32_t length = postOrder.length();
 
         MOZ_ASSERT(predecessorVectors.length() == 0);
         if (!predecessorVectors.growBy(length))
             return false;
 
@@ -405,17 +405,17 @@ class JS_PUBLIC_API(DominatorTree)
             }
         }
         predecessorSets.finish();
         return true;
     }
 
     // Initialize `doms` such that the immediate dominator of the `root` is the
     // `root` itself and all others are `UNDEFINED`.
-    static MOZ_MUST_USE bool initializeDominators(mozilla::Vector<uint32_t>& doms,
+    static MOZ_MUST_USE bool initializeDominators(JS::ubi::Vector<uint32_t>& doms,
                                                   uint32_t length) {
         MOZ_ASSERT(doms.length() == 0);
         if (!doms.growByUninitialized(length))
             return false;
         doms[length - 1] = length - 1;
         for (uint32_t i = 0; i < length - 1; i++)
             doms[i] = UNDEFINED;
         return true;
@@ -509,17 +509,17 @@ class JS_PUBLIC_API(DominatorTree)
      * that embedders with knowledge of the graph's implementation will do the
      * Right Thing.
      *
      * Returns `mozilla::Nothing()` on OOM failure. It is the caller's
      * responsibility to handle and report the OOM.
      */
     static mozilla::Maybe<DominatorTree>
     Create(JSRuntime* rt, AutoCheckCannotGC& noGC, const Node& root) {
-        mozilla::Vector<Node> postOrder;
+        JS::ubi::Vector<Node> postOrder;
         PredecessorSets predecessorSets;
         if (!predecessorSets.init() || !doTraversal(rt, noGC, root, postOrder, predecessorSets))
             return mozilla::Nothing();
 
         MOZ_ASSERT(postOrder.length() < UINT32_MAX);
         uint32_t length = postOrder.length();
         MOZ_ASSERT(postOrder[length - 1] == root);
 
@@ -528,22 +528,22 @@ class JS_PUBLIC_API(DominatorTree)
         // possible. This greatly improves the performance of this
         // implementation, but we have to pay a little bit of upfront cost to
         // convert our data structures to play along first.
 
         NodeToIndexMap nodeToPostOrderIndex;
         if (!mapNodesToTheirIndices(postOrder, nodeToPostOrderIndex))
             return mozilla::Nothing();
 
-        mozilla::Vector<mozilla::Vector<uint32_t>> predecessorVectors;
+        JS::ubi::Vector<JS::ubi::Vector<uint32_t>> predecessorVectors;
         if (!convertPredecessorSetsToVectors(root, postOrder, predecessorSets, nodeToPostOrderIndex,
                                              predecessorVectors))
             return mozilla::Nothing();
 
-        mozilla::Vector<uint32_t> doms;
+        JS::ubi::Vector<uint32_t> doms;
         if (!initializeDominators(doms, length))
             return mozilla::Nothing();
 
         bool changed = true;
         while (changed) {
             changed = false;
 
             // Iterate over the non-root nodes in reverse post order.
--- a/js/public/UbiNodeShortestPaths.h
+++ b/js/public/UbiNodeShortestPaths.h
@@ -64,29 +64,29 @@ struct JS_PUBLIC_API(BackEdge)
     EdgeName& name() { return name_; }
 
     const JS::ubi::Node& predecessor() const { return predecessor_; }
 };
 
 /**
  * A path is a series of back edges from which we discovered a target node.
  */
-using Path = mozilla::Vector<BackEdge*>;
+using Path = JS::ubi::Vector<BackEdge*>;
 
 /**
  * The `JS::ubi::ShortestPaths` type represents a collection of up to N shortest
  * retaining paths for each of a target set of nodes, starting from the same
  * root node.
  */
 struct JS_PUBLIC_API(ShortestPaths)
 {
   private:
     // Types, type aliases, and data members.
 
-    using BackEdgeVector = mozilla::Vector<BackEdge::Ptr>;
+    using BackEdgeVector = JS::ubi::Vector<BackEdge::Ptr>;
     using NodeToBackEdgeVectorMap = js::HashMap<Node, BackEdgeVector, js::DefaultHasher<Node>,
                                                 js::SystemAllocPolicy>;
 
     struct Handler;
     using Traversal = BreadthFirst<Handler>;
 
     /**
      * A `JS::ubi::BreadthFirst` traversal handler that records back edges for
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2657,21 +2657,26 @@ FindPath(JSContext* cx, unsigned argc, V
         // We can't tolerate the GC moving things around while we're searching
         // the heap. Check that nothing we do causes a GC.
         JS::AutoCheckCannotGC autoCannotGC;
 
         JS::ubi::Node start(args[0]), target(args[1]);
 
         heaptools::FindPathHandler handler(cx, start, target, &nodes, edges);
         heaptools::FindPathHandler::Traversal traversal(cx->runtime(), handler, autoCannotGC);
-        if (!traversal.init() || !traversal.addStart(start))
+        if (!traversal.init() || !traversal.addStart(start)) {
+            ReportOutOfMemory(cx);
             return false;
-
-        if (!traversal.traverse())
+        }
+
+        if (!traversal.traverse()) {
+            if (!cx->isExceptionPending())
+                ReportOutOfMemory(cx);
             return false;
+        }
 
         if (!handler.foundPath) {
             // We didn't find any paths from the start to the target.
             args.rval().setUndefined();
             return true;
         }
     }
 
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -189,23 +189,23 @@ static const PhaseInfo phases[] = {
     // numbers.
 };
 
 static ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } };
 
 // Mapping from all nodes with a multi-parented child to a Vector of all
 // multi-parented children and their descendants. (Single-parented children will
 // not show up in this list.)
-static mozilla::Vector<Phase> dagDescendants[Statistics::NumTimingArrays];
+static mozilla::Vector<Phase, 0, SystemAllocPolicy> dagDescendants[Statistics::NumTimingArrays];
 
 struct AllPhaseIterator {
     int current;
     int baseLevel;
     size_t activeSlot;
-    mozilla::Vector<Phase>::Range descendants;
+    mozilla::Vector<Phase, 0, SystemAllocPolicy>::Range descendants;
 
     explicit AllPhaseIterator(const Statistics::PhaseTimeTable table)
       : current(0)
       , baseLevel(0)
       , activeSlot(PHASE_DAG_NONE)
       , descendants(dagDescendants[PHASE_DAG_NONE].all()) /* empty range */
     {
     }
@@ -824,17 +824,17 @@ Statistics::initialize()
                 return false;
             j++;
         } while (j != PHASE_LIMIT && phases[j].parent != PHASE_MULTI_PARENTS);
     }
     MOZ_ASSERT(dagSlot <= MaxMultiparentPhases - 1);
 
     // Fill in the depth of each node in the tree. Multi-parented nodes
     // have depth 0.
-    mozilla::Vector<Phase> stack;
+    mozilla::Vector<Phase, 0, SystemAllocPolicy> stack;
     if (!stack.append(PHASE_LIMIT)) // Dummy entry to avoid special-casing the first node
         return false;
     for (int i = 0; i < PHASE_LIMIT; i++) {
         if (phases[i].parent == PHASE_NO_PARENT ||
             phases[i].parent == PHASE_MULTI_PARENTS)
         {
             stack.clear();
         } else {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInByteSize.js
@@ -0,0 +1,19 @@
+if (!('oomTest' in this))
+    quit();
+
+oomTest(() => byteSize({}));
+oomTest(() => byteSize({ w: 1, x: 2, y: 3 }));
+oomTest(() => byteSize({ w:1, x:2, y:3, z:4, a:6, 0:0, 1:1, 2:2 }));
+oomTest(() => byteSize([1, 2, 3]));
+oomTest(() => byteSize(function () {}));
+
+function f1() {
+  return 42;
+}
+oomTest(() => byteSizeOfScript(f1));
+
+oomTest(() => byteSize("1234567"));
+oomTest(() => byteSize("千早ぶる神代"));
+
+let s = Symbol();
+oomTest(() => byteSize(s));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInFindPath.js
@@ -0,0 +1,19 @@
+if (!('oomTest' in this))
+    quit();
+
+var o = { w: { x: { y: { z: {} } } } };
+oomTest(() => findPath(o, o.w.x.y.z));
+
+var a = [ , o ];
+oomTest(() => findPath(a, o));
+
+function C() {}
+C.prototype.obj = {};
+var c = new C;
+
+oomTest(() => findPath(c, c.obj));
+
+function f(x) { return function g(y) { return x+y; }; }
+var o = {}
+var gc = f(o);
+oomTest(() => findPath(gc, o));
--- a/js/src/jsapi-tests/testThreadingThread.cpp
+++ b/js/src/jsapi-tests/testThreadingThread.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "jsalloc.h"
+
 #include "mozilla/Atomics.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Move.h"
 #include "mozilla/Vector.h"
 #include "jsapi-tests/tests.h"
 #include "threading/Thread.h"
 
 BEGIN_TEST(testThreadingThreadJoin)
@@ -57,17 +59,17 @@ BEGIN_TEST(testThreadingThreadId)
     return true;
 }
 END_TEST(testThreadingThreadId)
 
 BEGIN_TEST(testThreadingThreadVectorMoveConstruct)
 {
     const static size_t N = 10;
     mozilla::Atomic<int> count(0);
-    mozilla::Vector<js::Thread> v;
+    mozilla::Vector<js::Thread, 0, js::SystemAllocPolicy> v;
     for (auto i : mozilla::MakeRange(N)) {
         CHECK(v.emplaceBack([](mozilla::Atomic<int>* countp){(*countp)++;}, &count));
         CHECK(v.length() == i + 1);
     }
     for (auto& th : v)
         th.join();
     CHECK(count == 10);
     return true;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -6145,16 +6145,18 @@ struct PerformanceGroup {
 
   public:
     // Compatibility with RefPtr<>
     void AddRef();
     void Release();
     uint64_t refCount_;
 };
 
+using PerformanceGroupVector = mozilla::Vector<RefPtr<js::PerformanceGroup>, 0, SystemAllocPolicy>;
+
 /**
  * Commit any Performance Monitoring data.
  *
  * Until `FlushMonitoring` has been called, all PerformanceMonitoring data is invisible
  * to the outside world and can cancelled with a call to `ResetMonitoring`.
  */
 extern JS_PUBLIC_API(bool)
 FlushPerformanceMonitoring(JSRuntime*);
@@ -6203,21 +6205,21 @@ extern JS_PUBLIC_API(void)
 AddCPOWPerformanceDelta(JSRuntime*, uint64_t delta);
 
 typedef bool
 (*StopwatchStartCallback)(uint64_t, void*);
 extern JS_PUBLIC_API(bool)
 SetStopwatchStartCallback(JSRuntime*, StopwatchStartCallback, void*);
 
 typedef bool
-(*StopwatchCommitCallback)(uint64_t, mozilla::Vector<RefPtr<PerformanceGroup>>&, void*);
+(*StopwatchCommitCallback)(uint64_t, PerformanceGroupVector&, void*);
 extern JS_PUBLIC_API(bool)
 SetStopwatchCommitCallback(JSRuntime*, StopwatchCommitCallback, void*);
 
 typedef bool
-(*GetGroupsCallback)(JSContext*, mozilla::Vector<RefPtr<PerformanceGroup>>&, void*);
+(*GetGroupsCallback)(JSContext*, PerformanceGroupVector&, void*);
 extern JS_PUBLIC_API(bool)
 SetGetPerformanceGroupsCallback(JSRuntime*, GetGroupsCallback, void*);
 
 } /* namespace js */
 
 
 #endif /* jsapi_h */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -967,17 +967,17 @@ GCRuntime::setNextScheduled(uint32_t cou
     nextScheduled = count;
 }
 
 bool
 GCRuntime::parseAndSetZeal(const char* str)
 {
     int frequency = -1;
     bool foundFrequency = false;
-    mozilla::Vector<int> zeals;
+    mozilla::Vector<int, 0, SystemAllocPolicy> zeals;
 
     static const struct {
         const char* const zealMode;
         size_t length;
         uint32_t zeal;
     } zealModes[] = {
 #define ZEAL_MODE(name, value) {#name, sizeof(#name) - 1, value},
         JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
@@ -2638,18 +2638,19 @@ GCRuntime::releaseRelocatedArenasWithout
 // Sometimes protect them instead and hold onto them until the next GC sweep
 // phase to catch any pointers to them that didn't get forwarded.
 
 void
 GCRuntime::releaseHeldRelocatedArenas()
 {
 #ifdef DEBUG
     unprotectHeldRelocatedArenas();
-    releaseRelocatedArenas(relocatedArenasToRelease);
+    Arena* arenas = relocatedArenasToRelease;
     relocatedArenasToRelease = nullptr;
+    releaseRelocatedArenas(arenas);
 #endif
 }
 
 void
 GCRuntime::releaseHeldRelocatedArenasWithoutUnlocking(const AutoLockGC& lock)
 {
 #ifdef DEBUG
     unprotectHeldRelocatedArenas();
@@ -3129,17 +3130,17 @@ GCRuntime::decommitArenas(AutoLockGC& lo
 {
     // Verify that all entries in the empty chunks pool are decommitted.
     for (ChunkPool::Iter chunk(emptyChunks(lock)); !chunk.done(); chunk.next())
         MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
 
     // Build a Vector of all current available Chunks. Since we release the
     // gc lock while doing the decommit syscall, it is dangerous to iterate
     // the available list directly, as concurrent operations can modify it.
-    mozilla::Vector<Chunk*> toDecommit;
+    mozilla::Vector<Chunk*, 0, SystemAllocPolicy> toDecommit;
     MOZ_ASSERT(availableChunks(lock).verify());
     for (ChunkPool::Iter iter(availableChunks(lock)); !iter.done(); iter.next()) {
         if (!toDecommit.append(iter.get())) {
             // The OOM handler does a full, immediate decommit, so there is
             // nothing more to do here in any case.
             return onOutOfMallocMemory(lock);
         }
     }
--- a/js/src/vm/Stopwatch.cpp
+++ b/js/src/vm/Stopwatch.cpp
@@ -147,17 +147,17 @@ PerformanceMonitoring::commit()
         return true;
     }
 
     if (startedAtIteration_ != iteration_) {
         // No JS code has been monitored during this iteration.
         return true;
     }
 
-    GroupVector recentGroups;
+    PerformanceGroupVector recentGroups;
     recentGroups_.swap(recentGroups);
 
     bool success = true;
     if (stopwatchCommitCallback)
         success = stopwatchCommitCallback(iteration_, recentGroups, stopwatchCommitClosure);
 
     // Reset immediately, to make sure that we're not hit by the end
     // of a nested event loop (which would cause `commit` to be called
@@ -195,17 +195,17 @@ PerformanceGroupHolder::~PerformanceGrou
 
 void
 PerformanceGroupHolder::unlink()
 {
     initialized_ = false;
     groups_.clear();
 }
 
-const GroupVector*
+const PerformanceGroupVector*
 PerformanceGroupHolder::getGroups(JSContext* cx)
 {
     if (initialized_)
         return &groups_;
 
     if (!runtime_->performanceMonitoring.getGroupsCallback)
         return nullptr;
 
@@ -228,17 +228,17 @@ AutoStopwatch::AutoStopwatch(JSContext* 
 
     JSCompartment* compartment = cx_->compartment();
     if (compartment->scheduledForDestruction)
         return;
 
     JSRuntime* runtime = cx_->runtime();
     iteration_ = runtime->performanceMonitoring.iteration();
 
-    const GroupVector* groups = compartment->performanceMonitoring.getGroups(cx);
+    const PerformanceGroupVector* groups = compartment->performanceMonitoring.getGroups(cx);
     if (!groups) {
       // Either the embedding has not provided any performance
       // monitoring logistics or there was an error that prevents
       // performance monitoring.
       return;
     }
     for (auto group = groups->begin(); group < groups->end(); group++) {
       auto acquired = acquireGroup(*group);
--- a/js/src/vm/Stopwatch.h
+++ b/js/src/vm/Stopwatch.h
@@ -14,18 +14,16 @@
 
 /*
   An API for following in real-time the amount of CPU spent executing
   webpages, add-ons, etc.
 */
 
 namespace js {
 
-typedef mozilla::Vector<RefPtr<js::PerformanceGroup>> GroupVector;
-
 /**
  * A container for performance groups.
  *
  * Performance monitoring deals with the execution duration of code
  * that belongs to components, for a notion of components defined by
  * the embedding.  Typically, in a web browser, a component may be a
  * webpage and/or a frame and/or a module and/or an add-on and/or a
  * sandbox and/or a process etc.
@@ -38,33 +36,33 @@ struct PerformanceGroupHolder {
     /**
      * Get the groups to which this compartment belongs.
      *
      * Pre-condition: Execution must have entered the compartment.
      *
      * May return `nullptr` if the embedding has not initialized
      * support for performance groups.
      */
-    const GroupVector* getGroups(JSContext*);
+    const PerformanceGroupVector* getGroups(JSContext*);
 
     explicit PerformanceGroupHolder(JSRuntime* runtime)
       : runtime_(runtime)
       , initialized_(false)
     {  }
     ~PerformanceGroupHolder();
     void unlink();
   private:
     JSRuntime* runtime_;
 
     // `true` once a call to `getGroups` has succeeded.
     bool initialized_;
 
     // The groups to which this compartment belongs. Filled if and only
     // if `initialized_` is `true`.
-    GroupVector groups_;
+    PerformanceGroupVector groups_;
 };
 
 /**
  * Container class for everything related to performance monitoring.
  */
 struct PerformanceMonitoring {
     /**
      * The number of the current iteration of the event loop.
@@ -287,17 +285,17 @@ struct PerformanceMonitoring {
      * during the same event loop and to avoid committing stale
      * stopwatch results.
      */
     uint64_t startedAtIteration_;
 
     /**
      * Groups used in the current iteration.
      */
-    GroupVector recentGroups_;
+    PerformanceGroupVector recentGroups_;
 
     /**
      * The highest value of the timestamp counter encountered
      * during this iteration.
      */
     uint64_t highestTimestampCounter_;
 };
 
@@ -339,17 +337,17 @@ class AutoStopwatch final {
     // Timestamps captured while starting the stopwatch.
     uint64_t cyclesStart_;
     uint64_t CPOWTimeStart_;
 
     // The CPU on which we started the measure. Defined only
     // if `isMonitoringJank_` is `true`.
     cpuid_t cpuStart_;
 
-    mozilla::Vector<RefPtr<js::PerformanceGroup>> groups_;
+    PerformanceGroupVector groups_;
 
   public:
     // If the stopwatch is active, constructing an instance of
     // AutoStopwatch causes it to become the current owner of the
     // stopwatch.
     //
     // Previous owner is restored upon destruction.
     explicit AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -122,17 +122,17 @@ SimpleCount::report(JSContext* cx, Count
     return true;
 }
 
 
 // A count type that collects all matching nodes in a bucket.
 class BucketCount : public CountType {
 
     struct Count : CountBase {
-        mozilla::Vector<JS::ubi::Node::Id> ids_;
+        JS::ubi::Vector<JS::ubi::Node::Id> ids_;
 
         explicit Count(BucketCount& count)
           : CountBase(count),
             ids_()
         { }
     };
 
   public:
@@ -344,17 +344,17 @@ using CStringCountMap = HashMap<const ch
 // `const char*`.
 template <class Map, class GetName>
 static PlainObject*
 countMapToObject(JSContext* cx, Map& map, GetName getName) {
     // Build a vector of pointers to entries; sort by total; and then use
     // that to build the result object. This makes the ordering of entries
     // more interesting, and a little less non-deterministic.
 
-    mozilla::Vector<typename Map::Entry*> entries;
+    JS::ubi::Vector<typename Map::Entry*> entries;
     if (!entries.reserve(map.count())) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     for (auto r = map.all(); !r.empty(); r.popFront())
         entries.infallibleAppend(&r.front());
 
@@ -565,17 +565,17 @@ ByUbinodeType::count(CountBase& countBas
 bool
 ByUbinodeType::report(JSContext* cx, CountBase& countBase, MutableHandleValue report)
 {
     Count& count = static_cast<Count&>(countBase);
 
     // Build a vector of pointers to entries; sort by total; and then use
     // that to build the result object. This makes the ordering of entries
     // more interesting, and a little less non-deterministic.
-    mozilla::Vector<Entry*> entries;
+    JS::ubi::Vector<Entry*> entries;
     if (!entries.reserve(count.table.count()))
         return false;
     for (Table::Range r = count.table.all(); !r.empty(); r.popFront())
         entries.infallibleAppend(&r.front());
     qsort(entries.begin(), entries.length(), sizeof(*entries.begin()), compareEntries<Entry>);
 
     // Now build the result by iterating over the sorted vector.
     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
@@ -731,17 +731,17 @@ ByAllocationStack::report(JSContext* cx,
 #ifdef DEBUG
     // Check that nothing rehashes our table while we hold pointers into it.
     Generation generation = count.table.generation();
 #endif
 
     // Build a vector of pointers to entries; sort by total; and then use
     // that to build the result object. This makes the ordering of entries
     // more interesting, and a little less non-deterministic.
-    mozilla::Vector<Entry*> entries;
+    JS::ubi::Vector<Entry*> entries;
     if (!entries.reserve(count.table.count()))
         return false;
     for (Table::Range r = count.table.all(); !r.empty(); r.popFront())
         entries.infallibleAppend(&r.front());
     qsort(entries.begin(), entries.length(), sizeof(*entries.begin()), compareEntries<Entry>);
 
     // Now build the result by iterating over the sorted vector.
     Rooted<MapObject*> map(cx, MapObject::create(cx));
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
@@ -1022,23 +1022,28 @@ nsPerformanceStatsService::GetSnapshot(J
 }
 
 uint64_t
 nsPerformanceStatsService::GetNextId() {
   return ++mUIdCounter;
 }
 
 /* static*/ bool
-nsPerformanceStatsService::GetPerformanceGroupsCallback(JSContext* cx, JSGroupVector& out, void* closure) {
+nsPerformanceStatsService::GetPerformanceGroupsCallback(JSContext* cx,
+                                                        js::PerformanceGroupVector& out,
+                                                        void* closure)
+{
   RefPtr<nsPerformanceStatsService> self = reinterpret_cast<nsPerformanceStatsService*>(closure);
   return self->GetPerformanceGroups(cx, out);
 }
 
 bool
-nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx, JSGroupVector& out) {
+nsPerformanceStatsService::GetPerformanceGroups(JSContext* cx,
+                                                js::PerformanceGroupVector& out)
+{
   JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
   if (!global) {
     // While it is possible for a compartment to have no global
     // (e.g. atoms), this compartment is not very interesting for us.
     return true;
   }
 
   // All compartments belong to the top group.
@@ -1131,23 +1136,27 @@ nsPerformanceStatsService::StopwatchStar
   if (NS_FAILED(rv)) {
     return false;
   }
 
   return true;
 }
 
 /*static*/ bool
-nsPerformanceStatsService::StopwatchCommitCallback(uint64_t iteration, JSGroupVector& recentGroups, void* closure) {
+nsPerformanceStatsService::StopwatchCommitCallback(uint64_t iteration,
+                                                   js::PerformanceGroupVector& recentGroups,
+                                                   void* closure)
+{
   RefPtr<nsPerformanceStatsService> self = reinterpret_cast<nsPerformanceStatsService*>(closure);
   return self->StopwatchCommit(iteration, recentGroups);
 }
 
 bool
-nsPerformanceStatsService::StopwatchCommit(uint64_t iteration, JSGroupVector& recentGroups)
+nsPerformanceStatsService::StopwatchCommit(uint64_t iteration,
+                                           js::PerformanceGroupVector& recentGroups)
 {
   MOZ_ASSERT(iteration == mIteration);
   MOZ_ASSERT(!recentGroups.empty());
 
   uint64_t userTimeStop, systemTimeStop;
   nsresult rv = GetResources(&userTimeStop, &systemTimeStop);
   if (NS_FAILED(rv)) {
     return false;
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.h
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.h
@@ -14,17 +14,16 @@
 #include "nsIObserver.h"
 #include "nsPIDOMWindow.h"
 
 #include "nsIPerformanceStats.h"
 
 class nsPerformanceGroup;
 class nsPerformanceGroupDetails;
 
-typedef mozilla::Vector<RefPtr<js::PerformanceGroup>> JSGroupVector;
 typedef mozilla::Vector<RefPtr<nsPerformanceGroup>> GroupVector;
 
 /**
  * A data structure for registering observers interested in
  * performance alerts.
  *
  * Each performance group owns a single instance of this class.
  * Additionally, the service owns instances designed to observe the
@@ -187,18 +186,18 @@ protected:
    * - the compartment's own group.
    *
    * Pre-condition: the VM must have entered the JS compartment.
    *
    * The caller is expected to cache the results of this method, as
    * calling it more than once may not return the same instances of
    * performance groups.
    */
-  bool GetPerformanceGroups(JSContext* cx, JSGroupVector&);
-  static bool GetPerformanceGroupsCallback(JSContext* cx, JSGroupVector&, void* closure);
+  bool GetPerformanceGroups(JSContext* cx, js::PerformanceGroupVector&);
+  static bool GetPerformanceGroupsCallback(JSContext* cx, js::PerformanceGroupVector&, void* closure);
 
 
 
   /**********************************************************
    *
    * Sets of all performance groups, indexed by several keys.
    *
    * These sets do not keep the performance groups alive. Rather, a
@@ -324,18 +323,20 @@ protected:
    * measurement on outer loops is silently cancelled without any call
    * to this method.
    *
    * @param iteration The number of times we have started executing
    * JavaScript code.
    * @param recentGroups The groups that have seen activity during this
    * event.
    */
-  static bool StopwatchCommitCallback(uint64_t iteration, JSGroupVector& recentGroups, void* closure);
-  bool StopwatchCommit(uint64_t iteration, JSGroupVector& recentGroups);
+  static bool StopwatchCommitCallback(uint64_t iteration,
+                                      js::PerformanceGroupVector& recentGroups,
+                                      void* closure);
+  bool StopwatchCommit(uint64_t iteration, js::PerformanceGroupVector& recentGroups);
 
   /**
    * The number of times we have started executing JavaScript code.
    */
   uint64_t mIteration;
 
   /**
    * Commit performance measures of a single group.