Bug 1500926 - Part 1: Use a statically generated perfect hash in WebIDLGlobalNameHash, r=bzbarsky
authorNika Layzell <nika@thelayzells.com>
Sun, 21 Oct 2018 21:44:17 -0400
changeset 499348 80b71708f7ca5d177b3ffb46b518d69d45540310
parent 499347 fbb568aa121e33fedf00c3be48a04c1593af9591
child 499349 3d9631fca868cb87b172f0b615aea84042084356
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1500926
milestone65.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 1500926 - Part 1: Use a statically generated perfect hash in WebIDLGlobalNameHash, r=bzbarsky This strategy allows us to dodge dynamic allocations in WebIDLGlobalNameHash. This removes the Init() and Shutdown() methods, as well as Register. The RegisterBindings.cpp file now only contains static data and one method declaration for the WebIDLGlobalNameHash class. This should also be faster by making the hashtable lookup infallible. Differential Revision: https://phabricator.services.mozilla.com/D9406
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/WebIDLGlobalNameHash.cpp
dom/bindings/WebIDLGlobalNameHash.h
layout/build/nsLayoutStatics.cpp
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3407,41 +3407,16 @@ CreateGlobalOptionsWithXPConnect::PostCr
   NS_ENSURE_SUCCESS(rv, false);
 
   // Invoking the XPCWrappedNativeScope constructor automatically hooks it
   // up to the realm of aGlobal.
   (void) new XPCWrappedNativeScope(aCx, aGlobal, site);
   return true;
 }
 
