Bug 1208665 - r=Waldo a=abillings a=sylvestre
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 13 Oct 2015 10:59:16 +0200
changeset 289530 a304fc92ad35
parent 289527 7c27db967a21
child 289531 dac9adb22a59
push id5170
push usercbook@mozilla.com
push date2015-10-13 09:02 +0000
treeherdermozilla-beta@dac9adb22a59 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo, abillings, sylvestre
bugs1208665
milestone42.0
Bug 1208665 - r=Waldo a=abillings a=sylvestre
js/public/Utility.h
js/src/ds/LifoAlloc.h
js/src/jit/FixedList.h
js/src/jit/JitAllocPolicy.h
js/src/jit/LIR.cpp
js/src/jit/MIRGenerator.h
js/src/jit/MIRGraph.cpp
js/src/jsalloc.h
js/src/vm/MallocProvider.h
js/src/vm/Runtime.h
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -241,16 +241,46 @@ static inline char* js_strdup(const char
     QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
     MAKENAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
         T* ptr = NEWNAME<T>(mozilla::Forward<Args>(args)...); \
         return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \
     }
 
 JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
 
+namespace js {
+
+/*
+ * Calculate the number of bytes needed to allocate |numElems| contiguous
+ * instances of type |T|.  Return false if the calculation overflowed.
+ */
+template <typename T>
+MOZ_WARN_UNUSED_RESULT inline bool
+CalculateAllocSize(size_t numElems, size_t* bytesOut)
+{
+    *bytesOut = numElems * sizeof(T);
+    return (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) == 0;
+}
+
+/*
+ * Calculate the number of bytes needed to allocate a single instance of type
+ * |T| followed by |numExtra| contiguous instances of type |Extra|.  Return
+ * false if the calculation overflowed.
+ */
+template <typename T, typename Extra>
+MOZ_WARN_UNUSED_RESULT inline bool
+CalculateAllocSizeWithExtra(size_t numExtra, size_t* bytesOut)
+{
+    *bytesOut = sizeof(T) + numExtra * sizeof(Extra);
+    return (numExtra & mozilla::tl::MulOverflowMask<sizeof(Extra)>::value) == 0 &&
+           *bytesOut >= sizeof(T);
+}
+
+} /* namespace js */
+
 template <class T>
 static MOZ_ALWAYS_INLINE void
 js_delete(T* p)
 {
     if (p) {
         p->~T();
         js_free(p);
     }
@@ -266,52 +296,55 @@ js_delete_poison(T* p)
         js_free(p);
     }
 }
 
 template <class T>
 static MOZ_ALWAYS_INLINE T*
 js_pod_malloc()
 {
-    return (T*)js_malloc(sizeof(T));
+    return static_cast<T*>(js_malloc(sizeof(T)));
 }
 
 template <class T>
 static MOZ_ALWAYS_INLINE T*
 js_pod_calloc()
 {
-    return (T*)js_calloc(sizeof(T));
+    return static_cast<T*>(js_calloc(sizeof(T)));
 }
 
 template <class T>
 static MOZ_ALWAYS_INLINE T*
 js_pod_malloc(size_t numElems)
 {
-    if (MOZ_UNLIKELY(numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+    size_t bytes;
+    if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
         return nullptr;
-    return (T*)js_malloc(numElems * sizeof(T));
+    return static_cast<T*>(js_malloc(bytes));
 }
 
 template <class T>
 static MOZ_ALWAYS_INLINE T*
 js_pod_calloc(size_t numElems)
 {
-    if (MOZ_UNLIKELY(numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+    size_t bytes;
+    if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
         return nullptr;
-    return (T*)js_calloc(numElems * sizeof(T));
+    return static_cast<T*>(js_calloc(bytes));
 }
 
 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 (MOZ_UNLIKELY(newSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+    size_t bytes;
+    if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes)))
         return nullptr;
-    return (T*)js_realloc(prior, newSize * sizeof(T));
+    return static_cast<T*>(js_realloc(prior, bytes));
 }
 
 namespace js {
 
 template<typename T>
 struct ScopedFreePtrTraits
 {
     typedef T* type;
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -309,19 +309,20 @@ class LifoAlloc
                       "when the LifoAlloc is freed) need not be called");
         return newArrayUninitialized<T>(count);
     }
 
     // Create an array with uninitialized elements of type |T|.
     // The caller is responsible for initialization.
     template <typename T>
     T* newArrayUninitialized(size_t count) {
-        if (MOZ_UNLIKELY(count & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(count, &bytes)))
             return nullptr;
-        return static_cast<T*>(alloc(sizeof(T) * count));
+        return static_cast<T*>(alloc(bytes));
     }
 
     class Mark {
         BumpChunk* chunk;
         void* markInChunk;
         friend class LifoAlloc;
         Mark(BumpChunk* chunk, void* markInChunk) : chunk(chunk), markInChunk(markInChunk) {}
       public:
@@ -526,34 +527,34 @@ class LifoAllocPolicy
     LifoAlloc& alloc_;
 
   public:
     MOZ_IMPLICIT LifoAllocPolicy(LifoAlloc& alloc)
       : alloc_(alloc)
     {}
     template <typename T>
     T* pod_malloc(size_t numElems) {
-        if (MOZ_UNLIKELY(numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes)))
             return nullptr;
-        size_t bytes = numElems * sizeof(T);
         void* p = fb == Fallible ? alloc_.alloc(bytes) : alloc_.allocInfallible(bytes);
         return static_cast<T*>(p);
     }
     template <typename T>
     T* pod_calloc(size_t numElems) {
         T* p = pod_malloc<T>(numElems);
-        if (fb == Fallible && !p)
+        if (MOZ_UNLIKELY(!p))
             return nullptr;
         memset(p, 0, numElems * sizeof(T));
         return p;
     }
     template <typename T>
     T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
         T* n = pod_malloc<T>(newSize);
-        if (fb == Fallible && !n)
+        if (MOZ_UNLIKELY(!n))
             return nullptr;
         MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
         memcpy(n, p, Min(oldSize * sizeof(T), newSize * sizeof(T)));
         return n;
     }
     void free_(void* p) {
     }
     void reportAllocOverflow() const {
--- a/js/src/jit/FixedList.h
+++ b/js/src/jit/FixedList.h
@@ -32,19 +32,20 @@ class FixedList
     { }
 
     // Dynamic memory allocation requires the ability to report failure.
     bool init(TempAllocator& alloc, size_t length) {
         length_ = length;
         if (length == 0)
             return true;
 
-        if (MOZ_UNLIKELY(length & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(length, &bytes)))
             return false;
-        list_ = (T*)alloc.allocate(length * sizeof(T));
+        list_ = (T*)alloc.allocate(bytes);
         return list_ != nullptr;
     }
 
     size_t empty() const {
         return length_ == 0;
     }
 
     size_t length() const {
@@ -55,19 +56,20 @@ class FixedList
         MOZ_ASSERT(num < length_);
         length_ -= num;
     }
 
     bool growBy(TempAllocator& alloc, size_t num) {
         size_t newlength = length_ + num;
         if (newlength < length_)
             return false;
-        if (MOZ_UNLIKELY(newlength & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(newlength, &bytes)))
             return false;
-        T* list = (T*)alloc.allocate((length_ + num) * sizeof(T));
+        T* list = (T*)alloc.allocate(bytes);
         if (MOZ_UNLIKELY(!list))
             return false;
 
         for (size_t i = 0; i < length_; i++)
             list[i] = list_[i];
 
         length_ += num;
         list_ = list;
--- a/js/src/jit/JitAllocPolicy.h
+++ b/js/src/jit/JitAllocPolicy.h
@@ -43,22 +43,23 @@ class TempAllocator
     void* allocate(size_t bytes)
     {
         void* p = lifoScope_.alloc().alloc(bytes);
         if (!ensureBallast())
             return nullptr;
         return p;
     }
 
-    template <size_t ElemSize>
-    void* allocateArray(size_t n)
+    template <typename T>
+    T* allocateArray(size_t n)
     {
-        if (MOZ_UNLIKELY(n & mozilla::tl::MulOverflowMask<ElemSize>::value))
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(n, &bytes)))
             return nullptr;
-        void* p = lifoScope_.alloc().alloc(n * ElemSize);
+        T* p = static_cast<T*>(lifoScope_.alloc().alloc(bytes));
         if (MOZ_UNLIKELY(!ensureBallast()))
             return nullptr;
         return p;
     }
 
     LifoAlloc* lifoAlloc()
     {
         return &lifoScope_.alloc();
@@ -74,19 +75,20 @@ class JitAllocPolicy
     TempAllocator& alloc_;
 
   public:
     MOZ_IMPLICIT JitAllocPolicy(TempAllocator& alloc)
       : alloc_(alloc)
     {}
     template <typename T>
     T* pod_malloc(size_t numElems) {
-        if (MOZ_UNLIKELY(numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes)))
             return nullptr;
-        return static_cast<T*>(alloc_.allocate(numElems * sizeof(T)));
+        return static_cast<T*>(alloc_.allocate(bytes));
     }
     template <typename T>
     T* pod_calloc(size_t numElems) {
         T* p = pod_malloc<T>(numElems);
         if (MOZ_LIKELY(p))
             memset(p, 0, numElems * sizeof(T));
         return p;
     }
@@ -107,19 +109,20 @@ class JitAllocPolicy
 
 class OldJitAllocPolicy
 {
   public:
     OldJitAllocPolicy()
     {}
     template <typename T>
     T* pod_malloc(size_t numElems) {
-        if (MOZ_UNLIKELY(numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value))
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes)))
             return nullptr;
