Bug 1033442 - Remove non-pod realloc from MallocProvider and AllocPolicy; r=jonco
☠☠ backed out by 93add398e8df ☠ ☠
authorTerrence Cole <terrence@mozilla.com>
Tue, 05 Aug 2014 14:06:35 -0700
changeset 221613 a818fd0874fef18782457445e7a1a1356a116952
parent 221612 70803a5f468399d53409b9c3e1df441ed02d1282
child 221614 ce7de20d461a8d226d86db0ec2490f9e4136b319
push id583
push userbhearsum@mozilla.com
push dateMon, 24 Nov 2014 19:04:58 +0000
treeherdermozilla-release@c107e74250f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1033442
milestone34.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 1033442 - Remove non-pod realloc from MallocProvider and AllocPolicy; r=jonco
js/public/Utility.h
js/src/ds/LifoAlloc.h
js/src/gc/ForkJoinNursery.cpp
js/src/gc/ForkJoinNursery.h
js/src/jit/IonAllocPolicy.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jsalloc.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.h
js/src/shell/js.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/MallocProvider.h
js/src/vm/Runtime.h
js/src/vm/StringBuffer.cpp
js/src/vm/StringBuffer.h
js/xpconnect/src/XPCLocale.cpp
memory/replace/dmd/DMD.cpp
mfbt/AllocPolicy.h
mfbt/Vector.h
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -558,16 +558,26 @@ template <class T>
 static MOZ_ALWAYS_INLINE T *
 js_pod_calloc(size_t numElems)
 {
     if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
         return nullptr;
     return (T *)js_calloc(numElems * sizeof(T));
 }
 
