private void ScanString()

in AjaxMinDll/JavaScript/jsscanner.cs [2277:2606]


        private void ScanString(char delimiter)
        {
            int start = ++m_currentPosition;
            m_decodedString = null;
            m_literalIssues = false;
            StringBuilder result = null;
            try
            {
                char ch;
                while ((ch = GetChar(m_currentPosition++)) != delimiter)
                {
                    if (ch != '\\')
                    {
                        // this is the common non escape case
                        if (IsLineTerminator(ch, 0))
                        {
                            // TODO: we want to flag this string as unterminated *and having issues*,
                            // and then somehow output a line-break in the output to duplicate the
                            // source. However, we will need to figure out how to NOT combine the statement
                            // with the next statement. For instance:
                            //      var x = "unterminated
                            //      var y = 42;
                            // should NOT get combined to: var x="unterminated,y=42;
                            // (same for moving it inside for-statements, combining expression statements, etc.)
                            //m_literalIssues = true;
                            HandleError(JSError.UnterminatedString);

                            // back up to the start of the line terminator
                            --m_currentPosition;
                            if (GetChar(m_currentPosition - 1) == '\r')
                            {
                                --m_currentPosition;
                            }

                            break;
                        }

                        if ('\0' == ch)
                        {
                            // whether it's a null literal character within the string or an
                            // actual end of file, this string literal has issues....
                            m_literalIssues = true;

                            if (IsEndOfFile)
                            {
                                m_currentPosition--;
                                HandleError(JSError.UnterminatedString);
                                break;
                            }

                        }

                        if (AllowEmbeddedAspNetBlocks
                            && ch == '<'
                            && GetChar(m_currentPosition) == '%')
                        {
                            // start of an ASP.NET block INSIDE a string literal.
                            // just skip the entire ASP.NET block -- move forward until
                            // we find the closing %> delimiter, then we'll continue on
                            // with the next character.
                            SkipAspNetReplacement();

                            // asp.net blocks insides strings can cause issues
                            m_literalIssues = true;
                        }
                        else if (0xd800 <= ch && ch <= 0xdbff)
                        {
                            // high-surrogate! Make sure the next character is a low surrogate
                            // or we'll throw an error.
                            ch = GetChar(m_currentPosition);
                            if (0xdc00 <= ch && ch <= 0xdfff)
                            {
                                // we're good. Advance past the pair.
                                ++m_currentPosition;
                            }
                            else if (ch == '\\' && GetChar(m_currentPosition + 1) == 'u')
                            {
                                // we have a unicode escape. Start working on that escaped value.
                                if (null == result)
                                {
                                    result = StringBuilderPool.Acquire();
                                }

                                // start points to the first position that has not been written to the StringBuilder.
                                // The first time we get in here that position is the beginning of the string, after that
                                // is the character immediately following the escape sequence
                                if (m_currentPosition - start > 0)
                                {
                                    // append all the non escape chars to the string builder
                                    result.Append(m_strSourceCode, start, m_currentPosition - start);
                                }

                                int lowSurrogate;
                                if (ScanHexSequence(m_currentPosition += 2, 'u', out lowSurrogate))
                                {
                                    // valid escape, so make sure the unescaped value is added to the result regardless.
                                    result.Append((char)lowSurrogate);
                                    start = m_currentPosition;

                                    // now make sure it's in low-surrogate range
                                    if (lowSurrogate < 0xdc00 || 0xdfff < lowSurrogate)
                                    {
                                        // not a low-surrogate
                                        m_literalIssues = true;
                                        HandleError(JSError.HighSurrogate);
                                    }
                                }
                                else
                                {
                                    // not a valid unicode escape sequence, so no -- we are not 
                                    // followed by a low-surrogate
                                    m_literalIssues = true;
                                    HandleError(JSError.HighSurrogate);
                                }
                            }
                            else
                            {
                                // not followed by a low-surrogate
                                m_literalIssues = true;
                                HandleError(JSError.HighSurrogate);
                            }
                        }
                        else if (0xdc00 <= ch && ch <= 0xdfff)
                        {
                            // low-surrogate by itself! This is an error, but keep going
                            m_literalIssues = true;
                            HandleError(JSError.LowSurrogate);
                        }
                    }
                    else
                    {
                        // ESCAPE CASE
                        var esc = 0;

                        // got an escape of some sort. Have to use the StringBuilder
                        if (null == result)
                        {
                            result = StringBuilderPool.Acquire();
                        }

                        // start points to the first position that has not been written to the StringBuilder.
                        // The first time we get in here that position is the beginning of the string, after that
                        // is the character immediately following the escape sequence
                        if (m_currentPosition - start - 1 > 0)
                        {
                            // append all the non escape chars to the string builder
                            result.Append(m_strSourceCode, start, m_currentPosition - start - 1);
                        }

                        // state variable to be reset
                        bool seqOfThree = false;

                        ch = GetChar(m_currentPosition++);
                        switch (ch)
                        {
                            // line terminator crap
                            case '\r':
                                if ('\n' == GetChar(m_currentPosition))
                                {
                                    m_currentPosition++;
                                }

                                goto case '\n';

                            case '\n':
                            case '\u2028':
                            case '\u2029':
                                m_currentLine++;
                                m_startLinePosition = m_currentPosition;
                                break;

                            // classic single char escape sequences
                            case 'b':
                                result.Append((char)8);
                                break;

                            case 't':
                                result.Append((char)9);
                                break;

                            case 'n':
                                result.Append((char)10);
                                break;

                            case 'v':
                                // \v inside strings can cause issues
                                m_literalIssues = true;
                                result.Append((char)11);
                                break;

                            case 'f':
                                result.Append((char)12);
                                break;

                            case 'r':
                                result.Append((char)13);
                                break;

                            case '"':
                                result.Append('"');
                                break;

                            case '\'':
                                result.Append('\'');
                                break;

                            case '\\':
                                result.Append('\\');
                                break;

                            // hexadecimal escape sequence /xHH, \uHHHH, or \u{H+}
                            case 'u':
                            case 'x':
                                string unescaped;
                                if (ScanHexEscape(ch, out unescaped))
                                {
                                    // successfully escaped the character sequence
                                    result.Append(unescaped);
                                }
                                else
                                {
                                    // wasn't valid -- keep the original and flag this as having issues
                                    result.Append(m_strSourceCode.Substring(m_currentPosition - 2, 2));
                                    m_literalIssues = true;
                                    HandleError(JSError.BadHexEscapeSequence);
                                }
                                break;

                            case '0':
                            case '1':
                            case '2':
                            case '3':
                                seqOfThree = true;
                                esc = (ch - '0') << 6;
                                goto case '4';

                            case '4':
                            case '5':
                            case '6':
                            case '7':
                                // octal literals inside strings can cause issues
                                m_literalIssues = true;

                                // esc is reset at the beginning of the loop and it is used to check that we did not go through the cases 1, 2 or 3
                                if (!seqOfThree)
                                {
                                    esc = (ch - '0') << 3;
                                }

                                ch = GetChar(m_currentPosition++);
                                if ('0' <= ch && ch <= '7')
                                {
                                    if (seqOfThree)
                                    {
                                        esc |= (ch - '0') << 3;
                                        ch = GetChar(m_currentPosition++);
                                        if ('0' <= ch && ch <= '7')
                                        {
                                            esc |= ch - '0';
                                            result.Append((char)esc);
                                        }
                                        else
                                        {
                                            result.Append((char)(esc >> 3));

                                            // do not skip over this char we have to read it back
                                            --m_currentPosition;
                                        }
                                    }
                                    else
                                    {
                                        esc |= ch - '0';
                                        result.Append((char)esc);
                                    }
                                }
                                else
                                {
                                    if (seqOfThree)
                                    {
                                        result.Append((char)(esc >> 6));
                                    }
                                    else
                                    {
                                        result.Append((char)(esc >> 3));
                                    }

                                    // do not skip over this char we have to read it back
                                    --m_currentPosition;
                                }

                                HandleError(JSError.OctalLiteralsDeprecated);
                                break;

                            default:
                                // not an octal number, ignore the escape '/' and simply append the current char
                                result.Append(ch);
                                break;
                        }

                        start = m_currentPosition;
                    }
                }

                // update the unescaped string
                if (null != result)
                {
                    if (m_currentPosition - start - 1 > 0)
                    {
                        // append all the non escape chars to the string builder
                        result.Append(m_strSourceCode, start, m_currentPosition - start - 1);
                    }
                    m_decodedString = result.ToString();
                }
                else if (m_currentPosition == m_currentToken.StartPosition + 1)
                {
                    // empty unterminated string!
                    m_decodedString = string.Empty;
                }
                else
                {
                    // might be an unterminated string, so make sure that last character is the terminator
                    int numDelimiters = (GetChar(m_currentPosition - 1) == delimiter ? 2 : 1);
                    m_decodedString = m_strSourceCode.Substring(m_currentToken.StartPosition + 1, m_currentPosition - m_currentToken.StartPosition - numDelimiters);
                }
            }
            finally
            {
                result.Release();
            }
        }