Bug 1449395 - Remove nsStaticAtomSetup. r=froydnj draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 29 Mar 2018 11:48:18 +1100
changeset 774593 45384697a0c31a57255ff51d72c106c0b7f0635c
parent 774574 7478c40b82f7f6a3f509145cb674eb022b055f0f
push id104438
push usernnethercote@mozilla.com
push dateThu, 29 Mar 2018 04:48:25 +0000
reviewersfroydnj
bugs1449395, 62008, 20992, 21040, 104040
milestone61.0a1
Bug 1449395 - Remove nsStaticAtomSetup. r=froydnj Each nsStaticAtomSetup contains a pointer to a static atom, and also a pointer to the canonical pointer to that static atom. Which is pretty weird! The notable thing thing about it is that these structs are in an array, and that gives us the only way to iterate over all static atoms in a single class, for registration and lookups. But thanks to various other recent changes to the implementation of static atoms, we can now put the static atoms themselves into an array, which can be iterated over. So this patch does that. With that done, nsStaticAtomSetup is no longer necessary. According to the `size` utility, on Linux64 this reduces the size of libxul.so by the following amounts: > text: 62008 bytes > data: 20992 bytes > bss: 21040 bytes > total: 104040 bytes - The bss reduction is one word per atom, because the canonical static atom pointers (e.g. nsGkAtoms::foo) have moved from .bss to .data, because they're now initialized at compile time instead of runtime. - The data reduction is one word per atom, because we remove two words per atom for the nsStaticAtomSetup removal, but gain one word per atom from the previous bullet point. - I'm not sure about the text reduction. It's three words per atom. Maybe because there is one less relocation per atom? Other notable things in the patch: - nsICSSAnonBoxPseudo and nsICSSPseudoElement now inherit from nsStaticAtom, not nsAtom, because that's more precise. - Each static atoms array now has an enum associated with it, which is used in various ways. - In the big comment about the macros at the top of nsStaticAtom.h, the pre- and post-expansion forms are now shown interleaved. The interleaving reduces duplication and makes the comment much easier to read and maintain. The comment also has an introduction that explains the constraints and goals of the implementation. - The SUBCLASS macro variations are gone. There are few enough users of these macros now that always passing the atom type has become simpler. MozReview-Commit-ID: 1GmfKidLjaU
layout/style/nsCSSAnonBoxes.cpp
layout/style/nsCSSAnonBoxes.h
layout/style/nsCSSPseudoElements.cpp
layout/style/nsCSSPseudoElements.h
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsGkAtoms.cpp
xpcom/ds/nsGkAtoms.h
xpcom/ds/nsStaticAtom.h
xpcom/io/nsDirectoryService.cpp
xpcom/io/nsDirectoryService.h
--- a/layout/style/nsCSSAnonBoxes.cpp
+++ b/layout/style/nsCSSAnonBoxes.cpp
@@ -16,63 +16,61 @@ namespace mozilla {
 namespace detail {
 
 MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 extern constexpr CSSAnonBoxAtoms gCSSAnonBoxAtoms = {
   #define CSS_ANON_BOX(name_, value_) \
     NS_STATIC_ATOM_INIT_STRING(value_)
   #include "nsCSSAnonBoxList.h"
   #undef CSS_ANON_BOX
-
-  #define CSS_ANON_BOX(name_, value_) \
-    NS_STATIC_ATOM_INIT_ATOM(CSSAnonBoxAtoms, name_, value_)
-  #include "nsCSSAnonBoxList.h"
-  #undef CSS_ANON_BOX
+  {
+    #define CSS_ANON_BOX(name_, value_) \
+      NS_STATIC_ATOM_INIT_ATOM( \
+        nsICSSAnonBoxPseudo, CSSAnonBoxAtoms, name_, value_)
+    #include "nsCSSAnonBoxList.h"
+    #undef CSS_ANON_BOX
+  }
 };
 MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 
 } // namespace detail
 } // namespace mozilla
 
+// Non-inheriting boxes must come first in nsCSSAnonBoxList.h so that
+// `NonInheriting` values can index into this array and other similar arrays.
+const nsStaticAtom* const nsCSSAnonBoxes::sAtoms =
+  mozilla::detail::gCSSAnonBoxAtoms.mAtoms;
+
 #define CSS_ANON_BOX(name_, value_) \
-  NS_STATIC_ATOM_SUBCLASS_DEFN_PTR(nsICSSAnonBoxPseudo, nsCSSAnonBoxes, name_)
+  NS_STATIC_ATOM_DEFN_PTR( \
+    nsICSSAnonBoxPseudo, mozilla::detail::CSSAnonBoxAtoms, \
+    mozilla::detail::gCSSAnonBoxAtoms, nsCSSAnonBoxes, name_)
 #include "nsCSSAnonBoxList.h"
 #undef CSS_ANON_BOX
 
-static const nsStaticAtomSetup sCSSAnonBoxAtomSetup[] = {
-  // Non-inheriting boxes must come first in nsCSSAnonBoxList.h so that
-  // `NonInheriting` values can index into this array and other similar arrays.
-  #define CSS_ANON_BOX(name_, value_) \
-    NS_STATIC_ATOM_SUBCLASS_SETUP( \
-      mozilla::detail::gCSSAnonBoxAtoms, nsCSSAnonBoxes, name_)
-  #include "nsCSSAnonBoxList.h"
-  #undef CSS_ANON_BOX
-};
-
 void nsCSSAnonBoxes::RegisterStaticAtoms()
 {
-  NS_RegisterStaticAtoms(sCSSAnonBoxAtomSetup);
+  NS_RegisterStaticAtoms(sAtoms, sAtomsLen);
 }
 
 bool nsCSSAnonBoxes::IsAnonBox(nsAtom *aAtom)
 {
-  return nsStaticAtomUtils::IsMember(aAtom, sCSSAnonBoxAtomSetup);
+  return nsStaticAtomUtils::IsMember(aAtom, sAtoms, sAtomsLen);
 }
 
 #ifdef MOZ_XUL
 /* static */ bool
 nsCSSAnonBoxes::IsTreePseudoElement(nsAtom* aPseudo)
 {
   MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudo));
   return StringBeginsWith(nsDependentAtomString(aPseudo),
                           NS_LITERAL_STRING(":-moz-tree-"));
 }
 #endif
 
 /* static*/ nsCSSAnonBoxes::NonInheriting
 nsCSSAnonBoxes::NonInheritingTypeForPseudoTag(nsAtom* aPseudo)
 {
   MOZ_ASSERT(IsNonInheritingAnonBox(aPseudo));
-  Maybe<uint32_t> index =
-    nsStaticAtomUtils::Lookup(aPseudo, sCSSAnonBoxAtomSetup);
+  Maybe<uint32_t> index = nsStaticAtomUtils::Lookup(aPseudo, sAtoms, sAtomsLen);
   MOZ_RELEASE_ASSERT(index.isSome());
   return static_cast<NonInheriting>(*index);
 }
--- a/layout/style/nsCSSAnonBoxes.h
+++ b/layout/style/nsCSSAnonBoxes.h
@@ -7,32 +7,45 @@
 /* atom list for CSS anonymous boxes */
 
 #ifndef nsCSSAnonBoxes_h___
 #define nsCSSAnonBoxes_h___
 
 #include "nsAtom.h"
 #include "nsStaticAtom.h"
 
-// Empty class derived from nsAtom so that function signatures can
-// require an atom from this atom list.
-class nsICSSAnonBoxPseudo : public nsAtom {};
+// Trivial subclass of nsStaticAtom so that function signatures can require an
+// atom from this atom list.
+class nsICSSAnonBoxPseudo : public nsStaticAtom
+{
+public:
+  constexpr nsICSSAnonBoxPseudo(const char16_t* aStr, uint32_t aLength,
+                                uint32_t aStringOffset)
+    : nsStaticAtom(aStr, aLength, aStringOffset)
+  {}
+};
 
 namespace mozilla {
 namespace detail {
 
 struct CSSAnonBoxAtoms
 {
   #define CSS_ANON_BOX(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
   #include "nsCSSAnonBoxList.h"
   #undef CSS_ANON_BOX
 
-  #define CSS_ANON_BOX(name_, value_) NS_STATIC_ATOM_DECL_ATOM(name_)
-  #include "nsCSSAnonBoxList.h"
-  #undef CSS_ANON_BOX
+  enum class Atoms {
+    #define CSS_ANON_BOX(name_, value_) \
+      NS_STATIC_ATOM_ENUM(name_)
+    #include "nsCSSAnonBoxList.h"
+    #undef CSS_ANON_BOX
+    AtomsCount
+  };
+
+  const nsICSSAnonBoxPseudo mAtoms[static_cast<size_t>(Atoms::AtomsCount)];
 };
 
 extern const CSSAnonBoxAtoms gCSSAnonBoxAtoms;
 
 } // namespace detail
 } // namespace mozilla
 
 class nsCSSAnonBoxes {
@@ -45,20 +58,26 @@ public:
   static bool IsTreePseudoElement(nsAtom* aPseudo);
 #endif
   static bool IsNonElement(nsAtom* aPseudo)
   {
     return aPseudo == mozText || aPseudo == oofPlaceholder ||
            aPseudo == firstLetterContinuation;
   }
 
-#define CSS_ANON_BOX(name_, value_) \
-  NS_STATIC_ATOM_SUBCLASS_DECL_PTR(nsICSSAnonBoxPseudo, name_)
-#include "nsCSSAnonBoxList.h"
-#undef CSS_ANON_BOX
+private:
+  static const nsStaticAtom* const sAtoms;
+  static constexpr size_t sAtomsLen =
+    mozilla::ArrayLength(mozilla::detail::gCSSAnonBoxAtoms.mAtoms);
+
+public:
+  #define CSS_ANON_BOX(name_, value_) \
+    NS_STATIC_ATOM_DECL_PTR(nsICSSAnonBoxPseudo, name_)
+  #include "nsCSSAnonBoxList.h"
+  #undef CSS_ANON_BOX
 
   typedef uint8_t NonInheritingBase;
   enum class NonInheriting : NonInheritingBase {
 #define CSS_ANON_BOX(_name, _value) /* nothing */
 #define CSS_NON_INHERITING_ANON_BOX(_name, _value) _name,
 #include "nsCSSAnonBoxList.h"
 #undef CSS_NON_INHERITING_ANON_BOX
 #undef CSS_ANON_BOX
--- a/layout/style/nsCSSPseudoElements.cpp
+++ b/layout/style/nsCSSPseudoElements.cpp
@@ -19,61 +19,56 @@ namespace mozilla {
 namespace detail {
 
 MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 extern constexpr CSSPseudoElementAtoms gCSSPseudoElementAtoms = {
   #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
     NS_STATIC_ATOM_INIT_STRING(value_)
   #include "nsCSSPseudoElementList.h"
   #undef CSS_PSEUDO_ELEMENT
-
-  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-    NS_STATIC_ATOM_INIT_ATOM(CSSPseudoElementAtoms, name_, value_)
-  #include "nsCSSPseudoElementList.h"
-  #undef CSS_PSEUDO_ELEMENT
+  {
+    #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
+      NS_STATIC_ATOM_INIT_ATOM( \
+        nsICSSPseudoElement, CSSPseudoElementAtoms, name_, value_)
+    #include "nsCSSPseudoElementList.h"
+    #undef CSS_PSEUDO_ELEMENT
+  }
 };
 MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 
 } // namespace detail
 } // namespace mozilla
 
+const nsStaticAtom* const nsCSSPseudoElements::sAtoms =
+  mozilla::detail::gCSSPseudoElementAtoms.mAtoms;
+
 #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-  NS_STATIC_ATOM_SUBCLASS_DEFN_PTR( \
-    nsICSSPseudoElement, nsCSSPseudoElements, name_)
+  NS_STATIC_ATOM_DEFN_PTR( \
+    nsICSSPseudoElement, mozilla::detail::CSSPseudoElementAtoms, \
+    mozilla::detail::gCSSPseudoElementAtoms, nsCSSPseudoElements, name_);
 #include "nsCSSPseudoElementList.h"
 #undef CSS_PSEUDO_ELEMENT
 
-// Array of nsStaticAtomSetup for each of the pseudo-elements.
-static const nsStaticAtomSetup sCSSPseudoElementAtomSetup[] = {
-  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-    NS_STATIC_ATOM_SUBCLASS_SETUP( \
-      detail::gCSSPseudoElementAtoms, nsCSSPseudoElements, name_)
-  #include "nsCSSPseudoElementList.h"
-  #undef CSS_PSEUDO_ELEMENT
-};
-
-// Flags data for each of the pseudo-elements, which must be separate
-// from the previous array since there's no place for it in
-// nsStaticAtomSetup.
+// Flags data for each of the pseudo-elements.
 /* static */ const uint32_t
 nsCSSPseudoElements::kPseudoElementFlags[] = {
 #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
   flags_,
 #include "nsCSSPseudoElementList.h"
 #undef CSS_PSEUDO_ELEMENT
 };
 
 void nsCSSPseudoElements::RegisterStaticAtoms()
 {
-  NS_RegisterStaticAtoms(sCSSPseudoElementAtomSetup);
+  NS_RegisterStaticAtoms(sAtoms, sAtomsLen);
 }
 
 bool nsCSSPseudoElements::IsPseudoElement(nsAtom *aAtom)
 {
-  return nsStaticAtomUtils::IsMember(aAtom, sCSSPseudoElementAtomSetup);
+  return nsStaticAtomUtils::IsMember(aAtom, sAtoms, sAtomsLen);
 }
 
 /* static */ bool
 nsCSSPseudoElements::IsCSS2PseudoElement(nsAtom *aAtom)
 {
   // We don't implement this using PseudoElementHasFlags because callers
   // want to pass things that could be anon boxes.
   NS_ASSERTION(nsCSSPseudoElements::IsPseudoElement(aAtom) ||
@@ -89,18 +84,17 @@ nsCSSPseudoElements::IsCSS2PseudoElement
                    CSS_PSEUDO_ELEMENT_IS_CSS2),
                "result doesn't match flags");
   return result;
 }
 
 /* static */ CSSPseudoElementType
 nsCSSPseudoElements::GetPseudoType(nsAtom *aAtom, EnabledState aEnabledState)
 {
-  Maybe<uint32_t> index =
-    nsStaticAtomUtils::Lookup(aAtom, sCSSPseudoElementAtomSetup);
+  Maybe<uint32_t> index = nsStaticAtomUtils::Lookup(aAtom, sAtoms, sAtomsLen);
   if (index.isSome()) {
     auto type = static_cast<Type>(*index);
     // ::moz-placeholder is an alias for ::placeholder
     if (type == CSSPseudoElementType::mozPlaceholder) {
       type = CSSPseudoElementType::placeholder;
     }
     return IsEnabled(type, aEnabledState) ? type : Type::NotPseudo;
   }
@@ -120,19 +114,19 @@ nsCSSPseudoElements::GetPseudoType(nsAto
   }
 
   return Type::NotPseudo;
 }
 
 /* static */ nsAtom*
 nsCSSPseudoElements::GetPseudoAtom(Type aType)
 {
-  NS_ASSERTION(aType < Type::Count, "Unexpected type");
-  return *sCSSPseudoElementAtomSetup[
-    static_cast<CSSPseudoElementTypeBase>(aType)].mAtomp;
+  MOZ_ASSERT(aType < Type::Count, "Unexpected type");
+  return const_cast<nsStaticAtom*>(
+    &sAtoms[static_cast<CSSPseudoElementTypeBase>(aType)]);
 }
 
 /* static */ already_AddRefed<nsAtom>
 nsCSSPseudoElements::GetPseudoAtom(const nsAString& aPseudoElement)
 {
   if (DOMStringIsNull(aPseudoElement) || aPseudoElement.IsEmpty() ||
       aPseudoElement.First() != char16_t(':')) {
     return nullptr;
--- a/layout/style/nsCSSPseudoElements.h
+++ b/layout/style/nsCSSPseudoElements.h
@@ -51,16 +51,27 @@
 // Can we use the ChromeOnly document.createElement(..., { pseudo: "::foo" })
 // API for creating pseudo-implementing native anonymous content in JS with this
 // pseudo-element?
 #define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC           (1<<6)
 // Does this pseudo-element act like an item for containers (such as flex and
 // grid containers) and thus needs parent display-based style fixup?
 #define CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM        (1<<7)
 
+// Trivial subclass of nsStaticAtom so that function signatures can require an
+// atom from this atom list.
+class nsICSSPseudoElement : public nsStaticAtom
+{
+public:
+  constexpr nsICSSPseudoElement(const char16_t* aStr, uint32_t aLength,
+                                uint32_t aStringOffset)
+    : nsStaticAtom(aStr, aLength, aStringOffset)
+  {}
+};
+
 namespace mozilla {
 
 // The total count of CSSPseudoElement is less than 256,
 // so use uint8_t as its underlying type.
 typedef uint8_t CSSPseudoElementTypeBase;
 enum class CSSPseudoElementType : CSSPseudoElementTypeBase {
   // If the actual pseudo-elements stop being first here, change
   // GetPseudoType.
@@ -83,32 +94,33 @@ namespace detail {
 
 struct CSSPseudoElementAtoms
 {
   #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
     NS_STATIC_ATOM_DECL_STRING(name_, value_)
   #include "nsCSSPseudoElementList.h"
   #undef CSS_PSEUDO_ELEMENT
 
-  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-    NS_STATIC_ATOM_DECL_ATOM(name_)
-  #include "nsCSSPseudoElementList.h"
-  #undef CSS_PSEUDO_ELEMENT
+  enum class Atoms {
+    #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
+      NS_STATIC_ATOM_ENUM(name_)
+    #include "nsCSSPseudoElementList.h"
+    #undef CSS_PSEUDO_ELEMENT
+    AtomsCount
+  };
+
+  const nsICSSPseudoElement mAtoms[static_cast<size_t>(Atoms::AtomsCount)];
 };
 
 extern const CSSPseudoElementAtoms gCSSPseudoElementAtoms;
 
 } // namespace detail
 
 } // namespace mozilla
 
-// Empty class derived from nsAtom so that function signatures can
-// require an atom from this atom list.
-class nsICSSPseudoElement : public nsAtom {};
-
 class nsCSSPseudoElements
 {
   typedef mozilla::CSSPseudoElementType Type;
   typedef mozilla::CSSEnabledState EnabledState;
 
 public:
   static void RegisterStaticAtoms();
 
@@ -119,20 +131,26 @@ public:
   // This must match EAGER_PSEUDO_COUNT in Rust code.
   static const size_t kEagerPseudoCount = 4;
 
   static bool IsEagerlyCascadedInServo(const Type aType)
   {
     return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_CSS2);
   }
 
-#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-  NS_STATIC_ATOM_SUBCLASS_DECL_PTR(nsICSSPseudoElement, name_)
-#include "nsCSSPseudoElementList.h"
-#undef CSS_PSEUDO_ELEMENT
+private:
+  static const nsStaticAtom* const sAtoms;
+  static constexpr size_t sAtomsLen =
+    mozilla::ArrayLength(mozilla::detail::gCSSPseudoElementAtoms.mAtoms);
+
+public:
+  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
+    NS_STATIC_ATOM_DECL_PTR(nsICSSPseudoElement, name_);
+  #include "nsCSSPseudoElementList.h"
+  #undef CSS_PSEUDO_ELEMENT
 
   static Type GetPseudoType(nsAtom* aAtom, EnabledState aEnabledState);
 
   // Get the atom for a given Type. aType must be < CSSPseudoElementType::Count.
   // This only ever returns static atoms, so it's fine to return a raw pointer.
   static nsAtom* GetPseudoAtom(Type aType);
 
   // Get the atom for a given pseudo-element string (e.g. "::before").  This can
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -274,17 +274,17 @@ class nsAtomTable
 public:
   nsAtomSubTable& SelectSubTable(AtomTableKey& aKey);
   void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes);
   void GC(GCKind aKind);
   already_AddRefed<nsAtom> Atomize(const nsAString& aUTF16String);
   already_AddRefed<nsAtom> Atomize(const nsACString& aUTF8String);
   already_AddRefed<nsAtom> AtomizeMainThread(const nsAString& aUTF16String);
   nsStaticAtom* GetStaticAtom(const nsAString& aUTF16String);
-  void RegisterStaticAtoms(const nsStaticAtomSetup* aSetup, uint32_t aCount);
+  void RegisterStaticAtoms(const nsStaticAtom* aAtoms, size_t aAtomsLen);
 
   // The result of this function may be imprecise if other threads are operating
   // on atoms concurrently. It's also slow, since it triggers a GC before
   // counting.
   size_t RacySlowCount();
 
   // This hash table op is a static member of this class so that it can take
   // advantage of |friend| declarations.
@@ -642,26 +642,23 @@ nsAtomSubTable::AddSizeOfExcludingThisLo
   aSizes.mTable += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
   for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
     auto entry = static_cast<AtomTableEntry*>(iter.Get());
     entry->mAtom->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
   }
 }
 
 void
