/* -*- 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 specified 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. We want this to be constexpr so // it doesn't take up any space in memory. // // - 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(a, "a", 0x01234567, nsStaticAtom, Atom) // GK_ATOM(bb, "bb", 0x12345678, nsCSSPseudoElementStaticAtom, // PseudoElementAtom) // GK_ATOM(Ccc, "Ccc", 0x23456789, nsCSSAnonBoxPseudoStaticAtom, // InheritingAnonBoxAtom) // // Comments throughout this file and nsGkAtoms.cpp show how these entries get // expanded by macros. // 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_(uint32_t aLength, uint32_t aHash, uint32_t aOffset, \ bool aIsAsciiLowercase) \ : nsStaticAtom(aLength, aHash, aOffset, aIsAsciiLowercase) {} \ }; DEFINE_STATIC_ATOM_SUBCLASS(nsCSSAnonBoxPseudoStaticAtom) DEFINE_STATIC_ATOM_SUBCLASS(nsCSSPseudoElementStaticAtom) #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. // // Expansion of the example GK_ATOM entries from above: // // const char16_t a_string[sizeof("a")]; // const char16_t bb_string[sizeof("bb")]; // const char16_t Ccc_string[sizeof("Ccc")]; // #define GK_ATOM(name_, value_, hash_, is_ascii_lower_, 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 { // Expansion of the example GK_ATOM entries above: // // a, // bb, // Ccc, // #define GK_ATOM(name_, value_, hash_, is_ascii_lower_, type_, atom_type_) name_, #include "nsGkAtomList.h" #undef GK_ATOM AtomsCount }; const nsStaticAtom mAtoms[static_cast(Atoms::AtomsCount)]; }; // The GkAtoms instance is `extern const` so it can be defined in a .cpp file. // // XXX: The NS_EXTERNAL_VIS is necessary to work around an apparent GCC bug: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87494 #if defined(__GNUC__) && !defined(__clang__) extern NS_EXTERNAL_VIS const GkAtoms gGkAtoms; #else extern const GkAtoms gGkAtoms; #endif } // namespace detail } // namespace mozilla // This class holds the pointers to the individual atoms. class nsGkAtoms { private: friend void NS_InitAtomTable(); // 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(mozilla::detail::GkAtoms::Atoms::AtomsCount); public: static nsStaticAtom* GetAtomByIndex(size_t aIndex) { MOZ_ASSERT(aIndex < sAtomsLen); return const_cast(&sAtoms[aIndex]); } static size_t IndexOf(const nsStaticAtom* atom) { nsStaticAtom* firstAtom = GetAtomByIndex(0); size_t ret = atom - firstAtom; MOZ_ASSERT(ret < sAtomsLen); return ret; } // The definition of the pointer to each static atom. // // These types are not `static constexpr * const` -- even though these // atoms are immutable -- because they are often passed to functions with // `nsAtom*` parameters that can be passed both dynamic and static atoms. // // Expansion of the example GK_ATOM entries above: // // static constexpr nsStaticAtom* a = // const_cast( // &mozilla::detail::gGkAtoms.mAtoms[ // static_cast(mozilla::detail::GkAtoms::Atoms::a)]); // // static constexpr nsStaticAtom* bb = // const_cast( // &mozilla::detail::gGkAtoms.mAtoms[ // static_cast(mozilla::detail::GkAtoms::Atoms::bb)]); // // static constexpr nsStaticAtom* Ccc = // const_cast( // &mozilla::detail::gGkAtoms.mAtoms[ // static_cast(mozilla::detail::GkAtoms::Atoms::Ccc)]); // #define GK_ATOM(name_, value_, hash_, is_ascii_lower_, type_, atom_type_) \ static constexpr nsStaticAtom* name_ = const_cast( \ &mozilla::detail::gGkAtoms.mAtoms[static_cast( \ mozilla::detail::GkAtoms::Atoms::name_)]); #include "nsGkAtomList.h" #undef GK_ATOM }; inline bool nsAtom::IsEmpty() const { return this == nsGkAtoms::_empty; } #endif /* nsGkAtoms_h___ */