+template <class T>
+static MOZ_ALWAYS_INLINE T *
+js_pod_realloc(T *prior, size_t oldSize, size_t newSize)
+{
+    MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
+    if (newSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
+        return nullptr;
+    return (T *)js_realloc(prior, newSize * sizeof(T));
+}
+
 namespace js {
 
 template<typename T>
 struct ScopedFreePtrTraits
 {
     typedef T* type;
     static T* empty() { return nullptr; }
     static void release(T* ptr) { js_free(ptr); }
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -524,21 +524,24 @@ class LifoAllocPolicy
         if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
             return nullptr;
         T *p = (T *)malloc_(numElems * sizeof(T));
         if (fb == Fallible && !p)
             return nullptr;
         memset(p, 0, numElems * sizeof(T));
         return p;
     }
-    void *realloc_(void *p, size_t oldBytes, size_t bytes) {
-        void *n = malloc_(bytes);
+    template <typename T>
+    T *pod_realloc(T *p, size_t oldSize, size_t newSize) {
+        if (newSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
+            return nullptr;
+        T *n = (T *)malloc_(newSize * sizeof(T));
         if (fb == Fallible && !n)
             return nullptr;
-        memcpy(n, p, Min(oldBytes, bytes));
+        memcpy(n, p, Min(oldSize * sizeof(T), newSize * sizeof(T)));
         return n;
     }
     void free_(void *p) {
     }
     void reportAllocOverflow() const {
     }
 };
 
--- a/js/src/gc/ForkJoinNursery.cpp
+++ b/js/src/gc/ForkJoinNursery.cpp
@@ -580,35 +580,33 @@ ForkJoinNursery::allocateSlots(JSObject 
 
 HeapSlot *
 ForkJoinNursery::reallocateSlots(JSObject *obj, HeapSlot *oldSlots,
                                  uint32_t oldCount, uint32_t newCount)
 {
     if (newCount & mozilla::tl::MulOverflowMask<sizeof(HeapSlot)>::value)
         return nullptr;
 
-    size_t oldSize = oldCount * sizeof(HeapSlot);
-    size_t newSize = newCount * sizeof(HeapSlot);
-
     if (!isInsideNewspace(obj)) {
         JS_ASSERT_IF(oldSlots, !isInsideNewspace(oldSlots));
-        return static_cast<HeapSlot *>(cx_->realloc_(oldSlots, oldSize, newSize));
+        return obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
     }
 
     if (!isInsideNewspace(oldSlots))
-        return reallocateHugeSlots(oldSlots, oldSize, newSize);
+        return reallocateHugeSlots(obj, oldSlots, oldCount, newCount);
 
     // No-op if we're shrinking, we can't make use of the freed portion.
     if (newCount < oldCount)
         return oldSlots;
 
     HeapSlot *newSlots = allocateSlots(obj, newCount);
     if (!newSlots)
         return nullptr;
 
+    size_t oldSize = oldCount * sizeof(HeapSlot);
     js_memcpy(newSlots, oldSlots, oldSize);
     return newSlots;
 }
 
 ObjectElements *
 ForkJoinNursery::allocateElements(JSObject *obj, uint32_t nelems)
 {
     JS_ASSERT(nelems >= ObjectElements::VALUES_PER_HEADER);
@@ -645,19 +643,20 @@ ForkJoinNursery::allocateHugeSlots(size_
         return slots;
 
     // If this put fails, we will only leak the slots.
     (void)hugeSlots[hugeSlotsNew].put(slots);
     return slots;
 }
 
 HeapSlot *
-ForkJoinNursery::reallocateHugeSlots(HeapSlot *oldSlots, uint32_t oldSize, uint32_t newSize)
+ForkJoinNursery::reallocateHugeSlots(JSObject *obj, HeapSlot *oldSlots,
+                                     uint32_t oldCount, uint32_t newCount)
 {
-    HeapSlot *newSlots = static_cast<HeapSlot *>(cx_->realloc_(oldSlots, oldSize, newSize));
+    HeapSlot *newSlots = obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
     if (!newSlots)
         return newSlots;
 
     if (oldSlots != newSlots) {
         hugeSlots[hugeSlotsNew].remove(oldSlots);
         // If this put fails, we will only leak the slots.
         (void)hugeSlots[hugeSlotsNew].put(newSlots);
     }
--- a/js/src/gc/ForkJoinNursery.h
+++ b/js/src/gc/ForkJoinNursery.h
@@ -205,17 +205,18 @@ class ForkJoinNursery
     void *allocate(size_t size);
 
     // Allocate an external slot array and register it with this nursery.
     HeapSlot *allocateHugeSlots(size_t nslots);
 
     // Reallocate an external slot array, unregister the old array and
     // register the new array.  If the allocation fails then leave
     // everything unchanged.
-    HeapSlot *reallocateHugeSlots(HeapSlot *oldSlots, uint32_t oldSize, uint32_t newSize);
+    HeapSlot *reallocateHugeSlots(JSObject *obj, HeapSlot *oldSlots,
+                                  uint32_t oldCount, uint32_t newCount);
 
     // Walk the list of registered slot arrays and free them all.
     void sweepHugeSlots();
 
     // Set the position/end pointers to correspond to the numbered
     // chunk.  Returns false if the chunk could not be allocated, either
     // because we're OOM or because the nursery capacity is exhausted.
     bool setCurrentChunk(int index);
--- a/js/src/jit/IonAllocPolicy.h
+++ b/js/src/jit/IonAllocPolicy.h
@@ -107,21 +107,25 @@ class IonAllocPolicy
     T *pod_calloc(size_t numElems) {
         if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
             return nullptr;
         T *p = (T *)alloc_.allocate(numElems * sizeof(T));
         if (p)
             memset(p, 0, numElems * sizeof(T));
         return p;
     }
-    void *realloc_(void *p, size_t oldBytes, size_t bytes) {
-        void *n = malloc_(bytes);
+    template <typename T>
+    T *pod_realloc(T *p, size_t oldSize, size_t newSize) {
+        MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
+        if (newSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
+            return nullptr;
+        T *n = (T *)malloc_(newSize * sizeof(T));
         if (!n)
             return n;
-        memcpy(n, p, Min(oldBytes, bytes));
+        memcpy(n, p, Min(oldSize * sizeof(T), newSize * sizeof(T)));
         return n;
     }
     void free_(void *p) {
     }
     void reportAllocOverflow() const {
     }
 };
 
@@ -130,23 +134,16 @@ class IonAllocPolicy
 class OldIonAllocPolicy
 {
   public:
     OldIonAllocPolicy()
     {}
     void *malloc_(size_t bytes) {
         return GetIonContext()->temp->allocate(bytes);
     }
-    void *realloc_(void *p, size_t oldBytes, size_t bytes) {
-        void *n = malloc_(bytes);
-        if (!n)
-            return n;
-        memcpy(n, p, Min(oldBytes, bytes));
-        return n;
-    }
     void free_(void *p) {
     }
     void reportAllocOverflow() const {
     }
 };
 
 class AutoIonContextAlloc
 {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1049,17 +1049,17 @@ MPhi::congruentTo(const MDefinition *ins
     return congruentIfOperandsEqual(ins);
 }
 
 bool
 MPhi::reserveLength(size_t length)
 {
     // Initializes a new MPhi to have an Operand vector of at least the given
     // capacity. This permits use of addInput() instead of addInputSlow(), the
-    // latter of which may call realloc_().
+    // latter of which may call realloc().
     JS_ASSERT(numOperands() == 0);
 #if DEBUG
     capacity_ = length;
 #endif
     return inputs_.reserve(length);
 }
 
 static inline types::TemporaryTypeSet *
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5245,17 +5245,17 @@ class MPhi MOZ_FINAL : public MDefinitio
 
     // Initializes the operands vector to the given capacity,
     // permitting use of addInput() instead of addInputSlow().
     bool reserveLength(size_t length);
 
     // Use only if capacity has been reserved by reserveLength
     void addInput(MDefinition *ins);
 
-    // Appends a new input to the input vector. May call realloc_().
+    // Appends a new input to the input vector. May call realloc().
     // Prefer reserveLength() and addInput() instead, where possible.
     bool addInputSlow(MDefinition *ins, bool *ptypeChange = nullptr);
 
     MDefinition *foldsTo(TempAllocator &alloc);
 
     bool congruentTo(const MDefinition *ins) const;
 
     bool isIterator() const {
--- a/js/src/jsalloc.h
+++ b/js/src/jsalloc.h
@@ -22,17 +22,19 @@ namespace js {
 struct ContextFriendFields;
 
 /* Policy for using system memory functions and doing no error reporting. */
 class SystemAllocPolicy
 {
   public:
     void *malloc_(size_t bytes) { return js_malloc(bytes); }
     template <typename T> T *pod_calloc(size_t numElems) { return js_pod_calloc<T>(numElems); }
-    void *realloc_(void *p, size_t oldBytes, size_t bytes) { return js_realloc(p, bytes); }
+    template <typename T> T *pod_realloc(T *p, size_t oldSize, size_t newSize) {
+        return js_pod_realloc<T>(p, oldSize, newSize);
+    }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const {}
 };
 
 /*
  * Allocation policy that calls the system memory functions and reports errors
  * to the context. Since the JSContext given on construction is stored for
  * the lifetime of the container, this policy may only be used for containers
@@ -65,20 +67,21 @@ class TempAllocPolicy
     template <typename T>
     T *pod_calloc(size_t numElems) {
         T *p = js_pod_calloc<T>(numElems);
         if (MOZ_UNLIKELY(!p))
             p = (T *)onOutOfMemory(reinterpret_cast<void *>(1), numElems * sizeof(T));
         return p;
     }
 
-    void *realloc_(void *p, size_t oldBytes, size_t bytes) {
-        void *p2 = js_realloc(p, bytes);
+    template <typename T>
+    T *pod_realloc(T *prior, size_t oldSize, size_t newSize) {
+        T *p2 = js_pod_realloc<T>(prior, oldSize, newSize);
         if (MOZ_UNLIKELY(!p2))
-            p2 = onOutOfMemory(p2, bytes);
+            p2 = (T *)onOutOfMemory(p2, newSize * sizeof(T));
         return p2;
     }
 
     void free_(void *p) {
         js_free(p);
     }
 
     JS_FRIEND_API(void) reportAllocOverflow() const;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1434,21 +1434,22 @@ JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return cx->malloc_(nbytes);
 }
 
 JS_PUBLIC_API(void *)
-JS_realloc(JSContext *cx, void *p, size_t nbytes)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    return cx->realloc_(p, nbytes);
+JS_realloc(JSContext *cx, void *p, size_t oldBytes, size_t newBytes)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    return static_cast<void *>(cx->zone()->pod_realloc<uint8_t>(static_cast<uint8_t *>(p), oldBytes,
+                                                                newBytes));
 }
 
 JS_PUBLIC_API(void)
 JS_free(JSContext *cx, void *p)
 {
     return js_free(p);
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1856,17 +1856,17 @@ typedef bool
  */
 extern JS_PUBLIC_API(void)
 JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback);
 
 extern JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes);
 
 extern JS_PUBLIC_API(void *)
-JS_realloc(JSContext *cx, void *p, size_t nbytes);
+JS_realloc(JSContext *cx, void *p, size_t oldBytes, size_t newBytes);
 
 /*
  * A wrapper for js_free(p) that may delay js_free(p) invocation as a
  * performance optimization.
  * cx may be nullptr.
  */
 extern JS_PUBLIC_API(void)
 JS_free(JSContext *cx, void *p);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -977,17 +977,16 @@ class AutoAssertNoException
 class ContextAllocPolicy
 {
     ThreadSafeContext *const cx_;
 
   public:
     MOZ_IMPLICIT ContextAllocPolicy(ThreadSafeContext *cx) : cx_(cx) {}
     ThreadSafeContext *context() const { return cx_; }
     void *malloc_(size_t bytes) { return cx_->malloc_(bytes); }
-    void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx_->realloc_(p, oldBytes, bytes); }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const { js_ReportAllocationOverflow(cx_); }
 };
 
 /* Exposed intrinsics so that Ion may inline them. */
 bool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_IsObject(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -494,17 +494,17 @@ ReadEvalPrintLoop(JSContext *cx, Handle<
     do {
         /*
          * Accumulate lines until we get a 'compilable unit' - one that either
          * generates an error (before running out of source) or that compiles
          * cleanly.  This should be whenever we get a complete statement that
          * coincides with the end of a line.
          */
         int startline = lineno;
-        typedef Vector<char, 32, ContextAllocPolicy> CharBuffer;
+        typedef Vector<char, 32> CharBuffer;
         CharBuffer buffer(cx);
         do {
             ScheduleWatchdog(cx->runtime(), -1);
             gServiceInterrupt = false;
             errno = 0;
 
             char *line = GetLine(in, startline == lineno ? "js> " : "");
             if (!line) {
@@ -1502,17 +1502,17 @@ ReadLine(JSContext *cx, unsigned argc, j
         } else if (buflength < bufsize - 1) {
             break;
         }
 
         /* Else, grow our buffer for another pass. */
         char *tmp;
         bufsize *= 2;
         if (bufsize > buflength) {
-            tmp = (char *) JS_realloc(cx, buf, bufsize);
+            tmp = static_cast<char *>(JS_realloc(cx, buf, bufsize / 2, bufsize));
         } else {
             JS_ReportOutOfMemory(cx);
             tmp = nullptr;
         }
 
         if (!tmp) {
             JS_free(cx, buf);
             return false;
@@ -1524,17 +1524,17 @@ ReadLine(JSContext *cx, unsigned argc, j
     /* Treat the empty string specially. */
     if (buflength == 0) {
         args.rval().set(feof(from) ? NullValue() : JS_GetEmptyStringValue(cx));
         JS_free(cx, buf);
         return true;
     }
 
     /* Shrink the buffer to the real size. */
-    char *tmp = static_cast<char*>(JS_realloc(cx, buf, buflength));
+    char *tmp = static_cast<char *>(JS_realloc(cx, buf, bufsize, buflength));
     if (!tmp) {
         JS_free(cx, buf);
         return false;
     }
 
     buf = tmp;
 
     /*
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -270,27 +270,32 @@ ArrayBufferObject::class_constructor(JSC
  * Note that some callers are allowed to pass in a nullptr cx, so we allocate
  * with the cx if available and fall back to the runtime.  If oldptr is given,
  * it's expected to be a previously-allocated contents pointer that we then
  * realloc.
  */
 static void *
 AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr, size_t oldnbytes = 0)
 {
-    void *p;
+    uint8_t *p;
 
     // if oldptr is given, then we need to do a realloc
     if (oldptr) {
-        p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, nbytes) : js_realloc(oldptr, nbytes);
+        uint8_t *prior = static_cast<uint8_t *>(oldptr);
+        p = maybecx
+            ? maybecx->runtime()->pod_reallocCanGC<uint8_t>(prior, oldnbytes, nbytes)
+            : js_pod_realloc<uint8_t>(prior, oldnbytes, nbytes);
 
         // if we grew the array, we need to set the new bytes to 0
         if (p && nbytes > oldnbytes)
-            memset(reinterpret_cast<uint8_t *>(p) + oldnbytes, 0, nbytes - oldnbytes);
+            memset(p + oldnbytes, 0, nbytes - oldnbytes);
     } else {
-        p = maybecx ? maybecx->runtime()->callocCanGC(nbytes) : js_calloc(nbytes);
+        p = maybecx
+            ? maybecx->runtime()->pod_callocCanGC<uint8_t>(nbytes)
+            : js_pod_calloc<uint8_t>(nbytes);
     }
 
     if (!p && maybecx)
         js_ReportOutOfMemory(maybecx);
 
     return p;
 }
 
--- a/js/src/vm/MallocProvider.h
+++ b/js/src/vm/MallocProvider.h
@@ -54,40 +54,16 @@ struct MallocProvider
 {
     void *malloc_(size_t bytes) {
         Client *client = static_cast<Client *>(this);
         client->updateMallocCounter(bytes);
         void *p = js_malloc(bytes);
         return MOZ_LIKELY(!!p) ? p : client->onOutOfMemory(nullptr, bytes);
     }
 
-    void *realloc_(void *p, size_t oldBytes, size_t newBytes) {
-        Client *client = static_cast<Client *>(this);
-        /*
-         * For compatibility we do not account for realloc that decreases
-         * previously allocated memory.
-         */
-        if (newBytes > oldBytes)
-            client->updateMallocCounter(newBytes - oldBytes);
-        void *p2 = js_realloc(p, newBytes);
-        return MOZ_LIKELY(!!p2) ? p2 : client->onOutOfMemory(p, newBytes);
-    }
-
-    void *realloc_(void *p, size_t bytes) {
-        Client *client = static_cast<Client *>(this);
-        /*
-         * For compatibility we do not account for realloc that increases
-         * previously allocated memory.
-         */
-        if (!p)
-            client->updateMallocCounter(bytes);
-        void *p2 = js_realloc(p, bytes);
-        return MOZ_LIKELY(!!p2) ? p2 : client->onOutOfMemory(p, bytes);
-    }
-
     template <class T>
     T *pod_malloc() {
         return (T *)malloc_(sizeof(T));
     }
 
     template <class T>
     T *pod_calloc() {
         Client *client = static_cast<Client *>(this);
@@ -113,17 +89,17 @@ struct MallocProvider
     template <class T>
     mozilla::UniquePtr<T[], JS::FreePolicy>
     make_pod_array(size_t numElems) {
         return mozilla::UniquePtr<T[], JS::FreePolicy>(pod_malloc<T>(numElems));
     }
 
     template <class T>
     T *
-    pod_calloc(size_t numElems, JSCompartment *comp = nullptr, JSContext *cx = nullptr) {
+    pod_calloc(size_t numElems) {
         if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
             Client *client = static_cast<Client *>(this);
             client->reportAllocationOverflow();
             return nullptr;
         }
         Client *client = static_cast<Client *>(this);
         client->updateMallocCounter(numElems * sizeof(T));
         T *p = js_pod_calloc<T>(numElems);
@@ -131,26 +107,37 @@ struct MallocProvider
             client->onOutOfMemory(reinterpret_cast<void *>(1), sizeof(T));
             return nullptr;
         }
         return p;
     }
 
     template <class T>
     mozilla::UniquePtr<T[], JS::FreePolicy>
-    make_zeroed_pod_array(size_t numElems,
-                          JSCompartment *comp = nullptr,
-                          JSContext *cx = nullptr)
-    {
-        return mozilla::UniquePtr<T[], JS::FreePolicy>(pod_calloc<T>(numElems, comp, cx));
+    make_zeroed_pod_array(size_t numElems) {
+        return mozilla::UniquePtr<T[], JS::FreePolicy>(pod_calloc<T>(numElems));
     }
 
     template <class T>
     T *pod_realloc(T *prior, size_t oldSize, size_t newSize) {
-        return (T *)realloc_(prior, oldSize * sizeof(T), newSize * sizeof(T));
+        Client *client = static_cast<Client *>(this);
+        T *p = js_pod_realloc(prior, oldSize, newSize);
+        if (MOZ_LIKELY(p)) {
+            // For compatibility we do not account for realloc that decreases
+            // previously allocated memory.
+            if (newSize > oldSize)
+                client->updateMallocCounter((newSize - oldSize) * sizeof(T));
+            return p;
+        }
+        if (newSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
+            client->reportAllocationOverflow();
+            return nullptr;
+        }
+        client->onOutOfMemory(prior, newSize * sizeof(T));
+        return nullptr;
     }
 
     JS_DECLARE_NEW_METHODS(new_, malloc_, MOZ_ALWAYS_INLINE)
     JS_DECLARE_MAKE_METHODS(make_unique, new_, MOZ_ALWAYS_INLINE)
 };
 
 } /* namespace js */
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1366,28 +1366,38 @@ struct JSRuntime : public JS::shadow::Ru
 
     /*
      * These variations of malloc/calloc/realloc will call the
      * large-allocation-failure callback on OOM and retry the allocation.
      */
 
     static const unsigned LARGE_ALLOCATION = 25 * 1024 * 1024;
 
-    void *callocCanGC(size_t bytes) {
-        void *p = (void *)pod_calloc<uint8_t>(bytes);
+    template <typename T>
+    T *pod_callocCanGC(size_t numElems) {
+        T *p = pod_calloc<T>(numElems);
         if (MOZ_LIKELY(!!p))
             return p;
-        return onOutOfMemoryCanGC(reinterpret_cast<void *>(1), bytes);
+        if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
+            reportAllocationOverflow();
+            return nullptr;
+        }
+        return (T *)onOutOfMemoryCanGC(reinterpret_cast<void *>(1), numElems * sizeof(T));
     }
 
-    void *reallocCanGC(void *p, size_t bytes) {
-        void *p2 = realloc_(p, bytes);
+    template <typename T>
+    T *pod_reallocCanGC(T *p, size_t oldSize, size_t newSize) {
+        T *p2 = pod_realloc<T>(p, oldSize, newSize);
         if (MOZ_LIKELY(!!p2))
             return p2;
-        return onOutOfMemoryCanGC(p, bytes);
+        if (newSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
+            reportAllocationOverflow();
+            return nullptr;
+        }
+        return (T *)onOutOfMemoryCanGC(p, newSize * sizeof(T));
     }
 };
 
 namespace js {
 
 // When entering JIT code, the calling JSContext* is stored into the thread's
 // PerThreadData. This function retrieves the JSContext with the pre-condition
 // that the caller is JIT code or C++ called directly from JIT code. This
@@ -1653,17 +1663,21 @@ class RuntimeAllocPolicy
     MOZ_IMPLICIT RuntimeAllocPolicy(JSRuntime *rt) : runtime(rt) {}
     void *malloc_(size_t bytes) { return runtime->malloc_(bytes); }
 
     template <typename T>
     T *pod_calloc(size_t numElems) {
         return runtime->pod_calloc<T>(numElems);
     }
 
-    void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); }
+    template <typename T>
+    T *pod_realloc(T *p, size_t oldSize, size_t newSize) {
+        return runtime->pod_realloc<T>(p, oldSize, newSize);
+    }
+
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const {}
 };
 
 extern const JSSecurityCallbacks NullSecurityCallbacks;
 
 // Debugging RAII class which marks the current thread as performing an Ion
 // compilation, for use by CurrentThreadCan{Read,Write}CompilationData
--- a/js/src/vm/StringBuffer.cpp
+++ b/js/src/vm/StringBuffer.cpp
@@ -23,18 +23,17 @@ ExtractWellSized(ExclusiveContext *cx, B
 
     CharT *buf = cb.extractRawBuffer();
     if (!buf)
         return nullptr;
 
     /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
     JS_ASSERT(capacity >= length);
     if (length > Buffer::sMaxInlineStorage && capacity - length > length / 4) {
-        size_t bytes = sizeof(CharT) * (length + 1);
-        CharT *tmp = (CharT *)cx->realloc_(buf, bytes);
+        CharT *tmp = cx->zone()->pod_realloc<CharT>(buf, capacity, length + 1);
         if (!tmp) {
             js_free(buf);
             return nullptr;
         }
         buf = tmp;
     }
 
     return buf;
--- a/js/src/vm/StringBuffer.h
+++ b/js/src/vm/StringBuffer.h
@@ -28,18 +28,18 @@ namespace js {
  * See |extractWellSized|.
  */
 class StringBuffer
 {
     /*
      * The Vector's buffer is taken by the new string so use
      * ContextAllocPolicy.
      */
-    typedef Vector<Latin1Char, 64, ContextAllocPolicy> Latin1CharBuffer;
-    typedef Vector<jschar, 32, ContextAllocPolicy> TwoByteCharBuffer;
+    typedef Vector<Latin1Char, 64> Latin1CharBuffer;
+    typedef Vector<jschar, 32> TwoByteCharBuffer;
 
     ExclusiveContext *cx;
 
     /*
      * If Latin1 strings are enabled, cb starts out as a Latin1CharBuffer. When
      * a TwoByte char is appended, inflateChars() constructs a TwoByteCharBuffer
      * and copies the Latin1 chars.
      */
--- a/js/xpconnect/src/XPCLocale.cpp
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -208,18 +208,18 @@ private:
         rv = mDecoder->Convert(src, &srcLength, unichars, &unicharLength);
         if (NS_SUCCEEDED(rv)) {
           // terminate the returned string
           unichars[unicharLength] = 0;
 
           // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars
           if (unicharLength + 1 < srcLength + 1) {
             char16_t *shrunkUnichars =
-              (char16_t *)JS_realloc(cx, unichars,
-                                      (unicharLength + 1) * sizeof(char16_t));
+              static_cast<char16_t *>(JS_realloc(cx, unichars, srcLength + 1,
+                                                 unicharLength + 1));
             if (shrunkUnichars)
               unichars = shrunkUnichars;
           }
           JSString *str = JS_NewUCString(cx, reinterpret_cast<jschar*>(unichars), unicharLength);
           if (str) {
             rval.setString(str);
             return true;
           }
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -121,19 +121,22 @@ public:
   static void* realloc_(void* aPtr, size_t aNewSize)
   {
     void* p = gMallocTable->realloc(aPtr, aNewSize);
     ExitOnFailure(p);
     return p;
   }
 
   // This realloc_ is required for this to be a JS container AllocPolicy.
-  static void* realloc_(void* aPtr, size_t aOldSize, size_t aNewSize)
+  template <typename T>
+  static T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
   {
-    return InfallibleAllocPolicy::realloc_(aPtr, aNewSize);
+    if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
+      return nullptr;
+    return (T*)InfallibleAllocPolicy::realloc_((void *)aPtr, aNewSize * sizeof(T));
   }
 
   static void* memalign_(size_t aAlignment, size_t aSize)
   {
     void* p = gMallocTable->memalign(aAlignment, aSize);
     ExitOnFailure(p);
     return p;
   }
--- a/mfbt/AllocPolicy.h
+++ b/mfbt/AllocPolicy.h
@@ -7,36 +7,37 @@
 /*
  * An allocation policy concept, usable for structures and algorithms to
  * control how memory is allocated and how failures are handled.
  */
 
 #ifndef mozilla_AllocPolicy_h
 #define mozilla_AllocPolicy_h
 
+#include "mozilla/TemplateLib.h"
+
 #include <stddef.h>
 #include <stdlib.h>
 
 namespace mozilla {
 
 /*
  * Allocation policies are used to implement the standard allocation behaviors
  * in a customizable way.  Additionally, custom behaviors may be added to these
  * behaviors, such as additionally reporting an error through an out-of-band
  * mechanism when OOM occurs.  The concept modeled here is as follows:
  *
  *  - public copy constructor, assignment, destructor
  *  - void* malloc_(size_t)
  *      Responsible for OOM reporting when null is returned.
  *  - template <typename T> T* pod_calloc(size_t)
  *      Responsible for OOM reporting when null is returned.
- *  - void* realloc_(void*, size_t, size_t)
- *      Responsible for OOM reporting when null is returned.  The *used* bytes
- *      of the previous buffer is passed in (rather than the old allocation
- *      size), in addition to the *new* allocation size requested.
+ *  - template <typename T> T* pod_realloc(T*, size_t, size_t)
+ *      Responsible for OOM reporting when null is returned.  The old allocation
+ *      size is passed in, in addition to the new allocation size requested.
  *  - void free_(void*)
  *  - void reportAllocOverflow() const
  *      Called on allocation overflow (that is, an allocation implicitly tried
  *      to allocate more than the available memory space -- think allocating an
  *      array of large-size objects, where N * size overflows) before null is
  *      returned.
  *
  * mfbt provides (and typically uses by default) only MallocAllocPolicy, which
@@ -56,19 +57,22 @@ public:
   }
 
   template <typename T>
   T* pod_calloc(size_t aNumElems)
   {
     return static_cast<T*>(calloc(aNumElems, sizeof(T)));
   }
 
-  void* realloc_(void* aPtr, size_t aOldBytes, size_t aBytes)
+  template <typename T>
+  T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
   {
-    return realloc(aPtr, aBytes);
+    if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
+        return nullptr;
+    return static_cast<T*>(realloc(aPtr, aNewSize * sizeof(T)));
   }
 
   void free_(void* aPtr)
   {
     free(aPtr);
   }
 
   void reportAllocOverflow() const
--- a/mfbt/Vector.h
+++ b/mfbt/Vector.h
@@ -199,19 +199,17 @@ struct VectorImpl<T, N, AP, ThisVector, 
     }
   }
 
   static inline bool
   growTo(VectorBase<T, N, AP, ThisVector>& aV, size_t aNewCap)
   {
     MOZ_ASSERT(!aV.usingInlineStorage());
     MOZ_ASSERT(!CapacityHasExcessSpace<T>(aNewCap));
-    size_t oldSize = sizeof(T) * aV.mCapacity;
-    size_t newSize = sizeof(T) * aNewCap;
-    T* newbuf = reinterpret_cast<T*>(aV.realloc_(aV.mBegin, oldSize, newSize));
+    T* newbuf = aV.template pod_realloc<T>(aV.mBegin, aV.mCapacity, aNewCap);
     if (!newbuf) {
       return false;
     }
     aV.mBegin = newbuf;
     /* aV.mLength is unchanged. */
     aV.mCapacity = aNewCap;
     return true;
   }