-nsAtomTable::RegisterStaticAtoms(const nsStaticAtomSetup* aSetup,
-                                 uint32_t aCount)
+nsAtomTable::RegisterStaticAtoms(const nsStaticAtom* aAtoms, size_t aAtomsLen)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(!gStaticAtomsDone, "Static atom insertion is finished!");
 
-  for (uint32_t i = 0; i < aCount; ++i) {
-    const nsStaticAtom* atom = aSetup[i].mAtom;
-    nsStaticAtom** atomp = aSetup[i].mAtomp;
-
+  for (uint32_t i = 0; i < aAtomsLen; ++i) {
+    const nsStaticAtom* atom = &aAtoms[i];
     MOZ_ASSERT(nsCRT::IsAscii(atom->String()));
     MOZ_ASSERT(NS_strlen(atom->String()) == atom->GetLength());
 
     AtomTableKey key(atom);
     nsAtomSubTable& table = SelectSubTable(key);
     MutexAutoLock lock(table.mLock);
     AtomTableEntry* he = table.Add(key);
 
@@ -671,25 +668,25 @@ nsAtomTable::RegisterStaticAtoms(const n
       // - Create a dynamic atom and then register a static atom with the same
       //   string while the dynamic atom is alive.
       // Both cases can cause subtle bugs, and are disallowed. We're
       // programming in C++ here, not Smalltalk.
       nsAutoCString name;
       he->mAtom->ToUTF8String(name);
       MOZ_CRASH_UNSAFE_PRINTF("Atom for '%s' already exists", name.get());
     }
-    he->mAtom = *atomp = const_cast<nsStaticAtom*>(atom);
+    he->mAtom = const_cast<nsStaticAtom*>(atom);
   }
 }
 
 void