-static bool sRegisteredDOMNames = false;
-
-static void
-RegisterDOMNames()
-{
-  if (sRegisteredDOMNames) {
-    return;
-  }
-
-  // Register new DOM bindings
-  WebIDLGlobalNameHash::Init();
-
-  sRegisteredDOMNames = true;
-}
-
-/* static */
-bool
-CreateGlobalOptions<nsGlobalWindowInner>::PostCreateGlobal(JSContext* aCx,
-                                                           JS::Handle<JSObject*> aGlobal)
-{
-  RegisterDOMNames();
-
-  return CreateGlobalOptionsWithXPConnect::PostCreateGlobal(aCx, aGlobal);
-}
-
 #ifdef DEBUG
 void
 AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo,
                                JS::Handle<JS::Value> aValue)
 {
   switch (aJitInfo->returnType()) {
   case JSVAL_TYPE_UNKNOWN:
     // Any value is good.
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3099,17 +3099,16 @@ struct CreateGlobalOptions
 };
 
 template <>
 struct CreateGlobalOptions<nsGlobalWindowInner>
   : public CreateGlobalOptionsWithXPConnect
 {
   static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
     ProtoAndIfaceCache::WindowLike;
-  static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 };
 
 // The return value is true if we created and successfully performed our part of
 // the setup for the global, false otherwise.
 //
 // Typically this method's caller will want to ensure that
 // xpc::InitGlobalObjectOptions is called before, and xpc::InitGlobalObject is
 // called after, this method, to ensure that this global object and its
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6,16 +6,18 @@
 
 import os
 import re
 import string
 import math
 import textwrap
 import functools
 
+from perfecthash import PerfectHash
+
 from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLInterfaceMember, IDLUndefinedValue, IDLEmptySequenceValue, IDLDictionary
 from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, getAllTypes, Descriptor, MemberIsUnforgeable, iteratorNativeType
 
 AUTOGENERATED_WARNING_COMMENT = \
     "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n"
 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = \
     "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n"
 ADDPROPERTY_HOOK_NAME = '_addProperty'
@@ -24,16 +26,21 @@ OBJECT_MOVED_HOOK_NAME = '_objectMoved'
 CONSTRUCT_HOOK_NAME = '_constructor'
 LEGACYCALLER_HOOK_NAME = '_legacycaller'
 RESOLVE_HOOK_NAME = '_resolve'
 MAY_RESOLVE_HOOK_NAME = '_mayResolve'
 NEW_ENUMERATE_HOOK_NAME = '_newEnumerate'
 ENUM_ENTRY_VARIABLE_NAME = 'strings'
 INSTANCE_RESERVED_SLOTS = 1
 
+# This size is arbitrary. It is a power of 2 to make using it as a modulo
+# operand cheap, and is usually around 1/3-1/5th of the set size (sometimes
+# smaller for very large sets).
+GLOBAL_NAMES_PHF_SIZE=256
+
 
 def memberReservedSlot(member, descriptor):
     return ("(DOM_INSTANCE_RESERVED_SLOTS + %d)" %
             member.slotIndices[descriptor.interface.identifier.name])
 
 
 def memberXrayExpandoReservedSlot(member, descriptor):
     return ("(xpc::JSSLOT_EXPANDO_COUNT + %d)" %
@@ -13686,16 +13693,85 @@ class CGGetSystemBindingNames(CGAbstract
 
 def getGlobalNames(config):
     names = []
     for desc in config.getDescriptors(registersGlobalNamesOnWindow=True):
         names.append((desc.name, desc))
         names.extend((n.identifier.name, desc) for n in desc.interface.namedConstructors)
     return names
 
+class CGGlobalNames(CGGeneric):
+    def __init__(self, config):
+        currentOffset = 0
+        strings = []
+        entries = []
+        for name, desc in getGlobalNames(config):
+            # Add a string to the list.
+            offset = currentOffset
+            strings.append('/* %i */ "%s\\0"' % (offset, name))
+            currentOffset += len(name) + 1  # Add trailing null.
+
+            # Generate the entry declaration
+            # XXX(nika): mCreate & mEnabled require relocations. If we want to
+            # reduce those, we could move them into separate tables.
+            nativeEntry = fill("""
+                {
+                  /* mNameOffset */ ${nameOffset}, // "${name}"
+                  /* mNameLength */ ${nameLength},
+                  /* mConstructorId */ constructors::id::${realname},
+                  /* mCreate */ ${realname}_Binding::CreateInterfaceObjects,
+                  /* mEnabled */ ${enabled}
+                }
+                """,
+                nameOffset=offset,
+                nameLength=len(name),
+                name=name,
+                realname=desc.name,
+                enabled=("%s_Binding::ConstructorEnabled" % desc.name
+                         if desc.isExposedConditionally() else "nullptr"))
+
+            entries.append((name, nativeEntry))
+
+        # Build the perfect hash function.
+        phf = PerfectHash(entries, GLOBAL_NAMES_PHF_SIZE)
+
+        # Generate code for the PHF
+        phfCodegen = phf.codegen('WebIDLGlobalNameHash::sEntries',
+                                 'WebIDLNameTableEntry')
+        entries = phfCodegen.gen_entries(lambda e: e[1])
+        getter = phfCodegen.gen_jsflatstr_getter(
+            name='WebIDLGlobalNameHash::GetEntry',
+            return_type='const WebIDLNameTableEntry*',
+            # XXX(nika): It would be nice to have a length overload for
+            # JS_FlatStringEqualsAscii.
+            return_entry=dedent("""
+                if (JS_FlatStringEqualsAscii(aKey, sNames + entry.mNameOffset)) {
+                  return &entry;
+                }
+                return nullptr;
+                """))
+
+        define = fill("""
+            const uint32_t WebIDLGlobalNameHash::sCount = ${count};
+
+            const char WebIDLGlobalNameHash::sNames[] =
+              $*{strings}
+
+            $*{entries}
+
+            $*{getter}
+
+            """,
+            count=len(phf.entries),
+            strings="\n".join(strings) + ";\n",
+            entries=entries,
+            getter=getter)
+        CGGeneric.__init__(self, define=define)
+
+
 class CGGlobalNamesString(CGGeneric):
     def __init__(self, config):
         globalNames = getGlobalNames(config)
         currentOffset = 0
         strings = []
         for (name, _) in globalNames:
             strings.append('/* %i */ "%s\\0"' % (currentOffset, name))
             currentOffset += len(name) + 1 # Add trailing null.
@@ -17114,30 +17190,27 @@ class GlobalGenRoots():
         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
 
         # Done.
         return curr
 
     @staticmethod
     def RegisterBindings(config):
 
-        curr = CGList([CGGlobalNamesString(config), CGRegisterGlobalNames(config)])
-
-        # Wrap all of that in our namespaces.
-        curr = CGNamespace.build(['mozilla', 'dom'],
-                                 CGWrapper(curr, post='\n'))
+        curr = CGNamespace.build(['mozilla', 'dom'], CGGlobalNames(config))
         curr = CGWrapper(curr, post='\n')
 
         # Add the includes
         defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
                           for desc in config.getDescriptors(hasInterfaceObject=True,
                                                             isExposedInWindow=True,
                                                             register=True)]
         defineIncludes.append('mozilla/dom/WebIDLGlobalNameHash.h')
         defineIncludes.append('mozilla/dom/PrototypeList.h')
+        defineIncludes.append('mozilla/PerfectHash.h')
         defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface)
                                for desc in config.getDescriptors(isNavigatorProperty=True,
                                                                  register=True)])
         curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings',
                          curr)
 
         # Add include guards.
         curr = CGIncludeGuard('RegisterBindings', curr)
