mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
* This patch increase octane score +2~3%(x86 intel) * We suggest jobs number in third party build even if failed to find cpu core count Using -j options cause system fail some legacy system Signed-off-by: seonghyun kim <sh8281.kim@samsung.com>
6690 lines
273 KiB
C++
6690 lines
273 KiB
C++
/*
|
|
Copyright JS Foundation and other contributors, https://js.foundation/
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
/*
|
|
* Copyright (c) 2016-present Samsung Electronics Co., Ltd
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
|
* USA
|
|
*/
|
|
|
|
#include "Escargot.h"
|
|
#include "esprima.h"
|
|
#include "interpreter/ByteCode.h"
|
|
#include "parser/ast/AST.h"
|
|
#include "parser/CodeBlock.h"
|
|
#include "double-conversion.h"
|
|
#include "ieee.h"
|
|
|
|
#include "wtfbridge.h"
|
|
|
|
using namespace JSC::Yarr;
|
|
|
|
namespace Escargot {
|
|
|
|
namespace esprima {
|
|
|
|
enum Token {
|
|
BooleanLiteralToken = 1,
|
|
EOFToken = 2,
|
|
IdentifierToken = 3,
|
|
KeywordToken = 4,
|
|
NullLiteralToken = 5,
|
|
NumericLiteralToken = 6,
|
|
PunctuatorToken = 7,
|
|
StringLiteralToken = 8,
|
|
RegularExpressionToken = 9,
|
|
TemplateToken = 10
|
|
};
|
|
|
|
enum PlaceHolders {
|
|
ArrowParameterPlaceHolder
|
|
};
|
|
|
|
enum PunctuatorsKind {
|
|
LeftParenthesis,
|
|
RightParenthesis,
|
|
LeftBrace,
|
|
RightBrace,
|
|
Period,
|
|
PeriodPeriodPeriod,
|
|
Comma,
|
|
Colon,
|
|
SemiColon,
|
|
LeftSquareBracket,
|
|
RightSquareBracket,
|
|
GuessMark,
|
|
Wave,
|
|
UnsignedRightShift,
|
|
RightShift,
|
|
LeftShift,
|
|
Plus,
|
|
Minus,
|
|
Multiply,
|
|
Divide,
|
|
Mod,
|
|
ExclamationMark,
|
|
StrictEqual,
|
|
NotStrictEqual,
|
|
Equal,
|
|
NotEqual,
|
|
LogicalAnd,
|
|
LogicalOr,
|
|
PlusPlus,
|
|
MinusMinus,
|
|
BitwiseAnd,
|
|
BitwiseOr,
|
|
BitwiseXor,
|
|
LeftInequality,
|
|
RightInequality,
|
|
InPunctuator,
|
|
InstanceOfPunctuator,
|
|
|
|
Substitution,
|
|
UnsignedRightShiftEqual,
|
|
RightShiftEqual,
|
|
LeftShiftEqual,
|
|
PlusEqual,
|
|
MinusEqual,
|
|
MultiplyEqual,
|
|
DivideEqual,
|
|
ModEqual,
|
|
// ExclamationMarkEqual,
|
|
BitwiseAndEqual,
|
|
BitwiseOrEqual,
|
|
BitwiseXorEqual,
|
|
LeftInequalityEqual,
|
|
RightInequalityEqual,
|
|
SubstitutionEnd,
|
|
|
|
Arrow,
|
|
PunctuatorsKindEnd,
|
|
};
|
|
|
|
enum KeywordKind {
|
|
NotKeyword,
|
|
If,
|
|
In,
|
|
Do,
|
|
Var,
|
|
For,
|
|
New,
|
|
Try,
|
|
This,
|
|
Else,
|
|
Case,
|
|
Void,
|
|
With,
|
|
Enum,
|
|
Await,
|
|
While,
|
|
Break,
|
|
Catch,
|
|
Throw,
|
|
Const,
|
|
Class,
|
|
Super,
|
|
Return,
|
|
Typeof,
|
|
Delete,
|
|
Switch,
|
|
Export,
|
|
Import,
|
|
Default,
|
|
Finally,
|
|
Extends,
|
|
Function,
|
|
Continue,
|
|
Debugger,
|
|
InstanceofKeyword,
|
|
StrictModeReservedWord,
|
|
Implements,
|
|
Interface,
|
|
Package,
|
|
Private,
|
|
Protected,
|
|
Public,
|
|
Static,
|
|
Yield,
|
|
Let,
|
|
KeywordKindEnd
|
|
};
|
|
|
|
ALWAYS_INLINE bool isDecimalDigit(char16_t ch)
|
|
{
|
|
return (ch >= '0' && ch <= '9'); // 0..9
|
|
}
|
|
|
|
ALWAYS_INLINE bool isHexDigit(char16_t ch)
|
|
{
|
|
return isDecimalDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
|
|
}
|
|
|
|
ALWAYS_INLINE bool isOctalDigit(char16_t ch)
|
|
{
|
|
return (ch >= '0' && ch < '8'); // 0..7
|
|
}
|
|
|
|
ALWAYS_INLINE char16_t octalValue(char16_t ch)
|
|
{
|
|
ASSERT(isOctalDigit(ch));
|
|
return ch - '0';
|
|
}
|
|
|
|
ALWAYS_INLINE uint8_t toHexNumericValue(char16_t ch)
|
|
{
|
|
return ch < 'A' ? ch - '0' : (ch - 'A' + 10) & 0xF;
|
|
}
|
|
|
|
struct ParserCharPiece {
|
|
char16_t data[3];
|
|
size_t length;
|
|
|
|
ParserCharPiece(const char16_t a)
|
|
{
|
|
data[0] = a;
|
|
data[1] = 0;
|
|
length = 1;
|
|
}
|
|
|
|
ParserCharPiece(const char32_t a)
|
|
{
|
|
if (a < 0x10000) {
|
|
data[0] = a;
|
|
data[1] = 0;
|
|
length = 1;
|
|
} else {
|
|
data[0] = (char16_t)(0xD800 + ((a - 0x10000) >> 10));
|
|
data[1] = (char16_t)(0xDC00 + ((a - 0x10000) & 1023));
|
|
data[2] = 0;
|
|
length = 2;
|
|
}
|
|
}
|
|
|
|
ParserCharPiece(const char16_t a, const char16_t b)
|
|
{
|
|
data[0] = a;
|
|
data[1] = b;
|
|
data[2] = 0;
|
|
length = 2;
|
|
}
|
|
};
|
|
|
|
// ECMA-262 11.6 Identifier Names and Identifiers
|
|
ALWAYS_INLINE ParserCharPiece fromCodePoint(char32_t cp)
|
|
{
|
|
if (cp < 0x10000) {
|
|
return ParserCharPiece((char16_t)cp);
|
|
} else {
|
|
return ParserCharPiece((char16_t)(0xD800 + ((cp - 0x10000) >> 10)), (char16_t)(0xDC00 + ((cp - 0x10000) & 1023)));
|
|
}
|
|
}
|
|
|
|
extern bool isIdentifierPartData[128];
|
|
extern bool isIdentifierStartData[128];
|
|
|
|
NEVER_INLINE bool isIdentifierPartSlow(char32_t ch)
|
|
{
|
|
return (ch == 0xAA) || (ch == 0xB5) || (ch == 0xB7) || (ch == 0xBA) || (0xC0 <= ch && ch <= 0xD6) || (0xD8 <= ch && ch <= 0xF6) || (0xF8 <= ch && ch <= 0x02C1) || (0x02C6 <= ch && ch <= 0x02D1) || (0x02E0 <= ch && ch <= 0x02E4) || (ch == 0x02EC) || (ch == 0x02EE) || (0x0300 <= ch && ch <= 0x0374) || (ch == 0x0376) || (ch == 0x0377) || (0x037A <= ch && ch <= 0x037D) || (ch == 0x037F) || (0x0386 <= ch && ch <= 0x038A) || (ch == 0x038C) || (0x038E <= ch && ch <= 0x03A1) || (0x03A3 <= ch && ch <= 0x03F5) || (0x03F7 <= ch && ch <= 0x0481) || (0x0483 <= ch && ch <= 0x0487) || (0x048A <= ch && ch <= 0x052F) || (0x0531 <= ch && ch <= 0x0556) || (ch == 0x0559) || (0x0561 <= ch && ch <= 0x0587) || (0x0591 <= ch && ch <= 0x05BD) || (ch == 0x05BF) || (ch == 0x05C1) || (ch == 0x05C2) || (ch == 0x05C4) || (ch == 0x05C5) || (ch == 0x05C7) || (0x05D0 <= ch && ch <= 0x05EA) || (0x05F0 <= ch && ch <= 0x05F2) || (0x0610 <= ch && ch <= 0x061A) || (0x0620 <= ch && ch <= 0x0669) || (0x066E <= ch && ch <= 0x06D3) || (0x06D5 <= ch && ch <= 0x06DC) || (0x06DF <= ch && ch <= 0x06E8) || (0x06EA <= ch && ch <= 0x06FC) || (ch == 0x06FF) || (0x0710 <= ch && ch <= 0x074A) || (0x074D <= ch && ch <= 0x07B1) || (0x07C0 <= ch && ch <= 0x07F5) || (ch == 0x07FA) || (0x0800 <= ch && ch <= 0x082D) || (0x0840 <= ch && ch <= 0x085B) || (0x08A0 <= ch && ch <= 0x08B2) || (0x08E4 <= ch && ch <= 0x0963) || (0x0966 <= ch && ch <= 0x096F) || (0x0971 <= ch && ch <= 0x0983) || (0x0985 <= ch && ch <= 0x098C) || (ch == 0x098F) || (ch == 0x0990) || (0x0993 <= ch && ch <= 0x09A8) || (0x09AA <= ch && ch <= 0x09B0) || (ch == 0x09B2) || (0x09B6 <= ch && ch <= 0x09B9) || (0x09BC <= ch && ch <= 0x09C4) || (ch == 0x09C7) || (ch == 0x09C8) || (0x09CB <= ch && ch <= 0x09CE) || (ch == 0x09D7) || (ch == 0x09DC) || (ch == 0x09DD) || (0x09DF <= ch && ch <= 0x09E3) || (0x09E6 <= ch && ch <= 0x09F1) || (0x0A01 <= ch && ch <= 0x0A03) || (0x0A05 <= ch && ch <= 0x0A0A) || (ch == 0x0A0F) || (ch == 0x0A10) || (0x0A13 <= ch && ch <= 0x0A28) || (0x0A2A <= ch && ch <= 0x0A30) || (ch == 0x0A32) || (ch == 0x0A33) || (ch == 0x0A35) || (ch == 0x0A36) || (ch == 0x0A38) || (ch == 0x0A39) || (ch == 0x0A3C) || (0x0A3E <= ch && ch <= 0x0A42) || (ch == 0x0A47) || (ch == 0x0A48) || (0x0A4B <= ch && ch <= 0x0A4D) || (ch == 0x0A51) || (0x0A59 <= ch && ch <= 0x0A5C) || (ch == 0x0A5E) || (0x0A66 <= ch && ch <= 0x0A75) || (0x0A81 <= ch && ch <= 0x0A83) || (0x0A85 <= ch && ch <= 0x0A8D) || (0x0A8F <= ch && ch <= 0x0A91) || (0x0A93 <= ch && ch <= 0x0AA8) || (0x0AAA <= ch && ch <= 0x0AB0) || (ch == 0x0AB2) || (ch == 0x0AB3) || (0x0AB5 <= ch && ch <= 0x0AB9) || (0x0ABC <= ch && ch <= 0x0AC5) || (0x0AC7 <= ch && ch <= 0x0AC9) || (0x0ACB <= ch && ch <= 0x0ACD) || (ch == 0x0AD0) || (0x0AE0 <= ch && ch <= 0x0AE3) || (0x0AE6 <= ch && ch <= 0x0AEF) || (0x0B01 <= ch && ch <= 0x0B03) || (0x0B05 <= ch && ch <= 0x0B0C) || (ch == 0x0B0F) || (ch == 0x0B10) || (0x0B13 <= ch && ch <= 0x0B28) || (0x0B2A <= ch && ch <= 0x0B30) || (ch == 0x0B32) || (ch == 0x0B33) || (0x0B35 <= ch && ch <= 0x0B39) || (0x0B3C <= ch && ch <= 0x0B44) || (ch == 0x0B47) || (ch == 0x0B48) || (0x0B4B <= ch && ch <= 0x0B4D) || (ch == 0x0B56) || (ch == 0x0B57) || (ch == 0x0B5C) || (ch == 0x0B5D) || (0x0B5F <= ch && ch <= 0x0B63) || (0x0B66 <= ch && ch <= 0x0B6F) || (ch == 0x0B71) || (ch == 0x0B82) || (ch == 0x0B83) || (0x0B85 <= ch && ch <= 0x0B8A) || (0x0B8E <= ch && ch <= 0x0B90) || (0x0B92 <= ch && ch <= 0x0B95) || (ch == 0x0B99) || (ch == 0x0B9A) || (ch == 0x0B9C) || (ch == 0x0B9E) || (ch == 0x0B9F) || (ch == 0x0BA3) || (ch == 0x0BA4) || (0x0BA8 <= ch && ch <= 0x0BAA) || (0x0BAE <= ch && ch <= 0x0BB9) || (0x0BBE <= ch && ch <= 0x0BC2) || (0x0BC6 <= ch && ch <= 0x0BC8) || (0x0BCA <= ch && ch <= 0x0BCD) || (ch == 0x0BD0) || (ch == 0x0BD7) || (0x0BE6 <= ch && ch <= 0x0BEF) || (0x0C00 <= ch && ch <= 0x0C03) || (0x0C05 <= ch && ch <= 0x0C0C) || (0x0C0E <= ch && ch <= 0x0C10) || (0x0C12 <= ch && ch <= 0x0C28) || (0x0C2A <= ch && ch <= 0x0C39) || (0x0C3D <= ch && ch <= 0x0C44) || (0x0C46 <= ch && ch <= 0x0C48) || (0x0C4A <= ch && ch <= 0x0C4D) || (ch == 0x0C55) || (ch == 0x0C56) || (ch == 0x0C58) || (ch == 0x0C59) || (0x0C60 <= ch && ch <= 0x0C63) || (0x0C66 <= ch && ch <= 0x0C6F) || (0x0C81 <= ch && ch <= 0x0C83) || (0x0C85 <= ch && ch <= 0x0C8C) || (0x0C8E <= ch && ch <= 0x0C90) || (0x0C92 <= ch && ch <= 0x0CA8) || (0x0CAA <= ch && ch <= 0x0CB3) || (0x0CB5 <= ch && ch <= 0x0CB9) || (0x0CBC <= ch && ch <= 0x0CC4) || (0x0CC6 <= ch && ch <= 0x0CC8) || (0x0CCA <= ch && ch <= 0x0CCD) || (ch == 0x0CD5) || (ch == 0x0CD6) || (ch == 0x0CDE) || (0x0CE0 <= ch && ch <= 0x0CE3) || (0x0CE6 <= ch && ch <= 0x0CEF) || (ch == 0x0CF1) || (ch == 0x0CF2) || (0x0D01 <= ch && ch <= 0x0D03) || (0x0D05 <= ch && ch <= 0x0D0C) || (0x0D0E <= ch && ch <= 0x0D10) || (0x0D12 <= ch && ch <= 0x0D3A) || (0x0D3D <= ch && ch <= 0x0D44) || (0x0D46 <= ch && ch <= 0x0D48) || (0x0D4A <= ch && ch <= 0x0D4E) || (ch == 0x0D57) || (0x0D60 <= ch && ch <= 0x0D63) || (0x0D66 <= ch && ch <= 0x0D6F) || (0x0D7A <= ch && ch <= 0x0D7F) || (ch == 0x0D82) || (ch == 0x0D83) || (0x0D85 <= ch && ch <= 0x0D96) || (0x0D9A <= ch && ch <= 0x0DB1) || (0x0DB3 <= ch && ch <= 0x0DBB) || (ch == 0x0DBD) || (0x0DC0 <= ch && ch <= 0x0DC6) || (ch == 0x0DCA) || (0x0DCF <= ch && ch <= 0x0DD4) || (ch == 0x0DD6) || (0x0DD8 <= ch && ch <= 0x0DDF) || (0x0DE6 <= ch && ch <= 0x0DEF) || (ch == 0x0DF2) || (ch == 0x0DF3) || (0x0E01 <= ch && ch <= 0x0E3A) || (0x0E40 <= ch && ch <= 0x0E4E) || (0x0E50 <= ch && ch <= 0x0E59) || (ch == 0x0E81) || (ch == 0x0E82) || (ch == 0x0E84) || (ch == 0x0E87) || (ch == 0x0E88) || (ch == 0x0E8A) || (ch == 0x0E8D) || (0x0E94 <= ch && ch <= 0x0E97) || (0x0E99 <= ch && ch <= 0x0E9F) || (0x0EA1 <= ch && ch <= 0x0EA3) || (ch == 0x0EA5) || (ch == 0x0EA7) || (ch == 0x0EAA) || (ch == 0x0EAB) || (0x0EAD <= ch && ch <= 0x0EB9) || (0x0EBB <= ch && ch <= 0x0EBD) || (0x0EC0 <= ch && ch <= 0x0EC4) || (ch == 0x0EC6) || (0x0EC8 <= ch && ch <= 0x0ECD) || (0x0ED0 <= ch && ch <= 0x0ED9) || (0x0EDC <= ch && ch <= 0x0EDF) || (ch == 0x0F00) || (ch == 0x0F18) || (ch == 0x0F19) || (0x0F20 <= ch && ch <= 0x0F29) || (ch == 0x0F35) || (ch == 0x0F37) || (ch == 0x0F39) || (0x0F3E <= ch && ch <= 0x0F47) || (0x0F49 <= ch && ch <= 0x0F6C) || (0x0F71 <= ch && ch <= 0x0F84) || (0x0F86 <= ch && ch <= 0x0F97) || (0x0F99 <= ch && ch <= 0x0FBC) || (ch == 0x0FC6) || (0x1000 <= ch && ch <= 0x1049) || (0x1050 <= ch && ch <= 0x109D) || (0x10A0 <= ch && ch <= 0x10C5) || (ch == 0x10C7) || (ch == 0x10CD) || (0x10D0 <= ch && ch <= 0x10FA) || (0x10FC <= ch && ch <= 0x1248) || (0x124A <= ch && ch <= 0x124D) || (0x1250 <= ch && ch <= 0x1256) || (ch == 0x1258) || (0x125A <= ch && ch <= 0x125D) || (0x1260 <= ch && ch <= 0x1288) || (0x128A <= ch && ch <= 0x128D) || (0x1290 <= ch && ch <= 0x12B0) || (0x12B2 <= ch && ch <= 0x12B5) || (0x12B8 <= ch && ch <= 0x12BE) || (ch == 0x12C0) || (0x12C2 <= ch && ch <= 0x12C5) || (0x12C8 <= ch && ch <= 0x12D6) || (0x12D8 <= ch && ch <= 0x1310) || (0x1312 <= ch && ch <= 0x1315) || (0x1318 <= ch && ch <= 0x135A) || (0x135D <= ch && ch <= 0x135F) || (0x1369 <= ch && ch <= 0x1371) || (0x1380 <= ch && ch <= 0x138F) || (0x13A0 <= ch && ch <= 0x13F4) || (0x1401 <= ch && ch <= 0x166C) || (0x166F <= ch && ch <= 0x167F) || (0x1681 <= ch && ch <= 0x169A) || (0x16A0 <= ch && ch <= 0x16EA) || (0x16EE <= ch && ch <= 0x16F8) || (0x1700 <= ch && ch <= 0x170C) || (0x170E <= ch && ch <= 0x1714) || (0x1720 <= ch && ch <= 0x1734) || (0x1740 <= ch && ch <= 0x1753) || (0x1760 <= ch && ch <= 0x176C) || (0x176E <= ch && ch <= 0x1770) || (ch == 0x1772) || (ch == 0x1773) || (0x1780 <= ch && ch <= 0x17D3) || (ch == 0x17D7) || (ch == 0x17DC) || (ch == 0x17DD) || (0x17E0 <= ch && ch <= 0x17E9) || (0x180B <= ch && ch <= 0x180D) || (0x1810 <= ch && ch <= 0x1819) || (0x1820 <= ch && ch <= 0x1877) || (0x1880 <= ch && ch <= 0x18AA) || (0x18B0 <= ch && ch <= 0x18F5) || (0x1900 <= ch && ch <= 0x191E) || (0x1920 <= ch && ch <= 0x192B) || (0x1930 <= ch && ch <= 0x193B) || (0x1946 <= ch && ch <= 0x196D) || (0x1970 <= ch && ch <= 0x1974) || (0x1980 <= ch && ch <= 0x19AB) || (0x19B0 <= ch && ch <= 0x19C9) || (0x19D0 <= ch && ch <= 0x19DA) || (0x1A00 <= ch && ch <= 0x1A1B) || (0x1A20 <= ch && ch <= 0x1A5E) || (0x1A60 <= ch && ch <= 0x1A7C) || (0x1A7F <= ch && ch <= 0x1A89) || (0x1A90 <= ch && ch <= 0x1A99) || (ch == 0x1AA7) || (0x1AB0 <= ch && ch <= 0x1ABD) || (0x1B00 <= ch && ch <= 0x1B4B) || (0x1B50 <= ch && ch <= 0x1B59) || (0x1B6B <= ch && ch <= 0x1B73) || (0x1B80 <= ch && ch <= 0x1BF3) || (0x1C00 <= ch && ch <= 0x1C37) || (0x1C40 <= ch && ch <= 0x1C49) || (0x1C4D <= ch && ch <= 0x1C7D) || (0x1CD0 <= ch && ch <= 0x1CD2) || (0x1CD4 <= ch && ch <= 0x1CF6) || (ch == 0x1CF8) || (ch == 0x1CF9) || (0x1D00 <= ch && ch <= 0x1DF5) || (0x1DFC <= ch && ch <= 0x1F15) || (0x1F18 <= ch && ch <= 0x1F1D) || (0x1F20 <= ch && ch <= 0x1F45) || (0x1F48 <= ch && ch <= 0x1F4D) || (0x1F50 <= ch && ch <= 0x1F57) || (ch == 0x1F59) || (ch == 0x1F5B) || (ch == 0x1F5D) || (0x1F5F <= ch && ch <= 0x1F7D) || (0x1F80 <= ch && ch <= 0x1FB4) || (0x1FB6 <= ch && ch <= 0x1FBC) || (ch == 0x1FBE) || (0x1FC2 <= ch && ch <= 0x1FC4) || (0x1FC6 <= ch && ch <= 0x1FCC) || (0x1FD0 <= ch && ch <= 0x1FD3) || (0x1FD6 <= ch && ch <= 0x1FDB) || (0x1FE0 <= ch && ch <= 0x1FEC) || (0x1FF2 <= ch && ch <= 0x1FF4) || (0x1FF6 <= ch && ch <= 0x1FFC) || (ch == 0x200C) || (ch == 0x200D) || (ch == 0x203F) || (ch == 0x2040) || (ch == 0x2054) || (ch == 0x2071) || (ch == 0x207F) || (0x2090 <= ch && ch <= 0x209C) || (0x20D0 <= ch && ch <= 0x20DC) || (ch == 0x20E1) || (0x20E5 <= ch && ch <= 0x20F0) || (ch == 0x2102) || (ch == 0x2107) || (0x210A <= ch && ch <= 0x2113) || (ch == 0x2115) || (0x2118 <= ch && ch <= 0x211D) || (ch == 0x2124) || (ch == 0x2126) || (ch == 0x2128) || (0x212A <= ch && ch <= 0x2139) || (0x213C <= ch && ch <= 0x213F) || (0x2145 <= ch && ch <= 0x2149) || (ch == 0x214E) || (0x2160 <= ch && ch <= 0x2188) || (0x2C00 <= ch && ch <= 0x2C2E) || (0x2C30 <= ch && ch <= 0x2C5E) || (0x2C60 <= ch && ch <= 0x2CE4) || (0x2CEB <= ch && ch <= 0x2CF3) || (0x2D00 <= ch && ch <= 0x2D25) || (ch == 0x2D27) || (ch == 0x2D2D) || (0x2D30 <= ch && ch <= 0x2D67) || (ch == 0x2D6F) || (0x2D7F <= ch && ch <= 0x2D96) || (0x2DA0 <= ch && ch <= 0x2DA6) || (0x2DA8 <= ch && ch <= 0x2DAE) || (0x2DB0 <= ch && ch <= 0x2DB6) || (0x2DB8 <= ch && ch <= 0x2DBE) || (0x2DC0 <= ch && ch <= 0x2DC6) || (0x2DC8 <= ch && ch <= 0x2DCE) || (0x2DD0 <= ch && ch <= 0x2DD6) || (0x2DD8 <= ch && ch <= 0x2DDE) || (0x2DE0 <= ch && ch <= 0x2DFF) || (0x3005 <= ch && ch <= 0x3007) || (0x3021 <= ch && ch <= 0x302F) || (0x3031 <= ch && ch <= 0x3035) || (0x3038 <= ch && ch <= 0x303C) || (0x3041 <= ch && ch <= 0x3096) || (0x3099 <= ch && ch <= 0x309F) || (0x30A1 <= ch && ch <= 0x30FA) || (0x30FC <= ch && ch <= 0x30FF) || (0x3105 <= ch && ch <= 0x312D) || (0x3131 <= ch && ch <= 0x318E) || (0x31A0 <= ch && ch <= 0x31BA) || (0x31F0 <= ch && ch <= 0x31FF) || (0x3400 <= ch && ch <= 0x4DB5) || (0x4E00 <= ch && ch <= 0x9FCC) || (0xA000 <= ch && ch <= 0xA48C) || (0xA4D0 <= ch && ch <= 0xA4FD) || (0xA500 <= ch && ch <= 0xA60C) || (0xA610 <= ch && ch <= 0xA62B) || (0xA640 <= ch && ch <= 0xA66F) || (0xA674 <= ch && ch <= 0xA67D) || (0xA67F <= ch && ch <= 0xA69D) || (0xA69F <= ch && ch <= 0xA6F1) || (0xA717 <= ch && ch <= 0xA71F) || (0xA722 <= ch && ch <= 0xA788) || (0xA78B <= ch && ch <= 0xA78E) || (0xA790 <= ch && ch <= 0xA7AD) || (ch == 0xA7B0) || (ch == 0xA7B1) || (0xA7F7 <= ch && ch <= 0xA827) || (0xA840 <= ch && ch <= 0xA873) || (0xA880 <= ch && ch <= 0xA8C4) || (0xA8D0 <= ch && ch <= 0xA8D9) || (0xA8E0 <= ch && ch <= 0xA8F7) || (ch == 0xA8FB) || (0xA900 <= ch && ch <= 0xA92D) || (0xA930 <= ch && ch <= 0xA953) || (0xA960 <= ch && ch <= 0xA97C) || (0xA980 <= ch && ch <= 0xA9C0) || (0xA9CF <= ch && ch <= 0xA9D9) || (0xA9E0 <= ch && ch <= 0xA9FE) || (0xAA00 <= ch && ch <= 0xAA36) || (0xAA40 <= ch && ch <= 0xAA4D) || (0xAA50 <= ch && ch <= 0xAA59) || (0xAA60 <= ch && ch <= 0xAA76) || (0xAA7A <= ch && ch <= 0xAAC2) || (0xAADB <= ch && ch <= 0xAADD) || (0xAAE0 <= ch && ch <= 0xAAEF) || (0xAAF2 <= ch && ch <= 0xAAF6) || (0xAB01 <= ch && ch <= 0xAB06) || (0xAB09 <= ch && ch <= 0xAB0E) || (0xAB11 <= ch && ch <= 0xAB16) || (0xAB20 <= ch && ch <= 0xAB26) || (0xAB28 <= ch && ch <= 0xAB2E) || (0xAB30 <= ch && ch <= 0xAB5A) || (0xAB5C <= ch && ch <= 0xAB5F) || (ch == 0xAB64) || (ch == 0xAB65) || (0xABC0 <= ch && ch <= 0xABEA) || (ch == 0xABEC) || (ch == 0xABED) || (0xABF0 <= ch && ch <= 0xABF9) || (0xAC00 <= ch && ch <= 0xD7A3) || (0xD7B0 <= ch && ch <= 0xD7C6) || (0xD7CB <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFA6D) || (0xFA70 <= ch && ch <= 0xFAD9) || (0xFB00 <= ch && ch <= 0xFB06) || (0xFB13 <= ch && ch <= 0xFB17) || (0xFB1D <= ch && ch <= 0xFB28) || (0xFB2A <= ch && ch <= 0xFB36) || (0xFB38 <= ch && ch <= 0xFB3C) || (ch == 0xFB3E) || (ch == 0xFB40) || (ch == 0xFB41) || (ch == 0xFB43) || (ch == 0xFB44) || (0xFB46 <= ch && ch <= 0xFBB1) || (0xFBD3 <= ch && ch <= 0xFD3D) || (0xFD50 <= ch && ch <= 0xFD8F) || (0xFD92 <= ch && ch <= 0xFDC7) || (0xFDF0 <= ch && ch <= 0xFDFB) || (0xFE00 <= ch && ch <= 0xFE0F) || (0xFE20 <= ch && ch <= 0xFE2D) || (ch == 0xFE33) || (ch == 0xFE34) || (0xFE4D <= ch && ch <= 0xFE4F) || (0xFE70 <= ch && ch <= 0xFE74) || (0xFE76 <= ch && ch <= 0xFEFC) || (0xFF10 <= ch && ch <= 0xFF19) || (0xFF21 <= ch && ch <= 0xFF3A) || (ch == 0xFF3F) || (0xFF41 <= ch && ch <= 0xFF5A) || (0xFF66 <= ch && ch <= 0xFFBE) || (0xFFC2 <= ch && ch <= 0xFFC7) || (0xFFCA <= ch && ch <= 0xFFCF) || (0xFFD2 <= ch && ch <= 0xFFD7) || (0xFFDA <= ch && ch <= 0xFFDC);
|
|
}
|
|
|
|
ALWAYS_INLINE bool isIdentifierPart(char32_t ch)
|
|
{
|
|
if (LIKELY(ch < 128)) {
|
|
return isIdentifierPartData[ch];
|
|
} else {
|
|
return isIdentifierPartSlow(ch);
|
|
}
|
|
}
|
|
|
|
bool isIdentifierStartSlow(char32_t ch)
|
|
{
|
|
return (ch == 0xAA) || (ch == 0xB5) || (ch == 0xB7) || (ch == 0xBA) || (0xC0 <= ch && ch <= 0xD6) || (0xD8 <= ch && ch <= 0xF6) || (0xF8 <= ch && ch <= 0x02C1) || (0x02C6 <= ch && ch <= 0x02D1) || (0x02E0 <= ch && ch <= 0x02E4) || (ch == 0x02EC) || (ch == 0x02EE) || (0x0300 <= ch && ch <= 0x0374) || (ch == 0x0376) || (ch == 0x0377) || (0x037A <= ch && ch <= 0x037D) || (ch == 0x037F) || (0x0386 <= ch && ch <= 0x038A) || (ch == 0x038C) || (0x038E <= ch && ch <= 0x03A1) || (0x03A3 <= ch && ch <= 0x03F5) || (0x03F7 <= ch && ch <= 0x0481) || (0x0483 <= ch && ch <= 0x0487) || (0x048A <= ch && ch <= 0x052F) || (0x0531 <= ch && ch <= 0x0556) || (ch == 0x0559) || (0x0561 <= ch && ch <= 0x0587) || (0x0591 <= ch && ch <= 0x05BD) || (ch == 0x05BF) || (ch == 0x05C1) || (ch == 0x05C2) || (ch == 0x05C4) || (ch == 0x05C5) || (ch == 0x05C7) || (0x05D0 <= ch && ch <= 0x05EA) || (0x05F0 <= ch && ch <= 0x05F2) || (0x0610 <= ch && ch <= 0x061A) || (0x0620 <= ch && ch <= 0x0669) || (0x066E <= ch && ch <= 0x06D3) || (0x06D5 <= ch && ch <= 0x06DC) || (0x06DF <= ch && ch <= 0x06E8) || (0x06EA <= ch && ch <= 0x06FC) || (ch == 0x06FF) || (0x0710 <= ch && ch <= 0x074A) || (0x074D <= ch && ch <= 0x07B1) || (0x07C0 <= ch && ch <= 0x07F5) || (ch == 0x07FA) || (0x0800 <= ch && ch <= 0x082D) || (0x0840 <= ch && ch <= 0x085B) || (0x08A0 <= ch && ch <= 0x08B2) || (0x08E4 <= ch && ch <= 0x0963) || (0x0966 <= ch && ch <= 0x096F) || (0x0971 <= ch && ch <= 0x0983) || (0x0985 <= ch && ch <= 0x098C) || (ch == 0x098F) || (ch == 0x0990) || (0x0993 <= ch && ch <= 0x09A8) || (0x09AA <= ch && ch <= 0x09B0) || (ch == 0x09B2) || (0x09B6 <= ch && ch <= 0x09B9) || (0x09BC <= ch && ch <= 0x09C4) || (ch == 0x09C7) || (ch == 0x09C8) || (0x09CB <= ch && ch <= 0x09CE) || (ch == 0x09D7) || (ch == 0x09DC) || (ch == 0x09DD) || (0x09DF <= ch && ch <= 0x09E3) || (0x09E6 <= ch && ch <= 0x09F1) || (0x0A01 <= ch && ch <= 0x0A03) || (0x0A05 <= ch && ch <= 0x0A0A) || (ch == 0x0A0F) || (ch == 0x0A10) || (0x0A13 <= ch && ch <= 0x0A28) || (0x0A2A <= ch && ch <= 0x0A30) || (ch == 0x0A32) || (ch == 0x0A33) || (ch == 0x0A35) || (ch == 0x0A36) || (ch == 0x0A38) || (ch == 0x0A39) || (ch == 0x0A3C) || (0x0A3E <= ch && ch <= 0x0A42) || (ch == 0x0A47) || (ch == 0x0A48) || (0x0A4B <= ch && ch <= 0x0A4D) || (ch == 0x0A51) || (0x0A59 <= ch && ch <= 0x0A5C) || (ch == 0x0A5E) || (0x0A66 <= ch && ch <= 0x0A75) || (0x0A81 <= ch && ch <= 0x0A83) || (0x0A85 <= ch && ch <= 0x0A8D) || (0x0A8F <= ch && ch <= 0x0A91) || (0x0A93 <= ch && ch <= 0x0AA8) || (0x0AAA <= ch && ch <= 0x0AB0) || (ch == 0x0AB2) || (ch == 0x0AB3) || (0x0AB5 <= ch && ch <= 0x0AB9) || (0x0ABC <= ch && ch <= 0x0AC5) || (0x0AC7 <= ch && ch <= 0x0AC9) || (0x0ACB <= ch && ch <= 0x0ACD) || (ch == 0x0AD0) || (0x0AE0 <= ch && ch <= 0x0AE3) || (0x0AE6 <= ch && ch <= 0x0AEF) || (0x0B01 <= ch && ch <= 0x0B03) || (0x0B05 <= ch && ch <= 0x0B0C) || (ch == 0x0B0F) || (ch == 0x0B10) || (0x0B13 <= ch && ch <= 0x0B28) || (0x0B2A <= ch && ch <= 0x0B30) || (ch == 0x0B32) || (ch == 0x0B33) || (0x0B35 <= ch && ch <= 0x0B39) || (0x0B3C <= ch && ch <= 0x0B44) || (ch == 0x0B47) || (ch == 0x0B48) || (0x0B4B <= ch && ch <= 0x0B4D) || (ch == 0x0B56) || (ch == 0x0B57) || (ch == 0x0B5C) || (ch == 0x0B5D) || (0x0B5F <= ch && ch <= 0x0B63) || (0x0B66 <= ch && ch <= 0x0B6F) || (ch == 0x0B71) || (ch == 0x0B82) || (ch == 0x0B83) || (0x0B85 <= ch && ch <= 0x0B8A) || (0x0B8E <= ch && ch <= 0x0B90) || (0x0B92 <= ch && ch <= 0x0B95) || (ch == 0x0B99) || (ch == 0x0B9A) || (ch == 0x0B9C) || (ch == 0x0B9E) || (ch == 0x0B9F) || (ch == 0x0BA3) || (ch == 0x0BA4) || (0x0BA8 <= ch && ch <= 0x0BAA) || (0x0BAE <= ch && ch <= 0x0BB9) || (0x0BBE <= ch && ch <= 0x0BC2) || (0x0BC6 <= ch && ch <= 0x0BC8) || (0x0BCA <= ch && ch <= 0x0BCD) || (ch == 0x0BD0) || (ch == 0x0BD7) || (0x0BE6 <= ch && ch <= 0x0BEF) || (0x0C00 <= ch && ch <= 0x0C03) || (0x0C05 <= ch && ch <= 0x0C0C) || (0x0C0E <= ch && ch <= 0x0C10) || (0x0C12 <= ch && ch <= 0x0C28) || (0x0C2A <= ch && ch <= 0x0C39) || (0x0C3D <= ch && ch <= 0x0C44) || (0x0C46 <= ch && ch <= 0x0C48) || (0x0C4A <= ch && ch <= 0x0C4D) || (ch == 0x0C55) || (ch == 0x0C56) || (ch == 0x0C58) || (ch == 0x0C59) || (0x0C60 <= ch && ch <= 0x0C63) || (0x0C66 <= ch && ch <= 0x0C6F) || (0x0C81 <= ch && ch <= 0x0C83) || (0x0C85 <= ch && ch <= 0x0C8C) || (0x0C8E <= ch && ch <= 0x0C90) || (0x0C92 <= ch && ch <= 0x0CA8) || (0x0CAA <= ch && ch <= 0x0CB3) || (0x0CB5 <= ch && ch <= 0x0CB9) || (0x0CBC <= ch && ch <= 0x0CC4) || (0x0CC6 <= ch && ch <= 0x0CC8) || (0x0CCA <= ch && ch <= 0x0CCD) || (ch == 0x0CD5) || (ch == 0x0CD6) || (ch == 0x0CDE) || (0x0CE0 <= ch && ch <= 0x0CE3) || (0x0CE6 <= ch && ch <= 0x0CEF) || (ch == 0x0CF1) || (ch == 0x0CF2) || (0x0D01 <= ch && ch <= 0x0D03) || (0x0D05 <= ch && ch <= 0x0D0C) || (0x0D0E <= ch && ch <= 0x0D10) || (0x0D12 <= ch && ch <= 0x0D3A) || (0x0D3D <= ch && ch <= 0x0D44) || (0x0D46 <= ch && ch <= 0x0D48) || (0x0D4A <= ch && ch <= 0x0D4E) || (ch == 0x0D57) || (0x0D60 <= ch && ch <= 0x0D63) || (0x0D66 <= ch && ch <= 0x0D6F) || (0x0D7A <= ch && ch <= 0x0D7F) || (ch == 0x0D82) || (ch == 0x0D83) || (0x0D85 <= ch && ch <= 0x0D96) || (0x0D9A <= ch && ch <= 0x0DB1) || (0x0DB3 <= ch && ch <= 0x0DBB) || (ch == 0x0DBD) || (0x0DC0 <= ch && ch <= 0x0DC6) || (ch == 0x0DCA) || (0x0DCF <= ch && ch <= 0x0DD4) || (ch == 0x0DD6) || (0x0DD8 <= ch && ch <= 0x0DDF) || (0x0DE6 <= ch && ch <= 0x0DEF) || (ch == 0x0DF2) || (ch == 0x0DF3) || (0x0E01 <= ch && ch <= 0x0E3A) || (0x0E40 <= ch && ch <= 0x0E4E) || (0x0E50 <= ch && ch <= 0x0E59) || (ch == 0x0E81) || (ch == 0x0E82) || (ch == 0x0E84) || (ch == 0x0E87) || (ch == 0x0E88) || (ch == 0x0E8A) || (ch == 0x0E8D) || (0x0E94 <= ch && ch <= 0x0E97) || (0x0E99 <= ch && ch <= 0x0E9F) || (0x0EA1 <= ch && ch <= 0x0EA3) || (ch == 0x0EA5) || (ch == 0x0EA7) || (ch == 0x0EAA) || (ch == 0x0EAB) || (0x0EAD <= ch && ch <= 0x0EB9) || (0x0EBB <= ch && ch <= 0x0EBD) || (0x0EC0 <= ch && ch <= 0x0EC4) || (ch == 0x0EC6) || (0x0EC8 <= ch && ch <= 0x0ECD) || (0x0ED0 <= ch && ch <= 0x0ED9) || (0x0EDC <= ch && ch <= 0x0EDF) || (ch == 0x0F00) || (ch == 0x0F18) || (ch == 0x0F19) || (0x0F20 <= ch && ch <= 0x0F29) || (ch == 0x0F35) || (ch == 0x0F37) || (ch == 0x0F39) || (0x0F3E <= ch && ch <= 0x0F47) || (0x0F49 <= ch && ch <= 0x0F6C) || (0x0F71 <= ch && ch <= 0x0F84) || (0x0F86 <= ch && ch <= 0x0F97) || (0x0F99 <= ch && ch <= 0x0FBC) || (ch == 0x0FC6) || (0x1000 <= ch && ch <= 0x1049) || (0x1050 <= ch && ch <= 0x109D) || (0x10A0 <= ch && ch <= 0x10C5) || (ch == 0x10C7) || (ch == 0x10CD) || (0x10D0 <= ch && ch <= 0x10FA) || (0x10FC <= ch && ch <= 0x1248) || (0x124A <= ch && ch <= 0x124D) || (0x1250 <= ch && ch <= 0x1256) || (ch == 0x1258) || (0x125A <= ch && ch <= 0x125D) || (0x1260 <= ch && ch <= 0x1288) || (0x128A <= ch && ch <= 0x128D) || (0x1290 <= ch && ch <= 0x12B0) || (0x12B2 <= ch && ch <= 0x12B5) || (0x12B8 <= ch && ch <= 0x12BE) || (ch == 0x12C0) || (0x12C2 <= ch && ch <= 0x12C5) || (0x12C8 <= ch && ch <= 0x12D6) || (0x12D8 <= ch && ch <= 0x1310) || (0x1312 <= ch && ch <= 0x1315) || (0x1318 <= ch && ch <= 0x135A) || (0x135D <= ch && ch <= 0x135F) || (0x1369 <= ch && ch <= 0x1371) || (0x1380 <= ch && ch <= 0x138F) || (0x13A0 <= ch && ch <= 0x13F4) || (0x1401 <= ch && ch <= 0x166C) || (0x166F <= ch && ch <= 0x167F) || (0x1681 <= ch && ch <= 0x169A) || (0x16A0 <= ch && ch <= 0x16EA) || (0x16EE <= ch && ch <= 0x16F8) || (0x1700 <= ch && ch <= 0x170C) || (0x170E <= ch && ch <= 0x1714) || (0x1720 <= ch && ch <= 0x1734) || (0x1740 <= ch && ch <= 0x1753) || (0x1760 <= ch && ch <= 0x176C) || (0x176E <= ch && ch <= 0x1770) || (ch == 0x1772) || (ch == 0x1773) || (0x1780 <= ch && ch <= 0x17D3) || (ch == 0x17D7) || (ch == 0x17DC) || (ch == 0x17DD) || (0x17E0 <= ch && ch <= 0x17E9) || (0x180B <= ch && ch <= 0x180D) || (0x1810 <= ch && ch <= 0x1819) || (0x1820 <= ch && ch <= 0x1877) || (0x1880 <= ch && ch <= 0x18AA) || (0x18B0 <= ch && ch <= 0x18F5) || (0x1900 <= ch && ch <= 0x191E) || (0x1920 <= ch && ch <= 0x192B) || (0x1930 <= ch && ch <= 0x193B) || (0x1946 <= ch && ch <= 0x196D) || (0x1970 <= ch && ch <= 0x1974) || (0x1980 <= ch && ch <= 0x19AB) || (0x19B0 <= ch && ch <= 0x19C9) || (0x19D0 <= ch && ch <= 0x19DA) || (0x1A00 <= ch && ch <= 0x1A1B) || (0x1A20 <= ch && ch <= 0x1A5E) || (0x1A60 <= ch && ch <= 0x1A7C) || (0x1A7F <= ch && ch <= 0x1A89) || (0x1A90 <= ch && ch <= 0x1A99) || (ch == 0x1AA7) || (0x1AB0 <= ch && ch <= 0x1ABD) || (0x1B00 <= ch && ch <= 0x1B4B) || (0x1B50 <= ch && ch <= 0x1B59) || (0x1B6B <= ch && ch <= 0x1B73) || (0x1B80 <= ch && ch <= 0x1BF3) || (0x1C00 <= ch && ch <= 0x1C37) || (0x1C40 <= ch && ch <= 0x1C49) || (0x1C4D <= ch && ch <= 0x1C7D) || (0x1CD0 <= ch && ch <= 0x1CD2) || (0x1CD4 <= ch && ch <= 0x1CF6) || (ch == 0x1CF8) || (ch == 0x1CF9) || (0x1D00 <= ch && ch <= 0x1DF5) || (0x1DFC <= ch && ch <= 0x1F15) || (0x1F18 <= ch && ch <= 0x1F1D) || (0x1F20 <= ch && ch <= 0x1F45) || (0x1F48 <= ch && ch <= 0x1F4D) || (0x1F50 <= ch && ch <= 0x1F57) || (ch == 0x1F59) || (ch == 0x1F5B) || (ch == 0x1F5D) || (0x1F5F <= ch && ch <= 0x1F7D) || (0x1F80 <= ch && ch <= 0x1FB4) || (0x1FB6 <= ch && ch <= 0x1FBC) || (ch == 0x1FBE) || (0x1FC2 <= ch && ch <= 0x1FC4) || (0x1FC6 <= ch && ch <= 0x1FCC) || (0x1FD0 <= ch && ch <= 0x1FD3) || (0x1FD6 <= ch && ch <= 0x1FDB) || (0x1FE0 <= ch && ch <= 0x1FEC) || (0x1FF2 <= ch && ch <= 0x1FF4) || (0x1FF6 <= ch && ch <= 0x1FFC) || (ch == 0x200C) || (ch == 0x200D) || (ch == 0x203F) || (ch == 0x2040) || (ch == 0x2054) || (ch == 0x2071) || (ch == 0x207F) || (0x2090 <= ch && ch <= 0x209C) || (0x20D0 <= ch && ch <= 0x20DC) || (ch == 0x20E1) || (0x20E5 <= ch && ch <= 0x20F0) || (ch == 0x2102) || (ch == 0x2107) || (0x210A <= ch && ch <= 0x2113) || (ch == 0x2115) || (0x2118 <= ch && ch <= 0x211D) || (ch == 0x2124) || (ch == 0x2126) || (ch == 0x2128) || (0x212A <= ch && ch <= 0x2139) || (0x213C <= ch && ch <= 0x213F) || (0x2145 <= ch && ch <= 0x2149) || (ch == 0x214E) || (0x2160 <= ch && ch <= 0x2188) || (0x2C00 <= ch && ch <= 0x2C2E) || (0x2C30 <= ch && ch <= 0x2C5E) || (0x2C60 <= ch && ch <= 0x2CE4) || (0x2CEB <= ch && ch <= 0x2CF3) || (0x2D00 <= ch && ch <= 0x2D25) || (ch == 0x2D27) || (ch == 0x2D2D) || (0x2D30 <= ch && ch <= 0x2D67) || (ch == 0x2D6F) || (0x2D7F <= ch && ch <= 0x2D96) || (0x2DA0 <= ch && ch <= 0x2DA6) || (0x2DA8 <= ch && ch <= 0x2DAE) || (0x2DB0 <= ch && ch <= 0x2DB6) || (0x2DB8 <= ch && ch <= 0x2DBE) || (0x2DC0 <= ch && ch <= 0x2DC6) || (0x2DC8 <= ch && ch <= 0x2DCE) || (0x2DD0 <= ch && ch <= 0x2DD6) || (0x2DD8 <= ch && ch <= 0x2DDE) || (0x2DE0 <= ch && ch <= 0x2DFF) || (0x3005 <= ch && ch <= 0x3007) || (0x3021 <= ch && ch <= 0x302F) || (0x3031 <= ch && ch <= 0x3035) || (0x3038 <= ch && ch <= 0x303C) || (0x3041 <= ch && ch <= 0x3096) || (0x3099 <= ch && ch <= 0x309F) || (0x30A1 <= ch && ch <= 0x30FA) || (0x30FC <= ch && ch <= 0x30FF) || (0x3105 <= ch && ch <= 0x312D) || (0x3131 <= ch && ch <= 0x318E) || (0x31A0 <= ch && ch <= 0x31BA) || (0x31F0 <= ch && ch <= 0x31FF) || (0x3400 <= ch && ch <= 0x4DB5) || (0x4E00 <= ch && ch <= 0x9FCC) || (0xA000 <= ch && ch <= 0xA48C) || (0xA4D0 <= ch && ch <= 0xA4FD) || (0xA500 <= ch && ch <= 0xA60C) || (0xA610 <= ch && ch <= 0xA62B) || (0xA640 <= ch && ch <= 0xA66F) || (0xA674 <= ch && ch <= 0xA67D) || (0xA67F <= ch && ch <= 0xA69D) || (0xA69F <= ch && ch <= 0xA6F1) || (0xA717 <= ch && ch <= 0xA71F) || (0xA722 <= ch && ch <= 0xA788) || (0xA78B <= ch && ch <= 0xA78E) || (0xA790 <= ch && ch <= 0xA7AD) || (ch == 0xA7B0) || (ch == 0xA7B1) || (0xA7F7 <= ch && ch <= 0xA827) || (0xA840 <= ch && ch <= 0xA873) || (0xA880 <= ch && ch <= 0xA8C4) || (0xA8D0 <= ch && ch <= 0xA8D9) || (0xA8E0 <= ch && ch <= 0xA8F7) || (ch == 0xA8FB) || (0xA900 <= ch && ch <= 0xA92D) || (0xA930 <= ch && ch <= 0xA953) || (0xA960 <= ch && ch <= 0xA97C) || (0xA980 <= ch && ch <= 0xA9C0) || (0xA9CF <= ch && ch <= 0xA9D9) || (0xA9E0 <= ch && ch <= 0xA9FE) || (0xAA00 <= ch && ch <= 0xAA36) || (0xAA40 <= ch && ch <= 0xAA4D) || (0xAA50 <= ch && ch <= 0xAA59) || (0xAA60 <= ch && ch <= 0xAA76) || (0xAA7A <= ch && ch <= 0xAAC2) || (0xAADB <= ch && ch <= 0xAADD) || (0xAAE0 <= ch && ch <= 0xAAEF) || (0xAAF2 <= ch && ch <= 0xAAF6) || (0xAB01 <= ch && ch <= 0xAB06) || (0xAB09 <= ch && ch <= 0xAB0E) || (0xAB11 <= ch && ch <= 0xAB16) || (0xAB20 <= ch && ch <= 0xAB26) || (0xAB28 <= ch && ch <= 0xAB2E) || (0xAB30 <= ch && ch <= 0xAB5A) || (0xAB5C <= ch && ch <= 0xAB5F) || (ch == 0xAB64) || (ch == 0xAB65) || (0xABC0 <= ch && ch <= 0xABEA) || (ch == 0xABEC) || (ch == 0xABED) || (0xABF0 <= ch && ch <= 0xABF9) || (0xAC00 <= ch && ch <= 0xD7A3) || (0xD7B0 <= ch && ch <= 0xD7C6) || (0xD7CB <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFA6D) || (0xFA70 <= ch && ch <= 0xFAD9) || (0xFB00 <= ch && ch <= 0xFB06) || (0xFB13 <= ch && ch <= 0xFB17) || (0xFB1D <= ch && ch <= 0xFB28) || (0xFB2A <= ch && ch <= 0xFB36) || (0xFB38 <= ch && ch <= 0xFB3C) || (ch == 0xFB3E) || (ch == 0xFB40) || (ch == 0xFB41) || (ch == 0xFB43) || (ch == 0xFB44) || (0xFB46 <= ch && ch <= 0xFBB1) || (0xFBD3 <= ch && ch <= 0xFD3D) || (0xFD50 <= ch && ch <= 0xFD8F) || (0xFD92 <= ch && ch <= 0xFDC7) || (0xFDF0 <= ch && ch <= 0xFDFB) || (0xFE00 <= ch && ch <= 0xFE0F) || (0xFE20 <= ch && ch <= 0xFE2D) || (ch == 0xFE33) || (ch == 0xFE34) || (0xFE4D <= ch && ch <= 0xFE4F) || (0xFE70 <= ch && ch <= 0xFE74) || (0xFE76 <= ch && ch <= 0xFEFC) || (0xFF10 <= ch && ch <= 0xFF19) || (0xFF21 <= ch && ch <= 0xFF3A) || (ch == 0xFF3F) || (0xFF41 <= ch && ch <= 0xFF5A) || (0xFF66 <= ch && ch <= 0xFFBE) || (0xFFC2 <= ch && ch <= 0xFFC7) || (0xFFCA <= ch && ch <= 0xFFCF) || (0xFFD2 <= ch && ch <= 0xFFD7) || (0xFFDA <= ch && ch <= 0xFFDC);
|
|
}
|
|
|
|
ALWAYS_INLINE bool isIdentifierStart(char32_t ch)
|
|
{
|
|
if (LIKELY(ch < 128)) {
|
|
return isIdentifierStartData[ch];
|
|
} else {
|
|
return isIdentifierStartSlow(ch);
|
|
}
|
|
}
|
|
|
|
struct Curly {
|
|
char m_curly[4];
|
|
Curly() {}
|
|
Curly(const char curly[4])
|
|
{
|
|
m_curly[0] = curly[0];
|
|
m_curly[1] = curly[1];
|
|
m_curly[2] = curly[2];
|
|
m_curly[3] = curly[3];
|
|
}
|
|
};
|
|
|
|
namespace Messages {
|
|
const char* UnexpectedToken = "Unexpected token %s";
|
|
const char* UnexpectedTokenIllegal = "Unexpected token ILLEGAL";
|
|
const char* UnexpectedNumber = "Unexpected number";
|
|
const char* UnexpectedString = "Unexpected string";
|
|
const char* UnexpectedIdentifier = "Unexpected identifier";
|
|
const char* UnexpectedReserved = "Unexpected reserved word";
|
|
const char* UnexpectedTemplate = "Unexpected quasi %s";
|
|
const char* UnexpectedEOS = "Unexpected end of input";
|
|
const char* NewlineAfterThrow = "Illegal newline after throw";
|
|
const char* InvalidRegExp = "Invalid regular expression";
|
|
const char* UnterminatedRegExp = "Invalid regular expression: missing /";
|
|
const char* InvalidLHSInAssignment = "Invalid left-hand side in assignment";
|
|
const char* InvalidLHSInForIn = "Invalid left-hand side in for-in";
|
|
const char* InvalidLHSInForLoop = "Invalid left-hand side in for-loop";
|
|
const char* MultipleDefaultsInSwitch = "More than one default clause in switch statement";
|
|
const char* NoCatchOrFinally = "Missing catch or finally after try";
|
|
const char* UnknownLabel = "Undefined label \'%s\'";
|
|
const char* Redeclaration = "%s \'%s\' has already been declared";
|
|
const char* IllegalContinue = "Illegal continue statement";
|
|
const char* IllegalBreak = "Illegal break statement";
|
|
const char* IllegalReturn = "Illegal return statement";
|
|
const char* StrictModeWith = "Strict mode code may not include a with statement";
|
|
const char* StrictCatchVariable = "Catch variable may not be eval or arguments in strict mode";
|
|
const char* StrictVarName = "Variable name may not be eval or arguments in strict mode";
|
|
const char* StrictParamName = "Parameter name eval or arguments is not allowed in strict mode";
|
|
const char* StrictParamDupe = "Strict mode function may not have duplicate parameter names";
|
|
const char* StrictFunctionName = "Function name may not be eval or arguments in strict mode";
|
|
const char* StrictOctalLiteral = "Octal literals are not allowed in strict mode.";
|
|
const char* StrictLeadingZeroLiteral = "Decimals with leading zeros are not allowed in strict mode.";
|
|
const char* StrictDelete = "Delete of an unqualified identifier in strict mode.";
|
|
const char* StrictLHSAssignment = "Assignment to eval or arguments is not allowed in strict mode";
|
|
const char* StrictLHSPostfix = "Postfix increment/decrement may not have eval or arguments operand in strict mode";
|
|
const char* StrictLHSPrefix = "Prefix increment/decrement may not have eval or arguments operand in strict mode";
|
|
const char* StrictReservedWord = "Use of future reserved word in strict mode";
|
|
const char* TemplateOctalLiteral = "Octal literals are not allowed in template strings.";
|
|
const char* ParameterAfterRestParameter = "Rest parameter must be last formal parameter";
|
|
const char* DefaultRestParameter = "Unexpected token =";
|
|
const char* ObjectPatternAsRestParameter = "Unexpected token {";
|
|
const char* DuplicateProtoProperty = "Duplicate __proto__ fields are not allowed in object literals";
|
|
const char* ConstructorSpecialMethod = "Class constructor may not be an accessor";
|
|
const char* DuplicateConstructor = "A class may only have one constructor";
|
|
const char* StaticPrototype = "Classes may not have static property named prototype";
|
|
const char* MissingFromClause = "Unexpected token";
|
|
const char* NoAsAfterImportNamespace = "Unexpected token";
|
|
const char* InvalidModuleSpecifier = "Unexpected token";
|
|
const char* IllegalImportDeclaration = "Unexpected token";
|
|
const char* IllegalExportDeclaration = "Unexpected token";
|
|
const char* DuplicateBinding = "Duplicate binding %s";
|
|
const char* ForInOfLoopInitializer = "%s loop variable declaration may not have an initializer";
|
|
}
|
|
|
|
/*
|
|
export interface Comment {
|
|
multiLine: boolean;
|
|
slice: number[];
|
|
range: number[];
|
|
loc: any;
|
|
}
|
|
*/
|
|
|
|
struct ParserError : public gc {
|
|
String* description;
|
|
size_t index;
|
|
size_t line;
|
|
size_t col;
|
|
|
|
ParserError(size_t index, size_t line, size_t col, String* description)
|
|
{
|
|
this->index = index;
|
|
this->line = line;
|
|
this->col = col;
|
|
this->description = description;
|
|
}
|
|
|
|
ParserError(size_t index, size_t line, size_t col, const char* description)
|
|
{
|
|
this->index = index;
|
|
this->line = line;
|
|
this->col = col;
|
|
this->description = new ASCIIString(description);
|
|
}
|
|
};
|
|
|
|
struct ScanTemplteResult : public gc {
|
|
UTF16StringData valueCooked;
|
|
StringView raw;
|
|
bool head;
|
|
bool tail;
|
|
};
|
|
|
|
struct ScanRegExpResult {
|
|
String* body;
|
|
String* flags;
|
|
};
|
|
|
|
class Scanner;
|
|
|
|
class ScannerResult : public RefCounted<ScannerResult> {
|
|
public:
|
|
Scanner* scanner;
|
|
Token type : 4;
|
|
bool startWithZero : 1;
|
|
bool octal : 1;
|
|
bool plain : 1;
|
|
char prec : 8; // max prec is 11
|
|
// we don't needs init prec.
|
|
// prec is initialized by another function before use
|
|
|
|
size_t lineNumber;
|
|
size_t lineStart;
|
|
size_t start;
|
|
size_t end;
|
|
|
|
// TODO remove valueString
|
|
StringView valueString;
|
|
union {
|
|
KeywordKind valueKeywordKind : 8;
|
|
PunctuatorsKind valuePunctuatorsKind;
|
|
double valueNumber;
|
|
ScanTemplteResult* valueTemplate;
|
|
ScanRegExpResult valueRegexp;
|
|
};
|
|
|
|
inline ~ScannerResult();
|
|
|
|
inline void operator delete(void* obj)
|
|
{
|
|
}
|
|
|
|
inline void operator delete(void*, void*) {}
|
|
inline void operator delete[](void* obj) {}
|
|
inline void operator delete[](void*, void*) {}
|
|
ScannerResult()
|
|
{
|
|
}
|
|
|
|
ScannerResult(Scanner* scanner, Token type, size_t lineNumber, size_t lineStart, size_t start, size_t end)
|
|
{
|
|
this->scanner = scanner;
|
|
this->type = type;
|
|
this->startWithZero = this->octal = false;
|
|
this->plain = false;
|
|
this->lineNumber = lineNumber;
|
|
this->valueNumber = 0;
|
|
this->lineStart = lineStart;
|
|
this->start = start;
|
|
this->end = end;
|
|
}
|
|
|
|
ScannerResult(Scanner* scanner, Token type, StringView valueString, size_t lineNumber, size_t lineStart, size_t start, size_t end)
|
|
: valueString(valueString)
|
|
{
|
|
this->scanner = scanner;
|
|
this->type = type;
|
|
this->startWithZero = this->octal = false;
|
|
this->plain = false;
|
|
this->lineNumber = lineNumber;
|
|
this->valueNumber = 0;
|
|
this->lineStart = lineStart;
|
|
this->start = start;
|
|
this->end = end;
|
|
}
|
|
|
|
ScannerResult(Scanner* scanner, Token type, double value, size_t lineNumber, size_t lineStart, size_t start, size_t end)
|
|
{
|
|
this->scanner = scanner;
|
|
this->type = type;
|
|
this->startWithZero = this->octal = false;
|
|
this->plain = false;
|
|
this->valueNumber = value;
|
|
this->lineNumber = lineNumber;
|
|
this->lineStart = lineStart;
|
|
this->start = start;
|
|
this->end = end;
|
|
}
|
|
|
|
ScannerResult(Scanner* scanner, Token type, ScanTemplteResult* value, size_t lineNumber, size_t lineStart, size_t start, size_t end)
|
|
{
|
|
this->scanner = scanner;
|
|
this->type = type;
|
|
this->startWithZero = this->octal = false;
|
|
this->plain = false;
|
|
this->valueNumber = 0;
|
|
this->valueTemplate = value;
|
|
this->lineNumber = lineNumber;
|
|
this->lineStart = lineStart;
|
|
this->start = start;
|
|
this->end = end;
|
|
}
|
|
};
|
|
|
|
class ErrorHandler : public gc {
|
|
public:
|
|
// errors: Error[];
|
|
// tolerant: boolean;
|
|
|
|
ErrorHandler()
|
|
{
|
|
// this->errors = [];
|
|
// this->tolerant = false;
|
|
}
|
|
|
|
// recordError(error: Error): void {
|
|
// this->errors.push(error);
|
|
// };
|
|
|
|
void tolerate(Error* error)
|
|
{
|
|
/*
|
|
if (this->tolerant) {
|
|
this->recordError(error);
|
|
} else {
|
|
throw error;
|
|
}*/
|
|
throw error;
|
|
}
|
|
|
|
Error* constructError(String* msg, size_t column)
|
|
{
|
|
Error* error = new (NoGC) Error(msg);
|
|
error->column = column;
|
|
return error;
|
|
// try {
|
|
// throw error;
|
|
// } catch (base) {
|
|
/* istanbul ignore else */
|
|
// if (Object.create && Object.defineProperty) {
|
|
// error = Object.create(base);
|
|
// Object.defineProperty(error, 'column', { value: column });
|
|
// }
|
|
// } finally {
|
|
// return error;
|
|
// }
|
|
}
|
|
|
|
Error* createError(size_t index, size_t line, size_t col, String* description, ErrorObject::Code code)
|
|
{
|
|
UTF16StringDataNonGCStd msg = u"Line ";
|
|
char lineStringBuf[512];
|
|
snprintf(lineStringBuf, sizeof(lineStringBuf), "%zu", line);
|
|
std::string lineString = lineStringBuf;
|
|
msg += UTF16StringDataNonGCStd(lineString.begin(), lineString.end());
|
|
msg += u": ";
|
|
if (description->length()) {
|
|
msg += UTF16StringDataNonGCStd(description->toUTF16StringData().data());
|
|
}
|
|
Error* error = constructError(new UTF16String(msg.data(), msg.length()), col);
|
|
error->index = index;
|
|
error->lineNumber = line;
|
|
error->description = description;
|
|
error->errorCode = code;
|
|
return error;
|
|
};
|
|
|
|
void throwError(size_t index, size_t line, size_t col, String* description, ErrorObject::Code code)
|
|
{
|
|
throw this->createError(index, line, col, description, code);
|
|
}
|
|
|
|
void tolerateError(size_t index, size_t line, size_t col, String* description, ErrorObject::Code code)
|
|
{
|
|
Error* error = this->createError(index, line, col, description, code);
|
|
/*
|
|
if (this->tolerant) {
|
|
this->recordError(error);
|
|
} else {
|
|
throw error;
|
|
}*/
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
#define SCANNER_RESULT_POOL_INITIAL_SIZE 128
|
|
class Scanner : public gc {
|
|
public:
|
|
StringView source;
|
|
ErrorHandler* errorHandler;
|
|
// trackComment: boolean;
|
|
|
|
size_t length;
|
|
size_t index;
|
|
size_t lineNumber;
|
|
size_t lineStart;
|
|
std::vector<Curly> curlyStack;
|
|
bool isPoolEnabled;
|
|
ScannerResult* initialResultMemoryPool[SCANNER_RESULT_POOL_INITIAL_SIZE];
|
|
size_t initialResultMemoryPoolSize;
|
|
std::vector<ScannerResult*, gc_allocator<ScannerResult*>> resultMemoryPool;
|
|
char scannerResultInnerPool[SCANNER_RESULT_POOL_INITIAL_SIZE * sizeof(ScannerResult)];
|
|
|
|
~Scanner()
|
|
{
|
|
isPoolEnabled = false;
|
|
}
|
|
|
|
|
|
Scanner(StringView code, ErrorHandler* handler, size_t startLine = 0, size_t startColumn = 0)
|
|
{
|
|
isPoolEnabled = true;
|
|
source = code;
|
|
errorHandler = handler;
|
|
// trackComment = false;
|
|
|
|
length = code.length();
|
|
index = 0;
|
|
lineNumber = ((length > 0) ? 1 : 0) + startLine;
|
|
lineStart = startColumn;
|
|
|
|
initialResultMemoryPoolSize = SCANNER_RESULT_POOL_INITIAL_SIZE;
|
|
ScannerResult* ptr = (ScannerResult*)scannerResultInnerPool;
|
|
for (size_t i = 0; i < SCANNER_RESULT_POOL_INITIAL_SIZE; i++) {
|
|
ptr[i].scanner = this;
|
|
initialResultMemoryPool[i] = &ptr[i];
|
|
}
|
|
}
|
|
|
|
ScannerResult* createScannerResult()
|
|
{
|
|
if (initialResultMemoryPoolSize) {
|
|
initialResultMemoryPoolSize--;
|
|
return initialResultMemoryPool[initialResultMemoryPoolSize];
|
|
} else if (resultMemoryPool.size() == 0) {
|
|
auto ret = (ScannerResult*)GC_MALLOC(sizeof(ScannerResult));
|
|
return ret;
|
|
} else {
|
|
auto ret = resultMemoryPool.back();
|
|
resultMemoryPool.pop_back();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
bool eof()
|
|
{
|
|
return index >= length;
|
|
}
|
|
|
|
void throwUnexpectedToken(const char* message = Messages::UnexpectedTokenIllegal)
|
|
{
|
|
this->errorHandler->throwError(this->index, this->lineNumber, this->index - this->lineStart + 1, new ASCIIString(message), ErrorObject::SyntaxError);
|
|
}
|
|
/*
|
|
tolerateUnexpectedToken() {
|
|
this->errorHandler.tolerateError(this->index, this->lineNumber,
|
|
this->index - this->lineStart + 1, Messages.UnexpectedTokenIllegal);
|
|
};
|
|
*/
|
|
void tolerateUnexpectedToken()
|
|
{
|
|
throwUnexpectedToken();
|
|
}
|
|
// ECMA-262 11.4 Comments
|
|
|
|
// skipSingleLineComment(offset: number): Comment[] {
|
|
void skipSingleLineComment(size_t /*offset*/)
|
|
{
|
|
// let comments: Comment[];
|
|
// size_t start, loc;
|
|
|
|
/*
|
|
if (this->trackComment) {
|
|
comments = [];
|
|
start = this->index - offset;
|
|
loc = {
|
|
start: {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart - offset
|
|
},
|
|
end: {}
|
|
};
|
|
}*/
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
++this->index;
|
|
if (isLineTerminator(ch)) {
|
|
/*
|
|
if (this->trackComment) {
|
|
loc.end = {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart - 1
|
|
};
|
|
const entry: Comment = {
|
|
multiLine: false,
|
|
slice: [start + offset, this->index - 1],
|
|
range: [start, this->index - 1],
|
|
loc: loc
|
|
};
|
|
comments.push(entry);
|
|
}*/
|
|
if (ch == 13 && this->source.bufferedCharAt(this->index) == 10) {
|
|
++this->index;
|
|
}
|
|
++this->lineNumber;
|
|
this->lineStart = this->index;
|
|
// return comments;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (this->trackComment) {
|
|
loc.end = {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart
|
|
};
|
|
const entry: Comment = {
|
|
multiLine: false,
|
|
slice: [start + offset, this->index],
|
|
range: [start, this->index],
|
|
loc: loc
|
|
};
|
|
comments.push(entry);
|
|
}*/
|
|
|
|
// return comments;
|
|
return;
|
|
}
|
|
|
|
// skipMultiLineComment(): Comment[] {
|
|
void skipMultiLineComment()
|
|
{
|
|
// let comments: Comment[];
|
|
// size_t start, loc;
|
|
/*
|
|
if (this->trackComment) {
|
|
comments = [];
|
|
start = this->index - 2;
|
|
loc = {
|
|
start: {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart - 2
|
|
},
|
|
end: {}
|
|
};
|
|
}
|
|
*/
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
if (isLineTerminator(ch)) {
|
|
if (ch == 0x0D && this->source.bufferedCharAt(this->index + 1) == 0x0A) {
|
|
++this->index;
|
|
}
|
|
++this->lineNumber;
|
|
++this->index;
|
|
this->lineStart = this->index;
|
|
} else if (ch == 0x2A) {
|
|
// Block comment ends with '*/'.
|
|
if (this->source.bufferedCharAt(this->index + 1) == 0x2F) {
|
|
this->index += 2;
|
|
/*
|
|
if (this->trackComment) {
|
|
loc.end = {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart
|
|
};
|
|
const entry: Comment = {
|
|
multiLine: true,
|
|
slice: [start + 2, this->index - 2],
|
|
range: [start, this->index],
|
|
loc: loc
|
|
};
|
|
comments.push(entry);
|
|
}
|
|
return comments;
|
|
*/
|
|
return;
|
|
}
|
|
++this->index;
|
|
} else {
|
|
++this->index;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Ran off the end of the file - the whole thing is a comment
|
|
if (this->trackComment) {
|
|
loc.end = {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart
|
|
};
|
|
const entry: Comment = {
|
|
multiLine: true,
|
|
slice: [start + 2, this->index],
|
|
range: [start, this->index],
|
|
loc: loc
|
|
};
|
|
comments.push(entry);
|
|
}*/
|
|
|
|
tolerateUnexpectedToken();
|
|
// return comments;
|
|
return;
|
|
}
|
|
|
|
void scanComments()
|
|
{
|
|
bool start = (this->index == 0);
|
|
while (LIKELY(!this->eof())) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
|
|
if (isWhiteSpace(ch)) {
|
|
++this->index;
|
|
} else if (isLineTerminator(ch)) {
|
|
++this->index;
|
|
if (ch == 0x0D && this->source.bufferedCharAt(this->index) == 0x0A) {
|
|
++this->index;
|
|
}
|
|
++this->lineNumber;
|
|
this->lineStart = this->index;
|
|
start = true;
|
|
} else if (ch == 0x2F) { // U+002F is '/'
|
|
ch = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch == 0x2F) {
|
|
this->index += 2;
|
|
this->skipSingleLineComment(2);
|
|
start = true;
|
|
} else if (ch == 0x2A) { // U+002A is '*'
|
|
this->index += 2;
|
|
this->skipMultiLineComment();
|
|
} else {
|
|
break;
|
|
}
|
|
} else if (start && ch == 0x2D) { // U+002D is '-'
|
|
// U+003E is '>'
|
|
if ((this->source.bufferedCharAt(this->index + 1) == 0x2D) && (this->source.bufferedCharAt(this->index + 2) == 0x3E)) {
|
|
// '-->' is a single-line comment
|
|
this->index += 3;
|
|
this->skipSingleLineComment(3);
|
|
} else {
|
|
break;
|
|
}
|
|
} else if (ch == 0x3C) { // U+003C is '<'
|
|
if (this->length > this->index + 4) {
|
|
if (this->source.bufferedCharAt(this->index + 1) == '!'
|
|
&& this->source.bufferedCharAt(this->index + 2) == '-'
|
|
&& this->source.bufferedCharAt(this->index + 3) == '-') {
|
|
this->index += 4; // `<!--`
|
|
this->skipSingleLineComment(4);
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static bool equals(const char16_t* c1, const char* c2, size_t len)
|
|
{
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (c1[i] != c2[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ECMA-262 11.6.2.2 Future Reserved Words
|
|
#define STRING_CMP(str, len) \
|
|
if (LIKELY(data.has8BitContent)) { \
|
|
return strncmp(str, (char*)data.buffer, len) == 0; \
|
|
} else { \
|
|
return equals((char16_t*)data.buffer, str, len); \
|
|
}
|
|
|
|
#define STRING_CMP2(str1, str2, len) \
|
|
if (LIKELY(data.has8BitContent)) { \
|
|
if (strncmp(str1, (char*)data.buffer, len) == 0) { \
|
|
return true; \
|
|
} else if (strncmp(str2, (char*)data.buffer, len) == 0) { \
|
|
return true; \
|
|
} \
|
|
} else { \
|
|
return equals((char16_t*)data.buffer, str1, len) || equals((char16_t*)data.buffer, str2, len); \
|
|
}
|
|
|
|
bool isFutureReservedWord(const StringView& id)
|
|
{
|
|
StringBufferAccessData data = id.bufferAccessData();
|
|
if (data.length == 4) {
|
|
STRING_CMP("enum", 4)
|
|
} else if (data.length == 5) {
|
|
STRING_CMP("super", 5)
|
|
} else if (data.length == 6) {
|
|
STRING_CMP2("export", "import", 6)
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
bool isStrictModeReservedWord(const T& id)
|
|
{
|
|
StringBufferAccessData data = id.bufferAccessData();
|
|
switch (data.length) {
|
|
case 3: // let
|
|
STRING_CMP("let", 3)
|
|
break;
|
|
case 5: // yield
|
|
STRING_CMP("yield", 3)
|
|
break;
|
|
case 6: // static public
|
|
STRING_CMP2("static", "public", 6)
|
|
break;
|
|
case 7: // private package
|
|
STRING_CMP2("private", "package", 7)
|
|
break;
|
|
case 9: // protected interface
|
|
STRING_CMP2("protected", "interface", 9)
|
|
break;
|
|
case 10: // implements
|
|
STRING_CMP("implements", 10)
|
|
break;
|
|
}
|
|
|
|
#undef STRING_CMP
|
|
#undef STRING_CMP2
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
bool isRestrictedWord(const T& id)
|
|
{
|
|
StringBufferAccessData data = id.bufferAccessData();
|
|
if (data.length == 4) {
|
|
return data.equalsSameLength("eval");
|
|
} else if (data.length == 9) {
|
|
return data.equalsSameLength("arguments");
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ECMA-262 11.6.2.1 Keywords
|
|
ALWAYS_INLINE KeywordKind isKeyword(const StringBufferAccessData& data)
|
|
{
|
|
// 'const' is specialized as Keyword in V8.
|
|
// 'yield' and 'let' are for compatibility with SpiderMonkey and ES.next.
|
|
// Some others are from future reserved words.
|
|
|
|
register size_t length = data.length;
|
|
char16_t first = data.charAt(0);
|
|
char16_t second;
|
|
switch (first) {
|
|
case 'a':
|
|
// TODO await
|
|
break;
|
|
case 'b':
|
|
if (length == 5 && data.equalsSameLength("break", 1)) {
|
|
return Break;
|
|
}
|
|
break;
|
|
case 'c':
|
|
if (length == 4) {
|
|
if (data.equalsSameLength("case", 1)) {
|
|
return Case;
|
|
}
|
|
} else if (length == 5) {
|
|
second = data.charAt(1);
|
|
if (second == 'a' && data.equalsSameLength("catch", 2)) {
|
|
return Catch;
|
|
} else if (second == 'o' && data.equalsSameLength("const", 2)) {
|
|
const char* env = getenv("ESCARGOT_TREAT_CONST_AS_VAR");
|
|
if (env && strlen(env)) {
|
|
return Var;
|
|
}
|
|
return Const;
|
|
} else if (second == 'l' && data.equalsSameLength("class", 2)) {
|
|
return Class;
|
|
}
|
|
} else if (length == 8) {
|
|
if (data.equalsSameLength("continue", 1)) {
|
|
return Continue;
|
|
}
|
|
}
|
|
break;
|
|
case 'd':
|
|
if (length == 8) {
|
|
if (data.equalsSameLength("debugger", 1)) {
|
|
return Debugger;
|
|
}
|
|
} else if (length == 2) {
|
|
if (data.equalsSameLength("do", 1)) {
|
|
return Do;
|
|
}
|
|
} else if (length == 6) {
|
|
if (data.equalsSameLength("delete", 1)) {
|
|
return Delete;
|
|
}
|
|
} else if (length == 7) {
|
|
if (data.equalsSameLength("default", 1)) {
|
|
return Default;
|
|
}
|
|
}
|
|
break;
|
|
case 'e':
|
|
if (length == 4) {
|
|
second = data.charAt(1);
|
|
if (second == 'l' && data.equalsSameLength("else", 2)) {
|
|
return Else;
|
|
} else if (second == 'n' && data.equalsSameLength("enum", 2)) {
|
|
return Enum;
|
|
}
|
|
} else if (length == 6) {
|
|
if (data.equalsSameLength("export", 1)) {
|
|
return Export;
|
|
}
|
|
} else if (length == 7) {
|
|
if (data.equalsSameLength("extends", 1)) {
|
|
return Extends;
|
|
}
|
|
}
|
|
break;
|
|
case 'f':
|
|
if (length == 3) {
|
|
if (data.equalsSameLength("for", 1)) {
|
|
return For;
|
|
}
|
|
} else if (length == 7) {
|
|
if (data.equalsSameLength("finally", 1)) {
|
|
return Finally;
|
|
}
|
|
} else if (length == 8) {
|
|
if (data.equalsSameLength("function", 1)) {
|
|
return Function;
|
|
}
|
|
}
|
|
break;
|
|
case 'i':
|
|
if (length == 2) {
|
|
second = data.charAt(1);
|
|
if (second == 'f') {
|
|
return If;
|
|
} else if (second == 'n') {
|
|
return In;
|
|
}
|
|
} else if (length == 6) {
|
|
if (data.equalsSameLength("import", 1)) {
|
|
return Import;
|
|
}
|
|
} else if (length == 10) {
|
|
if (data.equalsSameLength("instanceof", 1)) {
|
|
return InstanceofKeyword;
|
|
}
|
|
}
|
|
break;
|
|
case 'l':
|
|
if (length == 3 && data.equalsSameLength("let", 1)) {
|
|
return Let;
|
|
}
|
|
break;
|
|
case 'n':
|
|
if (length == 3 && data.equalsSameLength("new", 1)) {
|
|
return New;
|
|
}
|
|
break;
|
|
case 'r':
|
|
if (length == 6 && data.equalsSameLength("return", 1)) {
|
|
return Return;
|
|
}
|
|
break;
|
|
case 's':
|
|
if (length == 5 && data.equalsSameLength("super", 1)) {
|
|
return Super;
|
|
} else if (length == 6 && data.equalsSameLength("switch", 1)) {
|
|
return Switch;
|
|
}
|
|
break;
|
|
case 't':
|
|
switch (length) {
|
|
case 3:
|
|
if (data.equalsSameLength("try", 1)) {
|
|
return Try;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (data.equalsSameLength("this", 1)) {
|
|
return This;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (data.equalsSameLength("throw", 1)) {
|
|
return Throw;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (data.equalsSameLength("typeof", 1)) {
|
|
return Typeof;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 'v':
|
|
if (length == 3 && data.equalsSameLength("var", 1)) {
|
|
return Var;
|
|
} else if (length == 4 && data.equalsSameLength("void", 1)) {
|
|
return Void;
|
|
}
|
|
break;
|
|
case 'w':
|
|
if (length == 4 && data.equalsSameLength("with", 1)) {
|
|
return With;
|
|
} else if (length == 5 && data.equalsSameLength("while", 1)) {
|
|
return While;
|
|
}
|
|
break;
|
|
case 'y':
|
|
if (length == 5 && data.equalsSameLength("yield", 1)) {
|
|
return Yield;
|
|
}
|
|
break;
|
|
}
|
|
return NotKeyword;
|
|
}
|
|
|
|
char32_t codePointAt(size_t i)
|
|
{
|
|
char32_t cp, first, second;
|
|
cp = this->source.bufferedCharAt(i);
|
|
if (cp >= 0xD800 && cp <= 0xDBFF) {
|
|
second = this->source.bufferedCharAt(i + 1);
|
|
if (second >= 0xDC00 && second <= 0xDFFF) {
|
|
first = cp;
|
|
cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
|
}
|
|
}
|
|
|
|
return cp;
|
|
}
|
|
|
|
int hexValue(char16_t ch)
|
|
{
|
|
int c = 0;
|
|
if (ch >= '0' && ch <= '9') {
|
|
c = ch - '0';
|
|
} else if (ch >= 'a' && ch <= 'f') {
|
|
c = ch - 'a' + 10;
|
|
} else if (ch >= 'A' && ch <= 'F') {
|
|
c = ch - 'A' + 10;
|
|
} else {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
return c;
|
|
}
|
|
|
|
struct CharOrEmptyResult {
|
|
char32_t code;
|
|
bool isEmpty;
|
|
|
|
CharOrEmptyResult(char32_t code, bool isEmpty)
|
|
{
|
|
this->code = code;
|
|
this->isEmpty = isEmpty;
|
|
}
|
|
};
|
|
|
|
CharOrEmptyResult scanHexEscape(char prefix)
|
|
{
|
|
size_t len = (prefix == 'u') ? 4 : 2;
|
|
char32_t code = 0;
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
if (!this->eof() && isHexDigit(this->source.bufferedCharAt(this->index))) {
|
|
code = code * 16 + hexValue(this->source.bufferedCharAt(this->index++));
|
|
} else {
|
|
return CharOrEmptyResult(0, true);
|
|
}
|
|
}
|
|
|
|
return CharOrEmptyResult(code, false);
|
|
}
|
|
|
|
char32_t scanUnicodeCodePointEscape()
|
|
{
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
char32_t code = 0;
|
|
|
|
// At least, one hex digit is required.
|
|
if (ch == '}') {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
while (!this->eof()) {
|
|
ch = this->source.bufferedCharAt(this->index++);
|
|
if (!isHexDigit(ch)) {
|
|
break;
|
|
}
|
|
code = code * 16 + hexValue(ch);
|
|
}
|
|
|
|
if (code > 0x10FFFF || ch != '}') {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
return code;
|
|
};
|
|
|
|
StringView getIdentifier()
|
|
{
|
|
const size_t start = this->index++;
|
|
while (UNLIKELY(!this->eof())) {
|
|
const char16_t ch = this->source.bufferedCharAt(this->index);
|
|
if (UNLIKELY(ch == 0x5C)) {
|
|
// Blackslash (U+005C) marks Unicode escape sequence.
|
|
this->index = start;
|
|
return this->getComplexIdentifier();
|
|
} else if (UNLIKELY(ch >= 0xD800 && ch < 0xDFFF)) {
|
|
// Need to handle surrogate pairs.
|
|
this->index = start;
|
|
return this->getComplexIdentifier();
|
|
}
|
|
if (isIdentifierPart(ch)) {
|
|
++this->index;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return StringView(this->source, start, this->index);
|
|
}
|
|
|
|
StringView getComplexIdentifier()
|
|
{
|
|
char32_t cp = this->codePointAt(this->index);
|
|
ParserCharPiece piece = ParserCharPiece(cp);
|
|
UTF16StringDataNonGCStd id(piece.data, piece.length);
|
|
this->index += id.length();
|
|
|
|
// '\u' (U+005C, U+0075) denotes an escaped character.
|
|
char32_t ch;
|
|
if (cp == 0x5C) {
|
|
if (this->source.bufferedCharAt(this->index) != 0x75) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
++this->index;
|
|
if (this->source.bufferedCharAt(this->index) == '{') {
|
|
++this->index;
|
|
ch = this->scanUnicodeCodePointEscape();
|
|
} else {
|
|
CharOrEmptyResult res = this->scanHexEscape('u');
|
|
ch = res.code;
|
|
cp = ch;
|
|
if (res.isEmpty || ch == '\\' || !isIdentifierStart(cp)) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
}
|
|
id = ch;
|
|
}
|
|
|
|
while (!this->eof()) {
|
|
cp = this->codePointAt(this->index);
|
|
if (!isIdentifierPart(cp)) {
|
|
break;
|
|
}
|
|
// ch = Character.fromCodePoint(cp);
|
|
ch = cp;
|
|
piece = ParserCharPiece(ch);
|
|
id += UTF16StringDataNonGCStd(piece.data, piece.length);
|
|
this->index += piece.length;
|
|
|
|
// '\u' (U+005C, U+0075) denotes an escaped character.
|
|
if (cp == 0x5C) {
|
|
// id = id.substr(0, id.length - 1);
|
|
id.erase(id.length() - 1);
|
|
|
|
if (this->source.bufferedCharAt(this->index) != 0x75) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
++this->index;
|
|
if (this->source.bufferedCharAt(this->index) == '{') {
|
|
++this->index;
|
|
ch = this->scanUnicodeCodePointEscape();
|
|
} else {
|
|
CharOrEmptyResult res = this->scanHexEscape('u');
|
|
ch = res.code;
|
|
cp = ch;
|
|
if (res.isEmpty || ch == '\\' || !isIdentifierPart(cp)) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
}
|
|
piece = ParserCharPiece(ch);
|
|
id += UTF16StringDataNonGCStd(piece.data, piece.length);
|
|
}
|
|
}
|
|
|
|
String* str = new UTF16String(id.data(), id.length());
|
|
return StringView(str, 0, str->length());
|
|
}
|
|
|
|
struct OctalToDecimalResult {
|
|
char16_t code;
|
|
bool octal;
|
|
|
|
OctalToDecimalResult(char16_t code, bool octal)
|
|
{
|
|
this->code = code;
|
|
this->octal = octal;
|
|
}
|
|
};
|
|
OctalToDecimalResult octalToDecimal(char16_t ch)
|
|
{
|
|
// \0 is not octal escape sequence
|
|
bool octal = (ch != '0');
|
|
char16_t code = octalValue(ch);
|
|
|
|
if (!this->eof() && isOctalDigit(this->source.bufferedCharAt(this->index))) {
|
|
octal = true;
|
|
code = code * 8 + octalValue(this->source.bufferedCharAt(this->index++));
|
|
|
|
// 3 digits are only allowed when string starts
|
|
// with 0, 1, 2, 3
|
|
// if ('0123'.indexOf(ch) >= 0 && !this->eof() && Character.isOctalDigit(this->source.charCodeAt(this->index))) {
|
|
if ((ch >= '0' && ch <= '3') && !this->eof() && isOctalDigit(this->source.bufferedCharAt(this->index))) {
|
|
code = code * 8 + octalValue(this->source.bufferedCharAt(this->index++));
|
|
}
|
|
}
|
|
|
|
return OctalToDecimalResult(code, octal);
|
|
};
|
|
|
|
// ECMA-262 11.6 Names and Keywords
|
|
|
|
RefPtr<ScannerResult> scanIdentifier(char16_t ch0)
|
|
{
|
|
Token type;
|
|
const size_t start = this->index;
|
|
|
|
// Backslash (U+005C) starts an escaped character.
|
|
StringView id = UNLIKELY(ch0 == 0x5C) ? this->getComplexIdentifier() : this->getIdentifier();
|
|
|
|
// There is no keyword or literal with only one character.
|
|
// Thus, it must be an identifier.
|
|
KeywordKind keywordKind = NotKeyword;
|
|
auto data = id.StringView::bufferAccessData();
|
|
if (data.length == 1) {
|
|
type = Token::IdentifierToken;
|
|
} else if ((keywordKind = this->isKeyword(data))) {
|
|
type = Token::KeywordToken;
|
|
} else if (data.length == 4) {
|
|
if (data.equalsSameLength("null")) {
|
|
type = Token::NullLiteralToken;
|
|
} else if (data.equalsSameLength("true")) {
|
|
type = Token::BooleanLiteralToken;
|
|
} else {
|
|
type = Token::IdentifierToken;
|
|
}
|
|
} else if ((data.length == 5 && data.equalsSameLength("false"))) {
|
|
type = Token::BooleanLiteralToken;
|
|
} else {
|
|
type = Token::IdentifierToken;
|
|
}
|
|
|
|
if (keywordKind) {
|
|
RefPtr<ScannerResult> r = adoptRef(new (createScannerResult()) ScannerResult(this, type, id, this->lineNumber, this->lineStart, start, this->index));
|
|
r->valueKeywordKind = keywordKind;
|
|
return r;
|
|
} else {
|
|
return adoptRef(new (createScannerResult()) ScannerResult(this, type, id, this->lineNumber, this->lineStart, start, this->index));
|
|
}
|
|
}
|
|
|
|
// ECMA-262 11.7 Punctuators
|
|
RefPtr<ScannerResult> scanPunctuator(char16_t ch0)
|
|
{
|
|
RefPtr<ScannerResult> token = adoptRef(new (createScannerResult()) ScannerResult(this, Token::PunctuatorToken, StringView(), this->lineNumber, this->lineStart, this->index, this->index));
|
|
|
|
PunctuatorsKind kind;
|
|
// Check for most common single-character punctuators.
|
|
size_t start = this->index;
|
|
char ch1, ch2, ch3;
|
|
switch (ch0) {
|
|
case '(':
|
|
++this->index;
|
|
kind = LeftParenthesis;
|
|
break;
|
|
|
|
case '{':
|
|
this->curlyStack.push_back(Curly("{\0\0"));
|
|
++this->index;
|
|
kind = LeftBrace;
|
|
break;
|
|
|
|
case '.':
|
|
++this->index;
|
|
kind = Period;
|
|
if (this->source.bufferedCharAt(this->index) == '.' && this->source.bufferedCharAt(this->index + 1) == '.') {
|
|
// Spread operator: ...
|
|
this->index += 2;
|
|
// resultStr = "...";
|
|
kind = PeriodPeriodPeriod;
|
|
}
|
|
break;
|
|
|
|
case '}':
|
|
++this->index;
|
|
if (!this->curlyStack.size()) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
this->curlyStack.pop_back();
|
|
kind = RightBrace;
|
|
break;
|
|
case ')':
|
|
kind = RightParenthesis;
|
|
++this->index;
|
|
break;
|
|
case ';':
|
|
kind = SemiColon;
|
|
++this->index;
|
|
break;
|
|
case ',':
|
|
kind = Comma;
|
|
++this->index;
|
|
break;
|
|
case '[':
|
|
kind = LeftSquareBracket;
|
|
++this->index;
|
|
break;
|
|
case ']':
|
|
kind = RightSquareBracket;
|
|
++this->index;
|
|
break;
|
|
case ':':
|
|
kind = Colon;
|
|
++this->index;
|
|
break;
|
|
case '?':
|
|
kind = GuessMark;
|
|
++this->index;
|
|
break;
|
|
case '~':
|
|
kind = Wave;
|
|
++this->index;
|
|
break;
|
|
|
|
case '>':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '>') {
|
|
ch2 = this->source.bufferedCharAt(this->index + 2);
|
|
if (ch2 == '>') {
|
|
ch3 = this->source.bufferedCharAt(this->index + 3);
|
|
if (ch3 == '=') {
|
|
this->index += 4;
|
|
kind = UnsignedRightShiftEqual;
|
|
} else {
|
|
kind = UnsignedRightShift;
|
|
this->index += 3;
|
|
}
|
|
} else if (ch2 == '=') {
|
|
kind = RightShiftEqual;
|
|
this->index += 3;
|
|
} else {
|
|
kind = RightShift;
|
|
this->index += 2;
|
|
}
|
|
} else if (ch1 == '=') {
|
|
kind = RightInequalityEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = RightInequality;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '<':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '<') {
|
|
ch2 = this->source.bufferedCharAt(this->index + 2);
|
|
if (ch2 == '=') {
|
|
kind = LeftShiftEqual;
|
|
this->index += 3;
|
|
} else {
|
|
kind = LeftShift;
|
|
this->index += 2;
|
|
}
|
|
} else if (ch1 == '=') {
|
|
kind = LeftInequalityEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = LeftInequality;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '=':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
ch2 = this->source.bufferedCharAt(this->index + 2);
|
|
if (ch2 == '=') {
|
|
kind = StrictEqual;
|
|
this->index += 3;
|
|
} else {
|
|
kind = Equal;
|
|
this->index += 2;
|
|
}
|
|
} else if (ch1 == '>') {
|
|
kind = Arrow;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Substitution;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '!':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
ch2 = this->source.bufferedCharAt(this->index + 2);
|
|
if (ch2 == '=') {
|
|
kind = NotStrictEqual;
|
|
this->index += 3;
|
|
} else {
|
|
kind = NotEqual;
|
|
this->index += 2;
|
|
}
|
|
} else {
|
|
kind = ExclamationMark;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '&':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '&') {
|
|
kind = LogicalAnd;
|
|
this->index += 2;
|
|
} else if (ch1 == '=') {
|
|
kind = BitwiseAndEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = BitwiseAnd;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '|':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '|') {
|
|
kind = LogicalOr;
|
|
this->index += 2;
|
|
} else if (ch1 == '=') {
|
|
kind = BitwiseOrEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = BitwiseOr;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '^':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
kind = BitwiseXorEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = BitwiseXor;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '+':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '+') {
|
|
kind = PlusPlus;
|
|
this->index += 2;
|
|
} else if (ch1 == '=') {
|
|
kind = PlusEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Plus;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '-':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '-') {
|
|
kind = MinusMinus;
|
|
this->index += 2;
|
|
} else if (ch1 == '=') {
|
|
kind = MinusEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Minus;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '*':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
kind = MultiplyEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Multiply;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '/':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
kind = DivideEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Divide;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '%':
|
|
ch1 = this->source.bufferedCharAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
kind = ModEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Mod;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (UNLIKELY(this->index == token->start)) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
token->valuePunctuatorsKind = kind;
|
|
token->valueString = StringView(this->source, start, this->index);
|
|
return RefPtr<ScannerResult>(token);
|
|
}
|
|
|
|
// ECMA-262 11.8.3 Numeric Literals
|
|
|
|
RefPtr<ScannerResult> scanHexLiteral(size_t start)
|
|
{
|
|
uint64_t number = 0;
|
|
double numberDouble = 0.0;
|
|
bool shouldUseDouble = false;
|
|
bool scanned = false;
|
|
|
|
size_t shiftCount = 0;
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
if (!isHexDigit(ch)) {
|
|
break;
|
|
}
|
|
if (shouldUseDouble) {
|
|
numberDouble = numberDouble * 16 + toHexNumericValue(ch);
|
|
} else {
|
|
number = (number << 4) + toHexNumericValue(ch);
|
|
if (++shiftCount >= 16) {
|
|
shouldUseDouble = true;
|
|
numberDouble = number;
|
|
number = 0;
|
|
}
|
|
}
|
|
this->index++;
|
|
scanned = true;
|
|
}
|
|
|
|
if (!scanned) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
if (isIdentifierStart(this->source.bufferedCharAt(this->index))) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
if (shouldUseDouble) {
|
|
ASSERT(number == 0);
|
|
return adoptRef(new (createScannerResult()) ScannerResult(this, Token::NumericLiteralToken, numberDouble, this->lineNumber, this->lineStart, start, this->index));
|
|
} else {
|
|
ASSERT(numberDouble == 0.0);
|
|
return adoptRef(new (createScannerResult()) ScannerResult(this, Token::NumericLiteralToken, number, this->lineNumber, this->lineStart, start, this->index));
|
|
}
|
|
}
|
|
|
|
RefPtr<ScannerResult> scanBinaryLiteral(size_t start)
|
|
{
|
|
uint64_t number = 0;
|
|
bool scanned = false;
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
if (ch != '0' && ch != '1') {
|
|
break;
|
|
}
|
|
number = (number << 1) + ch - '0';
|
|
this->index++;
|
|
scanned = true;
|
|
}
|
|
|
|
if (!scanned) {
|
|
// only 0b or 0B
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
if (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
/* istanbul ignore else */
|
|
if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
}
|
|
|
|
return adoptRef(new (createScannerResult()) ScannerResult(this, Token::NumericLiteralToken, number, this->lineNumber, this->lineStart, start, this->index));
|
|
}
|
|
|
|
RefPtr<ScannerResult> scanOctalLiteral(char16_t prefix, size_t start)
|
|
{
|
|
uint64_t number = 0;
|
|
bool scanned = false;
|
|
bool octal = isOctalDigit(prefix);
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
if (!isOctalDigit(ch)) {
|
|
break;
|
|
}
|
|
number = (number << 3) + ch - '0';
|
|
this->index++;
|
|
scanned = true;
|
|
}
|
|
|
|
if (!octal && !scanned) {
|
|
// only 0o or 0O
|
|
throwUnexpectedToken();
|
|
}
|
|
|
|
if (isIdentifierStart(this->source.bufferedCharAt(this->index)) || isDecimalDigit(this->source.bufferedCharAt(this->index))) {
|
|
throwUnexpectedToken();
|
|
}
|
|
RefPtr<ScannerResult> ret = adoptRef(new (createScannerResult()) ScannerResult(this, Token::NumericLiteralToken, number, this->lineNumber, this->lineStart, start, this->index));
|
|
ret->octal = octal;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool isImplicitOctalLiteral()
|
|
{
|
|
// Implicit octal, unless there is a non-octal digit.
|
|
// (Annex B.1.1 on Numeric Literals)
|
|
for (size_t i = this->index + 1; i < this->length; ++i) {
|
|
const char16_t ch = this->source.bufferedCharAt(i);
|
|
if (ch == '8' || ch == '9') {
|
|
return false;
|
|
}
|
|
if (!isOctalDigit(ch)) {
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RefPtr<ScannerResult> scanNumericLiteral()
|
|
{
|
|
const size_t start = this->index;
|
|
char16_t ch = this->source.bufferedCharAt(start);
|
|
char16_t startChar = ch;
|
|
ASSERT(isDecimalDigit(ch) || (ch == '.'));
|
|
// 'Numeric literal must start with a decimal digit or a decimal point');
|
|
|
|
std::string number;
|
|
number.reserve(32);
|
|
|
|
if (ch != '.') {
|
|
number = this->source.bufferedCharAt(this->index++);
|
|
ch = this->source.bufferedCharAt(this->index);
|
|
|
|
// Hex number starts with '0x'.
|
|
// Octal number starts with '0'.
|
|
// Octal number in ES6 starts with '0o'.
|
|
// Binary number in ES6 starts with '0b'.
|
|
if (number == "0") {
|
|
if (ch == 'x' || ch == 'X') {
|
|
++this->index;
|
|
return this->scanHexLiteral(start);
|
|
}
|
|
if (ch == 'b' || ch == 'B') {
|
|
++this->index;
|
|
return this->scanBinaryLiteral(start);
|
|
}
|
|
if (ch == 'o' || ch == 'O') {
|
|
return this->scanOctalLiteral(ch, start);
|
|
}
|
|
|
|
if (ch && isOctalDigit(ch)) {
|
|
if (this->isImplicitOctalLiteral()) {
|
|
return this->scanOctalLiteral(ch, start);
|
|
}
|
|
}
|
|
}
|
|
|
|
while (isDecimalDigit(this->source.bufferedCharAt(this->index))) {
|
|
number += this->source.bufferedCharAt(this->index++);
|
|
}
|
|
ch = this->source.bufferedCharAt(this->index);
|
|
}
|
|
|
|
if (ch == '.') {
|
|
number += this->source.bufferedCharAt(this->index++);
|
|
while (isDecimalDigit(this->source.bufferedCharAt(this->index))) {
|
|
number += this->source.bufferedCharAt(this->index++);
|
|
}
|
|
ch = this->source.bufferedCharAt(this->index);
|
|
}
|
|
|
|
if (ch == 'e' || ch == 'E') {
|
|
number += this->source.bufferedCharAt(this->index++);
|
|
|
|
ch = this->source.bufferedCharAt(this->index);
|
|
if (ch == '+' || ch == '-') {
|
|
number += this->source.bufferedCharAt(this->index++);
|
|
}
|
|
if (isDecimalDigit(this->source.bufferedCharAt(this->index))) {
|
|
while (isDecimalDigit(this->source.bufferedCharAt(this->index))) {
|
|
number += this->source.bufferedCharAt(this->index++);
|
|
}
|
|
} else {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
}
|
|
|
|
if (isIdentifierStart(this->source.bufferedCharAt(this->index))) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
int length = number.length();
|
|
int length_dummy;
|
|
double_conversion::StringToDoubleConverter converter(double_conversion::StringToDoubleConverter::ALLOW_HEX
|
|
| double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES
|
|
| double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES,
|
|
0.0, double_conversion::Double::NaN(),
|
|
"Infinity", "NaN");
|
|
double ll = converter.StringToDouble(number.data(), length, &length_dummy);
|
|
|
|
auto ret = adoptRef(new (createScannerResult()) ScannerResult(this, Token::NumericLiteralToken, ll, this->lineNumber, this->lineStart, start, this->index));
|
|
if (startChar == '0' && length >= 2 && ll >= 1) {
|
|
ret->startWithZero = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// ECMA-262 11.8.4 String Literals
|
|
|
|
RefPtr<ScannerResult> scanStringLiteral()
|
|
{
|
|
// TODO apply rope-string
|
|
const size_t start = this->index;
|
|
char16_t quote = this->source.bufferedCharAt(start);
|
|
ASSERT((quote == '\'' || quote == '"'));
|
|
// 'String literal must starts with a quote');
|
|
|
|
++this->index;
|
|
bool octal = false;
|
|
bool stringViewAllLatin1 = true;
|
|
bool isPlainCase = true;
|
|
|
|
UTF16StringDataNonGCStd stringUTF16;
|
|
StringView str;
|
|
|
|
#define CONVERT_UNPLAIN_CASE_IF_NEEDED() \
|
|
if (isPlainCase) { \
|
|
auto temp = str.toUTF16StringData(); \
|
|
stringUTF16 = UTF16StringDataNonGCStd(temp.data(), temp.length()); \
|
|
isPlainCase = false; \
|
|
}
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index++);
|
|
|
|
if (ch == quote) {
|
|
quote = '\0';
|
|
break;
|
|
} else if (ch == '\\') {
|
|
ch = this->source.bufferedCharAt(this->index++);
|
|
CONVERT_UNPLAIN_CASE_IF_NEEDED()
|
|
if (!ch || !isLineTerminator(ch)) {
|
|
switch (ch) {
|
|
case 'u':
|
|
case 'x':
|
|
if (this->source.bufferedCharAt(this->index) == '{') {
|
|
++this->index;
|
|
ParserCharPiece piece(this->scanUnicodeCodePointEscape());
|
|
stringUTF16 += UTF16StringDataNonGCStd(piece.data, piece.length);
|
|
} else {
|
|
CharOrEmptyResult res = this->scanHexEscape(ch);
|
|
if (res.isEmpty) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
const char32_t unescaped = res.code;
|
|
ParserCharPiece piece(unescaped);
|
|
stringUTF16 += UTF16StringDataNonGCStd(piece.data, piece.length);
|
|
}
|
|
break;
|
|
case 'n':
|
|
stringUTF16 += '\n';
|
|
break;
|
|
case 'r':
|
|
stringUTF16 += '\r';
|
|
break;
|
|
case 't':
|
|
stringUTF16 += '\t';
|
|
break;
|
|
case 'b':
|
|
stringUTF16 += '\b';
|
|
break;
|
|
case 'f':
|
|
stringUTF16 += '\f';
|
|
break;
|
|
case 'v':
|
|
stringUTF16 += '\x0B';
|
|
break;
|
|
|
|
default:
|
|
if (ch && isOctalDigit(ch)) {
|
|
OctalToDecimalResult octToDec = this->octalToDecimal(ch);
|
|
|
|
octal = octToDec.octal || octal;
|
|
stringUTF16 += octToDec.code;
|
|
} else if (isDecimalDigit(ch)) {
|
|
octal = true;
|
|
stringUTF16 += ch;
|
|
} else {
|
|
stringUTF16 += ch;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
++this->lineNumber;
|
|
if (ch == '\r' && this->source.bufferedCharAt(this->index) == '\n') {
|
|
++this->index;
|
|
} else if (ch == '\n' && this->source.bufferedCharAt(this->index) == '\r') {
|
|
++this->index;
|
|
}
|
|
this->lineStart = this->index;
|
|
}
|
|
} else if (isLineTerminator(ch)) {
|
|
break;
|
|
} else {
|
|
if (isPlainCase) {
|
|
if (ch >= 256) {
|
|
stringViewAllLatin1 = false;
|
|
}
|
|
str = StringView(this->source, start + 1, start + 1 + str.length() + 1);
|
|
} else {
|
|
stringUTF16 += ch;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (quote != '\0') {
|
|
this->index = start;
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
RefPtr<ScannerResult> ret;
|
|
if (isPlainCase) {
|
|
if (!str.string()->has8BitContent() && stringViewAllLatin1) {
|
|
str = StringView(new Latin1String((char16_t*)str.bufferAccessData().buffer, str.length()), 0, str.length());
|
|
}
|
|
ret = adoptRef(new (createScannerResult()) ScannerResult(this, Token::StringLiteralToken, str, /*octal, */ this->lineNumber, this->lineStart, start, this->index));
|
|
} else {
|
|
String* newStr;
|
|
if (isAllLatin1(stringUTF16.data(), stringUTF16.length())) {
|
|
newStr = new Latin1String(stringUTF16.data(), stringUTF16.length());
|
|
} else {
|
|
newStr = new UTF16String(stringUTF16.data(), stringUTF16.length());
|
|
}
|
|
ret = adoptRef(new (createScannerResult()) ScannerResult(this, Token::StringLiteralToken, StringView(newStr, 0, newStr->length()), /*octal, */ this->lineNumber, this->lineStart, start, this->index));
|
|
}
|
|
ret->octal = octal;
|
|
ret->plain = isPlainCase;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// ECMA-262 11.8.6 Template Literal Lexical Components
|
|
|
|
RefPtr<ScannerResult> scanTemplate()
|
|
{
|
|
// TODO apply rope-string
|
|
UTF16StringDataNonGCStd cooked;
|
|
bool terminated = false;
|
|
size_t start = this->index;
|
|
|
|
bool head = (this->source.bufferedCharAt(start) == '`');
|
|
bool tail = false;
|
|
size_t rawOffset = 2;
|
|
|
|
++this->index;
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index++);
|
|
if (ch == '`') {
|
|
rawOffset = 1;
|
|
tail = true;
|
|
terminated = true;
|
|
break;
|
|
} else if (ch == '$') {
|
|
if (this->source.bufferedCharAt(this->index) == '{') {
|
|
this->curlyStack.push_back(Curly("${\0"));
|
|
++this->index;
|
|
terminated = true;
|
|
break;
|
|
}
|
|
cooked += ch;
|
|
} else if (ch == '\\') {
|
|
ch = this->source.bufferedCharAt(this->index++);
|
|
if (!isLineTerminator(ch)) {
|
|
switch (ch) {
|
|
case 'n':
|
|
cooked += '\n';
|
|
break;
|
|
case 'r':
|
|
cooked += '\r';
|
|
break;
|
|
case 't':
|
|
cooked += '\t';
|
|
break;
|
|
case 'u':
|
|
case 'x':
|
|
if (this->source.bufferedCharAt(this->index) == '{') {
|
|
++this->index;
|
|
cooked += this->scanUnicodeCodePointEscape();
|
|
} else {
|
|
const size_t restore = this->index;
|
|
CharOrEmptyResult res = this->scanHexEscape(ch);
|
|
const char32_t unescaped = res.code;
|
|
if (!res.isEmpty) {
|
|
ParserCharPiece piece(unescaped);
|
|
cooked += UTF16StringDataNonGCStd(piece.data, piece.length);
|
|
} else {
|
|
this->index = restore;
|
|
cooked += ch;
|
|
}
|
|
}
|
|
break;
|
|
case 'b':
|
|
cooked += '\b';
|
|
break;
|
|
case 'f':
|
|
cooked += '\f';
|
|
break;
|
|
case 'v':
|
|
cooked += '\v';
|
|
break;
|
|
|
|
default:
|
|
if (ch == '0') {
|
|
if (isDecimalDigit(this->source.bufferedCharAt(this->index))) {
|
|
// Illegal: \01 \02 and so on
|
|
this->throwUnexpectedToken(Messages::TemplateOctalLiteral);
|
|
}
|
|
cooked += (char16_t)'\0';
|
|
} else if (isOctalDigit(ch)) {
|
|
// Illegal: \1 \2
|
|
this->throwUnexpectedToken(Messages::TemplateOctalLiteral);
|
|
} else {
|
|
cooked += ch;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
++this->lineNumber;
|
|
if (ch == '\r' && this->source.bufferedCharAt(this->index) == '\n') {
|
|
++this->index;
|
|
}
|
|
this->lineStart = this->index;
|
|
}
|
|
} else if (isLineTerminator(ch)) {
|
|
++this->lineNumber;
|
|
if (ch == '\r' && this->source.bufferedCharAt(this->index) == '\n') {
|
|
++this->index;
|
|
}
|
|
this->lineStart = this->index;
|
|
cooked += '\n';
|
|
} else {
|
|
cooked += ch;
|
|
}
|
|
}
|
|
|
|
if (!terminated) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
if (!head) {
|
|
this->curlyStack.pop_back();
|
|
}
|
|
|
|
ScanTemplteResult* result = new ScanTemplteResult();
|
|
result->head = head;
|
|
result->tail = tail;
|
|
result->raw = StringView(this->source, start + 1, this->index - rawOffset);
|
|
result->valueCooked = UTF16StringData(cooked.data(), cooked.length());
|
|
|
|
return adoptRef(new (createScannerResult()) ScannerResult(this, Token::TemplateToken, result, this->lineNumber, this->lineStart, start, this->index));
|
|
}
|
|
|
|
// ECMA-262 11.8.5 Regular Expression Literals
|
|
/*
|
|
testRegExp(pattern: string, flags: string) {
|
|
// The BMP character to use as a replacement for astral symbols when
|
|
// translating an ES6 "u"-flagged pattern to an ES5-compatible
|
|
// approximation.
|
|
// Note: replacing with '\uFFFF' enables false positives in unlikely
|
|
// scenarios. For example, `[\u{1044f}-\u{10440}]` is an invalid
|
|
// pattern that would not be detected by this substitution.
|
|
const astralSubstitute = '\uFFFF';
|
|
let tmp = pattern;
|
|
let self = this;
|
|
|
|
if (flags.indexOf('u') >= 0) {
|
|
tmp = tmp
|
|
// Replace every Unicode escape sequence with the equivalent
|
|
// BMP character or a constant ASCII code point in the case of
|
|
// astral symbols. (See the above note on `astralSubstitute`
|
|
// for more information.)
|
|
.replace(/\\u\{([0-9a-fA-F]+)\}|\\u([a-fA-F0-9]{4})/g, function($0, $1, $2) {
|
|
const codePoint = parseInt($1 || $2, 16);
|
|
if (codePoint > 0x10FFFF) {
|
|
self.throwUnexpectedToken(Messages.InvalidRegExp);
|
|
}
|
|
if (codePoint <= 0xFFFF) {
|
|
return String.fromCharCode(codePoint);
|
|
}
|
|
return astralSubstitute;
|
|
})
|
|
// Replace each paired surrogate with a single ASCII symbol to
|
|
// avoid throwing on regular expressions that are only valid in
|
|
// combination with the "u" flag.
|
|
.replace(
|
|
/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
|
astralSubstitute
|
|
);
|
|
}
|
|
|
|
// First, detect invalid regular expressions.
|
|
try {
|
|
RegExp(tmp);
|
|
} catch (e) {
|
|
this->throwUnexpectedToken(Messages.InvalidRegExp);
|
|
}
|
|
|
|
// Return a regular expression object for this pattern-flag pair, or
|
|
// `null` in case the current environment doesn't support the flags it
|
|
// uses.
|
|
try {
|
|
return new RegExp(pattern, flags);
|
|
} catch (exception) {
|
|
return null;
|
|
}
|
|
};
|
|
*/
|
|
|
|
String* scanRegExpBody()
|
|
{
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
ASSERT(ch == '/');
|
|
// assert(ch == '/', 'Regular expression literal must start with a slash');
|
|
|
|
// TODO apply rope-string
|
|
char16_t ch0 = this->source.bufferedCharAt(this->index++);
|
|
UTF16StringDataNonGCStd str(&ch0, 1);
|
|
bool classMarker = false;
|
|
bool terminated = false;
|
|
|
|
while (!this->eof()) {
|
|
ch = this->source.bufferedCharAt(this->index++);
|
|
str += ch;
|
|
if (ch == '\\') {
|
|
ch = this->source.bufferedCharAt(this->index++);
|
|
// ECMA-262 7.8.5
|
|
if (isLineTerminator(ch)) {
|
|
this->throwUnexpectedToken(Messages::UnterminatedRegExp);
|
|
}
|
|
str += ch;
|
|
} else if (isLineTerminator(ch)) {
|
|
this->throwUnexpectedToken(Messages::UnterminatedRegExp);
|
|
} else if (classMarker) {
|
|
if (ch == ']') {
|
|
classMarker = false;
|
|
}
|
|
} else {
|
|
if (ch == '/') {
|
|
terminated = true;
|
|
break;
|
|
} else if (ch == '[') {
|
|
classMarker = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!terminated) {
|
|
this->throwUnexpectedToken(Messages::UnterminatedRegExp);
|
|
}
|
|
|
|
// Exclude leading and trailing slash.
|
|
str = str.substr(1, str.length() - 2);
|
|
/*
|
|
return {
|
|
value: body,
|
|
literal: str
|
|
};*/
|
|
if (isAllASCII(str.data(), str.length())) {
|
|
return new ASCIIString(str.data(), str.length());
|
|
} else {
|
|
return new UTF16String(str.data(), str.length());
|
|
}
|
|
}
|
|
|
|
String* scanRegExpFlags()
|
|
{
|
|
// UTF16StringData str = '';
|
|
UTF16StringDataNonGCStd flags;
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.bufferedCharAt(this->index);
|
|
if (!isIdentifierPart(ch)) {
|
|
break;
|
|
}
|
|
|
|
++this->index;
|
|
if (ch == '\\' && !this->eof()) {
|
|
ch = this->source.bufferedCharAt(this->index);
|
|
if (ch == 'u') {
|
|
++this->index;
|
|
const size_t restore = this->index;
|
|
char32_t ch32;
|
|
CharOrEmptyResult res = this->scanHexEscape('u');
|
|
ch32 = res.code;
|
|
if (!res.isEmpty) {
|
|
ParserCharPiece piece(ch32);
|
|
flags += UTF16StringDataNonGCStd(piece.data, piece.length);
|
|
/*
|
|
for (str += '\\u'; restore < this->index; ++restore) {
|
|
str += this->source[restore];
|
|
}*/
|
|
} else {
|
|
this->index = restore;
|
|
flags += 'u';
|
|
// str += '\\u';
|
|
}
|
|
this->tolerateUnexpectedToken();
|
|
} else {
|
|
// str += '\\';
|
|
this->tolerateUnexpectedToken();
|
|
}
|
|
} else {
|
|
flags += ch;
|
|
// str += ch;
|
|
}
|
|
}
|
|
|
|
/*
|
|
return {
|
|
value: flags,
|
|
literal: str
|
|
};
|
|
*/
|
|
if (isAllASCII(flags.data(), flags.length())) {
|
|
return new ASCIIString(flags.data(), flags.length());
|
|
} else {
|
|
return new UTF16String(flags.data(), flags.length());
|
|
}
|
|
}
|
|
|
|
RefPtr<ScannerResult> scanRegExp()
|
|
{
|
|
const size_t start = this->index;
|
|
|
|
String* body = this->scanRegExpBody();
|
|
String* flags = this->scanRegExpFlags();
|
|
// const value = this->testRegExp(body.value, flags.value);
|
|
|
|
ScanRegExpResult result;
|
|
result.body = body;
|
|
result.flags = flags;
|
|
RefPtr<ScannerResult> res = adoptRef(new (createScannerResult()) ScannerResult(this, Token::RegularExpressionToken, StringView(), this->lineNumber, this->lineStart, start, this->index));
|
|
res->valueRegexp = result;
|
|
return res;
|
|
};
|
|
|
|
RefPtr<ScannerResult> lex()
|
|
{
|
|
if (UNLIKELY(this->eof())) {
|
|
return adoptRef(new (createScannerResult()) ScannerResult(this, Token::EOFToken, this->lineNumber, this->lineStart, this->index, this->index));
|
|
}
|
|
|
|
const char16_t cp = this->source.bufferedCharAt(this->index);
|
|
|
|
if (isIdentifierStart(cp)) {
|
|
return this->scanIdentifier(cp);
|
|
}
|
|
|
|
// Very common: ( and ) and ;
|
|
/*
|
|
if (cp == 0x28 || cp == 0x29 || cp == 0x3B) {
|
|
return this->scanPunctuator(cp0);
|
|
}
|
|
*/
|
|
|
|
// String literal starts with single quote (U+0027) or double quote (U+0022).
|
|
if (cp == 0x27 || cp == 0x22) {
|
|
return this->scanStringLiteral();
|
|
}
|
|
|
|
// Dot (.) U+002E can also start a floating-point number, hence the need
|
|
// to check the next character.
|
|
if (cp == 0x2E) {
|
|
if (isDecimalDigit(this->source.bufferedCharAt(this->index + 1))) {
|
|
return this->scanNumericLiteral();
|
|
}
|
|
return this->scanPunctuator(cp);
|
|
}
|
|
|
|
if (isDecimalDigit(cp)) {
|
|
return this->scanNumericLiteral();
|
|
}
|
|
|
|
// Template literals start with ` (U+0060) for template head
|
|
// or } (U+007D) for template middle or template tail.
|
|
if (cp == 0x60 || (cp == 0x7D && (this->curlyStack.size() && strcmp(this->curlyStack.back().m_curly, "${") == 0))) {
|
|
return this->scanTemplate();
|
|
}
|
|
|
|
// Possible identifier start in a surrogate pair.
|
|
if (cp >= 0xD800 && cp < 0xDFFF) {
|
|
if (isIdentifierStart(this->codePointAt(this->index))) {
|
|
return this->scanIdentifier(cp);
|
|
}
|
|
}
|
|
return this->scanPunctuator(cp);
|
|
}
|
|
};
|
|
|
|
inline ScannerResult::~ScannerResult()
|
|
{
|
|
if (this->scanner->isPoolEnabled) {
|
|
if (this->scanner->initialResultMemoryPoolSize < SCANNER_RESULT_POOL_INITIAL_SIZE) {
|
|
this->scanner->initialResultMemoryPool[this->scanner->initialResultMemoryPoolSize++] = this;
|
|
return;
|
|
}
|
|
this->scanner->resultMemoryPool.push_back(this);
|
|
}
|
|
}
|
|
|
|
struct Config : public gc {
|
|
bool range : 1;
|
|
bool loc : 1;
|
|
bool tokens : 1;
|
|
bool comment : 1;
|
|
bool tolerant : 1;
|
|
bool parseSingleFunction : 1;
|
|
CodeBlock* parseSingleFunctionTarget;
|
|
SmallValue parseSingleFunctionChildIndex; // use SmallValue for saving index. this reduce memory leak from stack
|
|
};
|
|
|
|
|
|
struct Context : public gc {
|
|
bool allowIn : 1;
|
|
bool allowYield : 1;
|
|
bool isAssignmentTarget : 1;
|
|
bool isBindingElement : 1;
|
|
bool inFunctionBody : 1;
|
|
bool inIteration : 1;
|
|
bool inSwitch : 1;
|
|
bool inCatch : 1;
|
|
bool inDirectCatchScope : 1;
|
|
bool inWith : 1;
|
|
bool inLoop : 1;
|
|
bool strict : 1;
|
|
RefPtr<ScannerResult> firstCoverInitializedNameError;
|
|
std::vector<std::pair<AtomicString, size_t>> labelSet; // <LabelString, with statement count>
|
|
std::vector<FunctionDeclarationNode*> functionDeclarationsInDirectCatchScope;
|
|
};
|
|
|
|
struct Marker : public gc {
|
|
size_t index;
|
|
size_t lineNumber;
|
|
size_t lineStart;
|
|
};
|
|
|
|
struct MetaNode : public gc {
|
|
size_t index;
|
|
size_t line;
|
|
size_t column;
|
|
};
|
|
|
|
|
|
struct ArrowParameterPlaceHolderNode : public gc {
|
|
String* type;
|
|
ExpressionNodeVector params;
|
|
};
|
|
|
|
struct DeclarationOptions : public gc {
|
|
bool inFor;
|
|
};
|
|
|
|
class Parser : public gc {
|
|
public:
|
|
::Escargot::Context* escargotContext;
|
|
Config config;
|
|
ParserASTNodeHandler delegate;
|
|
ErrorHandler errorHandlerInstance;
|
|
ErrorHandler* errorHandler;
|
|
Scanner* scanner;
|
|
Scanner scannerInstance;
|
|
/*
|
|
std::unordered_map<IdentifierNode*, RefPtr<ScannerResult>,
|
|
std::hash<IdentifierNode*>, std::equal_to<IdentifierNode*>, GCUtil::gc_malloc_ignore_off_page_allocator<std::pair<IdentifierNode*, RefPtr<ScannerResult>>>>
|
|
nodeExtraInfo;
|
|
*/
|
|
enum SourceType {
|
|
Script,
|
|
Module
|
|
};
|
|
SourceType sourceType;
|
|
RefPtr<ScannerResult> lookahead;
|
|
bool hasLineTerminator;
|
|
|
|
Context contextInstance;
|
|
Context* context;
|
|
std::vector<RefPtr<ScannerResult>, gc_allocator_ignore_off_page<RefPtr<ScannerResult>>> tokens;
|
|
Marker startMarker;
|
|
Marker lastMarker;
|
|
|
|
Vector<ASTScopeContext*, gc_allocator_ignore_off_page<ASTScopeContext*>> scopeContexts;
|
|
bool trackUsingNames;
|
|
size_t stackLimit;
|
|
|
|
ASTScopeContext fakeContext;
|
|
|
|
ASTScopeContext* popScopeContext(const MetaNode& node)
|
|
{
|
|
auto ret = scopeContexts.back();
|
|
scopeContexts.pop_back();
|
|
return ret;
|
|
}
|
|
|
|
void extractNamesFromFunctionParams(const PatternNodeVector& vector)
|
|
{
|
|
if (this->config.parseSingleFunction)
|
|
return;
|
|
for (size_t i = 0; i < vector.size(); i++) {
|
|
ASSERT(vector[i]->isIdentifier());
|
|
IdentifierNode* id = (IdentifierNode*)vector[i].get();
|
|
scopeContexts.back()->insertName(id->name(), true);
|
|
}
|
|
}
|
|
|
|
void pushScopeContext(const PatternNodeVector& params, AtomicString functionName)
|
|
{
|
|
if (this->config.parseSingleFunction) {
|
|
fakeContext = ASTScopeContext();
|
|
scopeContexts.push_back(&fakeContext);
|
|
return;
|
|
}
|
|
auto parentContext = scopeContexts.back();
|
|
scopeContexts.push_back(new ASTScopeContext(this->context->strict));
|
|
scopeContexts.back()->m_functionName = functionName;
|
|
scopeContexts.back()->m_inCatch = this->context->inCatch;
|
|
scopeContexts.back()->m_inWith = this->context->inWith;
|
|
scopeContexts.back()->m_parameters.resizeWithUninitializedValues(params.size());
|
|
for (size_t i = 0; i < params.size(); i++) {
|
|
ASSERT(params[i]->isIdentifier());
|
|
IdentifierNode* id = (IdentifierNode*)params[i].get();
|
|
scopeContexts.back()->m_parameters[i] = id->name();
|
|
}
|
|
if (parentContext) {
|
|
parentContext->m_childScopes.push_back(scopeContexts.back());
|
|
}
|
|
}
|
|
|
|
Parser(::Escargot::Context* escargotContext, StringView code, ParserASTNodeHandler delegate, size_t stackRemain, size_t startLine = 0, size_t startColumn = 0)
|
|
: errorHandler(&errorHandlerInstance)
|
|
, scannerInstance(code, this->errorHandler, startLine, startColumn)
|
|
{
|
|
if (stackRemain >= STACK_LIMIT_FROM_BASE) {
|
|
stackRemain = STACK_LIMIT_FROM_BASE;
|
|
}
|
|
volatile int sp;
|
|
volatile size_t currentStackBase = (size_t)&sp;
|
|
#ifdef STACK_GROWS_DOWN
|
|
this->stackLimit = currentStackBase - stackRemain;
|
|
#else
|
|
this->stackLimit = currentStackBase + stackRemain;
|
|
#endif
|
|
this->escargotContext = escargotContext;
|
|
trackUsingNames = true;
|
|
config.range = false;
|
|
config.loc = false;
|
|
// config.source = String::emptyString;
|
|
config.tokens = false;
|
|
config.comment = false;
|
|
config.tolerant = false;
|
|
config.parseSingleFunction = false;
|
|
config.parseSingleFunctionTarget = nullptr;
|
|
config.parseSingleFunctionChildIndex = SmallValue((uint32_t)0);
|
|
/*
|
|
this->config = {
|
|
range: (typeof options.range == 'boolean') && options.range,
|
|
loc: (typeof options.loc == 'boolean') && options.loc,
|
|
source: null,
|
|
tokens: (typeof options.tokens == 'boolean') && options.tokens,
|
|
comment: (typeof options.comment == 'boolean') && options.comment,
|
|
tolerant: (typeof options.tolerant == 'boolean') && options.tolerant
|
|
};
|
|
if (this->config.loc && options.source && options.source !== null) {
|
|
this->config.source = String(options.source);
|
|
}*/
|
|
|
|
this->delegate = delegate;
|
|
|
|
this->scanner = &scannerInstance;
|
|
if (stackRemain >= STACK_LIMIT_FROM_BASE) {
|
|
stackRemain = STACK_LIMIT_FROM_BASE;
|
|
}
|
|
|
|
// this->sourceType = (options && options.sourceType == 'module') ? 'module' : 'script';
|
|
this->sourceType = Script;
|
|
this->lookahead = nullptr;
|
|
this->hasLineTerminator = false;
|
|
|
|
this->context = &contextInstance;
|
|
this->context->allowIn = true;
|
|
this->context->allowYield = true;
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->isBindingElement = true;
|
|
this->context->inFunctionBody = false;
|
|
this->context->inIteration = false;
|
|
this->context->inSwitch = false;
|
|
this->context->inCatch = false;
|
|
this->context->inDirectCatchScope = false;
|
|
this->context->inWith = false;
|
|
this->context->inLoop = false;
|
|
this->context->strict = this->sourceType == Module;
|
|
|
|
this->startMarker.index = 0;
|
|
this->startMarker.lineNumber = this->scanner->lineNumber;
|
|
this->startMarker.lineStart = 0;
|
|
|
|
this->lastMarker.index = 0;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = 0;
|
|
|
|
this->nextToken();
|
|
this->lastMarker.index = this->scanner->index;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = this->scanner->lineStart;
|
|
}
|
|
|
|
void throwError(const char* messageFormat, String* arg0 = String::emptyString, String* arg1 = String::emptyString, ErrorObject::Code code = ErrorObject::SyntaxError)
|
|
{
|
|
UTF16StringDataNonGCStd msg;
|
|
if (arg0->length() && arg1->length()) {
|
|
char message[512];
|
|
UTF8StringData d1 = arg0->toUTF8StringData();
|
|
UTF8StringData d2 = arg1->toUTF8StringData();
|
|
snprintf(message, sizeof(message), messageFormat, d1.data(), d2.data());
|
|
auto temp = utf8StringToUTF16String(message, strlen(message));
|
|
msg = UTF16StringDataNonGCStd(temp.data(), temp.length());
|
|
} else if (arg0->length()) {
|
|
char message[512];
|
|
UTF8StringData d1 = arg0->toUTF8StringData();
|
|
snprintf(message, sizeof(message), messageFormat, d1.data());
|
|
auto temp = utf8StringToUTF16String(message, strlen(message));
|
|
msg = UTF16StringDataNonGCStd(temp.data(), temp.length());
|
|
} else {
|
|
msg.assign(messageFormat, &messageFormat[strlen(messageFormat)]);
|
|
}
|
|
|
|
size_t index = this->lastMarker.index;
|
|
size_t line = this->lastMarker.lineNumber;
|
|
size_t column = this->lastMarker.index - this->lastMarker.lineStart + 1;
|
|
throw this->errorHandler->createError(index, line, column, new UTF16String(msg.data(), msg.length()), code);
|
|
}
|
|
|
|
void tolerateError(const char* messageFormat, String* arg0 = String::emptyString, String* arg1 = String::emptyString, ErrorObject::Code code = ErrorObject::SyntaxError)
|
|
{
|
|
throwError(messageFormat, arg0, arg1, code);
|
|
}
|
|
|
|
void replaceAll(UTF16StringDataNonGCStd& str, const UTF16StringDataNonGCStd& from, const UTF16StringDataNonGCStd& to)
|
|
{
|
|
if (from.empty())
|
|
return;
|
|
size_t start_pos = 0;
|
|
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
|
str.replace(start_pos, from.length(), to);
|
|
start_pos += to.length();
|
|
}
|
|
}
|
|
|
|
// Throw an exception because of the token.
|
|
Error* unexpectedTokenError(RefPtr<ScannerResult> token = nullptr, const char* message = nullptr)
|
|
{
|
|
const char* msg;
|
|
if (message)
|
|
msg = message;
|
|
else
|
|
msg = Messages::UnexpectedToken;
|
|
|
|
String* value;
|
|
if (token) {
|
|
if (!msg) {
|
|
msg = (token->type == Token::EOFToken) ? Messages::UnexpectedEOS : (token->type == Token::IdentifierToken) ? Messages::UnexpectedIdentifier : (token->type == Token::NumericLiteralToken) ? Messages::UnexpectedNumber : (token->type == Token::StringLiteralToken) ? Messages::UnexpectedString : (token->type == Token::TemplateToken) ? Messages::UnexpectedTemplate : Messages::UnexpectedToken;
|
|
|
|
if (token->type == Token::KeywordToken) {
|
|
if (this->scanner->isFutureReservedWord(token->valueString)) {
|
|
msg = Messages::UnexpectedReserved;
|
|
} else if (this->context->strict && this->scanner->isStrictModeReservedWord(token->valueString)) {
|
|
msg = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
value = new StringView((token->type == Token::TemplateToken) ? token->valueTemplate->raw : token->valueString);
|
|
} else {
|
|
value = new ASCIIString("ILLEGAL");
|
|
}
|
|
|
|
// msg = msg.replace('%0', value);
|
|
UTF16StringDataNonGCStd msgData;
|
|
msgData.assign(msg, &msg[strlen(msg)]);
|
|
UTF16StringDataNonGCStd valueData = UTF16StringDataNonGCStd(value->toUTF16StringData().data());
|
|
replaceAll(msgData, UTF16StringDataNonGCStd(u"%s"), valueData);
|
|
|
|
// if (token && typeof token.lineNumber == 'number') {
|
|
if (token) {
|
|
const size_t index = token->start;
|
|
const size_t line = token->lineNumber;
|
|
const size_t column = token->start - this->lastMarker.lineStart + 1;
|
|
return this->errorHandler->createError(index, line, column, new UTF16String(msgData.data(), msgData.length()), ErrorObject::SyntaxError);
|
|
} else {
|
|
const size_t index = this->lastMarker.index;
|
|
const size_t line = this->lastMarker.lineNumber;
|
|
const size_t column = index - this->lastMarker.lineStart + 1;
|
|
return this->errorHandler->createError(index, line, column, new UTF16String(msgData.data(), msgData.length()), ErrorObject::SyntaxError);
|
|
}
|
|
}
|
|
|
|
void throwUnexpectedToken(RefPtr<ScannerResult> token, const char* message = nullptr)
|
|
{
|
|
throw this->unexpectedTokenError(token, message);
|
|
}
|
|
|
|
void tolerateUnexpectedToken(RefPtr<ScannerResult> token, const char* message = nullptr)
|
|
{
|
|
// this->errorHandler.tolerate(this->unexpectedTokenError(token, message));
|
|
throwUnexpectedToken(token, message);
|
|
}
|
|
|
|
void collectComments()
|
|
{
|
|
this->scanner->scanComments();
|
|
/*
|
|
if (!this->config.comment) {
|
|
this->scanner->scanComments();
|
|
} else {
|
|
const comments: Comment[] = this->scanner->scanComments();
|
|
if (comments.length > 0 && this->delegate) {
|
|
for (let i = 0; i < comments.length; ++i) {
|
|
const e: Comment = comments[i];
|
|
let node;
|
|
node = {
|
|
type: e.multiLine ? 'BlockComment' : 'LineComment',
|
|
value: this->scanner->source.slice(e.slice[0], e.slice[1])
|
|
};
|
|
if (this->config.range) {
|
|
node.range = e.range;
|
|
}
|
|
if (this->config.loc) {
|
|
node.loc = e.loc;
|
|
}
|
|
const metadata = {
|
|
start: {
|
|
line: e.loc.start.line,
|
|
column: e.loc.start.column,
|
|
offset: e.range[0]
|
|
},
|
|
end: {
|
|
line: e.loc.end.line,
|
|
column: e.loc.end.column,
|
|
offset: e.range[1]
|
|
}
|
|
};
|
|
this->delegate(node, metadata);
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
RefPtr<ScannerResult> nextToken()
|
|
{
|
|
RefPtr<ScannerResult> token = this->lookahead;
|
|
|
|
this->lastMarker.index = this->scanner->index;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = this->scanner->lineStart;
|
|
|
|
this->collectComments();
|
|
|
|
this->startMarker.index = this->scanner->index;
|
|
this->startMarker.lineNumber = this->scanner->lineNumber;
|
|
this->startMarker.lineStart = this->scanner->lineStart;
|
|
|
|
RefPtr<ScannerResult> next = this->scanner->lex();
|
|
this->hasLineTerminator = (token && next) ? (token->lineNumber != next->lineNumber) : false;
|
|
|
|
if (next && this->context->strict && next->type == Token::IdentifierToken) {
|
|
if (this->scanner->isStrictModeReservedWord(next->valueString)) {
|
|
next->type = Token::KeywordToken;
|
|
}
|
|
}
|
|
this->lookahead = next;
|
|
|
|
/*
|
|
if (this->config.tokens && next.type !== Token.EOF) {
|
|
this->tokens.push(this->convertToken(next));
|
|
}
|
|
*/
|
|
|
|
return token;
|
|
}
|
|
|
|
RefPtr<ScannerResult> nextRegexToken()
|
|
{
|
|
this->collectComments();
|
|
|
|
RefPtr<ScannerResult> token = this->scanner->scanRegExp();
|
|
/*
|
|
if (this->config.tokens) {
|
|
// Pop the previous token, '/' or '/='
|
|
// This is added from the lookahead token.
|
|
this->tokens.pop();
|
|
|
|
this->tokens.push(this->convertToken(token));
|
|
}*/
|
|
|
|
// Prime the next lookahead.
|
|
this->lookahead = token;
|
|
this->nextToken();
|
|
|
|
return token;
|
|
}
|
|
|
|
bool inProgramParsingAndInFunctionSourceNode()
|
|
{
|
|
return !this->config.parseSingleFunction && this->scopeContexts.size() > 1;
|
|
}
|
|
|
|
MetaNode createNode()
|
|
{
|
|
MetaNode n;
|
|
n.index = this->startMarker.index;
|
|
n.line = this->startMarker.lineNumber;
|
|
n.column = this->startMarker.index - this->startMarker.lineStart;
|
|
return n;
|
|
}
|
|
|
|
MetaNode startNode(RefPtr<ScannerResult> token)
|
|
{
|
|
MetaNode n;
|
|
n.index = token->start;
|
|
n.line = token->lineNumber;
|
|
n.column = token->start - token->lineStart;
|
|
return n;
|
|
}
|
|
|
|
template <typename T>
|
|
RefPtr<T> finalize(MetaNode meta, T* node)
|
|
{
|
|
/*
|
|
if (this->config.range) {
|
|
node.range = [meta.index, this->lastMarker.index];
|
|
}
|
|
|
|
if (this->config.loc) {
|
|
node.loc = {
|
|
start: {
|
|
line: meta.line,
|
|
column: meta.column
|
|
},
|
|
end: {
|
|
line: this->lastMarker.lineNumber,
|
|
column: this->lastMarker.index - this->lastMarker.lineStart
|
|
}
|
|
};
|
|
if (this->config.source) {
|
|
node.loc.source = this->config.source;
|
|
}
|
|
}*/
|
|
auto type = node->type();
|
|
if (type == CallExpression) {
|
|
CallExpressionNode* c = (CallExpressionNode*)node;
|
|
if (c->callee()->isIdentifier()) {
|
|
if (((IdentifierNode*)c->callee())->name().string()->equals("eval")) {
|
|
scopeContexts.back()->m_hasEval = true;
|
|
}
|
|
}
|
|
} else if (type == WithStatement) {
|
|
scopeContexts.back()->m_hasWith = true;
|
|
} else if (type == YieldExpression) {
|
|
scopeContexts.back()->m_hasYield = true;
|
|
} else if (type == CatchClause) {
|
|
scopeContexts.back()->m_hasCatch = true;
|
|
}
|
|
|
|
node->m_loc = NodeLOC(meta.index);
|
|
if (this->delegate) {
|
|
this->delegate(node, NodeLOC(meta.index),
|
|
NodeLOC(this->lastMarker.index));
|
|
}
|
|
|
|
return adoptRef(node);
|
|
}
|
|
|
|
// Expect the next token to match the specified punctuator.
|
|
// If not, an exception will be thrown.
|
|
|
|
void expect(PunctuatorsKind value)
|
|
{
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
if (token->type != Token::PunctuatorToken || token->valuePunctuatorsKind != value) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
|
|
// Quietly expect a comma when in tolerant mode, otherwise delegates to expect().
|
|
|
|
void expectCommaSeparator()
|
|
{
|
|
/*
|
|
if (this->config.tolerant) {
|
|
let token = this->lookahead;
|
|
if (token.type == Token.Punctuator && token.value == ',') {
|
|
this->nextToken();
|
|
} else if (token.type == Token.Punctuator && token.value == ';') {
|
|
this->nextToken();
|
|
this->tolerateUnexpectedToken(token);
|
|
} else {
|
|
this->tolerateUnexpectedToken(token, Messages.UnexpectedToken);
|
|
}
|
|
} else {
|
|
this->expect(',');
|
|
}*/
|
|
this->expect(PunctuatorsKind::Comma);
|
|
}
|
|
|
|
// Expect the next token to match the specified keyword.
|
|
// If not, an exception will be thrown.
|
|
|
|
void expectKeyword(KeywordKind keyword)
|
|
{
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
if (token->type != Token::KeywordToken || token->valueKeywordKind != keyword) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
|
|
// Return true if the next token matches the specified punctuator.
|
|
|
|
bool match(PunctuatorsKind value)
|
|
{
|
|
return this->lookahead->type == Token::PunctuatorToken && this->lookahead->valuePunctuatorsKind == value;
|
|
}
|
|
|
|
// Return true if the next token matches the specified keyword
|
|
|
|
bool matchKeyword(KeywordKind keyword)
|
|
{
|
|
return this->lookahead->type == Token::KeywordToken && this->lookahead->valueKeywordKind == keyword;
|
|
}
|
|
|
|
// Return true if the next token matches the specified contextual keyword
|
|
// (where an identifier is sometimes a keyword depending on the context)
|
|
|
|
bool matchContextualKeyword(KeywordKind keyword)
|
|
{
|
|
return this->lookahead->type == Token::IdentifierToken && this->lookahead->valueKeywordKind == keyword;
|
|
}
|
|
|
|
// Return true if the next token is an assignment operator
|
|
|
|
bool matchAssign()
|
|
{
|
|
if (this->lookahead->type != Token::PunctuatorToken) {
|
|
return false;
|
|
}
|
|
PunctuatorsKind op = this->lookahead->valuePunctuatorsKind;
|
|
|
|
if (op >= Substitution && op < SubstitutionEnd) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Cover grammar support.
|
|
//
|
|
// When an assignment expression position starts with an left parenthesis, the determination of the type
|
|
// of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead)
|
|
// or the first comma. This situation also defers the determination of all the expressions nested in the pair.
|
|
//
|
|
// There are three productions that can be parsed in a parentheses pair that needs to be determined
|
|
// after the outermost pair is closed. They are:
|
|
//
|
|
// 1. AssignmentExpression
|
|
// 2. BindingElements
|
|
// 3. AssignmentTargets
|
|
//
|
|
// In order to avoid exponential backtracking, we use two flags to denote if the production can be
|
|
// binding element or assignment target.
|
|
//
|
|
// The three productions have the relationship:
|
|
//
|
|
// BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression
|
|
//
|
|
// with a single exception that CoverInitializedName when used directly in an Expression, generates
|
|
// an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the
|
|
// first usage of CoverInitializedName and report it when we reached the end of the parentheses pair.
|
|
//
|
|
// isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not
|
|
// effect the current flags. This means the production the parser parses is only used as an expression. Therefore
|
|
// the CoverInitializedName check is conducted.
|
|
//
|
|
// inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates
|
|
// the flags outside of the parser. This means the production the parser parses is used as a part of a potential
|
|
// pattern. The CoverInitializedName check is deferred.
|
|
|
|
typedef Node* (Parser::*ParseFunction)();
|
|
|
|
void checkRecursiveLimit()
|
|
{
|
|
volatile int sp;
|
|
size_t currentStackBase = (size_t)&sp;
|
|
#ifdef STACK_GROWS_DOWN
|
|
if (UNLIKELY(currentStackBase < stackLimit)) {
|
|
#else
|
|
if (UNLIKELY(currentStackBase > stackLimit)) {
|
|
#endif
|
|
this->throwError("too many recursion in script", String::emptyString, String::emptyString, ErrorObject::RangeError);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
RefPtr<Node> isolateCoverGrammar(T parseFunction)
|
|
{
|
|
const bool previousIsBindingElement = this->context->isBindingElement;
|
|
const bool previousIsAssignmentTarget = this->context->isAssignmentTarget;
|
|
RefPtr<ScannerResult> previousFirstCoverInitializedNameError = this->context->firstCoverInitializedNameError;
|
|
|
|
this->context->isBindingElement = true;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
|
|
this->checkRecursiveLimit();
|
|
RefPtr<Node> result = (this->*parseFunction)();
|
|
if (this->context->firstCoverInitializedNameError != nullptr) {
|
|
this->throwUnexpectedToken(this->context->firstCoverInitializedNameError);
|
|
}
|
|
|
|
this->context->isBindingElement = previousIsBindingElement;
|
|
this->context->isAssignmentTarget = previousIsAssignmentTarget;
|
|
this->context->firstCoverInitializedNameError = previousFirstCoverInitializedNameError;
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
RefPtr<Node> isolateCoverGrammarWithFunctor(T parseFunction)
|
|
{
|
|
const bool previousIsBindingElement = this->context->isBindingElement;
|
|
const bool previousIsAssignmentTarget = this->context->isAssignmentTarget;
|
|
RefPtr<ScannerResult> previousFirstCoverInitializedNameError = this->context->firstCoverInitializedNameError;
|
|
|
|
this->context->isBindingElement = true;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
|
|
this->checkRecursiveLimit();
|
|
RefPtr<Node> result = parseFunction();
|
|
if (this->context->firstCoverInitializedNameError != nullptr) {
|
|
this->throwUnexpectedToken(this->context->firstCoverInitializedNameError);
|
|
}
|
|
|
|
this->context->isBindingElement = previousIsBindingElement;
|
|
this->context->isAssignmentTarget = previousIsAssignmentTarget;
|
|
this->context->firstCoverInitializedNameError = previousFirstCoverInitializedNameError;
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
RefPtr<Node> inheritCoverGrammar(T parseFunction)
|
|
{
|
|
const bool previousIsBindingElement = this->context->isBindingElement;
|
|
const bool previousIsAssignmentTarget = this->context->isAssignmentTarget;
|
|
RefPtr<ScannerResult> previousFirstCoverInitializedNameError = this->context->firstCoverInitializedNameError;
|
|
|
|
this->context->isBindingElement = true;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
|
|
this->checkRecursiveLimit();
|
|
RefPtr<Node> result = (this->*parseFunction)();
|
|
|
|
this->context->isBindingElement = this->context->isBindingElement && previousIsBindingElement;
|
|
this->context->isAssignmentTarget = this->context->isAssignmentTarget && previousIsAssignmentTarget;
|
|
if (previousFirstCoverInitializedNameError)
|
|
this->context->firstCoverInitializedNameError = previousFirstCoverInitializedNameError;
|
|
|
|
return result;
|
|
}
|
|
|
|
void consumeSemicolon()
|
|
{
|
|
if (this->match(PunctuatorsKind::SemiColon)) {
|
|
this->nextToken();
|
|
} else if (!this->hasLineTerminator) {
|
|
if (this->lookahead->type != Token::EOFToken && !this->match(PunctuatorsKind::RightBrace)) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
this->lastMarker.index = this->startMarker.index;
|
|
this->lastMarker.lineNumber = this->startMarker.lineNumber;
|
|
this->lastMarker.lineStart = this->startMarker.lineStart;
|
|
}
|
|
}
|
|
|
|
IdentifierNode* finishIdentifier(RefPtr<ScannerResult> token, bool isScopeVariableName)
|
|
{
|
|
auto ret = new IdentifierNode(AtomicString(this->escargotContext, token->valueString));
|
|
// nodeExtraInfo.insert(std::make_pair(ret, token));
|
|
if (trackUsingNames)
|
|
scopeContexts.back()->insertUsingName(ret->name());
|
|
return ret;
|
|
}
|
|
|
|
#define DEFINE_AS_NODE(TypeName) \
|
|
RefPtr<TypeName##Node> as##TypeName##Node(RefPtr<Node> n) \
|
|
{ \
|
|
if (!n) \
|
|
return RefPtr<TypeName##Node>(nullptr); \
|
|
ASSERT(n->is##TypeName##Node()); \
|
|
return RefPtr<TypeName##Node>((TypeName##Node*)n.get()); \
|
|
}
|
|
|
|
DEFINE_AS_NODE(Expression);
|
|
DEFINE_AS_NODE(Statement);
|
|
|
|
// ECMA-262 12.2 Primary Expressions
|
|
|
|
RefPtr<Node> parsePrimaryExpression()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
RefPtr<Node> expr;
|
|
// let value, token, raw;
|
|
RefPtr<ScannerResult> token;
|
|
|
|
switch (this->lookahead->type) {
|
|
case Token::IdentifierToken:
|
|
if (this->sourceType == SourceType::Module && this->lookahead->valueKeywordKind == KeywordKind::Await) {
|
|
this->tolerateUnexpectedToken(this->lookahead);
|
|
}
|
|
expr = this->finalize(node, finishIdentifier(this->nextToken(), true));
|
|
break;
|
|
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
if (this->context->strict && this->lookahead->octal) {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages::StrictOctalLiteral);
|
|
}
|
|
if (this->context->strict && this->lookahead->startWithZero) {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages::StrictLeadingZeroLiteral);
|
|
}
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
token = this->nextToken();
|
|
// raw = this->getTokenRaw(token);
|
|
if (token->type == Token::NumericLiteralToken) {
|
|
if (this->context->inLoop || token->valueNumber == 0)
|
|
this->scopeContexts.back()->insertNumeralLiteral(Value(token->valueNumber));
|
|
expr = this->finalize(node, new LiteralNode(Value(token->valueNumber)));
|
|
} else {
|
|
if (inProgramParsingAndInFunctionSourceNode()) {
|
|
expr = this->finalize(node, new LiteralNode(Value(String::emptyString)));
|
|
} else {
|
|
auto sv = StringView::createStringView(token->valueString);
|
|
expr = this->finalize(node, new LiteralNode(Value(sv)));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Token::BooleanLiteralToken:
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
token = this->nextToken();
|
|
// token.value = (token.value === 'true');
|
|
// raw = this->getTokenRaw(token);
|
|
{
|
|
bool value = token->valueString == "true";
|
|
this->scopeContexts.back()->insertNumeralLiteral(Value(value));
|
|
expr = this->finalize(node, new LiteralNode(Value(value)));
|
|
}
|
|
break;
|
|
|
|
case Token::NullLiteralToken:
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
token = this->nextToken();
|
|
// token.value = null;
|
|
// raw = this->getTokenRaw(token);
|
|
this->scopeContexts.back()->insertNumeralLiteral(Value(Value::Null));
|
|
expr = this->finalize(node, new LiteralNode(Value(Value::Null)));
|
|
break;
|
|
|
|
case Token::TemplateToken:
|
|
expr = this->parseTemplateLiteral();
|
|
break;
|
|
|
|
case Token::PunctuatorToken: {
|
|
PunctuatorsKind value = this->lookahead->valuePunctuatorsKind;
|
|
switch (value) {
|
|
case LeftParenthesis:
|
|
this->context->isBindingElement = false;
|
|
expr = this->inheritCoverGrammar(&Parser::parseGroupExpression);
|
|
break;
|
|
case LeftSquareBracket:
|
|
expr = this->inheritCoverGrammar(&Parser::parseArrayInitializer);
|
|
break;
|
|
case LeftBrace:
|
|
expr = this->inheritCoverGrammar(&Parser::parseObjectInitializer);
|
|
break;
|
|
case Divide:
|
|
case DivideEqual:
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->scanner->index = this->startMarker.index;
|
|
token = this->nextRegexToken();
|
|
// raw = this->getTokenRaw(token);
|
|
expr = this->finalize(node, new RegExpLiteralNode(token->valueRegexp.body, token->valueRegexp.flags));
|
|
break;
|
|
default:
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Token::KeywordToken:
|
|
if (!this->context->strict && this->context->allowYield && this->matchKeyword(KeywordKind::Yield)) {
|
|
expr = this->parseIdentifierName();
|
|
} else if (!this->context->strict && this->matchKeyword(KeywordKind::Let)) {
|
|
throwUnexpectedToken(this->nextToken());
|
|
} else {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
if (this->matchKeyword(KeywordKind::Function)) {
|
|
expr = this->parseFunctionExpression();
|
|
} else if (this->matchKeyword(KeywordKind::This)) {
|
|
this->nextToken();
|
|
expr = this->finalize(node, new ThisExpressionNode());
|
|
} else if (this->matchKeyword(KeywordKind::Class)) {
|
|
expr = this->parseClassExpression();
|
|
} else {
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
struct ParseParameterOptions {
|
|
PatternNodeVector params;
|
|
std::vector<AtomicString, gc_allocator<AtomicString>> paramSet;
|
|
RefPtr<ScannerResult> stricted;
|
|
const char* message;
|
|
RefPtr<ScannerResult> firstRestricted;
|
|
ParseParameterOptions()
|
|
{
|
|
firstRestricted = nullptr;
|
|
stricted = nullptr;
|
|
message = nullptr;
|
|
}
|
|
};
|
|
|
|
|
|
void validateParam(ParseParameterOptions& options, RefPtr<ScannerResult> param, AtomicString name)
|
|
{
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
options.stricted = param;
|
|
options.message = Messages::StrictParamName;
|
|
}
|
|
if (std::find(options.paramSet.begin(), options.paramSet.end(), name) != options.paramSet.end()) {
|
|
options.stricted = param;
|
|
options.message = Messages::StrictParamDupe;
|
|
}
|
|
} else if (!options.firstRestricted) {
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
options.firstRestricted = param;
|
|
options.message = Messages::StrictParamName;
|
|
} else if (this->scanner->isStrictModeReservedWord(name)) {
|
|
options.firstRestricted = param;
|
|
options.message = Messages::StrictReservedWord;
|
|
} else if (std::find(options.paramSet.begin(), options.paramSet.end(), name) != options.paramSet.end()) {
|
|
options.stricted = param;
|
|
options.message = Messages::StrictParamDupe;
|
|
}
|
|
}
|
|
options.paramSet.push_back(name);
|
|
}
|
|
|
|
RefPtr<RestElementNode> parseRestElement(std::vector<RefPtr<ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<ScannerResult>>>& params)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->throwError("Rest element is not supported yet");
|
|
|
|
this->nextToken();
|
|
if (this->match(LeftBrace)) {
|
|
this->throwError(Messages::ObjectPatternAsRestParameter);
|
|
}
|
|
params.push_back(this->lookahead);
|
|
|
|
RefPtr<IdentifierNode> param = this->parseVariableIdentifier();
|
|
if (this->match(Equal)) {
|
|
this->throwError(Messages::DefaultRestParameter);
|
|
}
|
|
if (!this->match(RightParenthesis)) {
|
|
this->throwError(Messages::ParameterAfterRestParameter);
|
|
}
|
|
|
|
return this->finalize(node, new RestElementNode(param.get()));
|
|
}
|
|
|
|
RefPtr<Node> parsePattern(std::vector<RefPtr<ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<ScannerResult>>>& params, String* kind = String::emptyString)
|
|
{
|
|
RefPtr<Node> pattern;
|
|
|
|
if (this->match(LeftSquareBracket)) {
|
|
this->throwError("Array pattern is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// pattern = this->parseArrayPattern(params, kind);
|
|
} else if (this->match(LeftBrace)) {
|
|
this->throwError("Object pattern is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// pattern = this->parseObjectPattern(params, kind);
|
|
} else {
|
|
if (this->matchKeyword(KeywordKind::Let) && (kind->equals("const") || kind->equals("let"))) {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages::UnexpectedToken);
|
|
}
|
|
params.push_back(this->lookahead);
|
|
pattern = this->parseVariableIdentifier(kind);
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
|
|
RefPtr<Node> parsePatternWithDefault(std::vector<RefPtr<ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<ScannerResult>>>& params, String* kind = String::emptyString)
|
|
{
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
|
|
RefPtr<Node> pattern = this->parsePattern(params, kind);
|
|
if (this->match(PunctuatorsKind::Substitution)) {
|
|
this->throwError("Assignment in parameter is not supported yet");
|
|
|
|
this->nextToken();
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = true;
|
|
RefPtr<Node> right = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->context->allowYield = previousAllowYield;
|
|
pattern = this->finalize(this->startNode(startToken), new AssignmentExpressionSimpleNode(pattern.get(), right.get()));
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
|
|
bool parseFormalParameter(ParseParameterOptions& options)
|
|
{
|
|
RefPtr<Node> param;
|
|
bool trackUsingNamesBefore = trackUsingNames;
|
|
trackUsingNames = false;
|
|
std::vector<RefPtr<ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<ScannerResult>>> params;
|
|
RefPtr<ScannerResult> token = this->lookahead;
|
|
if (token->type == Token::PunctuatorToken && token->valuePunctuatorsKind == PunctuatorsKind::PeriodPeriodPeriod) {
|
|
RefPtr<RestElementNode> param = this->parseRestElement(params);
|
|
this->validateParam(options, params.back(), param->argument()->name());
|
|
options.params.push_back(param);
|
|
trackUsingNames = true;
|
|
return false;
|
|
}
|
|
|
|
param = this->parsePatternWithDefault(params);
|
|
for (size_t i = 0; i < params.size(); i++) {
|
|
AtomicString as(this->escargotContext, params[i]->valueString);
|
|
this->validateParam(options, params[i], as);
|
|
}
|
|
options.params.push_back(param);
|
|
trackUsingNames = trackUsingNamesBefore;
|
|
return !this->match(PunctuatorsKind::RightParenthesis);
|
|
}
|
|
|
|
struct ParseFormalParametersResult {
|
|
PatternNodeVector params;
|
|
RefPtr<ScannerResult> stricted;
|
|
RefPtr<ScannerResult> firstRestricted;
|
|
const char* message;
|
|
ParseFormalParametersResult(PatternNodeVector params, RefPtr<ScannerResult> stricted, RefPtr<ScannerResult> firstRestricted, const char* message)
|
|
{
|
|
this->params = std::move(params);
|
|
this->stricted = stricted;
|
|
this->firstRestricted = firstRestricted;
|
|
this->message = nullptr;
|
|
}
|
|
};
|
|
|
|
ParseFormalParametersResult parseFormalParameters(RefPtr<ScannerResult> firstRestricted = nullptr)
|
|
{
|
|
ParseParameterOptions options;
|
|
|
|
options.firstRestricted = firstRestricted;
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (!this->match(RightParenthesis)) {
|
|
options.paramSet.clear();
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->parseFormalParameter(options)) {
|
|
break;
|
|
}
|
|
this->expect(Comma);
|
|
}
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
return ParseFormalParametersResult(options.params, options.stricted, options.firstRestricted, options.message);
|
|
}
|
|
|
|
// ECMA-262 12.2.5 Array Initializer
|
|
|
|
RefPtr<SpreadElementNode> parseSpreadElement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(PunctuatorsKind::PeriodPeriodPeriod);
|
|
RefPtr<Node> arg = this->inheritCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->throwError("Spread element is not supported yet");
|
|
return this->finalize(node, new SpreadElementNode(arg.get()));
|
|
}
|
|
|
|
RefPtr<Node> parseArrayInitializer()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
// const elements: Node.ArrayExpressionElement[] = [];
|
|
ExpressionNodeVector elements;
|
|
|
|
this->expect(LeftSquareBracket);
|
|
while (!this->match(RightSquareBracket)) {
|
|
if (this->match(Comma)) {
|
|
this->nextToken();
|
|
elements.push_back(nullptr);
|
|
} else if (this->match(PeriodPeriodPeriod)) {
|
|
RefPtr<SpreadElementNode> element = this->parseSpreadElement();
|
|
if (!this->match(RightSquareBracket)) {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->expect(Comma);
|
|
}
|
|
elements.push_back(element);
|
|
} else {
|
|
elements.push_back(this->inheritCoverGrammar(&Parser::parseAssignmentExpression));
|
|
if (!this->match(RightSquareBracket)) {
|
|
this->expect(Comma);
|
|
}
|
|
}
|
|
}
|
|
this->expect(RightSquareBracket);
|
|
|
|
return this->finalize(node, new ArrayExpressionNode(std::move(elements)));
|
|
}
|
|
|
|
// ECMA-262 12.2.6 Object Initializer
|
|
|
|
RefPtr<Node> parsePropertyMethod(ParseFormalParametersResult& params)
|
|
{
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
pushScopeContext(params.params, AtomicString());
|
|
extractNamesFromFunctionParams(params.params);
|
|
const bool previousStrict = this->context->strict;
|
|
RefPtr<Node> body = this->isolateCoverGrammar(&Parser::parseFunctionSourceElements);
|
|
if (this->context->strict && params.firstRestricted) {
|
|
this->tolerateUnexpectedToken(params.firstRestricted, params.message);
|
|
}
|
|
if (this->context->strict && params.stricted) {
|
|
this->tolerateUnexpectedToken(params.stricted, params.message);
|
|
}
|
|
this->context->strict = previousStrict;
|
|
|
|
return body;
|
|
}
|
|
|
|
RefPtr<FunctionExpressionNode> parsePropertyMethodFunction()
|
|
{
|
|
const bool isGenerator = false;
|
|
MetaNode node = this->createNode();
|
|
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = false;
|
|
ParseFormalParametersResult params = this->parseFormalParameters();
|
|
RefPtr<Node> method = this->parsePropertyMethod(params);
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(params.params), method.get(), popScopeContext(node), isGenerator));
|
|
}
|
|
|
|
RefPtr<Node> parseObjectPropertyKey()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
|
|
RefPtr<Node> key;
|
|
switch (token->type) {
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
if (this->context->strict && token->octal) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictOctalLiteral);
|
|
}
|
|
if (this->context->strict && this->lookahead->startWithZero) {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages::StrictLeadingZeroLiteral);
|
|
}
|
|
// const raw = this->getTokenRaw(token);
|
|
{
|
|
Value v;
|
|
if (token->type == Token::NumericLiteralToken) {
|
|
if (this->context->inLoop || token->valueNumber == 0)
|
|
this->scopeContexts.back()->insertNumeralLiteral(Value(token->valueNumber));
|
|
v = Value(token->valueNumber);
|
|
} else {
|
|
auto sv = StringView::createStringView(token->valueString);
|
|
v = Value(sv);
|
|
}
|
|
key = this->finalize(node, new LiteralNode(v));
|
|
}
|
|
break;
|
|
|
|
case Token::IdentifierToken:
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::KeywordToken: {
|
|
bool trackUsingNamesBefore = this->trackUsingNames;
|
|
this->trackUsingNames = false;
|
|
key = this->finalize(node, finishIdentifier(token, false));
|
|
this->trackUsingNames = trackUsingNamesBefore;
|
|
break;
|
|
}
|
|
case Token::PunctuatorToken:
|
|
if (token->valuePunctuatorsKind == LeftSquareBracket) {
|
|
key = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->expect(RightSquareBracket);
|
|
} else {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
bool qualifiedPropertyName(RefPtr<ScannerResult> token)
|
|
{
|
|
switch (token->type) {
|
|
case Token::IdentifierToken:
|
|
case Token::StringLiteralToken:
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::NumericLiteralToken:
|
|
case Token::KeywordToken:
|
|
return true;
|
|
case Token::PunctuatorToken:
|
|
return token->valuePunctuatorsKind == LeftSquareBracket;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool isPropertyKey(Node* key, const char* value)
|
|
{
|
|
if (key->type() == Identifier) {
|
|
return ((IdentifierNode*)key)->name() == value;
|
|
} else if (key->type() == Literal) {
|
|
if (((LiteralNode*)key)->value().isString()) {
|
|
return ((LiteralNode*)key)->value().asString()->equals(value);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
RefPtr<PropertyNode> parseObjectProperty(bool& hasProto, std::vector<std::pair<AtomicString, size_t>>& usedNames) //: Node.Property
|
|
{
|
|
MetaNode node = this->createNode();
|
|
RefPtr<ScannerResult> token = this->lookahead;
|
|
|
|
PropertyNode::Kind kind;
|
|
RefPtr<Node> key; //'': Node.PropertyKey;
|
|
RefPtr<Node> value; //: Node.PropertyValue;
|
|
|
|
bool computed = false;
|
|
bool method = false;
|
|
bool shorthand = false;
|
|
|
|
if (token->type == Token::IdentifierToken) {
|
|
this->nextToken();
|
|
key = this->finalize(node, finishIdentifier(token, true));
|
|
} else if (this->match(PunctuatorsKind::Multiply)) {
|
|
this->nextToken();
|
|
} else {
|
|
computed = this->match(LeftSquareBracket);
|
|
key = this->parseObjectPropertyKey();
|
|
}
|
|
|
|
bool lookaheadPropertyKey = this->qualifiedPropertyName(this->lookahead);
|
|
if (token->type == Token::IdentifierToken && token->valueString == "get" && lookaheadPropertyKey) {
|
|
kind = PropertyNode::Kind::Get;
|
|
computed = this->match(LeftSquareBracket);
|
|
key = this->parseObjectPropertyKey();
|
|
this->context->allowYield = false;
|
|
value = this->parseGetterMethod();
|
|
|
|
} else if (token->type == Token::IdentifierToken && token->valueString == "set" && lookaheadPropertyKey) {
|
|
kind = PropertyNode::Kind::Set;
|
|
computed = this->match(LeftSquareBracket);
|
|
key = this->parseObjectPropertyKey();
|
|
value = this->parseSetterMethod();
|
|
|
|
} else if (token->type == Token::PunctuatorToken && token->valueString == "*" && lookaheadPropertyKey) {
|
|
kind = PropertyNode::Kind::Init;
|
|
computed = this->match(LeftSquareBracket);
|
|
key = this->parseObjectPropertyKey();
|
|
value = this->parseGeneratorMethod();
|
|
method = true;
|
|
|
|
} else {
|
|
if (!key) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
kind = PropertyNode::Kind::Init;
|
|
if (this->match(PunctuatorsKind::Colon)) {
|
|
if (!computed && this->isPropertyKey(key.get(), "__proto__")) {
|
|
if (hasProto) {
|
|
this->tolerateError(Messages::DuplicateProtoProperty);
|
|
}
|
|
hasProto = true;
|
|
}
|
|
this->nextToken();
|
|
value = this->inheritCoverGrammar(&Parser::parseAssignmentExpression);
|
|
|
|
/* TODO(ES6) this part is only for es6
|
|
} else if (this->match(LeftParenthesis)) {
|
|
value = this->parsePropertyMethodFunction();
|
|
method = true;
|
|
|
|
} else if (token->type == Token::IdentifierToken) {
|
|
Node* id = this->finalize(node, finishIdentifier(token, true));
|
|
if (this->match(Substitution)) {
|
|
this->context->firstCoverInitializedNameError = this->lookahead;
|
|
this->nextToken();
|
|
shorthand = true;
|
|
Node* init = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
value = this->finalize(node, new AssignmentExpressionSimpleNode(id, init));
|
|
} else {
|
|
shorthand = true;
|
|
value = id;
|
|
}
|
|
*/
|
|
} else {
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
}
|
|
}
|
|
|
|
if (key->isIdentifier()) {
|
|
AtomicString as = key->asIdentifier()->name();
|
|
bool seenInit = kind == PropertyNode::Kind::Init;
|
|
bool seenGet = kind == PropertyNode::Kind::Get;
|
|
bool seenSet = kind == PropertyNode::Kind::Set;
|
|
for (size_t i = 0; i < usedNames.size(); i++) {
|
|
if (usedNames[i].first == as) {
|
|
if (usedNames[i].second == PropertyNode::Kind::Init) {
|
|
if (this->context->strict) {
|
|
if (seenInit || seenGet || seenSet) {
|
|
this->throwError("invalid object literal");
|
|
}
|
|
} else {
|
|
if (seenGet || seenSet) {
|
|
this->throwError("invalid object literal");
|
|
}
|
|
}
|
|
seenInit = true;
|
|
} else if (usedNames[i].second == PropertyNode::Kind::Get) {
|
|
if (seenInit || seenGet) {
|
|
this->throwError("invalid object literal");
|
|
}
|
|
seenGet = true;
|
|
} else if (usedNames[i].second == PropertyNode::Kind::Set) {
|
|
if (seenInit || seenSet) {
|
|
this->throwError("invalid object literal");
|
|
}
|
|
seenSet = true;
|
|
}
|
|
}
|
|
}
|
|
usedNames.push_back(std::make_pair(as, kind));
|
|
}
|
|
|
|
// return this->finalize(node, new PropertyNode(kind, key, computed, value, method, shorthand));
|
|
return this->finalize(node, new PropertyNode(key.get(), value.get(), kind, computed));
|
|
}
|
|
|
|
RefPtr<Node> parseObjectInitializer()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expect(LeftBrace);
|
|
PropertiesNodeVector properties;
|
|
bool hasProto = false;
|
|
std::vector<std::pair<AtomicString, size_t>> usedNames;
|
|
while (!this->match(RightBrace)) {
|
|
properties.push_back(this->parseObjectProperty(hasProto, usedNames));
|
|
if (!this->match(RightBrace)) {
|
|
this->expectCommaSeparator();
|
|
}
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
return this->finalize(node, new ObjectExpressionNode(std::move(properties)));
|
|
}
|
|
|
|
// ECMA-262 12.2.9 Template Literals
|
|
TemplateElement* parseTemplateHead()
|
|
{
|
|
ASSERT(this->lookahead->type == Token::TemplateToken);
|
|
|
|
MetaNode node = this->createNode();
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
TemplateElement* tm = new TemplateElement();
|
|
tm->value = token->valueTemplate->valueCooked;
|
|
tm->raw = token->valueTemplate->raw;
|
|
tm->tail = token->valueTemplate->tail;
|
|
return tm;
|
|
}
|
|
|
|
TemplateElement* parseTemplateElement()
|
|
{
|
|
if (this->lookahead->type != Token::TemplateToken) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
TemplateElement* tm = new TemplateElement();
|
|
tm->value = token->valueTemplate->valueCooked;
|
|
tm->raw = token->valueTemplate->raw;
|
|
tm->tail = token->valueTemplate->tail;
|
|
return tm;
|
|
}
|
|
|
|
RefPtr<Node> parseTemplateLiteral()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ExpressionNodeVector expressions;
|
|
TemplateElementVector* quasis = new (GC) TemplateElementVector;
|
|
quasis->push_back(this->parseTemplateHead());
|
|
while (!quasis->back()->tail) {
|
|
expressions.push_back(this->parseExpression());
|
|
TemplateElement* quasi = this->parseTemplateElement();
|
|
quasis->push_back(quasi);
|
|
}
|
|
|
|
return this->finalize(node, new TemplateLiteralNode(quasis, std::move(expressions)));
|
|
}
|
|
/*
|
|
// ECMA-262 12.2.10 The Grouping Operator
|
|
|
|
reinterpretExpressionAsPattern(expr) {
|
|
switch (expr.type) {
|
|
case Syntax.Identifier:
|
|
case Syntax.MemberExpression:
|
|
case Syntax.RestElement:
|
|
case Syntax.AssignmentPattern:
|
|
break;
|
|
case Syntax.SpreadElement:
|
|
expr.type = Syntax.RestElement;
|
|
this->reinterpretExpressionAsPattern(expr.argument);
|
|
break;
|
|
case Syntax.ArrayExpression:
|
|
expr.type = Syntax.ArrayPattern;
|
|
for (let i = 0; i < expr.elements.length; i++) {
|
|
if (expr.elements[i] !== null) {
|
|
this->reinterpretExpressionAsPattern(expr.elements[i]);
|
|
}
|
|
}
|
|
break;
|
|
case Syntax.ObjectExpression:
|
|
expr.type = Syntax.ObjectPattern;
|
|
for (let i = 0; i < expr.properties.length; i++) {
|
|
this->reinterpretExpressionAsPattern(expr.properties[i].value);
|
|
}
|
|
break;
|
|
case Syntax.AssignmentExpression:
|
|
expr.type = Syntax.AssignmentPattern;
|
|
delete expr.operator;
|
|
this->reinterpretExpressionAsPattern(expr.left);
|
|
break;
|
|
default:
|
|
// Allow other node type for tolerant parsing.
|
|
break;
|
|
}
|
|
}
|
|
|
|
parseGroupExpression(): ArrowParameterPlaceHolderNode | Node.Expression {
|
|
let expr;
|
|
|
|
this->expect('(');
|
|
if (this->match(')')) {
|
|
this->nextToken();
|
|
if (!this->match('=>')) {
|
|
this->expect('=>');
|
|
}
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: []
|
|
};
|
|
} else {
|
|
const startToken = this->lookahead;
|
|
let params = [];
|
|
if (this->match('...')) {
|
|
expr = this->parseRestElement(params);
|
|
this->expect(')');
|
|
if (!this->match('=>')) {
|
|
this->expect('=>');
|
|
}
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: [expr]
|
|
};
|
|
} else {
|
|
let arrow = false;
|
|
this->context.isBindingElement = true;
|
|
expr = this->inheritCoverGrammar(this->parseAssignmentExpression);
|
|
|
|
if (this->match(',')) {
|
|
const expressions = [];
|
|
|
|
this->context.isAssignmentTarget = false;
|
|
expressions.push(expr);
|
|
while (this->startMarker.index < this->scanner.length) {
|
|
if (!this->match(',')) {
|
|
break;
|
|
}
|
|
this->nextToken();
|
|
|
|
if (this->match('...')) {
|
|
if (!this->context.isBindingElement) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
expressions.push(this->parseRestElement(params));
|
|
this->expect(')');
|
|
if (!this->match('=>')) {
|
|
this->expect('=>');
|
|
}
|
|
this->context.isBindingElement = false;
|
|
for (let i = 0; i < expressions.length; i++) {
|
|
this->reinterpretExpressionAsPattern(expressions[i]);
|
|
}
|
|
arrow = true;
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: expressions
|
|
};
|
|
} else {
|
|
expressions.push(this->inheritCoverGrammar(this->parseAssignmentExpression));
|
|
}
|
|
if (arrow) {
|
|
break;
|
|
}
|
|
}
|
|
if (!arrow) {
|
|
expr = this->finalize(this->startNode(startToken), new Node.SequenceExpression(expressions));
|
|
}
|
|
}
|
|
|
|
if (!arrow) {
|
|
this->expect(')');
|
|
if (this->match('=>')) {
|
|
if (expr.type === Syntax.Identifier && expr.name === 'yield') {
|
|
arrow = true;
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: [expr]
|
|
};
|
|
}
|
|
if (!arrow) {
|
|
if (!this->context.isBindingElement) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
if (expr.type === Syntax.SequenceExpression) {
|
|
for (let i = 0; i < expr.expressions.length; i++) {
|
|
this->reinterpretExpressionAsPattern(expr.expressions[i]);
|
|
}
|
|
} else {
|
|
this->reinterpretExpressionAsPattern(expr);
|
|
}
|
|
|
|
const params = (expr.type === Syntax.SequenceExpression ? expr.expressions : [expr]);
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: params
|
|
};
|
|
}
|
|
}
|
|
this->context.isBindingElement = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
*/
|
|
void reinterpretExpressionAsPattern(Node* expr)
|
|
{
|
|
switch (expr->type()) {
|
|
case ArrayExpression:
|
|
this->throwError("Array pattern is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/* TODO(ES6) this part is only for es6
|
|
expr.type = Syntax.ArrayPattern;
|
|
for (let i = 0; i < expr.elements.length; i++) {
|
|
if (expr.elements[i] !== null) {
|
|
this->reinterpretExpressionAsPattern(expr.elements[i]);
|
|
}
|
|
}
|
|
*/
|
|
break;
|
|
case ObjectExpression:
|
|
this->throwError("Object pattern is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/* TODO(ES6) this part is only for es6
|
|
expr.type = Syntax.ObjectPattern;
|
|
for (let i = 0; i < expr.properties.length; i++) {
|
|
this->reinterpretExpressionAsPattern(expr.properties[i].value);
|
|
}
|
|
*/
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
RefPtr<Node> parseGroupExpression()
|
|
{
|
|
RefPtr<Node> expr;
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (this->match(RightParenthesis)) {
|
|
this->throwError("Arrow function is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
} else {
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
this->context->isBindingElement = true;
|
|
expr = this->inheritCoverGrammar(&Parser::parseAssignmentExpression);
|
|
|
|
if (this->match(Comma)) {
|
|
ExpressionNodeVector expressions;
|
|
|
|
this->context->isAssignmentTarget = false;
|
|
expressions.push_back(expr);
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->match(Comma)) {
|
|
break;
|
|
}
|
|
this->nextToken();
|
|
|
|
expressions.push_back(this->inheritCoverGrammar(&Parser::parseAssignmentExpression));
|
|
}
|
|
expr = this->finalize(this->startNode(startToken), new SequenceExpressionNode(std::move(expressions)));
|
|
}
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
return expr;
|
|
}
|
|
// ECMA-262 12.3 Left-Hand-Side Expressions
|
|
|
|
ArgumentVector parseArguments()
|
|
{
|
|
this->expect(LeftParenthesis);
|
|
ArgumentVector args;
|
|
if (!this->match(RightParenthesis)) {
|
|
while (true) {
|
|
RefPtr<Node> expr;
|
|
if (this->match(PeriodPeriodPeriod)) {
|
|
expr = this->parseSpreadElement();
|
|
} else {
|
|
expr = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
}
|
|
args.push_back(expr);
|
|
if (this->match(RightParenthesis)) {
|
|
break;
|
|
}
|
|
this->expectCommaSeparator();
|
|
}
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
return args;
|
|
}
|
|
|
|
bool isIdentifierName(RefPtr<ScannerResult> token)
|
|
{
|
|
return token->type == Token::IdentifierToken || token->type == Token::KeywordToken || token->type == Token::BooleanLiteralToken || token->type == Token::NullLiteralToken;
|
|
}
|
|
|
|
RefPtr<IdentifierNode> parseIdentifierName()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
if (!this->isIdentifierName(token)) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
return this->finalize(node, finishIdentifier(token, true));
|
|
}
|
|
|
|
RefPtr<Node> parseNewExpression()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
RefPtr<IdentifierNode> id = this->parseIdentifierName();
|
|
// assert(id.name === 'new', 'New expression must start with `new`');
|
|
|
|
Node* expr;
|
|
if (this->match(Period)) {
|
|
this->nextToken();
|
|
if (this->lookahead->type == Token::IdentifierToken && this->context->inFunctionBody && this->lookahead->valueString == "target") {
|
|
// TODO
|
|
RefPtr<IdentifierNode> property = this->parseIdentifierName();
|
|
this->throwError("Meta property is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// expr = new Node.MetaProperty(id, property);
|
|
} else {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
} else {
|
|
RefPtr<Node> callee = this->isolateCoverGrammar(&Parser::parseLeftHandSideExpression);
|
|
ArgumentVector args;
|
|
if (this->match(LeftParenthesis))
|
|
args = this->parseArguments();
|
|
if (args.size() > 65535) {
|
|
this->throwError("too many arguments in new");
|
|
}
|
|
expr = new NewExpressionNode(callee.get(), std::move(args));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
}
|
|
|
|
return this->finalize(node, expr);
|
|
}
|
|
|
|
RefPtr<Node> parseLeftHandSideExpressionAllowCall()
|
|
{
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = true;
|
|
|
|
RefPtr<Node> expr;
|
|
if (this->matchKeyword(Super) && this->context->inFunctionBody) {
|
|
MetaNode node = this->createNode();
|
|
this->nextToken();
|
|
this->throwError("super keyword is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// expr = this->finalize(node, new Node.Super());
|
|
if (!this->match(LeftParenthesis) && !this->match(Period) && !this->match(LeftSquareBracket)) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
} else {
|
|
expr = this->inheritCoverGrammar(this->matchKeyword(New) ? &Parser::parseNewExpression : &Parser::parsePrimaryExpression);
|
|
}
|
|
|
|
while (true) {
|
|
if (this->match(Period)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(Period);
|
|
bool trackUsingNamesBefore = this->trackUsingNames;
|
|
this->trackUsingNames = false;
|
|
RefPtr<IdentifierNode> property = this->parseIdentifierName();
|
|
this->trackUsingNames = trackUsingNamesBefore;
|
|
expr = this->finalize(this->startNode(startToken), new MemberExpressionNode(expr.get(), property.get(), true));
|
|
|
|
} else if (this->match(LeftParenthesis)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = false;
|
|
ArgumentVector args = this->parseArguments();
|
|
if (args.size() > 65535) {
|
|
this->throwError("too many arguments in call");
|
|
}
|
|
expr = this->finalize(this->startNode(startToken), new CallExpressionNode(expr.get(), std::move(args)));
|
|
|
|
} else if (this->match(LeftSquareBracket)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(LeftSquareBracket);
|
|
RefPtr<Node> property = this->isolateCoverGrammar(&Parser::parseExpression);
|
|
this->expect(RightSquareBracket);
|
|
expr = this->finalize(this->startNode(startToken), new MemberExpressionNode(expr.get(), property.get(), false));
|
|
|
|
} else if (this->lookahead->type == Token::TemplateToken && this->lookahead->valueTemplate->head) {
|
|
RefPtr<Node> quasi = this->parseTemplateLiteral();
|
|
expr = this->convertTaggedTempleateExpressionToCallExpression(this->startNode(startToken), this->finalize(this->startNode(startToken), new TaggedTemplateExpressionNode(expr.get(), quasi.get())));
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
return expr;
|
|
}
|
|
|
|
RefPtr<Node> parseSuper()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expectKeyword(Super);
|
|
if (!this->match(LeftSquareBracket) && !this->match(Period)) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
this->throwError("super keyword is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// return this->finalize(node, new Node.Super());
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<Node> parseLeftHandSideExpression()
|
|
{
|
|
// assert(this->context->allowIn, 'callee of new expression always allow in keyword.');
|
|
ASSERT(this->context->allowIn);
|
|
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
RefPtr<Node> expr = (this->matchKeyword(Super) && this->context->inFunctionBody) ? this->parseSuper() : this->inheritCoverGrammar(this->matchKeyword(New) ? &Parser::parseNewExpression : &Parser::parsePrimaryExpression);
|
|
|
|
while (true) {
|
|
if (this->match(LeftSquareBracket)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(LeftSquareBracket);
|
|
RefPtr<Node> property = this->isolateCoverGrammar(&Parser::parseExpression);
|
|
this->expect(RightSquareBracket);
|
|
expr = this->finalize(node, new MemberExpressionNode(expr.get(), property.get(), false));
|
|
|
|
} else if (this->match(Period)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(Period);
|
|
bool trackUsingNamesBefore = this->trackUsingNames;
|
|
this->trackUsingNames = false;
|
|
RefPtr<IdentifierNode> property = this->parseIdentifierName();
|
|
this->trackUsingNames = trackUsingNamesBefore;
|
|
expr = this->finalize(node, new MemberExpressionNode(expr.get(), property.get(), true));
|
|
|
|
} else if (this->lookahead->type == Token::TemplateToken && this->lookahead->valueTemplate->head) {
|
|
RefPtr<Node> quasi = this->parseTemplateLiteral();
|
|
expr = this->convertTaggedTempleateExpressionToCallExpression(node, this->finalize(node, new TaggedTemplateExpressionNode(expr.get(), quasi.get())).get());
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
RefPtr<Node> convertTaggedTempleateExpressionToCallExpression(MetaNode node, RefPtr<TaggedTemplateExpressionNode> taggedTemplateExpression)
|
|
{
|
|
TemplateLiteralNode* templateLiteral = (TemplateLiteralNode*)taggedTemplateExpression->quasi();
|
|
ArgumentVector args;
|
|
ExpressionNodeVector elements;
|
|
for (size_t i = 0; i < templateLiteral->quasis()->size(); i++) {
|
|
UTF16StringData& sd = (*templateLiteral->quasis())[i]->value;
|
|
String* str = new UTF16String(std::move(sd));
|
|
elements.push_back(this->finalize(node, new LiteralNode(Value(str))));
|
|
}
|
|
|
|
RefPtr<ArrayExpressionNode> arrayExpressionForRaw;
|
|
{
|
|
ExpressionNodeVector elements;
|
|
for (size_t i = 0; i < templateLiteral->quasis()->size(); i++) {
|
|
String* str = new StringView((*templateLiteral->quasis())[i]->raw);
|
|
elements.push_back(this->finalize(node, new LiteralNode(Value(str))));
|
|
}
|
|
arrayExpressionForRaw = this->finalize(node, new ArrayExpressionNode(std::move(elements)));
|
|
}
|
|
|
|
RefPtr<ArrayExpressionNode> quasiVector = this->finalize(node, new ArrayExpressionNode(std::move(elements), this->escargotContext->staticStrings().raw, arrayExpressionForRaw.get()));
|
|
args.push_back(quasiVector.get());
|
|
for (size_t i = 0; i < templateLiteral->expressions().size(); i++) {
|
|
args.push_back(templateLiteral->expressions()[i]);
|
|
}
|
|
return this->finalize(node, new CallExpressionNode(taggedTemplateExpression->expr(), std::move(args)));
|
|
}
|
|
|
|
// ECMA-262 12.4 Update Expressions
|
|
|
|
RefPtr<Node> parseUpdateExpression()
|
|
{
|
|
RefPtr<Node> expr;
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
|
|
if (this->match(PlusPlus) || this->match(MinusMinus)) {
|
|
bool isPlus = this->match(PlusPlus);
|
|
MetaNode node = this->startNode(startToken);
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
if (expr->isLiteral() || expr->type() == ASTNodeType::ThisExpression) {
|
|
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
|
|
}
|
|
if (this->context->strict && expr->type() == Identifier && this->scanner->isRestrictedWord(((IdentifierNode*)expr.get())->name())) {
|
|
this->tolerateError(Messages::StrictLHSPrefix);
|
|
}
|
|
if (!this->context->isAssignmentTarget && this->context->strict) {
|
|
this->tolerateError(Messages::InvalidLHSInAssignment);
|
|
}
|
|
bool prefix = true;
|
|
|
|
if (isPlus) {
|
|
expr = this->finalize(node, new UpdateExpressionIncrementPrefixNode(expr.get()));
|
|
} else {
|
|
expr = this->finalize(node, new UpdateExpressionDecrementPrefixNode(expr.get()));
|
|
}
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
expr = this->inheritCoverGrammar(&Parser::parseLeftHandSideExpressionAllowCall);
|
|
if (!this->hasLineTerminator && this->lookahead->type == Token::PunctuatorToken) {
|
|
if (this->match(PlusPlus) || this->match(MinusMinus)) {
|
|
bool isPlus = this->match(PlusPlus);
|
|
if (this->context->strict && expr->isIdentifier() && this->scanner->isRestrictedWord(((IdentifierNode*)expr.get())->name())) {
|
|
this->tolerateError(Messages::StrictLHSPostfix);
|
|
}
|
|
if (!this->context->isAssignmentTarget && this->context->strict) {
|
|
this->tolerateError(Messages::InvalidLHSInAssignment);
|
|
}
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->nextToken();
|
|
|
|
if (expr->isLiteral() || expr->type() == ASTNodeType::ThisExpression) {
|
|
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
|
|
}
|
|
|
|
if (isPlus) {
|
|
expr = this->finalize(this->startNode(startToken), new UpdateExpressionIncrementPostfixNode(expr.get()));
|
|
} else {
|
|
expr = this->finalize(this->startNode(startToken), new UpdateExpressionDecrementPostfixNode(expr.get()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.5 Unary Operators
|
|
|
|
RefPtr<Node> parseUnaryExpression()
|
|
{
|
|
RefPtr<Node> expr;
|
|
|
|
if (this->match(Plus)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionPlusNode(expr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->match(Minus)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionMinusNode(expr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->match(Wave)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionBitwiseNotNode(expr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->match(ExclamationMark)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionLogicalNotNode(expr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->matchKeyword(Delete)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
RefPtr<Node> exprOld = expr;
|
|
expr = this->finalize(node, new UnaryExpressionDeleteNode(expr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
if (this->context->strict && exprOld->isIdentifier()) {
|
|
this->tolerateError(Messages::StrictDelete);
|
|
}
|
|
if (exprOld->isIdentifier()) {
|
|
this->scopeContexts.back()->m_hasEvaluateBindingId = true;
|
|
}
|
|
} else if (this->matchKeyword(Void)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionVoidNode(expr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->matchKeyword(Typeof)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
RefPtr<Node> exprOld = expr;
|
|
expr = this->finalize(node, new UnaryExpressionTypeOfNode(expr.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
if (exprOld->isIdentifier()) {
|
|
AtomicString s = exprOld->asIdentifier()->name();
|
|
if (!this->scopeContexts.back()->hasName(s)) {
|
|
this->scopeContexts.back()->m_hasEvaluateBindingId = true;
|
|
}
|
|
}
|
|
} else {
|
|
expr = this->parseUpdateExpression();
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
RefPtr<Node> parseExponentiationExpression()
|
|
{
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
RefPtr<Node> expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
// TODO
|
|
/*
|
|
if (expr->type != Syntax.UnaryExpression && this->match('**')) {
|
|
this->nextToken();
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
const left = expr;
|
|
const right = this->isolateCoverGrammar(this->parseExponentiationExpression);
|
|
expr = this->finalize(this->startNode(startToken), new Node.BinaryExpression('**', left, right));
|
|
}*/
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.6 Exponentiation Operators
|
|
// ECMA-262 12.7 Multiplicative Operators
|
|
// ECMA-262 12.8 Additive Operators
|
|
// ECMA-262 12.9 Bitwise Shift Operators
|
|
// ECMA-262 12.10 Relational Operators
|
|
// ECMA-262 12.11 Equality Operators
|
|
// ECMA-262 12.12 Binary Bitwise Operators
|
|
// ECMA-262 12.13 Binary Logical Operators
|
|
|
|
int binaryPrecedence(RefPtr<ScannerResult> token)
|
|
{
|
|
if (token->type == Token::PunctuatorToken) {
|
|
if (token->valuePunctuatorsKind == Substitution) {
|
|
return 0;
|
|
} else if (token->valuePunctuatorsKind == LogicalOr) {
|
|
return 1;
|
|
} else if (token->valuePunctuatorsKind == LogicalAnd) {
|
|
return 2;
|
|
} else if (token->valuePunctuatorsKind == BitwiseOr) {
|
|
return 3;
|
|
} else if (token->valuePunctuatorsKind == BitwiseXor) {
|
|
return 4;
|
|
} else if (token->valuePunctuatorsKind == BitwiseAnd) {
|
|
return 5;
|
|
} else if (token->valuePunctuatorsKind == Equal) {
|
|
return 6;
|
|
} else if (token->valuePunctuatorsKind == NotEqual) {
|
|
return 6;
|
|
} else if (token->valuePunctuatorsKind == StrictEqual) {
|
|
return 6;
|
|
} else if (token->valuePunctuatorsKind == NotStrictEqual) {
|
|
return 6;
|
|
} else if (token->valuePunctuatorsKind == RightInequality) {
|
|
return 7;
|
|
} else if (token->valuePunctuatorsKind == LeftInequality) {
|
|
return 7;
|
|
} else if (token->valuePunctuatorsKind == RightInequalityEqual) {
|
|
return 7;
|
|
} else if (token->valuePunctuatorsKind == LeftInequalityEqual) {
|
|
return 7;
|
|
} else if (token->valuePunctuatorsKind == LeftShift) {
|
|
return 8;
|
|
} else if (token->valuePunctuatorsKind == RightShift) {
|
|
return 8;
|
|
} else if (token->valuePunctuatorsKind == UnsignedRightShift) {
|
|
return 8;
|
|
} else if (token->valuePunctuatorsKind == Plus) {
|
|
return 9;
|
|
} else if (token->valuePunctuatorsKind == Minus) {
|
|
return 9;
|
|
} else if (token->valuePunctuatorsKind == Multiply) {
|
|
return 11;
|
|
} else if (token->valuePunctuatorsKind == Divide) {
|
|
return 11;
|
|
} else if (token->valuePunctuatorsKind == Mod) {
|
|
return 11;
|
|
}
|
|
return 0;
|
|
} else if (token->type == Token::KeywordToken) {
|
|
if (token->valueKeywordKind == In) {
|
|
return this->context->allowIn ? 7 : 0;
|
|
} else if (token->valueKeywordKind == InstanceofKeyword) {
|
|
return 7;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
RefPtr<Node> parseBinaryExpression()
|
|
{
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
|
|
RefPtr<Node> expr = this->inheritCoverGrammar(&Parser::parseExponentiationExpression);
|
|
|
|
RefPtr<ScannerResult> token = this->lookahead;
|
|
std::vector<RefPtr<ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<ScannerResult>>> tokenKeeper;
|
|
int prec = this->binaryPrecedence(token);
|
|
if (prec > 0) {
|
|
this->nextToken();
|
|
|
|
token->prec = prec;
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
std::vector<RefPtr<ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<ScannerResult>>> markers;
|
|
markers.push_back(startToken);
|
|
markers.push_back(this->lookahead);
|
|
RefPtr<Node> left = expr;
|
|
RefPtr<Node> right = this->isolateCoverGrammar(&Parser::parseExponentiationExpression);
|
|
|
|
std::vector<void*, GCUtil::gc_malloc_ignore_off_page_allocator<void*>> stack;
|
|
left->ref();
|
|
stack.push_back(left.get());
|
|
token->ref();
|
|
stack.push_back(token.get());
|
|
right->ref();
|
|
stack.push_back(right.get());
|
|
|
|
while (true) {
|
|
prec = this->binaryPrecedence(this->lookahead);
|
|
if (prec <= 0) {
|
|
break;
|
|
}
|
|
|
|
// Reduce: make a binary expression from the three topmost entries.
|
|
while ((stack.size() > 2) && (prec <= ((ScannerResult*)stack[stack.size() - 2])->prec)) {
|
|
right = (Node*)stack.back();
|
|
right->deref();
|
|
stack.pop_back();
|
|
RefPtr<ScannerResult> operator_ = (ScannerResult*)stack.back();
|
|
operator_->deref();
|
|
stack.pop_back();
|
|
left = (Node*)stack.back();
|
|
left->deref();
|
|
stack.pop_back();
|
|
markers.pop_back();
|
|
MetaNode node = this->startNode(markers.back());
|
|
auto e = this->finalize(node, finishBinaryExpression(left.get(), right.get(), operator_.get()));
|
|
e->ref();
|
|
stack.push_back(e.get());
|
|
}
|
|
|
|
// Shift.
|
|
token = this->nextToken();
|
|
token->prec = prec;
|
|
token->ref();
|
|
stack.push_back(token.get());
|
|
markers.push_back(this->lookahead);
|
|
auto e = this->isolateCoverGrammar(&Parser::parseExponentiationExpression);
|
|
e->ref();
|
|
stack.push_back(e.get());
|
|
}
|
|
|
|
// Final reduce to clean-up the stack.
|
|
size_t i = stack.size() - 1;
|
|
expr = (Node*)stack[i];
|
|
expr->deref();
|
|
markers.pop_back();
|
|
while (i > 1) {
|
|
MetaNode node = this->startNode(markers.back());
|
|
expr = this->finalize(node, finishBinaryExpression((Node*)stack[i - 2], expr.get(), (ScannerResult*)stack[i - 1]));
|
|
((Node*)stack[i - 2])->deref();
|
|
((ScannerResult*)stack[i - 1])->deref();
|
|
i -= 2;
|
|
}
|
|
RELEASE_ASSERT(i == 0);
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
Node* finishBinaryExpression(Node* left, Node* right, ScannerResult* token)
|
|
{
|
|
Node* nd;
|
|
if (token->type == Token::PunctuatorToken) {
|
|
PunctuatorsKind oper = token->valuePunctuatorsKind;
|
|
// Additive Operators
|
|
if (oper == Plus)
|
|
nd = new BinaryExpressionPlusNode(left, right);
|
|
else if (oper == Minus)
|
|
nd = new BinaryExpressionMinusNode(left, right);
|
|
|
|
// Bitwise Shift Operators
|
|
else if (oper == LeftShift)
|
|
nd = new BinaryExpressionLeftShiftNode(left, right);
|
|
else if (oper == RightShift)
|
|
nd = new BinaryExpressionSignedRightShiftNode(left, right);
|
|
else if (oper == UnsignedRightShift)
|
|
nd = new BinaryExpressionUnsignedRightShiftNode(left, right);
|
|
|
|
// Multiplicative Operators
|
|
else if (oper == Multiply)
|
|
nd = new BinaryExpressionMultiplyNode(left, right);
|
|
else if (oper == Divide)
|
|
nd = new BinaryExpressionDivisionNode(left, right);
|
|
else if (oper == Mod)
|
|
nd = new BinaryExpressionModNode(left, right);
|
|
|
|
// Relational Operators
|
|
else if (oper == LeftInequality)
|
|
nd = new BinaryExpressionLessThanNode(left, right);
|
|
else if (oper == RightInequality)
|
|
nd = new BinaryExpressionGreaterThanNode(left, right);
|
|
else if (oper == LeftInequalityEqual)
|
|
nd = new BinaryExpressionLessThanOrEqualNode(left, right);
|
|
else if (oper == RightInequalityEqual)
|
|
nd = new BinaryExpressionGreaterThanOrEqualNode(left, right);
|
|
|
|
// Equality Operators
|
|
else if (oper == Equal)
|
|
nd = new BinaryExpressionEqualNode(left, right);
|
|
else if (oper == NotEqual)
|
|
nd = new BinaryExpressionNotEqualNode(left, right);
|
|
else if (oper == StrictEqual)
|
|
nd = new BinaryExpressionStrictEqualNode(left, right);
|
|
else if (oper == NotStrictEqual)
|
|
nd = new BinaryExpressionNotStrictEqualNode(left, right);
|
|
|
|
// Binary Bitwise Operator
|
|
else if (oper == BitwiseAnd)
|
|
nd = new BinaryExpressionBitwiseAndNode(left, right);
|
|
else if (oper == BitwiseXor)
|
|
nd = new BinaryExpressionBitwiseXorNode(left, right);
|
|
else if (oper == BitwiseOr)
|
|
nd = new BinaryExpressionBitwiseOrNode(left, right);
|
|
else if (oper == LogicalOr)
|
|
nd = new BinaryExpressionLogicalOrNode(left, right);
|
|
else if (oper == LogicalAnd)
|
|
nd = new BinaryExpressionLogicalAndNode(left, right);
|
|
else
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
} else {
|
|
ASSERT(token->type == Token::KeywordToken);
|
|
if (token->valueKeywordKind == KeywordKind::In)
|
|
nd = new BinaryExpressionInNode(left, right);
|
|
else if (token->valueKeywordKind == KeywordKind::InstanceofKeyword)
|
|
nd = new BinaryExpressionInstanceOfNode(left, right);
|
|
else
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
return nd;
|
|
}
|
|
|
|
// ECMA-262 12.14 Conditional Operator
|
|
|
|
RefPtr<Node> parseConditionalExpression()
|
|
{
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
|
|
RefPtr<Node> expr = this->inheritCoverGrammar(&Parser::parseBinaryExpression);
|
|
if (this->match(GuessMark)) {
|
|
this->nextToken();
|
|
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = true;
|
|
RefPtr<Node> consequent = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
this->expect(Colon);
|
|
RefPtr<Node> alternate = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
|
|
expr = this->finalize(this->startNode(startToken), new ConditionalExpressionNode(expr.get(), consequent.get(), alternate.get()));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.15 Assignment Operators
|
|
/*
|
|
void checkPatternParam(ParseParameterOptions options, Node* param)
|
|
{
|
|
switch (param->type()) {
|
|
case Identifier:
|
|
this->validateParam(options, nodeExtraInfo.find(((IdentifierNode*)param))->second, ((IdentifierNode*)param)->name());
|
|
break;
|
|
case RestElement:
|
|
this->checkPatternParam(options, ((RestElementNode*)param)->argument());
|
|
break;
|
|
// case AssignmentPattern:
|
|
case AssignmentExpression:
|
|
case AssignmentExpressionBitwiseAnd:
|
|
case AssignmentExpressionBitwiseOr:
|
|
case AssignmentExpressionBitwiseXor:
|
|
case AssignmentExpressionDivision:
|
|
case AssignmentExpressionLeftShift:
|
|
case AssignmentExpressionMinus:
|
|
case AssignmentExpressionMod:
|
|
case AssignmentExpressionMultiply:
|
|
case AssignmentExpressionPlus:
|
|
case AssignmentExpressionSignedRightShift:
|
|
case AssignmentExpressionUnsignedRightShift:
|
|
case AssignmentExpressionSimple:
|
|
this->checkPatternParam(options, ((AssignmentExpressionSimpleNode*)param)->left());
|
|
break;
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
case Syntax.ArrayPattern:
|
|
for (let i = 0; i < param.elements.length; i++) {
|
|
if (param.elements[i] !== null) {
|
|
this->checkPatternParam(options, param.elements[i]);
|
|
}
|
|
}
|
|
break;
|
|
case Syntax.YieldExpression:
|
|
break;
|
|
default:
|
|
RELEASE_ASSERT(param->type() == Object)
|
|
assert(param.type === Syntax.ObjectPattern, 'Invalid type');
|
|
for (let i = 0; i < param.properties.length; i++) {
|
|
this->checkPatternParam(options, param.properties[i].value);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
/*
|
|
reinterpretAsCoverFormalsList(expr) {
|
|
let params = [expr];
|
|
let options;
|
|
|
|
switch (expr.type) {
|
|
case Syntax.Identifier:
|
|
break;
|
|
case ArrowParameterPlaceHolder:
|
|
params = expr.params;
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
options = {
|
|
paramSet: {}
|
|
};
|
|
|
|
for (let i = 0; i < params.length; ++i) {
|
|
const param = params[i];
|
|
if (param.type === Syntax.AssignmentPattern) {
|
|
if (param.right.type === Syntax.YieldExpression) {
|
|
if (param.right.argument) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
param.right.type = Syntax.Identifier;
|
|
param.right.name = 'yield';
|
|
delete param.right.argument;
|
|
delete param.right.delegate;
|
|
}
|
|
}
|
|
this->checkPatternParam(options, param);
|
|
params[i] = param;
|
|
}
|
|
|
|
if (this->context.strict || !this->context.allowYield) {
|
|
for (let i = 0; i < params.length; ++i) {
|
|
const param = params[i];
|
|
if (param.type === Syntax.YieldExpression) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (options.message === Messages.StrictParamDupe) {
|
|
const token = this->context.strict ? options.stricted : options.firstRestricted;
|
|
this->throwUnexpectedToken(token, options.message);
|
|
}
|
|
|
|
return {
|
|
params: params,
|
|
stricted: options.stricted,
|
|
firstRestricted: options.firstRestricted,
|
|
message: options.message
|
|
};
|
|
}
|
|
*/
|
|
RefPtr<Node> parseAssignmentExpression()
|
|
{
|
|
RefPtr<Node> expr;
|
|
|
|
if (!this->context->allowYield && this->matchKeyword(Yield)) {
|
|
expr = this->parseYieldExpression();
|
|
} else {
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
RefPtr<ScannerResult> token = startToken;
|
|
expr = this->parseConditionalExpression();
|
|
/*
|
|
if (expr->type() == ArrowParameterPlaceHolder || this->match('=>')) {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// ECMA-262 14.2 Arrow Function Definitions
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
const list = this->reinterpretAsCoverFormalsList(expr);
|
|
|
|
if (list) {
|
|
if (this->hasLineTerminator) {
|
|
this->tolerateUnexpectedToken(this->lookahead);
|
|
}
|
|
this->context->firstCoverInitializedNameError = null;
|
|
|
|
const previousStrict = this->context->strict;
|
|
const previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = true;
|
|
|
|
const node = this->startNode(startToken);
|
|
this->expect('=>');
|
|
const body = this->match('{') ? this->parseFunctionSourceElements() :
|
|
this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
const expression = body.type !== Syntax.BlockStatement;
|
|
|
|
if (this->context->strict && list.firstRestricted) {
|
|
this->throwUnexpectedToken(list.firstRestricted, list.message);
|
|
}
|
|
if (this->context->strict && list.stricted) {
|
|
this->tolerateUnexpectedToken(list.stricted, list.message);
|
|
}
|
|
expr = this->finalize(node, new Node.ArrowFunctionExpression(list.params, body, expression));
|
|
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
}
|
|
|
|
} else {
|
|
if (this->matchAssign()) {
|
|
if (!this->context->isAssignmentTarget) {
|
|
this->tolerateError(Messages.InvalidLHSInAssignment);
|
|
}
|
|
|
|
if (this->context->strict && expr.type === Syntax.Identifier) {
|
|
const id = <Node.Identifier>(expr);
|
|
if (this->scanner.isRestrictedWord(id.name)) {
|
|
this->tolerateUnexpectedToken(token, Messages.StrictLHSAssignment);
|
|
}
|
|
if (this->scanner.isStrictModeReservedWord(id.name)) {
|
|
this->tolerateUnexpectedToken(token, Messages.StrictReservedWord);
|
|
}
|
|
}
|
|
|
|
if (!this->match('=')) {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
this->reinterpretExpressionAsPattern(expr);
|
|
}
|
|
|
|
token = this->nextToken();
|
|
const right = this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
expr = this->finalize(this->startNode(startToken), new Node.AssignmentExpression(token.value, expr, right));
|
|
this->context->firstCoverInitializedNameError = null;
|
|
}
|
|
}*/
|
|
if (this->matchAssign()) {
|
|
if (!this->context->isAssignmentTarget && this->context->strict) {
|
|
this->tolerateError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
|
|
}
|
|
|
|
if (this->context->strict && expr->type() == Identifier) {
|
|
IdentifierNode* id = (IdentifierNode*)(expr.get());
|
|
if (this->scanner->isRestrictedWord(id->name())) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictLHSAssignment);
|
|
}
|
|
if (this->scanner->isStrictModeReservedWord(id->name())) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictReservedWord);
|
|
}
|
|
}
|
|
|
|
if (!this->match(Substitution)) {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
this->reinterpretExpressionAsPattern(expr.get());
|
|
}
|
|
|
|
if (expr->isLiteral() || expr->type() == ASTNodeType::ThisExpression) {
|
|
this->throwError(Messages::InvalidLHSInAssignment, String::emptyString, String::emptyString, ErrorObject::ReferenceError);
|
|
}
|
|
|
|
Node* exprResult;
|
|
token = this->nextToken();
|
|
RefPtr<Node> right = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
if (token->valuePunctuatorsKind == Substitution) {
|
|
exprResult = new AssignmentExpressionSimpleNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == PlusEqual) {
|
|
exprResult = new AssignmentExpressionPlusNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == MinusEqual) {
|
|
exprResult = new AssignmentExpressionMinusNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == MultiplyEqual) {
|
|
exprResult = new AssignmentExpressionMultiplyNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == DivideEqual) {
|
|
exprResult = new AssignmentExpressionDivisionNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == ModEqual) {
|
|
exprResult = new AssignmentExpressionModNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == LeftShiftEqual) {
|
|
exprResult = new AssignmentExpressionLeftShiftNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == RightShiftEqual) {
|
|
exprResult = new AssignmentExpressionSignedRightShiftNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == UnsignedRightShiftEqual) {
|
|
exprResult = new AssignmentExpressionUnsignedShiftNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == BitwiseXorEqual) {
|
|
exprResult = new AssignmentExpressionBitwiseXorNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == BitwiseAndEqual) {
|
|
exprResult = new AssignmentExpressionBitwiseAndNode(expr.get(), right.get());
|
|
} else if (token->valuePunctuatorsKind == BitwiseOrEqual) {
|
|
exprResult = new AssignmentExpressionBitwiseOrNode(expr.get(), right.get());
|
|
} else {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
expr = this->finalize(this->startNode(startToken), exprResult);
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.16 Comma Operator
|
|
|
|
RefPtr<Node> parseExpression()
|
|
{
|
|
RefPtr<ScannerResult> startToken = this->lookahead;
|
|
RefPtr<Node> expr = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
|
|
if (this->match(Comma)) {
|
|
ExpressionNodeVector expressions;
|
|
expressions.push_back(expr);
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->match(Comma)) {
|
|
break;
|
|
}
|
|
this->nextToken();
|
|
expressions.push_back(this->isolateCoverGrammar(&Parser::parseAssignmentExpression));
|
|
}
|
|
|
|
expr = this->finalize(this->startNode(startToken), new SequenceExpressionNode(std::move(expressions)));
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 13.2 Block
|
|
|
|
RefPtr<StatementNode> parseStatementListItem()
|
|
{
|
|
RefPtr<StatementNode> statement;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->isBindingElement = true;
|
|
if (this->lookahead->type == KeywordToken) {
|
|
switch (this->lookahead->valueKeywordKind) {
|
|
case Function:
|
|
statement = this->parseFunctionDeclaration();
|
|
break;
|
|
default:
|
|
statement = this->parseStatement();
|
|
break;
|
|
}
|
|
} else {
|
|
statement = this->parseStatement();
|
|
}
|
|
/*
|
|
if (this->lookahead.type === Token.Keyword) {
|
|
switch (this->lookahead.value) {
|
|
case 'export':
|
|
if (this->sourceType !== 'module') {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages.IllegalExportDeclaration);
|
|
}
|
|
statement = this->parseExportDeclaration();
|
|
break;
|
|
case 'import':
|
|
if (this->sourceType !== 'module') {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages.IllegalImportDeclaration);
|
|
}
|
|
statement = this->parseImportDeclaration();
|
|
break;
|
|
case 'const':
|
|
statement = this->parseLexicalDeclaration({ inFor: false });
|
|
break;
|
|
case 'function':
|
|
statement = this->parseFunctionDeclaration();
|
|
break;
|
|
case 'class':
|
|
statement = this->parseClassDeclaration();
|
|
break;
|
|
case 'let':
|
|
statement = this->isLexicalDeclaration() ? this->parseLexicalDeclaration({ inFor: false }) : this->parseStatement();
|
|
break;
|
|
default:
|
|
statement = this->parseStatement();
|
|
break;
|
|
}
|
|
} else {
|
|
statement = this->parseStatement();
|
|
}*/
|
|
|
|
return statement;
|
|
}
|
|
|
|
RefPtr<BlockStatementNode> parseBlock()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expect(LeftBrace);
|
|
RefPtr<StatementContainer> block = StatementContainer::create();
|
|
StatementNode* referNode = nullptr;
|
|
while (true) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
referNode = block->appendChild(this->parseStatementListItem().get(), referNode);
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
return this->finalize(node, new BlockStatementNode(block.get()));
|
|
}
|
|
/*
|
|
// ECMA-262 13.3.1 Let and Const Declarations
|
|
|
|
parseLexicalBinding(kind: string, options): Node.VariableDeclarator {
|
|
const node = this->createNode();
|
|
let params = [];
|
|
const id = this->parsePattern(params, kind);
|
|
|
|
// ECMA-262 12.2.1
|
|
if (this->context.strict && id.type === Syntax.Identifier) {
|
|
if (this->scanner.isRestrictedWord((<Node.Identifier>(id)).name)) {
|
|
this->tolerateError(Messages.StrictVarName);
|
|
}
|
|
}
|
|
|
|
let init = null;
|
|
if (kind === 'const') {
|
|
if (!this->matchKeyword('in') && !this->matchContextualKeyword('of')) {
|
|
this->expect('=');
|
|
init = this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
}
|
|
} else if ((!options.inFor && id.type !== Syntax.Identifier) || this->match('=')) {
|
|
this->expect('=');
|
|
init = this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
}
|
|
|
|
return this->finalize(node, new Node.VariableDeclarator(id, init));
|
|
}
|
|
|
|
parseBindingList(kind: string, options): Node.VariableDeclarator[] {
|
|
let list = [this->parseLexicalBinding(kind, options)];
|
|
|
|
while (this->match(',')) {
|
|
this->nextToken();
|
|
list.push(this->parseLexicalBinding(kind, options));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
isLexicalDeclaration(): boolean {
|
|
const previousIndex = this->scanner.index;
|
|
const previousLineNumber = this->scanner.lineNumber;
|
|
const previousLineStart = this->scanner.lineStart;
|
|
this->collectComments();
|
|
const next = <any>this->scanner.lex();
|
|
this->scanner.index = previousIndex;
|
|
this->scanner.lineNumber = previousLineNumber;
|
|
this->scanner.lineStart = previousLineStart;
|
|
|
|
return (next.type === Token.Identifier) ||
|
|
(next.type === Token.Punctuator && next.value === '[') ||
|
|
(next.type === Token.Punctuator && next.value === '{') ||
|
|
(next.type === Token.Keyword && next.value === 'let') ||
|
|
(next.type === Token.Keyword && next.value === 'yield');
|
|
}
|
|
|
|
parseLexicalDeclaration(options): Node.VariableDeclaration {
|
|
const node = this->createNode();
|
|
const kind = this->nextToken().value;
|
|
assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const');
|
|
|
|
const declarations = this->parseBindingList(kind, options);
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, new Node.VariableDeclaration(declarations, kind));
|
|
}
|
|
|
|
// ECMA-262 13.3.3 Destructuring Binding Patterns
|
|
|
|
parseBindingRestElement(params, kind: string): Node.RestElement {
|
|
const node = this->createNode();
|
|
this->expect('...');
|
|
params.push(this->lookahead);
|
|
const arg = this->parseVariableIdentifier(kind);
|
|
return this->finalize(node, new Node.RestElement(arg));
|
|
}
|
|
|
|
|
|
parseArrayPattern(params, kind: string): Node.ArrayPattern {
|
|
const node = this->createNode();
|
|
|
|
this->expect('[');
|
|
const elements: Node.ArrayPatternElement[] = [];
|
|
while (!this->match(']')) {
|
|
if (this->match(',')) {
|
|
this->nextToken();
|
|
elements.push(null);
|
|
} else {
|
|
if (this->match('...')) {
|
|
elements.push(this->parseBindingRestElement(params, kind));
|
|
break;
|
|
} else {
|
|
elements.push(this->parsePatternWithDefault(params, kind));
|
|
}
|
|
if (!this->match(']')) {
|
|
this->expect(',');
|
|
}
|
|
}
|
|
|
|
}
|
|
this->expect(']');
|
|
|
|
return this->finalize(node, new Node.ArrayPattern(elements));
|
|
}
|
|
|
|
parsePropertyPattern(params, kind: string): Node.Property {
|
|
const node = this->createNode();
|
|
|
|
let computed = false;
|
|
let shorthand = false;
|
|
const method = false;
|
|
|
|
let key: Node.PropertyKey;
|
|
let value: Node.PropertyValue;
|
|
|
|
if (this->lookahead.type === Token.Identifier) {
|
|
const keyToken = this->lookahead;
|
|
key = this->parseVariableIdentifier();
|
|
const init = this->finalize(node, new Node.Identifier(keyToken.value));
|
|
if (this->match('=')) {
|
|
params.push(keyToken);
|
|
shorthand = true;
|
|
this->nextToken();
|
|
const expr = this->parseAssignmentExpression();
|
|
value = this->finalize(this->startNode(keyToken), new Node.AssignmentPattern(init, expr));
|
|
} else if (!this->match(':')) {
|
|
params.push(keyToken);
|
|
shorthand = true;
|
|
value = init;
|
|
} else {
|
|
this->expect(':');
|
|
value = this->parsePatternWithDefault(params, kind);
|
|
}
|
|
} else {
|
|
computed = this->match('[');
|
|
key = this->parseObjectPropertyKey();
|
|
this->expect(':');
|
|
value = this->parsePatternWithDefault(params, kind);
|
|
}
|
|
|
|
return this->finalize(node, new Node.Property('init', key, computed, value, method, shorthand));
|
|
}
|
|
|
|
parseObjectPattern(params, kind: string): Node.ObjectPattern {
|
|
const node = this->createNode();
|
|
const properties: Node.Property[] = [];
|
|
|
|
this->expect('{');
|
|
while (!this->match('}')) {
|
|
properties.push(this->parsePropertyPattern(params, kind));
|
|
if (!this->match('}')) {
|
|
this->expect(',');
|
|
}
|
|
}
|
|
this->expect('}');
|
|
|
|
return this->finalize(node, new Node.ObjectPattern(properties));
|
|
}
|
|
|
|
parsePattern(params, kind?: string): Node.BindingIdentifier | Node.BindingPattern {
|
|
let pattern;
|
|
|
|
if (this->match('[')) {
|
|
pattern = this->parseArrayPattern(params, kind);
|
|
} else if (this->match('{')) {
|
|
pattern = this->parseObjectPattern(params, kind);
|
|
} else {
|
|
if (this->matchKeyword('let') && (kind === 'const' || kind === 'let')) {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages.UnexpectedToken);
|
|
}
|
|
params.push(this->lookahead);
|
|
pattern = this->parseVariableIdentifier(kind);
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
|
|
parsePatternWithDefault(params, kind?: string): Node.AssignmentPattern | Node.BindingIdentifier | Node.BindingPattern {
|
|
const startToken = this->lookahead;
|
|
|
|
let pattern = this->parsePattern(params, kind);
|
|
if (this->match('=')) {
|
|
this->nextToken();
|
|
const previousAllowYield = this->context.allowYield;
|
|
this->context.allowYield = true;
|
|
const right = this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
this->context.allowYield = previousAllowYield;
|
|
pattern = this->finalize(this->startNode(startToken), new Node.AssignmentPattern(pattern, right));
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
*/
|
|
|
|
// ECMA-262 13.3.2 Variable Statement
|
|
RefPtr<IdentifierNode> parseVariableIdentifier(String* kind = String::emptyString)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
RefPtr<ScannerResult> token = this->nextToken();
|
|
if (token->type == Token::KeywordToken && token->valueKeywordKind == Yield) {
|
|
if (this->context->strict) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictReservedWord);
|
|
}
|
|
if (!this->context->allowYield) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
} else if (token->type != Token::IdentifierToken) {
|
|
if (this->context->strict && token->type == Token::KeywordToken && this->scanner->isStrictModeReservedWord(token->valueString)) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictReservedWord);
|
|
} else {
|
|
if (this->context->strict || token->valueString != "let" || (!kind->equals("var"))) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
} else if (this->sourceType == Module && token->type == Token::IdentifierToken && token->valueString == "Await") {
|
|
this->tolerateUnexpectedToken(token);
|
|
}
|
|
|
|
return this->finalize(node, finishIdentifier(token, true));
|
|
}
|
|
|
|
struct DeclarationOptions {
|
|
bool inFor;
|
|
};
|
|
|
|
RefPtr<VariableDeclaratorNode> parseVariableDeclaration(DeclarationOptions& options)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
std::vector<RefPtr<ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<ScannerResult>>> params;
|
|
RefPtr<Node> id = this->parsePattern(params, new ASCIIString("var"));
|
|
|
|
// ECMA-262 12.2.1
|
|
if (this->context->strict && id->type() == Identifier) {
|
|
if (this->scanner->isRestrictedWord(((IdentifierNode*)id.get())->name())) {
|
|
this->tolerateError(Messages::StrictVarName);
|
|
}
|
|
}
|
|
|
|
if (id->type() == Identifier && !this->config.parseSingleFunction) {
|
|
this->scopeContexts.back()->insertName(((IdentifierNode*)id.get())->name(), true);
|
|
}
|
|
|
|
RefPtr<Node> init;
|
|
if (this->match(Substitution)) {
|
|
this->nextToken();
|
|
init = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
} else if (id->type() != Identifier && !options.inFor) {
|
|
this->expect(Substitution);
|
|
}
|
|
|
|
return this->finalize(node, new VariableDeclaratorNode(id.get(), init.get()));
|
|
}
|
|
|
|
VariableDeclaratorVector parseVariableDeclarationList(DeclarationOptions& options)
|
|
{
|
|
DeclarationOptions opt;
|
|
opt.inFor = options.inFor;
|
|
|
|
VariableDeclaratorVector list;
|
|
list.push_back(this->parseVariableDeclaration(opt));
|
|
while (this->match(Comma)) {
|
|
this->nextToken();
|
|
list.push_back(this->parseVariableDeclaration(opt));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
RefPtr<VariableDeclarationNode> parseVariableStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Var);
|
|
DeclarationOptions opt;
|
|
opt.inFor = false;
|
|
VariableDeclaratorVector declarations = this->parseVariableDeclarationList(opt);
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, new VariableDeclarationNode(std::move(declarations) /*, 'var'*/));
|
|
}
|
|
|
|
// ECMA-262 13.4 Empty Statement
|
|
|
|
RefPtr<EmptyStatementNode> parseEmptyStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(SemiColon);
|
|
return this->finalize(node, new EmptyStatementNode());
|
|
}
|
|
|
|
// ECMA-262 13.5 Expression Statement
|
|
|
|
RefPtr<ExpressionStatementNode> parseExpressionStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
RefPtr<Node> expr = this->parseExpression();
|
|
this->consumeSemicolon();
|
|
return this->finalize(node, new ExpressionStatementNode(expr.get()));
|
|
}
|
|
|
|
// ECMA-262 13.6 If statement
|
|
|
|
RefPtr<IfStatementNode> parseIfStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
RefPtr<Node> consequent;
|
|
RefPtr<Node> alternate;
|
|
|
|
this->expectKeyword(If);
|
|
this->expect(LeftParenthesis);
|
|
RefPtr<Node> test = this->parseExpression();
|
|
|
|
if (!this->match(RightParenthesis) && this->config.tolerant) {
|
|
this->tolerateUnexpectedToken(this->nextToken());
|
|
consequent = this->finalize(this->createNode(), new EmptyStatementNode());
|
|
} else {
|
|
this->expect(RightParenthesis);
|
|
consequent = this->parseStatement(this->context->strict ? false : true);
|
|
if (this->matchKeyword(Else)) {
|
|
this->nextToken();
|
|
alternate = this->parseStatement();
|
|
}
|
|
}
|
|
|
|
return this->finalize(node, new IfStatementNode(test.get(), consequent.get(), alternate.get()));
|
|
}
|
|
|
|
// ECMA-262 13.7.2 The do-while Statement
|
|
|
|
RefPtr<DoWhileStatementNode> parseDoWhileStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Do);
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
this->context->inIteration = true;
|
|
RefPtr<Node> body = this->parseStatement(false);
|
|
this->context->inIteration = previousInIteration;
|
|
|
|
this->expectKeyword(While);
|
|
this->expect(LeftParenthesis);
|
|
RefPtr<Node> test = this->parseExpression();
|
|
this->expect(RightParenthesis);
|
|
if (this->match(SemiColon)) {
|
|
this->nextToken();
|
|
}
|
|
|
|
return this->finalize(node, new DoWhileStatementNode(test.get(), body.get()));
|
|
}
|
|
|
|
// ECMA-262 13.7.3 The while Statement
|
|
|
|
RefPtr<WhileStatementNode> parseWhileStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
RefPtr<Node> body;
|
|
|
|
bool prevInLoop = this->context->inLoop;
|
|
this->context->inLoop = true;
|
|
|
|
this->expectKeyword(While);
|
|
this->expect(LeftParenthesis);
|
|
RefPtr<Node> test = this->parseExpression();
|
|
|
|
if (!this->match(RightParenthesis) && this->config.tolerant) {
|
|
this->tolerateUnexpectedToken(this->nextToken());
|
|
body = this->finalize(this->createNode(), new EmptyStatementNode());
|
|
} else {
|
|
this->expect(RightParenthesis);
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
this->context->inIteration = true;
|
|
body = this->parseStatement(false);
|
|
this->context->inIteration = previousInIteration;
|
|
}
|
|
|
|
this->context->inLoop = prevInLoop;
|
|
|
|
return this->finalize(node, new WhileStatementNode(test.get(), body.get()));
|
|
}
|
|
|
|
// ECMA-262 13.7.4 The for Statement
|
|
// ECMA-262 13.7.5 The for-in and for-of Statements
|
|
|
|
RefPtr<StatementNode> parseForStatement()
|
|
{
|
|
RefPtr<Node> init;
|
|
RefPtr<Node> test;
|
|
RefPtr<Node> update;
|
|
bool forIn = true;
|
|
RefPtr<Node> left;
|
|
RefPtr<Node> right;
|
|
|
|
bool prevInLoop = this->context->inLoop;
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(For);
|
|
this->expect(LeftParenthesis);
|
|
|
|
if (this->match(SemiColon)) {
|
|
this->nextToken();
|
|
} else {
|
|
if (this->matchKeyword(Var)) {
|
|
MetaNode metaInit = this->createNode();
|
|
this->nextToken();
|
|
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
DeclarationOptions opt;
|
|
opt.inFor = true;
|
|
VariableDeclaratorVector declarations = this->parseVariableDeclarationList(opt);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (declarations.size() == 1 && this->matchKeyword(In)) {
|
|
RefPtr<VariableDeclaratorNode> decl = declarations[0];
|
|
// if (decl->init() && (decl.id.type === Syntax.ArrayPattern || decl.id.type === Syntax.ObjectPattern || this->context->strict)) {
|
|
if (decl->init() && (decl->id()->type() == ArrayExpression || decl->id()->type() == ObjectExpression || this->context->strict)) {
|
|
this->tolerateError(Messages::ForInOfLoopInitializer, new ASCIIString("for-in"));
|
|
}
|
|
init = this->finalize(metaInit, new VariableDeclarationNode(std::move(declarations) /*, 'var'*/));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseExpression();
|
|
init = nullptr;
|
|
} else if (declarations.size() == 1 && declarations[0]->init() == nullptr
|
|
&& this->lookahead->type == Token::IdentifierToken && this->lookahead->valueString == "of") {
|
|
init = this->finalize(metaInit, new VariableDeclarationNode(std::move(declarations) /*, 'var'*/));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseAssignmentExpression();
|
|
init = nullptr;
|
|
forIn = false;
|
|
} else {
|
|
init = this->finalize(metaInit, new VariableDeclarationNode(std::move(declarations) /*, 'var'*/));
|
|
this->expect(SemiColon);
|
|
}
|
|
} else if (this->matchKeyword(Const) || this->matchKeyword(Let)) {
|
|
// TODO
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
/*
|
|
init = this->createNode();
|
|
const kind = this->nextToken().value;
|
|
|
|
if (!this->context->strict && this->lookahead.value === 'in') {
|
|
init = this->finalize(init, new Node.Identifier(kind));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseExpression();
|
|
init = null;
|
|
} else {
|
|
const previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
const declarations = this->parseBindingList(kind, { inFor: true });
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (declarations.length === 1 && declarations[0].init === null && this->matchKeyword('in')) {
|
|
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseExpression();
|
|
init = null;
|
|
} else if (declarations.length === 1 && declarations[0].init === null && this->matchContextualKeyword('of')) {
|
|
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseAssignmentExpression();
|
|
init = null;
|
|
forIn = false;
|
|
} else {
|
|
this->consumeSemicolon();
|
|
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
|
|
}
|
|
}
|
|
*/
|
|
} else {
|
|
RefPtr<ScannerResult> initStartToken = this->lookahead;
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
init = this->inheritCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (this->matchKeyword(In)) {
|
|
if (init->isLiteral() || init->type() == ASTNodeType::AssignmentExpression || init->type() == ASTNodeType::ThisExpression) {
|
|
this->tolerateError(Messages::InvalidLHSInForIn);
|
|
}
|
|
|
|
this->nextToken();
|
|
this->reinterpretExpressionAsPattern(init.get());
|
|
left = init;
|
|
right = this->parseExpression();
|
|
init = nullptr;
|
|
} else if (this->lookahead->type == Token::IdentifierToken && this->lookahead->valueString == "of") {
|
|
this->throwError("for of is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
if (!this->context->isAssignmentTarget || init.type === Syntax.AssignmentExpression) {
|
|
this->tolerateError(Messages.InvalidLHSInForLoop);
|
|
}
|
|
|
|
this->nextToken();
|
|
this->reinterpretExpressionAsPattern(init);
|
|
left = init;
|
|
right = this->parseAssignmentExpression();
|
|
init = null;
|
|
forIn = false;
|
|
*/
|
|
} else {
|
|
if (this->match(Comma)) {
|
|
ExpressionNodeVector initSeq;
|
|
initSeq.push_back(init);
|
|
while (this->match(Comma)) {
|
|
this->nextToken();
|
|
initSeq.push_back(this->isolateCoverGrammar(&Parser::parseAssignmentExpression));
|
|
}
|
|
init = this->finalize(this->startNode(initStartToken), new SequenceExpressionNode(std::move(initSeq)));
|
|
}
|
|
this->expect(SemiColon);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (left == nullptr) {
|
|
this->context->inLoop = true;
|
|
if (!this->match(SemiColon)) {
|
|
test = this->parseExpression();
|
|
}
|
|
this->expect(SemiColon);
|
|
if (!this->match(RightParenthesis)) {
|
|
update = this->parseExpression();
|
|
}
|
|
}
|
|
|
|
RefPtr<Node> body = nullptr;
|
|
if (!this->match(RightParenthesis) && this->config.tolerant) {
|
|
this->tolerateUnexpectedToken(this->nextToken());
|
|
body = this->finalize(this->createNode(), new EmptyStatementNode());
|
|
} else {
|
|
this->expect(RightParenthesis);
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
this->context->inIteration = true;
|
|
auto functor = std::bind(&Parser::parseStatement, std::ref(*this), false);
|
|
body = this->isolateCoverGrammarWithFunctor(functor);
|
|
this->context->inIteration = previousInIteration;
|
|
}
|
|
|
|
this->context->inLoop = prevInLoop;
|
|
|
|
if (left == nullptr) {
|
|
return this->finalize(node, new ForStatementNode(init.get(), test.get(), update.get(), body.get()));
|
|
} else {
|
|
if (forIn) {
|
|
return this->finalize(node, new ForInStatementNode(left.get(), right.get(), body.get(), false));
|
|
} else {
|
|
this->throwError("For of is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// return this->finalize(node, new Node.ForOfStatement(left, right, body));
|
|
}
|
|
}
|
|
/*
|
|
return (typeof left === 'undefined') ?
|
|
this->finalize(node, new Node.ForStatement(init, test, update, body)) :
|
|
forIn ? this->finalize(node, new Node.ForInStatement(left, right, body)) :
|
|
this->finalize(node, new Node.ForOfStatement(left, right, body));
|
|
|
|
*/
|
|
}
|
|
|
|
void removeLabel(AtomicString label)
|
|
{
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
|
|
if (this->context->labelSet[i].first == label) {
|
|
this->context->labelSet.erase(this->context->labelSet.begin() + i);
|
|
return;
|
|
}
|
|
}
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
bool hasLabel(AtomicString label)
|
|
{
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
|
|
if (this->context->labelSet[i].first == label) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
// ECMA-262 13.8 The continue statement
|
|
|
|
RefPtr<Node> parseContinueStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Continue);
|
|
|
|
RefPtr<IdentifierNode> label = nullptr;
|
|
if (this->lookahead->type == IdentifierToken && !this->hasLineTerminator) {
|
|
label = this->parseVariableIdentifier();
|
|
|
|
if (!hasLabel(label->name())) {
|
|
this->throwError(Messages::UnknownLabel, label->name().string());
|
|
}
|
|
}
|
|
|
|
if (label) {
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
|
|
if (this->context->labelSet[i].first == label->name() && this->context->labelSet[i].second == 1) {
|
|
this->throwError(Messages::UnknownLabel, label->name().string());
|
|
}
|
|
}
|
|
}
|
|
|
|
this->consumeSemicolon();
|
|
if (label == nullptr && !this->context->inIteration) {
|
|
this->throwError(Messages::IllegalContinue);
|
|
}
|
|
|
|
if (label) {
|
|
auto string = label->name().string();
|
|
return this->finalize(node, new ContinueLabelStatementNode(string));
|
|
} else
|
|
return this->finalize(node, new ContinueStatementNode());
|
|
}
|
|
|
|
// ECMA-262 13.9 The break statement
|
|
|
|
RefPtr<Node> parseBreakStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Break);
|
|
|
|
RefPtr<IdentifierNode> label = nullptr;
|
|
if (this->lookahead->type == IdentifierToken && !this->hasLineTerminator) {
|
|
label = this->parseVariableIdentifier();
|
|
|
|
if (!hasLabel(label->name())) {
|
|
this->throwError(Messages::UnknownLabel, label->name().string());
|
|
}
|
|
}
|
|
|
|
this->consumeSemicolon();
|
|
if (label == nullptr && !this->context->inIteration && !this->context->inSwitch) {
|
|
this->throwError(Messages::IllegalBreak);
|
|
}
|
|
|
|
if (label) {
|
|
auto string = label->name().string();
|
|
return this->finalize(node, new BreakLabelStatementNode(string));
|
|
} else
|
|
return this->finalize(node, new BreakStatementNode());
|
|
}
|
|
|
|
// ECMA-262 13.10 The return statement
|
|
|
|
RefPtr<Node> parseReturnStatement()
|
|
{
|
|
if (!this->context->inFunctionBody) {
|
|
this->tolerateError(Messages::IllegalReturn);
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Return);
|
|
|
|
bool hasArgument = !this->match(SemiColon) && !this->match(RightBrace) && !this->hasLineTerminator && this->lookahead->type != EOFToken;
|
|
RefPtr<Node> argument = nullptr;
|
|
if (hasArgument) {
|
|
argument = this->parseExpression();
|
|
}
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, new ReturnStatmentNode(argument.get()));
|
|
}
|
|
|
|
// ECMA-262 13.11 The with statement
|
|
|
|
RefPtr<Node> parseWithStatement()
|
|
{
|
|
if (this->context->strict) {
|
|
this->tolerateError(Messages::StrictModeWith);
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(With);
|
|
this->expect(LeftParenthesis);
|
|
RefPtr<Node> object = this->parseExpression();
|
|
this->expect(RightParenthesis);
|
|
|
|
bool prevInWith = this->context->inWith;
|
|
this->context->inWith = true;
|
|
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
|
|
this->context->labelSet[i].second++;
|
|
}
|
|
|
|
RefPtr<StatementNode> body = this->parseStatement(false);
|
|
this->context->inWith = prevInWith;
|
|
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i++) {
|
|
this->context->labelSet[i].second--;
|
|
}
|
|
|
|
return this->finalize(node, new WithStatementNode(object, body.get()));
|
|
}
|
|
|
|
// ECMA-262 13.12 The switch statement
|
|
|
|
RefPtr<SwitchCaseNode> parseSwitchCase()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
RefPtr<Node> test;
|
|
if (this->matchKeyword(Default)) {
|
|
this->nextToken();
|
|
test = nullptr;
|
|
} else {
|
|
this->expectKeyword(Case);
|
|
test = this->parseExpression();
|
|
}
|
|
this->expect(Colon);
|
|
|
|
RefPtr<StatementContainer> consequent = StatementContainer::create();
|
|
while (true) {
|
|
if (this->match(RightBrace) || this->matchKeyword(Default) || this->matchKeyword(Case)) {
|
|
break;
|
|
}
|
|
consequent->appendChild(this->parseStatementListItem());
|
|
}
|
|
|
|
return this->finalize(node, new SwitchCaseNode(test.get(), consequent.get()));
|
|
}
|
|
|
|
RefPtr<SwitchStatementNode> parseSwitchStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Switch);
|
|
|
|
this->expect(LeftParenthesis);
|
|
RefPtr<Node> discriminant = this->parseExpression();
|
|
this->expect(RightParenthesis);
|
|
|
|
bool previousInSwitch = this->context->inSwitch;
|
|
this->context->inSwitch = true;
|
|
|
|
RefPtr<StatementContainer> casesA = StatementContainer::create(), casesB = StatementContainer::create();
|
|
RefPtr<Node> deflt;
|
|
bool defaultFound = false;
|
|
this->expect(LeftBrace);
|
|
while (true) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
RefPtr<SwitchCaseNode> clause = this->parseSwitchCase();
|
|
if (clause->isDefaultNode()) {
|
|
if (defaultFound) {
|
|
this->throwError(Messages::MultipleDefaultsInSwitch);
|
|
}
|
|
deflt = clause;
|
|
defaultFound = true;
|
|
} else {
|
|
if (defaultFound) {
|
|
casesA->appendChild(clause);
|
|
} else {
|
|
casesB->appendChild(clause);
|
|
}
|
|
}
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
this->context->inSwitch = previousInSwitch;
|
|
|
|
return this->finalize(node, new SwitchStatementNode(discriminant.get(), casesA.get(), deflt.get(), casesB.get(), false));
|
|
}
|
|
|
|
// ECMA-262 13.13 Labelled Statements
|
|
|
|
RefPtr<Node> parseLabelledStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
RefPtr<Node> expr = this->parseExpression();
|
|
|
|
StatementNode* statement;
|
|
if ((expr->type() == Identifier) && this->match(Colon)) {
|
|
this->nextToken();
|
|
|
|
RefPtr<IdentifierNode> id = (IdentifierNode*)expr.get();
|
|
if (hasLabel(id->name())) {
|
|
this->throwError(Messages::Redeclaration, new ASCIIString("Label"), id->name().string());
|
|
}
|
|
|
|
this->context->labelSet.push_back(std::make_pair(id->name(), 0));
|
|
RefPtr<StatementNode> labeledBody = this->parseStatement(this->context->strict ? false : true);
|
|
removeLabel(id->name());
|
|
|
|
statement = new LabeledStatementNode(labeledBody.get(), id->name().string());
|
|
} else {
|
|
this->consumeSemicolon();
|
|
statement = new ExpressionStatementNode(expr.get());
|
|
}
|
|
return this->finalize(node, statement);
|
|
}
|
|
|
|
// ECMA-262 13.14 The throw statement
|
|
|
|
RefPtr<Node> parseThrowStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Throw);
|
|
|
|
if (this->hasLineTerminator) {
|
|
this->throwError(Messages::NewlineAfterThrow);
|
|
}
|
|
|
|
RefPtr<Node> argument = this->parseExpression();
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, new ThrowStatementNode(argument.get()));
|
|
}
|
|
|
|
// ECMA-262 13.15 The try statement
|
|
|
|
RefPtr<CatchClauseNode> parseCatchClause()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expectKeyword(Catch);
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (this->match(RightParenthesis)) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
bool prevInCatch = this->context->inCatch;
|
|
this->context->inCatch = true;
|
|
|
|
std::vector<FunctionDeclarationNode*> vecBefore = std::move(this->context->functionDeclarationsInDirectCatchScope);
|
|
bool prevInDirectCatchScope = this->context->inDirectCatchScope;
|
|
this->context->inDirectCatchScope = true;
|
|
|
|
std::vector<RefPtr<ScannerResult>, GCUtil::gc_malloc_ignore_off_page_allocator<RefPtr<ScannerResult>>> params;
|
|
RefPtr<Node> param = this->parsePattern(params);
|
|
|
|
std::vector<String*, GCUtil::gc_malloc_ignore_off_page_allocator<String*>> paramMap;
|
|
for (size_t i = 0; i < params.size(); i++) {
|
|
bool has = false;
|
|
for (size_t j = 0; j < paramMap.size(); j++) {
|
|
if (paramMap[j]->equals(¶ms[i]->valueString)) {
|
|
has = true;
|
|
}
|
|
}
|
|
if (has) {
|
|
this->tolerateError(Messages::DuplicateBinding, ¶ms[i]->valueString);
|
|
} else {
|
|
paramMap.push_back(StringView::createStringView(params[i]->valueString));
|
|
}
|
|
}
|
|
|
|
if (this->context->strict && param->type() == Identifier) {
|
|
IdentifierNode* id = (IdentifierNode*)param.get();
|
|
if (this->scanner->isRestrictedWord(id->name())) {
|
|
this->tolerateError(Messages::StrictCatchVariable);
|
|
}
|
|
}
|
|
|
|
this->expect(RightParenthesis);
|
|
RefPtr<Node> body = this->parseBlock();
|
|
|
|
this->context->inCatch = prevInCatch;
|
|
|
|
this->context->inDirectCatchScope = prevInDirectCatchScope;
|
|
|
|
std::vector<FunctionDeclarationNode*> vec = std::move(this->context->functionDeclarationsInDirectCatchScope);
|
|
|
|
this->context->functionDeclarationsInDirectCatchScope = std::move(vecBefore);
|
|
|
|
return this->finalize(node, new CatchClauseNode(param.get(), nullptr, body.get(), vec));
|
|
}
|
|
|
|
RefPtr<BlockStatementNode> parseFinallyClause()
|
|
{
|
|
this->expectKeyword(Finally);
|
|
return this->parseBlock();
|
|
}
|
|
|
|
RefPtr<TryStatementNode> parseTryStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Try);
|
|
|
|
RefPtr<BlockStatementNode> block = this->parseBlock();
|
|
RefPtr<CatchClauseNode> handler = this->matchKeyword(Catch) ? this->parseCatchClause() : nullptr;
|
|
RefPtr<BlockStatementNode> finalizer = this->matchKeyword(Finally) ? this->parseFinallyClause() : nullptr;
|
|
|
|
if (!handler && !finalizer) {
|
|
this->throwError(Messages::NoCatchOrFinally);
|
|
}
|
|
|
|
return this->finalize(node, new TryStatementNode(block.get(), handler.get(), CatchClauseNodeVector(), finalizer.get()));
|
|
}
|
|
|
|
// ECMA-262 13.16 The debugger statement
|
|
|
|
RefPtr<StatementNode> parseDebuggerStatement()
|
|
{
|
|
this->throwError("debugger keyword is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
const node = this->createNode();
|
|
this->expectKeyword('debugger');
|
|
this->consumeSemicolon();
|
|
return this->finalize(node, new Node.DebuggerStatement());
|
|
*/
|
|
}
|
|
|
|
// ECMA-262 13 Statements
|
|
RefPtr<StatementNode> parseStatement(bool allowFunctionDeclaration = true)
|
|
{
|
|
checkRecursiveLimit();
|
|
RefPtr<StatementNode> statement;
|
|
switch (this->lookahead->type) {
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
case Token::TemplateToken:
|
|
case Token::RegularExpressionToken:
|
|
statement = this->parseExpressionStatement();
|
|
break;
|
|
|
|
case Token::PunctuatorToken: {
|
|
PunctuatorsKind value = this->lookahead->valuePunctuatorsKind;
|
|
if (value == LeftBrace) {
|
|
statement = this->parseBlock();
|
|
} else if (value == LeftParenthesis) {
|
|
statement = this->parseExpressionStatement();
|
|
} else if (value == SemiColon) {
|
|
statement = this->parseEmptyStatement();
|
|
} else {
|
|
statement = this->parseExpressionStatement();
|
|
}
|
|
break;
|
|
}
|
|
case Token::IdentifierToken:
|
|
statement = asStatementNode(this->parseLabelledStatement());
|
|
break;
|
|
|
|
case Token::KeywordToken:
|
|
switch (this->lookahead->valueKeywordKind) {
|
|
case Break:
|
|
statement = asStatementNode(this->parseBreakStatement());
|
|
break;
|
|
case Continue:
|
|
statement = asStatementNode(this->parseContinueStatement());
|
|
break;
|
|
case Debugger:
|
|
statement = asStatementNode(this->parseDebuggerStatement());
|
|
break;
|
|
case Do:
|
|
statement = asStatementNode(this->parseDoWhileStatement());
|
|
break;
|
|
case For:
|
|
statement = asStatementNode(this->parseForStatement());
|
|
break;
|
|
case Function: {
|
|
if (!allowFunctionDeclaration) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
statement = asStatementNode(this->parseFunctionDeclaration());
|
|
break;
|
|
}
|
|
case If:
|
|
statement = asStatementNode(this->parseIfStatement());
|
|
break;
|
|
case Return:
|
|
statement = asStatementNode(this->parseReturnStatement());
|
|
break;
|
|
case Switch:
|
|
statement = asStatementNode(this->parseSwitchStatement());
|
|
break;
|
|
case Throw:
|
|
statement = asStatementNode(this->parseThrowStatement());
|
|
break;
|
|
case Try:
|
|
statement = asStatementNode(this->parseTryStatement());
|
|
break;
|
|
case Var:
|
|
statement = asStatementNode(this->parseVariableStatement());
|
|
break;
|
|
case While:
|
|
statement = asStatementNode(this->parseWhileStatement());
|
|
break;
|
|
case With:
|
|
statement = asStatementNode(this->parseWithStatement());
|
|
break;
|
|
default:
|
|
statement = asStatementNode(this->parseExpressionStatement());
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
return statement;
|
|
}
|
|
|
|
// ECMA-262 14.1 Function Definition
|
|
|
|
RefPtr<BlockStatementNode> parseFunctionSourceElements()
|
|
{
|
|
if (this->config.parseSingleFunction) {
|
|
if (this->config.parseSingleFunctionChildIndex.asUint32()) {
|
|
size_t realIndex = this->config.parseSingleFunctionChildIndex.asUint32() - 1;
|
|
this->config.parseSingleFunctionChildIndex = SmallValue(this->config.parseSingleFunctionChildIndex.asUint32() + 1);
|
|
InterpretedCodeBlock* currentTarget = this->config.parseSingleFunctionTarget->asInterpretedCodeBlock();
|
|
size_t orgIndex = this->lookahead->end;
|
|
this->expect(LeftBrace);
|
|
|
|
StringView src = currentTarget->childBlocks()[realIndex]->src();
|
|
this->scanner->index = src.length() + orgIndex;
|
|
|
|
this->scanner->lineNumber = currentTarget->childBlocks()[realIndex]->sourceElementStart().line;
|
|
this->scanner->lineStart = currentTarget->childBlocks()[realIndex]->sourceElementStart().index - currentTarget->childBlocks()[realIndex]->sourceElementStart().column;
|
|
this->lookahead->lineNumber = this->scanner->lineNumber;
|
|
this->lookahead->lineNumber = this->scanner->lineStart;
|
|
this->lookahead->type = Token::PunctuatorToken;
|
|
this->lookahead->valuePunctuatorsKind = PunctuatorsKind::RightBrace;
|
|
this->expect(RightBrace);
|
|
return this->finalize(this->createNode(), new BlockStatementNode(StatementContainer::create().get()));
|
|
}
|
|
this->config.parseSingleFunctionChildIndex = SmallValue(this->config.parseSingleFunctionChildIndex.asUint32() + 1);
|
|
}
|
|
bool prevInDirectCatchScope = this->context->inDirectCatchScope;
|
|
this->context->inDirectCatchScope = false;
|
|
|
|
MetaNode nodeStart = this->createNode();
|
|
|
|
this->expect(LeftBrace);
|
|
RefPtr<StatementContainer> body = this->parseDirectivePrologues();
|
|
|
|
auto previousLabelSet = this->context->labelSet;
|
|
bool previousInIteration = this->context->inIteration;
|
|
bool previousInSwitch = this->context->inSwitch;
|
|
bool previousInFunctionBody = this->context->inFunctionBody;
|
|
|
|
this->context->labelSet.clear();
|
|
this->context->inIteration = false;
|
|
this->context->inSwitch = false;
|
|
this->context->inFunctionBody = true;
|
|
|
|
StatementNode* referNode = nullptr;
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
referNode = body->appendChild(this->parseStatementListItem().get(), referNode);
|
|
}
|
|
|
|
this->expect(RightBrace);
|
|
|
|
this->context->labelSet = previousLabelSet;
|
|
this->context->inIteration = previousInIteration;
|
|
this->context->inSwitch = previousInSwitch;
|
|
this->context->inFunctionBody = previousInFunctionBody;
|
|
|
|
scopeContexts.back()->m_locStart.line = nodeStart.line;
|
|
scopeContexts.back()->m_locStart.column = nodeStart.column;
|
|
scopeContexts.back()->m_locStart.index = nodeStart.index;
|
|
|
|
scopeContexts.back()->m_locEnd.index = this->lastMarker.index;
|
|
#ifndef NDEBUG
|
|
scopeContexts.back()->m_locEnd.line = this->lastMarker.lineNumber;
|
|
scopeContexts.back()->m_locEnd.column = this->lastMarker.index - this->lastMarker.lineStart;
|
|
#endif
|
|
|
|
this->context->inDirectCatchScope = prevInDirectCatchScope;
|
|
|
|
if (this->config.parseSingleFunction) {
|
|
return this->finalize(nodeStart, new BlockStatementNode(body.get()));
|
|
} else {
|
|
return this->finalize(nodeStart, new BlockStatementNode(StatementContainer::create().get()));
|
|
}
|
|
}
|
|
|
|
RefPtr<FunctionDeclarationNode> parseFunctionDeclaration(bool identifierIsOptional = false)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Function);
|
|
|
|
bool isGenerator = this->match(Multiply);
|
|
if (isGenerator) {
|
|
this->nextToken();
|
|
}
|
|
|
|
const char* message = nullptr;
|
|
RefPtr<IdentifierNode> id;
|
|
RefPtr<ScannerResult> firstRestricted = nullptr;
|
|
|
|
if (!identifierIsOptional || !this->match(LeftParenthesis)) {
|
|
RefPtr<ScannerResult> token = this->lookahead;
|
|
id = this->parseVariableIdentifier();
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(token->valueString)) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictFunctionName);
|
|
}
|
|
} else {
|
|
if (this->scanner->isRestrictedWord(token->valueString)) {
|
|
firstRestricted = token;
|
|
message = Messages::StrictFunctionName;
|
|
} else if (this->scanner->isStrictModeReservedWord(token->valueString)) {
|
|
firstRestricted = token;
|
|
message = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = !isGenerator;
|
|
|
|
ParseFormalParametersResult formalParameters = this->parseFormalParameters(firstRestricted);
|
|
PatternNodeVector params = std::move(formalParameters.params);
|
|
RefPtr<ScannerResult> stricted = formalParameters.stricted;
|
|
firstRestricted = formalParameters.firstRestricted;
|
|
if (formalParameters.message) {
|
|
message = formalParameters.message;
|
|
}
|
|
|
|
scopeContexts.back()->insertName(id->name(), true);
|
|
scopeContexts.back()->insertUsingName(id->name());
|
|
pushScopeContext(params, id->name());
|
|
extractNamesFromFunctionParams(params);
|
|
|
|
bool previousStrict = this->context->strict;
|
|
RefPtr<BlockStatementNode> body = this->parseFunctionSourceElements();
|
|
if (this->context->strict && firstRestricted) {
|
|
this->throwUnexpectedToken(firstRestricted, message);
|
|
}
|
|
if (this->context->strict && stricted) {
|
|
this->tolerateUnexpectedToken(stricted, message);
|
|
}
|
|
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
if (this->context->inDirectCatchScope) {
|
|
scopeContexts.back()->m_needsSpecialInitialize = true;
|
|
}
|
|
|
|
RefPtr<FunctionDeclarationNode> fd = this->finalize(node, new FunctionDeclarationNode(id->name(), std::move(params), body.get(), popScopeContext(node), isGenerator));
|
|
|
|
if (this->context->inDirectCatchScope) {
|
|
this->context->functionDeclarationsInDirectCatchScope.push_back(fd.get());
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
RefPtr<FunctionExpressionNode> parseFunctionExpression()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Function);
|
|
|
|
bool isGenerator = this->match(Multiply);
|
|
if (isGenerator) {
|
|
this->nextToken();
|
|
}
|
|
|
|
const char* message = nullptr;
|
|
RefPtr<IdentifierNode> id;
|
|
RefPtr<ScannerResult> firstRestricted = nullptr;
|
|
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = !isGenerator;
|
|
|
|
if (!this->match(LeftParenthesis)) {
|
|
RefPtr<ScannerResult> token = this->lookahead;
|
|
id = (!this->context->strict && !isGenerator && this->matchKeyword(Yield)) ? this->parseIdentifierName() : this->parseVariableIdentifier();
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(token->valueString)) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictFunctionName);
|
|
}
|
|
} else {
|
|
if (this->scanner->isRestrictedWord(token->valueString)) {
|
|
firstRestricted = token;
|
|
message = Messages::StrictFunctionName;
|
|
} else if (this->scanner->isStrictModeReservedWord(token->valueString)) {
|
|
firstRestricted = token;
|
|
message = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
ParseFormalParametersResult formalParameters = this->parseFormalParameters(firstRestricted);
|
|
PatternNodeVector params = std::move(formalParameters.params);
|
|
RefPtr<ScannerResult> stricted = formalParameters.stricted;
|
|
firstRestricted = formalParameters.firstRestricted;
|
|
if (formalParameters.message) {
|
|
message = formalParameters.message;
|
|
}
|
|
|
|
AtomicString fnName;
|
|
if (id)
|
|
fnName = id->name();
|
|
|
|
pushScopeContext(params, fnName);
|
|
|
|
if (id) {
|
|
scopeContexts.back()->insertName(fnName, false);
|
|
scopeContexts.back()->insertUsingName(fnName);
|
|
}
|
|
|
|
extractNamesFromFunctionParams(params);
|
|
bool previousStrict = this->context->strict;
|
|
RefPtr<BlockStatementNode> body = this->parseFunctionSourceElements();
|
|
if (this->context->strict && firstRestricted) {
|
|
this->throwUnexpectedToken(firstRestricted, message);
|
|
}
|
|
if (this->context->strict && stricted) {
|
|
this->tolerateUnexpectedToken(stricted, message);
|
|
}
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
return this->finalize(node, new FunctionExpressionNode(fnName, std::move(params), body.get(), popScopeContext(node), isGenerator));
|
|
}
|
|
|
|
// ECMA-262 14.1.1 Directive Prologues
|
|
|
|
RefPtr<Node> parseDirective()
|
|
{
|
|
RefPtr<ScannerResult> token = this->lookahead;
|
|
StringView directiveValue;
|
|
bool isLiteral = false;
|
|
|
|
MetaNode node = this->createNode();
|
|
RefPtr<Node> expr = this->parseExpression();
|
|
if (expr->type() == Literal) {
|
|
isLiteral = true;
|
|
directiveValue = token->valueString;
|
|
}
|
|
this->consumeSemicolon();
|
|
|
|
if (isLiteral) {
|
|
return this->finalize(node, new DirectiveNode(asExpressionNode(expr).get(), directiveValue));
|
|
} else {
|
|
return this->finalize(node, new ExpressionStatementNode(expr.get()));
|
|
}
|
|
}
|
|
|
|
RefPtr<StatementContainer> parseDirectivePrologues()
|
|
{
|
|
RefPtr<ScannerResult> firstRestricted = nullptr;
|
|
|
|
RefPtr<StatementContainer> body = StatementContainer::create();
|
|
while (true) {
|
|
RefPtr<ScannerResult> token = this->lookahead;
|
|
if (token->type != StringLiteralToken) {
|
|
break;
|
|
}
|
|
|
|
RefPtr<Node> statement = this->parseDirective();
|
|
body->appendChild(statement->asStatementNode());
|
|
|
|
if (statement->type() != Directive) {
|
|
break;
|
|
}
|
|
|
|
DirectiveNode* directive = (DirectiveNode*)statement.get();
|
|
if (token->plain && directive->value().equals("use strict")) {
|
|
this->scopeContexts.back()->m_isStrict = this->context->strict = true;
|
|
if (firstRestricted) {
|
|
this->tolerateUnexpectedToken(firstRestricted, Messages::StrictOctalLiteral);
|
|
}
|
|
} else {
|
|
if (!firstRestricted && token->octal) {
|
|
firstRestricted = token;
|
|
}
|
|
}
|
|
}
|
|
|
|
return body;
|
|
}
|
|
|
|
// ECMA-262 14.3 Method Definitions
|
|
|
|
RefPtr<FunctionExpressionNode> parseGetterMethod()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(LeftParenthesis);
|
|
this->expect(RightParenthesis);
|
|
|
|
bool isGenerator = false;
|
|
ParseFormalParametersResult params(PatternNodeVector(), nullptr, nullptr, nullptr);
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = false;
|
|
RefPtr<Node> method = this->parsePropertyMethod(params);
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
extractNamesFromFunctionParams(params.params);
|
|
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(params.params), method.get(), popScopeContext(node), isGenerator));
|
|
}
|
|
|
|
RefPtr<FunctionExpressionNode> parseSetterMethod()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ParseParameterOptions options;
|
|
|
|
bool isGenerator = false;
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = false;
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (this->match(RightParenthesis)) {
|
|
this->tolerateUnexpectedToken(this->lookahead);
|
|
} else {
|
|
this->parseFormalParameter(options);
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
ParseFormalParametersResult options2(std::move(options.params), options.stricted, options.firstRestricted, options.message);
|
|
RefPtr<Node> method = this->parsePropertyMethod(options2);
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
extractNamesFromFunctionParams(options.params);
|
|
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(options.params), method.get(), popScopeContext(node), isGenerator));
|
|
}
|
|
|
|
FunctionExpressionNode* parseGeneratorMethod()
|
|
{
|
|
// TODO
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
MetaNode node = this->createNode();
|
|
const isGenerator = true;
|
|
const previousAllowYield = this->context.allowYield;
|
|
|
|
this->context.allowYield = true;
|
|
const params = this->parseFormalParameters();
|
|
this->context.allowYield = false;
|
|
const method = this->parsePropertyMethod(params);
|
|
this->context.allowYield = previousAllowYield;
|
|
|
|
return this->finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator));
|
|
*/
|
|
}
|
|
|
|
// ECMA-262 14.4 Generator Function Definitions
|
|
|
|
Node* parseYieldExpression()
|
|
{
|
|
this->throwError("yield keyword is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
const node = this->createNode();
|
|
this->expectKeyword('yield');
|
|
|
|
let argument = null;
|
|
let delegate = false;
|
|
if (!this->hasLineTerminator) {
|
|
const previousAllowYield = this->context.allowYield;
|
|
this->context.allowYield = false;
|
|
delegate = this->match('*');
|
|
if (delegate) {
|
|
this->nextToken();
|
|
argument = this->parseAssignmentExpression();
|
|
} else {
|
|
if (!this->match(';') && !this->match('}') && !this->match(')') && this->lookahead.type !== Token.EOF) {
|
|
argument = this->parseAssignmentExpression();
|
|
}
|
|
}
|
|
this->context.allowYield = previousAllowYield;
|
|
}
|
|
|
|
return this->finalize(node, new Node.YieldExpression(argument, delegate));
|
|
*/
|
|
}
|
|
|
|
// ECMA-262 14.5 Class Definitions
|
|
/*
|
|
parseClassElement(hasConstructor): Node.Property {
|
|
let token = this.lookahead;
|
|
let node = this.createNode();
|
|
|
|
let kind: string;
|
|
let key: Node.PropertyKey;
|
|
let value: Node.FunctionExpression;
|
|
let computed = false;
|
|
let method = false;
|
|
let isStatic = false;
|
|
|
|
if (this.match('*')) {
|
|
this.nextToken();
|
|
} else {
|
|
computed = this.match('[');
|
|
key = this.parseObjectPropertyKey();
|
|
const id = <Node.Identifier>key;
|
|
if (id.name === 'static' && (this.qualifiedPropertyName(this.lookahead) || this.match('*'))) {
|
|
token = this.lookahead;
|
|
isStatic = true;
|
|
computed = this.match('[');
|
|
if (this.match('*')) {
|
|
this.nextToken();
|
|
} else {
|
|
key = this.parseObjectPropertyKey();
|
|
}
|
|
}
|
|
}
|
|
|
|
const lookaheadPropertyKey = this.qualifiedPropertyName(this.lookahead);
|
|
if (token.type === Token.Identifier) {
|
|
if (token.value === 'get' && lookaheadPropertyKey) {
|
|
kind = 'get';
|
|
computed = this.match('[');
|
|
key = this.parseObjectPropertyKey();
|
|
this.context.allowYield = false;
|
|
value = this.parseGetterMethod();
|
|
} else if (token.value === 'set' && lookaheadPropertyKey) {
|
|
kind = 'set';
|
|
computed = this.match('[');
|
|
key = this.parseObjectPropertyKey();
|
|
value = this.parseSetterMethod();
|
|
}
|
|
} else if (token.type === Token.Punctuator && token.value === '*' && lookaheadPropertyKey) {
|
|
kind = 'init';
|
|
computed = this.match('[');
|
|
key = this.parseObjectPropertyKey();
|
|
value = this.parseGeneratorMethod();
|
|
method = true;
|
|
}
|
|
|
|
if (!kind && key && this.match('(')) {
|
|
kind = 'init';
|
|
value = this.parsePropertyMethodFunction();
|
|
method = true;
|
|
}
|
|
|
|
if (!kind) {
|
|
this.throwUnexpectedToken(this.lookahead);
|
|
}
|
|
|
|
if (kind === 'init') {
|
|
kind = 'method';
|
|
}
|
|
|
|
if (!computed) {
|
|
if (isStatic && this.isPropertyKey(key, 'prototype')) {
|
|
this.throwUnexpectedToken(token, Messages.StaticPrototype);
|
|
}
|
|
if (!isStatic && this.isPropertyKey(key, 'constructor')) {
|
|
if (kind !== 'method' || !method || value.generator) {
|
|
this.throwUnexpectedToken(token, Messages.ConstructorSpecialMethod);
|
|
}
|
|
if (hasConstructor.value) {
|
|
this.throwUnexpectedToken(token, Messages.DuplicateConstructor);
|
|
} else {
|
|
hasConstructor.value = true;
|
|
}
|
|
kind = 'constructor';
|
|
}
|
|
}
|
|
|
|
|
|
return this.finalize(node, new Node.MethodDefinition(key, computed, value, kind, isStatic));
|
|
}
|
|
|
|
parseClassElementList(): Node.Property[] {
|
|
const body = [];
|
|
let hasConstructor = { value: false };
|
|
|
|
this.expect('{');
|
|
while (!this.match('}')) {
|
|
if (this.match(';')) {
|
|
this.nextToken();
|
|
} else {
|
|
body.push(this.parseClassElement(hasConstructor));
|
|
}
|
|
}
|
|
this.expect('}');
|
|
|
|
return body;
|
|
}
|
|
|
|
parseClassBody(): Node.ClassBody {
|
|
const node = this.createNode();
|
|
const elementList = this.parseClassElementList();
|
|
|
|
return this.finalize(node, new Node.ClassBody(elementList));
|
|
}
|
|
|
|
parseClassDeclaration(identifierIsOptional?: boolean): Node.ClassDeclaration {
|
|
const node = this.createNode();
|
|
|
|
const previousStrict = this.context.strict;
|
|
this.context.strict = true;
|
|
this.expectKeyword('class');
|
|
|
|
const id = (identifierIsOptional && (this.lookahead.type !== Token.Identifier)) ? null : this.parseVariableIdentifier();
|
|
let superClass = null;
|
|
if (this.matchKeyword('extends')) {
|
|
this.nextToken();
|
|
superClass = this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall);
|
|
}
|
|
const classBody = this.parseClassBody();
|
|
this.context.strict = previousStrict;
|
|
|
|
return this.finalize(node, new Node.ClassDeclaration(id, superClass, classBody));
|
|
}
|
|
|
|
parseClassExpression(): Node.ClassExpression {
|
|
const node = this.createNode();
|
|
|
|
const previousStrict = this.context.strict;
|
|
this.context.strict = true;
|
|
this.expectKeyword('class');
|
|
const id = (this.lookahead.type === Token.Identifier) ? this.parseVariableIdentifier() : null;
|
|
let superClass = null;
|
|
if (this.matchKeyword('extends')) {
|
|
this.nextToken();
|
|
superClass = this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall);
|
|
}
|
|
const classBody = this.parseClassBody();
|
|
this.context.strict = previousStrict;
|
|
|
|
return this.finalize(node, new Node.ClassExpression(id, superClass, classBody));
|
|
}
|
|
*/
|
|
Node* parseClassExpression()
|
|
{
|
|
this->throwError("class keyword is not supported yet");
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
// ECMA-262 15.1 Scripts
|
|
// ECMA-262 15.2 Modules
|
|
|
|
RefPtr<ProgramNode> parseProgram()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
scopeContexts.push_back(new ASTScopeContext(this->context->strict));
|
|
RefPtr<StatementContainer> body = this->parseDirectivePrologues();
|
|
StatementNode* referNode = nullptr;
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
referNode = body->appendChild(this->parseStatementListItem().get(), referNode);
|
|
}
|
|
scopeContexts.back()->m_locStart.line = node.line;
|
|
scopeContexts.back()->m_locStart.column = node.column;
|
|
scopeContexts.back()->m_locStart.index = node.index;
|
|
|
|
MetaNode endNode = this->createNode();
|
|
#ifndef NDEBUG
|
|
scopeContexts.back()->m_locEnd.line = endNode.line;
|
|
scopeContexts.back()->m_locEnd.column = endNode.column;
|
|
#endif
|
|
scopeContexts.back()->m_locEnd.index = endNode.index;
|
|
return this->finalize(node, new ProgramNode(body.get(), scopeContexts.back() /*, this->sourceType*/));
|
|
}
|
|
|
|
// ECMA-262 15.2.2 Imports
|
|
/*
|
|
parseModuleSpecifier(): Node.Literal {
|
|
const node = this.createNode();
|
|
|
|
if (this.lookahead.type !== Token.StringLiteral) {
|
|
this.throwError(Messages.InvalidModuleSpecifier);
|
|
}
|
|
|
|
const token = this.nextToken();
|
|
const raw = this.getTokenRaw(token);
|
|
return this.finalize(node, new Node.Literal(token.value, raw));
|
|
}
|
|
|
|
// import {<foo as bar>} ...;
|
|
parseImportSpecifier(): Node.ImportSpecifier {
|
|
const node = this.createNode();
|
|
|
|
let local;
|
|
const imported = this.parseIdentifierName();
|
|
if (this.matchContextualKeyword('as')) {
|
|
this.nextToken();
|
|
local = this.parseVariableIdentifier();
|
|
} else {
|
|
local = imported;
|
|
}
|
|
|
|
return this.finalize(node, new Node.ImportSpecifier(local, imported));
|
|
}
|
|
|
|
// {foo, bar as bas}
|
|
parseNamedImports(): Node.ImportSpecifier[] {
|
|
this.expect('{');
|
|
const specifiers: Node.ImportSpecifier[] = [];
|
|
while (!this.match('}')) {
|
|
specifiers.push(this.parseImportSpecifier());
|
|
if (!this.match('}')) {
|
|
this.expect(',');
|
|
}
|
|
}
|
|
this.expect('}');
|
|
|
|
return specifiers;
|
|
}
|
|
|
|
// import <foo> ...;
|
|
parseImportDefaultSpecifier(): Node.ImportDefaultSpecifier {
|
|
const node = this.createNode();
|
|
const local = this.parseIdentifierName();
|
|
return this.finalize(node, new Node.ImportDefaultSpecifier(local));
|
|
}
|
|
|
|
// import <* as foo> ...;
|
|
parseImportNamespaceSpecifier(): Node.ImportNamespaceSpecifier {
|
|
const node = this.createNode();
|
|
|
|
this.expect('*');
|
|
if (!this.matchContextualKeyword('as')) {
|
|
this.throwError(Messages.NoAsAfterImportNamespace);
|
|
}
|
|
this.nextToken();
|
|
const local = this.parseIdentifierName();
|
|
|
|
return this.finalize(node, new Node.ImportNamespaceSpecifier(local));
|
|
}
|
|
|
|
parseImportDeclaration(): Node.ImportDeclaration {
|
|
if (this.context.inFunctionBody) {
|
|
this.throwError(Messages.IllegalImportDeclaration);
|
|
}
|
|
|
|
const node = this.createNode();
|
|
this.expectKeyword('import');
|
|
|
|
let src: Node.Literal;
|
|
let specifiers: Node.ImportDeclarationSpecifier[] = [];
|
|
if (this.lookahead.type === Token.StringLiteral) {
|
|
// import 'foo';
|
|
src = this.parseModuleSpecifier();
|
|
} else {
|
|
if (this.match('{')) {
|
|
// import {bar}
|
|
specifiers = specifiers.concat(this.parseNamedImports());
|
|
} else if (this.match('*')) {
|
|
// import * as foo
|
|
specifiers.push(this.parseImportNamespaceSpecifier());
|
|
} else if (this.isIdentifierName(this.lookahead) && !this.matchKeyword('default')) {
|
|
// import foo
|
|
specifiers.push(this.parseImportDefaultSpecifier());
|
|
if (this.match(',')) {
|
|
this.nextToken();
|
|
if (this.match('*')) {
|
|
// import foo, * as foo
|
|
specifiers.push(this.parseImportNamespaceSpecifier());
|
|
} else if (this.match('{')) {
|
|
// import foo, {bar}
|
|
specifiers = specifiers.concat(this.parseNamedImports());
|
|
} else {
|
|
this.throwUnexpectedToken(this.lookahead);
|
|
}
|
|
}
|
|
} else {
|
|
this.throwUnexpectedToken(this.nextToken());
|
|
}
|
|
|
|
if (!this.matchContextualKeyword('from')) {
|
|
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
|
|
this.throwError(message, this.lookahead.value);
|
|
}
|
|
this.nextToken();
|
|
src = this.parseModuleSpecifier();
|
|
}
|
|
this.consumeSemicolon();
|
|
|
|
return this.finalize(node, new Node.ImportDeclaration(specifiers, src));
|
|
}
|
|
|
|
// ECMA-262 15.2.3 Exports
|
|
|
|
parseExportSpecifier(): Node.ExportSpecifier {
|
|
const node = this.createNode();
|
|
|
|
const local = this.parseIdentifierName();
|
|
let exported = local;
|
|
if (this.matchContextualKeyword('as')) {
|
|
this.nextToken();
|
|
exported = this.parseIdentifierName();
|
|
}
|
|
|
|
return this.finalize(node, new Node.ExportSpecifier(local, exported));
|
|
}
|
|
|
|
parseExportDeclaration(): Node.ExportDeclaration {
|
|
if (this.context.inFunctionBody) {
|
|
this.throwError(Messages.IllegalExportDeclaration);
|
|
}
|
|
|
|
const node = this.createNode();
|
|
this.expectKeyword('export');
|
|
|
|
let exportDeclaration;
|
|
if (this.matchKeyword('default')) {
|
|
// export default ...
|
|
this.nextToken();
|
|
if (this.matchKeyword('function')) {
|
|
// export default function foo () {}
|
|
// export default function () {}
|
|
const declaration = this.parseFunctionDeclaration(true);
|
|
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
|
|
} else if (this.matchKeyword('class')) {
|
|
// export default class foo {}
|
|
const declaration = this.parseClassDeclaration(true);
|
|
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
|
|
} else {
|
|
if (this.matchContextualKeyword('from')) {
|
|
this.throwError(Messages.UnexpectedToken, this.lookahead.value);
|
|
}
|
|
// export default {};
|
|
// export default [];
|
|
// export default (1 + 2);
|
|
const declaration = this.match('{') ? this.parseObjectInitializer() :
|
|
this.match('[') ? this.parseArrayInitializer() : this.parseAssignmentExpression();
|
|
this.consumeSemicolon();
|
|
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
|
|
}
|
|
|
|
} else if (this.match('*')) {
|
|
// export * from 'foo';
|
|
this.nextToken();
|
|
if (!this.matchContextualKeyword('from')) {
|
|
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
|
|
this.throwError(message, this.lookahead.value);
|
|
}
|
|
this.nextToken();
|
|
const src = this.parseModuleSpecifier();
|
|
this.consumeSemicolon();
|
|
exportDeclaration = this.finalize(node, new Node.ExportAllDeclaration(src));
|
|
|
|
} else if (this.lookahead.type === Token.Keyword) {
|
|
// export var f = 1;
|
|
let declaration;
|
|
switch (this.lookahead.value) {
|
|
case 'let':
|
|
case 'const':
|
|
declaration = this.parseLexicalDeclaration({ inFor: false });
|
|
break;
|
|
case 'var':
|
|
case 'class':
|
|
case 'function':
|
|
declaration = this.parseStatementListItem();
|
|
break;
|
|
default:
|
|
this.throwUnexpectedToken(this.lookahead);
|
|
}
|
|
exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(declaration, [], null));
|
|
|
|
} else {
|
|
const specifiers = [];
|
|
let source = null;
|
|
let isExportFromIdentifier = false;
|
|
|
|
this.expect('{');
|
|
while (!this.match('}')) {
|
|
isExportFromIdentifier = isExportFromIdentifier || this.matchKeyword('default');
|
|
specifiers.push(this.parseExportSpecifier());
|
|
if (!this.match('}')) {
|
|
this.expect(',');
|
|
}
|
|
}
|
|
this.expect('}');
|
|
|
|
if (this.matchContextualKeyword('from')) {
|
|
// export {default} from 'foo';
|
|
// export {foo} from 'foo';
|
|
this.nextToken();
|
|
source = this.parseModuleSpecifier();
|
|
this.consumeSemicolon();
|
|
} else if (isExportFromIdentifier) {
|
|
// export {default}; // missing fromClause
|
|
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
|
|
this.throwError(message, this.lookahead.value);
|
|
} else {
|
|
// export {foo};
|
|
this.consumeSemicolon();
|
|
}
|
|
exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(null, specifiers, source));
|
|
}
|
|
|
|
return exportDeclaration;
|
|
}
|
|
*/
|
|
};
|
|
|
|
RefPtr<ProgramNode> parseProgram(::Escargot::Context* ctx, StringView source, ParserASTNodeHandler handler, bool strictFromOutside, size_t stackRemain)
|
|
{
|
|
Parser parser(ctx, source, handler, stackRemain);
|
|
parser.context->strict = strictFromOutside;
|
|
RefPtr<ProgramNode> nd = parser.parseProgram();
|
|
return nd;
|
|
}
|
|
|
|
std::tuple<RefPtr<Node>, ASTScopeContext*> parseSingleFunction(::Escargot::Context* ctx, InterpretedCodeBlock* codeBlock, size_t stackRemain)
|
|
{
|
|
Parser parser(ctx, codeBlock->src(), nullptr, stackRemain, codeBlock->sourceElementStart().line, codeBlock->sourceElementStart().column);
|
|
parser.trackUsingNames = false;
|
|
parser.config.parseSingleFunction = true;
|
|
parser.config.parseSingleFunctionTarget = codeBlock;
|
|
auto scopeCtx = new ASTScopeContext(codeBlock->isStrict());
|
|
parser.scopeContexts.pushBack(scopeCtx);
|
|
auto nd = parser.parseFunctionSourceElements();
|
|
return std::make_tuple(nd, scopeCtx);
|
|
}
|
|
|
|
bool isIdentifierPartData[128] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
|
};
|
|
|
|
bool isIdentifierStartData[128] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
|
};
|
|
}
|
|
}
|