-RegisterStaticAtoms(const nsStaticAtomSetup* aSetup, uint32_t aCount)
+NS_RegisterStaticAtoms(const nsStaticAtom* aAtoms, size_t aAtomsLen)
 {
   MOZ_ASSERT(gAtomTable);
-  gAtomTable->RegisterStaticAtoms(aSetup, aCount);
+  gAtomTable->RegisterStaticAtoms(aAtoms, aAtomsLen);
 }
 
 already_AddRefed<nsAtom>
 NS_Atomize(const char* aUTF8String)
 {
   MOZ_ASSERT(gAtomTable);
   return gAtomTable->Atomize(nsDependentCString(aUTF8String));
 }
--- a/xpcom/ds/nsGkAtoms.cpp
+++ b/xpcom/ds/nsGkAtoms.cpp
@@ -9,35 +9,34 @@
 namespace mozilla {
 namespace detail {
 
 MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 extern constexpr GkAtoms gGkAtoms = {
   #define GK_ATOM(name_, value_) NS_STATIC_ATOM_INIT_STRING(value_)
   #include "nsGkAtomList.h"
   #undef GK_ATOM
-
-  #define GK_ATOM(name_, value_) \
-    NS_STATIC_ATOM_INIT_ATOM(GkAtoms, name_, value_)
-  #include "nsGkAtomList.h"
-  #undef GK_ATOM
+  {
+    #define GK_ATOM(name_, value_) \
+      NS_STATIC_ATOM_INIT_ATOM(nsStaticAtom, GkAtoms, name_, value_)
+    #include "nsGkAtomList.h"
+    #undef GK_ATOM
+  }
 };
 MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 
 } // namespace detail
 } // namespace mozilla
 
