Closed Bug 74709 Opened 23 years ago Closed 23 years ago

infinite loop: promise passed as base type confuses iterator

Categories

(Core :: XPCOM, defect, P1)

defect

Tracking

()

RESOLVED DUPLICATE of bug 70083
mozilla0.9.1

People

(Reporter: dbaron, Assigned: scc)

References

Details

Attachments

(1 file)

I think I might understand what's going on with the string infinite
loop I noticed on Menday.  (It may or may not be a new bug, since the code
may never have been tested until I tested it yesterday after fixing the
bustage caused by the removal of nsLiteralChar.)  This is my
understanding based on reading the code.  gdb isn't actually usable
enough for me to check this in the debugger.

But anyway, the problem can be seen if you uncomment (i.e., comment out
the |#ifdef DEBUG_dbaron|) the |#define CSS_REPORT_PARSE_ERRORS| at the
top of content/html/style/src/nsCSSScanner.h and then load the attached
page.  This should trigger the parser error in nsCSSParser.cpp, line 2354.
This is a call that is basically:

ReportUnexpectedToken(sc, tok,
                      NS_LITERAL_STRING("Expected ") +
                      nsLocalString(stopString, 1) +
                      NS_LITERAL_STRING(" but found"))

where the function is defined as:

static void ReportUnexpectedToken(nsCSSScanner *sc,
                                  nsCSSToken& tok,
                                  const nsAReadableString& err)
{
  nsAutoString error(err + NS_LITERAL_STRING(" '"));
  /* do other stuff */
}

The first line of this function is where I see the infinite loop.  I
think the problem is caused by calling |operator+(nsAString&,
nsAString&)| even though the first of the two arguments is really an
|nsPromiseConcatenation|.  This means the outermost
|nsPromiseConcatenation| passed to the constructor of |nsAutoString|
has its mFragmentIdentifierMask set to 1 rather than to (1<<2).  Then
|copy_string| is called from |nsAString::do_AppendFromReadable| which
is called from the constructor of |nsAutoString| (through about 3 other
functions).  However, once the nsReadableFragment<PRUnichar> indicating
the beginning of the nsReadingIterator<PRUnichar> is advanced to the
second fragment (which is the right fragment of the innermost
|nsPromiseConcatenation|), the outermost |nsPromiseConcatenation|
thinks that fragment is referring to its right fragment.  So when we
try to advance to the third fragment (via a call to
|nsReadingIterator<PRUnichar>::advance| that calls
|nsReadingIterator<PRUnichar>::normalize_forward| which calls
|nsPromiseConcatenation::GetReadableFragment|), the outermost promise
thinks there is no next fragment and returns null and leaves the
iterator's fragment pointing to the second fragment.  This means that
the iterator's |mPosition| is equal to |mFragment.mEnd| so
|nsReadingIterator<PRUnichar>::size_forward| returns 0, but the
iterator's mPosition is still not the the same as the |last| parameter
to |copy_string|, so |copy_string| goes into an infinite loop with the
assertion on line 77 of nsAlgorithm.h.

I can see ways to fix this (at the cost of an extra virtual function
call) in |operator+(nsAString&, nsAString&)| for the case when the
left operand to that function is an |nsPromiseConcatenation|.  I
can't see a way to fix it when the right operand is an
|nsPromiseConcatenation|.
I changed my mind again.  I think we could fix this by adding a |virtual
PRUInt32 GetFragmentIdentifierMask();| to |nsA[C]String|.  It would return 0 for
|nsA[C]String|, but |nsPromise[C]Concatenation| would override it and turn the
result of GetFragmentIdentifierMask, and |nsPromise[C]Substring| would override
it and forward the call to the string of which it's a substring.  In both forms
of |nsPromise[C]Concatenation::nsPromise[C]Concatenation| we would then have to
ensure that |mFragmentIdentifierMask| is set to |(tmp =
NS_MAX(mStrings[kLeftString]->GetFragmentIdentifierMask(),
mStrings[kRightString]->GetFragmentIdentifierMask()))?tmp<<1:1|.  The
|nsPromise[C]Concatenation| constructor and |operator+| that take an
|nsPromise[C]Concatenation| as their first argument would then not really be
needed, although they would save a virtual function call and a comparison to 0.
A danger of this is that one could exceed the limit of 32
|nsPromiseConcatenation|s by concatenating in different places (perhaps
recursively).
Blocks: 70090
Status: NEW → ASSIGNED
This can actually be fixed as part of the construction of an aggregate string,
i.e., in this case, the concatenation.  Concatenation dispatching to particular
fragments is costly in the case of multifragment strings already.  We need to
change this to calculating the number of fragments on each leaf at construction
time, and stepping down exactly the right branch.  This fix will automatically
fix this problem as well.  The mirror of this bug is that one can't concatenate
a multi-fragment string currently, and for the same reason.  Moving this bug
under the demesne of the other fragment work.
Blocks: 76334
No longer blocks: 70090
Severity: normal → major
OS: Linux → All
Priority: -- → P1
Hardware: PC → All
Target Milestone: --- → mozilla0.9.1
It sounds strange to be marking this bug as a duplicate of another, but here's
the story: correct iteration requires fixing iterators, fragments, the API for
|GetXXXFragment| and the routine currently named |Promises|.  Fixing iteration
will make this bug go away.  70083 is all about fixing iteration for just this
reason.

*** This bug has been marked as a duplicate of 70083 ***
Status: ASSIGNED → RESOLVED
Closed: 23 years ago
Resolution: --- → DUPLICATE
Component: String → XPCOM
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: