Bug 637242, patch 2 of 3: Make nsRuleNode::Sweep nonrecursive to avoid stack exhaustion crashes. r=dbaron

This commit is contained in:
Mats Palmgren 2014-07-13 13:01:44 +00:00
Родитель 270cd1ba97
Коммит 6b3fa1ca1c
2 изменённых файлов: 95 добавлений и 42 удалений

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

@ -1331,8 +1331,7 @@ SetFactor(const nsCSSValue& aValue, float& aField, bool& aCanStoreInRuleTree,
NS_NOTREACHED("SetFactor: inappropriate unit");
}
// Overloaded new operator. Initializes the memory to 0 and relies on an arena
// (which comes from the presShell) to perform the allocation.
// Overloaded new operator that allocates from a presShell arena.
void*
nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
{
@ -1415,6 +1414,7 @@ nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent,
: mPresContext(aContext),
mParent(aParent),
mRule(aRule),
mNextSibling(nullptr),
mDependentBits((uint32_t(aLevel) << NS_RULE_NODE_LEVEL_SHIFT) |
(aIsImportant ? NS_RULE_NODE_IS_IMPORTANT : 0)),
mNoneBits(0),
@ -8920,18 +8920,8 @@ nsRuleNode::Mark()
node->mDependentBits |= NS_RULE_NODE_GC_MARK;
}
static PLDHashOperator
SweepRuleNodeChildren(PLDHashTable *table, PLDHashEntryHdr *hdr,
uint32_t number, void *arg)
{
ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>(hdr);
if (entry->mRuleNode->Sweep())
return PL_DHASH_REMOVE; // implies NEXT, unless |ed with STOP
return PL_DHASH_NEXT;
}
bool
nsRuleNode::Sweep()
nsRuleNode::DestroyIfNotMarked()
{
// If we're not marked, then we have to delete ourself.
// However, we never allow the root node to GC itself, because nsStyleSet
@ -8946,36 +8936,92 @@ nsRuleNode::Sweep()
// Clear our mark, for the next time around.
mDependentBits &= ~NS_RULE_NODE_GC_MARK;
return false;
}
// Call sweep on the children, since some may not be marked, and
// remove any deleted children from the child lists.
if (HaveChildren()) {
uint32_t childrenDestroyed;
if (ChildrenAreHashed()) {
PLDHashTable *children = ChildrenHash();
uint32_t oldChildCount = children->entryCount;
PL_DHashTableEnumerate(children, SweepRuleNodeChildren, nullptr);
childrenDestroyed = oldChildCount - children->entryCount;
} else {
childrenDestroyed = 0;
for (nsRuleNode **children = ChildrenListPtr(); *children; ) {
nsRuleNode *next = (*children)->mNextSibling;
if ((*children)->Sweep()) {
// This rule node was destroyed, so implicitly advance by
// making *children point to the next entry.
*children = next;
++childrenDestroyed;
} else {
// Advance.
children = &(*children)->mNextSibling;
}
PLDHashOperator
nsRuleNode::SweepHashEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
uint32_t number, void *arg)
{
ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>(hdr);
nsRuleNode* node = entry->mRuleNode;
if (node->DestroyIfNotMarked()) {
return PL_DHASH_REMOVE; // implies NEXT, unless |ed with STOP
}
if (node->HaveChildren()) {
// When children are hashed mNextSibling is not normally used but we use it
// here to build a list of children that needs to be swept.
nsRuleNode** headQ = static_cast<nsRuleNode**>(arg);
node->mNextSibling = *headQ;
*headQ = node;
}
return PL_DHASH_NEXT;
}
void
nsRuleNode::SweepChildren(nsTArray<nsRuleNode*>& aSweepQueue)
{
NS_ASSERTION(!(mDependentBits & NS_RULE_NODE_GC_MARK),
"missing DestroyIfNotMarked() call");
NS_ASSERTION(HaveChildren(),
"why call SweepChildren with no children?");
uint32_t childrenDestroyed = 0;
nsRuleNode* survivorsWithChildren = nullptr;
if (ChildrenAreHashed()) {
PLDHashTable* children = ChildrenHash();
uint32_t oldChildCount = children->entryCount;
PL_DHashTableEnumerate(children, SweepHashEntry, &survivorsWithChildren);
childrenDestroyed = oldChildCount - children->entryCount;
if (childrenDestroyed == oldChildCount) {
PL_DHashTableDestroy(children);
mChildren.asVoid = nullptr;
}
} else {
for (nsRuleNode** children = ChildrenListPtr(); *children; ) {
nsRuleNode* next = (*children)->mNextSibling;
if ((*children)->DestroyIfNotMarked()) {
// This rule node was destroyed, unlink it from the list by
// making *children point to the next entry.
*children = next;
++childrenDestroyed;
} else {
children = &(*children)->mNextSibling;
}
}
survivorsWithChildren = ChildrenList();
}
if (survivorsWithChildren) {
aSweepQueue.AppendElement(survivorsWithChildren);
}
NS_ASSERTION(childrenDestroyed <= mRefCnt, "wrong ref count");
mRefCnt -= childrenDestroyed;
NS_POSTCONDITION(IsRoot() || mRefCnt > 0,
"We didn't get swept, so we'd better have style contexts "
"pointing to us or to one of our descendants, which means "
"we'd better have a nonzero mRefCnt here!");
}
bool
nsRuleNode::Sweep()
{
NS_ASSERTION(IsRoot(), "must start sweeping at a root");
NS_ASSERTION(!mNextSibling, "root must not have mNextSibling");
if (DestroyIfNotMarked()) {
return true;
}
nsAutoTArray<nsRuleNode*, 70> sweepQueue;
sweepQueue.AppendElement(this);
while (!sweepQueue.IsEmpty()) {
nsTArray<nsRuleNode*>::index_type last = sweepQueue.Length() - 1;
nsRuleNode* ruleNode = sweepQueue[last];
sweepQueue.RemoveElementAt(last);
for (; ruleNode; ruleNode = ruleNode->mNextSibling) {
if (ruleNode->HaveChildren()) {
ruleNode->SweepChildren(sweepQueue);
}
}
mRefCnt -= childrenDestroyed;
NS_POSTCONDITION(IsRoot() || mRefCnt > 0,
"We didn't get swept, so we'd better have style contexts "
"pointing to us or to one of our descendants, which means "
"we'd better have a nonzero mRefCnt here!");
}
return false;
}

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

@ -306,6 +306,12 @@ private:
const PLDHashEntryHdr *aHdr,
const void *aKey);
static PLDHashOperator
SweepHashEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
uint32_t number, void *arg);
void SweepChildren(nsTArray<nsRuleNode*>& aSweepQueue);
bool DestroyIfNotMarked();
static const PLDHashTableOps ChildrenHashOps;
static PLDHashOperator
@ -401,8 +407,7 @@ private:
uint32_t mRefCnt;
public:
// Overloaded new operator. Initializes the memory to 0 and relies on an arena
// (which comes from the presShell) to perform the allocation.
// Overloaded new operator that allocates from a presShell arena.
void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW;
void Destroy() { DestroyInternal(nullptr); }
@ -713,6 +718,8 @@ public:
* ancestors until it reaches a marked one. Sweep recursively sweeps
* the children, destroys any that are unmarked, and clears marks,
* returning true if the node on which it was called was destroyed.
* If children are hashed, the mNextSibling field on the children is
* temporarily used internally by Sweep.
*/
void Mark();
bool Sweep();