-        return static_cast<T*>(GetJitContext()->temp->allocate(numElems * sizeof(T)));
+        return static_cast<T*>(GetJitContext()->temp->allocate(bytes));
     }
     void free_(void* p) {
     }
     void reportAllocOverflow() const {
     }
 };
 
 class AutoJitContextAlloc
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -102,18 +102,17 @@ LBlock::init(TempAllocator& alloc)
     size_t phiIndex = 0;
     size_t numPreds = block_->numPredecessors();
     for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
         MPhi* phi = *i;
         MOZ_ASSERT(phi->numOperands() == numPreds);
 
         int numPhis = (phi->type() == MIRType_Value) ? BOX_PIECES : 1;
         for (int i = 0; i < numPhis; i++) {
-            void* array = alloc.allocateArray<sizeof(LAllocation)>(numPreds);
-            LAllocation* inputs = static_cast<LAllocation*>(array);
+            LAllocation* inputs = alloc.allocateArray<LAllocation>(numPreds);
             if (!inputs)
                 return false;
 
             void* addr = &phis_[phiIndex++];
             LPhi* lphi = new (addr) LPhi(phi, inputs);
             lphi->setBlock(this);
         }
     }
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -57,20 +57,21 @@ class MIRGenerator
     CompileInfo& info() {
         return *info_;
     }
     const OptimizationInfo& optimizationInfo() const {
         return *optimizationInfo_;
     }
 
     template <typename T>