--- a/dom/bindings/WebIDLGlobalNameHash.cpp
+++ b/dom/bindings/WebIDLGlobalNameHash.cpp
@@ -23,178 +23,16 @@
 #include "nsGlobalWindow.h"
 #include "nsIMemoryReporter.h"
 #include "nsTHashtable.h"
 #include "WrapperFactory.h"
 
 namespace mozilla {
 namespace dom {
 
-struct MOZ_STACK_CLASS WebIDLNameTableKey
-{
-  explicit WebIDLNameTableKey(JSFlatString* aJSString)
-    : mLength(js::GetFlatStringLength(aJSString))
-  {
-    mNogc.emplace();
-    JSLinearString* jsString = js::FlatStringToLinearString(aJSString);
-    if (js::LinearStringHasLatin1Chars(jsString)) {
-      mLatin1String = reinterpret_cast<const char*>(
-        js::GetLatin1LinearStringChars(*mNogc, jsString));
-      mTwoBytesString = nullptr;
-      mHash = mLatin1String ? HashString(mLatin1String, mLength) : 0;
-    } else {
-      mLatin1String = nullptr;
-      mTwoBytesString = js::GetTwoByteLinearStringChars(*mNogc, jsString);
-      mHash = mTwoBytesString ? HashString(mTwoBytesString, mLength) : 0;
-    }
-  }
-  explicit WebIDLNameTableKey(const char* aString, size_t aLength)
-    : mLatin1String(aString),
-      mTwoBytesString(nullptr),
-      mLength(aLength),
-      mHash(HashString(aString, aLength))
-  {
-    MOZ_ASSERT(aString[aLength] == '\0');
-  }
-
-  Maybe<JS::AutoCheckCannotGC> mNogc;
-  const char* mLatin1String;
-  const char16_t* mTwoBytesString;
-  size_t mLength;
-  PLDHashNumber mHash;
-};
-
-struct WebIDLNameTableEntry : public PLDHashEntryHdr
-{
-  typedef const WebIDLNameTableKey& KeyType;
-  typedef const WebIDLNameTableKey* KeyTypePointer;
-
-  explicit WebIDLNameTableEntry(KeyTypePointer aKey)
-    : mNameOffset(0),
-      mNameLength(0),
-      mConstructorId(constructors::id::_ID_Count),
-      mCreate(nullptr),
-      mEnabled(nullptr)
-  {}
-  WebIDLNameTableEntry(WebIDLNameTableEntry&& aEntry)
-    : mNameOffset(aEntry.mNameOffset),
-      mNameLength(aEntry.mNameLength),
-      mConstructorId(aEntry.mConstructorId),
-      mCreate(aEntry.mCreate),
-      mEnabled(aEntry.mEnabled)
-  {}
-  ~WebIDLNameTableEntry()
-  {}
-
-  bool KeyEquals(KeyTypePointer aKey) const
-  {
-    if (mNameLength != aKey->mLength) {
-      return false;
-    }
-
-    const char* name = WebIDLGlobalNameHash::sNames + mNameOffset;
-
-    if (aKey->mLatin1String) {
-      return ArrayEqual(aKey->mLatin1String, name, aKey->mLength);
-    }
-
-    return nsCharTraits<char16_t>::compareASCII(aKey->mTwoBytesString, name,
-                                                aKey->mLength) == 0;
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType aKey)
-  {
-    return &aKey;
-  }
-
-  static PLDHashNumber HashKey(KeyTypePointer aKey)
-  {
-    return aKey->mHash;
-  }
-
-  enum { ALLOW_MEMMOVE = true };
-
-  uint16_t mNameOffset;
-  uint16_t mNameLength;
-  constructors::id::ID mConstructorId;
-  CreateInterfaceObjectsMethod mCreate;
-  // May be null if enabled unconditionally
-  WebIDLGlobalNameHash::ConstructorEnabled mEnabled;
-};
-
-static nsTHashtable<WebIDLNameTableEntry>* sWebIDLGlobalNames;
-
-class WebIDLGlobalNamesHashReporter final : public nsIMemoryReporter
-{
-  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
-
-  ~WebIDLGlobalNamesHashReporter() {}
-
-public:
-  NS_DECL_ISUPPORTS
-
-  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData, bool aAnonymize) override
-  {
-    int64_t amount =
-      sWebIDLGlobalNames ?
-      sWebIDLGlobalNames->ShallowSizeOfIncludingThis(MallocSizeOf) : 0;
-
-    MOZ_COLLECT_REPORT(
-      "explicit/dom/webidl-globalnames", KIND_HEAP, UNITS_BYTES, amount,
-      "Memory used by the hash table for WebIDL's global names.");
-
-    return NS_OK;
-  }
-};
-
-NS_IMPL_ISUPPORTS(WebIDLGlobalNamesHashReporter, nsIMemoryReporter)
-
-/* static */
-void
-WebIDLGlobalNameHash::Init()
-{
-  sWebIDLGlobalNames = new nsTHashtable<WebIDLNameTableEntry>(sCount);
-  RegisterWebIDLGlobalNames();
-
-  RegisterStrongMemoryReporter(new WebIDLGlobalNamesHashReporter());
-}
-
-/* static */
-void
-WebIDLGlobalNameHash::Shutdown()
-{
-  delete sWebIDLGlobalNames;
-}
-
-/* static */
-void
-WebIDLGlobalNameHash::Register(uint16_t aNameOffset, uint16_t aNameLength,
-                               CreateInterfaceObjectsMethod aCreate,
-                               ConstructorEnabled aEnabled,
-                               constructors::id::ID aConstructorId)
-{
-  const char* name = sNames + aNameOffset;
-  WebIDLNameTableKey key(name, aNameLength);
-  WebIDLNameTableEntry* entry = sWebIDLGlobalNames->PutEntry(key);
-  entry->mNameOffset = aNameOffset;
-  entry->mNameLength = aNameLength;
-  entry->mCreate = aCreate;
-  entry->mEnabled = aEnabled;
-  entry->mConstructorId = aConstructorId;
-}
-
-/* static */
-void
-WebIDLGlobalNameHash::Remove(const char* aName, uint32_t aLength)
-{
-  WebIDLNameTableKey key(aName, aLength);
-  sWebIDLGlobalNames->RemoveEntry(key);
-}
-
 static JSObject*
 FindNamedConstructorForXray(JSContext* aCx, JS::Handle<jsid> aId,
                             const WebIDLNameTableEntry* aEntry)
 {
   JSObject* interfaceObject =
     GetPerInterfaceObjectHandle(aCx, aEntry->mConstructorId,
                                 aEntry->mCreate,
                                 /* aDefineOnGlobal = */ false);
@@ -226,22 +64,17 @@ WebIDLGlobalNameHash::DefineIfEnabled(JS
                                       JS::Handle<jsid> aId,
                                       JS::MutableHandle<JS::PropertyDescriptor> aDesc,
                                       bool* aFound)
 {
   MOZ_ASSERT(JSID_IS_STRING(aId), "Check for string id before calling this!");
 
   const WebIDLNameTableEntry* entry;
   {
-    WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
-    // Rooting analysis thinks nsTHashtable<...>::GetEntry may GC because it
-    // ends up calling through PLDHashTableOps' matchEntry function pointer, but
-    // we know WebIDLNameTableEntry::KeyEquals can't cause a GC.
-    JS::AutoSuppressGCAnalysis suppress;
-    entry = sWebIDLGlobalNames->GetEntry(key);
+    entry = GetEntry(JSID_TO_FLAT_STRING(aId));
   }
 
   if (!entry) {
     *aFound = false;
     return true;
   }
 
   *aFound = true;
@@ -340,40 +173,35 @@ WebIDLGlobalNameHash::DefineIfEnabled(JS
 
   return true;
 }
 
 /* static */
 bool
 WebIDLGlobalNameHash::MayResolve(jsid aId)
 {
-  WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
-  // Rooting analysis thinks nsTHashtable<...>::Contains may GC because it ends
-  // up calling through PLDHashTableOps' matchEntry function pointer, but we
-  // know WebIDLNameTableEntry::KeyEquals can't cause a GC.
-  JS::AutoSuppressGCAnalysis suppress;
-  return sWebIDLGlobalNames->Contains(key);
+  return GetEntry(JSID_TO_FLAT_STRING(aId)) != nullptr;
 }
 
 /* static */
 bool
 WebIDLGlobalNameHash::GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
                                NameType aNameType, JS::AutoIdVector& aNames)
 {
   // aObj is always a Window here, so GetProtoAndIfaceCache on it is safe.
   ProtoAndIfaceCache* cache = GetProtoAndIfaceCache(aObj);
-  for (auto iter = sWebIDLGlobalNames->Iter(); !iter.Done(); iter.Next()) {
-    const WebIDLNameTableEntry* entry = iter.Get();
+  for (size_t i = 0; i < sCount; ++i) {
+    const WebIDLNameTableEntry& entry = sEntries[i];
     // If aNameType is not AllNames, only include things whose entry slot in the
     // ProtoAndIfaceCache is null.
     if ((aNameType == AllNames ||
-         !cache->HasEntryInSlot(entry->mConstructorId)) &&
-        (!entry->mEnabled || entry->mEnabled(aCx, aObj))) {
-      JSString* str = JS_AtomizeStringN(aCx, sNames + entry->mNameOffset,
-                                        entry->mNameLength);
+         !cache->HasEntryInSlot(entry.mConstructorId)) &&
+        (!entry.mEnabled || entry.mEnabled(aCx, aObj))) {
+      JSString* str = JS_AtomizeStringN(aCx, sNames + entry.mNameOffset,
+                                        entry.mNameLength);
       if (!str || !aNames.append(NON_INTEGER_ATOM_TO_JSID(str))) {
         return false;
       }
     }
   }
 
   return true;
 }
--- a/dom/bindings/WebIDLGlobalNameHash.h
+++ b/dom/bindings/WebIDLGlobalNameHash.h
@@ -6,48 +6,46 @@
 
 #ifndef mozilla_dom_WebIDLGlobalNameHash_h__
 #define mozilla_dom_WebIDLGlobalNameHash_h__
 
 #include "js/RootingAPI.h"
 #include "nsTArray.h"
 #include "mozilla/dom/BindingDeclarations.h"
 
+class JSFlatString;
+
 namespace mozilla {
 namespace dom {
 
-struct WebIDLNameTableEntry;
-
 namespace constructors {
 namespace id {
 enum ID : uint16_t;
 } // namespace id
 } // namespace constructors
 
-class WebIDLGlobalNameHash
+struct WebIDLNameTableEntry
 {
-public:
-  static void Init();
-  static void Shutdown();
-
   // Check whether a constructor should be enabled for the given object.
   // Note that the object should NOT be an Xray, since Xrays will end up
   // defining constructors on the underlying object.
-  // This is a typedef for the function type itself, not the function
-  // pointer, so it's more obvious that pointers to a ConstructorEnabled
-  // can be null.
-  typedef bool
-  (*ConstructorEnabled)(JSContext* cx, JS::Handle<JSObject*> obj);
+  typedef bool (*ConstructorEnabled)(JSContext* cx, JS::Handle<JSObject*> obj);
 
-  static void Register(uint16_t aNameOffset, uint16_t aNameLength,
-                       CreateInterfaceObjectsMethod aCreate,
-                       ConstructorEnabled aEnabled,
-                       constructors::id::ID aConstructorId);
+  uint16_t mNameOffset;
+  uint16_t mNameLength;
+  constructors::id::ID mConstructorId;
+  CreateInterfaceObjectsMethod mCreate;
+  // May be null if enabled unconditionally
+  ConstructorEnabled mEnabled;
+};
 
-  static void Remove(const char* aName, uint32_t aLength);
+class WebIDLGlobalNameHash
+{
+public:
+  typedef WebIDLNameTableEntry::ConstructorEnabled ConstructorEnabled;
 
   // Returns false if something failed. aFound is set to true if the name is in
   // the hash, whether it's enabled or not.
   static bool DefineIfEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
                               JS::Handle<jsid> aId,
                               JS::MutableHandle<JS::PropertyDescriptor> aDesc,
                               bool* aFound);
 
@@ -64,22 +62,30 @@ public:
   // Returns false if an exception has been thrown on aCx.
   static bool GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
                        NameType aNameType,
                        JS::AutoIdVector& aNames);
 
 private:
   friend struct WebIDLNameTableEntry;
 
-  // The total number of names that we will add to the hash.
+  // Look up an entry by key name. `nullptr` if the entry was not found.
+  // The impl of GetEntry is generated by Codegen.py in RegisterBindings.cpp
+  static const WebIDLNameTableEntry* GetEntry(JSFlatString* aKey);
+
+  // The total number of names in the hash.
   // The value of sCount is generated by Codegen.py in RegisterBindings.cpp.
   static const uint32_t sCount;
 
-  // The names that will be registered in the hash, concatenated as one big
-  // string with \0 as a separator between names.
+  // The name table entries in the hash.
+  // The value of sEntries is generated by Codegen.py in RegisterBindings.cpp.
+  static const WebIDLNameTableEntry sEntries[];
+
+  // The names registered in the hash, concatenated as one big string with \0
+  // as a separator between names.
   // The value of sNames is generated by Codegen.py in RegisterBindings.cpp.
   static const char sNames[];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_WebIDLGlobalNameHash_h__
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -346,17 +346,16 @@ nsLayoutStatics::Shutdown()
 
   nsAttrValue::Shutdown();
   nsContentUtils::Shutdown();
   nsLayoutStylesheetCache::Shutdown();
 
   ShutdownJSEnvironment();
   nsGlobalWindowInner::ShutDown();
   nsGlobalWindowOuter::ShutDown();
-  WebIDLGlobalNameHash::Shutdown();
   nsListControlFrame::Shutdown();
   nsXBLService::Shutdown();
   FrameLayerBuilder::Shutdown();
 
   CubebUtils::ShutdownLibrary();
   WebAudioUtils::Shutdown();
 
   nsCORSListenerProxy::Shutdown();