/* -*- 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/. */ #ifndef nsGkAtoms_h___ #define nsGkAtoms_h___ #include "nsAtom.h" // Static atoms are structured carefully to satisfy a lot of constraints. // // - We have ~2300 static atoms. // // - 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. // // nsGkAtoms below defines static atoms in a way that satisfies these // constraints. It uses nsGkAtomList.h, which defines the names and values of // the atoms. // // nsGkAtomList.h is generated by StaticAtoms.py and has entries that look // like this: // // GK_ATOM(one, "one", 0x01234567, nsStaticAtom, Atom) // GK_ATOM(two, "two", 0x12345678, nsICSSPseudoElement, PseudoElementAtom) // GK_ATOM(three, "three", 0x23456789, nsICSSAnonBoxPseudo, InheritingAnonBoxAtom) // // After macro expansion, the atom definitions look like the following: // // ====> nsGkAtoms.h <==== // // namespace mozilla { // namespace detail { // // struct GkAtoms // { // // The declaration of each atom's string. // const char16_t one_string[sizeof("one")]; // const char16_t two_string[sizeof("two")]; // const char16_t three_string[sizeof("three")]; // // // The enum value for each atom. // enum class Atoms { // one_, // two_, // three_, // AtomsCount // }; // // const nsStaticAtom mAtoms[static_cast(Atoms::AtomsCount)]; // }; // // } // namespace detail // } // namespace mozilla // // // This class holds the pointers to the individual atoms. // class nsGkAtoms // { // 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 = // static_cast(detail::MyAtoms::Atoms::AtomsCount); // // public: // // These types are not `nsStaticAtom* const`, etc. -- 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. // static nsStaticAtom* one; // static nsICSSPseudoElement* two; // static nsICSSAnonBoxPseudo* three; // }; // // ====> nsGkAtoms.cpp <==== // // namespace mozilla { // 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. // static constexpr GkAtoms gGkAtoms = { // // The initialization of each atom's string. // u"one", // u"two", // u"three", // { // // The initialization of the atoms themselves. // nsStaticAtom( // u"one", 3, // 0x01234567, // offsetof(GkAtoms, mAtoms[static_cast(GkAtoms::Atoms::one)]) - // offsetof(GkAtoms, one_string)), // nsStaticAtom( // u"two", 3, // 0x12345678, // offsetof(GkAtoms, mAtoms[static_cast(GkAtoms::Atoms::two)]) - // offsetof(GkAtoms, two_string)), // nsStaticAtom( // u"three", 3, // 0x23456789, // offsetof(GkAtoms, mAtoms[static_cast(GkAtoms::Atoms::three)]) - // offsetof(GkAtoms, three_string)), // } // }; // MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING // // } // namespace detail // } // namespace mozilla // // const nsStaticAtom* const nsGkAtoms::sAtoms = // mozilla::detail::gGkAtoms.mAtoms; // // // Definition of the pointer to the static atom. // nsStaticAtom* nsGkAtoms::one = // const_cast(static_cast( // &detail::gGkAtoms.mAtoms[ // static_cast(detail::GkAtoms::Atoms::one)]); // nsICSSPseudoElement* nsGkAtoms::two = // const_cast(static_cast( // &detail::gGkAtoms.mAtoms[ // static_cast(detail::GkAtoms::Atoms::two)]); // nsICSSAnonBoxPseudo* nsGkAtoms::three = // const_cast(static_cast( // &detail::gGkAtoms.mAtoms[ // static_cast(detail::GkAtoms::Atoms::three)]); // Trivial subclasses of nsStaticAtom so that function signatures can require // an atom from a specific atom list. #define DEFINE_STATIC_ATOM_SUBCLASS(name_) \ class name_ : public nsStaticAtom \ { \ public: \ constexpr name_(const char16_t* aStr, uint32_t aLength, \ uint32_t aHash, uint32_t aOffset) \ : nsStaticAtom(aStr, aLength, aHash, aOffset) {} \ }; DEFINE_STATIC_ATOM_SUBCLASS(nsICSSAnonBoxPseudo) DEFINE_STATIC_ATOM_SUBCLASS(nsICSSPseudoElement) #undef DEFINE_STATIC_ATOM_SUBCLASS namespace mozilla { 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. // // A `detail` namespace is used because the things within it aren't directly // referenced by external users of these static atoms. struct GkAtoms { // The declaration of each atom's string. #define GK_ATOM(name_, value_, hash_, type_, atom_type_) \ const char16_t name_##_string[sizeof(value_)]; #include "nsGkAtomList.h" #undef GK_ATOM // The enum value for each atom. enum class Atoms { #define GK_ATOM(name_, value_, hash_, type_, atom_type_) \ name_, #include "nsGkAtomList.h" #undef GK_ATOM AtomsCount }; const nsStaticAtom mAtoms[static_cast(Atoms::AtomsCount)]; }; } // namespace detail } // namespace mozilla class nsGkAtoms { private: static const nsStaticAtom* const sAtoms; static constexpr size_t sAtomsLen = static_cast(mozilla::detail::GkAtoms::Atoms::AtomsCount); public: static void RegisterStaticAtoms(); static nsStaticAtom* GetAtomByIndex(size_t aIndex) { MOZ_ASSERT(aIndex < sAtomsLen); return const_cast(&sAtoms[aIndex]); } // The declaration of the pointer to each static atom. // // XXX: Eventually this should be combined with its definition and the // pointer should be made `constexpr`. See bug 1449787. #define GK_ATOM(name_, value_, hash_, type_, atom_type_) \ static type_* name_; #include "nsGkAtomList.h" #undef GK_ATOM }; #endif /* nsGkAtoms_h___ */