-    T * allocate(size_t count = 1) {
-        if (count & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
+    T* allocate(size_t count = 1) {
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(count, &bytes)))
             return nullptr;
-        return reinterpret_cast<T*>(alloc().allocate(sizeof(T) * count));
+        return static_cast<T*>(alloc().allocate(bytes));
     }
 
     // Set an error state and prints a message. Returns false so errors can be
     // propagated up.
     bool abort(const char* message, ...);
     bool abortFmt(const char* message, va_list ap);
 
     bool errored() const {
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -340,17 +340,17 @@ MBasicBlock::NewAsmJS(MIRGraph& graph, C
 
     if (pred) {
         block->stackPosition_ = pred->stackPosition_;
 
         if (block->kind_ == PENDING_LOOP_HEADER) {
             size_t nphis = block->stackPosition_;
 
             TempAllocator& alloc = graph.alloc();
-            MPhi* phis = (MPhi*)alloc.allocateArray<sizeof(MPhi)>(nphis);
+            MPhi* phis = alloc.allocateArray<MPhi>(nphis);
             if (!phis)
                 return nullptr;
 
             // Note: Phis are inserted in the same order as the slots.
             for (size_t i = 0; i < nphis; i++) {
                 MDefinition* predSlot = pred->getSlot(i);
 
                 MOZ_ASSERT(predSlot->type() != MIRType_Value);
--- a/js/src/jsalloc.h
+++ b/js/src/jsalloc.h
@@ -55,41 +55,49 @@ class TempAllocPolicy
 
     /*
      * Non-inline helper to call JSRuntime::onOutOfMemory with minimal
      * code bloat.
      */
     JS_FRIEND_API(void*) onOutOfMemory(AllocFunction allocFunc, size_t nbytes,
                                        void* reallocPtr = nullptr);
 
+    template <typename T>
+    T* onOutOfMemoryTyped(AllocFunction allocFunc, size_t numElems, void* reallocPtr = nullptr) {
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes)))
+            return nullptr;
+        return static_cast<T*>(onOutOfMemory(allocFunc, bytes, reallocPtr));
+    }
+
   public:
     MOZ_IMPLICIT TempAllocPolicy(JSContext* cx) : cx_((ContextFriendFields*) cx) {} // :(
     MOZ_IMPLICIT TempAllocPolicy(ContextFriendFields* cx) : cx_(cx) {}
 
     template <typename T>
     T* pod_malloc(size_t numElems) {
         T* p = js_pod_malloc<T>(numElems);
         if (MOZ_UNLIKELY(!p))
-            p = static_cast<T*>(onOutOfMemory(AllocFunction::Malloc, numElems * sizeof(T)));
+            p = onOutOfMemoryTyped<T>(AllocFunction::Malloc, numElems);
         return p;
     }
 
     template <typename T>
     T* pod_calloc(size_t numElems) {
         T* p = js_pod_calloc<T>(numElems);
         if (MOZ_UNLIKELY(!p))
-            p = static_cast<T*>(onOutOfMemory(AllocFunction::Calloc, numElems * sizeof(T)));
+            p = onOutOfMemoryTyped<T>(AllocFunction::Calloc, numElems);
         return p;
     }
 
     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 = static_cast<T*>(onOutOfMemory(AllocFunction::Realloc, newSize * sizeof(T), prior));
+            p2 = onOutOfMemoryTyped<T>(AllocFunction::Realloc, newSize, prior);
         return p2;
     }
 
     void free_(void* p) {
         js_free(p);
     }
 
     JS_FRIEND_API(void) reportAllocOverflow() const;
--- a/js/src/vm/MallocProvider.h
+++ b/js/src/vm/MallocProvider.h
@@ -54,44 +54,40 @@ struct MallocProvider
 {
     template <class T>
     T* pod_malloc() {
         return pod_malloc<T>(1);
     }
 
     template <class T>
     T* pod_malloc(size_t numElems) {
-        size_t bytes = numElems * sizeof(T);
         T* p = js_pod_malloc<T>(numElems);
         if (MOZ_LIKELY(p)) {
-            client()->updateMallocCounter(bytes);
+            client()->updateMallocCounter(numElems * sizeof(T));
             return p;
         }
-        if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
             client()->reportAllocationOverflow();
             return nullptr;
         }
         p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, bytes);
         if (p)
             client()->updateMallocCounter(bytes);
         return p;
     }
 
     template <class T, class U>
     T* pod_malloc_with_extra(size_t numExtra) {
-        if (MOZ_UNLIKELY(numExtra & mozilla::tl::MulOverflowMask<sizeof(U)>::value)) {
+        size_t bytes;
+        if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) {
             client()->reportAllocationOverflow();
             return nullptr;
         }
-        size_t bytes = sizeof(T) + numExtra * sizeof(U);
-        if (MOZ_UNLIKELY(bytes < sizeof(T))) {
-            client()->reportAllocationOverflow();
-            return nullptr;
-        }
-        T* p = reinterpret_cast<T*>(js_pod_malloc<uint8_t>(bytes));
+        T* p = static_cast<T*>(js_malloc(bytes));
         if (MOZ_LIKELY(p)) {
             client()->updateMallocCounter(bytes);
             return p;
         }
         p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, bytes);
         if (p)
             client()->updateMallocCounter(bytes);
         return p;