-#define GK_ATOM(name_, value_) NS_STATIC_ATOM_DEFN_PTR(nsGkAtoms, name_)
+const nsStaticAtom* const nsGkAtoms::sAtoms = mozilla::detail::gGkAtoms.mAtoms;
+
+#define GK_ATOM(name_, value_) \
+  NS_STATIC_ATOM_DEFN_PTR( \
+    nsStaticAtom, mozilla::detail::GkAtoms, mozilla::detail::gGkAtoms, \
+    nsGkAtoms, name_)
 #include "nsGkAtomList.h"
 #undef GK_ATOM
 
-static const nsStaticAtomSetup sGkAtomSetup[] = {
-  #define GK_ATOM(name_, value_) \
-    NS_STATIC_ATOM_SETUP(mozilla::detail::gGkAtoms, nsGkAtoms, name_)
-  #include "nsGkAtomList.h"
-  #undef GK_ATOM
-};
-
 void nsGkAtoms::RegisterStaticAtoms()
 {
-  NS_RegisterStaticAtoms(sGkAtomSetup);
+  NS_RegisterStaticAtoms(sAtoms, sAtomsLen);
 }
 
--- a/xpcom/ds/nsGkAtoms.h
+++ b/xpcom/ds/nsGkAtoms.h
@@ -14,29 +14,39 @@ namespace mozilla {
 namespace detail {
 
 struct GkAtoms
 {
   #define GK_ATOM(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
   #include "nsGkAtomList.h"
   #undef GK_ATOM
 
-  #define GK_ATOM(name_, value_) NS_STATIC_ATOM_DECL_ATOM(name_)
-  #include "nsGkAtomList.h"
-  #undef GK_ATOM
+  enum class Atoms {
+    #define GK_ATOM(name_, value_) NS_STATIC_ATOM_ENUM(name_)
+    #include "nsGkAtomList.h"
+    #undef GK_ATOM
+    AtomsCount
+  };
+
+  const nsStaticAtom mAtoms[static_cast<size_t>(Atoms::AtomsCount)];
 };
 
 extern const GkAtoms gGkAtoms;
 
 } // namespace detail
 } // namespace mozilla
 
 class nsGkAtoms
 {
+private:
+  static const nsStaticAtom* const sAtoms;
+  static constexpr size_t sAtomsLen =
+    mozilla::ArrayLength(mozilla::detail::gGkAtoms.mAtoms);
+
 public:
   static void RegisterStaticAtoms();
 
-  #define GK_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(name_)
+  #define GK_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(nsStaticAtom, name_)
   #include "nsGkAtomList.h"
   #undef GK_ATOM
 };
 
 #endif /* nsGkAtoms_h___ */
--- a/xpcom/ds/nsStaticAtom.h
+++ b/xpcom/ds/nsStaticAtom.h
@@ -4,245 +4,259 @@
  * 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 nsStaticAtom_h__
 #define nsStaticAtom_h__
 
 #include <stdint.h>
 #include "nsAtom.h"
+#include "mozilla/ArrayUtils.h"
 #include "mozilla/Maybe.h"
 
-// The following macros are used to define static atoms, typically in
-// conjunction with a .h file that defines the names and values of the atoms.
+// Static atoms are structured carefully to satisfy a lot of constraints.
+//
+// - We have ~2700 static atoms. They are divided across ~4 classes, with the
+//   majority in nsGkAtoms.
+//
+// - We want them to be constexpr so they end up in .rodata, and thus shared
+//   between processes, minimizing memory usage.
+//
+// - We need them to be in an array, so we can iterate over them (for
+//   registration and lookups).
+//
+// - Each static atom has a string literal associated with it. We can't use a
+//   pointer to the string literal because then the atoms won't end up in
+//   .rodata. Therefore the string literals and the atoms must be arranged in a
+//   way such that a numeric index can be used instead. This numeric index
+//   (nsStaticAtom::mStringOffset) must be computable at compile-time to keep
+//   the static atom constexpr. It should also not be too large (a uint32_t is
+//   reasonable).
+//
+// - Each static atom stores the hash value of its associated string literal;
+//   it's used in various ways. The hash value must be computed at
+//   compile-time, to keep the static atom constexpr.
+//
+// - As well as accessing each static atom via array indexing, we need an
+//   individual pointer, e.g. nsGkAtoms::foo. Ideally this would be constexpr
+//   so it doesn't take up any space in memory. Unfortunately MSVC's constexpr
+//   support is buggy and so this isn't possible yet. See bug 1449787.
+//
+// - The array of static atoms can't be in a .h file, because it's a huge
+//   constexpr expression, which would blow out compile times. But the
+//   individual pointers for the static atoms must be in a .h file so they are
+//   public.
+//
+// The macros below are used to define static atoms in a way that satisfies
+// these constraints. They are used in conjunction with a .h file that defines
+// the names and values of the atoms.
 //
 // For example, the .h file might be called MyAtomList.h and look like this:
 //
 //   MY_ATOM(one, "one")
 //   MY_ATOM(two, "two")
 //   MY_ATOM(three, "three")
 //
