700d1a756cc36dc5d5ed90508b1908a4f165a900
[WebKit-https.git] / Source / JavaScriptCore / parser / Lexer.h
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012, 2013 Apple Inc. All rights reserved.
4  *  Copyright (C) 2010 Zoltan Herczeg (zherczeg@inf.u-szeged.hu)
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License
17  *  along with this library; see the file COPYING.LIB.  If not, write to
18  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #pragma once
24
25 #include "Lookup.h"
26 #include "ParserArena.h"
27 #include "ParserModes.h"
28 #include "ParserTokens.h"
29 #include "SourceCode.h"
30 #include <wtf/ASCIICType.h>
31 #include <wtf/Vector.h>
32
33 namespace JSC {
34
35 enum LexerFlags {
36     LexerFlagsIgnoreReservedWords = 1, 
37     LexerFlagsDontBuildStrings = 2,
38     LexexFlagsDontBuildKeywords = 4
39 };
40
41 enum class LexerEscapeParseMode { Template, String };
42
43 struct ParsedUnicodeEscapeValue;
44
45 bool isLexerKeyword(const Identifier&);
46
47 template <typename T>
48 class Lexer {
49     WTF_MAKE_NONCOPYABLE(Lexer);
50     WTF_MAKE_FAST_ALLOCATED;
51
52 public:
53     Lexer(VM*, JSParserBuiltinMode, JSParserScriptMode);
54     ~Lexer();
55
56     // Character manipulation functions.
57     static bool isWhiteSpace(T character);
58     static bool isLineTerminator(T character);
59     static unsigned char convertHex(int c1, int c2);
60     static UChar convertUnicode(int c1, int c2, int c3, int c4);
61
62     // Functions to set up parsing.
63     void setCode(const SourceCode&, ParserArena*);
64     void setIsReparsingFunction() { m_isReparsingFunction = true; }
65     bool isReparsingFunction() const { return m_isReparsingFunction; }
66
67     JSTokenType lex(JSToken*, unsigned, bool strictMode);
68     bool nextTokenIsColon();
69     int lineNumber() const { return m_lineNumber; }
70     ALWAYS_INLINE int currentOffset() const { return offsetFromSourcePtr(m_code); }
71     ALWAYS_INLINE int currentLineStartOffset() const { return offsetFromSourcePtr(m_lineStart); }
72     ALWAYS_INLINE JSTextPosition currentPosition() const
73     {
74         return JSTextPosition(m_lineNumber, currentOffset(), currentLineStartOffset());
75     }
76     JSTextPosition positionBeforeLastNewline() const { return m_positionBeforeLastNewline; }
77     JSTokenLocation lastTokenLocation() const { return m_lastTokenLocation; }
78     void setLastLineNumber(int lastLineNumber) { m_lastLineNumber = lastLineNumber; }
79     int lastLineNumber() const { return m_lastLineNumber; }
80     bool prevTerminator() const { return m_terminator; }
81     JSTokenType scanRegExp(JSToken*, UChar patternPrefix = 0);
82     enum class RawStringsBuildMode { BuildRawStrings, DontBuildRawStrings };
83     JSTokenType scanTemplateString(JSToken*, RawStringsBuildMode);
84
85     // Functions for use after parsing.
86     bool sawError() const { return m_error; }
87     void setSawError(bool sawError) { m_error = sawError; }
88     String getErrorMessage() const { return m_lexErrorMessage; }
89     void setErrorMessage(const String& errorMessage) { m_lexErrorMessage = errorMessage; }
90     String sourceURLDirective() const { return m_sourceURLDirective; }
91     String sourceMappingURLDirective() const { return m_sourceMappingURLDirective; }
92     void clear();
93     void setOffset(int offset, int lineStartOffset)
94     {
95         m_error = 0;
96         m_lexErrorMessage = String();
97
98         m_code = sourcePtrFromOffset(offset);
99         m_lineStart = sourcePtrFromOffset(lineStartOffset);
100         ASSERT(currentOffset() >= currentLineStartOffset());
101
102         m_buffer8.shrink(0);
103         m_buffer16.shrink(0);
104         if (LIKELY(m_code < m_codeEnd))
105             m_current = *m_code;
106         else
107             m_current = 0;
108     }
109     void setLineNumber(int line)
110     {
111         m_lineNumber = line;
112     }
113     void setTerminator(bool terminator)
114     {
115         m_terminator = terminator;
116     }
117
118     JSTokenType lexExpectIdentifier(JSToken*, unsigned, bool strictMode);
119
120     ALWAYS_INLINE StringView getToken(const JSToken& token)
121     {
122         SourceProvider* sourceProvider = m_source->provider();
123         ASSERT_WITH_MESSAGE(token.m_location.startOffset <= token.m_location.endOffset, "Calling this function with the baked token.");
124         return sourceProvider->getRange(token.m_location.startOffset, token.m_location.endOffset);
125     }
126
127 private:
128     void record8(int);
129     void append8(const T*, size_t);
130     void record16(int);
131     void record16(T);
132     void recordUnicodeCodePoint(UChar32);
133     void append16(const LChar*, size_t);
134     void append16(const UChar* characters, size_t length) { m_buffer16.append(characters, length); }
135
136     ALWAYS_INLINE void shift();
137     ALWAYS_INLINE bool atEnd() const;
138     ALWAYS_INLINE T peek(int offset) const;
139
140     ParsedUnicodeEscapeValue parseUnicodeEscape();
141     void shiftLineTerminator();
142
143     ALWAYS_INLINE int offsetFromSourcePtr(const T* ptr) const { return ptr - m_codeStart; }
144     ALWAYS_INLINE const T* sourcePtrFromOffset(int offset) const { return m_codeStart + offset; }
145
146     String invalidCharacterMessage() const;
147     ALWAYS_INLINE const T* currentSourcePtr() const;
148     ALWAYS_INLINE void setOffsetFromSourcePtr(const T* sourcePtr, unsigned lineStartOffset) { setOffset(offsetFromSourcePtr(sourcePtr), lineStartOffset); }
149
150     ALWAYS_INLINE void setCodeStart(const StringView&);
151
152     ALWAYS_INLINE const Identifier* makeIdentifier(const LChar* characters, size_t length);
153     ALWAYS_INLINE const Identifier* makeIdentifier(const UChar* characters, size_t length);
154     ALWAYS_INLINE const Identifier* makeLCharIdentifier(const LChar* characters, size_t length);
155     ALWAYS_INLINE const Identifier* makeLCharIdentifier(const UChar* characters, size_t length);
156     ALWAYS_INLINE const Identifier* makeRightSizedIdentifier(const UChar* characters, size_t length, UChar orAllChars);
157     ALWAYS_INLINE const Identifier* makeIdentifierLCharFromUChar(const UChar* characters, size_t length);
158     ALWAYS_INLINE const Identifier* makeEmptyIdentifier();
159
160     ALWAYS_INLINE bool lastTokenWasRestrKeyword() const;
161     
162     ALWAYS_INLINE void skipWhitespace();
163
164     template <int shiftAmount> void internalShift();
165     template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType parseKeyword(JSTokenData*);
166     template <bool shouldBuildIdentifiers> ALWAYS_INLINE JSTokenType parseIdentifier(JSTokenData*, unsigned lexerFlags, bool strictMode);
167     template <bool shouldBuildIdentifiers> NEVER_INLINE JSTokenType parseIdentifierSlowCase(JSTokenData*, unsigned lexerFlags, bool strictMode);
168     enum StringParseResult {
169         StringParsedSuccessfully,
170         StringUnterminated,
171         StringCannotBeParsed
172     };
173     template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseString(JSTokenData*, bool strictMode);
174     template <bool shouldBuildStrings> NEVER_INLINE StringParseResult parseStringSlowCase(JSTokenData*, bool strictMode);
175
176
177     template <bool shouldBuildStrings, LexerEscapeParseMode escapeParseMode> ALWAYS_INLINE StringParseResult parseComplexEscape(bool strictMode, T stringQuoteCharacter);
178     ALWAYS_INLINE StringParseResult parseTemplateLiteral(JSTokenData*, RawStringsBuildMode);
179     
180     using NumberParseResult = Variant<double, const Identifier*>;
181     ALWAYS_INLINE NumberParseResult parseHex();
182     ALWAYS_INLINE Optional<NumberParseResult> parseBinary();
183     ALWAYS_INLINE Optional<NumberParseResult> parseOctal();
184     ALWAYS_INLINE Optional<NumberParseResult> parseDecimal();
185     ALWAYS_INLINE void parseNumberAfterDecimalPoint();
186     ALWAYS_INLINE bool parseNumberAfterExponentIndicator();
187     ALWAYS_INLINE bool parseMultilineComment();
188
189     ALWAYS_INLINE void parseCommentDirective();
190     ALWAYS_INLINE String parseCommentDirectiveValue();
191
192     template <unsigned length>
193     ALWAYS_INLINE bool consume(const char (&input)[length]);
194
195     void fillTokenInfo(JSToken*, JSTokenType, int lineNumber, int endOffset, int lineStartOffset, JSTextPosition endPosition);
196
197     static const size_t initialReadBufferCapacity = 32;
198
199     int m_lineNumber;
200     int m_lastLineNumber;
201
202     Vector<LChar> m_buffer8;
203     Vector<UChar> m_buffer16;
204     Vector<UChar> m_bufferForRawTemplateString16;
205     bool m_terminator;
206     int m_lastToken;
207
208     const SourceCode* m_source;
209     unsigned m_sourceOffset;
210     const T* m_code;
211     const T* m_codeStart;
212     const T* m_codeEnd;
213     const T* m_codeStartPlusOffset;
214     const T* m_lineStart;
215     JSTextPosition m_positionBeforeLastNewline;
216     JSTokenLocation m_lastTokenLocation;
217     bool m_isReparsingFunction;
218     bool m_atLineStart;
219     bool m_error;
220     String m_lexErrorMessage;
221
222     String m_sourceURLDirective;
223     String m_sourceMappingURLDirective;
224
225     T m_current;
226
227     IdentifierArena* m_arena;
228
229     VM* m_vm;
230     bool m_parsingBuiltinFunction;
231     JSParserScriptMode m_scriptMode;
232 };
233
234 template <>
235 ALWAYS_INLINE bool Lexer<LChar>::isWhiteSpace(LChar ch)
236 {
237     return ch == ' ' || ch == '\t' || ch == 0xB || ch == 0xC || ch == 0xA0;
238 }
239
240 template <>
241 ALWAYS_INLINE bool Lexer<UChar>::isWhiteSpace(UChar ch)
242 {
243     return (ch < 256) ? Lexer<LChar>::isWhiteSpace(static_cast<LChar>(ch)) : (u_charType(ch) == U_SPACE_SEPARATOR || ch == 0xFEFF);
244 }
245
246 template <>
247 ALWAYS_INLINE bool Lexer<LChar>::isLineTerminator(LChar ch)
248 {
249     return ch == '\r' || ch == '\n';
250 }
251
252 template <>
253 ALWAYS_INLINE bool Lexer<UChar>::isLineTerminator(UChar ch)
254 {
255     return ch == '\r' || ch == '\n' || (ch & ~1) == 0x2028;
256 }
257
258 template <typename T>
259 inline unsigned char Lexer<T>::convertHex(int c1, int c2)
260 {
261     return (toASCIIHexValue(c1) << 4) | toASCIIHexValue(c2);
262 }
263
264 template <typename T>
265 inline UChar Lexer<T>::convertUnicode(int c1, int c2, int c3, int c4)
266 {
267     return (convertHex(c1, c2) << 8) | convertHex(c3, c4);
268 }
269
270 template <typename T>
271 ALWAYS_INLINE const Identifier* Lexer<T>::makeIdentifier(const LChar* characters, size_t length)
272 {
273     return &m_arena->makeIdentifier(m_vm, characters, length);
274 }
275
276 template <typename T>
277 ALWAYS_INLINE const Identifier* Lexer<T>::makeIdentifier(const UChar* characters, size_t length)
278 {
279     return &m_arena->makeIdentifier(m_vm, characters, length);
280 }
281
282 template <>
283 ALWAYS_INLINE const Identifier* Lexer<LChar>::makeRightSizedIdentifier(const UChar* characters, size_t length, UChar)
284 {
285     return &m_arena->makeIdentifierLCharFromUChar(m_vm, characters, length);
286 }
287
288 template <>
289 ALWAYS_INLINE const Identifier* Lexer<UChar>::makeRightSizedIdentifier(const UChar* characters, size_t length, UChar orAllChars)
290 {
291     if (!(orAllChars & ~0xff))
292         return &m_arena->makeIdentifierLCharFromUChar(m_vm, characters, length);
293
294     return &m_arena->makeIdentifier(m_vm, characters, length);
295 }
296
297 template <typename T>
298 ALWAYS_INLINE const Identifier* Lexer<T>::makeEmptyIdentifier()
299 {
300     return &m_arena->makeEmptyIdentifier(m_vm);
301 }
302
303 template <>
304 ALWAYS_INLINE void Lexer<LChar>::setCodeStart(const StringView& sourceString)
305 {
306     ASSERT(sourceString.is8Bit());
307     m_codeStart = sourceString.characters8();
308 }
309
310 template <>
311 ALWAYS_INLINE void Lexer<UChar>::setCodeStart(const StringView& sourceString)
312 {
313     ASSERT(!sourceString.is8Bit());
314     m_codeStart = sourceString.characters16();
315 }
316
317 template <typename T>
318 ALWAYS_INLINE const Identifier* Lexer<T>::makeIdentifierLCharFromUChar(const UChar* characters, size_t length)
319 {
320     return &m_arena->makeIdentifierLCharFromUChar(m_vm, characters, length);
321 }
322
323 template <typename T>
324 ALWAYS_INLINE const Identifier* Lexer<T>::makeLCharIdentifier(const LChar* characters, size_t length)
325 {
326     return &m_arena->makeIdentifier(m_vm, characters, length);
327 }
328
329 template <typename T>
330 ALWAYS_INLINE const Identifier* Lexer<T>::makeLCharIdentifier(const UChar* characters, size_t length)
331 {
332     return &m_arena->makeIdentifierLCharFromUChar(m_vm, characters, length);
333 }
334
335 #if ASSERT_DISABLED
336 ALWAYS_INLINE bool isSafeBuiltinIdentifier(VM&, const Identifier*) { return true; }
337 #else
338 bool isSafeBuiltinIdentifier(VM&, const Identifier*);
339 #endif
340
341 template <typename T>
342 ALWAYS_INLINE JSTokenType Lexer<T>::lexExpectIdentifier(JSToken* tokenRecord, unsigned lexerFlags, bool strictMode)
343 {
344     JSTokenData* tokenData = &tokenRecord->m_data;
345     JSTokenLocation* tokenLocation = &tokenRecord->m_location;
346     ASSERT((lexerFlags & LexerFlagsIgnoreReservedWords));
347     const T* start = m_code;
348     const T* ptr = start;
349     const T* end = m_codeEnd;
350     JSTextPosition startPosition = currentPosition();
351     if (ptr >= end) {
352         ASSERT(ptr == end);
353         goto slowCase;
354     }
355     if (!WTF::isASCIIAlpha(*ptr))
356         goto slowCase;
357     ++ptr;
358     while (ptr < end) {
359         if (!WTF::isASCIIAlphanumeric(*ptr))
360             break;
361         ++ptr;
362     }
363
364     // Here's the shift
365     if (ptr < end) {
366         if ((!WTF::isASCII(*ptr)) || (*ptr == '\\') || (*ptr == '_') || (*ptr == '$'))
367             goto slowCase;
368         m_current = *ptr;
369     } else
370         m_current = 0;
371
372     m_code = ptr;
373     ASSERT(currentOffset() >= currentLineStartOffset());
374
375     // Create the identifier if needed
376     if (lexerFlags & LexexFlagsDontBuildKeywords
377 #if !ASSERT_DISABLED
378         && !m_parsingBuiltinFunction
379 #endif
380         )
381         tokenData->ident = 0;
382     else
383         tokenData->ident = makeLCharIdentifier(start, ptr - start);
384
385     tokenLocation->line = m_lineNumber;
386     tokenLocation->lineStartOffset = currentLineStartOffset();
387     tokenLocation->startOffset = offsetFromSourcePtr(start);
388     tokenLocation->endOffset = currentOffset();
389     ASSERT(tokenLocation->startOffset >= tokenLocation->lineStartOffset);
390     tokenRecord->m_startPosition = startPosition;
391     tokenRecord->m_endPosition = currentPosition();
392 #if !ASSERT_DISABLED
393     if (m_parsingBuiltinFunction) {
394         if (!isSafeBuiltinIdentifier(*m_vm, tokenData->ident))
395             return ERRORTOK;
396     }
397 #endif
398
399     m_lastToken = IDENT;
400     return IDENT;
401     
402 slowCase:
403     return lex(tokenRecord, lexerFlags, strictMode);
404 }
405
406 } // namespace JSC