@@ -105,44 +101,40 @@ struct MallocProvider
 
     template <class T>
     T* pod_calloc() {
         return pod_calloc<T>(1);
     }
 
     template <class T>
     T* pod_calloc(size_t numElems) {
-        size_t bytes = numElems * sizeof(T);
         T* p = js_pod_calloc<T>(numElems);
         if (MOZ_LIKELY(p)) {
-            client()->updateMallocCounter(bytes);
+            client()->updateMallocCounter(numElems * sizeof(T));
             return p;
         }
-        if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
             client()->reportAllocationOverflow();
             return nullptr;
         }
         p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, bytes);
         if (p)
             client()->updateMallocCounter(bytes);
         return p;
     }
 
     template <class T, class U>
     T* pod_calloc_with_extra(size_t numExtra) {
-        if (MOZ_UNLIKELY(numExtra & mozilla::tl::MulOverflowMask<sizeof(U)>::value)) {
+        size_t bytes;
+        if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) {
             client()->reportAllocationOverflow();
             return nullptr;
         }
-        size_t bytes = sizeof(T) + numExtra * sizeof(U);
-        if (MOZ_UNLIKELY(bytes < sizeof(T))) {
-            client()->reportAllocationOverflow();
-            return nullptr;
-        }
-        T* p = reinterpret_cast<T*>(js_pod_calloc<uint8_t>(bytes));
+        T* p = static_cast<T*>(js_calloc(bytes));
         if (p) {
             client()->updateMallocCounter(bytes);
             return p;
         }
         p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, bytes);
         if (p)
             client()->updateMallocCounter(bytes);
         return p;
