Bug 1007878 - Part 2: Add a C++ type to represent MozMap. r=khuey, a=sledru
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 23 May 2014 17:32:38 -0400
changeset 200394 35de1beea9c009a469d3a42f5006c82386fe92d9
parent 200393 c9458c2fa26a10064205fd438a58133b39d2718f
child 200395 340a07fc3173be65e0204eb1f5acef42041ba867
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, sledru
bugs1007878
milestone31.0a2
Bug 1007878 - Part 2: Add a C++ type to represent MozMap. r=khuey, a=sledru
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/MozMap.h
dom/bindings/Nullable.h
dom/bindings/TypedArray.h
dom/bindings/moz.build
xpcom/glue/nsTHashtable.h
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -26,28 +26,30 @@
 #include "mozilla/MemoryReporting.h"
 #include "nsCycleCollector.h"
 #include "nsIXPConnect.h"
 #include "MainThreadUtils.h"
 #include "nsISupportsImpl.h"
 #include "qsObjectHelper.h"
 #include "xpcpublic.h"
 #include "nsIVariant.h"
+#include "pldhash.h" // For PLDHashOperator
 
 #include "nsWrapperCacheInlines.h"
 
 class nsIJSID;
 class nsPIDOMWindow;
 
 extern nsresult
 xpc_qsUnwrapArgImpl(JSContext* cx, JS::Handle<JS::Value> v, const nsIID& iid, void** ppArg,
                     nsISupports** ppArgRef, JS::MutableHandle<JS::Value> vp);
 
 namespace mozilla {
 namespace dom {
+template<typename DataType> class MozMap;
 
 struct SelfRef
 {
   SelfRef() : ptr(nullptr) {}
   explicit SelfRef(nsISupports *p) : ptr(p) {}
   ~SelfRef() { NS_IF_RELEASE(ptr); }
 
   nsISupports* ptr;
@@ -1929,16 +1931,43 @@ public:
         T& val = seqp->Value();
         T* ptr = &val;
         SequenceTracer<T>::TraceSequence(trc, ptr, ptr+1);
       }
     }
   }
 };
 
+// XXXbz It's not clear whether it's better to add a pldhash dependency here
+// (for PLDHashOperator) or add a BindingUtils.h dependency (for
+// SequenceTracer) to MozMap.h...
+template<typename T>
+static PLDHashOperator
+TraceMozMapValue(T* aValue, void* aClosure)
+{
+  JSTracer* trc = static_cast<JSTracer*>(aClosure);
+  // Act like it's a one-element sequence to leverage all that infrastructure.
+  SequenceTracer<T>::TraceSequence(trc, aValue, aValue + 1);
+  return PL_DHASH_NEXT;
+}
+
+// sequence<MozMap>
+template<typename T>
+class SequenceTracer<MozMap<T>, false, false, false>
+{
+  explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated
+
+public:
+  static void TraceSequence(JSTracer* trc, MozMap<T>* seqp, MozMap<T>* end) {
+    for (; seqp != end; ++seqp) {
+      seqp->EnumerateValues(TraceMozMapValue<T>, trc);
+    }
+  }
+};
+
 template<typename T>
 void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq)
 {
   SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
                                    seq.Elements() + seq.Length());
 }
 
 template<typename T>
@@ -2002,16 +2031,68 @@ public:
     InfallibleTArray<T>* mInfallibleArray;
     FallibleTArray<T>* mFallibleArray;
     Nullable<nsTArray<T> >* mNullableArray;
   };
 
   SequenceType mSequenceType;
 };
 
