Bug 1407117 - Simplify static atom representation. r=froydnj,bz.

Currently nsAtom::mString points to the interior of an nsStringBuffer. For
static atoms this requires the use of nsFakeStringBuffer, which is pretty
gross.

This patch changes things so that nsAtom::mString points to a static char
buffer for static atoms. This simplifies a number of things:

- nsFakeStringBuffer and CheckStaticAtomSizes are no longer needed.

- FakeBufferRefCountHelper is no longer needed.

- nsAtom's constructor for static atoms is simpler.

- RegisterStaticAtoms() is simpler.

On the flip-side, a couple of things get more complicated.

- nsAtom::ToString() treats static and dynamic atoms differently.

- nsAtom::GetStringBuffer() is now only valid for dynamic atoms. This
  function is only used in two places, both involving DOMString, so those
  locations are updated appropriately. This also requires updating some other
  code assigning nsStrings to DOMStrings, because we can't assume that
  nsStrings are shared.

On Linux64 this change reduces the size of the binary by 8752 B, and moves
81968 B from the .data to the .rodata section, where it can be shared between
processes.

--HG--
extra : rebase_source : 0f6fcdec1c525aa66222e208b66a9f9026f69bcb
This commit is contained in:
Nicholas Nethercote 2017-10-12 10:52:17 +11:00
Родитель ab27f10405
Коммит f2d1f3b005
8 изменённых файлов: 56 добавлений и 123 удалений

Просмотреть файл

@ -543,7 +543,7 @@ nsAttrValue::ToString(mozilla::dom::DOMString& aResult) const
case eAtom: case eAtom:
{ {
nsAtom *atom = static_cast<nsAtom*>(GetPtr()); nsAtom *atom = static_cast<nsAtom*>(GetPtr());
aResult.SetStringBuffer(atom->GetStringBuffer(), atom->GetLength()); aResult.SetOwnedAtom(atom, mozilla::dom::DOMString::eNullNotExpected);
break; break;
} }
default: default:

Просмотреть файл

@ -1801,8 +1801,7 @@ public:
void GetNodeName(mozilla::dom::DOMString& aNodeName) void GetNodeName(mozilla::dom::DOMString& aNodeName)
{ {
const nsString& nodeName = NodeName(); const nsString& nodeName = NodeName();
aNodeName.SetStringBuffer(nsStringBuffer::FromString(nodeName), aNodeName.SetOwnedString(nodeName);
nodeName.Length());
} }
MOZ_MUST_USE nsresult GetBaseURI(nsAString& aBaseURI) const; MOZ_MUST_USE nsresult GetBaseURI(nsAString& aBaseURI) const;
// Return the base URI for the document. // Return the base URI for the document.
@ -1866,12 +1865,7 @@ public:
void GetLocalName(mozilla::dom::DOMString& aLocalName) const void GetLocalName(mozilla::dom::DOMString& aLocalName) const
{ {
const nsString& localName = LocalName(); const nsString& localName = LocalName();
if (localName.IsVoid()) { aLocalName.SetOwnedString(localName);
aLocalName.SetNull();
} else {
aLocalName.SetStringBuffer(nsStringBuffer::FromString(localName),
localName.Length());
}
} }
nsDOMAttributeMap* GetAttributes(); nsDOMAttributeMap* GetAttributes();

Просмотреть файл

@ -179,7 +179,14 @@ public:
MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?"); MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected); MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
if (aNullHandling == eNullNotExpected || aAtom) { if (aNullHandling == eNullNotExpected || aAtom) {
SetStringBuffer(aAtom->GetStringBuffer(), aAtom->GetLength()); if (aAtom->IsStaticAtom()) {
// XXX: bug 1407858 will replace this with a direct assignment of the
// static atom that doesn't go via nsString.
AsAString().AssignLiteral(aAtom->GetUTF16String(), aAtom->GetLength());
} else {
// Dynamic atoms always have a string buffer.
SetStringBuffer(aAtom->GetStringBuffer(), aAtom->GetLength());
}
} else if (aNullHandling == eTreatNullAsNull) { } else if (aNullHandling == eTreatNullAsNull) {
SetNull(); SetNull();
} }

Просмотреть файл