@@ -160,21 +152,22 @@ struct MallocProvider
         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) {
+        size_t bytes;
+        if (MOZ_UNLIKELY(!CalculateAllocSize<T>(newSize, &bytes))) {
             client()->reportAllocationOverflow();
             return nullptr;
         }
-        p = (T*)client()->onOutOfMemory(AllocFunction::Realloc, newSize * sizeof(T), prior);
+        p = (T*)client()->onOutOfMemory(AllocFunction::Realloc, bytes, prior);
         if (p && newSize > oldSize)
             client()->updateMallocCounter((newSize - oldSize) * sizeof(T));
         return p;
     }
 
     JS_DECLARE_NEW_METHODS(new_, pod_malloc<uint8_t>, MOZ_ALWAYS_INLINE)
     JS_DECLARE_MAKE_METHODS(make_unique, new_, MOZ_ALWAYS_INLINE)
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1450,33 +1450,35 @@ struct JSRuntime : public JS::shadow::Ru
 
     static const unsigned LARGE_ALLOCATION = 25 * 1024 * 1024;
 
     template <typename T>
     T* pod_callocCanGC(size_t numElems) {
         T* p = pod_calloc<T>(numElems);
         if (MOZ_LIKELY(!!p))
             return p;
-        if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
+        size_t bytes;
+        if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
             reportAllocationOverflow();
             return nullptr;
         }
-        return (T*)onOutOfMemoryCanGC(js::AllocFunction::Calloc, numElems * sizeof(T));
+        return static_cast<T*>(onOutOfMemoryCanGC(js::AllocFunction::Calloc, 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;
-        if (newSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
+        size_t bytes;
+        if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes))) {
             reportAllocationOverflow();
             return nullptr;
         }
-        return (T*)onOutOfMemoryCanGC(js::AllocFunction::Realloc, newSize * sizeof(T), p);
+        return static_cast<T*>(onOutOfMemoryCanGC(js::AllocFunction::Realloc, bytes, p));
     }
 
     /*
      * Debugger.Memory functions like takeCensus use this embedding-provided
      * function to assess the size of malloc'd blocks of memory.
      */
     mozilla::MallocSizeOf debuggerMallocSizeOf;