-// The code defining the static atoms should look something like this:
-//
-//   ====> MyAtoms.h <====
-//
-//   namespace detail {
-//
-//   struct MyAtoms
-//   {
-//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
-//     #include "MyAtomList.h"
-//     #undef MY_ATOM
-//
-//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DECL_ATOM(name_)
-//     #include "MyAtomList.h"
-//     #undef MY_ATOM
-//   };
-//
-//   extern const MyAtoms gMyAtoms;
-//
-//   } // namespace detail
-//
-//   class nsMyAtoms
-//   {
-//   public:
-//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(name_)
-//     #include "MyAtomList.h"
-//     #undef MY_ATOM
-//   };
-//
-//   ====> MyAtoms.cpp <====
-//
-//   MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
-//   static constexpr MyAtoms gMyAtoms = {
-//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_INIT_STRING(value_)
-//     #include "MyAtomList.h"
-//     #undef MY_ATOM
-//
-//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_INIT_ATOM(MyAtoms, name_, value_)
-//     #include "MyAtomList.h"
-//     #undef MY_ATOM
-//   };
-//   MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
-//
-//   #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DEFN_PTR(nsMyAtoms, name_)
-//   #include "MyAtomList.h"
-//   #undef MY_ATOM
-//
-//   static const nsStaticAtomSetup sMyAtomSetup[] = {
-//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_SETUP(mozilla::detail::gMyAtoms, nsMyAtoms, name_)
-//     #include "MyAtomList.h"
-//     #undef MY_ATOM
-//   };
-//
-// The macros expand to the following:
+// The code defining the static atoms should look something like the following.
+// ("<<<"/"---"/">>>" markers are used below to indicate what the macros look
+// like before and after expansion.)
 //
 //   ====> MyAtoms.h <====
 //
 //   // A `detail` namespace is used because the things within it aren't
 //   // directly referenced by external users of these static atoms.
 //   namespace detail {
 //
 //   // This `detail` class contains the atom strings and the atom objects.
 //   // Because they are together in a class, the mStringOffset field of the
 //   // atoms will be small and can be initialized at compile time.
 //   struct MyAtoms
 //   {
+//     <<<
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
+//     #include "MyAtomList.h"
+//     #undef MY_ATOM
+//     ---
 //     const char16_t one_string[4];
 //     const char16_t two_string[4];
 //     const char16_t three_string[6];
+//     >>>
 //
-//     const nsStaticAtom one_atom;
-//     const nsStaticAtom two_atom;
-//     const nsStaticAtom three_atom;
+//     enum class Atoms {
+//       <<<
+//       #define MY_ATOM(name_, value_) NS_STATIC_ATOM_ENUM(name_)
+//       #include "MyAtomList.h"
+//       #undef MY_ATOM
+//       ---
+//       one,
+//       two,
+//       three,
+//       >>>
+//       AtomsCount
+//     };
+//
+//     const nsStaticAtom mAtoms[static_cast<size_t>(Atoms::AtomsCount)];
 //   };
 //
+//   // The MyAtoms instance is `extern const` so it can be defined in a .cpp
+//   // file.
 //   extern const MyAtoms gMyAtoms;
 //
 //   } // namespace detail
 //
 //   // This class holds the pointers to the individual atoms.
 //   class nsMyAtoms
 //   {
+//   private:
+//     // This is a useful handle to the array of atoms, used below and also
+//     // possibly by Rust code.
+//     static const nsStaticAtom* const sAtoms;
+//
+//     // The number of atoms, used below.
+//     static constexpr size_t sAtomsLen =
+//       mozilla::ArrayLength(detail::gMyAtoms.mAtoms);
+//
 //   public:
+//     // The type is not `nsStaticAtom* const` -- even though these atoms are
+//     // immutable -- because they are often passed to functions with
+//     // `nsAtom*` parameters, i.e. that can be passed both dynamic and
+//     // static.
+//     <<<
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(nsStaticAtom, name_)
+//     #include "MyAtomList.h"
+//     #undef MY_ATOM
+//     ---
 //     static nsStaticAtom* one;
 //     static nsStaticAtom* two;
 //     static nsStaticAtom* three;
+//     >>>
 //   };
 //
 //   ====> MyAtoms.cpp <====
 //
+//   namespace detail {
+//
 //   // Need to suppress some MSVC warning weirdness with WrappingMultiply().
 //   MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 //   // Because this is `constexpr` it ends up in read-only memory where it can
 //   // be shared between processes.
-//   extern constexpr MyAtoms gMyAtoms = {
+//   static constexpr MyAtoms gMyAtoms = {
+//     <<<
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_INIT_STRING(value_)
+//     #include "MyAtomList.h"
+//     #undef MY_ATOM
+//     ---
 //     u"one",
 //     u"two",
 //     u"three",
-//
-//     nsStaticAtom(u"one", 3, offsetof(MyAtoms, one_atom) -
-//                             offsetof(MyAtoms, one_string)),
-//     nsStaticAtom(u"two", 3, offsetof(MyAtoms, two_atom) -
-//                             offsetof(MyAtoms, two_string)),
-//     nsStaticAtom(u"three", 3, offsetof(MyAtoms, three_atom) -
-//                               offsetof(MyAtoms, three_string)),
+//     >>>
+//     {
+//       <<<
+//       #define MY_ATOM(name_, value_) NS_STATIC_ATOM_INIT_ATOM(nsStaticAtom, MyAtoms, name_, value_)
+//       #include "MyAtomList.h"
+//       #undef MY_ATOM
+//       ---
+//       nsStaticAtom(
+//         u"one", 3,
+//         offsetof(MyAtoms, mAtoms[static_cast<size_t>(MyAtoms::Atoms::one)]) -
+//         offsetof(MyAtoms, one_string)),
+//       nsStaticAtom(
+//         u"two", 3,
+//         offsetof(MyAtoms, mAtoms[static_cast<size_t>(MyAtoms::Atoms::two)]) -
+//         offsetof(MyAtoms, two_string)),
+//       nsStaticAtom(
+//         u"three", 3,
+//         offsetof(MyAtoms, mAtoms[static_cast<size_t>(MyAtoms::Atoms::three)]) -
+//         offsetof(MyAtoms, three_string)),
+//       >>>
+//     }
 //   };
 //   MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 //
-//   nsStaticAtom* MyAtoms::one;
-//   nsStaticAtom* MyAtoms::two;
-//   nsStaticAtom* MyAtoms::three;
+//   } // namespace detail
+//
+//   const nsStaticAtom* const nsMyAtoms::sAtoms =
+//     mozilla::detail::gMyAtoms.mAtoms;
 //