@ -65,6 +65,9 @@ public:
{ {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
MOZ_ASSERT(aLength <= mData.Length()); MOZ_ASSERT(aLength <= mData.Length());
// XXX: Bug 1408793 suggests encapsulating the following sequence within
// DOMString.
nsStringBuffer* buf = nsStringBuffer::FromString(mData); nsStringBuffer* buf = nsStringBuffer::FromString(mData);
if (buf) { if (buf) {
// We have to use SetEphemeralStringBuffer, because once we release our // We have to use SetEphemeralStringBuffer, because once we release our

Просмотреть файл

@ -83,8 +83,8 @@ nsHTMLTags::RegisterAtoms(void)
{ {
// let's verify that all names in the the table are lowercase... // let's verify that all names in the the table are lowercase...
for (int32_t i = 0; i < NS_HTML_TAG_MAX; ++i) { for (int32_t i = 0; i < NS_HTML_TAG_MAX; ++i) {
nsAutoString temp1((char16_t*)sTagAtoms_info[i].mStringBuffer->Data()); nsAutoString temp1((char16_t*)sTagAtoms_info[i].mString);
nsAutoString temp2((char16_t*)sTagAtoms_info[i].mStringBuffer->Data()); nsAutoString temp2((char16_t*)sTagAtoms_info[i].mString);
ToLowerCase(temp1); ToLowerCase(temp1);
NS_ASSERTION(temp1.Equals(temp2), "upper case char in table"); NS_ASSERTION(temp1.Equals(temp2), "upper case char in table");
} }
@ -93,7 +93,7 @@ nsHTMLTags::RegisterAtoms(void)
// correct. // correct.
for (int32_t i = 0; i < NS_HTML_TAG_MAX; ++i) { for (int32_t i = 0; i < NS_HTML_TAG_MAX; ++i) {
nsAutoString temp1(sTagUnicodeTable[i]); nsAutoString temp1(sTagUnicodeTable[i]);
nsAutoString temp2((char16_t*)sTagAtoms_info[i].mStringBuffer->Data()); nsAutoString temp2((char16_t*)sTagAtoms_info[i].mString);
NS_ASSERTION(temp1.Equals(temp2), "Bad unicode tag name!"); NS_ASSERTION(temp1.Equals(temp2), "Bad unicode tag name!");
} }

Просмотреть файл

@ -52,17 +52,14 @@ public:
uint32_t GetLength() const { return mLength; } uint32_t GetLength() const { return mLength; }
void ToString(nsAString& aBuf) const void ToString(nsAString& aString) const;
{
// See the comment on |mString|'s declaration.
nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
}
void ToUTF8String(nsACString& aString) const; void ToUTF8String(nsACString& aString) const;
// This is only valid for dynamic atoms.
nsStringBuffer* GetStringBuffer() const nsStringBuffer* GetStringBuffer() const
{ {
// See the comment on |mString|'s declaration. // See the comment on |mString|'s declaration.
MOZ_ASSERT(IsDynamicAtom());
return nsStringBuffer::FromData(mString); return nsStringBuffer::FromData(mString);
} }
@ -90,7 +87,7 @@ private:
// Construction and destruction is done entirely by |friend|s. // Construction and destruction is done entirely by |friend|s.
nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash); nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash);
nsAtom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash); nsAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash);
protected: protected:
~nsAtom(); ~nsAtom();
@ -99,9 +96,10 @@ private:
uint32_t mLength: 30; uint32_t mLength: 30;
uint32_t mKind: 2; // nsAtom::AtomKind uint32_t mKind: 2; // nsAtom::AtomKind
uint32_t mHash; uint32_t mHash;
// WARNING! There is an invisible constraint on |mString|: the chars it // WARNING! For static atoms, this is a pointer to a static char buffer. For
// points to must belong to an nsStringBuffer. This is so that the // non-static atoms it points to the chars in an nsStringBuffer. This means
// nsStringBuffer::FromData() calls above are valid. // that nsStringBuffer::FromData(mString) calls are only valid for non-static
// atoms.
char16_t* mString; char16_t* mString;
}; };

Просмотреть файл

