LCOV - code coverage report
Current view: top level - js/src/frontend - TokenStream.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 550 1090 50.5 %
Date: 2017-07-14 16:53:18 Functions: 40 70 57.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : // JS lexical scanner.
       8             : 
       9             : #include "frontend/TokenStream.h"
      10             : 
      11             : #include "mozilla/ArrayUtils.h"
      12             : #include "mozilla/IntegerTypeTraits.h"
      13             : #include "mozilla/PodOperations.h"
      14             : 
      15             : #include <ctype.h>
      16             : #include <stdarg.h>
      17             : #include <stdio.h>
      18             : #include <string.h>
      19             : 
      20             : #include "jsatom.h"
      21             : #include "jscntxt.h"
      22             : #include "jscompartment.h"
      23             : #include "jsexn.h"
      24             : #include "jsnum.h"
      25             : 
      26             : #include "frontend/BytecodeCompiler.h"
      27             : #include "frontend/ReservedWords.h"
      28             : #include "js/CharacterEncoding.h"
      29             : #include "js/UniquePtr.h"
      30             : #include "vm/HelperThreads.h"
      31             : #include "vm/StringBuffer.h"
      32             : #include "vm/Unicode.h"
      33             : 
      34             : using mozilla::ArrayLength;
      35             : using mozilla::Maybe;
      36             : using mozilla::PodAssign;
      37             : using mozilla::PodCopy;
      38             : using mozilla::PodZero;
      39             : 
      40             : struct ReservedWordInfo
      41             : {
      42             :     const char* chars;         // C string with reserved word text
      43             :     js::frontend::TokenKind tokentype;
      44             : };
      45             : 
      46             : static const ReservedWordInfo reservedWords[] = {
      47             : #define RESERVED_WORD_INFO(word, name, type) \
      48             :     {js_##word##_str, js::frontend::type},
      49             :     FOR_EACH_JAVASCRIPT_RESERVED_WORD(RESERVED_WORD_INFO)
      50             : #undef RESERVED_WORD_INFO
      51             : };
      52             : 
      53             : // Returns a ReservedWordInfo for the specified characters, or nullptr if the
      54             : // string is not a reserved word.
      55             : template <typename CharT>
      56             : static const ReservedWordInfo*
      57      319048 : FindReservedWord(const CharT* s, size_t length)
      58             : {
      59      319048 :     MOZ_ASSERT(length != 0);
      60             : 
      61             :     size_t i;
      62             :     const ReservedWordInfo* rw;
      63             :     const char* chars;
      64             : 
      65             : #define JSRW_LENGTH()           length
      66             : #define JSRW_AT(column)         s[column]
      67             : #define JSRW_GOT_MATCH(index)   i = (index); goto got_match;
      68             : #define JSRW_TEST_GUESS(index)  i = (index); goto test_guess;
      69             : #define JSRW_NO_MATCH()         goto no_match;
      70             : #include "frontend/ReservedWordsGenerated.h"
      71             : #undef JSRW_NO_MATCH
      72             : #undef JSRW_TEST_GUESS
      73             : #undef JSRW_GOT_MATCH
      74             : #undef JSRW_AT
      75             : #undef JSRW_LENGTH
      76             : 
      77             :   got_match:
      78       49847 :     return &reservedWords[i];
      79             : 
      80             :   test_guess:
      81       24218 :     rw = &reservedWords[i];
      82       24218 :     chars = rw->chars;
      83      100306 :     do {
      84      112871 :         if (*s++ != (unsigned char)(*chars++))
      85       12565 :             goto no_match;
      86             :     } while (--length != 0);
      87       11653 :     return rw;
      88             : 
      89             :   no_match:
      90      257548 :     return nullptr;
      91             : }
      92             : 
      93             : static const ReservedWordInfo*
      94      106021 : FindReservedWord(JSLinearString* str)
      95             : {
      96      212042 :     JS::AutoCheckCannotGC nogc;
      97      106021 :     return str->hasLatin1Chars()
      98      106021 :            ? FindReservedWord(str->latin1Chars(nogc), str->length())
      99      212042 :            : FindReservedWord(str->twoByteChars(nogc), str->length());
     100             : }
     101             : 
     102             : template <typename CharT>
     103             : static bool
     104         192 : IsIdentifier(const CharT* chars, size_t length)
     105             : {
     106             :     using namespace js;
     107             : 
     108         192 :     if (length == 0)
     109           0 :         return false;
     110             : 
     111         192 :     if (!unicode::IsIdentifierStart(char16_t(*chars)))
     112           1 :         return false;
     113             : 
     114         191 :     const CharT* end = chars + length;
     115        3233 :     while (++chars != end) {
     116        1521 :         if (!unicode::IsIdentifierPart(char16_t(*chars)))
     117           0 :             return false;
     118             :     }
     119             : 
     120         191 :     return true;
     121             : }
     122             : 
     123             : static uint32_t
     124           0 : GetSingleCodePoint(const char16_t** p, const char16_t* end)
     125             : {
     126             :     using namespace js;
     127             : 
     128             :     uint32_t codePoint;
     129           0 :     if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(**p)) && *p + 1 < end) {
     130           0 :         char16_t lead = **p;
     131           0 :         char16_t maybeTrail = *(*p + 1);
     132           0 :         if (unicode::IsTrailSurrogate(maybeTrail)) {
     133           0 :             *p += 2;
     134           0 :             return unicode::UTF16Decode(lead, maybeTrail);
     135             :         }
     136             :     }
     137             : 
     138           0 :     codePoint = **p;
     139           0 :     (*p)++;
     140           0 :     return codePoint;
     141             : }
     142             : 
     143             : static bool
     144           0 : IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length)
     145             : {
     146             :     using namespace js;
     147             : 
     148           0 :     if (IsIdentifier(chars, length))
     149           0 :         return true;
     150             : 
     151           0 :     if (length == 0)
     152           0 :         return false;
     153             : 
     154           0 :     const char16_t* p = chars;
     155           0 :     const char16_t* end = chars + length;
     156             :     uint32_t codePoint;
     157             : 
     158           0 :     codePoint = GetSingleCodePoint(&p, end);
     159           0 :     if (!unicode::IsIdentifierStart(codePoint))
     160           0 :         return false;
     161             : 
     162           0 :     while (p < end) {
     163           0 :         codePoint = GetSingleCodePoint(&p, end);
     164           0 :         if (!unicode::IsIdentifierPart(codePoint))
     165           0 :             return false;
     166             :     }
     167             : 
     168           0 :     return true;
     169             : }
     170             : 
     171             : namespace js {
     172             : 
     173             : namespace frontend {
     174             : 
     175             : bool
     176         181 : IsIdentifier(JSLinearString* str)
     177             : {
     178         362 :     JS::AutoCheckCannotGC nogc;
     179         181 :     return str->hasLatin1Chars()
     180         181 :            ? ::IsIdentifier(str->latin1Chars(nogc), str->length())
     181         362 :            : ::IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length());
     182             : }
     183             : 
     184             : bool
     185          11 : IsIdentifier(const char* chars, size_t length)
     186             : {
     187          11 :     return ::IsIdentifier(chars, length);
     188             : }
     189             : 
     190             : bool
     191           0 : IsIdentifier(const char16_t* chars, size_t length)
     192             : {
     193           0 :     return ::IsIdentifier(chars, length);
     194             : }
     195             : 
     196             : bool
     197           0 : IsKeyword(JSLinearString* str)
     198             : {
     199           0 :     if (const ReservedWordInfo* rw = FindReservedWord(str))
     200           0 :         return TokenKindIsKeyword(rw->tokentype);
     201             : 
     202           0 :     return false;
     203             : }
     204             : 
     205             : TokenKind
     206      106021 : ReservedWordTokenKind(PropertyName* str)
     207             : {
     208      106021 :     if (const ReservedWordInfo* rw = FindReservedWord(str))
     209         656 :         return rw->tokentype;
     210             : 
     211      105365 :     return TOK_NAME;
     212             : }
     213             : 
     214             : const char*
     215           0 : ReservedWordToCharZ(PropertyName* str)
     216             : {
     217           0 :     if (const ReservedWordInfo* rw = FindReservedWord(str))
     218           0 :         return ReservedWordToCharZ(rw->tokentype);
     219             : 
     220           0 :     return nullptr;
     221             : }
     222             : 
     223             : const char*
     224           0 : ReservedWordToCharZ(TokenKind tt)
     225             : {
     226           0 :     MOZ_ASSERT(tt != TOK_NAME);
     227           0 :     switch (tt) {
     228             : #define EMIT_CASE(word, name, type) case type: return js_##word##_str;
     229           0 :       FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
     230             : #undef EMIT_CASE
     231             :       default:
     232           0 :         MOZ_ASSERT_UNREACHABLE("Not a reserved word PropertyName.");
     233             :     }
     234             :     return nullptr;
     235             : }
     236             : 
     237             : PropertyName*
     238        2599 : TokenStreamAnyChars::reservedWordToPropertyName(TokenKind tt) const
     239             : {
     240        2599 :     MOZ_ASSERT(tt != TOK_NAME);
     241        2599 :     switch (tt) {
     242             : #define EMIT_CASE(word, name, type) case type: return cx->names().name;
     243           0 :       FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
     244             : #undef EMIT_CASE
     245             :       default:
     246           0 :         MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind.");
     247             :     }
     248             :     return nullptr;
     249             : }
     250             : 
     251        2449 : TokenStream::SourceCoords::SourceCoords(JSContext* cx, uint32_t ln)
     252        2449 :   : lineStartOffsets_(cx), initialLineNum_(ln), lastLineIndex_(0)
     253             : {
     254             :     // This is actually necessary!  Removing it causes compile errors on
     255             :     // GCC and clang.  You could try declaring this:
     256             :     //
     257             :     //   const uint32_t TokenStream::SourceCoords::MAX_PTR;
     258             :     //
     259             :     // which fixes the GCC/clang error, but causes bustage on Windows.  Sigh.
     260             :     //
     261        2449 :     uint32_t maxPtr = MAX_PTR;
     262             : 
     263             :     // The first line begins at buffer offset 0.  MAX_PTR is the sentinel.  The
     264             :     // appends cannot fail because |lineStartOffsets_| has statically-allocated
     265             :     // elements.
     266        2449 :     MOZ_ASSERT(lineStartOffsets_.capacity() >= 2);
     267        2449 :     MOZ_ALWAYS_TRUE(lineStartOffsets_.reserve(2));
     268        2449 :     lineStartOffsets_.infallibleAppend(0);
     269        2449 :     lineStartOffsets_.infallibleAppend(maxPtr);
     270        2449 : }
     271             : 
     272             : MOZ_ALWAYS_INLINE bool
     273      124078 : TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset)
     274             : {
     275      124078 :     uint32_t lineIndex = lineNumToIndex(lineNum);
     276      124078 :     uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
     277             : 
     278      124078 :     MOZ_ASSERT(lineStartOffsets_[0] == 0 && lineStartOffsets_[sentinelIndex] == MAX_PTR);
     279             : 
     280      124078 :     if (lineIndex == sentinelIndex) {
     281             :         // We haven't seen this newline before.  Update lineStartOffsets_
     282             :         // only if lineStartOffsets_.append succeeds, to keep sentinel.
     283             :         // Otherwise return false to tell TokenStream about OOM.
     284      116543 :         uint32_t maxPtr = MAX_PTR;
     285      116543 :         if (!lineStartOffsets_.append(maxPtr)) {
     286             :             static_assert(mozilla::IsSame<decltype(lineStartOffsets_.allocPolicy()),
     287             :                                           TempAllocPolicy&>::value,
     288             :                           "this function's caller depends on it reporting an "
     289             :                           "error on failure, as TempAllocPolicy ensures");
     290           0 :             return false;
     291             :         }
     292             : 
     293      116543 :         lineStartOffsets_[lineIndex] = lineStartOffset;
     294             :     } else {
     295             :         // We have seen this newline before (and ungot it).  Do nothing (other
     296             :         // than checking it hasn't mysteriously changed).
     297             :         // This path can be executed after hitting OOM, so check lineIndex.
     298        7535 :         MOZ_ASSERT_IF(lineIndex < sentinelIndex, lineStartOffsets_[lineIndex] == lineStartOffset);
     299             :     }
     300      124078 :     return true;
     301             : }
     302             : 
     303             : MOZ_ALWAYS_INLINE bool
     304        3102 : TokenStreamAnyChars::SourceCoords::fill(const TokenStreamAnyChars::SourceCoords& other)
     305             : {
     306        3102 :     MOZ_ASSERT(lineStartOffsets_.back() == MAX_PTR);
     307        3102 :     MOZ_ASSERT(other.lineStartOffsets_.back() == MAX_PTR);
     308             : 
     309        3102 :     if (lineStartOffsets_.length() >= other.lineStartOffsets_.length())
     310          50 :         return true;
     311             : 
     312        3052 :     uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
     313        3052 :     lineStartOffsets_[sentinelIndex] = other.lineStartOffsets_[sentinelIndex];
     314             : 
     315       37558 :     for (size_t i = sentinelIndex + 1; i < other.lineStartOffsets_.length(); i++) {
     316       34506 :         if (!lineStartOffsets_.append(other.lineStartOffsets_[i]))
     317           0 :             return false;
     318             :     }
     319        3052 :     return true;
     320             : }
     321             : 
     322             : MOZ_ALWAYS_INLINE uint32_t
     323      193517 : TokenStreamAnyChars::SourceCoords::lineIndexOf(uint32_t offset) const
     324             : {
     325             :     uint32_t iMin, iMax, iMid;
     326             : 
     327      193517 :     if (lineStartOffsets_[lastLineIndex_] <= offset) {
     328             :         // If we reach here, offset is on a line the same as or higher than
     329             :         // last time.  Check first for the +0, +1, +2 cases, because they
     330             :         // typically cover 85--98% of cases.
     331      183196 :         if (offset < lineStartOffsets_[lastLineIndex_ + 1])
     332      107484 :             return lastLineIndex_;      // lineIndex is same as last time
     333             : 
     334             :         // If we reach here, there must be at least one more entry (plus the
     335             :         // sentinel).  Try it.
     336       75712 :         lastLineIndex_++;
     337       75712 :         if (offset < lineStartOffsets_[lastLineIndex_ + 1])
     338       43028 :             return lastLineIndex_;      // lineIndex is one higher than last time
     339             : 
     340             :         // The same logic applies here.
     341       32684 :         lastLineIndex_++;
     342       32684 :         if (offset < lineStartOffsets_[lastLineIndex_ + 1]) {
     343        9545 :             return lastLineIndex_;      // lineIndex is two higher than last time
     344             :         }
     345             : 
     346             :         // No luck.  Oh well, we have a better-than-default starting point for
     347             :         // the binary search.
     348       23139 :         iMin = lastLineIndex_ + 1;
     349       23139 :         MOZ_ASSERT(iMin < lineStartOffsets_.length() - 1);   // -1 due to the sentinel
     350             : 
     351             :     } else {
     352       10321 :         iMin = 0;
     353             :     }
     354             : 
     355             :     // This is a binary search with deferred detection of equality, which was
     356             :     // marginally faster in this case than a standard binary search.
     357             :     // The -2 is because |lineStartOffsets_.length() - 1| is the sentinel, and we
     358             :     // want one before that.
     359       33460 :     iMax = lineStartOffsets_.length() - 2;
     360      508294 :     while (iMax > iMin) {
     361      237417 :         iMid = iMin + (iMax - iMin) / 2;
     362      237417 :         if (offset >= lineStartOffsets_[iMid + 1])
     363       88065 :             iMin = iMid + 1;    // offset is above lineStartOffsets_[iMid]
     364             :         else
     365      149352 :             iMax = iMid;        // offset is below or within lineStartOffsets_[iMid]
     366             :     }
     367       33460 :     MOZ_ASSERT(iMax == iMin);
     368       33460 :     MOZ_ASSERT(lineStartOffsets_[iMin] <= offset && offset < lineStartOffsets_[iMin + 1]);
     369       33460 :     lastLineIndex_ = iMin;
     370       33460 :     return iMin;
     371             : }
     372             : 
     373             : uint32_t
     374       94385 : TokenStreamAnyChars::SourceCoords::lineNum(uint32_t offset) const
     375             : {
     376       94385 :     uint32_t lineIndex = lineIndexOf(offset);
     377       94385 :     return lineIndexToNum(lineIndex);
     378             : }
     379             : 
     380             : uint32_t
     381       85525 : TokenStreamAnyChars::SourceCoords::columnIndex(uint32_t offset) const
     382             : {
     383       85525 :     uint32_t lineIndex = lineIndexOf(offset);
     384       85525 :     uint32_t lineStartOffset = lineStartOffsets_[lineIndex];
     385       85525 :     MOZ_ASSERT(offset >= lineStartOffset);
     386       85525 :     return offset - lineStartOffset;
     387             : }
     388             : 
     389             : void
     390       13607 : TokenStreamAnyChars::SourceCoords::lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum,
     391             :                                                          uint32_t* columnIndex) const
     392             : {
     393       13607 :     uint32_t lineIndex = lineIndexOf(offset);
     394       13607 :     *lineNum = lineIndexToNum(lineIndex);
     395       13607 :     uint32_t lineStartOffset = lineStartOffsets_[lineIndex];
     396       13607 :     MOZ_ASSERT(offset >= lineStartOffset);
     397       13607 :     *columnIndex = offset - lineStartOffset;
     398       13607 : }
     399             : 
     400             : #ifdef _MSC_VER
     401             : #pragma warning(push)
     402             : #pragma warning(disable:4351)
     403             : #endif
     404             : 
     405        2449 : TokenStreamAnyChars::TokenStreamAnyChars(JSContext* cx, const ReadOnlyCompileOptions& options,
     406        2449 :                                          StrictModeGetter* smg)
     407        2449 :   : srcCoords(cx, options.lineno),
     408             :     options_(options),
     409             :     tokens(),
     410             :     cursor(),
     411             :     lookahead(),
     412        2449 :     lineno(options.lineno),
     413             :     flags(),
     414             :     linebase(0),
     415             :     prevLinebase(size_t(-1)),
     416        2449 :     filename(options.filename()),
     417             :     displayURL_(nullptr),
     418             :     sourceMapURL_(nullptr),
     419             :     cx(cx),
     420        2449 :     mutedErrors(options.mutedErrors()),
     421       12245 :     strictModeGetter(smg)
     422             : {
     423        2449 : }
     424             : 
     425        2449 : TokenStream::TokenStream(JSContext* cx, const ReadOnlyCompileOptions& options,
     426        2449 :                          const CharT* base, size_t length, StrictModeGetter* smg)
     427             :   : TokenStreamAnyChars(cx, options, smg),
     428        2449 :     userbuf(cx, base, length, options.column),
     429        4898 :     tokenbuf(cx)
     430             : {
     431             :     // Nb: the following tables could be static, but initializing them here is
     432             :     // much easier.  Don't worry, the time to initialize them for each
     433             :     // TokenStream is trivial.  See bug 639420.
     434             : 
     435             :     // See Parser::assignExpr() for an explanation of isExprEnding[].
     436        2449 :     memset(isExprEnding, 0, sizeof(isExprEnding));
     437        2449 :     isExprEnding[TOK_COMMA] = 1;
     438        2449 :     isExprEnding[TOK_SEMI]  = 1;
     439        2449 :     isExprEnding[TOK_COLON] = 1;
     440        2449 :     isExprEnding[TOK_RP]    = 1;
     441        2449 :     isExprEnding[TOK_RB]    = 1;
     442        2449 :     isExprEnding[TOK_RC]    = 1;
     443        2449 : }
     444             : 
     445             : #ifdef _MSC_VER
     446             : #pragma warning(pop)
     447             : #endif
     448             : 
     449             : bool
     450        1902 : TokenStreamAnyChars::checkOptions()
     451             : {
     452             :     // Constrain starting columns to half of the range of a signed 32-bit value,
     453             :     // to avoid overflow.
     454        1902 :     if (options().column >= mozilla::MaxValue<int32_t>::value / 2 + 1) {
     455           0 :         reportErrorNoOffset(JSMSG_BAD_COLUMN_NUMBER);
     456           0 :         return false;
     457             :     }
     458             : 
     459        1902 :     return true;
     460             : }
     461             : 
     462             : // Use the fastest available getc.
     463             : #if defined(HAVE_GETC_UNLOCKED)
     464             : # define fast_getc getc_unlocked
     465             : #elif defined(HAVE__GETC_NOLOCK)
     466             : # define fast_getc _getc_nolock
     467             : #else
     468             : # define fast_getc getc
     469             : #endif
     470             : 
     471             : MOZ_MUST_USE MOZ_ALWAYS_INLINE bool
     472      124078 : TokenStream::updateLineInfoForEOL()
     473             : {
     474      124078 :     prevLinebase = linebase;
     475      124078 :     linebase = userbuf.offset();
     476      124078 :     lineno++;
     477      124078 :     return srcCoords.add(lineno, linebase);
     478             : }
     479             : 
     480             : MOZ_ALWAYS_INLINE void
     481      104629 : TokenStreamAnyChars::updateFlagsForEOL()
     482             : {
     483      104629 :     flags.isDirtyLine = false;
     484      104629 : }
     485             : 
     486             : // This gets the next char, normalizing all EOL sequences to '\n' as it goes.
     487             : bool
     488     1012579 : TokenStream::getChar(int32_t* cp)
     489             : {
     490     1012579 :     if (MOZ_UNLIKELY(!userbuf.hasRawChars())) {
     491           0 :         flags.isEOF = true;
     492           0 :         *cp = EOF;
     493           0 :         return true;
     494             :     }
     495             : 
     496     1012579 :     int32_t c = userbuf.getRawChar();
     497             : 
     498             :     do {
     499             :         // Normalize the char16_t if it was a newline.
     500     1012579 :         if (MOZ_UNLIKELY(c == '\n'))
     501       21368 :             break;
     502             : 
     503      991211 :         if (MOZ_UNLIKELY(c == '\r')) {
     504             :             // If it's a \r\n sequence: treat as a single EOL, skip over the \n.
     505           0 :             if (MOZ_LIKELY(userbuf.hasRawChars()))
     506           0 :                 userbuf.matchRawChar('\n');
     507             : 
     508           0 :             break;
     509             :         }
     510             : 
     511      991211 :         if (MOZ_UNLIKELY(c == unicode::LINE_SEPARATOR || c == unicode::PARA_SEPARATOR))
     512             :             break;
     513             : 
     514      991211 :         *cp = c;
     515      991211 :         return true;
     516             :     } while (false);
     517             : 
     518       21368 :     if (!updateLineInfoForEOL())
     519           0 :         return false;
     520             : 
     521       21368 :     *cp = '\n';
     522       21368 :     return true;
     523             : }
     524             : 
     525             : // This gets the next char. It does nothing special with EOL sequences, not
     526             : // even updating the line counters.  It can be used safely if (a) the
     527             : // resulting char is guaranteed to be ungotten (by ungetCharIgnoreEOL()) if
     528             : // it's an EOL, and (b) the line-related state (lineno, linebase) is not used
     529             : // before it's ungotten.
     530             : int32_t
     531     2112202 : TokenStream::getCharIgnoreEOL()
     532             : {
     533     2112202 :     if (MOZ_LIKELY(userbuf.hasRawChars()))
     534     2112095 :         return userbuf.getRawChar();
     535             : 
     536         107 :     flags.isEOF = true;
     537         107 :     return EOF;
     538             : }
     539             : 
     540             : void
     541       15555 : TokenStream::ungetChar(int32_t c)
     542             : {
     543       15555 :     if (c == EOF)
     544           0 :         return;
     545       15555 :     MOZ_ASSERT(!userbuf.atStart());
     546       15555 :     userbuf.ungetRawChar();
     547       15555 :     if (c == '\n') {
     548             : #ifdef DEBUG
     549        7478 :         int32_t c2 = userbuf.peekRawChar();
     550        7478 :         MOZ_ASSERT(TokenBuf::isRawEOLChar(c2));
     551             : #endif
     552             : 
     553             :         // If it's a \r\n sequence, also unget the \r.
     554        7478 :         if (!userbuf.atStart())
     555        7478 :             userbuf.matchRawCharBackwards('\r');
     556             : 
     557        7478 :         MOZ_ASSERT(prevLinebase != size_t(-1));    // we should never get more than one EOL char
     558        7478 :         linebase = prevLinebase;
     559        7478 :         prevLinebase = size_t(-1);
     560        7478 :         lineno--;
     561             :     } else {
     562        8077 :         MOZ_ASSERT(userbuf.peekRawChar() == c);
     563             :     }
     564             : }
     565             : 
     566             : void
     567      335614 : TokenStream::ungetCharIgnoreEOL(int32_t c)
     568             : {
     569      335614 :     if (c == EOF)
     570         107 :         return;
     571      335507 :     MOZ_ASSERT(!userbuf.atStart());
     572      335507 :     userbuf.ungetRawChar();
     573             : }
     574             : 
     575             : // Return true iff |n| raw characters can be read from this without reading past
     576             : // EOF or a newline, and copy those characters into |cp| if so.  The characters
     577             : // are not consumed: use skipChars(n) to do so after checking that the consumed
     578             : // characters had appropriate values.
     579             : bool
     580        5178 : TokenStream::peekChars(int n, char16_t* cp)
     581             : {
     582             :     int i, j;
     583             :     int32_t c;
     584             : 
     585       76775 :     for (i = 0; i < n; i++) {
     586       72313 :         c = getCharIgnoreEOL();
     587       72313 :         if (c == EOF)
     588           0 :             break;
     589       72313 :         if (c == '\n') {
     590         716 :             ungetCharIgnoreEOL(c);
     591         716 :             break;
     592             :         }
     593       71597 :         cp[i] = char16_t(c);
     594             :     }
     595       76775 :     for (j = i - 1; j >= 0; j--)
     596       71597 :         ungetCharIgnoreEOL(cp[j]);
     597        5178 :     return i == n;
     598             : }
     599             : 
     600             : size_t
     601           0 : TokenStream::TokenBuf::findEOLMax(size_t start, size_t max)
     602             : {
     603           0 :     const CharT* p = rawCharPtrAt(start);
     604             : 
     605           0 :     size_t n = 0;
     606             :     while (true) {
     607           0 :         if (p >= limit_)
     608           0 :             break;
     609           0 :         if (n >= max)
     610           0 :             break;
     611           0 :         n++;
     612           0 :         if (TokenBuf::isRawEOLChar(*p++))
     613           0 :             break;
     614             :     }
     615           0 :     return start + n;
     616             : }
     617             : 
     618             : bool
     619         260 : TokenStream::advance(size_t position)
     620             : {
     621         260 :     const CharT* end = userbuf.rawCharPtrAt(position);
     622       95936 :     while (userbuf.addressOfNextRawChar() < end) {
     623             :         int32_t c;
     624       47838 :         if (!getChar(&c))
     625           0 :             return false;
     626             :     }
     627             : 
     628         260 :     Token* cur = &tokens[cursor];
     629         260 :     cur->pos.begin = userbuf.offset();
     630             :     MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type));
     631         260 :     lookahead = 0;
     632         260 :     return true;
     633             : }
     634             : 
     635             : void
     636       81523 : TokenStream::tell(Position* pos)
     637             : {
     638       81523 :     pos->buf = userbuf.addressOfNextRawChar(/* allowPoisoned = */ true);
     639       81523 :     pos->flags = flags;
     640       81523 :     pos->lineno = lineno;
     641       81523 :     pos->linebase = linebase;
     642       81523 :     pos->prevLinebase = prevLinebase;
     643       81523 :     pos->lookahead = lookahead;
     644       81523 :     pos->currentToken = currentToken();
     645      202815 :     for (unsigned i = 0; i < lookahead; i++)
     646      121292 :         pos->lookaheadTokens[i] = tokens[(cursor + 1 + i) & ntokensMask];
     647       81523 : }
     648             : 
     649             : void
     650        4116 : TokenStream::seek(const Position& pos)
     651             : {
     652        4116 :     userbuf.setAddressOfNextRawChar(pos.buf, /* allowPoisoned = */ true);
     653        4116 :     flags = pos.flags;
     654        4116 :     lineno = pos.lineno;
     655        4116 :     linebase = pos.linebase;
     656        4116 :     prevLinebase = pos.prevLinebase;
     657        4116 :     lookahead = pos.lookahead;
     658             : 
     659        4116 :     tokens[cursor] = pos.currentToken;
     660        6587 :     for (unsigned i = 0; i < lookahead; i++)
     661        2471 :         tokens[(cursor + 1 + i) & ntokensMask] = pos.lookaheadTokens[i];
     662        4116 : }
     663             : 
     664             : bool
     665        3102 : TokenStream::seek(const Position& pos, const TokenStream& other)
     666             : {
     667        3102 :     if (!srcCoords.fill(other.srcCoords))
     668           0 :         return false;
     669        3102 :     seek(pos);
     670        3102 :     return true;
     671             : }
     672             : 
     673             : bool
     674           0 : TokenStream::reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
     675             :                                            bool strictMode, unsigned errorNumber, va_list args)
     676             : {
     677           0 :     if (!strictMode && !options().extraWarningsOption)
     678           0 :         return true;
     679             : 
     680           0 :     ErrorMetadata metadata;
     681           0 :     if (!computeErrorMetadata(&metadata, offset))
     682           0 :         return false;
     683             : 
     684           0 :     if (strictMode) {
     685           0 :         ReportCompileError(cx, Move(metadata), Move(notes), JSREPORT_ERROR, errorNumber, args);
     686           0 :         return false;
     687             :     }
     688             : 
     689           0 :     return compileWarning(Move(metadata), Move(notes), JSREPORT_WARNING | JSREPORT_STRICT,
     690           0 :                           errorNumber, args);
     691             : }
     692             : 
     693             : bool
     694           0 : TokenStreamAnyChars::compileWarning(ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
     695             :                                     unsigned flags, unsigned errorNumber, va_list args)
     696             : {
     697           0 :     if (options().werrorOption) {
     698           0 :         flags &= ~JSREPORT_WARNING;
     699           0 :         ReportCompileError(cx, Move(metadata), Move(notes), flags, errorNumber, args);
     700           0 :         return false;
     701             :     }
     702             : 
     703           0 :     return ReportCompileWarning(cx, Move(metadata), Move(notes), flags, errorNumber, args);
     704             : }
     705             : 
     706             : void
     707           0 : TokenStreamAnyChars::computeErrorMetadataNoOffset(ErrorMetadata* err)
     708             : {
     709           0 :     err->isMuted = mutedErrors;
     710           0 :     err->filename = filename;
     711           0 :     err->lineNumber = 0;
     712           0 :     err->columnNumber = 0;
     713             : 
     714           0 :     MOZ_ASSERT(err->lineOfContext == nullptr);
     715           0 : }
     716             : 
     717             : bool
     718           0 : TokenStreamAnyChars::fillExcludingContext(ErrorMetadata* err, uint32_t offset)
     719             : {
     720           0 :     err->isMuted = mutedErrors;
     721             : 
     722             :     // If this TokenStreamAnyChars doesn't have location information, try to
     723             :     // get it from the caller.
     724           0 :     if (!filename && !cx->helperThread()) {
     725           0 :         NonBuiltinFrameIter iter(cx,
     726             :                                  FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
     727           0 :                                  cx->compartment()->principals());
     728           0 :         if (!iter.done() && iter.filename()) {
     729           0 :             err->filename = iter.filename();
     730           0 :             err->lineNumber = iter.computeLine(&err->columnNumber);
     731           0 :             return false;
     732             :         }
     733             :     }
     734             : 
     735             :     // Otherwise use this TokenStreamAnyChars's location information.
     736           0 :     err->filename = filename;
     737           0 :     srcCoords.lineNumAndColumnIndex(offset, &err->lineNumber, &err->columnNumber);
     738           0 :     return true;
     739             : }
     740             : 
     741             : bool
     742           0 : TokenStream::computeErrorMetadata(ErrorMetadata* err, uint32_t offset)
     743             : {
     744           0 :     if (offset == NoOffset) {
     745           0 :         computeErrorMetadataNoOffset(err);
     746           0 :         return true;
     747             :     }
     748             : 
     749             :     // This function's return value isn't a success/failure indication: it
     750             :     // returns true if this TokenStream's location information could be used,
     751             :     // and it returns false when that information can't be used (and so we
     752             :     // can't provide a line of context).
     753           0 :     if (!fillExcludingContext(err, offset))
     754           0 :         return true;
     755             : 
     756             :     // Add a line of context from this TokenStream to help with debugging.
     757           0 :     return computeLineOfContext(err, offset);
     758             : }
     759             : 
     760             : bool
     761           0 : TokenStream::computeLineOfContext(ErrorMetadata* err, uint32_t offset)
     762             : {
     763             :     // This function presumes |err| is filled in *except* for line-of-context
     764             :     // fields.  It exists to make |TokenStream::computeErrorMetadata|, above,
     765             :     // more readable.
     766             : 
     767             :     // We only have line-start information for the current line.  If the error
     768             :     // is on a different line, we can't easily provide context.  (This means
     769             :     // any error in a multi-line token, e.g. an unterminated multiline string
     770             :     // literal, won't have context.)
     771           0 :     if (err->lineNumber != lineno)
     772           0 :         return true;
     773             : 
     774           0 :     constexpr size_t windowRadius = ErrorMetadata::lineOfContextRadius;
     775             : 
     776             :     // The window must start within the current line, no earlier than
     777             :     // |windowRadius| characters before |offset|.
     778           0 :     MOZ_ASSERT(offset >= linebase);
     779           0 :     size_t windowStart = (offset - linebase > windowRadius) ?
     780           0 :                          offset - windowRadius :
     781           0 :                          linebase;
     782             : 
     783             :     // The window must start within the portion of the current line that we
     784             :     // actually have in our buffer.
     785           0 :     if (windowStart < userbuf.startOffset())
     786           0 :         windowStart = userbuf.startOffset();
     787             : 
     788             :     // The window must end within the current line, no later than
     789             :     // windowRadius after offset.
     790           0 :     size_t windowEnd = userbuf.findEOLMax(offset, windowRadius);
     791           0 :     size_t windowLength = windowEnd - windowStart;
     792           0 :     MOZ_ASSERT(windowLength <= windowRadius * 2);
     793             : 
     794             :     // Create the windowed string, not including the potential line
     795             :     // terminator.
     796           0 :     StringBuffer windowBuf(cx);
     797           0 :     if (!windowBuf.append(userbuf.rawCharPtrAt(windowStart), windowLength) ||
     798           0 :         !windowBuf.append('\0'))
     799             :     {
     800           0 :         return false;
     801             :     }
     802             : 
     803           0 :     err->lineOfContext.reset(windowBuf.stealChars());
     804           0 :     if (!err->lineOfContext)
     805           0 :         return false;
     806             : 
     807           0 :     err->lineLength = windowLength;
     808           0 :     err->tokenOffset = offset - windowStart;
     809           0 :     return true;
     810             : }
     811             : 
     812             : bool
     813           0 : TokenStream::reportStrictModeError(unsigned errorNumber, ...)
     814             : {
     815             :     va_list args;
     816           0 :     va_start(args, errorNumber);
     817           0 :     bool result = reportStrictModeErrorNumberVA(nullptr, currentToken().pos.begin, strictMode(),
     818           0 :                                                 errorNumber, args);
     819           0 :     va_end(args);
     820           0 :     return result;
     821             : }
     822             : 
     823             : void
     824           0 : TokenStream::reportError(unsigned errorNumber, ...)
     825             : {
     826             :     va_list args;
     827           0 :     va_start(args, errorNumber);
     828             : 
     829           0 :     ErrorMetadata metadata;
     830           0 :     if (computeErrorMetadata(&metadata, currentToken().pos.begin))
     831           0 :         ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
     832             : 
     833           0 :     va_end(args);
     834           0 : }
     835             : 
     836             : void
     837           0 : TokenStreamAnyChars::reportErrorNoOffset(unsigned errorNumber, ...)
     838             : {
     839             :     va_list args;
     840           0 :     va_start(args, errorNumber);
     841             : 
     842           0 :     ErrorMetadata metadata;
     843           0 :     computeErrorMetadataNoOffset(&metadata);
     844             : 
     845           0 :     ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
     846             : 
     847           0 :     va_end(args);
     848           0 : }
     849             : 
     850             : bool
     851           0 : TokenStream::warning(unsigned errorNumber, ...)
     852             : {
     853             :     va_list args;
     854           0 :     va_start(args, errorNumber);
     855             : 
     856           0 :     ErrorMetadata metadata;
     857             :     bool result =
     858           0 :         computeErrorMetadata(&metadata, currentToken().pos.begin) &&
     859           0 :         compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
     860             : 
     861           0 :     va_end(args);
     862           0 :     return result;
     863             : }
     864             : 
     865             : bool
     866           0 : TokenStream::reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
     867             :                                              unsigned errorNumber, va_list args)
     868             : {
     869           0 :     if (!options().extraWarningsOption)
     870           0 :         return true;
     871             : 
     872           0 :     ErrorMetadata metadata;
     873           0 :     if (!computeErrorMetadata(&metadata, offset))
     874           0 :         return false;
     875             : 
     876           0 :     return compileWarning(Move(metadata), Move(notes), JSREPORT_STRICT | JSREPORT_WARNING,
     877           0 :                           errorNumber, args);
     878             : }
     879             : 
     880             : void
     881           0 : TokenStream::error(unsigned errorNumber, ...)
     882             : {
     883             :     va_list args;
     884           0 :     va_start(args, errorNumber);
     885             : 
     886           0 :     ErrorMetadata metadata;
     887           0 :     if (computeErrorMetadata(&metadata, currentToken().pos.begin))
     888           0 :         ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
     889             : 
     890           0 :     va_end(args);
     891           0 : }
     892             : 
     893             : void
     894           0 : TokenStream::errorAt(uint32_t offset, unsigned errorNumber, ...)
     895             : {
     896             :     va_list args;
     897           0 :     va_start(args, errorNumber);
     898             : 
     899           0 :     ErrorMetadata metadata;
     900           0 :     if (computeErrorMetadata(&metadata, offset))
     901           0 :         ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
     902             : 
     903           0 :     va_end(args);
     904           0 : }
     905             : 
     906             : // We have encountered a '\': check for a Unicode escape sequence after it.
     907             : // Return the length of the escape sequence and the character code point (by
     908             : // value) if we found a Unicode escape sequence.  Otherwise, return 0.  In both
     909             : // cases, do not advance along the buffer.
     910             : uint32_t
     911           0 : TokenStream::peekUnicodeEscape(uint32_t* codePoint)
     912             : {
     913           0 :     int32_t c = getCharIgnoreEOL();
     914           0 :     if (c != 'u') {
     915           0 :         ungetCharIgnoreEOL(c);
     916           0 :         return 0;
     917             :     }
     918             : 
     919             :     CharT cp[3];
     920             :     uint32_t length;
     921           0 :     c = getCharIgnoreEOL();
     922           0 :     if (JS7_ISHEX(c) && peekChars(3, cp) &&
     923           0 :         JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]))
     924             :     {
     925           0 :         *codePoint = (JS7_UNHEX(c) << 12) |
     926           0 :                      (JS7_UNHEX(cp[0]) << 8) |
     927           0 :                      (JS7_UNHEX(cp[1]) << 4) |
     928           0 :                      JS7_UNHEX(cp[2]);
     929           0 :         length = 5;
     930           0 :     } else if (c == '{') {
     931           0 :         length = peekExtendedUnicodeEscape(codePoint);
     932             :     } else {
     933           0 :         length = 0;
     934             :     }
     935             : 
     936           0 :     ungetCharIgnoreEOL(c);
     937           0 :     ungetCharIgnoreEOL('u');
     938           0 :     return length;
     939             : }
     940             : 
     941             : uint32_t
     942           0 : TokenStream::peekExtendedUnicodeEscape(uint32_t* codePoint)
     943             : {
     944             :     // The opening brace character was already read.
     945           0 :     int32_t c = getCharIgnoreEOL();
     946             : 
     947             :     // Skip leading zeros.
     948           0 :     uint32_t leadingZeros = 0;
     949           0 :     while (c == '0') {
     950           0 :         leadingZeros++;
     951           0 :         c = getCharIgnoreEOL();
     952             :     }
     953             : 
     954             :     CharT cp[6];
     955           0 :     size_t i = 0;
     956           0 :     uint32_t code = 0;
     957           0 :     while (JS7_ISHEX(c) && i < 6) {
     958           0 :         cp[i++] = c;
     959           0 :         code = code << 4 | JS7_UNHEX(c);
     960           0 :         c = getCharIgnoreEOL();
     961             :     }
     962             : 
     963             :     uint32_t length;
     964           0 :     if (c == '}' && (leadingZeros > 0 || i > 0) && code <= unicode::NonBMPMax) {
     965           0 :         *codePoint = code;
     966           0 :         length = leadingZeros + i + 3;
     967             :     } else {
     968           0 :         length = 0;
     969             :     }
     970             : 
     971           0 :     ungetCharIgnoreEOL(c);
     972           0 :     while (i--)
     973           0 :         ungetCharIgnoreEOL(cp[i]);
     974           0 :     while (leadingZeros--)
     975           0 :         ungetCharIgnoreEOL('0');
     976             : 
     977           0 :     return length;
     978             : }
     979             : 
     980             : uint32_t
     981           0 : TokenStream::matchUnicodeEscapeIdStart(uint32_t* codePoint)
     982             : {
     983           0 :     uint32_t length = peekUnicodeEscape(codePoint);
     984           0 :     if (length > 0 && unicode::IsIdentifierStart(*codePoint)) {
     985           0 :         skipChars(length);
     986           0 :         return length;
     987             :     }
     988           0 :     return 0;
     989             : }
     990             : 
     991             : bool
     992           0 : TokenStream::matchUnicodeEscapeIdent(uint32_t* codePoint)
     993             : {
     994           0 :     uint32_t length = peekUnicodeEscape(codePoint);
     995           0 :     if (length > 0 && unicode::IsIdentifierPart(*codePoint)) {
     996           0 :         skipChars(length);
     997           0 :         return true;
     998             :     }
     999           0 :     return false;
    1000             : }
    1001             : 
    1002             : // Helper function which returns true if the first length(q) characters in p are
    1003             : // the same as the characters in q.
    1004             : template<typename CharT>
    1005             : static bool
    1006        4598 : CharsMatch(const CharT* p, const char* q)
    1007             : {
    1008        4736 :     while (*q) {
    1009        4596 :         if (*p++ != *q++)
    1010        4458 :             return false;
    1011             :     }
    1012             : 
    1013           2 :     return true;
    1014             : }
    1015             : 
    1016             : bool
    1017        2588 : TokenStream::getDirectives(bool isMultiline, bool shouldWarnDeprecated)
    1018             : {
    1019             :     // Match directive comments used in debugging, such as "//# sourceURL" and
    1020             :     // "//# sourceMappingURL". Use of "//@" instead of "//#" is deprecated.
    1021             :     //
    1022             :     // To avoid a crashing bug in IE, several JavaScript transpilers wrap single
    1023             :     // line comments containing a source mapping URL inside a multiline
    1024             :     // comment. To avoid potentially expensive lookahead and backtracking, we
    1025             :     // only check for this case if we encounter a '#' character.
    1026             : 
    1027        2588 :     if (!getDisplayURL(isMultiline, shouldWarnDeprecated))
    1028           0 :         return false;
    1029        2588 :     if (!getSourceMappingURL(isMultiline, shouldWarnDeprecated))
    1030           0 :         return false;
    1031             : 
    1032        2588 :     return true;
    1033             : }
    1034             : 
    1035             : bool
    1036        5176 : TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated,
    1037             :                           const char* directive, uint8_t directiveLength,
    1038             :                           const char* errorMsgPragma,
    1039             :                           UniqueTwoByteChars* destination)
    1040             : {
    1041        5176 :     MOZ_ASSERT(directiveLength <= 18);
    1042             :     char16_t peeked[18];
    1043             : 
    1044        5176 :     if (peekChars(directiveLength, peeked) && CharsMatch(peeked, directive)) {
    1045           2 :         if (shouldWarnDeprecated) {
    1046           0 :             if (!warning(JSMSG_DEPRECATED_PRAGMA, errorMsgPragma))
    1047           0 :                 return false;
    1048             :         }
    1049             : 
    1050           2 :         skipChars(directiveLength);
    1051           2 :         tokenbuf.clear();
    1052             : 
    1053             :         do {
    1054             :             int32_t c;
    1055         107 :             if (!peekChar(&c))
    1056           0 :                 return false;
    1057             : 
    1058         107 :             if (c == EOF || unicode::IsSpaceOrBOM2(c))
    1059           4 :                 break;
    1060             : 
    1061         105 :             consumeKnownChar(c);
    1062             : 
    1063             :             // Debugging directives can occur in both single- and multi-line
    1064             :             // comments. If we're currently inside a multi-line comment, we also
    1065             :             // need to recognize multi-line comment terminators.
    1066         105 :             if (isMultiline && c == '*') {
    1067             :                 int32_t c2;
    1068           0 :                 if (!peekChar(&c2))
    1069           0 :                     return false;
    1070             : 
    1071           0 :                 if (c2 == '/') {
    1072           0 :                     ungetChar('*');
    1073           0 :                     break;
    1074             :                 }
    1075             :             }
    1076             : 
    1077         105 :             if (!tokenbuf.append(c))
    1078           0 :                 return false;
    1079             :         } while (true);
    1080             : 
    1081           2 :         if (tokenbuf.empty()) {
    1082             :             // The directive's URL was missing, but this is not quite an
    1083             :             // exception that we should stop and drop everything for.
    1084           0 :             return true;
    1085             :         }
    1086             : 
    1087           2 :         size_t length = tokenbuf.length();
    1088             : 
    1089           2 :         *destination = cx->make_pod_array<char16_t>(length + 1);
    1090           2 :         if (!*destination)
    1091           0 :             return false;
    1092             : 
    1093           2 :         PodCopy(destination->get(), tokenbuf.begin(), length);
    1094           2 :         (*destination)[length] = '\0';
    1095             :     }
    1096             : 
    1097        5176 :     return true;
    1098             : }
    1099             : 
    1100             : bool
    1101        2588 : TokenStream::getDisplayURL(bool isMultiline, bool shouldWarnDeprecated)
    1102             : {
    1103             :     // Match comments of the form "//# sourceURL=<url>" or
    1104             :     // "/\* //# sourceURL=<url> *\/"
    1105             :     //
    1106             :     // Note that while these are labeled "sourceURL" in the source text,
    1107             :     // internally we refer to it as a "displayURL" to distinguish what the
    1108             :     // developer would like to refer to the source as from the source's actual
    1109             :     // URL.
    1110             : 
    1111             :     static const char sourceURLDirective[] = " sourceURL=";
    1112        2588 :     constexpr uint8_t sourceURLDirectiveLength = ArrayLength(sourceURLDirective) - 1;
    1113        2588 :     return getDirective(isMultiline, shouldWarnDeprecated,
    1114             :                         sourceURLDirective, sourceURLDirectiveLength,
    1115        2588 :                         "sourceURL", &displayURL_);
    1116             : }
    1117             : 
    1118             : bool
    1119        2588 : TokenStream::getSourceMappingURL(bool isMultiline, bool shouldWarnDeprecated)
    1120             : {
    1121             :     // Match comments of the form "//# sourceMappingURL=<url>" or
    1122             :     // "/\* //# sourceMappingURL=<url> *\/"
    1123             : 
    1124             :     static const char sourceMappingURLDirective[] = " sourceMappingURL=";
    1125        2588 :     constexpr uint8_t sourceMappingURLDirectiveLength = ArrayLength(sourceMappingURLDirective) - 1;
    1126        2588 :     return getDirective(isMultiline, shouldWarnDeprecated,
    1127             :                         sourceMappingURLDirective, sourceMappingURLDirectiveLength,
    1128        2588 :                         "sourceMappingURL", &sourceMapURL_);
    1129             : }
    1130             : 
    1131             : MOZ_ALWAYS_INLINE Token*
    1132      565076 : TokenStream::newToken(ptrdiff_t adjust)
    1133             : {
    1134      565076 :     cursor = (cursor + 1) & ntokensMask;
    1135      565076 :     Token* tp = &tokens[cursor];
    1136      565076 :     tp->pos.begin = userbuf.offset() + adjust;
    1137             : 
    1138             :     // NOTE: tp->pos.end is not set until the very end of getTokenInternal().
    1139             :     MOZ_MAKE_MEM_UNDEFINED(&tp->pos.end, sizeof(tp->pos.end));
    1140             : 
    1141      565076 :     return tp;
    1142             : }
    1143             : 
    1144             : MOZ_ALWAYS_INLINE JSAtom*
    1145       21832 : TokenStream::atomize(JSContext* cx, CharBuffer& cb)
    1146             : {
    1147       21832 :     return AtomizeChars(cx, cb.begin(), cb.length());
    1148             : }
    1149             : 
    1150             : #ifdef DEBUG
    1151             : static bool
    1152      555417 : IsTokenSane(Token* tp)
    1153             : {
    1154             :     // Nb: TOK_EOL should never be used in an actual Token;  it should only be
    1155             :     // returned as a TokenKind from peekTokenSameLine().
    1156      555417 :     if (tp->type < 0 || tp->type >= TOK_LIMIT || tp->type == TOK_EOL)
    1157           0 :         return false;
    1158             : 
    1159      555417 :     if (tp->pos.end < tp->pos.begin)
    1160           0 :         return false;
    1161             : 
    1162      555417 :     return true;
    1163             : }
    1164             : #endif
    1165             : 
    1166             : bool
    1167           0 : TokenStream::matchTrailForLeadSurrogate(char16_t lead, char16_t* trail, uint32_t* codePoint)
    1168             : {
    1169           0 :     int32_t maybeTrail = getCharIgnoreEOL();
    1170           0 :     if (!unicode::IsTrailSurrogate(maybeTrail)) {
    1171           0 :         ungetCharIgnoreEOL(maybeTrail);
    1172           0 :         return false;
    1173             :     }
    1174             : 
    1175           0 :     if (trail)
    1176           0 :         *trail = maybeTrail;
    1177           0 :     *codePoint = unicode::UTF16Decode(lead, maybeTrail);
    1178           0 :     return true;
    1179             : }
    1180             : 
    1181             : bool
    1182           0 : TokenStream::putIdentInTokenbuf(const char16_t* identStart)
    1183             : {
    1184             :     int32_t c;
    1185             :     uint32_t qc;
    1186           0 :     const CharT* tmp = userbuf.addressOfNextRawChar();
    1187           0 :     userbuf.setAddressOfNextRawChar(identStart);
    1188             : 
    1189           0 :     tokenbuf.clear();
    1190             :     for (;;) {
    1191           0 :         c = getCharIgnoreEOL();
    1192             : 
    1193           0 :         if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) {
    1194             :             char16_t trail;
    1195             :             uint32_t codePoint;
    1196           0 :             if (matchTrailForLeadSurrogate(c, &trail, &codePoint)) {
    1197           0 :                 if (!unicode::IsIdentifierPart(codePoint))
    1198           0 :                     break;
    1199             : 
    1200           0 :                 if (!tokenbuf.append(c) || !tokenbuf.append(trail)) {
    1201           0 :                     userbuf.setAddressOfNextRawChar(tmp);
    1202           0 :                     return false;
    1203             :                 }
    1204           0 :                 continue;
    1205             :             }
    1206             :         }
    1207             : 
    1208           0 :         if (!unicode::IsIdentifierPart(char16_t(c))) {
    1209           0 :             if (c != '\\' || !matchUnicodeEscapeIdent(&qc))
    1210           0 :                 break;
    1211             : 
    1212           0 :             if (MOZ_UNLIKELY(unicode::IsSupplementary(qc))) {
    1213             :                 char16_t lead, trail;
    1214           0 :                 unicode::UTF16Encode(qc, &lead, &trail);
    1215           0 :                 if (!tokenbuf.append(lead) || !tokenbuf.append(trail)) {
    1216           0 :                     userbuf.setAddressOfNextRawChar(tmp);
    1217           0 :                     return false;
    1218             :                 }
    1219           0 :                 continue;
    1220             :             }
    1221             : 
    1222           0 :             c = qc;
    1223             :         }
    1224             : 
    1225           0 :         if (!tokenbuf.append(c)) {
    1226           0 :             userbuf.setAddressOfNextRawChar(tmp);
    1227           0 :             return false;
    1228             :         }
    1229           0 :     }
    1230           0 :     userbuf.setAddressOfNextRawChar(tmp);
    1231           0 :     return true;
    1232             : }
    1233             : 
    1234             : enum FirstCharKind {
    1235             :     // A char16_t has the 'OneChar' kind if it, by itself, constitutes a valid
    1236             :     // token that cannot also be a prefix of a longer token.  E.g. ';' has the
    1237             :     // OneChar kind, but '+' does not, because '++' and '+=' are valid longer tokens
    1238             :     // that begin with '+'.
    1239             :     //
    1240             :     // The few token kinds satisfying these properties cover roughly 35--45%
    1241             :     // of the tokens seen in practice.
    1242             :     //
    1243             :     // We represent the 'OneChar' kind with any positive value less than
    1244             :     // TOK_LIMIT.  This representation lets us associate each one-char token
    1245             :     // char16_t with a TokenKind and thus avoid a subsequent char16_t-to-TokenKind
    1246             :     // conversion.
    1247             :     OneChar_Min = 0,
    1248             :     OneChar_Max = TOK_LIMIT - 1,
    1249             : 
    1250             :     Space = TOK_LIMIT,
    1251             :     Ident,
    1252             :     Dec,
    1253             :     String,
    1254             :     EOL,
    1255             :     BasePrefix,
    1256             :     Other,
    1257             : 
    1258             :     LastCharKind = Other
    1259             : };
    1260             : 
    1261             : // OneChar: 40,  41,  44,  58,  59,  63,  91,  93,  123, 125, 126:
    1262             : //          '(', ')', ',', ':', ';', '?', '[', ']', '{', '}', '~'
    1263             : // Ident:   36, 65..90, 95, 97..122: '$', 'A'..'Z', '_', 'a'..'z'
    1264             : // Dot:     46: '.'
    1265             : // Equals:  61: '='
    1266             : // String:  34, 39: '"', '\''
    1267             : // Dec:     49..57: '1'..'9'
    1268             : // Plus:    43: '+'
    1269             : // BasePrefix:  48: '0'
    1270             : // Space:   9, 11, 12, 32: '\t', '\v', '\f', ' '
    1271             : // EOL:     10, 13: '\n', '\r'
    1272             : //
    1273             : #define T_COMMA     TOK_COMMA
    1274             : #define T_COLON     TOK_COLON
    1275             : #define T_BITNOT    TOK_BITNOT
    1276             : #define Templat     String
    1277             : #define _______     Other
    1278             : static const uint8_t firstCharKinds[] = {
    1279             : /*         0        1        2        3        4        5        6        7        8        9    */
    1280             : /*   0+ */ _______, _______, _______, _______, _______, _______, _______, _______, _______,   Space,
    1281             : /*  10+ */     EOL,   Space,   Space,     EOL, _______, _______, _______, _______, _______, _______,
    1282             : /*  20+ */ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
    1283             : /*  30+ */ _______, _______,   Space, _______,  String, _______,   Ident, _______, _______,  String,
    1284             : /*  40+ */  TOK_LP,  TOK_RP, _______, _______, T_COMMA,_______,  _______, _______,BasePrefix,  Dec,
    1285             : /*  50+ */     Dec,     Dec,     Dec,     Dec,     Dec,     Dec,     Dec,    Dec,  T_COLON,TOK_SEMI,
    1286             : /*  60+ */ _______, _______, _______,TOK_HOOK, _______,   Ident,   Ident,   Ident,   Ident,   Ident,
    1287             : /*  70+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
    1288             : /*  80+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
    1289             : /*  90+ */   Ident,  TOK_LB, _______,  TOK_RB, _______,   Ident, Templat,   Ident,   Ident,   Ident,
    1290             : /* 100+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
    1291             : /* 110+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
    1292             : /* 120+ */   Ident,   Ident,   Ident,  TOK_LC, _______,  TOK_RC,T_BITNOT, _______
    1293             : };
    1294             : #undef T_COMMA
    1295             : #undef T_COLON
    1296             : #undef T_BITNOT
    1297             : #undef Templat
    1298             : #undef _______
    1299             : 
    1300             : static_assert(LastCharKind < (1 << (sizeof(firstCharKinds[0]) * 8)),
    1301             :               "Elements of firstCharKinds[] are too small");
    1302             : 
    1303             : bool
    1304      555417 : TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
    1305             : {
    1306             :     int c;
    1307             :     uint32_t qc;
    1308             :     Token* tp;
    1309             :     FirstCharKind c1kind;
    1310             :     const CharT* numStart;
    1311             :     bool hasExp;
    1312             :     DecimalPoint decimalPoint;
    1313             :     const CharT* identStart;
    1314             :     bool hadUnicodeEscape;
    1315             : 
    1316             :     // Check if in the middle of a template string. Have to get this out of
    1317             :     // the way first.
    1318      555417 :     if (MOZ_UNLIKELY(modifier == TemplateTail)) {
    1319         665 :         if (!getStringOrTemplateToken('`', &tp))
    1320           0 :             goto error;
    1321         665 :         goto out;
    1322             :     }
    1323             : 
    1324             :   retry:
    1325     1377600 :     if (MOZ_UNLIKELY(!userbuf.hasRawChars())) {
    1326         616 :         tp = newToken(0);
    1327         616 :         tp->type = TOK_EOF;
    1328         616 :         flags.isEOF = true;
    1329         616 :         goto out;
    1330             :     }
    1331             : 
    1332     1376984 :     c = userbuf.getRawChar();
    1333     1376984 :     MOZ_ASSERT(c != EOF);
    1334             : 
    1335             :     // Chars not in the range 0..127 are rare.  Getting them out of the way
    1336             :     // early allows subsequent checking to be faster.
    1337     1376984 :     if (MOZ_UNLIKELY(c >= 128)) {
    1338           0 :         if (unicode::IsSpaceOrBOM2(c)) {
    1339           0 :             if (c == unicode::LINE_SEPARATOR || c == unicode::PARA_SEPARATOR) {
    1340           0 :                 if (!updateLineInfoForEOL())
    1341           0 :                     goto error;
    1342             : 
    1343           0 :                 updateFlagsForEOL();
    1344             :             }
    1345             : 
    1346           0 :             goto retry;
    1347             :         }
    1348             : 
    1349           0 :         tp = newToken(-1);
    1350             : 
    1351             :         static_assert('$' < 128,
    1352             :                       "IdentifierStart contains '$', but as !IsUnicodeIDStart('$'), "
    1353             :                       "ensure that '$' is never handled here");
    1354             :         static_assert('_' < 128,
    1355             :                       "IdentifierStart contains '_', but as !IsUnicodeIDStart('_'), "
    1356             :                       "ensure that '_' is never handled here");
    1357           0 :         if (unicode::IsUnicodeIDStart(char16_t(c))) {
    1358           0 :             identStart = userbuf.addressOfNextRawChar() - 1;
    1359           0 :             hadUnicodeEscape = false;
    1360           0 :             goto identifier;
    1361             :         }
    1362             : 
    1363           0 :         if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) {
    1364             :             uint32_t codePoint;
    1365           0 :             if (matchTrailForLeadSurrogate(c, nullptr, &codePoint) &&
    1366           0 :                 unicode::IsUnicodeIDStart(codePoint))
    1367             :             {
    1368           0 :                 identStart = userbuf.addressOfNextRawChar() - 2;
    1369           0 :                 hadUnicodeEscape = false;
    1370           0 :                 goto identifier;
    1371             :             }
    1372             :         }
    1373             : 
    1374           0 :         goto badchar;
    1375             :     }
    1376             : 
    1377             :     // Get the token kind, based on the first char.  The ordering of c1kind
    1378             :     // comparison is based on the frequency of tokens in real code -- Parsemark
    1379             :     // (which represents typical JS code on the web) and the Unreal demo (which
    1380             :     // represents asm.js code).
    1381             :     //
    1382             :     //                  Parsemark   Unreal
    1383             :     //  OneChar         32.9%       39.7%
    1384             :     //  Space           25.0%        0.6%
    1385             :     //  Ident           19.2%       36.4%
    1386             :     //  Dec              7.2%        5.1%
    1387             :     //  String           7.9%        0.0%
    1388             :     //  EOL              1.7%        0.0%
    1389             :     //  BasePrefix       0.4%        4.9%
    1390             :     //  Other            5.7%       13.3%
    1391             :     //
    1392             :     // The ordering is based mostly only Parsemark frequencies, with Unreal
    1393             :     // frequencies used to break close categories (e.g. |Dec| and |String|).
    1394             :     // |Other| is biggish, but no other token kind is common enough for it to
    1395             :     // be worth adding extra values to FirstCharKind.
    1396             :     //
    1397     1376984 :     c1kind = FirstCharKind(firstCharKinds[c]);
    1398             : 
    1399             :     // Look for an unambiguous single-char token.
    1400             :     //
    1401     1376984 :     if (c1kind <= OneChar_Max) {
    1402      228472 :         tp = newToken(-1);
    1403      228472 :         tp->type = TokenKind(c1kind);
    1404      228472 :         goto out;
    1405             :     }
    1406             : 
    1407             :     // Skip over non-EOL whitespace chars.
    1408             :     //
    1409     1148512 :     if (c1kind == Space)
    1410      710488 :         goto retry;
    1411             : 
    1412             :     // Look for an identifier.
    1413             :     //
    1414      438024 :     if (c1kind == Ident) {
    1415      213027 :         tp = newToken(-1);
    1416      213027 :         identStart = userbuf.addressOfNextRawChar() - 1;
    1417      213027 :         hadUnicodeEscape = false;
    1418             : 
    1419             :       identifier:
    1420             :         for (;;) {
    1421     1626860 :             c = getCharIgnoreEOL();
    1422     1626860 :             if (c == EOF)
    1423         100 :                 break;
    1424             : 
    1425     1626760 :             if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) {
    1426             :                 uint32_t codePoint;
    1427           0 :                 if (matchTrailForLeadSurrogate(c, nullptr, &codePoint)) {
    1428           0 :                     if (!unicode::IsIdentifierPart(codePoint))
    1429           0 :                         break;
    1430             : 
    1431           0 :                     continue;
    1432             :                 }
    1433             :             }
    1434             : 
    1435     1626760 :             if (!unicode::IsIdentifierPart(char16_t(c))) {
    1436      212927 :                 if (c != '\\' || !matchUnicodeEscapeIdent(&qc))
    1437      212927 :                     break;
    1438           0 :                 hadUnicodeEscape = true;
    1439             :             }
    1440     1413833 :         }
    1441      213027 :         ungetCharIgnoreEOL(c);
    1442             : 
    1443             :         // Identifiers containing no Unicode escapes can be processed directly
    1444             :         // from userbuf.  The rest must use the escapes converted via tokenbuf
    1445             :         // before atomizing.
    1446             :         const CharT* chars;
    1447             :         size_t length;
    1448      213027 :         if (hadUnicodeEscape) {
    1449           0 :             if (!putIdentInTokenbuf(identStart))
    1450           0 :                 goto error;
    1451             : 
    1452           0 :             chars = tokenbuf.begin();
    1453           0 :             length = tokenbuf.length();
    1454             :         } else {
    1455      213027 :             chars = identStart;
    1456      213027 :             length = userbuf.addressOfNextRawChar() - identStart;
    1457             :         }
    1458             : 
    1459             :         // Represent reserved words as reserved word tokens.
    1460      213027 :         if (!hadUnicodeEscape) {
    1461      213027 :             if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) {
    1462       60844 :                 tp->type = rw->tokentype;
    1463       60844 :                 goto out;
    1464             :             }
    1465             :         }
    1466             : 
    1467      152183 :         JSAtom* atom = AtomizeChars(cx, chars, length);
    1468      152183 :         if (!atom)
    1469           0 :             goto error;
    1470      152183 :         tp->type = TOK_NAME;
    1471      152183 :         tp->setName(atom->asPropertyName());
    1472      152183 :         goto out;
    1473             :     }
    1474             : 
    1475             :     // Look for a decimal number.
    1476             :     //
    1477      224997 :     if (c1kind == Dec) {
    1478        6378 :         tp = newToken(-1);
    1479        6378 :         numStart = userbuf.addressOfNextRawChar() - 1;
    1480             : 
    1481             :       decimal:
    1482        9627 :         decimalPoint = NoDecimal;
    1483        9627 :         hasExp = false;
    1484       29953 :         while (JS7_ISDEC(c))
    1485       10163 :             c = getCharIgnoreEOL();
    1486             : 
    1487        9627 :         if (c == '.') {
    1488          23 :             decimalPoint = HasDecimal;
    1489             :           decimal_dot:
    1490          26 :             do {
    1491          51 :                 c = getCharIgnoreEOL();
    1492          51 :             } while (JS7_ISDEC(c));
    1493             :         }
    1494        9629 :         if (c == 'e' || c == 'E') {
    1495           0 :             hasExp = true;
    1496           0 :             c = getCharIgnoreEOL();
    1497           0 :             if (c == '+' || c == '-')
    1498           0 :                 c = getCharIgnoreEOL();
    1499           0 :             if (!JS7_ISDEC(c)) {
    1500           0 :                 ungetCharIgnoreEOL(c);
    1501           0 :                 reportError(JSMSG_MISSING_EXPONENT);
    1502           0 :                 goto error;
    1503             :             }
    1504           0 :             do {
    1505           0 :                 c = getCharIgnoreEOL();
    1506           0 :             } while (JS7_ISDEC(c));
    1507             :         }
    1508        9629 :         ungetCharIgnoreEOL(c);
    1509             : 
    1510        9629 :         if (c != EOF) {
    1511        9622 :             if (unicode::IsIdentifierStart(char16_t(c))) {
    1512           0 :                 reportError(JSMSG_IDSTART_AFTER_NUMBER);
    1513           0 :                 goto error;
    1514             :             }
    1515             : 
    1516        9622 :             if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) {
    1517             :                 uint32_t codePoint;
    1518           0 :                 if (matchTrailForLeadSurrogate(c, nullptr, &codePoint) &&
    1519           0 :                     unicode::IsIdentifierStart(codePoint))
    1520             :                 {
    1521           0 :                     reportError(JSMSG_IDSTART_AFTER_NUMBER);
    1522           0 :                     goto error;
    1523             :                 }
    1524             :             }
    1525             :         }
    1526             : 
    1527             :         // Unlike identifiers and strings, numbers cannot contain escaped
    1528             :         // chars, so we don't need to use tokenbuf.  Instead we can just
    1529             :         // convert the char16_t characters in userbuf to the numeric value.
    1530             :         double dval;
    1531        9629 :         if (!((decimalPoint == HasDecimal) || hasExp)) {
    1532       19208 :             if (!GetDecimalInteger(cx, numStart, userbuf.addressOfNextRawChar(), &dval))
    1533           0 :                 goto error;
    1534             :         } else {
    1535             :             const CharT* dummy;
    1536          25 :             if (!js_strtod(cx, numStart, userbuf.addressOfNextRawChar(), &dummy, &dval))
    1537           0 :                 goto error;
    1538             :         }
    1539        9629 :         tp->type = TOK_NUMBER;
    1540        9629 :         tp->setNumber(dval, decimalPoint);
    1541        9629 :         goto out;
    1542             :     }
    1543             : 
    1544             :     // Look for a string or a template string.
    1545             :     //
    1546      218619 :     if (c1kind == String) {
    1547       21167 :         if (!getStringOrTemplateToken(c, &tp))
    1548           0 :             goto error;
    1549       21167 :         goto out;
    1550             :     }
    1551             : 
    1552             :     // Skip over EOL chars, updating line state along the way.
    1553             :     //
    1554      197452 :     if (c1kind == EOL) {
    1555             :         // If it's a \r\n sequence: treat as a single EOL, skip over the \n.
    1556      102701 :         if (c == '\r' && userbuf.hasRawChars())
    1557           0 :             userbuf.matchRawChar('\n');
    1558      102701 :         if (!updateLineInfoForEOL())
    1559           0 :             goto error;
    1560      102701 :         updateFlagsForEOL();
    1561      102701 :         goto retry;
    1562             :     }
    1563             : 
    1564             :     // Look for a hexadecimal, octal, or binary number.
    1565             :     //
    1566       94751 :     if (c1kind == BasePrefix) {
    1567        3591 :         tp = newToken(-1);
    1568             :         int radix;
    1569        3591 :         c = getCharIgnoreEOL();
    1570        3930 :         if (c == 'x' || c == 'X') {
    1571         339 :             radix = 16;
    1572         339 :             c = getCharIgnoreEOL();
    1573         339 :             if (!JS7_ISHEX(c)) {
    1574           0 :                 ungetCharIgnoreEOL(c);
    1575           0 :                 reportError(JSMSG_MISSING_HEXDIGITS);
    1576           0 :                 goto error;
    1577             :             }
    1578         339 :             numStart = userbuf.addressOfNextRawChar() - 1;  // one past the '0x'
    1579        2789 :             while (JS7_ISHEX(c))
    1580        1225 :                 c = getCharIgnoreEOL();
    1581        3252 :         } else if (c == 'b' || c == 'B') {
    1582           0 :             radix = 2;
    1583           0 :             c = getCharIgnoreEOL();
    1584           0 :             if (c != '0' && c != '1') {
    1585           0 :                 ungetCharIgnoreEOL(c);
    1586           0 :                 reportError(JSMSG_MISSING_BINARY_DIGITS);
    1587           0 :                 goto error;
    1588             :             }
    1589           0 :             numStart = userbuf.addressOfNextRawChar() - 1;  // one past the '0b'
    1590           0 :             while (c == '0' || c == '1')
    1591           0 :                 c = getCharIgnoreEOL();
    1592        3255 :         } else if (c == 'o' || c == 'O') {
    1593           3 :             radix = 8;
    1594           3 :             c = getCharIgnoreEOL();
    1595           3 :             if (c < '0' || c > '7') {
    1596           0 :                 ungetCharIgnoreEOL(c);
    1597           0 :                 reportError(JSMSG_MISSING_OCTAL_DIGITS);
    1598           0 :                 goto error;
    1599             :             }
    1600           3 :             numStart = userbuf.addressOfNextRawChar() - 1;  // one past the '0o'
    1601          21 :             while ('0' <= c && c <= '7')
    1602           9 :                 c = getCharIgnoreEOL();
    1603        3249 :         } else if (JS7_ISDEC(c)) {
    1604           0 :             radix = 8;
    1605           0 :             numStart = userbuf.addressOfNextRawChar() - 1;  // one past the '0'
    1606           0 :             while (JS7_ISDEC(c)) {
    1607             :                 // Octal integer literals are not permitted in strict mode code.
    1608           0 :                 if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
    1609           0 :                     goto error;
    1610             : 
    1611             :                 // Outside strict mode, we permit 08 and 09 as decimal numbers,
    1612             :                 // which makes our behaviour a superset of the ECMA numeric
    1613             :                 // grammar. We might not always be so permissive, so we warn
    1614             :                 // about it.
    1615           0 :                 if (c >= '8') {
    1616           0 :                     if (!warning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09"))
    1617           0 :                         goto error;
    1618             : 
    1619             :                     // Use the decimal scanner for the rest of the number.
    1620        3249 :                     goto decimal;
    1621             :                 }
    1622           0 :                 c = getCharIgnoreEOL();
    1623             :             }
    1624             :         } else {
    1625             :             // '0' not followed by 'x', 'X' or a digit;  scan as a decimal number.
    1626        3249 :             numStart = userbuf.addressOfNextRawChar() - 1;
    1627        3249 :             goto decimal;
    1628             :         }
    1629         342 :         ungetCharIgnoreEOL(c);
    1630             : 
    1631         342 :         if (c != EOF) {
    1632         342 :             if (unicode::IsIdentifierStart(char16_t(c))) {
    1633           0 :                 reportError(JSMSG_IDSTART_AFTER_NUMBER);
    1634           0 :                 goto error;
    1635             :             }
    1636             : 
    1637         342 :             if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) {
    1638             :                 uint32_t codePoint;
    1639           0 :                 if (matchTrailForLeadSurrogate(c, nullptr, &codePoint) &&
    1640           0 :                     unicode::IsIdentifierStart(codePoint))
    1641             :                 {
    1642           0 :                     reportError(JSMSG_IDSTART_AFTER_NUMBER);
    1643           0 :                     goto error;
    1644             :                 }
    1645             :             }
    1646             :         }
    1647             : 
    1648             :         double dval;
    1649             :         const char16_t* dummy;
    1650         342 :         if (!GetPrefixInteger(cx, numStart, userbuf.addressOfNextRawChar(), radix, &dummy, &dval))
    1651           0 :             goto error;
    1652         342 :         tp->type = TOK_NUMBER;
    1653         342 :         tp->setNumber(dval, NoDecimal);
    1654         342 :         goto out;
    1655             :     }
    1656             : 
    1657             :     // This handles everything else.
    1658             :     //
    1659       91160 :     MOZ_ASSERT(c1kind == Other);
    1660       91160 :     tp = newToken(-1);
    1661       91160 :     switch (c) {
    1662             :       case '.':
    1663       40457 :         c = getCharIgnoreEOL();
    1664       40457 :         if (JS7_ISDEC(c)) {
    1665           2 :             numStart = userbuf.addressOfNextRawChar() - 2;
    1666           2 :             decimalPoint = HasDecimal;
    1667           2 :             hasExp = false;
    1668           2 :             goto decimal_dot;
    1669             :         }
    1670       40455 :         if (c == '.') {
    1671         152 :             if (matchChar('.')) {
    1672         152 :                 tp->type = TOK_TRIPLEDOT;
    1673         152 :                 goto out;
    1674             :             }
    1675             :         }
    1676       40303 :         ungetCharIgnoreEOL(c);
    1677       40303 :         tp->type = TOK_DOT;
    1678       40303 :         goto out;
    1679             : 
    1680             :       case '=':
    1681       22770 :         if (matchChar('='))
    1682        2858 :             tp->type = matchChar('=') ? TOK_STRICTEQ : TOK_EQ;
    1683       19912 :         else if (matchChar('>'))
    1684        1851 :             tp->type = TOK_ARROW;
    1685             :         else
    1686       18061 :             tp->type = TOK_ASSIGN;
    1687       22770 :         goto out;
    1688             : 
    1689             :       case '+':
    1690        6652 :         if (matchChar('+'))
    1691         712 :             tp->type = TOK_INC;
    1692             :         else
    1693        5940 :             tp->type = matchChar('=') ? TOK_ADDASSIGN : TOK_ADD;
    1694        6652 :         goto out;
    1695             : 
    1696             :       case '\\': {
    1697           0 :         uint32_t escapeLength = matchUnicodeEscapeIdStart(&qc);
    1698           0 :         if (escapeLength > 0) {
    1699           0 :             identStart = userbuf.addressOfNextRawChar() - escapeLength - 1;
    1700           0 :             hadUnicodeEscape = true;
    1701           0 :             goto identifier;
    1702             :         }
    1703           0 :         goto badchar;
    1704             :       }
    1705             : 
    1706             :       case '|':
    1707        1689 :         if (matchChar('|'))
    1708        1459 :             tp->type = TOK_OR;
    1709             :         else
    1710         230 :             tp->type = matchChar('=') ? TOK_BITORASSIGN : TOK_BITOR;
    1711        1689 :         goto out;
    1712             : 
    1713             :       case '^':
    1714          18 :         tp->type = matchChar('=') ? TOK_BITXORASSIGN : TOK_BITXOR;
    1715          18 :         goto out;
    1716             : 
    1717             :       case '&':
    1718        1807 :         if (matchChar('&'))
    1719        1642 :             tp->type = TOK_AND;
    1720             :         else
    1721         165 :             tp->type = matchChar('=') ? TOK_BITANDASSIGN : TOK_BITAND;
    1722        1807 :         goto out;
    1723             : 
    1724             :       case '!':
    1725        4786 :         if (matchChar('='))
    1726        1183 :             tp->type = matchChar('=') ? TOK_STRICTNE : TOK_NE;
    1727             :         else
    1728        3603 :             tp->type = TOK_NOT;
    1729        4786 :         goto out;
    1730             : 
    1731             :       case '<':
    1732        1154 :         if (options().allowHTMLComments) {
    1733             :             // Treat HTML begin-comment as comment-till-end-of-line.
    1734        1154 :             if (matchChar('!')) {
    1735           0 :                 if (matchChar('-')) {
    1736           0 :                     if (matchChar('-'))
    1737           0 :                         goto skipline;
    1738           0 :                     ungetChar('-');
    1739             :                 }
    1740           0 :                 ungetChar('!');
    1741             :             }
    1742             :         }
    1743        1154 :         if (matchChar('<')) {
    1744          23 :             tp->type = matchChar('=') ? TOK_LSHASSIGN : TOK_LSH;
    1745             :         } else {
    1746        1131 :             tp->type = matchChar('=') ? TOK_LE : TOK_LT;
    1747             :         }
    1748        1154 :         goto out;
    1749             : 
    1750             :       case '>':
    1751         781 :         if (matchChar('>')) {
    1752          56 :             if (matchChar('>'))
    1753          21 :                 tp->type = matchChar('=') ? TOK_URSHASSIGN : TOK_URSH;
    1754             :             else
    1755          35 :                 tp->type = matchChar('=') ? TOK_RSHASSIGN : TOK_RSH;
    1756             :         } else {
    1757         725 :             tp->type = matchChar('=') ? TOK_GE : TOK_GT;
    1758             :         }
    1759         781 :         goto out;
    1760             : 
    1761             :       case '*':
    1762         224 :         if (matchChar('*'))
    1763           0 :             tp->type = matchChar('=') ? TOK_POWASSIGN : TOK_POW;
    1764             :         else
    1765         224 :             tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL;
    1766         224 :         goto out;
    1767             : 
    1768             :       case '/':
    1769             :         // Look for a single-line comment.
    1770        9963 :         if (matchChar('/')) {
    1771        7439 :             if (!peekChar(&c))
    1772           0 :                 goto error;
    1773        7439 :             if (c == '@' || c == '#') {
    1774         279 :                 consumeKnownChar(c);
    1775             : 
    1776         279 :                 bool shouldWarn = c == '@';
    1777         279 :                 if (!getDirectives(false, shouldWarn))
    1778           0 :                     goto error;
    1779             :             }
    1780             : 
    1781             :         skipline:
    1782      397755 :             do {
    1783      405194 :                 if (!getChar(&c))
    1784           0 :                     goto error;
    1785      405194 :             } while (c != EOF && c != '\n');
    1786             : 
    1787        7439 :             ungetChar(c);
    1788        7439 :             cursor = (cursor - 1) & ntokensMask;
    1789        7439 :             goto retry;
    1790             :         }
    1791             : 
    1792             :         // Look for a multi-line comment.
    1793        2524 :         if (matchChar('*')) {
    1794        2220 :             unsigned linenoBefore = lineno;
    1795             : 
    1796             :             do {
    1797      547068 :                 if (!getChar(&c))
    1798           0 :                     return false;
    1799             : 
    1800      547068 :                 if (c == EOF) {
    1801           0 :                     reportError(JSMSG_UNTERMINATED_COMMENT);
    1802           0 :                     goto error;
    1803             :                 }
    1804             : 
    1805      547068 :                 if (c == '*' && matchChar('/'))
    1806        2220 :                     break;
    1807             : 
    1808      544848 :                 if (c == '@' || c == '#') {
    1809        2309 :                     bool shouldWarn = c == '@';
    1810        2309 :                     if (!getDirectives(true, shouldWarn))
    1811           0 :                         goto error;
    1812      544848 :                 }
    1813             :             } while (true);
    1814             : 
    1815        2220 :             if (linenoBefore != lineno)
    1816        1919 :                 updateFlagsForEOL();
    1817        2220 :             cursor = (cursor - 1) & ntokensMask;
    1818        2220 :             goto retry;
    1819             :         }
    1820             : 
    1821             :         // Look for a regexp.
    1822         304 :         if (modifier == Operand) {
    1823         210 :             tokenbuf.clear();
    1824             : 
    1825         210 :             bool inCharClass = false;
    1826             :             do {
    1827        6506 :                 if (!getChar(&c))
    1828           0 :                     goto error;
    1829             : 
    1830        3358 :                 if (c == '\\') {
    1831         243 :                     if (!tokenbuf.append(c))
    1832           0 :                         goto error;
    1833         243 :                     if (!getChar(&c))
    1834           0 :                         goto error;
    1835        3115 :                 } else if (c == '[') {
    1836          80 :                     inCharClass = true;
    1837        3035 :                 } else if (c == ']') {
    1838          80 :                     inCharClass = false;
    1839        2955 :                 } else if (c == '/' && !inCharClass) {
    1840             :                     // For compat with IE, allow unescaped / in char classes.
    1841         210 :                     break;
    1842             :                 }
    1843        3148 :                 if (c == '\n' || c == EOF) {
    1844           0 :                     ungetChar(c);
    1845           0 :                     reportError(JSMSG_UNTERMINATED_REGEXP);
    1846           0 :                     goto error;
    1847             :                 }
    1848        3148 :                 if (!tokenbuf.append(c))
    1849           0 :                     goto error;
    1850             :             } while (true);
    1851             : 
    1852         210 :             RegExpFlag reflags = NoFlags;
    1853         210 :             unsigned length = tokenbuf.length() + 1;
    1854             :             while (true) {
    1855         488 :                 if (!peekChar(&c))
    1856           0 :                     goto error;
    1857         349 :                 if (c == 'g' && !(reflags & GlobalFlag))
    1858          86 :                     reflags = RegExpFlag(reflags | GlobalFlag);
    1859         263 :                 else if (c == 'i' && !(reflags & IgnoreCaseFlag))
    1860          50 :                     reflags = RegExpFlag(reflags | IgnoreCaseFlag);
    1861         213 :                 else if (c == 'm' && !(reflags & MultilineFlag))
    1862           3 :                     reflags = RegExpFlag(reflags | MultilineFlag);
    1863         210 :                 else if (c == 'y' && !(reflags & StickyFlag))
    1864           0 :                     reflags = RegExpFlag(reflags | StickyFlag);
    1865         210 :                 else if (c == 'u' && !(reflags & UnicodeFlag))
    1866           0 :                     reflags = RegExpFlag(reflags | UnicodeFlag);
    1867             :                 else
    1868             :                     break;
    1869         139 :                 if (!getChar(&c))
    1870           0 :                     goto error;
    1871         139 :                 length++;
    1872             :             }
    1873             : 
    1874         210 :             if (!peekChar(&c))
    1875           0 :                 goto error;
    1876         210 :             if (JS7_ISLET(c)) {
    1877           0 :                 char buf[2] = { '\0', '\0' };
    1878           0 :                 tp->pos.begin += length + 1;
    1879           0 :                 buf[0] = char(c);
    1880           0 :                 reportError(JSMSG_BAD_REGEXP_FLAG, buf);
    1881           0 :                 consumeKnownChar(c);
    1882           0 :                 goto error;
    1883             :             }
    1884         210 :             tp->type = TOK_REGEXP;
    1885         210 :             tp->setRegExpFlags(reflags);
    1886         210 :             goto out;
    1887             :         }
    1888             : 
    1889          94 :         tp->type = matchChar('=') ? TOK_DIVASSIGN : TOK_DIV;
    1890          94 :         goto out;
    1891             : 
    1892             :       case '%':
    1893          16 :         tp->type = matchChar('=') ? TOK_MODASSIGN : TOK_MOD;
    1894          16 :         goto out;
    1895             : 
    1896             :       case '-':
    1897         843 :         if (matchChar('-')) {
    1898          72 :             if (options().allowHTMLComments && !flags.isDirtyLine) {
    1899             :                 int32_t c2;
    1900           4 :                 if (!peekChar(&c2))
    1901           0 :                     goto error;
    1902             : 
    1903           4 :                 if (c2 == '>')
    1904           0 :                     goto skipline;
    1905             :             }
    1906             : 
    1907          72 :             tp->type = TOK_DEC;
    1908             :         } else {
    1909         771 :             tp->type = matchChar('=') ? TOK_SUBASSIGN : TOK_SUB;
    1910             :         }
    1911         843 :         goto out;
    1912             : 
    1913             :       badchar:
    1914             :       default:
    1915           0 :         reportError(JSMSG_ILLEGAL_CHARACTER);
    1916           0 :         goto error;
    1917             :     }
    1918             : 
    1919             :     MOZ_CRASH("should have jumped to |out| or |error|");
    1920             : 
    1921             :   out:
    1922      555417 :     flags.isDirtyLine = true;
    1923      555417 :     tp->pos.end = userbuf.offset();
    1924             : #ifdef DEBUG
    1925             :     // Save the modifier used to get this token, so that if an ungetToken()
    1926             :     // occurs and then the token is re-gotten (or peeked, etc.), we can assert
    1927             :     // that both gets have used the same modifiers.
    1928      555417 :     tp->modifier = modifier;
    1929      555417 :     tp->modifierException = NoException;
    1930             : #endif
    1931      555417 :     MOZ_ASSERT(IsTokenSane(tp));
    1932      555417 :     *ttp = tp->type;
    1933      555417 :     return true;
    1934             : 
    1935             :   error:
    1936             :     // We didn't get a token, so don't set |flags.isDirtyLine|.  And don't
    1937             :     // poison any of |*tp|: if we haven't allocated a token, |tp| could be
    1938             :     // uninitialized.
    1939           0 :     flags.hadError = true;
    1940             : #ifdef DEBUG
    1941             :     // Poisoning userbuf on error establishes an invariant: once an erroneous
    1942             :     // token has been seen, userbuf will not be consulted again.  This is true
    1943             :     // because the parser will deal with the illegal token by aborting parsing
    1944             :     // immediately.
    1945           0 :     userbuf.poison();
    1946             : #endif
    1947             :     MOZ_MAKE_MEM_UNDEFINED(ttp, sizeof(*ttp));
    1948           0 :     return false;
    1949             : }
    1950             : 
    1951             : bool
    1952       21832 : TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
    1953             : {
    1954             :     int c;
    1955       21832 :     int nc = -1;
    1956             : 
    1957       21832 :     bool parsingTemplate = (untilChar == '`');
    1958             : 
    1959       21832 :     *tp = newToken(-1);
    1960       21832 :     tokenbuf.clear();
    1961             : 
    1962             :     // We need to detect any of these chars:  " or ', \n (or its
    1963             :     // equivalents), \\, EOF.  Because we detect EOL sequences here and
    1964             :     // put them back immediately, we can use getCharIgnoreEOL().
    1965      691160 :     while ((c = getCharIgnoreEOL()) != untilChar) {
    1966      335329 :         if (c == EOF) {
    1967           0 :             ungetCharIgnoreEOL(c);
    1968           0 :             error(JSMSG_UNTERMINATED_STRING);
    1969           0 :             return false;
    1970             :         }
    1971             : 
    1972      335329 :         if (c == '\\') {
    1973             :             // When parsing templates, we don't immediately report errors for
    1974             :             // invalid escapes; these are handled by the parser.
    1975             :             // In those cases we don't append to tokenbuf, since it won't be
    1976             :             // read.
    1977         239 :             if (!getChar(&c))
    1978           0 :                 return false;
    1979             : 
    1980         239 :             switch (c) {
    1981           0 :               case 'b': c = '\b'; break;
    1982           0 :               case 'f': c = '\f'; break;
    1983         173 :               case 'n': c = '\n'; break;
    1984           6 :               case 'r': c = '\r'; break;
    1985          10 :               case 't': c = '\t'; break;
    1986           0 :               case 'v': c = '\v'; break;
    1987             : 
    1988             :               case '\n':
    1989             :                 // ES5 7.8.4: an escaped line terminator represents
    1990             :                 // no character.
    1991           0 :                 continue;
    1992             : 
    1993             :               // Unicode character specification.
    1994             :               case 'u': {
    1995           2 :                 uint32_t code = 0;
    1996             : 
    1997             :                 int32_t c2;
    1998           2 :                 if (!peekChar(&c2))
    1999           0 :                     return false;
    2000             : 
    2001           2 :                 uint32_t start = userbuf.offset() - 2;
    2002             : 
    2003           2 :                 if (c2 == '{') {
    2004           0 :                     consumeKnownChar('{');
    2005             : 
    2006           0 :                     bool first = true;
    2007           0 :                     bool valid = true;
    2008             :                     do {
    2009           0 :                         int32_t c = getCharIgnoreEOL();
    2010           0 :                         if (c == EOF) {
    2011           0 :                             if (parsingTemplate) {
    2012           0 :                                 setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
    2013           0 :                                 valid = false;
    2014           0 :                                 break;
    2015             :                             }
    2016           0 :                             reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
    2017           0 :                             return false;
    2018             :                         }
    2019           0 :                         if (c == '}') {
    2020           0 :                             if (first) {
    2021           0 :                                 if (parsingTemplate) {
    2022           0 :                                     setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
    2023           0 :                                     valid = false;
    2024           0 :                                     break;
    2025             :                                 }
    2026           0 :                                 reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
    2027           0 :                                 return false;
    2028             :                             }
    2029           0 :                             break;
    2030             :                         }
    2031             : 
    2032           0 :                         if (!JS7_ISHEX(c)) {
    2033           0 :                             if (parsingTemplate) {
    2034             :                                 // We put the character back so that we read
    2035             :                                 // it on the next pass, which matters if it
    2036             :                                 // was '`' or '\'.
    2037           0 :                                 ungetCharIgnoreEOL(c);
    2038           0 :                                 setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
    2039           0 :                                 valid = false;
    2040           0 :                                 break;
    2041             :                             }
    2042           0 :                             reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
    2043           0 :                             return false;
    2044             :                         }
    2045             : 
    2046           0 :                         code = (code << 4) | JS7_UNHEX(c);
    2047           0 :                         if (code > unicode::NonBMPMax) {
    2048           0 :                             if (parsingTemplate) {
    2049           0 :                                 setInvalidTemplateEscape(start + 3, InvalidEscapeType::UnicodeOverflow);
    2050           0 :                                 valid = false;
    2051           0 :                                 break;
    2052             :                             }
    2053           0 :                             reportInvalidEscapeError(start + 3, InvalidEscapeType::UnicodeOverflow);
    2054           0 :                             return false;
    2055             :                         }
    2056             : 
    2057           0 :                         first = false;
    2058             :                     } while (true);
    2059             : 
    2060           0 :                     if (!valid)
    2061           0 :                         continue;
    2062             : 
    2063           0 :                     MOZ_ASSERT(code <= unicode::NonBMPMax);
    2064           0 :                     if (code < unicode::NonBMPMin) {
    2065           0 :                         c = code;
    2066             :                     } else {
    2067           0 :                         if (!tokenbuf.append(unicode::LeadSurrogate(code)))
    2068           0 :                             return false;
    2069           0 :                         c = unicode::TrailSurrogate(code);
    2070             :                     }
    2071           2 :                     break;
    2072             :                 }
    2073             : 
    2074             :                 CharT cp[4];
    2075           6 :                 if (peekChars(4, cp) &&
    2076           4 :                     JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3]))
    2077             :                 {
    2078           2 :                     c = JS7_UNHEX(cp[0]);
    2079           2 :                     c = (c << 4) + JS7_UNHEX(cp[1]);
    2080           2 :                     c = (c << 4) + JS7_UNHEX(cp[2]);
    2081           2 :                     c = (c << 4) + JS7_UNHEX(cp[3]);
    2082           2 :                     skipChars(4);
    2083             :                 } else {
    2084           0 :                     if (parsingTemplate) {
    2085           0 :                         setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
    2086           0 :                         continue;
    2087             :                     }
    2088           0 :                     reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
    2089           0 :                     return false;
    2090             :                 }
    2091           2 :                 break;
    2092             :               }
    2093             : 
    2094             :               // Hexadecimal character specification.
    2095             :               case 'x': {
    2096             :                 CharT cp[2];
    2097           0 :                 if (peekChars(2, cp) && JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
    2098           0 :                     c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
    2099           0 :                     skipChars(2);
    2100             :                 } else {
    2101           0 :                     uint32_t start = userbuf.offset() - 2;
    2102           0 :                     if (parsingTemplate) {
    2103           0 :                         setInvalidTemplateEscape(start, InvalidEscapeType::Hexadecimal);
    2104           0 :                         continue;
    2105             :                     }
    2106           0 :                     reportInvalidEscapeError(start, InvalidEscapeType::Hexadecimal);
    2107           0 :                     return false;
    2108             :                 }
    2109           0 :                 break;
    2110             :               }
    2111             : 
    2112             :               default:
    2113             :                 // Octal character specification.
    2114          48 :                 if (JS7_ISOCT(c)) {
    2115           5 :                     int32_t val = JS7_UNOCT(c);
    2116             : 
    2117           5 :                     if (!peekChar(&c))
    2118           0 :                         return false;
    2119             : 
    2120             :                     // Strict mode code allows only \0, then a non-digit.
    2121           5 :                     if (val != 0 || JS7_ISDEC(c)) {
    2122           0 :                         if (parsingTemplate) {
    2123           0 :                             setInvalidTemplateEscape(userbuf.offset() - 2, InvalidEscapeType::Octal);
    2124           0 :                             continue;
    2125             :                         }
    2126           0 :                         if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
    2127           0 :                             return false;
    2128           0 :                         flags.sawOctalEscape = true;
    2129             :                     }
    2130             : 
    2131           5 :                     if (JS7_ISOCT(c)) {
    2132           0 :                         val = 8 * val + JS7_UNOCT(c);
    2133           0 :                         consumeKnownChar(c);
    2134           0 :                         if (!peekChar(&c))
    2135           0 :                             return false;
    2136           0 :                         if (JS7_ISOCT(c)) {
    2137           0 :                             int32_t save = val;
    2138           0 :                             val = 8 * val + JS7_UNOCT(c);
    2139           0 :                             if (val <= 0xFF)
    2140           0 :                                 consumeKnownChar(c);
    2141             :                             else
    2142           0 :                                 val = save;
    2143             :                         }
    2144             :                     }
    2145             : 
    2146           5 :                     c = char16_t(val);
    2147             :                 }
    2148          48 :                 break;
    2149             :             }
    2150      335090 :         } else if (TokenBuf::isRawEOLChar(c)) {
    2151           9 :             if (!parsingTemplate) {
    2152           0 :                 ungetCharIgnoreEOL(c);
    2153           0 :                 error(JSMSG_UNTERMINATED_STRING);
    2154           0 :                 return false;
    2155             :             }
    2156           9 :             if (c == '\r') {
    2157           0 :                 c = '\n';
    2158           0 :                 if (userbuf.peekRawChar() == '\n')
    2159           0 :                     skipCharsIgnoreEOL(1);
    2160             :             }
    2161             : 
    2162           9 :             if (!updateLineInfoForEOL())
    2163           0 :                 return false;
    2164             : 
    2165           9 :             updateFlagsForEOL();
    2166      335081 :         } else if (parsingTemplate && c == '$') {
    2167         665 :             if ((nc = getCharIgnoreEOL()) == '{')
    2168         665 :                 break;
    2169           0 :             ungetCharIgnoreEOL(nc);
    2170             :         }
    2171             : 
    2172      334664 :         if (!tokenbuf.append(c)) {
    2173           0 :             ReportOutOfMemory(cx);
    2174           0 :             return false;
    2175             :         }
    2176             :     }
    2177             : 
    2178       21832 :     JSAtom* atom = atomize(cx, tokenbuf);
    2179       21832 :     if (!atom)
    2180           0 :         return false;
    2181             : 
    2182       21832 :     if (!parsingTemplate) {
    2183       20856 :         (*tp)->type = TOK_STRING;
    2184             :     } else {
    2185         976 :         if (c == '$' && nc == '{')
    2186         665 :             (*tp)->type = TOK_TEMPLATE_HEAD;
    2187             :         else
    2188         311 :             (*tp)->type = TOK_NO_SUBS_TEMPLATE;
    2189             :     }
    2190             : 
    2191       21832 :     (*tp)->setAtom(atom);
    2192       21832 :     return true;
    2193             : }
    2194             : 
    2195             : const char*
    2196           0 : TokenKindToDesc(TokenKind tt)
    2197             : {
    2198           0 :     switch (tt) {
    2199             : #define EMIT_CASE(name, desc) case TOK_##name: return desc;
    2200           0 :       FOR_EACH_TOKEN_KIND(EMIT_CASE)
    2201             : #undef EMIT_CASE
    2202             :       case TOK_LIMIT:
    2203           0 :         MOZ_ASSERT_UNREACHABLE("TOK_LIMIT should not be passed.");
    2204             :         break;
    2205             :     }
    2206             : 
    2207           0 :     return "<bad TokenKind>";
    2208             : }
    2209             : 
    2210             : #ifdef DEBUG
    2211             : const char*
    2212           0 : TokenKindToString(TokenKind tt)
    2213             : {
    2214           0 :     switch (tt) {
    2215             : #define EMIT_CASE(name, desc) case TOK_##name: return "TOK_" #name;
    2216           0 :       FOR_EACH_TOKEN_KIND(EMIT_CASE)
    2217             : #undef EMIT_CASE
    2218           0 :       case TOK_LIMIT: break;
    2219             :     }
    2220             : 
    2221           0 :     return "<bad TokenKind>";
    2222             : }
    2223             : #endif
    2224             : 
    2225             : } // namespace frontend
    2226             : 
    2227             : } // namespace js
    2228             : 
    2229             : 
    2230             : JS_FRIEND_API(int)
    2231           0 : js_fgets(char* buf, int size, FILE* file)
    2232             : {
    2233             :     int n, i, c;
    2234             :     bool crflag;
    2235             : 
    2236           0 :     n = size - 1;
    2237           0 :     if (n < 0)
    2238           0 :         return -1;
    2239             : 
    2240           0 :     crflag = false;
    2241           0 :     for (i = 0; i < n && (c = fast_getc(file)) != EOF; i++) {
    2242           0 :         buf[i] = c;
    2243           0 :         if (c == '\n') {        // any \n ends a line
    2244           0 :             i++;                // keep the \n; we know there is room for \0
    2245           0 :             break;
    2246             :         }
    2247           0 :         if (crflag) {           // \r not followed by \n ends line at the \r
    2248           0 :             ungetc(c, file);
    2249           0 :             break;              // and overwrite c in buf with \0
    2250             :         }
    2251           0 :         crflag = (c == '\r');
    2252             :     }
    2253             : 
    2254           0 :     buf[i] = '\0';
    2255           0 :     return i;
    2256             : }

Generated by: LCOV version 1.13