Merge inbound to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Sun, 16 Jun 2019 00:45:47 +0300
changeset 479068 69628e2c8fd19479d7ec64de4c9694c845497083
parent 479053 5f48ef706159275c966c80b13cf09d7dcc1db932 (current diff)
parent 479067 51440617724b12fec25ba05b0b2e25241c92683e (diff)
child 479069 556f3b5ab4c5ef4d3f1e56077963390744a5e62b
child 479081 cff891caa80d612bb72a18ecf2dffeb33433ce92
push id88046
push usershindli@mozilla.com
push dateSat, 15 Jun 2019 21:52:09 +0000
treeherderautoland@556f3b5ab4c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone69.0a1
first release with
nightly linux32
69628e2c8fd1 / 69.0a1 / 20190615214733 / files
nightly linux64
69628e2c8fd1 / 69.0a1 / 20190615214733 / files
nightly mac
69628e2c8fd1 / 69.0a1 / 20190615214733 / files
nightly win32
69628e2c8fd1 / 69.0a1 / 20190615214733 / files
nightly win64
69628e2c8fd1 / 69.0a1 / 20190615214733 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/browser/base/content/test/tabPrompts/browser_multiplePrompts.js
+++ b/browser/base/content/test/tabPrompts/browser_multiplePrompts.js
@@ -8,24 +8,24 @@
  * the oldest one.
  */
 add_task(async function() {
   const PROMPTCOUNT = 9;
 
   let contentScript = function(MAX_PROMPT) {
     var i = MAX_PROMPT;
     let fns = ["alert", "prompt", "confirm"];
-    window.addEventListener("message", function() {
+    function openDialog() {
       i--;
       if (i) {
-        window.postMessage("ping", "*");
+        SpecialPowers.Services.tm.dispatchToMainThread(openDialog);
       }
       window[fns[i % 3]](fns[i % 3] + " countdown #" + i);
-    });
-    window.postMessage("ping", "*");
+    }
+    SpecialPowers.Services.tm.dispatchToMainThread(openDialog);
   };
   let url = "data:text/html,<script>(" + encodeURIComponent(contentScript.toSource()) + ")(" + PROMPTCOUNT + ");</script>";
 
   let promptsOpenedPromise = new Promise(function(resolve) {
     let unopenedPromptCount = PROMPTCOUNT;
     Services.obs.addObserver(function observer() {
       unopenedPromptCount--;
       if (!unopenedPromptCount) {
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -665,16 +665,18 @@ support-files =
   examples/scopes-worker.js
   examples/doc-event-handler.html
   examples/doc-editor-scroll.html
   examples/doc-eval-throw.html
   examples/doc-sourceURL-breakpoint.html
   examples/doc-step-in-uninitialized.html
   examples/doc-idb-run-to-completion.html
   examples/doc-scopes-xrays.html
+  examples/doc-message-run-to-completion.html
+  examples/doc-message-run-to-completion-frame.html
 
 [browser_dbg-asm.js]
 [browser_dbg-audiocontext.js]
 [browser_dbg-async-stepping.js]
 [browser_dbg-sourcemapped-breakpoint-console.js]
 skip-if = (os == "win" && ccov) # Bug 1453549
 [browser_dbg-xhr-breakpoints.js]
 [browser_dbg-xhr-run-to-completion.js]
@@ -804,8 +806,9 @@ skip-if = true
 [browser_dbg-worker-scopes.js]
 skip-if = (os == 'linux' && debug) || ccov #Bug 1456013
 [browser_dbg-event-handler.js]
 [browser_dbg-eval-throw.js]
 [browser_dbg-sourceURL-breakpoint.js]
 [browser_dbg-old-breakpoint.js]
 [browser_dbg-idb-run-to-completion.js]
 [browser_dbg-scopes-xrays.js]
+[browser_dbg-message-run-to-completion.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-message-run-to-completion.js
@@ -0,0 +1,21 @@
+/* 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/>. */
+
+// Test that messages from postMessage calls are not delivered while paused in
+// the debugger.
+add_task(async function() {
+  const dbg = await initDebugger("doc-message-run-to-completion.html");
+  invokeInTab("test", "doc-message-run-to-completion.html");
+  await waitForPaused(dbg);
+  let result = await dbg.client.evaluate("event.data");
+  is(result.result, "first", "first message delivered in order");
+  resume(dbg);
+  await waitForPaused(dbg);
+  result = await dbg.client.evaluate("event.data");
+  is(result.result, "second", "second message delivered in order");
+  resume(dbg);
+  await waitForPaused(dbg);
+  result = await dbg.client.evaluate("event.data");
+  is(result.result, "third", "third message delivered in order");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/examples/doc-message-run-to-completion-frame.html
@@ -0,0 +1,1 @@
+<span>HELLO</span>
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/examples/doc-message-run-to-completion.html
@@ -0,0 +1,19 @@
+<iframe id="iframe" src="doc-message-run-to-completion-frame.html"></iframe>
+<script>
+function test() {
+  var messageName = "test-message";
+
+  function handleMessage(event) {
+    // While paused here, the additional posted messages should not be delivered.
+    // The debugger should pause three times.
+    debugger;
+  }
+
+  window.addEventListener("message", handleMessage, true);
+  document.getElementById("iframe").contentWindow.addEventListener("message", handleMessage, true);
+
+  window.postMessage("first", "*");
+  document.getElementById("iframe").contentWindow.postMessage("second", "*");
+  window.postMessage("third", "*");
+}
+</script>
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -96,16 +96,17 @@
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
 #include "mozilla/dom/StyleSheetList.h"
 #include "mozilla/dom/SVGUseElement.h"
 #include "mozilla/net/CookieSettings.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/CDATASection.h"
 #include "mozilla/dom/ProcessingInstruction.h"
+#include "mozilla/dom/PostMessageEvent.h"
 #include "nsDOMString.h"
 #include "nsNodeUtils.h"
 #include "nsLayoutUtils.h"  // for GetFrameForPoint
 #include "nsIFrame.h"
 #include "nsIBrowserChild.h"
 
 #include "nsRange.h"
 #include "mozilla/dom/DocumentType.h"
@@ -11082,16 +11083,17 @@ static void FireOrClearDelayedEvents(nsT
       fm->FireDelayedEvents(aDocuments[i]);
       RefPtr<PresShell> presShell = aDocuments[i]->GetPresShell();
       if (presShell) {
         // Only fire events for active documents.
         bool fire = aFireEvents && aDocuments[i]->GetInnerWindow() &&
                     aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
         presShell->FireOrClearDelayedEvents(fire);
       }
+      aDocuments[i]->FireOrClearPostMessageEvents(aFireEvents);
     }
   }
 }
 
 void Document::PreloadPictureClosed() {
   MOZ_ASSERT(mPreloadPictureDepth > 0);
   mPreloadPictureDepth--;
   if (mPreloadPictureDepth == 0) {
@@ -11438,16 +11440,37 @@ void Document::UnsuppressEventHandlingAn
 }
 
 void Document::AddSuspendedChannelEventQueue(net::ChannelEventQueue* aQueue) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(EventHandlingSuppressed());
   mSuspendedQueues.AppendElement(aQueue);
 }
 
+bool Document::SuspendPostMessageEvent(PostMessageEvent* aEvent) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (EventHandlingSuppressed() || !mSuspendedPostMessageEvents.IsEmpty()) {
+    mSuspendedPostMessageEvents.AppendElement(aEvent);
+    return true;
+  }
+  return false;
+}
+
+void Document::FireOrClearPostMessageEvents(bool aFireEvents) {
+  nsTArray<RefPtr<PostMessageEvent>> events;
+  events.SwapElements(mSuspendedPostMessageEvents);
+
+  if (aFireEvents) {
+    for (PostMessageEvent* event : events) {
+      event->Run();
+    }
+  }
+}
+
 static bool SetSuppressedEventListenerInSubDocument(Document* aDocument,
                                                     void* aData) {
   aDocument->SetSuppressedEventListener(static_cast<EventListener*>(aData));
   return true;
 }
 
 void Document::SetSuppressedEventListener(EventListener* aListener) {
   mSuppressedEventListener = aListener;
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -258,16 +258,17 @@ class ChannelEventQueue;
 
 namespace mozilla {
 namespace dom {
 
 class Document;
 class DOMStyleSheetSetList;
 class ResizeObserver;
 class ResizeObserverController;
+class PostMessageEvent;
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE NS_DEFINE_EVENT_STATE_MACRO(0)
 // Window activation status
 #define NS_DOCUMENT_STATE_WINDOW_INACTIVE NS_DEFINE_EVENT_STATE_MACRO(1)
 
@@ -2878,16 +2879,28 @@ class Document : public nsINode,
   /**
    * Note a ChannelEventQueue which has been suspended on the document's behalf
    * to prevent XHRs from running content scripts while event handling is
    * suppressed. The document is responsible for resuming the queue after
    * event handling is unsuppressed.
    */
   void AddSuspendedChannelEventQueue(net::ChannelEventQueue* aQueue);
 
+  /**
+   * Returns true if a postMessage event should be suspended instead of running.
+   * The document is responsible for running the event later, in the order they
+   * were received.
+   */
+  bool SuspendPostMessageEvent(PostMessageEvent* aEvent);
+
+  /**
+   * Run any suspended postMessage events, or clear them.
+   */
+  void FireOrClearPostMessageEvents(bool aFireEvents);
+
   void SetHasDelayedRefreshEvent() { mHasDelayedRefreshEvent = true; }
 
   /**
    * Flag whether we're about to fire the window's load event for this document.
    */
   void SetLoadEventFiring(bool aFiring) { mLoadEventFiring = aFiring; }
 
   /**
@@ -4825,16 +4838,20 @@ class Document : public nsINode,
   RefPtr<Document> mDisplayDocument;
 
   uint32_t mEventsSuppressed;
 
   // Any XHR ChannelEventQueues that were suspended on this document while
   // events were suppressed.
   nsTArray<RefPtr<net::ChannelEventQueue>> mSuspendedQueues;
 
+  // Any postMessage events that were suspended on this document while events
+  // were suppressed.
+  nsTArray<RefPtr<PostMessageEvent>> mSuspendedPostMessageEvents;
+
   RefPtr<EventListener> mSuppressedEventListener;
 
   /**
    * https://html.spec.whatwg.org/#ignore-destructive-writes-counter
    */
   uint32_t mIgnoreDestructiveWritesCounter;
 
   /**
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -67,16 +67,32 @@ PostMessageEvent::Run() {
   // that's probably better than crashing.
 
   RefPtr<nsGlobalWindowInner> targetWindow;
   if (mTargetWindow->IsClosedOrClosing() ||
       !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
       targetWindow->IsDying())
     return NS_OK;
 
+  // If the window's document has suppressed event handling, hand off this event
+  // for running later. We check the top window's document so that when multiple
+  // same-origin windows exist in the same top window, postMessage events will
+  // be delivered in the same order they were posted, regardless of which window
+  // they were posted to.
+  if (nsCOMPtr<nsPIDOMWindowOuter> topWindow =
+          targetWindow->GetOuterWindow()->GetTop()) {
+    if (nsCOMPtr<nsPIDOMWindowInner> topInner =
+            topWindow->GetCurrentInnerWindow()) {
+      if (topInner->GetExtantDoc() &&
+          topInner->GetExtantDoc()->SuspendPostMessageEvent(this)) {
+        return NS_OK;
+      }
+    }
+  }
+
   JSAutoRealm ar(cx, targetWindow->GetWrapper());
 
   // Ensure that any origin which might have been provided is the origin of this
   // window's document.  Note that we do this *now* instead of when postMessage
   // is called because the target window might have been navigated to a
   // different location between then and now.  If this check happened when
   // postMessage was called, it would be fairly easy for a malicious webpage to
   // intercept messages intended for another site by carefully timing navigation
--- a/js/public/MemoryFunctions.h
+++ b/js/public/MemoryFunctions.h
@@ -71,17 +71,18 @@ namespace JS {
   _(DOMBinding)                          \
   _(CTypeFFIType)                        \
   _(CTypeFFITypeElements)                \
   _(CTypeFunctionInfo)                   \
   _(CTypeFieldInfo)                      \
   _(CDataBufferPtr)                      \
   _(CDataBuffer)                         \
   _(CClosureInfo)                        \
-  _(CTypesInt64)
+  _(CTypesInt64)                         \
+  _(PerfMeasurement)
 
 enum class MemoryUse : uint8_t {
 #define DEFINE_MEMORY_USE(Name) Name,
   JS_FOR_EACH_PUBLIC_MEMORY_USE(DEFINE_MEMORY_USE)
 #undef DEFINE_MEMORY_USE
 };
 
 /**
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2852,17 +2852,17 @@ void TypeDescr::traceInstances(JSTracer*
 namespace {
 
 struct TraceListVisitor {
   typedef Vector<int32_t, 0, SystemAllocPolicy> VectorType;
   VectorType stringOffsets, objectOffsets, valueOffsets;
 
   void visitReference(ReferenceTypeDescr& descr, uint8_t* mem);
 
-  bool fillList(Vector<int32_t>& entries);
+  bool fillList(Vector<uint32_t>& entries);
 };
 
 }  // namespace
 
 void TraceListVisitor::visitReference(ReferenceTypeDescr& descr, uint8_t* mem) {
   VectorType* offsets;
   // TODO/AnyRef-boxing: Once a WasmAnyRef is no longer just a JSObject*
   // we must revisit this structure.
@@ -2884,55 +2884,62 @@ void TraceListVisitor::visitReference(Re
   }
 
   AutoEnterOOMUnsafeRegion oomUnsafe;
   if (!offsets->append((uintptr_t)mem)) {
     oomUnsafe.crash("TraceListVisitor::visitReference");
   }
 }
 
-bool TraceListVisitor::fillList(Vector<int32_t>& entries) {
-  return entries.appendAll(stringOffsets) && entries.append(-1) &&
-         entries.appendAll(objectOffsets) && entries.append(-1) &&
-         entries.appendAll(valueOffsets) && entries.append(-1);
+bool TraceListVisitor::fillList(Vector<uint32_t>& entries) {
+  return entries.append(stringOffsets.length()) &&
+         entries.append(objectOffsets.length()) &&
+         entries.append(valueOffsets.length()) &&
+         entries.appendAll(stringOffsets) && entries.appendAll(objectOffsets) &&
+         entries.appendAll(valueOffsets);
 }
 
 static bool CreateTraceList(JSContext* cx, HandleTypeDescr descr) {
   // Trace lists are only used for inline typed objects. We don't use them
   // for larger objects, both to limit the size of the trace lists and
   // because tracing outline typed objects is considerably more complicated
   // than inline ones.
   if (!InlineTypedObject::canAccommodateType(descr) || descr->transparent()) {
     return true;
   }
 
   TraceListVisitor visitor;
   visitReferences(*descr, nullptr, visitor);
 
-  Vector<int32_t> entries(cx);
+  Vector<uint32_t> entries(cx);
   if (!visitor.fillList(entries)) {
     return false;
   }
 
   // Trace lists aren't necessary for descriptors with no references.
   MOZ_ASSERT(entries.length() >= 3);
   if (entries.length() == 3) {
+    MOZ_ASSERT(entries[0] == 0 && entries[1] == 0 && entries[2] == 0);
     return true;
   }
 
-  int32_t* list = cx->pod_malloc<int32_t>(entries.length());
+  uint32_t* list = cx->pod_malloc<uint32_t>(entries.length());
   if (!list) {
     return false;
   }
 
   PodCopy(list, entries.begin(), entries.length());
 
-  descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(list));
+  size_t size = entries.length() * sizeof(uint32_t);
+  InitReservedSlot(descr, JS_DESCR_SLOT_TRACE_LIST, list, size,
+                   MemoryUse::TypeDescrTraceList);
   return true;
 }
 
 /* static */
 void TypeDescr::finalize(FreeOp* fop, JSObject* obj) {
   TypeDescr& descr = obj->as<TypeDescr>();
   if (descr.hasTraceList()) {
-    fop->free_(const_cast<int32_t*>(descr.traceList()));
+    auto list = const_cast<uint32_t*>(descr.traceList());
+    size_t size = (3 + list[0] + list[1] + list[2]) * sizeof(uint32_t);
+    fop->free_(obj, list, size, MemoryUse::TypeDescrTraceList);
   }
 }
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -179,27 +179,28 @@ class TypeDescr : public NativeObject {
 
   // Type descriptors may contain a list of their references for use during
   // scanning. Marking code is optimized to use this list to mark inline
   // typed objects, rather than the slower trace hook. This list is only
   // specified when (a) the descriptor is short enough that it can fit in an
   // InlineTypedObject, and (b) the descriptor contains at least one
   // reference. Otherwise its value is undefined.
   //
-  // The list is three consecutive arrays of int32_t offsets, with each array
-  // terminated by -1. The arrays store offsets of string, object/anyref, and
-  // value references in the descriptor, in that order.
+  // The list is three consecutive arrays of uint32_t offsets, preceded by a
+  // header consisting of the length of each array. The arrays store offsets of
+  // string, object/anyref, and value references in the descriptor, in that
+  // order.
   // TODO/AnyRef-boxing: once anyref has a more complicated structure, we must
   // revisit this.
   MOZ_MUST_USE bool hasTraceList() const {
     return !getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).isUndefined();
   }
-  const int32_t* traceList() const {
+  const uint32_t* traceList() const {
     MOZ_ASSERT(hasTraceList());
-    return reinterpret_cast<int32_t*>(
+    return reinterpret_cast<uint32_t*>(
         getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
   }
 
   void initInstances(const JSRuntime* rt, uint8_t* mem, size_t length);
   void traceInstances(JSTracer* trace, uint8_t* mem, size_t length);
 
   static void finalize(FreeOp* fop, JSObject* obj);
 };
--- a/js/src/gc/GCEnum.h
+++ b/js/src/gc/GCEnum.h
@@ -103,17 +103,21 @@ enum class ZealMode {
   _(ScopeData)                             \
   _(WeakMapObject)                         \
   _(ShapeKids)                             \
   _(ShapeCache)                            \
   _(ModuleBindingMap)                      \
   _(BaselineScript)                        \
   _(IonScript)                             \
   _(ArgumentsData)                         \
-  _(RareArgumentsData)
+  _(RareArgumentsData)                     \
+  _(RegExpStatics)                         \
+  _(RegExpSharedBytecode)                  \
+  _(TypedArrayElements)                    \
+  _(TypeDescrTraceList)
 
 #define JS_FOR_EACH_MEMORY_USE(_)  \
   JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \
   JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
 
 enum class MemoryUse : uint8_t {
 #define DEFINE_MEMORY_USE(Name) Name,
   JS_FOR_EACH_MEMORY_USE(DEFINE_MEMORY_USE)
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1404,17 +1404,17 @@ void js::GCMarker::lazilyMarkChildren(Ob
   if (JSFunction* fun = group->maybeInterpretedFunction()) {
     traverseEdge(group, static_cast<JSObject*>(fun));
   }
 }
 
 void JS::BigInt::traceChildren(JSTracer* trc) { return; }
 
 template <typename Functor>
-static void VisitTraceList(const Functor& f, const int32_t* traceList,
+static void VisitTraceList(const Functor& f, const uint32_t* traceList,
                            uint8_t* memory);
 
 // Call the trace hook set on the object, if present. If further tracing of
 // NativeObject fields is required, this will return the native object.
 enum class CheckGeneration { DoChecks, NoChecks };
 template <typename Functor>
 static inline NativeObject* CallTraceHook(Functor&& f, JSTracer* trc,
                                           JSObject* obj,
@@ -1444,32 +1444,33 @@ static inline NativeObject* CallTraceHoo
 
   if (!clasp->isNative()) {
     return nullptr;
   }
   return &obj->as<NativeObject>();
 }
 
 template <typename Functor>
-static void VisitTraceList(const Functor& f, const int32_t* traceList,
+static void VisitTraceList(const Functor& f, const uint32_t* traceList,
                            uint8_t* memory) {
-  while (*traceList != -1) {
+  size_t stringCount = *traceList++;
+  size_t objectCount = *traceList++;
+  size_t valueCount = *traceList++;
+  for (size_t i = 0; i < stringCount; i++) {
     f(reinterpret_cast<JSString**>(memory + *traceList));
     traceList++;
   }
-  traceList++;
-  while (*traceList != -1) {
+  for (size_t i = 0; i < objectCount; i++) {
     JSObject** objp = reinterpret_cast<JSObject**>(memory + *traceList);
     if (*objp) {
       f(objp);
     }
     traceList++;
   }
-  traceList++;
-  while (*traceList != -1) {
+  for (size_t i = 0; i < valueCount; i++) {
     f(reinterpret_cast<Value*>(memory + *traceList));
     traceList++;
   }
 }
 
 /*** Mark-stack Marking *****************************************************/
 
 GCMarker::MarkQueueProgress GCMarker::processMarkQueue() {
--- a/js/src/gc/Scheduling.h
+++ b/js/src/gc/Scheduling.h
@@ -774,16 +774,18 @@ class MemoryTracker {
   using Map = HashMap<Key, size_t, Hasher, SystemAllocPolicy>;
 
   // Map containing the allocated size associated with each instance of a
   // container that uses ZoneAllocPolicy.
   using ZoneAllocPolicyMap =
       HashMap<ZoneAllocPolicy*, size_t, DefaultHasher<ZoneAllocPolicy*>,
               SystemAllocPolicy>;
 
+  bool allowMultipleAssociations(MemoryUse use) const;
+
   size_t getAndRemoveEntry(const Key& key, LockGuard<Mutex>& lock);
 
   Mutex mutex;
   Map map;
   ZoneAllocPolicyMap policyMap;
 };
 
 #endif  // DEBUG
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -693,27 +693,37 @@ void MemoryTracker::checkEmptyOnDestroy(
     for (auto r = policyMap.all(); !r.empty(); r.popFront()) {
       fprintf(stderr, "  %p 0x%zx\n", r.front().key(), r.front().value());
     }
   }
 
   MOZ_ASSERT(ok);
 }
 
+inline bool MemoryTracker::allowMultipleAssociations(MemoryUse use) const {
+  // For most uses only one association is possible for each GC thing. Allow a
+  // one-to-many relationship only where necessary.
+  return use == MemoryUse::RegExpSharedBytecode;
+}
+
 void MemoryTracker::trackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
   MOZ_ASSERT(cell->isTenured());
 
   LockGuard<Mutex> lock(mutex);
 
   Key key{cell, use};
   AutoEnterOOMUnsafeRegion oomUnsafe;
   auto ptr = map.lookupForAdd(key);
   if (ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("Association already present: %p 0x%zx %s", cell,
-                            nbytes, MemoryUseName(use));
+    if (!allowMultipleAssociations(use)) {
+      MOZ_CRASH_UNSAFE_PRINTF("Association already present: %p 0x%zx %s", cell,
+                              nbytes, MemoryUseName(use));
+    }
+    ptr->value() += nbytes;
+    return;
   }
 
   if (!map.add(ptr, key, nbytes)) {
     oomUnsafe.crash("MemoryTracker::noteExternalAlloc");
   }
 }
 
 void MemoryTracker::untrackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
@@ -722,23 +732,36 @@ void MemoryTracker::untrackMemory(Cell* 
   LockGuard<Mutex> lock(mutex);
 
   Key key{cell, use};
   auto ptr = map.lookup(key);
   if (!ptr) {
     MOZ_CRASH_UNSAFE_PRINTF("Association not found: %p 0x%zx %s", cell, nbytes,
                             MemoryUseName(use));
   }
-  if (ptr->value() != nbytes) {
+
+  if (!allowMultipleAssociations(use) && ptr->value() != nbytes) {
     MOZ_CRASH_UNSAFE_PRINTF(
         "Association for %p %s has different size: "
         "expected 0x%zx but got 0x%zx",
         cell, MemoryUseName(use), ptr->value(), nbytes);
   }
-  map.remove(ptr);
+
+  if (ptr->value() < nbytes) {
+    MOZ_CRASH_UNSAFE_PRINTF(
+        "Association for %p %s size is too small: "
+        "expected at least 0x%zx but got 0x%zx",
+        cell, MemoryUseName(use), nbytes, ptr->value());
+  }
+
+  ptr->value() -= nbytes;
+
+  if (ptr->value() == 0) {
+    map.remove(ptr);
+  }
 }
 
 void MemoryTracker::swapMemory(Cell* a, Cell* b, MemoryUse use) {
   MOZ_ASSERT(a->isTenured());
   MOZ_ASSERT(b->isTenured());
 
   Key ka{a, use};
   Key kb{b, use};
--- a/js/src/irregexp/RegExpEngine.h
+++ b/js/src/irregexp/RegExpEngine.h
@@ -63,24 +63,20 @@ struct RegExpCompileData
     RegExpTree* tree;
     bool simple;
     bool contains_anchor;
     int capture_count;
 };
 
 struct RegExpCode
 {
-    jit::JitCode* jitCode;
-    uint8_t* byteCode;
+    jit::JitCode* jitCode = nullptr;
+    uint8_t* byteCode = nullptr;
 
-    RegExpCode()
-      : jitCode(nullptr), byteCode(nullptr)
-    {}
-
-    bool empty() {
+    bool empty() const {
         return !jitCode && !byteCode;
     }
 
     void destroy() {
         js_free(byteCode);
     }
 };
 
--- a/js/src/irregexp/RegExpInterpreter.cpp
+++ b/js/src/irregexp/RegExpInterpreter.cpp
@@ -125,18 +125,19 @@ irregexp::InterpretCode(JSContext* cx, c
 
     uint32_t current_char = current ? chars[current - 1] : '\n';
 
     RegExpStackCursor stack(cx);
 
     if (!stack.init())
         return RegExpRunStatus_Error;
 
-    int32_t numRegisters = Load32Aligned(pc);
-    pc += 4;
+    auto header = reinterpret_cast<const RegExpByteCodeHeader*>(byteCode);
+    int32_t numRegisters = header->numRegisters;
+    pc += sizeof(RegExpByteCodeHeader);
 
     // Most of the time we need 8 or fewer registers.  Specify an initial
     // size of 8 here, therefore, so that the vector contents can be stack
     // allocated in the majority of cases.  See bug 1387394.
     Vector<int32_t, 8, SystemAllocPolicy> registers;
     if (!registers.growByUninitialized(numRegisters)) {
         ReportOutOfMemory(cx);
         return RegExpRunStatus_Error;
--- a/js/src/irregexp/RegExpMacroAssembler.cpp
+++ b/js/src/irregexp/RegExpMacroAssembler.cpp
@@ -106,33 +106,38 @@ InterpretedRegExpMacroAssembler::Interpr
   : RegExpMacroAssembler(cx, *alloc, numSavedRegisters),
     pc_(0),
     advance_current_start_(0),
     advance_current_offset_(0),
     advance_current_end_(kInvalidPC),
     buffer_(nullptr),
     length_(0)
 {
-    // The first int32 word is the number of registers.
+    // The first int32 word is the size of the buffer.
+    Emit32(0);
+    // The second int32 word is the number of registers.
     Emit32(0);
 }
 
 InterpretedRegExpMacroAssembler::~InterpretedRegExpMacroAssembler()
 {
     js_free(buffer_);
 }
 
 RegExpCode
 InterpretedRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only)
 {
     Bind(&backtrack_);
     Emit(BC_POP_BT, 0);
 
-    // Update the number of registers.
-    *(int32_t*)buffer_ = num_registers_;
+    // Update the buffer length and number of registers.
+    MOZ_ASSERT(size_t(length_) > sizeof(RegExpByteCodeHeader));
+    auto header = reinterpret_cast<RegExpByteCodeHeader*>(buffer_);
+    header->length = length_;
+    header->numRegisters = num_registers_;
 
     RegExpCode res;
     res.byteCode = buffer_;
     buffer_ = nullptr;
     return res;
 }
 
 void
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -866,18 +866,19 @@ static void FindStartOfUninitializedAndU
       }
     }
     *startOfUninitialized = first;
   } else {
     *startOfUninitialized = *startOfUndefined;
   }
 }
 
-static void AllocateObjectBufferWithInit(JSContext* cx, TypedArrayObject* obj,
-                                         int32_t count) {
+static void AllocateAndInitTypedArrayBuffer(JSContext* cx,
+                                            TypedArrayObject* obj,
+                                            int32_t count) {
   AutoUnsafeCallWithABI unsafe;
 
   obj->initPrivate(nullptr);
 
   // Negative numbers or zero will bail out to the slow path, which in turn will
   // raise an invalid argument exception or create a correct object with zero
   // elements.
   if (count <= 0 || uint32_t(count) >= INT32_MAX / obj->bytesPerElement()) {
@@ -890,17 +891,17 @@ static void AllocateObjectBufferWithInit
   size_t nbytes = count * obj->bytesPerElement();
   MOZ_ASSERT((CheckedUint32(nbytes) + sizeof(Value)).isValid(),
              "JS_ROUNDUP must not overflow");
 
   nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
   void* buf = cx->nursery().allocateZeroedBuffer(obj, nbytes,
                                                  js::ArrayBufferContentsArena);
   if (buf) {
-    obj->initPrivate(buf);
+    InitObjectPrivate(obj, buf, nbytes, MemoryUse::TypedArrayElements);
   }
 }
 
 void MacroAssembler::initTypedArraySlots(Register obj, Register temp,
                                          Register lengthReg,
                                          LiveRegisterSet liveRegs, Label* fail,
                                          TypedArrayObject* templateObj,
                                          TypedArrayLength lengthKind) {
@@ -959,17 +960,17 @@ void MacroAssembler::initTypedArraySlots
     liveRegs.addUnchecked(obj);
     liveRegs.addUnchecked(lengthReg);
     PushRegsInMask(liveRegs);
     setupUnalignedABICall(temp);
     loadJSContext(temp);
     passABIArg(temp);
     passABIArg(obj);
     passABIArg(lengthReg);
-    callWithABI(JS_FUNC_TO_DATA_PTR(void*, AllocateObjectBufferWithInit));
+    callWithABI(JS_FUNC_TO_DATA_PTR(void*, AllocateAndInitTypedArrayBuffer));
     PopRegsInMask(liveRegs);
 
     // Fail when data elements is set to NULL.
     branchPtr(Assembler::Equal, Address(obj, dataSlotOffset), ImmWord(0), fail);
   }
 }
 
 void MacroAssembler::initGCSlots(Register obj, Register temp,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3023,16 +3023,21 @@ JS_PUBLIC_API void JS_SetReservedSlot(JS
 }
 
 JS_PUBLIC_API void JS_InitReservedSlot(JSObject* obj, uint32_t index, void* ptr,
                                        size_t nbytes, JS::MemoryUse use) {
   InitReservedSlot(&obj->as<NativeObject>(), index, ptr, nbytes,
                    js::MemoryUse(use));
 }
 
+JS_PUBLIC_API void JS_InitPrivate(JSObject* obj, void* data, size_t nbytes,
+                                  JS::MemoryUse use) {
+  InitObjectPrivate(&obj->as<NativeObject>(), data, nbytes, js::MemoryUse(use));
+}
+
 JS_PUBLIC_API JSObject* JS_NewArrayObject(
     JSContext* cx, const JS::HandleValueArray& contents) {
   MOZ_ASSERT(!cx->zone()->isAtomsZone());
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   cx->check(contents);
   return NewDenseCopiedArray(cx, contents.length(), contents.begin());
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -907,16 +907,19 @@ extern JS_PUBLIC_API bool InstanceofOper
                                              HandleValue v, bool* bp);
 
 }  // namespace JS
 
 extern JS_PUBLIC_API void* JS_GetPrivate(JSObject* obj);
 
 extern JS_PUBLIC_API void JS_SetPrivate(JSObject* obj, void* data);
 
+extern JS_PUBLIC_API void JS_InitPrivate(JSObject* obj, void* data,
+                                         size_t nbytes, JS::MemoryUse use);
+
 extern JS_PUBLIC_API void* JS_GetInstancePrivate(JSContext* cx,
                                                  JS::Handle<JSObject*> obj,
                                                  const JSClass* clasp,
                                                  JS::CallArgs* args);
 
 extern JS_PUBLIC_API JSObject* JS_GetConstructor(JSContext* cx,
                                                  JS::Handle<JSObject*> proto);
 
@@ -1764,16 +1767,19 @@ extern JS_PUBLIC_API void JS_InitReserve
                                               JS::MemoryUse use);
 
 template <typename T>
 void JS_InitReservedSlot(JSObject* obj, uint32_t index, T* ptr,
                          JS::MemoryUse use) {
   JS_InitReservedSlot(obj, index, ptr, sizeof(T), use);
 }
 
+extern JS_PUBLIC_API void JS_InitPrivate(JSObject* obj, void* data,
+                                         size_t nbytes, JS::MemoryUse use);
+
 /************************************************************************/
 
 /* native that can be called as a ctor */
 static constexpr unsigned JSFUN_CONSTRUCTOR = 0x400;
 
 /* | of all the JSFUN_* flags */
 static constexpr unsigned JSFUN_FLAGS_MASK = 0x400;
 
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -1,20 +1,21 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "perf/jsperf.h"
 
-#include "gc/FreeOp.h"
 #include "js/PropertySpec.h"
 #include "vm/JSContext.h" /* for error messages */
 #include "vm/JSObject.h"  /* for unwrapping without a context */
 
+#include "gc/FreeOp-inl.h"
+
 using namespace js;
 using JS::PerfMeasurement;
 
 // You cannot forward-declare a static object in C++, so instead
 // we have to forward-declare the helper function that refers to it.
 static PerfMeasurement* GetPM(JSContext* cx, JS::HandleValue value,
                               const char* fname);
 
@@ -164,24 +165,24 @@ static bool pm_construct(JSContext* cx, 
   if (!obj) return false;
 
   if (!JS_FreezeObject(cx, obj)) return false;
 
   PerfMeasurement* p =
       cx->new_<PerfMeasurement>(PerfMeasurement::EventMask(mask));
   if (!p) return false;
 
-  JS_SetPrivate(obj, p);
+  JS_InitPrivate(obj, p, sizeof(*p), JS::MemoryUse::PerfMeasurement);
   args.rval().setObject(*obj);
   return true;
 }
 
 static void pm_finalize(JSFreeOp* fop, JSObject* obj) {
-  js::FreeOp::get(fop)->delete_(
-      static_cast<PerfMeasurement*>(JS_GetPrivate(obj)));
+  auto pm = static_cast<PerfMeasurement*>(JS_GetPrivate(obj));
+  js::FreeOp::get(fop)->delete_(obj, pm, MemoryUse::PerfMeasurement);
 }
 
 // Helpers (declared above)
 
 static PerfMeasurement* GetPM(JSContext* cx, JS::HandleValue value,
                               const char* fname) {
   if (!value.isObject()) {
     UniqueChars bytes =
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -393,16 +393,17 @@ AtomsTable::getPartitionIndex(const Atom
   size_t index = lookup.hash >> (32 - PartitionShift);
   MOZ_ASSERT(index < PartitionCount);
   return index;
 }
 
 inline void AtomsTable::tracePinnedAtomsInSet(JSTracer* trc, AtomSet& atoms) {
   for (auto r = atoms.all(); !r.empty(); r.popFront()) {
     JSAtom* atom = r.front().unbarrieredGet();
+    MOZ_DIAGNOSTIC_ASSERT(atom);
     if (atom->isPinned()) {
       TraceRoot(trc, &atom, "interned_atom");
       MOZ_ASSERT(r.front().unbarrieredGet() == atom);
     }
   }
 }
 
 void AtomsTable::tracePinnedAtoms(JSTracer* trc,
@@ -466,18 +467,22 @@ void js::TraceWellKnownSymbols(JSTracer*
 }
 
 void AtomsTable::sweepAll(JSRuntime* rt) {
   for (size_t i = 0; i < PartitionCount; i++) {
     AutoLock lock(rt, partitions[i]->lock);
     AtomSet& atoms = partitions[i]->atoms;
     for (AtomSet::Enum e(atoms); !e.empty(); e.popFront()) {
       JSAtom* atom = e.front().unbarrieredGet();
+      MOZ_DIAGNOSTIC_ASSERT(atom);
       if (IsAboutToBeFinalizedUnbarriered(&atom)) {
+        MOZ_ASSERT(!atom->isPinned());
         e.removeFront();
+      } else {
+        MOZ_ASSERT(atom == e.front().unbarrieredGet());
       }
     }
   }
 }
 
 AtomsTable::SweepIterator::SweepIterator(AtomsTable& atoms)
     : atoms(atoms), partitionIndex(0) {
   startSweepingPartition();
@@ -579,18 +584,22 @@ bool AtomsTable::sweepIncrementally(Swee
   // Sweep the table incrementally until we run out of work or budget.
   while (!atomsToSweep.empty()) {
     budget.step();
     if (budget.isOverBudget()) {
       return false;
     }
 
     JSAtom* atom = atomsToSweep.front();
+    MOZ_DIAGNOSTIC_ASSERT(atom);
     if (IsAboutToBeFinalizedUnbarriered(&atom)) {
+      MOZ_ASSERT(!atom->isPinned());
       atomsToSweep.removeFront();
+    } else {
+      MOZ_ASSERT(atom == atomsToSweep.front());
     }
     atomsToSweep.popFront();
   }
 
   for (size_t i = 0; i < PartitionCount; i++) {
     MOZ_ASSERT(!partitions[i]->atomsAddedWhileSweeping);
   }
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -898,17 +898,17 @@ bool js::StringHasRegExpMetaChars(JSLine
   }
 
   return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
 }
 
 /* RegExpShared */
 
 RegExpShared::RegExpShared(JSAtom* source, RegExpFlags flags)
-    : source(source), flags(flags), canStringMatch(false), parenCount(0) {}
+    : source(source), parenCount(0), flags(flags), canStringMatch(false) {}
 
 void RegExpShared::traceChildren(JSTracer* trc) {
   // Discard code to avoid holding onto ExecutablePools.
   if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC()) {
     discardJitCode();
   }
 
   TraceNullableEdge(trc, &source, "RegExpShared source");
@@ -923,17 +923,20 @@ void RegExpShared::discardJitCode() {
   }
 
   // We can also purge the tables used by JIT code.
   tables.clearAndFree();
 }
 
 void RegExpShared::finalize(FreeOp* fop) {
   for (auto& comp : compilationArray) {
-    fop->free_(comp.byteCode);
+    if (comp.byteCode) {
+      size_t length = comp.byteCodeLength();
+      fop->free_(this, comp.byteCode, length, MemoryUse::RegExpSharedBytecode);
+    }
   }
   tables.~JitCodeTables();
 }
 
 /* static */
 bool RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re,
                            HandleLinearString input, CompilationMode mode,
                            ForceByteCodeEnum force) {
@@ -990,16 +993,18 @@ bool RegExpShared::compile(JSContext* cx
         ReportOutOfMemory(cx);
         return false;
       }
     }
     compilation.jitCode = code.jitCode;
   } else if (code.byteCode) {
     MOZ_ASSERT(tables.empty(), "RegExpInterpreter does not use data tables");
     compilation.byteCode = code.byteCode;
+    AddCellMemory(re, compilation.byteCodeLength(),
+                  MemoryUse::RegExpSharedBytecode);
   }
 
   return true;
 }
 
 /* static */
 bool RegExpShared::compileIfNecessary(JSContext* cx,
                                       MutableHandleRegExpShared re,
--- a/js/src/vm/RegExpShared.h
+++ b/js/src/vm/RegExpShared.h
@@ -41,16 +41,25 @@ using MutableHandleRegExpShared = JS::Mu
 
 enum RegExpRunStatus {
   RegExpRunStatus_Error,
   RegExpRunStatus_Success,
   RegExpRunStatus_Success_NotFound
 };
 
 /*
+ * Layout of the reg exp bytecode header.
+ */
+struct RegExpByteCodeHeader
+{
+  uint32_t length;          // Number of instructions.
+  uint32_t numRegisters;    // Number of registers used.
+};
+
+/*
  * A RegExpShared is the compiled representation of a regexp. A RegExpShared is
  * potentially pointed to by multiple RegExpObjects. Additionally, C++ code may
  * have pointers to RegExpShareds on the stack. The RegExpShareds are kept in a
  * table so that they can be reused when compiling the same regex string.
  *
  * To save memory, a RegExpShared is not created for a RegExpObject until it is
  * needed for execution. When a RegExpShared needs to be created, it is looked
  * up in a per-compartment table to allow reuse between objects.
@@ -72,33 +81,37 @@ class RegExpShared : public gc::TenuredC
   using JitCodeTables = Vector<JitCodeTable, 0, SystemAllocPolicy>;
 
  private:
   friend class RegExpStatics;
   friend class RegExpZone;
 
   struct RegExpCompilation {
     WeakHeapPtr<jit::JitCode*> jitCode;
-    uint8_t* byteCode;
-
-    RegExpCompilation() : byteCode(nullptr) {}
+    uint8_t* byteCode = nullptr;
 
     bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
       return byteCode || (force == DontForceByteCode && jitCode);
     }
+
+    size_t byteCodeLength() const {
+      MOZ_ASSERT(byteCode);
+      auto header = reinterpret_cast<RegExpByteCodeHeader*>(byteCode);
+      return header->length;
+    }
   };
 
+  RegExpCompilation compilationArray[4];
+
   /* Source to the RegExp, for lazy compilation. */
   GCPtr<JSAtom*> source;
 
+  uint32_t parenCount;
   JS::RegExpFlags flags;
   bool canStringMatch;
-  size_t parenCount;
-
-  RegExpCompilation compilationArray[4];
 
   static int CompilationIndex(CompilationMode mode, bool latin1) {
     switch (mode) {
       case Normal:
         return latin1 ? 0 : 1;
       case MatchOnly:
         return latin1 ? 2 : 3;
     }
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -19,29 +19,29 @@ using namespace js;
  * provide an appropriate finalizer. We lazily create and store an instance of
  * that js::Class in a global reserved slot.
  */
 
 static void resc_finalize(FreeOp* fop, JSObject* obj) {
   MOZ_ASSERT(fop->onMainThread());
   RegExpStatics* res =
       static_cast<RegExpStatics*>(obj->as<RegExpStaticsObject>().getPrivate());
-  fop->delete_(res);
+  fop->delete_(obj, res, MemoryUse::RegExpStatics);
 }
 
 static void resc_trace(JSTracer* trc, JSObject* obj) {
   void* pdata = obj->as<RegExpStaticsObject>().getPrivate();
   if (pdata) {
     static_cast<RegExpStatics*>(pdata)->trace(trc);
   }
 }
 
 static const ClassOps RegExpStaticsObjectClassOps = {nullptr, /* addProperty */
                                                      nullptr, /* delProperty */
-                                                     nullptr, /* enumerate */
+                                                     nullptr, /* enumreate */
                                                      nullptr, /* newEnumerate */
                                                      nullptr, /* resolve */
                                                      nullptr, /* mayResolve */
                                                      resc_finalize,
                                                      nullptr, /* call */
                                                      nullptr, /* hasInstance */
                                                      nullptr, /* construct */
                                                      resc_trace};
@@ -55,17 +55,19 @@ RegExpStaticsObject* RegExpStatics::crea
       NewObjectWithGivenProto<RegExpStaticsObject>(cx, nullptr);
   if (!obj) {
     return nullptr;
   }
   RegExpStatics* res = cx->new_<RegExpStatics>();
   if (!res) {
     return nullptr;
   }
-  obj->setPrivate(static_cast<void*>(res));
+  // TODO: This doesn't account for match vector heap memory used if there are
+  // more 10 matches. This is likely to be rare.
+  InitObjectPrivate(obj, res, MemoryUse::RegExpStatics);
   return obj;
 }
 
 bool RegExpStatics::executeLazy(JSContext* cx) {
   if (!pendingLazyEvaluation) {
     return true;
   }
 
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -117,20 +117,22 @@ bool TypedArrayObject::ensureHasBuffer(J
   MOZ_ALWAYS_TRUE(buffer->addView(cx, tarray));
 
   // tarray is not shared, because if it were it would have a buffer.
   memcpy(buffer->dataPointer(), tarray->dataPointerUnshared(),
          tarray->byteLength());
 
   // If the object is in the nursery, the buffer will be freed by the next
   // nursery GC. Free the data slot pointer if the object has no inline data.
+  size_t nbytes = JS_ROUNDUP(tarray->byteLength(), sizeof(Value));
   Nursery& nursery = cx->nursery();
   if (tarray->isTenured() && !tarray->hasInlineElements() &&
       !nursery.isInside(tarray->elements())) {
     js_free(tarray->elements());
+    RemoveCellMemory(tarray, nbytes, MemoryUse::TypedArrayElements);
   }
 
   tarray->setPrivate(buffer->dataPointer());
 
   tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*buffer));
 
   // Notify compiled jit code that the base pointer has moved.
   MarkObjectStateChange(cx, tarray);
@@ -161,17 +163,18 @@ void TypedArrayObject::finalize(FreeOp* 
 
   // Typed arrays with a buffer object do not need to be free'd
   if (curObj->hasBuffer()) {
     return;
   }
 
   // Free the data slot pointer if it does not point into the old JSObject.
   if (!curObj->hasInlineElements()) {
-    fop->free_(curObj->elements());
+    size_t nbytes = JS_ROUNDUP(curObj->byteLength(), sizeof(Value));
+    fop->free_(obj, curObj->elements(), nbytes, MemoryUse::TypedArrayElements);
   }
 }
 
 /* static */
 size_t TypedArrayObject::objectMoved(JSObject* obj, JSObject* old) {
   TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
   const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
   MOZ_ASSERT(newObj->elementsRaw() == oldObj->elementsRaw());
@@ -197,16 +200,18 @@ size_t TypedArrayObject::objectMoved(JSO
   // have any data to move.
   if (!buf) {
     return 0;
   }
 
   Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
   if (!nursery.isInside(buf)) {
     nursery.removeMallocedBuffer(buf);
+    size_t nbytes = JS_ROUNDUP(newObj->byteLength(), sizeof(Value));
+    AddCellMemory(newObj, nbytes, MemoryUse::TypedArrayElements);
     return 0;
   }
 
   // Determine if we can use inline data for the target array. If this is
   // possible, the nursery will have picked an allocation size that is large
   // enough.
   size_t nbytes = oldObj->byteLength();
 
@@ -235,17 +240,17 @@ size_t TypedArrayObject::objectMoved(JSO
     nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
     void* data = newObj->zone()->pod_malloc<uint8_t>(
         nbytes, js::ArrayBufferContentsArena);
     if (!data) {
       oomUnsafe.crash(
           "Failed to allocate typed array elements while tenuring.");
     }
     MOZ_ASSERT(!nursery.isInside(data));
-    newObj->initPrivate(data);
+    InitObjectPrivate(newObj, data, nbytes, MemoryUse::TypedArrayElements);
   }
 
   mozilla::PodCopy(newObj->elements(), oldObj->elements(), nbytes);
 
   // Set a forwarding pointer for the element buffers in case they were
   // preserved on the stack by Ion.
   nursery.setForwardingPointerWhileTenuring(
       oldObj->elements(), newObj->elements(),
@@ -507,22 +512,21 @@ class TypedArrayObjectTemplate : public 
 #ifdef DEBUG
     if (len == 0) {
       uint8_t* output = tarray->fixedData(TypedArrayObject::FIXED_DATA_START);
       output[0] = TypedArrayObject::ZeroLengthArrayData;
     }
 #endif
   }
 
-  static void initTypedArrayData(TypedArrayObject* tarray, int32_t len,
-                                 void* buf, gc::AllocKind allocKind) {
+  static void initTypedArrayData(TypedArrayObject* tarray, void* buf,
+                                 size_t nbytes, gc::AllocKind allocKind) {
     if (buf) {
-      tarray->initPrivate(buf);
+      InitObjectPrivate(tarray, buf, nbytes, MemoryUse::TypedArrayElements);
     } else {
-      size_t nbytes = len * BYTES_PER_ELEMENT;
 #ifdef DEBUG
       constexpr size_t dataOffset = TypedArrayObject::dataOffset();
       constexpr size_t offset = dataOffset + sizeof(HeapSlot);
       MOZ_ASSERT(offset + nbytes <= GetGCKindBytes(allocKind));
 #endif
 
       void* data = tarray->fixedData(FIXED_DATA_START);
       tarray->initPrivate(data);
@@ -566,17 +570,17 @@ class TypedArrayObjectTemplate : public 
       buf = cx->nursery().allocateZeroedBuffer(obj, nbytes,
                                                js::ArrayBufferContentsArena);
       if (!buf) {
         ReportOutOfMemory(cx);
         return nullptr;
       }
     }
 
-    initTypedArrayData(obj, len, buf, allocKind);
+    initTypedArrayData(obj, buf, nbytes, allocKind);
 
     return obj;
   }
 
   static TypedArrayObject* makeTypedArrayWithTemplate(
       JSContext* cx, TypedArrayObject* templateObj, HandleObject array) {
     MOZ_ASSERT(!IsWrapper(array));
     MOZ_ASSERT(!array->is<ArrayBufferObjectMaybeShared>());