Bug 1033442 - Remove non-pod calloc from MallocProvider and AllocPolicy; r=jonco
authorTerrence Cole <terrence@mozilla.com>
Tue, 05 Aug 2014 14:06:35 -0700
changeset 198985 a8138c05044d8ab72aaf7361e6c2514017db29fa
parent 198984 ce8b92c9ad6389fb55e108ca992e0bdb312619d6
child 198986 f2091b49fee734c3a83e992894c4e37920d89744
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjonco
bugs1033442
milestone34.0a1
Bug 1033442 - Remove non-pod calloc from MallocProvider and AllocPolicy; r=jonco
js/public/HashTable.h
js/src/ds/LifoAlloc.h
js/src/jit/IonAllocPolicy.h
js/src/jit/RematerializedFrame.cpp
js/src/jsalloc.h
js/src/jscntxt.h
js/src/jsinfer.cpp
js/src/jsiter.cpp
js/src/jsscript.cpp
js/src/vm/MallocProvider.h
js/src/vm/Runtime.h
js/src/vm/Shape.cpp
js/src/vm/Shape.h
layout/style/nsNthIndexCache.h
memory/replace/dmd/DMD.cpp
mfbt/AllocPolicy.h
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -1075,17 +1075,17 @@ class HashTable : private AllocPolicy
     }
 
     static Entry *createTable(AllocPolicy &alloc, uint32_t capacity)
     {
         static_assert(sFreeKey == 0,
                       "newly-calloc'd tables have to be considered empty");
         static_assert(sMaxCapacity <= SIZE_MAX / sizeof(Entry),
                       "would overflow allocating max number of entries");
-        return static_cast<Entry*>(alloc.calloc_(capacity * sizeof(Entry)));
+        return alloc.template pod_calloc<Entry>(capacity);
     }
 
     static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity)
     {
         for (Entry *e = oldTable, *end = e + capacity; e < end; ++e)
             e->destroyIfLive();
         alloc.free_(oldTable);
     }
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -514,21 +514,24 @@ class LifoAllocPolicy
 
   public:
     MOZ_IMPLICIT LifoAllocPolicy(LifoAlloc &alloc)
       : alloc_(alloc)
     {}
     void *malloc_(size_t bytes) {
         return fb == Fallible ? alloc_.alloc(bytes) : alloc_.allocInfallible(bytes);
     }
-    void *calloc_(size_t bytes) {
-        void *p = malloc_(bytes);
+    template <typename T>
+    T *pod_calloc(size_t numElems) {
+        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, bytes);
+        memset(p, 0, numElems * sizeof(T));
         return p;
     }
     void *realloc_(void *p, size_t oldBytes, size_t bytes) {
         void *n = malloc_(bytes);
         if (fb == Fallible && !n)
             return nullptr;
         memcpy(n, p, Min(oldBytes, bytes));
         return n;
--- a/js/src/jit/IonAllocPolicy.h
+++ b/js/src/jit/IonAllocPolicy.h
@@ -98,20 +98,23 @@ class IonAllocPolicy
 
   public:
     MOZ_IMPLICIT IonAllocPolicy(TempAllocator &alloc)
       : alloc_(alloc)
     {}
     void *malloc_(size_t bytes) {
         return alloc_.allocate(bytes);
     }
-    void *calloc_(size_t bytes) {
-        void *p = alloc_.allocate(bytes);
+    template <typename T>
+    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, bytes);
+            memset(p, 0, numElems * sizeof(T));
         return p;
     }
     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;