-//   static const nsStaticAtomSetup sMyAtomSetup[] = {
-//     { &detail::gMyAtoms.one_atom, &nsMyAtoms::one },
-//     { &detail::gMyAtoms.two_atom, &nsMyAtoms::two },
-//     { &detail::gMyAtoms.three_atom, &nsMyAtoms::three },
-//   };
+//   <<<
+//   #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DEFN_PTR(nsStaticAtom, detail::MyAtoms, detail::gMyAtoms, nsMyAtoms, name_)
+//   #include "MyAtomList.h"
+//   #undef MY_ATOM
+//   ---
+//   nsStaticAtom* nsMyAtoms::one =
+//     const_cast<nsStaticAtom*>(&detail::gMyAtoms.mAtoms[
+//       static_cast<size_t>(detail::MyAtoms::Atoms::one)]);
+//   nsStaticAtom* nsMyAtoms::two =
+//     const_cast<nsStaticAtom*>(&detail::gMyAtoms.mAtoms[
+//       static_cast<size_t>(detail::MyAtoms::Atoms::two)]);
+//   nsStaticAtom* nsMyAtoms::three =
+//     const_cast<nsStaticAtom*>(&detail::gMyAtoms.mAtoms[
+//       static_cast<size_t>(detail::MyAtoms::Atoms::three)]);
+//   >>>
 //
-// When RegisterStaticAtoms(sMyAtomSetup) is called it iterates over
-// sMyAtomSetup[]. E.g. for the first atom it does roughly the following:
-// - MyAtoms::one = one_atom
-// - inserts one_atom into the atom table
+// When NS_RegisterStaticAtoms(sAtoms, sAtomsLen) is called it iterates
+// over the atoms, inserting them into the atom table.
 
-// The declaration of the atom's string, which must be within the same `detail`
-// class as NS_STATIC_ATOM_DECL_ATOM.
+// The declaration of the atom's string.
 #define NS_STATIC_ATOM_DECL_STRING(name_, value_) \
   const char16_t name_##_string[sizeof(value_)];
 
-// The declaration of the static atom itself, which must be within the same
-// `detail` class as NS_STATIC_ATOM_DECL_STRING.
-#define NS_STATIC_ATOM_DECL_ATOM(name_) \
-  const nsStaticAtom name_##_atom;
+// The enum value for the atom.
+#define NS_STATIC_ATOM_ENUM(name_) \
+  name_,
 
-// The declaration of the pointer to the static atom, which must be within
-// a class.
-#define NS_STATIC_ATOM_DECL_PTR(name_) \
-  static nsStaticAtom* name_;
-
-// Like NS_STATIC_ATOM_DECL, but for sub-classes of nsStaticAtom.
-#define NS_STATIC_ATOM_SUBCLASS_DECL_PTR(type_, name_) \
+// The declaration of the pointer to the static atom. `type_` must be
+// `nsStaticAtom` or a subclass thereof.
+// XXX: Eventually this should be combined with NS_STATIC_ATOM_DEFN_PTR and the
+// pointer should be made `constexpr`. See bug 1449787.
+#define NS_STATIC_ATOM_DECL_PTR(type_, name_) \
   static type_* name_;
 
-// The initialization of the atom's string, which must be within a `constexpr`
-// instance of the `detail` class.
+// The initialization of the atom's string.
 #define NS_STATIC_ATOM_INIT_STRING(value_) \
   u"" value_,
 
-// The initialization of the static atom itself, which must be within a
-// `constexpr` instance of the `detail` class.
+// The initialization of the atom itself. `type_` must be `nsStaticAtom` or a
+// subclass thereof.
 //
 // Note that |value_| is an 8-bit string, and so |sizeof(value_)| is equal
 // to the number of chars (including the terminating '\0'). The |u""| prefix
 // converts |value_| to a 16-bit string.