@ -47,26 +47,6 @@ using namespace mozilla;
//---------------------------------------------------------------------- //----------------------------------------------------------------------
class CheckStaticAtomSizes
{
CheckStaticAtomSizes()
{
static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
sizeof(nsStringBuffer().mRefCount)) &&
(sizeof(nsFakeStringBuffer<1>().mSize) ==
sizeof(nsStringBuffer().mStorageSize)) &&
(offsetof(nsFakeStringBuffer<1>, mRefCnt) ==
offsetof(nsStringBuffer, mRefCount)) &&
(offsetof(nsFakeStringBuffer<1>, mSize) ==
offsetof(nsStringBuffer, mStorageSize)) &&
(offsetof(nsFakeStringBuffer<1>, mStringData) ==
sizeof(nsStringBuffer)),
"mocked-up strings' representations should be compatible");
}
};
//----------------------------------------------------------------------
enum class GCKind { enum class GCKind {
RegularOperation, RegularOperation,
Shutdown, Shutdown,
@ -102,39 +82,6 @@ public:
// See nsAtom::AddRef() and nsAtom::Release(). // See nsAtom::AddRef() and nsAtom::Release().
static Atomic<int32_t, ReleaseAcquire> gUnusedAtomCount(0); static Atomic<int32_t, ReleaseAcquire> gUnusedAtomCount(0);
#if defined(NS_BUILD_REFCNT_LOGGING)
// nsFakeStringBuffers don't really use the refcounting system, but we
// have to give a coherent series of addrefs and releases to the
// refcount logging system, or we'll hit assertions when running with
// XPCOM_MEM_LOG_CLASSES=nsStringBuffer.
class FakeBufferRefcountHelper
{
public:
explicit FakeBufferRefcountHelper(nsStringBuffer* aBuffer)
: mBuffer(aBuffer)
{
// Account for the initial static refcount of 1, so that we don't
// hit a refcount logging assertion when this object first appears
// with a refcount of 2.
NS_LOG_ADDREF(aBuffer, 1, "nsStringBuffer", sizeof(nsStringBuffer));
}
~FakeBufferRefcountHelper()
{
// We told the refcount logging system in the ctor that this
// object was created, so now we have to tell it that it was
// destroyed, to avoid leak reports. This may cause odd the
// refcount isn't actually 0.
NS_LOG_RELEASE(mBuffer, 0, "nsStringBuffer");
}
private:
nsStringBuffer* mBuffer;
};
UniquePtr<nsTArray<FakeBufferRefcountHelper>> gFakeBuffers;
#endif
// This constructor is for dynamic atoms and HTML5 atoms. // This constructor is for dynamic atoms and HTML5 atoms.
nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash) nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash)
: mRefCnt(1) : mRefCnt(1)
@ -171,30 +118,16 @@ nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash)
} }
// This constructor is for static atoms. // This constructor is for static atoms.
nsAtom::nsAtom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash) nsAtom::nsAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash)
: mLength(aLength) : mLength(aLength)
, mKind(static_cast<uint32_t>(AtomKind::StaticAtom)) , mKind(static_cast<uint32_t>(AtomKind::StaticAtom))
, mHash(aHash) , mHash(aHash)
, mString(static_cast<char16_t*>(aStringBuffer->Data())) , mString(const_cast<char16_t*>(aString))
{ {
#if defined(NS_BUILD_REFCNT_LOGGING)
MOZ_ASSERT(NS_IsMainThread());
if (!gFakeBuffers) {
gFakeBuffers = MakeUnique<nsTArray<FakeBufferRefcountHelper>>();
}
gFakeBuffers->AppendElement(aStringBuffer);
#endif
// Technically we could currently avoid doing this addref by instead making
// the static atom buffers have an initial refcount of 2.
aStringBuffer->AddRef();
MOZ_ASSERT(mHash == HashString(mString, mLength)); MOZ_ASSERT(mHash == HashString(mString, mLength));
MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated"); MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
MOZ_ASSERT(aStringBuffer && MOZ_ASSERT(NS_strlen(mString) == mLength, "correct storage");
aStringBuffer->StorageSize() == (mLength + 1) * sizeof(char16_t),
"correct storage");
} }
nsAtom::~nsAtom() nsAtom::~nsAtom()
@ -205,6 +138,20 @@ nsAtom::~nsAtom()
} }
} }
void
nsAtom::ToString(nsAString& aString) const
{
// See the comment on |mString|'s declaration.
if (IsStaticAtom()) {
// AssignLiteral() lets us assign without copying. This isn't a string
// literal, but it's a static atom and thus has an unbounded lifetime,
// which is what's important.
aString.AssignLiteral(mString, mLength);
} else {
nsStringBuffer::FromData(mString)->ToString(mLength, aString);
}
}
void void
nsAtom::ToUTF8String(nsACString& aBuf) const nsAtom::ToUTF8String(nsACString& aBuf) const
{ {
@ -560,10 +507,6 @@ NS_InitAtomTable()
void void
NS_ShutdownAtomTable() NS_ShutdownAtomTable()
{ {
#if defined(NS_BUILD_REFCNT_LOGGING)
gFakeBuffers = nullptr;
#endif
delete gStaticAtomTable; delete gStaticAtomTable;
gStaticAtomTable = nullptr; gStaticAtomTable = nullptr;
@ -632,17 +575,15 @@ nsAtomFriend::RegisterStaticAtoms(const nsStaticAtom* aAtoms,
} }
for (uint32_t i = 0; i < aAtomCount; ++i) { for (uint32_t i = 0; i < aAtomCount; ++i) {
nsStringBuffer* stringBuffer = aAtoms[i].mStringBuffer; const char16_t* string = aAtoms[i].mString;
nsAtom** atomp = aAtoms[i].mAtom; nsAtom** atomp = aAtoms[i].mAtom;
MOZ_ASSERT(nsCRT::IsAscii(static_cast<char16_t*>(stringBuffer->Data()))); MOZ_ASSERT(nsCRT::IsAscii(string));
uint32_t stringLen = stringBuffer->StorageSize() / sizeof(char16_t) - 1; uint32_t stringLen = NS_strlen(string);
uint32_t hash; uint32_t hash;
AtomTableEntry* he = AtomTableEntry* he = GetAtomHashEntry(string, stringLen, &hash);
GetAtomHashEntry(static_cast<char16_t*>(stringBuffer->Data()),
stringLen, &hash);
nsAtom* atom = he->mAtom; nsAtom* atom = he->mAtom;
if (atom) { if (atom) {
@ -657,7 +598,7 @@ nsAtomFriend::RegisterStaticAtoms(const nsStaticAtom* aAtoms,
"Static atom registration for %s should be pushed back", name.get()); "Static atom registration for %s should be pushed back", name.get());
} }
} else { } else {
atom = new nsAtom(stringBuffer, stringLen, hash); atom = new nsAtom(string, stringLen, hash);
he->mAtom = atom; he->mAtom = atom;
} }
*atomp = atom; *atomp = atom;

Просмотреть файл

@ -11,34 +11,24 @@
#include "nsStringBuffer.h" #include "nsStringBuffer.h"
#define NS_STATIC_ATOM(buffer_name, atom_ptr) \ #define NS_STATIC_ATOM(buffer_name, atom_ptr) \
{ (nsStringBuffer*) &buffer_name, atom_ptr } { buffer_name, atom_ptr }
// Note that |str_data| is an 8-bit string, and so |sizeof(str_data)| is equal
// to the number of chars (including the terminating '\0'). The |u""| prefix
// converts |str_data| to a 16-bit string, which is assigned.
#define NS_STATIC_ATOM_BUFFER(buffer_name, str_data) \ #define NS_STATIC_ATOM_BUFFER(buffer_name, str_data) \
static nsFakeStringBuffer<sizeof(str_data)> buffer_name = \ static const char16_t buffer_name[sizeof(str_data)] = u"" str_data; \
{ 1, sizeof(str_data) * sizeof(char16_t), (u"" str_data) }; static_assert(sizeof(str_data[0]) == 1, "non-8-bit static atom literal");
/** /**
* Holds data used to initialize large number of atoms during startup. Use * Holds data used to initialize large number of atoms during startup. Use
* the above macros to initialize these structs. They should never be accessed * the above macros to initialize these structs. They should never be accessed
* directly other than from AtomTable.cpp * directly other than from AtomTable.cpp.
*/ */
struct nsStaticAtom struct nsStaticAtom
{ {
// mStringBuffer points to the string buffer for a permanent atom, and is const char16_t* const mString;
// therefore safe as a non-owning reference. nsAtom** const mAtom;
nsStringBuffer* MOZ_NON_OWNING_REF mStringBuffer;
nsAtom** mAtom;
};
/**
* This is a struct with the same binary layout as a nsStringBuffer.
*/
template<uint32_t size>
struct nsFakeStringBuffer
{
int32_t mRefCnt;
uint32_t mSize;
char16_t mStringData[size];
}; };
// Register an array of static atoms with the atom table // Register an array of static atoms with the atom table