@@ -127,22 +130,16 @@ class IonAllocPolicy
 class OldIonAllocPolicy
 {
   public:
     OldIonAllocPolicy()
     {}
     void *malloc_(size_t bytes) {
         return GetIonContext()->temp->allocate(bytes);
     }
-    void *calloc_(size_t bytes) {
-        void *p = GetIonContext()->temp->allocate(bytes);
-        if (p)
-            memset(p, 0, bytes);
-        return p;
-    }
     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) {
--- a/js/src/jit/RematerializedFrame.cpp
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -47,17 +47,17 @@ RematerializedFrame::RematerializedFrame
 RematerializedFrame::New(ThreadSafeContext *cx, uint8_t *top, InlineFrameIterator &iter)
 {
     unsigned numFormals = iter.isFunctionFrame() ? iter.callee()->nargs() : 0;
     unsigned numActualArgs = Max(numFormals, iter.numActualArgs());
     size_t numBytes = sizeof(RematerializedFrame) +
         (numActualArgs + iter.script()->nfixed()) * sizeof(Value) -
         sizeof(Value); // 1 Value included in sizeof(RematerializedFrame)
 
-    void *buf = cx->calloc_(numBytes);
+    void *buf = cx->pod_calloc<uint8_t>(numBytes);
     if (!buf)
         return nullptr;
 
     return new (buf) RematerializedFrame(cx, top, numActualArgs, iter);
 }
 
 /* static */ bool
 RematerializedFrame::RematerializeInlineFrames(ThreadSafeContext *cx, uint8_t *top,
--- a/js/src/jsalloc.h
+++ b/js/src/jsalloc.h
@@ -21,17 +21,17 @@ 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); }
-    void *calloc_(size_t bytes) { return js_calloc(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); }
     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
@@ -57,20 +57,21 @@ class TempAllocPolicy
 
     void *malloc_(size_t bytes) {
         void *p = js_malloc(bytes);
         if (MOZ_UNLIKELY(!p))
             p = onOutOfMemory(nullptr, bytes);
         return p;
     }
 