-#define NS_STATIC_ATOM_INIT_ATOM(class_, name_, value_) \
-  nsStaticAtom(u"" value_, \
-               sizeof(value_) - 1, \
-               offsetof(class_, name_##_atom) - \
-               offsetof(class_, name_##_string)),
-
-// The definition of the pointer to the static atom. Initially null, it is
-// set by RegisterStaticAtoms() to point to a heap-allocated nsStaticAtom.
-#define NS_STATIC_ATOM_DEFN_PTR(class_, name_) \
-  nsStaticAtom* class_::name_;
-
-// Like NS_STATIC_ATOM_DEFN, but for sub-classes of nsStaticAtom.
-#define NS_STATIC_ATOM_SUBCLASS_DEFN_PTR(type_, class_, name_) \
-  type_* class_::name_;
+#define NS_STATIC_ATOM_INIT_ATOM(type_, detailClass_, name_, value_) \
+  type_(u"" value_, \
+        sizeof(value_) - 1, \
+        offsetof(detailClass_, \
+                 mAtoms[static_cast<size_t>(detailClass_::Atoms::name_)]) - \
+        offsetof(detailClass_, name_##_string)),
 
-// The StaticAtomSetup. Used during start-up, and in some cases afterwards.
-#define NS_STATIC_ATOM_SETUP(detailObj_, class_, name_) \
-  { &detailObj_.name_##_atom, &class_::name_ },
-
-// Like NS_STATIC_ATOM_SUBCLASS, but for sub-classes of nsStaticAtom.
-#define NS_STATIC_ATOM_SUBCLASS_SETUP(detailObj_, class_, name_) \
-  { &detailObj_.name_##_atom, \
-    reinterpret_cast<nsStaticAtom**>(&class_::name_) },
-
-// Holds data used to initialize large number of atoms during startup. Use
-// NS_STATIC_ATOM_SETUP to initialize these structs.
-struct nsStaticAtomSetup
-{
-  const nsStaticAtom* const mAtom;
-  nsStaticAtom** const mAtomp;
-};
+// Definition of the pointer to the static atom. `type_` must be `nsStaticAtom`
+// or a subclass thereof.
+#define NS_STATIC_ATOM_DEFN_PTR(type_, detailClass_, detailObj_, class_, name_) \
+  type_* class_::name_ = const_cast<type_*>( \
+    &detailObj_.mAtoms[static_cast<size_t>(detailClass_::Atoms::name_)]);
 
 // Register an array of static atoms with the atom table.
-template<uint32_t N>
 void
-NS_RegisterStaticAtoms(const nsStaticAtomSetup (&aSetup)[N])
-{
-  extern void RegisterStaticAtoms(const nsStaticAtomSetup* aSetup,
-                                  uint32_t aCount);
-  RegisterStaticAtoms(aSetup, N);
-}
+NS_RegisterStaticAtoms(const nsStaticAtom* aAtoms, size_t aAtomsLen);
 
 // This class holds basic operations on arrays of static atoms.
 class nsStaticAtomUtils {
 public:
-  template<uint32_t N>
-  static mozilla::Maybe<uint32_t> Lookup(nsAtom *aAtom,
-                                         const nsStaticAtomSetup (&aSetup)[N])
-  {
-    return Lookup(aAtom, aSetup, N);
-  }
-
-  template<uint32_t N>
-  static bool IsMember(nsAtom *aAtom, const nsStaticAtomSetup (&aSetup)[N])
-  {
-    return Lookup(aAtom, aSetup, N).isSome();
-  }
-
-private:
   static mozilla::Maybe<uint32_t> Lookup(nsAtom* aAtom,
-                                         const nsStaticAtomSetup* aSetup,
+                                         const nsStaticAtom* aAtoms,
                                          uint32_t aCount)
   {
     for (uint32_t i = 0; i < aCount; i++) {
-      if (aAtom == *(aSetup[i].mAtomp)) {
+      if (aAtom == &aAtoms[i]) {
         return mozilla::Some(i);
       }
     }
     return mozilla::Nothing();
   }
+
+  static bool IsMember(nsAtom* aAtom, const nsStaticAtom* aAtoms,
+                       uint32_t aCount)
+  {
+    return Lookup(aAtom, aAtoms, aCount).isSome();
+  }
 };
 
 #endif
--- a/xpcom/io/nsDirectoryService.cpp
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -97,56 +97,54 @@ nsDirectoryService::Create(nsISupports* 
 namespace mozilla {
 namespace detail {
 
 MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 extern constexpr DirectoryAtoms gDirectoryAtoms = {
   #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_INIT_STRING(value_)
   #include "nsDirectoryServiceAtomList.h"
   #undef DIR_ATOM
-
-  #define DIR_ATOM(name_, value_) \
-    NS_STATIC_ATOM_INIT_ATOM(DirectoryAtoms, name_, value_)
-  #include "nsDirectoryServiceAtomList.h"
-  #undef DIR_ATOM
+  {
+    #define DIR_ATOM(name_, value_) \
+      NS_STATIC_ATOM_INIT_ATOM(nsStaticAtom, DirectoryAtoms, name_, value_)
+    #include "nsDirectoryServiceAtomList.h"
+    #undef DIR_ATOM
+  }
 };
 MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 
 } // namespace detail
 } // namespace mozilla
 
+const nsStaticAtom* const nsDirectoryService::sAtoms =
+  mozilla::detail::gDirectoryAtoms.mAtoms;
+
 #define DIR_ATOM(name_, value_) \
-  NS_STATIC_ATOM_DEFN_PTR(nsDirectoryService, name_)
+  NS_STATIC_ATOM_DEFN_PTR( \
+    nsStaticAtom, mozilla::detail::DirectoryAtoms, \
+    mozilla::detail::gDirectoryAtoms, nsDirectoryService, name_)
 #include "nsDirectoryServiceAtomList.h"
 #undef DIR_ATOM
 
-static const nsStaticAtomSetup sDirectoryServiceAtomSetup[] = {
-  #define DIR_ATOM(name_, value_) \
-    NS_STATIC_ATOM_SETUP( \
-      mozilla::detail::gDirectoryAtoms, nsDirectoryService, name_)
-  #include "nsDirectoryServiceAtomList.h"
-  #undef DIR_ATOM
-};
-
 NS_IMETHODIMP
 nsDirectoryService::Init()
 {
   NS_NOTREACHED("nsDirectoryService::Init() for internal use only!");
   return NS_OK;
 }
 
 void
 nsDirectoryService::RealInit()
 {
   NS_ASSERTION(!gService,
                "nsDirectoryService::RealInit Mustn't initialize twice!");
 
   gService = new nsDirectoryService();
 
-  NS_RegisterStaticAtoms(sDirectoryServiceAtomSetup);
+  NS_RegisterStaticAtoms(sAtoms, sAtomsLen);
 
   // Let the list hold the only reference to the provider.
   nsAppFileLocationProvider* defaultProvider = new nsAppFileLocationProvider;
   gService->mProviders.AppendElement(defaultProvider);
 }
 
 nsDirectoryService::~nsDirectoryService()
 {
--- a/xpcom/io/nsDirectoryService.h
+++ b/xpcom/io/nsDirectoryService.h
@@ -25,19 +25,24 @@ namespace mozilla {
 namespace detail {
 
 struct DirectoryAtoms
 {
   #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
   #include "nsDirectoryServiceAtomList.h"
   #undef DIR_ATOM
 
-  #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DECL_ATOM(name_)
-  #include "nsDirectoryServiceAtomList.h"
-  #undef DIR_ATOM
+  enum class Atoms {
+    #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_ENUM(name_)
+    #include "nsDirectoryServiceAtomList.h"
+    #undef DIR_ATOM
+    AtomsCount
+  };
+
+  const nsStaticAtom mAtoms[static_cast<size_t>(Atoms::AtomsCount)];
 };
 
 extern const DirectoryAtoms gDirectoryAtoms;
 
 } // namespace detail
 } // namespace mozilla
 
 class nsDirectoryService final
@@ -69,15 +74,19 @@ public:
 private:
   ~nsDirectoryService();
 
   nsresult GetCurrentProcessDirectory(nsIFile** aFile);
 
   nsInterfaceHashtable<nsCStringHashKey, nsIFile> mHashtable;
   nsTArray<nsCOMPtr<nsIDirectoryServiceProvider>> mProviders;
 
+  static const nsStaticAtom* const sAtoms;
+  static constexpr size_t sAtomsLen =
+    mozilla::ArrayLength(mozilla::detail::gDirectoryAtoms.mAtoms);
+
 public:
-  #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(name_)
+  #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(nsStaticAtom, name_)
   #include "nsDirectoryServiceAtomList.h"
   #undef DIR_ATOM
 };
 
 #endif