+ m_buffer16.shrink(0);
+ return StringParsedSuccessfully;
+}
+
+#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
+// While the lexer accepts <LF><CR> (not <CR><LF>) sequence
+// as one line terminator and increments one line number,
+// TemplateLiteral considers it as two line terminators <LF> and <CR>.
+//
+// TemplateLiteral normalizes line terminators as follows.
+//
+// <LF> => <LF>
+// <CR> => <LF>
+// <CR><LF> => <LF>
+// <\u2028> => <\u2028>
+// <\u2029> => <\u2029>
+//
+// So, <LF><CR> should be normalized to <LF><LF>.
+// However, the lexer should increment the line number only once for <LF><CR>.
+//
+// To achieve this, LineNumberAdder holds the current status of line terminator sequence.
+// When TemplateLiteral lexer encounters a line terminator, it notifies to LineNumberAdder.
+// LineNumberAdder maintains the status and increments the line number when it's necessary.
+// For example, LineNumberAdder increments the line number only once for <LF><CR> and <CR><LF>.
+template<typename CharacterType>
+class LineNumberAdder {
+public:
+ LineNumberAdder(int& lineNumber)
+ : m_lineNumber(lineNumber)
+ {
+ }
+
+ void clear()
+ {
+ m_previous = 0;
+ }
+
+ void add(CharacterType character)
+ {
+ ASSERT(Lexer<CharacterType>::isLineTerminator(character));
+ if ((character + m_previous) == ('\n' + '\r'))
+ m_previous = 0;
+ else {
+ ++m_lineNumber;
+ m_previous = character;
+ }
+ }
+
+private:
+ int& m_lineNumber;
+ CharacterType m_previous { 0 };
+};
+
+template <typename T>
+template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>::parseTemplateLiteral(JSTokenData* tokenData, RawStringsBuildMode rawStringsBuildMode)
+{
+ const T* stringStart = currentSourcePtr();
+ const T* rawStringStart = currentSourcePtr();
+
+ LineNumberAdder<T> lineNumberAdder(m_lineNumber);
+
+ while (m_current != '`') {
+ if (UNLIKELY(m_current == '\\')) {
+ lineNumberAdder.clear();
+ if (stringStart != currentSourcePtr() && shouldBuildStrings)
+ append16(stringStart, currentSourcePtr() - stringStart);
+ shift();
+
+ LChar escape = singleEscape(m_current);
+
+ // Most common escape sequences first.
+ if (escape) {
+ if (shouldBuildStrings)
+ record16(escape);
+ shift();
+ } else if (UNLIKELY(isLineTerminator(m_current))) {
+ if (m_current == '\r') {
+ lineNumberAdder.add(m_current);
+ shift();
+ if (m_current == '\n') {
+ lineNumberAdder.add(m_current);
+ shift();
+ }
+ } else {
+ lineNumberAdder.add(m_current);
+ shift();
+ }
+ } else {
+ bool strictMode = true;
+ StringParseResult result = parseComplexEscape<shouldBuildStrings>(EscapeParseMode::Template, strictMode, '`');
+ if (result != StringParsedSuccessfully)
+ return result;
+ }
+
+ stringStart = currentSourcePtr();
+ continue;
+ }
+
+ if (m_current == '$' && peek(1) == '{')
+ break;
+
+ // Fast check for characters that require special handling.
+ // Catches 0, \n, \r, 0x2028, and 0x2029 as efficiently
+ // as possible, and lets through all common ASCII characters.
+ if (UNLIKELY(((static_cast<unsigned>(m_current) - 0xE) & 0x2000))) {
+ // End of input is not allowed.
+ // Unlike String, line terminator is allowed.
+ if (atEnd()) {
+ m_lexErrorMessage = ASCIILiteral("Unexpected EOF");
+ return atEnd() ? StringUnterminated : StringCannotBeParsed;
+ }
+
+ if (isLineTerminator(m_current)) {
+ if (m_current == '\r') {
+ // Normalize <CR>, <CR><LF> to <LF>.
+ if (shouldBuildStrings) {
+ if (stringStart != currentSourcePtr())
+ append16(stringStart, currentSourcePtr() - stringStart);
+ if (rawStringStart != currentSourcePtr() && rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings)
+ m_bufferForRawTemplateString16.append(rawStringStart, currentSourcePtr() - rawStringStart);
+
+ record16('\n');
+ if (rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings)
+ m_bufferForRawTemplateString16.append('\n');
+ }
+ lineNumberAdder.add(m_current);
+ shift();
+ if (m_current == '\n') {
+ lineNumberAdder.add(m_current);
+ shift();
+ }
+ stringStart = currentSourcePtr();
+ rawStringStart = currentSourcePtr();
+ } else {
+ lineNumberAdder.add(m_current);
+ shift();
+ }
+ continue;
+ }
+ // Anything else is just a normal character
+ }
+
+ lineNumberAdder.clear();
+ shift();
+ }
+
+ bool isTail = m_current == '`';
+
+ if (shouldBuildStrings) {
+ if (currentSourcePtr() != stringStart)
+ append16(stringStart, currentSourcePtr() - stringStart);
+ if (rawStringStart != currentSourcePtr() && rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings)
+ m_bufferForRawTemplateString16.append(rawStringStart, currentSourcePtr() - rawStringStart);
+ }
+
+ if (shouldBuildStrings) {
+ tokenData->cooked = makeIdentifier(m_buffer16.data(), m_buffer16.size());
+ // Line terminator normalization (e.g. <CR> => <LF>) should be applied to both the raw and cooked representations.
+ if (rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings)
+ tokenData->raw = makeIdentifier(m_bufferForRawTemplateString16.data(), m_bufferForRawTemplateString16.size());
+ else
+ tokenData->raw = makeEmptyIdentifier();
+ } else {
+ tokenData->cooked = makeEmptyIdentifier();
+ tokenData->raw = makeEmptyIdentifier();
+ }
+ tokenData->isTail = isTail;
+
+ m_buffer16.shrink(0);
+ m_bufferForRawTemplateString16.shrink(0);
+
+ if (isTail) {
+ // Skip `
+ shift();
+ } else {
+ // Skip $ and {
+ shift();
+ shift();
+ }
+