-    void *calloc_(size_t bytes) {
-        void *p = js_calloc(bytes);
+    template <typename T>
+    T *pod_calloc(size_t numElems) {
+        T *p = js_pod_calloc<T>(numElems);
         if (MOZ_UNLIKELY(!p))
-            p = onOutOfMemory(nullptr, bytes);
+            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);
         if (MOZ_UNLIKELY(!p2))
             p2 = onOutOfMemory(p2, bytes);
         return p2;
--- 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 *calloc_(size_t bytes) { return cx_->calloc_(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);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3384,17 +3384,17 @@ CheckNewScriptProperties(JSContext *cx, 
         !type->addDefiniteProperties(cx, baseobj) ||
         !initializerList.append(done))
     {
         return;
     }
 
     size_t numBytes = sizeof(TypeNewScript)
                     + (initializerList.length() * sizeof(TypeNewScript::Initializer));
-    TypeNewScript *newScript = (TypeNewScript *) cx->calloc_(numBytes);
+    TypeNewScript *newScript = (TypeNewScript *) type->zone()->pod_calloc<uint8_t>(numBytes);
     if (!newScript)
         return;
 
     new (newScript) TypeNewScript();
 
     type->setNewScript(newScript);
 
     newScript->fun = fun;
@@ -3547,17 +3547,17 @@ JSScript::makeTypes(JSContext *cx)
 {
     JS_ASSERT(!types);
 
     AutoEnterAnalysis enter(cx);
 
     unsigned count = TypeScript::NumTypeSets(this);
 
     TypeScript *typeScript = (TypeScript *)
-        cx->calloc_(TypeScript::SizeIncludingTypeArray(count));
+        zone()->pod_calloc<uint8_t>(TypeScript::SizeIncludingTypeArray(count));
     if (!typeScript)
         return false;
 
     types = typeScript;
 
 #ifdef DEBUG
     StackTypeSet *typeArray = typeScript->typeArray();
     for (unsigned i = 0; i < nTypeSets(); i++) {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1779,17 +1779,17 @@ js_NewGenerator(JSContext *cx, const Int
                    (-1 + /* one Value included in JSGenerator */
                     vplen +
                     VALUES_PER_STACK_FRAME +
                     stackfp->script()->nslots()) * sizeof(HeapValue);
 
     JS_ASSERT(nbytes % sizeof(Value) == 0);
     JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(HeapValue) == 0);
 
-    JSGenerator *gen = (JSGenerator *) cx->calloc_(nbytes);
+    JSGenerator *gen = (JSGenerator *) obj->zone()->pod_calloc<uint8_t>(nbytes);
     if (!gen)
         return nullptr;
 
     /* Cut up floatingStack space. */
     HeapValue *genvp = gen->stackSnapshot;
     SetValueRangeToUndefined((Value *)genvp, vplen);
 
     InterpreterFrame *genfp = reinterpret_cast<InterpreterFrame *>(genvp + vplen);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1133,34 +1133,34 @@ JSScript::initScriptCounts(JSContext *cx
 {
     JS_ASSERT(!hasScriptCounts());
 
     size_t n = 0;
 
     for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc))
         n += PCCounts::numCounts(JSOp(*pc));
 
-    size_t bytes = (length() * sizeof(PCCounts)) + (n * sizeof(double));
-    char *base = (char *) cx->calloc_(bytes);
+    size_t nbytes = (length() * sizeof(PCCounts)) + (n * sizeof(double));
+    uint8_t *base = zone()->pod_calloc<uint8_t>(nbytes);
     if (!base)
         return false;
 
     /* Create compartment's scriptCountsMap if necessary. */
     ScriptCountsMap *map = compartment()->scriptCountsMap;
     if (!map) {
         map = cx->new_<ScriptCountsMap>();
         if (!map || !map->init()) {
             js_free(base);
             js_delete(map);
             return false;
         }
         compartment()->scriptCountsMap = map;
     }
 
-    char *cursor = base;
+    uint8_t *cursor = base;
 
     ScriptCounts scriptCounts;
     scriptCounts.pcCountsVector = (PCCounts *) cursor;
     cursor += length() * sizeof(PCCounts);
 
     for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc)) {
         JS_ASSERT(uintptr_t(cursor) % sizeof(double) == 0);
         scriptCounts.pcCountsVector[pcToOffset(pc)].counts = (double *) cursor;
@@ -1172,17 +1172,17 @@ JSScript::initScriptCounts(JSContext *cx
     }
 
     if (!map->putNew(this, scriptCounts)) {
         js_free(base);
         return false;
     }
     hasScriptCounts_ = true; // safe to set this;  we can't fail after this point
 
-    JS_ASSERT(size_t(cursor - base) == bytes);
+    JS_ASSERT(size_t(cursor - base) == nbytes);
 
     /* Enable interrupts in any interpreter frames running on this script. */
     for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
         if (iter->isInterpreter())
             iter->asInterpreter()->enableInterruptsIfRunning(this);
     }
 
     return true;
@@ -2353,41 +2353,38 @@ JSScript::Create(ExclusiveContext *cx, H
     script->setSourceObject(sourceObject);
     script->sourceStart_ = bufStart;
     script->sourceEnd_ = bufEnd;
 
     return script;
 }
 
 static inline uint8_t *
-AllocScriptData(ExclusiveContext *cx, size_t size)
+AllocScriptData(JS::Zone *zone, size_t size)
 {
-    uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
+    if (!size)
+        return nullptr;
+
+    uint8_t *data = zone->pod_calloc<uint8_t>(JS_ROUNDUP(size, sizeof(Value)));
     if (!data)
         return nullptr;
-
-    // All script data is optional, so size might be 0. In that case, we don't care about alignment.
-    JS_ASSERT(size == 0 || size_t(data) % sizeof(Value) == 0);
+    JS_ASSERT(size_t(data) % sizeof(Value) == 0);
     return data;
 }
 
 /* static */ bool
 JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nconsts,
                         uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes,
                         uint32_t nblockscopes, uint32_t nTypeSets)
 {
     size_t size = ScriptDataSize(script->bindings.count(), nconsts, nobjects, nregexps, ntrynotes,
                                  nblockscopes);
-    if (size > 0) {
-        script->data = AllocScriptData(cx, size);
-        if (!script->data)
-            return false;
-    } else {
-        script->data = nullptr;
-    }
+    script->data = AllocScriptData(script->zone(), size);
+    if (size && !script->data)
+        return false;
     script->dataSize_ = size;
 
     JS_ASSERT(nTypeSets <= UINT16_MAX);
     script->nTypeSets_ = uint16_t(nTypeSets);
 
     uint8_t *cursor = script->data;
     if (nconsts != 0) {
         script->setHasArray(CONSTS);
@@ -2885,18 +2882,18 @@ js::CloneScript(JSContext *cx, HandleObj
     uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
     uint32_t nregexps  = src->hasRegexps()  ? src->regexps()->length  : 0;
     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
     uint32_t nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0;
 
     /* Script data */
 
     size_t size = src->dataSize();
-    uint8_t *data = AllocScriptData(cx, size);
-    if (!data)
+    uint8_t *data = AllocScriptData(cx->zone(), size);
+    if (size && !data)
         return nullptr;
 
     /* Bindings */
 
     Rooted<Bindings> bindings(cx);
     InternalHandle<Bindings*> bindingsHandle =
         InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
     if (!Bindings::clone(cx, bindingsHandle, data, src))
@@ -3147,17 +3144,17 @@ JSScript::destroyDebugScript(FreeOp *fop
 
 bool
 JSScript::ensureHasDebugScript(JSContext *cx)
 {
     if (hasDebugScript_)
         return true;
 
     size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
-    DebugScript *debug = (DebugScript *) cx->calloc_(nbytes);
+    DebugScript *debug = (DebugScript *) zone()->pod_calloc<uint8_t>(nbytes);
     if (!debug)
         return false;
 
     /* Create compartment's debugScriptMap if necessary. */
     DebugScriptMap *map = compartment()->debugScriptMap;
     if (!map) {
         map = cx->new_<DebugScriptMap>();
         if (!map || !map->init()) {
--- a/js/src/vm/MallocProvider.h
+++ b/js/src/vm/MallocProvider.h
@@ -18,16 +18,20 @@
  *     These allocators are for system memory whose lifetime is not associated
  *     with a GC thing. See js/src/jsalloc.h.
  *
  *       - SystemAllocPolicy: No extra functionality over bare allocators.
  *
  *       - TempAllocPolicy: Adds automatic error reporting to the provided
  *         Context when allocations fail.
  *
+ *       - ContextAllocPolicy: forwards to the JSContext MallocProvider.
+ *
+ *       - RuntimeAllocPolicy: forwards to the JSRuntime MallocProvider.
+ *
  *   - MallocProvider. A mixin base class that handles automatically updating
  *     the GC's state in response to allocations that are tied to a GC lifetime
  *     or are for a particular GC purpose. These allocators must only be used
  *     for memory that will be freed when a GC thing is swept.
  *
  *       - gc::Zone:  Automatically triggers zone GC.
  *       - JSRuntime: Automatically triggers full GC.
  *       - ThreadsafeContext > ExclusiveContext > JSContext:
@@ -50,23 +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 *calloc_(size_t bytes) {
-        Client *client = static_cast<Client *>(this);
-        client->updateMallocCounter(bytes);
-        void *p = js_calloc(bytes);
-        return MOZ_LIKELY(!!p) ? p : client->onOutOfMemory(reinterpret_cast<void *>(1), 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);
@@ -88,17 +85,24 @@ struct MallocProvider
 
     template <class T>
     T *pod_malloc() {
         return (T *)malloc_(sizeof(T));
     }
 
     template <class T>
     T *pod_calloc() {
-        return (T *)calloc_(sizeof(T));
+        Client *client = static_cast<Client *>(this);
+        client->updateMallocCounter(sizeof(T));
+        T *p = js_pod_calloc<T>();
+        if (MOZ_UNLIKELY(!p)) {
+            client->onOutOfMemory(reinterpret_cast<void *>(1), sizeof(T));
+            return nullptr;
+        }
+        return p;
     }
 
     template <class T>
     T *pod_malloc(size_t numElems) {
         if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
             Client *client = static_cast<Client *>(this);
             client->reportAllocationOverflow();
             return nullptr;
@@ -115,33 +119,39 @@ struct MallocProvider
     template <class T>
     T *
     pod_calloc(size_t numElems, JSCompartment *comp = nullptr, JSContext *cx = nullptr) {
         if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
             Client *client = static_cast<Client *>(this);
             client->reportAllocationOverflow();
             return nullptr;
         }
-        return (T *)calloc_(numElems * sizeof(T));
+        Client *client = static_cast<Client *>(this);
+        client->updateMallocCounter(numElems * sizeof(T));
+        T *p = js_pod_calloc<T>(numElems);
+        if (MOZ_UNLIKELY(!p)) {
+            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));
     }
 
     template <class T>
     T *pod_realloc(T *prior, size_t oldSize, size_t newSize) {
         return (T *)realloc_(prior, oldSize * sizeof(T), newSize * sizeof(T));
     }
 
-
     JS_DECLARE_NEW_METHODS(new_, malloc_, MOZ_ALWAYS_INLINE)
     JS_DECLARE_MAKE_METHODS(make_unique, new_, MOZ_ALWAYS_INLINE)
 };
 
 } /* namespace js */
 
 #endif /* vm_MallocProvider_h */
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1365,17 +1365,17 @@ 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 = calloc_(bytes);
+        void *p = (void *)pod_calloc<uint8_t>(bytes);
         if (MOZ_LIKELY(!!p))
             return p;
         return onOutOfMemoryCanGC(reinterpret_cast<void *>(1), bytes);
     }
 
     void *reallocCanGC(void *p, size_t bytes) {
         void *p2 = realloc_(p, bytes);
         if (MOZ_LIKELY(!!p2))
@@ -1645,17 +1645,22 @@ SetValueRangeToNull(Value *vec, size_t l
  */
 class RuntimeAllocPolicy
 {
     JSRuntime *const runtime;
 
   public:
     MOZ_IMPLICIT RuntimeAllocPolicy(JSRuntime *rt) : runtime(rt) {}
     void *malloc_(size_t bytes) { return runtime->malloc_(bytes); }
-    void *calloc_(size_t bytes) { return runtime->calloc_(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); }
     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
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -40,20 +40,20 @@ ShapeTable::init(ThreadSafeContext *cx, 
     uint32_t sizeLog2 = CeilingLog2Size(entryCount);
     uint32_t size = JS_BIT(sizeLog2);
     if (entryCount >= size - (size >> 2))
         sizeLog2++;
     if (sizeLog2 < MIN_SIZE_LOG2)
         sizeLog2 = MIN_SIZE_LOG2;
 
     /*
-     * Use rt->calloc_ for memory accounting and overpressure handling
+     * Use rt->calloc for memory accounting and overpressure handling
      * without OOM reporting. See ShapeTable::change.
      */
-    entries = (Shape **) cx->calloc_(sizeOfEntries(JS_BIT(sizeLog2)));
+    entries = cx->pod_calloc<Shape *>(JS_BIT(sizeLog2));
     if (!entries)
         return false;
 
     hashShift = HASH_BITS - sizeLog2;
     for (Shape::Range<NoGC> r(lastProp); !r.empty(); r.popFront()) {
         Shape &shape = r.front();
         JS_ASSERT(cx->isThreadLocal(&shape));
         Shape **spp = search(shape.propid(), true);
@@ -255,17 +255,17 @@ ShapeTable::change(int log2Delta, Thread
 
     /*
      * Grow, shrink, or compress by changing this->entries.
      */
     int oldlog2 = HASH_BITS - hashShift;
     int newlog2 = oldlog2 + log2Delta;
     uint32_t oldsize = JS_BIT(oldlog2);
     uint32_t newsize = JS_BIT(newlog2);
-    Shape **newTable = (Shape **) cx->calloc_(sizeOfEntries(newsize));
+    Shape **newTable = cx->pod_calloc<Shape *>(newsize);
     if (!newTable)
         return false;
 
     /* Now that we have newTable allocated, update members. */
     hashShift = HASH_BITS - newlog2;
     removedCount = 0;
     Shape **oldTable = entries;
     entries = newTable;
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -156,19 +156,16 @@ struct ShapeTable {
 
     ~ShapeTable() {
         js_free(entries);
     }
 
     /* By definition, hashShift = HASH_BITS - log2(capacity). */
     uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift); }
 
-    /* Computes the size of the entries array for a given capacity. */
-    static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); }
-
     /*
      * This counts the ShapeTable object itself (which must be
      * heap-allocated) and its |entries| array.
      */
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + mallocSizeOf(entries);
     }
 
@@ -182,17 +179,17 @@ struct ShapeTable {
      * Try to grow the table.  On failure, reports out of memory on cx
      * and returns false.  This will make any extant pointers into the
      * table invalid.  Don't call this unless needsToGrow() is true.
      */
     bool grow(ThreadSafeContext *cx);
 
     /*
      * NB: init and change are fallible but do not report OOM, so callers can
-     * cope or ignore. They do however use the context's calloc_ method in
+     * cope or ignore. They do however use the context's calloc method in
      * order to update the malloc counter on success.
      */
     bool            init(ThreadSafeContext *cx, Shape *lastProp);
     bool            change(int log2Delta, ThreadSafeContext *cx);
     Shape           **search(jsid id, bool adding);
 };
 
 /*
--- a/layout/style/nsNthIndexCache.h
+++ b/layout/style/nsNthIndexCache.h
@@ -56,17 +56,22 @@ private:
   // If -2, needs to be computed.
   // If -1, needs to be computed but known not to be 1.
   // If 0, the node is not at any index in its parent.
   typedef int32_t CacheEntry;
 
   class SystemAllocPolicy {
   public:
     void *malloc_(size_t bytes) { return ::malloc(bytes); }
-    void *calloc_(size_t bytes) { return ::calloc(bytes, 1); }
+
+    template <typename T>
+    T *pod_calloc(size_t numElems) {
+      return static_cast<T *>(::calloc(numElems, sizeof(T)));
+    }
+
     void *realloc_(void *p, size_t bytes) { return ::realloc(p, bytes); }
     void free_(void *p) { ::free(p); }
     void reportAllocOverflow() const {}
   };
 
   typedef js::HashMap<nsIContent*, CacheEntry, js::DefaultHasher<nsIContent*>,
                       SystemAllocPolicy> Cache;
 
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -104,16 +104,24 @@ public:
 
   static void* calloc_(size_t aSize)
   {
     void* p = gMallocTable->calloc(1, aSize);
     ExitOnFailure(p);
     return p;
   }
 
+  template <typename T>
+  static T* pod_calloc(size_t aNumElems)
+  {
+    void* p = gMallocTable->calloc(aNumElems, sizeof(T));
+    ExitOnFailure(p);
+    return (T*)p;
+  }
+
   // This realloc_ is the one we use for direct reallocs within DMD.
   static void* realloc_(void* aPtr, size_t aNewSize)
   {
     void* p = gMallocTable->realloc(aPtr, aNewSize);
     ExitOnFailure(p);
     return p;
   }
 
--- a/mfbt/AllocPolicy.h
+++ b/mfbt/AllocPolicy.h
@@ -21,17 +21,17 @@ 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.
- *  - void* calloc_(size_t)
+ *  - 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.
  *  - void free_(void*)
  *  - void reportAllocOverflow() const
  *      Called on allocation overflow (that is, an allocation implicitly tried
@@ -50,19 +50,20 @@ namespace mozilla {
 class MallocAllocPolicy
 {
 public:
   void* malloc_(size_t aBytes)
   {
     return malloc(aBytes);
   }
 
-  void* calloc_(size_t aBytes)
+  template <typename T>
+  T* pod_calloc(size_t aNumElems)
   {
-    return calloc(aBytes, 1);
+    return static_cast<T*>(calloc(aNumElems, sizeof(T)));
   }
 
   void* realloc_(void* aPtr, size_t aOldBytes, size_t aBytes)
   {
     return realloc(aPtr, aBytes);
   }
 
   void free_(void* aPtr)