+// Rooter class for MozMap; this is what we mostly use in the codegen.
+template<typename T>
+class MOZ_STACK_CLASS MozMapRooter : private JS::CustomAutoRooter
+{
+public:
+  MozMapRooter(JSContext *aCx, MozMap<T>* aMozMap
+               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+      mMozMap(aMozMap),
+      mMozMapType(eMozMap)
+  {
+  }
+
+  MozMapRooter(JSContext *aCx, Nullable<MozMap<T>>* aMozMap
+                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+      mNullableMozMap(aMozMap),
+      mMozMapType(eNullableMozMap)
+  {
+  }
+
+private:
+  enum MozMapType {
+    eMozMap,
+    eNullableMozMap
+  };
+
+  virtual void trace(JSTracer *trc) MOZ_OVERRIDE
+  {
+    MozMap<T>* mozMap;
+    if (mMozMapType == eMozMap) {
+      mozMap = mMozMap;
+    } else {
+      MOZ_ASSERT(mMozMapType == eNullableMozMap);
+      if (mNullableMozMap->IsNull()) {
+        // Nothing to do
+        return;
+      }
+      mozMap = &mNullableMozMap->Value();
+    }
+
+    mozMap->EnumerateValues(TraceMozMapValue<T>, trc);
+  }
+
+  union {
+    MozMap<T>* mMozMap;
+    Nullable<MozMap<T>>* mNullableMozMap;
+  };
+
+  MozMapType mMozMapType;
+};
+
 template<typename T>
 class MOZ_STACK_CLASS RootedUnion : public T,
                                     private JS::CustomAutoRooter
 {
 public:
   RootedUnion(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
     T(),
     JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -34,17 +34,17 @@ def toStringBool(arg):
     return str(not not arg).lower()
 
 
 def toBindingNamespace(arg):
     return re.sub("((_workers)?$)", "Binding\\1", arg)
 
 
 def isTypeCopyConstructible(type):
-    # Nullable and sequence stuff doesn't affect copy/constructibility
+    # Nullable and sequence stuff doesn't affect copy-constructibility
     type = type.unroll()
     return (type.isPrimitive() or type.isString() or type.isEnum() or
             (type.isUnion() and
              CGUnionStruct.isUnionCopyConstructible(type)) or
             (type.isDictionary() and
              CGDictionary.isDictionaryCopyConstructible(type.inner)) or
             # Interface types are only copy-constructible if they're Gecko
             # interfaces.  SpiderMonkey interfaces are not copy-constructible
@@ -1015,26 +1015,34 @@ class CGHeaders(CGWrapper):
                         # XXXbz maybe dictionaries with interface members
                         # should just have out-of-line constructors and
                         # destructors?
                         headerSet.add(typeDesc.headerFile)
             elif unrolled.isDictionary():
                 headerSet.add(self.getDeclarationFilename(unrolled.inner))
             elif unrolled.isCallback():
                 # Callbacks are both a type and an object
-                headerSet.add(self.getDeclarationFilename(t.unroll()))
+                headerSet.add(self.getDeclarationFilename(unrolled))
             elif unrolled.isFloat() and not unrolled.isUnrestricted():
                 # Restricted floats are tested for finiteness
                 bindingHeaders.add("mozilla/FloatingPoint.h")
                 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
             elif unrolled.isEnum():
                 filename = self.getDeclarationFilename(unrolled.inner)
                 declareIncludes.add(filename)
             elif unrolled.isPrimitive():
                 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
+            elif unrolled.isMozMap():
+                if dictionary or jsImplementedDescriptors:
+                    declareIncludes.add("mozilla/dom/MozMap.h")
+                else:
+                    bindingHeaders.add("mozilla/dom/MozMap.h")
+                # Also add headers for the type the MozMap is
+                # parametrized over, if needed.
+                addHeadersForType((t.inner, descriptor, dictionary))
 
         map(addHeadersForType,
             getAllTypes(descriptors + callbackDescriptors, dictionaries,
                         callbacks))
 
         # Now make sure we're not trying to include the header from inside itself
         declareIncludes.discard(prefix + ".h")
 
@@ -1140,30 +1148,32 @@ def UnionTypes(descriptors, dictionaries
     owningUnionStructs = dict()
 
     for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks):
         # Add info for the given type.  descriptor and dictionary, if present, are
         # used to figure out what to do with interface types.
         assert not descriptor or not dictionary
 
         t = t.unroll()
+        while t.isMozMap():
+            t = t.inner.unroll()
         if not t.isUnion():
             continue
         name = str(t)
         if name not in unionStructs:
             providers = getRelevantProviders(descriptor, config)
             # FIXME: Unions are broken in workers.  See bug 809899.
             unionStructs[name] = CGUnionStruct(t, providers[0])
             owningUnionStructs[name] = CGUnionStruct(t, providers[0],
                                                      ownsMembers=True)
 
-            for f in t.flatMemberTypes:
-                f = f.unroll()
+            def addHeadersForType(f):
                 if f.nullable():
                     headers.add("mozilla/dom/Nullable.h")
+                f = f.unroll()
                 if f.isInterface():
                     if f.isSpiderMonkeyInterface():
                         headers.add("jsfriendapi.h")
                         headers.add("mozilla/dom/TypedArray.h")
                     else:
                         for p in providers:
                             try:
                                 typeDesc = p.getDescriptor(f.inner.identifier.name)
@@ -1189,16 +1199,24 @@ def UnionTypes(descriptors, dictionaries
                     # Need to see the actual definition of the enum,
                     # unfortunately.
                     headers.add(CGHeaders.getDeclarationFilename(f.inner))
                 elif f.isCallback():
                     # Callbacks always use strong refs, so we need to include
                     # the right header to be able to Release() in our inlined
                     # code.
                     headers.add(CGHeaders.getDeclarationFilename(f))
+                elif f.isMozMap():
+                    headers.add("mozilla/dom/MozMap.h")
+                    # And add headers for the type we're parametrized over
+                    addHeadersForType(f.inner)
+
+            for f in t.flatMemberTypes:
+                assert not f.nullable()
+                addHeadersForType(f)
 
     return (headers, implheaders, declarations,
             CGList(SortedDictValues(unionStructs) +
                    SortedDictValues(owningUnionStructs),
                    "\n"))
 
 
 def UnionConversions(descriptors, dictionaries, callbacks, config):
@@ -1217,17 +1235,17 @@ def UnionConversions(descriptors, dictio
 
         t = t.unroll()
         if not t.isUnion():
             continue
         name = str(t)
         if name not in unionConversions:
             providers = getRelevantProviders(descriptor, config)
             unionConversions[name] = CGUnionConversionStruct(t, providers[0])
-            for f in t.flatMemberTypes:
+            def addHeadersForType(f, providers):
                 f = f.unroll()
                 if f.isInterface():
                     if f.isSpiderMonkeyInterface():
                         headers.add("jsfriendapi.h")
                         headers.add("mozilla/dom/TypedArray.h")
                     elif f.inner.isExternal():
                         providers = getRelevantProviders(descriptor, config)
                         for p in providers:
@@ -1244,16 +1262,23 @@ def UnionConversions(descriptors, dictio
                     # descriptor provider, if we have one at all.
                     if (f.isGeckoInterface() and
                         providers[0].getDescriptor(f.inner.identifier.name).hasXPConnectImpls):
                         headers.add("nsDOMQS.h")
                 elif f.isDictionary():
                     headers.add(CGHeaders.getDeclarationFilename(f.inner))
                 elif f.isPrimitive():
                     headers.add("mozilla/dom/PrimitiveConversions.h")
+                elif f.isMozMap():
+                    headers.add("mozilla/dom/MozMap.h")
+                    # And the internal type of the MozMap
+                    addHeadersForType(f.inner, providers)
+
+            for f in t.flatMemberTypes:
+                addHeadersForType(f, providers)
 
     return (headers,
             CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
                       post="\n\n"))
 
 
 class Argument():
     """
@@ -10875,16 +10900,18 @@ class CGForwardDeclarations(CGWrapper):
                 builder.addInMozillaDom(t.inner.identifier.name, isStruct=True)
             elif t.isCallbackInterface():
                 builder.addInMozillaDom(t.inner.identifier.name)
             elif t.isUnion():
                 # Forward declare both the owning and non-owning version,
                 # since we don't know which one we might want
                 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
                 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
+            elif t.isMozMap():
+                forwardDeclareForType(t.inner, workerness)
             # Don't need to do anything for void, primitive, string, any or object.
             # There may be some other cases we are missing.
 
         # Needed for at least Wrap.
         for d in descriptors:
             builder.add(d.nativeType)
 
         # We just about always need NativePropertyHooks
@@ -10978,16 +11005,18 @@ class CGBindingRoot(CGThing):
 
         bindingHeaders["nsDOMQS.h"] = any(d.hasXPConnectImpls for d in descriptors)
         # Only mainthread things can have hasXPConnectImpls
         provider = config.getDescriptorProvider(False)
 
         def checkForXPConnectImpls(typeInfo):
             type, _, _ = typeInfo
             type = type.unroll()
+            while type.isMozMap():
+                type = type.inner.unroll()
             if not type.isInterface() or not type.isGeckoInterface():
                 return False
             try:
                 typeDesc = provider.getDescriptor(type.inner.identifier.name)
             except NoSuchDescriptorError:
                 return False
             return typeDesc.hasXPConnectImpls
         addHeaderBasedOnTypes("nsDOMQS.h", checkForXPConnectImpls)
new file mode 100644
--- /dev/null
+++ b/dom/bindings/MozMap.h
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+/**
+ * Class for representing MozMap arguments.  This is an nsTHashtable
+ * under the hood, but we don't want to leak that implementation
+ * detail.
+ */
+
+#ifndef mozilla_dom_MozMap_h
+#define mozilla_dom_MozMap_h
+
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace binding_detail {
+template<typename DataType>
+class MozMapEntry : public nsStringHashKey
+{
+public:
+  MozMapEntry(const nsAString* aKeyTypePointer)
+    : nsStringHashKey(aKeyTypePointer)
+  {
+  }
+
+  // Move constructor so we can do MozMaps of MozMaps.
+  MozMapEntry(MozMapEntry<DataType>&& aOther)
+    : nsStringHashKey(aOther),
+      mData(Move(aOther.mData))
+  {
+  }
+
+  DataType mData;
+};
+} // namespace binding_detail
+
+template<typename DataType>
+class MozMap : protected nsTHashtable<binding_detail::MozMapEntry<DataType>>
+{
+public:
+  typedef typename binding_detail::MozMapEntry<DataType> EntryType;
+  typedef nsTHashtable<EntryType> Base;
+  typedef MozMap<DataType> SelfType;
+
+  MozMap()
+  {
+  }
+
+  // Move constructor so we can do MozMap of MozMap.
+  MozMap(SelfType&& aOther) :
+    Base(Move(aOther))
+  {
+  }
+
+  // The return value is only safe to use until an AddEntry call.
+  const DataType& Get(const nsAString& aKey) const
+  {
+    const EntryType* ent = this->GetEntry(aKey);
+    MOZ_ASSERT(ent, "Why are you using a key we didn't claim to have?");
+    return ent->mData;
+  }
+
+  // The return value is only safe to use until an AddEntry call.
+  const DataType* GetIfExists(const nsAString& aKey) const
+  {
+    const EntryType* ent = this->GetEntry(aKey);
+    if (!ent) {
+      return nullptr;
+    }
+    return &ent->mData;
+  }
+
+  void GetKeys(nsTArray<nsString>& aKeys) const {
+    // Sadly, EnumerateEntries is not a const method
+    const_cast<SelfType*>(this)->EnumerateEntries(KeyEnumerator, &aKeys);
+  }
+
+  // XXXbz we expose this generic enumerator for tracing.  Otherwise we'd end up
+  // with a dependency on BindingUtils.h here for the SequenceTracer bits.
+  typedef PLDHashOperator (* Enumerator)(DataType* aValue, void* aClosure);
+  void EnumerateValues(Enumerator aEnumerator, void *aClosure)
+  {
+    ValueEnumClosure args = { aEnumerator, aClosure };
+    this->EnumerateEntries(ValueEnumerator, &args);
+  }
+
+  DataType* AddEntry(const nsAString& aKey) NS_WARN_UNUSED_RESULT
+  {
+    // XXXbz can't just use fallible_t() because our superclass has a
+    // private typedef by that name.
+    EntryType* ent = this->PutEntry(aKey, mozilla::fallible_t());
+    if (!ent) {
+      return nullptr;
+    }
+    return &ent->mData;
+  }
+
+private:
+  static PLDHashOperator
+  KeyEnumerator(EntryType* aEntry, void* aClosure)
+  {
+    nsTArray<nsString>& keys = *static_cast<nsTArray<nsString>*>(aClosure);
+    keys.AppendElement(aEntry->GetKey());
+    return PL_DHASH_NEXT;
+  }
+
+  struct ValueEnumClosure {
+    Enumerator mEnumerator;
+    void* mClosure;
+  };
+
+  static PLDHashOperator
+  ValueEnumerator(EntryType* aEntry, void* aClosure)
+  {
+    ValueEnumClosure* enumClosure = static_cast<ValueEnumClosure*>(aClosure);
+    return enumClosure->mEnumerator(&aEntry->mData, enumClosure->mClosure);
+  }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MozMap_h
--- a/dom/bindings/Nullable.h
+++ b/dom/bindings/Nullable.h
@@ -4,16 +4,17 @@
  * 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/. */
 
 #ifndef mozilla_dom_Nullable_h
 #define mozilla_dom_Nullable_h
 
 #include "mozilla/Assertions.h"
 #include "nsTArrayForwardDeclare.h"
+#include "mozilla/Move.h"
 
 namespace mozilla {
 namespace dom {
 
 // Support for nullable types
 template <typename T>
 struct Nullable
 {
@@ -28,16 +29,32 @@ public:
     : mIsNull(true)
   {}
 
   explicit Nullable(T aValue)
     : mIsNull(false)
     , mValue(aValue)
   {}
 
+  explicit Nullable(Nullable<T>&& aOther)
+    : mIsNull(aOther.mIsNull)
+    , mValue(mozilla::Move(aOther.mValue))
+  {}
+
+  Nullable(const Nullable<T>& aOther)
+    : mIsNull(aOther.mIsNull)
+    , mValue(aOther.mValue)
+  {}
+
+  void operator=(const Nullable<T>& aOther)
+  {
+    mIsNull = aOther.mIsNull;
+    mValue = aOther.mValue;
+  }
+
   void SetValue(T aValue) {
     mValue = aValue;
     mIsNull = false;
   }
 
   // For cases when |T| is some type with nontrivial copy behavior, we may want
   // to get a reference to our internal copy of T and work with it directly
   // instead of relying on the copying version of SetValue().
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_dom_TypedArray_h
 #define mozilla_dom_TypedArray_h
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/RootingAPI.h"
 #include "js/TracingAPI.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 /*
  * Class that just handles the JSObject storage and tracing for typed arrays
@@ -24,16 +25,22 @@ namespace dom {
 struct TypedArrayObjectStorage : AllTypedArraysBase {
 protected:
   JSObject* mObj;
 
   TypedArrayObjectStorage()
   {
   }
 
+  explicit TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther)
+    : mObj(aOther.mObj)
+  {
+    aOther.mObj = nullptr;
+  }
+
 public:
   inline void TraceSelf(JSTracer* trc)
   {
     if (mObj) {
       JS_CallObjectTracer(trc, &mObj, "TypedArray.mObj");
     }
   }
 
@@ -57,16 +64,25 @@ struct TypedArray_base : public TypedArr
     DoInit(obj);
   }
 
   TypedArray_base()
   {
     mObj = nullptr;
   }
 
+  explicit TypedArray_base(TypedArray_base&& aOther)
+    : TypedArrayObjectStorage(Move(aOther)),
+      mData(aOther.mData),
+      mLength(aOther.mLength)
+  {
+    aOther.mData = nullptr;
+    aOther.mLength = 0;
+  }
+
 private:
   T* mData;
   uint32_t mLength;
 
 public:
   inline bool Init(JSObject* obj)
   {
     MOZ_ASSERT(!inited());
@@ -118,16 +134,21 @@ struct TypedArray : public TypedArray_ba
   TypedArray(JSObject* obj) :
     TypedArray_base<T,UnboxArray>(obj)
   {}
 
   TypedArray() :
     TypedArray_base<T,UnboxArray>()
   {}
 
+  explicit TypedArray(TypedArray&& aOther)
+    : TypedArray_base<T,UnboxArray>(Move(aOther))
+  {
+  }
+
   static inline JSObject*
   Create(JSContext* cx, nsWrapperCache* creator, uint32_t length,
          const T* data = nullptr) {
     JS::Rooted<JSObject*> creatorWrapper(cx);
     Maybe<JSAutoCompartment> ac;
     if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) {
       ac.construct(cx, creatorWrapper);
     }
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -19,16 +19,17 @@ EXPORTS.mozilla.dom += [
     'CallbackObject.h',
     'Date.h',
     'DOMJSClass.h',
     'DOMJSProxyHandler.h',
     'DOMString.h',
     'Errors.msg',
     'Exceptions.h',
     'JSSlots.h',
+    'MozMap.h',
     'NonRefcountedDOMObject.h',
     'Nullable.h',
     'OwningNonNull.h',
     'PrimitiveConversions.h',
     'RootedDictionary.h',
     'ToJSValue.h',
     'TypedArray.h',
     'UnionMember.h',
--- a/xpcom/glue/nsTHashtable.h
+++ b/xpcom/glue/nsTHashtable.h
@@ -369,17 +369,17 @@ private:
 
 template<class EntryType>
 nsTHashtable<EntryType>::nsTHashtable(
   nsTHashtable<EntryType>&& aOther)
   : mTable(mozilla::Move(aOther.mTable))
 {
   // aOther shouldn't touch mTable after this, because we've stolen the table's
   // pointers but not overwitten them.
-  MOZ_MAKE_MEM_UNDEFINED(aOther.mTable, sizeof(aOther.mTable));
+  MOZ_MAKE_MEM_UNDEFINED(&aOther.mTable, sizeof(aOther.mTable));
 
   // Indicate that aOther is not initialized.  This will make its destructor a
   // nop, which is what we want.
   aOther.mTable.entrySize = 0;
 }
 
 template<class EntryType>
 nsTHashtable<EntryType>::~nsTHashtable()